3. 经典降噪算法(空间域)
低照度图像,说白了就是信噪比极低。噪点像沙子一样铺满画面,暗部细节几乎被淹没。这时候你第一个想到的,往往是空间域滤波——直接在像素点上做文章。
这一章我们聊三种最经典的算法:均值滤波、高斯滤波、中值滤波。它们原理简单,实现也不复杂,但用在低照度场景下,各有各的“坑”。
3.1 均值滤波:最朴素的想法
均值滤波的思路很直接:用邻域内所有像素的平均值代替中心像素。你想想看,如果噪点是随机独立的,平均一下应该能抵消一部分。
数学上就是:
g(x,y) = (1 / (m×n)) * Σ f(i,j) (i,j ∈ 邻域)
其中 m×n 是核大小,f 是输入图像,g 是输出。
核心特点:
- 计算极快,适合实时预处理
- 对高斯噪声有一定抑制效果
- 但会模糊边缘和纹理
我在项目中遇到过一件事:用 5×5 均值滤波处理一张夜间监控画面,噪点确实少了,但车牌上的数字直接糊成一团。嗯,这就是均值滤波的代价——它把所有像素“一视同仁”,边缘和噪声一起被抹平。
低照度场景的致命伤:
- 暗区信噪比极低,均值滤波后边缘几乎不可恢复
- 核越大,模糊越严重,细节丢失不可逆
- 对椒盐噪声(脉冲噪声)效果很差——一个异常值就能拉偏整个均值
3.2 高斯滤波:加权的智慧
高斯滤波比均值滤波聪明一点。它不再简单平均,而是根据距离中心像素的远近,赋予不同的权重——越近权重越大,越远权重越小。权重由高斯函数决定。
二维高斯函数:
G(x,y) = (1 / (2πσ²)) * exp(-(x²+y²) / (2σ²))
σ 控制着权重的分布范围。σ 越大,权重分布越平坦,滤波效果越接近均值滤波;σ 越小,中心像素的权重越大,保留细节的能力越强。
| 参数 | 作用 | 低照度场景建议 |
|---|---|---|
| 核大小 | 决定邻域范围 | 3×3 或 5×5,过大则细节全失 |
| σ | 控制权重分布 | 0.8~1.5,太小降噪不足,太大边缘模糊 |
我个人习惯在低照度场景下先用 σ=1.0 的 3×3 高斯滤波试水。为什么?因为 3×3 核计算量小,σ=1.0 能在降噪和保边之间取得一个相对平衡的点。
避坑指南:我曾经在 σ 设置上吃过亏。有一次处理暗光环境下的纹理细节(比如布料褶皱),我把 σ 设到了 2.5,结果纹理全没了,画面像涂了一层油。后来我改成 σ=1.2,配合后续的锐化操作,效果才勉强能看。
高斯滤波的局限性也很明显:它本质上还是低通滤波。低照度图像中的噪声和细节在频域上往往重叠严重,高斯滤波在抑制噪声的同时,必然损失高频细节。
3.3 中值滤波:对抗异常值的利器
中值滤波的思路完全不同。它不再做加权平均,而是取邻域内所有像素的中位数作为输出。
步骤很简单:
- 将邻域内的像素值排序
- 取中间位置的值
- 赋值给中心像素
为什么中值滤波对椒盐噪声特别有效?因为椒盐噪声是极端的亮或暗点,排序后它们会跑到序列的两端,中位数根本不受影响。
对比一下三种滤波对椒盐噪声的响应(3×3 核,中心像素被椒盐噪声污染):
- 均值滤波:被噪声拉偏,输出值偏离真实值
- 高斯滤波:同样被拉偏,只是程度略轻
- 中值滤波:完全忽略异常值,输出接近真实值
但中值滤波在低照度场景下也有自己的问题。低照度图像中的噪声往往是高斯噪声和散粒噪声的混合体,不是单纯的椒盐噪声。中值滤波对高斯噪声的抑制能力其实一般——它只是“跳过”了极端值,但对均匀分布的噪声没有平均效果。
低照度场景的局限性总结:
- 中值滤波保边能力比均值滤波强,但比高斯滤波弱(在连续渐变区域)
- 处理彩色图像时需要逐通道操作,计算量是均值滤波的 3 倍
- 核越大,排序开销越大,实时性下降明显
3.4 三种算法的核心对比
| 指标 | 均值滤波 | 高斯滤波 | 中值滤波 |
|---|---|---|---|
| 降噪能力(高斯噪声) | 中等 | 较好 | 一般 |
| 降噪能力(椒盐噪声) | 差 | 差 | 优秀 |
| 边缘保留 | 差 | 中等 | 较好 |
| 计算复杂度 | O(n²) | O(n²) | O(n² log n) |
| 低照度场景适用性 | 低 | 中等 | 中等(需配合其他算法) |
你想想看,这三种算法其实都是“一刀切”的思路——用一个固定的核去处理整张图像。低照度场景下,不同区域的噪声水平差异很大:暗区噪声大,亮区噪声小。固定核的滤波方法很难兼顾全局。
3.5 知识结构图:空间域降噪算法全景
3.6 代码实现:从原理到实战
下面我用 Python + OpenCV 实现这三种滤波,并对比它们在低照度场景下的表现。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取低照度图像(假设已有)
img = cv2.imread('low_light.jpg', cv2.IMREAD_GRAYSCALE)
# 均值滤波
mean_blur = cv2.blur(img, (5, 5))
# 高斯滤波
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 1.0)
# 中值滤波
median_blur = cv2.medianBlur(img, 5)
# 显示对比
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1), plt.imshow(img, cmap='gray'), plt.title('原始低照度图像')
plt.subplot(2, 2, 2), plt.imshow(mean_blur, cmap='gray'), plt.title('均值滤波 5×5')
plt.subplot(2, 2, 3), plt.imshow(gaussian_blur, cmap='gray'), plt.title('高斯滤波 5×5 σ=1.0')
plt.subplot(2, 2, 4), plt.imshow(median_blur, cmap='gray'), plt.title('中值滤波 5×5')
plt.tight_layout()
plt.show()
实战建议:
- 低照度场景下,优先尝试高斯滤波(3×3,σ=0.8~1.2),计算量小,效果相对可控
- 如果图像中有明显的椒盐噪声(比如传感器坏点),先用中值滤波做一次预处理
- 均值滤波尽量少用——除非你对实时性要求极高,且对图像质量不敏感