VVC帧内亮度角度预测--帧内编码学习(二)

对于帧内预测来说,主要包括角度预测和划分。

在xCompressCU函数中体现为

xCheckRDCostIntra( tempCS, bestCS, partitioner, currTestMode );

以及

 xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode
        , tempMotCandLUTs
        , bestMotCandLUTs
        , partitioner.currArea()
      );

xCheckModeSplit函数中主要是根据划分模式进行划分,再递归回xCompressCU,与HEVC中递归差不多。

这里主要讲帧内亮度角度预测过程,VTM版本为4.0.1。

xCheckRDCostIntra函数进来之后,会进行相应参数的初始化,之后调用亮度角度预测

m_pcIntraSearch->estIntraPredLumaQT( cu, partitioner, bestCostSoFar );

和色度角度预测

 m_pcIntraSearch->estIntraPredChromaQT( cu, ( !useIntraSubPartitions || ( CS::isDualITree( *cu.cs ) && !isLuma( CHANNEL_TYPE_CHROMA ) ) ) ? partitioner : subTuPartitioner, maxCostAllowedForChroma );

帧内亮度预测主要过程为:

1、参考像素的获取与滤波;具体可见---https://blog.csdn.net/pengyouyou/article/details/89027301

2、在原HEVC的35种模式中,挑选出numModesForFullRD种哈达玛代价最小的模式-------RMD第一步;

      其中planner模式讲解见---https://blog.csdn.net/pengyouyou/article/details/89041932

3、在RMD第一步得到的角度模式中,对其相邻的新增角度模式计算哈达玛代价值,同样保留numModesForFullRD种代价最小的模式-------RMD第二步;

4、添加MPM模式;

5、对候选模式进行RDO过程,选出代价最小的模式。

虽然大致过程为这5个步骤,但是在MRL和ISP等技术的引入使得具体过程更加复杂。

具体程序注释如下:

#if JVET_M0102_INTRA_SUBPARTITIONS
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar )//亮度角度预测
#else
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner )
#endif
{
  CodingStructure       &cs            = *cu.cs;
  const SPS             &sps           = *cs.sps;
  const uint32_t             uiWidthBit    = g_aucLog2[partitioner.currArea().lwidth() ];//
  const uint32_t             uiHeightBit   =                   g_aucLog2[partitioner.currArea().lheight()];
  // 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);
  //===== 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" );
  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_M0464_UNI_MTS
  const int width   = partitioner.currArea().lwidth();
  const int height  = partitioner.currArea().lheight();

  // 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
  uint8_t emtUsageFlag = 0;
  const int maxSizeEMT = EMT_INTRA_MAX_CU_WITH_QTBT;
  if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getUseIntraEMT() )
  {
    emtUsageFlag = cu.emtFlag == 1 ? 2 : 1;
  }

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

  if( width * height < 64 && !isAllIntra )
  {
    emtUsageFlag = 0; //this forces the recalculation of the candidates list. Why is this necessary? (to be checked)
  }
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS
#if JVET_M0464_UNI_MTS
  const int width   = partitioner.currArea().lwidth();
  const int height  = partitioner.currArea().lheight();
  int nOptionsForISP = NUM_INTRA_SUBPARTITIONS_MODES;//ISP划分模式:HOR_INTRA_SUBPARTITIONS = 1,  VER_INTRA_SUBPARTITIONS = 2,
  //默认的NUM_INTRA_SUBPARTITIONS_MODES=3
#else
  int nOptionsForISP = cu.emtFlag == 0 ? NUM_INTRA_SUBPARTITIONS_MODES : 1;
#endif
  double bestCurrentCost = bestCostSoFar;//当前最佳代价

  int ispOptions[NUM_INTRA_SUBPARTITIONS_MODES] = { 0 };//ISP模式初始为non-ISP

  if( nOptionsForISP > 1 ) {
    auto splitsThatCanBeUsedForISP = CU::canUseISPSplit( width, height, cu.cs->sps->getMaxTrSize() );//得到可能进行的ISP模式
    if( splitsThatCanBeUsedForISP == CAN_USE_VER_AND_HORL_SPLITS ) {//水平和垂直ISP均可以
      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 )//先水平ISP,默认nOptionsForISP=3
      {
        ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
        ispOptions[2] = VER_INTRA_SUBPARTITIONS;
      }
      else//先垂直ISP,默认nOptionsForISP=3
      {
        ispOptions[1] = VER_INTRA_SUBPARTITIONS;
        ispOptions[2] = HOR_INTRA_SUBPARTITIONS;
      }
    }
    else if( splitsThatCanBeUsedForISP == HOR_INTRA_SUBPARTITIONS )
    {
      nOptionsForISP = 2;
      ispOptions[1] = HOR_INTRA_SUBPARTITIONS;//水平ISP
    }
    else if( splitsThatCanBeUsedForISP == VER_INTRA_SUBPARTITIONS )
    {
      nOptionsForISP = 2;
      ispOptions[1] = VER_INTRA_SUBPARTITIONS;//垂直ISP
    }
    else
    {
      nOptionsForISP = 1;//non-ISP
    }
  }
  if( nOptionsForISP > 1 )//ISP模式清空列表
  {
    //variables for the full RD list without MRL modes
    m_rdModeListWithoutMrl      .clear();
    m_rdModeListWithoutMrlHor   .clear();
    m_rdModeListWithoutMrlVer   .clear();
    //variables with data from regular intra used to skip ISP splits
    m_intraModeDiagRatio        .clear();
    m_intraModeHorVerRatio      .clear();
    m_intraModeTestedNormalIntra.clear();
  }
#endif

  static_vector<uint32_t,   FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;//哈达玛模式列表
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;//代价候选列表
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;//绝对变换残差和列表

  static_vector<int, FAST_UDI_MAX_RDMODE_NUM> extendRefList;//扩展参考列表MRL索引或ISP(MRL模式下ISP索引最终被取消)
  static_vector<int, FAST_UDI_MAX_RDMODE_NUM>* nullList = NULL;

  auto &pu = *cu.firstPU;//得到PU
#if !JVET_M0464_UNI_MTS
  int puIndex = 0;
#endif
  {
    //列表清空
    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; // 可使用的模式数量
    static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList;//率失真模式列表
    int numModesForFullRD = 3;//采用全RDO的模式数
    numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];//映射表
#if INTRA_FULL_SEARCH
    numModesForFullRD = numModesAvailable;//全搜索
#endif
#if !JVET_M0464_UNI_MTS
    if( emtUsageFlag != 2 )
#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);//pu是否在图像第一行
      int numOfPassesExtendRef = (isFirstLineOfCtu ? 1 : MRL_NUM_REF_LINES);//MRL最大值
#endif
      pu.multiRefIdx = 0;//non-MRL索引

      //===== init pattern for luma prediction =====
      initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );
      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);//预测像素
        DistParam distParam;//失真参数
        const bool bUseHadamard = cu.transQuantBypass == 0;//是否采用哈达玛变换
#if JVET_M0427_INLOOP_RESHAPER
        if (cu.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag())
        {
          CompArea      tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
          PelBuf tmpOrg = m_tmpStorageLCU.getBuf(tmpArea);
          tmpOrg.copyFrom(piOrg);
          tmpOrg.rspSignal(m_pcReshape->getFwdLUT());
          m_pcRdCost->setDistParam(distParam, tmpOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);//设置失真参数
        }
        else
