4. 镜头阴影校正(LSC):从渐晕到FPGA加速

镜头阴影校正,英文叫Lens Shading Correction,简称LSC。说白了,就是解决照片四周发暗、中心亮的问题。你拿手机拍一张纯白墙壁的照片,放大看四角,十有八九是偏暗的。这就是渐晕现象。

我在项目中遇到过好几次,客户说“你们的sensor是不是坏了,怎么四周颜色不对?”其实不是sensor的问题,是镜头的光学特性决定的。今天我们就把它讲透。

4.1 渐晕现象与LSC原理

渐晕(Vignetting)产生的原因主要有三个:

  • 光学渐晕:镜头边缘的光线被镜筒遮挡,入射角度越大,衰减越严重
  • 像素角度响应:边缘像素对斜入射光的灵敏度下降
  • 镜头cos⁴定律:理想情况下,照度与cos⁴θ成正比,θ是入射角

你想想看,一个广角镜头,边缘入射角可能到30°以上,cos⁴(30°) ≈ 0.56,也就是说边缘只有中心一半的亮度。这可不是小问题。

核心思路:LSC就是给每个像素乘一个增益系数,边缘乘大一点,中心乘小一点,让整幅画面亮度均匀。

增益矩阵怎么来?一般有两种方式:

  1. 标定法:拍一张均匀光源下的灰卡,计算每个像素需要的补偿增益
  2. 模型拟合法:用多项式或网格模型拟合渐晕曲线,生成增益矩阵

我个人习惯用标定法,简单直接。但要注意,标定时的光源均匀性必须保证,否则你校正的是光源不均匀,不是镜头阴影。

4.2 增益矩阵生成方法

增益矩阵的生成,我把它分成三步:

步骤 操作 注意事项
1. 采集raw图 均匀光源下拍灰卡,取raw数据 光源色温要匹配实际场景
2. 计算增益 中心亮度 / 各像素亮度 避免除以0,加小epsilon
3. 平滑滤波 用低通滤波器去除噪声 保留低频趋势,滤除高频噪点

增益矩阵的尺寸通常比图像小很多,比如16×16或者32×32。为什么?因为渐晕是低频变化,不需要每个像素都存一个增益值,那样太浪费存储了。

经验之谈:我曾经用64×64的增益矩阵,结果FPGA的BRAM不够用了。后来改成32×32,配合双线性插值,效果几乎一样,资源省了一半。

4.3 FPGA双线性插值实现

增益矩阵是稀疏的,我们需要对每个像素插值得到精确的增益值。双线性插值是性价比最高的选择。

先看插值公式:

// 已知四个网格点的增益 G00, G10, G01, G11
// 像素在网格内的归一化坐标 (u, v),范围 [0, 1)

G_top    = G00 * (1 - u) + G10 * u
G_bottom = G01 * (1 - u) + G11 * u
G_pixel  = G_top * (1 - v) + G_bottom * v

FPGA实现时,我建议用流水线结构:

  1. 坐标计算模块:根据像素坐标算出网格索引和归一化坐标
  2. BRAM读取模块:同时读出四个相邻网格点的增益值
  3. 插值运算模块:用乘法器和加法器完成三次线性插值
  4. 乘法校正模块:将插值得到的增益与像素值相乘

注意:增益值通常是定点小数,比如Q1.8格式。乘法后要小心溢出,建议输出位宽比输入大2-3bit。

下面是我画的一个FPGA LSC流水线结构图:

FPGA LSC双线性插值流水线 FPGA LSC 双线性插值流水线 坐标计算 像素→网格索引+uv BRAM读取 读出G00~G11 插值运算 三次线性插值 乘法校正 增益 × 像素值 像素坐标 校正后像素 说明: 流水线每拍处理一个像素,坐标计算→BRAM读取→插值→乘法,四级流水。 BRAM使用双端口,同时读出四个增益值,不增加额外延迟。

嗯,这里要注意BRAM的读取时序。增益矩阵存在BRAM里,每个时钟周期要读出四个值。我一般用双端口BRAM,一个端口读偶数行,一个端口读奇数行,这样一拍就能拿到全部四个值。

4.4 调试技巧与避坑指南

调试LSC,我踩过的坑不少,分享几个实用的:

我曾经犯过的错:增益矩阵的边界处理没做好,图像边缘出现奇怪的条纹。原因是网格坐标计算时,边界像素的索引越界了。解决方案是在坐标计算模块加饱和截断,把索引限制在[0, grid_width-2]范围内。

调试步骤我建议这样走:

  • 第一步:用MATLAB/Python离线验证增益矩阵和插值算法,确保数学正确
  • 第二步:FPGA仿真,注入测试向量,比对RTL输出和软件参考结果
  • 第三步:上板实测,拍均匀灰卡,检查校正后的均匀性
  • 第四步:拍真实场景,检查是否有artifact

小技巧:调试时可以把增益矩阵的值直接映射成灰度图显示出来。如果增益图是平滑的渐变,说明标定没问题;如果出现跳变或噪点,那就是标定或滤波出了问题。

还有一个容易被忽略的点:增益矩阵的精度。我建议用Q1.8格式,也就是1bit整数位+8bit小数位,增益范围0~1.996。如果增益超过2.0,说明镜头阴影太严重,可能需要考虑光学方案改进,而不是一味靠算法补偿。

最后说一句,LSC的效果评估,除了看均匀性,还要看信噪比。增益放大的同时,噪声也被放大了。边缘区域的SNR会下降,这是物理限制,算法只能缓解,不能消除。