VVC学习之五:VTM帧内预测之亮度预测——estIntraPredLumaQT()详解

前一章讲解了VTM帧内预测的主要大致框架,接下来主要学习VTM中相关技术的代码实现。首先,是亮度分量进行帧内预测的入口函数estIntraPredLumaQT(),这个函数描述了亮度帧内预测的主要流程,包括参考样本生成多行预测RMDMPM和最后Full RDO最优模式选择ISP等。其主要流程见下图:
VVC学习之五:VTM帧内预测之亮度预测——estIntraPredLumaQT()详解_第1张图片

代码具体实现如下(VTM-4.0):

进行full RD 搜索的模式个数
const uint8_t g_aucIntraModeNumFast_UseMPM_2D[7 - MIN_CU_LOG2 + 1][7 - MIN_CU_LOG2 + 1] =
{
  {3, 3, 3, 3, 2, 2},  //   4x4,   4x8,   4x16,   4x32,   4x64,   4x128,
  {3, 3, 3, 3, 3, 2},  //   8x4,   8x8,   8x16,   8x32,   8x64,   8x128,
  {3, 3, 3, 3, 3, 2},  //  16x4,  16x8,  16x16,  16x32,  16x64,  16x128,
  {3, 3, 3, 3, 3, 2},  //  32x4,  32x8,  32x16,  32x32,  32x64,  32x128,
  {2, 3, 3, 3, 3, 2},  //  64x4,  64x8,  64x16,  64x32,  64x64,  64x128,
  {2, 2, 2, 2, 2, 3},  // 128x4, 128x8, 128x16, 128x32, 128x64, 128x128,
};
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar )
{
  CodingStructure       &cs            = *cu.cs;
  const SPS             &sps           = *cs.sps;
  const uint32_t       uiWidthBit    =    g_aucLog2[partitioner.currArea().lwidth() ];         // 当前cu的宽度2对数,用于获取进行full RD的模式个数
  const uint32_t       uiHeightBit   =    g_aucLog2[partitioner.currArea().lheight()];		   // 当前cu的高度2对数,用于获取进行full RD的模式个数

  // Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
  // 计算lamda,用于帧内RMD中粗略Hadmard cost的计算
  const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);


  //===== loop over partitions =====
   // 初始化上下文模式信息
  const TempCtx ctxStart          ( m_CtxCache, m_CABACEstimator->getCtx() );
  const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::IntraLumaMpmFlag, m_CABACEstimator->getCtx()));
  const TempCtx ctxStartMHIntraMode ( m_CtxCache, SubCtx( Ctx::MHIntraPredMode,        m_CABACEstimator->getCtx() ) );
  const TempCtx ctxStartMrlIdx      ( m_CtxCache, SubCtx( Ctx::MultiRefLineIdx,        m_CABACEstimator->getCtx() ) );

  CHECK( !cu.firstPU, "CU has no PUs" );
  // 是否保存亮度预测残差,用于CCLM色度预测
  const bool keepResi   = cs.pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;

  uint32_t extraModes = 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3

#if JVET_M0102_INTRA_SUBPARTITIONS      // ISP参数初始化,获取ISP可能的划分方向
  const int width   = partitioner.currArea().lwidth();    // CU宽度
  const int height  = partitioner.currArea().lheight();   // CU高度
  int nOptionsForISP = NUM_INTRA_SUBPARTITIONS_MODES;    // ISP可选的模式,3种
  ///< NOT_INTRA_SUBPARTITIONS不进行,HOR_INTRA_SUBPARTITIONS水平ISP,VER_INTRA_SUBPARTITIONS垂直ISP
  double bestCurrentCost = bestCostSoFar;    // 当前最优cost,用于ISP的提前终止

    /// < 根据CU宽度和高度,获取当前CU可能的ISP预测模式(notISP、HorISP、VerISP)
  int ispOptions[NUM_INTRA_SUBPARTITIONS_MODES] = { 0 };
  if( nOptionsForISP > 1 )
  {
    auto splitsThatCanBeUsedForISP = CU::canUseISPSplit( width, height, cu.cs->sps->getMaxTrSize() );  // 判断可进行ISP模式类型
    if( splitsThatCanBeUsedForISP == CAN_USE_VER_AND_HORL_SPLITS )    // 水平垂直都可以
    {
      const CodingUnit* cuLeft  = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( -1, 0 ), partitioner.chType ) : nullptr;
      const CodingUnit* cuAbove = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( 0, -1 ), partitioner.chType ) : nullptr;
      bool ispHorIsFirstTest = CU::firstTestISPHorSplit( width, height, COMPONENT_Y, cuLeft, cuAbove );
      if( ispHorIsFirstTest )
      {
        ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
        ispOptions[2] = VER_INTRA_SUBPARTITIONS;
      }
      else
      {
        ispOptions[1] = VER_INTRA_SUBPARTITIONS;
        ispOptions[2] = HOR_INTRA_SUBPARTITIONS;
      }
    }
    else if( splitsThatCanBeUsedForISP == HOR_INTRA_SUBPARTITIONS )    // 只能进行水平ISP,ISP循环次数变为2(not、 hor)
    {
      nOptionsForISP = 2;
      ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
    }
    else if( splitsThatCanBeUsedForISP == VER_INTRA_SUBPARTITIONS )    // 只能进行垂直ISP,ISP循环次数变为2(not、 ver)
    {
      nOptionsForISP = 2;
      ispOptions[1] = VER_INTRA_SUBPARTITIONS;
    }
    else      // 不能进行ISP,ISP循环次数变为1(not)
    {
      nOptionsForISP = 1;
    }
  }
  if( nOptionsForISP > 1 )    // 如果进行ISP,初始化相关参数列表
  {
    //variables for the full RD list without MRL modes 帧内ISP预测不支持多参考行
    m_rdModeListWithoutMrl      .clear();
    m_rdModeListWithoutMrlHor   .clear();
    m_rdModeListWithoutMrlVer   .clear();
    //variables with data from regular intra used to skip ISP splits   用于跳过ISP的相关参数
    m_intraModeDiagRatio        .clear();  // (常规帧内模式的预测残差变换后,对角线能量比率)
    m_intraModeHorVerRatio      .clear();  // 水平垂直比率
    m_intraModeTestedNormalIntra.clear();
  }
#endif

  static_vector<uint32_t,   FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;  // hadmard模式列表,用于MPM
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;  // hadmard cost列表,用于更新选择最优RMD模式
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;   // hadmard distortion列表,用于帧间模式的intra快速终止判定

  static_vector<int, FAST_UDI_MAX_RDMODE_NUM> extendRefList;  // 参考行索引列表
  static_vector<int, FAST_UDI_MAX_RDMODE_NUM>* nullList = NULL;

  auto &pu = *cu.firstPU;

  {
    CandHadList.clear(); 
    CandCostList.clear();
    uiHadModeList.clear();
    extendRefList.clear();

    CHECK(pu.cu != &cu, "PU is not contained in the CU");

    //===== determine set of modes to be tested (using prediction signal only) =====
    int numModesAvailable = NUM_LUMA_MODE; // total number of Intra modes  // 帧内有效预测模式,67种
    static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList;  // 进行full RD的模式容器

    int numModesForFullRD = 3;     // 存储进行full RD搜索的模式个数,根据CU尺寸决定,代码块上部已经列出
    numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];

#if INTRA_FULL_SEARCH      // 默认关闭帧内模式全搜索
    numModesForFullRD = numModesAvailable;
