深度学习与图像处理 | 基于传统图像处理的自动驾驶车道线检测

简介: 本节介绍了基于OpenCV的传统图像处理算法在车道线检测中的应用,重点讲解了如何通过HSV颜色空间提取黄色和白色车道线、使用高斯模糊降噪、Canny算子提取边缘、感兴趣区域裁剪以及霍夫变换检测线段。最终通过对检测到的线段进行聚类与平均,得到代表左右车道线的直线,并实现车道线的可视化显示。该方法为自动驾驶小车提供了转向控制依据。

image.png


阐述了基于OpenCV的传统图像处理算法在车道线检测技术中的应用。

本节将使用传统图像处理算法检测车道线,然后根据车道线方向逐帧调整小车运行状态。这个过程涉及两个方面:感知和动作规划。感知部分主要通过车道线检测来实现,动作规划则通过操控转向角度来实现。车道线检测的目的就是希望能够根据检测到的车道线位置来计算最终应该转向的角度,从而控制小车始终行驶在当前车道线内。

由于道路环境比较简单,可以进一步简化控制变量。对于油门值,可以在运行时保持低匀速,这样只需要控制转向角度即可,实现起来更加容易。这种模式类似于现实生活中在驾驶汽车时开启了定速巡航功能。

下面针对模拟环境采集到的图像,进行算法分析。

#01、基于HSV空间的特定颜色区域提取

从仿真平台捕获的图像上进行分析,小车左侧是黄实线,右侧是白实线。最终目标是希望小车一直运行在这两条车道线中间。因此,首先要提取出这两条线才能定性分析出它们的斜率,从而为小车转向角度提供依据。具体的,可以通过颜色空间变换来提取车道线区域。为了方便将黄色线和白色线从图像中提取出来,可以将图像从RGB空间转换到HSV空间再处理。这里首先解释下RGB和HSV颜色空间的区别。

RGB是平时接触最多的颜色空间,由三个通道表示一幅图像,分别为红色(R)、绿色(G)和蓝色(B)。RGB颜色空间是图像处理中最基本、最常用的颜色空间,其利用三个颜色分量的线性组合来表示颜色,任何颜色都与这三个分量有关,但是这三个分量是高度相关的,想对图像的颜色进行调整需要同时更改这三个分量才行。在图像处理领域,针对特定颜色提取问题,使用较多的是HSV颜色空间,它比RGB空间更接近人类对色彩的感知经验,可以非常直观地表达色彩的色调、鲜艳程度和明暗程度,方便进行颜色的对比。

HSV表达彩色图像的方式由三个部分组成:色调(Hue)、饱和度(Saturation)、明度(Value)。其中色调用角度度量表示,取值范围为0~360,不同角度代表不同的色彩信息,即所处的光谱颜色的位置。

如果想要提取出黄色线,可以将色调范围控制在30~90。注意,在OpenCV中色调取值范围是[0~180],因此上述黄色范围需要缩小1倍,即[15~45]。检测白色车道线也是采用类似的原理。读者可以自行查找色调表来找到特定颜色的色调范围。

具体实现代码如下(opencv_drive/img_analysis.py):
python import cv2 import numpy as np import math #----------------------1.基于HSV空间的特定颜色区域提取------------------ # 读取图像并转换到HSV空间 frame = cv2.imread('test.jpg') hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 黄色线检测 lower_blue = np.array([15, 40, 40]) upper_blue = np.array([45, 255, 255]) yellow_mask = cv2.inRange(hsv, lower_blue, upper_blue) cv2.imwrite('yellow_mask.jpg', yellow_mask) # 白色线检测 lower_blue = np.array([0, 0, 200]) upper_blue = np.array([180, 30, 255]) white_mask = cv2.inRange(hsv, lower_blue, upper_blue) # 保存中间结果 cv2.imwrite('yellow_mask.jpg', yellow_mask) cv2.imwrite('white_mask.jpg', white_mask)
上述代码首先将图像从BGR空间转换到了HSV空间,然后使用cv2.inRange()函数提取特定颜色范围内的图像区域。

特定颜色区域提取效果如图1.37所示。

image.png


