4. 空域降噪基础:均值滤波、高斯滤波、中值滤波原理、在Y/U/V分量上的应用差异

各位同学,今天我们进入空域降噪的第一课。说白了,空域降噪就是直接在像素点上做文章——用周围像素的信息来“修复”当前像素。听起来简单,但里面的门道不少。我在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分量(亮度) 推荐:高斯滤波 窗口:3x3 或 5x5 σ:0.8~1.2 慎用:均值滤波 U分量(色度) 推荐:高斯滤波 窗口:5x5 或 7x7 σ:1.5~2.5 可接受:均值滤波 V分量(色度) 推荐:高斯滤波 窗口:5x5 或 7x7 σ:1.5~2.5 可接受:均值滤波 关键差异总结 • Y分量:细节丰富,滤波强度要轻,优先高斯滤波,窗口不宜大 • U/V分量:细节少,滤波强度可以大,高斯/均值均可,窗口可大 • 中值滤波:只在椒盐噪声/坏点场景使用,Y分量慎用大窗口

我个人的经验是: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分量上既想降噪又想保边,可以试试“双边滤波”或者“引导滤波”——这两个是空域降噪的进阶版,我们后面章节会详细讲。但基础不牢,地动山摇,先把均值、高斯、中值这三个基本功练扎实了,再谈进阶。