在计算机视觉的应用中。常常会用到提取一条直线的精确位置这种工作。这时就要用到直线的拟合算法了。
这里,我也贴一个利用最小二乘法计算最佳拟合直线的代码。
这个代码是我曾经学习《机器视觉算法与应用(双语版)》[德] 斯蒂格(Steger C) 著;杨少荣 等 译 的书时写的。
全部的公式推导都在书中 3.8.1 。还算比較实用。
与一元线性回归算法的差别:一元线性回归算法假定 X 是无误差的,仅仅有 Y 有误差。 而这个算法如果每一个点的 X Y 坐标的误差都是符合 0 均值的正态分布的。 因此,在计算机视觉的应用中比普通的一元线性回归拟合的结果要好。
#include <QVector>
#include <QPoint>
#include <math.h>
/// 本代码用到了 Qt5 中的 QVector 和 QPoint。可是能够非常easy的改为其它数组类型。
/**
* 最小二乘法直线拟合(不是常见的一元线性回归算法)
* 将离散点拟合为 a x + b y + c = 0 型直线
* 如果每一个点的 X Y 坐标的误差都是符合 0 均值的正态分布的。
* 与一元线性回归算法的差别:一元线性回归算法假定 X 是无误差的,仅仅有 Y 有误差。
*/
bool lineFit(const QVector<QPoint> &points, double &a, double &b, double &c)
{
int size = points.size();
if(size < 2)
{
a = 0;
b = 0;
c = 0;
return false;
}
double x_mean = 0;
double y_mean = 0;
for(int i = 0; i < size; i++)
{
x_mean += points[i].x();
y_mean += points[i].y();
}
x_mean /= size;
y_mean /= size; //至此,计算出了 x y 的均值
double Dxx = 0, Dxy = 0, Dyy = 0;
for(int i = 0; i < size; i++)
{
Dxx += (points[i].x() - x_mean) * (points[i].x() - x_mean);
Dxy += (points[i].x() - x_mean) * (points[i].y() - y_mean);
Dyy += (points[i].y() - y_mean) * (points[i].y() - y_mean);
}
double lambda = ( (Dxx + Dyy) - sqrt( (Dxx - Dyy) * (Dxx - Dyy) + 4 * Dxy * Dxy) ) / 2.0;
double den = sqrt( Dxy * Dxy + (lambda - Dxx) * (lambda - Dxx) );
a = Dxy / den;
b = (lambda - Dxx) / den;
c = - a * x_mean - b * y_mean;
return true;
}