基于 HoughLinesP函数应用

本文介绍了如何利用OpenCV中的HoughLinesP函数进行概率霍夫变换,以精确检测图像中的直线,特别是应用于车道检测。通过设置参数如距离分辨率、角度分辨率、阈值、最小直线长度和最大直线间隙,可以有效地识别和定位车道线。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.概率霍夫变换

HoughLinesP能够检测出线端,即能够检测出图像中直线的两个端点,确切地定位图像中的直线。

void HoughLinesP(InputArray image,
OutputArray lines,
double rho, 
double theta, 
int threshold,
double minLineLength=0,
double maxLineGap=0 )

image为输入图像,要求是8位单通道图像

lines为输出的直线向量,每条线用4个元素表示,即直线的两个端点的4个坐标值

rho和theta分别为距离和角度的分辨率

threshold为阈值,即步骤3中的阈值

minLineLength为最小直线长度,在步骤5中要用到,即如果小于该值,则不被认为是一条直线

maxLineGap为最大直线间隙,在步骤4中要用到,即如果有两条线段是在一条直线上,但它们之间因为有间隙,所以被认为是两个线段,如果这个间隙大于该值,则被认为是两条线段,否则是一条。

首先来检测棋盘的竖直线段:

#include "stdafx.h"
#include <opencv2/opencv.hpp>    
#include<iostream>
using namespace cv;  
using namespace std;  

int main()  
{  
   Mat src=imread("D://vvoo//qipan.jpg"); 
   Mat gray,canny;
    cvtColor(src,gray,CV_RGB2GRAY);
    Canny(gray,canny,50,100);
    //imshow("canny",canny);

    vector<Vec4i> lines;
    HoughLinesP(canny,lines,1,CV_PI/180,20,20,600);
    //select approprivate lines acoording to the slope
    for (int i = 0;i < lines.size();i ++)
        {
            Vec4i I=lines[i];
			int dx=I[2]-I[0];
			int dy=I[2]-I[1];
			double angle = atan2(double(dy),dx) * 180 /CV_PI;
			 if (abs(angle) <= 20) // reject near horizontal lines
				 continue;
			  
			 if((I[1]>I[3]+90)||(I[1]<I[3]-90))//the height of two points should more than 90 pixels
			 {
				 line(src,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(0,0,255),1,CV_AA);
			 }
        }
 //vector
  
  
   
    lines;  
    //HoughLinesP(canny,lines,1,CV_PI/180,20,200,1000);  //horizontal
    //select approprivate lines acoording to the slope  
    //for (int i = 0;i < lines.size();i ++)  
        //{  
            //Vec4i I=lines[i];  
            //int dx=I[2]-I[0];  
            //int dy=I[3]-I[1];  
            //double angle = atan2(double(dy),dx) * 180 /CV_PI;  
             //if (abs(angle) >=5) // reject near horizontal lines  
             //    continue;  
                
            // if((I[0]>I[2]+90)||(I[0]<I[2]-90))//the height of two points should more than 90 pixels  
            // {  
                 //line(src,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(0,0,255),2,CV_AA);  
             //}  
       // }
imshow("src",src);   
waitKey(0);
return 0;  
}  
  
  
  

函数atan2()是求两点之间的角度(弧度),根据弧度转换角度公式:弧度变角度 180/π×弧度,最后就是斜率。

2.车道检测

1.车道线检测
#include "stdafx.h"
#include <opencv2/opencv.hpp>    
#include<iostream>
#include<time.h>
using namespace cv;  
using namespace std;  
clock_t start,stop;


int main()  
{  
    Mat frame,gray,canny;  
    bool stop = false;  
    VideoCapture cap("D:\\vvoo1\\32.avi");//打开视频文件 

	double fps = cap.get(CV_CAP_PROP_FPS); 
    cout << "Input video's Frame per seconds : " << fps << endl;
    if (!cap.isOpened())  
    {  
        cout << "读取视频有误" << endl;  
        return -1;  
    }  
 while (!stop)  
    {  
    start=clock();
    cap >> frame;   
   
    Mat srcROI =frame(Range(250,frame.rows),Range(50,frame.cols-120)); 
    cvtColor(srcROI,gray,CV_RGB2GRAY);
    Canny(gray,canny,50,100);
    //blur(gray,gray,Size(3,3));
    //imshow("gray",gray);
    imshow("canny",canny);
    

    vector<Vec4i> lines;
    HoughLinesP(canny,lines,1,CV_PI/180,20,10,50);

    //select approprivate lines acoording to the slope
    for (int i = 0;i < lines.size();i ++)
        {
            Vec4i I=lines[i];
			int dx=I[2]-I[0];
			int dy=I[2]-I[1];
			double angle = atan2(double(dy),dx) * 180 /CV_PI;
			 if (abs(angle) <= 20)
				 continue;
			 if((I[1]>I[3]+90)||(I[1]<I[3]-90))
			 {
				 line(srcROI,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(0,0,255),3,CV_AA);
				line(gray,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(0,0,255),3,CV_AA);
			 }
        }

	imshow("frame",frame);
	imshow("gray",gray);
		
	/*stop=clock();
	cout<<"fps : "<<1.0/(((double)(stop-start))/ CLOCKS_PER_SEC)<<endl;*/

        if(waitKey(10) == 27) //wait for 'esc' key press for 10 ms. If 'esc' key is pressed, break loop
        {
            cout<<"video paused!, press q to quit, any other key to continue"<<endl;
            if(waitKey(0) == 'q')
            {
                cout << "terminated by user" << endl;
                break;
            }
        }
    }  
    return 0;  
}  

