4、坏点校正(DPC):静态与动态坏点检测、校正算法选择

坏点校正,说白了就是给图像传感器“排雷”。

每个传感器都有坏点,这是物理规律。我经手过的项目里,没有一颗sensor是完美无缺的。坏点不处理,拍出来的照片就像脸上长了麻子,尤其是暗光下,那叫一个明显。

这一章,咱们就聊聊怎么把这些“雷”找出来,再填平它。

4.1 坏点的本质:它长什么样?

坏点分三种,我习惯用“死、活、偏”来记:

类型 表现 成因
死点(Dead Pixel) 始终为0或接近0,全黑 光电二极管损坏,完全不感光
亮点(Hot Pixel / Stuck Pixel) 始终为最大值或接近最大值,全白 电路漏电或击穿,持续输出高电平
偏点(Defect Pixel) 响应曲线偏移,比正常像素亮或暗 制造工艺偏差,增益异常

你想想看,一个亮点在暗场下就像黑夜里的灯泡,非常扎眼。而死点在亮场下,就像白墙上有个黑点,同样无法忍受。

关键指标:坏点率通常用PPM(百万分之一)表示。消费级sensor的坏点率一般在50-200 PPM之间。但这不是重点,重点是坏点的分布——如果坏点扎堆出现在人脸区域,那画面就毁了。

4.2 静态坏点检测:出厂就定好的“黑名单”

静态坏点检测,就是给sensor做一次“入职体检”。

我在项目中遇到过,有些模组厂为了赶工期,静态坏点表做得非常粗糙,结果量产时发现大量坏点投诉。嗯,后来我们强制要求每颗模组都必须生成独立的静态坏点表。

4.2.1 检测流程

  1. 采集暗场:盖上镜头盖,拍一张全黑图。曝光时间要足够长,让正常像素的噪声充分显现。
  2. 采集亮场:对着均匀光源(如积分球),拍一张全白图。亮度控制在饱和值的80%左右。
  3. 阈值判定:
    • 暗场中,像素值 > 暗场阈值 + 噪声标准差 × K → 标记为亮点
    • 亮场中,像素值 < 亮场阈值 - 噪声标准差 × K → 标记为死点
  4. 生成坏点表:将坐标写入OTP(One Time Programmable)或校准文件。

我的经验:K值一般取3~5。K=3时检测灵敏度高,但容易把正常像素误判为坏点(虚警);K=5时虚警率低,但可能漏掉轻微坏点。我通常先用K=3做一次粗筛,再用K=5做一次确认,取交集。

4.2.2 静态校正:查表替换

校正方法很简单——查坏点表,找到坏点坐标,然后用周围像素插值替换。

// 伪代码:坏点校正(中值替换)
for each pixel (x, y) in bad_pixel_table:
    // 取3x3邻域内的正常像素
    neighbors = get_valid_neighbors(x, y, 3)
    // 用中值替换坏点
    output[x][y] = median(neighbors)

常用的插值方法有:

  • 中值滤波:鲁棒性好,能抵抗孤立噪声,但会损失细节。
  • 均值滤波:计算简单,但容易模糊边缘。
  • 方向插值:沿边缘方向插值,保留细节效果好,但计算量大。

我个人习惯在边缘区域用方向插值,在平坦区域用中值滤波。这样既能保细节,又能省算力。

4.3 动态坏点检测:实时抓“现行”

静态坏点表只能处理出厂时就存在的坏点。但sensor在使用过程中,受温度、电压、老化等因素影响,会不断产生新的坏点。这就是动态坏点。

说白了,静态坏点是“已知罪犯”,动态坏点是“实时犯罪”。

4.3.1 检测原理

动态坏点检测的核心思想是:坏点与周围像素的差异,远大于正常像素之间的差异。

具体做法:

  1. 对每个像素,计算它与周围8个像素的差值。
  2. 如果差值超过某个动态阈值,就标记为候选坏点。
  3. 对候选坏点做多帧确认——连续N帧都被标记,才最终判定为坏点。

注意:动态阈值不能是固定值。它应该根据当前帧的噪声水平自适应调整。我曾经见过一个项目,动态阈值设死了,结果在暗光下把大量正常像素误判为坏点,画面全是“马赛克”。

4.3.2 自适应阈值计算

我常用的自适应阈值公式:

threshold = base_threshold + noise_level * gain_factor
  • base_threshold:基础阈值,通常取10~20(8bit数据)。
  • noise_level:当前帧的噪声估计值,可以用局部标准差或全局噪声模型。
  • gain_factor:增益因子,通常取2~4。增益越高,阈值越大,避免在暗光下误判。

4.4 校正算法选择:什么时候用哪种?

校正算法没有银弹。不同的场景,适合不同的方法。我整理了一个对比表:

算法 优点 缺点 适用场景
中值替换 鲁棒性强,实现简单 模糊细节,边缘锯齿 平坦区域、静态坏点
均值替换 计算量最小 易受噪声影响,模糊严重 低端芯片、实时性要求极高
方向插值 保留边缘细节 计算量大,方向判断可能出错 边缘区域、高画质要求
梯度自适应 兼顾细节和平坦区域 实现复杂,需要调参 动态坏点、通用场景

我个人的经验是:静态坏点用中值替换,动态坏点用梯度自适应。前者稳定可靠,后者灵活智能。

4.5 知识结构图:坏点校正全景

下面这张图,帮你把整个DPC模块的脉络理清楚:

坏点校正(DPC)知识结构 坏点校正 DPC 静态坏点检测 动态坏点检测 校正算法选择 暗场采集 亮场采集 阈值判定 生成坏点表 邻域差值计算 自适应阈值 多帧确认 中值替换 方向插值 梯度自适应 💡 核心思路:静态坏点靠“查户口”(出厂校准),动态坏点靠“现场抓”(实时检测) 校正时,静态用中值保稳定,动态用梯度自适应保细节

4.6 避坑指南:我踩过的那些坑

做DPC调优这么多年,我踩过的坑不少。挑几个典型的说说:

  • 静态坏点表不更新:我曾经遇到一个项目,sensor在高温下产生了大量新坏点,但静态坏点表还是出厂时的。结果就是,温度一高,画面全是亮点。后来我们加了一个“在线坏点更新”机制——每1000帧检测一次,把新发现的坏点加入动态表。
  • 动态检测阈值太激进:有一次,为了追求“零坏点”,我把动态阈值设得非常低。结果在暗光下,正常像素的噪声被误判为坏点,画面全是闪烁的“星星”。嗯,后来我学乖了——阈值一定要和噪声水平联动。
  • 校正算法选错:在纹理丰富的区域用中值替换,结果边缘全糊了。后来我改用方向插值,效果立竿见影。所以,没有最好的算法,只有最合适的场景。

重要提醒:DPC一定要放在BLC(黑电平校正)之后、去噪之前。为什么?因为BLC会改变像素的数值,如果先做DPC再做BLC,坏点检测的阈值就全乱了。这个顺序问题,我见过不止一个团队搞反。

好了,坏点校正这块,核心就是“检测”和“校正”两件事。检测要准,校正要稳。静态坏点靠出厂校准,动态坏点靠实时检测。校正算法根据场景灵活选择——平坦区用中值,边缘区用方向,通用场景用梯度自适应。

记住一句话:坏点校正不是要把所有坏点都消灭,而是让坏点“看起来不像坏点”。