AE经典算法:均值法、中心权重法、分区权重法、直方图法

自动曝光(AE)说白了,就是让摄像头自己找到那个「不亮不暗刚刚好」的亮度值。我入行那会儿,第一个任务就是调AE算法。当时觉得不就是算个平均亮度嘛,后来才发现,这里面的门道深着呢。

今天咱们就把四种最经典的AE算法掰开揉碎了讲。每种算法都有它的脾气,选对了,画面通透;选错了,要么过曝一片白,要么暗部死黑。

1. 均值法——最朴素的曝光策略

均值法的思路很简单:把整帧图像所有像素的亮度值加起来,除以像素总数,得到一个平均亮度。然后跟目标亮度(通常是128,8位图像的中值)比较,调整曝光时间或增益。

// 均值法核心逻辑
uint32_t sum = 0;
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        sum += frame[y][x];
    }
}
uint8_t avg_luma = sum / (width * height);
int error = TARGET_LUMA - avg_luma;
// 根据error调整曝光参数

我在项目中遇到过一个问题:大面积的蓝天背景下,人脸完全欠曝。均值法把整个天空的亮度平均进去了,导致人脸区域曝光不足。这就是均值法的致命伤——它一视同仁,不区分前景和背景。

适用场景:光照均匀、没有明显主体、背景简单的场景。比如仓库监控、走廊、隧道内部。

注意:均值法对极端亮暗区域非常敏感。画面中如果有一盏灯或者一块反光板,整个画面的曝光都会被带偏。

2. 中心权重法——聚焦画面中央

中心权重法认为,画面的主体通常放在中央区域。所以它给中央区域更高的权重,边缘区域权重低一些。说白了,就是「中央说了算,边缘靠边站」。

常见的权重分配方式是这样的:

区域 权重系数 说明
中央区域(约占画面30%) 0.6 主体通常在这里
中间区域(约占画面40%) 0.3 过渡区域
边缘区域(约占画面30%) 0.1 干扰信息,权重最低
// 中心权重法示意
float weight_map[HEIGHT][WIDTH];
// 初始化权重,中央高,边缘低
float weighted_sum = 0.0f;
float total_weight = 0.0f;
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        weighted_sum += frame[y][x] * weight_map[y][x];
        total_weight += weight_map[y][x];
    }
}
uint8_t weighted_avg = (uint8_t)(weighted_sum / total_weight);

中心权重法比均值法聪明一些。我记得有一次做门禁摄像头,人脸正好在画面中央,中心权重法表现很好。但问题来了——如果主体不在中央呢?比如监控画面中,人从左边走进来,中央权重法就会把背景当作主体,导致人脸过曝。

我的经验:中心权重法适合固定机位、主体大概率在中央的场景。比如会议摄像头、考勤机、人脸识别闸机。但安防监控中,人经常从边缘进入画面,这时候中心权重法就不太够用了。

3. 分区权重法——灵活的区域策略

分区权重法把画面分成若干块(比如3×3、5×5、7×7),每块独立计算平均亮度,然后给每块分配不同的权重。权重可以根据场景动态调整,也可以预设。

我常用的分区方式是5×5网格:

// 5×5分区权重法
#define GRID_X 5
#define GRID_Y 5
uint32_t block_sum[GRID_Y][GRID_X] = {0};
uint32_t block_count[GRID_Y][GRID_X] = {0};

// 统计每个分区的亮度总和
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        int by = y * GRID_Y / height;
        int bx = x * GRID_X / width;
        block_sum[by][bx] += frame[y][x];
        block_count[by][bx]++;
    }
}

// 计算每个分区的平均亮度
uint8_t block_avg[GRID_Y][GRID_X];
for (int i = 0; i < GRID_Y; i++) {
    for (int j = 0; j < GRID_X; j++) {
        block_avg[i][j] = block_sum[i][j] / block_count[i][j];
    }
}

// 应用权重矩阵(可动态调整)
float weight[GRID_Y][GRID_X] = {
    {0.02, 0.03, 0.05, 0.03, 0.02},
    {0.03, 0.06, 0.10, 0.06, 0.03},
    {0.05, 0.10, 0.20, 0.10, 0.05},
    {0.03, 0.06, 0.10, 0.06, 0.03},
    {0.02, 0.03, 0.05, 0.03, 0.02}
};

