3. 坏点校正(DPC)

坏点校正,简称 DPC,是 ISP 流水线里最基础也最容易被忽视的一环。我刚开始做工业相机时,总觉得这玩意儿不就是抹几个像素点嘛,后来在一条手机盖板玻璃检测线上被狠狠上了一课——因为一个暗坏点,系统把同一位置的黑点误判成了划伤,每天误报几百片。从那以后,我对 DPC 再也不敢马虎。

说白了,坏点就是传感器上那些「不听话」的像素。它们要么永远亮着,要么永远黑着,要么响应忽高忽低。在缺陷检测场景里,这些坏点会直接污染特征,你想想看,一个坏点被算法当成缺陷,那后果多严重。

3.1 静态坏点与动态坏点

坏点分两类,咱们先搞清楚它们的区别。

类型 表现 成因 是否固定位置
静态坏点 始终全亮(白点)或全黑(黑点) 制造缺陷、物理损伤 是,位置固定
动态坏点 响应异常,随温度/增益变化 暗电流不均、随机噪声 否,时有时无

静态坏点好理解。传感器出厂时就有,或者使用中被激光打坏了、被灰尘砸了,那个像素就「死」了。我在项目中遇到过一片 2000 万像素的 CMOS,刚上电就有 3 个白点,位置纹丝不动,这就是典型的静态坏点。

动态坏点就狡猾多了。它们平时看着正常,一旦温度升高或者增益拉大,就开始「抽风」。我记得有一次做低照度检测,增益开到 16dB,画面里突然冒出一片闪烁的亮点,第二天降温又没了。嗯,这就是动态坏点在作祟。

关键认知:静态坏点靠标定就能解决,动态坏点必须靠实时检测算法。工业检测中,动态坏点的危害往往比静态坏点更大,因为它不可预测。

3.2 坏点检测算法

检测坏点,核心思路就一句话:看这个像素跟周围邻居比,是不是「不正常」。常用的方法有两种:梯度法和中值法。

3.2.1 梯度法

梯度法,说白了就是算差值。我取当前像素跟上下左右四个方向的差值,如果某个方向上的差值远大于正常范围,那就说明有问题。

具体做法是这样的:

  1. 计算水平方向梯度:G_h = |P(i,j) - P(i,j-1)| + |P(i,j) - P(i,j+1)|
  2. 计算垂直方向梯度:G_v = |P(i,j) - P(i-1,j)| + |P(i,j) - P(i+1,j)|
  3. 计算对角方向梯度:G_d1 和 G_d2
  4. 取四个方向的最小梯度值 G_min
  5. 如果 G_min > Th,判定为坏点

阈值 Th 怎么定?我一般用全局统计 + 局部自适应。先算整幅图的梯度均值 μ 和标准差 σ,然后设 Th = μ + k * σ。k 值我习惯取 3~5,太严容易误检,太松又漏检。

实战经验:梯度法对边缘区域的坏点容易误判。我曾在一条 PCB 焊点检测线上发现,电路板边缘的梯度值天然就高,用梯度法会把正常边缘当成坏点。后来我加了一步边缘掩膜,先判断当前像素是否在边缘区域,如果是,就降低检测灵敏度。

3.2.2 中值法

中值法更稳健一些。它的逻辑是:取当前像素周围 3x3 或 5x5 邻域的中值,如果当前像素跟中值的差异超过阈值,就判定为坏点。

代码实现很简单:

// 中值法坏点检测示例
uint16_t median_filter(uint16_t* window, int size) {
    // 排序取中值
    sort(window, window + size);
    return window[size / 2];
}

bool is_bad_pixel(uint16_t center, uint16_t* neighbors, int size, int threshold) {
    uint16_t med = median_filter(neighbors, size);
    int diff = abs((int)center - (int)med);
    return diff > threshold;
}

中值法的好处是对边缘不敏感。因为中值本身就能保留边缘信息,不像均值会把边缘模糊掉。我个人的习惯是:平坦区域用梯度法,纹理区域用中值法,或者两者结合投票。

注意:中值法在坏点密集的区域会失效。如果 3x3 邻域里有两个坏点,中值可能本身就是坏点。我曾经在传感器有坏点簇的情况下吃过亏,后来改用 5x5 邻域 + 多级中值才解决。

3.3 坏点校正与补偿策略

检测到坏点之后,怎么补?这里有几个常用策略。

3.3.1 中值替换

最简单粗暴的方法。用邻域中值直接替换坏点像素值。速度快,效果好,适合大多数场景。

3.3.2 线性插值

利用坏点周围正常像素做双线性插值。精度比中值高,但计算量大一些。我一般在 Bayer 格式下做,只插同色通道的像素。

3.3.3 自适应加权

根据坏点周围像素的梯度方向,选择插值方向。比如水平梯度小,就优先用水平方向的像素做插值。这个策略在边缘区域效果最好。

策略 计算量 精度 适用场景
中值替换 低 中 平坦区域、实时性要求高
线性插值 中 高 纹理区域、Bayer 格式
自适应加权 高 最高 边缘区域、高精度检测

我的建议:工业缺陷检测场景,优先用中值替换 + 自适应加权的组合。中值替换兜底,自适应加权处理边缘。别为了省那几毫秒的计算时间,让坏点毁了整个检测结果。

3.4 坏点校正整体流程

下面这张图是我在实际项目中总结的 DPC 流程,你可以参考一下。

坏点校正(DPC)整体流程 原始 RAW 图像 静态坏点标定(查表) 动态坏点检测(梯度法/中值法) 是否坏点? 是 坏点校正(插值/中值) 否 保留原像素值 校正后 RAW 图像 说明 静态坏点:查表替换 动态坏点:实时检测 校正后统一输出

整个流程走下来,你会发现:静态坏点靠标定,动态坏点靠算法,校正策略看场景。这三件事做好了,DPC 这块基本就稳了。

避坑指南:我曾经在一条产线上发现,DPC 校正后的图像在坏点位置出现了「十字架」伪影。查了半天,原来是中值替换时用了 3x3 窗口,而坏点正好在 Bayer 格式的绿色通道上,导致插值方向错了。后来改成 5x5 窗口 + 同色通道插值,问题解决。嗯,细节决定成败。