Python编程:ISP中的白平衡(White Balance)

白平衡(White Balance)是图像信号处理(ISP)中的关键步骤,用于消除光源色温图像颜色的影响,使白色物体不同光照条件下都能呈现真实的白色

白平衡的基本原理

白平衡通过调整图像中R、G、B三个通道的增益,使得在特定光源下白色物体能够呈现中性色(R=G=B)。

主要概念

  • 色温:表示光源颜色的物理量,单位是开尔文(K)

  • 灰色世界假设:认为自然场景的平均反射率是中性灰色

  • 完美反射体假设:认为图像中最亮的点是白色

白平衡产生的原因

1. 光源色温差异(根本原因)

  • 不同光源的色温特性

    光源类型 色温范围(K) 颜色表现
    烛光/白炽灯 1500-2500 偏橙红色
    日出/日落 2500-3500 偏黄色
    日光 5000-6500 接近白色
    阴天 6500-8000 偏蓝色
    阴影区域 8000-10000 明显偏蓝
  • 人眼适应性:人脑会自动校正颜色感知(称为"色恒常性"),但相机传感器不具备这种能力

2. 传感器特性

  • 拜耳滤镜响应:RGB滤光片对不同波长光的透过率不同

  • 光电转换非线性:传感器对光谱的响应与人眼视觉系统不完全匹配

3. 图像处理流水线需求

  • 后续处理的基准:准确的色彩再现是gamma校正、色彩增强等处理的前提

  • 跨设备一致性:使不同设备拍摄的同一场景色彩表现一致

白平衡不准确的影响

1. 对图像质量的直接影响

  • 色彩失真

    • 偏蓝(高色温未校正)

    • 偏黄(低色温未校正)

    • 示例:白纸在不同光源下表现

      # 未校正白平衡的白色RGB值示例
      白炽灯下: (220, 180, 150)  # 偏黄
      日光下:   (210, 210, 210)  # 接近理想白
      阴影下:   (180, 190, 220)  # 偏蓝
  • 细节损失

    • 过度的白平衡校正可能导致:

      • 高光溢出(clipping)

      • 阴影细节丢失

2. 对后续处理的影响

  • 色彩相关处理失效

    # 肤色检测在不同白平衡下的差异
    正确白平衡: HSV范围[0-30, 30-150, 80-255]
    偏黄白平衡: 需要调整为[15-45, ...]
    偏蓝白平衡: 需要调整为[-15-15, ...]
  • 计算机视觉算法性能下降

    • 目标检测mAP降低5-15%

    • 图像分类准确率下降10-20%

解决方案演进

1. 传统方法局限性

  • 灰度世界假设在单色场景失效

  • 完美反射体在无白色区域失效

2. 现代解决方案

  • 基于学习的方法

    # 深度学习白平衡网络示例
    class WBNet(nn.Module):
        def __init__(self):
            super().__init__()
            self.encoder = ResNet50()
            self.regressor = nn.Sequential(
                nn.Linear(2048, 512),
                nn.ReLU(),
                nn.Linear(512, 3))  # 输出RGB增益
      
        def forward(self, x):
            features = self.encoder(x)
            gains = torch.sigmoid(self.regressor(features)) * 2 + 0.5  # 限制增益范围
            return gains
  • 多帧融合

    • 结合不同白平衡设置的多个帧

    • 通过语义分析选择最佳区域

评估指标

1. 客观指标

  • 色差ΔE

    def deltaE(gt, pred):
        # 转换为Lab色彩空间
        lab_gt = cv2.cvtColor(gt, cv2.COLOR_RGB2LAB)
        lab_pred = cv2.cvtColor(pred, cv2.COLOR_RGB2LAB)
        return np.sqrt(np.sum((lab_gt - lab_pred)**2, axis=2)).mean()
  • 灰色卡检测

    • 使用ColorChecker中的中性色块

    • 理想情况下RGB值应相等

白平衡算法分类

1. 灰度世界算法(Gray World Algorithm)

import cv2
import numpy as np

def gray_world_white_balance(img):
    """
    灰度世界白平衡算法
    :param img: 输入图像(BGR格式)
    :return: 白平衡后的图像
    """
    b, g, r = cv2.split(img.astype('float32'))
    
    # 计算各通道均值
    avg_b = np.mean(b)
    avg_g = np.mean(g)
    avg_r = np.mean(r)
    
    # 计算增益系数
    k = (avg_g / avg_b + avg_g / avg_r + 1) / 3
    kb = k * avg_g / avg_b
    kr = k * avg_g / avg_r
    
    # 应用增益
    b = np.clip(b * kb, 0, 255)
    r = np.clip(r * kr, 0, 255)
    
    return cv2.merge([b, g, r]).astype('uint8')

2. 完美反射算法(Perfect Reflector)

