一、提取车道线
在道路检测中,提取车道线至关重要。我们采用两种主要方法进行车道线提取:梯度提取和颜色提取。梯度提取包括高斯滤波、灰度化、梯度处理、二值化和形态学变换(膨胀和腐蚀),能有效突出车道线的边缘特征;颜色提取则通过 HLS 颜色空间提取白色车道线和使用 Lab 颜色空间提取黄色车道线,可以进一步增强车道线的可区分性。这两种方法结合使用,有助于实现更准确的车道线检测。
1.1、梯度提取
高斯滤波
- 使用高斯滤波器对输入图像进行平滑,减少噪声,帮助后续的梯度计算。
灰度化
- 将彩色图像转换为灰度图像,以简化图像数据。
梯度处理
- 使用Sobel算子计算图像的梯度,可以计算x和y方向的梯度。
二值化
- 通过阈值处理将图像二值化,突出边缘。
形态学变换(膨胀,腐蚀)
- 使用膨胀和腐蚀来去除噪声和填补空洞。
1.2、颜色提取
白色提取(HLS空间)
- 通过转换到HLS颜色空间来提取白色车道线。
黄色提取(Lab空间)
- 将图像转换到Lab颜色空间,以便提取黄色区域。
二、梯度提取
导入模块
python
import cv2
import numpy as np
输入图像
python
img_wrp=cv2.imread('img_wrp.png')
高斯滤波
python
img_Gaussian=cv2.GaussianBlur(img_wrp,(7,7),sigmaX=1)
灰度化
python
img_gray=cv2.cvtColor(img_Gaussian,cv2.COLOR_BGR2GRAY)
梯度处理
python
img_Sobel=cv2.Sobel(img_gray,-1,dx=1,dy=0)
二值化
python
ret,img_threshold=cv2.threshold(img_Sobel,127,255,cv2.THRESH_BINARY)
形态学变换(先膨胀后腐蚀)
python
kernel=np.ones((11,11),np.uint8)
img_dilate=cv2.dilate(img_threshold,kernel,iterations=2)
img_erode=cv2.erode(img_dilate, kernel, iterations=1)
输出图像
python
cv2.imshow('img_wrp', img_wrp)
cv2.imshow('img_Gaussian', img_Gaussian)
cv2.imshow('img_gray', img_gray)
cv2.imshow('img_Sobel', img_Sobel)
cv2.imshow('img_threshold', img_threshold)
cv2.imshow('img_erode', img_erode)
cv2.waitKey(0)
完整代码
python
import cv2
import numpy as np
# 读取透视变换后的图像 'img_wrp.png'
img_wrp = cv2.imread('img_wrp.png')
# 对图像应用高斯模糊,减少噪声,使用的卷积核为 (7, 7),sigmaX 为 1
img_Gaussian = cv2.GaussianBlur(img_wrp, (7, 7), sigmaX=1)
# 将模糊后的图像转换为灰度图
img_gray = cv2.cvtColor(img_Gaussian, cv2.COLOR_BGR2GRAY)
# 对灰度图像应用 Sobel 边缘检测,使用水平梯度 (dx=1, dy=0)
img_Sobel = cv2.Sobel(img_gray, -1, dx=1, dy=0)
# 对 Sobel 输出的图像应用二值化处理,将图像转换为黑白图像
# 当像素值 > 127 时,设为 255;否则设为 0
ret, img_threshold = cv2.threshold(img_Sobel, 127, 255, cv2.THRESH_BINARY)
# 创建一个 11x11 的全 1 核,进行形态学操作
kernel = np.ones((11, 11), np.uint8)
# 对二值化图像进行膨胀操作,增加白色区域
img_dilate = cv2.dilate(img_threshold, kernel, iterations=2)
# 对膨胀后的图像进行腐蚀操作,减少白色区域
img_erode = cv2.erode(img_dilate, kernel, iterations=1)
# 显示各步骤处理后的图像
cv2.imshow('img_wrp', img_wrp) # 显示原始透视变换图
cv2.imshow('img_Gaussian', img_Gaussian) # 显示高斯模糊后的图像
cv2.imshow('img_gray', img_gray) # 显示灰度图像
cv2.imshow('img_Sobel', img_Sobel) # 显示 Sobel 边缘检测结果
cv2.imshow('img_threshold', img_threshold) # 显示二值化后的图像
cv2.imshow('img_erode', img_erode) # 显示腐蚀后的图像
# 等待按键后关闭窗口
cv2.waitKey(0)
- 读取透视变换后保存的图像。
- 对图像应用高斯模糊,减少噪声以平滑图像。
- 将模糊图像转换为灰度图,以便进行边缘检测。
- 使用 Sobel 算子检测图像的水平边缘。
- 将 Sobel 输出的图像进行二值化处理,以获取明显的边缘部分。
- 进行形态学的膨胀和腐蚀操作,增加和减少白色区域,进一步改善图像质量。
- 显示处理后各个步骤的图像,方便观察效果。
三、颜色提取
导入模块
python
import cv2
import numpy as np
输入图像
python
img_wrp=cv2.imread('img_wrp.png')
提取白色车道线
python
img_hls=cv2.cvtColor(img_wrp,cv2.COLOR_BGR2HLS)
l_channel=img_hls[:,:,1]
l_channel=l_channel/np.max(l_channel)*255
binary_output1=np.zeros_like(l_channel)
binary_output1[(l_channel>220) & (l_channel<255)]=1
提取黄色车道线
python
img_lab=cv2.cvtColor(img_wrp,cv2.COLOR_BGR2Lab)
img_lab[:,240:,:]=(0,0,0)
lab_b=img_lab[:,:,2]
if np.max(lab_b)>100:
lab_b=lab_b/np.max(lab_b)*255
binary_output2=np.zeros_like(l_channel)
binary_output2[(lab_b > 212) & (lab_b < 220)] = 1
对车道线进行合并
python
#直接进行相加
binary_output=binary_output1+binary_output2
或
python
#创建新的模板
binary_output=np.zeros_like(binary_output1)
binary_output[(binary_output1 == 1) | (binary_output2 == 1)] = 1
形态学变换(先膨胀后腐蚀)
python
kernel=np.ones((15,15),np.uint8)
img_dilate_binary_output=cv2.dilate(binary_output,kernel,iterations=1)
img_erode_binary_output=cv2.erode(img_dilate_binary_output, kernel, iterations=1)
输出图像
python
cv2.imshow('binary_output1',binary_output1)
cv2.imshow('binary_output2', binary_output2)
cv2.imshow('binary_output', binary_output)
cv2.imshow('img_erode_binary_output', img_erode_binary_output)
cv2.waitKey(0)
完整代码
python
import cv2 # 导入 OpenCV 库,用于图像处理
import numpy as np # 导入 NumPy 库,用于数值计算
# 读取透视变换后的图像 'img_wrp.png'
img_wrp = cv2.imread('img_wrp.png')
# 将图像从 BGR 转换到 HLS 颜色空间
img_hls = cv2.cvtColor(img_wrp, cv2.COLOR_BGR2HLS)
# 提取 HLS 中的亮度通道 (L通道)
l_channel = img_hls[:, :, 1]
# 将亮度通道归一化到 0 到 255 的范围
l_channel = l_channel / np.max(l_channel) * 255
# 创建一个和亮度通道同样大小的零数组,用于存放二值化结果
binary_output1 = np.zeros_like(l_channel)
# 使用阈值条件筛选出亮度通道中处于 220 到 255 之间的像素点
binary_output1[(l_channel > 220) & (l_channel < 255)] = 1
# 提取黄色车道线
# 转换原图像为 Lab 颜色空间
img_lab = cv2.cvtColor(img_wrp, cv2.COLOR_BGR2Lab)
# 将 Lab 图像的某些区域(240列及之后的所有区域)设为黑色
img_lab[:, 240:, :] = (0, 0, 0)
# 提取 Lab 图像中的 b 通道
lab_b = img_lab[:, :, 2]
# 对 b 通道进行归一化处理(如果最大值大于 100)
if np.max(lab_b) > 100:
lab_b = lab_b / np.max(lab_b) * 255
# 创建一个与亮度通道同样大小的零数组,用于存放二值化结果
binary_output2 = np.zeros_like(l_channel)
# 使用阈值条件筛选出 b 通道中处于 212 到 220 之间的像素点
binary_output2[(lab_b > 212) & (lab_b < 220)] = 1
# 创建一个新的二值图像 binary_output,结合两个二值图像
binary_output = np.zeros_like(binary_output1)
binary_output[(binary_output1 == 1) | (binary_output2 == 1)] = 1
# 创建一个 15x15 的全 1 核,用于形态学操作
kernel = np.ones((15, 15), np.uint8)
# 对结合的二值图像进行膨胀操作,增加白色区域
img_dilate_binary_output = cv2.dilate(binary_output, kernel, iterations=1)
# 对膨胀后的图像进行腐蚀操作,以减少白色区域
img_erode_binary_output = cv2.erode(img_dilate_binary_output, kernel, iterations=1)
# 显示不同阶段的二值化输出图像
cv2.imshow('binary_output1', binary_output1) # 显示亮度通道的二值化结果
cv2.imshow('binary_output2', binary_output2) # 显示黄色车道线的二值化结果
cv2.imshow('binary_output', binary_output) # 显示结合后的二值图像
cv2.imshow('img_erode_binary_output', img_erode_binary_output) # 显示腐蚀处理后的图像
# 等待按键后关闭窗口
cv2.waitKey(0)
- 读取透视变换后的图像,并将其转换为 HLS 颜色空间,以提取亮度通道。
- 将亮度通道归一化并进行二值化,提取亮度较高的区域。
- 转换图像为 Lab 颜色空间,提取 b 通道并进行归一化处理,选择黄色车道线。
- 创建一个结合两个输出的最终二值图像,该图像包含亮度和黄色车道线的信息。
- 使用膨胀和腐蚀操作改善二值图像的形态,去除噪声和空隙。
- 显示每个步骤的结果,以便进行视觉比较和分析。
四、库函数
4.1、cvtColor()
python
cv.cvtColor( src, code[, dst[, dstCn[, hint]]] ) -> dst
方法 | 描述 |
---|---|
src | 输入图像:8 位无符号、16 位无符号 ( CV_16UC... ) 或单精度浮点。 |
dst | 输出图像的大小和深度与 src 相同。 |
code | 色彩空间转换代码(请参阅 ColorConversionCodes)。 |
dstCn | 目标图像中的通道数;如果参数为 0,则 Channels 的数量会自动从 src 和 code 中得出。 |
hint | 实现修改标志。请参阅 AlgorithmHint |
4.2、GaussianBlur()

