第四讲:坏点校正(DPC)实战——静态坏点与动态坏点检测、DPC强度对画质的影响、车规级DPC策略

各位工程师朋友,大家好。欢迎来到《车载ISP调试实战案例集》第四讲。

今天咱们聊聊坏点校正,也就是DPC。这玩意儿看着简单,但坑特别多。我刚开始做车载调试那会儿,觉得坏点嘛,不就是几个像素点坏了,补上不就完了?后来发现,事情远没那么简单。

坏点校正,说白了就是找出传感器上那些“不听话”的像素,然后用邻居的值把它替换掉。但怎么找?怎么换?换完会不会影响画质?在车规级场景下,这些问题都得仔细掂量。

一、静态坏点与动态坏点:两种“捣蛋鬼”

坏点分两种:静态坏点和动态坏点。我习惯把它们比作“固定捣蛋鬼”和“随机捣蛋鬼”。

1. 静态坏点

静态坏点,就是传感器出厂时就有的坏点。位置固定,亮度也固定。要么一直亮(白点),要么一直暗(黑点)。

这类坏点好处理。你想想看,只要在暗场下拍一张图,那些一直亮的点就暴露了。同样,在亮场下拍一张,那些一直暗的点也藏不住。

检测方法:

  • 暗场检测:盖上镜头盖,拍一张全黑图。正常像素值应该接近0,但坏点会明显偏亮。
  • 亮场检测:对着均匀光源拍一张全白图。正常像素值应该接近饱和值,但坏点会明显偏暗。

我在项目中遇到过一台摄像头,暗场下密密麻麻全是白点。一开始以为是传感器坏了,后来发现是镜头盖没盖严实……嗯,这种低级错误我也犯过。

2. 动态坏点

动态坏点就狡猾多了。它们不是一直坏,而是偶尔“抽风”一下。可能这帧是好的,下一帧就冒出一个亮点,再下一帧又恢复正常。

这类坏点没法通过一次标定搞定。你得在实时画面里抓它。

检测方法:

  • 帧间比较:比较相邻几帧的同一位置像素。如果某个点突然变亮或变暗,而周围像素没变,那它很可能就是动态坏点。
  • 邻域比较:看某个像素和它周围8个邻居的差异。如果差异太大,那它就有嫌疑。

核心区别:静态坏点靠标定,动态坏点靠实时检测。两者策略完全不同。

二、DPC强度对画质的影响:过犹不及

DPC强度,说白了就是校正的“力度”。力度太小,坏点去不掉;力度太大,好点也被误伤。

我见过一些调试新手,一上来就把DPC强度拉到最大。结果坏点是没了,但画面变得像油画一样,细节全糊了。为什么会这样?因为DPC本质上是一种低通滤波,它会把高频细节也一起抹掉。

具体影响:

DPC强度 坏点去除效果 画质影响
只能去除明显的坏点 细节保留较好
能去除大部分坏点 轻微细节损失
几乎能去除所有坏点 细节明显模糊,出现“油画感”

我个人习惯的做法是:先设一个中等强度,然后对着测试图卡看细节。如果发现边缘变模糊了,就降一点;如果还能看到坏点,就加一点。反复调,直到找到那个平衡点。

小技巧:可以用一张带有精细纹理的图卡来评估DPC对细节的影响。比如ISO 12233分辨率测试卡,看它的楔形线是否清晰。

三、车规级DPC策略:安全第一

车规级和消费级最大的区别是什么?是安全。消费级摄像头坏了一个点,最多是照片不好看。车载摄像头坏了一个点,可能就漏掉了一个行人或一辆车。

所以车规级DPC策略,核心就两个字:保守。

车规级DPC策略要点:

  1. 宁可漏判,不可误判。误判会把好点当成坏点校正,导致画面出现“假细节”。这在自动驾驶场景下是致命的。
  2. 动态坏点阈值要宽松。我建议把动态坏点的检测阈值设得比静态坏点高一些。因为动态坏点出现的概率低,没必要为了抓一个随机事件而牺牲整体画质。
  3. 坏点表要定期更新。传感器用久了,坏点会增多。车规级要求摄像头在每次上电时都做一次坏点检测,更新坏点表。
  4. 校正算法要可追溯。一旦出了事故,你得能查出来某个像素是不是被校正过。所以校正记录要保存下来。

我曾经参与过一个项目,客户要求DPC强度必须能动态调整。白天光线好,坏点容易被发现,强度可以高一点;晚上光线暗,坏点不明显,强度就得降下来。嗯,这个需求很合理,但实现起来挺麻烦的。我们最后是用了一个查表法,根据环境亮度自动切换DPC参数。

警告:千万不要在车规级产品上使用“强力DPC”模式。我曾经见过一个案例,某供应商为了通过坏点测试,把DPC强度调得极高。结果在夜间场景下,路边的交通标志牌边缘全被磨平了,差点导致识别系统误判。这种教训,一次就够了。

四、实战代码示例:一个简单的DPC实现

下面是一个简化的DPC实现,只做静态坏点校正。实际项目中会复杂得多,但核心思路是一样的。

// 静态坏点校正示例
void static_dpc(uint16_t *raw_img, int width, int height, 
                uint16_t *bad_pixel_table, int table_size) {
    for (int i = 0; i < table_size; i++) {
        int x = bad_pixel_table[i * 2];
        int y = bad_pixel_table[i * 2 + 1];
        
        // 检查坐标是否在图像范围内
        if (x < 1 || x >= width - 1 || y < 1 || y >= height - 1) {
            continue;
        }
        
        // 用周围4个邻居的平均值替换坏点
        uint32_t sum = 0;
        sum += raw_img[(y - 1) * width + x];     // 上
        sum += raw_img[(y + 1) * width + x];     // 下
        sum += raw_img[y * width + (x - 1)];     // 左
        sum += raw_img[y * width + (x + 1)];     // 右
        
        raw_img[y * width + x] = (uint16_t)(sum / 4);
    }
}

这段代码很简单,但有个问题:如果坏点周围也有坏点怎么办?嗯,实际项目中我们会用更复杂的插值算法,比如中值滤波或者加权平均。但核心思想不变——用邻居的值来替换坏点。

五、总结与避坑指南

好了,这一讲的内容就到这里。咱们回顾一下重点:

  • 静态坏点靠标定,动态坏点靠实时检测。
  • DPC强度要适中,过强会损失细节。
  • 车规级策略要保守,安全第一。

避坑指南:

  • 我曾经在调试时忽略了坏点表的更新机制,结果摄像头用了半年后,坏点越来越多,画面越来越花。后来才意识到,坏点表必须动态更新。
  • 我还犯过一个错误:把动态坏点的阈值设得太低,结果画面一直在“闪烁”。因为那些正常的像素点被误判为坏点,每帧都在被校正,看起来就像在抖动。
  • 最后提醒一句:DPC调试时,一定要用真实的坏点样本。不要用软件模拟的坏点,因为模拟的和真实的差别很大。我见过有人用模拟数据调出来的参数,一上真机就崩了。

下一讲,咱们聊聊“黑电平校正(BLC)实战”。这个环节看似基础,但调不好会直接影响整个画面的色彩和亮度。到时候见。