VVC代码 BMS 帧内预测学习之二:亮度色度预测函数 estIntraPredLumaQT()及estIntraPredChromaQT()

亮度模式决策函数:estIntraPredLumaQT()

函数主要分为RMD(粗选择)过程,MPM获取过程及RDO过程。
1、RMD过程通过SATD进行候选模式的选择,主要预测函数为:predIntraAng()
函数详细分析见:
https://blog.csdn.net/yolo_life/article/details/81736464
2、获取6种MPM(HEVC为3种)的函数为getIntraMPMs()
3、对RMD过程后选出的模式加上MPM选出的模式进行预测及变换量化的RDO过程的主要函数为:xRecurIntraCodingLumaQT()

Void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner )
{
  CodingStructure       &cs            = *cu.cs;
  const SPS             &sps           = *cs.sps;
  const UInt             uiWidthBit    = cs.pcv->rectCUs ? g_aucLog2[partitioner.currArea().lwidth() ] : CU::getIntraSizeIdx(cu);
  const UInt             uiHeightBit   =                   g_aucLog2[partitioner.currArea().lheight()];
  //预测过程中,HEVC_USE_PART_SIZE这个宏没开,CU没有划分为PU,与JEM中QTBT保持一致,下文将此宏下相关内容删除
#if JEM_TOOLS
  auto                   slsCtrl       = dynamic_cast<SaveLoadEncInfoCtrl*>( m_modeCtrl );
#endif

  // Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
  const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);


//******************* QTBT下没有循环进行下列步骤 **********************===== loop over partitions =====

  const TempCtx ctxStart          ( m_CtxCache, m_CABACEstimator->getCtx() );
  const TempCtx ctxStartIntraMode ( m_CtxCache, SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA],        m_CABACEstimator->getCtx() ) );

  CHECK( !cu.firstPU, "CU has no PUs" );
  const bool keepResi   = cs.pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;

#if JEM_TOOLS
//下列变量:存储多NSST通道的快速帧内模式扫描结果 variables for saving fast intra modes scan results across multiple NSST passes
  bool NSSTLoadFlag = sps.getSpsNext().getUseNSST() && cu.nsstIdx != 0;
  bool NSSTSaveFlag = sps.getSpsNext().getUseNSST() && cu.nsstIdx == 0 && !cu.pdpc;

  NSSTSaveFlag &= sps.getSpsNext().getUseIntraEMT() ? cu.emtFlag == 0 : true;
#endif

#if JEM_TOOLS
//NSST下模式多两个
  UInt extraModes = sps.getSpsNext().getUseNSST() ? 2 : 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#else
  UInt extraModes = 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#endif

#if JEM_TOOLS
  const int width   = partitioner.currArea().lwidth();
  const int height  = partitioner.currArea().lheight();
#endif

#if JEM_TOOLS
  // Marking EMT usage for faster EMT
  // 0: EMT is either not applicable for current CU (cuWidth > EMT_INTRA_MAX_CU or cuHeight > EMT_INTRA_MAX_CU), not active in the config file or the fast decision algorithm is not used in this case
  // 1: EMT fast algorithm can be applied for the current CU, and the DCT2 is being checked
  // 2: EMT is being checked for current CU. Stored results of DCT2 can be utilized for speedup
  UChar emtUsageFlag = 0;
  const int maxSizeEMT = cs.pcv->noRQT ? EMT_INTRA_MAX_CU_WITH_QTBT : EMT_INTRA_MAX_CU;
  if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getSpsNext().getUseIntraEMT() )
  {
    emtUsageFlag = cu.emtFlag == 1 ? 2 : 1;
  }

  Bool isAllIntra = m_pcEncCfg->getIntraPeriod() == 1;

  if( cs.pcv->rectCUs )
  {
    if( ( width * height < 64 && !isAllIntra ) || ( slsCtrl && m_pcEncCfg->getUseSaveLoadEncInfo() && m_pcEncCfg->getIntraEMT() && LOAD_ENC_INFO == slsCtrl->getSaveLoadTag( cu ) /*&& m_modeCtrl->getSaveLoadEmtCuFlag(cu.cs->area)==0*/ ) )
    {
      emtUsageFlag = 0; //this forces the recalculation of the candidates list. Why is this necessary? (to be checked)
    }
    //not very sure about this command. It should be further checked when the EMT and the NSST are combined!!!
    NSSTSaveFlag |= m_pcEncCfg->getNSST() && m_pcEncCfg->getIntraEMT() && slsCtrl && m_pcEncCfg->getUseSaveLoadEncInfo() && LOAD_ENC_INFO == slsCtrl->getSaveLoadTag( cu );
  }

  NSSTLoadFlag &= !(m_pcEncCfg->getNSST() && m_pcEncCfg->getUseSaveLoadEncInfo() && (LOAD_ENC_INFO == slsCtrl->getSaveLoadTag(cu)));
