3、镜头阴影校正:从物理成因到工程实现
镜头阴影校正,圈里人常叫 LSC(Lens Shading Correction)。
说白了,就是解决照片四周变暗、偏色的问题。你拿手机对着白墙拍一张,如果中间亮、四角暗,那就是镜头阴影。我刚开始做 ISP 时,觉得这问题不大,直到有一次调试一款超大光圈镜头,四周亮度掉了将近 40%,客户直接拒收……嗯,从那以后我再也不敢小看 LSC 了。
3.1 镜头阴影的物理成因
为什么会暗角?主要有两个物理原因。
| 成因类型 | 英文术语 | 物理机制 | 典型表现 |
|---|---|---|---|
| 亮度衰减 | Luma Shading | cos⁴θ 定律:边缘光线入射角大,到达 sensor 的光强按 cos⁴θ 衰减 | 四角亮度明显低于中心,灰度均匀 |
| 颜色偏移 | Color Shading | 不同波长光线通过镜头折射率不同,microlens 对边缘光线收集效率不一致 | 四角偏绿或偏紫,R/G/B 通道衰减比例不同 |
我个人习惯把这两个问题分开处理。亮度衰减是几何光学问题,颜色偏移是材料与工艺问题。你想想看,如果混在一起调,参数会变得非常难收敛。
关键认知: Luma Shading 是各通道同比例衰减,Color Shading 是各通道衰减比例不同。校正策略必须区分对待。
3.2 增益补偿原理
校正思路很简单——哪里暗,就补哪里。
对每个像素位置 (x, y),乘上一个增益系数 G(x, y)。
I_corrected(x, y) = I_raw(x, y) × G(x, y)
增益系数怎么定?以中心亮度为基准。
G(x, y) = L_center / L(x, y)
其中 L_center 是图像中心区域的亮度值,L(x, y) 是当前位置的亮度值。
这里有个坑——我曾经遇到过增益系数过大导致噪声被放大的情况。四角增益可能达到 1.5x 甚至 2.0x,暗部噪声也跟着翻倍。所以实际工程中,我会设置一个最大增益上限,比如 2.5x,超过的部分直接裁掉,宁可留一点暗角,也不要满屏噪点。
注意: 增益补偿会放大噪声。建议在 LSC 之后紧接着做降噪处理,或者采用分通道增益限制策略——R/G/B 通道分别设上限,避免某个通道噪声异常突出。
3.3 网格校正方法
实际镜头阴影不是完美的 cos⁴θ 曲线,受镜头组装公差、microlens 偏移等因素影响,阴影分布往往是不规则的。用解析公式硬拟合,效果很差。
工业界主流做法是——网格校正。
3.3.1 网格划分
把整幅图像划分成 M×N 个网格。每个网格顶点存储一个增益值。
| 参数 | 典型值 | 说明 |
|---|---|---|
| 水平网格数 | 9 ~ 17 | 奇数,保证中心点在网格上 |
| 垂直网格数 | 7 ~ 13 | 奇数,保证中心点在网格上 |
| 增益精度 | Q8.8 定点数 | 16bit,8bit 整数 + 8bit 小数 |
3.3.2 双线性插值
对于不在网格顶点上的像素,用周围四个顶点的增益做双线性插值。
G(x, y) = (1 - dx)(1 - dy) × G00 + dx(1 - dy) × G10 + (1 - dx)dy × G01 + dx × dy × G11
其中 dx, dy 是像素到左上角顶点的归一化距离。
我个人习惯用 11×9 的网格。网格太密,标定数据量大,存储开销也大;网格太疏,插值误差大,校正后会有明显的块状痕迹。11×9 是个不错的平衡点。
工程技巧: 网格增益值建议用定点数存储,不要用浮点。浮点运算在硬件 pipeline 里太贵,定点数用移位加乘法就能搞定,延迟低很多。
3.4 标定流程
标定是 LSC 最关键的环节。标定做不好,后面所有校正都是白搭。
3.4.1 标定环境
- 光源: 均匀面光源(积分球或背光板),亮度均匀性 < 1%
- 色温: 至少覆盖 D65(6500K)、D50(5000K)、A 光源(2856K)
- 镜头: 对焦到无穷远,光圈开到最大(阴影最明显)
- sensor: 曝光时间调整到中心区域不饱和,通常目标 DN 值在 maxDN 的 80% 左右
3.4.2 标定步骤
- 采集 raw 图: 每个色温下拍 5~10 帧,做帧平均降噪
- 提取通道数据: 分离 R/Gr/Gb/B 四个通道(Bayer 格式)
- 计算增益矩阵: 对每个通道,以中心区域平均值为基准,逐像素计算增益
- 网格化: 将增益矩阵下采样到 M×N 网格,每个网格顶点取邻域均值
- 平滑处理: 对网格增益做 3×3 中值滤波,去除异常点
- 存储参数: 写入 sensor 模组的 OTP 或 flash
避坑指南: 我曾经遇到过标定完效果很好,但换了一颗镜头就完全失效的情况。原因是镜头组装公差导致阴影分布偏移。后来我要求产线每颗模组单独标定,把 LSC 参数写入 OTP,问题才解决。量产时千万别用「一颗标定,全体通用」的方案。
3.5 知识结构总览
下面这张图把 LSC 的核心逻辑串起来了,你可以对照着理解。
3.6 工程经验总结
- 网格增益要平滑: 相邻网格增益差不要超过 0.05,否则校正后会出现肉眼可见的条纹
- 多色温标定: 至少标 D65 和 A 光两个色温,Color Shading 在不同色温下差异很大
- 产线标定效率: 每颗模组标定时间控制在 3 秒以内,否则产线节拍跟不上
- 温度补偿: 高温下 sensor 暗电流变化会影响阴影分布,高端方案会做温度查表补偿
个人建议: 如果你刚开始调 LSC,先只调 Luma Shading,把亮度均匀性做到 95% 以上,再调 Color Shading。两个问题混在一起调,你很难判断是亮度问题还是颜色问题。分步调试,效率高得多。