H.266/VVC代码学习笔记13:VTM6.0中的getInterMergeCandidates()函数

getInterMergeCandidates()函数是帧间预测技术中一个非常重要的函数,它是Merge列表的构造函数,所有的Merge模式都要先构造一个Merge候选列表,然后根据不同的模式在此regularMerge列表的基础上去延伸出相对应的Merge列表,就比如MMVD的Merge列表需要在常规Merge列表的基础上去选择两个初始MV,然后构造MMVDMerge。

普通Merge列表的构造方式大致如下:
我们可以根据下图所示去构造候选列表:
①首先检查空域相邻的块是否有可用的MV,并检查冗余,检查顺序顺序为:A1——>B1——>B0——>A0——>B2。空域相邻最多选出4个候选。

② 再检查时域相邻的块,并检查冗余,检查顺序如下:BR——>CT。时域相邻最多选出1个候选。时域的候选具体可以参考TMVP技术:H.266/VVC相关技术学习笔记:VVC中TMVP技术(时域Merge列表的构建)

③如果空域时域检查完后,Merge列表仍没有填满5个(如果已经填满了5个,则不使用HMVP方法),则采用基于历史的参考空间候选技术(HMVP),同时也要检查冗余,该技术我后续会讲一下的。H.266/VVC相关技术学习笔记:帧间预测中的HMVP技术(基于历史的MV预测)

④当上述的三种填充方法填充完之后,如果填满刚好5个或者仍不足5个,则使用成对组合平均法去填充Merge列表,同时仍要检查冗余,但是成对组合平均法最多只能填充一个候选。该技术具体如下:
H.266/VVC相关技术学习笔记:VVC中成对平均候选技术(用于Merge列表的构建)

⑤如果上述4种方法都没能填满候选列表,则最后用零矢量(0,0)补满候选列表。

关于帧间预测技术的基本原理的原文链接如下:
H.266/VVC相关技术学习笔记:视频编码中帧间预测技术的基本原理

具体的在VTM6.0版本中的代码如下:关于整个列表构造的流程我都在代码中有详细的注释,其中若有什么不对的地方欢迎大家指正~

//构造帧间常规Merge的候选列表函数的入口,其余的Merge模式的候选列表都要基于该常规Merge列表去构造或者直接使用常规的Merge列表
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();//获取Merge列表的长度(即最大候选数量)
  const bool canFastExit     = pu.cs->pps->getLog2ParallelMergeLevelMinus2() == 0;//候选快速退出

#if !JVET_L0090_PAIR_AVG
  // this variable is unused if remove HEVC combined candidates
  bool isCandInter[MRG_MAX_NUM_CANDS];
#endif
  //这里对候选列表中的每一个候选的MV进行初始化
  for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
  {
#if !JVET_L0090_PAIR_AVG
    isCandInter[ui] = false;
#endif
    mrgCtx.GBiIdx[ui] = GBI_DEFAULT;//该参数是双向预测权重的索引,每个候选都有5种权重去选择
    mrgCtx.interDirNeighbours[ui] = 0;//每个候选相邻块的帧间预测方向:前向、后向、双向
    mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;//每个候选MV所在相邻块的Merge类型

    //每个Merge列表中有12个mvFieldNeighbours,总共存储12个候选MV,每个候选相邻块可以提供一个前向MV和后向MV,可以自适应地选择使用单向或者是双向预测。
    mrgCtx.mvFieldNeighbours[(ui << 1)    ].refIdx = NOT_VALID;//每个候选所在相邻块区域的前向参考帧索引,标识该相邻块的前向参考帧是哪一个
    mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;//每个候选所在相邻块区域的后向参考帧索引,标识该相邻块的后向参考帧是哪一个
#if JVET_O0057_ALTHPELIF
    mrgCtx.useAltHpelIf[ui] = false;//MV是否使用1/2像素精度
#endif
  }

  mrgCtx.numValidMergeCand = maxNumMergeCand;//最大的可用候选MV的数量
  // compute the location of the current PU        当前PU的中心位置

  int cnt = 0;//候选计数器,记下已经列入候选列表中的候选综述

  const Position posLT = pu.Y().topLeft();//当前块空域左上相邻位置
  const Position posRT = pu.Y().topRight();//当前块空域右上相邻位置
  const Position posLB = pu.Y().bottomLeft();//当前块空域左下相邻位置
  MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;//定义五个空域相邻位置的运动信息

  //首先遍历left左边(A1)相邻块
  const PredictionUnit* puLeft = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );//获取左块

  const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );//标志A1块是否可用

  //如果左块可用
  if( isAvailableA1 )
  {
    miLeft = puLeft->getMotionInfo( posLB.offset(-1, 0) );//获取左块的运动信息

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

    // 获取帧间预测方向
    mrgCtx.interDirNeighbours[cnt] = miLeft.interDir;//获取候选相邻块的预测方向赋给当前编码块
#if JVET_O0057_ALTHPELIF
    mrgCtx.useAltHpelIf[cnt] = miLeft.useAltHpelIf;//使用1/2像素精度
#endif
    mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->GBiIdx : GBI_DEFAULT;//如果是双向预测,则自适应选择权重系数,否则默认idx为2
    // 从左块获取前向MV
    mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);

    if (slice.isInterB())//如果是B帧,则获取后向MV
    {
      mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miLeft.mv[1], miLeft.refIdx[1]);
    }
    if (mrgCandIdx == cnt && canFastExit)
    {
      return;
    }

    cnt++;
  }

  //提前终止
  if (cnt == maxNumMergeCand)//如果当前候选总数等于最大的Merge候选数
  {
    return;
  }


  //遍历above上边(B1)相邻块,以下流程同左块的处理一样,可以参考左块的处理过程
  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
  //遍历右上(B0)相邻块,以下流程同左块的处理一样,可以参考左块的处理过程
  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
  //遍历左下(A0)相邻块,以下流程同左块的处理一样,可以参考左块的处理过程
  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
  //遍历左上(B2)相邻块,以下流程同左块的处理一样,可以参考左块的处理过程
  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;
  }


  //这里开始时域相邻块的遍历,遍历时域上参考块的右下(RB)以及中心(CT)位置的运动信息
  if (slice.getEnableTMVPFlag() && (pu.lumaSize().width + pu.lumaSize().height > 12))
  {
    //>> MTK colocated-RightBottom右下同位块
    // 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 );//右下角同位块的位置:在当前亮度PU右下角的位置上再做适当的偏移
    const PreCalcValues& pcv = *cs.pcv;

    Position posC0;
    Position posC1 = pu.Y().center();//中心同位块为当前亮度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;//当前PU的同位块的MV
    int       iRefIdx     = 0;//参考帧索引
    int       dir         = 0;//预测方向
    unsigned  uiArrayAddr = cnt;//时域候选在候选队列中的位置
    bool      bExistMV    = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, iRefIdx ) )
                              || getColocatedMVP( pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx );
    //该函数进行时域上MV的比例伸缩调整,最终得到比例缩放过的当前PU的运动信息。
    if (bExistMV)
    {
      dir     |= 1;//和1按位或
      mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);//获取同位块的前向MV
    }

    if (slice.isInterB())//如果是B帧,则获取同位块的后向MV
    {
      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;//为true,则将TMVP(时域候选)添加到Merge列表中
      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++;//候选总数+1
      }
    }
  }

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

  int maxNumMergeCandMin1 = maxNumMergeCand - 1;//空域和时域候选块遍历之后最多选出5个候选
  if (cnt != maxNumMergeCandMin1)//如果当前候选列表中的候选数不足5个,则继续接下来的MVP填充,HMVP以及空间平均候选
  {
    bool isAvailableSubPu = false;
    unsigned subPuMvpPos = 0;
#if JVET_L0090_PAIR_AVG
    bool isShared = false;

    //接下进行HMVP候选的构造过程,然后将HMVP中的可用的候选从后向前依次填充到Merge列表中
    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之后进行成对平均候选(即组合平均候选)
  {
    //如果Merge列表还没有被填满,则进行组合平均候选过程
    if (cnt > 1 && cnt < maxNumMergeCand)
    {

      mrgCtx.mvFieldNeighbours[cnt * 2].setMvField( Mv( 0, 0 ), NOT_VALID );//则用零MV初始化当前候选块的前向MV
      mrgCtx.mvFieldNeighbours[cnt * 2 + 1].setMvField( Mv( 0, 0 ), NOT_VALID );//则用零MV初始化当前候选块的后向MV
      // calculate average MV for L0 and L1 seperately
      //计算L0和L1的平均MV
      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索引
        const short refIdxJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].refIdx;//第二个候选块的MV索引

        // 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
          const Mv& MvJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;//第二个候选块的MV

          // average two MVs
          //平均两个候选块的MV得到一个新的MV
          Mv avgMv = MvI;
          avgMv += MvJ;
          roundAffineMv(avgMv.hor, avgMv.ver, 1);

          mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( avgMv, refIdxI );//组合后的新的MV就赋给当前的候选MV
        }
        // only one MV is valid, take the only one MV
        //如果仅有其中一个候选块MV是有效的,则只用该MV
        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++;//总的MV候选数量+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;

  //如果还没有填充满Merge列表,则使用零MV去进行最后的填充
  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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值