#endif

  static_vector<UInt,   FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;
  static_vector<Double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
  static_vector<Double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;

  auto &pu = *cu.firstPU;
#if JEM_TOOLS
  int puIndex = 0;
#endif
  {
    CandHadList.clear();
    CandCostList.clear();
    uiHadModeList.clear();

    CHECK(pu.cu != &cu, "PU is not contained in the CU");
    
//******************************************** 粗选择RMD过程

    //===== determine set of modes to be tested (using prediction signal only) =====
    Int numModesAvailable = NUM_LUMA_MODE; //67, total number of Intra modes
    static_vector< UInt, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList;

    Int numModesForFullRD = 3;
    if( cs.pcv->rectCUs )
    {
    //RMD模式的个数与块大小一一对应,uiWidthBit 与uiHeightBit 为log2后值
      numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
    }
    else
    {
      numModesForFullRD = m_pcEncCfg->getFastUDIUseMPMEnabled() ? g_aucIntraModeNumFast_UseMPM[uiWidthBit] : g_aucIntraModeNumFast_NotUseMPM[uiWidthBit];
#if JEM_TOOLS
      if( cs.sps->getSpsNext().getUseIntra65Ang() )
      {
        numModesForFullRD -= 1;
      }
#endif
    }

#if INTRA_FULL_SEARCH
//删除非活动区域代码
#endif


#if JEM_TOOLS
//******************************************** emtUsageFlag != 2
    if( emtUsageFlag != 2 )
#endif
    {
      // this should always be true
      CHECK( !pu.Y().valid(), "PU is not valid" );

      //===== init pattern for luma prediction =====
      //初始过程,未滤波/滤波参考样本
      initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );
      if( numModesForFullRD != numModesAvailable )//67
      {
        CHECK( numModesForFullRD >= numModesAvailable, "Too many modes for full RD search" );

        const CompArea &area = pu.Y();

        PelBuf piOrg         = cs.getOrgBuf(area);
        PelBuf piPred        = cs.getPredBuf(area);

        DistParam distParam;

        const Bool bUseHadamard = cu.transQuantBypass == 0;//变换量化旁路

        m_pcRdCost->setDistParam(distParam, piOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);

        distParam.applyWeight = false;

        bool bSatdChecked[NUM_INTRA_MODE];
        memset( bSatdChecked, 0, sizeof( bSatdChecked ) );

#if JEM_TOOLS
        if( !NSSTLoadFlag )
#endif
        {
//******************************************** HEVC模式的循环        
          for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
          {
            UInt       uiMode = modeIdx;
            Distortion uiSad  = 0;
#if JEM_TOOLS
            if( ( cu.partSize == SIZE_2Nx2N ) && cu.nsstIdx >= ( uiMode <= DC_IDX ? 3 : 4 ) )
            {
              continue;
            }
#endif

            //skip扩展的角度模式,即skip JEM 中的新模式  Skip checking extended Angular modes in the first round of SATD
            if( uiMode > DC_IDX && ( uiMode & 1 ) )
            {
              continue;
            }

            bSatdChecked[uiMode] = true;

            pu.intraDir[0] = modeIdx;

            if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )
            {
            //水平或垂直预测采用DPCM
            //水平:预测值的列被参考块覆盖,将参考块其他可获取的值再赋值给预测值
              encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
            }
            else
            {
            //进入67个模式的预测及参考像素的处理
              predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
            }
            // use Hadamard transform here
            uiSad += distParam.distFunc(distParam);

            // NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
            m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA], ctxStartIntraMode );
		//被选模式与非被选模式编码
            UInt64 fracModeBits = xFracModeBitsIntra(pu, uiMode, CHANNEL_TYPE_LUMA);
		//总的代价
            Double cost = ( Double ) uiSad + ( Double ) fracModeBits * sqrtLambdaForFirstPass;

            DTRACE( g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", uiSad, fracModeBits, cost, uiMode );
		//更新候选列表
		//对numModesForFullRD + extraModes个根据cost进行排序,得到从小到大排序的CandCostList
            updateCandList( uiMode, cost,  uiRdModeList, CandCostList, numModesForFullRD + extraModes );
        //对 3 + extraModes个根据uiSad进行排序,得到从小到大排序的CandHadList
            updateCandList( uiMode, uiSad, uiHadModeList, CandHadList, 3                 + extraModes );
          }
#if JEM_TOOLS
          if( NSSTSaveFlag )
          {
            // save found best modes
            m_uiSavedNumRdModesNSST  = numModesForFullRD;
            m_uiSavedRdModeListNSST  = uiRdModeList;
            m_dSavedModeCostNSST     = CandCostList;
            // PBINTRA fast
            m_uiSavedHadModeListNSST = uiHadModeList;
            m_dSavedHadListNSST      = CandHadList;
            NSSTSaveFlag             = false;
          }
#endif
        } // NSSTFlag

