个人兴趣爱好,最近在学习一些无人驾驶相关的技术,根基网上的教程用C++写了一下车道检测的实现,这里的车道检测是基础版本,需要满足几个先决条件:
(1)无人车保持在同车道的高速路中行驶
(2)车道线清晰可见
(3)无人车与同车道内前车保持足够远的距离。
现在的版本依旧有许多的问题,后续继续改进。
思路:
1.灰度转换 G=0.5*(B+R),可以实现白线与黄线的增强
2.滤波
3.二值化
4.截取RIO
5.边缘检测
6.HoughLinesP直线检测
7.直线拟合
下面直接贴代码main.cpp
github
#include "process.h"
#define endLine 320
int main()
{
VideoCapture cap("./2.mp4");
if(!cap.isOpened())
{
cout<< "open failed "<< endl;
}
int thresh=160;
Mat threshold_img,gray;
Mat dstImg;
Mat frame;
Mat edges;
Mat dilateImg;
cap>>frame;
gray=Mat(frame.size(),CV_8UC1);
cout<< "row: "<< frame.rows << "\n"<<"cols:"<<frame.cols<<endl;
int i,j;
namedWindow("result");
//RIO区域
Mat mask = Mat(gray.size(),CV_8UC1);
vector<vector<Point> > contour;
vector<Point> pts;
pts.push_back(Point(460,300));
pts.push_back(Point(60,540));
pts.push_back(Point(900,540));
pts.push_back(Point(500,300));
contour.push_back(pts);
drawContours(mask,contour,0,Scalar::all(255),-1);
VideoWriter videoWrite("video.mp4",CV_FOURCC('M','J','P','G'),30,frame.size());
while(1)
{
cap>>frame;
//G=0.5*(B+R)
for(i=0;i<gray.rows;i++)
{
uchar* p1=gray.ptr<uchar>(i);
uchar* p2=frame.ptr<uchar>(i);
for(j=0;j<gray.cols;j++)
{
p1[j]=0.5*(p2[3*j]+p2[3*j+2]);
}
}
//滤波
blur(gray,gray ,Size(3,3));
imshow("gray",gray);
//二值化
threshold(gray,threshold_img,thresh,255, CV_THRESH_OTSU );
//imshow("thresh",threshold_img);
//截取RIO
threshold_img.copyTo(dstImg,mask);
//imshow("dstImg",dstImg);
//膨胀
Mat element = getStructuringElement(MORPH_RECT,Size(5,5));
dilate(dstImg,dilateImg,element);
//imshow("erodeImg",dilateImg);
//边缘检测
Canny(dilateImg,edges,120,200,3);
vector<Vec4i> lines;
Vec4i leftLine,rightLine;
vector<Vec2f>::iterator it;
//霍夫直线变换
HoughLinesP(edges, lines, 1, CV_PI/180, 50, 0,10);
cout<<"num of line is "<< lines.size()<<endl;
int m=0,n=0;
for(int j=0;j<4;j++)
{
leftLine[j]=0;
rightLine[j]=0;
}
for( size_t i = 0; i < lines.size(); i++ )
{
Vec4i l;
l=lines[i];
float angle;
angle=atan2(l[2]-l[0],l[3]-l[1])*180/CV_PI;
if(angle>90)
{
m++;
for(int j=0;j<4;j++)
{
leftLine[j]+=l[j];
}
}
else if(angle< 90)
{
n++;
for(int j=0;j<4;j++)
{
rightLine[j]+=l[j];
}
}
}
//计算直线平均值
for(int j=0;j<4;j++)
{
rightLine[j]=rightLine[j]/n;
leftLine[j]=leftLine[j]/m;
}
float angle_left,angle_right;
//计算角度
angle_left=atan2(leftLine[2]-leftLine[0],leftLine[3]-leftLine[1]);
angle_right=atan2(rightLine[2]-rightLine[0],rightLine[3]-rightLine[1]);
cout << "left:"<< angle_left<<endl;
cout << "right"<< angle_right<<endl;
float leftK,rightK;
leftK=static_cast<float>(leftLine[1]-leftLine[3])/(leftLine[2]-leftLine[0]);
cout <<"leftLine 0 1 2 3 "<<leftLine[0]<<" "<<leftLine[1]<<" "<<leftLine[2]<<" "<<leftLine[3]<<" "<<endl;
cout << "leftK= "<< leftK << endl;
leftLine[0]=leftLine[0]-(frame.cols-leftLine[1])/leftK;
leftLine[1]=frame.cols;
if(leftLine[3]>endLine)
{
leftLine[2]=leftLine[2]+(leftLine[3]-endLine)/leftK;
leftLine[3]=endLine;
}
rightK=static_cast<float>(rightLine[3]-rightLine[1])/(rightLine[2]-rightLine[0]);
rightLine[2]=rightLine[2]+(frame.cols-rightLine[3])/rightK;
rightLine[3]=frame.cols;
if(rightLine[1]>endLine)
{
rightLine[0]=rightLine[0]-(rightLine[1]-endLine)/rightK;
rightLine[1]=endLine;
}
Mat GreenImg=Mat(frame.size(),CV_8UC3,Scalar(0,255,0));
Mat maskGreen=Mat(frame.size(),CV_8UC3,Scalar(0,0,0));
vector<vector<Point> > maskContour;
vector<Point> maskPts;
maskPts.push_back(Point(leftLine[2],leftLine[3]));
maskPts.push_back(Point(leftLine[0],leftLine[1]));
maskPts.push_back(Point(rightLine[2],rightLine[3]));
maskPts.push_back(Point(rightLine[0],rightLine[1]));
maskContour.push_back(maskPts);
drawContours(maskGreen,maskContour,0,Scalar::all(255),-1);
GreenImg.copyTo(maskGreen,maskGreen);
//imshow("maskGreen",maskGreen);
addWeighted(frame,0.8,maskGreen,0.2,0,frame);
line( frame, Point(rightLine[0],rightLine[1]),Point(rightLine[2],rightLine[3]), Scalar(0,0,255), 3, CV_AA);
line( frame, Point(leftLine[0],leftLine[1]),Point(leftLine[2],leftLine[3]), Scalar(0,0,255), 3, CV_AA);
circle(frame,Point(rightLine[0],rightLine[1]),10,Scalar(0,0,255));
circle(frame,Point(leftLine[2],leftLine[3]),10,Scalar(0,0,255));
videoWrite<<frame;
imshow("result",frame);
waitKey(30);
}
return 0;
}
在直线道路上效果还不错,但是受限于截取RIO的方法,一些问题暂时无法解决,这里只是提供一个简单的思路和实现。