4. 空域降噪基础
各位同学,今天我们进入空域降噪的第一课。说白了,空域降噪就是直接在像素点上做文章——用周围像素的信息来“修复”当前像素。听起来简单,但里面的门道不少。我在ISP调试初期,就因为在Y分量上用了不对路的滤波器,结果纹理糊得一塌糊涂,被算法总监叫去“喝茶”……嗯,从那以后我对这三个基础滤波器的脾气算是摸透了。
核心认知:空域降噪是ISP Pipeline中最先接触噪声的关卡。它不依赖帧间信息,只靠当前帧的空间邻域做决策。速度快,但容易丢细节。选对滤波器、用对分量,是ISP调优的基本功。
4.1 均值滤波:最朴素,但代价不小
均值滤波的原理,你想想看,就是取一个窗口内所有像素的平均值,替代中心像素。比如一个3x3的窗口,把9个像素加起来除以9,完事。
// 均值滤波核心(3x3窗口)
uint8_t mean_filter(uint8_t window[3][3]) {
uint32_t sum = 0;
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
sum += window[y][x];
return (uint8_t)(sum / 9);
}
我在项目中遇到过一个问题:用均值滤波去处理Y分量的高斯噪声,效果还行,但一旦遇到边缘,立马露馅。边缘两侧的像素值差异大,一平均,边缘就模糊了。说白了,均值滤波把噪声和边缘信息“一视同仁”地抹平了。
| 特性 | 均值滤波 |
|---|---|
| 噪声抑制能力 | 中等(对高斯噪声有效,对椒盐噪声无效) |
| 边缘保留能力 | 差(边缘模糊严重) |
| 计算复杂度 | 低(O(n²)) |
| 适用场景 | 预处理、对细节要求不高的场景 |
避坑指南:我曾经在Y分量上直接套用5x5均值滤波,结果人脸皮肤变成了“塑料质感”。Y分量承载了大部分亮度细节,均值滤波对纹理的破坏是毁灭性的。除非你确定场景不需要细节,否则慎用。
4.2 高斯滤波:给邻域像素“论资排辈”
高斯滤波比均值滤波聪明一点。它不再平均分配权重,而是根据像素到中心点的距离,按高斯分布分配权重。离中心越近,权重越大;越远,权重越小。
// 3x3高斯核(σ≈0.85)
const float gauss_kernel[3][3] = {
{1.0/16, 2.0/16, 1.0/16},
{2.0/16, 4.0/16, 2.0/16},
{1.0/16, 2.0/16, 1.0/16}
};
uint8_t gauss_filter(uint8_t window[3][3]) {
float sum = 0.0f;
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
sum += window[y][x] * gauss_kernel[y][x];
return (uint8_t)(sum + 0.5f);
}
我个人习惯在Y分量上优先考虑高斯滤波,因为它对纹理的保留比均值滤波好不少。但要注意σ的选择——σ太小,降噪效果差;σ太大,又回到均值滤波的老路上了。
| 特性 | 高斯滤波 |
|---|---|
| 噪声抑制能力 | 中等偏上(对高斯噪声效果好) |
| 边缘保留能力 | 中等(比均值滤波好,但仍会模糊边缘) |
| 计算复杂度 | 中等(需要浮点或定点乘法) |
| 适用场景 | Y分量轻度降噪、U/V分量预处理 |
经验之谈:我建议在U/V分量上使用高斯滤波时,σ可以适当放大。因为色度分量的细节敏感度比亮度低,稍微模糊一点人眼看不出来,但噪声抑制效果会好很多。
4.3 中值滤波:专治“椒盐噪声”
中值滤波的思路完全不同——它取窗口内所有像素的中间值,而不是平均值。这个“中间值”是什么意思?就是把像素值排序,取排在中间的那个。
// 中值滤波核心(3x3窗口)
uint8_t median_filter(uint8_t window[3][3]) {
uint8_t temp[9];
int idx = 0;
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
temp[idx++] = window[y][x];
// 简单排序取中值(实际工程用快速选择)
for (int i = 0; i < 9; i++)
for (int j = i+1; j < 9; j++)
if (temp[i] > temp[j]) {
uint8_t t = temp[i];
temp[i] = temp[j];
temp[j] = t;
}
return temp[4]; // 第5个元素是中值
}
中值滤波对椒盐噪声简直是“天生克星”。为什么?因为椒盐噪声是极端值(要么0,要么255),排序后它们会被挤到两端,中间值根本不受影响。我在项目中遇到过传感器坏点导致的椒盐噪声,均值滤波和高斯滤波都搞不定,换成中值滤波,瞬间干净了。
| 特性 | 中值滤波 |
|---|---|
| 噪声抑制能力 | 对椒盐噪声极强,对高斯噪声一般 |
| 边缘保留能力 | 强(边缘不会模糊) |
| 计算复杂度 | 高(需要排序,O(n² log n)) |
| 适用场景 | 坏点校正、椒盐噪声场景、边缘保护需求 |
避坑指南:我曾经在Y分量上大面积使用中值滤波,结果纹理细节虽然保住了,但画面出现了“油画效应”——平滑区域变得一块一块的。中值滤波不适合大窗口(超过5x5)在Y分量上使用,除非你愿意接受这种艺术效果。
4.4 Y/U/V分量上的应用差异
好了,三个滤波器讲完了。但真正到ISP里,你不能对Y、U、V三个分量“一视同仁”。它们各自的特点决定了滤波策略必须不同。
我个人的经验是:Y分量是“精细活”,U/V分量是“粗活”。Y分量承载了人眼最敏感的亮度信息,滤波稍有不慎,纹理、边缘、细节全没了。U/V分量则不然,人眼对色度细节的敏感度只有亮度的一半左右,所以滤波可以“下重手”。
实战建议:在ISP Pipeline中,我通常这样配置:
- Y分量:3x3高斯滤波,σ=1.0,只做轻度降噪。如果场景有椒盐噪声,额外加一道3x3中值滤波,但只作用在检测到的坏点上。
- U/V分量:5x5高斯滤波,σ=2.0,或者直接用5x5均值滤波。降噪强度可以比Y分量大2~3倍。
4.5 三种滤波器的对比与选型
最后,我把三个滤波器放在一起做个对比。你想想看,选型的时候其实就是在“噪声类型”、“细节保留”、“计算资源”三个维度上做权衡。
| 维度 | 均值滤波 | 高斯滤波 | 中值滤波 |
|---|---|---|---|
| 噪声类型适配 | 高斯噪声 | 高斯噪声 | 椒盐噪声 |
| 边缘保留 | 差 | 中 | 好 |
| 纹理保留 | 差 | 中 | 好(但可能产生油画效应) |
| 计算开销 | 低 | 中 | 高 |
| Y分量适用性 | 不推荐 | 推荐 | 仅限坏点场景 |
| U/V分量适用性 | 可接受 | 推荐 | 不推荐 |
一个小技巧:如果你在Y分量上既想降噪又想保边,可以试试“双边滤波”或者“引导滤波”——这两个是空域降噪的进阶版,我们后面章节会详细讲。但基础不牢,地动山摇,先把均值、高斯、中值这三个基本功练扎实了,再谈进阶。