一 函数说明
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);`
`}`
`}`
`