4. 镜头阴影校正查找表实现
任务目标
实现基于查找表(LUT)的镜头阴影校正(Lens Shading Correction, LSC),支持双线性插值。输入图像每个像素的R/G/B通道乘以对应位置的校正增益,增益值存储于低分辨率查找表中,通过双线性插值获得逐像素增益。
实现方案
查找表尺寸为 LUT_H × LUT_W(远小于图像尺寸),每个表项存储R/G/B三通道增益。对图像坐标 (x, y),映射到LUT坐标空间后,取周围4个表项做双线性插值,得到该像素的增益向量并逐通道相乘。边界像素采用钳位处理。
关键完整代码
import numpy as np
from typing import Tuple
def generate_lut(lut_h: int, lut_w: int, vignetting_func) -> np.ndarray:
"""
生成LUT表,形状 (lut_h, lut_w, 3),值为各通道增益。
vignetting_func(r_norm) 输入归一化半径(0~1),返回增益标量。
"""
lut = np.zeros((lut_h, lut_w, 3), dtype=np.float32)
cy, cx = (lut_h - 1) / 2.0, (lut_w - 1) / 2.0
max_r = np.sqrt(cy**2 + cx**2)
for j in range(lut_h):
for i in range(lut_w):
r = np.sqrt((j - cy)**2 + (i - cx)**2) / max_r
gain = vignetting_func(r)
lut[j, i, :] = gain # 三通道使用相同增益
return lut
def apply_lsc_bilinear(img: np.ndarray, lut: np.ndarray) -> np.ndarray:
"""
对图像应用LUT双线性插值校正。
img: (H, W, 3) uint8,lut: (lut_h, lut_w, 3) float32
返回校正后 uint8 图像。
"""
H, W = img.shape[:2]
lut_h, lut_w = lut.shape[:2]
out = np.zeros_like(img, dtype=np.float32)
# 坐标映射:图像(0,0) → LUT(0,0),图像(H-1,W-1) → LUT(lut_h-1,lut_w-1)
scale_y = (lut_h - 1) / (H - 1)
scale_x = (lut_w - 1) / (W - 1)
for y in range(H):
fy = y * scale_y
y0 = int(np.floor(fy))
y1 = min(y0 + 1, lut_h - 1)
wy = fy - y0
for x in range(W):
fx = x * scale_x
x0 = int(np.floor(fx))
x1 = min(x0 + 1, lut_w - 1)
wx = fx - x0
# 双线性插值增益
g00 = lut[y0, x0]
g10 = lut[y0, x1]
g01 = lut[y1, x0]
g11 = lut[y1, x1]
gain = (g00 * (1 - wx) + g10 * wx) * (1 - wy) + \
(g01 * (1 - wx) + g11 * wx) * wy
out[y, x] = img[y, x] * gain
return np.clip(out, 0, 255).astype(np.uint8)
# ---- 最小验证示例 ----
if __name__ == "__main__":
# 模拟径向渐晕:中心增益1.0,边缘增益1.8
vign = lambda r: 1.0 + 0.8 * r**2
# 生成小尺寸LUT和测试图
lut = generate_lut(8, 8, vign)
test_img = np.full((32, 32, 3), 128, dtype=np.uint8) # 均匀灰色图
corrected = apply_lsc_bilinear(test_img, lut)
# 验证:中心像素增益≈1.0,角落像素增益≈1.8
center_val = corrected[16, 16, 0]
corner_val = corrected[0, 0, 0]
print(f"中心像素值: {center_val} (预期≈128)")
print(f"角落像素值: {corner_val} (预期≈230, 即128*1.8)")
assert abs(center_val - 128) < 2, "中心校正失败"
assert abs(corner_val - 230) < 5, "角落校正失败"
print("验证通过")
运行与验证
依赖:仅需 numpy。
运行方式:
python lsc_lut.py
验证逻辑:代码内置自检——对均匀灰度图施加中心增益1.0、边缘增益1.8的渐晕模型,校正后中心像素应保持约128,角落像素应升至约230(128×1.8)。若偏差在容差范围内,打印“验证通过”。
判定标准:程序输出包含“验证通过”且无异常退出,即证明LUT生成与双线性插值逻辑正确。
关键说明
- LUT坐标与图像坐标采用线性映射,边界像素通过
min()钳位到LUT边缘,避免越界。 - 实际ISP中LUT通常由标定设备测量获得,本案例用解析函数模拟生成,便于验证算法正确性。
- 三通道共用同一增益表;若需独立校正,将LUT第三维分别赋值即可,代码结构无需改动。