4、白平衡(AWB):色温与色彩恒常性、灰度世界法、完美反射法、色温估计

白平衡,说白了就是让白色物体在任何光源下都呈现白色。你想想看,一张白纸在日光灯下发青,在白炽灯下发黄,但人眼看起来还是白的——这就是色彩恒常性。但摄像头没这个本事,它拍出来是什么色温就是什么色温,所以得靠算法来校正。

我刚开始做ISP调试那会儿,最头疼的就是AWB。有一次在户外阴天场景下,整个画面偏蓝得厉害,客户说你们这摄像头是不是坏了?其实不是坏了,是色温估计没做好。从那以后,我对AWB的每个细节都不敢马虎。

4.1 色温与色彩恒常性

色温用开尔文(K)表示。低色温偏红黄(比如烛光2000K),高色温偏蓝(比如阴天7000K)。标准日光大概是5500K左右。

光源类型 色温范围(K) 视觉感受
烛光/日出日落 2000 - 3000 暖黄
白炽灯 2800 - 3200 暖白
日光灯 4000 - 5000 中性偏冷
正午日光 5000 - 5500 标准白
阴天/阴影 6500 - 7500 冷蓝

色彩恒常性是人眼的一种补偿机制。不管光源怎么变,大脑会自动把熟悉物体的颜色“纠正”回来。但CMOS传感器是线性器件,它忠实记录每个像素的R、G、B值,不会自动补偿。所以AWB算法的任务,就是模拟人眼的这种能力。

核心思路:找到当前场景的色温,然后对R、G、B通道分别乘上增益系数,让中性色(灰、白)的R=G=B。

4.2 灰度世界法

这是最经典的AWB方法,也是我最早接触的算法。它的假设很简单:一张色彩丰富的图片,所有像素的R、G、B平均值应该趋于相等,也就是“灰度世界”。

具体做法:

  1. 计算整幅图像的R、G、B均值:R_avg、G_avg、B_avg
  2. 计算目标灰度值:Gray = (R_avg + G_avg + B_avg) / 3
  3. 计算各通道增益:R_gain = Gray / R_avg,G_gain = Gray / G_avg,B_gain = Gray / B_avg
  4. 对每个像素应用增益:R_out = R_in * R_gain,G_out = G_in * G_gain,B_out = B_in * B_gain
// 灰度世界法伪代码
void awb_gray_world(uint8_t* img, int width, int height) {
    long sum_r = 0, sum_g = 0, sum_b = 0;
    int total = width * height;
    
    for (int i = 0; i < total; i++) {
        sum_r += img[i * 3 + 0];
        sum_g += img[i * 3 + 1];
        sum_b += img[i * 3 + 2];
    }
    
    float avg_r = sum_r / total;
    float avg_g = sum_g / total;
    float avg_b = sum_b / total;
    float gray = (avg_r + avg_g + avg_b) / 3.0f;
    
    float gain_r = gray / avg_r;
    float gain_g = gray / avg_g;
    float gain_b = gray / avg_b;
    
    // 应用增益(略)
}

我曾经踩过的坑:灰度世界法在单色场景下会完全失效。比如拍一片蓝天,画面全是蓝色,算法会强行把蓝色压下去,结果天空变成灰色。所以实际工程中,不能直接对整个画面做均值,得先做场景判断。

4.3 完美反射法

这个方法假设图像中最亮的像素点就是白色(或中性色)的反射点。说白了,就是找画面里最亮的那些像素,认为它们应该是纯白的。

实现步骤:

  1. 把图像从RGB转到YUV或计算亮度Y = 0.299R + 0.587G + 0.114B
  2. 找出亮度最高的前1%~5%的像素
  3. 对这些“候选白点”分别统计R、G、B的均值
  4. 用这些均值计算增益,和灰度世界法类似

我个人习惯把两种方法结合起来用。灰度世界法处理大面积色块场景容易翻车,完美反射法在暗光下又容易误判。两者加权融合,效果会好很多。

方法 优点 缺点 适用场景
灰度世界法 实现简单,计算量小 单色场景失效 色彩丰富的户外场景
完美反射法 对高光区域敏感 暗光下误检率高 有明确白色物体的场景
两者融合 鲁棒性好 需要调权重参数 通用场景

4.4 色温估计

色温估计是AWB里最核心也最麻烦的一步。说白了,就是猜当前场景的光源色温是多少。猜对了,白平衡就准;猜错了,画面就偏色。

常用的方法有两种:

  • 基于统计的色温估计:把R/G和B/G的比值映射到色温曲线上。不同色温下,R/G和B/G的比值有固定规律。比如低色温下R/G大、B/G小;高色温下反过来。
  • 基于学习的色温估计:用大量标注好的数据训练一个模型,输入图像特征,输出色温值。现在很多手机ISP都在用这种方法。

我的经验:实际工程中,色温估计不能只靠一帧。我会做一个滑动窗口,对连续几帧的色温做平滑滤波。这样能避免画面闪烁——你想想看,如果每帧色温都在跳,拍视频时画面颜色忽冷忽热,用户肯定受不了。

色温估计的精度直接影响最终效果。我见过一些方案,色温估计误差在±500K以内,人眼基本看不出偏色。但如果误差超过1000K,画面就会明显偏蓝或偏黄。

AWB白平衡算法流程 原始RAW图像 统计R/G/B均值 & 亮度 灰度世界法 完美反射法 色温估计法 加权融合 & 场景判断 输出校正后图像 注意: 单色场景需跳过 或降低权重

嗯,这里要注意一点:色温估计不是越精确越好。有时候场景里混合了多种光源(比如室内既有日光灯又有窗户进来的自然光),这时候硬要估计一个色温反而会出问题。我遇到过一个案例,会议室里一半是日光灯一半是阳光,AWB怎么调都偏色。最后我们加了多光源检测,把画面分区处理,才算搞定。

实战要点总结:

  • 灰度世界法和完美反射法各有短板,建议融合使用
  • 色温估计要做帧间平滑,避免闪烁
  • 单色场景要特殊处理,不能硬套算法
  • 混合光源场景考虑分区白平衡