HEVC幀間預測之四——運動估計(一)
阿新 • • 發佈:2019-02-06
其實HM的運動估計這部分與H.264相比基本沒有變化,如果看過JMVC運動估計的程式碼,會發現xTZSearch的結構幾乎就是一樣的。所以,嚴格來講,這部分的東西沒有什麼太多新鮮的東西,相信以前研究過TZSearch的人看這部分程式碼會很輕鬆。先看運動估計的主調函式:
//!< 運動估計 Void TEncSearch::xMotionEstimation( TComDataCU* pcCU, TComYuv* pcYuvOrg, Int iPartIdx, RefPicList eRefPicList, TComMv* pcMvPred, Int iRefIdxPred, TComMv& rcMv, UInt& ruiBits, UInt& ruiCost, Bool bBi ) { UInt uiPartAddr; Int iRoiWidth; Int iRoiHeight; TComMv cMvHalf, cMvQter; TComMv cMvSrchRngLT; TComMv cMvSrchRngRB; TComYuv* pcYuv = pcYuvOrg; m_iSearchRange = m_aaiAdaptSR[eRefPicList][iRefIdxPred]; //!< 根據參考幀列表型別、參考幀序號自適應設定搜尋範圍 Int iSrchRng = ( bBi ? m_bipredSearchRange : m_iSearchRange ); //!< 根據是否是雙向預測設定搜尋範圍 TComPattern* pcPatternKey = pcCU->getPattern (); //!< 用於獲取neighbor的資訊 Double fWeight = 1.0; pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iRoiWidth, iRoiHeight ); //!< 獲取PU的地址,寬度和高度 if ( bBi ) { TComYuv* pcYuvOther = &m_acYuvPred[1-(Int)eRefPicList]; pcYuv = &m_cYuvPredTemp; pcYuvOrg->copyPartToPartYuv( pcYuv, uiPartAddr, iRoiWidth, iRoiHeight ); pcYuv->removeHighFreq( pcYuvOther, uiPartAddr, iRoiWidth, iRoiHeight ); fWeight = 0.5; } // Search key pattern initialization pcPatternKey->initPattern( pcYuv->getLumaAddr( uiPartAddr ), pcYuv->getCbAddr ( uiPartAddr ), pcYuv->getCrAddr ( uiPartAddr ), iRoiWidth, iRoiHeight, pcYuv->getStride(), 0, 0, 0, 0 ); //!< 設定待搜尋的PU的相關引數,首地址,寬度,高度,跨度等 //!< 獲取參考影象首地址和跨度 Pel* piRefY = pcCU->getSlice()->getRefPic( eRefPicList, iRefIdxPred )->getPicYuvRec()->getLumaAddr( pcCU->getAddr(), pcCU->getZorderIdxInCU() + uiPartAddr ); Int iRefStride = pcCU->getSlice()->getRefPic( eRefPicList, iRefIdxPred )->getPicYuvRec()->getStride(); TComMv cMvPred = *pcMvPred; //!< 設定運動估計的搜尋範圍,LeftTop & RightBottom if ( bBi ) xSetSearchRange ( pcCU, rcMv , iSrchRng, cMvSrchRngLT, cMvSrchRngRB ); else xSetSearchRange ( pcCU, cMvPred, iSrchRng, cMvSrchRngLT, cMvSrchRngRB ); m_pcRdCost->getMotionCost ( 1, 0 ); m_pcRdCost->setPredictor ( *pcMvPred ); //!< m_mvPredictor = *pcMvPred m_pcRdCost->setCostScale ( 2 ); setWpScalingDistParam( pcCU, iRefIdxPred, eRefPicList ); //!< 設定跟weighted prediction相關的引數 // Do integer search if ( !m_iFastSearch || bBi ) //!< m_iFastSearch is true { xPatternSearch ( pcPatternKey, piRefY, iRefStride, &cMvSrchRngLT, &cMvSrchRngRB, rcMv, ruiCost ); } else //!< Fast Search { rcMv = *pcMvPred; xPatternSearchFast ( pcCU, pcPatternKey, piRefY, iRefStride, &cMvSrchRngLT, &cMvSrchRngRB, rcMv, ruiCost ); } m_pcRdCost->getMotionCost( 1, 0 ); m_pcRdCost->setCostScale ( 1 ); {//!< 分畫素搜尋 xPatternSearchFracDIF( pcCU, pcPatternKey, piRefY, iRefStride, &rcMv, cMvHalf, cMvQter, ruiCost ,bBi ); } m_pcRdCost->setCostScale( 0 ); rcMv <<= 2; //!< 整畫素 rcMv += (cMvHalf <<= 1); //!< 1/2 畫素 rcMv += cMvQter; //!< 1/4 畫素 //!< 故rcMv最終以1/4畫素為單位 UInt uiMvBits = m_pcRdCost->getBits( rcMv.getHor(), rcMv.getVer() ); ruiBits += uiMvBits; ruiCost = (UInt)( floor( fWeight * ( (Double)ruiCost - (Double)m_pcRdCost->getCost( uiMvBits ) ) ) + (Double)m_pcRdCost->getCost( ruiBits ) ); }
基本思想就是用TZSearch演算法先進行整畫素搜尋,確定一個區域性的最佳值,然後以這個最佳點為中心再進行精度更高的分畫素搜尋。
接下來我們先考慮整畫素搜尋的情況,進入到xPatternSearchFast中去:
Void TEncSearch::xPatternSearchFast( TComDataCU* pcCU, TComPattern* pcPatternKey, Pel* piRefY, Int iRefStride, TComMv* pcMvSrchRngLT, TComMv* pcMvSrchRngRB, TComMv& rcMv, UInt& ruiSAD ) {//!< 獲取相鄰PU: A, B, C的運動向量,作為預測運動向量 pcCU->getMvPredLeft ( m_acMvPredictors[0] ); pcCU->getMvPredAbove ( m_acMvPredictors[1] ); pcCU->getMvPredAboveRight ( m_acMvPredictors[2] ); switch ( m_iFastSearch ) { case 1: xTZSearch( pcCU, pcPatternKey, piRefY, iRefStride, pcMvSrchRngLT, pcMvSrchRngRB, rcMv, ruiSAD ); break; default: break; } }
我們可以看到,這個函式很短,先是獲得預測的運動向量,接著呼叫xTZSearch進行搜尋,xTZSearch這個函式相對比較長,裡面呼叫了很多子函式,因此,一口氣講完這個函式不容易,改為一步步分析各個子函式,再合起來分析xTZSearch的整體功能。