4. 曝光控制基础:曝光三角、直方图与自动曝光算法
曝光控制,说白了就是决定「多少光进到传感器里」。我刚开始做图像处理时,总觉得曝光是相机的事,跟我算法工程师没关系。直到有一次在强光下做车牌识别,过曝导致车牌号全白——嗯,从那以后我再也不敢轻视曝光控制了。
4.1 曝光三角:光圈、快门、ISO
这三个参数共同决定最终图像的亮度。它们的关系很简单:曝光量 = 光圈 × 快门 × ISO。但每个参数的影响远不止亮度这么简单。
| 参数 | 物理含义 | 对图像的影响 | 典型值范围 |
|---|---|---|---|
| 光圈 (Aperture) | 镜头通光孔径大小 | 控制进光量 + 景深 | f/1.4 ~ f/22 |
| 快门 (Shutter) | 传感器感光时间 | 控制进光量 + 运动模糊 | 1/8000s ~ 30s |
| ISO | 传感器信号放大增益 | 控制亮度 + 噪声水平 | 100 ~ 12800 |
核心原则:优先调光圈和快门,ISO 是最后手段。因为 ISO 本质是放大信号,同时也会放大噪声。
4.1.1 光圈:不只是通光量
光圈值用 f-number 表示,比如 f/2.8。数值越小,光圈越大,进光越多。但有个坑——光圈越大,景深越浅。
我在做文档扫描 App 时遇到过:用 f/1.8 大光圈拍文档,中间清晰,边缘模糊。用户投诉说「拍出来字是花的」。后来我建议固定到 f/5.6 ~ f/8,问题就解决了。
避坑指南:场景自适应调参时,如果检测到文本/条码场景,建议强制收小光圈(f/5.6 以上),保证全画面清晰度。
4.1.2 快门:冻结运动还是记录轨迹
快门速度决定了运动物体在画面中的表现。1/1000s 可以冻结飞鸟,1/30s 就会拍出拖影。
我个人的经验是:手持拍摄的安全快门是 1/焦距。比如用 50mm 镜头,快门至少 1/50s。低于这个值,手抖就会导致模糊。
但自动曝光算法不会考虑「手抖」——它只算亮度。所以很多手机在暗光下会降到 1/10s,结果拍出来全是糊的。这就是为什么 OIS(光学防抖)在暗光下那么重要。
4.1.3 ISO:噪声的源头
ISO 是传感器信号的放大倍数。ISO 100 是原生感光度,噪声最小。ISO 3200 以上,暗部就会出现明显的彩色噪点。
我记得有一次做夜景模式,算法把 ISO 拉到 6400,结果图像全是「雪花」。后来我们改用多帧合成 + 低 ISO,效果反而更好。
注意:ISO 不是物理进光量,而是电子放大。放大信号的同时,也放大了传感器本身的暗电流噪声和读出噪声。高 ISO 图像后期降噪会损失细节。
4.2 直方图分析:曝光质量的「体检报告」
直方图是图像亮度分布的统计图。横轴是亮度(0~255),纵轴是像素数量。我每次拿到一张图,第一件事就是看直方图——它比人眼更客观。
4.2.1 直方图的三种典型形态
- 正常曝光:直方图分布均匀,左右两端都有少量像素,中间饱满。
- 过曝:像素集中在右侧(高亮区),右端「撞墙」——细节丢失。
- 欠曝:像素集中在左侧(暗区),左端「撞墙」——暗部死黑。
你想想看,如果直方图两端都撞墙,说明这张图动态范围已经超出传感器能力了。这时候单纯调曝光没用,得用 HDR 或多帧合成。
4.2.2 直方图在自动曝光中的应用
自动曝光算法会实时计算直方图的统计特征:
// 伪代码:基于直方图的曝光评估
float computeExposureScore(Histogram hist) {
float mean = hist.mean(); // 平均亮度
float std = hist.stddev(); // 标准差
float clipLeft = hist.cdf(10); // 暗部裁剪比例
float clipRight = hist.cdf(245); // 亮部裁剪比例
// 目标:平均亮度在 100~150,裁剪比例 < 1%
if (mean < 100) return UNDER_EXPOSED;
if (mean > 150) return OVER_EXPOSED;
if (clipLeft > 0.01 || clipRight > 0.01) return CLIPPING;
return GOOD;
}
关键指标:自动曝光算法通常以「平均亮度落在 100~150 区间」为目标。但场景自适应系统会根据场景类型调整这个目标值——比如夜景可以偏暗(目标 80),雪景可以偏亮(目标 180)。
4.3 自动曝光算法原理
自动曝光(AE)本质上是一个闭环控制系统。传感器测量当前亮度,算法计算偏差,然后调整曝光参数,直到亮度达到目标值。
4.3.1 经典 AE 算法:爬山法
爬山法是最基础的 AE 算法。它像爬山一样,一步步调整参数,直到「山顶」(目标亮度)。
- 测量当前图像的平均亮度 Y_cur
- 计算与目标亮度 Y_target 的差值 ΔY = Y_target - Y_cur
- 根据 ΔY 调整曝光时间或增益
- 重复 1~3,直到 |ΔY| < 阈值
但爬山法有个问题——容易在明暗交替场景中震荡。比如你从室内走到室外,亮度突变,算法会来回调整,画面一闪一闪的。
4.3.2 改进算法:加权区域测光
为了解决全局平均亮度不准确的问题,业界引入了区域加权:
| 测光模式 | 权重分布 | 适用场景 |
|---|---|---|
| 中央重点测光 | 画面中央 60% 权重 | 人像、主体在中央 |
| 点测光 | 仅中心 2~5% 区域 | 逆光、舞台 |
| 矩阵/评价测光 | 全画面分区加权 | 通用场景 |
我个人习惯用矩阵测光 + 人脸检测加权。如果画面中检测到人脸,人脸区域的权重提升到 60% 以上。这样即使背景很亮,人脸也不会欠曝。
4.3.3 现代 AE 算法:场景自适应曝光
这是本课程的核心内容之一。场景自适应 AE 不再是简单的「亮度闭环」,而是结合场景理解:
// 场景自适应 AE 决策流程
SceneType detectScene(Image frame) {
if (hasFace(frame)) return PORTRAIT;
if (isNight(frame)) return NIGHT;
if (isBacklit(frame)) return BACKLIT;
return GENERAL;
}
ExposureParams computeAE(SceneType scene, Histogram hist) {
switch(scene) {
case PORTRAIT:
targetBrightness = 130; // 人脸偏亮
maxISO = 800; // 控制噪声
break;
case NIGHT:
targetBrightness = 80; // 保留暗部氛围
maxShutter = 1/15; // 防手抖
break;
case BACKLIT:
targetBrightness = 120; // 提亮主体
enableHDR = true; // 开启 HDR
break;
default:
targetBrightness = 110;
}
// ... 执行 PID 控制
}
经验之谈:场景自适应 AE 的关键不是算法多复杂,而是场景分类的准确率。我见过很多方案,场景分类准确率只有 70%,结果 AE 反而比通用算法更差。先做好场景分类,再优化 AE 参数。
4.4 曝光控制知识体系
下面这张图总结了本章的核心知识结构:
4.5 本章小结
曝光控制是图像质量的基石。光圈、快门、ISO 三者相互制约,没有「万能参数」。直方图是客观评价曝光质量的有效工具。自动曝光算法从简单的爬山法发展到场景自适应 AE,核心是让机器理解「当前在拍什么」。
我曾经在一个项目中踩过坑:用固定目标亮度 120 去拍雪景,结果雪地变成灰色。后来加入场景分类,雪景目标亮度调到 180,才还原出「雪白」的效果。嗯,这就是场景自适应的价值。