3. 搜索策略与爬山算法:全局搜索、爬山算法原理、步长自适应策略、安防场景下的搜索陷阱

调AF算法,说白了就是在找一个最优解。你想想看,镜头要对准目标,焦距要清晰,这本质上就是一个搜索问题。我们得在无数个可能的焦距位置里,找到那个让图像最清晰的点。

这一章,我们就来聊聊搜索策略。我会结合自己在安防项目里踩过的坑,把全局搜索、爬山算法、步长自适应这些概念讲透。

3.1 全局搜索:最笨但最可靠的方法

全局搜索,也叫穷举搜索。它的思路很简单:把整个搜索空间遍历一遍,找到最优值。

在AF里,就是把镜头从最近端推到最远端,每走一步算一次清晰度评价函数值,最后挑出最大值对应的位置。

核心特点:

  • 优点:一定能找到全局最优解,不会陷入局部极值
  • 缺点:速度极慢,不适合实时场景

我记得有一次做固定枪机的调试,场景很简单,背景单一。我直接用全局搜索做对比实验,结果一个完整的对焦周期花了将近3秒。这在安防场景里是完全不能接受的——你想想看,如果有人在画面里走动,3秒后才对上焦,关键信息早丢了。

所以,全局搜索在实际产品里很少单独使用。它更多是作为标定工具或参考基准。比如,我们在实验室里用全局搜索跑一遍,记录下每个位置的清晰度值,然后拿这个数据去验证其他算法的效果。

对比项 全局搜索 爬山算法
搜索速度 慢(遍历全部) 快(定向搜索)
全局最优 保证 不保证
适用场景 标定、离线分析 实时对焦
计算量 大 小

3.2 爬山算法原理:像爬山一样找峰值

爬山算法,名字很形象。你想象一下,你在一个山谷里,目标是爬到最高的山顶。你只能感知脚下的坡度,然后朝着坡度最陡的方向走一步。走一步,停一下,再判断方向,再走一步。直到你发现无论往哪个方向走,高度都在下降,那你就认为到山顶了。

在AF里,这个「高度」就是清晰度评价函数值,「方向」就是镜头的移动方向。

算法流程很简单:

  1. 初始化:镜头停在当前位置,计算清晰度值 F0
  2. 试探:向一个方向移动一步,计算新位置的清晰度值 F1
  3. 判断:
    • 如果 F1 > F0,说明方向正确,继续向该方向移动
    • 如果 F1 < F0,说明方向错了,反向移动
    • 如果 F1 ≈ F0,可能到了平坦区域,需要特殊处理
  4. 重复步骤2-3,直到满足停止条件
// 伪代码:基础爬山算法
int currentPos = getCurrentLensPosition();
float currentSharpness = calcSharpness(currentPos);

while (true) {
    int nextPos = currentPos + stepSize;
    float nextSharpness = calcSharpness(nextPos);
    
    if (nextSharpness > currentSharpness) {
        // 方向正确,继续前进
        currentPos = nextPos;
        currentSharpness = nextSharpness;
    } else {
        // 方向错误,反向搜索
        stepSize = -stepSize;
        nextPos = currentPos + stepSize;
        nextSharpness = calcSharpness(nextPos);
        
        if (nextSharpness > currentSharpness) {
            currentPos = nextPos;
            currentSharpness = nextSharpness;
        } else {
            // 两边都下降,到达峰值
            break;
        }
    }
}

嗯,这里要注意。基础爬山算法有个致命问题:它只能找到局部最优。如果清晰度曲线有多个峰值,它很可能停在第一个峰上,而不是最高的那个。

3.3 步长自适应策略:让算法更聪明

固定步长的爬山算法,就像你闭着眼睛走路,每一步都迈一样大。步子小了,走得慢;步子大了,容易跨过山顶。

我个人习惯用步长自适应策略。核心思路是:根据当前「坡度」动态调整步长。

  • 坡度陡(清晰度变化大):用大步长,快速接近峰值
  • 坡度缓(清晰度变化小):用小步长,精细定位

具体实现方式有很多种,我常用的是比例自适应:

