ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

自动聚焦算法解析:从评价函数到搜索策略的完整指南

自动聚焦算法解析:从评价函数到搜索策略的完整指南 1. 自动聚焦到底在解决什么问题1.1 为什么“对上焦”本质上是个数学问题聊AF自动聚焦算法之前,得先说清楚一件事:对焦这个动作,在物理世界里看起来是拧镜头环、推镜组,但落到算法工程师手里,它就是一个典型的搜索优化问题。镜头从最近端移动到最远端,这条路径上存在无数个位置,哪一点拍出来的图像最清晰,我们需要让马达去找到那一点——这就是自动聚焦的全部核心。你可能会说,清晰不清晰用人眼一看就知道了,为什么还要搞一堆算法?问题在于,人眼判断是主观的、滞后的,而且摄像头取景画面在验证前无法预知某个镜头位置的真实清晰度。机器不可能像人一样站在相机后面“感觉”画面清了,它必须用某个量化指标来打分:当前位置下画面的清晰度是多少分。这个分数一般叫对焦评价函数值(Focus Value)。打分函数选得好不好、搜索策略跑得快不快,直接决定了一个自动聚焦系统的成败。所以自动聚焦系统,本质上要做三件事:第一,找到能准确反映图像清晰度的评价函数;第二,设计一套能快速逼近最佳位置的搜索策略;第三,合理选择聚焦区域,避免背景干扰。这三件事互相耦合,但任何一件做得不到位,整体体验都会崩。很多入门者拿到AF模块只看马达驱动,不关心评价函数,结果发现镜头来回转动却永远对不准,就是这个原因。这个领域适合谁研究?一个是嵌入式图像工程师,需要在IPC、无人机、内窥镜这些设备上跑实时对焦;另一个是做ISP或摄像头模组算法的人,处理PDAF校准、AF曲线补偿等工作。即便你是做手机拍照APP上层应用的,理解底层AF逻辑也能帮你更好地排查对焦异常问题。这篇文章我就从这三件事展开,把AF自动聚焦从原理到落地的完整链路梳理一遍。1.2 三大主流AF方案的演进:CDAF、PDAF、混合对焦自动聚焦方案这么多年来演化出好几条路线,但目前主流就三种:对比度检测对焦(CDAF)、相位检测对焦(PDAF),以及把两者结合的混合对焦(Hybrid AF)。对比度检测是历史最悠久的方案。它的逻辑很直观:画面越清晰,边缘越锐利,相邻像素间的灰度变化越剧烈,整体图像的高频分量就越强。AF控制器通过连续移动镜头并计算图像的清晰度分数,找到分数最大的位置。这样做的好处是不需要额外硬件,任何一颗普通摄像头CMOS都能用,成本极低;代价是它是一个搜索过程,必须“先动一下,再判断”,所以速度天生就慢,而且遇到画面里没有明显边缘纹理的纯色区域时,评价函数会陷入平坦状态,很难判断该往哪走。相位检测则是用硬件把问题变成“一次性计算”。在CMOS传感器上内嵌专门的对焦像素,这些像素通过微透镜遮挡将光线分为左右两路(或上下两路),从而同时感知两个方向的光。当画面失焦时,两路图像会产生相位差,这个差值直接决定了当前的离焦方向和离焦量。控制器拿到这个数据后,可以一步把镜头推到目标位置附近,速度非常快。代价是传感器上要集成专用像素,挤占成像像素的面积,影响画质;低光环境下对焦像素信噪比下降,精度也会打折扣。混合对焦的做法则很务实:PDAF快速粗调到大致合焦范围,再由CDAF精细扫描锁定峰值位置。手机、无反相机基本都用这种方式来兼顾速度与精度。理解了这三条路线,后面再聊算法细节时就不会一头雾水——每个方案的算法设计重心完全不同。2. 核心算法拆解:评价函数、搜索策略、窗口设计2.1 怎么判断画面“清楚”——对焦评价函数对焦评价函数是整个AF系统里最底层、最核心的一块。它的作用是输入一帧图像,输出一个数值,反映当前画面的清晰程度。这个函数的设计要求是:在正确合焦位置附近要有明显峰值,离焦时单调下降,并且对噪声不过于敏感。常用的评价函数大概能分成几类。第一类基于梯度,代表是Tenengrad函数和Sobel算子,先用Sobel算子计算图像在水平和垂直方向的梯度,再求梯度的平方和作为分数。图像越清楚,边缘越陡峭,梯度幅值自然越大。第二类基于拉普拉斯算子,直接求图像的二阶导数,它对高通成分更敏感,锐利边缘响应很强,但也更容易被噪声干扰。第三类基于统计特性,比如灰度方差函数、信息熵函数。灰度方差衡量像素灰度的离散程度,图像越清晰,灰度分布越分散,方差越大;信息熵则从信息量角度衡量细节丰富度。在实际工程中,我见过最多的是带阈值的改进型Tenengrad。为什么加阈值?因为纯梯度响应会把噪声算进去,尤其是在暗光环境下,随机噪声产生的梯度值可能比真实边缘还大。针对这一点,常见的做法是设定一个梯度阈值,只有超过阈值的像素梯度才纳入累加运算。这个阈值可以用固定值,也可以根据整帧图像的噪声水平自适应调整。提一句:评价函数不是越复杂越好。在嵌入式平台上一帧1080p图像全图算拉普拉斯,耗时可能超过20毫秒,而AF通常要求控制在几十毫秒内完成一次评价计算。很多时候把图像先做一次下采样,再在缩小后的图上算清晰度,效果并不会差太多,但速度能提升好几倍。这里还涉及一个关键概念:单峰性。评价函数曲线在整个聚焦范围内应该是单峰的,也就是只有一个最大值。如果因为场景纹理周期性强(比如铁丝网、栅栏、条纹布料)导致曲线出现多个波峰,搜索算法很容易陷入局部极值。遇到这种场景,AI人眼都容易对错位置,更别说机器了。2.2 怎么移动镜头——搜索策略与步进控制有了评价函数,接下来要解决的是“镜头怎么走”的问题。最常见的搜索策略是全区间扫描和爬山法。全区间扫描是笨办法但绝对可靠:把镜头从近端到远端按固定步长走一遍,记录每个位置的评价函数值,最终选择分数最大的点,再让马达走回那个位置。这种方法的优点是永远不会漏掉全局峰值,缺点是速度慢。假设对焦范围分成50步,每走一步需要5毫秒计算时间,来回跑一趟就要几百毫秒,放在连续对焦场景下根本没法用。爬山法的思路类似于“沿着山坡往上走”。算法先让镜头朝一个方向移动一步,比较前后两个位置的评价值;如果值在变大,说明方向正确,继续走;如果值开始下降,说明走过头了,就掉头以更小步长往回追;反复迭代直到找到峰值。这个算法问题在于,如果起步方向就错了,就得花额外时间调头。更麻烦的是在评价曲线出现局部极值时,爬山法极易被困在错误的峰上。工业界实践中通常会采用“粗搜细搜”的两步策略:第一步,用较大步长快速扫描全区间,大致锁定峰值所在区域;第二步,在小范围内用爬山法或二分搜索精确寻找峰值位置。这个思路本质上是在“探索”和“利用”之间取平衡——先用大概率覆盖全范围的粗扫避免陷入局部极值,再用小步长细搜保证精度。还有一类基于模型的方法是值得关注的:当已知镜头位置与对焦评价值之间满足类似二次曲线的模型时,可以对采样点做曲线拟合,直接估计峰值位置。这样做的好处是,只需要3到5个采样点就能算出峰位,速度极快。缺点是曲线模型依赖场景特性,普适性不够强。很多手机方案在微距场景下会切换到拟合模式,因为在近距离处景深极浅,评价值曲线非常尖锐,粗扫的步长稍大就跳过峰值了。2.3 聚焦区域怎么选——窗口与ROI策略第三个核心模块是聚焦区域选择。全图画面的清晰度评价,听起来合理,实际用起来却很糟糕。原因在于,场景里往往同时存在前景和背景,背景占画面面积大,它的清晰度会把真实主体淹没。举个例子,你拿着手机拍一朵花,背景是虚化掉的草坪。整幅画面里草坪占了70%,模糊的草坪没有高频细节,分数很低;花朵只占画面中间30%,但花朵清晰时它的边缘响应很强。如果对全图求平均梯度,花朵的贡献会被稀释,导致评价函数峰值不明显。假如背景里恰有树叶被风吹动,这部分区域的高频响应还会不断变化,直接干扰对焦稳定性。因此绝大多数AF算法都会设置ROI区域,常见做法是中央矩形框,权重最大,四周权重降低。高级一些的方案会结合人脸检测、人眼检测或者主体跟踪算法,把ROI框自动移动到目标主体上。还有一个折中策略是分块评价:把画面均分成多个小区域,各自独立计算清晰度分数,再按照不同权重融合成总分数。权重分布可以通过场景识别来动态调整——检测到人脸时提高人脸区块权重,检测到风景时提高中景区块权重。窗口的选择还要考虑噪声水平。ROI越小,参与统计的像素越少,评价值的抖动就越明显。在暗光环境下尤其如此。我自己处理过一个问题:AF在正常光线下工作良好,一进暗光就开始不停呼吸。用图像工具查看评价函数曲线后发现,ROI内的评价值随时间产生严重抖动,同一个镜头位置在不同帧算出的清晰度分数差异很大。后来把ROI从中央10%扩大到30%,又加了多帧时域平滑,问题立刻缓解了。原因无他——统计样本增加了,方差自然下降。3. 从原理到落地:一个完整AF模块的实操记录3.1 硬件平台与整体架构在讲具体实现之前,先交代下我常用的一个入门参考平台:一颗RK3588或海思3516系列主控,搭配索尼IMX系列传感器,镜头是音圈马达(VCM)。VCM的驱动方式一般通过PWM控制,步进位置通过DAC或者I2C写入马达驱动IC。整个过程里,ISP负责输出RAW或者YUV帧数据,主控上跑AF算法模块。AF模块的架构其实不复杂,关键是一条闭环反馈链路:马达移动到某个位置,传感器曝光得到一帧图像,图像经过ISP处理后送到AF算法模块,计算出评价值,再由控制策略判断下一步往哪个方向走多少步,然后继续驱动马达。这条链路可以用一个死循环来概括,内部不同的状态机来管理当前处于粗扫、细扫还是稳定跟踪阶段。一个关键点是:AF算法拿到的输入图像最好是不经过太多后处理的原始帧。如果拿到的是算法美化过的磨皮图像,边缘都被抹掉了,评价函数值会严重失真。很多测试时遇到的问题都是色彩滤镜、降噪、强锐化叠加导致的。所以有条件的话,建议从ISP的3A统计通道获取单独的统计数据,而不是从编码后的视频流里截图。3.2 评价函数的工程实现细节工程实现上,评价函数用的是带阈值的Tenengrad,核心代码结构大致是这个样子:int compute_focus_value(unsigned char *img, int width, int height, int stride) { int gx, gy, mag; int sum 0; int threshold 24; for (int y 1; y height - 1; y) { for (int x 1; x width - 1; x) { gx -img[(y-1)*stride x-1] - 2*img[y*stride x-1] - img[(y1)*stride x-1] img[(y-1)*stride x1] 2*img[y*stride x1] img[(y1)*stride x1]; gy -img[(y-1)*stride x-1] - 2*img[(y-1)*stride x] - img[(y-1)*stride x1] img[(y1)*stride x-1] 2*img[(y1)*stride x] img[(y1)*stride x1]; mag (gx * gx gy * gy) 8; if (mag threshold) { sum mag; } } } return sum; }注意几个工程细节:图像数据要是灰度图,彩色图像先做灰度转换;Sobel核的系数2是为了突出中心像素的梯度响应;求和过程中用移位替代除法,避免在嵌入式平台上跑浮点。这段代码处理640x480的灰度图,在1GHz左右的CPU上大约需要8到10毫秒,对于AF流程够用了。如果你算整幅图觉得太慢,可以考虑用隔行采样:每两行取一行,每两列取一列,计算量降到原来的四分之一,评价值曲线形态基本不受影响,只是绝对值变小。但这招在画面主体很细的情况下要慎用——细纹理在降采样后可能直接消失,峰值被抹平。关于ROI,我的常用做法是支持配置多个矩形区域,每个区域有自己的权重,最后做加权求和:int total_score 0; int total_weight 0; for (int i 0; i roi_count; i) { int score compute_focus_value_in_roi(roi[i]); total_score score * roi[i].weight; total_weight roi[i].weight; } int focus_value total_score / total_weight;这样做的好处是,上层策略可以根据场景动态调整ROI配置——检测到人脸时把人脸框权重提到0.7,否则用默认中央权重。整个评价模块对外只暴露一个接口,策略层完全不关心内部实现。3.3 搜索策略的参数调优搜索策略这块,我用的是一套状态机:IDLE → SEARCH_COARSE → SEARCH_FINE → LOCKED → IDLE。粗扫阶段把整个行程按大步长分成N段,逐段采样;细扫阶段在小范围用爬山法精确定位;最后进入锁定状态做窗口监测,一旦评价值连续下降超过阈值,认为场景变化了,重新触发对焦。粗扫步长的设定跟镜头行程和对焦精度相关。一个典型的VCM行程是200步左右,如果允许粗扫耗时200毫秒,那步长大约就是10步。在步长设定上有一个基础原则:粗扫步长不能大于景深对应的镜头位移量,否则可能会跨过整个清晰度峰值区域。具体来说,景深越浅——比如微距场景或光圈全开时——粗扫步长要越小。做过微距拍摄的人应该深有体会,焦点只移动一丁点,画面就糊了。细扫阶段采用递减步长,是一个很实用的技巧。进入细扫时,先以粗扫步长的四分之一作为搜索步长找到大致峰值;确定方向后,再减半步长做精确峰值搜索;如果评价值抖动明显,可以再减半一次。这样总共经历三次步长递减,精度提高的同时不会让算法因为步长太小被困在局部噪声中。我调这个状态机时遇到过一个很典型的参数问题:细扫步长减到过小时,马达实际上走不动——音圈马达存在物理滞后和死区,你写入一个过小的步进值,镜头可能纹丝不动或者抖动一下又弹回去。当时测出来的现象是,画面里偶尔出现轻微呼吸感,小步长修正时还会偶尔往错误方向跑。排查方法很简单:在调测界面里手动输入不同步长,用仪器(或人眼观察固定测试图卡)确认电机真实位移情况,再反推代码里逻辑步长和实际物理位移的对应关系。最终结论是逻辑上2步以下几乎无位移,我必须把搜索步长的最小值限制在4步以上。这一步排查花了我接近一天时间,分享出来是想提醒你,算法细节再完美,也要适配物理硬件的真实特性。4. 实战中避不开的问题与排查思路4.1 反复振荡、来回找焦点怎么办自动聚焦最常见的问题之一,就是镜头像呼吸一样来回抽动,永远稳定不下来。遇到这种情况,我习惯先看评价函数曲线。如果是峰值区域比较平坦,也就是所谓的平顶曲线,可能原因是对焦评价函数对纵深变化不敏感。常见于广角镜头拍远景,景深极大,镜头往前往后移动一段距离,清晰度分数变化都不明显。这时即使锁定的位置稍微偏离真实峰值,人眼看也不糊,系统却因为杂讯一直在微调。解决思路是放大评价函数的峰值对比度,常用的手段包括加高通滤波增强边缘,或者改用拉普拉斯算子这类对高频更敏感的评价函数。另外,ROI内如果存在高动态区域(比如风吹动的树叶、闪烁的灯光),评价值忽高忽低,也会持续触发重新对焦。解决方式是增加多帧平滑,不要对单帧的分数变化立刻抠扳机触发对焦。如果是马达在真实机械层面来回反弹,要检查控制回路。VCM驱动电路里如果存在零位不准确或回程差,镜头在到达指令位置后并没有停稳,传感器却已经曝光了,评价值自然会包含运动模糊成分。再往深一层看,可能是PID闭环控制的参数过冲。这种物理层面的振荡,靠修改AF算法很难根治,得回头调马达驱动参数。4.2 低对比度场景下合不上焦怎么办拍白墙、天空、纯色桌面时,评价函数曲线基本上是一条平平的直线,没有明显的峰,搜索算法根本无从下手。这是CDAF方案的天生短板。工程上有几条常用的补救路径。第一条是切换ROI策略,把评价区域从全局改为中央区域,因为很多“看似无纹理”的场景里有细微的灰尘、污渍、光照渐变,中央区域或许能捕捉到微弱的梯度信息,虽然信号强度低但足够产生一个微小峰值。第二条是引入主动辅助光源或主动测距。比如在一些IPC摄像头上会配备红外补光灯,光照不足时用红外照明来增加纹理对比。有ToF或激光测距模块的设备,则直接读取距离信息驱动镜头到对应位置,不需要依赖图像内容。第三条路是跑一次背景纹理统计,如果整帧图像梯度能量低于阈值,直接判定为“低纹理场景”,AF模块跳过一个扫描周期,保持上一次对焦位置不变。这个方法看起来有点赖皮,但现实中很有效。你在办公室用会议摄像头时,镜头对着白墙,它能保持对焦不乱动,比反复找焦点强得多。4.3 运动目标追焦过程中的拖影与失焦追焦是另一个大坑,尤其拍摄跑步、球类运动时。传统AF算法天然是延迟的——它必须基于当前帧确定镜头下一步动作,但从算出结果到镜头移动到位的这段时间里,目标早就跑了。除非目标移动方向有规律可循,否则追焦永远会滞后于目标。工程上提高追焦能力的常用手段首先是提高对焦帧率,把AF评价线程和驱动线程分离,评价计算尽量用最新的帧,减少从曝光到驱动的链路延迟。其次是结合运动预测,利用前几帧目标在图像中的位置变化,外推出当前帧的目标位置,再在这个位置上去评价清晰度。更进一步的做法是计算目标在纵向上的运动——如果主体大小在持续变大,说明在朝相机靠近,系统可以提前向近端方向推动镜头。这一类逻辑在监控场景下特别重要,门禁摄像头前的人快步走过,如果每次都用目标出现后的第一帧去对焦,必然拍糊。另外有一种取巧但很实用的做法:在追焦模式下,允许评价帧不等同于出图帧。也就是说,用低分辨率、高帧率的预览流跑AF算法,用高分辨率帧做最终出图,两条链路并行。低分辨率帧计算量小、帧率高,能让AF响应更快。代价是需要算法对低分辨率图像和最终成像的对焦位置做标定校准,不然会出现“预览清晰、出图发糊”的偏差。5. 方案选型与未来方向5.1 不同设备上的AF方案怎么选选AF方案,没有谁绝对好谁绝对差,关键要看产品定位和成本约束。手机摄像头现在的标配基本是PDAFCDAF混合对焦。中高端机型还会上全像素双核对焦(也就是2x2OCL),每个像素都能做相位检测,对焦覆盖范围几乎全画面。这样的方案对马达精度和Sensor端校准要求极高,供应链和调校门槛都很高。如果只是做入门级硬件,用一颗普通OV传感器加CDAF烧完一套对焦算法,也能在产品参数表上写“自动对焦”。但用户体验的差异很快就会在天桥下对比评测视频里被暴露。安防监控IPC是另一个典型场景。这类产品往往面临长期固定机位、场景光照大幅变化、室内外温差大等挑战。我自己做IPC项目时倾向于用CDAF配合多帧平滑,再加上红外切换时的焦点偏移补偿。红外滤光片切换时,光路长度变化会导致焦点偏移,这是很多IPC画质问题的隐藏源头,需要做IR-cut切换后的AF校准。如果预算允许,加上一颗ToF模块来辅助低照度对焦会省很多调参精力。无人机和运动相机,追求的是快和稳。起飞的瞬间,地表视角极速变化,如果AF反应太慢,远景会全糊。这类设备用PDAF或者连续对焦的混合方案会更合适。无人机还有个特殊场景:拍远景时物距接近无穷远,对焦马达需要快速定位到远焦端,这依靠锁定远端限位再稍微回退一点来实现,避免撞击机械限位产生噪音和磨损。工业视觉或者显微镜场景又是另一套逻辑。检测物件在载物台上位置相对固定,允许对焦时间更长,但对精度要求极高,普通视频AF的步进分辨率根本不够。工业方案普遍做法是使用高精度直线电机或压电马达,配合全局搜索加曲线拟合,在评价值曲线上做亚像素级峰值定位。5.2 从传统算法到AI辅助对焦这几年AI算法对AF的渗透越来越明显。一个典型方向是端到端的离焦量估计:训练一个深度卷积神经网络,输入一帧图像,直接输出镜头应该往哪个方向移动多少。这类方法的核心在于训练数据的构造——要么用大光圈镜头在不同焦距下采集成对图像,要么利用仿真渲染的方式生成大量数据。和传统拉普拉斯算子或Sobel算子相比,CNN对“模糊”与“清晰”的表征能力更强,尤其在低照度、大光圈背景虚化等复杂场景下优势明显。不过,AI辅助AF在量产落地时还面临几个现实问题:第一,计算资源有限,在IPC上跑一个轻量级分类器可能都要占用整颗NPU,对成本和功耗不友好;第二,可解释性差,一旦在某个边角场景下失灵,很难定位是训练数据覆盖不足还是模型结构问题;第三,标定和验收体系很费劲,传统AF有明确的评价函数曲线和峰值指标,AI方案很难定义统一的量化考核标准。我的个人判断是,未来几年的主流不会是AI完全替代传统AF,而是AI作为“先验信息”输入给传统框架。比如用AI检测场景类别,调整评价函数权重和搜索范围;用AI识别人脸/动物/车辆,动态切换ROI框和追焦策略。传统算法依然负责稳定性和精度,AI负责理解场景和提供语义信息,两者结合既能发挥AI的场景理解能力,又能保持传统算法在工程上的确定性。还有一个有意思的方向是多摄协同。多摄手机上的超广角、广角、长焦三颗摄像头可以在同一时刻采样不同焦段图像,利用不同视角的视差信息辅助主摄对焦。这本质上是把PDAF的相位差原理推广到了双摄系统。虽然目前还是以人眼对焦、智能切摄为主,但未来在竖屏视频拍摄、运动抓拍场景上,多摄协同做主动测距和跟焦的潜力非常大。6. 写在最后的经验碎片做AF算法这些年,踩过的坑比写过的代码多得多。最大的体会是:不要迷信算法复杂度,要解决问题本身。很多团队一上来就上深度学习,但实际痛点可能只是VCM驱动里的回差补偿没做好,或者IR-cut切换后的焦点偏移没校准。把基础链路里的每一个环节量化、观察、调优,往往比换更高级的算法更能提升体验。如果你在做项目时能拿到马达位置的反馈,建议一定把马达位置、评价函数值、帧号这三者记录下来,画成二维曲线来分析。这套可视化方法帮我排查了无数次问题。很多AF异常在代码逻辑里根本看不出来,一画图就原形毕露,比如步进和评价值错位、马达响应滞后、峰值被噪声掩盖,在曲线图上都是一目了然的事。最后分享一个小技巧:在调对焦算法时,准备一张黑白条纹的测试卡,贴在一个有刻度的滑轨上改变物距,同时用脚本把评价值和马达位置同步打印出来。这套调试脚手架,比买再贵的仪器都管用。无论未来AF算法怎么演进,先理解评价函数、搜索策略、ROI设计这三块地基,再去看任何新方案都会觉得通透许多。
返回列表