Advertisement

无人驾驶入门-基于OpenCV的初级车道线检测

阅读量:

基于OpenCV的初级车道线检测

    • 环境

    • 步骤

    • 代码分析

      • 1. 图像输入
      • 2. 灰度处理
      • 3. 滤波处理
      • 4. 边缘提取
      • 5. 感兴趣区域
      • 6. 霍夫变换
      • 7. 直线拟合
    • 参考

环境

  • OpenCV 4.5.0
  • C++

步骤

  1. 图像输入
  2. 灰度处理
  3. 滤波处理
  4. 边缘提取
  5. 感兴趣区域
  6. 霍夫变换
  7. 直线拟合

代码分析

1. 图像输入

复制代码
       Mat  image;
       image = imread("img/whiteCarLaneSwitch.jpg");
image

imread函数

复制代码
    Mat cv::imread	(	const String & 	filename,
                    int 	        flags = IMREAD_COLOR 
                )

参数1:filename—需要载入图片的路径名。主要有绝对路径法、相对路径法、命令行参数法等,具体可参考使用imread()函数读取图片的六种正确姿势
参数2:flags—加载图像的颜色类型(cv::ImreadModes)。默认是3通道BGR彩色图像。

2. 灰度处理

复制代码
    	Mat  gray_image;
    	cv::cvtColor(image, gray_image, COLOR_BGR2GRAY);
gray_image

cvtColor函数

复制代码
    void cv::cvtColor(cv::InputArray src, 
                  cv::OutputArray dst, 
                  int code, 
                  int dstCn = 0)

参数1:src—输入图像即要进行颜色空间变换的原图像。
参数2:dst—输出图像即进行颜色空间变换后存储图像。
参数3:code—转换的代码或标识,即在此确定将什么制式的图片转换成什么制式的图片。cv::ColorConversionCodes
参数4:dstCn—目标图像通道数,如果取值为0,则由src和code决定。

3. 滤波处理

复制代码
    	Mat blur_image;
    	//bilateralFilter(gray_image, blur_image, 13, 15, 15);
    	cv::GaussianBlur(gray_image, blur_image, cv::Size(3, 3), 0, 0);
blur_image

GaussianBlur函数

复制代码
    void cv::GaussianBlur(cv::InputArray src, 
                      cv::OutputArray dst, 
                      cv::Size ksize, 
                      double sigmaX, 
                      double sigmaY = (0.0), 
                      int borderType = 4)

参数1:src—输入图像;图像可以具有任意数量的通道,这些通道是独立处理的。
参数2:dst—输出图像;与src具有相同大小和类型。
参数3:ksize—高斯核大小;ksize.width和ksize.height可以不同,但它们都必须为正奇数。 或者,它们可以为零,然后根据sigma计算得出。
参数4:sigmaX—高斯核在x方向的标准差。
参数5:sigmaY—高斯核在y方向的标准差。sigmaY=0时,其值自动由sigmaX确定(sigmaY=sigmaX);sigmaY=sigmaX=0时,它们的值将由ksize.width和ksize.height自动确定。
参数6:borderType—像素外插策略。

4. 边缘提取

复制代码
    	Mat canny_image;
    	cv::Canny(blur_image, canny_image, 50, 150, 3);
canny_image

Canny函数

复制代码
    void cv::Canny(cv::InputArray image, 
               cv::OutputArray edges, 
               double threshold1, 
               double threshold2, 
               int apertureSize = 3, 
               bool L2gradient = false)

参数1:image—输入图像;8位。
参数2:edges—输出边缘;单通道8位图像,其大小与image相同。
参数3:threshold1—阈值1
参数4:threshold2—阈值2
参数5:apertureSize—Sobel算子大小。
参数6:L2gradient—是否采用更精确的方式计算图像梯度。

关于阈值:

  • 两个参数不区分较大阈值和较小阈值,函数会自动比较区分两个阈值的大小,不过一般情况下,较大阈值与较小阈值的比值在2:1到3:1之间。
  • 如果该像素的梯度值小于较小阈值,则该像素为非边缘像素;
  • 如果该像素的梯度值大于较大阈值,则该像素为边缘像素;
  • 如果该像素的梯度值介于较小阈值与较大阈值之间,需要进一步检测该像素的3×3邻域内的8个点,如果这8个点内有一个或以上的点梯度超过了较大阈值,则该像素为边缘像素,否则不是边缘像素。

5. 感兴趣区域

复制代码
    //感兴趣区域 ROI
    Mat region_of_interest(Mat& srcImg)
    {
    
    	Mat dstImg;
    	Mat mask = Mat::zeros(srcImg.size(), CV_8UC1);
    	int img_width = srcImg.size().width;
    	int img_height = srcImg.size().height;
    
    	vector<vector<Point>> contour;
    	vector<Point> pts;
    	pts.push_back(Point(img_width * 0.45, img_height * 0.6));
    	pts.push_back(Point(img_width * 0.55, img_height * 0.6));
    	pts.push_back(Point(img_width, img_height));
    	pts.push_back(Point(img_width, img_height));
    	pts.push_back(Point(0, img_height));
    	pts.push_back(Point(0, img_height));
    	contour.push_back(pts);
    
    	drawContours(mask, contour, 0, Scalar::all(255), -1);
    	srcImg.copyTo(dstImg, mask);
    	return dstImg;
    }
    
    	Mat ROI_image;
    	ROI_image= region_of_interest(canny_image);
