第1章:车道线检测基础

各位同学好,我是你们的ADAS算法讲师。今天咱们正式进入车道保持算法的核心环节——车道线检测。

说实话,车道线检测是整个车道保持系统的眼睛。眼睛不好使,后面控制再牛也白搭。我当年刚入行时,就吃过这个亏。有一次路测,车辆在弯道突然偏离,吓得我一身冷汗。后来排查发现,就是图像预处理参数没调好。

嗯,咱们从最基础的开始讲起。

1.1 图像预处理:把原始图像变成算法能看懂的样子

摄像头采集的原始图像,说白了就是一堆像素点。直接拿来做检测,效果很差。为什么?因为光照、阴影、路面纹理都会干扰。所以我们需要预处理。

1.1.1 灰度化

彩色图像有三个通道(R、G、B),信息量太大。车道线检测其实只关心亮度变化,不关心颜色。所以第一步,把彩色图转成灰度图。

我个人习惯用加权平均法:

gray = 0.299 * R + 0.587 * G + 0.114 * B

为什么权重不一样?因为人眼对绿色最敏感,对蓝色最不敏感。这个公式是ITU-R标准的,直接拿来用就行。

我的小技巧: 在OpenCV里直接用 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 就行。别自己手写循环,效率差太多。

1.1.2 高斯滤波

灰度化之后,图像里还有噪声。比如路面上的小石子、远处的树影。这些噪声会让边缘检测出错。

高斯滤波的原理很简单:用一个高斯核去卷积图像。每个像素的值,变成它周围像素的加权平均。权重由高斯分布决定。

我记得有一次项目,在隧道入口处车道线总是检测失败。后来发现是隧道内外的光照突变,导致图像噪声剧增。加了高斯滤波后,问题就解决了。

blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)

这里 (5,5) 是核大小,1.5 是标准差。核越大,图像越模糊。我一般用 5x5 或 3x3,太大容易把车道线边缘也模糊掉。

避坑指南: 我曾经把核设成 15x15,结果车道线边缘全没了,Canny检测出来一堆乱七八糟的东西。记住,滤波不是越强越好。

1.1.3 Canny边缘检测

滤波之后,我们要找图像中亮度变化剧烈的地方。这些地方大概率就是车道线。

Canny边缘检测是经典算法。它分四步:

  1. 计算梯度: 用Sobel算子算x方向和y方向的梯度
  2. 非极大值抑制: 只保留梯度方向上的局部最大值
  3. 双阈值检测: 高阈值确定强边缘,低阈值连接弱边缘
  4. 边缘连接: 把断开的边缘连起来

你想想看,为什么需要双阈值?因为单阈值不好使。阈值设高了,车道线断断续续;设低了,一堆假边缘。双阈值可以保留真正的边缘,同时抑制噪声。

edges = cv2.Canny(blurred, 50, 150)

这里 50 是低阈值,150 是高阈值。我建议低阈值设为高阈值的一半到三分之一。具体数值得根据你的摄像头和场景调。

核心要点: Canny的阈值参数直接影响检测效果。我一般先在直方图上看看梯度分布,再选阈值。别瞎猜,用数据说话。

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: 最大间隙,连接断开的线段
我的经验: threshold 设50-100之间。太小会检测出很多假直线,太大又漏检。minLineLength 设100左右,可以过滤掉路面上的小纹理。

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掩码。为什么?因为车道线只出现在图像下半部分。天空、树木这些区域不需要处理,能减少大量干扰。

注意: ROI的形状要根据你的摄像头安装位置调整。我见过有人直接把ROI设成矩形,结果弯道时车道线跑出ROI外,检测失败。建议用梯形,更符合透视关系。

好了,今天的内容就到这里。图像预处理和霍夫变换是车道线检测的基石。下一章,我们会讲如何对检测到的直线进行后处理,筛选出真正的车道线。

记住,算法是死的,场景是活的。多调参、多测试,才能找到最适合你项目的参数组合。