4、AWB算法实战:灰度世界法、完美反射法、色温估计、高通AWB调参接口

白平衡(AWB)是ISP pipeline里最“玄学”的模块之一。为什么这么说?因为人眼有色彩恒常性,不管在日光灯下还是烛光下,看白纸都觉得是白的。但传感器没这个本事,它老老实实地把色温差异反映在RGB数值上。AWB要做的,就是把这个偏色掰回来。

我个人习惯把AWB算法分成两类:基于统计的假设法和基于学习的估计法。高通平台里,两种思路都有体现。今天我们先啃硬骨头——灰度世界法和完美反射法,再聊聊色温估计的工程落地,最后把高通AWB调参接口捋一遍。

核心观点:没有万能的AWB算法。灰度世界法在色彩丰富的场景很准,但遇到大块单色物体就翻车。完美反射法对高光区域敏感,但暗光下容易误判。工程上通常是多算法融合+场景分类。

4.1 灰度世界法(Gray World Assumption)

这个假设很朴素:一张色彩丰富的自然图像,所有像素的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. 用增益校正每个像素
// 灰度世界法C实现片段
void awb_gray_world(uint16_t *r, uint16_t *g, uint16_t *b, int width, int height) {
    uint64_t sum_r = 0, sum_g = 0, sum_b = 0;
    int total = width * height;

    for (int i = 0; i < total; i++) {
        sum_r += r[i];
        sum_g += g[i];
        sum_b += b[i];
    }

    float avg_r = (float)sum_r / total;
    float avg_g = (float)sum_g / total;
    float avg_b = (float)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;

    // 应用增益(注意饱和处理)
    for (int i = 0; i < total; i++) {
        r[i] = min(r[i] * gain_r, 1023);  // 假设10bit RAW
        g[i] = min(g[i] * gain_g, 1023);
        b[i] = min(b[i] * gain_b, 1023);
    }
}

避坑指南:我曾经在一个绿植场景的项目里直接用灰度世界法,结果画面偏紫得一塌糊涂。为什么?因为整帧都是绿色,平均下来G通道远高于R和B,算法强行把绿色压下去,反而引入了紫色色偏。后来加了“色彩丰富度检测”,当场景主色调占比超过阈值时,自动降低灰度世界法的权重。

4.2 完美反射法(Perfect Reflector / White Patch)

完美反射法的假设是:图像中最亮的像素点应该是白色的(R=G=B)。这个“最亮”不一定是纯白,而是指场景中反射率最高的区域。

实现上有几个变种:

  • 经典版:取全图最亮的0.1%~1%像素,计算这些像素的R、G、B均值,然后按灰度世界法的方式求增益
  • 改进版:不只看亮度,还看“白度”——R、G、B通道值接近的程度,筛选出既亮又白的像素
  • 高通平台常用版:结合亮度阈值和色度阈值做双重筛选
// 完美反射法——高通风格实现
void awb_perfect_reflector(uint16_t *r, uint16_t *g, uint16_t *b, int w, int h) {
    int total = w * h;
    // 第一步:计算每个像素的亮度Y和色度CbCr
    // 第二步:筛选Y > Y_threshold 且 |Cb|+|Cr| < C_threshold 的像素
    // 第三步:对筛选出的像素求R、G、B均值
    // 第四步:计算增益

    // 实际项目中,Y_threshold通常取最大亮度的90%
    // C_threshold需要根据传感器噪声水平调整
}

我的经验:完美反射法在室内灯光场景特别好用,因为灯管、白色墙壁都是天然的“完美反射体”。但户外逆光场景要小心——天空过曝区域虽然很亮,但色温信息已经丢失了,用它做参考反而会引入错误。我一般会加一个“过曝检测”,把饱和像素排除掉。

4.3 色温估计(Color Temperature Estimation)

色温估计是AWB的进阶玩法。它不直接算增益,而是先估计当前光源的色温,然后查表得到对应的R、G、B增益。

高通平台常用的色温估计方法:

方法 原理 优点 缺点
R/G-B/G色度空间法 将像素映射到R/G和B/G坐标系,与标准光源轨迹对比 计算简单,硬件友好 对噪声敏感
色温曲线拟合法 用多项式拟合普朗克轨迹在色度图上的投影 连续色温估计,精度高 需要标定
统计投票法 每个像素“投票”给最接近的色温区间 鲁棒性好 计算量大

我个人最常用的是R/G-B/G色度空间法。为什么?因为它直观,而且高通ISP硬件直接支持这个坐标系的统计信息采集。

