欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HEVC函数入门(17)——编码一个CU

程序员文章站 2022-07-07 14:07:05
...

本文转载整理自http://blog.csdn.net/NB_vol_1/article/details/51152578,做了一些微调,这里也感谢这位博主的这篇博客,让我对CU的编码有了更深入的了解。
注意事项
1、帧间预测分为两种:merge模式(skip模式是一种特殊的merge模式)和AMVP模式(即普通的帧间预测模式);
2、merge模式只支持2Nx2N的划分。
3、 帧内预测只支持2Nx2N和NxN的划分模式
CU编码流程
一、先确定iMinQP和iMaxQP
这两个值可以从码率控制对象中得到,也可以自定义
二、如果是P、B类型的slice
1、先处理merge模式和AMVP模式的2Nx2N划分模式
通过一个循环遍历iMinQP到iMaxQP之间的所有QP,对每一个QP,尝试merge模式以及AMVP的2Nx2N模式,然后选出最优的模式。
2、处理AMVP剩余的划分模式
(1)处理NxN模式
(2)处理2NxN模式
(3)处理Nx2N模式
(4)测试AMP_ENC_SPEEDUP宏是否开启
(5)如果AMP_ENC_SPEEDUP宏开启
1)如果TestAMP_Hor、TestAMP_Ver(可以通过一个函数去检测)为真,那么处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式:
①处理2NxnU模式
②处理2NxnD模式
③处理nLx2N模式
④处理nRx2N模式
2)如果TestAMP_Hor、TestAMP_Ver为假,但是定义了AMP_MRG宏,而且TestMergeAMP_Hor、TestMergeAMP_Ver(也可以通过函数去检测)为真,那么处 理 2NxnU、2NxnD、nLx2N、nRx2N这四种模式:
①处理2NxnU模式
②处理2NxnD模式
③处理nLx2N模式
④处理nRx2N模式
3)否则(即TestAMP_Hor、TestAMP_Ver为假,也没有定义AMP_MRG宏)不再处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式。
(6)如果AMP_ENC_SPEEDUP宏没有开启,那么直接处理下面模式
①处理2NxnU模式
②处理2NxnD模式
③处理nLx2N模式
④处理nRx2N模式
这里的AMP是指PU的不对称分割,而①②③④中的UDLR可以用一张图理解:
HEVC函数入门(17)——编码一个CU
三、如果是I类型的slice
那么处理帧内预测的两种模式:2Nx2N和NxN,这里注意BSlice和PSlice也可以进行帧内预测,具体请参考:http://blog.csdn.net/qq_21747841/article/details/73200806
四、尝试PCM模式
注意满足一定条件才会尝试PCM模式。
五、递归处理子CU
同样通过一个循环遍历iMinQP到iMaxQP之间的所有QP,对于每一个QP,递归调用xCompressCU。
六、通过比较选出最优的模式
下面是编码的流程图:
HEVC函数入门(17)——编码一个CU
总结,其实xCompressCU的作用就是从LCU开始深度遍历,计算每一个depth上最优的模式,再综合比较各个depth上最优的模式,选出最优的模式
为了便于理解把xCompressCU的一些无关代码删除,下面是精简版的xCompressCU

