4、自动白平衡(AWB)算法与硬件实现

自动白平衡,说白了就是让相机在各种光源下都能把白色拍成白色。你想想看,在钨丝灯下拍的白纸偏黄,在日光灯下又偏蓝,这谁受得了?AWB 就是干这个活的——把色偏拉回来。

我个人习惯把 AWB 拆成四个环节:统计 → 估计 → 计算 → 校正。今天咱们重点聊前三个,以及它们在硬件里怎么落地。

4.1 灰度世界算法

这是最经典的假设:场景里所有颜色的平均值应该是灰色。说白了,R、G、B 三个通道的均值应该相等。

公式很简单:

R_gain = G_mean / R_mean
B_gain = G_mean / B_mean

我在项目中遇到过一个问题——如果场景里大片都是红色(比如夕阳下的红墙),灰度世界假设就崩了。整张图会被强行拉成灰蒙蒙的,红色全没了。

避坑: 我曾经在一个监控项目里直接用灰度世界,结果傍晚的夕阳场景全变成了阴天效果。后来加了饱和度检测,只对低饱和度区域做统计,才把问题解决。

4.2 完美反射算法

这个思路不一样——它认为图像里最亮的点应该是白色。找到这些“完美反射点”,用它们的 RGB 比值来算增益。

实现步骤:

  1. 把每个像素的 R、G、B 加起来,或者取最大值
  2. 挑出亮度排前 1%~5% 的像素
  3. 用这些像素的 R/G/B 均值算增益
// 伪代码:完美反射统计
for each pixel (r, g, b):
    luminance = max(r, g, b)
    if luminance > threshold:
        sum_r += r; sum_g += g; sum_b += b
        count++

R_gain = sum_g / sum_r
B_gain = sum_g / sum_b
经验: 阈值怎么定?我一般取整图亮度直方图的 98% 分位点。这样既不会漏掉真正的白点,也不会被噪点干扰。

4.3 色温估计方法

灰度世界和完美反射都是“盲猜”,没有利用光源本身的特性。色温估计就不一样了——它先判断当前是什么光源,再查表找对应的增益。

常用的做法是画一个 色温曲线:

色温 (K) R/G 比值 B/G 比值 典型场景
2500 2.1 0.6 白炽灯
4000 1.4 0.9 荧光灯
5500 1.0 1.0 日光
6500 0.8 1.3 阴天

实际工作时,把统计到的 R/G、B/G 比值往这条曲线上投影,找到最近的色温点,再插值出增益。

关键点: 色温曲线不是一条直线,而是弯曲的。不同 sensor 的曲线也不一样,需要标定。我见过有人直接拿公开数据用,结果偏色偏得一塌糊涂。

4.4 增益计算与校正

不管用哪种方法,最后都要算出 R 和 B 通道的增益,然后乘到每个像素上。

// 增益校正
R_out = R_in * R_gain
G_out = G_in * 1.0   // G 通道通常不动
B_out = B_in * B_gain

嗯,这里要注意:增益不能太大,否则会放大噪点。我一般限制最大增益不超过 4.0,超过的部分用裁切处理。

4.5 硬件加速中的统计引擎设计

软件里做 AWB 统计很简单——遍历一遍图像就行。但硬件里不行,因为数据是流水线流过来的,你只有一次机会。

所以我们需要一个 统计引擎,在数据流过的时候实时收集信息。

4.5.1 直方图统计

直方图是 AWB 的基础。硬件里一般用 双端口 RAM 来存直方图数据:

  • 每个像素进来,根据它的值找到对应的 bin
  • 把那个 bin 的计数器加 1
  • 一帧结束后,CPU 读出直方图数据

我做过一个设计,用了 256 个 bin,每个 bin 20 位宽,支持最大 100 万像素的图像。面积不大,但够用。

4.5.2 分块统计

整图统计太粗糙了。实际场景里,光源可能不均匀——比如窗口边亮、室内暗。这时候需要把图像分成 M×N 块,每块单独统计。

硬件实现:

  1. 用行计数器 + 列计数器算出当前像素属于哪个块
  2. 每个块维护一组累加器(R_sum, G_sum, B_sum, count)
  3. 帧结束后,CPU 遍历所有块,挑出有效的块做加权平均
技巧: 我习惯把图像分成 16×12 块。块太多,硬件面积大;块太少,又看不出局部色温变化。16×12 是个不错的折中。

4.5.3 统计引擎的硬件架构

下面这张图是我常用的统计引擎结构:

AWB 统计引擎硬件架构 AWB 统计引擎硬件架构 像素数据流 分块控制器 直方图 RAM 分块累加器组 总线仲裁器 CPU 接口 工作流程: 1. 像素数据流进入分块控制器,确定当前像素属于哪个块 2. 同时更新直方图 RAM 和对应块的分块累加器 3. 帧结束后,CPU 通过总线仲裁器读取所有统计数据

4.5.4 硬件实现的几个坑

我曾经踩过的坑:
  • 直方图溢出: 一帧图像可能有上百万像素,直方图 bin 的位宽不够就会溢出。我一般预留 20 位,支持 100 万像素。
  • 分块边界处理: 像素刚好落在块边界上怎么办?我的做法是让分块控制器用“小于等于”判断,保证每个像素只属于一个块。
  • 流水线延迟: 统计引擎不能拖慢主流水线。我习惯用寄存器打拍,把统计逻辑拆成 2~3 级流水,保证时序收敛。

4.6 三种算法的对比

算法 优点 缺点 硬件开销 适用场景
灰度世界 实现简单,计算量小 大色块场景失效 低(只需累加器) 色彩丰富的自然场景
完美反射 对白色物体敏感 没有白点就崩 中(需要排序或阈值比较) 有高光、镜面反射的场景
色温估计 物理意义明确,鲁棒性好 需要标定,查表占用存储 高(需要 ROM 和插值器) 专业相机、安防监控
我的建议: 实际产品里很少只用一种算法。我一般用灰度世界做基础,再用完美反射做修正,最后用色温估计做兜底。三种算法投票,取两个一致的作为最终结果。

嗯,AWB 这块内容不少,但核心就这些。统计引擎是硬件的骨架,算法是软件的灵魂。两者配合好了,才能在各种光源下都拍出舒服的颜色。