#endif

    {
      // this should always be true
      CHECK( !pu.Y().valid(), "PU is not valid" );
#if ENABLE_JVET_L0283_MRL
      bool isFirstLineOfCtu = (((pu.block(COMPONENT_Y).y)&((pu.cs->sps)->getMaxCUWidth() - 1)) == 0);   // 如果cu位于ctu(图片)最上部,不能进行多行预测
      int numOfPassesExtendRef = (isFirstLineOfCtu ? 1 : MRL_NUM_REF_LINES);   // 多行预测可以使用最多三个参考行,0, 1,3行
#endif
      pu.multiRefIdx = 0;   // 多行预测参考行索引

      //===== init pattern for luma prediction =====
      //获取相邻样点,并判定是否对其进行平滑滤波,得到最终参考样点,后面会详细解读此函数
      initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );
      
      //### 下面代码块进行RMD和MPM主题
      //### RMD分成两个阶段,第一个阶段对HEVC中三种模式进行RMD选择,对选择到的模式,进行+-1的扩展模式搜索
      //### MPM列表分成三份,传统模式的RMD,多行预测的MPM,ISP的MPM,分别生成
      if( numModesForFullRD != numModesAvailable )
      {
        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);   // 预测信号缓存区

 		// distortion parameters setting
        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 ) );

		// 第一轮RMD,测试DC,planar和HEVC中的33个角度预测方向
        {
          for( int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
          {
            uint32_t       uiMode = modeIdx;
            Distortion uiSad  = 0;

            // 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 ) )
            {
              encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
            }
            else
            {
            	// 生成预测值,后面详细介绍
              predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
            }
            // use Hadamard transform here
            uiSad += distParam.distFunc(distParam);      // hadmard变换计算失真

            // NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
            m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
            m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
            m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );

            uint64_t fracModeBits = xFracModeBitsIntra(pu, uiMode, CHANNEL_TYPE_LUMA);   // 模式编码比特数

            double cost = ( double ) uiSad + ( double ) fracModeBits * sqrtLambdaForFirstPass;   // hadmard代价

            DTRACE( g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", uiSad, fracModeBits, cost, uiMode );

            updateCandList( uiMode, cost,  uiRdModeList, CandCostList
              , extendRefList, 0
              , numModesForFullRD + extraModes );  // 更新full RD 模式列表和多行参考预测列表(用cost为代价)
            updateCandList(uiMode, (double) uiSad, uiHadModeList, CandHadList
              , *nullList, -1
              , 3 + extraModes);      // 更新帧内Hadmard cost 列表(用distortion作为代价)
          }
        } // NSSTFlag

        // forget the extra modes
        uiRdModeList.resize( numModesForFullRD );
        CandCostList.resize(numModesForFullRD);
        extendRefList.resize(numModesForFullRD);
        
        // 第二轮RMD,测试第一轮中的前numModesForFullRD个模式中的角度模式的前后相邻角度模式
        static_vector<unsigned, FAST_UDI_MAX_RDMODE_NUM> parentCandList(FAST_UDI_MAX_RDMODE_NUM);  // 存储第一轮中的模式
        std::copy_n(uiRdModeList.begin(), numModesForFullRD, parentCandList.begin());   // 为parentCandList赋值

        // Second round of SATD for extended Angular modes 第二轮RMD
        for (int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++)
        {
          unsigned parentMode = parentCandList[modeIdx];
          if (parentMode > (DC_IDX + 1) && parentMode < (NUM_LUMA_MODE - 1))
          {
            for (int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2)
            {
              unsigned mode = parentMode + subModeIdx;
              if (!bSatdChecked[mode])
              {
                pu.intraDir[0] = mode;
                if (useDPCMForFirstPassIntraEstimation(pu, mode))
                {
                  encPredIntraDPCM(COMPONENT_Y, piOrg, piPred, mode);
                }
                else
                {
                  predIntraAng(COMPONENT_Y, piPred, pu,
                               IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, true, pu));
                }
                // use Hadamard transform here
                Distortion sad = distParam.distFunc(distParam);

                // NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
                m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
                m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
                m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );

                uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
                double cost = (double) sad + (double) fracModeBits * sqrtLambdaForFirstPass;
                updateCandList(mode, cost, uiRdModeList, CandCostList
                  , extendRefList, 0
                  , numModesForFullRD);    // 更新RD模式列表,扩展参考行模式列表
                updateCandList(mode, (double)sad, uiHadModeList, CandHadList
                  , *nullList, -1
                  , 3);   // 更新hadmardcost列表
                bSatdChecked[mode] = true;
              }
            }
          }
        }
#if JVET_M0102_INTRA_SUBPARTITIONS
        if( nOptionsForISP > 1 )  // 如果需要做ISP,在多行预测模式之前,将RD模式列表存在m_rdModeListWithoutMrl中
        {
          //we save the list with no mrl modes to keep only the Hadamard selected modes (no mpms)
          m_rdModeListWithoutMrl.resize( numModesForFullRD );
          std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_rdModeListWithoutMrl.begin() );
        }
