<h1 style="text-align: center"> 车道线检测 (Lane Detection)</h1>
## 1、实验内容
本实验使用数字图像处理的基本方法,构建了一个车道线检测模型。该模型可以识别图像中所有的车道线,并得到完整的车道线信息。模型在tuSimple Lane Dataset大小为100的数据子集进行了测试,达到了较好的结果。
## 2、实现思路
实现车道线检测,主要包含两部分操作
1. 道路图像的处理,主要包括灰度图转换、基于高斯平滑的图像去噪、基于Canny算法的边缘提取
2. 车道线检测方法,主要包括获取感兴趣区域(ROI)、形态学闭运算、基于Hough变换的直线检测
模型的处理流程如下,
<img src="./result/image_of_readme/img.png" alt="image-20210214120515919" style="zoom:80%;" />
### 2.1 道路图像处理
通过对道路图像进行处理,突出图像中的车道线部分。模型将彩色图像转化成灰度图像进行处理,目的是简化模型的复杂度,提高运行效率。
#### 2.1.1 高斯平滑
由于光照、路面情况、拍摄质量等问题,道路图像上存在很多噪声,通过高斯滤波使图像变得平滑,减弱图像中的噪声对结果的影响,提高车道线检测模型的鲁棒性。
高斯平滑就是使用高斯滤波器与原图像进行卷积,得到平滑图像。与均值滤波类似,它们都是取滤波器窗口内像素的加权均值作为输出。高斯滤波器的权值分布满足二维高斯函数。
![](https://latex.codecogs.com/gif.latex?h(x,y)=e^{-\frac{x^2+y^2}{2\sigma^2}})
由于高斯平滑是线性离散滤波,因此离散形式的高斯滤波器为
![](https://latex.codecogs.com/gif.latex?H_{i,j}&space;=&space;\frac{1}{2&space;\pi&space;\sigma&space;^&space;2}e&space;^{-\frac{(i&space;-&space;k&space;-&space;1)^2&space;+&space;(j&space;-&space;k&space;-&space;1)^2}{2&space;\sigma&space;^&space;2}})
本实验采用 $3\times3$ 的高斯滤波器。具体实现为定义 `Kernel` 类实现通用的卷积操作,定义派生类 `GaussianKernel` 实现不同 size 和 $\sigma$ 高斯滤波器的构建的运算,实现接口如下:
```c++
/* Kernel.h */
class Kernel
{
public:
double **data;
int size;
Kernel(int size); // 空的卷积核
Kernel(Kernel &cp); // 拷贝构造函数
~Kernel();
double *operator[](const int idx) const;
// 卷积操作
template<typename T1, typename T2>
void convolve(const Img<T1> &src, Img<T2> &dst, const bool is_clip = true) const;
};
class GaussianKernel : public Kernel
{
public:
double sigma;
GaussianKernel(const int size, const double sigma);
GaussianKernel(GaussianKernel &cp);
};
```
#### 2.1.2 边缘提取
在实验过程中,我曾尝试采用以下方法进行边缘提取的方法。由于在图像中车道线的灰度值较大,因此我设计了一种参数自适应的阈值分割算法,把车道线从图像中抽取出来。具体方法如下:统计图像的灰度分布,选取整体灰度分布相应比例对应的灰度值作为阈值,对图像进行二值化。效果如下:
![阈值分割效果](./result/image_of_readme/binary.png)
可以发现,通过阈值分割有效的过滤掉了大部分背景,如山脉、路面、车辆,这为下面的直线检测去除了一定的干扰。但是由于部分图像存在反光或较亮区域,这导致一些车道线丢失,或特征不再明显,如下图。
![阈值分割导致车道线信息丢失](./result/image_of_readme/binary_failure.png)
虽然可以通过求图像梯度的方法将大面积的高亮度区域滤除,但是直接将原图转成二值图像处理,会丢失车道线的细节信息导致结果车道线信息不完整。因此舍弃该方案。
最终采用基于图像梯度的边缘提取方法——Canny算法。Canny主要包含三个步骤:
1. Sobel算子:计算图像梯度
2. 非极大值抑制:去除非边缘的噪点,细化边缘
3. 双阈值:检测并连接边缘
(1)Sobel 算子计算图像梯度
灰度图可以看做灰度值 $h(x,y)$ 关于 $(x,y)$ 坐标的二元函数,计算图像梯度可以通过Sobel算子计算得到。
* $x$ 方向梯度: ${grad}_x(x,y) = \frac{\partial h(x,y)}{\partial x}$
* $y$ 方向梯度: ${grad}_y(x,y) = \frac{\partial h(x,y)}{\partial x}$
* 梯度幅度: $grad = \sqrt{{grad_x}^2 + {gard_y}^2}$
* 梯度方向:$gard_\theta = arctan(\frac{grad_y}{grad_x})$
其中计算 $x,y$ 方向的梯度使用Sobel算子对图像进行卷积
<img src="https://latex.codecogs.com/png.latex?grad_x&space;=&space;\begin{bmatrix}&space;-1&space;&&space;0&space;&&space;1&space;\\&space;-2&space;&&space;0&space;&&space;2&space;\\&space;-1&space;&&space;0&space;&&space;1&space;\end{bmatrix}&space;\times&space;img&space;\quad&space;grad_y&space;=&space;\begin{bmatrix}&space;1&space;&&space;2&space;&&space;1&space;\\&space;0&space;&&space;0&space;&&space;0&space;\\&space;-1&space;&&space;-2&space;&&space;-1&space;\end{bmatrix}&space;\times&space;img" title="grad_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix} \times img \quad grad_y = \begin{bmatrix} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{bmatrix} \times img" />
Sobel 算子计算梯度效果如下:
![Sobel计算图像梯度](./result/image_of_readme/Sobel.png)
(2)非极大值抑制
分析上图发现,由于图像灰度存在起伏,所以有一些不是边缘的区域也存在较大的梯度。采用非极大值抑制(NMS)的方法,消除梯度图像中非边缘的噪声,并将边缘细化。
NMS实现的思路如下:计算每个中心像素点沿梯度方向邻域内各点的梯度值,如果该中心像素点的梯度值是以上像素点梯度值的局部极大值,则保留梯度,否则梯度置为零。由于邻域内在梯度方向上的点不一定是在整数坐标位置,因此需要通过插值计算邻域内梯度方向点的梯度值。实现效果如下:
![非极大值抑制效果](./result/image_of_readme/nms.png)
一些非边缘的噪点得到了一定程度的抑制,边缘也得到细化。
(3)双阈值检测和边缘连接
需要将得到的梯度图像进行阈值分割,得到二值图以便后续进行hough变换。采用双阈值对图像进行阈值分割,实现思路如下:
* 当梯度值大于高阈值时,将其灰度值取为255。
* 当梯度值小于低阈值时,将其灰度值取为0。
* 当梯度介于两者之间是,如果该点邻域内有高阈值点,则取为255,否则取0。
双阈值处理中,高阈值将物体边缘和背景区分开,但是当高阈值较大时,可能导致边缘断断续续;此时低阈值平滑边缘轮廓,能实现较好的分割效果。同时借鉴之前尝试对灰度图做阈值分割的思路,采用整体灰度分布相应比例处的灰度值为高阈值,低阈值取高阈值的 $\frac{2}{3}$,实现效果如下:
![双阈值效果](./result/image_of_readme/canny.png)
Canny 边缘提取的实现接口如下:
```c++
#ifndef LANE_DETECTION_EDGE_DETECTION_H
#define LANE_DETECTION_EDGE_DETECTION_H
#include "Img.hpp"
#include "Kernel.h"
// 阈值分割
void TurnBinary(Img<uchar> &src, const double weight);
// 膨胀运算
void Dilation(const Img<uchar> &src, Img<uchar> &dst, int kernel_size);
// 腐蚀运算
void Erosion(const Img<uchar> &src, Img<uchar> &dst, int kernel_size);
// 遮盖无效部分
void RoiMask(Img<uchar> &src);
// Sobel 算子计算梯度
void Sobel(const Img<uchar> &src, Img<uchar> &dst, Img<double> &theta);
// 非极大值抑制
void NonMaxSuppression(const Img<uchar> &src, Img<uchar> &dst, const Img<double> &theta);
// 双阈值处理
void DoubleThreshold(Img<uchar> &image, const double weight = 0.9);
// Canny 边缘检测
void Canny(Img<uchar> &image, const double weight = 0.9);
#endif //LANE_DETECTION_EDGE_DETECTION_H
```
### 2.2 车道线检测
#### 2.2
辣椒种子
- 粉丝: 4275
- 资源: 5837
最新资源
- 氢燃料电池液态水仿真 液态水质量源为水蒸气的冷凝,可以解析出阴极催化层、扩散层及流道内部的液态水体积分数分布
- Java毕设项目:基于spring+mybatis+mysql实现的员工信息管理系统【含源码+数据库+毕业论文】
- 配电网单相接地故障模型,MATLAB2022a模型 可以进行单相接地故障仿真,两相接地故障,三相接地故障仿真等
- Java毕设项目:基于spring+mybatis+mysql实现的学生信息管理系统【含源码+数据库+毕业论文】
- 铁路轨道缺陷数据集,4278张原始图片,支持YOLOV8格式的标注,可识别是否有裂缝,间隙缺陷, 图片和标注信息可参考:https://backend.blog.csdn.net/article/de
- 铁路轨道缺陷数据集,4278张原始图片,支持YOLOV9格式的标注,可识别是否有裂缝,间隙缺陷, 图片和标注信息可参考:https://mp.csdn.net/mp-blog/creation/edi
- 编程喵JS调试工具.编程喵JS调试工具.zip
- Comsol冻土水热力,本案例物采用两个PDE模块,分别表示水分场和温度场,一个固体力学模块,表示应力场,求解器在求解THM问题中采用瞬态求解器 在求解应力问题中,采用稳态求解器 通过本案例可以学
- SVPWM+死区补偿(基于电流极性)+高频注入法辨识PMSM的dq轴电感(离线辨识) 1.模型的中的电机,为采用自建的电机模型 2.适用于spmsm和ipmsm, 3.基于两相静止坐标轴电压注入,可通
- 铁路轨道缺陷数据集,4278张原始图片,支持YOLOV7格式的标注,可识别是否有裂缝,间隙缺陷, 图片和标注信息可参考:https://backend.blog.csdn.net/article/de
- java的概要介绍与分析
- 报告类的概要介绍与分析
- 线程-3-线程控制ooo-
- vscode的概要介绍与分析
- 基于MMC的同步发电机控制策略研究 基于MMC的同步发电机(Vitual Synchronous Generator based Modular Multilevel Converter, MMC-V
- 铁路轨道缺陷数据集,4278张原始图片,支持PASICAL VOC XML格式的标注,可识别是否有裂缝,间隙缺陷, 图片和标注信息可参考:https://backend.blog.csdn.net/a
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