平滑手绘曲线
我有一个允许用户绘制曲线的程序。但这些曲线看起来不太好——它们看起来摇摇欲坠,而且是手绘的。
所以我想要一种能够自动平滑它们的算法。我知道平滑过程中存在固有的模糊性,因此它不会每次都完美,但这种算法似乎确实存在于多个绘图包中,并且它们工作得很好。
有类似这样的代码示例吗? C# 是完美的,但我可以翻译其他语言。
I've got a program that allows users to draw curves. But these curves don't look nice - they look wobbly and hand-drawn.
So I want an algorithm that will automatically smooth them. I know there are inherent ambiguities in the smoothing process, so it won't be perfect every time, but such algorithms do seem to exist in several drawing packages and they work quite well.
Are there any code samples for something like this? C# would be perfect, but I can translate from other languages.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可以使用 Ramer–Douglas– 减少点数Peucker 算法 此处有一个 C# 实现。我使用 WPF PolyQuadraticBezierSegment 进行了尝试,根据容差,它显示出少量的改进。
经过一番搜索源(1, 2)似乎表明使用Graphic Gems 效果很好,C 代码可用。 几何工具也有一些值得研究的资源。
这是我制作的一个粗略的示例,仍然存在一些小问题,但在很多情况下效果很好时间。这是 FitCurves.c 的快速而肮脏的 C# 端口。问题之一是,如果不减少原始点,则计算出的误差为 0,并且提前终止,样本会提前使用点减少算法。
You can reduce the number of points using the Ramer–Douglas–Peucker algorithm there is a C# implementation here. I gave this a try using WPFs PolyQuadraticBezierSegment and it showed a small amount of improvement depending on the tolerance.
After a bit of searching sources (1,2) seem to indicate that using the curve fitting algorithm from Graphic Gems by Philip J Schneider works well, the C code is available. Geometric Tools also has some resources that could be worth investigating.
This is a rough sample I made, there are still some glitches but it works well a lot of the time. Here is the quick and dirty C# port of FitCurves.c. One of the issues is that if you don't reduce the original points the calculated error is 0 and it terminates early the sample uses the point reduction algorithm beforehand.
Kris 的答案是原始代码到 C# 的一个非常好的移植,但性能并不理想,并且在某些地方浮点不稳定可能会导致一些问题并返回 NaN 值(原始代码中也是如此)。我创建了一个库,其中包含我自己的端口以及 Ramer-Douglas-Peuker,并且不仅可以使用 WPF 点,还可以使用新的支持 SIMD 的矢量类型和 Unity 3D:
https://github.com/burningmime/curves
Kris's answer is a very good port of the original to C#, but the performance isn't ideal and there are some places where floating point instability can cause some issues and return NaN values (this is true in the original code too). I created a library that contains my own port of it as well as Ramer-Douglas-Peuker, and should work not only with WPF points, but the new SIMD-enabled vector types and Unity 3D also:
https://github.com/burningmime/curves
也许这篇基于 WPF+Bezier 的文章是一个好的开始:通过一组带有贝塞尔基元的 2D 点
Maybe this WPF+Bezier-based article is a good start: Draw a Smooth Curve through a Set of 2D Points with Bezier Primitives
嗯,克里斯的工作非常有用。
我意识到他指出的关于算法因计算错误以 0 结束的错误而提前终止的问题是由于重复了一个点并且计算出的正切是无限的这一事实。
我已经根据 Kris 的代码完成了 Java 的翻译,我相信它工作得很好:
编辑:
我仍在努力并试图在算法上获得更好的行为。我意识到,在非常尖的角度上,贝塞尔曲线的表现并不好。所以我尝试将贝塞尔曲线与线条结合起来,这就是结果:
这是后者工作的图像,白色是线条,红色是贝塞尔曲线:
使用这种方法,我们使用的控制点更少且更准确。
线条创建的灵敏度可以通过 lineSensitivity 属性进行调整。如果您根本不想使用线条,只需将其设置为无限即可。
我相信这可以改进。请随意贡献:)
该算法没有做任何减少,并且由于我的帖子中解释的第一个,我们必须运行一个。 附加 FitCurves 更有效(要存储的点更少,渲染速度更快)
这是一个 DouglasPeuckerReduction 实现,对我来说,在某些情况下,它比我在这里使用我自己的 SortedList 和 Line 实现的 。你必须自己做,抱歉。
Well, the job of Kris was very useful.
I realized that the problem he pointed out about the algorithm terminating earlier because of a miscalculated error ending on 0, is due to the fact that one point is repeated and the computed tangent is infinite.
I have done a translation to Java, based on the code of Kris, it works fine I believe:
EDIT:
I still working and trying to get a better behavior on the algorithm. I realized that on very spiky angles, the Bezier curves simply don't behave good. So I tried to combine Bezier curves with Lines and this is the result:
Here an image of the latter working, white are lines, and red are Bezier:
Using this approach we use less control points and more accurate.
The sensitivity for the lines creation can be adjusted through the lineSensitivity attribute. If you don't want lines to be used at all just set it to infinite.
I'm sure this can be improved. Feel free to contribute :)
The algorithm is not doing any reduction, and because of the first explained in my post we have to run one. Here is a DouglasPeuckerReduction implementation, which for me works in some cases even more efficiently (less points to store and faster to render) than an additional FitCurves
I am using here my own implementation of a SortedList, and of a Line. You will have to make it yourself, sorry.
我还没有测试过它,但想到的一种方法是在某个时间间隔内采样值并创建一个样条线来连接这些点。
例如,假设曲线的 x 值从 0 开始,到 10 结束。因此,您在 x=1,2,3,4,5,6,7,8,9,10 处采样 y 值并创建样条线从点 (0, y(0)), (1,y(1)), ... (10, y(10))
可能会出现诸如用户意外绘制尖峰之类的问题,但它可能是值得一试
I haven't tested it, but one approach that comes to mind would be sampling values in some interval and creating a spline to connect the dots.
For example, say the x value of your curve starts at 0 and ends at 10. So you sample the y values at x=1,2,3,4,5,6,7,8,9,10 and create a spline from the points (0, y(0)), (1,y(1)), ... (10, y(10))
It would probably have problems such as accidental spikes drawn by the user, but it may be worth a shot
对于 Kris 答案的 Silverlight 用户来说,Point 是有问题的,而 Vector 并不存在。这是支持以下代码的最小 Vector 类:
还必须解决长度和 +、- 运算符的使用问题。我选择只向 FitCurves 类添加函数,并在编译器抱怨的地方重写它们的用法。
For Silverlight users of Kris' answer, Point is hobbled and Vector doesn't exist. This is a minimal Vector class that supports the code:
Also had to address use of Length and +,- operators. I chose to just add functions to the FitCurves class, and rewrite their usages where the compiler complained.