整体来说检测到效果一般。

2.可简单的区分左右车道,并左右车道各保留一条车道线(2017.05.31改)。

// hough_fitline.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2/opencv.hpp>    
#include<iostream>
#include<time.h>
using namespace cv;  
using namespace std;  


int main()  
{  
	int Fnumber=0;
    Mat frame,gray,canny;  
    bool stop = false;  
    VideoCapture cap("D:\\vvoo1\\32.avi");//打开视频文件 

	double fps = cap.get(CV_CAP_PROP_FPS); 
    cout << "Input video's Frame per seconds : " << fps << endl;
    if (!cap.isOpened())  
    {  
        cout << "读取视频有误" << endl;  
        return -1;  
    }  
 while (!stop)  
    {  
    cap >> frame;   
    Fnumber++;
    Mat srcROI =frame(Range(250,frame.rows),Range(50,frame.cols-120)); 
    cvtColor(srcROI,gray,CV_RGB2GRAY);
	//cout<<"srcROI.rows="<<frame.rows<<endl<<"srcROI.cols"<<frame.cols<<endl;
	circle(frame,Point(300,260),3,Scalar(255,0,0),3);
    Canny(gray,canny,50,100);
    //blur(gray,gray,Size(3,3));
    //imshow("gray",gray);
    imshow("canny",canny);
    cout<<"Fnumber:"<<Fnumber<<endl;

    vector<Vec4i> lines;
    HoughLinesP(canny,lines,1,CV_PI/180,20,10,50);

    //select approprivate lines acoording to the slope
	int j=0,k=0;
    for (int i = 0;i < lines.size();i ++)
        {
			
            Vec4i I=lines[i];
			int dx=I[2]-I[0];
			int dy=I[2]-I[1];
			double angle = atan2(double(dy),dx) * 180 /CV_PI;
			if (abs(angle) <= 20)
				 continue;
			
			if((I[1]>I[3]+90)||(I[1]<I[3]-90))//selecting the length of lane
			{
				
				if(I[0]<260||I[2]<260)//how to optimize the left and right lane
				{
				    j++;
					if(j==1) 
					{
				    cout<<"left_Point(I[0],I[1])="<<Point(I[0],I[1])<<endl<<"left_Point(I[2],I[3])="<<Point(I[2],I[3])<<endl;
				    line(srcROI,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(0,0,255),2,CV_AA);
					}
				 }
				else
				{
					k++;
					if(k==1) {
                    line(srcROI,Point(I[0],I[1]),Point(I[2],I[3]),Scalar(255,0,255),2,CV_AA);
					cout<<"right_Point(I[0],I[1])="<<Point(I[0],I[1])<<endl<<"right_Point(I[2],I[3])="<<Point(I[2],I[3])<<endl;}
				   
				}
			 }
        }

	imshow("frame",frame);
	if(Fnumber==30)
		imwrite("D://vvoo3//image.jpg",frame);
	//imshow("gray",gray);

        if(waitKey(10) == 27) //wait for 'esc' key press for 10 ms. If 'esc' key is pressed, break loop
        {
            cout<<"video paused!, press q to quit, any other key to continue"<<endl;
            if(waitKey(0) == 'q')
            {
                cout << "terminated by user" << endl;
                break;
            }
        }
    }  
    return 0;  
}  



效果不好。


3.IplImage 格式

#include "stdafx.h"  
#include<opencv2/opencv.hpp>  
#include<cstdio>  
#include<iostream>  
using namespace std;  
using namespace cv; 