#endif
        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第一步--在传统的35模式中进行
          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 ) )//跳过新增的角度模式,RMD第一步
            {
              continue;
            }
            bSatdChecked[uiMode] = true;//校验确认
            pu.intraDir[0] = modeIdx;//存储亮度角度预测模式
            if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )//使用DPCM
            {
              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);//获得绝对变换残差和
            // 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;//计算哈达玛代价值
            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 );//更新率失真模式、代价列表、扩展参考列表
            updateCandList(uiMode, (double) uiSad, uiHadModeList, CandHadList
              , *nullList, -1
              , 3 + extraModes);//更新哈达玛模式、绝对变换残差和(哈达玛变换,不考虑比特率)列表
          }
        } // NSSTFlag
        // forget the extra modes
        uiRdModeList.resize( numModesForFullRD );//仅保留全RDO数量的模式
        CandCostList.resize(numModesForFullRD);
        extendRefList.resize(numModesForFullRD);
        static_vector<unsigned, FAST_UDI_MAX_RDMODE_NUM> parentCandList(FAST_UDI_MAX_RDMODE_NUM);
        std::copy_n(uiRdModeList.begin(), numModesForFullRD, parentCandList.begin());//将RMD第一步的模式候选保存
        //RMD第二步--细化角度搜索 Second round of SATD for extended Angular modes//
        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);//
                updateCandList(mode, (double)sad, uiHadModeList, CandHadList
                  , *nullList, -1
                  , 3);
                bSatdChecked[mode] = true;//确认校验
              }
            }
          }
        }
#if JVET_M0102_INTRA_SUBPARTITIONS
        if( nOptionsForISP > 1 ) {//如果能进行ISP
          //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() );//保存non-MRL的哈达玛代价模式
        }
#endif
#if ENABLE_JVET_L0283_MRL//MRL-MPM
        pu.multiRefIdx = 1;//MRL索引
        const int  numMPMs = NUM_MOST_PROBABLE_MODES;//MPM模式数
        unsigned  multiRefMPM [numMPMs];//MPM模式
        PU::getIntraMPMs(pu, multiRefMPM);//获得MPM模式
        for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)//遍历MRL,扩展索引1和3
        {
          int multiRefIdx = MULTI_REF_LINE_IDX[mRefNum];//转换MRL索引
          pu.multiRefIdx = multiRefIdx;
          {//对参考像素滤波并获取
            initIntraPatternChType(cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, false, pu));
          }
          for (int x = 0; x < numMPMs; x++)//遍历MRL-MPM
          {
            uint32_t mode = multiRefMPM[x];//得到MPM模式
            {
              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, multiRefIdx, numModesForFullRD);
            }
          }
        }
#endif
        CandCostList.resize(numModesForFullRD);//保留代价最小的numModesForFullRD种模式
        extendRefList.resize(numModesForFullRD);
        if( m_pcEncCfg->getFastUDIUseMPMEnabled() )
        {//non-MRL MPM//正常MPM模式
          const int numMPMs = NUM_MOST_PROBABLE_MODES;
          unsigned  uiPreds[numMPMs];
          pu.multiRefIdx = 0;//non-MRL
          const int numCand = PU::getIntraMPMs( pu, uiPreds );
          for( int j = 0; j < numCand; j++ )//遍历MPM
          {
            bool mostProbableModeIncluded = false;//是否已经包括MPM模式
            int  mostProbableMode         = uiPreds[j];//MPM模式
            for( int i = 0; i < numModesForFullRD; i++ )//MPM校验
            {
              mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i] && extendRefList[i] == 0);//non-MRL MPM中是否包括了当前MPM
            }
            if( !mostProbableModeIncluded )//MPM没有被包括
            {
              extendRefList.push_back(0);//non-MRL标记
              numModesForFullRD++;//增加全RDO模式数
              uiRdModeList.push_back( mostProbableMode );//保存MPM到列表
            }
          }