#endif
#if ENABLE_JVET_L0283_MRL // 多行预测MPM列表建立
        pu.multiRefIdx = 1;   // 此处参考行索引设置为1,目的为设置一个标志位,在getMPM中获取多行预测的MPM
        const int  numMPMs = NUM_MOST_PROBABLE_MODES;   // MPM数量6
        unsigned  multiRefMPM [numMPMs];
        PU::getIntraMPMs(pu, multiRefMPM); // 获取多参考行预测的MPM列表,MRL仅对MPM列表中的模式进行
        for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)
        {
          //** MULTI_REF_LINE_IDX[4] = {0, 1, 3, 0}  **
          int multiRefIdx = MULTI_REF_LINE_IDX[mRefNum];
          pu.multiRefIdx = multiRefIdx;
          {
          	// 获取多行参考预测参考样点
            initIntraPatternChType(cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, false, pu));
          }
          for (int x = 0; x < numMPMs; x++)
          {
            uint32_t mode = multiRefMPM[x];
            {
              pu.intraDir[0] = mode;

              if (useDPCMForFirstPassIntraEstimation(pu, mode))
              {
                encPredIntraDPCM(COMPONENT_Y, piOrg, piPred, mode);
              }
              else
              {
                predIntraAng(COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, true, pu));
              }

              // use Hadamard transform here
              Distortion sad = distParam.distFunc(distParam);

              // NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
              m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
              m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
              m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );

              uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
				
			  // 用参考行索引更新full RD模式列表
              double cost = (double)sad + (double)fracModeBits * sqrtLambdaForFirstPass;
              updateCandList(mode, cost, uiRdModeList, CandCostList, extendRefList, multiRefIdx, numModesForFullRD);
            }
          }
        }
#endif
        CandCostList.resize(numModesForFullRD);
        extendRefList.resize(numModesForFullRD);

		// **************** MPM normal intra ************************
        if( m_pcEncCfg->getFastUDIUseMPMEnabled() )
        {
          const int numMPMs = NUM_MOST_PROBABLE_MODES;
          unsigned  uiPreds[numMPMs]; // MPM列表
          pu.multiRefIdx = 0; // 参考行0,非多行帧内预测MPM
          const int numCand = PU::getIntraMPMs( pu, uiPreds );

		 // MPM去重加入RD mode list
          for( int j = 0; j < numCand; j++ )
          {
            bool mostProbableModeIncluded = false;
            int  mostProbableMode         = uiPreds[j];

            for( int i = 0; i < numModesForFullRD; i++ )
            {
              mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i] && extendRefList[i] == 0);
            }
            if( !mostProbableModeIncluded )
            {
              extendRefList.push_back(0);
              numModesForFullRD++;
              uiRdModeList.push_back( mostProbableMode );
            }
          }
#if JVET_M0102_INTRA_SUBPARTITIONS
          if( nOptionsForISP > 1 ) // 如果当前块需要进行ISP预测,
          {
            //** 对水平、垂直ISP候选列表加入MPM模式
            //** 此过程结束后,ISP候选模式列表为intra_RMD+ ISP_MPM
            
            //we add the ISP MPMs to the list without mrl modes
            m_rdModeListWithoutMrlHor = m_rdModeListWithoutMrl; //没有进行多行预测前的full RD mode list
            m_rdModeListWithoutMrlVer = m_rdModeListWithoutMrl;
            static_vector<uint32_t, FAST_UDI_MAX_RDMODE_NUM>* listPointer; 
            for( int k = 1; k < nOptionsForISP; k++ )
            {
              cu.ispMode = ispOptions[k];
              listPointer = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor : m_rdModeListWithoutMrlVer );
              const int numCandISP = PU::getIntraMPMs( pu, uiPreds ); // 获取ISP预测的MPM模式列表,返回相邻有效模式数目
              for( int j = 0; j < numCandISP; j++ ) 
              {
                bool mostProbableModeIncluded = false;
                int  mostProbableMode = uiPreds[j];

                for( int i = 0; i < listPointer->size(); i++ )
                {
                  mostProbableModeIncluded |= ( mostProbableMode == listPointer->at( i ) );
                }
                if( !mostProbableModeIncluded )
                {
                  listPointer->push_back( mostProbableMode );
                }
              }
            }
            cu.ispMode = NOT_INTRA_SUBPARTITIONS; //返回初始化状态
          }
