opencv检测中线

先解释一下何为中线检测:
   程序输入的是一段视频,拍摄的是一条白色直线。

如图所示opencv检测中线_第1张图片

要求拟合直线的中线并计算中线到图像中心的距离以及和竖直轴的夹角。


思路很明确: 堆每一帧作如下处理:先滤波,去除噪声,防止后面检测直线时的干扰;然后用Hough检测找到图片中的直线;将得到的直线拟合成一条中线并最后显示出来;然后循环,依次处理每一帧并显示。

然而没想到遇到了不少坑。。。。。。。


一开始的时候先把视频截了个图,计划先对单张照片进行处理,成功之后再处理视频。然而我是全屏播放的条件下截的屏,然后就挖了一个坑。。。。。。

这一步还是比较顺利的(虽然花了我一个晚上的时间。。。。。。),遇到了第一个问题

vector  lines;//the rho and theta of each line
    HoughLines(edge,lines,0.1,CV_PI/360,13,0,0);
我一开始想的拟合方法是对每一条线的rho 和 theta 相加然后取平均值,进而得到中线的rho和theta。theta这么做是没问题的,但是rho就不行了,如果白线左边检测出来1条线,而右边检测出来100条线,这种方法得到的’中线‘肯定会明显偏向右边。

解决思路:对白线的左边和右边分别拟合两条直线,然后再用拟合得到的两条线的rho取平均值得到中线的rho。

解决方法: 新建一个array储存lines的rho,然后对rho进行排序,然后计算相邻元素的差值,当相邻差值出现剧烈变化时判定之后的线属于另外一边。



代码如下

sort(arr_rho,arr_rho+lines.size());
    //for(int i=(total-count);i 10*abs(arr_rho[i+1]-arr_rho[i+2]))); 
        {
            left = 0;
            divide = i;
        }
        if(left)
        {
            rho_mid_l += arr_rho[i];
            count_l++;
        }
        else 
        {
            rho_mid_r += arr_rho[i];
            count_r++;
        }
    }
    rho_mid_l += arr_rho[divide];
    count_l++;
    rho_mid_r -= arr_rho[divide];
    count_r--;
    rho_mid_l = rho_mid_l/count_l;
    rho_mid_r = rho_mid_r/count_r;
    rho_mid=(rho_mid_l+rho_mid_r)/2.0;
 

检测这张截图的时候没毛病,不过之后用到视频里面就发现经常出问题,苦思良久才发现是判断条件除了问题

改成如下就好使了

if((abs(arr_rho[i]-arr_rho[i+1]) > 10*abs(arr_rho[i+1]-arr_rho[i+2]))&&left&&(abs(arr_rho[i+1]-arr_rho[i])>10))
&&left 就防止了divide的值不正确的情况(考虑arr_rho[]={100,101,200,200.9,201,201.1},这种情况下divede就会是3,而正确的应该是2)
&&(abs(arr_rho[i+1]-arr_rho[i])>10)) 则避免的这种情况下的误判:arr_rho[]={100,102,100.01,201,202,} 若没有这个条件程序就会判断100后面的rho都是另外一边的了


然后对于有些图片还是会出现中线不准确,甚至没办法画出中线的情况;

经过不断调试明白了opencv里面rho和theta的几何含义:坐标轴原点为左上角,向下为y轴,向右为x轴, theta为弧度角属于[0,CV_PI),顺时针为正方向!rho的符号与取决于直线在原点的上方还是下方,下正上负。

之后因为实际视频的大小比截图的大小小了将近3倍,几乎检测不出来直线(阈值设的太高了)后来又是不断得调节滤波和hough检测的参数,才能检测出中线。

封装成函数也费了一番功夫,不过封装好之后用和调试都舒服多了。

最后贴一下源码吧

/*
* video.cpp
* Author:
*   Weixiankui
*/    
#include  // for standard I/O
#include    // for strings
#include   // for controlling float print precision
#include   // string to number conversion
#include      // Basic OpenCV structures (cv::Mat, Scalar)
#include   // Gaussian Blur
#include 
#include  

using namespace std;
using namespace cv;


double Distance(Point core,Point pt1,Point pt2)
{
    double A,B,C,dis;
    A=pt2.y-pt1.y;
    B=pt1.x-pt2.x;
    C=pt2.x*pt1.y-pt1.x*pt2.y;
    dis=abs(A*core.x+B*core.y+C)/sqrt(A*A+B*B);
    return dis;
}

void MiddleLine(Mat image,Point *points);

int main(int argc,char **argv)
{
    if(argc!=2)
    {
        printf("usage:\n./video \n");
        return -1;
    }
    const char *win="video~";
    VideoCapture video(argv[1]);
    namedWindow(win,WINDOW_AUTOSIZE);
    char key=0;
    Mat head;
    video >> head;
    Point core=Point(head.cols*0.5,head.rows*0.5);//to get the center coordinates

    
    for(;;)
    {
        
        Mat src;
        video>>src;
        Point points[2];
        MiddleLine(src,points);//input the image and get two points of the middle line int the Point array(2nd parament)
        line(src,points[0],points[1],Scalar(0,0,255),2,CV_AA);//draw the line
        imshow(win,src);
        double dist = Distance(core,points[0],points[1]);//get the distance between the middle line and the center point
        cout<<"Distance"< lines;
    HoughLines(edge,lines,0.1,CV_PI/360,13,0,0);
    //cout<<"lines.size()  "<0?lines[i][1]:(lines[i][1]-CV_PI);
        
        arr_rho[count]=abs(lines[i][0]);
        sum += lines[i][0]*0.1;
        //cout<<"count"< 10*abs(arr_rho[i+1]-arr_rho[i+2]))&&left&&(abs(arr_rho[i+1]-arr_rho[i])>10))//add the && left condition other wise the divide may be given to numer,and the program would have problem; 
        {
            left = 0;
            divide = i;
    //        cout<<"divide"<0)
        rho=rho_mid;
    else
        rho=-rho_mid;
    rho=theta_mid>0?rho_mid:-rho_mid;
    //theta=theta_mid
    theta_mid=theta_mid/count;
    float theta=theta_mid>0?theta_mid:(CV_PI+theta_mid);
    Point pt1,pt2;
    double a =cos(theta),b=sin(theta);
    double x0=a*rho,y0=b*rho;

    points[0].x=cvRound(x0+10000*(b)); 
    points[0].y=cvRound(y0+10000*(-a));
    points[1].x=cvRound(x0+10000*(-b));
    points[1].y=cvRound(y0+10000*(a));
    cout<<"theta"<

今天把矫正相机的作业写完了,好多线性代数的知识都忘了,从今天起开始看线代的mooc



你可能感兴趣的:(opencv)