统计信息采集:R/G/B通道统计、区域统计与权重、YUV域统计、RK平台统计寄存器配置
各位做ISP的兄弟,咱们今天聊聊AWB最基础、也最容易被忽视的一环——统计信息采集。
说白了,AWB算法再牛,如果前端统计信息不准,后面全是白搭。我当年刚接手RK平台时,就吃过这个亏。白平衡怎么调都偏色,折腾了两周,最后发现是统计窗口配置错了。嗯,从那以后,我养成了一个习惯:先看统计,再调算法。
4.1 R/G/B通道统计——AWB的“眼睛”
AWB的核心任务,是估计场景中的色温。怎么估计?靠的就是R、G、B三个通道的统计值。
每个像素点进来,ISP硬件会分别累加R、G、B的亮度值。同时还会统计像素个数,这样我们就能算出平均值。
核心公式(硬件帮你算好了):
- R_avg = sum(R) / pixel_count
- G_avg = sum(G) / pixel_count
- B_avg = sum(B) / pixel_count
这三个值,就是AWB算法的输入。后面所有的色温估计、增益计算,都基于它们。
你可能会问:直接全图平均不就行了?
没那么简单。如果场景里有一块大面积纯色物体,比如红墙、绿草地,全图平均会把AWB带偏。所以,我们需要更精细的统计方式。
4.2 区域统计与权重——让算法“看”得更聪明
RK平台的ISP,会把整幅图像划分成M×N个统计块(block)。每个块独立统计R、G、B的累加值和像素数。
我个人习惯把图像分成15×15或17×17的网格。为什么是这个数?因为RK3588的统计寄存器宽度刚好能支持到32×32,但分太细了,后续处理负担重;分太粗了,又没法排除异常区域。
分好块之后,关键一步来了——给每个块分配权重。
| 区域类型 | 权重建议 | 说明 |
|---|---|---|
| 画面中央区域 | 高(如2.0~4.0) | 主体通常在这里,优先保证中央色温准确 |
| 画面边缘区域 | 中(如1.0~1.5) | 边缘可能有镜头暗角,权重不宜过高 |
| 过曝/欠曝区域 | 0(排除) | 亮度超出统计范围的数据,直接丢弃 |
| 纯色大块区域 | 低(如0.1~0.3) | 通过色度阈值判断,降低其对全局的影响 |
我的经验:权重配置不要一刀切。我曾经在一个户外监控项目里,把中央权重设到5.0,结果画面边缘偏色严重。后来改成中央2.5、边缘1.0、四角0.8,效果就好多了。调试时,建议先看统计值的分布图,再调权重。
4.3 YUV域统计——另一种视角
除了RGB统计,RK平台还提供了YUV域的统计信息。为什么需要YUV?
因为RGB三个通道相关性太强。比如一个物体从亮处移到暗处,R、G、B同时变化,但色度(Cb、Cr)相对稳定。YUV统计,说白了就是把亮度和色度分开看。
YUV统计主要提供以下信息:
- Y直方图:256级亮度分布,用于判断场景整体亮度
- Cb、Cr均值:反映整体色偏方向
- Cb、Cr二维直方图:256×256的色度分布,这是AWB算法的“金矿”
我记得在做一个室内灯光复杂的项目时,RGB统计出来的色温一直在3000K~6000K之间跳变。后来我看了Cb-Cr二维直方图,发现有两个明显的聚类——一个是暖色灯区域,一个是窗边自然光区域。用这个信息做多区域AWB,问题就解决了。
4.4 RK平台统计寄存器配置——动手实战
理论说完了,咱们看看RK3588上怎么配寄存器。这部分是硬核内容,我建议你对照RK的TRM手册一起看。
4.4.1 统计窗口配置
首先,要告诉ISP统计哪块区域。默认是全图,但你可以裁剪。
// RK3588 AWB统计窗口配置示例
// 寄存器基址:AWB_CTRL_BASE = 0xFD8C0000
// 1. 使能AWB统计模块
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0000) |= (1 << 0);
// 2. 设置统计窗口起始坐标 (x_start, y_start)
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0004) = (100 << 16) | 80; // x=100, y=80
// 3. 设置统计窗口结束坐标 (x_end, y_end)
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0008) = (1900 << 16) | 1080; // x=1900, y=1080
// 4. 设置分块数:水平17块,垂直13块
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x000C) = (17 << 16) | 13;
注意:窗口坐标的单位是像素,但分块数必须是奇数。为什么?因为中央块正好对应画面中心。RK的硬件设计如此,如果你设成偶数,中央区域会落在两块之间,权重计算会很麻烦。
4.4.2 权重表配置
权重表是一维数组,按行优先顺序写入。每个块对应一个8位权重值(0~255)。
// 权重表配置(17×13 = 221个权重值)
// 寄存器地址连续,从 0x0010 开始
uint8_t weight_table[221];
// 填充权重值...
// 中央区域权重设200,边缘设100,四角设50
for (int i = 0; i < 221; i++) {
*(volatile uint8_t*)(AWB_CTRL_BASE + 0x0010 + i) = weight_table[i];
}
这里有个坑:权重表写入后,硬件不会立即生效。你需要触发一次“统计更新”命令。
// 触发统计更新
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0000) |= (1 << 4);
// 等待更新完成
while (*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0000) & (1 << 4));
4.4.3 YUV统计配置
YUV统计的配置稍微复杂一点,需要设置Cb-Cr二维直方图的bin宽度。
// YUV统计配置
// 设置Cb、Cr的bin宽度:每个bin覆盖8个灰度级
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0100) = (8 << 8) | 8;
// 使能YUV统计
*(volatile uint32_t*)(AWB_CTRL_BASE + 0x0104) |= (1 << 0);
// 读取Y直方图(256个bin,地址从0x0200开始)
uint32_t y_hist[256];
for (int i = 0; i < 256; i++) {
y_hist[i] = *(volatile uint32_t*)(AWB_CTRL_BASE + 0x0200 + i * 4);
}
避坑指南:我曾经把Cb-Cr的bin宽度设成4,结果直方图太稀疏,很多bin是空的。后来改成8,数据分布就合理了。具体设多少,取决于你的场景动态范围。一般建议8~16之间。
4.5 知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以把它当作调试时的检查清单。
4.6 调试建议
最后,分享几个我在项目中积累的调试技巧:
- 先读后写:配置寄存器之前,先读一遍默认值。RK平台的默认值有时会变,别想当然。
- 验证统计窗口:在图像上叠加显示统计块的边界,确认窗口位置对不对。我见过有人把x_start和y_start写反了,结果统计的是画面外的数据。
- 权重可视化:把权重表转成灰度图,一眼就能看出权重分布是否合理。中央亮、边缘暗才是对的。
- YUV直方图检查:如果Cb-Cr二维直方图全集中在(128,128)附近,说明bin宽度太大,分辨率不够。如果分布太散,说明bin宽度太小。
重要提醒:统计配置改完后,一定要重新触发统计更新。我踩过这个坑——改了权重表,但忘了触发更新,结果算法读到的还是旧数据,白忙活半天。