1. 自动对焦概述:对焦原理、评价函数基础、对焦窗口选择策略

自动对焦,说白了就是让镜头自己找到最清晰的那个位置。你想想看,手机拍照时轻轻一点,画面就清楚了——背后其实是一套算法在拼命算“到底哪张图最锐利”。

我刚开始做这个方向时,以为自动对焦就是找个最大值那么简单。结果第一次在户外强光下调试,镜头来回震荡,死活定不住。嗯,从那以后我才真正开始理解:对焦这件事,远比你想象的要复杂。

1.1 对焦原理:从物理到数字

传统相机靠拧镜头来找焦点。数字相机呢?它靠的是图像传感器上的对比度变化。

核心逻辑就一句话:图像越清晰,相邻像素的亮度差异越大。反过来,图像越模糊,像素之间就越“平”。

对焦的本质:在镜头移动范围内,找到使图像“高频信息”最大的那个位置。

我习惯把这个过程拆成三步:

  1. 移动镜头——步进电机带着镜头从近到远扫一遍
  2. 计算清晰度——每到一个位置,抓一帧图,算一个分数
  3. 寻找峰值——分数最高的位置,就是焦点

听起来简单?但实际项目中,噪声、光照变化、运动物体都会让这个“峰值”变得面目全非。我曾经在一个监控项目里,因为风吹树叶的晃动,对焦曲线出现了三个假峰……那周我几乎每天都在看曲线图。

1.2 评价函数基础:怎么算“清晰”

评价函数(Focus Measure)就是用来给图像清晰度打分的。不同的函数,敏感度、计算量、抗噪能力都不一样。

我个人最常用的三类:

类别 代表算法 核心思路 我常用的场景
梯度类 Tenengrad、Laplacian、Sobel 计算图像边缘的强度 日常拍照、文档扫描
频域类 FFT高频能量、DCT系数 分析图像中高频成分多少 工业检测、显微镜
统计类 方差、熵、自相关 衡量像素值的离散程度 低光照、纹理简单场景

我的经验:梯度类函数在纹理丰富的场景下表现最好,但遇到纯色墙面就抓瞎。频域类更稳定,但计算量大,移动端扛不住。实际项目中我通常先用梯度类做粗搜,再用频域类做精搜——这叫“粗精结合”。

给你看一段最简单的 Tenengrad 实现:

// 简化版 Tenengrad 评价函数
float tenengrad_score(unsigned char* img, int w, int h) {
    float score = 0.0f;
    for (int y = 1; y < h-1; y++) {
        for (int x = 1; x < w-1; x++) {
            // Sobel 水平梯度
            int gx = -img[(y-1)*w + (x-1)] + img[(y-1)*w + (x+1)]
                     -2*img[y*w + (x-1)] + 2*img[y*w + (x+1)]
                     -img[(y+1)*w + (x-1)] + img[(y+1)*w + (x+1)];
            // Sobel 垂直梯度
            int gy = -img[(y-1)*w + (x-1)] -2*img[(y-1)*w + x] - img[(y-1)*w + (x+1)]
                     +img[(y+1)*w + (x-1)] +2*img[(y+1)*w + x] + img[(y+1)*w + (x+1)];
            score += sqrtf(gx*gx + gy*gy);
        }
    }
    return score / (w * h);
}

这段代码我用了很多年。核心就是算每个像素的梯度幅值,然后累加取平均。分数越高,图像越清晰。

注意:评价函数对噪声极其敏感。我曾经在一个夜间监控项目里,直接用原始图像算 Tenengrad,结果噪声被当成“清晰度”放大了三倍。后来加了高斯滤波前置处理,曲线才恢复正常。

1.3 对焦窗口选择策略:别算整张图

为什么不能算整张图?两个原因:

  • 计算量太大——1080p 图像全算一遍,手机功耗扛不住
  • 背景干扰——你只想对焦主体,但背景的纹理可能更丰富,算法会被带偏

所以,我们需要选一个“窗口”,只在这个区域内算清晰度。

我总结了几种常用策略:

策略 做法 优点 缺点
中心固定窗 取图像中心 1/3 区域 简单、稳定 主体不在中心时失效
多区域加权 划分 3x3 或 5x5 网格,中心权重高 兼顾中心和边缘 参数调起来麻烦
人脸/主体检测 先检测主体,再围绕主体开窗 精准、用户体验好 依赖检测算法,有延迟
自适应窗口 根据图像内容动态调整窗口大小和位置 最灵活 实现复杂,容易出 bug

我的建议:项目初期先用中心固定窗,把对焦曲线调平滑了,再上自适应策略。一上来就搞复杂窗口,出了问题你都不知道是窗口的问题还是评价函数的问题。

我曾经在一个扫码枪项目里,用了中心固定窗。结果用户经常扫条码时,条码在画面边缘,对焦全对到背景上去了。后来改成多区域加权,中心权重 0.5,四角各 0.125,问题就解决了。

1.4 本章知识结构

下面这张图,是我自己梳理的自动对焦基础框架。每次带新人时,我都会先让他们看这张图:

自动对焦基础框架 自动对焦基础框架 对焦原理 镜头移动控制 评价函数计算 窗口选择策略 步进电机 / 音圈马达 梯度类 / 频域类 / 统计类 固定窗 / 加权 / 自适应 找到清晰度峰值 → 输出焦点位置 关键思路 1. 对焦 = 搜索 + 评价 + 决策 三个环节的闭环 2. 评价函数和窗口策略决定了搜索的准确性和效率 3. 实际项目中,80% 的问题出在窗口选择和噪声处理上

这张图我每次培训都会用。你注意看:对焦不是单一算法,而是“移动-计算-决策”的闭环。任何一个环节出问题,最终焦点都会偏。

调试小技巧:刚开始调试时,先把窗口固定,只调评价函数。等曲线形状对了,再动窗口。一次只改一个变量——这是我踩过无数坑之后总结出来的铁律。

好了,这一章的内容就到这里。对焦原理、评价函数、窗口策略——这三块是后面所有章节的基石。你先把它们吃透,后面讲爬山算法、激光辅助、多摄融合时,才能跟得上。