float final_luma = 0.0f;
for (int i = 0; i < GRID_Y; i++) {
    for (int j = 0; j < GRID_X; j++) {
        final_luma += block_avg[i][j] * weight[i][j];
    }
}

分区权重法的好处是灵活。你可以根据场景动态调整权重矩阵。比如检测到人脸时,把人脸所在区域的权重提高;检测到运动目标时,把目标区域的权重提高。

实战技巧:我在做智能交通摄像头时,把路面区域权重设高,天空区域权重设低。这样不管晴天阴天,车牌和车身都能保持合适的曝光。

分区权重法也有坑。分区太细,计算量大,嵌入式芯片扛不住;分区太粗,又失去了分区的意义。我一般建议在嵌入式平台上用5×5或7×7,再大就得上硬件加速了。

4. 直方图法——基于统计分布的曝光

直方图法不只看平均亮度,它分析整帧图像的亮度分布。直方图的横轴是亮度(0~255),纵轴是像素数量。好的曝光,直方图应该分布均匀,没有明显的「断崖」或「堆积」。

直方图法的核心思路:让直方图尽量覆盖整个亮度范围,同时避免过曝(像素堆积在255附近)和欠曝(像素堆积在0附近)。

// 直方图统计
uint32_t histogram[256] = {0};
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        histogram[frame[y][x]]++;
    }
}

// 计算过曝和欠曝比例
uint32_t total_pixels = width * height;
uint32_t overexposed = 0;  // 亮度 > 250
uint32_t underexposed = 0; // 亮度 < 10
for (int i = 250; i < 256; i++) overexposed += histogram[i];
for (int i = 0; i < 10; i++) underexposed += histogram[i];

float over_ratio = (float)overexposed / total_pixels;
float under_ratio = (float)underexposed / total_pixels;

// 如果过曝比例超过阈值,降低曝光
if (over_ratio > 0.05f) {
    // 减少曝光时间或增益
}
// 如果欠曝比例超过阈值,增加曝光
if (under_ratio > 0.05f) {
    // 增加曝光时间或增益
}

直方图法还有一个进阶玩法——直方图均衡。它不只是调整曝光,还能提升对比度。但要注意,直方图均衡在安防监控中要慎用,因为它会改变图像的「真实感」,对后续的智能分析(比如人脸识别)可能产生负面影响。

我曾经踩过的坑:有一次做夜间监控,直方图法把暗部提得太亮,结果噪点全出来了,画面全是雪花。后来我加了限制——暗部提亮不超过原始亮度的1.5倍,同时配合3D降噪,才把效果做出来。

四种算法对比

算法 计算量 抗干扰能力 主体适应性 典型场景
均值法 低 差 差 均匀光照场景
中心权重法 低 中 中 人脸识别、会议
分区权重法 中 强 强 智能监控、交通
直方图法 高 强 中 复杂光照、逆光

你想想看,实际项目中怎么选?我个人习惯是:先跑均值法做基准,然后根据场景需求叠加分区权重或直方图约束。没有万能的算法,只有合适的组合。

我的建议:在嵌入式平台上,优先用分区权重法。它比均值法和中心权重法灵活,又比直方图法省资源。如果芯片算力够(比如有NPU或ISP硬件加速),再上直方图法做精细调优。

AE经典算法知识体系 自动曝光(AE) 均值法 中心权重法 分区权重法 直方图法 计算量:低 抗干扰:差 均匀光照场景 计算量:低 抗干扰:中 人脸识别场景 计算量:中 抗干扰:强 智能监控场景 计算量:高 抗干扰:强 复杂光照场景 嵌入式平台推荐:分区权重法(5×5网格) 兼顾灵活性与计算效率,适合大多数安防监控场景

嗯,四种算法讲完了。最后说一句:算法是死的,场景是活的。别死磕一种算法,多试试组合方案。我见过太多工程师拿着均值法跑所有场景,结果被客户骂到怀疑人生。灵活一点,你的摄像头才能「聪明」起来。