AWB 看不到物体原本的颜色,只能看到“光源光谱 × 表面反射率 × 传感器响应”的结果。仅凭一张 RGB 图像同时求出光源和反射率,本质上是一个欠定问题;经典算法之所以可用,是因为它们额外加入了关于场景的统计假设。
Gray World:假设颜色在整体上互相抵消
灰世界假设认为,一个颜色足够丰富的场景,其平均反射率接近中性。在线性 RGB 中计算均值 后,可令三个通道趋向同一目标:
最后常再以绿色通道归一化增益。
Gray World:平均颜色真的是灰吗
灰世界假设认为足够丰富的场景长期平均应接近中性;当画面由单一物体颜色占据时,算法会把内容误当光源。
森林场景会被错误补入洋红,日落可能被过度降温。现代 AWB 会结合中性候选、语义、肤色与先验光源模型。
这个方法的优势是简单、稳定且无需训练;它的弱点也来自同一个假设。森林、蓝天、水下、红色舞台和大面积彩色墙面并不满足“颜色丰富且均衡”,算法会把物体固有颜色误认为光源偏色。
White Patch 与中性点假设
White Patch 假设场景中最亮、接近漫反射白的区域能够代表光源颜色。它在存在白纸、灰卡或高反射中性物体时很有效,但容易被灯具、镜面高光和通道饱和欺骗。
中性点方法不要求候选一定最亮,而是在颜色空间中寻找可能为灰色的像素。问题在于:经过未知光源照明后,中性物体本来就会偏离中性轴,筛选范围过窄会漏掉候选,过宽又会混入彩色物体。
因此实际系统往往组合多个估计器,而不是把所有场景交给单一规则。
先筛像素,再谈均值
统计输入的质量通常比均值公式更重要。常见排除条件包括:
- 过暗像素:通道比例容易受黑电平残差和读出噪声支配;
- 饱和像素:至少一个通道已经裁剪,RGB 比例不再可信;
- 高色度像素:更可能属于强色物体,而非中性候选;
- 坏点、耀斑和镜面反射:会制造局部异常统计;
- 面积过小的候选:可能只是噪声或偶然高光。
可以把亮度与色度条件写成一个明确的候选掩码。令
则一个简化的可信集合为
排除噪声主导的暗部, 排除接近裁剪的像素, 则限制候选色度。StatisticsMaskLab 展示的正是这三类门限之间的取舍。
统计区域决定 AWB 看见什么
饱和高光、深噪声、强物体色与人脸都不应被同等对待。候选筛选常比最后的增益公式更重要。
中性候选更可靠但数量少。工程上需要在样本覆盖率、噪声与先验偏差之间平衡,并对低置信度场景保守更新。
空间分块也很重要。直接汇总全图会让大面积背景支配结果;先在每个块内统计,再对有效块加权,可以保留场景分布信息,并为后续的混合光检测提供依据。
用稳健统计限制离群值
即使通过掩码,候选中仍可能混入彩色物体或镜面高光。与普通均值相比,截尾均值会先删除每个通道两端各 比例的样本:
其中 是排序后的样本。下面代码把 Gray World、可信像素 mask 与 10% 截尾均值串成一个可运行的估计器,返回光源 RGB 色度而不是直接输出 WB 增益,以便与总览篇的“估计/应用”职责分离。
import numpy as np
def gray_world_illuminant(linear_rgb, dark=0.03, bright=0.92, chroma_limit=0.35, trim=0.10): """从 [0, 1] 线性 RGB 估计归一化光源颜色。""" rgb = np.asarray(linear_rgb, dtype=np.float64) if rgb.ndim != 3 or rgb.shape[-1] != 3: raise ValueError("linear_rgb 必须是 H×W×3")
y = rgb.mean(axis=-1) peak = rgb.max(axis=-1) chroma = (peak - rgb.min(axis=-1)) / np.maximum(y, 1e-6) mask = (y > dark) & (y < bright) & (peak < 0.98) & (chroma < chroma_limit) candidates = rgb[mask] if len(candidates) < 32: raise ValueError("可信候选不足,应降低置信度而不是强行更新")
k = int(len(candidates) * trim) sorted_rgb = np.sort(candidates, axis=0)3 collapsed lines
core = sorted_rgb[k:len(candidates) - k] if k else sorted_rgb illuminant = core.mean(axis=0) return illuminant / np.maximum(illuminant.sum(), 1e-6), mask这里的阈值与 10% 截尾比例是教学默认值,不是跨相机通用标定。逐通道排序也会打散 RGB 样本对应关系;它适合演示稳健中心,不足以代替色度空间聚类、空间分块权重与相机光源轨迹约束。
统计必须处于合适的颜色域
AWB 通常在线性 RAW 或近似线性 RGB 上统计。若直接平均 Gamma 编码后的像素,暗部被相对放大,均值不再与采集到的光子比例对应。
同时还要明确 BLC、LSC 与坏点校正的顺序:黑电平残差会污染暗部通道比,镜头阴影会让画面不同位置呈现不同颜色倾向,坏点则会影响小区域候选。统计模块依赖这些基础校正提供可信输入。
从候选分布得到光源
筛选后不应只保留三个全局均值。更完整的调试数据包括:
stats = { "block_rgb": block_rgb, "valid_mask": valid_mask, "candidate_count": candidate_count, "chromaticity_histogram": uv_histogram, "highlight_ratio": highlight_ratio,}候选在色度空间中聚成单一紧密峰,通常比散布在多处更可信。估计器可以把 Gray World、White Patch、已知光源轨迹和场景分类结果融合,并保留各自得分供决策层使用。
如何验证经典统计方法
测试集不能只包含色卡和普通街景,还应覆盖:
- 大面积单色物体与低颜色多样性场景;
- 暖光、日光、荧光灯和窄带 LED;
- 饱和灯具、镜面高光与深阴影;
- 灰卡位于中心、边缘和不同亮度区间;
- 多种肤色、天空、草地与水下场景;
- 不同曝光量、模拟增益和传感器温度。
经典统计方法不是过时的“简单基线”。它们仍然提供可解释、低成本的观测量;真正的工程难点,是知道假设何时成立,并把候选质量交给后续置信度层判断。