#if AMP_ENC_SPEEDUP
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize )//完成实际编码一个CU操作的是xCompressCU方法,CompressCU中调用的xCompressCU则相当于四叉树的根节点。
#else
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth )
#endif
{//总结,其实xCompressCU的作用就是从LCU开始深度遍历,计算每一个depth上最优的模式,再综合比较各个depth上最优的模式,选出最优的模式
  TComPic* pcPic = rpcBestCU->getPic();
  DEBUG_STRING_NEW(sDebug)

  // get Original YUV data from picture
  m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() );

    // variable for Early CU determination
  Bool    bSubBranch = true;

  // variable for Cbf fast mode PU decision
  Bool    doNotBlockPu = true;
  Bool    earlyDetectionSkipMode = false;

  Bool bBoundary = false;
  UInt uiLPelX   = rpcBestCU->getCUPelX();//getCUPelX返回CU的左上角的X坐标
  UInt uiRPelX   = uiLPelX + rpcBestCU->getWidth(0)  - 1;//getWidth(0)返回当前CU的宽度,参数是深度值,0表示返回的是当前CU的宽度,而不是其子CU的宽度
  UInt uiTPelY   = rpcBestCU->getCUPelY();
  UInt uiBPelY   = uiTPelY + rpcBestCU->getHeight(0) - 1;
 // Int A;
 // Int deltaQP_self = ((uiLPelX>=416 && uiLPelX <832) && (uiTPelY >= 240 && uiTPelY < 480)) ? 20 : 0;////

 // Int deltaQP_self = 10;
  Int iBaseQP = xComputeQP( rpcBestCU, uiDepth ) ;  //调用xComputeQP得出基本的量化步长
  Int iMinQP;// 最小的步长  
  Int iMaxQP;// 最大的步长
  Bool isAddLowestQP = false;

  const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();

  if( (g_uiMaxCUWidth>>uiDepth) >= (g_uiMaxCUWidth >> ( rpcTempCU->getSlice()->getPPS()->getMaxCuDQPDepth())) )//使用了iBaseQP
  {
    Int idQP = m_pcEncCfg->getMaxDeltaQP();
    iMinQP = Clip3( -rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP-idQP ) ;
    iMaxQP = Clip3( -rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP+idQP ) ;
  }
  else
  {
    iMinQP = rpcTempCU->getQP(0);
    iMaxQP = rpcTempCU->getQP(0);
  }

  if ( m_pcEncCfg->getUseRateCtrl() )//使用速率控制,注意这里的QP使用了,码率控制对象计算出来的QP 通过QP,码率控制对象控制了编码器的比特率
  {
    iMinQP = m_pcRateCtrl->getRCQP() ;
    iMaxQP = m_pcRateCtrl->getRCQP() ;
  }
  //删除的无关代码
   TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx());
  // We need to split, so don't try these modes.
  if ( ( uiRPelX < rpcBestCU->getSlice()->getSPS()->getPicWidthInLumaSamples() ) &&
       ( uiBPelY < rpcBestCU->getSlice()->getSPS()->getPicHeightInLumaSamples() ) )
  {  // 此循环测试每一种量化步长,计算率失真,选出最优的QP 
    for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++)
    { 
      const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP);
      // 是否为无损模式  
      if (bIsLosslessMode)
      {
        iQP = lowestQP;
      }

      m_ChromaQpAdjIdc = 0;
      if (pcSlice->getUseChromaQpAdj())
      {
        /* Pre-estimation of chroma QP based on input block activity may be performed
         * here, using for example m_ppcOrigYuv[uiDepth] */
        /* To exercise the current code, the index used for adjustment is based on
         * block position
         */
        Int lgMinCuSize = pcSlice->getSPS()->getLog2MinCodingBlockSize() +
                          std::max<Int>(0, pcSlice->getSPS()->getLog2DiffMaxMinCodingBlockSize()-Int(pcSlice->getPPS()->getMaxCuChromaQpAdjDepth()));
        m_ChromaQpAdjIdc = ((uiLPelX >> lgMinCuSize) + (uiTPelY >> lgMinCuSize)) % (pcSlice->getPPS()->getChromaQpAdjTableSize() + 1);
      }
       // 初始化  
      rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

      // do inter modes, SKIP and 2Nx2N
      /*  
            ** 在处理所有的其他模式之前,先处理帧间skip和2Nx2N的模式 
            ** 特别是对于2Nx2N的划分,要分两次处理: 
            ** 1、尝试merge模式——xCheckRDCostMerge2Nx2N 
            ** 2、尝试普通的帧间预测(即AMVP)——xCheckRDCostInter 
      */  
      if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
      {
        // skip模式处理
        // 2Nx2N
        if(m_pcEncCfg->getUseEarlySkipDetection())
        {
          xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) );//inter模式的入口
          rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );//by Competition for inter_2Nx2N
        }
        //merge模式
        // SKIP  
        xCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode );//by Merge for inter_2Nx2N   merge模式的入口
        rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

        if(!m_pcEncCfg->getUseEarlySkipDetection())
        { // 2Nx2N模式
          // 2Nx2N, NxN
          xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) );
          rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
          if(m_pcEncCfg->getUseCbfFastMode())
          {
            doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
          }
        }
      }

      if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.如果搜索无损模式,则恢复循环变量。
      {
        iQP = iMinQP;
      }
    }

    if(!earlyDetectionSkipMode)
    {  // 在实际的处理过程当中,对LCU的划分都是以4x4大小的块进行划分的,这是为了处理方便,然后以Z扫描的方式进行扫描,这也是为了方便递归 
        // 遍历每一种量化步长
      for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++)
      {
        const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP); // If lossless, then iQP is irrelevant for subsequent modules.如果无损,那么iQP与后续模块无关。

        if (bIsLosslessMode)
        {
          iQP = lowestQP;
        }

        rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

        // do inter modes, NxN, 2NxN, and Nx2N
        // do inter modes, NxN, 2NxN, and Nx2N  
                /* 
                ** 普通的帧间预测(普通的帧间预测就是AMVP)开始: 
                ** 注意:这里不再处理merge模式和普通帧间的2Nx2N划分模式, 
                ** 这是因为前面已经处理过2Nx2N的划分模式了,merge模式只对于2Nx2N的划分才有效 
                ** 因此下面的处理是没有merge模式和2Nx2N的划分模式的 
                */  
        if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
        {
          // 2Nx2N, NxN
            // NxN模式的处理
          if(!( (rpcBestCU->getWidth(0)==8) && (rpcBestCU->getHeight(0)==8) ))
          {
            if( uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth && doNotBlockPu)
            {
              xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   );
              rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            }
          }
           // Nx2N模式的处理 
          if(doNotBlockPu)
          {
            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_Nx2N DEBUG_STRING_PASS_INTO(sDebug)  );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_Nx2N )
            {
              doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
            }
          }
          // 2NxN的模式
          if(doNotBlockPu)
          {
            xCheckRDCostInter      ( rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug)  );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxN)
            {
              doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
            }
          }

          //! Try AMP (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N) AMPPU的不对称分割
          //! Try AMP (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N)  
