一 函数说明
set_child_to_be_considered
核心递归函数,AV1多叉树划分的递归标记逻辑
本函数只负责递归标记哪些块需要处理,并不计算COST
cost的计算在后续阶段完成,
函数作用
这是AV1编码中实现多叉树(四叉树)递归划分的第一阶段:递归标记。
* 当一个块需要继续划分时(split_flag=TRUE),本函数会递归地将其4个子块
* 标记为需要处理,如果子块也需要继续划分,则继续递归标记子块的子块。
* 【完整递归划分流程(两个阶段)】
阶段1,递归标记,本函数。
set_child_to_be_considered() 递归标记哪些块需要处理
计算4个子块索引
标记子块需要处理consider_block = 2
如果depth_step > 1 递归标记子块的子块
结果:构建候选块列表,哪些块需要测试
阶段2:构建候选数组
build_cand_block_array() 根据标记构建候选块数组
遍历预定义的块几何数组
根据consider block标记,生成leaf data array 叶子块数组
结果, leaf data array 包含所有需要处理的块
阶段3 计算Cost
svt_aom_mode_decision_sb()->process_block()->md_encode_block()
遍历lead_data_array 中的每个块
对每个块执行完整的模式决策,预测,变换,量化,RDO
计算每个块的最佳Cost,存储在ctx->md_blk_arr_nsq[blk_index].cost
结果:每个块都有cost值
阶段4:递归决策,根据Cost
update_d2_decision() -> svt_aom_d2_inter_depth_block_decision()
当初里完一个方形块的所有4个子块后,调用深度间决策
比较父块cost vs 子块1,cost + 子块2,cost + 子块3 cost + 子块4 cost +划分码率
如果子块总的cost,更低,设置split_flag = TRUE 继续划分
如果父块cost更低,设置split_flag = TRUE 不划分
递归向上遍历父块链,直到到达根节点
结果:确定最终的划分结构
递归划分原理
AV1 的块划分是四叉树,每个块可以划分成4个子块
* - 128x128 -> 4个 64x64
* - 64x64 -> 4个 32x32
* - 32x32 -> 4个 16x16
* - 16x16 -> 4个 8x8
* - 8x8 -> 4个 4x4
递归标记流程,本函数
1 计算当前块的4个子块索引,child_block_idx 1,2,3,4
2 将每个子块标记为需要处理consider block = 2
3 如果depth_step > 1 还有更深的层次需要处理,递归调用本函数处理每个子块
4 递归终止条件,块尺寸, <= 4x4 或者depth_step <= 1
递归标记示例
假设处理一个64x64块,depth_step=2 需要处理到16x16
第一层 递归 64x64块
计算4个32x32子块索引
标记4个32x32子块需要处理
因为depth_step=2>1 ,递归处理每个32x32子块
第2层递归,32x32块
计算4个16x16子块索引
标记4个16x16子块需要处理
因为depth_step=1, 不再递归,到达目标深度
最终结果:64x64块的所有16x16子块都被标记为需要处理
本函数只标记,不计算cost
Cost在后续的md_encode_block中计算
最终的划分决策在svt_aom_d2_inter_depth_block_decision 中根据cost决定
pcs: 图像控制集指针
ctx: 模式决策上下文指针
results_ptr 候选块数组指针,
blk_index 当前块的MDS索引
sb_index SUperBLock尺寸
sb_size SuperBlock 尺寸
pred_depth 预测深度
pred_sq_idx 预测方形块索引
depth_step 深度步长 生育需要处理的深度层数,每递归一次减掉1
disabllw_nsq 是否禁止非方形块
二 代码注释
static void set_child_to_be_considered(PictureControlSet *pcs, ModeDecisionContext *ctx, MdcSbData *results_ptr,
uint32_t blk_index, uint32_t sb_index, int32_t sb_size, int8_t pred_depth,
uint8_t pred_sq_idx, int8_t depth_step, const uint8_t disallow_nsq)
{
//递归终止条件1,4x4块没有子块,或者8x8块且禁止4x4, 直接返回
//这是递归的边界条件,防止无限递归
if (blk_geom->sq_size <= 4 || (blk_geom->sq_size == 8 && ctx->disallow_4x4))
return;
//计算4个子块的索引
//AV1的块划分是四叉树,每个块划分成4个子块,左上,右上,左下,右下
//child_block_idx 1 第一个子块 左上的索引 = 父块索引 + d1深度偏移
const uint32_t child_block_idx_1 = blk_index + blk_geom->d1_depth_offset;
// child_block_idx_2: 第二个子块(右上)的索引 = 第一个子块索引 + 深度偏移
const uint32_t child_block_idx_2 = child_block_idx_1 +
ns_depth_offset[blk_geom->svt_aom_geom_idx][blk_geom->depth + 1];
// child_block_idx_3: 第三个子块(左下)的索引 = 第二个子块索引 + 深度偏移
const uint32_t child_block_idx_3 = child_block_idx_2 +
ns_depth_offset[blk_geom->svt_aom_geom_idx][blk_geom->depth + 1];
// child_block_idx_4: 第四个子块(右下)的索引 = 第三个子块索引 + 深度偏移
const uint32_t child_block_idx_4 = child_block_idx_3 +
ns_depth_offset[blk_geom->svt_aom_geom_idx][blk_geom->depth + 1];
// 【可选优化:子块可用性检查】
// 如果启用了深度细化控制,且需要检查子块是否可用
// 只有当所有4个子块都在PD0阶段被测试过时,才继续递归
if (ctx->depth_refinement_ctrls.enabled && ctx->depth_refinement_ctrls.prune_child_if_not_avail) {
// 统计有多少个子块在PD0阶段有成本可用(即被测试过)
const uint8_t count = ctx->cost_avail[child_block_idx_1] + ctx->cost_avail[child_block_idx_2] +
ctx->cost_avail[child_block_idx_3] + ctx->cost_avail[child_block_idx_4];
// LPD0(轻量级PD0)可能跳过某些块,所以如果至少有一个子块被处理过,或者使用常规PD0,才需要检查
// 如果少于4个子块可用,说明某些子块在PD0阶段被跳过,此时不继续递归(提前终止)
if ((count || ctx->lpd0_ctrls.pd0_level == REGULAR_PD0) && count < 4)
return;
}
// 【标记当前块需要划分】
// refined_split_flag[blk_index] = TRUE 表示当前块需要继续划分成子块
results_ptr->refined_split_flag[blk_index] = TRUE;
// ========== 【递归处理第一个子块(左上)】==========
// 将第一个子块标记为需要处理(consider_block = 2 表示这是子块)
results_ptr->consider_block[child_block_idx_1] = 2;
// 初始时,子块的refined_split_flag设为FALSE(后续会根据成本决定是否继续划分)
results_ptr->refined_split_flag[child_block_idx_1] = FALSE;
// 【递归调用】如果还有更深的层次需要处理(depth_step > 1),递归处理第一个子块
// depth_step - 1: 每递归一次,深度步长减1,直到depth_step=1时停止递归
if (depth_step > 1)
set_child_to_be_considered(pcs,
ctx,
results_ptr,
child_block_idx_1, // 递归处理第一个子块
sb_index,
sb_size,
pred_depth,
pred_sq_idx,
depth_step - 1, // 深度步长减1
disallow_nsq);
// ========== 【递归处理第二个子块(右上)】==========
results_ptr->consider_block[child_block_idx_2] = 2;
results_ptr->refined_split_flag[child_block_idx_2] = FALSE;
if (depth_step > 1)
set_child_to_be_considered(pcs,
ctx,
results_ptr,
child_block_idx_2, // 递归处理第二个子块
sb_index,
sb_size,
pred_depth,
pred_sq_idx,
depth_step - 1,
disallow_nsq);
// ========== 【递归处理第三个子块(左下)】==========
results_ptr->consider_block[child_block_idx_3] = 2;
results_ptr->refined_split_flag[child_block_idx_3] = FALSE;
if (depth_step > 1)
set_child_to_be_considered(pcs,
ctx,
results_ptr,
child_block_idx_3, // 递归处理第三个子块
sb_index,
sb_size,
pred_depth,
pred_sq_idx,
depth_step - 1,
disallow_nsq);
// ========== 【递归处理第四个子块(右下)】==========
results_ptr->consider_block[child_block_idx_4] = 2;
results_ptr->refined_split_flag[child_block_idx_4] = FALSE;
if (depth_step > 1)
set_child_to_be_considered(pcs,
ctx,
results_ptr,
child_block_idx_4, // 递归处理第四个子块
sb_index,
sb_size,
pred_depth,
pred_sq_idx,
depth_step - 1,
disallow_nsq);
}
}

4827

被折叠的 条评论
为什么被折叠?



