第二章:3A算法基础理论——AE、AWB、AF的核心原理
各位同学,欢迎来到第二章。这一章我们不讲代码,也不谈平台移植,先把3A算法的“根”扎牢。
自动曝光(AE)、自动白平衡(AWB)、自动对焦(AF),这三个模块在ISP(图像信号处理)流水线里是铁三角。我见过不少工程师,调参调得飞起,但问起测光原理、色温估计、对焦检测的区别,反而说不清楚。嗯,这其实很危险——算法移植时,一旦底层逻辑变了,你连问题出在哪都找不到。
所以今天,我们就把这三兄弟的底裤扒干净。
2.1 自动曝光(AE):测光原理与目标亮度
AE 要解决什么问题?说白了就一句话:让图像不暗不亮,细节都在。但现实场景的亮度范围动辄 100dB 以上,而 sensor 的动态范围可能只有 60-70dB。怎么取舍?这就是测光的艺术。
2.1.1 测光模式
我个人习惯把测光模式分成四类,你在不同场景下得选对武器:
| 测光模式 | 原理简述 | 适用场景 | 我踩过的坑 |
|---|---|---|---|
| 全局测光 | 全图像素亮度取平均 | 光照均匀的户外 | 逆光时人脸全黑 |
| 中央重点 | 中心区域权重更高 | 人像、会议 | 主体不在中心就翻车 |
| 点测光 | 只取画面 1%-5% 区域 | 舞台、逆光特写 | 对焦点选错直接过曝 |
| 多区测光 | 画面分区独立测光+AI融合 | 复杂光场景 | 算力开销大,低端芯片扛不住 |
2.1.2 目标亮度与 18% 灰
你想想看,为什么相机拍一张白纸和一张黑纸,出来的亮度不一样?因为 AE 的参考标准是 18% 中性灰。这是人眼感知的中等亮度点,也是整个曝光控制的锚点。
实际算法里,我们会把测光得到的亮度值映射到目标亮度区间。比如:
// 伪代码:AE 目标亮度调整
target_luminance = 128; // 8bit 图像,18% 灰对应约 128
current_luminance = get_avg_luminance(frame);
gain = target_luminance / (current_luminance + 1e-6);
// 限制增益范围,防止震荡
gain = clamp(gain, 0.5, 4.0);
apply_exposure_gain(gain);
2.2 自动白平衡(AWB):色温估计与颜色校正
AWB 的核心任务,就是让白色物体在任何色温下都看起来是白的。听起来简单?其实不然。人眼有色彩恒常性,但 sensor 没有——它在日光灯下发绿,在白炽灯下发黄。
2.2.1 色温估计方法
我做过好几个 AWB 方案,最常用的就两种:
- 灰世界法(Gray World):假设场景中所有颜色的平均是灰色。如果 R/G/B 均值不相等,就调整增益让它们相等。
- 完美反射法(Perfect Reflector):找到画面中最亮的像素,假设它是白色,然后以此为基准校正。
但说实话,这两种方法单独用都有问题。灰世界法遇到大面积单色场景(比如蓝天、绿草地)会翻车;完美反射法如果场景里没有真正的白色物体,也会偏色。
2.2.2 颜色校正矩阵(CCM)
色温估计完了,接下来就是校正。校正不是简单调 RGB 增益,而是要用一个 3x3 的矩阵:
// CCM 校正示例
[R'] [ 1.8 -0.5 -0.3 ] [R]
[G'] = [-0.2 1.6 -0.4 ] [G]
[B'] [-0.1 -0.3 1.4 ] [B]
这个矩阵的作用是消除 sensor 的串扰和色偏。不同色温下,CCM 矩阵也不同。我一般会准备 5-8 组 CCM,对应 2500K、4000K、5500K、6500K、8000K 等典型色温点,然后线性插值。
| 色温点 | 场景举例 | R 增益 | G 增益 | B 增益 |
|---|---|---|---|---|
| 2500K | 白炽灯 | 1.8 | 1.0 | 1.2 |
| 5500K | 日光 | 1.0 | 1.0 | 1.0 |
| 8000K | 阴天 | 1.1 | 1.0 | 1.6 |
2.3 自动对焦(AF):对比度检测与相位检测
AF 的目标很直接——让图像最清晰。但怎么定义“清晰”?这就引出了两种主流方案。
2.3.1 对比度检测(CDAF)
CDAF 的原理很简单:图像越清晰,边缘越锐利,相邻像素的差异越大。我们用 Sobel 或 Laplacian 算子计算图像的梯度值,然后找梯度最大的镜头位置。
// CDAF 清晰度评价函数(Tenengrad)
float calc_sharpness(uint8_t* frame, int w, int h) {
float sum = 0;
for (int y = 1; y < h-1; y++) {
for (int x = 1; x < w-1; x++) {
int gx = frame[(y-1)*w+x+1] + 2*frame[y*w+x+1] + frame[(y+1)*w+x+1]
- frame[(y-1)*w+x-1] - 2*frame[y*w+x-1] - frame[(y+1)*w+x-1];
int gy = frame[(y+1)*w+x-1] + 2*frame[(y+1)*w+x] + frame[(y+1)*w+x+1]
- frame[(y-1)*w+x-1] - 2*frame[(y-1)*w+x] - frame[(y-1)*w+x+1];
sum += gx*gx + gy*gy;
}
}
return sum / (w * h);
}
CDAF 的优点是精度高,但缺点也很明显——慢。它需要镜头走完整个行程才能找到峰值,俗称“拉风箱”。
2.3.2 相位检测(PDAF)
PDAF 就聪明多了。它在 sensor 上埋了一排特殊的相位检测像素,左右各看一半画面。如果左右图像对齐,说明合焦;如果错位,就知道该往哪个方向推镜头。
相位差与离焦量的关系:
// PDAF 相位差计算
phase_diff = left_pixel - right_pixel;
// 正数表示前焦,负数表示后焦
lens_step = phase_diff * calibration_factor;
2.3.3 混合对焦策略
在实际项目中,我很少只用一种方案。下面这个表格总结了不同场景下的选择:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 静态拍照 | CDAF | 精度优先,不差那几百毫秒 |
| 视频追焦 | PDAF + CDAF | 既要速度又要精度 |
| 低光环境 | PDAF | CDAF 在暗光下梯度太弱,容易误判 |
| 快速抓拍 | PDAF | 一帧定焦,不拖泥带水 |
好了,这一章的内容就是这些。AE、AWB、AF 三个模块的原理,是后续所有移植和优化工作的基础。你把这些吃透了,后面讲平台适配、算力优化、流水线并行时,才能跟得上节奏。