ATMVP和STMVP的相关总结

本文详细介绍了ATMVP(可选时域运动矢量预测)和STMVP(空-时域运动矢量预测)两种预测方法。这两种方法作为merge模式的候选者,能够进一步划分CU,并对子CU进行运动信息预测,从而提高编码效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

后面看OBMC的时候,发现人家说,ATMVP的每个子块都是一个MC块,这是为啥啊?这边说ATMVP和STMVP都只是对merge只添加一个候选的事啊?为什么要把每个子块都当成MC块?难道说对于每个子块的运动信息,都是要去做MC,然后checkRD吗?确定一个合适的MV之后才加入到merge的候选列表中?但是代码中没有MC这个小CU的过程啊?还是说就是在merge列表选最优的时候,补偿列表中MV的对应块而已?这样就是对某个子块做MV了。

ATMVP(alternative temporal motion vector prediction)可选时域运动矢量预测

STMVP(spatial temporal motino vector prediction)空-时域运动矢量预测

这两种方法都是作为新添加的merge模式的候选被加入到merge的list中的。ATMVP和STMVP都有专门的参数来控制开关

在JEM中,每个CU原本都是按照和HEVC一样的方式去建立merge的候选列表的,即搜索当前CU的5个邻块和时域向量。ATMVP和STMVP的加入,使得原来CU可以划分为更小的CU,然后对子CU在对应的参考帧中去寻找对应的MV信息,然后再把对应的MV信息做一定的判断之后,加入到merge模式的候选列表中。同样的,对于STMVP,是对当前CU划分为子CU,然后再从当前帧中当前块的空间邻块中寻找对应的MV信息,做处理之后,加入到merge的候选列表中。那么这样来看,因为新增加了两个候选者,再最终做RDcost检查的时候,就要多考虑这两个的影响,这样一来,是对性能有所提升的。

ATMVP:

分为两步:1.在当前CU内,通过时域矢量来识别参考帧和对应块。2.将当前CU划分为子CU,然后在参考帧中寻找对应CU的MV信息。

第一步中,参考图像和对应块由当前CU的空间相邻块的运动信息所决定。为了避免相邻块的重复扫描,使用当前CU的merge列表中第一个候选块。第一个可用的运动矢量和它的参考序号被设为时域向量和运动源图像的索引。(其实就是为了找到运动源图像也就是参考图像)这样,ATMVP会比TMVP的对应块被更精确的识别。(对应块是位于当前CU的右下或者中心的位置。而且为什么会被更精确的识别呢?我觉得是因为它不只给了MV,还给了索引,导致会直接去找对应索引的图像,会更方便。)

           

第二步,子CU的对应块通过运动源图像中的时域向量被识别,这个过程是通过给当前CU的坐标添加时域向量实现的。对于每个子CU,其对应块的运动信息被用来获取当前块子CU的运动信息。在N*N的块的运动信息被识别出来后,被转化为运动矢量和参考帧索引。

具体代码实现,主要是通过以下函数实现的

getInterMergeSubPUTmvpCandidate( uiAbsPartIdx,  uiPUIdx,  iCount, pcMvFieldNeighbours, puhInterDirNeighbours,pcMvFieldSP[MGR_TYPE_SUBPU_ATMVP], puhInterDirSP[MGR_TYPE_SUBPU_ATMVP], &pcMvFieldNeighbours[iCount<<1], &puhInterDirNeighbours[iCount], bMrgIdxMatchATMVPCan,
#if VCEG_AZ06_IC
      bICFlag,
#endif
      pDecCurrCU, uiDecCurrAbsPartIdx);

进去之后:

