HEVC帧内预测编码之LCU四叉树结构分块

本文深入探讨了HEVC标准中CU(编码单元)的四叉树分割过程,并通过具体代码解析了TEncCu::xCompressCU函数的递归实现方式。此外,还展示了如何通过编程手段输出LCU(最大编码单元)的分割模式。

原文地址:http://blog.youkuaiyun.com/yangxiao_xiang/article/details/8270723

从十月份开始,接触HEVC已经将近两个月了,可是效果并不是很明显,这两天都在看代码,经过一段时间的折腾,加上分析学习HEVC_CJL兄弟的文章,终于对HEVC帧内预测编码有了一定的理解,现在主要把本人对于如何LCU如何进一步细分成CU的过程跟大家分享一下,好了,闲话少叙,下面进入主题:


首先,对于四叉树的分割形式,大家想必都已经了解了,这里就不进行过多的赘述,下面是常见的四叉树结构示意图:

接下来是代码部分:

该过程主要由TEncCu::xCompressCU函数的递归实现

    // further split进一步进行CU的分割
    if( bSubBranch && bTrySplitDQP && uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth )
    {
      UChar       uhNextDepth         = uiDepth+1;
      TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth];
      TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth];

      for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )//每次分成四个更小的CU
    {
        pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.
        pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.

        Bool bInSlice = pcSubBestPartCU->getSCUAddr()+pcSubBestPartCU->getTotalNumPart()>pcSlice->getDependentSliceCurStartCUAddr()&&pcSubBestPartCU->getSCUAddr()<pcSlice->getDependentSliceCurEndCUAddr();
        if(bInSlice && ( pcSubBestPartCU->getCUPelX() < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
        {
          if( m_bUseSBACRD )
          {
            if ( 0 == uiPartUnitIdx) //initialize RD with previous depth buffer
            {
              m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
            }
            else
            {
              m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);
            }
          }

#if AMP_ENC_SPEEDUP
          if ( rpcBestCU->isIntra(0) )
          {
            xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, SIZE_NONE );//递归函数
          }
          else
          {
            xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, rpcBestCU->getPartitionSize(0) );//递归函数
          }
#else
          xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth );         
#endif

          rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );         // Keep best part data to current temporary data.
          xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth );
        }
        else if (bInSlice)
        {
          pcSubBestPartCU->copyToPic( uhNextDepth );
          rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );
        }
      }
既然已经知道,CU的分割是通过递归实现的,那么怎么确定哪个uiDepth的CU为rpcBestCU呢?

上述递归函数结束后,然后再通过xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth);判断决定是否选择本层CU还是下层CU.

