4. AE基础
任务目标
在典型安防或车载场景中,感兴趣区域(ROI)往往不在画面中心,例如车道远端或门禁人脸区域。全局平均亮度统计会导致ROI过曝或欠曝。本案例实现一个支持自定义权重矩阵的AE算法,使曝光统计显著偏向指定区域,同时保留全局亮度作为参考。
核心原理
权重区域AE的关键在于将图像划分为N×N块(如15×15),对每块计算平均亮度,再与对应权重相乘后累加,最后除以权重总和得到加权平均亮度。该亮度值作为AE控制环路的反馈量,与目标亮度比较后通过PID或查找表调整曝光时间和增益。
完整实现
// ae_weighted_region.h
#ifndef AE_WEIGHTED_REGION_H
#define AE_WEIGHTED_REGION_H
#include <stdint.h>
#define AE_GRID_SIZE 15 // 15x15 分块
#define AE_MAX_GAIN_DB 48 // 最大模拟增益 48dB
typedef struct {
float weight[AE_GRID_SIZE][AE_GRID_SIZE]; // 权重矩阵,范围0.0~1.0
float target_luma; // 目标亮度(8bit,0~255)
float cur_luma; // 当前加权亮度
float exposure_ms; // 当前曝光时间(毫秒)
float gain_db; // 当前增益(dB)
float kp; // 比例系数
float ki; // 积分系数
float integral; // 积分项累积
} AEWeightedCtrl;
// 初始化:设置默认中心权重,目标亮度120,曝光10ms,增益0dB
void ae_weighted_init(AEWeightedCtrl *ae);
// 设置自定义权重(如左上区域优先)
void ae_weighted_set_roi(AEWeightedCtrl *ae, int row_start, int row_end,
int col_start, int col_end, float weight_val);
// 输入15x15块平均亮度数组(每块0~255),更新曝光参数
void ae_weighted_process(AEWeightedCtrl *ae, const uint8_t block_luma[AE_GRID_SIZE][AE_GRID_SIZE]);
#endif
// ae_weighted_region.c
#include "ae_weighted_region.h"
#include <string.h>
static float db_to_linear(float db) {
return (db > 0.0f) ? (1.0f + db / 24.0f) : 1.0f; // 简化:24dB=2x增益
}
void ae_weighted_init(AEWeightedCtrl *ae) {
memset(ae, 0, sizeof(*ae));
ae->target_luma = 120.0f;
ae->exposure_ms = 10.0f;
ae->gain_db = 0.0f;
ae->kp = 0.15f;
ae->ki = 0.02f;
// 默认中心权重:中心5x5区域权重1.0,其余0.2
for (int r = 0; r < AE_GRID_SIZE; r++) {
for (int c = 0; c < AE_GRID_SIZE; c++) {
int dr = (r >= 5 && r < 10) ? 1 : 0;
int dc = (c >= 5 && c < 10) ? 1 : 0;
ae->weight[r][c] = (dr && dc) ? 1.0f : 0.2f;
}
}
}
void ae_weighted_set_roi(AEWeightedCtrl *ae, int row_start, int row_end,
int col_start, int col_end, float weight_val) {
if (row_start < 0) row_start = 0;
if (row_end >= AE_GRID_SIZE) row_end = AE_GRID_SIZE - 1;
if (col_start < 0) col_start = 0;
if (col_end >= AE_GRID_SIZE) col_end = AE_GRID_SIZE - 1;
for (int r = row_start; r <= row_end; r++) {
for (int c = col_start; c <= col_end; c++) {
ae->weight[r][c] = weight_val;
}
}
}
void ae_weighted_process(AEWeightedCtrl *ae, const uint8_t block_luma[AE_GRID_SIZE][AE_GRID_SIZE]) {
float weighted_sum = 0.0f;
float weight_total = 0.0f;
for (int r = 0; r < AE_GRID_SIZE; r++) {
for (int c = 0; c < AE_GRID_SIZE; c++) {
weighted_sum += block_luma[r][c] * ae->weight[r][c];
weight_total += ae->weight[r][c];
}
}
if (weight_total < 0.001f) weight_total = 1.0f;
ae->cur_luma = weighted_sum / weight_total;
// 亮度误差(归一化到0~1)
float err = (ae->target_luma - ae->cur_luma) / 255.0f;
ae->integral += err;
if (ae->integral > 1.0f) ae->integral = 1.0f;
if (ae->integral < -1.0f) ae->integral = -1.0f;
float delta = ae->kp * err + ae->ki * ae->integral;
// 调整曝光时间(0.1ms ~ 60ms)
ae->exposure_ms *= (1.0f + delta);
if (ae->exposure_ms < 0.1f) ae->exposure_ms = 0.1f;
if (ae->exposure_ms > 60.0f) ae->exposure_ms = 60.0f;
// 若曝光已达上限,再调整增益(0~48dB)
if (ae->exposure_ms >= 59.9f && ae->gain_db < AE_MAX_GAIN_DB) {
ae->gain_db += delta * 10.0f;
if (ae->gain_db < 0.0f) ae->gain_db = 0.0f;
if (ae->gain_db > AE_MAX_GAIN_DB) ae->gain_db = AE_MAX_GAIN_DB;
}
}
测试与验证
// test_ae_weighted.c
#include <stdio.h>
#include "ae_weighted_region.h"
int main() {
AEWeightedCtrl ae;
ae_weighted_init(&ae);
// 模拟场景:ROI在左上角(0~4行,0~4列),亮度80;其余区域亮度200
uint8_t luma[AE_GRID_SIZE][AE_GRID_SIZE];
for (int r = 0; r < AE_GRID_SIZE; r++)
for (int c = 0; c < AE_GRID_SIZE; c++)
luma[r][c] = (r < 5 && c < 5) ? 80 : 200;
// 设置左上角5x5区域权重为1.0,其余为0.1
ae_weighted_set_roi(&ae, 0, 4, 0, 4, 1.0f);
for (int r = 0; r < AE_GRID_SIZE; r++)
for (int c = 0; c < AE_GRID_SIZE; c++)
if (!(r < 5 && c < 5)) ae.weight[r][c] = 0.1f;
printf("初始: 曝光=%.2fms 增益=%.1fdB\n", ae.exposure_ms, ae.gain_db);
// 迭代10帧模拟AE收敛
for (int i = 0; i < 10; i++) {
ae_weighted_process(&ae, luma);
printf("帧%d: 加权亮度=%.1f 曝光=%.2fms 增益=%.1fdB\n",
i + 1, ae.cur_luma, ae.exposure_ms, ae.gain_db);
}
// 验证:加权亮度应接近目标120
if (ae.cur_luma > 110 && ae.cur_luma < 130) {
printf("PASS: 加权亮度收敛到目标范围\n");
return 0;
} else {
printf("FAIL: 加权亮度未收敛,当前=%.1f\n", ae.cur_luma);
return 1;
}
}
编译与运行
gcc -o test_ae test_ae_weighted.c ae_weighted_region.c -lm
./test_ae
预期输出(确定性结果):由于ROI亮度80低于目标120,AE将增加曝光时间。经过多帧迭代,加权亮度逐步上升并收敛至120附近。第10帧时曝光时间应显著大于初始10ms,且加权亮度在110~130区间内。
关键说明
- 权重矩阵在15×15网格上定义,
ae_weighted_set_roi可任意设置矩形区域的高权重,实现非中心ROI优先。 - 曝光调整采用增量式PI控制,曝光时间优先调节,达到上限后才提升增益,符合真实ISP策略。
- 实际接入ISP时,
block_luma由3A统计硬件提供,本代码用模拟数据验证算法逻辑。