3、AF核心数据结构:af_tuning_params、af_frame_info、af_output_t 结构体解析

做高通平台AF开发,说白了就是跟这三个结构体打交道。我刚开始接触时也觉得头大,一堆字段不知道先看哪个。后来踩过几次坑才明白——这三个结构体就是AF算法的“输入-处理-输出”管道。

今天咱们就把它们拆开揉碎了讲清楚。

3.1 af_tuning_params:调参的“配方书”

这个结构体存的是静态调优参数。每个模组都有自己的特性,比如VCM的行程、镜头的光学中心偏移量。这些参数在量产前就定好了,运行时基本不改。

我个人习惯把它理解成“菜谱”——告诉你盐放多少、火候多大。AF算法照着这个“菜谱”去算,才能找到准确的对焦位置。

字段名 类型 说明
iMinFocusPos int32_t VCM最小位置(通常是0)
iMaxFocusPos int32_t VCM最大位置(比如1023)
iSearchStep int32_t 粗搜步长,单位是VCM的DAC步
iFineSearchStep int32_t 精搜步长,一般比粗搜小2~4倍
iAFThreshold int32_t AF触发阈值,低于这个值才启动对焦
bEnableLensMove bool 是否允许镜头移动(调试用)

关键点:iSearchStep和iFineSearchStep直接影响对焦速度和精度。步长太大容易错过峰值,步长太小又慢。我一般建议粗搜步长设为VCM全行程的1/20左右,精搜步长再减半。

我的经验:有一次客户反馈对焦慢,查了半天发现是iSearchStep设成了1。你想想看,全行程1023步,每一步都去抓图算对比度,不慢才怪。后来改成50步,速度直接提升5倍。

3.2 af_frame_info:每一帧的“体检报告”

这个结构体是每帧图像的输入信息。AF算法每处理一帧,就要从ISP拿到这些数据。说白了,它就是算法判断“当前画面清不清晰”的依据。

typedef struct {
    uint32_t    frame_id;       // 帧ID,用于同步
    int32_t     af_sharpness;   // 对比度值(核心指标)
    int32_t     af_luminance;   // 亮度值
    int32_t     af_confidence;  // 置信度(0~1000)
    uint32_t    af_zone_count;  // 对焦区域数量
    int32_t     af_zone_sharpness[AF_MAX_ZONES]; // 每个区域的对比度
} af_frame_info_t;

这里我最想强调af_confidence这个字段。很多人只看对比度值,忽略了置信度。我曾经遇到一个场景——画面里有强光斑,对比度值很高,但置信度很低。如果只看对比度,算法会误以为已经合焦了,实际上画面是糊的。

避坑指南:我曾经在调试夜景模式时,发现AF总是停在错误位置。后来打印日志才发现,af_luminance低于某个阈值后,对比度值就不可靠了。所以一定要结合亮度和置信度一起判断,别单看对比度。

3.3 af_output_t:算法的“执行指令”

算法算完了,总得告诉硬件怎么动吧?af_output_t就是干这个的。它告诉VCM驱动:往哪个方向走、走多少步、要不要停下来。

字段名 类型 说明
iTargetPos int32_t 目标位置(VCM DAC值)
iCurrentPos int32_t 当前位置(用于闭环控制)
eState af_state_t AF状态:IDLE/SEARCHING/FOCUSED/FAILED
eDirection af_dir_t 移动方向:NEAR/FAR/NONE
bLensStop bool 是否停止镜头

嗯,这里要注意eState的状态机转换。很多新手直接写死FOCUSED状态,结果画面稍微动一下就重新对焦,来回抽风。正确的做法是:FOCUSED之后要等对比度掉到iAFThreshold以下,才切回SEARCHING。

3.4 三个结构体的协作流程

我把它们的关系画成了下面这张图,你看一眼就明白了:

AF核心数据结构协作流程 af_tuning_params 静态调优参数 (步长/阈值/行程) af_frame_info 每帧输入数据 (对比度/亮度/置信度) af_output_t 算法执行结果 (目标位置/状态/方向) 读取 每帧输入 输出 AF搜索算法 (爬山/全搜/混合) 反馈(闭环) 💡 协作要点 1. af_tuning_params 在初始化时加载,运行中一般不修改 2. af_frame_info 每帧更新,算法根据它决定下一步动作 3. af_output_t 驱动VCM移动,移动后等待下一帧反馈

3.5 实际开发中的使用套路

讲完理论,咱们看看代码里怎么用。我贴一段简化版的AF处理流程:

// 1. 初始化时加载调参
af_tuning_params_t tuning = {
    .iMinFocusPos = 0,
    .iMaxFocusPos = 1023,
    .iSearchStep = 50,
    .iFineSearchStep = 10,
    .iAFThreshold = 200,
    .bEnableLensMove = true
};

// 2. 每帧处理
void af_process_frame(af_frame_info_t *frame, af_output_t *output) {
    // 检查置信度,太低就不处理
    if (frame->af_confidence < 300) {
        output->bLensStop = true;
        return;
    }

    // 对比度低于阈值才启动搜索
    if (frame->af_sharpness < tuning.iAFThreshold) {
        output->eState = AF_STATE_SEARCHING;
        // 执行爬山算法...
    } else {
        output->eState = AF_STATE_FOCUSED;
        output->bLensStop = true;
    }
}

我的习惯:调试阶段我会把af_frame_info里的所有字段都打印出来,特别是af_confidence和af_luminance。这样一旦对焦异常,看日志就能快速定位是算法问题还是输入数据问题。

3.6 常见坑点总结

  • 步长设置不合理:粗搜步长太大容易错过峰值,太小又慢。建议根据VCM行程动态调整。
  • 忽略置信度:对比度值高不代表画面清晰,一定要结合置信度判断。
  • 状态机混乱:FOCUSED状态要保持住,别一有波动就重新搜索。
  • 帧同步问题:frame_id要严格对应,否则算法拿到的对比度值和实际画面不匹配。