第二章 黑电平校正(BLC)实战:BLC原理、暗电流提取方法、不同温度下的BLC补偿策略
2.1 黑电平校正到底在干什么?
说实话,很多刚入行的工程师觉得BLC很简单——不就是减个值嘛?
嗯,表面上看确实是这样。但你要真这么想,那调试出来的图像在暗光下大概率会翻车。
BLC的核心任务只有一个:把传感器在无光条件下的输出值,校正到我们期望的“纯黑”水平。通常这个期望值是0,或者某些平台会留一个小的偏移量(比如16或64)。
我刚开始做车载ISP时,遇到过一台样机,晚上拍出来的画面整体发紫。排查了半天,最后发现是BLC没做好,暗电流把R通道抬高了十几个LSB。你想想看,暗光下这点偏差有多明显。
一句话总结:BLC不准,暗光偏色、动态范围损失、黑电平闪烁,全都会找上门来。
2.2 暗电流是怎么来的?
传感器在完全没有光照时,为什么还会有输出?
说白了,就是热噪声。硅基半导体在温度作用下,电子会自发跃迁,产生电荷。温度越高,这个现象越严重。
暗电流有几个特点,我列一下:
- 与温度强相关:温度每升高6-8°C,暗电流大约翻一倍
- 与曝光时间成正比:曝光越长,积累的暗电荷越多
- 像素间不均匀:每个像素的暗电流大小不一样,这就是FPN(固定模式噪声)的来源
- 与增益相关:模拟增益放大的不只是信号,暗电流也被放大了
我在调试一个800万像素的车载传感器时,发现角落像素的暗电流比中心区域高了将近30%。后来查资料才知道,这跟传感器制造工艺的微透镜倾斜角度有关。
2.3 暗电流提取方法——实战经验
提取暗电流,说白了就是找到“纯黑”条件下的传感器输出。方法有好几种,我挑最常用的三种说。
方法一:物理遮光法(最准确)
把镜头完全盖住,或者用遮光帘挡住光线。拍一组全黑图像,取平均值。
优点:最接近真实暗电流。缺点:产线上不好操作,需要专门的暗室环境。
我的习惯:实验室调试阶段,我一般用物理遮光法做基准。盖上镜头盖后,再在相机前面盖一层黑绒布,确保万无一失。
方法二:OB(Optical Black)像素法(最常用)
现在的车载传感器,基本都带有被金属层遮挡的OB像素。这些像素分布在有效像素区域的边缘,它们接收不到光,只反映暗电流水平。
具体做法:
- 从RAW数据中提取OB像素区域的数值
- 对OB像素做均值或中值滤波(我建议用中值,能剔除个别坏点)
- 用这个值作为当前帧的暗电流估计
// 伪代码示例:OB像素提取暗电流
uint16_t extract_black_level(uint16_t *raw, int ob_width, int ob_height) {
uint32_t sum = 0;
int count = 0;
// 假设OB区域在图像左侧,宽度为ob_width
for (int y = 0; y < ob_height; y++) {
for (int x = 0; x < ob_width; x++) {
// 跳过边界和已知坏点
if (is_defect_pixel(x, y)) continue;
sum += raw[y * stride + x];
count++;
}
}
return (uint16_t)(sum / count);
}
注意:OB像素的物理位置和有效像素不完全一样,它们的暗电流会有细微差异。我曾经遇到过OB区域温度比感光区低2°C的情况,导致BLC校正不足,画面偏暗。后来加了温度补偿系数才解决。
方法三:统计直方图法(应急用)
如果传感器没有OB像素,或者OB像素坏了怎么办?
可以拍一张暗场景图像,分析直方图。暗电流通常表现为直方图左侧的一个峰值,找到这个峰值的位置,就是暗电流的估计值。
这个方法精度有限,我一般只在产线快速标定时用。
2.4 不同温度下的BLC补偿策略
这是BLC调试里最头疼的部分。常温下调得好好的,一上车载到夏天暴晒,画面全飘了。
为什么?因为暗电流随温度变化是非线性的。我整理了一个典型车载传感器的暗电流-温度关系表:
| 温度(°C) | 暗电流(LSB@12bit) | 相对25°C的变化量 |
|---|---|---|
| -20 | 2 | -87% |
| 0 | 5 | -68% |
| 25 | 16 | 基准 |
| 60 | 48 | +200% |
| 85 | 112 | +600% |
| 105 | 256 | +1500% |
看到没?85°C时暗电流是25°C的7倍。如果不做温度补偿,BLC直接崩了。
我的补偿策略:三段式
第一段:标定阶段
- 在-20°C、0°C、25°C、60°C、85°C、105°C六个温度点,分别提取暗电流值
- 建立温度-暗电流查找表(LUT)
- 每个温度点至少采集100帧取平均,减少随机噪声影响
第二段:插值阶段
- 运行时,根据当前传感器温度,在LUT中做线性插值
- 如果温度超出标定范围,用最近端点值外推(但要加限幅)
// 温度补偿插值示例
float get_black_level_by_temp(int temp_c) {
// temp_lut[] 和 black_lut[] 是标定好的数据
// 假设有6个标定点
if (temp_c <= temp_lut[0]) return black_lut[0];
if (temp_c >= temp_lut[5]) return black_lut[5];
for (int i = 0; i < 5; i++) {
if (temp_c >= temp_lut[i] && temp_c < temp_lut[i+1]) {
float ratio = (float)(temp_c - temp_lut[i]) /
(temp_lut[i+1] - temp_lut[i]);
return black_lut[i] + ratio * (black_lut[i+1] - black_lut[i]);
}
}
return black_lut[0]; // fallback
}
第三段:动态微调阶段
- 利用每帧的OB像素实时修正LUT的预测值
- 如果OB像素均值与LUT预测值偏差超过5个LSB,触发重新标定提醒
- 我习惯加一个IIR低通滤波,防止单帧噪声导致BLC抖动
避坑指南:我曾经在某个项目里,直接用OB像素的实时值做BLC,没做温度补偿。结果车子从地库开出来,温度骤变,BLC值跳了20多个LSB,画面闪了好几下。后来老老实实加了温度LUT + OB微调的双重策略,才彻底解决。
2.5 实战中的几个坑
最后分享几个我踩过的坑,希望能帮你省点时间:
- 坑一:BLC校正要在增益之前做,还是在增益之后做?
答案是增益之前。因为增益会放大暗电流,先减后放才能保证信噪比。我见过有方案在增益后做BLC,暗部噪声大了将近一倍。 - 坑二:不同颜色通道的暗电流不一样。
别偷懒用同一个值。R、Gr、Gb、B四个通道要分别提取暗电流,尤其是R通道通常偏高。我习惯用4个独立的BLC参数。 - 坑三:长曝光场景下,暗电流会饱和。
如果曝光时间超过1秒,暗电流可能积累到接近饱和值。这时候BLC已经救不了了,需要从曝光策略上限制。 - 坑四:产线标定时,注意传感器预热。
刚上电的传感器温度不稳定,暗电流一直在漂。我要求产线至少预热5分钟再开始标定,否则数据根本不能用。
总结一下:BLC不是简单的减法,它涉及温度、曝光、增益、通道差异等多个维度。做好BLC,你的暗光图像质量就成功了一半。
下一章我们聊去噪(Denoise),这是车载ISP里最吃经验的部分,到时候我会分享一些不太常见的降噪技巧。