最小二乘法擬合直線 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) 是線上某點。 

 1         float *line = new float[4];
 2         CvMemStorage* storage = cvCreateMemStorage(0);
 3         
 4         //往點序列中存放需要參與擬合直線的點
 5         CvSeq* point_seq = cvCreateSeq( CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage );
 6         for(int j=0;j<temp.size();j++)
 7         {
 8             cvSeqPush(point_seq, &cvPoint2D32f(temp[j].stPot().x,temp[j].stPot().y));
 9             cvSeqPush(point_seq, &cvPoint2D32f(temp[j].enPot().x,temp[j].enPot().y));
10         }
11 
12         cvFitLine(point_seq,CV_DIST_L2,0,0.01,0.01,line);   //CV_DIST_L2表示最小二乘法
13 
14         //line[0],line[1]為x,y的單位方向向量
15         //line[2],line[3]為直線經過某點的X,Y值
16         //正好組成參數方程
17         //X=line[0]t+line[2]
18         //Y=line[1]t+line[3]
19         
20         //根據line[4]計算自己需要的直線或線段
21         Point A(0,line[3]-(line[1]*line[2]/line[0]));
22         Point B(src->width,(src->width-line[2])*line[1]/line[0]+line[3]);
23         cvLine(src,A,B,CV_RGB(255,0,0),3,8,0);
24 
25         cvClearSeq(point_seq);
26         cvReleaseMemStorage(&storage);        

 

思路二:

  自己寫算法,但是需要注意,openCV中原點在屏幕的左上角,與我們數學中的直角坐標系不太一樣。     

 1 //最小二乘法擬合直線
 2 void line_fit(vector<MyLine>& h,IplImage* src)
 3 {
 4     int n = 2*h.size();
 5     int k;                       //目標直線斜率
 6     int b;                       //目標直線截距
 7     vector<MyLine>::iterator it = h.begin();
 8     int sumx=0,sumy=0,sumxy=0,sumxsq=0;
 9     while(it != h.end())
10     {
11         sumx += it->stPot().x;
12         sumx += it->enPot().x;
13         sumy += it->stPot().y;
14         sumy += it->enPot().y;
15         sumxy += it->stPot().x*it->stPot().y;
16         sumxy += it->enPot().x*it->enPot().y;
17         sumxsq += it->stPot().x*it->stPot().x;
18         sumxsq += it->enPot().x*it->enPot().x;
19         it++;
20     }
21     
22     if(sumxsq == (sumx*sumx/n))
23         k = atan2(0,1.0);
24     else
25         k = (sumxy-((sumx*sumy)/n))/(sumxsq-(sumx*sumx/n));
26     b = (sumy-k*sumx)/n;
27     cvLine(src,Point(0,b),Point(src->width,k*src->width+b),CV_RGB(255,0,0),1,8,0);
28 }

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM