第1章:车道线检测基础
各位同学好,我是你们的ADAS算法讲师。今天咱们正式进入车道保持算法的核心环节——车道线检测。
说实话,车道线检测是整个车道保持系统的眼睛。眼睛不好使,后面控制再牛也白搭。我当年刚入行时,就吃过这个亏。有一次路测,车辆在弯道突然偏离,吓得我一身冷汗。后来排查发现,就是图像预处理参数没调好。
嗯,咱们从最基础的开始讲起。
1.1 图像预处理:把原始图像变成算法能看懂的样子
摄像头采集的原始图像,说白了就是一堆像素点。直接拿来做检测,效果很差。为什么?因为光照、阴影、路面纹理都会干扰。所以我们需要预处理。
1.1.1 灰度化
彩色图像有三个通道(R、G、B),信息量太大。车道线检测其实只关心亮度变化,不关心颜色。所以第一步,把彩色图转成灰度图。
我个人习惯用加权平均法:
gray = 0.299 * R + 0.587 * G + 0.114 * B
为什么权重不一样?因为人眼对绿色最敏感,对蓝色最不敏感。这个公式是ITU-R标准的,直接拿来用就行。
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 就行。别自己手写循环,效率差太多。
1.1.2 高斯滤波
灰度化之后,图像里还有噪声。比如路面上的小石子、远处的树影。这些噪声会让边缘检测出错。
高斯滤波的原理很简单:用一个高斯核去卷积图像。每个像素的值,变成它周围像素的加权平均。权重由高斯分布决定。
我记得有一次项目,在隧道入口处车道线总是检测失败。后来发现是隧道内外的光照突变,导致图像噪声剧增。加了高斯滤波后,问题就解决了。
blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)
这里 (5,5) 是核大小,1.5 是标准差。核越大,图像越模糊。我一般用 5x5 或 3x3,太大容易把车道线边缘也模糊掉。
1.1.3 Canny边缘检测
滤波之后,我们要找图像中亮度变化剧烈的地方。这些地方大概率就是车道线。
Canny边缘检测是经典算法。它分四步:
- 计算梯度: 用Sobel算子算x方向和y方向的梯度
- 非极大值抑制: 只保留梯度方向上的局部最大值
- 双阈值检测: 高阈值确定强边缘,低阈值连接弱边缘
- 边缘连接: 把断开的边缘连起来
你想想看,为什么需要双阈值?因为单阈值不好使。阈值设高了,车道线断断续续;设低了,一堆假边缘。双阈值可以保留真正的边缘,同时抑制噪声。
edges = cv2.Canny(blurred, 50, 150)
这里 50 是低阈值,150 是高阈值。我建议低阈值设为高阈值的一半到三分之一。具体数值得根据你的摄像头和场景调。
1.2 霍夫变换直线检测原理
边缘检测之后,我们得到了一堆边缘点。但这些点还没形成直线。霍夫变换就是用来找直线的。
1.2.1 从图像空间到参数空间
传统的直线方程是 y = kx + b。但k和b有缺点:垂直线的k是无穷大,没法表示。
所以霍夫变换用极坐标:
ρ = x * cos(θ) + y * sin(θ)
其中ρ是原点到直线的距离,θ是直线的法线角度。这样所有直线都能表示。
原理是这样的:图像空间中的一个点 (x, y),对应参数空间中的一条正弦曲线。多个点对应的曲线如果交于一点,那这些点就在同一条直线上。
说白了,就是把「找直线」的问题,变成了「找交点」的问题。
1.2.2 标准霍夫变换 vs 概率霍夫变换
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 标准霍夫变换 | 计算所有边缘点,精度高 | 直线清晰、噪声少的场景 |
| 概率霍夫变换 | 随机采样边缘点,速度快 | 实时性要求高的场景(如ADAS) |
我建议在车道保持系统中用概率霍夫变换。为什么?因为实时性更重要。标准霍夫变换虽然精度高,但计算量太大,跑不到30fps。
lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=50,
minLineLength=100, maxLineGap=50)
参数说明:
- rho: ρ的步长,一般设为1像素
- theta: θ的步长,1度对应 π/180 弧度
- threshold: 投票阈值,越大直线越少
- minLineLength: 最小线段长度,过滤短线段
- maxLineGap: 最大间隙,连接断开的线段
1.2.3 霍夫变换的局限性
霍夫变换不是万能的。我遇到过几个坑:
- 弯道检测困难: 霍夫变换只能检测直线,弯道需要分段拟合
- 遮挡问题: 前车挡住车道线时,检测会中断
- 阴影干扰: 树影、桥墩阴影会产生假直线
嗯,这些问题我们后面章节会讲怎么解决。今天先把基础打牢。
1.3 完整代码示例
最后,给一个完整的车道线检测预处理流程。这是我项目里常用的模板:
import cv2
import numpy as np
def preprocess_lane_image(img):
# 1. 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 高斯滤波
blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)
# 3. Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)
# 4. 感兴趣区域(ROI)掩码
mask = np.zeros_like(edges)
height, width = edges.shape
# 定义梯形ROI,只保留车道区域
polygon = np.array([[
(0, height),
(width, height),
(width//2 + 100, height//2),
(width//2 - 100, height//2)
]], np.int32)
cv2.fillPoly(mask, polygon, 255)
masked_edges = cv2.bitwise_and(edges, mask)
# 5. 概率霍夫变换
lines = cv2.HoughLinesP(masked_edges, rho=1, theta=np.pi/180,
threshold=50, minLineLength=100, maxLineGap=50)
return lines, masked_edges
这段代码里,我加了一个ROI掩码。为什么?因为车道线只出现在图像下半部分。天空、树木这些区域不需要处理,能减少大量干扰。
好了,今天的内容就到这里。图像预处理和霍夫变换是车道线检测的基石。下一章,我们会讲如何对检测到的直线进行后处理,筛选出真正的车道线。
记住,算法是死的,场景是活的。多调参、多测试,才能找到最适合你项目的参数组合。