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(自动白平衡)之前。为什么?因为阴影校正是对传感器原始数据的补偿,必须在颜色处理之前做。
整个模块分三部分:
- 地址生成器:根据当前像素坐标,算出它在增益矩阵中的网格索引 (row, col),以及插值系数 dx, dy。
- 增益读取器:从 SRAM 中读出四个顶点的增益值。
- 插值计算器:做双线性插值,输出最终增益。
- 乘法器:把增益乘到像素值上,输出校正后的像素。
下面是我画的一个 LSC 硬件架构图,你看看就明白了:
这个架构的流水线深度是 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)的硬件设计。那个模块虽然简单,但坑也不少。