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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章