■ 特定颜色区域提取(从左至右:原图、白色提取区域、黄色提取区域)

02、基于高斯模糊的噪声滤除

观察前面的颜色区域提取效果,会发现有不少的离散噪声点,这些噪声会对后面的计算造成干扰,因此可以先提前用滤波算法处理一下。这里可以使用1.3节中介绍过的高斯模糊来消除这些高频噪声,具体可以通过OpenCV提供的现成的高斯模糊函数cv2.GaussianBlur()来实现。

实现代码如下:

frame = cv2.imread('test.jpg')
frame = cv2.GaussianBlur(frame,(5,5),1) # 添加高斯模糊代码

在读入图像后立即用高斯模糊操作一下,高斯核大小选择5×5,然后再进行特定颜色提取,最终效果如图1.38所示:

image.png


■ 高斯模糊后特定颜色区域提取(从左至右:原图、白色提取区域、黄色提取区域)


对比上述处理效果,可以看到使用高斯模糊后离散噪声点被有效滤除了,整个检测结果更“干净”了。

#03、基于Canny算子的边缘轮廓提取

目前仅获得了车道线区域,为了方便后续计算车道线角度,需要得到车道线具体的线段信息,即从区域中提取出线段。这里可以使用Canny算法来实现。

Canny边缘检测是从图像中提取结构信息的一种技术,于1986年被提出,目前已得到广泛应用。

Canny算法包括5个步骤:

(1)使用高斯滤波器,以平滑图像、滤除噪声;

(2)计算图像中每个像素点的梯度强度和方向;

(3)应用非极大值抑制(Non-Maximum Suppression,NMS),消除边缘检测带来的杂散响应;

(4)应用双阈值检测来确定真实的和潜在的边缘;

(5)抑制孤立的弱边缘;

OpenCV中集成了Canny算法,只需要一行代码即可实现。

具体实现代码如下:
python # 黄色线边缘提取 yellow_edge = cv2.Canny(yellow_mask, 200, 400) # 白色线边缘提取 whitewhite_edge = cv2.Canny(white_mask, 200, 400)
上述代码中200和400这两个参数表示Canny算子的低、高阈值,一般可以不用修改。Canny边缘检测效果如图1.39所示:

image.png


■ Canny边缘检测(从左至右:原图、白色边缘、黄色边缘)


可以看到,通过Canny边缘检测,准确的将每个子区域的外围轮廓提取了出来,后续只需要处理这些整幅图像中的边缘线段即可,大幅减少了需要处理的图像数据量。

#04、感兴趣区域提取

在利用OpenCV对图像进行处理时,通常会遇到一种情况,就是只需要对部分感兴趣区域(Region Of Interest,ROI)进行处理。例如针对本章这个模拟平台自动驾驶任务,正常情况下,黄色车道线位于图像左下角,白色车道线位于图像右下角,而图像中其他区域并不需要处理。因此,针对黄色车道线只需要提取图像左下部分,针对白色车道线只需要提取图像右下部分即可。

具体代码如下:
python # ----------------------------4.感兴趣区域提取---------------------------- def region_of_interest(edges, color="yellow"): height, width = edges.shape mask = np.zeros_like(edges) # 定义感兴趣区域掩码轮廓 if color == 'yellow': polygon = np.array([[(0, height * 1 / 2), (width * 1 / 2, height * 1 / 2), (width * 1 / 2, height), (0, height)]], np.int32) else: polygon = np.array([[(width * 1 / 2, height * 1 / 2), (width, height * 1 / 2), (width, height), (width * 1 / 2, height)]], np.int32) # 填充感兴趣区域掩码 cv2.fillPoly(mask, polygon, 255) # 提取感兴趣区域 croped_edge = cv2.bitwise_and(edges, mask) return croped_edge # 黄色车道线感兴趣区域提取 yellow_croped = region_of_interest(yellow_edge, color="yellow")cv2.imwrite("yellow_croped.jpg", yellow_croped) # 白色车道线感兴趣区域提取 white_croped = region_of_interest(white_edge, color="white")cv2.imwrite("white_croped.jpg", white_croped)
上述代码定义了region_of_interest()函数来提取感兴趣区域。具体实现时预先设置好一个值全为0的掩码区域,然后将需要提取的掩码区域赋值为255,最后使用cv2.bitwise_and()函数将掩码图像mask和待处理图像edges进行逐像素与运算,从而将非感兴趣区域像素赋值为0。

