我正在构建一个Kinect SDK WPF应用程序,并使用Kinect移动"光标"/手对象。
我遇到的问题是,在每秒30帧的速度下,由于Kinect的精度,光标实际上有点不规律地跳跃(例如,当你的手静止时,物体在5px的空间内移动)。
我打算写一个算法,不简单地移动我的"光标"短跑的X/Y到屏幕上的正确位置,但表现得更像一个"移动手向这个X/Y坐标",所以它是一个更平滑的运动。
谁能给我指一篇别人写过的好文章,这样我就可以避免重复工作了。
我知道这可能很常见,但由于我更多的是一个业务开发人员,我不确定这种功能的名称,所以如果这是一个n00b问题,请提前道歉。
当我使用Kinect时,我只是使用了一些简单的数学方法(我认为这叫做线性回归)去移动光标当前位置和目标位置之间的一个点。获取光标的位置,获取用户的手所在的位置(转换为屏幕坐标),然后将光标移动到两者之间的某个点。
float currentX = ..., currentY = ..., targetX = ..., targetY = ...;
float diffX = targetX - currentX;
float diffY = targetY - currentY;
float delta = 0.5f; // 0 = no movement, 1 = move directly to target point.
currentX = currentX + delta * diffX;
currentY = currentY + delta * diffY;
你仍然会有抖动,这取决于delta,但它会更平滑,通常在更小的区域。
在一个相关的注意事项,你看了Kinect的骨架平滑参数?你可以让SDK处理一些过滤考虑您的输入值(那些跳跃位置)作为具有低频和高频部分的信号。低频部分表示粗略的位置/运动,而高频部分包含较小距离内的快速跳跃。
所以你需要或寻找的是一个低通滤波器。如果你设法用正确的参数设置它,它会过滤掉高频部分,并留下粗糙的位置(但与Kinect所能得到的一样准确)。该参数为滤波器的交叉频率。你得多玩一会儿,你就会明白了。
时间离散值的实现示例如下(最初来自维基百科):
static final float ALPHA = 0.15f;
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
for ( int i=0; i<input.length; i++ ) {
output[i] = output[i] + ALPHA * (input[i] - output[i]);
}
return output;
}
您可以将位置向量的X和Y分量的最后值放入该函数中以平滑它们(input[0]
用于X, input[1]
用于Y, output[0]
和output[1]
是前一个函数调用的结果)。
就像我已经说过的,你必须找到一个很好的平衡平滑因子ALPHA
(0≤ALPHA≤1):
- 太大,信号不会得到足够的平滑,效果不够
- 太小,信号会平滑'太多',光标会滞后于用户的移动,惯性太大
(如果你看一下公式newout = out + alpha * (in - out)
,你会发现当alpha值为0时,你只需要再次使用旧的out
值,因此该值永远不会改变;当值为1时,你有newout = out + in - out
,这意味着你不会平滑任何东西,但总是取最新的值)
解决这个问题的一个非常简单的想法是将光标显示在过去一些位置的平均值处。例如,假设您跟踪手的最后五个位置,然后在该位置显示光标。然后,如果用户的手相对静止,那么从一帧到另一帧的抖动应该相当低,因为最后五帧的手将大致处于相同的位置,噪声应该被抵消。如果用户在屏幕上移动光标,当光标从旧位置移动到新位置时,光标将产生动画,因为当你考虑手的最后五个位置时,平均位置将慢慢地在新旧位置之间插入。
这种方法很容易调整。您可以转换数据点,使旧点的权重高于或低于新点,并可以调整您保留的历史记录的长度。
希望这对你有帮助!