#if JVET_M0102_INTRA_SUBPARTITIONS
          //ISP-MPM
          if( nOptionsForISP > 1 )//ISP模式
          {
            //we add the ISP MPMs to the list without mrl modes
            //添加ISP的MPM模式到non-MRL列表
            m_rdModeListWithoutMrlHor = m_rdModeListWithoutMrl;
            m_rdModeListWithoutMrlVer = m_rdModeListWithoutMrl;
            static_vector<uint32_t, FAST_UDI_MAX_RDMODE_NUM>* listPointer;
            for( int k = 1; k < nOptionsForISP; k++ ) {//遍历ISP模式
              cu.ispMode = ispOptions[k];//得到ISP模式
              listPointer = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor : m_rdModeListWithoutMrlVer );//水平或垂直
              const int numCandISP = PU::getIntraMPMs( pu, uiPreds );//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 );
        }
      }
#if !JVET_M0464_UNI_MTS
      if( emtUsageFlag == 1 ){
        // Store the modes to be checked with RD
        m_savedNumRdModes[puIndex] = numModesForFullRD;
        std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_savedRdModeList[puIndex] );
        std::copy_n(extendRefList.begin(), numModesForFullRD, m_savedExtendRefList[puIndex]);
      }
#endif
    }
#if !JVET_M0464_UNI_MTS
    else //emtUsage = 2 (here we potentially reduce the number of modes that will be full-RD checked)
    {
      if( isAllIntra && m_pcEncCfg->getFastIntraEMT() )
      {
        double thresholdSkipMode = 1.0 + 1.4 / sqrt( ( double ) ( width*height ) );

        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] );
            extendRefList.push_back(m_savedExtendRefList[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() );
        CandCostList.resize(numModesForFullRD);
        extendRefList.resize(numModesForFullRD);
        std::copy_n(m_savedExtendRefList[puIndex], m_savedNumRdModes[puIndex], extendRefList.begin());
      }
    }
#endif

#if JVET_M0102_INTRA_SUBPARTITIONS
    //对于ISP列表移除non-MPM
    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();//复制并清除ISP列表
      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++ ){//遍历ISP模式
        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] ){//列表中存在MPM模式
              listPointer->push_back( listPointerCopy->at( k ) );//存入ISP列表
              break;
            }
          }
        }
      }
      cu.ispMode = NOT_INTRA_SUBPARTITIONS;
    }
#endif
    CHECK( numModesForFullRD != uiRdModeList.size(), "Inconsistent state!" );
    // after this point, don't use numModesForFullRD
    // PBINTRA fast
#if JVET_M0464_UNI_MTS
    if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable )
#else
    if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable && emtUsageFlag != 2 )
#endif
    {
      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;
      }
    }

#if JVET_M0102_INTRA_SUBPARTITIONS && JVET_M0464_UNI_MTS
    if ( nOptionsForISP > 1 )
    {
      //we create a single full RD list that includes all intra modes using regular intra, MRL and ISP
      auto* firstIspList  = ispOptions[1] == HOR_INTRA_SUBPARTITIONS ? &m_rdModeListWithoutMrlHor : &m_rdModeListWithoutMrlVer;
      auto* secondIspList = ispOptions[1] == HOR_INTRA_SUBPARTITIONS ? &m_rdModeListWithoutMrlVer : &m_rdModeListWithoutMrlHor;

      if ( m_pcEncCfg->getUseFastISP() )
      {
        // find the first non-MRL mode
        size_t indexFirstMode = std::find( extendRefList.begin(), extendRefList.end(), 0 ) - extendRefList.begin();
        // if not found, just take the last mode
        if( indexFirstMode >= extendRefList.size() ) indexFirstMode = extendRefList.size() - 1;
        // move the mode indicated by indexFirstMode to the beginning
        for( int idx = ((int)indexFirstMode) - 1; idx >= 0; idx-- )
        {
          std::swap( extendRefList[idx], extendRefList[idx + 1] );
          std::swap( uiRdModeList [idx], uiRdModeList [idx + 1] );
        }
        //insert all ISP modes after the first non-mrl mode
        uiRdModeList.insert( uiRdModeList.begin() + 1, secondIspList->begin(), secondIspList->end() );
        uiRdModeList.insert( uiRdModeList.begin() + 1, firstIspList->begin() , firstIspList->end()  );

        extendRefList.insert( extendRefList.begin() + 1, secondIspList->size(), MRL_NUM_REF_LINES + ispOptions[2] );
        extendRefList.insert( extendRefList.begin() + 1, firstIspList->size() , MRL_NUM_REF_LINES + ispOptions[1] );
      }
      else
      {
        //insert all ISP modes at the end of the current list
        uiRdModeList.insert( uiRdModeList.end(), secondIspList->begin(), secondIspList->end() );
        uiRdModeList.insert( uiRdModeList.end(), firstIspList->begin() , firstIspList->end()  );

        extendRefList.insert( extendRefList.end(), secondIspList->size(), MRL_NUM_REF_LINES + ispOptions[2] );
        extendRefList.insert( extendRefList.end(), firstIspList->size() , MRL_NUM_REF_LINES + ispOptions[1] );
      }
    }
    CHECKD(uiRdModeList.size() != extendRefList.size(),"uiRdModeList and extendRefList do not have the same size!");
