4. 黑电平校正(BLC):暗电流的“清零”艺术
各位同学,欢迎来到第四章。今天我们来聊聊ISP管线里一个看似不起眼,但绕不开的环节——黑电平校正(Black Level Correction, BLC)。
说白了,BLC要解决的就是一个问题:传感器在“全黑”环境下,为什么还会输出非零的像素值?这个“不该有的底数”,就是黑电平。如果不把它减掉,后面的白平衡、Gamma校正全都会算偏。我早年调试一颗安防Sensor时,就因为BLC参数没对准,导致暗部画面整体发紫,折腾了两天才找到根因——嗯,从那以后我再也不敢小看这步了。
4.1 黑电平是怎么产生的?
黑电平的源头,主要是暗电流。你可以把CMOS传感器的每个像素想象成一个微型水桶,光子打进来产生电子,电子积累成电压信号。但问题是,即使没有光,硅基半导体本身的热激发也会产生电子——这就是暗电流。
暗电流的大小受几个因素影响:
- 温度:温度每升高6-8°C,暗电流大约翻一倍。我做过一个车载项目,夏天车内温度能到70°C,黑电平直接漂了30多个DN(数字码值),不实时补偿根本没法看。
- 曝光时间:曝光越长,暗电流积累越多。长曝光天文摄影里,BLC是标配操作。
- 工艺差异:不同像素、不同列之间的暗电流不完全一致,这就引入了固定模式噪声(FPN)。
另外,ADC的偏移也会贡献一部分黑电平。Sensor输出的原始RAW数据里,通常会预留一些“暗像素”(Optical Black, OB)区域——这些像素被金属遮光层挡住,理论上应该输出0,但实际读出来是一个非零值。这个值,就是我们要减掉的基准。
核心概念:黑电平 ≠ 噪声。噪声是随机的,黑电平是固定的偏移量。BLC的目标是去掉这个固定偏移,而不是滤除噪声。
4.2 校正原理:减法与钳位
BLC的原理非常直观:从每个像素值中减去一个基准黑电平值。但这里有几个细节需要注意。
假设Sensor输出的原始RAW数据是12-bit,黑电平基准是64(即全黑时输出64 DN)。那么校正公式就是:
P_out = max(P_in - BLC_offset, 0)
为什么要取max(..., 0)?因为减法后可能出现负值,而像素值不能为负,必须钳位到0。这个钳位操作,就是BLC的“校正”动作。
但事情没这么简单。实际工程中,黑电平基准不是固定不变的:
- 全局BLC:整幅图像减去同一个值。简单,但无法处理列间差异。
- 列BLC:每列单独计算OB像素的平均值,逐列减去。能消除列FPN,但计算量大一些。
- 动态BLC:根据温度、增益、曝光时间实时调整BLC偏移量。高端安防和车载方案常用。
我个人习惯在调试初期先用全局BLC快速验证管线,等图像基本正常了,再切到列BLC去优化暗部均匀性。一步到位反而容易出问题——你想想看,如果白平衡都没调对,花精力优化列BLC有什么意义?
调试技巧:拿到一颗新Sensor时,先拍一张全黑帧(盖上镜头盖),统计OB区域的平均值。这个值就是你的初始BLC_offset。如果不同增益下黑电平漂移明显,就需要做增益相关的BLC补偿表。
4.3 实现方法:从硬件到软件
BLC的实现可以放在硬件ISP里,也可以用软件后处理。我分别讲一下。
4.3.1 硬件BLC(ISP Pipeline内)
大多数ISP芯片都内置了BLC模块。典型流程如下:
- 从RAW数据中提取OB像素行/列(通常Sensor会预留顶部或底部的几行遮光像素)。
- 计算OB像素的平均值(或中位数,避免坏点干扰)。
- 将平均值作为BLC偏移量,写入ISP寄存器。
- ISP硬件在像素数据进入管线时,自动执行减法+钳位。
硬件BLC的优点是零延迟、不占CPU。缺点是不够灵活——如果OB区域被遮挡或损坏,BLC值就会出错。我曾经遇到一个案例,Sensor的OB行因为封装应力产生了暗电流异常,导致BLC偏移量偏大,整张图暗部发黑。最后不得不在软件里做二次补偿。
4.3.2 软件BLC(后处理)
如果ISP不支持BLC,或者你需要更精细的控制,可以在CPU/GPU上做软件BLC。代码实现很简单:
// 软件BLC示例(C++)
void black_level_correction(uint16_t* raw, int width, int height,
int blc_offset, int bit_depth) {
int max_val = (1 << bit_depth) - 1;
for (int i = 0; i < width * height; i++) {
int corrected = (int)raw[i] - blc_offset;
raw[i] = (uint16_t)std::clamp(corrected, 0, max_val);
}
}
这段代码很简单,但实际工程中要注意:
- 数据类型:RAW数据通常是10/12/14-bit,用uint16_t存储。减法前要转成int,避免下溢。
- 钳位范围:不能低于0,也不能超过bit_depth的最大值。
- 性能优化:如果每帧都做,建议用NEON/SSE指令集并行处理。我做过一个4K@60fps的软件ISP,BLC这一步用NEON优化后,单帧耗时从3ms降到了0.3ms。
注意:BLC必须在任何其他ISP模块之前执行。如果先做了去噪或增益,黑电平偏移会被放大或扩散,后续校正就困难了。管线顺序是铁律。
4.4 BLC与黑电平校准流程
下面这张SVG图展示了BLC在ISP管线中的位置,以及黑电平校准的完整流程。
4.5 工程中的坑与对策
做BLC这么多年,我踩过不少坑。挑几个典型的分享给大家。
| 问题 | 现象 | 原因 | 对策 |
|---|---|---|---|
| OB区域被遮挡 | BLC偏移量偏大,暗部发黑 | 镜头或灰尘遮挡了OB像素 | 使用OB区域的中位数而非均值;定期校准 |
| 温度漂移 | 开机后图像逐渐变亮或变暗 | 暗电流随温度变化 | 加入温度传感器,动态调整BLC |
| 增益相关偏移 | 高增益下暗部偏色 | 模拟增益放大了黑电平偏移 | 建立增益-BLC查找表(LUT) |
| 列FPN残留 | 图像出现竖条纹 | 全局BLC无法消除列间差异 | 改用列BLC,每列独立计算偏移 |
避坑指南:我曾经在一个项目中,为了省成本用了全局BLC,结果量产时发现部分Sensor的OB区域有坏点,导致BLC偏移量异常。后来我强制在BLC之前加了一步OB坏点检测——把OB像素中偏离均值超过3σ的点剔除掉。这个改动虽然只加了十几行代码,但彻底解决了问题。
4.6 小结
黑电平校正,说白了就是给图像“清零”。它不复杂,但位置关键、影响深远。记住几个要点:
- BLC必须在ISP管线的最前端执行。
- 黑电平基准不是固定的,要考虑温度、增益、曝光时间的影响。
- OB区域的质量直接决定BLC效果,别忘了做坏点剔除。
- 硬件BLC快但不灵活,软件BLC灵活但吃性能——根据项目选型。