本节摘要:滤波是预处理的第一道工序,目标是压掉噪声、给后续算法一个干净的输入。本节讲均值、高斯、中值、双边四种滤波器的最小例子与机理,核心结论是"噪声类型决定滤波器":高斯噪声用高斯滤波,椒盐噪声只有中值滤波能救,既要去噪又要保边缘时上双边滤波。卷积窗口与 σ 参数的选择依据一并给出。
阅读完本节,你应当能够:
找一个有噪声的场景(老照片扫描件、暗光拍照都行),四行代码各滤一遍:
import cv2 img = cv2.imread('noisy.jpg') mean_blur = cv2.blur(img, (5, 5)) # 均值滤波 gauss_blur = cv2.GaussianBlur(img, (5, 5), sigmaX=1.5) # 高斯滤波 median_blur = cv2.medianBlur(img, 5) # 中值滤波 bilateral = cv2.bilateralFilter(img, 9, 75, 75) # 双边滤波
四个窗口摆在一起对比,你会有非常直观的感受:均值滤波把整张图糊成一团,细节和噪声同归于尽;高斯滤波也糊,但过渡更自然;中值滤波对那些孤立的黑白噪点效果惊人——原图上星星点点的"雪花"几乎消失,而物体轮廓依然清晰;双边滤波最微妙,平坦区域的噪声被压掉,边缘却几乎没动。
如果你手头没有噪声图,可以自己造椒盐噪声来验证:
import numpy as np noisy = img.copy() coords = np.random.randint(0, high=512, size=(2000, 2)) for y, x in coords: noisy[y, x] = 0 if np.random.rand() > 0.5 else 255 # 随机撒黑白点
撒完点再分别用高斯和中值去处理,差异会明显到不需要我解释——高斯滤波后的图里那些黑点变成了灰色的"晕",中值滤波后它们直接消失。这个实验值得亲手做一次,它比任何文字都更能建立"选型"的直觉。
均值滤波和高斯滤波本质上是一家人,都用"卷积"操作:一个小方阵(卷积核)在图像上滑动,每到一个位置,核里的权重与覆盖的像素相乘再求和,结果作为输出图该位置的值。
均值滤波的核最简单——所有权重相等。3×3 的核就是九个 1/9,输出是邻域九个像素的平均值。它的物理直觉是"相信群体,抹平个体":一个像素的值偏离邻居太远,就被邻居平均掉。噪声是随机的、正负相抵的,平均之后幅度缩小;但边缘也是"偏离邻居"的像素,于是同样被平均掉——这就是均值滤波糊掉细节的根源。
高斯滤波把"人人平等"改成"近邻话语权大":核内权重按二维高斯分布(俗称钟形曲线)分配,中心像素权重最大,距离越远权重越小。这样输出既平滑了噪声,又保留了部分结构信息,因为中心像素自己还占大头。参数有两个:核尺寸越大、σ 越大,平滑力度越强。两者的关系要理解成"σ 决定钟的胖瘦,核尺寸决定截到多远"——一般让核半径覆盖到 3σ 左右即可,所以 (5,5) 配 σ≈1.5、(7,7) 配 σ≈2 是常见起点。
中值滤波不做加权求和,它把窗口内所有像素值排序,取中间那个。这个改动看似不大,效果却质变,原因在统计性质上:椒盐噪声的值是 0 或 255 这类极端值,排序后必然沉到两端,中间值几乎不可能被它占据——所以极端噪声被"结构性忽略"。而线性滤波是求平均,一个 255 的噪点会把平均值硬拉高十几二十,留下一个灰色晕斑。
代价也有:中值滤波要排序,比加权求和慢;而且它对高斯噪声(每个像素都带小幅随机扰动)的效果反而不如高斯滤波好,因为中值对"分布居中的扰动"不敏感。工具没有万能的,病要对症。
高斯滤波的权重只看空间距离。双边滤波加了一个条件:像素值差得多的邻居,权重也要打折。于是平坦区域内(大家值相近)它表现得像高斯滤波,正常去噪;跨过边缘时(另一侧像素值突变)对面像素权重骤降,输出几乎不受边缘另一侧影响——边缘得以保留。两个关键参数分别控制这两层:d 是空间邻域直径,sigmaColor 是颜色相似度阈值(75 是常见起点,值越大越接近普通高斯)。
天下没有免费的午餐:双边滤波的计算量约是高斯的数倍,而且参数不当时会留下"卡通感"(平坦区过度平滑、边缘硬得像画出来的)。实时视频场景里慎用,离线精修场景里真香。
四种滤波器的机制对比:
| 滤波器 | 机制 | 强项 | 弱项 | 典型场景 |
|---|---|---|---|---|
| 均值 blur | 邻域等权平均 | 实现最简单、快 | 糊边缘、对椒盐无效 | 快速降采样前粗处理 |
| 高斯 GaussianBlur | 距离加权平均 | 高斯噪声克星、平滑自然 | 边缘仍会变钝 | Canny 前置、金字塔缩放 |
| 中值 medianBlur | 窗口取中值 | 椒盐噪声克星、保边缘 | 慢一些、核须为奇数 | 扫描件、老照片去雪花 |
| 双边 bilateralFilter | 距离加相似度加权 | 去噪且保边缘 | 最慢、参数敏感 | 美颜、工业检测保边去噪 |
我的默认起点:高斯滤波 (5,5)、σ 由函数自动按核尺寸推算(传 0 即可,需要更强平滑时再显式给 σ);中值滤波核 5;双边滤波 d=9、两个 sigma 都是 75。从起点开始,按结果往两边调,比每次从零猜快得多。核尺寸必须是正奇数,这是硬性要求——中值需要排他居中的那个数,偶数窗口没有"正中间"。
滤波不是独立环节,它服务于下游。接阈值分割,滤波强度要够,否则噪声点在二值化后变成虚假小白斑;接 Canny 边缘检测,OpenCV 文档明确建议先做高斯平滑,因为 Canny 对噪声敏感,噪声会产生大量假边缘;接特征匹配,滤波不足会导致特征点扎堆在噪声上。原则是:先看下游算法对噪声的容忍度,再定滤波力度,而不是无脑调到看起来舒服。
有初学者先阈值化再滤波——顺序反了。滤波的设计目标是压"灰度域里的连续噪声",二值化之后噪声已经凝固成斑点,线性滤波对它基本无效,只能靠形态学(下一节的开运算)收拾。记住链路顺序:灰度域滤波 → 二值化 → 形态学修正。
⚠️ 常见坑:彩色图上直接做强滤波,颜色交界处出现"串色"。三个通道各自独立滤波,边界处不同通道的平滑程度不一致,混出脏色。更好做法是转到 HSV 等空间,只滤亮度通道,或降低滤波强度。
💡 关键直觉:判断"该滤多强"的土办法——把处理结果放大看边缘。边缘还能看出清晰的两级过渡,说明滤波没过头;边缘变成一条缓坡,说明已经伤了结构,往后退一档。滤波的目标从来不是"好看",是"下游算法吃得下"。
实际拿到的图不会标注自己是什么噪声,诊断靠放大观察:
| 噪声类型 | 视觉特征 | 来源 | 对策 |
|---|---|---|---|
| 高斯噪声 | 整图均匀的细密颗粒,暗部更明显 | 传感器电子噪声、高 ISO | 高斯滤波 |
| 椒盐噪声 | 随机分布的孤立黑白点 | 位传输错误、老扫描仪 | 中值滤波 |
| 斑点噪声 | 明暗相间的颗粒团(医学超声常见) | 相干成像原理 | 中值或专用去斑 |
| 量化噪声 | 色带、台阶状过渡 | 位深不足 | 抖动或提高位深 |
诊断动作固定:放大四百百分比看平坦区,颗粒均匀细密是高斯系、孤立极端点是椒盐系、有颗粒团是斑点系。对症之后再谈参数,药不对症时调参数只是安慰剂。
会更强,但"强"的方向未必是你要的。两次高斯近似一次更大 σ 的高斯,平滑力度翻倍的同时细节损失也翻倍。需要更强平滑时直接加大核或 σ,语义更清晰;两遍滤波只在"第一遍后仍残留少量噪点"的微调场景有意义。
排序取中需要"正中间"的那个数,偶数个元素没有唯一的中值(须再平均,破坏了"取真实像素值"的性质)。同理高斯核的尺寸约定奇数,是为了有明确的中心像素对齐。
物理上传感器噪声在三个通道相关(同一像素的读出过程),但滤波时 OpenCV 按通道独立处理。实际影响是彩色噪声去除后可能出现轻微色偏,观感要求高的场景在处理后做一次轻微的色彩平衡,或改在亮度通道滤波(转 HSV 只处理 V 通道再转回)。
图像的噪声压下去了,但位置可能还不对——拍摄倾斜、视角透视都会让目标变形。下一节的几何变换解决"摆正"的问题。