【问题标题】:Simple circular gesture detection简单的圆形手势检测
【发布时间】:2011-01-24 12:18:48
【问题描述】:

我正在寻找一种简单的程序化方式来检测用户是否绘制了圆形。我在 C 中工作,但很高兴使用伪代码工作。用谷歌搜索一下会发现一些(希望是)过于复杂的方法。

我将鼠标坐标跟踪为浮点数,并创建了一个向量数组来跟踪鼠标随时间的移动。本质上,我希望检测何时绘制了一个圆圈,然后忽略与该圆圈无关的所有运动数据。

我对如何实现这一点有一个基本的想法:

使用轮询功能跟踪所有移动。每次轮询函数时,都会存储当前鼠标位置。在这里,我们遍历历史位置数据并进行粗略的“对齐位置”以比较两个位置。如果新位置距离旧位置足够近,我们会删除旧位置之前的所有历史数据。

虽然这在理论上可行,但在实践中却是一团糟。有没有人有什么建议?如果建议的方法可以检测它是顺时针还是逆时针绘制,则加分。

【问题讨论】:

  • 我用 Multitouch for Java 做了类似的事情,但我认为不再是这样了。可以从点到点画一条线并计算所有点的中位数en.wikipedia.org/wiki/Winding_number,但我不确定如何选择随机运动变为圆形的点。

标签: c gesture-recognition


【解决方案1】:

这里有一个算法来判断一个点数组是否适合一个圆:

  1. 计算点的质心(所有 x 和 y 坐标的平均值)

  2. 计算所有点到质心的距离

  3. 找出最大和最小距离

  4. 如果最大 - 最小

注意,这也会检测圆的一部分,因此您需要确定扫过足够多的角度才能使其成为一个完整的圆。

为此:

  1. 如上计算质心
  2. 计算质心和每个点之间的角度(使用 atan2 函数)
  3. 将角度映射到线段(我发现 12 个 30 度线段适合我;只需将角度除以 30 并向下舍入为整数 - 假设您在此处以度为单位)
  4. 如果所有线段至少包含 1 个点,则它是一个圆(即映射的线段数组包含 0 到 11 之间的所有值)

  5. 奖励:增加角度是逆时针的;顺时针递减

【讨论】:

  • 在完全没有运动的情况下不会给出误报吗?
  • 在圆形算法中,每个段至少需要 1 个点,所以不需要。如果你想检测一个部分,你只能在你有 x 个不同点的情况下运行算法。根据系统的不同,您可能会或可能不会获得没有移动的点数据。例如在 iOS 上,您可以从移动的触摸中获取数据。
【解决方案2】:

1 - 选择任意 3 个点

2 - 如果点是共线的 +/- 'some buffer' 那么它不是一个圆。

3 - 使用维基百科上描述的方法来寻找三角形的外接圆来找到候选圆的中点和半径

三角形的外心可以通过画任意两个来构造 的三个垂直平分线。对于三个不共线的点, 这两条线不能平行,外心为点 他们交叉的地方。平分线上的任何一点到 它平分的两个点,由此得出该点,在 两个平分线与所有三个三角形顶点的距离相等。这 外接半径是它到三个顶点中任何一个顶点的距离。

4 - 检查到剩余点的距离。如果这些点在“候选圆半径”+/-“一些缓冲余量”之内,那么它就是一个圆。

5 - 要确定方向,只需从中点计算第一点和第二点之间的角度。负角是正确的。留下一个正角。 (可以根据您使用的坐标系反转)

