一、黑电平校正(BLC)原理
黑电平校正,简称BLC,是ISP流水线的第一关。
说白了,传感器在完全无光的情况下,输出的像素值不应该是0。为什么?因为传感器本身有暗电流,还有读出电路的偏置。我刚开始做ISP时,就被这个坑过——拿到的raw图整体偏绿,怎么调白平衡都不对,后来才发现是黑电平没校正好。
1.1 黑电平是怎么产生的
传感器每个像素,即使没有光子入射,也会产生微弱的电流。这叫暗电流。温度越高,暗电流越大。另外,读出电路也有固定的偏置电压。这两者叠加,就形成了黑电平。
你可以把黑电平理解成传感器的「底噪」。它是个固定的偏移量,通常在16到64之间(12bit raw数据)。
核心公式:
校正后的像素值 = 原始像素值 - 黑电平值
如果结果小于0,就截断到0。
1.2 为什么需要BLC
不校黑电平,后面所有处理都会出问题。举个例子:
- 自动曝光(AE)会误判场景亮度
- 白平衡(AWB)会引入色偏
- 去马赛克(Demosaic)会产生伪色
我见过一个产品,BLC没做好,暗光下整张图发紫。折腾了两周,最后发现就是黑电平参数没更新。
二、硬件架构设计
BLC的硬件实现,其实不复杂。但要做好,得考虑几个关键点。
2.1 整体架构
我习惯把BLC模块分成三个子模块:
- 统计模块:收集暗像素区域的统计值
- 计算模块:根据统计值算出当前帧的黑电平
- 校正模块:对每个像素做减法运算
下面这张图,是我自己画的BLC硬件架构:
2.2 暗像素区域的选择
传感器通常会在边缘预留一些被遮挡的像素。这些像素收不到光,理论上就是纯黑。我们拿它们来算黑电平。
但要注意:
- 暗像素区域要避开边缘效应
- 一般取4行×4列到16行×16列
- 不同颜色通道要分开统计
我的经验:暗像素区域不要选在角落。角落的像素受工艺影响大,统计值不稳定。我一般选在传感器上边缘中间位置,取8行×8列。
三、流水线实现
BLC的流水线设计,核心是处理好时序。
3.1 单帧流水线
对于每一帧图像,BLC的处理流程是这样的:
- 帧起始:加载上一帧计算好的黑电平参数
- 行处理:对每个像素做减法,同时统计暗像素值
- 帧结束:根据统计值计算当前帧的黑电平,供下一帧使用
这里有个关键点:当前帧用的是上一帧的黑电平参数。为什么?因为统计和计算需要时间,没法做到「当前帧统计、当前帧校正」。
注意:如果场景变化剧烈,这种「滞后校正」会出问题。比如从暗室突然走到阳光下,前几帧的黑电平可能不准。我遇到过这种情况,后来加了帧间滤波才解决。
3.2 硬件流水线级数
我一般把BLC设计成3级流水线:
| 流水线级 | 操作 | 延迟 |
|---|---|---|
| 第1级 | 读取像素值,减去黑电平 | 1个时钟 |
| 第2级 | 判断是否为暗像素,累加统计值 | 1个时钟 |
| 第3级 | 输出校正结果,更新计数器 | 1个时钟 |
这样设计,每个时钟周期可以处理一个像素。对于1080p@30fps的传感器,完全够用。
四、参数自适应更新策略
黑电平不是一成不变的。温度变了、增益变了、甚至传感器老化,都会影响黑电平。所以参数必须自适应更新。
4.1 更新策略
我常用的策略是「帧间滑动平均」:
// 伪代码:黑电平自适应更新
blc_new = (blc_old * 7 + blc_current) / 8;
这样既跟得上变化,又不会因为单帧噪声导致参数抖动。
4.2 不同增益下的处理
传感器增益变化时,黑电平也会跟着变。我一般会做一张查找表:
| 增益倍数 | 黑电平偏移量 |
|---|---|
| 1x | 0 |
| 2x | +2 |
| 4x | +6 |
| 8x | +14 |
这张表需要在实验室标定。我建议标定三个温度点:0°C、25°C、60°C,然后线性插值。
4.3 异常处理
有时候暗像素区域会被强光照射到(比如镜头漏光)。这时候统计值会异常偏高。
我的做法是加一个阈值判断:如果当前帧的统计值比上一帧大了50%以上,就认为异常,保持上一帧的参数不变。
避坑指南:我曾经遇到过一个case,暗像素区域被灰尘遮挡,导致黑电平统计值一直偏低。后来我加了「暗像素有效性检测」——如果连续5帧的统计值都低于某个下限,就报警并恢复默认参数。
五、总结
BLC看起来简单,就是个减法。但要做好,得考虑统计、计算、更新三个环节。硬件设计上,3级流水线足够;参数更新上,滑动平均加异常检测是标配。
嗯,这一章就到这里。记住一句话:BLC是ISP的基石,基石不稳,后面全白搭。