最小二乘法拟合直线 C++/OpenCV

问题:

  我们在拥有一系列散列的点(x1,y1),(x2,y2)... (xm,ym),这些点在一条直线附近,通过点拟合直线。

  我在工程中是要拟合一系列线段,其实一条线段就对应着两个要拟合的点,算法上稍有区别,原理完全一致。

思路一:

  用OpenCV自带的最小二乘法拟合

  函数:cvFitLine()

    void cvFitLine( const CvArr* points,    int dist_type,    double param,  double reps,   double aeps,   float* line );

  

  points 

  2D 或 3D 点集,32-比特整数或浮点数坐标 ,存放输入点。

    Char* cvSeqPush(CvSeq* seq,void* element=NULL)

    功能:存放元素到序列尾部。            

       

     dist_type 
        拟合的距离类型.

    dist_type=CV_DIST_L2 (L2): ρ(r)=r2/2 (最小二乘法,我们要用的)

    dist_type=CV_DIST_L1 (L1): ρ(r)=r 
    dist_type=CV_DIST_L12 (L1-L2): ρ(r)=2?[sqrt(1+r2/2) - 1] 
    dist_type=CV_DIST_FAIR (Fair): ρ(r)=C2?[r/C - log(1 + r/C)], C=1.3998 
    dist_type=CV_DIST_WELSCH (Welsch): ρ(r)=C2/2?[1 - exp(-(r/C)2)], C=2.9846 
    dist_type=CV_DIST_HUBER (Huber): ρ(r)= r2/2, if r < C; C?(r-C/2), otherwise; C=1.345  

  param 
  对某些距离的数字参数,如果是 0, 则选择某些最优值 

  reps, aeps 
  半径 (坐标原点到直线的距离) 和角度的精度,一般设为0.01。 

  line 
  输出的直线参数。2D 拟合情况下,它是包含 4 个浮点数的数组 (vx, vy, x0, y0),其中 (vx, vy) 是线的单位向量而 (x0, y0) 是线上的某个点. 

对 3D 拟合,它是包含 6 个浮点数的数组 (vx, vy, vz, x0, y0, z0), 其中 (vx, vy, vz) 是线的单位向量,而 (x0, y0, z0) 是线上某点。 

float *line = new float[4];
        CvMemStorage* storage = cvCreateMemStorage(0);

        //往点序列中存放需要参与拟合直线的点
        CvSeq* point_seq = cvCreateSeq( CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage );
        for(int j=0;j<temp.size();j++)
        {
            cvSeqPush(point_seq, &cvPoint2D32f(temp[j].stPot().x,temp[j].stPot().y));
            cvSeqPush(point_seq, &cvPoint2D32f(temp[j].enPot().x,temp[j].enPot().y));
        }

        cvFitLine(point_seq,CV_DIST_L2,0,0.01,0.01,line);   //CV_DIST_L2表示最小二乘法

        //line[0],line[1]为x,y的单位方向向量
        //line[2],line[3]为直线经过某点的X,Y值
        //正好组成参数方程
        //X=line[0]t+line[2]
        //Y=line[1]t+line[3]

        //根据line[4]计算自己需要的直线或线段
        Point A(0,line[3]-(line[1]*line[2]/line[0]));
        Point B(src->width,(src->width-line[2])*line[1]/line[0]+line[3]);
        cvLine(src,A,B,CV_RGB(255,0,0),3,8,0);

        cvClearSeq(point_seq);
        cvReleaseMemStorage(&storage);

思路二:

  自己写算法,但是需要注意,openCV中原点在屏幕的左上角,与我们数学中的直角坐标系不太一样。   

//最小二乘法拟合直线
void line_fit(vector<MyLine>& h,IplImage* src)
{
    int n = 2*h.size();
    int k;                       //目标直线斜率
    int b;                       //目标直线截距
    vector<MyLine>::iterator it = h.begin();
    int sumx=0,sumy=0,sumxy=0,sumxsq=0;
    while(it != h.end())
    {
        sumx += it->stPot().x;
        sumx += it->enPot().x;
        sumy += it->stPot().y;
        sumy += it->enPot().y;
        sumxy += it->stPot().x*it->stPot().y;
        sumxy += it->enPot().x*it->enPot().y;
        sumxsq += it->stPot().x*it->stPot().x;
        sumxsq += it->enPot().x*it->enPot().x;
        it++;
    }

    if(sumxsq == (sumx*sumx/n))
        k = atan2(0,1.0);
    else
        k = (sumxy-((sumx*sumy)/n))/(sumxsq-(sumx*sumx/n));
    b = (sumy-k*sumx)/n;
    cvLine(src,Point(0,b),Point(src->width,k*src->width+b),CV_RGB(255,0,0),1,8,0);
}

OpenCV中提供了函数fitLine()来实现最小二乘法拟合直线。 使用方法如下: 1. 导入必要的库和模块: ```python import cv2 import numpy as np ``` 2. 准备数据: 我们需要提供一些点的坐标,用于拟合直线。这些点可以是图像中的像素点,也可以是其他任意坐标系中的点。 ```python # 生成一些随机点 points = np.random.randint(0, 200, (10, 2)) ``` 3. 拟合直线: 使用fitLine()函数来拟合直线,该函数需要传入一些参数: - points:需要拟合的点的坐标。 - distType:点到直线的距离类型,可以选择CV_DIST_L1, CV_DIST_L2或CV_DIST_L12。 - param:累加器分辨率与rho的比值,一般取值为1。 - reps:在一条直线上的最小点数,一般取值为0。 - aeps:角度的最小精度,一般取值为0.01度。 ```python # 使用fitLine()函数拟合直线 vx, vy, x, y = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) ``` 4. 绘制直线: 得到直线的参数后,我们可以使用这些参数来绘制直线。可以使用cv2.line()函数来实现。 ```python # 绘制直线 lefty = int((-x*vy/vx) + y) righty = int(((200-x)*vy/vx)+y) cv2.line(img, (199,righty), (0,lefty), (0,255,0), 2) ``` 完整代码如下: ```python import cv2 import numpy as np # 生成一些随机点 points = np.random.randint(0, 200, (10, 2)) # 使用fitLine()函数拟合直线 vx, vy, x, y = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) # 绘制直线 img = np.zeros((200, 200, 3), dtype=np.uint8) lefty = int((-x*vy/vx) + y) righty = int(((200-x)*vy/vx)+y) cv2.line(img, (199,righty), (0,lefty), (0,255,0), 2) cv2.imshow("Fit Line", img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 运行结果如下图所示: ![拟合直线结果](https://img-blog.csdnimg.cn/20210622152054570.png)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值