4. 经典降噪算法(变换域)

聊到图像降噪,咱们得先搞清楚一个核心问题:噪声在哪儿?

在空间域里,噪声就是那些突兀的像素点,跟信号混在一起,很难直接分离。但换个角度看呢?如果把图像从「像素空间」映射到「频率空间」,噪声和信号就分开了。这就是变换域降噪的基本思路。

我个人习惯把这类方法叫做「换个战场打仗」。今天咱们就重点讲两个经典战场:傅里叶变换和小波变换。

4.1 傅里叶变换:从空间到频率

傅里叶变换的核心思想很简单:任何信号都可以分解成不同频率的正弦波之和。图像里的低频对应平滑区域,高频对应边缘和纹理——当然,也包括噪声。

核心公式(离散傅里叶变换,DFT):
F(u,v) = Σ Σ f(x,y) * e^(-j2π(ux/M + vy/N))

其中 f(x,y) 是空间域像素值,F(u,v) 是频率域系数。

我在项目中遇到过一个问题:用傅里叶变换做降噪,低频保留得很好,但高频细节也跟着被抹掉了。为什么?因为傅里叶变换的基函数是全局的——它用无限长的正弦波去拟合信号,局部突变(比如边缘)会被平均到整个频带上。

傅里叶降噪的局限:
  • 无法同时兼顾频率和位置信息
  • 对非平稳信号(如图像边缘)处理效果差
  • 阈值处理容易产生振铃效应(Gibbs现象)

说白了,傅里叶变换就像用一把大刷子刷墙——整体颜色均匀了,但墙角的细节也糊了。那有没有更精细的工具?有,小波变换。

4.2 小波变换:多分辨率分析

小波变换的厉害之处在于:它既能看全局,又能看局部。它用一组有限长、会衰减的「小波」基函数去分解信号,这些基函数可以伸缩和平移,从而在不同尺度上分析信号。

你想想看,图像里的噪声通常是高频的、局部的。小波变换可以把图像分解成不同频率的子带:低频近似分量(LL)、水平细节(LH)、垂直细节(HL)、对角细节(HH)。噪声主要集中在高频细节子带里。

二维离散小波变换(DWT)分解结构:
输入图像 → [LL1, LH1, HL1, HH1] → 对LL1继续分解 → [LL2, LH2, HL2, HH2] → ...

我记得第一次做小波降噪时,看到分解后的子带图,心里就一个感觉:太清晰了。噪声在HH子带里像撒了一把盐,而边缘在LH和HL里还有结构信息。这就给了我们操作空间。

二维离散小波变换(DWT)多分辨率分解 原始图像 LL₁ 低频近似 LH₁ 水平细节 HL₁ 垂直细节 HH₁ 对角细节 LL₂ LH₂ HL₂ HH₂ 关键理解: • LL子带:低频近似,包含图像主要能量,噪声少 • LH/HL子带:中频细节,包含边缘和纹理,部分噪声 • HH子带:高频细节,噪声最集中,也是降噪的主要目标 • 分解层数越多,频率分辨率越高,但计算量也越大 • 降噪时只处理高频子带,保留LL子带不变,保护图像主体

4.3 小波阈值降噪:软阈值 vs 硬阈值

有了小波分解,接下来就是怎么处理那些高频系数。最经典的方法就是阈值降噪。核心思想:小于阈值的系数认为是噪声,置零;大于阈值的系数认为是信号,保留或收缩。

这里有两个流派:硬阈值和软阈值。我刚开始做的时候也纠结过选哪个,后来踩了坑才明白。

方法 公式 特点 缺点
硬阈值 ŵ = w, if |w| ≥ T
ŵ = 0, if |w| < T
保留大于阈值的系数原值,边缘保持好 系数在阈值处不连续,重构图像易产生振铃和伪影
软阈值 ŵ = sign(w) * (|w| - T), if |w| ≥ T
ŵ = 0, if |w| < T
系数连续过渡,图像更平滑,伪影少 会收缩所有系数,导致边缘模糊,细节损失
我曾经踩过的坑:

做暗光图像降噪时,我一开始用了硬阈值,结果图像边缘出现了明显的「锯齿」和「振铃」。后来换成软阈值,平滑是平滑了,但暗光下的微弱细节也跟着没了。最后我用了自适应阈值 + 软硬折中的方法——对不同子带用不同阈值,对LH/HL子带用较小的软阈值,对HH子带用较大的硬阈值。效果好了很多。

