如何获得最接近给定点的三次贝塞尔曲线?

时间:2011-10-11 16:03:27

标签: c# algorithm math bezier curve-fitting

给出n分:

p0,p1,p2,...,pn;

如何得到点c1,c2,以便由

定义的三次贝塞尔曲线

p0,c1,c2,pn

最接近给定点?

我尝试过最小二乘法。我在http://www.mathworks.com/matlabcentral/fileexchange/15542-cubic-bezier-least-square-fitting阅读pdf文档后写了这篇文章。但我找不到一个好的t(i)功能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace BezierFitting
{
    class CubicBezierFittingCalculator
    {
        private List<Point> data;

        public CubicBezierFittingCalculator(List<Point> data)
        {
            this.data = data;
        }

        private double t(int i)
        {
            return (double)(i - 1) / (data.Count - 1);
            // double s = 0.0, d = 0.0;
            // 
            // for (int j = 1; j < data.Count; j++)
            // {
            //     if (j < i)
            //     {
            //         s += (data[j] - data[j - 1]).Length;
            //     }
            //     d += (data[j] - data[j - 1]).Length;
            // }
            // return s / d;
        }

        public void Calc(ref Point p1, ref Point p2)
        {
            double n = data.Count;
            Vector p0 = (Vector)data.First();
            Vector p3 = (Vector)data.Last();

            double a1 = 0.0, a2 = 0.0, a12 = 0.0;
            Vector c1 = new Vector(0.0, 0.0), c2 = new Vector(0.0, 0.0);
            for (int i = 1; i <= n; i++)
            {
                double ti = t(i), qi = 1 - ti;
                double ti2 = ti * ti, qi2 = qi * qi;
                double ti3 = ti * ti2, qi3 = qi * qi2;
                double ti4 = ti * ti3, qi4 = qi * qi3;
                a1 += ti2 * qi4;
                a2 += ti4 * qi2;
                a12 += ti3 * qi3;

                Vector pi = (Vector)data[i - 1];
                Vector m = pi - qi3 * p0 - ti3 * p3;
                c1 += ti * qi2 * m;
                c2 += ti2 * qi * m;
            }
            a1 *= 9.0;
            a2 *= 9.0;
            a12 *= 9.0;
            c1 *= 3.0;
            c2 *= 3.0;

            double d = a1 * a2 - a12 * a12;
            p1 = (Point)((a2 * c1 - a12 * c2) / d);
            p2 = (Point)((a1 * c2 - a12 * c1) / d);
        }
    }
}

获得最接近给定点的三次贝塞尔曲线的最佳方法是什么?

例如,这里有30分:

22, 245
26, 240
39, 242
51, 231
127, 189
136, 185
140, 174
147, 171
163, 162
169, 155
179, 107
181, 147
189, 168
193, 187
196, 75
199, 76
200, 185
201, 68
204, 73
205, 68
208, 123
213, 118
216, 210
216, 211
218, 68
226, 65
227, 110
228, 102
229, 87
252, 247

这些点分布在由四个点控制的三次贝塞尔曲线周围:

P0(0,256),P1(512,0),P2(0,0),P3(256,256)。

假设曲线是从(0,256)到(256,256),如何让两个控制点靠近原点?

5 个答案:

答案 0 :(得分:11)

您可能需要查看此页面:http://www.antigrain.com/research/bezier_interpolation/index.html

这是一个非常好的实现,尽管作者写道:“这种方法纯粹是启发式和经验性的。从严格的数学建模的角度来看,它可能会给出一个错误的结果。但在实践中,结果是足够好的,它需要绝对最少的计算。“

它是用C ++编写的,但很容易移植到任何语言...... 通过控制点计算功能传递每个“边缘”,然后通过贝塞尔计算一个,你就拥有它。 要在多边形上执行bezier平滑,请使用最后一个和第一个点传递最后一个边。

Bezier smooth

答案 1 :(得分:4)

(三次)贝塞尔曲线是一种定义一般形式P = A * t ^ 3 + B * t ^ 2 + C * t + D的三次参数样条的方法,其中P,A,B,C和D是(二维的,即矢量)权重。使用Bernstein多项式,您可以计算给定四个控制点P0,P1,P2和P3的权重A,B,C和D,如实际上所有矢量绘图程序所知。

由于你只有四个控制点,但想要适应任意数量的任意点,我怀疑没有独特的解决方案。例如,给定输入(0,0),(1,0),(1,1)和(0,1),对于每个“最优”解决方案(无论它是什么),您可以通过镜像来构造等效解决方案。沿主对角线的样条曲线。

这种问题的一般方法是构造一个方程,用于所有点到一般贝塞尔曲线的距离的平方和(即由变量定义的曲线 A ,B,C和D),计算第一个偏差并将其设置为零并求解A,B,C和D的最终系统。这通常会导致线性方程组(对不起,我需要一些时间做数学,自从我这样做了很长时间......)。但是,正如我所说,我认为对于您的问题,这不会产生独特的解决方案。

如果您在输入点上定义一个顺序,即您希望使用单个贝塞尔曲线拟合多边形线,我认为对于一个独特的解决方案有太多的自由度(但是再说一次,我没有时间或技能证明......)

答案 2 :(得分:2)

如果要使用尖点创建曲线,则问题非常严重。我可以想一个启发式来创建一组初始控制点。对于第一个控制点,尝试从距离到第一个锚点的距离进行排序时,获取可用点的前1/3。排序是必要的,否则,你可能会跳过。取1/3的点并进行线性最小二乘拟合,这具有线性时间复杂度。这为您提供了曲线需要起飞的方向。用最后的1/3做同样的事情,你就有了“着陆”的方向。

使用这些线性解决方案创建指向远离锚点的向量,然后尝试使这些向量越来越短,尝试最小化错误。控制点将沿着锚点的那些向量。

以下是其他一些想法(我只能发布两个链接!): physics forum question bezier curve fitting thesis

答案 3 :(得分:1)

根据您的问题判断,我假设您只希望优化曲线拟合在三次贝塞尔曲线的2'内部'控制点上。这不是一个容易解决的问题,因为参数化地描述了贝塞尔曲线。最明显的解决方案是使用最小二乘正交距离回归,但这很困难,因为您需要在Bezier曲线上为您希望拟合的每个点生成足点参数。如果这个问题需要一个特定的分析解决方案并且你有一些数学教育,我会建议阅读Peigl和Tiller的“The NURBS Book”,并熟悉近似理论和优化技术。如果没有,我会采用更具启发性的方法,因为这类问题不太可能通过简单的答案来解决。

答案 4 :(得分:1)

Khan的函数使用两个版本的t [i],其中t [i]表示近似曲线上最接近点到输入点p [i]的猜测。第一个简单地使用均匀的t [i] = i /(N-1),第二个使用和弦长度。虽然我找不到Khan的功能,但我认为这只是计算线性距离p [i]到p [i + 1],将t [0]设置为0,t [1]到距离p [0]到p [1],t [2]到t [1] +距离p [1]到p [2],依此类推。除以最后一个值,将所有内容放在0-1范围内。