3. AE曝光计算基础
任务目标
本案例实现AE(自动曝光)核心计算模块:输入当前帧亮度统计值(平均亮度、亮度权重分布)与目标亮度,输出曝光时间、模拟增益、数字增益三者的初始配置值。算法采用经典直方图权重法,适用于高通Spectra ISP平台上的预览与拍照场景。
核心实现
// ae_exposure_calc.h
#ifndef AE_EXPOSURE_CALC_H
#define AE_EXPOSURE_CALC_H
#include <stdint.h>
#define AE_HIST_BINS 256
#define AE_MAX_EXPOSURE_TIME_US 33000.0f // 最大曝光33ms
#define AE_MIN_EXPOSURE_TIME_US 100.0f // 最小曝光0.1ms
#define AE_MAX_ANALOG_GAIN 16.0f
#define AE_MIN_ANALOG_GAIN 1.0f
#define AE_MAX_DIGITAL_GAIN 4.0f
#define AE_MIN_DIGITAL_GAIN 1.0f
typedef struct {
float exposure_time_us; // 曝光时间(us)
float analog_gain; // 模拟增益
float digital_gain; // 数字增益
} ae_result_t;
typedef struct {
uint32_t hist[AE_HIST_BINS]; // 256级灰度直方图
uint32_t total_pixels; // 总像素数
} ae_stats_t;
// 核心计算函数
ae_result_t ae_calc_exposure(const ae_stats_t *stats, float target_luma);
#endif
// ae_exposure_calc.c
#include "ae_exposure_calc.h"
#include <math.h>
// 计算加权亮度(中亮区域权重更高)
static float ae_calc_weighted_luma(const ae_stats_t *stats) {
float weighted_sum = 0.0f;
uint32_t weight_sum = 0;
for (int i = 0; i < AE_HIST_BINS; i++) {
if (stats->hist[i] == 0) continue;
float luma = (float)i / 255.0f;
float weight = 1.0f;
// 中间调(0.3~0.7)权重1.5,暗部/亮部权重0.8
if (luma > 0.3f && luma < 0.7f) {
weight = 1.5f;
} else {
weight = 0.8f;
}
weighted_sum += luma * weight * (float)stats->hist[i];
weight_sum += (uint32_t)(weight * (float)stats->hist[i]);
}
if (weight_sum == 0) return 0.0f;
return weighted_sum / (float)weight_sum;
}
ae_result_t ae_calc_exposure(const ae_stats_t *stats, float target_luma) {
ae_result_t result = {0};
if (stats == NULL || stats->total_pixels == 0) {
return result;
}
// 1. 计算当前加权亮度
float current_luma = ae_calc_weighted_luma(stats);
// 2. 计算总增益需求(线性关系)
float total_gain = target_luma / (current_luma + 0.001f);
if (total_gain < 0.1f) total_gain = 0.1f;
if (total_gain > 100.0f) total_gain = 100.0f;
// 3. 曝光时间优先(先调时间,再调增益)
float exposure_time = 10000.0f; // 默认10ms
float analog_gain = 1.0f;
float digital_gain = 1.0f;
// 3.1 先尝试只调曝光时间
float time_candidate = exposure_time * total_gain;
if (time_candidate <= AE_MAX_EXPOSURE_TIME_US) {
exposure_time = time_candidate;
} else {
// 曝光时间到上限,剩余增益分配给模拟增益
exposure_time = AE_MAX_EXPOSURE_TIME_US;
float remaining_gain = total_gain * 10000.0f / AE_MAX_EXPOSURE_TIME_US;
// 3.2 模拟增益优先
if (remaining_gain <= AE_MAX_ANALOG_GAIN) {
analog_gain = remaining_gain;
} else {
analog_gain = AE_MAX_ANALOG_GAIN;
// 3.3 最后剩余给数字增益
digital_gain = remaining_gain / AE_MAX_ANALOG_GAIN;
if (digital_gain > AE_MAX_DIGITAL_GAIN) {
digital_gain = AE_MAX_DIGITAL_GAIN;
}
}
}
// 4. 边界裁剪
if (exposure_time < AE_MIN_EXPOSURE_TIME_US) exposure_time = AE_MIN_EXPOSURE_TIME_US;
if (exposure_time > AE_MAX_EXPOSURE_TIME_US) exposure_time = AE_MAX_EXPOSURE_TIME_US;
if (analog_gain < AE_MIN_ANALOG_GAIN) analog_gain = AE_MIN_ANALOG_GAIN;
if (analog_gain > AE_MAX_ANALOG_GAIN) analog_gain = AE_MAX_ANALOG_GAIN;
if (digital_gain < AE_MIN_DIGITAL_GAIN) digital_gain = AE_MIN_DIGITAL_GAIN;
if (digital_gain > AE_MAX_DIGITAL_GAIN) digital_gain = AE_MAX_DIGITAL_GAIN;
result.exposure_time_us = exposure_time;
result.analog_gain = analog_gain;
result.digital_gain = digital_gain;
return result;
}
// 测试入口 test_ae.c
#include <stdio.h>
#include <string.h>
#include "ae_exposure_calc.h"
int main() {
// 构造测试统计:模拟中等亮度场景(平均亮度约0.5)
ae_stats_t stats;
memset(&stats, 0, sizeof(stats));
stats.total_pixels = 1920 * 1080;
// 在灰度128附近集中分布
for (int i = 100; i < 156; i++) {
stats.hist[i] = stats.total_pixels / 56;
}
float target_luma = 0.5f; // 目标亮度50%
ae_result_t result = ae_calc_exposure(&stats, target_luma);
printf("当前加权亮度: %.3f\n", ae_calc_weighted_luma(&stats));
printf("曝光时间: %.2f us\n", result.exposure_time_us);
printf("模拟增益: %.2f\n", result.analog_gain);
printf("数字增益: %.2f\n", result.digital_gain);
// 验证:计算等效亮度
float effective_luma = 0.5f * (result.exposure_time_us / 10000.0f) *
result.analog_gain * result.digital_gain;
printf("等效输出亮度: %.3f (目标0.5)\n", effective_luma);
return 0;
}
编译与运行
gcc -o test_ae test_ae.c ae_exposure_calc.c -lm
./test_ae
验证方法
程序输出中“等效输出亮度”应接近目标亮度0.5(误差<5%)。当前构造的直方图中心在灰度128(归一化0.502),默认曝光10ms、增益1.0时亮度已接近目标,因此输出曝光时间应接近10000us,模拟/数字增益接近1.0。若将直方图整体左移(如集中在灰度64附近),曝光时间应相应增大以补偿亮度不足。
算法说明
本实现采用“曝光时间→模拟增益→数字增益”的优先级策略:先通过拉长曝光时间提升亮度,达到上限后再依次增加模拟增益和数字增益。加权亮度计算给予中间调更高权重,避免高光或阴影区域主导曝光决策。该策略符合ISP AE模块的典型控制逻辑,可直接接入高通Spectra平台的传感器驱动层。