SVT-AV1源码分析-函数svt_aom_motion_estimation_kernel

一 svt_aom_motion_estimation_kernel函数作用

这段代码是EBSDK 中的一个运动估计 内核函数,用于处理视频编码中的运动估计任务。运动估计任务。运动估计是视频编码中的一个关键步骤,目的是在时间域上找到当前块在参考帧中的最佳匹配块,从而减少视频数据的冗余。

函数主要功能

函数svt_aom_motion_estimation_kernel 无限循环,从输入队列中获取任务并且处理,然后将结果输出到结果队列。主要处理以下几种任务类型。

1 PAME 逐块运动估计,用于P或B帧的运动估计。

2 TFME 时间滤波运动估计,用于实践滤波器中的运动估计

3 DG_DETECTOR_HME 动态GOP检测器的HME部分。

代码结构解析

1 获取输入对象

EB_GET_FULL_OBJECT(me_context_ptr->picture_decision_results_input_fifo_ptr, &in_results_wrapper_ptr)

从输入队列picture_decision_results_input_fifo_ptr中获取一个完整的对象,这个任务包含了当前需要处理的运动估计相关信息。

2 获取任务类型

if (in_results_ptr->task_type == TASK_TFME)

m_context_ptr->me_ctx->me_type = ME_MCTF;

else if (in_results_ptr->task_type == TASK_PAME || in_results_ptr->task_type == TASK_SUPPERRES_RE_ME)

me_context_ptr->me_ctx->me_type = ME_OPEN_LOOP;

else if (in_results_ptr->task_type == TASK_DG_DETECTOR_HME)

me_context_ptr->me_ctx->me_type = ME_DG_DETECTOR;

根据任务类型设置运动估计的类型me_type

3 ME核心信号推导

if (in_results_ptr->task_type == TASK_PAME) ||

(in_results_ptr->task_type == TASK_SUPPERRES_RE_ME)

svt_aom_sig_deriv_me(scs, pcs, me_context_ptr->me_ctx);

else if (in_result_ctx->task_type == TASK_TFME)

svt_aom_sig_deriv_me_tf(pcs, me_context_ptr->me_ctx);

4 处理PAME或者SUPERRES_RE_ME任务

if (in_results_ptr->task_type == TASK_PAME)

if (in_results_ptr->task_type == TASK_SUPPERRES_RE_ME)

这部分代码主要处理PAME和SUPPERRES_RE_ME任务,包括以下步骤

1 获取图片缓冲区指针

EbPictureBufferDesc *sixteenth_picture_ptr;

EbPictureBufferDesc *quarter_picture_ptr;

EbPictureBufferDesc *input_padded_pic;

EbPictureBufferDesc *input_pic;

这些指针分别指向1/16下采样,1/4采样,输入填充图片和原始输入图片的缓冲区

2 获取段索引和图片尺寸

uint32_t segment_index = in_results_ptr->segment_index;

uint32_t pic_width_in_b64 = (pcs->aligned_width + scs->b64_size - 1) / scs->b64_size;

uint32_t picture_height_in_b64 = (pcs->aligned_height + scs->b64_size - 1) / scs->b64_size;

计算图片在64x64块尺寸下的宽度和高度,并获取当前段索引。

3 转换段索引为XY坐标

SEGMENT_CONVERT_IDX_TO_XY(segment_index, x_segment_index, y_segment_index, pcs->me_segments_column_count);

将一维段索引转换为二维XY坐标

4 计算64x64块的起始和结束索引

uint32_t x_b64_start_index = SEGMENT_START_IDX(x_segment_index, pic_width_in_b64, pcs->me_segments_column_count);

uint32_t x_b64_end_index = SEGMENT_END_IDX(x_segment_index, pic_width_in_b64, pcs->me_segments_column_count);

uint32_t y_b64_start_index = SEGMENT_START_IDX(y_segment_index, picture_height_in_b64, pcs->me_segments_row_count);

uint32_t y_b64_end_index = SEGMENT_END_IDX(y_segment_index, picture_height_in_b64, pcs->me_segments_row_count);

计算当前段中64x64块的起始和结束索引。

5 运动估计处理

if (!skip_me) {

if (pcs->slice_type != I_SLICE) {

// 使用缩放源参考帧(如果需要)

svt_aom_use_scaled_source_refs_if_needed(pcs, input_pic, pa_ref_obj_, &input_padded_pic, &quarter_picture_ptr, &sixteenth_picture_ptr);

// 64x64 块循环

for (uint32_t y_b64_index = y_b64_start_index; y_b64_index < y_b64_end_index; ++y_b64_index) {

for (uint32_t x_b64_index = x_b64_start_index; x_b64_index < x_b64_end_index; ++x_b64_index) {

// 获取 64x64 块的索引和起始位置

uint32_t b64_index = (uint16_t)(x_b64_index + y_b64_index * pic_width_in_b64);

uint32_t b64_origin_x = x_b64_index * scs->b64_size;

uint32_t b64_origin_y = y_b64_index * scs->b64_size;

// 加载 64x64 块数据到中间缓冲区

uint32_t buffer_index = (input_pic->org_y + b64_origin_y) * input_pic->stride_y +

input_pic->org_x + b64_origin_x;

me_context_ptr->me_ctx->b64_src_ptr = &input_padded_pic->buffer_ybuffer_index;

me_context_ptr->me_ctx->b64_src_stride = input_padded_pic->stride_y;

// 加载 1/4 和 1/16 下采样块数据

if (me_context_ptr->me_ctx->enable_hme_level1_flag) {

buffer_index = (quarter_picture_ptr->org_y + (b64_origin_y >> 1)) * quarter_picture_ptr->stride_y +

quarter_picture_ptr->org_x + (b64_origin_x >> 1);

me_context_ptr->me_ctx->quarter_b64_buffer = &quarter_picture_ptr->buffer_ybuffer_index;

me_context_ptr->me_ctx->quarter_b64_buffer_stride = quarter_picture_ptr->stride_y;

}

if (me_context_ptr->me_ctx->enable_hme_level0_flag) {

buffer_index = (sixteenth_picture_ptr->org_y + (b64_origin_y >> 2)) * sixteenth_picture_ptr->stride_y +

sixteenth_picture_ptr->org_x + (b64_origin_x >> 2);

me_context_ptr->me_ctx->sixteenth_b64_buffer = &sixteenth_picture_ptr->buffer_ybuffer_index;

me_context_ptr->me_ctx->sixteenth_b64_buffer_stride = sixteenth_picture_ptr->stride_y;

}

// 设置运动估计参数

me_context_ptr->me_ctx->me_type = ME_OPEN_LOOP;

// 设置参考帧信息

if ((in_results_ptr->task_type == TASK_PAME) || (in_results_ptr->task_type == TASK_SUPERRES_RE_ME)) {

me_context_ptr->me_ctx->num_of_list_to_search = (pcs->slice_type == P_SLICE) ? 1 : 2;

me_context_ptr->me_ctx->num_of_ref_pic_to_search0 = pcs->ref_list0_count_try;

if (pcs->slice_type == B_SLICE)

me_context_ptr->me_ctx->num_of_ref_pic_to_search1 = pcs->ref_list1_count_try;

me_context_ptr->me_ctx->temporal_layer_index = pcs->temporal_layer_index;

me_context_ptr->me_ctx->is_ref = pcs->is_ref;

// 处理超分辨率或帧调整

if (pcs->frame_superres_enabled || pcs->frame_resize_enabled) {

for (int i = 0; i < me_context_ptr->me_ctx->num_of_list_to_search; i++) {

for (int j = 0; j < me_context_ptr->me_ctx->num_of_ref_pic_to_searchi; j++) {

uint8_t sr_denom_idx = svt_aom_get_denom_idx(pcs->superres_denom);

uint8_t resize_denom_idx = svt_aom_get_denom_idx(pcs->resize_denom);

EbPaReferenceObject *ref_object = (EbPaReferenceObject *)pcs->ref_pa_pic_ptr_arrayij->object_ptr;

me_context_ptr->me_ctx->me_ds_ref_arrayij.picture_ptr = ref_object->downscaled_input_padded_picture_ptrsr_denom_idxresize_denom_idx;

me_context_ptr->me_ctx->me_ds_ref_arrayij.quarter_picture_ptr = ref_object->downscaled_quarter_downsampled_picture_ptrsr_denom_idxresize_denom_idx;

me_context_ptr->me_ctx->me_ds_ref_arrayij.sixteenth_picture_ptr = ref_object->downscaled_sixteenth_downsampled_picture_ptrsr_denom_idxresize_denom_idx;

me_context_ptr->me_ctx->me_ds_ref_arrayij.picture_number = ref_object->picture_number;

}

}

} else {

for (int i = 0; i < me_context_ptr->me_ctx->num_of_list_to_search; i++) {

for (int j = 0; j < me_context_ptr->me_ctx->num_of_ref_pic_to_searchi; j++) {

EbPaReferenceObject *ref_object = (EbPaReferenceObject *)pcs->ref_pa_pic_ptr_arrayij->object_ptr;

me_context_ptr->me_ctx->me_ds_ref_arrayij.picture_ptr = ref_object->input_padded_pic;

me_context_ptr->me_ctx->me_ds_ref_arrayij.quarter_picture_ptr = ref_object->quarter_downsampled_picture_ptr;

me_context_ptr->me_ctx->me_ds_ref_arrayij.sixteenth_picture_ptr = ref_object->sixteenth_downsampled_picture_ptr;

me_context_ptr->me_ctx->me_ds_ref_arrayij.picture_number = ref_object->picture_number;

}

}

}

}

// 执行运动估计

svt_aom_motion_estimation_b64(pcs, b64_index, b64_origin_x, b64_origin_y, me_context_ptr->me_ctx, input_pic);

// 处理全局运动估计

if ((in_results_ptr->task_type == TASK_PAME) || (in_results_ptr->task_type == TASK_SUPERRES_RE_ME)) {

svt_block_on_mutex(pcs->me_processed_b64_mutex);

pcs->me_processed_b64_count++;

if (pcs->me_processed_b64_count == pcs->b64_total_count) {

if (pcs->gm_ctrls.enabled && (!pcs->gm_ctrls.pp_enabled || pcs->gm_pp_detected)) {

svt_aom_global_motion_estimation(pcs, input_pic);

} else {

memset(pcs->is_global_motion, FALSE, MAX_NUM_OF_REF_PIC_LIST * REF_LIST_MAX_DEPTH);

}

}

svt_release_mutex(pcs->me_processed_b64_mutex);

}

}

}

}

// 处理开放环路内块估计

if (scs->in_loop_ois == 0 && pcs->tpl_ctrls.enable)

for (uint32_t y_b64_index = y_b64_start_index; y_b64_index < y_b64_end_index; ++y_b64_index)

for (uint32_t x_b64_index = x_b64_start_index; x_b64_index < x_b64_end_index; ++x_b64_index) {

uint32_t b64_index = (uint16_t)(x_b64_index + y_b64_index * pic_width_in_b64);

svt_aom_open_loop_intra_search_mb(pcs, b64_index, input_pic);

}

}

这部分代码主要处理 PAME 和 SUPERRES_RE_ME 任务的运动估计,包括:

  • 加载 64x64 块数据 :从输入图片中加载当前 64x64 块的数据到中间缓冲区。
  • 加载下采样块数据 :如果启用了 HME(Hierarchical Motion Estimation),加载 1/4 和 1/16 下采样块数据。
  • 设置参考帧信息 :根据切片类型(P 或 B)设置参考帧列表和数量。
  • 执行运动估计 :调用 `svt_aom_motion_estimation_b64` 函数进行运动估计。
  • 处理全局运动估计 :在所有 64x64 块处理完成后,执行全局运动估计。
  • 开放环路内块估计 :如果启用了开放环路内块估计,处理每个 64x64 块的内块估计。
5. 处理 TFME 任务
复制代码
`
`

else if (in_results

复制代码
`
`

>task_type == TASK_TFME) {

// gm pre-processing for only base B

if (pcs->gm_ctrls.pp_enabled && pcs->gm_pp_enabled && in_results_ptr->segment_index == 0)

svt_aom_gm_pre_processor(pcs, pcs->temp_filt_pcs_list);

// temporal filtering start

me_context_ptr->me_ctx->me_type = ME_MCTF;

svt_av1_init_temporal_filtering(pcs->temp_filt_pcs_list, pcs, me_context_ptr, in_results_ptr->segment_index);

// Release the Input Results

svt_release_object(in_results_wrapper_ptr);

}

这部分代码处理TAME任务,主要是时间滤波器的运动估计,包括

全局运动处理,如果启用了全局运动预处理且当前段索引为0,则调用svt_aom_gm_pre_processor 进行与处理。

初始化时间滤波,调用svt_av1_init_temporal_filtering 初始化时与滤波

6 处理DG_DETECTOR_HME任务

else if (in_results_ptr->task_type == TASK_DG_DETECTOR_HME) {

// dynamic gop detector

dg_detector_hme_level0(pcs, in_results_ptr->segment_index);

// Release the Input Results

svt_release_object(in_results_wrapper_ptr);

}

这部分代码处理DG_DETECTOR_HME任务,主要是动态GOP检测器的HME部分,调用dg_detector_hme_level0 函数进行处理

7 发布结果

// Get Empty Results Object

svt_get_empty_object(me_context_ptr->motion_estimation_results_output_fifo_ptr,

&out_results_wrapper);

MotionEstimationResults out_results = (MotionEstimationResults )out_results_wrapper->object_ptr;

out_results->pcs_wrapper = in_results_ptr->pcs_wrapper;

out_results->segment_index = segment_index;

out_results->task_type = in_results_ptr->task_type;

// Release the Input Results

svt_release_object(in_results_wrapper_ptr);

// Post the Full Results Object

svt_post_full_object(out_results_wrapper);

获取一个空的结果对象,填充结果数据(如 pcs_wrapper, segment_index 和task_type) 然后发布结果到输出队列

总结

这个函数是EBSDK汇总运动估计模块的核心部分,负责处理不同类型的运动估计任务,通过从输入队列获取任务,根据任务类型进行相应的处理,然后将结果发布到输出队列

主要功能

1 任务类型处理,根据不同任务类型PAME,TFME,DG_DETECTOR_HME执行相应的运动估计逻辑

2 运动估计内核 对64x64块进行运动估计,包括加载数据,设置参考帧信息和执行运动估计算法

3 全局运动估计 在所有块处理完后,执行全局运动估计

4 结果发布 ,将处理结果发布到输出队列,供后续模块使用。

相关推荐
AI棒棒牛几秒前
YOLO26 改进:融合 LW-DETR 思想的轻量 TokenMixer,让 Neck 从局部堆叠走向局部-全局协同!嘎嘎创新!
人工智能·深度学习·yolo·目标检测
longlongzihan3 分钟前
从LeetCode 19. 删除链表的倒数第 N 个结点深入理解“哨兵节点”
c++·算法·leetcode·链表
Hello.Reader4 分钟前
REA 深度实战让 AI Agent 学会逆向工程,从软件行为分析到二进制原理还原
人工智能
William一直在路上5 分钟前
垂直领域大模型训练的一点思考和探索
人工智能·大模型·llm
腾讯云大数据5 分钟前
本体立骨架,语义供刻度——DataBuddy对企业AI语义架构的一次完整回答
大数据·人工智能·架构·云计算·腾讯云
武汉星际互动9 分钟前
从“一件事”到“一类事”,政务服务改革的“下一站”在哪里?
大数据·人工智能·政务
朝朝辞暮i12 分钟前
VLA 系统学习第 20 课——Diffusion Policy 为什么要先给动作加噪,再学习把噪声去掉?
人工智能·python·深度学习·transformer·vla
七夜zippoe12 分钟前
Agent 权限边界:别让 AI 误删你的生产库(安全设计)
数据库·人工智能·安全·ai·权限边界·安全设计
田里的水稻14 分钟前
RL_强化学习概述---具身机器人
人工智能·深度学习·神经网络·机器学习
朝朝辞暮i15 分钟前
第一部分:ACT 阶段总结——从 Behavior Cloning 到完整的 Transformer Action Policy
人工智能·pytorch·深度学习·transformer·vla