Улучшает ли распознавание наличие вариаций жестов в библиотеке жестов? - PullRequest
14 голосов
/ 12 октября 2011

Я работаю над реализацией распознавания жестов в своем приложении, используя Gestures Builder для создания библиотеки жестов.Мне интересно, поможет ли несколько вариантов жеста или затруднит распознавание (или исполнение).Например, я хочу распознать круговой жест.У меня будет как минимум два варианта - один для круга по часовой стрелке, другой для против часовой стрелки, с тем же семантическим значением, чтобы пользователю не приходилось об этом думать.Однако мне интересно, было бы желательно сохранить несколько жестов для каждого направления, например, с различными радиусами или с разными формами, которые «достаточно близки» - например, в форме яйца, эллипсов и т. Д., Включая различные угловые поворотыкаждого.У кого-нибудь есть опыт с этим?

1 Ответ

19 голосов
/ 14 октября 2011

ОК, после некоторого эксперимента и чтения исходного кода Android я немного научился ... Во-первых, мне кажется, что мне не обязательно беспокоиться о создании разных жестов в моей библиотеке жестов, чтобы покрыть разные угловые повороты. или направления (по часовой стрелке / против часовой стрелки) моего кругового жеста. По умолчанию в GestureStore используется тип последовательности SEQUENCE_SENSITIVE (что означает, что начальная и конечная точки имеют значение) и стиль ориентации ORIENTATION_SENSITIVE (что означает, что угол поворота имеет значение). Однако эти значения по умолчанию могут быть переопределены с помощью setOrientationStyle (ORIENTATION_INVARIANT) и setSequenceType (SEQUENCE_INVARIANT).

Кроме того, чтобы процитировать комментарии в источнике ... "при использовании SEQUENCE_SENSITIVE в настоящее время разрешены только жесты с одним ударом", а "ORIENTATION_SENSITIVE и ORIENTATION_INVARIANT предназначены только для жестов SEQUENCE_SENSITIVE".

Интересно, что ORIENTATION_SENSITIVE означает больше, чем просто "вопросы ориентации". Его значение равно 2, и связанные с ним комментарии и некоторые связанные (недокументированные) константы подразумевают, что вы можете запрашивать разные уровни чувствительности.

// at most 2 directions can be recognized
public static final int ORIENTATION_SENSITIVE = 2;
// at most 4 directions can be recognized
static final int ORIENTATION_SENSITIVE_4 = 4;
// at most 8 directions can be recognized
static final int ORIENTATION_SENSITIVE_8 = 8;

Во время вызова GestureLibary.recognize () значение типа ориентации (1, 2, 4 или 8) передается в GestureUtils.minimumCosineDistance () в качестве параметра numOrientations, после чего выполняются некоторые вычисления, которые выше моего класс оплаты (см. ниже). Если кто-то может объяснить это, мне интересно. Я понял, что он вычисляет угловую разницу между двумя жестами, но я не понимаю, как он использует параметр numOrientations. Я ожидаю, что, если я укажу значение 2, он найдет минимальное расстояние между жестом A и двумя вариациями жеста B: один вариант - «нормальный B», а другой - B, развернутый на 180 градусов. Таким образом, я ожидаю, что значение 8 будет учитывать 8 вариаций B, разнесенных на 45 градусов. Однако, хотя я не полностью понимаю приведенную ниже математику, мне не кажется, что значение numOrientations 4 или 8 используется непосредственно в любых вычислениях, хотя значения больше 2 приводят к отдельному пути кода. Может быть, поэтому другие значения недокументированы.

/**
 * Calculates the "minimum" cosine distance between two instances.
 * 
 * @param vector1
 * @param vector2
 * @param numOrientations the maximum number of orientation allowed
 * @return the distance between the two instances (between 0 and Math.PI)
 */
static float minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
    final int len = vector1.length;
    float a = 0;
    float b = 0;
    for (int i = 0; i < len; i += 2) {
        a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1];
        b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i];
    }
    if (a != 0) {
        final float tan = b/a;
        final double angle = Math.atan(tan);
        if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) {
            return (float) Math.acos(a);
        } else {
            final double cosine = Math.cos(angle);
            final double sine = cosine * tan; 
            return (float) Math.acos(a * cosine + b * sine);
        }
    } else {
        return (float) Math.PI / 2;
    }
}

Основываясь на моем чтении, я предположил, что самый простой и лучший подход - иметь один сохраненный круговой жест, устанавливая тип последовательности и ориентацию инвариантными. Таким образом, все круглые должны соответствовать очень хорошо, независимо от направления или ориентации. Так что я попробовал это, и он дал высокие результаты (в диапазоне от 25 до 70) для почти всего, что отдаленно напоминало круг. Тем не менее, он также дал около 20 баллов за жесты, которые даже не были близки к круговым (горизонтальные линии, V-образные формы и т. Д.). Таким образом, я не чувствовал себя хорошо по поводу разделения между тем, что должно быть совпадением, а что не должно. Лучше всего работать с двумя сохраненными жестами, по одному в каждом направлении, и использовать SEQUENCE_SENSITIVE в сочетании с ORIENTATION_INVARIANT. Это дает мне 2,5 балла или выше для чего-то неопределенно круглого, но баллы ниже 1 (или вообще без совпадений) для жестов, которые не круглые.

...