3. 坏点校正(DPC)
坏点校正,简称 DPC,是 ISP 流水线里最基础也最容易被忽视的一环。我刚开始做工业相机时,总觉得这玩意儿不就是抹几个像素点嘛,后来在一条手机盖板玻璃检测线上被狠狠上了一课——因为一个暗坏点,系统把同一位置的黑点误判成了划伤,每天误报几百片。从那以后,我对 DPC 再也不敢马虎。
说白了,坏点就是传感器上那些「不听话」的像素。它们要么永远亮着,要么永远黑着,要么响应忽高忽低。在缺陷检测场景里,这些坏点会直接污染特征,你想想看,一个坏点被算法当成缺陷,那后果多严重。
3.1 静态坏点与动态坏点
坏点分两类,咱们先搞清楚它们的区别。
| 类型 | 表现 | 成因 | 是否固定位置 |
|---|---|---|---|
| 静态坏点 | 始终全亮(白点)或全黑(黑点) | 制造缺陷、物理损伤 | 是,位置固定 |
| 动态坏点 | 响应异常,随温度/增益变化 | 暗电流不均、随机噪声 | 否,时有时无 |
静态坏点好理解。传感器出厂时就有,或者使用中被激光打坏了、被灰尘砸了,那个像素就「死」了。我在项目中遇到过一片 2000 万像素的 CMOS,刚上电就有 3 个白点,位置纹丝不动,这就是典型的静态坏点。
动态坏点就狡猾多了。它们平时看着正常,一旦温度升高或者增益拉大,就开始「抽风」。我记得有一次做低照度检测,增益开到 16dB,画面里突然冒出一片闪烁的亮点,第二天降温又没了。嗯,这就是动态坏点在作祟。
关键认知:静态坏点靠标定就能解决,动态坏点必须靠实时检测算法。工业检测中,动态坏点的危害往往比静态坏点更大,因为它不可预测。
3.2 坏点检测算法
检测坏点,核心思路就一句话:看这个像素跟周围邻居比,是不是「不正常」。常用的方法有两种:梯度法和中值法。
3.2.1 梯度法
梯度法,说白了就是算差值。我取当前像素跟上下左右四个方向的差值,如果某个方向上的差值远大于正常范围,那就说明有问题。
具体做法是这样的:
- 计算水平方向梯度:
G_h = |P(i,j) - P(i,j-1)| + |P(i,j) - P(i,j+1)| - 计算垂直方向梯度:
G_v = |P(i,j) - P(i-1,j)| + |P(i,j) - P(i+1,j)| - 计算对角方向梯度:
G_d1和G_d2 - 取四个方向的最小梯度值
G_min - 如果
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 这块基本就稳了。
避坑指南:我曾经在一条产线上发现,DPC 校正后的图像在坏点位置出现了「十字架」伪影。查了半天,原来是中值替换时用了 3x3 窗口,而坏点正好在 Bayer 格式的绿色通道上,导致插值方向错了。后来改成 5x5 窗口 + 同色通道插值,问题解决。嗯,细节决定成败。