【问题标题】:Does having variations of gestures in gesture library improve recognition?手势库中的手势变化会提高识别率吗?
【发布时间】:2011-12-06 07:21:04
【问题描述】:

我正致力于在我的应用中实现手势识别,使用 Gestures Builder 创建一个手势库。我想知道手势的多种变化是否会帮助或阻碍识别(或性能)。例如,我想识别一个圆形手势。我将至少有两种变体——一种用于顺时针方向,一种用于逆时针方向,具有相同的语义,这样用户就不必考虑它了。但是,我想知道是否需要为每个方向保存几个手势,例如,各种半径的手势,或者具有“足够接近”的不同形状 - 如鸡蛋形状、椭圆等,包括不同的角度旋转每个。有人有这方面的经验吗?

【问题讨论】:

    标签: android gesture gesture-recognition gestures


    【解决方案1】:

    好的,经过一些实验和阅读android源码后,我学到了一点……首先,看来我不必担心在我的手势库中创建不同的手势来覆盖不同的角度旋转或我的圆形手势的方向(顺时针/逆时针)。默认情况下,GestureStore 使用 SEQUENCE_SENSITIVE 的序列类型(意味着起点和终点很重要)和 ORIENTATION_SENSITIVE 的方向样式(意味着旋转角度很重要)。但是,可以使用 'setOrientationStyle(ORIENTATION_INVARIANT)' 和 setSequenceType(SEQUENCE_INVARIANT) 覆盖这些默认值。

    此外,引用源中的 cmets...“当使用 SEQUENCE_SENSITIVE 时,当前仅允许单笔手势”和“ORIENTATION_SENSITIVE 和 ORIENTATION_INVARIANT 仅用于 SEQUENCE_SENSITIVE 手势”。

    有趣的是,ORIENTATION_SENSITIVE 似乎不仅仅意味着“方向很重要”。它的值为 2,与它相关的 cmets 和一些相关的(未记录的)常量意味着您可以请求不同级别的灵敏度。

    // 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)作为参数 numOrientations 传递给 GestureUtils.minimumCosineDistance(),因此执行一些超出我的计算支付等级(见下文)。如果有人可以解释这一点,我很感兴趣。我知道它正在计算两个手势之间的角度差,但我不明白它使用 numOrientations 参数的方式。我的期望是,如果我指定值 2,它会找到手势 A 和手势 B 的两个变体之间的最小距离——一个变体是“正常 B”,另一个是 B 旋转 180 度。因此,我希望值 8 会考虑 B 的 8 个变体,相隔 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 的范围内),几乎所有与圆形相似的东西。但是,对于甚至不接近圆形的手势(水平线、V 形等),它也返回了 20 左右的分数。所以,我对应该匹配和不应该匹配之间的分离感觉不好。似乎效果最好的是存储两个手势,每个方向一个,并结合使用 SEQUENCE_SENSITIVE 和 ORIENTATION_INVARIANT。对于任何模糊的圆形动作,我的得分为 2.5 或更高,但对于非圆形手势,得分低于 1(或根本不匹配)。

    【讨论】:

    • 另外,在我的实验中,我发现没有必要将不同半径的圆形手势存储到手势库中,以便与不同半径的绘制手势得到很好的匹配——圆形手势我在屏幕上绘制了不同半径的相同存储手势(即具有可比较的分数)。关于性能,我还没有做计时,但根据代码,我怀疑性能与库中的手势数量大致呈线性关系。
    • 这里有个提示:如果你调用 setSequenceType(GestureStore.SEQUENCE_INVARIANT),一定要在调用 load() 之前做,否则你会发现你根本没有匹配到。
    • 谢谢你。使用额外的方向敏感度级别解决了我在使用类似笔画但旋转不同的手势时遇到的问题。在我使用此设置之前,像 plus 和 equal 这样的手势会发生冲突。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多