#endif
    //===== 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();//全RDO模式数
#if JVET_M0102_INTRA_SUBPARTITIONS
    PartSplit intraSubPartitionsProcOrder = TU_NO_ISP;//
    int       bestNormalIntraModeIndex    = -1;//最佳帧内模式索引
    uint8_t   bestIspOption               = NOT_INTRA_SUBPARTITIONS;//最佳ISP选项
    TUIntraSubPartitioner subTuPartitioner( partitioner );//subTU划分器
#if JVET_M0464_UNI_MTS
    bool      ispHorAllZeroCbfs = false, ispVerAllZeroCbfs = false;
    //********全RDO搜索*********//
    for (uint32_t uiMode = 0; uiMode < numModesForFullRD; uiMode++)
    {
      // set luma prediction mode
      uint32_t uiOrgMode = uiRdModeList[uiMode];//预测模式
      cu.ispMode = extendRefList[uiMode] > MRL_NUM_REF_LINES ? extendRefList[uiMode] - MRL_NUM_REF_LINES : NOT_INTRA_SUBPARTITIONS;//ISP模式
#else
    if ( !cu.ispMode && !cu.emtFlag )
    {
      m_modeCtrl->setEmtFirstPassNoIspCost( MAX_DOUBLE );
    }

    for( uint32_t ispOptionIdx = 0; ispOptionIdx < nOptionsForISP; ispOptionIdx++ )
    {
      cu.ispMode = ispOptions[ispOptionIdx];
      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++ )
      {
        // 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];
#endif
        pu.intraDir[0] = uiOrgMode;
        int multiRefIdx = 0;
        pu.multiRefIdx = multiRefIdx;
        if( cu.ispMode )//ISP模式
        {
          intraSubPartitionsProcOrder = CU::getISPType( cu, COMPONENT_Y );
          bool tuIsDividedInRows = CU::divideTuInRows( cu );
#if !OPTIMAL_IN_ANG
#if JVET_M0464_UNI_MTS
          if ( ( tuIsDividedInRows && ispHorAllZeroCbfs ) || ( !tuIsDividedInRows && ispVerAllZeroCbfs ) )
          {
            continue;
          }
#endif
          if( m_intraModeDiagRatio.at( bestNormalIntraModeIndex ) > 1.25 )
          {
            continue;
          }
          if( ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) > 1.25 && tuIsDividedInRows ) || ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) < 0.8 && !tuIsDividedInRows ) )
          {
            continue;
          }
#endif
        }
        else//Non-ISP模式
        {
          multiRefIdx = extendRefList[uiMode];
          pu.multiRefIdx = multiRefIdx;//扩展索引
          CHECK( pu.multiRefIdx && ( pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX ), "ERL" );
        }