#if JEM_TOOLS
        else
        {

          // restore saved modes
          numModesForFullRD = m_uiSavedNumRdModesNSST;
          uiRdModeList      = m_uiSavedRdModeListNSST;
          CandCostList      = m_dSavedModeCostNSST;
          // PBINTRA fast
          uiHadModeList     = m_uiSavedHadModeListNSST;
          CandHadList       = m_dSavedHadListNSST;


          if( cu.nsstIdx == 3 && cu.partSize == SIZE_2Nx2N )
          {
            // remove uiMode <= DC_IDX
            Int cnt = 0;

            for( int i = 0; i < numModesForFullRD; i++ )
            {
              if( uiRdModeList[i] <= DC_IDX )
              {
                for( UInt j = i; j < numModesForFullRD + 1 - cnt; j++ )
                {
                  uiRdModeList[j] = uiRdModeList[j + 1];
                  CandCostList[j] = CandCostList[j + 1];
                }
                cnt++;
                i--;
              }
            }

            if( m_pcEncCfg->getUsePbIntraFast() )
            {
              // PBINTRA fast
              cnt = 0;
              for( int i = 0; i < 3; i++ )
              {
                if( uiHadModeList[i] <= DC_IDX )
                {
                  for( UInt j = i; j < 3 + 1 - cnt; j++ )
                  {
                    uiHadModeList[j] = uiHadModeList[j + 1];
                    CandHadList[j]   = CandHadList  [j + 1];
                  }

                  cnt++;
                  i--;
                }
              }
            }
          }
          NSSTLoadFlag = false;
        } // NSSTFlag
#endif
        // forget the extra modes
        uiRdModeList.resize( numModesForFullRD );

