3、黑电平校正(BLC):原理、实现方法、动态黑电平与固定黑电平
黑电平校正,圈里人常叫 BLC(Black Level Correction)。
说白了,就是让传感器在「全黑」环境下输出的像素值归零。你想想看,一个理想的图像传感器,没光的时候应该输出 0。但现实不是这样——传感器本身有暗电流,ADC 也有偏置,导致全黑时输出一个非零值。这个值,就是黑电平。
如果不把它减掉,整张画面会发灰、发雾,暗部细节全被埋没。我刚开始调 ISP 时,接过一块老 sensor,黑电平没处理好,拍出来的夜景像蒙了一层纱。后来才发现,是 BLC 的参数没对准。
3.1 黑电平是怎么产生的?
黑电平的来源主要有三个:
- 暗电流:半导体热激发产生的电子,温度越高越明显。夏天户外和冬天室内,暗电流能差好几倍。
- ADC 偏置:模数转换器为了避开噪声区,会人为加一个偏移量。比如 10-bit 的 sensor,输出范围是 0~1023,但实际有效数据可能从 64 开始。
- 像素结构差异:不同颜色通道的暗电流不一样,R、Gr、Gb、B 四个通道的黑电平可能各不相同。
关键认知:黑电平不是噪声,是偏置。噪声是随机的,黑电平是固定的(或缓慢变化的)。BLC 做的是减法,不是滤波。
3.2 固定黑电平 vs 动态黑电平
这是 BLC 设计里最核心的分支。我直接上表格对比:
| 对比项 | 固定黑电平(OB 行) | 动态黑电平(统计法) |
|---|---|---|
| 原理 | 利用 sensor 边缘的光学黑像素(OB 行) | 从有效像素区域统计暗区均值 |
| 实时性 | 每帧实时更新 | 需要统计,有延迟 |
| 抗干扰 | 强(OB 行不受场景影响) | 弱(场景中如果有大面积暗区会误判) |
| 硬件开销 | 低(只需读取 OB 行) | 高(需要统计模块) |
| 适用场景 | 绝大多数消费级 sensor | 工业/安防等极端温度场景 |
我个人习惯,能用 OB 行就别用动态统计。为什么?因为动态统计太容易翻车了。我曾经在一个夜间监控项目里试过动态 BLC,结果画面里有一块黑布,统计模块把它当成黑电平,直接减过头,黑布里的细节全没了。
3.3 固定黑电平的实现方法
大多数 sensor 会在像素阵列的顶部或底部留几行「光学黑像素」。这些像素被金属遮光层盖住,收不到光,但电路结构跟有效像素完全一样。
实现流程很简单:
- 读取 OB 行(通常是 4~16 行)的像素值
- 按通道分别求均值(R、Gr、Gb、B)
- 用这个均值作为该帧的黑电平值
- 从有效像素中逐像素减去对应通道的黑电平
代码实现(伪代码):
// 假设 raw 数据是 10-bit,OB 行在顶部
uint16_t ob_sum[4] = {0};
int ob_count[4] = {0};
for (int row = 0; row < OB_ROWS; row++) {
for (int col = 0; col < WIDTH; col++) {
int ch = get_channel(row, col); // 0:R, 1:Gr, 2:Gb, 3:B
ob_sum[ch] += raw[row][col];
ob_count[ch]++;
}
}
// 计算各通道黑电平
uint16_t black_level[4];
for (int i = 0; i < 4; i++) {
black_level[i] = ob_sum[i] / ob_count[i];
}
// 逐像素校正
for (int row = OB_ROWS; row < HEIGHT; row++) {
for (int col = 0; col < WIDTH; col++) {
int ch = get_channel(row, col);
int corrected = (int)raw[row][col] - (int)black_level[ch];
if (corrected < 0) corrected = 0;
out[row][col] = (uint16_t)corrected;
}
}
工程小技巧:OB 行数据不要直接用单行,容易受随机噪声干扰。我一般取 8 行做中值滤波后再求均值,稳定性好很多。
3.4 动态黑电平的实现方法
有些场景下,OB 行不够用。比如温度剧烈变化时,暗电流漂移很快,OB 行和有效像素区的温度不一致。这时候就需要动态黑电平。
常见做法是:
- 对整帧图像做直方图统计
- 找到直方图左侧的峰值(暗区集中区域)
- 以该峰值为基准,减去一个固定偏移量
- 或者用分块统计,取暗块的最小值
但这里有个坑——如果场景里全是暗的(比如拍夜空),动态统计会把真实信号当成黑电平减掉。我踩过这个坑,后来加了一个保护逻辑:动态黑电平不能低于 OB 行测得的基准值。
警告:动态黑电平一定要加上下限钳位。下限用 OB 行值,上限用 sensor 手册标称的最大黑电平。否则温度突变时,画面可能瞬间变黑或变灰。
3.5 BLC 在 ISP 流水线中的位置
BLC 是 ISP 流水线的第一级处理。它紧跟在 RAW 数据接收之后,在去噪、去马赛克、白平衡之前。为什么?
- 如果不先减掉黑电平,后面的增益(比如数字增益、白平衡增益)会把黑电平一起放大,暗部噪声跟着起飞
- 黑电平校正后的数据才是「真正的 0 基准」,后续所有处理都依赖这个基准
我画了一张流程图,帮你理清 BLC 在流水线中的位置:
3.6 工程中的常见问题
做 BLC 调试时,有几个问题我几乎每次都会遇到:
- OB 行被污染:有些 sensor 的 OB 行离有效像素太近,强光下会有光串扰。我遇到过 OB 行读数偏高,导致整帧画面偏暗。解决办法是增加 OB 行数量,或者用边缘几行做保护。
- 通道间黑电平不一致:R 通道和 B 通道的黑电平可能差 2~3 个 DN(数字码值)。如果不分开处理,白平衡会出问题。一定要按通道独立计算。
- 温度漂移:sensor 开机后温度会上升,黑电平会缓慢变化。固定黑电平方案需要定期更新 OB 行均值,不能只取一帧。
我的经验值:对于 10-bit RAW,黑电平通常在 32~64 之间。如果测出来超过 100,先检查 sensor 配置,大概率是寄存器没设对。
3.7 调试 checklist
每次调 BLC,我都会过一遍这个清单:
- 确认 sensor 的 OB 行位置和数量(看 datasheet)
- 全黑环境下抓一帧 RAW,检查各通道均值
- 盖上镜头盖,确认输出接近 0(允许 ±1 DN 波动)
- 用均匀光源拍灰卡,检查暗部是否有色偏
- 温度变化 ±20°C 时,黑电平漂移是否在容忍范围内
- 确认 BLC 后的数据没有负值(硬件要钳位到 0)
嗯,BLC 看起来简单,但它是 ISP 流水线的基石。这块没做好,后面的处理全是白费功夫。我见过太多工程师花大量时间调 AWB、调去噪,结果问题根源是黑电平偏了 2 个 DN。先打好地基,再盖楼。