ubuntu20+dlib19.22+0pencv4.5.0的机器视觉算法之路(四)

基于Opencv的车道线识别算法

关于基于opencv的车道线识别,网上很多教程,有些不是特别详细,这里实战了一下,顺便给大家分享。

关键步骤:
1.从视频中读取图像

cap.read(img)

2.高斯降噪

img = lane.noise(img); 

3.转为灰度图并二值化

cv::cvtColor(noise_img, output_img, cv::COLOR_RGB2GRAY);
cv::threshold(output_img, output_img, 120, 256, cv::THRESH_BINARY);

4.卷积

cv::Matx33f kernel(-1, 0, 1, -2, 0, 2, -1, 0, 1);
cv::filter2D(output_img, output_img, output_img.depth(), kernel);

这里的卷积核为3X3, 为
-1,0, 1
-2,0, 2
-1, 0, 1
作用是:过滤大白区域,进行细化,这里主要是天空,我们可以做个比较上图是卷积前的,下图是卷积后的。
在这里插入图片描述
在这里插入图片描述

5.ROI,过滤掉不用的区域, 获取感兴趣的兴趣

cv::Mat ROI_img;
cv::Mat mask = cv::Mat::zeros(edge_img.size(), edge_img.type());
cv::Point pts[4] = {
cv::Point(210, 720), cv::Point(550, 450), cv::Point(717, 450), cv::Point(1280, 720)};
cv::fillConvexPoly(mask, pts, 4, cv::Scalar(255, 0, 0));
cv::bitwise_and(edge_img, mask, ROI_img);

长这样:
在这里插入图片描述

6.利用霍夫变换(原理网上很多教程),这里是为了再图片中找出线段

std::vector<cv::Vec4i> lines; //定义此vector的名字为lines
cv::HoughLinesP(ROI_img, lines, 1, CV_PI / 180, 20, 20, 30);

s t d : : v e c t o r < c v : : V e c 4 i > std::vector<cv::Vec4i> std::vector<cv::Vec4i>,HoughLinesP函数存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。

7.如果找到线段不为空,则对检测的线段进行过滤
按照斜率、端点与图中心的位置来进行剔除,分类(分为左车道线还是右边车道线)

std::vector<std::vector<cv::Vec4i>> Lane::possibleline(std::vector<cv::Vec4i> lines, cv::Mat ROI_img)
{
    //按照斜率、端点与图中心的位置来进行剔除,分类
    double slope_th = 0.3;         //设定斜率阈值
    double mid = ROI_img.cols / 2; //计算中点,记得cols 加s
    // std::cout << "mid:  " << mid << std::endl;
    std::vector<std::vector<cv::Vec4i>> out;
    std::vector<cv::Vec4i> left, right;
    std::vector<double> slope_ass;
    std::vector<cv::Vec4i> line;
    cv::Point ini, fin;
    int a = 0;
    //首先根据斜率剔除部分线条
    for (auto i : lines)
    { 
        ini = cv::Point(i[0], i[1]);
        fin = cv::Point(i[2], i[3]);
        double slope = (static_cast<double>(fin.y) - static_cast<double>(ini.y)) / (static_cast<double>(fin.x) - static_cast<double>(ini.x) + 0.00001);
        // std::cout << "slope:" << slope << std::endl;
        if (std::abs(slope) > slope_th)
        {
            a++;
            slope_ass.push_back(slope);
            line.push_back(i);
        }
    }
    //接下来对剩下的线条进行分类
    for (size_t i = 0; i < line.size(); i++)
    {
        ini = cv::Point(line[i][0], line[i][1]);
        fin = cv::Point(line[i][2], line[i][3]);
        if (slope_ass[i] < 0 && ini.x < mid && fin.x < mid)
        {
            // std::cout << "左" << std::endl;
            left.push_back(line[i]);
            left_flag = true;
        }
        else if (slope_ass[i] > 0 && ini.x > mid && fin.x > mid)
        {
            // std::cout << "右" << std::endl;
            right.push_back(line[i]);
            right_flag = true;
        }
    }
    out.push_back(left);
    out.push_back(right);
    return out;
}

其实好像也可以加根据长度条件进行筛选。

8.将所有分类线段的初始点和最终点提取出来,并使用最小二乘法从中拟合出一条新线
使用的函数是最小二乘法拟合 cv::DIST_L2

cv::fitLine(left, left_line, cv::DIST_L2, 0, 0.01, 0.01);

9.填充并,绘制车道线
填充效果:
在这里插入图片描述
然后将填充区域(绿色区域)和原图像进行融合:

cv::addWeighted(out_img, 0.3, input_img, 1.0 - 0.3, 0, input_img);

最后使用cv::line进行绘制

cv::line(input_img, output[0], output[1], cv::Scalar(0, 0, 255), 5);
cv::line(input_img, output[2], output[3], cv::Scalar(0, 0, 255), 5);

10.最终效果
在这里插入图片描述

这里说明一下,我是使用Cmake进行编译的C++工程。
完整工程文件:https://download.csdn.net/download/weixin_39735688/43146799

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏融化了这季节

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值