Python编程:ISP中LSC校正

镜头阴影校正(Lens Shading Correction, LSC)

镜头阴影校正是相机ISP(图像信号处理器)中的一个关键步骤,用于补偿由于镜头光学特性导致的图像亮度不均匀现象(即暗角渐晕)。该现象表现为图像中心较亮、四角较暗,并可能伴随颜色偏移。


镜头阴影的产生原因

  1. 光学渐晕(Vignetting)

    • 光线进入镜头时,边缘的光线路径比中心更长,导致边缘亮度衰减。

    • 与镜头设计(如光圈大小、镜片数量)密切相关。

  2. 机械渐晕

    • 镜头筒或滤镜遮挡边缘光线(常见于广角镜头)。

  3. 像素响应差异

    • 传感器边缘像素的感光效率可能低于中心像素。

镜头阴影的影响

  • 亮度不均匀:图像四角比中心暗(如典型的“暗角”效果)。

  • 颜色偏移:不同颜色通道(R/G/B)的衰减程度不同,导致边缘色偏。

  • 动态范围损失:阴影区域的信号强度降低,影响后期处理(如HDR合成)。

镜头阴影校正方法

(1) 基于增益图的校正(Gain Map)

  • 原理

    • 预先标定镜头的亮度衰减分布,生成增益图(Gain Map)

    • 对图像每个像素乘以对应的增益值,补偿亮度衰减。

  • 增益图生成

    • 拍摄均匀光照下的标定板(如灰卡)。

    • 计算每个位置的增益系数:

  • 校正公式

Python实现(单通道增益校正)

import numpy as np
import cv2

def lens_shading_correction(image, gain_map):
    """
    使用增益图校正镜头阴影
    :param image: 输入图像(单通道或Bayer RAW)
    :param gain_map: 增益图(与image同尺寸)
    :return: 校正后的图像
    """
    corrected = image.astype(np.float32) * gain_map
    return np.clip(corrected, 0, 255).astype(np.uint8)

# 示例:生成模拟增益图(中心亮、边缘暗)
h, w = 512, 512
x = np.linspace(-1, 1, w)
y = np.linspace(-1, 1, h)
xx, yy = np.meshgrid(x, y)
gain_map = 1.0 - 0.5 * (xx**2 + yy**2)  # 二次衰减模型
gain_map = np.clip(gain_map, 0.5, 1.0)   # 限制增益范围

# 应用校正
raw_image = np.random.randint(0, 255, (h, w), dtype=np.uint8)
corrected_image = lens_shading_correction(raw_image, gain_map)

(2) 分通道校正(Bayer RAW)

  • 问题

    • Bayer格式的R/G/B通道可能有不同的衰减特性。

  • 方法

    • 为每个颜色通道(R/Gr/Gb/B)单独计算增益图。

    • 校正时根据Bayer模式选择对应的增益图。

Python实现(Bayer RGGB分通道校正)

def bayer_lens_shading_correction(bayer_raw, gain_maps):
    """
    Bayer RAW图像的分通道镜头阴影校正
    :param bayer_raw: 输入Bayer图像(RGGB格式)
    :param gain_maps: 四通道增益图 [R, Gr, Gb, B]
    :return: 校正后的Bayer图像
    """
    corrected = bayer_raw.copy()
    h, w = bayer_raw.shape
    
    # RGGB模式:偶数行 R G, 奇数行 G B
    corrected[0::2, 0::2] *= gain_maps[0]  # R
    corrected[0::2, 1::2] *= gain_maps[1]  # Gr
    corrected[1::2, 0::2] *= gain_maps[2]  # Gb
    corrected[1::2, 1::2] *= gain_maps[3]  # B
    
    return np.clip(corrected, 0, 65535).astype(bayer_raw.dtype)

(3) 多项式拟合校正

  • 原理

    • 用多项式函数(如二次或四次)拟合亮度衰减分布。

    • 适用于无标定数据的场景(如手机摄像头实时处理)。

  • 公式

Python实现(多项式拟合校正)

def polynomial_shading_correction(image, k1=0.2, k2=0.1):
    """
    多项式拟合镜头阴影校正
    :param image: 输入图像
    :param k1, k2: 多项式系数
    :return: 校正后的图像
    """
    h, w = image.shape
    xc, yc = w // 2, h // 2
    xx, yy = np.meshgrid(np.arange(w), np.arange(h))
    r = np.sqrt((xx - xc)**2 + (yy - yc)**2) / max(xc, yc)
    
    gain_map = 1.0 + k1 * r**2 + k2 * r**4
    return np.clip(image.astype(np.float32) * gain_map, 0, 255).astype(np.uint8)

完整示例

