4. 图像滤波与增强:均值滤波、高斯滤波、中值滤波、双边滤波、直方图均衡化

各位同学,今天我们来聊聊图像预处理里最基础、也最实用的一块——滤波与增强。

说实话,我刚开始做嵌入式视觉那会儿,总觉得滤波就是“模糊一下”,没什么技术含量。直到有一次在产线上调试一个瑕疵检测项目,光线稍微一变,检测结果就崩了。后来才发现,问题就出在滤波参数没调好。嗯,从那以后,我再也不敢小看这几个滤波函数了。

今天这章,我会把均值、高斯、中值、双边这四种滤波,以及直方图均衡化,一个一个拆开讲。每个我都会结合自己的项目经验,告诉你什么时候用、怎么调参、有什么坑。

4.1 均值滤波:简单但别乱用

均值滤波,说白了就是把每个像素的值,替换成它周围一圈像素的平均值。

它的核函数长这样:

K = 1/9 * [1 1 1]
          [1 1 1]
          [1 1 1]

OpenCV里调用很简单:

import cv2
import numpy as np

img = cv2.imread('image.jpg')
blurred = cv2.blur(img, (5, 5))  # 5x5的核

我个人习惯,均值滤波只用在两种场景:一是快速预览,二是对噪声要求不高的简单平滑。为什么?因为它会把边缘也一起模糊掉。你想想看,边缘处的像素和周围差异很大,一平均,边缘就没了。

注意: 均值滤波对椒盐噪声几乎无效。我曾经在一个户外监控项目里试过,结果噪声没去掉,图像反而糊成一团。

4.2 高斯滤波:更自然的模糊

高斯滤波和均值滤波的区别在于——它给中心像素更高的权重,离中心越远权重越低。这样模糊出来的效果更自然,不会出现那种“方块感”。

它的核函数基于高斯分布:

G(x,y) = (1/(2πσ²)) * exp(-(x²+y²)/(2σ²))

OpenCV用法:

gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)

第三个参数是σ(标准差)。σ越大,图像越模糊。

我在项目中遇到过一个情况:用高斯滤波做预处理,然后跑边缘检测。σ设成1.0时,边缘太碎;设成3.0时,边缘又太弱。最后调了半天,发现σ=1.8效果最好。所以这个参数,真的得试。

小技巧: 核大小一般取奇数,比如3、5、7。σ一般取核大小的1/6左右,比如5x5的核,σ≈0.8。

4.3 中值滤波:去椒盐噪声的利器

中值滤波的原理很简单——把窗口内的像素排序,取中间值替换中心像素。

为什么它对椒盐噪声特别有效?因为椒盐噪声是极端值(要么0,要么255),排序后它肯定在两端,中间值根本不受影响。

median = cv2.medianBlur(img, 5)  # 5x5窗口

我曾经在一个工业相机项目里,图像上全是白点(椒盐噪声)。用均值滤波试了,没用;高斯滤波试了,也没用。最后换成中值滤波,一下就干净了。嗯,这个坑我踩过,所以印象特别深。

注意: 中值滤波的窗口不宜过大。我见过有人用15x15的窗口,结果图像细节全没了,像水彩画一样。一般3x3或5x5就够了。

4.4 双边滤波:保边去噪

双边滤波是这四种里最“聪明”的一个。它同时考虑了两个因素:空间距离和像素值差异。

  • 空间距离:离中心越近,权重越大(和高斯一样)
  • 像素值差异:和中心像素值越接近,权重越大

这样一来,在平坦区域它会平滑噪声,在边缘处它会保留边缘。

bilateral = cv2.bilateralFilter(img, 9, 75, 75)

参数说明:

  • 第一个参数:图像
  • 第二个参数:窗口直径(一般9或15)
  • 第三个参数:颜色空间σ(越大,颜色差异容忍度越高)
  • 第四个参数:空间σ(越大,空间影响范围越广)

我个人习惯,在做人脸美化或皮肤检测时,双边滤波是首选。它能把皮肤上的小瑕疵去掉,同时保留眼睛、嘴巴这些边缘。

避坑指南: 双边滤波计算量很大。在嵌入式设备上,我建议窗口直径不要超过9,否则帧率会掉得很厉害。我曾经在树莓派上试过15x15的窗口,结果只有2帧/秒,根本没法用。

4.5 直方图均衡化:让图像“亮”起来

直方图均衡化,说白了就是把图像的灰度分布“拉平”。如果一张图整体偏暗,均衡化后暗部细节就出来了。

原理很简单:统计每个灰度级的像素数量,然后重新映射,让各个灰度级的像素数量尽量均匀。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)

对于彩色图像,一般先转到YUV或HSV空间,只对亮度通道做均衡化:

img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])
img_equalized = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)

我在项目中遇到过一个场景:在昏暗的仓库里做条码识别。原图几乎看不清条码,但直方图均衡化后,条码清晰可见。不过要注意,它也会放大噪声。

注意: 直方图均衡化不适合所有场景。如果图像本身对比度已经很高,均衡化反而会让它失真。我建议先看直方图分布,再决定要不要用。

4.6 四种滤波对比总结

滤波类型 去噪效果 边缘保留 计算速度 适用场景
均值滤波 一般 快速预览、简单平滑
高斯滤波 较好 一般 通用去噪、预处理
中值滤波 椒盐噪声极好 较好 中等 椒盐噪声、工业检测
双边滤波 较好 极好 保边去噪、人脸美化

4.7 实战建议

最后,我给大家几个实战中的建议:

  1. 先看噪声类型:如果是椒盐噪声,直接上中值滤波;如果是高斯噪声,用高斯滤波。
  2. 参数从小往大调:核大小从3开始,不行再试5、7。别一上来就15x15。
  3. 嵌入式设备要权衡:双边滤波虽然效果好,但计算量大。如果帧率要求高,优先考虑高斯或中值。
  4. 直方图均衡化不是万能药:它适合低对比度图像,但会放大噪声。如果噪声严重,先滤波再均衡化。

好了,这章的内容就到这里。下一章我们会讲边缘检测,到时候你会发现,滤波做得好不好,直接决定了边缘检测的效果。嗯,到时候见。