4、噪声估计方法:基于块的噪声估计、基于变换域的噪声估计、基于深度学习的噪声估计
噪声估计,说白了就是搞清楚「这张图里到底有多少噪声」。
我刚开始做夜景算法时,总觉得降噪就是使劲模糊。结果呢?细节全没了,照片像油画。后来才明白——不知道噪声长什么样,就别想把它去掉。
这一章,我们聊聊三种主流的噪声估计思路。它们各有各的脾气,也各有各的用武之地。
4.1 基于块的噪声估计
这是最经典的方法。我习惯叫它「找平地的思路」。
你想啊,一张图里总有纹理平坦的区域——比如天空、墙壁、桌面。这些地方本来应该是均匀的,像素值的波动基本就是噪声本身。
4.1.1 基本原理
把图像切成小块,比如 8×8 或 16×16。对每个块计算方差。方差小的块,说明纹理少,噪声占主导。把这些「干净块」的方差收集起来,就能估计出全局噪声水平。
核心假设:图像中存在足够多的平坦区域,且噪声在空间上平稳。
4.1.2 实现步骤
- 图像分块:将图像划分为不重叠的 N×N 小块。
- 计算局部方差:对每个块计算像素值的方差 σ²_local。
- 筛选平坦块:保留方差最小的前 10%~20% 的块。
- 统计估计:对这些块的方差取中位数或均值,得到噪声方差 σ²_n。
// 伪代码:基于块的噪声估计
function estimateNoiseBlockBased(image, blockSize=8, percentile=0.2):
blocks = splitIntoBlocks(image, blockSize)
variances = []
for block in blocks:
variance = computeVariance(block)
variances.append(variance)
// 取方差最小的 percentile 比例的块
sortedVars = sort(variances)
thresholdIdx = int(len(sortedVars) * percentile)
selectedVars = sortedVars[:thresholdIdx]
noiseVariance = median(selectedVars)
return sqrt(noiseVariance) // 噪声标准差
我的经验:块大小选 8×8 比较稳妥。太大容易混入纹理,太小统计不稳定。我在项目中遇到过一块全是渐变天空的情况——方差其实不小,但那是光照变化,不是噪声。后来我加了梯度检测,把梯度大的块也排除掉,效果好了很多。
4.1.3 优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,计算快 | 对纹理丰富的图像估计不准 |
| 不需要先验知识 | 假设噪声空间平稳,实际不总是成立 |
| 适合实时处理 | 低噪声场景下容易低估 |
4.2 基于变换域的噪声估计
这个方法更巧妙。它把图像变换到另一个「域」里,让噪声和信号更容易分开。
为什么会这样?你想想看,自然图像在频域或小波域里,能量通常集中在低频或少数大系数上。而噪声呢?它均匀分布在所有频率上。这就给了我们可乘之机。
4.2.1 小波域估计
我个人最喜欢用小波变换。把图像分解成不同尺度和方向的子带。高频子带里,信号能量弱,噪声占主导。直接用高频子带的系数就能估计噪声。
// 伪代码:小波域噪声估计
function estimateNoiseWavelet(image):
// 做一层小波分解
[LL, LH, HL, HH] = dwt2(image, 'db4')
// HH 子带是高频对角线方向,噪声最集中
noiseSigma = median(abs(HH(:))) / 0.6745
return noiseSigma
关键公式:σ_n = median(|HH|) / 0.6745
0.6745 是高斯分布的中位数与标准差的换算系数。这个公式来自 Donoho 的经典,我用了好多年,非常稳。
4.2.2 DCT 域估计
另一种思路是用 DCT(离散余弦变换)。把图像块变换到 DCT 域,DC 系数代表均值,AC 系数代表纹理和噪声。对于平坦块,AC 系数基本就是噪声。
我记得有一次做手机夜景算法,传感器在暗光下噪声模型很复杂——不是简单的高斯。基于块的估计完全失效。换成 DCT 域方法后,把每个块的 AC 系数直方图拟合一下,居然把非高斯噪声也估计出来了。
4.2.3 优缺点对比
| 方法 | 优势 | 局限 |
|---|---|---|
| 小波域 | 对非平稳噪声鲁棒,多尺度分析 | 计算量稍大,小波基选择有影响 |
| DCT 域 | 与 JPEG/HEVC 兼容,硬件友好 | 块效应可能干扰估计 |
| FFT 域 | 全局分析,适合周期性噪声 | 对空间变化噪声不敏感 |
4.3 基于深度学习的噪声估计
这是近五年的主流方向。说白了,就是让神经网络自己学会「看」噪声。
你想想看,传统方法需要手工设计特征和假设。深度学习不一样——给它海量的「干净图 + 噪声图」对,它自己就能总结出噪声的规律。
4.3.1 网络架构思路
- 端到端估计:输入单张噪声图,直接输出噪声标准差图(每个像素一个值)。
- 噪声分离网络:类似 U-Net 结构,编码器提取特征,解码器输出噪声分量。
- 自监督方法:不需要干净图,利用噪声在空间上的独立性来估计。比如 Noise2Noise、Noise2Void 的思路。
我踩过的坑:曾经用合成噪声训练了一个网络,效果惊艳。一上真实手机数据,直接翻车。为什么?合成噪声太「干净」了——真实传感器噪声有行噪声、固定模式噪声、量化噪声,合成数据根本模拟不全。后来我改用真实数据 + 少量合成数据混合训练,才算稳住。
4.3.2 典型网络结构
输入: 噪声图像 (H, W, 3)
↓
Conv3×3 + ReLU → 64通道
↓
Conv3×3 + ReLU → 128通道
↓
Conv3×3 + ReLU → 256通道
↓
Conv3×3 + ReLU → 128通道
↓
Conv3×3 + ReLU → 64通道
↓
Conv3×3 → 1通道 (噪声标准差图)
↓
输出: 每个像素的噪声标准差 σ(x, y)
实用建议:输出层不要用 ReLU,用 Softplus 或 ELU+1,保证噪声标准差始终为正。我一开始用 ReLU,结果某些像素输出 0,导致后续降噪除零崩溃……
4.3.3 训练数据生成
| 方法 | 做法 | 适用场景 |
|---|---|---|
| 合成数据 | 干净图 + 高斯/泊松噪声模型 | 快速验证,预训练 |
| 真实数据 | 多帧平均得到近似干净图 | 实际产品,精度高 |
| 混合训练 | 合成 + 真实按比例混合 | 兼顾泛化与精度 |
4.4 三种方法对比总结
嗯,到这里三种方法都聊完了。我画了一张图,帮你把它们的定位看清楚。
重要提醒:没有万能的方法。我在项目中见过有人非要用深度学习估计一个平稳高斯噪声——杀鸡用牛刀,还增加了功耗。反过来,也有人用块方法去估计传感器行噪声——根本估计不出来。选方法之前,先搞清楚你的噪声长什么样。
4.5 实际工程中的选择建议
最后,我根据自己的经验,给几个实际建议:
- 手机实时预览:用基于块的估计。计算量小,够用就行。
- 专业相机后处理:用变换域方法。精度更高,能处理复杂噪声。
- 计算摄影旗舰机:上深度学习。但一定要用真实数据微调,别信合成数据的泛化能力。
- 混合方案:我现在的做法是——用块方法做快速粗估计,用轻量 CNN 做精细估计。两者互补,速度和精度都能兼顾。