4. 坏点校正(DPC):坏点类型、检测与替换策略
坏点校正,简称 DPC,是 RAW 域预处理里绕不开的一环。我刚开始做 ISP 的时候,觉得这步挺简单的——不就是把坏点抹掉吗?后来踩了几个坑才发现,坏点处理的好坏,直接决定了后面去噪、锐化的效果。处理不好,整张图就毁了。
说白了,坏点就是传感器上那些「不听话」的像素。它们要么一直亮着,要么一直暗着,要么响应曲线跟别人不一样。你想想看,几千万个像素点里混进去几个这样的「刺头」,拍出来的照片就会出现亮点、暗点或者彩点。
4.1 坏点类型:静态 vs 动态
坏点分两类,处理方式完全不同。我习惯先分清楚再动手。
| 类型 | 成因 | 表现 | 处理时机 |
|---|---|---|---|
| 静态坏点 | 制造缺陷、物理损伤 | 固定位置,始终异常 | 出厂标定,一次处理 |
| 动态坏点 | 温度、增益、老化 | 随机出现,时好时坏 | 实时检测,逐帧处理 |
静态坏点,就是传感器出厂时就带有的缺陷。比如某个像素短路了,输出永远是最大值,拍出来就是个白点。这类坏点位置固定,可以在产线标定时记录下来,生成一张坏点列表(Defect Pixel List)。
动态坏点就麻烦多了。它们受温度影响很大——相机拍久了,传感器发热,一些本来正常的像素就开始「抽风」。我记得有一次做手机摄像头调试,室温下跑得好好的,放到高温箱里一测,满屏的闪烁亮点。这就是典型的动态坏点。
4.2 坏点检测算法
检测坏点,核心思路就一句话:看这个像素跟邻居比,是不是「不正常」。具体怎么比,各家有各家的招。
4.2.1 基于邻域统计的检测
这是最基础的方法。对每个像素,取它周围 3x3 或 5x5 的窗口,计算均值或中值。如果当前像素值跟这个统计值差太多,就判为坏点。
// 伪代码:基于中值的坏点检测
for each pixel (x, y) {
window = get_neighbors(x, y, 3x3);
median = compute_median(window);
diff = abs(pixel(x,y) - median);
if (diff > threshold) {
mark_as_defect(x, y);
}
}
这个方法简单,但阈值不好设。设小了误检多,设大了漏检多。我早期做的一个项目就吃过这个亏——阈值设得太严,把正常的纹理细节当坏点给抹了,画面糊成一片。
4.2.2 基于方向梯度的检测
这个方法更聪明一些。它考虑到了图像边缘的情况——如果像素正好在边缘上,跟邻居差异大是正常的,不能判为坏点。
具体做法是:计算像素在水平、垂直、两个对角线方向上的梯度。如果四个方向上的梯度都很大,那这个像素大概率是坏点。如果只有一个方向梯度大,那可能是边缘。
4.2.3 自适应阈值
固定阈值的问题很明显——暗区噪声大,亮区噪声小,一刀切肯定不行。自适应阈值会根据局部亮度调整检测门限。
我常用的做法是:先估算当前区域的噪声水平,然后阈值 = 噪声标准差 × 系数。暗区噪声大,阈值自动抬高,减少误检;亮区噪声小,阈值降低,保证不漏检。
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 邻域统计 | 实现简单,速度快 | 边缘误检,阈值难调 | 低端芯片,实时性要求高 |
| 方向梯度 | 准确率高,保护边缘 | 计算量大 | 中高端 ISP |
| 自适应阈值 | 适应不同亮度场景 | 需要噪声估计 | 复杂光照环境 |
4.3 坏点替换策略
检测到坏点之后,怎么把它「修好」?不是简单地抹掉,而是用合理的值填回去。
4.3.1 中值替换
取邻域像素的中值来替换坏点。优点是鲁棒性好,不受单个异常值影响。缺点是会损失一些细节。
4.3.2 均值替换
取邻域像素的均值。优点是平滑效果好,缺点是对噪声敏感。如果邻域里恰好也有坏点,均值会被拉偏。
4.3.3 梯度导向插值
先判断边缘方向,然后沿着边缘方向取像素做插值。这样能最大程度保留图像细节。我比较推荐这个方法,尤其是在纹理丰富的区域。
// 梯度导向插值示例
if (horizontal_gradient < vertical_gradient) {
// 水平方向更平滑,沿水平插值
pixel(x,y) = (pixel(x-1,y) + pixel(x+1,y)) / 2;
} else {
// 垂直方向更平滑,沿垂直插值
pixel(x,y) = (pixel(x,y-1) + pixel(x,y+1)) / 2;
}
4.4 自适应校正策略
实际工程中,坏点校正不是「一刀切」的。我习惯把策略分成几个等级,根据场景动态切换。
- 低增益场景: 噪声小,坏点容易识别。用严格的检测阈值,配合梯度导向插值。
- 高增益场景: 噪声放大,坏点淹没在噪声里。适当放宽检测阈值,用中值替换更安全。
- 高温场景: 动态坏点增多。加强检测力度,同时提高替换的平滑度。
- 边缘区域: 保护纹理,用方向梯度检测 + 梯度导向插值。
- 平坦区域: 简单中值替换即可,不需要复杂计算。
我曾经在一个安防摄像头项目里,把自适应策略做成了查表——根据增益和温度查表决定检测阈值和替换方式。效果比单一策略好很多,而且计算量没增加多少。
4.5 DPC 全流程框架
下面这张图是我自己总结的 DPC 处理流程,从检测到替换,每一步都有对应的策略选择。
这张图把整个流程串起来了。从输入 RAW 图开始,先判断坏点类型,静态的直接查表,动态的走实时检测。检测方法根据场景选,替换策略也根据需求定。最后输出校正后的 RAW 数据。
嗯,坏点校正这块,说白了就是「先发现,再修复」。发现的方法决定了准确率,修复的方法决定了画质。两者配合好了,才能做到既去掉坏点,又不损伤正常细节。我做了这么多年 ISP,DPC 始终是我不敢掉以轻心的一环——它太基础了,基础到出了问题往往要排查很久才能找到根因。