python
cv.GaussianBlur( src, ksize, sigmaX[, dst[, sigmaY[, borderType[, hint]]]] ) -> DST
| 方法 | 描述 |
| src | 输入图像;图像可以有任意数量的通道,这些通道是独立处理的,但深度应为 CV_8U、CV_16U、CV_16S、CV_32F 或 CV_64F。 |
| dst | 输出图像的大小和类型与 src 相同。 |
| ksize | Gaussian kernel 大小。ksize.width 和 ksize.height 可以不同,但它们都必须是正数和奇数。或者,它们可以是零,然后根据 sigma 计算。 |
| sigmaX | X 方向上的高斯核标准差 |
| sigmaY | Y 方向的高斯核标准差;如果 sigmaY 为零,则设置为等于 sigmaX,如果两个 sigma 都为零,则分别从 ksize.width 和 ksize.height 计算出来(详见 getGaussianKernel);为了完全控制结果,而不管所有这些语义将来可能如何修改,建议指定所有 ksize、sigmaX 和 sigmaY。 |
| borderType | 像素外插方法,请参阅 BorderTypes。不支持BORDER_WRAP。 |
hint | 实现修改标志。请参阅 AlgorithmHint |
---|
4.3、threshold()
将固定级别的阈值应用于每个数组元素。
该函数将固定级别的阈值应用于多通道数组。该函数通常用于从灰度图像中获取双层(二进制)图像( compare 也可用于此目的)或用于去除噪声,即过滤掉值太小或太大的像素。该函数支持多种类型的阈值。它们由 type parameter 确定。
此外,特殊值 THRESH_OTSU 或 THRESH_TRIANGLE 可以与上述值之一结合使用。在这些情况下,该函数使用 Otsu 或 Triangle 算法确定最佳阈值,并使用它而不是指定的阈值。
注意
目前,Otsu 和 Triangle 方法仅针对 8 位单通道图像实现。
python
cv.threshold(src, thresh, maxval, type[, dst]) ->retval, dst
方法 | 描述 |
---|---|
src | 输入数组 (多通道、8 位或 32 位浮点) |
thresh | 阈值 |
maxval | Maximum 值,用于 THRESH_BINARY 和 THRESH_BINARY_INV 阈值类型。 |
type | 阈值类型 |
注意:
该方法有两个返回值retval、dst
阈值作的类型
|------------------------------------------------------------|-----------------------------------------------------------------------------|
| THRESH_BINARY Python: cv.THRESH_BINARY 阈值法 | |
| THRESH_BINARY_INV Python: cv.THRESH_BINARY_INV 反阈值法 | |
| THRESH_TRUNC Python: cv.THRESH_TRUNC 截断阈值法 | |
| THRESH_TOZERO Python: cv.THRESH_TOZERO 低阈值零处理 | |
| THRESH_TOZERO_INV Python: cv.THRESH_TOZERO_INV 超阈值零处理 | |
| THRESH_MASK Python: cv.THRESH_MASK | |
| THRESH_OTSU Python: cv.THRESH_OTSU OTSU阈值法 | flag 中,使用 Otsu 算法选择最佳阈值 |
| THRESH_TRIANGLE Python: cv.THRESH_TRIANGLE Triangle阈值法 | 标志,使用 Triangle 算法选择最佳阈值 |
4.4、Sobel()
python
cv.Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] ) -> dst
| 方法 | 描述 |
| src | 输入图像 |
| dst | 输出图像 |
| ddepth | 目标图像的所需深度,请参阅组合 |
| dx | 导数 X 的阶数 |
| dy | 导数 y 的阶数 |
| ksize | 扩展 Sobel 核的大小;它必须是 1、3、5 或 7。 |
| scale | 计算的导数值的可选比例因子;默认情况下,不应用缩放(有关详细信息,请参阅 getDerivKernels)。 |
| delta | 在将过滤的像素存储到 DST 之前添加到过滤像素的可选值。 |
borderType | 素外插方法,请参阅 BorderTypes。不支持BORDER_WRAP。 |
---|
4.5、dilate()
python
cv.dilate( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] ) -> dst
| 方法 | 描述 |
| src | 输入图像;通道数可以是任意的,但深度应该是 CV_8U、 CV_16U、 CV_16S、 CV_32F 或 CV_64F 之一。 |
| dst | 输出图像的大小和类型与 src 相同。 |
| kernel | 用于扩张的结构元件;如果 element=Mat(),则使用 3 x 3 矩形结构元素。可以使用 getStructuringElement 创建 Kernel |
| anchor | 锚点在元素中的位置;默认值 (-1, -1) 表示锚点位于元素中心。 |
| iterations | 应用扩张的次数。 |
| borderType | 像素外插方法,请参阅 BorderTypes。不支持BORDER_WRAP。 |
borderValue | border 值(如果边界为常量) |
---|
BorderTypes | |
---|---|
BORDER_CONSTANT Python:cv.BORDER_CONSTANT | `iiiiii |
BORDER_REPLICATE Python:cv.BORDER_REPLICATE | `aaaaaa |
BORDER_REFLECT Python:cv.BORDER_REFLECT | `fedcba |
BORDER_REFLECT_101 Python:cv.BORDER_REFLECT_101 | `gfedcb |
BORDER_TRANSPARENT Python:cv.BORDER_TRANSPARENT | `uvwxyz |
BORDER_REFLECT101 Python:cv.BORDER_REFLECT101 | 与 BORDER_REFLECT_101 相同 |
BORDER_DEFAULT Python:cv.BORDER_DEFAULT | 与 BORDER_REFLECT_101 相同 |
BORDER_ISOLATED Python:cv.BORDER_ISOLATED | 插值限制在 ROI 边界内。 |
4.6、erode()
python
cv.erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] ) -> dst
| 方法 | 描述 |
| src | 输入图像;通道数可以是任意的,但深度应该是 CV_8U、 CV_16U、 CV_16S、 CV_32F 或 CV_64F 之一。 |
| dst | 输出图像的大小和类型与 src 相同。 |
| kernel | 用于侵蚀的结构元件;如果 ,则使用矩形结构元素。可以使用 getStructuringElement 创建 Kernel。element=Mat()``3 x 3
|
| anchor | 锚点在元素中的位置;默认值 (-1, -1) 表示锚点位于元素中心。 |
| iterations | 应用 腐蚀的次数。 |
| borderType | 像素外插方法,请参阅 BorderTypes。不支持BORDER_WRAP。 |
borderValue | border 值(如果边界为常量) |
---|
BorderTypes | |
---|---|
BORDER_CONSTANT Python:cv.BORDER_CONSTANT | `iiiiii |
BORDER_REPLICATE Python:cv.BORDER_REPLICATE | `aaaaaa |
BORDER_REFLECT Python:cv.BORDER_REFLECT | `fedcba |
BORDER_REFLECT_101 Python:cv.BORDER_REFLECT_101 | `gfedcb |
BORDER_TRANSPARENT Python:cv.BORDER_TRANSPARENT | `uvwxyz |
BORDER_REFLECT101 Python:cv.BORDER_REFLECT101 | 与 BORDER_REFLECT_101 相同 |
BORDER_DEFAULT Python:cv.BORDER_DEFAULT | 与 BORDER_REFLECT_101 相同 |
BORDER_ISOLATED Python:cv.BORDER_ISOLATED | 插值限制在 ROI 边界内。 |