#if JEM_TOOLS
        if( cs.sps->getSpsNext().getUseIntra65Ang() )
        {
          static_vector<UInt, FAST_UDI_MAX_RDMODE_NUM> uiParentCandList( FAST_UDI_MAX_RDMODE_NUM );
          std::copy_n( uiRdModeList.begin(), numModesForFullRD, uiParentCandList.begin() );

//******************************************** JEM中扩展的角度模式循环,继续更新 Second round of SATD for extended Angular modes
          for( Int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++ )
          {
            UInt uiParentMode = uiParentCandList[modeIdx];
            if( uiParentMode > ( DC_IDX + 1 ) && uiParentMode < ( NUM_LUMA_MODE - 1 ) )
            {
              for( Int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2 )//隔着HEVC的预测方向即为JEM的
              {
              //与uiParentMode相邻的模式
                UInt uiMode = uiParentMode + subModeIdx;

                if( cu.partSize == SIZE_2Nx2N && cu.nsstIdx >= ( ( uiMode <= DC_IDX ) ? 3 : 4 ) )
                {
                  continue;
                }
			//没算过SATD的模式satd计算
                if( !bSatdChecked[uiMode] )
                {
                  pu.intraDir[0] = uiMode;

                  if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )
                  {
                    encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
                  }
                  else
                  {
                    predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
                  }
                  // use Hadamard transform here
                  Distortion uiSad = distParam.distFunc( distParam );

                  // NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
                  m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA], ctxStartIntraMode );

                  UInt64 fracModeBits = xFracModeBitsIntra( pu, uiMode, CHANNEL_TYPE_LUMA );

                  Double cost = ( Double ) uiSad + ( Double ) fracModeBits * sqrtLambdaForFirstPass;

                  updateCandList( uiMode, cost,  uiRdModeList,  CandCostList, numModesForFullRD );
                  updateCandList( uiMode, uiSad, uiHadModeList, CandHadList,  3 );
				//为避免计算重复
                  bSatdChecked[uiMode] = true;
                }
              }
            }
          }
        }

#endif
//******************************************** 获取MPM
        if( m_pcEncCfg->getFastUDIUseMPMEnabled() 
        {
          unsigned  numMPMs = pu.cs->pcv->numMPMs;//6种
          unsigned *uiPreds = ( unsigned* ) alloca( numMPMs * sizeof( unsigned ) );
         //获取MPM的函数,左侧,上方,planar,DC,左下,右上,左上;不足6个,会加上派生的模式:派生模式通过对MPM列表中角度预测模式-1或+1;不足6个,再加入垂直,水平,模式2。上述过程在满足6个时即停止
          const Int numCand = PU::getIntraMPMs( pu, uiPreds );

          for( Int j = 0; j < numCand; j++ )
          {
            Bool mostProbableModeIncluded = false;
            Int  mostProbableMode         = uiPreds[j];

#if JEM_TOOLS
            if( cu.partSize == SIZE_2Nx2N && cu.nsstIdx >= ( mostProbableMode <= DC_IDX ? 3 : 4 ) )
            {
              continue;
            }
#endif

            for( Int i = 0; i < numModesForFullRD; i++ )
            {
              mostProbableModeIncluded |= ( mostProbableMode == uiRdModeList[i] );
            }
            //之前选的与MPM合并
            if( !mostProbableModeIncluded )
            {
              numModesForFullRD++;
              uiRdModeList.push_back( mostProbableMode );
            }
          }
        }//getFastUDIUseMPMEnabled
//******************************************** 获取MPM end
      }// if( numModesForFullRD != numModesAvailable)
      else
      {
        for( Int i = 0; i < numModesForFullRD; i++ )
        {
          uiRdModeList.push_back( i );
        }
      }
#if JEM_TOOLS
//******************************************** emtUsageFlag == 1
      if( emtUsageFlag == 1 )
      {
        // Store the modes to be checked with RD
        m_savedNumRdModes[puIndex] = numModesForFullRD;
        std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_savedRdModeList[puIndex] );
      }
#endif
    }//if(emtUsageFlag != 2)
#if JEM_TOOLS
//******************************************** emtUsage = 2
    else //emtUsage = 2 (here we potentially reduce the number of modes that will be full-RD checked)
    {
      if( isAllIntra && m_pcEncCfg->getFastIntraEMT() )
      {
      //确定AMT跳过步骤的门限
        double thresholdSkipMode;
        if( cs.pcv->noRQT )
        {
          thresholdSkipMode = 1.0 + 1.4 / sqrt( ( double ) ( width*height ) );
        }
        else
        {
          switch( width )
          {
          case  4: thresholdSkipMode = 1.47; break; // Skip checking   4x4 Intra modes using the R-D cost in the DCT2-pass
          case  8: thresholdSkipMode = 1.28; break; // Skip checking   8x8 Intra modes using the R-D cost in the DCT2-pass
          case 16: thresholdSkipMode = 1.12; break; // Skip checking 16x16 Intra modes using the R-D cost in the DCT2-pass
          case 32: thresholdSkipMode = 1.06; break; // Skip checking 32x32 Intra modes using the R-D cost in the DCT2-pass
          default: thresholdSkipMode = 1.06; break; // Skip checking 32x32 Intra modes using the R-D cost in the DCT2-pass
          }
        }

        numModesForFullRD = 0;

        // Skip checking the modes with much larger R-D cost than the best mode
        for( Int i = 0; i < m_savedNumRdModes[puIndex]; i++ )
        {
          if( m_modeCostStore[puIndex][i] <= thresholdSkipMode * m_bestModeCostStore[puIndex] )
          {
            uiRdModeList.push_back( m_savedRdModeList[puIndex][i] );
            numModesForFullRD++;
          }
        }
      }
      else //this is necessary because we skip the candidates list calculation, since it was already obtained for the DCT-II. Now we load it
      {
        // Restore the modes to be checked with RD
        numModesForFullRD = m_savedNumRdModes[puIndex];
        uiRdModeList.resize( numModesForFullRD );
        std::copy_n( m_savedRdModeList[puIndex], m_savedNumRdModes[puIndex], uiRdModeList.begin() );
      }
    }
#endif


    CHECK( numModesForFullRD != uiRdModeList.size(), "Inconsistent state!" );

    // after this point, don't use numModesForFullRD

//******************************************** PBINTRA fast
#if JEM_TOOLS
    if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && cu.partSize == SIZE_2Nx2N && uiRdModeList.size() < numModesAvailable && emtUsageFlag != 2 )
#else
    if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && cu.partSize == SIZE_2Nx2N && uiRdModeList.size() < numModesAvailable )
#endif
    {
      if( CandHadList.size() < 3 || CandHadList[2] > cs.interHad * PBINTRA_RATIO )
      {
        uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 2 ) );
      }
      if( CandHadList.size() < 2 || CandHadList[1] > cs.interHad * PBINTRA_RATIO )
      {
        uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 1 ) );
      }
      if( CandHadList.size() < 1 || CandHadList[0] > cs.interHad * PBINTRA_RATIO )
      {
        cs.dist     = MAX_UINT;
        cs.interHad = 0;

        //===== reset context models =====
        m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode       [CHANNEL_TYPE_LUMA], ctxStartIntraMode );

        return;
      }
    }