感兴趣区域提取效果如图1.40所示。

image.png


■ 感兴趣区域提取(从左至右:原图、白色边缘、黄色边缘)


到这一步,可以看到基本上准确的把需要的黄色车道线和白色车道线提取了出来。

#05、基于霍夫变换的线段检测

目前提取出了比较精确的车道线轮廓,但是对于实际的自动驾驶任务来说还没有完成最终的目标,还需要对车道线轮廓再进一步处理,得到车道线的具体线段信息,即每条线段的起始点坐标,这样才能方便计算小车最终需要的转向角度。本小节将使用霍夫变换来完成这个任务。

霍夫变换(Hough Transform,HT),作用是检测图像中的直线、圆等几何图形。一条直线的表示方法有好多种,最常见的是y=mx+b的形式。结合这个任务,所要解决的问题就是对于最终检测出的感兴趣区域,怎么把图片中的直线提取出来。

这里可以设置两个坐标系,左边的坐标系表示的是(x,y)值,对应图像空间(Image Space),右边的坐标系表示的是的(m,b)值,对应参数空间(Parameter Space),即直线的参数值,如图1.41所示。

image.png


■ 霍夫变换图像空间到参数空间的转换

很显然,一个左侧坐标系中的(x,y)点在右边坐标系中对应的就是一条线。假设将左边坐标系图像中所有目标区域的像素坐标都对应到右边坐标系中的每条直线,那么右边坐标系中的交点(m,b)就表示左侧坐标系中有多个点经过(m,b)确定的直线。当右侧坐标系中这个交点(m,b)上相交的直线超过指定数量时,可以认为左侧坐标系图像中存在着表达式为y=mx+b的直线,有很多像素落在这条直线上。可以采用这种方法来估计图像中出现的直线,但是该方法存在一个问题,就是(m,b)的取值范围太大。为了解决这个问题,在直线的表示方面可以改用

image.png


的规范式代替一般直线表达式

image.png


,参数空间由此变成。这样图像空间中的一个像素点在参数空间中就是一条曲线(三角函数曲线)。以上就是霍夫直线检测的基本原理。

具体的,霍夫直线检测算法实现步骤如下:
(1)初始化

image.png


空间,令

image.png


,则

image.png


表示在该参数表示的直线上的像素点的个数。

(2)对于每一个像素值大于0的像素点(x,y),在参数空间中找出满足

image.png


(3)统计所有

image.png


的大小,取出

image.png


的参数,threshold是预设的阈值。

OpenCV已经封装好了基于霍夫变换的直线线段检测方法cv2.HoughLinesP(),下面就来使用它进行线段检测,代码如下:
python # ----------------------------5.基于霍夫变换的直线检测---------------------------- rho = 1 # 距离精度:1像素 angle = np.pi / 180 # 角度精度:1度 min_thr = 10 # 最少投票数 white_lines = cv2.HoughLinesP(white_croped, rho, angle, min_thr, np.array([]), minLineLength=8, maxLineGap=8) yellow_lines = cv2.HoughLinesP(yellow_croped, rho, angle, min_thr, np.array([]), minLineLength=8, maxLineGap=8) # 输出查看返回的线段 print(white_lines)
输出查看返回的lines内容,结果如下:
python [[[112 87 142 117]] [[ 94 69 134 119]] [[111 85 137 111]] [[107 84 132 115]] [[119 98 134 117]]]
返回的每组值都是一条线段,表示线段的起始位置(x_start,y_start,x_end,y_end)。从输出结果看到检测出来了很多小线段,但最终需要的是两条车道线,因此可以对检测出来的小线段做一下聚类和平均:
python # --------------------------------6.小线段聚类------------------------------------ def make_points(frame, line): '''根据直线斜率和截距计算指定高度处的起始坐标''' height, width, _ = frame.shape slope, intercept = line y1 = height y2 = int(y1 * 1 / 2) x1 = int((y1 - intercept) / slope) x2 = int((y2 - intercept) / slope) return [x1, y1, x2, y2] def average_lines(frame, lines, direction="left"): """对小线段进行聚类""" lane_line = [] if lines is None: print(direction + "没有检测到线段") return lane_line fits = [] # 计算每条小线段的斜率和截距 for line in lines: for x1, y1, x2, y2 in line: # 最小二乘法拟合 fit = np.polyfit((x1, x2), (y1, y2), 1) slope = fit[0] # 斜率 intercept = fit[1] # 截距 if direction == "left" and slope < 0: fits.append((slope, intercept)) elif direction == "right" and slope > 0: fits.append((slope, intercept)) # 计算所有小线段的平均斜率和截距 if len(fits) > 0: fit_average = np.average(fits, axis=0) lane_line = make_points(frame, fit_average) return lane_line # 聚合线段 yellow_lane = average_lines(frame, yellow_lines, direction="left") white_lane = average_lines(frame, white_lines, direction="right") print(white_lane)
上述代码定义了average_lines()函数用于聚合小线段,其中使用了numpy库中的polyfit()函数来拟合数据点,该函数封装了最小二乘算法来拟合直线,从而得到图像中每条小线段的斜率和截距。