#endif
        }
      }
      else
      {
        for( int i = 0; i < numModesForFullRD; i++ )
        {
          uiRdModeList.push_back( i );
        }
      }
    }
    
    //** 对ISP候选列表去除非ISP_MPM模式 **
    //** 刚开始没想明白为什么这里去除非MPM模式了,上面MPM过程还要讲两个列表融合
    //** 后来想想可能是为了保持Full RD list中的模式的顺序
    
#if JVET_M0102_INTRA_SUBPARTITIONS
    if( nOptionsForISP > 1 ) // we remove the non-MPMs from the ISP lists
    {
      static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeListCopyHor = m_rdModeListWithoutMrlHor;
      m_rdModeListWithoutMrlHor.clear();
      static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeListCopyVer = m_rdModeListWithoutMrlVer;
      m_rdModeListWithoutMrlVer.clear();
      static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > *listPointerCopy, *listPointer;
      for( int ispOptionIdx = 1; ispOptionIdx < nOptionsForISP; ispOptionIdx++ )
      {
        cu.ispMode = ispOptions[ispOptionIdx];
        //we get the mpm cand list
        const int numMPMs = NUM_MOST_PROBABLE_MODES;
        unsigned  uiPreds[numMPMs];

        pu.multiRefIdx = 0;

        PU::getIntraMPMs( pu, uiPreds );

        //we copy only the ISP MPMs
        listPointerCopy = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? uiRdModeListCopyHor : uiRdModeListCopyVer );
        listPointer     = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor : m_rdModeListWithoutMrlVer );
        for( int k = 0; k < listPointerCopy->size(); k++ )
        {
          for( int q = 0; q < numMPMs; q++ )
          {
            if( listPointerCopy->at( k ) == uiPreds[q] )
            {
              listPointer->push_back( listPointerCopy->at( k ) );
              break;
            }
          }
        }
      }
      cu.ispMode = NOT_INTRA_SUBPARTITIONS;
    }
#endif


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

    // after this point, don't use numModesForFullRD

    // PBINTRA fast
    if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable )
    {
      if( CandHadList.size() < 3 || CandHadList[2] > cs.interHad * PBINTRA_RATIO )
      {
        uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 2 ) );
#if JVET_M0102_INTRA_SUBPARTITIONS
        extendRefList.resize( std::min<size_t>( extendRefList.size(), 2 ) ); 
        if( nOptionsForISP > 1 )
        {
          m_rdModeListWithoutMrlHor.resize( std::min<size_t>( m_rdModeListWithoutMrlHor.size(), 2 ) );
          m_rdModeListWithoutMrlVer.resize( std::min<size_t>( m_rdModeListWithoutMrlVer.size(), 2 ) );
        }
#endif
      }
      if( CandHadList.size() < 2 || CandHadList[1] > cs.interHad * PBINTRA_RATIO )
      {
        uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 1 ) );
#if JVET_M0102_INTRA_SUBPARTITIONS
        extendRefList.resize( std::min<size_t>( extendRefList.size(), 1 ) );
        if( nOptionsForISP > 1 )
        {
          m_rdModeListWithoutMrlHor.resize( std::min<size_t>( m_rdModeListWithoutMrlHor.size(), 1 ) );
          m_rdModeListWithoutMrlVer.resize( std::min<size_t>( m_rdModeListWithoutMrlVer.size(), 1 ) );
        }
