第二章:图像预处理基础——色彩空间转换、高斯滤波与Canny边缘检测
各位同学好,我是你们的自动驾驶感知工程师。今天咱们聊聊图像预处理里最基础、也最关键的几个操作。说实话,很多刚入行的同学觉得预处理就是“调参”,随便搞搞就行。但我在实际项目中吃过不少亏——有一次车道线检测在晴天表现完美,一到阴雨天就崩了,最后发现是色彩空间没选对。嗯,从那以后我再也不敢小看预处理了。
2.1 色彩空间转换:为什么RGB不够用?
咱们摄像头拍出来的图像,默认是RGB格式。R、G、B三个通道,每个0-255。这玩意儿对人眼很友好,但对机器来说,有个致命问题——它对光照变化太敏感了。
你想想看,同样是黄色车道线,晴天和阴天的RGB值能差出一大截。我当年做第一个车道线项目时,用RGB阈值分割,调了一周参数,结果换个场景就废了。后来换成HSV,半小时搞定。
2.1.1 RGB vs HSV
| 色彩空间 | 通道含义 | 光照鲁棒性 | 典型应用 |
|---|---|---|---|
| RGB | 红、绿、蓝 | 差 | 显示、存储 |
| HSV | 色调、饱和度、明度 | 好 | 颜色分割、目标检测 |
| 灰度 | 亮度 | 中等 | 边缘检测、特征提取 |
HSV里,H(色调)代表颜色本质,S(饱和度)代表颜色纯度,V(明度)代表亮度。你看,亮度和颜色分开了。所以提取黄色车道线时,我只需要固定H的范围,V随便变都不影响。这就是为什么自动驾驶里HSV用得比RGB多。
核心结论:做车道线检测,优先用HSV做颜色分割。RGB更适合做显示,不适合做分析。
2.1.2 代码实现:RGB转HSV
import cv2
import numpy as np
# 读取图像
img = cv2.imread('road.jpg') # OpenCV默认是BGR,不是RGB!
# 转HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 定义黄色范围(我常用的值)
lower_yellow = np.array([20, 100, 100])
upper_yellow = np.array([30, 255, 255])
# 提取黄色区域
mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
result = cv2.bitwise_and(img, img, mask=mask)
个人经验:H的范围别设太死。我一般给±10的余量。比如黄色H是25,我就设15-35。不同摄像头色彩偏差不一样,建议先拍几张图看看直方图。
2.2 高斯滤波:去噪但不模糊边缘
图像里总有噪声。椒盐噪声、高斯噪声,乱七八糟的。直接做边缘检测,噪声会被当成边缘,结果惨不忍睹。所以要先滤波。
滤波方法很多,均值滤波、中值滤波、高斯滤波。我个人习惯用高斯滤波。为什么?因为它模拟了人眼的视觉特性——中心权重高,边缘权重低。说白了,就是“近朱者赤,近墨者黑”。
2.2.1 高斯核原理
高斯滤波的核心是高斯核。一个5x5的高斯核长这样:
1/273 × [
[1, 4, 7, 4, 1],
[4, 16, 26, 16, 4],
[7, 26, 41, 26, 7],
[4, 16, 26, 16, 4],
[1, 4, 7, 4, 1]
]
你看,中心41最大,角落1最小。这样平滑时,中心像素贡献最大,边缘像素贡献小。既去除了噪声,又保留了边缘的锐度。
避坑指南:我曾经把高斯核设得太大(比如15x15),结果车道线边缘全糊了,检测精度直接掉20%。核大小一般取3x3或5x5就够了,别贪心。
2.2.2 代码实现
# 高斯滤波
blurred = cv2.GaussianBlur(img, (5, 5), 1.5)
# 参数说明:
# (5,5) 是核大小,必须奇数
# 1.5 是标准差σ,控制平滑程度
σ越大,图像越模糊。我一般设1.0-2.0之间。太小了去噪效果差,太大了边缘丢失。
2.3 Canny边缘检测:从图像到轮廓
好了,现在图像干净了。下一步是找边缘。Canny算法是业界标准,没有之一。为什么?因为它做了三件事:去噪、找梯度、双阈值筛选。
2.3.1 Canny四步走
- 高斯滤波——去噪,上面刚讲过
- 计算梯度幅值和方向——用Sobel算子,得到每个像素的梯度大小和方向
- 非极大值抑制——只保留梯度方向上的局部最大值,让边缘变细
- 双阈值检测——高阈值确定强边缘,低阈值连接弱边缘
第四步是关键。高阈值设高了,边缘断断续续;设低了,噪声全进来了。我一般设高阈值100-150,低阈值50-75。比例大概2:1到3:1。
我的调参口诀:高阈值定主边缘,低阈值补断点。先调高阈值,再调低阈值。
2.3.2 代码实现
# 先转灰度
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
# Canny边缘检测
edges = cv2.Canny(gray, 50, 150)
# 50是低阈值,150是高阈值
# 返回二值图像,边缘为白色(255),背景为黑色(0)
你看,代码就一行。但这一行背后,是几十年的计算机视觉研究积累。
2.4 本章知识体系
下面这张图,是我自己画的预处理流程。建议你保存下来,每次做项目前看一眼。
2.5 实战建议
最后,给你几个我在项目中踩过的坑:
- 别在RGB里做阈值分割——光照一变就废,血的教训
- 高斯核大小和σ要匹配——核越大,σ也要越大,否则效果奇怪
- Canny阈值不是固定的——不同场景要调,我一般先试(50,150),再微调
- 预处理顺序不能乱——先转色彩空间,再滤波,最后边缘检测。顺序错了,效果天差地别
一个小技巧:调试时,把每一步的中间结果都显示出来。看一眼灰度图、看一眼滤波后的图、看一眼边缘图。哪里出问题一目了然。我调试时至少开4个窗口。
好了,这一章就到这里。预处理是基本功,但基本功往往决定上限。你把这些搞透了,后面做车道线拟合、地图匹配才会顺手。
可可精品资料库