// 步长自适应策略
float adaptiveStep(float currentSharpness, float prevSharpness, float baseStep) {
    float delta = abs(currentSharpness - prevSharpness);
    float ratio = delta / currentSharpness;  // 变化率
    
    if (ratio > 0.1) {
        // 变化剧烈,用大步
        return baseStep * 2;
    } else if (ratio > 0.02) {
        // 中等变化,用正常步长
        return baseStep;
    } else {
        // 变化平缓,用小步精细搜索
        return baseStep * 0.5;
    }
}

我的经验:步长自适应的阈值需要根据镜头特性调整。我一般会在实验室里先跑一遍全行程,记录下清晰度曲线的斜率分布,然后根据统计结果设定阈值。不同镜头的曲线特性差异很大,不能一套参数打天下。

3.4 安防场景下的搜索陷阱

理论讲完了,我们来聊聊实战。安防场景和实验室环境完全是两码事。我踩过的坑,说出来都是泪。

3.4.1 低光照陷阱

低光照下,图像信噪比急剧下降。清晰度评价函数的值会变得非常「毛躁」——相邻位置的清晰度值可能因为噪声而剧烈波动。

这会带来什么问题?

  • 爬山算法会误判方向:明明在往正确的方向走,但噪声让清晰度值突然下降,算法以为到顶了
  • 或者更糟:算法在平坦区域来回震荡,永远停不下来

我曾经在调试一个夜间监控项目时,发现算法在低光照下频繁误触发。后来分析数据才发现,清晰度曲线的信噪比已经低到无法区分真实变化和噪声了。

避坑指南:低光照下,建议先做图像预处理(如降噪),或者改用对噪声更鲁棒的评价函数(如基于梯度的函数)。另外,可以设置一个「最小变化阈值」——只有当清晰度变化超过这个阈值时,才认为方向有效。

3.4.2 运动模糊陷阱

安防场景里,运动物体是常态。行人走过、车辆驶过,都会在画面里留下运动模糊。

运动模糊对AF的影响很隐蔽。它会让清晰度评价函数出现假峰值。比如,一个运动物体刚好在某个位置停了一下,那个位置的清晰度值会突然升高,算法以为找到了焦点,实际上只是运动模糊的干扰。

我记得有一次做交通卡口的项目,算法总是在车辆经过时误对焦。后来分析发现,车辆在画面中短暂停留时,车身的纹理让清晰度值飙升,算法就被「骗」过去了。

解决方案:

  • 引入时域滤波:连续多帧的清晰度值做平均,消除瞬时干扰
  • 增加运动检测:检测到画面中有运动物体时,暂停AF或降低权重
  • 使用多区域评价:不只看全图,而是分区域计算,避免局部运动干扰全局

3.4.3 局部极值陷阱

安防场景的清晰度曲线往往不是单峰的。比如,画面中有玻璃反光、灯光、或者纹理重复的区域,都可能产生多个峰值。

爬山算法一旦陷入局部极值,就再也出不来了。

我的做法是:多起点搜索。在搜索空间里选几个不同的起始位置,分别跑爬山算法,最后取最优的那个结果。虽然增加了计算量,但能有效避免陷入局部极值。

3.5 知识结构图:搜索策略全景

下面这张图,把本章的核心知识点串起来了。你可以看到全局搜索和爬山算法的关系,以及步长自适应和搜索陷阱在整个体系中的位置。

搜索策略与爬山算法知识结构 搜索策略 全局搜索 爬山算法 搜索陷阱 遍历全部位置 保证全局最优 定向搜索 步长自适应 低光照陷阱 运动模糊陷阱 全局搜索 → 标定/离线分析工具 爬山算法 → 实时对焦 + 多起点搜索 低光照:降噪 + 最小变化阈值 运动模糊:时域滤波 + 运动检测 局部极值:多起点搜索 核心原则:速度与鲁棒性的平衡

3.6 本章小结

搜索策略是AF算法的骨架。全局搜索可靠但慢,爬山算法快但容易陷入局部最优。步长自适应是让爬山算法更聪明的关键技巧。

但真正决定算法能不能用的,是你对安防场景的理解。低光照、运动模糊、局部极值——这些陷阱在实验室里很难复现,但在现场一定会遇到。

我个人建议,在开发阶段就引入这些场景的模拟数据,而不是等到现场调试才发现问题。嗯,经验之谈,希望对你有帮助。