OpenCV实战(二)车道检测

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

车道检测案例,参考诸多csdn博主文章,得以写出,还有一些缺陷,希望大家可以改进并提出意见。

一、介绍

直接引入代码,部分注释在程序中解释。

二、具体实现

1.quickdemo.cpp:

#include "quickdemo.h"
#include <math.h>

#define _USE_MATH_DEFINES
#define M_PI 3.14159265358

//灰度化处理
//高斯平滑滤波降噪
void QuickDemo::gry_Demo(Mat& frame)
{
	//定义图像容器
	Mat gryMat;

    Point left_line[2];
    Point right_line[2];

	//修改图片尺寸大小
	double scale = 0.5;
	Size ResImgSiz = Size(frame.cols * scale, frame.rows * scale);
	Mat rFrame = Mat(ResImgSiz, frame.type());
	resize(frame, rFrame, ResImgSiz, INTER_LINEAR);
	//将原图转化为gry类型的图片
	cvtColor(rFrame, gryMat, COLOR_BGR2GRAY);
	imshow("gryMat", gryMat);//灰度化后的视频
	//imshow("frame", rFrame);//显示原视频

    Mat gauss_img;
	GaussianBlur(gryMat, gauss_img, Size(3, 3), 5, 5);
	//imshow("GausMat", gauss_img);//高斯模糊后的视频


    // 用一阶偏导有限差分计算梯度幅值和方向
    Mat gradXY, theta;
    getGrandient(gauss_img, gradXY, theta);

    // 局部非极大值抑制
    Mat local_img;
    nonLocalMaxValue(gradXY, theta, local_img);

    // 用双阈值算法检测和连接边缘
    Mat canny_img;
    doubleThreshold(40, 80, local_img, canny_img);

    //imshow("canny_img", canny_img);//显示检测的线条

    Mat roi_img;
    GetROI(canny_img, roi_img);
    imshow("roi_img", roi_img);
 
    Mat result = fitLines(roi_img, left_line, right_line);
    imshow("result", result);//检测到的车道线  

    Mat result_img;
    addWeighted(rFrame, 0.8, result, 0.5, 0, result_img);
    imshow("lane-lines", result_img);//最终车道线拟合
}

/*
手动选择梯形rio区域
*/
void QuickDemo::GetROI(Mat src, Mat& image)
{
    Mat mask = Mat::zeros(src.size(), src.type());
    int width = src.cols;
    int height = src.rows;

    //获取车道ROI区域,只对该部分进行处理
    vector<Point>pts;
    Point ptA((width / 11) * 4, (height / 20) * 20);
    Point ptB((width / 9) * 5, (height / 11) * 5);
    Point ptC((width / 11) * 5, (height / 9) * 4);
    Point ptD((width / 8) * 6, (height / 20) * 20);
    pts = { ptA ,ptB,ptC,ptD };

    fillPoly(mask, pts, Scalar::all(255));
    src.copyTo(image, mask);
}


