1. 程式人生 > >OpenCV 直線擬合及應用

OpenCV 直線擬合及應用

直線擬合顧名思義就是根據多個有限個數的點這裡寫圖片描述確定一條直線。依據為:
這裡寫圖片描述
其中這裡寫圖片描述為第i個點到直線的距離,p(d)則為確定最小值的函式。而不同的p(d)對應著不同的直線擬合方法:

OpenCV提供了7種(-1為使用者定義)直線擬合方法,如下:

CV_DIST_USER    =-1,  /* User defined distance */
CV_DIST_L1      =1,   /* distance = |x1-x2| + |y1-y2| */
CV_DIST_L2      =2,   /* the simple euclidean distance */
CV_DIST_C       =3,   /* distance = max(|x1-x2|,|y1-y2|) */
CV_DIST_L12 =4, /* L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1)) */ CV_DIST_FAIR =5, /* distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998 */ CV_DIST_WELSCH =6, /* distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846 */ CV_DIST_HUBER =7 /* distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345 */

OpenCV直線擬合函式:

CV_EXPORTS_W void fitLine( 
InputArray points, 
OutputArray line, 
int distType,
double param, 
double reps, 
double aeps );

points為2D的點:
distType即為上面提到的演算法;
param 是 上述公式中的常數C。如果取 0,則程式自動選取合適的值;
reps 表示直線到原點距離的精度,建議取 0.01;
aeps 表示直線角度的精度,建議取 0.01;
擬合結果即為函式的輸出 line,為Vec4f型別,line[0]、line[1] 存放的是直線的方向向量。line[2]、line[3] 存放的是直線上一個點的座標。
所以 ,直線的斜率即為:line[1]/line[0]。

直線擬合的應用:

#include <iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/opencv.hpp>  

#define  PI 3.141592653

using namespace cv;  
using namespace std;  

int main()
{   
    Mat SrcImage, thresholdImage,grayImage;
    SrcImage = imread("2.jpg");
    cvtColor(SrcImage,grayImage,CV_BGR2GRAY);
    threshold(grayImage,thresholdImage, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
    imshow("threshold",thresholdImage);
    vector<Point2f> onefitlinepoints,twofitlinepoints;
   //從上自下選擇點
    for (int i =SrcImage.cols/2-SrcImage.cols/5;i<SrcImage.cols/2+SrcImage.cols/5;i++)
     { for (int j=0;j<SrcImage.rows-1;j++)
        {if ((int)thresholdImage.at<uchar>(j,i)==255)
            {  circle(thresholdImage,Point(i,j),2,Scalar(0,255,0));
                onefitlinepoints.push_back(Point(i,j));
                break; 
             }}}
             //從下自上選擇點
     for (int k =SrcImage.cols/2-SrcImage.cols/5;k<SrcImage.cols/2+SrcImage.cols/5;k++)
      {  for (int l=SrcImage.rows-1;l>0;l--)
         {if ((int)thresholdImage.at<uchar>(l,k)==255)
            { circle(thresholdImage,Point(k,l),2,Scalar(0,255,0));
               twofitlinepoints.push_back(Point(k,l));
               break; 
             }}}
         //計算第一次擬合角度
     Vec4f oneline;
     fitLine(onefitlinepoints, oneline, CV_DIST_L1, 0, 0.01, 0.01);
     cout<<oneline[0]<<endl;
     cout<<oneline[1]<<endl;
    //求角度
     double  onefitlineradian =  atan(oneline[1]/oneline[0]);
     double  onefitlineangle = (onefitlineradian*180)/CV_PI;
     cout<<"直線擬合角度="<<onefitlineangle<<endl;

    //計算第二次擬合角度
    Vec4f twoline;
    fitLine(twofitlinepoints, twoline, CV_DIST_L1, 0, 0.01, 0.01);
    cout<<twoline[0]<<endl;
    cout<<twoline[1]<<endl;
    //求角度
    double  twofitlineradian =  atan(twoline[1]/twoline[0]);
    double  twofitlineangle = (twofitlineradian*180)/CV_PI;
    cout<<"直線擬合角度="<<twofitlineangle<<endl;

    double averagefitlineangle = (onefitlineangle+twofitlineangle)/2;
    cout<<"直線擬合平均角度="<<averagefitlineangle<<endl;

    //畫出直線
    Point2f point1,point2,point3;
    point2.x = oneline[2];
    point2.y = oneline[3];

    point1.x = 0;
    point1.y = oneline[1]*(point1.x-oneline[2])/oneline[0]+oneline[3];

    point3.x = SrcImage.cols;
    point3.y = oneline[1]*(point3.x-oneline[2])/oneline[0]+oneline[3];

    line(SrcImage,point1,point3,Scalar(0,0,255));

    imshow("直線擬合",SrcImage);
    waitKey(0);
    getchar();
    return 0;
}

這裡寫圖片描述

這裡寫圖片描述