int cur_frame = 0;         //用于指示g_capture的当前帧  
int g_slider_position = 0; 
void onTrackbarSlide(int pos);
CvCapture* pCapture = NULL;  
int main()
{  
    IplImage* pFrame = NULL;  
    IplImage* pCutFrame = NULL;  
    IplImage* pCutFrImg = NULL;  
    
    //声明CvMemStorage和CvSeg指针  
    CvMemStorage* storage = cvCreateMemStorage();  
    CvSeq* lines = NULL;  
    //生成视频的结构  
    VideoWriter writer("result.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(856, 480));  
    int nFrmNum = 0;  
    int CutHeight = 240;  
    cvNamedWindow("video", 1);  
    cvNamedWindow("BWmode", 1);  
    cvMoveWindow("video", 300, 0);  
    cvMoveWindow("BWmode", 300, 520);  
    if (!(pCapture = cvCaptureFromFile("D:\\vvoo1\\32.avi"))){  
        fprintf(stderr, "Can not open video file\n");  
        return -2;  
    } 

    //每次读取一桢的视频  
    while (pFrame = cvQueryFrame(pCapture))
{  
int frames = (int)cvGetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_COUNT); //以帧数来设置读入位置 
if(frames != 0)
{  
   cvCreateTrackbar("Frames", //进度条名称  
	    "video", //让进度条显示在最终结果的窗口  
         &g_slider_position,  
        frames,  
        onTrackbarSlide ); //调用一次onTrackbarSlide     
}  

        //设置ROI裁剪图像  
        cvSetImageROI(pFrame, cvRect(0, CutHeight, pFrame->width, pFrame->height - CutHeight));  
        nFrmNum++;  
        //第一次要申请内存p  
        if (nFrmNum == 1){  
            pCutFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height - CutHeight), pFrame->depth, pFrame->nChannels);  
            cvCopy(pFrame, pCutFrame, 0);  
            pCutFrImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1);  
            //转化成单通道图像再处理  
            cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);  
        }  
        else{  
            //获得剪切图  
            cvCopy(pFrame, pCutFrame, 0);  
#if 0     //反透视变换  
          //二维坐标下的点,类型为浮点  
            CvPoint2D32f srcTri[4], dstTri[4];  
            CvMat* warp_mat = cvCreateMat(3, 3, CV_32FC1);  
            //计算矩阵反射变换  
            srcTri[0].x = 10;  
            srcTri[0].y = 20;  
            srcTri[1].x = pCutFrame->width - 50;  
            srcTri[1].y = 0;  
            srcTri[2].x = 0;  
            srcTri[2].y = pCutFrame->height - 50;  
            srcTri[3].x = pCutFrame->width - 50;  
            srcTri[3].y = pCutFrame->height - 1;  
            //改变目标图像大小  
            dstTri[0].x = 0;  
            dstTri[0].y = 0;  
            dstTri[1].x = pCutFrame->width - 1;  
            dstTri[1].y = 0;  
            dstTri[2].x = 0;  
            dstTri[2].y = pCutFrame->height - 1;  
            dstTri[3].x = pCutFrame->width - 1;  
            dstTri[3].y = pCutFrame->height - 1;  
            //获得矩阵  
            cvGetPerspectiveTransform(srcTri, dstTri, warp_mat);  
            cout<<"warp_mat="<<warp_mat<<endl;  
            CvMat*inverse=cvCreateMat(3, 3, CV_32FC1);  
            cvInvert(warp_mat,inverse,CV_SVD);  
            //反透视变换  
            cvWarpPerspective(pCutFrame, pCutFrame, warp_mat);  
              
            //cvShowImage("video2", pFrame);  
  
#endif  
			
            //前景图转换为灰度图  
            cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);  
           
            cvCanny(pCutFrImg, pCutFrImg, 50, 120);  
         
            lines = cvHoughLines2(pCutFrImg,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,50,90,50);  
            for (int i = 0;i < lines->total;i ++)  
        {  
            //double k =INF;  
            CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);  
            int dx = line[1].x - line[0].x;  
            int dy = line[1].x - line[0].y;  
            double angle = atan2(double(dy),dx) * 180 /CV_PI;  
            if (abs(angle) <= 10)  
                continue;  
            if (line[0].y > line[1].y + 90 || line[0].y < line[1].y - 90)  
            {  
				//if(line[0].x<260||line[1].x<260)
                cvLine(pFrame,line[0],line[1],CV_RGB(255,0,0),2,CV_AA);
				/*else
                cvLine(pFrame,line[0],line[1],CV_RGB(255,0,255),2,CV_AA);  */
            }  
        }  
   
    //cvWarpPerspective(pCutFrame,pFrame,inverse);  
            //恢复ROI区域  
            cvResetImageROI(pFrame);  
            //写入视频流  
            writer << pFrame;  
            //显示图像  
            cvShowImage("video", pFrame);  
            //cvShowImage("BWmode", pCutFrImg);  
            //cvShowImage("video1", pCutFrame);   
            int temp = cvWaitKey(2);  
            if (temp == 32){  
                while (cvWaitKey() == -1);  
            }  
            else if (temp >= 0){  
                break;  
            }  
        }  
    }  
	//让进度条随着视频播放滚动  
        cur_frame = (int)cvGetCaptureProperty(pCapture,CV_CAP_PROP_POS_FRAMES);//提取当前帧           
        cvSetTrackbarPos("Frames","video",cur_frame);//设置进度条位置  
    //销毁窗口  
    cvDestroyWindow("video");  
    //cvDestroyWindow("BWmode");  
    //释放图像  
    cvReleaseImage(&pCutFrImg);  
    cvReleaseImage(&pCutFrame);  
    cvReleaseCapture(&pCapture);  
  
    return 0;  
} 
void onTrackbarSlide(int pos)
{
    if (pos!=cur_frame)
     {  
//如果回调函数onTrackbarSlide(int pos)中当前的函数参数pos与全局变量相等,  
//说明是滚动条自动移动造成的调用,不必重新设置g_capture的当前帧  
    cvSetCaptureProperty(  
    pCapture,  
    CV_CAP_PROP_POS_FRAMES,  pos  );  
     }  
}









评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值