第4章:图像预处理与滤波

各位同学,欢迎来到第四章。这一章我们聊点实在的——图像预处理。说白了,就是摄像头拍出来的原始图像,不能直接用。为什么?因为传感器有噪声,光照不均匀,对比度不够。我做了这么多年车载视觉,可以负责任地告诉你:预处理做得好,后面算法省一半的力气。

4.1 高斯滤波:最常用的平滑工具

高斯滤波,我愿称之为「温柔的模糊」。它不像均值滤波那样一刀切,而是根据高斯分布给中心像素更大的权重。你想想看,离中心越近的像素,理论上和中心像素越相关,对吧?

车载场景下,高斯滤波最常见的用途是降噪。比如夜间摄像头拍到的暗部噪声,用高斯滤波轻轻过一遍,效果立竿见影。

// 高斯滤波示例(C++ + OpenCV)
cv::Mat src = cv::imread("road.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
cv::GaussianBlur(src, dst, cv::Size(5, 5), 1.5);
// 参数说明:核大小5x5,sigma=1.5
我的经验:核大小选奇数,3x3或5x5最常用。sigma值我一般设1.0到2.0之间。太大图像会糊,太小没效果。

4.2 中值滤波:椒盐噪声的克星

中值滤波的原理很简单——取邻域内所有像素的中位数作为输出。它不像高斯滤波那样做加权平均,而是直接排序取中间值。

为什么它对椒盐噪声特别有效?因为椒盐噪声是极端的亮或暗点,排序后它们会跑到两端,中位数根本不受影响。我在项目中遇到过摄像头模组有坏点的情况,中值滤波一上,坏点直接消失。

cv::Mat median_result;
cv::medianBlur(src, median_result, 5);
// 核大小5x5,注意必须是奇数
注意:中值滤波计算量比高斯滤波大。在嵌入式平台上,如果核超过7x7,帧率可能会掉。我曾经在TDA4平台上踩过这个坑,后来改用3x3核,效果也够用。

4.3 双边滤波:保边去噪的利器

双边滤波,说白了就是「考虑空间距离 + 像素值差异」的滤波。它有两个sigma参数:sigma_space控制空间权重,sigma_color控制像素值权重。

为什么它能保边?因为边缘两侧的像素值差异大,sigma_color会让它们互相影响变小。我经常用它来做美颜效果的前处理,或者在做语义分割前保留边缘细节。

cv::Mat bilateral_result;
cv::bilateralFilter(src, bilateral_result, 9, 75, 75);
// 参数:直径9,sigma_color=75,sigma_space=75
核心要点:双边滤波在嵌入式上很慢。我建议只在关键帧上用,或者缩小图像尺寸后再用。别每帧都跑,帧率会崩。

4.4 直方图均衡化:让暗部亮起来

直方图均衡化,就是把图像的灰度分布拉平。原本集中在暗部的像素,会被映射到整个0-255范围。效果就是暗部细节变清晰了。

车载场景下,隧道出入口、夜间行车,直方图均衡化非常实用。但要注意——它会把噪声也放大。我曾经在夜间测试时,均衡化后整张图全是噪点,后来加了高斯滤波前置处理才解决。

cv::Mat equalized;
cv::equalizeHist(src, equalized);
// 只适用于单通道灰度图
方法 优点 缺点
全局直方图均衡化 简单、速度快 噪声放大、过曝
CLAHE 局部自适应、不过曝 计算量稍大

4.5 CLAHE:自适应直方图均衡化

CLAHE,全称是Contrast Limited Adaptive Histogram Equalization。它把图像分成一个个小块,每个块单独做均衡化,然后用插值消除块之间的边界。最关键的是——它限制了对比度增强的幅度,不会像全局均衡化那样把噪声放得太大。

我个人习惯在夜间行车视频上用CLAHE。效果比全局均衡化好太多,暗部细节清晰,天空也不会过曝。

cv::Ptr clahe = cv::createCLAHE();
clahe->setClipLimit(2.0);  // 对比度限制
clahe->setTilesGridSize(cv::Size(8, 8));  // 分块大小
cv::Mat clahe_result;
clahe->apply(src, clahe_result);
调参建议:clipLimit我一般设2.0到4.0之间。太大图像会不自然,太小效果不明显。分块大小8x8或16x16都行,看图像分辨率。

4.6 图像金字塔:多尺度分析的基石

图像金字塔,说白了就是图像的「缩放版本集合」。高斯金字塔是不断下采样,拉普拉斯金字塔是重建细节。

为什么需要它?因为很多算法需要多尺度信息。比如目标检测,大目标在小尺度上检测,小目标在大尺度上检测。我在做车道线检测时,就用高斯金字塔做多尺度融合,效果比单尺度好很多。

std::vector pyramid;
pyramid.push_back(src);
for (int i = 0; i < 3; i++) {
    cv::Mat down;
    cv::pyrDown(pyramid.back(), down);
    pyramid.push_back(down);
}
// 生成4层高斯金字塔
注意:金字塔层数不要太多。每层分辨率减半,3-4层就够了。层数太多,底层图像太小,信息丢失严重。

4.7 实战建议与避坑指南

好了,知识点讲完了。我总结几条实战经验:

  • 滤波顺序很重要:先降噪,再做增强。先做直方图均衡化再滤波,噪声会被放大。
  • 嵌入式平台要权衡:双边滤波和CLAHE计算量大,建议只在关键帧上用。普通帧用高斯滤波+全局均衡化就够了。
  • 参数调优靠实验:没有万能参数。我建议做一个参数扫描脚本,跑一遍数据集,选效果最好的。
我曾经踩过的坑:有一次在量产项目中,用了CLAHE后图像出现块状伪影。后来发现是分块大小设得太小,块之间插值不够平滑。改成16x16后问题解决。

嗯,这一章就到这里。下一章我们聊色彩空间转换与白平衡,那是另一个有意思的话题。各位同学,动手试试吧!