1. 程式人生 > >HEVC幀間預測之七——運動估計(四)

HEVC幀間預測之七——運動估計(四)

有了前面幾篇的鋪墊,本文就可以把整畫素部分的運動估計給結束掉了。到目前為止,只剩下xTZSearch這個函式沒分析了,在開始這個函式的程式碼解釋之前,讓我們共同來理一下TZSearch的基本流程:

1. 搜尋預測得到的mv所指向的點:中值預測mv,當前PU的左,上及右上PU的mv,還有零運動向量(0,0)

2. 在步驟1中找到匹配誤差最小的點作為接下來搜尋的起始點

3. 步長從1開始,以2的指數遞增,進行8點鑽石搜尋,該步驟中可以設定搜尋的最大次數(以某個步長遍歷一遍就算1次)

4. 如果步驟3搜尋得到的最佳步長為1,則需要以該最佳點為起點做1次兩點鑽石搜尋,因為前面8點搜尋的時候,這個最佳點的8個鄰點會有兩個沒有搜尋到

5. 如果步驟3搜尋得到的最佳步長大於某個閾值(iRaster),則以步驟2得到的點作為起點,做步長為iRaster的光柵掃描(即在運動搜尋的範圍內遍歷所有點)

6. 最後,在經過前面1~5歩之後,以得到的最佳點為起點,再次重複步驟3和4

7. 儲存最佳mv和SAD。

總結完畢,我只是把主線給列了出來,當中會有些不同,主要是我只考慮HM的預設配置,可以按照這個主線看下面程式碼及註釋了。

