3、图像滤波技术:均值滤波、高斯滤波、中值滤波原理与实现
图像滤波,说白了就是给图像「去噪」或者「磨皮」。
你想想看,摄像头拍出来的照片,或多或少都有噪点。可能是传感器温度太高,也可能是光线不够。这时候,滤波技术就派上用场了。
我个人习惯把滤波分成两类:线性滤波和非线性滤波。均值滤波和高斯滤波属于前者,中值滤波属于后者。嗯,咱们一个一个聊。
3.1 均值滤波:简单粗暴的平滑
均值滤波的原理,其实特别直观。就是拿一个窗口(比如3x3的方块),在图像上滑动。每到一个位置,就把窗口内所有像素的灰度值加起来,取个平均值,然后赋值给窗口中心的那个像素。
举个例子,你有一块3x3的区域,像素值分别是:
| 10 | 12 | 11 |
| 9 | 15 | 13 |
| 8 | 14 | 12 |
平均值 = (10+12+11+9+15+13+8+14+12) / 9 = 104 / 9 ≈ 11.56。中心像素就变成了12(四舍五入)。
我在项目中遇到过一个问题:用均值滤波去噪,结果图像边缘变得模糊了。为什么呢?因为均值滤波把边缘的突变也「平均」掉了。说白了,它一视同仁,不分你我。
g(x, y) = (1 / (m * n)) * Σ f(i, j)
其中 m*n 是窗口大小,f(i,j) 是原始像素值。
3.2 高斯滤波:带权重的平滑
高斯滤波和均值滤波的区别在哪?
均值滤波是「平均主义」,窗口内每个像素的权重都一样。高斯滤波则不同,它认为离中心越近的像素,应该越重要。权重按照高斯分布(也就是正态分布)来分配。
为什么会这样?因为自然界的噪声,很多都服从高斯分布。用高斯滤波去处理,理论上更科学。
我记得有一次做工业检测,产品表面有细微的纹理噪声。均值滤波把纹理细节也抹掉了,导致检测失败。换成高斯滤波后,噪声去掉了,纹理细节保留得还不错。
高斯滤波的权重计算,依赖于一个参数——σ(标准差)。σ越大,权重分布越平缓,平滑效果越强;σ越小,权重越集中在中心,平滑效果越弱。
OpenCV里实现高斯滤波,代码很简单:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('noisy_image.jpg')
# 高斯滤波,窗口大小5x5,sigma=1.0
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 1.0)
# 显示结果
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.3 中值滤波:对抗椒盐噪声的利器
中值滤波,名字就暴露了它的原理——取窗口内所有像素的中位数,而不是平均值。
你想想看,如果图像上有一些「椒盐噪声」(就是那种黑白点),均值滤波会把噪声扩散到周围像素,导致一片区域都变脏。但中值滤波不一样,它直接忽略掉极端值,只取中间的那个数。
我曾经处理过一张监控摄像头拍的照片,因为传输线路干扰,图像上全是黑白噪点。均值滤波和高斯滤波都试了,效果很差。最后用中值滤波,窗口大小3x3,一遍就干净了。
中值滤波的窗口大小,我建议用奇数。3x3、5x5、7x7都行。窗口越大,去噪能力越强,但图像细节损失也越大。
# 中值滤波,窗口大小5x5
median_blur = cv2.medianBlur(img, 5)
# 显示结果
cv2.imshow('Median Blur', median_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.4 三种滤波的对比与选择
说了这么多,到底该用哪个?我整理了一个表格,方便你对比:
| 滤波类型 | 原理 | 适用场景 | 缺点 |
|---|---|---|---|
| 均值滤波 | 取窗口内平均值 | 噪声均匀、对边缘要求不高 | 边缘模糊严重 |
| 高斯滤波 | 取加权平均值(高斯权重) | 高斯噪声、需要保留一定细节 | 计算量稍大 |
| 中值滤波 | 取窗口内中位数 | 椒盐噪声、脉冲噪声 | 对高斯噪声效果一般 |
我个人习惯是:
- 看到图像上有明显的黑白点(椒盐噪声),直接上中值滤波。
- 如果噪声比较均匀,像是一层薄雾(高斯噪声),用高斯滤波。
- 如果只是简单去噪,对效果要求不高,均值滤波也能凑合。
3.5 实战:综合应用
最后,咱们来个完整的例子。假设你有一张带噪声的图像,想看看三种滤波的效果对比:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('noisy_image.jpg')
# 均值滤波
mean_blur = cv2.blur(img, (5, 5))
# 高斯滤波
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 1.0)
# 中值滤波
median_blur = cv2.medianBlur(img, 5)
# 拼接显示
result = np.hstack((img, mean_blur, gaussian_blur, median_blur))
cv2.imshow('Original | Mean | Gaussian | Median', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
嗯,到这里,三种滤波的原理和实现就讲完了。记住一句话:没有最好的滤波,只有最合适的滤波。多试几次,你就能找到感觉。
下一章,咱们聊聊更高级的滤波技术——双边滤波和导向滤波。到时候你会发现,原来滤波还能这么玩。