4.4 实战:小波降噪在暗光图像上的应用

好了,理论讲完,咱们直接上手。下面是一个完整的Python实现,用PyWavelets库对暗光图像做小波降噪。

实战代码:小波阈值降噪
import cv2
import numpy as np
import pywt
import matplotlib.pyplot as plt

def wavelet_denoise(image, wavelet='db4', level=3, threshold=30, mode='soft'):
    """
    小波阈值降噪函数
    :param image: 输入图像(灰度图)
    :param wavelet: 小波基,'db4'是Daubechies 4阶小波
    :param level: 分解层数
    :param threshold: 阈值
    :param mode: 'soft' 或 'hard'
    :return: 降噪后的图像
    """
    # 1. 小波分解
    coeffs = pywt.wavedec2(image, wavelet, level=level)
    
    # 2. 对每一层的高频子带进行阈值处理
    coeffs_thresh = list(coeffs)
    coeffs_thresh[0] = coeffs[0]  # LL子带保持不变
    
    for i in range(1, len(coeffs_thresh)):
        # 每个元素是 (LH, HL, HH) 三元组
        detail_coeffs = list(coeffs_thresh[i])
        for j in range(3):  # 对LH, HL, HH分别处理
            if mode == 'soft':
                detail_coeffs[j] = pywt.threshold(detail_coeffs[j], threshold, mode='soft')
            elif mode == 'hard':
                detail_coeffs[j] = pywt.threshold(detail_coeffs[j], threshold, mode='hard')
        coeffs_thresh[i] = tuple(detail_coeffs)
    
    # 3. 小波重构
    denoised = pywt.waverec2(coeffs_thresh, wavelet)
    
    # 4. 裁剪到有效范围
    denoised = np.clip(denoised, 0, 255).astype(np.uint8)
    return denoised

# 读取暗光图像
img = cv2.imread('dark_scene.jpg', cv2.IMREAD_GRAYSCALE)

# 应用降噪
denoised_soft = wavelet_denoise(img, wavelet='db4', level=3, threshold=30, mode='soft')
denoised_hard = wavelet_denoise(img, wavelet='db4', level=3, threshold=30, mode='hard')

# 显示结果
plt.figure(figsize=(12, 4))
plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('原始暗光图像')
plt.subplot(132), plt.imshow(denoised_soft, cmap='gray'), plt.title('软阈值降噪')
plt.subplot(133), plt.imshow(denoised_hard, cmap='gray'), plt.title('硬阈值降噪')
plt.show()
实战经验总结:
  • 小波基选择:db4(Daubechies 4)在计算量和效果之间平衡较好,适合暗光图像。symlet系列也不错,对称性更好。
  • 分解层数:3层是常用选择。层数太少,噪声分离不彻底;层数太多,低频分量也被过度处理,图像变模糊。
  • 阈值选择:通用阈值 T = σ * sqrt(2 * log(N)),其中σ是噪声标准差,N是像素数。但暗光图像噪声不均匀,我建议用局部自适应阈值。
  • 软硬结合:对LH/HL子带用软阈值(保护边缘连续性),对HH子带用硬阈值(保留微弱细节)。

嗯,这里要注意一点:暗光图像的噪声往往不是高斯白噪声,而是泊松-高斯混合噪声。直接套用通用阈值公式效果不一定好。我个人的习惯是先做一次中值滤波估计噪声水平,再根据局部方差调整阈值。

另外,小波降噪有个天然优势——它不会像高斯滤波那样把边缘也模糊掉。因为小波变换本身就有多分辨率特性,低频近似分量几乎不受影响,只处理高频细节。这对暗光图像来说太重要了,因为暗光下本来就缺细节,再模糊就真没法看了。

暗光图像小波降噪的关键参数推荐:
参数 推荐值 说明
小波基 db4 或 sym4 平衡计算量与效果
分解层数 3 暗光图像噪声能量集中在高频
阈值模式 软阈值(LH/HL)
硬阈值(HH)
混合策略效果最佳
阈值大小 15-40 根据噪声水平动态调整

最后说一句:小波降噪不是万能的。如果图像信噪比太低(比如ISO 12800以上的暗光照片),小波降噪也救不回来。这时候就需要结合多帧合成或者深度学习的方法了。不过作为经典算法,小波降噪在计算资源有限、实时性要求高的场景下,依然是首选方案之一。