#else
    for (uint32_t uiMode = 0; uiMode < numModesForFullRD; uiMode++)
    {
      // set luma prediction mode
      uint32_t uiOrgMode = uiRdModeList[uiMode];

      pu.intraDir[0] = uiOrgMode;
      int multiRefIdx = extendRefList[uiMode];
      pu.multiRefIdx  = multiRefIdx;
      CHECK(pu.multiRefIdx && (pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX), "ERL");
#endif

      // 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 ){//ISP
        xRecurIntraCodingLumaQT( *csTemp, subTuPartitioner, bestCurrentCost, 0, intraSubPartitionsProcOrder );
      }
      else{//non-ISP
#if JVET_M0464_UNI_MTS
        xRecurIntraCodingLumaQT( *csTemp, partitioner, bestIspOption ? bestCurrentCost : MAX_DOUBLE, -1, TU_NO_ISP, bestIspOption );//
#else
        xRecurIntraCodingLumaQT( *csTemp, partitioner, MAX_DOUBLE, -1 );
#endif
      }

      if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] )
      {
#if JVET_M0464_UNI_MTS
        if ( cu.ispMode == HOR_INTRA_SUBPARTITIONS )
        {
          ispHorAllZeroCbfs |= ( m_pcEncCfg->getUseFastISP() && csTemp->tus[0]->lheight() > 2 && csTemp->cost >= bestCurrentCost );
        }
        else
        {
          ispVerAllZeroCbfs |= ( m_pcEncCfg->getUseFastISP() && csTemp->tus[0]->lwidth() > 2 && csTemp->cost >= bestCurrentCost );
        }
#endif
        csTemp->cost = MAX_DOUBLE;
#if JVET_M0428_ENC_DB_OPT
        csTemp->costDbOffset = 0;
#endif
      }
#else
      xRecurIntraCodingLumaQT( *csTemp, partitioner );
#endif
#if !JVET_M0464_UNI_MTS
#if JVET_M0102_INTRA_SUBPARTITIONS
      if (emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() && !cu.ispMode)
#else
      if( emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() )
#endif
      {
        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;//最佳PU模式
        bestExtendRef = multiRefIdx;//最佳扩展索引
#if JVET_M0102_INTRA_SUBPARTITIONS
        bestIspOption = cu.ispMode;//最佳ISP选项
#endif
#if !JVET_M0464_UNI_MTS
#if JVET_M0102_INTRA_SUBPARTITIONS
        if (emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() && !cu.ispMode)
#else
        if( ( emtUsageFlag == 1 ) && m_pcEncCfg->getFastIntraEMT() )
#endif
        {
          m_bestModeCostStore[puIndex] = csBest->cost; //cs.cost;
        }
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS
        if( csBest->cost < bestCurrentCost )
        {
          bestCurrentCost = csBest->cost;//保持当前代价最小
        }
        if( !cu.ispMode )
        {
          bestNormalIntraModeIndex = uiMode;//最佳模式索引
        }
#endif
      }
      csTemp->releaseIntermediateData();
    } // Mode loop
#if JVET_M0102_INTRA_SUBPARTITIONS
#if !JVET_M0464_UNI_MTS
    if (!cu.ispMode && !cu.emtFlag)
    {
      m_modeCtrl->setEmtFirstPassNoIspCost(csBest->cost);
    }
    }
#endif
    cu.ispMode = bestIspOption;
#endif
#if JVET_M0427_INLOOP_RESHAPER
    cs.useSubStructure(*csBest, partitioner.chType, pu.singleChan(CHANNEL_TYPE_LUMA), true, true, keepResi, keepResi);
#else
    cs.useSubStructure( *csBest, partitioner.chType, pu.singleChan( CHANNEL_TYPE_LUMA ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
#endif
    csBest->releaseIntermediateData();
    //=== update PU data ====
    pu.intraDir[0] = uiBestPUMode;
    pu.multiRefIdx = bestExtendRef;
  }
  //===== reset context models =====
  m_CABACEstimator->getCtx() = ctxStart;
}

 

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值