4. 镜头阴影校正(LSC):成因分析、网格校准与实时补偿算法

镜头阴影校正,英文叫 Lens Shading Correction,简称 LSC。这玩意儿在安防领域有多重要?我打个比方——你花大价钱买了个 4K 镜头,结果画面四角发暗、中间亮得像探照灯,那这 4K 还有什么意义?说白了,LSC 就是解决这个「中心亮、四周暗」的物理缺陷。

我刚开始调 ISP 那会儿,总觉得 LSC 是小事。直到有一次项目验收,客户拿着测光表对着画面四个角测亮度,然后问我:「为什么左上角比中心暗了 15%?」嗯,从那以后,我再也不敢小看阴影校正了。

4.1 阴影成因:物理根源与数学模型

镜头阴影主要分两类:亮度阴影和色彩阴影。咱们一个一个说。

4.1.1 亮度阴影(Luminance Shading)

也叫「余弦四次方定律」。你想想看,镜头是个圆形的光学系统,光线从中心通过时路径最短,从边缘通过时路径更长,而且入射角度也变了。数学上,边缘照度 E(θ) 和中心照度 E₀ 的关系是:

E(θ) = E₀ · cos⁴(θ)

其中 θ 是光线入射角。θ 越大,cos⁴(θ) 衰减得越厉害。举个例子,当 θ = 30° 时,cos⁴(30°) ≈ 0.56,也就是说边缘亮度只剩中心的一半多一点。

关键指标: 安防监控常用的 1/2.8" 传感器搭配 2.8mm 镜头,边缘亮度通常比中心低 30%~50%。如果镜头质量差,这个差距能到 60% 以上。

4.1.2 色彩阴影(Color Shading)

这个更隐蔽。不同波长的光通过镜头时折射率不同——红光折射小,蓝光折射大。结果就是:边缘区域的 R、G、B 通道衰减比例不一样,导致画面偏色。

我在项目中遇到过一台 IPC,画面中心颜色正常,但四角明显偏绿。一开始以为是白平衡没调好,折腾了半天才发现是色彩阴影在作怪。后来换了镜头,问题才解决。

阴影类型 物理成因 表现特征 校正难度
亮度阴影 cos⁴(θ) 衰减 四角变暗,中心正常 中等
色彩阴影 色散/IR-cut 偏移 边缘偏色(常见偏绿/偏紫) 较高
微透镜阴影 传感器微透镜阵列角度 边缘像素灵敏度下降 较低

4.2 网格校准:如何获取校正系数

校正阴影,首先得知道「暗了多少」。这就需要校准。我个人习惯用「灰卡法」——拍一张均匀照明的灰卡图像,然后分析每个像素位置的亮度衰减。

4.2.1 校准流程

  1. 搭建均匀光源: 用积分球或均匀面光源,保证整个画面照度一致。我见过有人用白墙加柔光灯,结果校准出来全是错的——因为白墙本身就不均匀。
  2. 拍摄原始 RAW 图: 关闭所有 ISP 处理(包括 LSC 本身),拍一张 RAW 格式的灰卡图像。
  3. 计算增益矩阵: 对每个像素位置 (x, y),计算增益 G(x, y) = Target / I(x, y)。其中 Target 是目标亮度(通常取中心区域平均值),I(x, y) 是实际亮度。
  4. 网格化存储: 全分辨率增益矩阵太大(比如 1920×1080),存不下。所以只存一个稀疏网格,比如 16×16 或 32×32 的网格点。
经验值: 安防监控常用的网格大小是 17×17(即水平和垂直各 17 个网格点)。网格太密浪费存储,太疏则校正精度不够。我一般建议:1080p 用 17×17,4K 用 33×33。

4.2.2 网格插值

校准得到的是稀疏网格,但实际校正时每个像素都需要增益值。所以要用插值算法把网格点「填满」。常用的插值方法有:

  • 双线性插值: 计算量小,效果够用。适合大多数安防场景。
  • 双三次插值: 平滑度更好,但计算量大。适合高端球机或枪机。
  • 最近邻插值: 速度快但会有块状效应。我基本不用,除非是低端芯片。

4.3 实时补偿算法:硬件实现与优化

校准完了,接下来就是实时补偿。这部分是 ISP pipeline 里的固定环节,通常在 demosaic 之前、黑电平校正之后执行。

4.3.1 算法流程

实时补偿的核心公式很简单:

I_out(x, y) = I_in(x, y) × G(x, y)

其中 G(x, y) 是从网格插值得到的增益值。但实际实现时,有几个坑要注意。

注意: 增益值不能无限大。如果某个像素的增益超过 4.0(即 12dB),说明镜头阴影太严重了。这时候应该先换镜头,而不是靠 ISP 硬拉。我曾经见过一个项目,增益拉到 8.0,结果暗部噪声全被放大了,画面惨不忍睹。

4.3.2 硬件加速技巧

在 FPGA 或 ASIC 上实现 LSC,通常用「查表 + 插值」的方式:

  • 行缓存: 因为插值需要相邻网格点的数据,所以硬件里要缓存几行像素。
  • 定点化: 增益值用 Q8.8 或 Q12.4 定点数表示,避免浮点运算。
  • 分通道处理: R、G、B 三个通道分别用不同的增益网格,因为色彩阴影的衰减曲线不一样。

4.3.3 代码示例(C 语言伪代码)

// 双线性插值计算增益
float interpolate_gain(float x, float y, float grid[GRID_H][GRID_W]) {
    int xi = (int)x;
    int yi = (int)y;
    float dx = x - xi;
    float dy = y - yi;
    
    float g00 = grid[yi][xi];
    float g10 = grid[yi][xi+1];
    float g01 = grid[yi+1][xi];
    float g11 = grid[yi+1][xi+1];
    
    // 双线性插值
    float g0 = g00 * (1-dx) + g10 * dx;
    float g1 = g01 * (1-dx) + g11 * dx;
    return g0 * (1-dy) + g1 * dy;
}

// 逐像素校正
void lsc_process(uint16_t *raw, int width, int height, float grid[GRID_H][GRID_W]) {
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            float gain = interpolate_gain(
                (float)x / width * (GRID_W-1),
                (float)y / height * (GRID_H-1),
                grid
            );
            raw[y*width + x] = clamp(raw[y*width + x] * gain, 0, 1023);
        }
    }
}

4.4 知识体系总览

下面这张图把 LSC 的完整链路画出来了。从物理成因到校准,再到实时补偿,每一步都有对应的工程要点。

镜头阴影校正(LSC) 知识体系 物理成因 网格校准 实时补偿 亮度阴影 cos⁴(θ) 衰减 边缘亮度下降 30%~50% 色彩阴影 色散 / IR-cut 偏移 边缘偏绿 / 偏紫 微透镜阴影 传感器微透镜角度 像素灵敏度下降 灰卡校准法 均匀光源 + RAW 图 计算增益矩阵 网格化存储 17×17 / 33×33 网格 稀疏存储节省空间 网格插值 双线性 / 双三次 逐像素计算增益 实时补偿:I_out = I_in × G(x,y) | 分通道处理 | 定点化加速 | 增益上限 4.0 核心原则:先换好镜头,再谈 ISP 校正。增益超过 4.0 时,噪声放大不可接受。

4.5 避坑指南与工程经验

做了这么多年 ISP 调优,我总结了几条 LSC 相关的血泪教训:

  • 校准环境必须均匀: 我曾经用了一个不均匀的面光源做校准,结果校正后的画面反而出现了「伪阴影」——因为校准数据本身就有问题。后来改用积分球,一次搞定。
  • 注意温度漂移: 镜头和传感器的光学特性会随温度变化。我建议在常温(25°C)和高温(60°C)下各做一次校准,然后取平均或做温度补偿。
  • IR-Cut 切换的影响: 安防摄像机白天用 IR-Cut,晚上切掉。IR-Cut 的玻璃厚度会影响光路,导致白天和晚上的阴影曲线不一样。我一般会做两套 LSC 参数,分别对应白天和夜晚模式。
  • 不要过度校正: 有些工程师为了追求「完美均匀」,把增益拉到很高。结果边缘噪声被放大,画面反而更差。我的经验是:边缘亮度不低于中心的 70% 就够用了。
调试小技巧: 在 ISP 调试工具里,把 LSC 增益值以伪彩色图显示出来。红色区域表示增益高(阴影严重),蓝色区域表示增益低(亮度正常)。一眼就能看出镜头质量好不好。