【问题标题】:Algorithm for path simplification and smoothing of 2D trajectories用于路径简化和平滑 2D 轨迹的算法
【发布时间】:2015-02-28 03:19:54
【问题描述】:

我正在寻找一种用于 2D 轨迹的路径简化和平滑的算法。所以我有一个二维点的有序列表。这些点应该被简化,例如使用 Ramer-Douglas-Peucker 算法。但是输出必须是平滑的,因此结果路径应该由贝塞尔曲线或样条曲线构成。 Ramer-Douglas-Peucker 算法是否有任何修改可以处理这个问题?

我在 paper.js 库中找到了一个路径简化算法,它完全符合我的要求:http://paperjs.org/examples/path-simplification/ 但我无法从未记录的 javascript 源代码中理解该算法。

【问题讨论】:

    标签: algorithm computational-geometry bezier


    【解决方案1】:

    您正在尝试做的事情称为 Curve Fitting.

    虽然 Ramer-Douglas-Peucker 算法通过去除不必要的点从本质上消除了折线中的“噪声”,但曲线拟合算法将通过这些点拟合贝塞尔曲线。

    Here 是 Youtube 上的一个很好的例子,here 是描述算法本身的原始论文。


    至于 Paper.js 示例:

    • This 是该特定功能的 Github 链接 你提到了,这是很好的评论。该研究论文认为 使用的是 this

    • 另外here 是关于邮件列表的一个非常简短的讨论 使用了什么,没有使用什么(显然使用了 Ramer-Douglas-Peucker 但后来删除了

    【讨论】:

    • 第一段中提供的原始论文链接如下:Efficient curve fitting
    • @IvanZ 据我所知,Frisken 的算法已获得三菱实验室的专利。
    【解决方案2】:

    你想做的工作属于“曲线拟合”的范畴。曲线拟合有很多不同的算法,但几乎所有的曲线拟合算法都可以分为两类:插值法和近似法。插值算法产生一条准确通过所有数据点的曲线,而近似算法产生一条靠近数据点的曲线。当然,也存在混合算法。

    由于您希望对数据点进行平滑处理,因此您应该寻找近似算法。对于您提到的两种算法:RDP 算法和 Schneider 算法(Paper.js 中的一种),它们都是近似算法。所以,基本上你可以使用它们中的任何一个。对于 RDP,在获得简化路径后,您可以使用通过简化路径的顶点创建 Catmull Rom 样条或 Overhauser 样条来获得平滑曲线。但是,您无法直接控制生成的样条线与原始路径中的顶点之间的偏差。

    对于施耐德算法,它将首先通过具有末端切线约束的三次贝塞尔曲线拟合数据点。如果与结果曲线的偏差太大,则它将数据点分成两个“区域”,并使用带有端切线约束的三次贝塞尔曲线拟合每个数据区域。将重复此过程,直到所有三次贝塞尔曲线的偏差都足够小。结果,它产生了一系列三次贝塞尔曲线,最多与 C1 连续性相连(很可能它实际上只是 G1)。此外,由于该算法从原始数据点评估端切线,因此数据点中的噪声将影响端切线评估,从而影响三次贝塞尔拟合。

    如果您可以花时间研究曲线拟合,您应该研究使用 B 样条曲线的最小二乘拟合。这将生成具有高连续性的输出曲线(例如,C2 用于三次 B 样条曲线)。如果您没有太多时间可以花费,那么 Schneider 的算法是一个不错的选择,它可以在实现成本(如果您必须用特定语言重新实现它)和结果曲线的质量之间取得平衡。

    【讨论】:

      【解决方案3】:

      就我而言,我发现 Catmull-Rom Splines 最容易应用。 Smooth Paths Using Catmull-Rom Splines 来自 Mika 的 Coding Bits 的文章非常有帮助。我用它在我的 Unity3D 项目中用 C# 实现了样条插值脚本。这是脚本:

      public static Vector2 CatmullRomInterpolation(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t, float alpha = .5f, float tension = 0)
      {
          float t01 = Mathf.Pow(Vector2.Distance(p0, p1), alpha);
          float t12 = Mathf.Pow(Vector2.Distance(p1, p2), alpha);
          float t23 = Mathf.Pow(Vector2.Distance(p2, p3), alpha);
          Vector2 m1 = (1.0f - tension) * (p2 - p1 + t12 * ((p1 - p0) / t01 - (p2 - p0) / (t01 + t12)));
          Vector2 m2 = (1.0f - tension) * (p2 - p1 + t12 * ((p3 - p2) / t23 - (p3 - p1) / (t12 + t23)));
          return (2.0f * (p1 - p2) + m1 + m2) * Mathf.Pow(t, 3) + (-3.0f * (p1 - p2) - m1 - m1 - m2) * Mathf.Pow(t, 2) + m1 * t + p1;
      }
      

      p0 和 p3 是控制点,应该不同于 p1 和 p2,p1 和 p2 是路径中的起点和终点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-27
        • 2013-03-05
        • 1970-01-01
        • 1970-01-01
        • 2019-07-14
        • 2013-01-15
        • 1970-01-01
        相关资源
        最近更新 更多