ISP Pipeline核心模块:DPCC、BLC、LSC

好,我们直接进入正题。今天聊的三个模块——DPCC、BLC、LSC——是ISP Pipeline里最靠前的几个处理环节。说白了,它们干的活就是“把RAW数据洗干净”。

你想想看,Sensor送出来的RAW数据,能直接用吗?不能。里面有坏点、有黑电平偏移、有镜头带来的亮度不均匀。如果不处理,后面做去噪、做色彩还原,全都会出问题。

我个人习惯把这三个模块叫做“RAW数据的三道净化关”。咱们一个一个来过。

4.1 DPCC(坏点校正)

DPCC全称是Defective Pixel Correction。坏点,就是Sensor上那些“不听话”的像素点——该亮的时候不亮,该暗的时候不暗。

4.1.1 坏点的类型

类型 表现 成因
亮点(Hot Pixel) 固定输出高值,画面中始终为白点 暗电流过大、像素漏电
暗点(Dead Pixel) 固定输出低值,画面中始终为黑点 光电二极管损坏、电路断路
闪烁点(Stuck Pixel) 输出值固定不变,不随光照变化 像素复位失效

我在项目中遇到过一种情况:某款Sensor在低温环境下,亮点数量会突然暴增。当时排查了很久,最后发现是温度补偿没做好。嗯,这个坑后面会细说。

4.1.2 DPCC的两种策略

DPCC分两步走:先检测,再校正。

静态坏点校正:依赖Sensor厂商提供的坏点表(Defect Table)。这个表在出厂时标定好,ISP直接查表替换。优点是快,缺点是——如果Sensor老化产生新的坏点,它就无能为力了。

动态坏点校正:实时检测当前帧中的异常像素。常见做法是拿当前像素跟周围8邻域做比较。如果差值超过阈值,就判定为坏点。

核心思路:动态检测通常用中值滤波或均值滤波来替换坏点。我个人更倾向中值滤波——它对边缘的保护更好,不会把细节磨掉。

来看一段简化的动态DPCC伪代码:

// 动态坏点检测与校正(简化版)
for each pixel (x, y) in RAW image:
    // 取3x3邻域
    neighbors = get_3x3_neighbors(x, y)
    median_val = median(neighbors)
    diff = abs(pixel_val - median_val)
    
    if diff > threshold:
        // 判定为坏点,用中值替换
        output[x][y] = median_val
    else:
        output[x][y] = pixel_val

注意:阈值设置很关键。设得太低,会把正常纹理误判为坏点;设得太高,坏点又漏检。我一般建议先做一段统计,取整帧像素差值的均值+3倍标准差作为初始阈值。

4.2 BLC(黑电平校正)

BLC,Black Level Correction。这个模块处理的是Sensor的“零点漂移”问题。

理想情况下,Sensor在全黑环境下的输出应该是0。但实际不是——因为有暗电流、有电路噪声,全黑时输出往往是一个非零值。这个值就是黑电平(Black Level)。

4.2.1 黑电平的来源

  • 暗电流:温度越高,暗电流越大,黑电平越高
  • ADC偏移:模数转换器本身有固定的偏置
  • 电路串扰:电源噪声、时钟耦合等

我记得有一次调试一个低光场景,画面整体偏绿。查了半天,最后发现是BLC没做对——黑电平校正值偏了,导致暗区的R、G、B通道比例失衡。你想想看,暗区的偏移量被放大到整个动态范围,颜色能对吗?

4.2.2 BLC的实现方式

最简单的做法:

// 黑电平校正
output = raw_pixel - black_level

// 如果Sensor有OB(Optical Black)区域
black_level = mean(OB_pixels)

这里有个细节:很多Sensor会预留几行或几列被遮挡的像素(OB区域),这些像素收不到光,它们的平均值就是当前帧的黑电平。用OB区域做BLC的好处是——它能跟随温度变化实时调整。

经验之谈:如果Sensor没有OB区域,就得在标定时测一条“黑电平-温度”曲线。运行时查表插值。我曾经在户外监控项目里吃过亏——白天晚上温差大,固定黑电平值导致画面质量忽好忽坏。

4.3 LSC(镜头阴影校正)

LSC,Lens Shading Correction。镜头阴影,也叫暗角——就是画面四周比中心暗的现象。

为什么会这样?两个原因:

  • 光强衰减:镜头中心到边缘的光程不同,边缘光强自然弱
  • 入射角效应:边缘光线以更大角度入射Sensor,量子效率下降

4.3.1 增益补偿原理

LSC的思路很简单:给边缘像素乘以一个大于1的增益,把亮度拉回来。

// LSC校正
output[x][y] = raw_pixel[x][y] * gain_table[x][y]

这个gain_table是一个二维数组,尺寸跟Sensor分辨率一样。中心区域增益接近1.0,越往边缘增益越大。

4.3.2 标定与插值

实际工程中,不会存一整张全分辨率增益表——太费内存了。通常的做法是:

  1. 在标定时,拍一张均匀亮度的灰卡
  2. 计算每个像素位置需要的增益值
  3. 下采样成一张稀疏网格(比如16x16或32x32)
  4. 运行时用双线性插值算出每个像素的增益

关键点:LSC的增益值不能太大。如果边缘增益超过2.0,会明显放大噪声。我一般控制在1.0~1.8之间。如果暗角太严重,建议从镜头端解决,别全靠ISP硬拉。

4.4 三个模块的处理顺序

这三个模块在Pipeline里的顺序是固定的:DPCC → BLC → LSC。

为什么是这个顺序?

  • 先做DPCC:把坏点去掉,避免坏点干扰后续的统计计算
  • 再做BLC:把黑电平减掉,让数据回到“真正的0点”
  • 最后做LSC:在干净的数据上做增益补偿

顺序错了,效果会打折扣。比如先做LSC再做BLC——增益会把黑电平也放大,导致暗区偏色更严重。

ISP Pipeline 前三级:RAW数据净化流程 RAW 输入 DPCC 坏点校正 中值替换 BLC 黑电平校正 减去OB均值 LSC 镜头阴影校正 增益补偿 净化后 RAW 顺序固定:先DPCC去坏点 → 再BLC归零 → 最后LSC做增益 顺序颠倒会导致噪声放大或偏色问题

4.5 工程中的常见问题

最后聊几个实际调试中容易踩的坑。

坑1:DPCC阈值与噪声混淆

我曾经在低光场景下,把噪声点误判成了坏点。结果画面被过度平滑,细节全没了。解决办法:动态阈值要跟ISO联动——ISO越高,阈值适当放宽。

坑2:BLC与LSC的耦合

如果BLC没做准,LSC的增益会把黑电平误差放大到整个画面。尤其是暗角严重的镜头,边缘增益大,黑电平误差被放大后会出现明显的“红边”或“绿边”。

调试建议:每次调整BLC值后,务必重新标定LSC增益表。这两个模块是强耦合的,分开标定容易出问题。

好了,这三个模块讲完了。它们虽然基础,但直接影响后面所有模块的输入质量。RAW数据洗得干不干净,就看这三关过得好不好。