4. 统计信息采集:从RAW图到亮度直方图与分区统计
好,我们进入AE收敛技术中最关键的一环——统计信息采集。
你想想看,自动曝光要收敛,它得先知道当前画面亮不亮、哪里亮、哪里暗。这些信息从哪来?就是从RAW图里算出来的。说白了,没有准确的统计信息,后面的AE算法就是盲人摸象。
我个人习惯把统计信息采集分成三个层次:全局直方图、分区统计、权重矩阵。咱们一层一层剥开。
4.1 从RAW图获取亮度直方图
直方图这东西,大家应该不陌生。横轴是亮度等级(0~1023,如果是10-bit RAW),纵轴是像素个数。但这里有个坑——RAW图的直方图和RGB直方图不是一回事。
RAW图每个像素只有一个颜色通道(R/G/B之一),所以它的直方图反映的是单通道的原始光强分布,还没经过白平衡、去马赛克、Gamma校正这些处理。这个原始分布对AE来说特别宝贵,因为它最真实地反映了场景的物理亮度。
具体怎么算?我贴一段伪代码,大家感受一下:
// 10-bit RAW, 宽度W, 高度H
uint32_t histogram[1024] = {0};
for (int y = 0; y < H; y++) {
for (int x = 0; x < W; x++) {
uint16_t pixel = raw_buffer[y * W + x];
// 直接累加,不做任何变换
histogram[pixel]++;
}
}
// 可选:归一化到百分比
float hist_norm[1024];
uint32_t total_pixels = W * H;
for (int i = 0; i < 1024; i++) {
hist_norm[i] = (float)histogram[i] / total_pixels;
}
嗯,这里要注意:不要对RAW像素做任何预处理。我在项目中遇到过有人先做了黑电平校正再算直方图,结果暗部细节全丢了,AE死活收敛不到目标亮度。后来查了半天才发现是统计源的问题。
4.2 分区统计(Zone Statistic)的原理
全局直方图有个致命问题——它不知道亮和暗分别在哪。比如一个画面左边亮右边暗,全局直方图可能看起来是中间调,但实际AE需要知道左边过曝了、右边欠曝了。
所以我们需要分区统计。
分区统计,就是把画面切成M×N个格子,每个格子独立算亮度统计值。这样AE就能知道每个区域的亮度情况。
我常用的分区方案:
| 应用场景 | 分区数 | 说明 |
|---|---|---|
| 普通拍照 | 5×5 或 7×7 | 平衡精度和计算量 |
| 人脸优先 | 9×9 或 11×11 | 需要更精细的人脸区域定位 |
| 视频/预览 | 3×3 或 5×5 | 帧率高,计算量要小 |
| HDR场景检测 | 15×15 以上 | 需要捕捉局部高动态细节 |
每个分区算什么呢?我一般算三个值:
- 平均亮度:分区内所有像素的均值,代表整体亮度水平
- 亮度方差:反映分区内的纹理/对比度,用于判断是平滑区域还是边缘区域
- 过曝/欠曝像素比例:亮度大于阈值(比如950/1023)或小于阈值(比如50/1023)的像素占比
我曾经在一个强背光场景的项目里,只用平均亮度做分区统计,结果AE把主体人脸压到全黑,背景倒是正常了。后来加上过曝/欠曝比例检测,才把背光场景的AE收敛做好。
4.3 权重矩阵的设计
有了分区统计,接下来就是权重矩阵。权重矩阵决定了AE更关注画面的哪个区域。
为什么需要权重?因为用户拍照时,通常主体在画面中央。如果中央和边缘的亮度权重一样,AE可能会为了照顾边缘而牺牲主体。
我常用的权重矩阵设计思路:
- 中心权重:中央区域权重高,边缘权重低。适合普通拍照场景。
- 人脸权重:检测到人脸后,人脸所在分区权重极高,其他区域权重降低。
- 亮度自适应权重:根据场景亮度动态调整。比如夜景下,暗部区域权重提高,防止AE把暗部压死。
- 用户触控权重:用户点击屏幕对焦/测光的位置,权重设为最高。
下面是一个典型的5×5中心权重矩阵示例:
// 5×5 中心权重矩阵
// 中心区域权重最高,向外递减
float weight_5x5[5][5] = {
{0.2, 0.4, 0.6, 0.4, 0.2},
{0.4, 0.8, 1.0, 0.8, 0.4},
{0.6, 1.0, 1.5, 1.0, 0.6},
{0.4, 0.8, 1.0, 0.8, 0.4},
{0.2, 0.4, 0.6, 0.4, 0.2}
};
// 归一化,使所有权重之和为1
float sum = 0;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
sum += weight_5x5[i][j];
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
weight_5x5[i][j] /= sum;
最终AE的亮度统计值就是:
float weighted_brightness = 0;
for (int i = 0; i < NUM_ZONES; i++) {
weighted_brightness += zone_stats[i].avg_luma * zone_weights[i];
}
这个weighted_brightness就是喂给AE控制器的核心输入。
4.4 整体流程:一张图说清楚
下面我用一张SVG图把整个统计信息采集的流程串起来:
4.5 实战中的几个关键点
最后,我总结几个实战中容易踩的坑:
- 统计源选择:一定要用RAW域数据,不要用RGB或YUV域。我见过有人用YUV的Y分量做统计,结果Gamma校正后的亮度分布和物理亮度完全对不上。
- Bayer格式处理:如果直接统计RAW,不同颜色通道的响应曲线不同。我建议先做简单的白平衡增益补偿,或者只统计G通道(因为G通道对亮度贡献最大)。
- 权重矩阵归一化:权重矩阵一定要归一化到总和为1,否则AE控制器的增益计算会出错。
- 统计帧率:统计信息不需要每帧都算。我一般设置每2~3帧算一次,节省带宽和功耗。
好了,这一章的内容就到这里。统计信息采集是AE收敛的基石,直方图给你全局视野,分区统计给你局部细节,权重矩阵告诉你该关注哪。三者配合好了,AE收敛才能又快又稳。