HEVC幀間預測之七——運動估計(四)
阿新 • • 發佈:2019-02-17
有了前面幾篇的鋪墊,本文就可以把整畫素部分的運動估計給結束掉了。到目前為止,只剩下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 ); }
接下來就剩下分畫素搜尋的討論了。