opencv识别车道线(霍夫线变换)

目录

  • 1、前言
  • 2、霍夫线变换
    • 2.1、霍夫线变换是什么?
    • 2.2、在opencv中的基本用法
      • 2.2.1、HoughLinesP函数定义
      • 2.2.2、用法
  • 3、识别车道
    • 3.1、优化
      • 3.1.1、降噪
      • 3.1.2、过滤方向
      • 3.1.3、截选区域
    • 3.2、测试其它图片
      • 3.2.1、代码
      • 3.2.2、图片1
      • 3.2.3、图片2
      • 3.2.4、图片3


1、前言

最近学习opencv学到了霍夫线变换,霍夫线变换是一个查找图像中直线的算法,它的其中一种应用场景就是识别车道,本文以识别车道为例,介绍霍夫线的简单用法。

2、霍夫线变换

2.1、霍夫线变换是什么?

下面是chatGPT给出的说明:

霍夫线变换(Hough Line Transform)是一种图像处理技术,可以用于检测图像中的直线。它的基本思想是,将直线转换为参数空间,并在参数空间中寻找与图像中的边缘相对应的点,从而找到这些直线。霍夫线变换常用于计算机视觉领域,例如在车道线检测、图像拼接、人脸识别等方面应用广泛。

原理性的东西这里不讲,因为有点复杂,我看得也有点懵。

2.2、在opencv中的基本用法

2.2.1、HoughLinesP函数定义

opencv实现霍夫线变换的函数是HoughLinesP,它的定义如下。

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

它的参数的含义如下:
image:8位、单通道二进制源图像。
lines:输出线的矢量。每条线由一个4元素矢量表示,可以传入vector< cv::Vec4i>类型。
控制精度:
rho:累加器的距离分辨率(以像素为单位)。
theta:累加器的角度分辨率(弧度)。
过滤:
threshold:累加器阈值参数。
minLineLength:最小行长度。小于该长度的线段将被拒绝。
maxLineGap:同一条线上链接点的最大允许间隙。

2.2.2、用法

因为HoughLinesP传入的图像必须是8位、单通道二进制源图像,所以在传入图像之前,需要做转灰度图-》转二进制图的操作。
opencv提供了一些转二进制图的方法,因为HoughLinesP的目的是找到直线,而直线其实也是轮廓的一部分,所以一般我们采用Canny算法来把灰度图转为二进制图。
例程:

#include 
#include 
#include 
#include 

using namespace cv;

int main() {
    Mat src = imread("road.png");
    imshow("src", src);
    
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    imshow("gray", gray);

    // Apply Canny edge detection
    Mat edges;
    Canny(gray, edges, 50, 150);
    imshow("canny", edges);

    // Perform Hough transform to find lines
    std::vector<Vec4i> lines;
    HoughLinesP(gray, lines, 1, CV_PI / 180, 50, 50, 10);

    // Draw lines on output image
    Mat dst = src.clone();
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i vline = lines[i];
        line(dst, Point(vline[0], vline[1]), Point(vline[2], vline[3]), Scalar(0, 0, 255), 2);
    }
    imshow("dst", dst);

    waitKey(0);

}

3、识别车道

首先准备一张图片,如下图所示,要识别出它的白色车道线。

我们直接使用上一节的例程,效果如下。
opencv识别车道线(霍夫线变换)_第1张图片
发现虽然车道是识别出来了,但是环境中的纹理也被误认为车道,所以要做进一步优化。

3.1、优化

3.1.1、降噪

从上面的Canny图可以看到,环境中的树木形成了密密麻麻的纹理,这些就是影响效果的因素之一。
经过测试,我选用了“二值化 - 》腐蚀 - 》膨胀”的方式来完成降噪,经过优化后的代码如下:

#include 
#include 
#include 
#include 

using namespace cv;