/*
通过计算拟合直线
*/
Mat QuickDemo::fitLines(Mat& image, Point* left_line, Point* right_line) {
    int height = image.rows;
    int width = image.cols;

    Mat out = Mat::zeros(image.size(), CV_8UC3);

    int cx = width / 2;
    int cy = height / 2;

    vector<Point> left_pts;
    vector<Point> right_pts;
    Vec4f left;

    for (int i = 100; i < (cx - 10); i++)
    {
        for (int j = cy; j < height; j++)
        {
            int pv = image.at<uchar>(j, i);
            if (pv == 255)
            {
                left_pts.push_back(Point(i, j));
            }
        }
    }

    for (int i = cx; i < (width - 20); i++)
    {
        for (int j = cy; j < height; j++)
        {
            int pv = image.at<uchar>(j, i);
            if (pv == 255)
            {
                right_pts.push_back(Point(i, j));
            }
        }
    }

    if (left_pts.size() > 2)
    {
        fitLine(left_pts, left, DIST_L1, 0, 0.01, 0.01);

        double k1 = left[1] / left[0];
        double step = left[3] - k1 * left[2];

        int x1 = int((height - step) / k1);
        int y2 = int((cx - 25) * k1 + step);

        Point left_spot_1 = Point(x1, height);
        Point left_spot_end = Point((cx - 25), y2);


        line(out, left_spot_1, left_spot_end, Scalar(0, 255, 255), 8, 8, 0);
        left_line[0] = left_spot_1;
        left_line[1] = left_spot_end;

    }
    else
    {
        line(out, left_line[0], left_line[1], Scalar(0, 255, 255), 8, 8, 0);
    }

    if (right_pts.size() > 2)
    {
        Point spot_1 = right_pts[0];
        Point spot_end = right_pts[right_pts.size() - 1];

        int x1 = spot_1.x;
        int y1 = spot_1.y;

        int x2 = spot_end.x;
        int y2 = spot_end.y;

        line(out, spot_1, spot_end, Scalar(0, 255, 255), 8, 8, 0);
        right_line[0] = spot_1;
        right_line[1] = spot_end;
    }
    else  {
        line(out, right_line[0], right_line[1], Scalar(0, 255, 255), 8, 8, 0);
    }
    return out;
}

/**
 用一阶偏导有限差分计算梯度幅值和方向
 img 输入原图像
 gradXY 输出的梯度幅值
 theta 输出的梯度方向
 */
void QuickDemo::getGrandient(Mat& img, Mat& gradXY, Mat& theta) {
    gradXY = Mat::zeros(img.size(), CV_8U);
    theta = Mat::zeros(img.size(), CV_8U);

    for (int j = 1; j < img.rows - 1; j++) {
        for (int i = 1; i < img.cols - 1; i++) {
            double gradY = double(img.ptr<uchar>(j - 1)[i - 1] + 2 * img.ptr<uchar>(j - 1)[i] + img.ptr<uchar>(j - 1)[i + 1] - img.ptr<uchar>(j + 1)[i - 1] - 2 * img.ptr<uchar>(j + 1)[i] - img.ptr<uchar>(j + 1)[i + 1]);
            double gradX = double(img.ptr<uchar>(j - 1)[i + 1] + 2 * img.ptr<uchar>(j)[i + 1] + img.ptr<uchar>(j + 1)[i + 1] - img.ptr<uchar>(j - 1)[i - 1] - 2 * img.ptr<uchar>(j)[i - 1] - img.ptr<uchar>(j + 1)[i - 1]);

            gradXY.ptr<uchar>(j)[i] = sqrt(gradX * gradX + gradY * gradY); //计算梯度
            theta.ptr<uchar>(j)[i] = atan(gradY / gradX); //计算梯度方向
        }
    }
}


/**
 局部非极大值抑制
 gradXY 输入的梯度幅值
 theta 输入的梯度方向
 dst 输出的经局部非极大值抑制后的图像
 */
void QuickDemo::nonLocalMaxValue(Mat& gradXY, Mat& theta, Mat& dst) {
    dst = gradXY.clone();
    for (int j = 1; j < gradXY.rows - 1; j++) {
        for (int i = 1; i < gradXY.cols - 1; i++) {
            double t = double(theta.ptr<uchar>(j)[i]);
            double g = double(dst.ptr<uchar>(j)[i]);
            if (g == 0.0) {
                continue;
            }
            double g0, g1;
            if ((t >= -(3 * M_PI / 8)) && (t < -(M_PI / 8))) {
                g0 = double(dst.ptr<uchar>(j - 1)[i - 1]);
                g1 = double(dst.ptr<uchar>(j + 1)[i + 1]);
            }
            else if ((t >= -(M_PI / 8)) && (t < M_PI / 8)) {
                g0 = double(dst.ptr<uchar>(j)[i - 1]);
                g1 = double(dst.ptr<uchar>(j)[i + 1]);
            }
            else if ((t >= M_PI / 8) && (t < 3 * M_PI / 8)) {
                g0 = double(dst.ptr<uchar>(j - 1)[i + 1]);
                g1 = double(dst.ptr<uchar>(j + 1)[i - 1]);
            }
            else {
                g0 = double(dst.ptr<uchar>(j - 1)[i]);
                g1 = double(dst.ptr<uchar>(j + 1)[i]);
            }

            if (g <= g0 || g <= g1) {
                dst.ptr<uchar>(j)[i] = 0.0;
            }
        }
    }
}