【讨论】:

    【解决方案3】:

    恕我直言,您绝对走在正确的轨道上。基本上,您需要将每个鼠标点与前一个鼠标点进行比较并计算它们之间的角度(如在第一个点位于原点的单位圆上所设想的那样)。为此,您可以使用以下公式:

    double angle = atan2(y2 - y1, x2 - x1) * 180 / PI;
    
    if (angle < 0)
        angle += 360;
    

    您最终得到的是,对于顺时针移动,角度将以正方向循环,而对于逆时针移动,角度将在负方向循环。您可以通过以下逻辑判断当前角度是大于还是小于前一个角度:

    if (angle2 > 270 && angle1 < 90)
    {
        angle1 += 360
    }
    else if (angle1 > 270 && angle2 < 90)
    {
        angle2 += 360
    }
    
    bool isPositive = (angle2-angle1 > 0);
    

    如果你得到一定数量的向量,它们的角度都在增加(isPositive 为真,比如说 10 次),你可以假设正在绘制一个顺时针圆;如果趋势为负(isPositive 为假 10 次),则为逆时针圆圈。 :)

    【讨论】:

    • 很好,但这实际上可以应用于边界点沿时钟/逆时针方向移动的任何形状。你可以用同样的逻辑画一个正方形、一个三角形等,也许仍然得到一个圆。这是由于手和鼠标的运动。用户不太可能画一条完美的线(例如正方形的一侧),因此曲线是可能导致误报的结果。如果只检测圆圈而没有其他遵循相同逻辑路径的东西,这当然不是什么大问题。
    【解决方案4】:

    没试过这个,但是看了你的问题就想到了这个想法,所以不妨和你分享一下:

    我假设必须在合理的时间内绘制圆,给定鼠标的稳定“采样率”,这将留下已知大小的二维向量(点)数组。将它们全部相加并除以 2D 向量的计数,以获得数组中“中心”点的估计值。然后从这个中心点到数组中的点形成向量并进行点积(通过向量长度进行归一化),确保点积的符号对于一系列点保持相同意味着这些点都以相同的方式移动方向,正号表示逆时针运动,负号则相反。如果累计角度超过2 PI,则画圆周运动..

    祝你好运。

    【讨论】:

    • 利用时间来限制运动是个好主意。我也在使用它。如果用户想要画一个圆圈,他的直觉会告诉他尽可能地这样做,因此边界循环或一些奇怪的非圆形运动的可能性会大大降低。问题是时间可能对一个用户来说还不错,但对另一个用户来说完全不合适,这使得这个解决方案远非完美。
    【解决方案5】:

    基于您的跟踪/轮询函数,它将浮点数对推送到堆栈上。这必须定期进行。

    1. 对列表中的两个相等条目进行基于阈值的搜索。现在您的堆栈中有两个索引;第一个和第二个相等的条目。将其视为一条线。
    2. 获取指数的绝对差值。然后除以二,得到这个点的坐标。 (线的中心。)
    3. 您有两个点:因此您可以通过将两个点之间的距离除以 2 来获得圆的半径。
    4. 将第 2 步的数量除以 2,现在你得到了四分之一。

      如果第 1 步的直线是垂直的并且直线的第一个点在顶部:如果第一个四分之一在中心点的左侧,则逆时针绘制圆。如果第一季度在中心点的右侧,则圆是顺时针绘制的。如果线的第一个点在底部,则反转(即 ccw => cw 和 cw => ccw)

      如果第 1 步的线是水平的并且列表的第一个点在左侧:如果第一个四分之一在中心点上方,则逆时针绘制圆。如果第一季度低于中心点,则圆是顺时针绘制的。如果线的第一个点在右边,则反转。

    5. 检查它是否是一个圆:遍历所有坐标对并计算到中心点的距离。根据计算的距离和到中心点的实际距离调整允许距离的阈值。

    在第 2 步和第 4 步中,如果时间间隔非常低(快速轮询),您可以通过取多个索引的平均值来进一步调整此算法。例如:数组中有 30 对,然后您在 0、1 和 28、29 处平均对以获得最高点。对所有其他点执行相同操作。

    我希望这很容易。

    【讨论】:

    • 如果手势检测是这里的主要主题,我实际上会使用某种触发计时器的事件。我目前正在使用 Qt 处理自定义手势,并使用按下鼠标按钮作为收集样本的起点。一旦发生另一个特定事件(例如释放鼠标按钮),此计时器将停止。在开始和结束时间点期间,可以检测鼠标是否正在移动并收集光标的位置。这样,您将获得更少的糟糕数据以及定义明确的点集,每个点都描述了从开始到结束的运动。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    相关资源
    最近更新 更多