{
#if JVET_C0035_ATMVP_SIMPLIFICATION
  TComPic *pColPic = getSlice()->getRefPic( RefPicList(getSlice()->isInterB() ? 1-getSlice()->getColFromL0Flag() : 0), getSlice()->getColRefIdx());
  Int iPocColPic             = pColPic->getPOC();
  TComMv cTMv;

  RefPicList eFetchRefPicList=RefPicList(getSlice()->isInterB() ? 1-getSlice()->getColFromL0Flag() : 0);
  if (uiCount)
  {
    UInt uiN=0;
    for( UInt uiCurrRefListId = 0; uiCurrRefListId < (getSlice()->getSliceType() == B_SLICE ?  2 : 1 ) ; uiCurrRefListId++ )
    {
      RefPicList  eCurrRefPicList = RefPicList( RefPicList( getSlice()->isInterB() ? (getSlice()->getColFromL0Flag()? uiCurrRefListId: 1- uiCurrRefListId) : uiCurrRefListId ));
      if ( puhInterDirNeighbours[uiN] & (1<<eCurrRefPicList) )
      {
        iPocColPic = getSlice()->getRefPic( eCurrRefPicList, pcMvFieldNeighbours[uiN*2+eCurrRefPicList].getRefIdx())->getPOC() ;
        cTMv  = pcMvFieldNeighbours[uiN*2+eCurrRefPicList].getMv();//获取第一个邻块的MV
        eFetchRefPicList = eCurrRefPicList;
        break;
      }
    }
  }
#endif

  TComDataCU* pcTempCU;
  Int iTempCUAddr, iTempAbsPartIdx, iTempPosX, iTempPosY;
  Int iPartition     = 0;
  Int iInterDirSaved = 0;
#if VCEG_AZ06_IC
  Bool bTmpICFlag;
#endif
  ///////////////////////////////////////////////////////////////////////
  ////////          GET Initial Temporal Vector                  ////////
  ///////////////////////////////////////////////////////////////////////
  
  TComMv cTempVector = cTMv;

  // compute the location of the current PU
  Int iCurrPosX, iCurrPosY, iWidth, iHeight;
  Int iPUWidth, iPUHeight, iNumPart, iNumPartLine;

  getPartPosition( uiPUIdx, iCurrPosX, iCurrPosY,      iWidth, iHeight);
  getSPPara      (  iWidth,  iHeight,  iNumPart, iNumPartLine, iPUWidth, iPUHeight);

  Int iOffsetX = iPUWidth/2;;
  Int iOffsetY = iPUHeight/2;
  
  TComMv cColMv;
  // use coldir.
  Bool bBSlice = getSlice()->isInterB();
  UInt bColL0  = getSlice()->getColFromL0Flag();
#if JVET_C0035_ATMVP_SIMPLIFICATION
  pColPic = getPicfromPOC(iPocColPic);
#else
  TComPic *pColPic = getPicfromPOC(iPocColPic);
#endif


  Bool bATMVPAvailFlag = false;
  TComMvField cDefaultMvField[2];
  cDefaultMvField[0].getMv().set(0, 0);
  cDefaultMvField[1].getMv().set(0, 0);


  Int         iTempCenterCUAddr, iTempCenterAbsPartIdx;
  Int         iCenterPosX, iCenterPosY;
#if VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE
  Int nOffset = 1 << ( VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE + 1 ); // 2 + VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE - 1
#endif
  
  Bool bInit = false;
  for( UInt uiLX = 0; uiLX < (bBSlice ? 2:1) && !bATMVPAvailFlag; uiLX++)//感觉一碰到2就是和前后向有关系。
  {//7281 rows
    RefPicList eListY = RefPicList( bBSlice ? (bColL0 ? uiLX: 1- uiLX) : uiLX );
    for (Int refIdxY = (bInit ? 0 : -1); refIdxY < getSlice()->getNumRefIdx(eListY) && !bATMVPAvailFlag; refIdxY++)//想的是在TMVP中寻找,这里参考的是外循环方向上的第几帧
    {//7280 rows
      if (!bInit) bInit = true;//第一次进来时false,第二次进来就是true了。//这么说的话,感觉,第一次进来是用的当前帧吧?(好像还不是,第一次应该是反方向上的帧吧,因为for刚来时是-1)
      else															   //然后第二次进来才是在list几的参考帧中找对应的块?
      {
        pColPic = getSlice()->getRefPic(eListY, refIdxY);
#if JVET_C0035_ATMVP_SIMPLIFICATION
        eFetchRefPicList = eListY;
#endif
      }
      Int iNewColPicPOC = pColPic->getPOC();
      if ( iNewColPicPOC!= iPocColPic)
      {
      //////////////// POC based scaling of the temporal vector /////////////
          Int iScale = xGetDistScaleFactor(getSlice()->getPOC(), iNewColPicPOC, getSlice()->getPOC(), iPocColPic);
        if ( iScale != 4096 )
          cTempVector=cTMv.scaleMv( iScale );
      }
      else
        cTempVector=cTMv;

#if VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE//下面这好像涉及到像素点的位置了                           //下面这个变量感觉就基本确定了时域中的位置
      iCenterPosX = iCurrPosX + ( ( iWidth /  iPUWidth ) >> 1 )  * iPUWidth + ( iPUWidth >> 1 ) +   ((cTempVector.getHor()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE)); 
      iCenterPosY = iCurrPosY + ( ( iHeight /  iPUHeight ) >> 1 )  * iPUHeight + (iPUHeight >> 1) + ((cTempVector.getVer()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE));
#else                              //中间位置得块                 *每个块的宽高   +  每个块的一半   +运动矢量,即偏移。
      iCenterPosX = iCurrPosX + ( ( iWidth /  iPUWidth ) >> 1 )  * iPUWidth + ( iPUWidth >> 1 ) +   ((cTempVector.getHor()+2)>>2); 
      iCenterPosY = iCurrPosY + ( ( iHeight /  iPUHeight ) >> 1 )  * iPUHeight + (iPUHeight >> 1) + ((cTempVector.getVer()+2)>>2) ;
#endif


      if(iWidth == iPUWidth && iHeight == iPUHeight)
      {
#if VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE
        iCenterPosX = iCurrPosX + (iWidth >> 1)  + ((cTempVector.getHor()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE));
        iCenterPosY = iCurrPosY + (iHeight >> 1) + ((cTempVector.getVer()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE));
#else
        iCenterPosX = iCurrPosX + (iWidth >> 1)  + ((cTempVector.getHor()+2)>>2);
        iCenterPosY = iCurrPosY + (iHeight >> 1) + ((cTempVector.getVer()+2)>>2);
#endif
      }

      iCenterPosX = Clip3( 0, pColPic->getPicYuvRec()->getWidth (COMPONENT_Y) - 1,  iCenterPosX  );
      iCenterPosY = Clip3( 0, pColPic->getPicYuvRec()->getHeight(COMPONENT_Y) - 1,  iCenterPosY  );

      // derivation of center motion parameters from the collocated CU
      pColPic->getCUAddrAndPartIdx( iCenterPosX , iCenterPosY , iTempCenterCUAddr, iTempCenterAbsPartIdx );//获取同位CU的addr和idx。
      TComDataCU* pcDefaultCU    = pColPic->getCtu( iTempCenterCUAddr );
      if( pcDefaultCU->getPredictionMode( iTempCenterAbsPartIdx ) != MODE_INTRA )
      {
        for( UInt uiCurrRefListId = 0; uiCurrRefListId < (bBSlice ? 2:1) ; uiCurrRefListId++ )
        {
          RefPicList  eCurrRefPicList = RefPicList( uiCurrRefListId );
          if (deriveScaledMotionTemporalForOneDirection(pcDefaultCU,eCurrRefPicList,cColMv, iTempCenterAbsPartIdx,0 //好吧,跑了那么多次,啥都没看出来
#if VCEG_AZ06_IC                                                      //那我猜一下,这就是对每个小块获取MV的过程,因为里面有getMV,而且传进去了当前的小块
            , bTmpICFlag
#endif
            ,pColPic
#if JVET_C0035_ATMVP_SIMPLIFICATION
            ,eFetchRefPicList
#endif
            ))
          {
            cDefaultMvField[uiCurrRefListId].setMvField(cColMv,0);//这个循环就只能进一次或者因为B帧进两次,存下来时域向量在参考帧中对应块的MV。
            bATMVPAvailFlag = true; // keep this variable here for later algrithm tuning
#if VCEG_AZ06_IC
            rbICFlag = bTmpICFlag;
#endif
          }
        }
      }
    }
  }
  // The advanced TMVP candidate is considered as available if and only if the center block contains motion 
  if ( bATMVPAvailFlag == true )
  {//7356
    // perform ATMVP based on center now
#if VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE
    iCurrPosX += ((cTempVector.getHor()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE));
    iCurrPosY += ((cTempVector.getVer()+nOffset)>>(2+VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE));
#else
    iCurrPosX += ((cTempVector.getHor()+2)>>2);
    iCurrPosY += ((cTempVector.getVer()+2)>>2);
#endif
    iInterDirSaved = (cDefaultMvField[0].getRefIdx() !=-1 ? 1: 0) + (cDefaultMvField[1].getRefIdx() !=-1 ? 2: 0);

    Int iPicWidth  = pColPic->getPicYuvRec()->getWidth (COMPONENT_Y) - 1;
    Int iPicHeight = pColPic->getPicYuvRec()->getHeight(COMPONENT_Y) - 1;

    for (Int i=iCurrPosY; i < iCurrPosY + iHeight; i += iPUHeight)
      for (Int j = iCurrPosX; j < iCurrPosX + iWidth; j += iPUWidth)//这里才是对子块赋MV的过程吧
      {
        iTempPosX     = j + iOffsetX;
        iTempPosY     = i + iOffsetY; 

        iTempPosX = Clip3( 0, iPicWidth,   iTempPosX  );
        iTempPosY = Clip3( 0, iPicHeight,  iTempPosY  );

        pColPic->getCUAddrAndPartIdx( iTempPosX, iTempPosY, iTempCUAddr, iTempAbsPartIdx );
        pcTempCU  = pColPic->getCtu( iTempCUAddr );

        if( pcTempCU && !pcTempCU->isIntra(iTempAbsPartIdx) )
        {
          for( UInt uiCurrRefListId = 0; uiCurrRefListId < (bBSlice ? 2:1); uiCurrRefListId++ )
          {
            RefPicList  eCurrRefPicList = RefPicList( uiCurrRefListId );
            if (deriveScaledMotionTemporalForOneDirection(pcTempCU,eCurrRefPicList, cColMv, iTempAbsPartIdx, 0, 
#if VCEG_AZ06_IC
              bTmpICFlag,
#endif
              pColPic 
#if JVET_C0035_ATMVP_SIMPLIFICATION
              ,eFetchRefPicList
#endif
              ))
            {
              pcMvFieldSP[2*iPartition + uiCurrRefListId].setMvField(cColMv,0);//这也确实是给几个子块赋值了
            }                
          }
        }
        else // intra coded, in this case, no motion vector is available for list 0 or list 1
        {
          pcMvFieldSP[2*iPartition + 0].setMvField(cDefaultMvField[0].getMv(),cDefaultMvField[0].getRefIdx());
          pcMvFieldSP[2*iPartition + 1].setMvField(cDefaultMvField[1].getMv(),cDefaultMvField[1].getRefIdx());
        }
        puhInterDirSP[iPartition] = (pcMvFieldSP[2*iPartition].getRefIdx()!=-1 ? 1: 0) + (pcMvFieldSP[2*iPartition+1].getRefIdx()!=-1 ? 2: 0);
        iPartition++;
      }  
      pcMvFieldDefault[0].setMvField(cDefaultMvField[0].getMv(),cDefaultMvField[0].getRefIdx());  //这个赋值啊,好像只是赋了那个初始确定的时域向量
      pcMvFieldDefault[1].setMvField(cDefaultMvField[1].getMv(),cDefaultMvField[1].getRefIdx());
      pcInterDirDefault[0] = iInterDirSaved ;
       
      if( bMrgIdxMatchATMVPCan) // only invoked at the decoder since here the parsing of the motion vectors are skipped
      {
        //store the sub-PU motion information
        UInt uiSPAddr;
        Int iNumSPInOneLine, iNumSP, iSPWidth, iSPHeight;
        getSPPara( iWidth, iHeight, iNumSP, iNumSPInOneLine, iSPWidth, iSPHeight);
        for (Int iPartitionIdx = 0; iPartitionIdx < iNumSP; iPartitionIdx++)
        {
          pDecCurrCU->getSPAbsPartIdx(uiDecCurrAbsPartIdx, iSPWidth, iSPHeight, iPartitionIdx, iNumSPInOneLine, uiSPAddr);
          pDecCurrCU->setInterDirSP(puhInterDirSP[iPartitionIdx], uiSPAddr, iSPWidth, iSPHeight);
          pDecCurrCU->getCUMvField( REF_PIC_LIST_0 )->setMvFieldSP(pDecCurrCU, uiSPAddr, pcMvFieldSP[2*iPartitionIdx], iSPWidth, iSPHeight);
          pDecCurrCU->getCUMvField( REF_PIC_LIST_1 )->setMvFieldSP(pDecCurrCU, uiSPAddr, pcMvFieldSP[2*iPartitionIdx + 1], iSPWidth, iSPHeight);
        }
      }
      return true;
  } 
  return false;
}