/**
 弱边缘点补充连接强边缘点
 img 弱边缘点补充连接强边缘点的输入和输出图像
 */
void QuickDemo::doubleThresholdLink(Mat& img) {
    // 循环找到强边缘点,把其领域内的弱边缘点变为强边缘点
    for (int j = 1; j < img.rows - 2; j++) {
        for (int i = 1; i < img.cols - 2; i++) {
            // 如果该点是强边缘点
            if (img.ptr<uchar>(j)[i] == 255) {
                // 遍历该强边缘点领域
                for (int m = -1; m < 1; m++) {
                    for (int n = -1; n < 1; n++) {
                        // 该点为弱边缘点(不是强边缘点,也不是被抑制的0点)
                        if (img.ptr<uchar>(j + m)[i + n] != 0 && img.ptr<uchar>(j + m)[i + n] != 255) {
                            img.ptr<uchar>(j + m)[i + n] = 255; //该弱边缘点补充为强边缘点
                        }
                    }
                }
            }
        }
    }

    for (int j = 0; j < img.rows - 1; j++) {
        for (int i = 0; i < img.cols - 1; i++) {
            // 如果该点依旧是弱边缘点,及此点是孤立边缘点
            if (img.ptr<uchar>(j)[i] != 255 && img.ptr<uchar>(j)[i] != 255) {
                img.ptr<uchar>(j)[i] = 0; //该孤立弱边缘点抑制
            }
        }
    }
}


/**
 用双阈值算法检测和连接边缘
 low 输入的低阈值
 high 输入的高阈值
 img 输入的原图像
 dst 输出的用双阈值算法检测和连接边缘后的图像
 */
void QuickDemo::doubleThreshold(double low, double high, Mat& img, Mat& dst) {
    dst = img.clone();

    // 区分出弱边缘点和强边缘点
    for (int j = 0; j < img.rows - 1; j++) {
        for (int i = 0; i < img.cols - 1; i++) {
            double x = double(dst.ptr<uchar>(j)[i]);
            // 像素点为强边缘点,置255
            if (x > high) {
                dst.ptr<uchar>(j)[i] = 255;
            }
            // 像素点置0,被抑制掉
            else if (x < low) {
                dst.ptr<uchar>(j)[i] = 0;
            }
        }
    }

    // 弱边缘点补充连接强边缘点
    doubleThresholdLink(dst);
}


2.quickdemo.h

代码如下(示例):

#pragma once
#include <opencv2/opencv.hpp> 
#include <opencv2/core/core.hpp> 
#include<opencv2/dnn.hpp>

using namespace std;
using namespace cv;


class QuickDemo {
public:
	void gry_Demo(Mat& frame);
	void getGrandient(Mat& img, Mat& gradXY, Mat& theta);
	void nonLocalMaxValue(Mat& gradXY, Mat& theta, Mat& dst);
	void doubleThresholdLink(Mat& img);
	void doubleThreshold(double low, double high, Mat& img, Mat& dst);
	void GetROI(Mat src, Mat& image);
	Mat fitLines(Mat& image, Point* left_line, Point* right_line);

};

主函数main.cpp

#include<iostream>
#include<opencv2/opencv.hpp>
#include "quickdemo.h"
using namespace std;
using namespace cv;


int main()
{
	Mat frame;

	QuickDemo qd;
	VideoCapture capture("D:\\Opencv项目\\素材\\车道检测\\carroad2.mp4");
	if (!capture.isOpened()){
		cout << "Can not open video file!" << endl;
		system("pause");
		return -1;
	}
	while (true)
	{

		char key = waitKey(20);
		if (key == 27)
		{
			break;
		}
		capture >> frame;
		qd.gry_Demo(frame);

	}
	capture.release();
	system("pause");
	return 0;
}



总结

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值