H.266/VC Code Learning Note 13: getInterMergeCandidates () Function

Keywords: less

GetInterMerge Candidates () function is a very important function in inter-frame prediction technology. It is the constructor of Merge list. All Merge modes must first construct a Merge candidate list, and then extend the corresponding Merge list on the basis of this regular Merge list according to different modes. For example, the Merge list of MMVD needs to be based on the regular Merge list. Go up and select two initial MVs, then construct MMVDMerge.

Ordinary Merge lists are structured roughly as follows:
We can construct a list of candidates according to the following figure:
First, check whether the adjacent blocks in the spatial domain are available MV. The order of inspection is A1 - > B1 - > B0 - > A0 - > B2.
(2) Re-examine adjacent blocks in time domain, and the order of inspection is as follows:BR->CT
(3) If the space-time domain is not filled up after the completion of the space-time domain inspection, the historical-based reference space candidate technology will be adopted, which I will follow later.
(4) Spatial average candidates
(5) If the above methods fail to fill the candidate list, the zero vector (0, 0) is used to fill the candidate list.
The original link is as follows:
H.266/VVC related technology learning notes: the basic principles of inter prediction technology in video coding

The specific code in the VTM6.0 version is as follows: I have detailed comments on the whole list construction process, and if there is anything wrong with it, you are welcome to correct it.

//The entry of candidate list functions for inter frame regular Merge is constructed, and the candidate lists of the remaining Merge modes are based on the regular Merge list to construct or directly use the regular Merge list.
void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
                                 int mmvdList,
                                 const int& mrgCandIdx )
{
  const CodingStructure &cs  = *pu.cs;
  const Slice &slice         = *pu.cs->slice;
  const uint32_t maxNumMergeCand = slice.getMaxNumMergeCand();//Get the length of the Merge list (that is, the maximum number of candidates)
  const bool canFastExit     = pu.cs->pps->getLog2ParallelMergeLevelMinus2() == 0;//Candidate Quick Exit

#if !JVET_L0090_PAIR_AVG
  // this variable is unused if remove HEVC combined candidates
  bool isCandInter[MRG_MAX_NUM_CANDS];
#endif
  //Here, each candidate MV in the candidate list is initialized.
  for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
  {
#if !JVET_L0090_PAIR_AVG
    isCandInter[ui] = false;
#endif
    mrgCtx.GBiIdx[ui] = GBI_DEFAULT;//This parameter is an index of two-way prediction weights. Each candidate has five weights to select.
    mrgCtx.interDirNeighbours[ui] = 0;//Inter-frame prediction directions for each candidate adjacent block: forward, backward and bidirectional
    mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;//Merge types of adjacent blocks for each candidate MV

    //There are 12 mvField Neighbours in each Merge list, and a total of 12 candidate MVs are stored. Each candidate adjacent block can provide a forward MV and a backward MV, which can be adaptively selected to use one-way or two-way prediction.
    mrgCtx.mvFieldNeighbours[(ui << 1)    ].refIdx = NOT_VALID;//The forward reference frame index of each candidate's adjacent block area identifies which forward reference frame of the adjacent block is.
    mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;//A backward reference frame index for each candidate's adjacent block area identifying which backward reference frame of the adjacent block is.
#if JVET_O0057_ALTHPELIF
    mrgCtx.useAltHpelIf[ui] = false;//Whether MV uses 1/2 pixel accuracy
#endif
  }

  mrgCtx.numValidMergeCand = maxNumMergeCand;//Number of Maximum Available Candidates MV
  // compute the location of the current PU at the center of the current PU

  int cnt = 0;//Candidate counters, note the candidates that have been listed in the candidate list

  const Position posLT = pu.Y().topLeft();//Upper-left adjacent position of current block space
  const Position posRT = pu.Y().topRight();//Right Upper Adjacent Location of Current Block Space
  const Position posLB = pu.Y().bottomLeft();//Neighboring Left-Lower Positions in Current Block Space
  MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;//Motion information defining adjacent positions in five spatial domains

  //First traverse the adjacent blocks on the left (A1) side of the left
  const PredictionUnit* puLeft = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );//Get left block

  const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );//Indicate whether block A1 is available

  //If the left block is available
  if( isAvailableA1 )
  {
    miLeft = puLeft->getMotionInfo( posLB.offset(-1, 0) );//Get the motion information of the left block

#if !JVET_L0090_PAIR_AVG
    isCandInter[cnt] = true;
#endif

    // Obtaining Interframe Prediction Direction
    mrgCtx.interDirNeighbours[cnt] = miLeft.interDir;//The prediction direction of candidate adjacent blocks is obtained to the current coding block.
#if JVET_O0057_ALTHPELIF
    mrgCtx.useAltHpelIf[cnt] = miLeft.useAltHpelIf;//Using 1/2 Pixel Accuracy
#endif
    mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->GBiIdx : GBI_DEFAULT;//If it is bidirectional prediction, the weight coefficients are selected adaptively, otherwise the default idx is 2.
    // Getting Forward MV from Left Block
    mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);

    if (slice.isInterB())//If it is a B frame, retrieve the backward MV
    {
      mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miLeft.mv[1], miLeft.refIdx[1]);
    }
    if (mrgCandIdx == cnt && canFastExit)
    {
      return;
    }

    cnt++;
  }

  //Early termination
  if (cnt == maxNumMergeCand)//If the current total number of candidates equals the maximum number of Merge candidates
  {
    return;
  }


  //Traversing the adjacent blocks above above (B1), the following process is the same as the processing of the left block, you can refer to the processing of the left block.
  const PredictionUnit *puAbove = cs.getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );

  bool isAvailableB1 = puAbove && isDiffMER( pu, *puAbove ) && pu.cu != puAbove->cu && CU::isInter( *puAbove->cu );

  if( isAvailableB1 )
  {
    miAbove = puAbove->getMotionInfo( posRT.offset( 0, -1 ) );

    if( !isAvailableA1 || ( miAbove != miLeft ) )
    {
#if !JVET_L0090_PAIR_AVG
      isCandInter[cnt] = true;
#endif

      // get Inter Dir
      mrgCtx.interDirNeighbours[cnt] = miAbove.interDir;
#if JVET_O0057_ALTHPELIF
      mrgCtx.useAltHpelIf[cnt] = miAbove.useAltHpelIf;
#endif
      // get Mv from Above
      mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->GBiIdx : GBI_DEFAULT;
      mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAbove.mv[0], miAbove.refIdx[0] );

      if( slice.isInterB() )
      {
        mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAbove.mv[1], miAbove.refIdx[1] );
      }
      if (mrgCandIdx == cnt && canFastExit)
      {
        return;
      }

      cnt++;
    }
  }

  // early termination
  if( cnt == maxNumMergeCand )
  {
    return;
  }

  int spatialCandPos = cnt;

  // above right
  //Traversing through the upper right (B0) adjacent blocks, the following process is the same as the processing of the left block, you can refer to the processing of the left block.
  const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );

  bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu );

  if( isAvailableB0 )
  {
    miAboveRight = puAboveRight->getMotionInfo( posRT.offset( 1, -1 ) );

    if( !isAvailableB1 || ( miAbove != miAboveRight ) )
    {
#if !JVET_L0090_PAIR_AVG
      isCandInter[cnt] = true;
#endif

      // get Inter Dir
      mrgCtx.interDirNeighbours[cnt] = miAboveRight.interDir;
#if JVET_O0057_ALTHPELIF
      mrgCtx.useAltHpelIf[cnt] = miAboveRight.useAltHpelIf;
#endif
      // get Mv from Above-right
      mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveRight->cu->GBiIdx : GBI_DEFAULT;
      mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveRight.mv[0], miAboveRight.refIdx[0] );

      if( slice.isInterB() )
      {
        mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveRight.mv[1], miAboveRight.refIdx[1] );
      }

      if (mrgCandIdx == cnt && canFastExit)
      {
        return;
      }

      cnt++;
    }
  }
  // early termination
  if( cnt == maxNumMergeCand )
  {
    return;
  }

  //left bottom
  //Traversing through the lower left (A0) adjacent blocks, the following process is the same as the processing of the left block, you can refer to the processing of the left block.
  const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );

  bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu );

  if( isAvailableA0 )
  {
    miBelowLeft = puLeftBottom->getMotionInfo( posLB.offset( -1, 1 ) );

    if( !isAvailableA1 || ( miBelowLeft != miLeft ) )
    {
#if !JVET_L0090_PAIR_AVG
      isCandInter[cnt] = true;
#endif

      // get Inter Dir
      mrgCtx.interDirNeighbours[cnt] = miBelowLeft.interDir;
#if JVET_O0057_ALTHPELIF
      mrgCtx.useAltHpelIf[cnt] = miBelowLeft.useAltHpelIf;
#endif
      mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeftBottom->cu->GBiIdx : GBI_DEFAULT;
      // get Mv from Bottom-Left
      mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miBelowLeft.mv[0], miBelowLeft.refIdx[0] );

      if( slice.isInterB() )
      {
        mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miBelowLeft.mv[1], miBelowLeft.refIdx[1] );
      }

      if (mrgCandIdx == cnt && canFastExit)
      {
        return;
      }

      cnt++;
    }
  }
  // early termination
  if( cnt == maxNumMergeCand )
  {
    return;
  }


  // above left
  //Traversing the upper left (B2) adjacent blocks, the following process is the same as the processing of the left block, you can refer to the processing of the left block.
  if ( cnt < 4 )
  {
    const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );

    bool isAvailableB2 = puAboveLeft && isDiffMER( pu, *puAboveLeft ) && CU::isInter( *puAboveLeft->cu );

    if( isAvailableB2 )
    {
      miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) );

      if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) )
      {
#if !JVET_L0090_PAIR_AVG
        isCandInter[cnt] = true;
#endif

        // get Inter Dir
        mrgCtx.interDirNeighbours[cnt] = miAboveLeft.interDir;
#if JVET_O0057_ALTHPELIF
        mrgCtx.useAltHpelIf[cnt] = miAboveLeft.useAltHpelIf;
#endif
        mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveLeft->cu->GBiIdx : GBI_DEFAULT;
        // get Mv from Above-Left
        mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveLeft.mv[0], miAboveLeft.refIdx[0] );

        if( slice.isInterB() )
        {
          mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveLeft.mv[1], miAboveLeft.refIdx[1] );
        }

        if (mrgCandIdx == cnt && canFastExit)
        {
          return;
        }

        cnt++;
      }
    }
  }
  // early termination
  if (cnt == maxNumMergeCand)
  {
    return;
  }


  //Here, the traversal of adjacent blocks in time domain is started, and the motion information of the lower right (RB) and central (CT) positions of reference blocks in time domain is traversed.
  if (slice.getEnableTMVPFlag() && (pu.lumaSize().width + pu.lumaSize().height > 12))
  {
    //> MTK colocated-RightBottom lower right parity block
    // offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to
    Position posRB = pu.Y().bottomRight().offset( -3, -3 );//Location of the right-lower-corner parity block: Make an appropriate offset at the position of the right-lower corner of the current brightness PU
    const PreCalcValues& pcv = *cs.pcv;

    Position posC0;
    Position posC1 = pu.Y().center();//The central parity block is the central position of the current brightness PU
    bool C0Avail = false;
    if (((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight))
    {
      int posYInCtu = posRB.y & pcv.maxCUHeightMask;
      if (posYInCtu + 4 < pcv.maxCUHeight)
      {
        posC0 = posRB.offset(4, 4);
        C0Avail = true;
      }
    }

    Mv        cColMv;//MV of the current PU isotope block
    int       iRefIdx     = 0;//Reference Frame Index
    int       dir         = 0;//Prediction direction
    unsigned  uiArrayAddr = cnt;//Location of Time Domain Candidates in Candidate Queues
    bool      bExistMV    = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, iRefIdx ) )
                              || getColocatedMVP( pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx );
    //This function adjusts the scaling ratio of MV in time domain, and finally gets the motion information of the scaling PU.
    if (bExistMV)
    {
      dir     |= 1;//1-bit or
      mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);//Getting Forward MV of Colocation Block
    }

    if (slice.isInterB())//If it is a B frame, the backward MV of the parity block is obtained.
    {
      bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, iRefIdx ) )
                   || getColocatedMVP( pu, REF_PIC_LIST_1, posC1, cColMv, iRefIdx );
      if (bExistMV)
      {
        dir     |= 2;
        mrgCtx.mvFieldNeighbours[2 * uiArrayAddr + 1].setMvField(cColMv, iRefIdx);
      }
    }

    if( dir != 0 )
    {
      bool addTMvp = true;//For true, add TMVP (time domain candidate) to the Merge list
      if( addTMvp )
      {
        mrgCtx.interDirNeighbours[uiArrayAddr] = dir;
#if !JVET_L0090_PAIR_AVG
        isCandInter              [uiArrayAddr] = true;
#endif
        mrgCtx.GBiIdx[uiArrayAddr] = GBI_DEFAULT;
#if JVET_O0057_ALTHPELIF
        mrgCtx.useAltHpelIf[uiArrayAddr] = false;
#endif
        if (mrgCandIdx == cnt && canFastExit)
        {
          return;
        }

        cnt++;//Total number of candidates + 1
      }
    }
  }

  // early termination
  if (cnt == maxNumMergeCand)
  {
    return;
  }

  int maxNumMergeCandMin1 = maxNumMergeCand - 1;//Up to five candidates are selected after traversal of candidate blocks in spatial and temporal domains
  if (cnt != maxNumMergeCandMin1)//If there are less than five candidates in the current candidate list, continue with the following MVP filling, HMVP and spatial average candidates
  {
    bool isAvailableSubPu = false;
    unsigned subPuMvpPos = 0;
#if JVET_L0090_PAIR_AVG
    bool isShared = false;

    //The next step is the construction of HMVP candidates, and then the available candidates in HMVP are filled in the Merge list from back to front in turn.
    bool bFound = addMergeHMVPCand(cs, mrgCtx, canFastExit
      , mrgCandIdx
      , maxNumMergeCandMin1, cnt
      , spatialCandPos
      , isAvailableSubPu, subPuMvpPos
      , CU::isIBC(*pu.cu)
      , isShared
    );
#else
    bool bFound = addMergeHMVPCand(slice, mrgCtx, isCandInter, canFastExit
      , (mmvdList != 0 && mrgCandIdx != -1) ? (const int)mrgCandIdxIBC : mrgCandIdx
      , maxNumMergeCandMin1, cnt, cnt, isAvailableSubPu, subPuMvpPos
      , mmvdList
    );
#endif
    if (bFound)
    {
      return;
    }
  }

#if JVET_L0090_PAIR_AVG
  // pairwise-average candidates
  //HMVP is followed by paired average candidates (i.e. combined average candidates)
  {
    //If the Merge list has not been filled, then the combination averaging candidate process is performed.
    if (cnt > 1 && cnt < maxNumMergeCand)
    {

      mrgCtx.mvFieldNeighbours[cnt * 2].setMvField( Mv( 0, 0 ), NOT_VALID );//The forward MV of the current candidate block is initialized with zero MV
      mrgCtx.mvFieldNeighbours[cnt * 2 + 1].setMvField( Mv( 0, 0 ), NOT_VALID );//Initialize the backward MV of the current candidate block with zero MV
      // calculate average MV for L0 and L1 seperately
      //Calculating the average MV of L0 and L1
      unsigned char interDir = 0;


#if JVET_O0057_ALTHPELIF
      mrgCtx.useAltHpelIf[cnt] = (mrgCtx.useAltHpelIf[0] == mrgCtx.useAltHpelIf[1]) ? mrgCtx.useAltHpelIf[0] : false;
#endif
      for( int refListId = 0; refListId < (slice.isInterB() ? 2 : 1); refListId++ )
      {
        const short refIdxI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].refIdx;//MV Index of the First Candidate Block
        const short refIdxJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].refIdx;//MV Index of the Second Candidate Block

        // both MVs are invalid, skip
        if( (refIdxI == NOT_VALID) && (refIdxJ == NOT_VALID) )
        {
          continue;
        }

        interDir += 1 << refListId;
        // both MVs are valid, average these two MVs
        if( (refIdxI != NOT_VALID) && (refIdxJ != NOT_VALID) )
        {
          const Mv& MvI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;//MV of the first candidate block
          const Mv& MvJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;//MV of the second candidate block

          // average two MVs
          //Average MV of two candidate blocks to get a new MV
          Mv avgMv = MvI;
          avgMv += MvJ;
          roundAffineMv(avgMv.hor, avgMv.ver, 1);

          mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( avgMv, refIdxI );//The combined new MV is assigned to the current candidate MV.
        }
        // only one MV is valid, take the only one MV
        //If only one candidate block MV is valid, only the MV is used.
        else if( refIdxI != NOT_VALID )
        {
          Mv singleMv = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
          mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxI );
        }
        else if( refIdxJ != NOT_VALID )
        {
          Mv singleMv = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
          mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxJ );
        }
      }

      mrgCtx.interDirNeighbours[cnt] = interDir;
      if( interDir > 0 )
      {
        cnt++;//Total number of MV candidates + 1
      }
    }

    // early termination
    if( cnt == maxNumMergeCand )
    {
      return;
    }
  }
