3. AE参数调优实战:目标亮度、权重分布、步长与防闪烁
AE(自动曝光)调得好不好,直接决定用户第一眼看到画面时的感受。太亮,过曝一片死白;太暗,暗部细节全丢。我这些年调过的 sensor 少说也有几十款,每次拿到新平台,第一件事就是先把 AE 的这几个核心参数摸一遍。
今天咱们就围绕四个关键点展开:目标亮度设定、权重分布、步长与阻尼、防闪烁策略。说白了,就是让 AE 既快又稳,还能在各种场景下输出一致的亮度。
核心观点:AE 调优不是追求“最亮”或“最暗”,而是追求“场景自适应”和“时间域平滑”。目标亮度是锚点,权重分布是策略,步长阻尼是控制力,防闪烁是底线。
3.1 目标亮度设定(Target Luma)
目标亮度,就是 AE 算法想要达到的画面平均亮度值。通常用 0-255 的 8bit 亮度值表示,或者归一化到 0-1。我见过很多新手工程师上来就问:“目标亮度设多少合适?”其实这个问题没有标准答案,得看场景。
举个例子,室内人像和户外风景,目标亮度能一样吗?室内人脸需要 180-200 左右才显得肤色通透,户外大场景 120-150 就够,再高反而刺眼。
| 场景类型 | 推荐目标亮度(8bit) | 说明 |
|---|---|---|
| 室内人像 | 180 - 210 | 人脸肤色通透,背景不过曝 |
| 户外风景 | 120 - 150 | 保留天空和地面细节 |
| 夜景/暗光 | 80 - 110 | 避免噪声放大,保留暗部层次 |
| 文档扫描 | 200 - 230 | 文字清晰,背景干净 |
我的经验:我曾经在一个安防项目中,把目标亮度设到 200,结果夜间画面全是噪声。后来降到 90,配合适当的增益控制,画面干净多了。记住:目标亮度不是越高越好,要跟 sensor 的 SNR 曲线匹配。
实际调优时,我习惯先给一个默认值(比如 150),然后跑几个典型场景,看直方图分布。如果高光区域经常溢出,就降低目标亮度;如果暗部经常欠曝,就适当提高。嗯,这里要注意:目标亮度改变后,AE 的收敛速度也会变,后面讲步长时会提到。
3.2 AE权重分布(中心权重 vs 全局权重)
权重分布决定了 AE 算法“更在意画面哪个区域”。你想想看,拍人像时,人脸通常在中部,如果背景很亮,全局平均亮度可能偏高,人脸反而欠曝。这时候就需要中心权重。
常见的权重模式有三种:
- 全局平均权重:每个像素一视同仁。适合风景、均匀光照场景。
- 中心加重权重:画面中心区域权重更高(比如中心占 60%,四周占 40%)。适合人像、主体居中的场景。
- 自定义权重:根据 ROI(感兴趣区域)动态调整。适合人脸追踪、物体追踪等高级应用。
我个人的习惯是:默认用中心加重,然后根据场景切换。比如检测到人脸,就切换到人脸区域权重;检测到逆光,就降低中心权重,避免背景过曝。
注意:权重分布改完后,一定要重新标定目标亮度。因为同样的物理亮度,用中心权重和全局权重算出来的平均亮度可能差 20-30 个灰度级。我曾经踩过这个坑,换了权重没调目标亮度,画面直接过曝。
权重分布的实现,本质上是一个二维矩阵。下面是一个简单的 5x5 中心权重矩阵示例:
// 5x5 中心权重矩阵(中心权重最高)
float weight[5][5] = {
{0.5, 0.8, 1.0, 0.8, 0.5},
{0.8, 1.5, 2.0, 1.5, 0.8},
{1.0, 2.0, 3.0, 2.0, 1.0},
{0.8, 1.5, 2.0, 1.5, 0.8},
{0.5, 0.8, 1.0, 0.8, 0.5}
};
// 归一化后使用
float total_weight = 0.0f;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
total_weight += weight[i][j];
// 加权平均亮度 = sum(亮度 * 权重) / total_weight
3.3 AE的步长与阻尼系数
步长和阻尼,说白了就是控制 AE 每次调整的幅度和速度。步长太大,画面会来回闪烁;步长太小,从暗到亮要等好几秒,用户体验极差。
我一般把步长分为粗调步长和细调步长:
- 粗调步长:当当前亮度与目标亮度偏差 > 30% 时使用。每次调整曝光时间的 20%-30%。
- 细调步长:当偏差 < 10% 时使用。每次调整 2%-5%,避免过冲。
阻尼系数的作用是“刹车”。比如当前亮度 200,目标亮度 150,如果步长算出来要减 50,但阻尼系数 0.3,实际只减 15。这样能防止 overshoot。
| 参数 | 推荐范围 | 作用 |
|---|---|---|
| 粗调步长 | 15% - 30% | 快速接近目标亮度 |
| 细调步长 | 2% - 5% | 精确收敛,避免抖动 |
| 阻尼系数 | 0.2 - 0.5 | 抑制过冲,增加稳定性 |
避坑指南:我曾经在一个项目中,把阻尼系数设到 0.8,结果 AE 收敛慢得像蜗牛,用户投诉切换场景后画面要 5 秒才稳定。后来改成 0.3,配合粗调步长 25%,1 秒内就稳了。阻尼不是越大越好,要跟步长配合着调。
3.4 AE的防闪烁策略(Anti-flicker)
防闪烁,主要是针对 50Hz/60Hz 的工频灯光。你想想看,在室内日光灯下,如果曝光时间不是工频周期的整数倍,画面就会出现明暗条纹,这就是 flicker。
防闪烁的核心思路就一条:让曝光时间锁定在工频周期的整数倍上。
- 50Hz 地区(中国、欧洲等):工频周期 20ms,曝光时间必须是 20ms 的整数倍(20ms、40ms、60ms...)。
- 60Hz 地区(美国、日本等):工频周期 16.67ms,曝光时间必须是 16.67ms 的整数倍。
实现上,AE 算法在计算目标曝光时间后,需要做一个“取整”操作:
// 防闪烁曝光时间对齐(50Hz 场景)
float target_exposure = 35.0f; // AE 算出来的目标曝光时间(ms)
float flicker_period = 20.0f; // 50Hz 周期
// 对齐到最近的整数倍
int n = (int)(target_exposure / flicker_period + 0.5f);
if (n < 1) n = 1;
float aligned_exposure = n * flicker_period;
// 输出:aligned_exposure = 40.0ms(对齐到 2 倍周期)
注意:防闪烁对齐后,曝光时间的步长会变大(从连续变成离散的 20ms 步长)。这会导致 AE 的收敛精度下降。我一般会在防闪烁模式下,适当增大阻尼系数,或者引入帧间平滑,避免画面出现“跳变感”。
另外,有些 sensor 支持自动防闪烁检测,可以自动识别当前是 50Hz 还是 60Hz 环境。如果硬件不支持,就需要在驱动层写一个频率检测算法,或者干脆让用户在菜单里手动选择。我个人更倾向于自动检测,毕竟你不能指望用户知道自己在 50Hz 还是 60Hz 地区。