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 核心逻辑。你看一眼,基本就全明白了。
4.6 避坑指南
最后,我把自己踩过的坑分享给大家,希望能帮你们少走弯路。
- 别把所有坏点都当动态坏点处理。静态坏点用查表法,效率高、效果好。动态检测算法只处理未知的坏点。
- 注意边界像素。图像边缘的像素没有完整的 3x3 邻域。我习惯的做法是,边界像素不做校正,或者只做镜像填充。
- 中值校正别用在纹理密集区域。如果坏点刚好在一条细线或一个角点上,中值会把细节抹掉。这种情况,我建议用方向性中值——只取同方向的像素做中值。
- 硬件里别用除法。均值校正需要除以邻域像素个数。硬件里除法很贵,用移位加加法替代。比如 3x3 窗口除以 9,可以近似为乘以 0.1111,用移位实现。
一句话总结:坏点校正,说白了就是“找出来,换掉它”。静态坏点靠查表,动态坏点靠算法。硬件实现时,资源、速度、精度三者要权衡好。