首先要定位车道线的基点(图片最下方车道出现的x轴坐标),由于车道线在的像素都集中在x轴一定范围内,因此把图片一分为二,左右两边的在x轴上的像素分布峰值非常有可能就是车道线基点。
以下为测试片x轴的像素分布图:
定位基点后,再使用使用滑动窗多项式拟合(sliding window polynomial fitting)来获取车道边界。这里使用9个200px宽的滑动窗来定位一条车道线像素:
void find_line(const cv::Mat& src,vector<cv::Point>& lp,vector<cv::Point>& rp,int& rightx_current,int& leftx_current,double& distance_from_center){ cv::Mat hist,nonzero,l,r; vector<cv::Point> nonzerol,nonzeror,lpoint,rpoint; int midpoint; cv::Point leftx_base,rightx_base; //选择滑窗个数 int nwindows = 9; //设置窗口高度 int window_height = int(src.rows/nwindows); //设置窗口宽度 int margin=50; //设置非零像素坐标最少个数 int minpix=50; //TODO 加入if设置图像连续性,如果leftx_current和rightx_current为零,则认为第一次执行,需要计算该两点,如果已经计算了,则不许再次计算。 //rowrange图像区域分割 //将图像处理为一行,以行相加为方法 cv::reduce(src.rowRange(src.rows/2,src.rows),hist,0,cv::REDUCE_SUM,CV_32S); midpoint=int(hist.cols/2); //将hist分为左右分别储存,并找出最大值 //minMaxIdx针对多通道,minMaxLoc针对单通道 cv::minMaxLoc(hist.colRange(0,midpoint),NULL,NULL,NULL,&leftx_base); cv::minMaxLoc(hist.colRange(midpoint,hist.cols),NULL,NULL,NULL,&rightx_base); //左右车道线基础点 leftx_current=leftx_base.x; rightx_current=rightx_base.x+midpoint; // 提前存入该基础点坐标 lpoint.push_back(cv::Point(leftx_current,src.rows)); rpoint.push_back(cv::Point(rightx_current,src.rows)); for(int i=0;i<nwindows;i++){ int win_y_low=src.rows-(i+1)*window_height; //计算选框x坐标点,并将计算结果限制在图像坐标内 int win_xleft_low = leftx_current - margin; win_xleft_low=win_xleft_low>0?win_xleft_low:0; win_xleft_low=win_xleft_low<src.rows?win_xleft_low:src.rows; //int win_xleft_high = leftx_current + margin; int win_xright_low = rightx_current - margin; win_xright_low=win_xright_low>0?win_xright_low:0; win_xright_low=win_xright_low<src.rows?win_xright_low:src.rows; //int win_xright_high = rightx_current + margin; //NOTE要确保参数都大于0,且在src图像范围内,不然会报错 //NOTE 设置为ROI矩形区域选择 l=src(cv::Rect(win_xleft_low,win_y_low,2*margin,window_height)); r=src(cv::Rect(win_xright_low,win_y_low,2*margin,window_height)); //NOTE 把像素值不为零的像素坐标存入矩阵 cv::findNonZero(l,nonzerol); cv::findNonZero(r,nonzeror); //计算每个选框的leftx_current和rightx_current中心点 if(nonzerol.size()>minpix){ int leftx=0; for(auto& n:nonzerol){ leftx+=n.x; } leftx_current=win_xleft_low+leftx/nonzerol.size(); } if(nonzeror.size()>minpix){ int rightx=0; for(auto& n:nonzeror){ rightx+=n.x; } rightx_current=win_xright_low+rightx/nonzeror.size(); } //将中心点坐标存入容器 lpoint.push_back(cv::Point(leftx_current,win_y_low)); rpoint.push_back(cv::Point(rightx_current,win_y_low)); } //拟合左右车道线坐标 cv::Mat leftx = polyfit(lpoint,2); cv::Mat rightx = polyfit(rpoint,2); //计算拟合曲线坐标 lp=polyval(leftx,lpoint,2); rp=polyval(rightx,rpoint,2); //计算车道偏离距离 int lane_width=abs(rpoint.front().x-lpoint.front().x); double lane_xm_per_pix=3.7/lane_width; double veh_pos=(((rpoint.front().x+lpoint.front().x)*lane_xm_per_pix)/2); double cen_pos=((src.cols*lane_xm_per_pix)/2); distance_from_center=veh_pos-cen_pos; // cout<<"dis"<<distance_from_center<<endl; // cout<<lp<<endl; }以下为滑动窗多项式拟合(sliding window polynomial fitting)得到的结果:
计算车道曲率及车辆相对车道中心位置利用检测车道得到的拟合值(find_line 返回的left_fit, right_fit)计算车道曲率,及车辆相对车道中心位置,代码在find_line中:
int lane_width=abs(rpoint.front().x-lpoint.front().x); double lane_xm_per_pix=3.7/lane_width; double veh_pos=(((rpoint.front().x+lpoint.front().x)*lane_xm_per_pix)/2); double cen_pos=((src.cols*lane_xm_per_pix)/2); distance_from_center=veh_pos-cen_pos; 处理原图,展示信息使用逆变形矩阵把鸟瞰二进制图检测的车道镶嵌回原图,并高亮车道区域,使用"cv::putText()"方法处理原图展示车道曲率及车辆相对车道中心位置信息:
void draw_area(const cv::Mat& src,vector<cv::Point>& lp,vector<cv::Point>& rp,const cv::Mat& Minv,double& distance_from_center){ vector<cv::Point> rflip,ptr; cv::Mat colormask=cv::Mat::zeros(src.rows,src.cols,CV_8UC3); cv::Mat dst,midst; //绘制车道线 cv::polylines(colormask,lp,false,cv::Scalar(0,255,0),5); cv::polylines(colormask,rp,false,cv::Scalar(0,0,255),5); //反转坐标,以便绘制填充区域 cv::flip(rp,rflip,1); //拼接坐标 cv::hconcat(lp,rflip,ptr); //绘制填充区域 const cv::Point* em[1]={&ptr[0]}; int nop=(int)ptr.size(); cv::fillPoly(colormask,em,&nop,1,cv::Scalar(200,200,0)); //反变形 cv::warpPerspective(colormask,midst,Minv,src.size(),cv::INTER_LINEAR); //将车道线图片和原始图片叠加 cv::addWeighted(src,1,midst,0.3,0,dst); //绘制文字 cv::putText(dst,"distance bias:"+to_string(distance_from_center)+"m",cv::Point(50,50),cv::FONT_HERSHEY_SIMPLEX,1,cv::Scalar(255,255,255),2); cv::imshow("video",dst); // cv::waitKey(10000); }以下为测试图片处理后结果:
以下为处理后测试视频链接: