H.266/VVC代碼學習筆記14:xCheckRDCostMerge2Nx2N()函數

近期學習了一下xCheckRDCostMerge2Nx2N()函數,這是編碼端幀間預測中非常重要的一個函數,該函數類似於幀內的xCheckRDCostIntra()函數,是對三種Merge模式:regular_Merge、CIIPMerge、MMVD_Merge;進行率失真代價的比較,選出代價最小也就是最優的一種Merge候選。大體上看明白了該函數的流程,簡要的講一下該函數的具體的流程,如下:
1、首先進行一些初始化的操作:初始化結構信息,Merge列表的定義,PU、CU的獲取等一系列初始化操作。

2、構造常規的Merge列表以及緊接着構造MMVD的Merge列表,選出MMVD的兩個起始點。關於常規的Merge列表的構造我在之前的博客已經詳細講過了。這裏直接給出鏈接:
H.266/VVC代碼學習筆記13:getInterMergeCandidates()函數

   PU::getInterMergeCandidates(pu, mergeCtx
      , 0
    );//獲取幀間的Merge候選列表
    PU::getInterMMVDMergeCandidates(pu, mergeCtx);//構造MMVDMerge 的候選列表

3、定義關於預測值存儲的一些緩存變量如下:

  PelUnitBuf                                  acMergeBuffer[MRG_MAX_NUM_CANDS];//保存所有Merge候選運動補償出來的預測值
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
  PelUnitBuf                                  acMergeTmpBuffer[MRG_MAX_NUM_CANDS];//保存所有Merge候選運動補償出來的預測值臨時變量
#endif
  PelUnitBuf                                  acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];//保存所有Merge候選運動補償出來的真實的預測值
  PelUnitBuf *                                acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM];//保存所有Merge候選運動補償出來的預測值臨時緩存的地址
  PelUnitBuf *                                singleMergeTempBuffer;//單Merge列表候選的預測值緩存

4、關於Merge模式的一些定義以及初始化操作:

struct ModeInfo
  {
    uint32_t mergeCand;//Merge候選數。
    bool     isRegularMerge;//是否是常規Merge
    bool     isMMVD;//是否是MMVD模式
    bool     isCIIP;//是否是CIIP
    ModeInfo() : mergeCand(0), isRegularMerge(false), isMMVD(false), isCIIP(false) {}//regular模式和MMVD模式以及CIIP模式都初始化爲不可用,Merge候選數量初始化爲0
    ModeInfo(const uint32_t mergeCand, const bool isRegularMerge, const bool isMMVD, const bool isCIIP) :
      mergeCand(mergeCand), isRegularMerge(isRegularMerge), isMMVD(isMMVD), isCIIP(isCIIP) {}
  };

5、對常規的Merge候選循環遍歷進行SAD的率失真代價比較,選出代價較小的幾個候選,並隨時更新列表。此時的循環是循環regular模式的Merge列表,此時當前CU默認是regular_Merge模式,MMVD以及CIIP都是在Regular_Merge的Merge列表SAD率失真代價比較完之後的基礎上再去改進的。這裏詳細的流程如下:
①將遍歷到的當前Merge候選的運動信息放入到當前pu中去,然後進行相應的運動補償(MC)得到當前pu的預測值存入緩存區singleMergeTempBuffer中。
②計算完預測值之後對雙向MV進行MV的細化操作(即DMVR),雖然DMVR叫做解碼端運動矢量細化,但還是要在編碼端默認進行該操作,否則將會導致編解碼不一致的問題。
③MV細化完後,計算SAD的RDCost,然後更新RDcost的模式列表RdModeList,以及候選的代價列表candCostList,列表的長度最多爲uiNumMrgSATDCand

6、在常規的Merge列表粗選完之後,再對CIIP模式進行粗選;首先從上述RDCost列表中選出前四個候選進行CIIP的預測。對四個較優的RDlist中的候選分別循環進行各候選MC的預測,然後與亮度分量Planar幀內模式的預測值進行加權,選出SAD下的RDcost最優的一種CIIP的組合,也即爲CIIP模式在4個Merge候選中最好的一種模式。同樣更新RdModeList以及candCostList

7、CIIP選擇完之後再對MMVD的64種模式進行RDcost的比較。同樣更新RdModeList以及candCostList。關於MMVD的技術細節參考博客:H.266/VVC相關技術學習筆記:幀間預測技術中的MMVD技術(Merge mode with MVD)

8、使用一個閾值去限制上述代價比較完後代價候選列表的數量

9、如果CIIP模式可用,先對之前選中的最優的CIIP組合進行正式的三個分量的intra預測值的計算。爲後面在HAD細選中CIIP加權預測做準備。

10、對前面通過SADCost粗選後的RdModeList列表中的所有候選進行HAD(SATD)細選,選出最優的一種候選,可能是regular模式、CIIPMerge或者MMVDMerge中的一種。(HAD就是帶有哈達嗎變換的SAD,即SATD。是一種更爲精確的絕對誤差的比較方法。相比於SAD的比較更爲細緻)。這裏的詳細過程如下:
①初始化的操作;
②判斷當前候選是三種Merge模式中的哪一種,爲相應的模式做預測的準備;

 //判斷當前候選是三種Merge模式中的哪一種,爲相應的模式做預測的準備
      if (uiNoResidualPass == 0 && RdModeList[uiMrgHADIdx].isCIIP)//CIIP模式
      {
        cu.mmvdSkip = false;
        mergeCtx.setMergeInfo(pu, uiMergeCand);//爲當前pu設置基礎的Merge信息
        pu.mhIntraFlag = true;//將當前pu設置爲CIIP模式
        pu.regularMergeFlag = false;
        pu.intraDir[0] = PLANAR_IDX;//幀內亮度模式採用Planar模式
        CHECK(pu.intraDir[0]<0 || pu.intraDir[0]>(NUM_LUMA_MODE - 1), "out of intra mode");
        pu.intraDir[1] = DM_CHROMA_IDX;//幀內色度模式採用DM模式
      }
      else if (RdModeList[uiMrgHADIdx].isMMVD)
      {
        cu.mmvdSkip = true;//將當前pu設置爲MMVDSkip模式
#if JVET_O0249_MERGE_SYNTAX
        pu.regularMergeFlag = true;
#endif
        mergeCtx.setMmvdMergeCandiInfo(pu, uiMergeCand);
      }
      else//當前pu爲常規Merge模式
      {
        cu.mmvdSkip = false;
#if JVET_O0249_MERGE_SYNTAX
        pu.regularMergeFlag = true;
#endif
        mergeCtx.setMergeInfo(pu, uiMergeCand);
      }
      PU::spanMotionInfo( pu, mergeCtx );//爲當前pu設置基礎的運動信息

③對當前候選進行MV的細化操作,MV細化之後再去重新求出當前PU對應Merge模式的預測值。對於CIIP模式,進行三個分量的加權操作;對於MMVD模式,重新計算MMVD模式預測值(通過MC);對於regular模式,直接拷貝上面已經計算好的預測值,因爲在上面已經對regular模式下的MV細化過了。

11、調用xEncodeInterResidual()函數,編碼當前Merge候選模式的inter預測值的殘差,並在該函數中完成HAD的比較,最終選出HAD最小的那個Merge模式

12、結束HAD細選的循環之後,選出三種Merge中最優的一種Merge候選,並計算出相應的預測值以及殘差,最後計算HAD下的RDcost,再去和其餘的Merge模式(TPM、Affine之類的模式)做SATD的RDcsot的比較。

最後,附上整個xCheckRDCostMerge2Nx2N()函數的代碼,並且代碼裏有詳細的註釋,由於某些地方看的不是很細,也可能看的不是太明白,或許有出錯的地方,歡迎大家指正,不過總體的流程應該沒啥大問題。

//對Merge列表檢查RDcost,這是Merge和Skip模式的第一主函數入口
void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
  const Slice &slice = *tempCS->slice;

  CHECK( slice.getSliceType() == I_SLICE, "Merge modes not available for I-slices" );
  //初始化結構信息
  tempCS->initStructData( encTestMode.qp, encTestMode.lossless );

  MergeCtx mergeCtx;//Merge的上下文模型
  const SPS &sps = *tempCS->sps;

  if( sps.getSBTMVPEnabledFlag() )//子塊Merge模式是否使用
  {
    Size bufSize = g_miScaling.scale( tempCS->area.lumaSize() );
    mergeCtx.subPuMvpMiBuf    = MotionBuf( m_SubPuMiBuf,    bufSize );
  }

  Mv   refinedMvdL0[MAX_NUM_PARTS_IN_CTU][MRG_MAX_NUM_CANDS];
  setMergeBestSATDCost( MAX_DOUBLE );//設置Merge中最優的SATDCost
  {
    // first get merge candidates獲取第一個Merge候選
    CodingUnit cu( tempCS->area );
    cu.cs       = tempCS;
    cu.predMode = MODE_INTER;//預測模式爲幀間模式
    cu.slice    = tempCS->slice;
    cu.tileIdx  = tempCS->picture->brickMap->getBrickIdxRsMap( tempCS->area.lumaPos() );

    PredictionUnit pu( tempCS->area );
    pu.cu = &cu;
    pu.cs = tempCS;
    pu.shareParentPos = tempCS->sharedBndPos;
    pu.shareParentSize = tempCS->sharedBndSize;
    PU::getInterMergeCandidates(pu, mergeCtx
      , 0
    );//獲取幀間的Merge候選列表
    PU::getInterMMVDMergeCandidates(pu, mergeCtx);//構造MMVDMerge 的候選列表
    pu.regularMergeFlag = true;//常規Merge的flag,初始化爲可用,一開始默認當前PU爲regular模式
  }
  bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM];
  for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
  {
    candHasNoResidual[ui] = false;
  }

  bool                                        bestIsSkip = false;
  bool                                        bestIsMMVDSkip = true;
  PelUnitBuf                                  acMergeBuffer[MRG_MAX_NUM_CANDS];//保存所有Merge候選運動補償出來的預測值
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
  PelUnitBuf                                  acMergeTmpBuffer[MRG_MAX_NUM_CANDS];//保存所有Merge候選運動補償出來的預測值臨時變量
#endif
  PelUnitBuf                                  acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];//保存所有Merge候選運動補償出來的真實的預測值
  PelUnitBuf *                                acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM];//保存所有Merge候選運動補償出來的預測值臨時緩存的地址
  PelUnitBuf *                                singleMergeTempBuffer;//單Merge列表候選的預測值緩存
  int                                         insertPos;//插入的位置
  unsigned                                    uiNumMrgSATDCand = mergeCtx.numValidMergeCand + MMVD_ADD_NUM;//需要進行SATD比較的所有候選的數量:regular_Merge列表中的有效候選以及64個MMVD的候選

  struct ModeInfo
  {
    uint32_t mergeCand;//Merge候選數。
    bool     isRegularMerge;//是否是常規Merge
    bool     isMMVD;//是否是MMVD模式
    bool     isCIIP;//是否是CIIP
    ModeInfo() : mergeCand(0), isRegularMerge(false), isMMVD(false), isCIIP(false) {}//regular模式和MMVD模式以及CIIP模式都初始化爲不可用,Merge候選數量初始化爲0
    ModeInfo(const uint32_t mergeCand, const bool isRegularMerge, const bool isMMVD, const bool isCIIP) :
      mergeCand(mergeCand), isRegularMerge(isRegularMerge), isMMVD(isMMVD), isCIIP(isCIIP) {}
  };

  static_vector<ModeInfo, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM>  RdModeList;//RDCost的模式列表
  bool                                        mrgTempBufSet = false;

  for (int i = 0; i < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; i++)//常規的6個Merge候選+MMVD的64種Merge候選
  {
    if (i < mergeCtx.numValidMergeCand)
    {
      RdModeList.push_back(ModeInfo(i, true, false, false));//往需要RDCost的模式列表中Push各種可用的Merge候選
    }
    else
    {
      RdModeList.push_back(ModeInfo(std::min(MMVD_ADD_NUM, i - mergeCtx.numValidMergeCand), false, true, false));
    }
  }

  const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
  for (unsigned i = 0; i < MMVD_MRG_MAX_RD_BUF_NUM; i++)
  {
    acMergeRealBuffer[i] = m_acMergeBuffer[i].getBuf(localUnitArea);//初始化每一個候選的預測值緩存
    if (i < MMVD_MRG_MAX_RD_NUM)
    {
      acMergeTempBuffer[i] = acMergeRealBuffer + i;
    }
    else
    {
      singleMergeTempBuffer = acMergeRealBuffer + i;
    }
  }

  bool isIntrainterEnabled = sps.getUseMHIntra();//從高層語法獲取CIIP模式是否可用(是否可用和當前CU是否是CIIP模式不是一個概念)

  //如果塊尺寸小於64、寬和高大於最大CU邊長(128)時。CIIP模式不可用
  if (bestCS->area.lwidth() * bestCS->area.lheight() < 64 || bestCS->area.lwidth() >= MAX_CU_SIZE || bestCS->area.lheight() >= MAX_CU_SIZE)
  {
    isIntrainterEnabled = false;
  }
  bool isTestSkipMerge[MRG_MAX_NUM_CANDS]; // record if the merge candidate has tried skip mode
                                           //如果Merge候選嘗試採用SKip,則記錄下來 

  for (uint32_t idx = 0; idx < MRG_MAX_NUM_CANDS; idx++)
  {
    isTestSkipMerge[idx] = false;
  }
  if( m_pcEncCfg->getUseFastMerge() || isIntrainterEnabled)//CIIP模式可用或者使用快速Merge模式
  {
    uiNumMrgSATDCand = NUM_MRG_SATD_CAND;//參與SATD選擇的Merge候選數量爲4
    if (isIntrainterEnabled)
    {
      uiNumMrgSATDCand += 1;//如果CIIP模式可用,則/參與SATD選擇的Merge候選數量+1
    }
    bestIsSkip       = false;//最好模式是否是Skip

    if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
    {
      if (slice.getSPS()->getIBCFlag())//SPS層標誌IBC模式可用
      {
        ComprCUCtx cuECtx = m_modeCtrl->getComprCUCtx();
        bestIsSkip = blkCache->isSkip(tempCS->area) && cuECtx.bestCU;
      }
      else
      bestIsSkip = blkCache->isSkip( tempCS->area );//最優的模式是否是Skip
      bestIsMMVDSkip = blkCache->isMMVDSkip(tempCS->area);//最優的模式是否是MMVDSkip
    }

    if (isIntrainterEnabled) // always perform low complexity check
    {
      bestIsSkip = false;//最優的模式不是SKip
    }

    static_vector<double, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> candCostList;//候選的代價列表(最多構造6+64個)

    // 1. Pass: get SATD-cost for selected candidates and reduce their count
    //1、比較STADcost,選擇最優的候選,並減少數量
    if( !bestIsSkip )//最好的模式不是Skip
    {
      RdModeList.clear();//清除RD候選模式列表
      mrgTempBufSet       = true;
      const TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx());

      CodingUnit &cu      = tempCS->addCU( tempCS->area, partitioner.chType );//是當前CU的引用變量,意味着對裏面的值修改也就改變了外層的CU相關的參數
      const double sqrtLambdaForFirstPassIntra = m_pcRdCost->getMotionLambda(cu.transQuantBypass) * FRAC_BITS_SCALE;
      partitioner.setCUData( cu );
      cu.slice            = tempCS->slice;
      cu.tileIdx          = tempCS->picture->brickMap->getBrickIdxRsMap( tempCS->area.lumaPos() );
      cu.skip             = false;//當前CU不爲Skip模式
      cu.mmvdSkip = false;//當前CU不爲MMVDSkip模式
      cu.triangle         = false;//當前CU不爲TPM模式
    //cu.affine
      cu.predMode         = MODE_INTER;//當前CU爲幀間預測模式
    //cu.LICFlag
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp               = encTestMode.qp;
    //cu.emtFlag  is set below

      PredictionUnit &pu  = tempCS->addPU( cu, partitioner.chType );//當前pu的引用變量

      DistParam distParam;//失真參數
      const bool bUseHadamard = !encTestMode.lossless && !tempCS->slice->getDisableSATDForRD();//是否使用哈達嗎變換

      //設置失真參數
      m_pcRdCost->setDistParam (distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth (CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);

      //當前編碼區域
      const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );

      //對有效的Merge候選循環遍歷進行SAD的率失真代價比較,選出代價較小的幾個候選
      //此時的循環是循環regular模式的Merge列表,此時當前CU默認是regular_Merge模式,MMVD以及CIIP都是在Regular_Merge的Merge列表SAD率失真代價比較完之後的基礎上再去改進的
      for (uint32_t uiMergeCand = 0; uiMergeCand < mergeCtx.numValidMergeCand; uiMergeCand++)
      {
        mergeCtx.setMergeInfo(pu, uiMergeCand);//設置當前pu的運動信息,將候選中的各種運動信息全部複製到當前pu中

        PU::spanMotionInfo(pu, mergeCtx);//擴展當前pu的運動信息
        pu.mvRefine = true;//當前pu的MV可被細化
        distParam.cur = singleMergeTempBuffer->Y();//當前CU的亮度失真參數,是從單Merge列表候選的預測緩存中得到的
#if JVET_O0108_DIS_DMVR_BDOF_CIIP       
        acMergeTmpBuffer[uiMergeCand] = m_acMergeTmpBuffer[uiMergeCand].getBuf(localUnitArea);//對acMergeTmpBuffer用m_acMergeTmpBuffer進行初始化爲0BUFF
        m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer, REF_PIC_LIST_X, true, true, &(acMergeTmpBuffer[uiMergeCand]));//運動補償得到幀間預測值,將預測值暫時保存在singleMergeTempBuffer中
#else
        m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer);
#endif
        acMergeBuffer[uiMergeCand] = m_acRealMergeBuffer[uiMergeCand].getBuf(localUnitArea);//用m_acRealMergeBuffer對其進行初始化,因爲m_acRealMergeBuffer裏面的值爲NULL,是不帶BIO的預測緩存,默認爲0
        acMergeBuffer[uiMergeCand].copyFrom(*singleMergeTempBuffer);//將MC之後的預測值singleMergeTempBuffer複製到acMergeBuffer中
        pu.mvRefine = false;

        //對於雙向的預測候選,直接進行MV的細化(即DMVR)
        if (mergeCtx.interDirNeighbours[uiMergeCand] == 3 && mergeCtx.mrgTypeNeighbours[uiMergeCand] == MRG_TYPE_DEFAULT_N)
        {
          mergeCtx.mvFieldNeighbours[2 * uiMergeCand].mv = pu.mv[0];
          mergeCtx.mvFieldNeighbours[2 * uiMergeCand + 1].mv = pu.mv[1];
          {
            int dx, dy, i, j, num = 0;
            dy = std::min<int>(pu.lumaSize().height, DMVR_SUBCU_HEIGHT);
            dx = std::min<int>(pu.lumaSize().width, DMVR_SUBCU_WIDTH);
            if (PU::checkDMVRCondition(pu))//檢查該PU是否使用DMVR技術
            {
              for (i = 0; i < (pu.lumaSize().height); i += dy)
              {
                for (j = 0; j < (pu.lumaSize().width); j += dx)
                {
                  refinedMvdL0[num][uiMergeCand] = pu.mvdL0SubPu[num];
                  num++;
                }
              }
            }
          }
        }

        Distortion uiSad = distParam.distFunc(distParam);
        m_CABACEstimator->getCtx() = ctxStart;
        uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);
        double cost = (double)uiSad + (double)fracBits * sqrtLambdaForFirstPassIntra;//計算SAD下的RDCost
        insertPos = -1;
        //更新在當前塊爲regularMerge 模式下的Merge候選代價列表
        updateCandList(ModeInfo(uiMergeCand, true, false, false), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
        if (insertPos != -1)
        {
          if (insertPos == RdModeList.size() - 1)
          {
            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
          }
          else
          {
            for (uint32_t i = uint32_t(RdModeList.size()) - 1; i > insertPos; i--)
            {
              swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
            }
            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);//將singleMergeTempBuffer和acMergeTempBuffer進行交換,也就是將幀間預測值放入到acMergeTempBuffer中去。
          }
        }
        CHECK(std::min(uiMergeCand + 1, uiNumMrgSATDCand) != RdModeList.size(), "");
      }


     
      if (isIntrainterEnabled)//CIIP模式可用
      {
        // prepare for Intra bits calculation
        //爲幀內比特計算做準備
        pu.mhIntraFlag = true;//自行將mhIntraFlag設爲真,進行CIIP的率失真選擇,選出最好的inter預測值,也即最好的Merge候選

        // save the to-be-tested merge candidates
        //保存已經測試過的Merge候選
        uint32_t MHIntraMergeCand[NUM_MRG_SATD_CAND];//CIIP的Merge候選列表


        //最多從RDCost列表中選出前四個候選進行CIIP的預測。
        for (uint32_t mergeCnt = 0; mergeCnt < std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand); mergeCnt++)
        {
          MHIntraMergeCand[mergeCnt] = RdModeList[mergeCnt].mergeCand;//之前已經RD過的Merge候選列表複製給CIIP的候選列表
        }

        //這裏對四個較優的RDlist中的候選分別循環進行各候選MC的預測,然後與亮度分量Planar幀內模式的預測值進行加權,選出SAD下的RDcost最優的一種CIIP的組合,也即爲CIIP模式在4個Merge候選中最好的一種模式
        for (uint32_t mergeCnt = 0; mergeCnt < std::min(std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand), 4); mergeCnt++)
        {
          uint32_t mergeCand = MHIntraMergeCand[mergeCnt];//CIIP的Merge候選索引
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
          acMergeTmpBuffer[mergeCand] = m_acMergeTmpBuffer[mergeCand].getBuf(localUnitArea);//再一次對acMergeTmpBuffer進行初始化。爲0BUFF
#else
          acMergeBuffer[mergeCand] = m_acRealMergeBuffer[mergeCand].getBuf(localUnitArea);
#endif

          // estimate merge bits//估計Merge的比特
          mergeCtx.setMergeInfo(pu, mergeCand);
#if CIIP_B_TO_P
          
          acMergeTmpBuffer[mergeCand] = m_acMergeTmpBuffer[mergeCand].getBuf(localUnitArea);//對acMergeTmpBuffer用m_acMergeTmpBuffer進行初始化爲0BUFF
          m_pcInterSearch->motionCompensation_X(pu, *singleMergeTempBuffer, REF_PIC_LIST_X, true, true);//運動補償得到幀間預測值

          acMergeBuffer[mergeCand] = m_acRealMergeBuffer[mergeCand].getBuf(localUnitArea);//用m_acRealMergeBuffer對其進行初始化,因爲m_acRealMergeBuffer裏面的值爲NULL,是不帶BIO的預測緩存,默認爲0
          acMergeBuffer[mergeCand].copyFrom(*singleMergeTempBuffer);//將MC之後的預測值singleMergeTempBuffer複製到acMergeBuffer中
          acMergeTempBuffer[mergeCand]->copyFrom(*singleMergeTempBuffer);

#endif
          // first round第一輪的計算去計算幀內預測值
          pu.intraDir[0] = PLANAR_IDX;//幀內亮度只用Planar模式去預測
          uint32_t intraCnt = 0;//幀內數量爲0
          // generate intrainter Y prediction生成內部的亮度預測值
          if (mergeCnt == 0)//如果Merge候選數量爲0,即爲第一輪進入幀內預測模式,這裏只對Y分量進行預測
          {
            m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Y());//幀內角度預測函數入口初始化幀內分量類型
            m_pcIntraSearch->predIntraAng(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu);//幀內角度預測函數入口
            m_pcIntraSearch->switchBuffer(pu, COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
          }
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
          pu.cs->getPredBuf(pu).copyFrom(acMergeTmpBuffer[mergeCand]);//將之前得到的每個Merge候選的inter預測值acMergeTmpBuffer[mergeCand]通過getPredBuf放入到對應分量的幀間BUFF中
#else
          pu.cs->getPredBuf(pu).copyFrom(acMergeBuffer[mergeCand]);
#endif
          if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
          {
            pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
          }

          //該函數即對幀內幀間的預測值進行加權預測(同樣只針對Y分量)
          m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));

          // calculate cost計算代價
          if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
          {
            pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getInvLUT());
          }
          distParam.cur = pu.cs->getPredBuf(pu).Y();
          Distortion sadValue = distParam.distFunc(distParam);
          if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
          {
            pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
          }
          m_CABACEstimator->getCtx() = ctxStart;
#if JVET_O0249_MERGE_SYNTAX
          pu.regularMergeFlag = false;
#endif
          uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);
          double cost = (double)sadValue + (double)fracBits * sqrtLambdaForFirstPassIntra;//最終的代價函數
          insertPos = -1;

          //重新更新候選列表,更新當前pu爲CIIP模式下的Merge代價列表
          updateCandList(ModeInfo(mergeCand, false, false, true), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
          if (insertPos != -1)
          {
            for (int i = int(RdModeList.size()) - 1; i > insertPos; i--)
            {
              swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
            }
            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);//得到CIIP的代價最小的預測值(即得到了CIIP的最優的Merge候選),存下來在後面和其他Merge模式去競爭
          }
        }

        pu.mhIntraFlag = false;//CIIP選出最佳的組合以後,將當前pu的CIIPFlag設置爲false,再對後面的Merge模式進行預測,
      }

      if ( pu.cs->sps->getUseMMVD() )//sps層MMVD模式可用
      {
        cu.mmvdSkip = true;//自行設置cu.mmvdSkip爲真,進行MMVD的率失真選擇
#if JVET_O0249_MERGE_SYNTAX
        pu.regularMergeFlag = true;//自行設置pu.regularMergeFlag ,因爲MMVD的Merge候選也是需要在regular模式的基礎上得到的
#endif
        const int tempNum = (mergeCtx.numValidMergeCand > 1) ? MMVD_ADD_NUM : MMVD_ADD_NUM >> 1;
        //對MMVD候選循環遍歷,選出SAD下RDCost最優的一種MMVD的組合
        for (int mmvdMergeCand = 0; mmvdMergeCand < tempNum; mmvdMergeCand++)//循環64遍,前32種得到起始點1以及對應的8個步長
                                                                             //因爲對於每種MV擴展都可以得到4*8種組合,因此兩個起始點總共是64種組合
        {
          int baseIdx = mmvdMergeCand / MMVD_MAX_REFINE_NUM;//候選基MV(即初始MV)索引,要麼0 要麼1,(這裏就是兩個初始MV的起點,每個初始MV有32種4*8的步長+方向的組合)
          int refineStep = (mmvdMergeCand - (baseIdx * MMVD_MAX_REFINE_NUM)) / 4;//表示8種步長
          if (refineStep >= m_pcEncCfg->getMmvdDisNum())
            continue;
          //設置MMVD候選的信息,得到每個擴展MV具體每個方向以及對應的步長
          mergeCtx.setMmvdMergeCandiInfo(pu, mmvdMergeCand);

          PU::spanMotionInfo(pu, mergeCtx);
          pu.mvRefine = true;
          distParam.cur = singleMergeTempBuffer->Y();
          pu.mmvdEncOptMode = (refineStep > 2 ? 2 : 1);
          CHECK(!pu.mmvdMergeFlag, "MMVD merge should be set");
          // Don't do chroma MC here

          //計算MMVD的運動補償預測值
          m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer, REF_PIC_LIST_X, true, false);
          pu.mmvdEncOptMode = 0;
          pu.mvRefine = false;
          Distortion uiSad = distParam.distFunc(distParam);//失真函數

          m_CABACEstimator->getCtx() = ctxStart;
          uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);//計算碼率
          double cost = (double)uiSad + (double)fracBits * sqrtLambdaForFirstPassIntra; //計算RDcost
          insertPos = -1;

          //再重新更新候選列表,更新當前pu爲MMVD模式下的Merge代價列表
          updateCandList(ModeInfo(mmvdMergeCand, false, true, false), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
          if (insertPos != -1)
          {
            for (int i = int(RdModeList.size()) - 1; i > insertPos; i--)
            {
              swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
            }
            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);//將代價更小的MMVD候選預測出的預測值放入到acMergeTempBuffer[insertPos]中
          }
        }
      }
      // Try to limit number of candidates using SATD-costs
      //使用一個閾值去限制SATD列表的數量
      for( uint32_t i = 1; i < uiNumMrgSATDCand; i++ )
      {
        if( candCostList[i] > MRG_FAST_RATIO * candCostList[0] )
        {
          uiNumMrgSATDCand = i;
          break;
        }
      }

      setMergeBestSATDCost( candCostList[0] );//設置最優的一個MergeRDcost,選出最好的一個Merge候選(可能是regular,也可能是MMVDMerge,也可能是CIIPmerge)

      if (isIntrainterEnabled)//CIIP模式可用,對之前選中的最優的CIIP組合進行正式的三個分量的intra預測值的計算
      {
        pu.mhIntraFlag = true;//當前pu的模式爲CIIP

        //對每個Merge候選循環
        for (uint32_t mergeCnt = 0; mergeCnt < uiNumMrgSATDCand; mergeCnt++)
        {
          if (RdModeList[mergeCnt].isCIIP)//當前模式是CIIP模式,則對當前塊進行幀內預測
          {
            pu.intraDir[0] = PLANAR_IDX;//亮度幀內模式使用Planar
            pu.intraDir[1] = DM_CHROMA_IDX;//色度幀內模式用DM模式
            uint32_t bufIdx = 0;

            //Cb分量
            m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Cb());
            m_pcIntraSearch->predIntraAng(COMPONENT_Cb, pu.cs->getPredBuf(pu).Cb(), pu);
            m_pcIntraSearch->switchBuffer(pu, COMPONENT_Cb, pu.cs->getPredBuf(pu).Cb(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cb, bufIdx));
            //Cr分量
            m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Cr());
            m_pcIntraSearch->predIntraAng(COMPONENT_Cr, pu.cs->getPredBuf(pu).Cr(), pu);
            m_pcIntraSearch->switchBuffer(pu, COMPONENT_Cr, pu.cs->getPredBuf(pu).Cr(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cr, bufIdx));
          }
        }
        pu.mhIntraFlag = false;
      }

      tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
      m_CABACEstimator->getCtx() = ctxStart;
    }
    else//如果是Skip模式
    {
      if (bestIsMMVDSkip)//如果最優模式是MMVDSkip模式
      {
        uiNumMrgSATDCand = mergeCtx.numValidMergeCand + ((mergeCtx.numValidMergeCand > 1) ? MMVD_ADD_NUM : MMVD_ADD_NUM >> 1);
        //uiNumMrgSATDCand的數量等於常規Merge列表中可用候選的數量+MMVD候選的數量(64/32)
      }
      else
      {
        uiNumMrgSATDCand = mergeCtx.numValidMergeCand;
      }
    }
  }
  m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
  uint32_t iteration;
  uint32_t iterationBegin = 0;
  if (encTestMode.lossless)//如果是殘差爲零,即無損編碼,則只迭代一次
  {
    iteration = 1;
  }
  else//否則迭代兩次
  {
    iteration = 2;
  }

//HAD就是帶有哈達嗎變換的SAD,即SATD。是一種更爲精確的絕對誤差的比較方法。相比於SAD的比較更爲細緻

  for (uint32_t uiNoResidualPass = iterationBegin; uiNoResidualPass < iteration; ++uiNoResidualPass)
  {
    //對前面通過SADCost粗選後的RdModeList列表中的所有候選進行HAD(SATD)細選,選出最優的一種候選,可能是regular模式、CIIPMerge或者MMVDMerge中的一種
    for( uint32_t uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ )
    {
      uint32_t uiMergeCand = RdModeList[uiMrgHADIdx].mergeCand;

      if (uiNoResidualPass != 0 && RdModeList[uiMrgHADIdx].isCIIP) // CIIP不支持Skip模式
      {
        if (isTestSkipMerge[uiMergeCand])
        {
          continue;
        }
      }

      if (((uiNoResidualPass != 0) && candHasNoResidual[uiMrgHADIdx])
       || ( (uiNoResidualPass == 0) && bestIsSkip ) )
      {
        continue;
      }

      // first get merge candidates
      //首選獲取Merge候選們
      CodingUnit &cu      = tempCS->addCU( tempCS->area, partitioner.chType );//定義當前CU的引用變量

      partitioner.setCUData( cu );
      cu.slice            = tempCS->slice;
      cu.tileIdx          = tempCS->picture->brickMap->getBrickIdxRsMap( tempCS->area.lumaPos() );
      cu.skip             = false;//Skip模式默認關閉
      cu.mmvdSkip = false;//MMVDSkip模式默認關閉
      cu.triangle         = false;//TPM模式默認關閉
    //cu.affine
      cu.predMode         = MODE_INTER;
    //cu.LICFlag
      cu.transQuantBypass = encTestMode.lossless;
      cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
      cu.qp               = encTestMode.qp;
      PredictionUnit &pu  = tempCS->addPU( cu, partitioner.chType );//定義當前pu的引用變量


      //判斷當前候選是三種Merge模式中的哪一種,爲相應的模式做預測的準備
      if (uiNoResidualPass == 0 && RdModeList[uiMrgHADIdx].isCIIP)//CIIP模式
      {
        cu.mmvdSkip = false;
        mergeCtx.setMergeInfo(pu, uiMergeCand);//爲當前pu設置基礎的Merge信息
        pu.mhIntraFlag = true;//將當前pu設置爲CIIP模式
        pu.regularMergeFlag = false;
        pu.intraDir[0] = PLANAR_IDX;//幀內亮度模式採用Planar模式
        CHECK(pu.intraDir[0]<0 || pu.intraDir[0]>(NUM_LUMA_MODE - 1), "out of intra mode");
        pu.intraDir[1] = DM_CHROMA_IDX;//幀內色度模式採用DM模式
      }
      else if (RdModeList[uiMrgHADIdx].isMMVD)
      {
        cu.mmvdSkip = true;//將當前pu設置爲MMVDSkip模式
#if JVET_O0249_MERGE_SYNTAX
        pu.regularMergeFlag = true;
#endif
        mergeCtx.setMmvdMergeCandiInfo(pu, uiMergeCand);
      }
      else//當前pu爲常規Merge模式
      {
        cu.mmvdSkip = false;
#if JVET_O0249_MERGE_SYNTAX
        pu.regularMergeFlag = true;
#endif
        mergeCtx.setMergeInfo(pu, uiMergeCand);
      }
      PU::spanMotionInfo( pu, mergeCtx );//爲當前pu設置基礎的運動信息



      if( m_pcEncCfg->getMCTSEncConstraint() )
      {
        bool isDMVR = PU::checkDMVRCondition( pu );
        if( ( isDMVR && MCTSHelper::isRefBlockAtRestrictedTileBoundary( pu ) ) || ( !isDMVR && !( MCTSHelper::checkMvBufferForMCTSConstraint( pu ) ) ) )
        {
          // Do not use this mode
          tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
          continue;
        }
      }

      //對當前候選進行MV的細化操作
      if( mrgTempBufSet )
      {
        {
          int dx, dy, i, j, num = 0;
          dy = std::min<int>(pu.lumaSize().height, DMVR_SUBCU_HEIGHT);
          dx = std::min<int>(pu.lumaSize().width, DMVR_SUBCU_WIDTH);
          //對當前候選運用DMVR技術
          if (PU::checkDMVRCondition(pu))
          {
            for (i = 0; i < (pu.lumaSize().height); i += dy)
            {
              for (j = 0; j < (pu.lumaSize().width); j += dx)
              {
                pu.mvdL0SubPu[num] = refinedMvdL0[num][uiMergeCand];
                num++;
              }
            }
          }
        }

        //MV細化之後再去重新求出當前PU對應Merge模式的預測值
        if (pu.mhIntraFlag)//CIIP模式可用
        {
          uint32_t bufIdx = 0;
          PelBuf tmpBuf = tempCS->getPredBuf(pu).Y();//獲取亮度塊的幀間預測信號
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
          tmpBuf.copyFrom(acMergeTmpBuffer[uiMergeCand].Y());
#else
          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Y());
#endif
          if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
          {
            tmpBuf.rspSignal(m_pcReshape->getFwdLUT());
          }

          //計算幀內幀間加權(對亮度做)
          m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, bufIdx));
          tmpBuf = tempCS->getPredBuf(pu).Cb();
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
          tmpBuf.copyFrom(acMergeTmpBuffer[uiMergeCand].Cb());
#else
          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Cb());
