4、坏点校正(DPC):坏点类型(静态/动态)、校正原理(中值/均值)、硬件实现考量

各位同学,咱们今天聊聊坏点校正。英文叫 Defect Pixel Correction,简称 DPC。

说实话,这玩意儿在 ISP 流水线里看着不起眼,但你要是忽略了它,后面整张图就全是“麻子”。我当年刚入行时,就吃过这个亏——调试一块 sensor,怎么调都觉得画面有噪点,后来才发现是坏点没处理干净。嗯,从那以后,我对 DPC 再也不敢马虎了。

4.1 什么是坏点?

简单说,坏点就是 sensor 上那些“不听话”的像素点。正常像素对光的响应是线性的,但坏点要么一直亮、要么一直暗、要么乱跳。

你想想看,一个 2000 万像素的 sensor,有几万个坏点很正常。如果不处理,拍出来的照片就像长了雀斑一样。

4.2 坏点类型:静态 vs 动态

坏点分两类,咱们得先搞清楚它们的脾气。

类型 成因 表现 处理难度
静态坏点 制造缺陷、物理损伤 固定位置、固定亮度 低(可离线标定)
动态坏点 温度、电压、老化 随机出现、时好时坏 高(需实时检测)

静态坏点,说白了就是 sensor 出厂时就带有的“胎记”。我习惯在产线标定阶段就把它们记下来,存成一张坏点表。后面处理时直接查表替换,省事。

动态坏点就麻烦多了。它会随着温度升高突然冒出来,过一会儿又自己消失。我记得有一次做安防摄像头项目,白天画面正常,到了晚上 sensor 发热,坏点全出来了。后来才意识到是动态坏点在作祟。

核心区别:静态坏点位置固定,动态坏点位置随机。前者靠查表,后者靠算法实时判断。

4.3 校正原理:中值 vs 均值

坏点找到了,怎么修?说白了就是用周围的好像素去“猜”这个坏点本该是什么值。常用的方法有两种。

4.3.1 中值校正

取坏点周围 3x3 或 5x5 邻域内所有像素的中位数,替换坏点。

// 伪代码示例:3x3 中值校正
uint8_t median_filter(uint8_t window[3][3]) {
    uint8_t values[9];
    int idx = 0;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            values[idx++] = window[i][j];
    // 排序取中值
    sort(values, 9);
    return values[4]; // 第5个元素是中值
}

优点:对孤立坏点效果极好,能保留边缘细节。
缺点:计算量大,排序操作在硬件里挺费资源的。

我个人习惯在动态坏点校正中用中值法。因为动态坏点往往是孤立的亮点或暗点,中值能很好地“剔除”它们。

4.3.2 均值校正

取邻域内所有像素的平均值,替换坏点。

// 伪代码示例:3x3 均值校正
uint8_t mean_filter(uint8_t window[3][3]) {
    uint32_t sum = 0;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            sum += window[i][j];
    return (uint8_t)(sum / 9);
}

优点:计算简单,硬件实现成本低。
缺点:会模糊边缘,如果坏点旁边有纹理,容易把纹理也抹掉。

我的经验:静态坏点用均值校正就够了。因为静态坏点位置已知,可以提前做平滑处理,不会影响实时性能。

4.4 硬件实现考量

做软件算法时,你随便写个排序函数就行。但到了硬件里,每一笔资源都得精打细算。我给大家列几个关键点。

4.4.1 Line Buffer 开销

做 3x3 窗口处理,至少需要缓存 3 行数据。对于 4K 分辨率,一行 3840 像素,每个像素 10bit,光 line buffer 就要 3840×10×3 ≈ 115Kbit。这还不算大,但如果是 5x5 窗口,就要 5 行,资源直接翻倍。

我建议:动态坏点用 3x3 窗口,静态坏点可以用更大的窗口,但只在查表命中时才启用,平时不占用资源。

4.4.2 排序 vs 选择

中值校正需要排序,但硬件里做全排序太奢侈了。实际工程中,我们只找中值,不排全序。

有一种叫“中值选择网络”的结构,用比较器两两比较,3x3 窗口只需要 19 个比较器就能找出中值。嗯,这个我在 FPGA 上验证过,效果不错。

4.4.3 坏点检测与校正的流水线

硬件里通常把检测和校正分成两级流水:

  • 第一级:检测当前像素是否为坏点(查表 + 动态判断)
  • 第二级:如果是坏点,执行校正;否则直通

这样做的好处是,检测和校正可以并行处理,不拖慢整体帧率。

注意:动态坏点检测不能只看单帧。我遇到过一种情况,某个像素在连续 3 帧里都是异常值,但第 4 帧又正常了。如果只根据单帧判断,很容易误判。建议做多帧统计,比如连续 3 帧异常才标记为坏点。

4.5 知识体系总览

下面这张图,是我自己整理的 DPC 核心逻辑。你看一眼,基本就全明白了。

坏点校正(DPC)知识体系 坏点校正 DPC 坏点类型 校正原理 硬件实现 静态坏点 动态坏点 中值校正 均值校正 Line Buffer 流水线设计 核心思路:先检测,后校正 静态坏点查表替换 | 动态坏点实时检测 + 中值/均值校正

4.6 避坑指南

最后,我把自己踩过的坑分享给大家,希望能帮你们少走弯路。

  • 别把所有坏点都当动态坏点处理。静态坏点用查表法,效率高、效果好。动态检测算法只处理未知的坏点。
  • 注意边界像素。图像边缘的像素没有完整的 3x3 邻域。我习惯的做法是,边界像素不做校正,或者只做镜像填充。
  • 中值校正别用在纹理密集区域。如果坏点刚好在一条细线或一个角点上,中值会把细节抹掉。这种情况,我建议用方向性中值——只取同方向的像素做中值。
  • 硬件里别用除法。均值校正需要除以邻域像素个数。硬件里除法很贵,用移位加加法替代。比如 3x3 窗口除以 9,可以近似为乘以 0.1111,用移位实现。

一句话总结:坏点校正,说白了就是“找出来,换掉它”。静态坏点靠查表,动态坏点靠算法。硬件实现时,资源、速度、精度三者要权衡好。


专注资料整理