4. 图像预处理:直方图均衡化、高斯滤波与中值滤波、边缘检测(Sobel/Canny)
图像预处理,说白了就是给原始图像“洗个澡、化个妆”。
在立体视觉系统里,这一步直接决定了后续匹配的成败。我见过太多项目,算法选得再好,预处理没做好,结果就是一团糟。
今天咱们就聊聊四个核心操作:直方图均衡化、高斯滤波、中值滤波,还有边缘检测。嗯,一个一个来。
4.1 直方图均衡化:让暗部亮起来
你想想看,如果一张图整体偏暗,或者对比度极低,立体匹配基本就废了。为什么?因为特征点太少,找不到对应关系。
直方图均衡化就是干这个的。它把像素值的分布拉得更均匀,让暗的地方变亮,亮的地方不至于过曝。
我在项目中遇到过这种情况:一个室内场景,光照不均匀,左边亮右边暗。直接做立体匹配,右边区域几乎全是误匹配。用了直方图均衡化之后,效果立竿见影。
// C++ 示例:直方图均衡化
cv::Mat src = cv::imread("left.png", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
cv::equalizeHist(src, dst);
4.2 高斯滤波:平滑噪声,保留边缘
高斯滤波是我最常用的平滑手段。它不像均值滤波那样“一刀切”,而是根据像素距离中心的远近,赋予不同的权重。
说白了,离中心越近的像素,贡献越大;越远的,贡献越小。这个权重分布,就是高斯函数。
// C++ 示例:高斯滤波
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(5, 5), 1.5);
参数 cv::Size(5, 5) 是核大小,1.5 是标准差。核越大,平滑越强,但边缘也会越模糊。
高斯滤波特别适合处理高斯噪声。什么是高斯噪声?就是那种在图像上随机分布、亮度变化平缓的噪点。比如传感器热噪声。
4.3 中值滤波:干掉椒盐噪声
中值滤波的思路很简单:把窗口内的像素排序,取中间值代替中心像素。
它对付椒盐噪声特别有效。什么是椒盐噪声?就是图像上随机出现的黑白点,像撒了盐和胡椒一样。
// C++ 示例:中值滤波
cv::Mat median;
cv::medianBlur(src, median, 5);
参数 5 是窗口大小,同样必须是奇数。
中值滤波的优点是能很好地保留边缘。因为它不取平均,而是取中间值,所以边缘不会像高斯滤波那样被模糊掉。
4.4 边缘检测:Sobel 与 Canny
边缘检测是立体视觉里最关键的一步。为什么?因为边缘是图像中信息最丰富的区域,也是立体匹配最可靠的依据。
4.4.1 Sobel 算子
Sobel 算子是一种基于一阶导数的边缘检测方法。它通过计算图像在 x 方向和 y 方向上的梯度,来找到边缘。
// C++ 示例:Sobel 边缘检测
cv::Mat grad_x, grad_y, grad;
cv::Sobel(src, grad_x, CV_16S, 1, 0, 3);
cv::Sobel(src, grad_y, CV_16S, 0, 1, 3);
cv::convertScaleAbs(grad_x, grad_x);
cv::convertScaleAbs(grad_y, grad_y);
cv::addWeighted(grad_x, 0.5, grad_y, 0.5, 0, grad);
参数 1, 0 表示计算 x 方向梯度,0, 1 表示 y 方向。最后用 addWeighted 把两个方向合并。
4.4.2 Canny 边缘检测
Canny 是目前最主流的边缘检测算法。它比 Sobel 复杂,但效果也更好。
它的流程大致是:
- 高斯滤波去噪
- 计算梯度幅值和方向
- 非极大值抑制(只保留梯度方向上的局部最大值)
- 双阈值检测(高阈值确定强边缘,低阈值连接弱边缘)
// C++ 示例:Canny 边缘检测
cv::Mat edges;
cv::Canny(src, edges, 50, 150);
参数 50 是低阈值,150 是高阈值。这两个值需要根据图像内容调整。
你想想看,Canny 为什么比 Sobel 好?因为它做了非极大值抑制,边缘更细、更干净。而且双阈值机制能有效抑制噪声产生的伪边缘。
4.5 知识体系总览
下面这张图,是我自己整理的预处理流程。你看一眼,心里就有数了。
这张图展示了预处理的标准流程:原始图像 → 直方图均衡化 → 滤波(高斯或中值) → 边缘检测 → 输出结果。
好了,这一章的内容就到这里。预处理是立体视觉的基石,花时间把它做好,后面的匹配和重建会轻松很多。
公众号:蓝海资料掘金营,微信deep3321