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 三个结构体的协作流程
我把它们的关系画成了下面这张图,你看一眼就明白了:
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要严格对应,否则算法拿到的对比度值和实际画面不匹配。