ROI_image

6. 霍夫变换

复制代码
    	//霍夫变换
    	Mat Hough_image;
    	cv::cvtColor(ROI_image, Hough_image, COLOR_GRAY2BGR);
    	vector<Vec4f>plines;//定义一个存放直线信息的向量
    	double rho = 1;
    	double theta = CV_PI / 180;
    	int threshold = 15;
    	double minLineLength = 40;
    	double maxLineGap = 20;
    	cv::HoughLinesP(ROI_image, plines, rho, theta, threshold, minLineLength, maxLineGap);
    	for (size_t i = 0; i < plines.size(); i++)
    	{
    		//Vec4f point1 = plines[i];
    		//cv::line(Hough_image, Point(point1[0], point1[1]), Point(point1[2], point1[3]), Scalar(255, 255, 0), 1, LINE_AA);
    
    		cv::line(Hough_image, Point(plines[i][0], plines[i][1]),
    		Point(plines[i][2], plines[i][3]), Scalar(255, 255, 0), 1, 8);
    	}
Hough_image

7. 直线拟合

复制代码
    Mat draw_lines(Mat& img, vector<Vec4f>  lines)
    {
    	Mat line_image = img.clone();
    	vector<Point> PointsL, PointsR;
    	cv::Vec4f lineL, lineR;
    	int y_min = img.size().height;
    	int y_max = 0;
    	for (int i = 0; i < lines.size(); i++)
    	{
    		Vec4f point1 = lines[i];
    		double k;
    
    		if (point1[1] < point1[3])
    		{
    			if (point1[1] < y_min)
    			{
    				y_min = point1[1];
    			}
    			if (point1[3] > y_max)
    			{
    				y_max = point1[3];
    			}
    			k=(point1[3]-point1[1])/(point1[2]-point1[0]);
    			if (k>0.3&&k<2.0)
    			{
    				PointsR.push_back(Point(point1[0], point1[1]));
    				PointsR.push_back(Point(point1[2], point1[3]));
    
    			}
    			else if(k<-0.3&&k>-2.0)
    			{
    				PointsL.push_back(Point(point1[0], point1[1]));
    				PointsL.push_back(Point(point1[2], point1[3]));
    			}
    		}
    		else
    		{
    			if (point1[3] < y_min)
    			{
    				y_min = point1[3];
    			}
    			if (point1[1] > y_max)
    			{
    				y_max = point1[1];
    			}
    			k=(point1[3]-point1[1])/(point1[2]-point1[0]);
    			if (k>0.3&&k<2.0)
    			{
    				PointsR.push_back(Point(point1[0], point1[1]));
    				PointsR.push_back(Point(point1[2], point1[3]));
    			}
    			else if(k<-0.3&&k>-2.0)
    			{
    				PointsL.push_back(Point(point1[0], point1[1]));
    				PointsL.push_back(Point(point1[2], point1[3]));
    			}
    		}
    		
    	}
    	//直线拟合
    	cv::fitLine(PointsL, lineL, DIST_HUBER, 0, 1e-2, 1e-2);
    	cv::fitLine(PointsR, lineR, DIST_HUBER, 0, 1e-2, 1e-2);
    
    	double k1,k2,b1,b2;
    	cv::Point pointL1,pointL2, pointR1, pointR2;
    	k1 = lineL[1] / lineL[0];
    	b1 = lineL[3] - k1 * lineL[2];
    	k2 = lineR[1] / lineR[0];
    	b2 = lineR[3] - k2 * lineR[2];
    	pointL1.x = (y_min - b1) / k1;
    	pointL1.y = y_min;
    	pointL2.x = (y_max - b1) / k1;
    	pointL2.y = y_max;
    	pointR1.x = (y_min - b2) / k2;
    	pointR1.y = y_min;
    	pointR2.x = (y_max - b2) / k2;
    	pointR2.y = y_max;
    	cv::line(line_image, Point(pointL1.x, pointL1.y), Point(pointL2.x, pointL2.y), Scalar(0, 0, 255), 2, LINE_AA);
    	cv::line(line_image, Point(pointR1.x, pointR1.y), Point(pointR2.x, pointR2.y), Scalar(255, 0, 0), 2, LINE_AA);
    	//cv::imshow("line_image", line_image);
    	return line_image;
    }
    
    	Mat lane_image;
    	lane_image=draw_lines(image, plines);
lane_image

参考

udacity/CarND-LaneLines-P1
无人驾驶技术入门(十四)| 初识图像之初级车道线检测
OpenCV-4.5.0 Docs

全部评论 (0)

还没有任何评论哟~