STMVP:

子CU的运动矢量按照栅格扫描的顺序迭代获得。对于以下图:

                                                  

子CU A的运动推导起始于识别两个空间邻居。第一个邻居是子CU A之上的N*N块,如果该块不可用或者被帧内编码,则检查子CU A上方其他N*N个块(从左到右,从块c开始)。第二个邻居是子CU A左边的一个块,如果该块不可用或被帧内编码,则检查子CU A左边的其他块(从上到下,从b开始)。对于每个列表从相邻块获得的运动信息根据参考帧被scale。然后导出A块的时间预测矢量,位置D的同位块的运动信息被取出并scale。最后,对于每个参考列表,对所有可用的运动矢量(最多三个)分别进行平均,并分配给当前子CU。

对于代码,使用以下函数:

getInterMergeSubPURecursiveCandidate(  uiAbsPartIdx,  uiPUIdx, pcMvFieldNeighbours, puhInterDirNeighbours, numValidMergeCand, peMergeTypeNeighbors  , pcMvFieldSP , puhInterDirSP,  iCount );

进去之后:

{
    Bool bAtmvpAva=false;
    Int iPartitionIdx     = 0;
    // compute the location of the current PU
    Int iCurrPosX, iCurrPosY;
    Int iWidth, iHeight;
    Int iPUWidth, iPUHeight, iNumPart, iNumPartLine;

    getPartPosition( uiPUIdx, iCurrPosX, iCurrPosY,      iWidth, iHeight);
    getSPPara      ( iWidth,  iHeight,  iNumPart, iNumPartLine, iPUWidth, iPUHeight);
#if JVET_C0035_ATMVP_SIMPLIFICATION
    UInt uiCurMRGType = MGR_TYPE_SUBPU_ATMVP_EXT;
#endif  
    UInt uiSameCount=0;
    UInt uiSameCountATMVP=0;
    for (Int i=0; i <  iHeight; i += iPUHeight)//这就分成亚PU了
    {//6385
      for (Int j = 0; j <  iWidth; j += iPUWidth)
      {//6384
          TComMvField cMvField[6],cMvFieldMedian[2];
          UChar ucInterDir[3],ucInterDirMedian=0;
          UInt uiMVCount=0;
          UInt uiSPAddr=0;
          TComDataCU* pcCULeftMedian= 0,*pcCUAboveMedian= 0;
          UInt uiLeftPartIdx_median=0, uiAbovePartIdx_median=0;
          getSPAbsPartIdx(uiAbsPartIdx, iPUWidth, iPUHeight, iPartitionIdx, iNumPartLine, uiSPAddr);
          uiSPAddr += m_absZIdxInCtu;
          //get left    1   
          if (iPartitionIdx%iNumPartLine ==0)//看是不是第一行
          {   //下面的for循环其实就是STMVP中如果c块不可用就去找右边的d块的过程
              for (UInt uiCurAddrY = i/iPUHeight; uiCurAddrY <iHeight/iPUHeight ; uiCurAddrY++)//这边是遍历了第一行的32个子PU(4*4)
              {
                UInt uiSPAddrCur=0;
                getSPAbsPartIdx(uiAbsPartIdx, iPUWidth, iPUHeight, uiCurAddrY* iNumPartLine, iNumPartLine, uiSPAddrCur);
                uiSPAddrCur += m_absZIdxInCtu;//这边这种形式应该是绝对地址的关系,ui获取了相对地址,加上绝对地址就得到了当前PU的地址。

                pcCULeftMedian = getPULeft( uiLeftPartIdx_median, uiSPAddrCur );
                if ( pcCULeftMedian &&!pcCULeftMedian->isIntra( uiLeftPartIdx_median ))//这个if估计在第一次循环或者周围MV信息或者CU信息不可用时,就不会进去。
                {
                  getNeighboringMvField(pcCULeftMedian,uiLeftPartIdx_median,cMvField,ucInterDir);
                  uiMVCount++;
                  break;
                }
              }
          }
          else
          {
#if JVET_C0035_ATMVP_SIMPLIFICATION
            ucInterDir[0]= puhInterDirSP[uiCurMRGType][iPartitionIdx-1];
            cMvField[0]=pcMvFieldSP[uiCurMRGType][((iPartitionIdx-1)<<1)  ];
            cMvField[1]=pcMvFieldSP[uiCurMRGType][((iPartitionIdx-1)<<1)+1];
#else
            ucInterDir[0]= puhInterDirSP[1][iPartitionIdx-1];
            cMvField[0]=pcMvFieldSP[1][((iPartitionIdx-1)<<1)  ];
            cMvField[1]=pcMvFieldSP[1][((iPartitionIdx-1)<<1)+1];
#endif
            uiMVCount++;
          }
          //get above   2
          if (iPartitionIdx < iNumPartLine)
          {
              for (UInt uiCurAddrX = iPartitionIdx; uiCurAddrX <iNumPartLine ; uiCurAddrX++)
              {
                UInt uiSPAddrCur=0;
                getSPAbsPartIdx(uiAbsPartIdx, iPUWidth, iPUHeight, uiCurAddrX, iNumPartLine, uiSPAddrCur);
                uiSPAddrCur += m_absZIdxInCtu;
                pcCUAboveMedian = getPUAbove( uiAbovePartIdx_median, uiSPAddrCur );
                if (pcCUAboveMedian &&!pcCUAboveMedian->isIntra( uiAbovePartIdx_median ))
                {
                  getNeighboringMvField(pcCUAboveMedian,uiAbovePartIdx_median,cMvField+(uiMVCount<<1),ucInterDir+uiMVCount);
                  uiMVCount++;
                  break;
                }
              }
          }
          else
          {
#if JVET_C0035_ATMVP_SIMPLIFICATION
            ucInterDir[uiMVCount]= puhInterDirSP[uiCurMRGType][iPartitionIdx-iNumPartLine];
            cMvField[(uiMVCount<<1)  ]=pcMvFieldSP[uiCurMRGType][((iPartitionIdx-iNumPartLine)<<1)  ];
            cMvField[(uiMVCount<<1)+1]=pcMvFieldSP[uiCurMRGType][((iPartitionIdx-iNumPartLine)<<1)+1];
#else
            ucInterDir[uiMVCount]= puhInterDirSP[1][iPartitionIdx-iNumPartLine];
            cMvField[(uiMVCount<<1)  ]=pcMvFieldSP[1][((iPartitionIdx-iNumPartLine)<<1)  ];
            cMvField[(uiMVCount<<1)+1]=pcMvFieldSP[1][((iPartitionIdx-iNumPartLine)<<1)+1];
#endif
            uiMVCount++;
          }

          {
            Bool bExistMV = false;
            ucInterDir[uiMVCount] = 0;
            if ( getSlice()->getEnableTMVPFlag())//获取时域运动矢量
            {
              Int iRefIdx = 0;
              TComMv cColMvTemp;
              UInt uiAbsPartIdxSP = g_auiZscanToRaster[uiSPAddr];
              UInt uiAbsPartSPAddrRB = uiSPAddr;
              //UInt uiNumPartInCUWidth = m_pcPic->getNumPartInCtuWidth();
              const UInt numPartInCtuWidth  = m_pcPic->getNumPartInCtuWidth();
              const UInt numPartInCtuHeight = m_pcPic->getNumPartInCtuHeight();
              Int uiLCUIdx = getCtuRsAddr();
              
              UInt uiPartAddrCenter = uiSPAddr;
              uiPartAddrCenter = g_auiRasterToZscan[ g_auiZscanToRaster[ uiPartAddrCenter ]//Merge的时域MV获取的时候就是这么获取的,一个中心的,一个右下角的。
                                        + ( iPUHeight/m_pcPic->getMinCUHeight()  )/2*numPartInCtuWidth
                                        + ( iPUWidth/m_pcPic->getMinCUWidth()  )/2];

              if      ( ( m_pcPic->getCtu(m_ctuRsAddr)->getCUPelX() + g_auiRasterToPelX[uiAbsPartIdxSP] + m_pcPic->getMinCUWidth() ) >= m_pcSlice->getSPS()->getPicWidthInLumaSamples() )  // image boundary check
              {
              }
              else if ( ( m_pcPic->getCtu(m_ctuRsAddr)->getCUPelY() + g_auiRasterToPelY[uiAbsPartIdxSP] + m_pcPic->getMinCUHeight() ) >= m_pcSlice->getSPS()->getPicHeightInLumaSamples() )
              {
              }
              else
              {
                if ( ( uiAbsPartIdxSP % numPartInCtuWidth < numPartInCtuWidth - 1 ) &&           // is not at the last column of LCU 
                     ( uiAbsPartIdxSP / numPartInCtuWidth < numPartInCtuHeight - 1 ) ) // is not at the last row    of LCU
                {
                  uiAbsPartSPAddrRB = g_auiRasterToZscan[ uiAbsPartIdxSP + numPartInCtuWidth + 1 ];//右下角
                  uiLCUIdx = getCtuRsAddr();
                }
                else if ( uiAbsPartIdxSP % numPartInCtuWidth < numPartInCtuWidth - 1 )           // is not at the last column of LCU But is last row of LCU
                {
#if COM16_C806_GEN_MRG_IMPROVEMENT
                  uiLCUIdx = getCtuRsAddr() + m_pcPic->getFrameWidthInCtus();
#endif
                  uiAbsPartSPAddrRB = g_auiRasterToZscan[ (uiAbsPartIdxSP + numPartInCtuWidth + 1) % m_pcPic->getNumPartitionsInCtu() ];
                }
                else if ( uiAbsPartIdxSP / numPartInCtuWidth < numPartInCtuHeight - 1 ) // is not at the last row of LCU But is last column of LCU
                {
                  uiAbsPartSPAddrRB = g_auiRasterToZscan[ uiAbsPartIdxSP + 1 ];
                  uiLCUIdx = getCtuRsAddr() + 1;
                }
                else //is the right bottom corner of LCU                       
                {
                  uiAbsPartSPAddrRB = 0;
#if COM16_C806_GEN_MRG_IMPROVEMENT
                  uiLCUIdx = getCtuRsAddr() + m_pcPic->getFrameWidthInCtus() + 1;
#endif
                }
              }
              bExistMV = uiLCUIdx >= 0 && xGetColMVP( REF_PIC_LIST_0, uiLCUIdx, uiAbsPartSPAddrRB, cColMvTemp, iRefIdx );
              if( bExistMV == false )
              {
                bExistMV = xGetColMVP( REF_PIC_LIST_0, getCtuRsAddr(), uiPartAddrCenter, cColMvTemp, iRefIdx );
              }
              if( bExistMV )
              {
                ucInterDir[uiMVCount] |= 1;
                cMvField[ 2 * uiMVCount ].setMvField( cColMvTemp, iRefIdx );
              }
    
              if ( getSlice()->isInterB() )//如果是B帧的话,要对两个列表进行组合
              {
                bExistMV = uiLCUIdx >= 0 && xGetColMVP( REF_PIC_LIST_1, uiLCUIdx, uiAbsPartSPAddrRB, cColMvTemp, iRefIdx);
                if( bExistMV == false )
                {
                  bExistMV = xGetColMVP( REF_PIC_LIST_1, getCtuRsAddr(), uiPartAddrCenter, cColMvTemp, iRefIdx );
                }
                if( bExistMV )
                {
                  ucInterDir[uiMVCount] |= 2;
                  cMvField[ 2 * uiMVCount + 1 ].setMvField( cColMvTemp, iRefIdx );
                }
              }
            }
            if(ucInterDir[uiMVCount]>0)
            {  
              uiMVCount++;
            }
          }
          generateMvField(   cMvField, ucInterDir, uiMVCount,cMvFieldMedian,ucInterDirMedian);

#if JVET_C0035_ATMVP_SIMPLIFICATION
          puhInterDirSP[uiCurMRGType][iPartitionIdx] = ucInterDirMedian;
          pcMvFieldSP[uiCurMRGType][(iPartitionIdx<<1)  ] = cMvFieldMedian[0];
          pcMvFieldSP[uiCurMRGType][(iPartitionIdx<<1)+1] = cMvFieldMedian[1];

          if (iPartitionIdx == 0  || 
            (//uiSameCount == iPartitionIdx &&
            puhInterDirSP[uiCurMRGType][iPartitionIdx]      == puhInterDirSP[uiCurMRGType][0] && 
            pcMvFieldSP[uiCurMRGType][(iPartitionIdx<<1)  ] == pcMvFieldSP[uiCurMRGType][0] &&
            pcMvFieldSP[uiCurMRGType][(iPartitionIdx<<1)+1] == pcMvFieldSP[uiCurMRGType][1] ))
          {
            uiSameCount++;
          }
          if ( uiSameCountATMVP == iPartitionIdx && 
            puhInterDirSP[MGR_TYPE_SUBPU_ATMVP_EXT][iPartitionIdx]      == puhInterDirSP[MGR_TYPE_SUBPU_ATMVP][iPartitionIdx] &&
            pcMvFieldSP[MGR_TYPE_SUBPU_ATMVP_EXT][(iPartitionIdx<<1)  ] == pcMvFieldSP[MGR_TYPE_SUBPU_ATMVP][((iPartitionIdx)<<1)  ] &&
            pcMvFieldSP[MGR_TYPE_SUBPU_ATMVP_EXT][(iPartitionIdx<<1)+1] == pcMvFieldSP[MGR_TYPE_SUBPU_ATMVP][((iPartitionIdx)<<1)+1] )              
          {
            uiSameCountATMVP++;
          }
#else
          puhInterDirSP[1][iPartitionIdx] = ucInterDirMedian;
          pcMvFieldSP[1][(iPartitionIdx<<1)  ] = cMvFieldMedian[0];
          pcMvFieldSP[1][(iPartitionIdx<<1)+1] = cMvFieldMedian[1];

          if (iPartitionIdx == 0  || 
            (uiSameCount == iPartitionIdx &&
            puhInterDirSP[1][iPartitionIdx]      == puhInterDirSP[1][0] && 
            pcMvFieldSP[1][(iPartitionIdx<<1)  ] == pcMvFieldSP[1][0] &&
            pcMvFieldSP[1][(iPartitionIdx<<1)+1] == pcMvFieldSP[1][1] ))
          {
            uiSameCount++;
          }
          if ( uiSameCountATMVP == iPartitionIdx && 
            puhInterDirSP[1][iPartitionIdx]      == puhInterDirSP[0][iPartitionIdx] &&
            pcMvFieldSP[1][(iPartitionIdx<<1)  ] == pcMvFieldSP[0][((iPartitionIdx)<<1)  ] &&
            pcMvFieldSP[1][(iPartitionIdx<<1)+1] == pcMvFieldSP[0][((iPartitionIdx)<<1)+1] )              
          {
            uiSameCountATMVP++;
          }
#endif

          iPartitionIdx++;
        }
      }
      Bool bAtmvpExtAva =true;
      if (uiSameCount == iNumPart)
      {
        for (UInt uiIdx=0; uiIdx <iCount;uiIdx++)
        {
#if JVET_C0035_ATMVP_SIMPLIFICATION
          if (peMergeTypeNeighbors[uiIdx]  !=  MGR_TYPE_SUBPU_ATMVP) 
#else
          if (peMergeTypeNeighbors[uiIdx]  !=  MGR_TYPE_SUBPU_TMVP) 
#endif
          {
#if JVET_C0035_ATMVP_SIMPLIFICATION
            if (puhInterDirNeighbours[uiIdx] == puhInterDirSP[uiCurMRGType][0] &&
              pcMvFieldNeighbours[uiIdx<<1]      == pcMvFieldSP[uiCurMRGType][0] &&
              pcMvFieldNeighbours[(uiIdx<<1)+1]  == pcMvFieldSP[uiCurMRGType][1])
#else
            if (puhInterDirNeighbours[uiIdx] == puhInterDirSP[1][0] &&
              pcMvFieldNeighbours[uiIdx<<1]      == pcMvFieldSP[1][0] &&
              pcMvFieldNeighbours[(uiIdx<<1)+1]  == pcMvFieldSP[1][1])
#endif
            {
              bAtmvpExtAva = false;
              break;
            }
          }
        }
      }
      if(bAtmvpExtAva && bAtmvpAva)
      {
        if(uiSameCountATMVP == iNumPart)
        {
           bAtmvpExtAva = false;
        }
      }
      return bAtmvpExtAva;
}

这个没怎么写注释,但其实和上面的ATMVP差不多。而且主要流程都是根据上面的定义走的。


说点闲话:

ATMVP和STMVP这个要获取子块的MV,那参考帧的子块MV是哪来的?又分割去新找的?--代码里看是直接读来的,貌似,没有就没有,有就有,也无所谓。咦有一点想知道,假如说分为4*4的子CU了,那么找到的那边的子块假如是8*4的有MV,而没算过4*4的MV的话,用不用这个MV?像上面说的,应该是有就有,没有就没有,应该无所谓。

以后看到什么再改吧。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值