// 接下来是2NxnU、2NxnD、nLx2N、nRx2N的划分模式的处理  
/* 
 ** 接下来的处理有点讲究: 
 ** 1、首先测试AMP_ENC_SPEEDUP宏(表示是否加快编码速度)是否开启 
 ** 2、如果AMP_ENC_SPEEDUP宏开启 
 **      (1)默认情况下,如果TestAMP_HorTestAMP_Ver为真,那么可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式 
 **      (2)如果TestAMP_HorTestAMP_Ver为假,但是开启了AMP_MRG宏,而且TestMergeAMP_HorTestMergeAMP_Ver为真,那么还是可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式 
 **          否则不再处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式 
 **      (3)由于上面会根据一些条件来判断是否需要处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因此某些时候速度会快一点 
 ** 3、如果AMP_ENC_SPEEDUP关闭 
 **      那么直接处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因为没有了条件限制,这四种模式都要测试,因此,速度会慢一点 

 */  
          if(pcSlice->getSPS()->getUseAMP() && uiDepth < g_uiMaxCUDepth-g_uiAddCUDepth )
          {
#if AMP_ENC_SPEEDUP
            Bool bTestAMP_Hor = false, bTestAMP_Ver = false;

#if AMP_MRG
            Bool bTestMergeAMP_Hor = false, bTestMergeAMP_Ver = false;
            // 测试TestAMP_HorTestAMP_Ver是否为真  
            deriveTestModeAMP (rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver, bTestMergeAMP_Hor, bTestMergeAMP_Ver);
#else
            deriveTestModeAMP (rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver);
#endif

            //! Do horizontal AMP做水平AMP
            // TestAMP_Hor为真的话,可以使用2NxnU和2NxnD这两种划分模式  
            if ( bTestAMP_Hor )
            {
              if(doNotBlockPu)
              {
                  // 处理2NxnU模式 
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug) );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }
              if(doNotBlockPu)
              {
                  // 处理2NxnD模式 
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug) );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }
            }
#if AMP_MRG// TestMergeAMP_Hor为真的话可以使用2NxnU、2NxnD这两种模式 
            else if ( bTestMergeAMP_Hor )
            { 
              if(doNotBlockPu)
              {// 处理2NxnU模式
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug), true );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }
              if(doNotBlockPu)
              {// 处理2NxnD模式
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug), true );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }
            }
#endif

            //! Do horizontal AMP
            // TestAMP_Ver为真可以处理nLx2N、nRx2N两种模式
            if ( bTestAMP_Ver )
            { // 处理nLx2N模式
              if(doNotBlockPu)
              {
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug) );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }
               // 处理nRx2N模式
              if(doNotBlockPu)
              {
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug) );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
              }
            }
