如何使手绘线条更加平滑?

4

我正在使用Kinect和Unity。

通过Kinect,我们可以检测手势,并在手势激活时在屏幕上绘制一条线,该线会跟随手的移动轨迹。每次更新时,最新(也是最后)的位置将作为线的一个点存储。然而,这些线经常看起来很不平滑。

下面是一个通用的图片,展示了我想要实现的效果:

Smoother versus Rough Lines

红线表示原始线,紫线表示新平滑线。如果用户突然停下并转向,我们希望它不要完全这样做,而是进行快速转向或循环。

我当前的解决方案是使用三次贝塞尔曲线,并且仅使用彼此相距X距离的点(Y个点之间使用三次贝塞尔曲线)。然而,其中有两个问题:

1)它经常无法保持到用户绘制的向外距离的曲线,例如,如果用户突然停止一条直线并倒转方向,则该线不会延伸到用户反转方向的点。

2)选择的“好”点也有可能是“坏”的随机跳跃点。

因此,我考虑了其他解决方案。其中包括限制点之间的最大角度(0度为直线)。但是,如果点的角度超出限制,则在尽可能按照绘制的线路行进的情况下降低角度背后的数学似乎很复杂。但也许不是这样。无论如何,我不确定该怎么做,正在寻求帮助。

请记住,这需要实时完成,因为用户正在绘制线条。

我认为你实际上想要的是某种最小二乘拟合。建立一个三次样条曲线,然后拟合其控制点,使得整体曲线的平方距离最小化。 - cfh
1个回答

4
您可以尝试使用Ramer-Douglas-Peucker算法来简化您的曲线:

https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm

这是一个简单的算法,参数化相当直观。您可以将其用作预处理步骤或在一个或多个其他算法之后使用。无论如何,这都是一个很好的算法工具。
使用角度来拒绝“跳跃”点可能有些棘手,正如您所见。一种选择是将N条线段的总长度与该链的两个端点之间的直线距离进行比较。您可以阈值化(totalLength/straightLineLength)的比率以识别要拒绝的线段。这将是一个快速计算,并且易于理解。
如果您想考虑线段长度和线段之间的角度,则可以将线段视为向量并计算叉积。如果您将两个向量想象成定义平行四边形的向量,并且如果了解平行四边形的面积是接受/拒绝点的方法,则叉积是另一种简单而快速的计算方法。

https://www.math.ucdavis.edu/~daddel/linear_algebra_appl/Applications/Determinant/Determinant/node4.html

如果你只有几十个点,你可以每次随机删除一个点,生成样条拟合,然后计算所有原始点的点到样条距离。根据所有这些点到样条距离,您可以生成一个指标(例如平均距离),您希望将其最小化:最佳拟合将通过消除点(Pn、Pn + k、...)从而产生样条拟合质量S。如果您将每个线段链分成大约六个线段一组,这种技术在处理更多点时无法很好地扩展,但如果您将每个线段链分成大约半打线段一组,它可能值得一试。
虽然对于这个问题来说有点过头了,但我想提一下欧拉曲线可以很好地拟合“自然”曲线。欧拉曲线的好处是,您可以通过空间中的两个点和这两个点处的切线生成欧拉曲线拟合。代码会变得复杂,但欧拉曲线(也称为美学曲线,如果我没记错的话)可以比Bezier n次样条更好或更有用地拟合自然曲线。

https://en.wikipedia.org/wiki/Euler_spiral


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接