我正在开发一个记录笔画的应用程序,你用指点设备绘制。
在上图中,我绘制了一个笔画,其中包含453个数据点。我的目标是在保持原始笔划形状的同时大幅减少数据点的数量。
对于有兴趣的人,上图所示笔画的坐标可以gist on GitHub获得。
事实上,Adobe Illustrator对我正在努力实现的目标有很好的实现。如果我在Illustrator中绘制类似的笔触(使用书法笔刷),则生成的形状将简化为我们在下面看到的内容。绘制笔划时,它看起来与我的应用程序非常相似。释放鼠标按钮后,曲线将简化为我们在此处看到的内容:
我们可以看到,笔画只有14个数据点。虽然还有其他控制点可以定义贝塞尔样条曲线的倾角(或者它们正在使用的任何样条曲线)。在这里,我们可以看到一些控制点:
我已经查看了像Ramer–Douglas–Peucker algorithm这样的算法,但这些算法似乎只从输入集中删除了点。如果我没有弄错的话,我正在寻找的方法也必须在集合中引入新的点来实现所需的曲线。
我遇到过类似iPhone smooth sketch drawing algorithm的问题。但那些似乎专注于从一小组输入点创建平滑曲线。我觉得我有相反的情况。
答案 0 :(得分:8)
我遇到了问题Smoothing a hand-drawn curve(这个问题可能实际上是一个骗局),其答案建议使用Ramer-Douglas-Peucker,然后根据Philip J. Schneiders approach应用曲线拟合。
快速调整所提供的示例代码到我的绘图方法会产生以下曲线:
问题的输入数据已减少到28个点(使用Bezier样条线绘制)。
我不确定Adobe正在使用哪种方法,但到目前为止,这个方法对我非常好。
因此,the code provided by Kris是为WPF编写的,并在这方面做出了一些假设。为了我的情况(因为我不想调整他的代码),我编写了以下片段:
private List<Point> OptimizeCurve( List<Point> curve ) {
const float tolerance = 1.5f;
const double error = 100.0;
// Remember the first point in the series.
Point startPoint = curve.First();
// Simplify the input curve.
List<Point> simplified = Douglas.DouglasPeuckerReduction( curve, tolerance ).ToList();
// Create a new curve from the simplified one.
List<System.Windows.Point> fitted = FitCurves.FitCurve( simplified.Select( p => new System.Windows.Point( p.X, p.Y ) ).ToArray(), error );
// Convert the points back to our desired type.
List<Point> fittedPoints = fitted.Select( p => new Point( (int)p.X, (int)p.Y ) ).ToList();
// Add back our first point.
fittedPoints.Insert( 0, startPoint );
return fittedPoints;
}
结果列表的格式为起点,控制点1 ,控制点2 ,终点
答案 1 :(得分:0)
我一直在广泛使用贝塞尔曲线简化,以尝试复制Illustrator的路径>简化。最有效且最类似于Illustrator的是Philip J. Schneider的Graphic的Gems简化,但还有一个附加步骤。该步骤排除了路径上的尖锐/成角度的点。
使用贝塞尔曲线路径:
在每个尖锐的贝塞尔曲线段处分割路径。因此,贝塞尔曲线手柄不平滑/共线的任何线段,或者相对于该线段的两条相邻曲线创建“尖锐点”的线段。您可以设置自己的阈值,以定义何时将细分视为“清晰”。 180度是平滑的,179.99或170度以下的任何值都是被认为是锐利段的阈值。
将这些路径中的每条路径都与原始路径分开后,将曲线拟合算法应用于每条路径,然后重新加入它们。
这将保留锋利的边缘,但会平滑多余的片段,使曲线沿着路径的其余部分拟合。
我的实现在paper.js中,但是可以使用fitcurve算法来利用相同的技术: