3、黑电平校正:从源头到工程落地
黑电平校正,听起来好像是个小问题?
其实不然。我见过不少团队,前期算法跑得飞起,一到量产就栽在黑电平上。说白了,黑电平没处理好,整个图像就偏色、发灰,后期怎么调都救不回来。
今天咱们就把黑电平这件事彻底讲透。
3.1 黑电平的来源
黑电平不是凭空冒出来的。它主要有两个来源:暗电流和ADC偏移。
3.1.1 暗电流
传感器里的硅材料,天生就有热激发效应。温度越高,电子越活跃。即使完全遮光,像素也会产生电荷。这就是暗电流。
我在项目中遇到过,夏天户外35度,暗电流能比实验室高出一倍多。嗯,这个坑后面会细说。
3.1.2 ADC偏移
模数转换器也不是完美的。它有个固定的偏置电压,哪怕输入是0,输出也不是0。这个偏移量,各家芯片不同,同一颗芯片不同通道也可能不同。
核心结论:黑电平 = 暗电流贡献 + ADC偏移贡献
两者叠加,导致全黑场景下,像素值不是0,而是某个正数。
3.2 黑电平校正算法
知道了来源,怎么校正?
常用的方法有三种。我按工程复杂度排个序:
3.2.1 全局减
最简单粗暴。整幅图像减去同一个值。
// 伪代码示例
uint16_t black_level = 64; // 全局黑电平值
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixel[y][x] = (pixel[y][x] > black_level) ?
(pixel[y][x] - black_level) : 0;
}
}
优点:快,省资源。缺点:精度不够。不同位置的黑电平其实不一样。
3.2.2 行/列相关减
这个就讲究了。每行或每列单独算黑电平。
为什么?因为传感器读出时,行与行之间有时间差,暗电流积累量不同。尤其卷帘快门,第一行和最后一行可能差了几十毫秒。
// 行相关黑电平校正
uint16_t black_level_per_row[height]; // 每行单独标定
for (int y = 0; y < height; y++) {
uint16_t bl = black_level_per_row[y];
for (int x = 0; x < width; x++) {
pixel[y][x] = (pixel[y][x] > bl) ? (pixel[y][x] - bl) : 0;
}
}
我的经验:行相关校正对消除竖条纹特别有效。有一次客户投诉图像有「水波纹」,其实就是行黑电平不一致导致的。
3.3 黑电平校准流程
校准不是一次性的。我建议按这个流程走:
- 暗场采集:盖上镜头盖,采集多帧全黑图像
- 帧平均:多帧平均,消除随机噪声
- 分区统计:按行/列/区域统计均值
- 标定表生成:写入传感器或ISP的OTP
- 温度补偿:不同温度下重新标定
注意:标定环境必须完全避光。我见过有人用手挡着镜头就标定,结果漏光导致黑电平偏了10多个DN。
3.4 实际工程中的注意事项
这部分全是血泪教训。我一条条说:
- 温度漂移:暗电流随温度指数增长。每升高10度,暗电流大约翻倍。所以黑电平不能只标一次。
- 增益影响:模拟增益放大后,黑电平也被放大了。校正要在增益之前做,或者做增益补偿。
- OB像素利用:很多传感器有光学黑像素(OB),可以直接用它们实时估算黑电平。但要注意OB像素本身也有噪声。
- 裁剪问题:校正后可能出现负值,一定要做截断处理。否则后续算法会崩。
避坑指南:我曾经在量产阶段发现,同一批次模组,黑电平差异能达到20个DN。后来查出来是PCB走线阻抗不一致导致的ADC偏移不同。从那以后,我坚持每颗模组单独标定。
3.5 知识体系总览
下面这张图,把黑电平校正的来龙去脉串起来了:
这张图把黑电平校正拆成了四个模块:来源、算法、流程、工程注意。你顺着箭头看,就能理解整个链路。
最后说一句:黑电平校正看着基础,但做扎实了,后面白平衡、去马赛克都会省心很多。别嫌麻烦,一步到位比返工强百倍。
公众号:蓝海资料掘金营,微信deep3321