以下是编程实现输出一个LCU的分割模式:

  // We need to split, so don't try these modes.
  if(!bSliceEnd && !bSliceStart && bInsidePicture )
  {
    for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++)
    {
      if (isAddLowestQP && (iQP == iMinQP))
      {
        iQP = lowestQP;
      }
      // variables for fast encoder decision
      bEarlySkip  = false;
      bTrySplit    = true;
      fRD_Skip    = MAX_DOUBLE;

      rpcTempCU->initEstData( uiDepth, iQP );
     //==输出分区深度信息depth==//
      cout<<"Depth:";
      for(Int i=0;i<=uiDepth;i++)
       cout<<"->";
       cout<<uiDepth<<endl;

      // do inter modes, SKIP and 2Nx2N

打印输出的结构为:

Depth:->0
Depth:->->1
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->1
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->1
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->1
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->2
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3
Depth:->->->->3


如有错误,还望不吝赐教,特别感谢zhuix7788的指导。

最终的LCU分割结果详见下一篇http://blog.youkuaiyun.com/yangxiao_xiang/article/details/8275181。



<think>嗯,用户给了一个C语言的结构体定义,想要分析它的结构组成。首先,我需要仔细看一下这个结构体的各个成员,理解每个成员的作用和数据类型。 结构体的名字是COM_MAP,看起来是和视频编码相关的,可能用于存储编码过程中的一些映射信息。比如,可能有SCU(可能是某种编码单元)、LCU的分割信息、运动矢量、参考索引等等。 第一个成员是u32类型的指针map_scu,注释说是SCU map,用于CU信息。SCU可能指的是Smallest Coding Unit,或者是某种编码单元的结构。CU是编码单元,常见于H.264/H.265等标准中。 接下来是map_split,这个成员的条件编译比较复杂。根据CTU_256是否为真,数组的维度不同。CTU_256可能指编码树单元的大小是256x256,那么MAX_CU_DEPTH2可能代表不同的深度。这里可能存储LCU(Largest Coding Unit)的分割信息,每个深度对应的块形状和数量。 然后是map_mv,二维数组指针,类型是s16,存储解码后的运动矢量。REFP_NUM可能是参考的数量,MV_D可能是运动矢量的维度,比如水平和垂直分量。 map_refi是参考索引,s8类型的指针,每个块可能有多个参考索引。 map_ipm是预测模式,s8指针,记录每个块的预测模式。 map_cu_mode是u32指针,可能存储CU的模式,比如内、间等。 接下来是条件编译的部分,比如TB_SPLIT_EXT,可能涉及变换块的分割,map_pb_tb_part记录分块信息。 map_depth是s8指针,记录每个CU的深度,这在编码树结构中很重要。 map_delta_qp是s8指针,存储QP的差值,用于量化参数的调整。 map_patch_idx可能与图像分块有关,记录每个CU所属的patch索引。 USE_SP相关的map_usp可能用于特殊编码工具,比如Skip模式或其他高级特性。 最后,在PATCH宏定义下,有一些临时映射的成员,如map_scu_temp、map_cu_mode_temp等,可能是用于临时存储或处理过程中的中间数据,避免直接修改原始数据。 需要确认每个成员的数据类型和用途,比如指针和数组的维度,条件编译的影响。此外,注释中的术语如SCU、LCU、CU、运动矢量、参考索引等都是视频编码中的常见概念,可能需要结合标准如HEVC/H.265来解释。 用户可能正在学习或开发视频编码相关的代码,需要理解这个结构体如何组织数据,以便进一步调试或扩展功能。因此,分析时要尽量详细,指出每个成员的作用和可能的应用场景,帮助用户建立整体结构的概念。</think>以下是对`COM_MAP`结构体的分层分析: --- ### **一、基础信息** 1. **结构体名称** `COM_MAP`:视频编码中用于存储编码单元(CU)相关信息的映射结构 2. **设计特征** - 包含多种编码信息映射表 - 使用条件编译适配不同编码配置(如`CTU_256`、`TB_SPLIT_EXT`等) --- ### **二、核心成员解析** #### **(1) 编码单元信息** | 成员名 | 类型 | 说明 | |------------------|---------------|----------------------------------------------------------------------| | `map_scu` | `u32*` | SCU(Smallest Coding Unit)映射表,存储CU的编码模式和控制信息 | | `map_cu_mode` | `u32*` | 记录每个CU的预测模式(内/间/特殊模式) | | `map_depth` | `s8*` | 存储CU的深度信息(反映编码划分层级) | #### **(2) 运动信息** | 成员名 | 类型 | 说明 | |------------------|--------------------------|----------------------------------------------------------------------| | `map_mv` | `s16(*)[REFP_NUM][MV_D]` | 二维运动矢量数组,维度:参考列表数 × 运动矢量分量(水平/垂直) | | `map_refi` | `s8(*)[REFP_NUM]` | 参考索引表,记录每个CU使用的参考编号 | #### **(3) 块划分信息** ```c #if CTU_256 s8 (*map_split)[MAX_CU_DEPTH2][NUM_BLOCK_SHAPE][MAX_CU_CNT_IN_LCU]; #else s8 (*map_split)[MAX_CU_DEPTH][NUM_BLOCK_SHAPE][MAX_CU_CNT_IN_LCU]; #endif ``` - **功能**:记录LCU(Largest Coding Unit)的递归划分信息 - **维度解析**: - `MAX_CU_DEPTH(2)`:最大划分深度(`CTU_256`开启时深度更大) - `NUM_BLOCK_SHAPE`:支持的块形状类型(方形/矩形等) - `MAX_CU_CNT_IN_LCU`:单个LCU内允许的最大CU数量 #### **(4) 其他辅助信息** | 成员名 | 类型 | 说明 | |--------------------|---------|----------------------------------------------------------------------| | `map_ipm` | `s8*` | 预测模式表(如DC/Planar/角度预测模式) | | `map_delta_qp` | `s8*` | 量化参数(QP)差值映射表 | | `map_patch_idx` | `s8*` | 图像分块索引(用于并行处理或分块编码) | --- ### **三、条件编译扩展** #### **(1) 变换块划分扩展** ```c #if TB_SPLIT_EXT u32* map_pb_tb_part; // 记录变换块(TB)的划分方式 #endif ``` #### **(2) 特殊编码工具支持** ```c #if USE_SP u8* map_usp; // 可能用于Skip/Merge等特殊预测模式标记 #endif ``` --- ### **、临时存储区(用于算法中间过程)** ```c #if PATCH u32* map_scu_temp; // SCU临时存储 u32* map_cu_mode_temp; // CU模式临时存储 s16 (*map_mv_temp)[REFP_NUM][MV_D]; // 运动矢量临时存储 s8 (*map_refi_temp)[REFP_NUM]; // 参考索引临时存储 #if USE_SP u8* map_usp_temp; // 特殊模式标记临时存储 #endif #endif ``` --- ### **五、典型应用场景** 1. **编码决策**:通过`map_split`和`map_depth`实现率失真优化的编码划分 2. **运动补偿**:利用`map_mv`和`map_refi`进行预测 3. **并行处理**:通过`map_patch_idx`支持分块编码的线程级并行 4. **码流生成**:结合`map_cu_mode`和`map_ipm`生成最终码流语法元素 --- ### **六、设计特点总结** 1. **内存布局优化**:通过指针数组实现动态内存分配,适应不同分辨率需求 2. **模块化设计**:通过条件编译支持可裁剪的编码功能 3. **数据隔离**:使用独立字段存储不同类型信息,提高缓存局部性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值