4、AWB核心算法框架:灰度世界假设、完美反射假设、白点检测与统计方法
聊到自动白平衡的核心算法,我脑子里第一个蹦出来的就是这三个经典方法。它们就像ISP工程师工具箱里的三把扳手——各有各的脾气,也各有各的用武之地。我个人习惯把这三种方法串起来理解,而不是孤立地看。
说白了,白平衡要解决的核心问题就一个:让白色物体在任何光源下都呈现为白色。听起来简单吧?但实际做起来,坑多得很。我刚开始调AWB那会儿,就吃过不少亏。
4.1 灰度世界假设(Gray World)
这个假设很直观:一张色彩丰富的自然图像,所有像素的R、G、B平均值应该趋于相等。换句话说,整幅图的平均颜色应该是灰色的。
为什么能这么假设?你想想看,自然界里各种颜色混合在一起,统计上确实会趋向中性灰。这个假设在大多数场景下都成立,尤其是户外风景、室内多物体场景。
算法实现其实就三步:
- 计算整幅图像R、G、B三个通道的均值
- 计算三个均值的平均,得到目标灰度值
- 分别计算每个通道的增益系数,然后应用
代码实现长这样:
// 灰度世界假设实现
void grayWorldAWB(uint8_t* img, int width, int height) {
double sumR = 0, sumG = 0, sumB = 0;
int total = width * height;
// 第一步:统计各通道总和
for (int i = 0; i < total; i++) {
sumR += img[i * 3 + 0];
sumG += img[i * 3 + 1];
sumB += img[i * 3 + 2];
}
// 第二步:计算均值
double avgR = sumR / total;
double avgG = sumG / total;
double avgB = sumB / total;
// 第三步:计算增益并应用
double targetGray = (avgR + avgG + avgB) / 3.0;
double gainR = targetGray / avgR;
double gainG = targetGray / avgG;
double gainB = targetGray / avgB;
// 应用增益
for (int i = 0; i < total; i++) {
img[i * 3 + 0] = clamp(img[i * 3 + 0] * gainR);
img[i * 3 + 1] = clamp(img[i * 3 + 1] * gainG);
img[i * 3 + 2] = clamp(img[i * 3 + 2] * gainB);
}
}
4.2 完美反射假设(Perfect Reflector)
这个假设和灰度世界正好相反。它认为:图像中最亮的那个像素点,应该是纯白色的。因为最亮的物体通常就是白色物体,它应该反射所有波长的光。
嗯,这里要注意。完美反射假设不是找整幅图最亮的那个点,而是找R、G、B三个通道都接近饱和的那些像素。我习惯的做法是:
- 遍历所有像素,计算每个像素的亮度值
- 选出亮度最高的前1%~5%的像素
- 用这些像素的R、G、B均值来估算光源颜色
- 计算增益并校正
两种假设的对比,我整理了一张表:
| 特性 | 灰度世界假设 | 完美反射假设 |
|---|---|---|
| 适用场景 | 色彩丰富的自然场景 | 存在白色/高光物体的场景 |
| 失效场景 | 大面积单色场景 | 无白色物体、过曝场景 |
| 计算复杂度 | 低(只需一次遍历) | 中(需要排序或阈值筛选) |
| 鲁棒性 | 一般 | 较好(有白色物体时) |
4.3 白点检测与统计方法
前面两种方法都是全局假设,说白了就是「一刀切」。但实际场景哪有那么理想?所以工业界更常用的是白点检测+统计的方法。
核心思路是:先找出图像中「可能是白色」的像素点,然后用这些点的颜色信息来估算光源。这样既避免了灰度世界被大面积单色干扰,也避免了完美反射找不到白色物体的尴尬。
白点检测的判据有很多种,我常用的有:
- 亮度判据:像素亮度要足够高(排除暗部噪声)
- 色度判据:R/G和B/G的比值要在合理范围内
- 饱和度判据:饱和度不能太高(排除彩色物体)
我曾经在调试一款手机摄像头时,发现白点检测的阈值设得太宽松,结果把浅蓝色的天空也当成了白点,导致白平衡偏蓝。后来我把色度判据的阈值收紧了一些,效果就好多了。
下面我用一张SVG流程图来展示白点检测与统计的完整流程:
实际工程中,我一般会把灰度世界和完美反射结合起来用。比如:
- 先用白点检测筛选出候选区域
- 如果候选白点数量足够多(比如超过总像素的5%),就用完美反射的思路
- 如果候选白点很少,说明场景中可能没有白色物体,这时候回退到灰度世界假设
最后说一句,白点检测的阈值参数千万别写死。我习惯把它们做成可调节的tuning参数,这样在不同传感器、不同镜头下都能灵活适配。你想想看,同样的算法在手机摄像头和监控摄像头上,表现可能天差地别——这就是调优的价值所在。