项目地址:https://github.com/georgesung/advanced_lane_detection
文章目录
- 技术路线
- 相机校准
- 车道检测流程
- 图像不失真
- 阈值二值图像
- 透视变换
- 多项式拟合
- 曲率半径
- 车辆偏离车道中心
- 用车道区域注释原始图像
- 讨论
技术路线
检测车道线使用计算机视觉技巧
车道线检测步骤如下:
- 给定一组棋盘图像,计算相机校准矩阵和失真系数。
- 对原始图像应用失真校正。
- 使用颜色变换、渐变等来创建阈值二值图像。
- 应用透视变换来校正二值图像(“鸟瞰图”)
- 检测车道像素并拟合以找到车道边界。
- 确定车道的曲率和车辆相对于中心的位置。
- 将检测到的车道边界变形回原始图像。
- 输出车道边界的视觉显示以及车道曲率和车辆位置的数值估计。
相机校准
使用“camera_cal/*.jpg”中的棋盘图像校准相机。对每个校准图像执行以下步骤:
- 转换为灰度
- 使用 OpenCV 的findChessboardCorners()函数查找棋盘角,假设棋盘为 9x6
在对所有校准图像执行上述步骤后,我使用 OpenCV 的calibrateCamera()函数来计算失真矩阵。使用失真矩阵,我使用 OpenCV 的undistort()函数对图像进行失真处理。
为了说明,以下是校准图像“camera_cal/calibration5.jpg”:
这是通过相机校准未失真的相同图像:
最终校准矩阵保存在pickle文件“calibrate_camera.p”中
车道检测流程
下面描述和说明了车道检测中涉及的步骤。为了说明,下面是我们将用作示例的原始图像:
图像不失真
使用“calibrate_camera.p”中的相机校准矩阵,我对输入图像进行了失真处理。下面是上面的示例图像,未失真:
执行相机校准的代码在“calibrate_camera.py”中。对于“test_images/.jpg”中的所有图像,该图像的未失真版本保存在“output_images/undistort_.png”中。
阈值二值图像
下一步是创建一个阈值化的二值图像,将未失真的图像作为输入。目标是识别可能是车道线一部分的像素。特别是,我执行以下操作:
- 应用以下带有阈值的过滤器,以创建对应于每个单独过滤器的单独“二进制图像
- 图像上的绝对水平 Sobel 算子
- 水平和垂直方向的Sobel算子并计算其大小
- Sobel算子计算梯度方向
- 将图像从RGB空间转换为HLS空间,并对S通道进行阈值处理
- 将上述二值图像组合起来,创建最终的二值图像
这是示例图像,通过组合上述阈值二值滤波器转换为二值图像:
生成阈值二进制图像的代码在“combined_thresh.py”中,特别是函数combined_thresh(). 对于“test_images/.jpg”中的所有图像,该图像的阈值二进制版本保存在“output_images/binary_.png”中。
透视变换
给定阈值二值图像,下一步是执行透视变换。目标是转换图像,以便我们获得车道的“鸟瞰图”,这使我们能够将曲线拟合到车道线(例如多项式拟合)。这完成的另一件事是“裁剪”原始图像中最有可能具有车道线像素的区域。
为了完成透视变换,我使用了 OpenCVgetPerspectiveTransform()和warpPerspective()函数。我对透视变换的源点和目标点进行了硬编码。源点和目标点是通过人工检查在视觉上确定的,尽管一个重要的改进是通过算法确定这些点。
这是应用透视变换后的示例图像:
执行透视变换的代码在“perspective_transform.py”中,特别是函数perspective_transform(). 对于“test_images/.jpg”中的所有图像,该图像的变形版本(即透视变换后)保存在“output_images/warped_.png”中。
多项式拟合
给定上一步中扭曲的二值图像,我现在将二阶多项式拟合到左右车道线。特别是,我执行以下操作:
- 计算图像下半部分的直方图
- 将图像分成 9 个水平切片
- 从底部切片开始,在直方图的左峰和右峰周围围上一个 200 像素宽的窗口(将直方图垂直分成两半)
- 向上水平窗口切片以找到可能属于左右车道一部分的像素,机会性地重新调整滑动窗口
- 给定2组像素(左右车道线候选像素),对每组拟合一个二阶多项式,代表估计的左右车道线
执行上述操作的代码在line_fit()'line_fit.py’的函数中。
由于我们的目标是从视频流中找到车道线,我们可以利用视频帧之间的时间相关性。
给定从前一个视频帧计算的多项式拟合,我实现的一个性能增强是从以前预测的车道线水平搜索 +/- 100 个像素。然后我们简单地对从我们的快速搜索中找到的那些像素执行二阶多项式拟合。如果我们没有找到足够的像素,我们可以返回一个错误(例如return None),并且函数的调用者将忽略当前帧(即保持车道线相同)并确保对下一帧执行完整搜索。总体而言,这将提高车道检测器的速度,如果我们要在量产的自动驾驶汽车中使用该检测器,这将非常有用。执行缩写搜索的代码在tune_fit()“line_fit.py”函数中。
利用时间相关性的另一个改进是平滑多项式拟合参数。这样做的好处是使检测器对噪声输入更加鲁棒。我对最近的 5 个视频帧使用了多项式系数的简单移动平均值(每条车道线 3 个值)。执行此平滑的代码位于文件“Line.py”add_fit()中的类的函数中Line。该Line班被用作此平滑功能明确一个帮手,而Line实例是在“line_fit.py”全局对象。
下面是我们原始示例图像的多项式拟合输出的说明。对于“test_images/.jpg”中的所有图像,该图像的多项式拟合注释版本保存在“output_images/polyfit_.png”中。
曲率半径
鉴于左右车道线的多项式拟合,我根据此处提供的公式计算了每条线的曲率半径。我还将距离单位从像素转换为米,假设垂直方向每 720 个像素 30 米,水平方向每 700 个像素 3.7 米。
最后,我平均了左右车道线的曲率半径,并在最终视频的注释中报告了这个值。
计算曲率半径的代码calc_curve()在’line_fit.py’中的函数中。
车辆偏离车道中心
鉴于左右车道线的多项式拟合,我计算了车辆与车道中心的偏移。最终视频中标注了车辆与中心的偏移。在从像素转换为米时,我做了与以前相同的假设。
为了计算车辆与车道线中心的偏移,我假设车辆的中心是图像的中心。我将车道的中心计算为左侧车道线底部 x 值和右侧车道线底部 x 值的平均 x 值。偏移量只是车辆的中心 x 值(即图像的中心 x 值)减去车道的中心 x 值。
计算车辆车道偏移的代码calc_vehicle_offset()位于“line_fit.py”中的函数中。
用车道区域注释原始图像
鉴于上述所有内容,我们可以使用车道区域以及有关车道曲率和车辆偏移的信息来注释原始图像。以下是执行此操作的步骤:
- 创建一个空白图像,并绘制我们的 polyfit 线(估计左右车道线)
- 填充线条之间的区域(绿色)
- 使用从透视变换计算的逆扭曲矩阵,“解开”上面的矩阵,使其与原始图像的透视对齐
- 将上面的注解叠加在原图上
- 向原始图像添加文本以显示车道曲率和车辆偏移
执行上述操作的代码final_viz()位于“line_fit.py”中的函数中。
以下是我们原始图像的最终注释版本。对于“test_images/.jpg”中的所有图像,该图像的最终注释版本保存在“output_images/annotated_.png”中。
讨论
这是基于计算机视觉的高级车道查找的初始版本。在多种情况下,此车道查找器不起作用。例如,优达学城挑战视频包括有裂缝的道路,这些裂缝可能被误认为是车道线(参见“challenge_video.mp4”)。此外,前面的其他车辆可能会欺骗车道查找器,使其认为它是车道的一部分。可以做更多的工作来使车道检测器更加健壮,例如基于深度学习的语义分割以找到可能是车道标记的像素(然后仅对这些像素执行 polyfit)。