#endif
      }
      if( CandHadList.size() < 1 || CandHadList[0] > cs.interHad * PBINTRA_RATIO )
      {
        cs.dist = std::numeric_limits<Distortion>::max();
        cs.interHad = 0;

        //===== reset context models =====
        m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
        m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
        m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );

        return;
      }
    }

    //===== check modes (using r-d costs) =====
    uint32_t       uiBestPUMode  = 0;
    int            bestExtendRef = 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();
    PartSplit intraSubPartitionsProcOrder = TU_NO_ISP;  // ISP子块预测顺序(左->右 or 上->下)
    int       bestNormalIntraModeIndex    = -1; 		// 最优传统帧内预测模式(reference_idx == 0)
    uint8_t   bestIspOption               = NOT_INTRA_SUBPARTITIONS; // 最优ISP划分方式

	// TUIntraSubPartitioner 类似于管理QTMTT划分的Partitioner类,管理ISP sub-partition, 含有ISP分割相关方法
    TUIntraSubPartitioner subTuPartitioner( partitioner );

    for( uint32_t ispOptionIdx = 0; ispOptionIdx < nOptionsForISP; ispOptionIdx++ ) // 第一层循环,ISP模式,(noISP,hor_ISP,ver_ISP)
    {
      cu.ispMode = ispOptions[ispOptionIdx]; // 配置cu ISP类型,non_ISP测试RD模式列表中的模式,hor和ver ISP测试相应ISP列表中的模式
      int numModesForFullRDispOption = cu.ispMode == NOT_INTRA_SUBPARTITIONS ? numModesForFullRD : cu.ispMode == HOR_INTRA_SUBPARTITIONS ? (int)m_rdModeListWithoutMrlHor.size() : (int)m_rdModeListWithoutMrlVer.size();
      for( uint32_t uiMode = 0; uiMode < numModesForFullRDispOption; uiMode++ ) //第二层循环,依据ISP类型,遍历相应模式列表中的模式,计算RDcost
      {
        // set luma prediction mode 获取相应预测模式
        uint32_t uiOrgMode = cu.ispMode == NOT_INTRA_SUBPARTITIONS ? uiRdModeList[uiMode] : cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor[uiMode] : m_rdModeListWithoutMrlVer[uiMode];

        pu.intraDir[0] = uiOrgMode;  // 配置pu预测模式

        int multiRefIdx = 0;
        pu.multiRefIdx = multiRefIdx;
        if( cu.ispMode ) //如果是ISP预测,根据non_isp模式信息进行提前终止判定
        {
          intraSubPartitionsProcOrder = CU::getISPType( cu, COMPONENT_Y ); // 子块处理顺序
          bool tuIsDividedInRows = CU::divideTuInRows( cu ); // 判断是否水平划分
          if( m_intraModeDiagRatio.at( bestNormalIntraModeIndex ) > 1.25 )
          {
            continue;
          }
          if( ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) > 1.25 && tuIsDividedInRows ) || ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) < 0.8 && !tuIsDividedInRows ) )
          {
            continue;
          }
        }
        else   //如果不是ISP预测,从extendRefList得到uiOrgMode对应参考行索引
        {
          multiRefIdx = extendRefList[uiMode];
          pu.multiRefIdx = multiRefIdx;
          CHECK( pu.multiRefIdx && ( pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX ), "ERL" );
        }

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

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

#if JVET_M0102_INTRA_SUBPARTITIONS
      if( cu.ispMode )
      {
        xRecurIntraCodingLumaQT( *csTemp, subTuPartitioner, bestCurrentCost, 0, intraSubPartitionsProcOrder ); // ISP RQT
      }
      else
      {
        xRecurIntraCodingLumaQT( *csTemp, partitioner, MAX_DOUBLE, -1 ); // non-ISP RQT
      }

      if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] )   // ISP预测的cbf默认不能为0,如果为0,不采用isp预测
      {
        csTemp->cost = MAX_DOUBLE;
      }
#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 )		// 根据RD cost,交换temp和best信息
      {
        std::swap( csTemp, csBest );

        uiBestPUMode  = uiOrgMode;	// 更新最优模式
        bestExtendRef = multiRefIdx;	// 更新预测参考行
#if JVET_M0102_INTRA_SUBPARTITIONS
        bestIspOption = cu.ispMode;	// 更新ISP模式
#endif

#if JVET_M0102_INTRA_SUBPARTITIONS
        if( csBest->cost < bestCurrentCost )
        {
          bestCurrentCost = csBest->cost;	// 更新最优RD cost,用于ISP提前终止
        }
        if( !cu.ispMode )
        {
          bestNormalIntraModeIndex = uiMode;	// 不是ISP预测,更新最优预测模式
        }
#endif
      }

      csTemp->releaseIntermediateData();	// 清除中间编码信息,初始化csTemp
    } // Mode loop
    }
    
    cu.ispMode = bestIspOption;	// 更新最终的最优ISP模式


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

    csBest->releaseIntermediateData();
    //=== update PU data ====
    pu.intraDir[0] = uiBestPUMode;
    pu.multiRefIdx = bestExtendRef;
  }

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

你可能感兴趣的:(VVC/H.266学习日记)