int main() {
    Mat src = imread("/road.png");
    imshow("src", src);

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    imshow("gray", gray);

	//二值化
	Mat thr;
    threshold(gray, thr, 100, 255, THRESH_BINARY);
    imshow("threshold", thr);

    // 腐蚀
    Mat eroded;
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
    erode(thr, eroded, element);

    // 膨胀
    Mat dilated;
    dilate(eroded, dilated, element);
    imshow("dilated", dilated);

    // Apply Canny edge detection
    Mat edges;
    Canny(dilated, edges, 50, 150);
    imshow("canny", edges);

    // Perform Hough transform to find lines
    std::vector<Vec4i> lines;
    HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10);

    // Draw lines on output image
    Mat dst = src.clone();
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i vline = lines[i];
        line(dst, Point(vline[0], vline[1]), Point(vline[2], vline[3]), Scalar(0, 0, 255), 2);
    }

    imshow("dst", dst);

    waitKey(0);
}

优化后的效果如下:
opencv识别车道线(霍夫线变换)_第2张图片
从Canny中明显可以看到环境纹理少了很多。

3.1.2、过滤方向

在上图中,可以看到还有一些横向的纹理影响了效果,我们可以通过直线的方向来做进一步过滤。
在车的视角下,车道是朝中间斜的,两边车道成八字型,如图所示。
在这里插入图片描述
也就是说,车道的线在图像上倾斜角度不会小,所以我们可以在得出最终结果时,添加一个过滤条件:倾斜角度小于20度的直线不满足条件。
修改代码如下:

.....
    // Perform Hough transform to find lines
    std::vector<Vec4i> lines;
    HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10);

    // Draw lines on output image
    Mat dst = src.clone();
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i vline = lines[i];
		/* 过滤倾斜45度及以下的斜线 */
        float tanVal = (float)(vline[3] - vline[1]) / (vline[2] - vline[0]);
        if (abs(tanVal) < tan(CV_PI / 18)) continue;

        line(dst, Point(vline[0], vline[1]), Point(vline[2], vline[3]), Scalar(0, 0, 255), 2);
    }
.......

效果:

3.1.3、截选区域

在识别车道时,因为车道是在车的脚下,需要识别的图像只有相机拍下的下半截,所以这里还可以加多一层优化:把上半截图像砍掉,只处理下半截图像。
修改代码:

int main() {
    Mat src = imread("road.png");
    Rect vaildRect(0, src.rows / 2, src.cols, src.rows / 2);
    Mat src = src(vaildRect);
    imshow("src", src);
......

效果:
opencv识别车道线(霍夫线变换)_第3张图片

3.2、测试其它图片

3.2.1、代码

经过前面的优化后,得到如下代码:

int main() {    
	Mat src = imread("road.png");
    Rect vaildRect(0, src.rows / 2, src.cols, src.rows / 2);
    src = src(vaildRect);
    imshow("src", src);

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    imshow("gray", gray);

	Mat thr;
    threshold(gray, thr, 150, 255, THRESH_BINARY);
    imshow("threshold", thr);

    // 腐蚀
    Mat eroded;
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
    erode(thr, eroded, element);

    // 膨胀
    Mat dilated;
    dilate(eroded, dilated, element);
    imshow("dilated", dilated);

    // Apply Canny edge detection
    Mat edges;
    Canny(dilated, edges, 50, 150);
    imshow("canny", edges);

    // Perform Hough transform to find lines
    std::vector<Vec4i> lines;
    HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10);

    // Draw lines on output image
    Mat dst = src.clone();
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i vline = lines[i];
        float tanVal = (float)(vline[3] - vline[1]) / (vline[2] - vline[0]);
        if (abs(tanVal) < tan(CV_PI / 18)) {
            continue;
        }

        line(dst, Point(vline[0], vline[1]), Point(vline[2], vline[3]), Scalar(0, 0, 255), 2);
    }

    imshow("dst", dst);

    waitKey(0);
}

下面用这份代码测试其它例子。

3.2.2、图片1

因为拍照时的亮度不一,所以需要根据亮度来调整二值化时的阀值,此例用的是
threshold(gray, thr, 170, 255, THRESH_BINARY);
opencv识别车道线(霍夫线变换)_第4张图片

3.2.3、图片2

opencv识别车道线(霍夫线变换)_第5张图片

3.2.4、图片3

threshold(gray, gray, 150, 255, THRESH_BINARY);

你可能感兴趣的:(C++,Opencv,opencv,计算机视觉,图像处理,车道识别,c++)