3、Bayer域处理:黑电平校正、镜头阴影校正、坏点校正的硬件实现

Bayer域是ISP Pipeline的起点。说白了,传感器刚把光信号转成电信号,数据还是最原始的状态。这时候有三个问题必须马上处理:黑电平不准、镜头带来的亮度衰减、以及传感器本身的坏点。这三个模块我习惯放在一起讲,因为它们在硬件上经常共用一些计算资源。

3.1 黑电平校正(BLC)

黑电平校正,名字听着挺唬人,其实道理很简单。传感器在完全无光的情况下,理论上应该输出0。但实际不是这样——由于暗电流、电路噪声等因素,它会输出一个非零的基底值。这个基底值就是黑电平。

校正公式就一行:

P_out = max(0, P_in - BLC_offset)

硬件实现上,我一般用减法器加饱和钳位电路。注意这里有个坑——BLC_offset不是全局统一的。不同颜色通道的暗电流特性不一样,所以R、Gr、Gb、B四个通道要分别配置。

关键参数:BLC_offset通常为64~256(12bit RAW),具体值取决于传感器型号和环境温度。温度每升高10°C,暗电流大约翻一倍。

我个人的经验:在项目中,我遇到过客户抱怨暗部偏色。排查下来,问题出在BLC_offset只用了全局值,没做通道分离。改成四通道独立配置后,问题立刻解决。

3.2 镜头阴影校正(LSC)

镜头阴影,也叫亮度衰减。你想想看,镜头中心到边缘的光路长度不一样,边缘收到的光自然少。再加上镜头本身的cos⁴定律,边缘亮度可能只有中心的60%~70%。

校正方法是用一个增益矩阵去乘每个像素。增益值预先标定好,存在查找表里。

P_out = P_in × gain_table[x][y]

硬件实现时,我推荐用双线性插值。为什么?因为全分辨率增益表太占存储了。比如一个200万像素的传感器,如果每个像素存一个16bit增益值,那就是4MB。这在ASIC里是很大的开销。

实际做法是:

  • 存储一个稀疏的增益网格(比如32×32或64×64)
  • 运行时用双线性插值算出每个像素的增益
  • 乘法器做一次乘法和一次移位

我曾经踩过的坑:增益值不能太大。如果边缘增益超过4倍,噪声会被严重放大。我见过一个方案,为了追求亮度均匀,把边缘增益设到6倍,结果暗部噪声直接炸了。后来我们加了增益上限钳位,并配合去噪模块一起调。

LSC的另一个难点是温度漂移。镜头阴影曲线会随温度变化,尤其是塑料镜头。我建议在标定时至少采集三个温度点(-20°C、25°C、60°C),然后做线性插值。

3.3 坏点校正(DPC)

传感器生产过程中,难免会有几个像素不听话。坏点分两种:

类型 表现 成因
亮点 始终输出高值(接近满阱) 像素短路、漏电
暗点 始终输出低值(接近0) 像素断路、光敏层损坏

校正策略有两种:

  1. 静态校正:出厂时标定坏点坐标,存到OTP里。运行时直接替换。
  2. 动态校正:实时检测坏点,用周围像素插值替换。

我个人的建议是两者结合。静态校正处理已知的固定坏点,动态校正处理使用过程中新出现的坏点。

动态检测的经典算法是:

if |P_center - median(neighbors)| > threshold:
    P_center = median(neighbors)

硬件实现时,需要开一个3×3或5×5的窗口。每个时钟周期读入一行数据,用Line Buffer缓存。我常用的窗口是5×5,因为3×3在斜线边缘容易误判。

注意:阈值不能一刀切。亮区和暗区的噪声水平不同,我习惯用自适应阈值——根据局部均值动态调整threshold。这样在暗部不会误把噪声当坏点,在亮部也不会漏检。

3.4 三模块的硬件融合

这三个模块在硬件上可以串行处理,也可以融合。我画一张图来说明典型的流水线结构:

Bayer域预处理流水线 RAW输入 BLC LSC DPC 校正后RAW 控制/配置寄存器 说明: 1. BLC/LSC/DPC可串行也可并行 2. 控制寄存器通过APB/AXI-Lite配置

这张图展示的是串行流水线。每个模块处理完数据后直接传给下一个。硬件上,每个模块之间只需要一个FIFO做数据缓冲。

我见过有些设计把BLC和LSC合并成一个模块,因为两者都是乘加运算。但我不建议这么做——分开调试更方便,而且BLC的减法器比乘法器省面积,硬凑在一起反而浪费。

3.5 硬件资源估算

以1080p@30fps为例,我列一个典型的资源消耗:

模块 Line Buffer 乘法器 加法器 查找表
BLC 0 0 1 4×16bit
LSC 2行 2 3 32×32×16bit
DPC 4行 0 8 1×16bit(阈值)

注意:Line Buffer是FPGA里最贵的资源。DPC用5×5窗口需要4行缓存,每行1920像素×12bit,合计约92Kbit。如果芯片面积紧张,可以考虑用3×3窗口,但校正效果会差一些。

3.6 调试与验证

这三个模块调试时,我习惯按这个顺序来:

  • 第一步:先关掉LSC和DPC,单独调BLC。拍一张全黑图,看输出是否接近0。
  • 第二步:打开LSC,拍一张均匀光照的灰卡图。看四角亮度是否和中心一致。
  • 第三步:打开DPC,拍一张平场图。看有没有残留的亮点或暗点。

我曾经在一个项目里,LSC和DPC一起开,结果怎么调都调不好。后来发现是LSC的增益放大了坏点的差异,导致DPC误判。解决办法是先做LSC,再做DPC,顺序不能乱。

嗯,Bayer域这三个模块,说起来都不复杂,但细节很多。硬件实现时,面积、功耗、精度三者要权衡。我一般先按最保守的方案做,然后根据时序和面积报告逐步优化。