如何平滑手绘线条?

How to smooth hand drawn lines?

所以我在 Unity 中使用 Kinect。

使用 Kinect,我们检测到一个手势,当它处于活动状态时,我们在屏幕上画一条线,跟随手的移动方向。每次更新位置都存储为一行中的最新(和最后)点。然而,这些线条通常看起来很不稳定。

这是一张显示我想要实现的目标的一般图片:

红色为原线,紫色为新的平滑线。如果用户突然停下来并转向,我们认为我们不希望它完全那样做,而是快速转向或循环。

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

1) 它通常不会将曲线保留到用户绘制的向外的距离,例如,如果用户突然停止一条线并反转方向,则该线很可能不会延伸到用户反转方向的点。

2)也有可能选中的"good"点实际上是一个"bad"随机跳跃点

所以我考虑了其他解决方案。一个包括限制点之间的最大角度(0 度是一条直线)。但是,如果该点的角度超出了限制,则在尽可能遵循绘制的线的同时降低角度的数学看起来很复杂。但也许不是。无论哪种方式,我都不确定该怎么做并寻求帮助。

请记住,这需要在用户画线时实时完成。

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

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

这是一个简单的算法,参数化相当直观。您可以将其用作预处理步骤,也可以在一种或多种其他算法之后使用。在任何情况下,它都是您工具箱中的一个很好的算法。

如您所见,使用角度拒绝 "jump" 点可能很棘手。一种选择是将 N 条线段的总长度与该 N 条线段链的最末端点之间的直线距离进行比较。您可以阈值比率 (totalLength/straightLineLength) 来识别要拒绝的线段。这将是一个快速的计算,而且很容易理解。

如果要考虑线段长度和线段间的角度,可以将线段视为向量并计算叉积。如果您将这两个向量想象成一个平行四边形,并且如果知道平行四边形的面积是 accept/reject 一个点的一种方法,那么叉积是另一种简单快速的计算方式。

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

如果你只有几十个点,你可以一次随机消除一个点,生成你的样条曲线拟合,然后计算所有原始点的点到样条曲线的距离。给定所有这些点到样条线的距离,您可以生成一个您想要最小化的度量(例如平均距离):最佳拟合将通过消除点(Pn,Pn + k,...)产生样条线拟合质量 S。此技术无法很好地扩展更多点,但如果您将每条线段链分成一组,每组可能有六个线段,则可能值得一试。

虽然对于这个问题来说有点矫枉过正,但我​​会提到欧拉曲线可以很好地拟合 "natural" 曲线。 Euler 曲线的优点在于,您可以通过 space 中的两个点和 space 中这两点的切线生成欧拉曲线拟合。代码变得多毛,但欧拉曲线(a.k.a。审美曲线,如果我没记错的话)可以生成更好的 and/or 比贝塞尔 n 次样条更有用的自然曲线拟合。

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