4、自动白平衡(AWB)基础:色温与白平衡的关系,AWB的经典算法(灰度世界、完美反射、色温估计),AWB的局限性。
各位同学,今天我们聊聊自动白平衡。说白了,AWB 就是让相机在不同光线下,都能把白色拍成白色。你想想看,一张白纸在日光灯下发青,在白炽灯下发黄,人眼会自动校正,但传感器不会。AWB 就是替人眼干这个活。
我个人习惯把 AWB 看作 ISP 里的“调色盘起点”。起点歪了,后面所有颜色处理都会跑偏。我在项目中遇到过不少案例,降噪和锐化做得再好,白平衡一错,整张图就废了。
4.1 色温与白平衡的关系
色温不是温度,是光线的颜色属性。单位是开尔文(K)。数值越低,光越暖(偏红黄);数值越高,光越冷(偏蓝)。
| 光源类型 | 典型色温(K) | 视觉感受 |
|---|---|---|
| 烛光 | 1500 - 2000 | 暖红 |
| 白炽灯 | 2500 - 3000 | 暖黄 |
| 日光灯 | 4000 - 5000 | 中性偏冷 |
| 正午日光 | 5000 - 6500 | 标准白 |
| 阴天 | 6500 - 8000 | 冷蓝 |
白平衡的核心逻辑很简单:找到场景中的“参考白”,然后反向补偿。如果参考白偏蓝,就加黄;偏黄,就加蓝。补偿量由 R、G、B 三个通道的增益决定。
关键公式:
Rout = Rin × Rgain
Gout = Gin × Ggain
Bout = Bin × Bgain
其中,Rgain、Ggain、Bgain 就是我们要算的增益系数。
4.2 AWB 的经典算法
业界常用的算法就三种。我按出场频率排个序:灰度世界、完美反射、色温估计。每种都有它的脾气。
4.2.1 灰度世界算法
这个算法假设:一张色彩丰富的图片,所有颜色的平均值应该是灰色。也就是 R、G、B 三个通道的均值相等。
实现步骤很简单:
- 计算整张图的 R、G、B 均值:Ravg、Gavg、Bavg。
- 计算全局灰度值:Gray = (Ravg + Gavg + Bavg) / 3。
- 计算各通道增益:Rgain = Gray / Ravg,Ggain = Gray / Gavg,Bgain = Gray / Bavg。
- 用增益校正每个像素。
// 灰度世界算法伪代码
float R_avg = mean(image, R_channel);
float G_avg = mean(image, G_channel);
float B_avg = mean(image, B_channel);
float Gray = (R_avg + G_avg + B_avg) / 3.0f;
float R_gain = Gray / R_avg;
float G_gain = Gray / G_avg;
float B_gain = Gray / B_avg;
for each pixel (r, g, b) in image:
r_out = r * R_gain;
g_out = g * G_gain;
b_out = b * B_gain;
避坑指南:我曾经在一个纯蓝色天空的场景里用灰度世界算法,结果天空被硬生生拉成了灰色。原因很简单——场景本身就不满足“色彩丰富”的假设。遇到大面积单色场景,灰度世界会翻车。
4.2.2 完美反射算法
这个算法假设:图像中最亮的像素点应该是白色。它认为场景里总有一个区域是纯白反射面。
实现步骤:
- 找到图像中最亮的像素(通常是 R+G+B 最大的那个)。
- 以这个像素的 R、G、B 值作为参考白。
- 计算增益:Rgain = 255 / Rmax,Ggain = 255 / Gmax,Bgain = 255 / Bmax。
- 用增益校正全图。
嗯,这里要注意。完美反射对高光区域非常敏感。如果场景里有个灯泡或者反光点,算法会把它当成白色,结果整张图偏冷。
警告:完美反射算法在低光环境下几乎不可用。因为低光场景里根本没有“足够亮”的像素,算法会强行拉高增益,导致噪声爆炸。我在做夜景模式时吃过这个亏。
4.2.3 色温估计算法
这个算法更聪明一些。它不依赖全局假设,而是直接估算当前场景的色温,然后查表得到增益。
实现思路:
- 在标定阶段,拍摄不同色温下的灰卡,记录 R/G 和 B/G 的比值。
- 建立色温-比值映射表。
- 运行时,计算当前图像的 R/G 和 B/G 比值。
- 在映射表中找到最接近的色温值。
- 用该色温对应的增益校正图像。
// 色温估计算法伪代码
// 标定表:色温 -> (R_gain, G_gain, B_gain)
struct CalibrationEntry {
float color_temperature;
float R_gain;
float G_gain;
float B_gain;
};
CalibrationEntry table[] = {
{3000, 1.8, 1.0, 1.4},
{4000, 1.4, 1.0, 1.2},
{5000, 1.2, 1.0, 1.1},
{6500, 1.0, 1.0, 1.0},
{8000, 0.9, 1.0, 0.8}
};
// 运行时估算色温
float R_over_G = mean(R_channel) / mean(G_channel);
float B_over_G = mean(B_channel) / mean(G_channel);
// 查表找到最接近的色温
float estimated_CT = lookup(table, R_over_G, B_over_G);
我的经验:色温估计算法是目前手机 ISP 的主流方案。它比灰度世界和完美反射都稳定,但依赖前期标定质量。标定做不好,算法就是空中楼阁。
4.3 AWB 的局限性
没有完美的算法。AWB 的局限性我总结为三点:
- 场景假设失效:灰度世界要求色彩丰富,完美反射要求存在白点。现实场景往往不满足这些假设。比如日落时的暖色调场景,算法会强行“校正”成白色,反而破坏了氛围。
- 混合光源难处理:室内窗边,一边是日光,一边是灯光。两种色温混在一起,AWB 不知道该听谁的。我见过不少手机在这种场景下拍出“阴阳脸”。
- 低光噪声放大:暗光下信号弱,AWB 的增益计算会被噪声干扰。增益一高,噪声跟着放大,颜色就飘了。
下面这张图是我自己整理的 AWB 算法对比,方便你快速理解差异。
工程建议:实际产品里很少只用一种算法。我通常的做法是:白天场景用灰度世界和完美反射做加权融合,低光场景切到色温估计。这样能兼顾稳定性和准确性。