#if AMP_MRG
            // TestMergeAMP_Ver为真可以处理nLx2N、nRx2N模式  
            else if ( bTestMergeAMP_Ver )
            {// 处理nLx2N模式
              if(doNotBlockPu)
              {
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug), true );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N )
                {
                  doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                }
              }// 处理nRx2N模式
              if(doNotBlockPu)
              {
                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug), true );
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
              }
            }
#endif

#else
            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N );
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

#endif
          }
        }
        // 帧间预测结束!!!! 

        // do normal intra modes
        // speedup for inter frames
        // 帧内预测开始,帧内预测只有两种划分:2Nx2N、NxN 
        Double intraCost = 0.0;

        if((rpcBestCU->getSlice()->getSliceType() == I_SLICE)                                     ||
           (rpcBestCU->getCbf( 0, COMPONENT_Y  ) != 0)                                            ||
          ((rpcBestCU->getCbf( 0, COMPONENT_Cb ) != 0) && (numberValidComponents > COMPONENT_Cb)) ||
          ((rpcBestCU->getCbf( 0, COMPONENT_Cr ) != 0) && (numberValidComponents > COMPONENT_Cr))  ) // avoid very complex intra if it is unlikely
        {// 帧内2Nx2N模式
          xCheckRDCostIntra( rpcBestCU, rpcTempCU, intraCost, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); //各种intra预测模式下的代价
          rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
          // 帧内NxN
          if( uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth )//g_uiMaxCUDepth=4,g_uiAddCUDepth=1
          {
            if( rpcTempCU->getWidth(0) > ( 1 << rpcTempCU->getSlice()->getSPS()->getQuadtreeTULog2MinSize() ) )
            {
              Double tmpIntraCost;
              xCheckRDCostIntra( rpcBestCU, rpcTempCU, tmpIntraCost, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   );
              intraCost = std::min(intraCost, tmpIntraCost);
              rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            }
          }
        }// 帧内预测结束!!!

        // test PCM
        // 尝试PCM模式
        if(pcPic->getSlice(0)->getSPS()->getUsePCM()
          && rpcTempCU->getWidth(0) <= (1<<pcPic->getSlice(0)->getSPS()->getPCMLog2MaxSize())
          && rpcTempCU->getWidth(0) >= (1<<pcPic->getSlice(0)->getSPS()->getPCMLog2MinSize()) )
        {
          UInt uiRawBits = getTotalBits(rpcBestCU->getWidth(0), rpcBestCU->getHeight(0), rpcBestCU->getPic()->getChromaFormat(), g_bitDepth);
          UInt uiBestBits = rpcBestCU->getTotalBits();
          if((uiBestBits > uiRawBits) || (rpcBestCU->getTotalCost() > m_pcRdCost->calcRdCost(uiRawBits, 0)))
          {
            xCheckIntraPCM (rpcBestCU, rpcTempCU);
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
          }
        }

        if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.
        {
          iQP = iMinQP;
        }
      }
    }
    // 重置比特数 
    m_pcEntropyCoder->resetBits();
     // 对分割标志进行编码
    m_pcEntropyCoder->encodeSplitFlag( rpcBestCU, 0, uiDepth, true );
    // 比特数量统计
    rpcBestCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
    rpcBestCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
    // 总的消耗统计
    rpcBestCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcBestCU->getTotalBits(), rpcBestCU->getTotalDistortion() );

    // Early CU determination
    // HM15.0的配置中没有使用早期的CU  
    if( m_pcEncCfg->getUseEarlyCU() && rpcBestCU->isSkipped(0) )
    {
      bSubBranch = false;
    }
    else
    {
      bSubBranch = true;
    }
  }
  else
  {
    bBoundary = true;
  }
   if ( m_pcEncCfg->getCUTransquantBypassFlagForceValue() )
  {
    iMaxQP = iMinQP; // If all TUs are forced into using transquant bypass, do not loop here.
  }
  // 从最小量化步长到最大量化步长,递归处理子CU,然后选取最优的量化步长和最优划分模式 
  for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++)
  {
    const Bool bIsLosslessMode = false; // False at this level. Next level down may set it to true.在这个级别是FALSE 下一级可能会将其设置为true。

    rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

    // further split
    // 进一步的分割  
    if( bSubBranch && uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth )
    {
      UChar       uhNextDepth         = uiDepth+1;
      TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth];
      TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth];
      DEBUG_STRING_NEW(sTempDebug)
