SVT-AV1编码 递归子块划分

一 函数说明

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);`
`}`
`}`
`
相关推荐
jay神1 小时前
基于深度学习和协同过滤算法的美妆商品推荐系统
人工智能·深度学习·算法·毕业设计·协同过滤算法
不穿格子的程序员2 小时前
从零开始写算法——图论篇2:课程表 + 实现前缀树(26叉树)
算法·深度优先·图论·dfs·bfs
啊阿狸不会拉杆2 小时前
《数字信号处理》第5章-数字滤波器的基本结构
python·算法·机器学习·matlab·信号处理·数字信号处理·dsp
AI 菌2 小时前
视觉令牌压缩:Vision-centric Token Compression in Large Language Model
人工智能·算法·语言模型·llm
Fleshy数模2 小时前
从原理到实战:逻辑回归,机器学习的“Hello World”
算法·机器学习·逻辑回归
少许极端2 小时前
算法奇妙屋(二十六)-二叉树的深度搜索问题
算法·二叉树·dfs
2401_841495642 小时前
【LeetCode刷题】二叉树的中序遍历
数据结构·python·算法·leetcode··递归·遍历
2301_817497332 小时前
C++中的适配器模式实战
开发语言·c++·算法
砚边数影2 小时前
逻辑回归实战(一):用户流失预测数据集设计,KingbaseES存储标签数据
java·人工智能·算法·机器学习·逻辑回归·线性回归·金仓数据库