opencv vs 下 车道线的检测

使用OpenCV进行车道线检测的C++代码实现,包括图像预处理、Canny边缘检测、Hough变换画直线等步骤。通过比较直线斜率和距离判断平行线,并找出最长的车道线。
摘要由CSDN通过智能技术生成
/*车道线检测,思路:
首先 ,对图像进行平滑处理,然后Canny边缘检测,使用Hough画直线
郭洪成
2015.3.31


*/
#include<cv.h>
#include<highgui.h>
#include<math.h>
#include<vector>
#include<string.h>
#include <atlstr.h>
#include <io.h>


//自定义LINE结构体 
//由pt0,pt1两个点连接成一段线段 
struct LINE  
{  
CvPoint pt0;  
CvPoint pt1;  
LINE( CvPoint pta, CvPoint ptb )  
{  
pt0 = pta;  
pt1 = ptb;  
}  //带参构造函数LINE( ),用于构造一个LINE
LINE()  
{  
pt0.x = pt0.y = 0;  
pt1.x = pt1.y = 0;  
}  //无参构造函数,原点(0,0)
};  








// 得到两条平行线之间的距离  
double GetDisOfParallelLines( LINE line0, LINE line1 )  
{  
CvPoint midPoint = cvPoint( (line0.pt0.x + line0.pt1.x)/2, (line0.pt0.y + line0.pt1.y)/2 ); // 中点  
double x_dis = line1.pt0.x - line1.pt1.x;  
if ( x_dis == 0.0 ) return fabs((double)(midPoint.x - line0.pt1.x)); // 如果line1 垂直x轴  


double a = (line1.pt0.y - line1.pt1.y) / x_dis;  
double b = line1.pt0.y - (line1.pt0.x * a);  
return fabs(a * midPoint.x - midPoint.y + b) / sqrt(a * a + 1);  
}  




//得到一条直线的tan率,也就是对边/邻边
double GetTanOfLine( LINE line )  
{  
double x_dis = line.pt0.x - line.pt1.x;  
if ( x_dis == 0.0 ) return 10e9;  
return (line.pt0.y - line.pt1.y) / x_dis;  
}  




//判断两条直线是否是平行线,通过球每条直线的tan率来比较
bool IsParallelLines( LINE line0, LINE line1 )  
{  
#define LIMIT (1.1547 / 3) //近似平行线的斜率之差的范围  


double angleTan0 = GetTanOfLine( line0 );  
double angleTan1 = GetTanOfLine( line1 );  
if ( fabs(angleTan0 - angleTan1) < (double)LIMIT )  
{  
return 1;  
}  
return 0;  
}  


//遍历文件夹文件的辅助函数
bool GetPicDirSetInFolder(CString strFindPath, std::vector<CString> &vecPathSet)  
{  
WIN32_FIND_DATA wfd;  
HANDLE hf = FindFirstFileA(strFindPath, &wfd);  


if (INVALID_HANDLE_VALUE != hf)  
{   
vecPathSet.push_back(wfd.cFileName);  
while (FindNextFileA(hf, &wfd))  
{  
vecPathSet.push_back(wfd.cFileName);  
}  
FindClose(hf);  
}  
return true;  



//计算一根线段的长度
double fGetLen(LINE fLine )
{
return  sqrt(  (fLine.pt0.x - fLine.pt1.x) * (fLine.pt0.x - fLine.pt1.x) +
( fLine.pt0.y - fLine.pt1.y) *( fLine.pt0.y - fLine.pt1.y)  )  ;
}


//获取所输入直线最长的一根
LINE fGetMaxLine(LINE fLine1 , LINE fLine2)
{
double fDisOfL1 = sqrt(  (fLine1.pt0.x - fLine1.pt1.x) * (fLine1.pt0.x - fLine1.pt1.x) +
( fLine1.pt0.y - fLine1.pt1.y) *( fLine1.pt0.y - fLine1.pt1.y) );


double fDisOfL2 = sqrt(  (fLine2.pt0.x - fLine2.pt1.x) * (fLine2.pt0.x - fLine2.pt1.x) +
( fLine2.pt0.y - fLine2.pt1.y) *( fLine2.pt0.y - fLine2.pt1.y) );


if ( fDisOfL1  >  fDisOfL2  )
return fLine1;
else return fLine2;
}






//主函数,里面的whiteVec和yellowVec是可以使用的
int main()
{
IplImage* fSrcImage ; 
IplImage *fTempImage = NULL ; 
IplImage *fTempImage1 = NULL ; 
CvSize fcvSize ; //图片大小,包含width , heigth
double fScale = 0.5 ; //设置图片的缩放倍数




//遍历文件夹所有图片
std::vector<CString> vecPathSet;  
CString findPath = "D:\\DEMO\\photoAlex\\rightwindow\\*.jpg";  
CString folder = "D:\\DEMO\\photoAlex\\rightwindow\\";  
CString procFolder = "D:\\DEMO\\photo\\result\\";  
CString procFolder1 = "D:\\DEMO\\photo\\result1\\";  


GetPicDirSetInFolder(findPath, vecPathSet);  


for (int iPic = 0; iPic < vecPathSet.size(); iPic ++)  
{  
CString path = folder;  
// load_img_path  存储的是打开文件夹
path.Append(vecPathSet[iPic]); 
CString save_path_result = procFolder;  
CString save_path_result1 = procFolder1;  
//save_img_path存储的是保存文件夹
save_path_result.Append(vecPathSet[iPic]);   
save_path_result1.Append(vecPathSet[iPic]);   
//save_path_result.Append(a);


// ProcessImage(path, save_img_path); // 进行图像处理  


fSrcImage= cvLoadImage(path , 0) ; // 载入文件夹下的所有图片
IplImage *fSrcImage1= cvLoadImage(path , 1) ;
IplImage *fSrcImage2= cvLoadImage(path , 1) ;//彩图
IplImage* fDstImage=cvCreateImage(cvGetSize(fSrcImage) , 8 , 1) ; 
IplImage* fColorImage=cvCreateImage(cvGetSize(fSrcImage) , 8 , 3) ; 
//分配空间,默认64KB
CvMemStorage* storage=cvCreateMemStorage(0) ; 
CvSeq* fLine1=0 ;                      // 指向所检测到的线的序列的第一条...




//cvThreshold(fSrcImage, fSrcImage, 80, 255.0, CV_THRESH_BINARY);

//进行形态学滤波,膨胀腐蚀,去掉噪音
cvErode(fSrcImage, fSrcImage, 0, 2);
cvDilate(fSrcImage, fSrcImage, 0, 2);


/*cvErode(fSrcImage2, fSrcImage2, 0, 2);
cvDilate(fSrcImage2, fSrcImage2, 0, 2);*/




//WashOffTheColor(fSrcImage2);


/*cvErode(fSrcImage2, fSrcImage2, 0, 3);
cvDilate(fSrcImage2, fSrcImage2, 0, 3);*/


//首先对源图像进行边缘检测 , 结果以灰度图显示,
cvCanny(fSrcImage , fDstImage , 20 , 150 , 3) ; 
//cvSobel(fSrcImage,fDstImage,0,1,5); 
cvCvtColor(fDstImage , fColorImage , CV_GRAY2BGR) ; 




cvNamedWindow("Canny" , 1) ; 
cvShowImage("Canny" , fDstImage) ;


//通过Hough直线检测返回给fLine1
//Hough直线返回的不是一条直线,而是一条直线上面的两个点,起点和终点
fLine1=cvHoughLines2(//返回一个指向CvSeq序列结构的的指针,
fDstImage , //要进行霍夫检测的图像,必须是8位的,
storage ,   //保存结果位置的指针,
CV_HOUGH_PROBABILISTIC , 
1 ,         //这两个参数都是用来设置直线的分辨率的
CV_PI/180 , 
100,       //一个阈值
20 , 
15     //表示支持所返回的直线的点的数量
) ; 


int fIndex ;      //fIndex为直线的索引


std::vector<LINE> fVector1;
std::vector<LINE> fVector2;
std::vector<LINE> fVector3;//存储连接之后的线段
std::vector<LINE> fVector4;//存放的是左边1/4的线段
std::vector<LINE> whiteVec;
std::vector<LINE> yellowVec;
for(fIndex=0 ; fIndex<fLine1->total ; fIndex++)    //   ←  遍历每一条霍夫直线
{
//↓ ↓ ↓ ↓  获得第fIndex条霍夫直线
CvPoint* line=(CvPoint*)cvGetSeqElem(fLine1 , fIndex) ;
//↓ ↓ ↓ ↓  对每条霍夫直线的两个端点画线段
cvLine(fSrcImage1 , line[0] , line[1] , CV_RGB(21 , 168 , 146) , 1 , CV_AA) ;
//↓ ↓ ↓ ↓ 对每条画好的直线,存储到vector里面
fVector1.push_back(LINE(line[0] , line[1]));
//LINE fLine2 = LINE(line[0] , line[1]);
}
 
//↓ ↓ ↓ ↓ 遍历vector里面的所有线段,使用双重循环,一条线段和其他所有的线段比较
//↓ ↓ ↓ ↓ 判断是否是相距为零的平行线,是的话,就连接两条直线
for(int i =0; i<fVector1.size() ;i++ )
{
/*if(sqrt( (fVector1[i].pt0.x - fVector1[i].pt1.x)  * (fVector1[i].pt0.x-fVector1[i].pt1.x) + 
(fVector1[i].pt0.y  - fVector1[i].pt1.y) * (fVector1[i].pt0.y  - fVector1[i].pt1.y) ) < 200   )
continue;*/
if( sqrt( (fVector1[i].pt0.x - fVector1[i].pt1.x)  * (fVector1[i].pt0.x-fVector1[i].pt1.x) + 
(fVector1[i].pt0.y  - fVector1[i].pt1.y) * (fVector1[i].pt0.y  - fVector1[i].pt1.y) ) < 150 ||
fabs(GetTanOfLine(fVector1[i]) ) <= 1.7 || fabs(GetTanOfLine(fVector1[i]) )>11   )    //←  对所有的直线判断,角度小于60°就舍弃
continue;

for(int j = i+1 ; j<fVector1.size() ;j++)
{
//if(GetDisOfParallelLines(fVector1[i] , fVector1[j]) == 0 )
if( IsParallelLines(fVector1[i] , fVector1[j]) == true  && GetDisOfParallelLines(fVector1[i] , fVector1[j]) < 2 )
{
cvLine(fSrcImage1 , fVector1[i].pt0 , fVector1[j].pt1 , CV_RGB(168 , 21 , 43) , 3 , 8) ;
fVector2.push_back(LINE (fVector1[i].pt0 , fVector1[j].pt1) );//将连接后的直线放进fVector2里面
}
}
}

//筛选连接之后的线
for (int i = 0 ; i<fVector2.size() ; i++)
{
//int max = 0 ;
if(fabs(GetTanOfLine(fVector2[i]) ) <=2.1)//倾斜角度过小,则排除,
continue;
for(int j = i; j<fVector2.size() ;j++) {

if( IsParallelLines(fVector2[i] , fVector2[j]) == true 
&& GetDisOfParallelLines(fVector2[i] , fVector2[j]) <2 )
{
LINE fLine3 = fGetMaxLine(fVector2[i] , fVector2[j]);
fVector3.push_back(fLine3);//fVector3里面存放的是比较之后的线段
cvLine(fSrcImage1 , fLine3.pt0 , fLine3.pt1 , CV_RGB(255 , 0, 0) , 6  , CV_AA) ;
}
}
}

//筛选连接之后的线
double max = 0 ;
int flag = 1;
for( int i = 0 ; i<fVector2.size() ; i++)
{
double max = fGetLen(fVector2[i]) ;
for(int j = i ; j<fVector2.size(); j++)
if(  fabs(GetTanOfLine(fVector2[i]) )>2.1
&& IsParallelLines(fVector2[i] , fVector2[j]) == true 
&& GetDisOfParallelLines(fVector2[i] , fVector2[j]) <2
&&  fGetLen(fVector2[j]) > max 
)
{
flag = j ;
max = fGetLen(fVector2[j]) ;
}
fVector3.push_back( fVector2[flag] );
cvLine( fSrcImage1 , fVector3[flag].pt0 ,  fVector3[flag].pt1 ,CV_RGB(225,0,0) , 6 , CV_AA );
}



int width = (fSrcImage1->width)/4 ;
//int height = fSrcImage1->height;
LINE fLine4;
int max = 0 , max1 = 0;
int flag = -1 , flag1 = -1;

//检测白线最长
for(int i = 0 ; i < fVector3.size() ; i++)//遍历fVec4里面的所有线段
{
if(fVector3[i].pt0.x > width 
|| fVector3[i].pt1.x > width
|| GetTanOfLine(  fVector3[i]  ) > 0  
)
continue;
if (  fGetLen(fVector3[i])  > max  )
{
max = fGetLen(fVector3[i]);
flag = i;
}
}
if(flag > -1) 
{
cvLine(fSrcImage1 , fVector3[flag].pt0 , fVector3[flag].pt1 , CV_RGB( 240 , 240 , 240 )  , 6 , CV_AA);
whiteVec.push_back(fVector3[flag]);
}



///检测白线第二长//
for(int i = 0 ; i < fVector3.size() ; i++)//遍历fVec4里面的所有线段
{
if(  fVector3[i].pt0.x > width  || fVector3[i].pt1.x >width
|| i==flag 
||GetDisOfParallelLines(fVector3[i] , fVector3[flag]) <3
|| GetTanOfLine(  fVector3[i]  ) > 0  
/*||GetDisOfParallelLines(fVector3[i] , fVector3[flag]) >11*/
)
continue;
if (  fGetLen(fVector3[i])  > max1  )
{
max1 = fGetLen(fVector3[i]);
flag1 = i;
}
}
if(flag1 > -1) 
{
cvLine(fSrcImage1 , fVector3[flag1].pt0 , fVector3[flag1].pt1 , CV_RGB( 240 , 240 , 240 )  , 6 , CV_AA);
whiteVec.push_back(fVector3[flag1]);
}



/检测黄线最长
int ywidth =( fSrcImage1->width )/ 2;
int yflag = -1 , yflag1 =-1;
int ymax = 0 , ymax1 =0;

for(int i = 0 ; i < fVector3.size() ; i++)//遍历fVec4里面的所有线段
{
if(fVector3[i].pt0.x < ywidth 
|| fVector3[i].pt1.x < ywidth
|| GetTanOfLine(  fVector3[i]  ) < 0  
)
continue;
if (  fGetLen(fVector3[i])  > ymax  )
{
ymax = fGetLen(fVector3[i]);
yflag = i;
}
}
if(yflag > -1) 
{
cvLine(fSrcImage1 , fVector3[yflag].pt0 , fVector3[yflag].pt1 , CV_RGB( 255 , 255 , 0  )  , 6 , CV_AA);
yellowVec.push_back(fVector3[yflag]);
}


///检测黄线第二长//
for(int i = 0 ; i < fVector3.size() ; i++)//遍历fVec4里面的所有线段
{
if(  fVector3[i].pt0.x < ywidth 
|| fVector3[i].pt1.x < ywidth
|| i==yflag 
||GetDisOfParallelLines(fVector3[i] , fVector3[yflag]) <3
|| GetTanOfLine(  fVector3[i]  ) < 0  
/*||GetDisOfParallelLines(fVector3[i] , fVector3[yflag]) >11*/
)
continue;
if (  fGetLen(fVector3[i])  > ymax1  )
{
ymax1 = fGetLen(fVector3[i]);
yflag1 = i;
}
}
if(yflag1 > -1) 
{
cvLine(fSrcImage1 , fVector3[yflag1].pt0 , fVector3[yflag1].pt1 , CV_RGB( 255 , 255 , 0 )  , 6 , CV_AA);
yellowVec.push_back(fVector3[yflag1]);
}




//设置图片缩放倍数
fcvSize.width = fColorImage->width * fScale ; 
fcvSize.height = fColorImage->height * fScale ; 
fTempImage  = cvCreateImage(fcvSize  ,  fColorImage->depth  , fColorImage->nChannels) ; 
fTempImage1  = cvCreateImage(fcvSize  ,  fSrcImage->depth  , fSrcImage->nChannels) ; 
IplImage *fTempImage2  = cvCreateImage(fcvSize  ,  fDstImage->depth  , fDstImage->nChannels) ; 
cvResize(fColorImage  ,  fTempImage  , CV_INTER_AREA) ; 
cvResize(fSrcImage  ,  fTempImage1  , CV_INTER_AREA) ; 
cvResize(fDstImage  ,  fTempImage2  , CV_INTER_AREA) ; 




/*cvNamedWindow("fColorImage" , 1) ; 
cvShowImage("fColorImage" , fColorImage) ; */


/*cvNamedWindow("fSrcImage" , 1) ; 
cvShowImage("fSrcImage" , fTempImage1) ; 


cvNamedWindow("HOUGH" , 1) ; 
cvShowImage("HOUGH" , fTempImage) ; 


cvNamedWindow("Canny" , 1) ; 
cvShowImage("Canny" , fTempImage2) ;*/


//fLoad1.Append(fLoad3);
   // cvSaveImage(save_path_result, fSrcImage1);
cvSaveImage(save_path_result1, fSrcImage1);
//cvSaveImage(save_path_result1, fSrcImage2);


cvReleaseImage(&fSrcImage2);
cvReleaseImage(&fSrcImage1);
cvReleaseImage(&fSrcImage);
cvWaitKey(0) ; 
}
return 0 ; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值