4. 黑电平校正(BLC):从暗电流到暗部细节
黑电平校正,圈里人常叫 BLC(Black Level Correction)。
这名字听起来挺学术,说白了就是告诉芯片:「你给我记住,0 就是 0,别把黑的说成灰的」。
我刚开始调 ISP 时,觉得 BLC 不就是减个固定值嘛,有啥好研究的?直到有一次,客户拿了一台夜景样机过来,暗部全是紫绿色的噪点,画面像蒙了一层雾。查了半天,就是黑电平没校好。嗯,从那以后,我再也不敢小看这个模块了。
4.1 黑电平是怎么来的?
先说说为什么会有黑电平。你想想看,一个像素在完全无光的情况下,按理说应该输出 0。但实际 CMOS 传感器不是这样的——它即使在暗箱里,也会读出一些信号。
原因主要有三个:
- 暗电流:半导体热激发产生的电子,温度越高越明显。夏天拍夜景,暗电流能比冬天翻一倍。
- 像素复位噪声:每次读出前复位,电压总有点残留。
- ADC 偏移:模数转换器本身有个底噪,通常在 1-2 LSB 左右。
这些因素叠加起来,就形成了一个固定的偏置值。我们管它叫 黑电平(Black Level)。
关键认知:黑电平不是「噪声」,而是「偏置」。它是有规律的、可预测的。BLC 的目的就是把这个偏置去掉,让暗部回到真正的 0。
4.2 BLC 算法原理:全局 vs 局部
BLC 的做法分两种。我调过的项目里,90% 的坑都出在选错方案上。
4.2.1 全局 BLC(Global BLC)
最简单粗暴的方式。整幅图像减去同一个值。
// 全局 BLC 伪代码
for each pixel (x, y):
pixel_out = pixel_raw - BL_global
// BL_global 通常取 OB(Optical Black)区域的平均值
BL_global = average(OB_pixels)
优点很明显:计算量小,硬件实现简单。缺点也很致命:它假设整个 sensor 的暗电流是均匀的。
但现实不是这样的。我遇到过一款 sensor,左上角比右下角温度高 3 度,暗电流差了 15 个 DN。用全局 BLC 一减,左上角暗部细节全没了,右下角还留着偏置。这就是典型的「顾头不顾尾」。
4.2.2 局部 BLC(Local BLC)
更精细的做法。把图像分成多个区域,每个区域独立计算黑电平。
// 局部 BLC 伪代码
// 将图像划分为 MxN 个 block
for each block (i, j):
BL_local[i][j] = average(OB_pixels_in_block[i][j])
// 对每个像素做双线性插值
for each pixel (x, y):
BL_interp = bilinear_interp(BL_local, x, y)
pixel_out = pixel_raw - BL_interp
这样做的好处是:能补偿 sensor 面上的非均匀性。坏处是:需要额外的存储和计算资源。
| 对比项 | 全局 BLC | 局部 BLC |
|---|---|---|
| 计算量 | 低(1 次减法) | 中(插值 + 多次减法) |
| 存储需求 | 1 个值 | MxN 个值 + 插值系数 |
| 非均匀性补偿 | 无 | 有 |
| 适用场景 | 低端 sensor、均匀光照 | 高端 sensor、大靶面、多温度场景 |
| 暗部细节保留 | 一般 | 好 |
我的建议:如果 sensor 靶面小于 1/2 英寸,且工作温度范围窄(0-40°C),全局 BLC 够用。如果是 1 英寸以上大靶面,或者要做车载、安防等宽温产品,老老实实上局部 BLC。别省这点资源,后面调暗部噪点会让你怀疑人生。
4.3 BLC 对暗部细节的影响
这是 BLC 最容易被忽视的地方。我见过很多工程师,BLC 参数调完就跑,从来不看暗部。
为什么会这样?因为 BLC 减多了,暗部细节就丢了;BLC 减少了,暗部偏置还在,噪点跟着放大。
具体来说:
- BLC 偏大(过减):暗部像素被减到负数,然后被 clamp 到 0。原本能看到的纹理,全变成死黑。这叫「暗部截断」。
- BLC 偏小(欠减):暗部残留偏置,经过后面的数字增益(DG)放大后,偏置变成明显的色块和条纹。这叫「暗部偏色」。
我记得有一次调一款手机摄像头,客户反馈夜景模式暗部发紫。我查了半天,发现是 BLC 欠减了 3 个 DN。这 3 个 DN 在 10x 数字增益下,变成了 30 个 DN 的偏置,直接导致 R 通道比 G/B 高出一截。减掉那 3 个 DN,紫色立马消失。
避坑指南:我曾经在调试一款安防摄像机时,发现暗部细节总是比竞品差一档。反复调了 LSC、降噪、gamma,都没用。最后用示波器量了 sensor 的 OB 区域,发现 sensor 厂商给的参考值是在 25°C 下测的,而实际产品工作温度是 50°C。暗电流翻倍了,BLC 却没跟着变。后来我加了一个温度查表补偿,问题才解决。
教训:BLC 参数不能只测一个温度点。至少测 3 个温度(低温、常温、高温),做线性插值。
4.4 调试参数与验证方法
BLC 调试,我一般分三步走。
4.4.1 参数清单
| 参数名 | 说明 | 典型范围 | 调试优先级 |
|---|---|---|---|
| BL_global | 全局黑电平偏置值 | 0-255(10bit) | 高 |
| BL_local_grid | 局部 BLC 网格大小 | 8x8 ~ 32x32 | 中 |
| BL_interp_mode | 插值方式(双线性/最近邻) | 0/1 | 低 |
| BL_clamp_min | 减法后的最小值 clamp | 0 | 高 |
| BL_temp_coeff | 温度补偿系数 | 0.1-2.0 DN/°C | 中 |
4.4.2 调试流程
- 暗场采集:盖上镜头盖,或者用暗箱。拍 10 帧以上,取平均,消除随机噪声。
- 计算 OB 均值:取 sensor 的 OB 像素(通常分布在 sensor 边缘),计算平均值。这就是初始 BL 值。
- 检查均匀性:把暗场图像显示出来,看有没有明显的亮斑或暗角。如果有,说明 sensor 非均匀性严重,需要局部 BLC。
- 微调验证:在 0.1-1 lux 的低照度下拍灰阶卡,看暗部灰阶是否平滑过渡。如果出现跳变或色偏,微调 BL 值。
- 温度遍历:在 -10°C、25°C、60°C 下重复步骤 1-4,建立温度-黑电平曲线。
4.4.3 验证指标
- 暗场均值:BLC 后,暗场图像的全图均值应接近 0(通常允许 ±1 DN 波动)。
- 暗场标准差:反映残留噪声。BLC 本身不降噪,但如果标准差突然变大,说明 BLC 引入了新的非均匀性。
- 暗部灰阶线性度:在 0-10% 的灰阶范围内,输入输出曲线应保持线性。如果出现拐点,说明 clamp 或过减了。
- 色温稳定性:在 D65、A 光、TL84 下分别测暗部色偏。BLC 应该与色温无关,如果出现色温相关的暗部偏色,说明 sensor 的 OB 区域有光谱响应差异。
一个小技巧:验证 BLC 是否准确,可以用「双帧差分法」。拍两帧暗场,相减。如果 BLC 准确,差分图像的均值应该接近 0,且没有固定图案。如果差分图像还有明显的条纹或块状结构,说明 BLC 没校干净。
4.5 BLC 在 Pipeline 中的位置
BLC 是 ISP Pipeline 的 第一个 处理模块。它紧跟在 sensor 原始数据之后,在去噪、去马赛克、白平衡之前。
为什么放在最前面?因为后面的所有模块都假设输入数据是「零偏置」的。如果 BLC 没做好,后面的 LSC、AWB、CCM 都会跟着出错。
你看这个流程,BLC 就像地基。地基没打平,上面盖的房子全是歪的。我每次接手一个新项目,第一件事就是先看 BLC 参数对不对。BLC 对了,后面 80% 的问题都好查;BLC 错了,你调 LSC、调 AWB、调降噪,都是在错误的数据上做无用功。
总结一句话:BLC 不是「减个值」那么简单。它决定了整个 Pipeline 的数据基线。基线对了,暗部细节才能保得住;基线错了,后面再怎么调也是白搭。