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 中值滤波:对抗异常值的利器

中值滤波的思路完全不同。它不再做加权平均,而是取邻域内所有像素的中位数作为输出。

步骤很简单:

  1. 将邻域内的像素值排序
  2. 取中间位置的值
  3. 赋值给中心像素

为什么中值滤波对椒盐噪声特别有效?因为椒盐噪声是极端的亮或暗点,排序后它们会跑到序列的两端,中位数根本不受影响。

对比一下三种滤波对椒盐噪声的响应(3×3 核,中心像素被椒盐噪声污染):

  • 均值滤波:被噪声拉偏,输出值偏离真实值
  • 高斯滤波:同样被拉偏,只是程度略轻
  • 中值滤波:完全忽略异常值,输出接近真实值

但中值滤波在低照度场景下也有自己的问题。低照度图像中的噪声往往是高斯噪声和散粒噪声的混合体,不是单纯的椒盐噪声。中值滤波对高斯噪声的抑制能力其实一般——它只是“跳过”了极端值,但对均匀分布的噪声没有平均效果。

低照度场景的局限性总结:

  • 中值滤波保边能力比均值滤波强,但比高斯滤波弱(在连续渐变区域)
  • 处理彩色图像时需要逐通道操作,计算量是均值滤波的 3 倍
  • 核越大,排序开销越大,实时性下降明显

3.4 三种算法的核心对比

指标 均值滤波 高斯滤波 中值滤波
降噪能力(高斯噪声) 中等 较好 一般
降噪能力(椒盐噪声) 差 差 优秀
边缘保留 差 中等 较好
计算复杂度 O(n²) O(n²) O(n² log n)
低照度场景适用性 低 中等 中等(需配合其他算法)

你想想看,这三种算法其实都是“一刀切”的思路——用一个固定的核去处理整张图像。低照度场景下,不同区域的噪声水平差异很大:暗区噪声大,亮区噪声小。固定核的滤波方法很难兼顾全局。

3.5 知识结构图:空间域降噪算法全景

空间域降噪算法知识结构 空间域降噪算法 均值滤波 高斯滤波 中值滤波 邻域像素平均值 计算快,边缘模糊 高斯权重分布 σ 控制平滑程度 排序取中位数 抗椒盐噪声强 低照度场景局限性 1. 固定核无法适应不同区域的噪声差异 2. 降噪与保边存在根本矛盾 3. 暗区细节在滤波后难以恢复

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),计算量小,效果相对可控
  • 如果图像中有明显的椒盐噪声(比如传感器坏点),先用中值滤波做一次预处理
  • 均值滤波尽量少用——除非你对实时性要求极高,且对图像质量不敏感