// 进一步的分割,当前CU又被划分成为4个子CU
// 对子CU进行初始化,计算坐标索引等等,然后把父亲CU的信息复制给子CU  
// 另外,tempSubCU会和rpcTempCU一样调用initEstData,进行清理,把旧的数据清除掉  
      for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )
      {//对子CU进行初始化,计算坐标索引等等,然后把父亲CU的信息复制给子CU,普通的CU初始化的时候不使用initCU,而是以父亲CU作为参数调用initSubCU进行初始化。
        pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP  );           // clear sub partition datas or init.
        pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP  );           // clear sub partition datas or init.

        if( ( pcSubBestPartCU->getCUPelX() < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
        {
          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
          DEBUG_STRING_NEW(sChild)
          if ( !rpcBestCU->isInter(0) )
          { // 递归处理子CU  
            xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), NUMBER_OF_PART_SIZES );
          }
          else
          {

            xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), rpcBestCU->getPartitionSize(0) );
          }
          DEBUG_STRING_APPEND(sTempDebug, sChild)
#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
        {
          pcSubBestPartCU->copyToPic( uhNextDepth );
          rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );
        }
      }
      // 计算并更新最优的代价——begin  
      if( !bBoundary )
      {
        m_pcEntropyCoder->resetBits();
        m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true );

        rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
        rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
      }
      // 计算RD代价
      rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );

      if( (g_uiMaxCUWidth>>uiDepth) == (g_uiMaxCUWidth >> ( rpcTempCU->getSlice()->getPPS()->getMaxCuDQPDepth())) && rpcTempCU->getSlice()->getPPS()->getUseDQP())
      {
        Bool hasResidual = false;
        for( UInt uiBlkIdx = 0; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx ++)
        {
          if( (     rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Y)
                || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cb) && (numberValidComponents > COMPONENT_Cb))
                || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cr) && (numberValidComponents > COMPONENT_Cr)) ) )
          {
            hasResidual = true;
            break;
          }
        }

        UInt uiTargetPartIdx = 0;
        if ( hasResidual )
        {
#if !RDO_WITHOUT_DQP_BITS
          m_pcEntropyCoder->resetBits();
          m_pcEntropyCoder->encodeQP( rpcTempCU, uiTargetPartIdx, false );
          rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bits
          rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
          rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
#endif

          Bool foundNonZeroCbf = false;
          rpcTempCU->setQPSubCUs( rpcTempCU->getRefQP( uiTargetPartIdx ), 0, uiDepth, foundNonZeroCbf );
          assert( foundNonZeroCbf );
        }
        else
        {
          rpcTempCU->setQPSubParts( rpcTempCU->getRefQP( uiTargetPartIdx ), 0, uiDepth ); // set QP to default QP
        }
      }

      m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);

      // TODO: this does not account for the slice bytes already written. See other instances of FIXED_NUMBER_OF_BYTES
      Bool isEndOfSlice        = rpcBestCU->getSlice()->getSliceMode()==FIXED_NUMBER_OF_BYTES
                                 && (rpcBestCU->getTotalBits()>rpcBestCU->getSlice()->getSliceArgument()<<3);
      Bool isEndOfSliceSegment = rpcBestCU->getSlice()->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES
                                 && (rpcBestCU->getTotalBits()>rpcBestCU->getSlice()->getSliceSegmentArgument()<<3);
      if(isEndOfSlice||isEndOfSliceSegment)
      {
        if (m_pcEncCfg->getCostMode()==COST_MIXED_LOSSLESS_LOSSY_CODING)
        {
          rpcBestCU->getTotalCost()=rpcTempCU->getTotalCost() + (1.0 / m_pcRdCost->getLambda());
        }
        else
        {
          rpcBestCU->getTotalCost()=rpcTempCU->getTotalCost()+1;
        }
      }// 选择最优的划分模式

      xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // RD compare current larger prediction
        // 计算并更新最优代价——end                                                                                // with sub partitioned prediction.
    }
  }
相关标签: 编码 hevc HM