def perfect_reflector_white_balance(img, percentile=0.1):
    """
    完美反射白平衡算法
    :param img: 输入图像(BGR格式)
    :param percentile: 选取最亮像素的比例
    :return: 白平衡后的图像
    """
    # 转换为浮点型
    img_float = img.astype('float32')
    b, g, r = cv2.split(img_float)
    
    # 计算亮度
    brightness = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 获取最亮像素的阈值
    threshold = np.percentile(brightness, 100 - percentile)
    
    # 计算最亮像素的各通道均值
    mask = brightness >= threshold
    avg_b = np.mean(b[mask])
    avg_g = np.mean(g[mask])
    avg_r = np.mean(r[mask])
    
    # 计算增益
    max_avg = max(avg_b, avg_g, avg_r)
    b = np.clip(b * max_avg / avg_b, 0, 255)
    g = np.clip(g * max_avg / avg_g, 0, 255)
    r = np.clip(r * max_avg / avg_r, 0, 255)
    
    return cv2.merge([b, g, r]).astype('uint8')

3. 基于色温的白平衡

def color_temperature_white_balance(img, temperature):
    """
    基于色温的白平衡
    :param img: 输入图像(BGR格式)
    :param temperature: 色温值(2500-10000K)
    :return: 白平衡后的图像
    """
    # 将图像转换为浮点型
    img_float = img.astype('float32') / 255.0
    
    # 计算色温调整参数
    if temperature <= 6600:
        # 暖色调(低色温)
        r = 1.0
        b = 1.0 - 0.0002 * (6600 - temperature)
    else:
        # 冷色调(高色温)
        r = 1.0 - 0.0002 * (temperature - 6600)
        b = 1.0
    
    # 应用调整
    img_float[:,:,0] *= b  # B通道
    img_float[:,:,2] *= r  # R通道
    
    # 限制范围并转换回uint8
    return (np.clip(img_float, 0, 1) * 255).astype('uint8')

自动白平衡(AWB)实现

def auto_white_balance(img, method='gray_world'):
    """
    自动白平衡
    :param img: 输入图像
    :param method: 白平衡方法('gray_world', 'perfect_reflector', 'color_temperature')
    :return: 白平衡后的图像
    """
    if method == 'gray_world':
        return gray_world_white_balance(img)
    elif method == 'perfect_reflector':
        return perfect_reflector_white_balance(img)
    elif method == 'color_temperature':
        # 自动估计色温
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        avg_intensity = np.mean(gray)
        temperature = 4000 + (avg_intensity / 255) * 6000  # 简单估计
        return color_temperature_white_balance(img, temperature)
    else:
        raise ValueError("Unsupported white balance method")

实际应用示例

# 读取图像
img = cv2.imread('input.jpg')

# 应用不同白平衡算法
gw_img = gray_world_white_balance(img)
pr_img = perfect_reflector_white_balance(img)
ct_img = color_temperature_white_balance(img, 5500)  # 5500K日光

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Gray World', gw_img)
cv2.imshow('Perfect Reflector', pr_img)
cv2.imshow('Color Temperature', ct_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

白平衡在ISP中的位置

在典型的ISP流水线中,白平衡通常位于:

  1. 黑电平校正之后

  2. 去马赛克之前(如果是RAW数据)

  3. 色彩校正矩阵(CCM)之前

性能优化技巧

  1. 查找表(LUT)优化

    def create_wb_lut(gains):
        """创建白平衡查找表"""
        lut = np.zeros(256, dtype='float32')
        for i in range(256):
            lut[i] = min(255, i * gains)
        return lut
    
    def apply_wb_with_lut(img, lut_b, lut_r):
        """使用LUT应用白平衡"""
        b, g, r = cv2.split(img)
        b = cv2.LUT(b, lut_b)
        r = cv2.LUT(r, lut_r)
        return cv2.merge([b, g, r])
  • GPU加速

    import cupy as cp
    
    def gpu_white_balance(img, gains):
        """使用GPU加速的白平衡"""
        img_gpu = cp.asarray(img)
        img_gpu[..., 0] *= gains[0]  # B
        img_gpu[..., 1] *= gains[1]  # G
        img_gpu[..., 2] *= gains[2]  # R
        return cp.asnumpy(cp.clip(img_gpu, 0, 255).astype('uint8'))

评估白平衡效果

  1. 主观评估:观察白色或中性色区域是否真实

  2. 客观评估

    def evaluate_wb(img, white_patch):
        """评估白平衡效果"""
        # white_patch是已知白色区域的坐标(x,y,w,h)
        patch = img[white_patch[1]:white_patch[1]+white_patch[3],
                    white_patch[0]:white_patch[0]+white_patch[2]]
        avg_color = np.mean(patch, axis=(0,1))
        error = np.std(avg_color)  # 标准差越小,白平衡越好
        return error

总结

白平衡算法选择建议:

  • 灰度世界:适用于自然场景,计算简单

  • 完美反射:适用于有明确白色参考的场景

  • 色温法:当光源色温已知时最准确

在实际ISP实现中,通常会结合多种方法并加入自适应机制,以获得最佳的白平衡效果。

你可能感兴趣的:(C++与python交互编程,python,ISP,白平衡)