//******************************************** RDO过程  check modes (using r-d costs) =====
//ENABLE_RQT_INTRA_SPEEDUP_MOD默认false,将其下内容删除

    UInt       uiBestPUMode  = 0;

    CodingStructure *csTemp = m_pTempCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];
    CodingStructure *csBest = m_pBestCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];

    csTemp->slice = cs.slice;
    csBest->slice = cs.slice;
    csTemp->initStructData();
    csBest->initStructData();

    // just to be sure
    numModesForFullRD = ( int ) uiRdModeList.size();
    for (UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++)
    {
      // set luma prediction mode
      UInt uiOrgMode = uiRdModeList[uiMode];

      pu.intraDir[0] = uiOrgMode;


      // set context models
      m_CABACEstimator->getCtx() = ctxStart;

      // determine residual for partition
      cs.initSubStructure( *csTemp, partitioner.chType, cs.area, true );


//******************************************** 根据给定模式下的划分,重建,完成变换量化及率失真计算
      xRecurIntraCodingLumaQT( *csTemp, partitioner );


#if JEM_TOOLS
      if( emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() )
      {
        m_modeCostStore[puIndex][uiMode] = csTemp->cost; //cs.cost;
      }
#endif


      DTRACE( g_trace_ctx, D_INTRA_COST, "IntraCost T %f (%d) \n", csTemp->cost, uiOrgMode );

      // check r-d cost
      if( csTemp->cost < csBest->cost )
      {
        std::swap( csTemp, csBest );



        uiBestPUMode  = uiOrgMode;

#if JEM_TOOLS
        if( ( emtUsageFlag == 1 ) && m_pcEncCfg->getFastIntraEMT() )
        {
          m_bestModeCostStore[puIndex] = csBest->cost; //cs.cost;
        }
#endif
      }


      csTemp->releaseIntermediateData();
    } // Mode loop
#endif

    cs.useSubStructure( *csBest, partitioner.chType, pu.singleChan( CHANNEL_TYPE_LUMA ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );

    csBest->releaseIntermediateData();
//**********************************************=== update PU data ====

    pu.intraDir[0] = uiBestPUMode;
  }

//**********************************************===== reset context models =====
  m_CABACEstimator->getCtx() = ctxStart;
}

色度模式决策函数:estIntraPreChromaQT()

函数的主要过程是先确定6个CCLM模式,再确定5种普通预测模式,最后对11种预测模式进行遍历,选最优模式。
获取候选模式的函数为:getIntraChromaCandModes()
主要的预测函数为:predIntraChromaLM(),该函数会调用两次,一次预测Cb,一次预测Cr

Void IntraSearch::estIntraPredChromaQT(CodingUnit &cu, Partitioner &partitioner)
{
  const ChromaFormat format   = cu.chromaFormat;

//HEVC_USE_PART_SIZE 默认false,删掉其下面相关内容

  const UInt    numberValidComponents = getNumberValidComponents(format);
  CodingStructure &cs = *cu.cs;
  const TempCtx ctxStart  ( m_CtxCache, m_CABACEstimator->getCtx() );

  cs.setDecomp( cs.area.Cb(), false );
  auto &pu = *cu.firstPU;

  {
    UInt       uiBestMode = 0;
    Distortion uiBestDist = 0;
    Double     dBestCost = MAX_DOUBLE;

//**************************************----- init mode list ----
    {
      UInt  uiMinMode = 0;
      UInt  uiMaxMode = NUM_CHROMA_MODE;

//**************************************----- check chroma modes -----
      UInt chromaCandModes[ NUM_CHROMA_MODE ];
      //11种模式,6CCLM+5种传统(DM+后续尝试):
      //当前色度块对应亮度块的5个位置,DM,去重,若<5
      //+左邻色度块的预测模式,若<5
      //+上方相邻色度块的预测模式,若<5
      //+左下色度块的预测模式,若<5
      //+右上色度块的预测模式,若<5
      //+左上色度块的预测模式,若<5
      //+Planar,若<5
      //+DC,若<5
      //+衍生模式,若<5
      //+垂直/水平/模式2,若<5
      PU::getIntraChromaCandModes( pu, chromaCandModes );

      // create a temporary CS
      CodingStructure &saveCS = *m_pSaveCS[0];
      saveCS.pcv      = cs.pcv;
      saveCS.picture  = cs.picture;
      saveCS.area.repositionTo( cs.area );
      saveCS.clearTUs();

      if( CS::isDualITree( cs ) )
      {
#if ENABLE_BMS
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
        {
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );

          do
          {
            cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType ).depth = partitioner.currTrDepth;
          } while( partitioner.nextPart( cs ) );

          partitioner.exitCurrSplit();
        }
        else
#endif
        cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType );
      }

      std::vector<TransformUnit*> orgTUs;

