第四章 RAW域降噪:空域与时域实战
降噪这活儿,说白了就是跟传感器抢细节。我刚开始调ISP的时候,总觉得降噪就是“糊一糊”,结果图像是干净了,但纹理全没了,被算法同事追着骂了好几天。后来才明白——降噪的本质是信号与噪声的分离,不是抹除。
RAW域的噪声有个特点:它跟信号强度有关,而且分布不均匀。你想想看,暗区的噪声往往比亮区更明显,因为信噪比低。所以降噪策略必须“看人下菜碟”。
核心原则:RAW域降噪要在拜耳阵列上直接操作,不能先插值再降噪。否则噪声会被插值算法“固化”到彩色通道里,后期怎么救都救不回来。
4.1 空域降噪:最基础的三种武器
空域降噪,就是只靠当前这一帧图像里的像素信息来做处理。我最早接触的就是这三种:均值、高斯、中值。听起来简单,但用好了能解决80%的日常问题。
4.1.1 均值滤波
均值滤波的原理,就是用一个窗口扫过图像,把窗口内所有像素的平均值赋给中心像素。说白了就是“大家一起平摊噪声”。
// 3x3均值滤波核
float kernel[3][3] = {
{1/9, 1/9, 1/9},
{1/9, 1/9, 1/9},
{1/9, 1/9, 1/9}
};
注意:均值滤波会严重模糊边缘。我曾在一条产线上看到有人用均值滤波做预处理,结果后续的边缘检测算法直接报废。如果你需要保留细节,别用均值。
4.1.2 高斯滤波
高斯滤波跟均值滤波的区别在于——它给窗口内的像素加了权重。离中心越近的像素权重越大,越远的权重越小。这个权重分布服从高斯函数。
为什么这样更好?因为自然图像中,相邻像素的相关性本来就随距离衰减。高斯滤波更符合这个规律,所以模糊效果更自然。
// 3x3高斯滤波核 (sigma=1.0)
float kernel[3][3] = {
{0.075, 0.124, 0.075},
{0.124, 0.204, 0.124},
{0.075, 0.124, 0.075}
};
我的经验:sigma值选1.0到1.5之间比较稳妥。sigma太小没效果,太大又糊成一片。我一般先试1.2,再根据噪声水平微调。
4.1.3 中值滤波
中值滤波就更有意思了——它取窗口内所有像素的中位数,而不是平均值。这玩意儿对付椒盐噪声(就是那种黑白点噪声)特别管用。
为什么会这样?因为椒盐噪声的像素值要么极大要么极小,取中位数时会被自动排除掉。而均值滤波会把噪声值“平均”进结果里,越平均越脏。
| 滤波类型 | 噪声类型 | 边缘保持 | 计算量 |
|---|---|---|---|
| 均值滤波 | 高斯噪声 | 差 | 低 |
| 高斯滤波 | 高斯噪声 | 中 | 中 |
| 中值滤波 | 椒盐噪声 | 好 | 高 |
4.2 时域降噪:多帧合成才是王道
空域降噪有个天花板——你不可能无限增大窗口,否则细节全没了。这时候就需要时域降噪出手了。
时域降噪的核心思想:噪声是随机的,信号是稳定的。多拍几帧,把相同位置的像素值平均一下,噪声就会互相抵消,信号则保持不变。
4.2.1 简单多帧平均
假设你拍了N帧图像,每帧的像素值 = 真实信号 + 噪声。N帧平均后,信号不变,噪声的标准差降低到原来的 1/√N。
// 多帧平均伪代码
for each pixel (x, y):
sum = 0
for frame = 1 to N:
sum += frame_buffer[frame][y][x]
output[y][x] = sum / N
关键指标:4帧平均能把噪声降低一半(1/√4 = 1/2),16帧平均能降低到1/4。但帧数越多,运动模糊越严重。我一般控制在4-8帧之间。
4.2.2 运动补偿
简单平均有个致命问题——如果场景里有运动物体,多帧平均会拖出鬼影。这时候就需要运动补偿了。
运动补偿的思路:先估计当前帧与参考帧之间的运动矢量,然后把参考帧的像素“挪”到对齐位置,再做平均。
我曾经在一个安防项目中遇到这个问题:夜晚监控画面噪声很大,用了8帧平均后,人走过去变成了一团模糊的影子。后来加了运动补偿,效果立竿见影。
避坑指南:运动估计本身也会出错。如果运动矢量算错了,补偿后的图像反而更糟。我建议设置一个“运动置信度”阈值,置信度低的区域就少做时域平均,甚至不做。
4.3 双边滤波与引导滤波:边缘保持的利器
前面说的几种方法,要么保边缘丢降噪,要么降噪丢边缘。双边滤波和引导滤波就是为了解决这个矛盾而生的。
4.3.1 双边滤波
双边滤波在计算权重时考虑两个因素:空间距离和像素值差异。空间距离近的权重大,像素值相近的权重大。
这意味着什么?在平坦区域,像素值差异小,双边滤波退化为高斯滤波,好好降噪。在边缘处,像素值差异大,边缘两侧的像素互相不参与加权,边缘就保住了。
// 双边滤波权重计算
weight = exp(-(dx² + dy²) / (2 * σ_space²))
* exp(-(I(x,y) - I(x0,y0))² / (2 * σ_range²))
参数调优:σ_space控制空间尺度,一般取图像尺寸的1-2%。σ_range控制像素值差异的敏感度,我习惯取图像动态范围的5-10%。这两个参数要配合着调,一个大了另一个就得小。
4.3.2 引导滤波
引导滤波是双边滤波的“升级版”。它假设输出图像是引导图像的局部线性变换。这个假设在数学上更优雅,计算效率也更高——复杂度是O(N),跟窗口大小无关。
引导滤波最大的优势:不会出现双边滤波的“梯度翻转”伪影。双边滤波在某些强边缘附近会产生不自然的明暗反转,引导滤波则不会。
| 特性 | 双边滤波 | 引导滤波 |
|---|---|---|
| 计算复杂度 | O(N·r²) | O(N) |
| 边缘保持 | 好 | 好 |
| 梯度翻转 | 可能 | 无 |
| 引导图需求 | 不需要 | 需要 |
实战建议:在RAW域做引导滤波时,可以用绿色通道作为引导图。因为绿色通道在拜耳阵列中采样率最高,信噪比最好,用它引导红色和蓝色通道的降噪,效果很稳。
4.4 降噪策略选择指南
说了这么多,到底怎么选?我总结了一个简单的决策流程:
- 先看噪声类型:椒盐噪声→中值滤波;高斯噪声→高斯滤波或双边滤波
- 再看运动情况:静态场景→多帧平均;动态场景→运动补偿+时域降噪
- 最后看算力:嵌入式平台→均值/高斯;PC端→引导滤波
嗯,这里要注意:没有万能算法。我见过太多人试图用一个滤波器解决所有问题,结果哪个都没调好。实际项目中,往往是空域+时域+边缘保持三种方法组合使用,才能达到最佳效果。
我的组合拳:先用2-4帧时域平均压低基础噪声,再用引导滤波做边缘保持降噪,最后用中值滤波清理残留的坏点。这套方案在多个项目中验证过,效果稳定。