黑电平校正:从物理机理到FPGA实现
黑电平校正,听起来好像是个小问题?
我刚开始做ISP的时候,也这么想。直到有一次,客户拿着一张纯黑图过来,说你们这相机拍出来怎么是灰的?我一看,暗部像素值居然有50多。嗯,从那以后,我再也不敢小看黑电平校正了。
黑电平是怎么来的?
说白了,黑电平就是传感器在完全无光的情况下,依然输出的信号值。
你想想看,一个理想的传感器,没光就应该输出0。但现实世界哪有那么完美?
黑电平的产生主要有三个来源:
- 暗电流:硅基半导体在热激发下,即使没有光子入射,也会产生电子-空穴对。温度每升高6-8度,暗电流就翻一倍。我在项目中遇到过,夏天户外35度时,黑电平比实验室25度时高了整整一倍。
- 读出噪声:像素电荷在转移、放大、模数转换过程中,电路本身会引入偏移。这个偏移量是固定的,跟温度关系不大。
- ADC偏移:模数转换器为了避开近地噪声,通常会人为加一个偏置。比如12位ADC,可能把0点设在64附近。
核心结论:黑电平 = 暗电流(温度相关) + 读出噪声(固定) + ADC偏移(固定)
暗场采集:你得知道怎么拍
要校正黑电平,首先得知道黑电平是多少。这就需要采集暗场数据。
暗场采集的方法,我总结了几条经验:
- 完全遮光:镜头盖盖好,最好用黑色遮光布包住镜头。我见过有人用手捂着,结果手指缝漏光,采集出来的暗场全是条纹。
- 相同曝光参数:暗场采集时,曝光时间、增益、帧率必须和实际拍摄一致。因为暗电流跟曝光时间成正比。
- 多帧平均:单帧暗场噪声太大,建议采集16-64帧做平均。我习惯用32帧,效果和64帧差不多,但省时间。
- 温度控制:如果产品工作温度范围宽,建议在多个温度点采集暗场。比如25℃、45℃、65℃各采一组。
小技巧:暗场采集时,把传感器温度记录下来。这样后期做温度补偿时,就有数据支撑了。
全局黑电平校正:简单粗暴
全局校正,就是整幅图像减去同一个值。公式很简单:
// 全局黑电平校正
uint16_t black_level = 64; // 从暗场数据统计得到
for (int i = 0; i < width * height; i++) {
pixel[i] = (pixel[i] > black_level) ? (pixel[i] - black_level) : 0;
}
这种方法的好处是快,一个减法就搞定。但问题也很明显——它假设所有像素的黑电平都一样。
实际上呢?传感器边缘和中心的暗电流往往不同。我测过一款CMOS,左上角黑电平是62,右下角是68。用全局校正,一边偏暗一边偏亮。
注意:全局校正只适用于黑电平一致性好的传感器。如果你发现校正后图像有区域性偏色,那就得考虑局部校正了。
局部黑电平校正:精细活
局部校正,说白了就是给每个像素或每个区域一个独立的黑电平值。
常用的方法有两种:
1. 基于OB像素的实时校正
传感器通常会在像素阵列边缘预留一些光学黑像素(OB,Optical Black)。这些像素被金属层遮挡,不感光,只反映暗电流。
算法流程:
// 基于OB像素的局部校正
// 假设每行有16个OB像素在左侧
for (int row = 0; row < height; row++) {
// 计算当前行OB像素均值
uint32_t ob_sum = 0;
for (int col = 0; col < 16; col++) {
ob_sum += ob_pixels[row][col];
}
uint16_t row_black = ob_sum / 16;
// 用该行的黑电平校正整行
for (int col = 16; col < width + 16; col++) {
pixel[row][col] = (pixel[row][col] > row_black) ?
(pixel[row][col] - row_black) : 0;
}
}
2. 基于暗场标定的二维校正
这种方法更精确,但需要预先存储一张暗场图像。
我习惯的做法是:
- 采集暗场图像D(x,y),做32帧平均
- 对D(x,y)做高斯滤波,去除随机噪声
- 实际校正时:P_out(x,y) = P_in(x,y) - D(x,y)
经验之谈:二维校正的效果最好,但需要额外存储一帧图像。对于资源紧张的嵌入式系统,可以用分块校正——把图像分成16x16的块,每个块存一个黑电平值,块内做双线性插值。
FPGA实现要点
在FPGA上实现黑电平校正,有几个坑我得提醒你:
1. 流水线设计
校正是一个像素级操作,非常适合流水线。我通常这样设计:
// 流水线阶段
Stage 1: 读取像素值 + 读取对应黑电平值
Stage 2: 减法运算
Stage 3: 钳位到0(防止下溢)
Stage 4: 输出
2. 存储资源
如果是基于OB像素的行校正,只需要一个行缓存存OB均值。如果是二维校正,需要存整帧暗场数据。
我建议:
- 行校正:用BRAM存256个16位值,够用
- 二维校正:用DDR3/DDR4存暗场数据,FPGA内部只做缓存
3. 时序约束
减法器在FPGA上延迟很小,但要注意数据对齐。特别是当黑电平值和像素值来自不同时钟域时,一定要做同步处理。
曾经踩过的坑:有一次我忘了对OB像素做同步,结果每行校正用的黑电平是上一行的值。图像出来是一行亮一行暗,像斑马线一样。查了两天才找到原因。
4. 资源估算
| 校正方式 | LUT | BRAM | DSP |
|---|---|---|---|
| 全局校正 | ~50 | 0 | 0 |
| 行校正(OB) | ~200 | 1 | 0 |
| 二维校正 | ~500 | 2-4 | 0 |
本章知识体系
下面这张图,是我画的黑电平校正整体流程。你可以看到从物理机理到FPGA落地的完整链路:
这张图把黑电平校正的四个环节串起来了。从产生机理出发,到采集方法,再到算法选择,最后落地到FPGA。每个环节都有对应的技术要点。
我的建议:刚接触黑电平校正时,先从全局校正入手。跑通流程后,再根据实际效果决定要不要升级到局部校正。别一上来就搞二维校正,容易把自己绕进去。
好了,黑电平校正就讲到这里。记住一句话:暗部细节好不好,黑电平校正见分晓。这个环节做扎实了,后面的白平衡、去马赛克才能发挥出效果。
公众号:蓝海资料掘金营,微信deep3321