//CU还划分TU?
      // create a store for the TUs
      for( const auto &ptu : cs.tus )
      {
        // for split TUs in HEVC, add the TUs without Chroma parts for correct setting of Cbfs
        if( pu.contains( *ptu, CHANNEL_TYPE_CHROMA ) || ( !cs.pcv->noRQT && !ptu->Cb().valid() && !ptu->Cr().valid() ) )
        {
          saveCS.addTU( *ptu, partitioner.chType );
          orgTUs.push_back( ptu );
        }
      }
#if JEM_TOOLS

      UInt auiSATDModeList[LM_FILTER_NUM];
      //LM滤波,4种
      if( pu.cs->pcv->noRQT && pu.cs->sps->getSpsNext().getUseLMChroma() && PU::isMFLMEnabled(pu))
      {
        UInt auiSATDSortedcost[LM_FILTER_NUM];
        DistParam distParam;
        const Bool bUseHadamard = true;
        Int iCurLMMFIdx = 0;
		//获取重构亮度的下采样
        xGetLumaRecPixels(pu, pu.Cb());

        initIntraPatternChType( cu, pu.Cb() );
        initIntraPatternChType( cu, pu.Cr() );

        //SATD checking for LMMF candidates
        for (UInt uiMode = LM_CHROMA_F1_IDX; uiMode < LM_CHROMA_F1_IDX + LM_FILTER_NUM; uiMode++)
        {
          UInt uiSad = 0;
          CodingStructure& cs = *(pu.cs);

          CompArea areaCb = pu.Cb();
          PelBuf piOrgCb = cs.getOrgBuf(areaCb);
          PelBuf piPredCb = cs.getPredBuf(areaCb);

          m_pcRdCost->setDistParam(distParam, piOrgCb, piPredCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, bUseHadamard);
          distParam.applyWeight = false;
          
//************************************** 预测Cb
          predIntraChromaLM(COMPONENT_Cb, piPredCb, pu, areaCb, uiMode);//MMLM,LM过程

#if !ENABLE_BMS
//ENABLE_BMS默认true,删除非活动区域代码
#endif
          uiSad += distParam.distFunc(distParam);

          CompArea areaCr = pu.Cr();
          PelBuf piOrgCr = cs.getOrgBuf(areaCr);
          PelBuf piPredCr = cs.getPredBuf(areaCr);

          m_pcRdCost->setDistParam(distParam, piOrgCr, piPredCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, bUseHadamard);

//************************************** 预测Cr
          predIntraChromaLM(COMPONENT_Cr, piPredCr, pu, areaCr, uiMode);

#if !ENABLE_BMS
          PelBuf savePredCr(m_pLMMFPredSaved[(uiMode - LM_CHROMA_F1_IDX) * 2 + 1], areaCr.width, areaCr);
          savePredCr.copyFrom(piPredCr);
#endif
          uiSad += distParam.distFunc(distParam);

          auiSATDSortedcost[iCurLMMFIdx] = uiSad;
          auiSATDModeList[iCurLMMFIdx] = uiMode;
          for (Int k = iCurLMMFIdx; k > 0 && auiSATDSortedcost[k] < auiSATDSortedcost[k - 1]; k--)
          {
            UInt tmp = auiSATDSortedcost[k];
            auiSATDSortedcost[k] = auiSATDSortedcost[k - 1];
            auiSATDSortedcost[k - 1] = tmp;

            tmp = auiSATDModeList[k];
            auiSATDModeList[k] = auiSATDModeList[k - 1];
            auiSATDModeList[k - 1] = tmp;
          }
          iCurLMMFIdx++;
        }
      }