需要注意的是,对于数字图像来说,y坐标轴是向下的,其原点在图像的左上角,而在数学上一般坐标系定义的坐标轴是向上的,因此,测试图像中左侧黄色实线斜率是负值(对应代码中slope<0),右侧白色实线斜率是正值(对应代码中slope>0)。

在求得每条小线段的斜率和截距后,使用了自定义的make_points()函数重新计算了该平均线对应到图像上指定高度处的起始坐标,计算方法如图1.42所示。

image.png


■ 车道线线段起始位置示意图

在计算每条车道线的起始坐标时,分别令起始点的纵坐标

image.png


,然后计算对应的横坐标|

image.png


的值,从而得到最终代表线段的起始点坐标

image.png


上述代码最后计算得到的是坐标数值,这样观察线段的坐标值不是很直观,可以写个函数显式的观察检测到的线段,代码如下:
#----------------------------7.可视化显示检测结果-------------------------------
def display_line(frame, line, line_color=(0, 0, 255), line_width=3):
    '''在原图上合成展示线段'''
    line_img = np.zeros_like(frame)
    x1, y1, x2, y2 = line
    cv2.line(line_img, (x1, y1), (x2, y2), line_color, line_width)
    line_img = cv2.addWeighted(frame, 0.8, line_img, 1, 1) # 图像合成方式显示检测结果    return line_img
# 显示检测结果
img_yellow = display_line(frame, yellow_lane, line_color=(0, 0, 255), line_width=3)img_white = display_line(frame, white_lane, line_color=(0, 0, 255), line_width=3)cv2.imwrite("img_yellow.jpg", img_yellow)cv2.imwrite("img_white.jpg", img_white)

车道线检测结果如图1.43所示,从左至右依次为原图、白色线检测结果和黄色线检测结果。

image.png

■ 车道线检测结果