#endif

  uint32_t uiArrayAddr = cnt;


  int iNumRefIdx = slice.isInterB() ? std::min(slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1)) : slice.getNumRefIdx(REF_PIC_LIST_0);

  int r = 0;
  int refcnt = 0;

  //If you haven't filled the Merge list yet, use zero MV to do the final filling.
  while (uiArrayAddr < maxNumMergeCand)
  {
#if !JVET_L0090_PAIR_AVG
    isCandInter               [uiArrayAddr     ] = true;
#endif
    mrgCtx.interDirNeighbours [uiArrayAddr     ] = 1;
    mrgCtx.GBiIdx             [uiArrayAddr     ] = GBI_DEFAULT;
    mrgCtx.mvFieldNeighbours  [uiArrayAddr << 1].setMvField(Mv(0, 0), r);
#if JVET_O0057_ALTHPELIF
    mrgCtx.useAltHpelIf[uiArrayAddr] = false;
#endif

    if (slice.isInterB())
    {
      mrgCtx.interDirNeighbours [ uiArrayAddr          ] = 3;
      mrgCtx.mvFieldNeighbours  [(uiArrayAddr << 1) + 1].setMvField(Mv(0, 0), r);
    }

    if ( mrgCtx.interDirNeighbours[uiArrayAddr] == 1 && pu.cs->slice->getRefPic(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[uiArrayAddr << 1].refIdx)->getPOC() == pu.cs->slice->getPOC())
    {
      mrgCtx.mrgTypeNeighbours[uiArrayAddr] = MRG_TYPE_IBC;
    }

    uiArrayAddr++;

    if (refcnt == iNumRefIdx - 1)
    {
      r = 0;
    }
    else
    {
      ++r;
      ++refcnt;
    }
  }
  mrgCtx.numValidMergeCand = uiArrayAddr;
}

Posted by PickledOnion on Tue, 08 Oct 2019 10:04:05 -0700