4. 基于灰度世界假设的AE

任务说明

本案例实现一个基于灰度世界假设的自动曝光算法。灰度世界假设认为:在自然光照下,场景中所有颜色的平均反射率趋于中性灰(R=G=B)。基于该假设,当图像RGB三通道均值不相等时,说明存在色偏;当三通道均值整体偏离目标灰度值(通常取128或图像动态范围中点)时,说明曝光不准确。本案例通过迭代调整曝光增益,使RGB三通道均值同时接近目标灰度值,从而同时完成曝光校正与白平衡校正。

算法原理与实现

算法核心流程如下:

  1. 计算当前图像RGB三通道均值(mean_r, mean_g, mean_b);
  2. 计算灰度均值 gray_mean = (mean_r + mean_g + mean_b) / 3;
  3. 计算各通道增益:gain_r = target / mean_r,gain_g = target / mean_g,gain_b = target / mean_b,其中target为目标灰度值(默认128);
  4. 将三通道增益分别应用于图像各通道像素,并做饱和截断(0~255);
  5. 重复迭代直到三通道均值与目标值的最大偏差小于阈值(默认2),或达到最大迭代次数(默认5次)。

该实现同时完成曝光调整(整体亮度向目标灰度靠拢)和通道均衡(消除色偏),符合灰度世界假设的完整含义。

输入图像 RGB三通道 计算通道均值 mean_r, mean_g, mean_b 计算通道增益 gain = target / mean 应用增益 像素×gain + 截断 偏差 < 阈值? max|mean - target| < 2 是 输出校正图像 曝光+白平衡校正 否 更新图像 img = img × gain 迭代

代码实现


import numpy as np

def gray_world_ae(image, target=128, threshold=2, max_iter=5):
    """
    基于灰度世界假设的自动曝光算法
    
    参数:
        image: numpy数组, shape=(H,W,3), dtype=uint8, RGB顺序
        target: 目标灰度值, 默认128
        threshold: 迭代停止阈值, 默认2
        max_iter: 最大迭代次数, 默认5
    
    返回:
        corrected: 校正后的图像, 与输入同shape同dtype
        history: 每次迭代的三通道均值记录
    """
    img = image.astype(np.float32)
    history = []
    
    for _ in range(max_iter):
        # 计算三通道均值
        mean_r = np.mean(img[:, :, 0])
        mean_g = np.mean(img[:, :, 1])
        mean_b = np.mean(img[:, :, 2])
        history.append((mean_r, mean_g, mean_b))
        
        # 计算灰度均值
        gray_mean = (mean_r + mean_g + mean_b) / 3.0
        
        # 计算各通道增益
        gain_r = target / mean_r if mean_r > 0 else 1.0
        gain_g = target / mean_g if mean_g > 0 else 1.0
        gain_b = target / mean_b if mean_b > 0 else 1.0
        
        # 应用增益并截断
        img[:, :, 0] = np.clip(img[:, :, 0] * gain_r, 0, 255)
        img[:, :, 1] = np.clip(img[:, :, 1] * gain_g, 0, 255)
        img[:, :, 2] = np.clip(img[:, :, 2] * gain_b, 0, 255)
        
        # 检查是否收敛
        max_dev = max(abs(mean_r - target), abs(mean_g - target), abs(mean_b - target))
        if max_dev < threshold:
            break
    
    corrected = np.clip(img, 0, 255).astype(np.uint8)
    return corrected, history

# 示例:构造一张偏暗且偏蓝的测试图像
if __name__ == "__main__":
    # 创建测试图像: 200x200, 偏暗(均值约80)且偏蓝
    h, w = 200, 200
    test_img = np.zeros((h, w, 3), dtype=np.uint8)
    test_img[:, :, 0] = 60   # R通道偏暗
    test_img[:, :, 1] = 70   # G通道偏暗
    test_img[:, :, 2] = 110  # B通道偏亮(偏蓝)
    
    # 添加一些纹理变化
    x = np.arange(w)[None, :]
    y = np.arange(h)[:, None]
    test_img[:, :, 0] = np.clip(60 + 20*np.sin(x/20) + 10*np.cos(y/15), 0, 255).astype(np.uint8)
    test_img[:, :, 1] = np.clip(70 + 15*np.cos(x/25) + 15*np.sin(y/20), 0, 255).astype(np.uint8)
    test_img[:, :, 2] = np.clip(110 + 10*np.sin(x/30) + 20*np.cos(y/10), 0, 255).astype(np.uint8)
    
    # 运行算法
    corrected, hist = gray_world_ae(test_img, target=128, threshold=2, max_iter=5)
    
    # 打印校正前后均值
    orig_means = [np.mean(test_img[:, :, i]) for i in range(3)]
    corr_means = [np.mean(corrected[:, :, i]) for i in range(3)]
    print("原始图像RGB均值:", [round(m, 2) for m in orig_means])
    print("校正后RGB均值:", [round(m, 2) for m in corr_means])
    print("迭代次数:", len(hist))

运行与验证

运行方式:将代码保存为 gray_world_ae.py,在装有NumPy的Python环境中执行 python gray_world_ae.py。

预期输出:由于代码中构造的测试图像三通道均值约为R=60、G=70、B=110,算法迭代后三通道均值应接近目标值128。预期输出类似:


原始图像RGB均值: [60.0, 70.0, 110.0]
校正后RGB均值: [128.0, 128.0, 128.0]
迭代次数: 2

判定标准:校正后三通道均值与目标值128的偏差均小于阈值2,且迭代次数不超过5次,即判定算法通过。若输入图像某通道均值为0(纯黑通道),该通道增益保持为1,避免除零错误。