Void TEncSearch::xTZSearch( TComDataCU* pcCU, TComPattern* pcPatternKey, Pel* piRefY, Int iRefStride, TComMv* pcMvSrchRngLT, TComMv* pcMvSrchRngRB, TComMv& rcMv, UInt& ruiSAD )
{//!< 確定運動估計搜尋範圍的邊界
  Int   iSrchRngHorLeft   = pcMvSrchRngLT->getHor();
  Int   iSrchRngHorRight  = pcMvSrchRngRB->getHor();
  Int   iSrchRngVerTop    = pcMvSrchRngLT->getVer();
  Int   iSrchRngVerBottom = pcMvSrchRngRB->getVer();
  //!< 以巨集定義方式對TZSearch的相關引數進行設定
  TZ_SEARCH_CONFIGURATION
  
  UInt uiSearchRange = m_iSearchRange;
  pcCU->clipMv( rcMv );
  rcMv >>= 2;
  // init TZSearchStruct
  IntTZSearchStruct cStruct;
  cStruct.iYStride    = iRefStride;
  cStruct.piRefY      = piRefY;
  cStruct.uiBestSad   = MAX_UINT;
  
  // set rcMv (Median predictor) as start point and as best point
  xTZSearchHelp( pcPatternKey, cStruct, rcMv.getHor(), rcMv.getVer(), 0, 0 ); //!< 中值預測mv
  
  // test whether one of PRED_A, PRED_B, PRED_C MV is better start point than Median predictor
  if ( bTestOtherPredictedMV ) //!< default is 0
  {
    for ( UInt index = 0; index < 3; index++ )
    {
      TComMv cMv = m_acMvPredictors[index];
      pcCU->clipMv( cMv );
      cMv >>= 2;
      xTZSearchHelp( pcPatternKey, cStruct, cMv.getHor(), cMv.getVer(), 0, 0 );	//!< A, B, C相鄰PU的mv
    }
  }
  
  // test whether zero Mv is better start point than Median predictor
  if ( bTestZeroVector ) //!< default is 1
  {
    xTZSearchHelp( pcPatternKey, cStruct, 0, 0, 0, 0 ); //!< 零mv
  }
  
  // start search,從以前面幾個mv作為搜尋起點得到的最好的位置開始進行接下來的搜尋
  Int  iDist = 0;
  Int  iStartX = cStruct.iBestX;
  Int  iStartY = cStruct.iBestY;
  
  // first search
  for ( iDist = 1; iDist <= (Int)uiSearchRange; iDist*=2 ) //!< 以2的冪次逐步擴大搜索步長
  {
    if ( bFirstSearchDiamond == 1 ) //!< default is 1
    {
      xTZ8PointDiamondSearch ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
    }
    else
    {
      xTZ8PointSquareSearch  ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
    }
    //!< bFirstSearchStop default is 1, uiFirstSearchRounds default is 3
    if ( bFirstSearchStop && ( cStruct.uiBestRound >= uiFirstSearchRounds ) ) // stop criterion
    {
      break;
    }
  }
  
  // test whether zero Mv is a better start point than Median predictor
  if ( bTestZeroVectorStart && ((cStruct.iBestX != 0) || (cStruct.iBestY != 0)) ) //!< bTestZeroVectorStart default is 0
  {
    xTZSearchHelp( pcPatternKey, cStruct, 0, 0, 0, 0 );
    if ( (cStruct.iBestX == 0) && (cStruct.iBestY == 0) )
    {
      // test its neighborhood
      for ( iDist = 1; iDist <= (Int)uiSearchRange; iDist*=2 )
      {
        xTZ8PointDiamondSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, 0, 0, iDist );
        if ( bTestZeroVectorStop && (cStruct.uiBestRound > 0) ) // stop criterion
        {
          break;
        }
      }
    }
  }
  
  // calculate only 2 missing points instead 8 points if cStruct.uiBestDistance == 1
  if ( cStruct.uiBestDistance == 1 ) //!< 當最佳搜尋步長等於1時,補充搜尋前面8點鑽石掃描遺漏的兩點
  {
    cStruct.uiBestDistance = 0;
    xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );
  }
  
  // raster search if distance is too big
  if ( bEnableRasterSearch && ( ((Int)(cStruct.uiBestDistance) > iRaster) || bAlwaysRasterSearch ) ) //!< bEnableRasterSearch default is 1, iRaster default is 5
  {//!< 當前面搜尋得到的最佳步長過大時,改用光柵搜尋法,步長定為iRaster,搜尋範圍為設定的運動估計範圍
    cStruct.uiBestDistance = iRaster;
    for ( iStartY = iSrchRngVerTop; iStartY <= iSrchRngVerBottom; iStartY += iRaster )
    {
      for ( iStartX = iSrchRngHorLeft; iStartX <= iSrchRngHorRight; iStartX += iRaster )
      {
        xTZSearchHelp( pcPatternKey, cStruct, iStartX, iStartY, 0, iRaster );
      }
    }
  }
  
  // raster refinement
  if ( bRasterRefinementEnable && cStruct.uiBestDistance > 0 ) //!< bRasterRefinementEnable default is 0
  {
    while ( cStruct.uiBestDistance > 0 )
    {
      iStartX = cStruct.iBestX;
      iStartY = cStruct.iBestY;
      if ( cStruct.uiBestDistance > 1 )
      {
        iDist = cStruct.uiBestDistance >>= 1;
        if ( bRasterRefinementDiamond == 1 )
        {
          xTZ8PointDiamondSearch ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
        }
        else
        {
          xTZ8PointSquareSearch  ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
        }
      }
      
      // calculate only 2 missing points instead 8 points if cStruct.uiBestDistance == 1
      if ( cStruct.uiBestDistance == 1 )
      {
        cStruct.uiBestDistance = 0;
        if ( cStruct.ucPointNr != 0 )
        {
          xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );
        }
      }
    }
  }
  
  // start refinement
  if ( bStarRefinementEnable && cStruct.uiBestDistance > 0 ) //!< bStarRefinementEnable default is 1
  {
    while ( cStruct.uiBestDistance > 0 )
    {//!< 在經過了上面幾個步驟的搜尋後,從最佳點開始進行第2次的8點鑽石掃描以及利用兩點掃描對遺漏點進行補充
      iStartX = cStruct.iBestX;
      iStartY = cStruct.iBestY;
      cStruct.uiBestDistance = 0;
      cStruct.ucPointNr = 0;
      for ( iDist = 1; iDist < (Int)uiSearchRange + 1; iDist*=2 )
      {
        if ( bStarRefinementDiamond == 1 ) //!< bStarRefinementDiamond default is 1
        {
          xTZ8PointDiamondSearch ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
        }
        else
        {
          xTZ8PointSquareSearch  ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );
        }
        if ( bStarRefinementStop && (cStruct.uiBestRound >= uiStarRefinementRounds) ) // stop criterion
        {
          break;
        }
      }
      
      // calculate only 2 missing points instead 8 points if cStrukt.uiBestDistance == 1
      if ( cStruct.uiBestDistance == 1 )
      {
        cStruct.uiBestDistance = 0;
        if ( cStruct.ucPointNr != 0 )
        {
          xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );
        }
      }
    }
  }
  
  // write out best match,獲得最佳匹配結果,mv和SAD
  rcMv.set( cStruct.iBestX, cStruct.iBestY );
  ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCost( cStruct.iBestX, cStruct.iBestY );
}


接下來就剩下分畫素搜尋的討論了。