// 色温估计伪代码——R/G-B/G空间法
float estimate_color_temp(float *rg_ratio, float *bg_ratio, int num_pixels) {
    // 1. 计算整帧的R/G和B/G均值
    float avg_rg = mean(rg_ratio, num_pixels);
    float avg_bg = mean(bg_ratio, num_pixels);

    // 2. 与标定的色温轨迹对比
    // 轨迹数据:{色温, R/G, B/G}
    // 例如:D65(6500K) -> {1.0, 1.0}
    //       A光源(2856K) -> {1.5, 0.6}

    // 3. 找到最近的轨迹点
    float min_dist = INFINITY;
    float est_ct = 5000;  // 默认
    for (int i = 0; i < num_ref_points; i++) {
        float dist = sqrt(pow(avg_rg - ref_rg[i], 2) + pow(avg_bg - ref_bg[i], 2));
        if (dist < min_dist) {
            min_dist = dist;
            est_ct = ref_ct[i];
        }
    }

    // 4. 可选:插值得到更精确的色温
    return est_ct;
}

关键点:色温估计的精度高度依赖标定数据。同一颗传感器在不同模组上,R/G-B/G轨迹都会有差异。量产时一定要做模组级标定,否则AWB会“飘”。

4.4 高通AWB调参接口

高通平台的AWB调参,核心是操作chromatix库里的XML配置文件。嗯,这里要注意,高通把AWB参数分成了好几层:

  • Sensor层:定义传感器的基础特性,如RGB响应曲线、暗电流
  • Module层:定义模组的光学特性,如镜头阴影、色差
  • AWB算法层:定义算法参数,如灰度世界权重、完美反射阈值、色温轨迹
  • Tuning层:定义场景分类和参数映射表

常用的调参接口和参数:

参数名 所属模块 作用 典型值范围
gray_world_weight AWB算法 灰度世界法的融合权重 0.0 ~ 1.0
perfect_reflector_threshold AWB算法 完美反射法的亮度阈值 0.8 ~ 0.95
color_temp_trajectory AWB算法 色温轨迹点列表 多组(R/G, B/G)坐标
scene_detection_enable Tuning 是否启用场景分类 0/1
awb_speed Tuning AWB收敛速度 0(慢) ~ 10(快)
<!-- chromatix XML中AWB配置片段 -->
<awb_config>
    <algorithm>
        <gray_world_weight>0.3</gray_world_weight>
        <perfect_reflector_weight>0.5</perfect_reflector_weight>
        <color_temp_weight>0.2</color_temp_weight>
        <perfect_reflector_threshold>0.9</perfect_reflector_threshold>
    </algorithm>
    <trajectory>
        <!-- 色温轨迹点:色温, R/G, B/G -->
        <point ct="2500" rg="1.8" bg="0.5"/>
        <point ct="4000" rg="1.3" bg="0.8"/>
        <point ct="6500" rg="1.0" bg="1.0"/>
        <point ct="8000" rg="0.8" bg="1.3"/>
    </trajectory>
</awb_config>

我曾经踩过的坑:调awb_speed参数时,为了追求快速收敛,设成了10。结果画面在切换场景时出现明显的“闪烁”——AWB每帧都在剧烈调整增益。后来我把速度降到3,再加了帧间平滑滤波,才解决了这个问题。记住:AWB不是越快越好,稳定比快速更重要。

4.5 AWB算法融合框架

实际工程中,不会只用单一算法。高通平台的典型做法是:多算法并行计算,然后根据场景置信度做加权融合。

下面这张图是我自己总结的AWB融合框架:

AWB多算法融合框架 RAW图像输入 统计信息提取(直方图/均值/色度) 灰度世界法 权重: 0.3 完美反射法 权重: 0.5 色温估计法 权重: 0.2 场景分类 & 置信度评估 加权融合 → 最终增益 校正后图像输出 权重根据场景动态调整 例如:绿植场景降低灰度世界权重

这个框架的好处是:当某个算法“犯错”时,其他算法还能兜底。比如灰度世界法在绿植场景翻车,但完美反射法如果检测到高光区域,还能给出合理的增益估计。

调参建议:刚开始调AWB时,先把三个算法的权重都设成1/3,然后对着标准色卡(如X-Rite ColorChecker)拍几张不同色温的照片。看哪个算法偏差最大,再逐步调整它的权重。我一般会先固定色温估计法的权重(因为它最稳定),再调灰度世界和完美反射的比例。

好了,AWB的核心算法和调参接口就讲到这里。灰度世界法和完美反射法是基础,色温估计是进阶,高通平台的调参接口是落地工具。把这几个点吃透了,大部分AWB问题都能搞定。