从图1.43可以看到,该方法已经能够准确地将两条车道线检测了出来。接下来就是根据这两条车道线进行小车驾驶方向控制了。
目录
相关文章
|
5月前
|
机器学习/深度学习 算法 定位技术
Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现裂缝的检测识别(C#代码UI界面版)
本项目基于YOLOv8模型与C#界面,结合Baumer工业相机,实现裂缝的高效检测识别。支持图像、视频及摄像头输入,具备高精度与实时性,适用于桥梁、路面、隧道等多种工业场景。
545 27
|
机器学习/深度学习 编解码 人工智能
人脸表情[七种表情]数据集(15500张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
本数据集包含15,500张已划分、已标注的人脸表情图像,覆盖惊讶、恐惧、厌恶、高兴、悲伤、愤怒和中性七类表情,适用于YOLO系列等深度学习模型的分类与检测任务。数据集结构清晰,分为训练集与测试集,支持多种标注格式转换,适用于人机交互、心理健康、驾驶监测等多个领域。
|
2月前
|
机器学习/深度学习 人工智能 文字识别
中药材图像识别数据集(100类,9200张)|适用于YOLO系列深度学习分类检测任务
本数据集包含9200张中药材图像,覆盖100种常见品类,已标注并划分为训练集与验证集,支持YOLO等深度学习模型。适用于中药分类、目标检测、AI辅助识别及教学应用,助力中医药智能化发展。
|
4月前
|
机器学习/深度学习 人工智能 监控
河道塑料瓶识别标准数据集 | 科研与项目必备(图片已划分、已标注)| 适用于YOLO系列深度学习分类检测任务【数据集分享】
随着城市化进程加快和塑料制品使用量增加,河道中的塑料垃圾问题日益严重。塑料瓶作为河道漂浮垃圾的主要类型,不仅破坏水体景观,还威胁水生生态系统的健康。传统的人工巡查方式效率低、成本高,难以满足实时监控与治理的需求。
|
4月前
|
机器学习/深度学习 传感器 人工智能
火灾火焰识别数据集(2200张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
在人工智能和计算机视觉的快速发展中,火灾检测与火焰识别逐渐成为智慧城市、公共安全和智能监控的重要研究方向。一个高质量的数据集往往是推动相关研究的核心基础。本文将详细介绍一个火灾火焰识别数据集,该数据集共包含 2200 张图片,并已按照 训练集(train)、验证集(val)、测试集(test) 划分,同时配有对应的标注文件,方便研究者快速上手模型训练与评估。
火灾火焰识别数据集(2200张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
|
4月前
|
机器学习/深度学习 人工智能 自动驾驶
7种交通场景数据集(千张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
在智能交通与自动驾驶技术快速发展的今天,如何高效、准确地感知道路环境已经成为研究与应用的核心问题。车辆、行人和交通信号灯作为城市交通系统的关键元素,对道路安全与交通效率具有直接影响。然而,真实道路场景往往伴随 复杂光照、遮挡、多目标混杂以及交通信号状态多样化 等挑战,使得视觉识别与检测任务难度显著增加。
|
4月前
|
机器学习/深度学习 人工智能 监控
坐姿标准好坏姿态数据集(图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
坐姿标准好坏姿态数据集的发布,填补了计算机视觉领域在“细分健康行为识别”上的空白。它不仅具有研究价值,更在实际应用层面具备广阔前景。从青少年的健康教育,到办公室的智能提醒,再到驾驶员的安全监控和康复训练,本数据集都能发挥巨大的作用。
坐姿标准好坏姿态数据集(图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
|
4月前
|
机器学习/深度学习 数据采集 算法
PCB电路板缺陷检测数据集(近千张图片已划分、已标注)| 适用于YOLO系列深度学习检测任务【数据集分享】
在现代电子制造中,印刷电路板(PCB)是几乎所有电子设备的核心组成部分。随着PCB设计复杂度不断增加,人工检测PCB缺陷不仅效率低,而且容易漏检或误判。因此,利用计算机视觉和深度学习技术对PCB缺陷进行自动检测成为行业发展的必然趋势。
PCB电路板缺陷检测数据集(近千张图片已划分、已标注)| 适用于YOLO系列深度学习检测任务【数据集分享】
|
4月前
|
机器学习/深度学习 编解码 人工智能
102类农业害虫数据集(20000张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
在现代农业发展中,病虫害监测与防治 始终是保障粮食安全和提高农作物产量的关键环节。传统的害虫识别主要依赖人工观察与统计,不仅效率低下,而且容易受到主观经验、环境条件等因素的影响,导致识别准确率不足。
|
机器学习/深度学习 人工智能 监控
单车、共享单车已标注数据集(图片已划分、已标注)|适用于深度学习检测任务【数据集分享】
数据是人工智能的“燃料”。一个高质量、标注精准的单车与共享单车数据集,不仅能够推动学术研究的进步,还能为智慧交通、智慧城市的建设提供有力支撑。 在计算机视觉领域,研究者们常常会遇到“数据鸿沟”问题:公开数据集与真实业务需求之间存在不匹配。本次分享的数据集正是为了弥补这一不足,使得研究人员与工程师能够快速切入单车检测领域,加速模型从实验室走向真实应用场景。

热门文章

最新文章