#endif

//************************************** save the dist
      Distortion baseDist = cs.dist;
//对11种(6个CCLM+5)模式进行遍历,选min
      for (UInt uiMode = uiMinMode; uiMode < uiMaxMode; uiMode++)
      {
        const int chromaIntraMode = chromaCandModes[uiMode];
#if JEM_TOOLS

        if( PU::isLMCMode( chromaIntraMode ) && ! PU::isLMCModeEnabled( pu, chromaIntraMode ) )
        {
          continue;
        }
#endif
#if JEM_TOOLS
        if( CS::isDualITree( cs ) && cu.nsstIdx == 3 )
        {
          int intraMode = chromaIntraMode;
#if JEM_TOOLS
          if( PU::isLMCMode( chromaIntraMode ) )
          {
            intraMode = PLANAR_IDX;
          }
          else
#endif
          if( intraMode == DM_CHROMA_IDX )
          {
            const PredictionUnit* lumaPu = cs.picture->cs->getPU( partitioner.currArea().lumaPos(), CHANNEL_TYPE_LUMA );
            intraMode = lumaPu->intraDir[0];
          }

          if( intraMode <= DC_IDX )
          {
            continue;
          }
        }
#endif
#if JEM_TOOLS
        if( pu.cs->pcv->noRQT && pu.cs->sps->getSpsNext().isELMModeMFLM())
        {
          if( chromaIntraMode >= LM_CHROMA_F1_IDX &&  chromaIntraMode < LM_CHROMA_F1_IDX + LM_FILTER_NUM)
          {
            if (auiSATDModeList[0] != chromaIntraMode)
            {
              continue;
            }
          }
        }
#endif

        cs.setDecomp( pu.Cb(), false );
        cs.dist = baseDist;
//**************************************----- restore context models -----
        m_CABACEstimator->getCtx() = ctxStart;

//**************************************----- chroma coding -----
        pu.intraDir[1] = chromaIntraMode;

        xRecurIntraChromaCodingQT( cs, partitioner );

        if (cs.pps->getUseTransformSkip())
        {
          m_CABACEstimator->getCtx() = ctxStart;
        }

        UInt64 fracBits   = xGetIntraFracBitsQT( cs, partitioner, false, true );
        Distortion uiDist = cs.dist;
        Double    dCost   = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist );

//**************************************----- compare -----
        if( dCost < dBestCost )
        {
          for( UInt i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
          {
            const CompArea &area = pu.blocks[i];

            saveCS.getRecoBuf     ( area ).copyFrom( cs.getRecoBuf   ( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
//删除非活动区域代码
#endif
            cs.picture->getRecoBuf( area ).copyFrom( cs.getRecoBuf( area ) );

            for( UInt j = 0; j < saveCS.tus.size(); j++ )
            {
              saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID );
#if ENABLE_CHROMA_422
//删除非活动区域代码
#endif
            }
          }

          dBestCost  = dCost;
          uiBestDist = uiDist;
          uiBestMode = chromaIntraMode;
        }
      }

      for( UInt i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
      {
        const CompArea &area = pu.blocks[i];

        cs.getRecoBuf         ( area ).copyFrom( saveCS.getRecoBuf( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
//删除非活动区域代码
#endif
        cs.picture->getRecoBuf( area ).copyFrom( cs.    getRecoBuf( area ) );

        for( UInt j = 0; j < saveCS.tus.size(); j++ )
        {
          orgTUs[ j ]->copyComponentFrom( *saveCS.tus[ j ], area.compID );
#if ENABLE_CHROMA_422
//删除非活动区域代码
#endif
        }
      }
    }

    pu.intraDir[1] = uiBestMode;
    cs.dist        = uiBestDist;
  }

//**************************************----- restore context models -----
  m_CABACEstimator->getCtx() = ctxStart;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章