第三章 场景亮度采集与统计
各位同学,今天我们来聊聊HDR流水线里一个特别关键的环节——场景亮度采集与统计。说白了,就是让芯片知道「眼前这画面到底有多亮」。我刚开始做HDR芯片那会儿,总觉得这步很简单,不就是测个亮度嘛。结果呢?第一次流片回来,画面忽明忽暗,像抽风一样。嗯,从那以后我再也不敢小看这个模块了。
3.1 直方图统计
直方图统计,是亮度采集最基础的手段。你想想看,一张图像里每个像素的亮度值,从0到255(8bit)或者0到1023(10bit),我们把它按区间统计一下,就能知道画面整体的亮度分布。
我个人习惯用256个bin来做直方图,每个bin代表一个亮度等级。这样既不会太粗导致丢失细节,也不会太细增加硬件开销。代码实现其实很简单:
// 伪代码:直方图统计模块
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
pixel = get_pixel(x, y);
bin_index = pixel >> 2; // 10bit转8bit
histogram[bin_index]++;
}
}
我在项目中遇到过一个问题:直方图统计的精度和速度怎么平衡?如果每个像素都做全精度统计,带宽压力很大。我的做法是——分区域采样。把画面分成16x16的块,每个块只统计中心区域的像素,这样数据量能减少到原来的1/4,效果却差不太多。
关键点:直方图统计的bin数量不是越多越好。8bit场景下128个bin就够用了,10bit场景建议256个bin。多了反而容易引入噪声。
3.2 区域曝光分析
直方图只能看全局,但HDR场景往往局部亮度差异巨大。比如逆光人像,人脸暗、背景亮。这时候就需要区域曝光分析。
区域曝光分析,就是把画面切成若干子区域,每个区域独立做亮度统计。我一般切成5x5或者7x7的网格。为什么是这个数?因为人眼对画面的关注区域大概就是5-7个区块,多了反而没有意义。
| 网格大小 | 适用场景 | 硬件开销 |
|---|---|---|
| 3x3 | 简单场景,快速处理 | 低 |
| 5x5 | 通用场景,平衡性能 | 中 |
| 7x7 | 复杂HDR场景 | 高 |
每个区域我们计算三个值:平均亮度、最大亮度、最小亮度。平均亮度用来做整体曝光补偿,最大和最小亮度用来判断动态范围是否溢出。
小技巧:区域曝光分析时,可以给中心区域更高的权重。因为大多数拍摄场景,主体都在画面中央。我曾经做过一个项目,中心区域权重设为1.5,边缘设为0.8,效果比均匀权重好很多。
3.3 动态范围检测
动态范围检测,说白了就是看场景里最亮和最暗的地方差了多少。这个值直接决定了我们要用几帧合成HDR。
我常用的方法是:统计每个区域的亮度分布,找到最亮区域的10%分位和最暗区域的10%分位,两者相除就是动态范围。为什么要用10%分位而不是直接用最大最小值?因为最大最小值容易被噪声干扰。你想想看,一个像素的坏点就能把最大值拉得很高,这显然不合理。
// 动态范围检测伪代码
float calc_dynamic_range(histogram_t *hist) {
// 找到暗部10%分位
int total = get_total_pixels(hist);
int dark_threshold = total * 0.1;
int bright_threshold = total * 0.9;
int dark_value = find_percentile(hist, dark_threshold);
int bright_value = find_percentile(hist, bright_threshold);
return (float)bright_value / (float)dark_value;
}
动态范围检测的结果,直接决定了HDR合成策略:
- 动态范围 < 2: 单帧就够了,不需要HDR
- 动态范围 2-4: 两帧合成,短帧+长帧
- 动态范围 4-8: 三帧合成,短帧+中帧+长帧
- 动态范围 > 8: 四帧甚至更多,或者考虑局部色调映射
注意:动态范围检测不能只看一帧。我遇到过场景里突然闪过一道光,动态范围瞬间飙升,如果立刻切换合成策略,画面会闪一下。所以一定要做时间上的平滑滤波。
3.4 闪烁检测与防抖
闪烁检测,主要是针对LED光源和荧光灯。这些光源以50Hz或60Hz的频率闪烁,人眼看不出来,但摄像头拍出来就是一条条明暗条纹。
检测方法其实不复杂:连续采集几帧,看同一区域的亮度是否周期性变化。如果变化频率接近50Hz或60Hz,那就是闪烁。
防抖呢?嗯,这里要注意,防抖和闪烁检测经常放在一起做。因为两者的数据来源是一样的——都是帧间亮度变化。
我建议的流程是这样的:
- 采集连续N帧的直方图数据
- 计算帧间亮度差异
- 如果差异呈现周期性,标记为闪烁
- 如果差异是随机抖动,标记为抖动
- 闪烁用曝光时间调整来消除,抖动用帧对齐来消除
我曾经踩过一个坑:把相机的机械快门抖动误判成了闪烁。那批芯片做出来,在日光灯下画面稳定,但在自然光下反而抖动。查了好久才发现,是机械快门的振动被传感器捕捉到了。后来我在算法里加了一个频率滤波器,只检测50Hz和60Hz附近的信号,问题就解决了。
避坑指南:闪烁检测的帧数不能太少。我建议至少采集16帧做分析。帧数太少,容易把随机噪声误判为闪烁。帧数太多,响应速度又跟不上。16帧是个不错的平衡点。
知识体系总览
下面这张图,是我自己整理的场景亮度采集与统计的整体框架。你可以看到,四个模块之间是有数据流向的:直方图统计是基础,区域曝光分析依赖直方图数据,动态范围检测又依赖区域分析的结果,闪烁检测则是在时间维度上做分析。
好了,这一章的内容就到这里。场景亮度采集与统计,看似基础,但做不好后面所有HDR处理都是白搭。我见过太多团队在这个环节偷懒,结果后面花几倍的时间去调参数。记住一句话:采集做扎实了,后面就顺了。