HEVC帧间预测之4——运动估计(一)
HEVC帧间预测之四——运动估计(一)
其实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的整体功能。