4、镜头阴影校正(LSC)硬件设计

镜头阴影校正,英文叫 Lens Shading Correction,简称 LSC。说白了,就是解决照片四周发暗、发偏色的问题。你拿手机拍一张纯色的白墙,如果发现中间亮、四周暗,或者中间白、四周偏绿偏红,那就是镜头阴影在作怪。

我刚开始做 ISP 的时候,觉得 LSC 不就是乘个增益嘛,有啥难的?后来真上了硬件,才发现坑不少。增益矩阵怎么存、怎么插值、面积怎么控,每一步都是权衡。今天咱们就把这些掰开揉碎了讲清楚。

4.1 LSC 原理:为什么会有阴影?

镜头阴影分两种:亮度阴影(Luma Shading)和颜色阴影(Color Shading)。

  • 亮度阴影:镜头中心透光多,边缘透光少。光线斜着进入传感器,光路变长,能量衰减。这符合 cos⁴θ 定律,说白了就是边缘收到的光只有中心的 60%~80%。
  • 颜色阴影:不同波长的光在镜头里折射率不同。红光偏折小,蓝光偏折大。结果就是边缘的 RGB 比例失衡,出现偏色。

校正方法很简单:给每个像素乘一个增益系数。中心区域增益接近 1.0,边缘区域增益大于 1.0。但这里有个问题——每个像素的增益都不一样,你不可能给 2000 万像素每个都配一个系数。那怎么办?

核心思路:用网格增益矩阵 + 插值。把图像分成 16x16 或 32x32 的网格,每个网格顶点存一个增益值。中间的像素通过插值算出来。

嗯,这里要注意:增益矩阵的精度直接影响校正效果。我见过有人用 8bit 存增益,结果校正完边缘还有 1% 的残留阴影。后来改成 12bit,问题就解决了。

4.2 增益矩阵存储与插值

增益矩阵怎么存?这是个存储和精度的博弈。

4.2.1 存储格式

我个人习惯用 U12.4 格式,也就是 12bit 整数 + 4bit 小数。为什么?

  • 增益范围通常在 1.0 ~ 4.0 之间。U12.4 能表示 0 ~ 15.9375,够用。
  • 4bit 小数精度是 1/16 = 0.0625,换算成亮度误差约 6%。对于人眼来说,这个精度已经看不出条纹了。
  • 总位宽 16bit,正好对齐 DDR 的 16bit 位宽,存取效率高。

存储时,通常把增益矩阵放在片外 DDR 里。ISP 启动时加载到片内 SRAM。网格大小我建议用 16x16,因为 32x32 的网格虽然更精细,但 SRAM 开销翻 4 倍。对于 1080p 图像,16x16 网格已经能很好地拟合镜头阴影曲线了。

小技巧:增益矩阵可以压缩。用差分编码,只存相邻网格的差值,能省 30%~50% 的存储空间。我在一个 4K 项目里用过,效果不错。

4.2.2 插值算法

插值方法有三种:最近邻、双线性、双三次。硬件里最常用的是 双线性插值

为什么?最近邻太粗糙,校正完能看到网格边界。双三次精度高,但硬件开销大——需要 4x4 个乘法器,面积吃不消。双线性只需要 2x2 个乘法器,精度也够。

双线性插值的公式长这样:

G(x, y) = (1 - dx) * (1 - dy) * G00 
        + dx * (1 - dy) * G10 
        + (1 - dx) * dy * G01 
        + dx * dy * G11

其中 dx, dy 是像素在网格内的归一化坐标,范围 0~1。G00~G11 是四个顶点的增益值。

硬件实现时,我建议用 定点数。dx, dy 用 8bit 小数,乘法结果截断到 16bit。这样只需要 4 个乘法器 + 3 个加法器,流水线深度 3 级,一个时钟周期就能出一个结果。

注意:插值后的增益值不能直接乘到像素上。要先做 归一化。因为增益矩阵的基准点是镜头中心,而图像中心可能不在网格顶点上。我见过有人忘了这步,结果校正完图像整体偏亮。

4.3 硬件实现方案

LSC 硬件模块通常放在 ISP 流水线的 BLC(黑电平校正)之后、AWB(自动白平衡)之前。为什么?因为阴影校正是对传感器原始数据的补偿,必须在颜色处理之前做。

整个模块分三部分:

  1. 地址生成器:根据当前像素坐标,算出它在增益矩阵中的网格索引 (row, col),以及插值系数 dx, dy。
  2. 增益读取器:从 SRAM 中读出四个顶点的增益值。
  3. 插值计算器:做双线性插值,输出最终增益。
  4. 乘法器:把增益乘到像素值上,输出校正后的像素。

下面是我画的一个 LSC 硬件架构图,你看看就明白了:

LSC 硬件加速器架构图 像素坐标 (x,y) 像素值 (Raw) 地址生成器 增益矩阵 SRAM 增益读取器 插值计算器 乘法器 校正后像素值 图例 输入 控制逻辑 存储 运算 输出

这个架构的流水线深度是 5 级:地址生成 -> SRAM 读取 -> 增益读取 -> 插值 -> 乘法。对于 4K@60fps 的实时处理,时钟频率做到 600MHz 就够了。如果频率不够,可以把乘法器拆成两级流水。

4.4 面积与精度权衡

做硬件设计,说白了就是在面积和精度之间找平衡。LSC 模块里,有几个关键参数会影响面积:

参数 面积影响 精度影响 我的建议
增益位宽 每增加 1bit,面积 +5% 8bit 有可见条纹,12bit 无 12bit 整数 + 4bit 小数
网格大小 16x16 比 32x32 面积小 4 倍 16x16 对广角镜头可能不够 根据镜头视场角选
插值方式 双三次面积是双线性的 4 倍 双三次精度高 10%~20% 双线性就够
SRAM 大小 16x16 网格需 512 字节 越大越灵活 片内 SRAM 够用就行

我曾经在一个项目里,为了省面积把增益位宽从 12bit 砍到 8bit。结果校正完的图像在边缘有肉眼可见的条纹,像水波纹一样。后来改回 12bit,问题解决。省那 5% 的面积,不值得。

经验之谈:如果你用的是广角镜头(视场角 > 80°),建议用 32x32 网格。因为广角镜头的阴影曲线更陡,16x16 网格插值出来的增益误差会超过 5%。如果是普通手机镜头,16x16 完全够用。

还有一个容易被忽略的点:增益矩阵的校准精度。校准是在产线上做的,用灰卡拍一张均匀光照的图像,然后反推出每个网格的增益。如果校准时的光照不均匀,或者灰卡有污渍,那增益矩阵本身就是错的。硬件再好也救不回来。

嗯,说到校准,我建议在硬件里加一个 旁路模式。调试的时候可以 bypass LSC,直接看原始数据。这样能快速定位问题是出在校准还是硬件。

最后总结一下:LSC 硬件设计,核心就是 存好增益、算好插值、控好面积。增益矩阵用 16bit 定点数,网格 16x16,插值用双线性,流水线 5 级。这套方案我在三个项目里验证过,面积约 0.05mm²(28nm 工艺),功耗不到 1mW,足够应付 4K@60fps 的实时处理。

下一节咱们聊聊黑电平校正(BLC)的硬件设计。那个模块虽然简单,但坑也不少。


专注资料整理