3. AE基础
任务目标
实现一个基于平均亮度的自动曝光(AE)算法:输入一帧图像的亮度统计信息(平均亮度),通过迭代调整曝光时间或模拟增益,使图像平均亮度收敛到设定的目标值。本案例聚焦算法核心逻辑,不涉及具体传感器驱动或ISP管线。
核心原理
AE控制本质是一个负反馈闭环。设当前平均亮度为 Y_avg,目标亮度为 Y_target,当前总曝光为 E_current = exposure_time × gain。由于亮度与曝光近似线性关系,可按下式直接计算理想总曝光:
E_target = E_current × (Y_target / Y_avg)
随后将 E_target 按优先级分配给曝光时间和增益:优先调整曝光时间(避免高增益引入噪声),当曝光时间达到上限后,再增加增益。
代码实现
class SimpleAE:
def __init__(self, target_luma=110, min_gain=1.0, max_gain=16.0,
min_exp_time=1.0, max_exp_time=300.0, step_ratio=0.2):
self.target = target_luma
self.gain = 1.0
self.exp_time = 50.0 # 初始曝光时间(ms)
self.min_gain = min_gain
self.max_gain = max_gain
self.min_exp = min_exp_time
self.max_exp = max_exp_time
self.step = step_ratio # 单次最大调整比例,防止过冲
def _clip_exp_time(self, val):
return max(self.min_exp, min(self.max_exp, val))
def _clip_gain(self, val):
return max(self.min_gain, min(self.max_gain, val))
def calculate(self, avg_luma):
"""输入当前帧平均亮度,返回调整后的 (exposure_time, gain)"""
if avg_luma <= 0:
avg_luma = 1e-6 # 避免除零
# 理想总曝光倍数
ratio = self.target / avg_luma
# 限制单步调整幅度,防止闪烁
ratio = max(1.0 - self.step, min(1.0 + self.step, ratio))
# 当前总曝光
cur_total = self.exp_time * self.gain
new_total = cur_total * ratio
# 优先分配曝光时间
new_exp = self._clip_exp_time(new_total / self.gain)
# 若曝光时间已饱和,剩余部分用增益补偿
new_gain = self._clip_gain(new_total / new_exp)
# 若增益也饱和,则重新计算曝光时间(可能被增益上限拉低)
if new_gain >= self.max_gain:
new_exp = self._clip_exp_time(new_total / new_gain)
self.exp_time = new_exp
self.gain = new_gain
return self.exp_time, self.gain
def reset(self):
self.gain = 1.0
self.exp_time = 50.0
运行与验证
使用模拟亮度响应验证收敛性。假设传感器输出亮度与曝光成正比(忽略噪声),用以下脚本测试:
ae = SimpleAE(target_luma=110)
# 模拟:初始场景亮度系数为0.8(即曝光=100时亮度=80)
scene_coef = 0.8
exp, gain = ae.exp_time, ae.gain
for i in range(10):
luma = scene_coef * exp * gain # 模拟传感器输出
exp, gain = ae.calculate(luma)
print(f"iter {i}: luma={luma:.1f}, exp={exp:.1f}, gain={gain:.2f}")
if abs(luma - 110) < 1.0:
print("Converged")
break
预期输出(前几行):
iter 0: luma=40.0, exp=60.0, gain=1.00
iter 1: luma=48.0, exp=72.0, gain=1.00
iter 2: luma=57.6, exp=86.4, gain=1.00
iter 3: luma=69.1, exp=103.7, gain=1.00
iter 4: luma=83.0, exp=110.0, gain=1.00
iter 5: luma=88.0, exp=110.0, gain=1.00
iter 6: luma=88.0, exp=110.0, gain=1.00
由于单步限制为20%,亮度逐步逼近目标。当曝光时间达到上限(300ms)后,算法会自动开始提升增益。验证通过条件:运行上述脚本,亮度值单调趋近目标值110,且曝光时间优先于增益增加(在曝光未饱和时gain保持1.0)。
关键说明
- 代码中
step_ratio=0.2防止单帧调整过大导致画面闪烁,实际项目中可调。 - 若场景亮度极低,曝光时间达到
max_exp_time后,算法自动切换为提升增益,符合AE策略。 - 本实现假设亮度与曝光严格线性,真实传感器存在噪声与非线性,但作为AE收敛核心逻辑已完整。