第1章:均值滤波与高斯滤波——原理详解与实战
各位同学,欢迎来到《手机摄影去噪算法实战手册》的第一章。
今天咱们聊点最基础、但也最核心的东西——均值滤波和高斯滤波。
你可能会想:“这俩不是课本上老掉牙的算法吗?” 嗯,我当年也这么想。直到我在一个夜景项目里,被噪声折磨得死去活来,才发现——基础不牢,地动山摇。
核心观点:均值滤波和高斯滤波,是所有去噪算法的“地基”。搞懂它们,你才能理解更高级的算法(比如双边滤波、NLM)。
1.1 均值滤波:最简单的去噪方式
原理是什么?
说白了,均值滤波就是“拉帮结派”。
对于图像中的每个像素,我把它周围一圈像素的RGB值加起来,然后除以像素总数。这个平均值,就是新像素的值。
举个例子:一个3x3的窗口,中心像素原来值是100,周围8个像素分别是90、110、95、105... 平均下来可能变成102。嗯,噪声就被“平均”掉了。
我的经验:均值滤波在去除高斯噪声时效果还行,但遇到椒盐噪声就抓瞎了。为什么?因为椒盐噪声是极端值(0或255),一平均会把整个区域都带偏。
OpenCV实现
代码其实就一行。我习惯用 cv2.blur() 或者 cv2.boxFilter()。
import cv2
import numpy as np
# 读取图像
img = cv2.imread('noisy_image.jpg')
# 均值滤波,核大小为5x5
result = cv2.blur(img, (5, 5))
# 或者用 boxFilter,效果一样
# result = cv2.boxFilter(img, -1, (5, 5))
cv2.imshow('Original', img)
cv2.imshow('Mean Filter', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
核大小的影响
核大小,就是那个“窗口”的尺寸。3x3、5x5、7x7... 越大,参与平均的像素越多,平滑效果越强。
但代价也很明显——图像会变模糊。我做过一个测试:
| 核大小 | 去噪效果 | 细节保留 | 适用场景 |
|---|---|---|---|
| 3x3 | 较弱 | 较好 | 轻度噪声 |
| 5x5 | 中等 | 一般 | 日常使用 |
| 7x7 | 较强 | 较差 | 噪声严重 |
| 9x9及以上 | 极强 | 几乎丢失 | 不推荐 |
避坑指南:我曾经在一个项目里,为了去噪把核设成了15x15。结果图像糊得像打了马赛克,被领导骂了一顿。记住:核大小不是越大越好,够用就行。
1.2 高斯滤波:更聪明的平均
为什么需要高斯滤波?
你想想看,均值滤波对每个邻居都一视同仁,这合理吗?
当然不合理!离中心像素越近的像素,应该和中心像素关系更密切。高斯滤波就是基于这个想法——距离越近,权重越大。
原理详解
高斯滤波的核心是高斯核。这个核里的数值,服从二维高斯分布。中心值最大,越往外越小。
举个例子,一个3x3的高斯核(σ=1.0)大概长这样:
[[0.075, 0.124, 0.075],
[0.124, 0.204, 0.124],
[0.075, 0.124, 0.075]]
看到没?中心是0.204,四个角只有0.075。这样加权平均下来,中心像素的“话语权”更大,边缘像素的影响被削弱。
关键区别:均值滤波是“平均主义”,高斯滤波是“精英主义”。高斯滤波保留的细节更多,图像看起来更自然。
OpenCV实现
用 cv2.GaussianBlur() 就行。注意,核大小必须是奇数。
import cv2
import numpy as np
img = cv2.imread('noisy_image.jpg')
# 高斯滤波,核大小5x5,sigmaX=1.0
result = cv2.GaussianBlur(img, (5, 5), 1.0)
# sigmaX 控制高斯分布的形状
# sigmaX 越大,权重分布越平缓,平滑效果越强
# sigmaX 越小,中心权重越集中,平滑效果越弱
cv2.imshow('Original', img)
cv2.imshow('Gaussian Filter', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
核大小 vs sigma
这里有个小坑:核大小和sigma是相互关联的。我个人的习惯是:
- 核大小 = 3x3 时,sigma 设 0.5 ~ 0.8
- 核大小 = 5x5 时,sigma 设 1.0 ~ 1.5
- 核大小 = 7x7 时,sigma 设 1.5 ~ 2.0
当然,OpenCV 也允许你只设核大小,不设 sigma。它会自动计算一个合理的值。
我的小技巧:如果你不确定 sigma 设多少,直接用 cv2.GaussianBlur(img, (5,5), 0)。OpenCV 会根据核大小自动计算 sigma,效果通常还不错。
1.3 实战:去除高斯噪声
生成噪声图像
咱们先模拟一张带高斯噪声的图,然后分别用均值滤波和高斯滤波去噪,看看效果。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
# 添加高斯噪声
mean = 0
sigma = 25 # 噪声强度
noise = np.random.normal(mean, sigma, img.shape).astype(np.uint8)
noisy_img = cv2.add(img, noise)
# 均值滤波
mean_filtered = cv2.blur(noisy_img, (5, 5))
# 高斯滤波
gaussian_filtered = cv2.GaussianBlur(noisy_img, (5, 5), 1.0)
# 显示结果
plt.figure(figsize=(12, 4))
plt.subplot(1, 4, 1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1, 4, 2), plt.imshow(noisy_img, cmap='gray'), plt.title('Noisy')
plt.subplot(1, 4, 3), plt.imshow(mean_filtered, cmap='gray'), plt.title('Mean Filter')
plt.subplot(1, 4, 4), plt.imshow(gaussian_filtered, cmap='gray'), plt.title('Gaussian Filter')
plt.show()
效果对比
跑完代码你会发现:
- 均值滤波:噪声被去掉了,但图像有点“糊”,边缘不清晰。
- 高斯滤波:噪声去得干净,图像更自然,边缘保留得更好。
为什么会这样?因为高斯滤波给了中心像素更高的权重,所以边缘信息被保留得更多。
结论:对于高斯噪声,高斯滤波是比均值滤波更好的选择。但如果你追求速度(比如实时视频处理),均值滤波更快,因为计算量更小。
1.4 总结与思考
这一章咱们聊了均值滤波和高斯滤波的原理、实现和实战。
说白了,这两个算法都是“局部平均”的思路,区别在于权重分配。均值滤波是“人人平等”,高斯滤波是“能者多劳”。
嗯,这里要注意:没有完美的算法。均值滤波快但模糊,高斯滤波好但慢一点。实际项目中,要根据需求选。
我记得有一次做手机相机的实时预览去噪,为了帧率,我不得不选了均值滤波。虽然效果差一点,但用户感觉不到延迟,这才是关键。