3. 自动曝光算法(AE)原理
自动曝光,说白了就是让摄像头自己找到那个「不亮不暗刚刚好」的曝光值。我刚开始做手机摄像头调试那会儿,总觉得AE不就是调调曝光时间嘛,后来被各种场景打脸——逆光、夜景、高动态……嗯,从那以后我再也不敢小看AE了。
这一章我们拆开来看:AE系统怎么搭的?测光模式怎么选?目标亮度定多少?收敛策略怎么走?
3.1 AE系统架构
先看整体框架。我习惯把AE系统分成四个模块:统计模块、分析模块、决策模块、执行模块。
流程其实不复杂:Sensor 采集到原始图像后,统计模块算出亮度分布;分析模块根据测光模式加权得到当前亮度;决策模块拿当前亮度和目标亮度做差,算出下一步要调多少;执行模块把曝光时间、增益、光圈写回 sensor。
3.2 测光模式
测光模式决定了「当前亮度怎么算」。同一个场景,不同测光模式算出来的亮度可能差很远。我遇到过好几次,用户说「画面忽亮忽暗」,查到最后是测光模式选错了。
| 测光模式 | 权重分布 | 适用场景 | 我踩过的坑 |
|---|---|---|---|
| 平均测光 | 全画面等权重 | 风景、均匀光照 | 逆光时主体过暗 |
| 中央重点测光 | 中心区域权重高(60-80%) | 人像、中心构图 | 主体偏离中心时失效 |
| 点测光 | 极小区域(1-5%) | 舞台、逆光特写 | 测光点选错直接过曝 |
3.2.1 平均测光
最简单粗暴的方式。把画面分成 M×N 个块,每个块算平均亮度,然后全部加起来取平均。公式长这样:
L_avg = (1 / (M * N)) * Σ Σ L(i, j)
好处是稳定,坏处是容易被大面积背景带偏。比如拍逆光人像,背景天空很亮,平均下来主体就黑了。
3.2.2 中央重点测光
这个模式给画面中心区域更高的权重。我一般用高斯权重或者阶梯权重:
L_weighted = Σ Σ w(i, j) * L(i, j) / Σ Σ w(i, j)
// 中心区域权重设为1.0,边缘设为0.3
// 过渡区域线性插值
人像场景我首选这个模式。但要注意——如果主体不在中心,效果还不如平均测光。
3.2.3 点测光
点测光只取画面中一个极小区域(通常是对焦点附近)。我做过一个舞台项目,演员追光跟着走,点测光跟着对焦点移动,效果非常好。
3.3 目标亮度设定
目标亮度就是「我们希望画面达到多亮」。这个值不是拍脑袋定的,我一般从三个维度考虑:
- 场景类型:室内、室外、夜景、逆光,目标亮度不同
- 用户偏好:有人喜欢亮一点,有人喜欢暗一点
- 传感器特性:不同 sensor 的线性区、信噪比拐点不一样
常用的目标亮度表达方式有两种:
| 方式 | 说明 | 典型值 |
|---|---|---|
| 8-bit 亮度值 | 0-255 范围内的目标值 | 120-140(中等亮度) |
| 直方图百分比 | 目标平均亮度占满量程的百分比 | 50%-60% |
3.4 AE收敛策略
收敛策略解决的是「怎么从当前亮度走到目标亮度」。走快了会 overshoot,走慢了会拖影。我把它分成三个子问题:
3.4.1 误差计算
error = L_target - L_current
// 归一化误差
error_norm = error / L_target
误差的正负决定方向,大小决定步长。
3.4.2 步长控制
步长控制我常用两种策略:
- 固定步长:简单但容易震荡。误差大时调得慢,误差小时又可能 overshoot。
- 自适应步长:误差大时大步快调,误差小时微调。我一般用分段线性函数:
if abs(error_norm) > 0.3:
step = 0.5 // 大步
elif abs(error_norm) > 0.1:
step = 0.2 // 中步
else:
step = 0.05 // 微调
3.4.3 防抖与滞回
这是最容易忽略的点。我遇到过画面在亮暗之间来回跳,就是因为没有加滞回。
3.4.4 曝光参数映射
最后一步是把步长换算成具体的曝光参数。我一般按这个优先级调整:
- 先调曝光时间:不引入噪声,但注意运动模糊
- 再调模拟增益:噪声可控范围内
- 最后调数字增益:会放大噪声,尽量少用
// 伪代码示例
if need_brighter:
if exposure_time < max_exposure:
exposure_time *= (1 + step)
elif analog_gain < max_analog_gain:
analog_gain *= (1 + step)
else:
digital_gain *= (1 + step)