# =============================================================
# class: lens_shading_correction
#   Correct the lens shading / vignetting
# =============================================================
class lens_shading_correction:
    def __init__(self, data, name="lens_shading_correction"):
        # convert to float32 in case it was not
        self.data = np.float32(data)
        self.name = name

    def flat_field_compensation(self, dark_current_image, flat_field_image):
        # dark_current_image:
        #       is captured from the camera with cap on
        #       and fully dark condition, several images captured and
        #       temporally averaged
        # flat_field_image:
        #       is found by capturing an image of a flat field test chart
        #       with certain lighting condition
        # Note: flat_field_compensation is memory intensive procedure because
        #       both the dark_current_image and flat_field_image need to be
        #       saved in memory beforehand
        print("----------------------------------------------------")
        print("Running lens shading correction with flat field compensation...")

        # convert to float32 in case it was not
        dark_current_image = np.float32(dark_current_image)
        flat_field_image = np.float32(flat_field_image)
        temp = flat_field_image - dark_current_image
        return np.average(temp) * np.divide((self.data - dark_current_image), temp)

    def approximate_mathematical_compensation(self, params, clip_min=0, clip_max=65535):
        # parms:
        #       parameters of a parabolic model y = a*(x-b)^2 + c
        #       For example, params = [0.01759, -28.37, -13.36]
        # Note: approximate_mathematical_compensation require less memory
        print("----------------------------------------------------")
        print("Running lens shading correction with approximate mathematical compensation...")
        width, height = utility.helpers(self.data).get_width_height()

        center_pixel_pos = [height/2, width/2]
        max_distance = utility.distance_euclid(center_pixel_pos, [height, width])

        # allocate memory for output
        temp = np.empty((height, width), dtype=np.float32)

        for i in range(0, height):
            for j in range(0, width):
                distance = utility.distance_euclid(center_pixel_pos, [i, j]) / max_distance
                # parabolic model
                gain = params[0] * (distance - params[1])**2 + params[2]
                temp[i, j] = self.data[i, j] * gain

        temp = np.clip(temp, clip_min, clip_max)
        return temp

    def __str__(self):
        return "lens shading correction. There are two methods: " + \
                "\n (1) flat_field_compensation: requires dark_current_image and flat_field_image" + \
                "\n (2) approximate_mathematical_compensation:"

代码实现了两种镜头阴影校正方法,用于补偿相机镜头产生的渐晕效应(vignetting)

  1. 平场补偿法(flat_field_compensation) - 基于实际测量的暗场和平场图像

  2. 近似数学模型法(approximate_mathematical_compensation) - 基于抛物线模型

方法一:平场补偿法

核心算法
temp = flat_field_image - dark_current_image
return np.average(temp) * np.divide((self.data - dark_current_image), temp)
工作原理
  1. 计算有效平场图像:flat_field_image - dark_current_image

  2. 计算输入图像的暗场补偿:self.data - dark_current_image

  3. 通过比值计算校正系数,并用平场平均值进行归一化

参数说明
  • dark_current_image:暗场图像(镜头盖关闭时拍摄的多帧平均)

  • flat_field_image:平场图像(均匀光照条件下拍摄的测试图)

特点
  • 高精度,基于实际测量数据

  • 内存消耗大(需要存储两幅参考图像)

  • 需要提前采集暗场和平场数据

方法二:近似数学模型法

核心算法
distance = utility.distance_euclid(center_pixel_pos, [i, j]) / max_distance
gain = params[0] * (distance - params[1])**2 + params[2]
temp[i, j] = self.data[i, j] * gain
工作原理
  1. 计算每个像素到图像中心的归一化距离

  2. 使用抛物线模型计算增益系数:y = a*(x-b)^2 + c

  3. 应用增益校正像素值

参数说明
  • params:抛物线模型参数 [a, b, c]

  • clip_min, clip_max:输出值的裁剪范围

特点
  • 内存效率高

  • 不需要参考图像

  • 精度依赖于模型参数的准确性

  • 计算量较大(逐像素处理)

 

应用场景对比

特性平场补偿法数学模型法
精度中等
内存需求
计算复杂度中等
前期准备需要暗场和平场图像需要模型参数
适用场景实验室环境实时处理

数学基础

平场补偿模型
校正后图像 = (原始图像 - 暗场) × (平场平均值 / (平场 - 暗场))
抛物线模型
增益系数 = a × (归一化距离 - b)² + c
校正后像素值 = 原始像素值 × 增益系数

镜头阴影校正的实际应用

(1) 在ISP流程中的位置

RAW数据 → 黑电平校正 → 坏点修复 → 镜头阴影校正 → 去马赛克 → ...
  • 必须在去马赛克之前完成,否则颜色插值会放大亮度不均。

(2) 手机摄像头的优化

  • 动态增益调整

    • 根据环境光强度(如低光场景)调整增益图的强度。

  • 温度补偿

    • 镜头阴影可能随温度变化,需动态更新增益图。

(3) 工业相机的标定

  • 使用高精度标定板(如24色卡)生成增益图。

  • 存储增益表供后续图像处理调用。

效果对比

校正前校正后
图像四角明显变暗亮度均匀,暗角消失
边缘颜色偏冷/暖颜色一致性提升

挑战与注意事项

  1. 过校正问题

    • 过度提升边缘增益会放大噪声(需平衡亮度和信噪比)。

  2. 实时性要求

    • 手机ISP需在毫秒级完成校正(通常硬件加速)。

  3. Bayer RAW处理

    • 需避免不同颜色通道的增益交叉污染。


总结

镜头阴影校正的核心是通过增益图或多项式模型补偿亮度衰减,关键点包括:

  1. 增益图标定(实验室或均匀光照下)。

  2. 分通道处理(针对Bayer RAW数据)。

  3. 动态调整(适应不同光照和温度条件)。

实际实现中需结合传感器数据和光学特性优化参数,以获得自然的校正效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值