点到直线距离 最小二乘法拟合直线

利用点到直线距离 最小二乘法拟合直线 非矩阵算法

-学习了几天最小二乘法拟合直线,总结一下主要有两种方式:

  1. 直线方程Y=kX+b, 利用Y轴方向距离 最小来拟合直线Σ(Yi-kXi-b)²--------[c++]两分钟弄懂"最小二乘法拟合直线
  2. 直线方程AX+BY+C=0,利用点到直线的距离最小来拟合直线Σ(AXi+BYi+C)²/(A²+B²)------最小二乘拟合直线真的没问题么

今天主要记录一下第二中方式利用点到直线的距离最小来拟合直线
,由于Σ(AXi+BYi+C)²/(A²+B²)这中式子求偏导后太复杂,网上大神们主要都是用矩阵运算来求解,知乎大神Dr.Sheng的文章,我用了他的方法,核心是SVD算法opencv里也有,达到了预期的效果,但是矩阵运算比较耗时间,我测了一下大概 (拟合800个点,执行100次需60ms)。我在想能否用普通计算的方式实现。知乎大神的的文章给了我灵感。结果和SVD算法完全一致。
下面说一下计算过程

  1. 拟合的直线必然过所有点的中心(Xavr,Yavr),即(X平均,Y平均)
  2. 将所有点的的坐标减去中心坐标(Xavr,Yavr),将直线移动到原点位置,直线过原点直线公式简化为y=kx,即kx-y=0
  3. 点到直线的距离公式:绝对值|AXi+BYi+C|/√(A²+B²);带入A=k,B=-1,C=0;简化为|AXi-Yi|/√(A²+1)
  4. 利用公式|AXi-Yi|/√(AA+1),最小二乘法求出A; ∑(AXi-Yi)²/(A²+1)最小,其对A的导数为0,导数化简后 ∑ XiYi*A²+(Xi²-Yi²)A-XiYi =0
  5. 令K=∑ XiYi; L=∑(Xi²-Yi²); M=∑ -XiYi(程序中用的A=∑ XiYi;B=∑(Xi²-Yi²);C=∑ -XiYi)
  6. 利用二次方程求根公式x=[-b±√(b²-4ac)]/2a,可以求出A=[-L±√(L²-4KM)]/2K,取加号A=[-L+√(L²-4KM)]/2K
  7. k=A
  8. n=Yavr - Xavr * k
  9. 直线公式即为y=kx+n

下面给出代码

 public static bool LineFitPCA(List<Point2d> Points, out double k, out double n)
 {
            k = 0.0;
            n = 0.0;
            double Xavr = 0.0;
            double Yavr = 0.0;
            int N = Points.Count;
            if (N < 2) return false;

foreach (var pt in Points)
            {
                Xavr += pt.X;
                Yavr += pt.Y;
            }
            Xavr = Xavr / N;
            Yavr = Yavr / N;
            double A = 0, B = 0, C = 0;
            for (int i = 0; i < N; i++)
            {
                double x = Points[i].X - Xavr;
                double y = Points[i].Y - Yavr;
                A += x * y;
                B += x * x - y * y;
                C += -x * y;
            }
            double k1 = (-B + Math.Sqrt(B * B - 4 * A * C)) / (2 * A);
            double k2 = (-B - Math.Sqrt(B * B - 4 * A * C)) / (2 * A);
           
            k = k1;
            n = Yavr - Xavr * k ;
            return true;

 }

你可能感兴趣的:(算法,经验分享)