4、坏点检测与校正:坏点分类、动态检测与插值补偿

坏点,说白了就是传感器像素阵列里的“瑕疵品”。

我刚开始做图像传感器时,觉得坏点不就是几个不亮的点嘛,补上就行了。后来发现,事情远没那么简单。坏点会随着温度、增益、曝光时间变化,甚至今天测是好的,明天就冒出来了。

这一章,我们就来彻底搞定坏点问题。

4.1 坏点分类:死点、亮点、热像素

坏点按表现行为,主要分三类。我习惯用一张表来区分它们:

类型 英文名 典型表现 成因 是否随温度变化
死点 Dead Pixel 输出始终为0或极低值 光电二极管损坏、电路断路 基本不变
亮点 Stuck Pixel / Hot Pixel 输出始终为饱和值或高值 电路短路、漏电流过大 轻微变化
热像素 Hot Pixel / Warm Pixel 暗场下输出随温度升高而增大 暗电流异常、杂质污染 显著变化

关键区分:死点和亮点是“硬故障”,出厂就定了。热像素是“软故障”,温度一高就冒出来,温度降下去又好了。这也是动态检测最难处理的地方。

我在项目中遇到过一种情况:客户反馈低温下图像完美,一到夏天就满屏“星星”。查了半天,就是热像素在作怪。所以,坏点检测必须覆盖全温度范围,不能只在实验室25℃下测。

4.2 动态坏点检测算法

静态坏点检测(比如出厂标定一张暗场图)只能对付死点和亮点。热像素怎么办?必须上动态检测。

动态检测的核心思路很简单:拿当前像素跟周围邻居比。如果它跟邻居差异太大,那它大概率有问题。

我常用的算法流程是这样的:

  1. 取窗口:以目标像素为中心,取5x5或3x3邻域。
  2. 计算统计量:计算邻域内像素的均值、中值或标准差。
  3. 设定阈值:根据传感器增益、ISO、温度查表得到动态阈值。
  4. 比较判决:如果目标像素值与邻域中值的偏差超过阈值,标记为坏点。

嗯,这里要注意阈值怎么设。设太严,好点被误杀;设太松,坏点漏检。我个人习惯用自适应阈值:

// 伪代码:自适应阈值动态坏点检测
uint8_t detect_bad_pixel(uint16_t center, uint16_t *neighbors, int size, float gain) {
    // 1. 计算邻域中值
    uint16_t median = quick_median(neighbors, size);
    
    // 2. 计算邻域标准差
    float std = calc_std(neighbors, size, median);
    
    // 3. 自适应阈值 = 基础阈值 * 增益因子 + 噪声基底
    float threshold = BASE_THRESHOLD * gain + 3.0f * std;
    
    // 4. 判决
    float diff = (float)abs((int)center - (int)median);
    if (diff > threshold) {
        return 1; // 坏点
    }
    return 0; // 好点
}

避坑指南:我曾经在低光照场景下,把好点误判为坏点。原因是邻域标准差太小,阈值被压得太低。后来我加了一个最小阈值保护,保证即使场景很平,阈值也不会低于某个下限。

4.3 坏点插值补偿技术

检测到坏点后,不能直接扔掉,得用周围好点的信息把它“补”回来。常用的补偿方法有几种:

4.3.1 邻域均值插值

最简单粗暴的方法。取坏点周围8个或4个邻居的平均值。优点是快,缺点是会模糊边缘。

4.3.2 中值插值

取邻域中值。对孤立坏点效果很好,而且能保留边缘。我比较推荐这个方法。

4.3.3 方向性插值

先判断边缘方向(水平、垂直、对角),然后只沿着边缘方向取邻居做插值。效果最好,但计算量也最大。

下面这张图展示了不同插值方法的效果对比:

坏点插值补偿方法对比 原始图像 含坏点 均值插值 边缘模糊 中值插值 边缘保留 方向性插值 效果最佳 选择建议 • 资源受限:均值插值 • 通用场景:中值插值 • 高质量要求:方向性插值 • 边缘敏感:方向性插值

4.4 实际工程中的坏点处理流程

在实际的ISP pipeline中,坏点校正通常放在黑电平校正之后、去马赛克之前。我总结了一个完整的处理流程:

  1. 出厂标定:在暗场和均匀光照下,生成静态坏点表(死点+亮点)。
  2. 温度补偿:根据温度传感器读数,从查找表中获取当前温度下的热像素阈值。
  3. 动态检测:逐帧扫描,用4.2节的算法检测新出现的坏点。
  4. 坏点合并:将动态检测到的坏点与静态坏点表合并。
  5. 插值补偿:对合并后的坏点列表,执行插值补偿。
  6. 结果校验:检查补偿后的图像是否出现新的异常。

重要提醒:动态检测不能每帧都做,太耗资源。我一般设置每30帧做一次全帧检测,或者只在场景切换、增益变化时触发检测。另外,检测到的坏点要加入临时坏点表,连续多帧都出现才确认为永久坏点,避免闪烁噪声误判。

4.5 性能权衡与选型建议

坏点校正的算法选择,本质上是在效果、速度、资源三者之间做权衡。我整理了一张选型表:

场景 推荐算法 原因
低端MCU、低帧率 静态坏点表 + 均值插值 资源占用极小,无动态检测开销
中端ISP、30fps 动态检测 + 中值插值 平衡效果与性能,适合大多数场景
高端安防、车载 全帧动态检测 + 方向性插值 对坏点零容忍,需要最高质量
科研、医疗成像 多帧融合 + 机器学习检测 追求极致精度,可接受较大计算量

我个人建议,不要一上来就上最复杂的算法。先跑通静态坏点表+中值插值,看看效果。如果客户反馈有热像素问题,再逐步加入动态检测。这样开发周期短,风险也低。

一个小技巧:在调试阶段,可以把检测到的坏点标记成红色或绿色,输出到屏幕上。这样一眼就能看出哪些点被检测到了,有没有漏检或误检。我每次调试新传感器都会用这个办法,非常直观。