#endif
          //計算幀內幀間加權(對Cb分量做)
          m_pcIntraSearch->geneWeightedPred(COMPONENT_Cb, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cb, bufIdx));
          tmpBuf = tempCS->getPredBuf(pu).Cr();
#if JVET_O0108_DIS_DMVR_BDOF_CIIP
          tmpBuf.copyFrom(acMergeTmpBuffer[uiMergeCand].Cr());
#else
          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Cr());
#endif
          //計算幀內幀間加權(幀間爲Affine模式,對Cr分量做)
          m_pcIntraSearch->geneWeightedPred(COMPONENT_Cr, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cr, bufIdx));
        }
        else
        {
          if (RdModeList[uiMrgHADIdx].isMMVD)//重新計算MMVD模式預測值
          {
            pu.mmvdEncOptMode = 0;
            m_pcInterSearch->motionCompensation(pu);
          }
          else if (uiNoResidualPass != 0 && RdModeList[uiMrgHADIdx].isCIIP)//CIIP不支持Skip模式
          {
            tempCS->getPredBuf().copyFrom(acMergeBuffer[uiMergeCand]);
          }
          else//regular模式
          {
            tempCS->getPredBuf().copyFrom(*acMergeTempBuffer[uiMrgHADIdx]);
          }
        }
      }
      else//如果mrgTempBufSet爲false,強制開啓MV細化功能
      {
        pu.mvRefine = true;
        m_pcInterSearch->motionCompensation( pu );
        pu.mvRefine = false;
      }

      //如果當前候選不是MMVD也不是CIIP模式
      if (!cu.mmvdSkip && !pu.mhIntraFlag && uiNoResidualPass != 0)
      {
        CHECK(uiMergeCand >= mergeCtx.numValidMergeCand, "out of normal merge");
        isTestSkipMerge[uiMergeCand] = true;
      }

      //編碼當前Merge候選模式的inter預測值的殘差,並在該函數中完成HAD的比較,最終選出HAD最小的那個Merge模式
      xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, uiNoResidualPass == 0 ? &candHasNoResidual[uiMrgHADIdx] : NULL );

      if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip && !pu.mhIntraFlag)
      {
        bestIsSkip = !bestCS->cus.empty() && bestCS->getCU( partitioner.chType )->rootCbf == 0;
      }
      tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
    }// end loop uiMrgHADIdx  結束HAD細選的循環

    if( uiNoResidualPass == 0 && m_pcEncCfg->getUseEarlySkipDetection() )
    {
      const CodingUnit     &bestCU = *bestCS->getCU( partitioner.chType );
      const PredictionUnit &bestPU = *bestCS->getPU( partitioner.chType );

      if( bestCU.rootCbf == 0 )
      {
        if( bestPU.mergeFlag )
        {
          m_modeCtrl->setEarlySkipDetected();
        }
        else if( m_pcEncCfg->getMotionEstimationSearchMethod() != MESEARCH_SELECTIVE )
        {
          int absolute_MV = 0;

          for( uint32_t uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
          {
            if( slice.getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
            {
              absolute_MV += bestPU.mvd[uiRefListIdx].getAbsHor() + bestPU.mvd[uiRefListIdx].getAbsVer();
            }
          }

          if( absolute_MV == 0 )
          {
            m_modeCtrl->setEarlySkipDetected();
          }
        }
      }
    }
  }


  //經過上面的HAD細選流程後選出三種Merge中最優的一種Merge候選,並計算出相應的預測值以及殘差,最後計算HAD下的RDcost,再去和其餘的Merge模式(TPM、Affine之類的模式)做SATD的RDcsot的比較
  if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
  {
    xCalDebCost( *bestCS, partitioner );
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章