运动估计

通过 点击打开链接 介绍的方法得到MVP之后,可以根据该MVP确定运动估计的搜索起点,然后进行运动估计

xMotionEstimation就是进行运动估计的入口函数
    1、先进行一些初始化,设置搜索范围
    2、如果是B类型的slice,或者没有使用快速搜索模式,那么调用xPatternSearch进行整像素精度的全搜索
    3、如果是P类型的slice或者使用了快速搜索模式,那么调用xPatternSearchFast进行整像素精度的快速搜索
    4、调用xPatternSearchFracDIF进行亚像素精度的搜索
    5、得到最优的MV,计算它的率失真代价等

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是用于访问相邻块/像素的一个工具类TComPattern*  pcPatternKey  = pcCU->getPattern        ();Double        fWeight       = 1.0;// 得到PU的索引和尺寸pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iRoiWidth, iRoiHeight );// 如果是B sliceif ( 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 );// 参考帧的像素Pel*        piRefY      = pcCU->getSlice()->getRefPic( eRefPicList, iRefIdxPred )->getPicYuvRec()->getLumaAddr( pcCU->getAddr(), pcCU->getZorderIdxInCU() + uiPartAddr );Int         iRefStride  = pcCU->getSlice()->getRefPic( eRefPicList, iRefIdxPred )->getPicYuvRec()->getStride();// MVPTComMv      cMvPred = *pcMvPred;// 设置运动估计的搜索范围if ( bBi )  xSetSearchRange   ( pcCU, rcMv   , iSrchRng, cMvSrchRngLT, cMvSrchRngRB );else        xSetSearchRange   ( pcCU, cMvPred, iSrchRng, cMvSrchRngLT, cMvSrchRngRB );// 取得率失真代价m_pcRdCost->getMotionCost ( 1, 0 );// 设置预测得到的MVm_pcRdCost->setPredictor  ( *pcMvPred );m_pcRdCost->setCostScale  ( 2 );// 权重预测方面的设置setWpScalingDistParam( pcCU, iRefIdxPred, eRefPicList );//  Do integer search// 先进行整像素搜索,确定一个局部的最佳值if ( !m_iFastSearch || bBi ){// 如果是B slice 或者 不是快速搜索模式,进行常规搜索xPatternSearch      ( pcPatternKey, piRefY, iRefStride, &cMvSrchRngLT, &cMvSrchRngRB, rcMv, ruiCost );}else{// 进行快速搜索rcMv = *pcMvPred;xPatternSearchFast  ( pcCU, pcPatternKey, piRefY, iRefStride, &cMvSrchRngLT, &cMvSrchRngRB, rcMv, ruiCost );}m_pcRdCost->getMotionCost( 1, 0 );m_pcRdCost->setCostScale ( 1 );// 进行亚像素搜索(1/2像素、1/4像素等),以提高搜索的精度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像素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 ) );
}

整像素运动估计

整像素精度的全搜索算法

Void TEncSearch::xPatternSearch( 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();UInt  uiSad;UInt  uiSadBest         = MAX_UINT;Int   iBestX = 0;Int   iBestY = 0;Pel*  piRefSrch;//-- jclee for using the SAD function pointer// 设置计算SAD的函数指针和参数m_pcRdCost->setDistParam( pcPatternKey, piRefY, iRefStride,  m_cDistParam );// fast encoder decision: use subsampled SAD for integer ME// 快速编码决策:使用亚像素的SAD计算if ( m_pcEncCfg->getUseFastEnc() ){if ( m_cDistParam.iRows > 8 ){m_cDistParam.iSubShift = 1;}}piRefY += (iSrchRngVerTop * iRefStride);// 在预定的范围内进行搜索for ( Int y = iSrchRngVerTop; y <= iSrchRngVerBottom; y++ ){for ( Int x = iSrchRngHorLeft; x <= iSrchRngHorRight; x++ ){//  find min. distortion position// piRefY是根据MVP得到的搜索起点,要在起点周围进行搜索// 得到该位置的像素块piRefSrch = piRefY + x;m_cDistParam.pCur = piRefSrch;setDistParamComp(0);m_cDistParam.bitDepth = g_bitDepthY;// 计算当前块作为参考块时的SADuiSad = m_cDistParam.DistFunc( &m_cDistParam );// motion costuiSad += m_pcRdCost->getCost( x, y );// 更新最优代价,以及坐标if ( uiSad < uiSadBest ){uiSadBest = uiSad;iBestX    = x;iBestY    = y;}}piRefY += iRefStride;}// 最优的MVrcMv.set( iBestX, iBestY );ruiSAD = uiSadBest - m_pcRdCost->getCost( iBestX, iBestY );return;
}

使用了TZ算法的快速整像素运动估计

TZSearch搜索算法的大概流程(具体代码实现可能会有点偏差):
    1、确定搜索起点。利用AMVP得到的MVP确定搜索起点。AMVP选出的候选MV有多个,选择其中代价最小的一个最为预测MV,并作为搜索的起始点
    2、以步长1开始,按照菱形或者正方形搜索模板,在搜索范围内进行搜索,然后按照2的指数次幂的方式递增步长,然后选出率失真代价最优的点作为搜索结果
    3、以2得到的搜索结果作为搜素起始点,以步长1开始,在该点的周围做两点搜索,目的是补充搜索最优点周围尚未搜索的点(从这一步开始可能就需要进行运动补偿了)
    4、如果3得到的最优结果对应额步长大于某个阈值,那么以这个点位中心,在一定的范围内做全搜索,然后再次选择最优点
    5、以4得到的最优点为新的起始点,重复2~4,细化搜索,当相邻两次的搜索得到的最优点一致的时候停止搜索

xPatternSearchFast(快速搜索)直接调用了xTZSearch,这就是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();// 该宏定义了一系列的变量,是TZ算法的配置信息TZ_SEARCH_CONFIGURATIONUInt uiSearchRange = m_iSearchRange;pcCU->clipMv( rcMv );rcMv >>= 2;// init TZSearchStruct// TZ结构,用于保存搜索参数和结果IntTZSearchStruct cStruct;cStruct.iYStride    = iRefStride;cStruct.piRefY      = piRefY;cStruct.uiBestSad   = MAX_UINT;// set rcMv (Median predictor) as start point and as best point// 计算以起始MV(即MVP)的SAD,xTZSearchHelp是一个计算SAD并更新最优代价的函数xTZSearchHelp( pcPatternKey, cStruct, rcMv.getHor(), rcMv.getVer(), 0, 0 );// test whether one of PRED_A, PRED_B, PRED_C MV is better start point than Median predictor// 系统中预定义了三个MV,计算这三个MV对应的SADif ( bTestOtherPredictedMV ){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 );}}// test whether zero Mv is better start point than Median predictor// 尝试零MVif ( bTestZeroVector ){xTZSearchHelp( pcPatternKey, cStruct, 0, 0, 0, 0 );}// start searchInt  iDist = 0;Int  iStartX = cStruct.iBestX;Int  iStartY = cStruct.iBestY;// first search// 第一次搜索// 以步长1开始,按照菱形或者正方形搜索模板,在搜索范围内进行搜索,然后按照2的指数次幂的方式递增步长for ( iDist = 1; iDist <= (Int)uiSearchRange; iDist*=2 ){// 棱形模板搜索if ( bFirstSearchDiamond == 1 ){xTZ8PointDiamondSearch ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );}// 正方形模板搜索else{xTZ8PointSquareSearch  ( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB, iStartX, iStartY, iDist );}if ( bFirstSearchStop && ( cStruct.uiBestRound >= uiFirstSearchRounds ) ) // stop criterion{break;}}// test whether zero Mv is a better start point than Median predictor// 尝试零MV是否更好if ( bTestZeroVectorStart && ((cStruct.iBestX != 0) || (cStruct.iBestY != 0)) ){xTZSearchHelp( pcPatternKey, cStruct, 0, 0, 0, 0 );if ( (cStruct.iBestX == 0) && (cStruct.iBestY == 0) ){// test its neighborhoodfor ( 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 ){cStruct.uiBestDistance = 0;xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );}// raster search if distance is too big// 第三次搜索// 如果第二次搜索得到的最优结果对应额步长大于某个阈值,那么以这个点位中心,在一定的范围内做全搜索,然后再次选择最优点if ( bEnableRasterSearch && ( ((Int)(cStruct.uiBestDistance) > iRaster) || bAlwaysRasterSearch ) ){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// 第四次搜索// 以第三次搜索得到的最优点为新的起始点,重复2~4,细化搜索,当相邻两次的搜索得到的最优点一致的时候停止搜索if ( bRasterRefinementEnable && cStruct.uiBestDistance > 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 == 1if ( cStruct.uiBestDistance == 1 ){cStruct.uiBestDistance = 0;if ( cStruct.ucPointNr != 0 ){xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );}}}}// start refinement// 第五次搜索// 以第四次搜索得到的最优点为新的起始点,重复2~4,细化搜索,当相邻两次的搜索得到的最优点一致的时候停止搜索if ( bStarRefinementEnable && cStruct.uiBestDistance > 0 ){while ( cStruct.uiBestDistance > 0 ){iStartX = cStruct.iBestX;iStartY = cStruct.iBestY;cStruct.uiBestDistance = 0;cStruct.ucPointNr = 0;for ( iDist = 1; iDist < (Int)uiSearchRange + 1; iDist*=2 ){if ( bStarRefinementDiamond == 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 == 1if ( cStruct.uiBestDistance == 1 ){cStruct.uiBestDistance = 0;if ( cStruct.ucPointNr != 0 ){xTZ2PointSearch( pcPatternKey, cStruct, pcMvSrchRngLT, pcMvSrchRngRB );}}}}// write out best matchrcMv.set( cStruct.iBestX, cStruct.iBestY );ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCost( cStruct.iBestX, cStruct.iBestY );
}
// TZ搜索的帮助函数
__inline Void TEncSearch::xTZSearchHelp( TComPattern* pcPatternKey, IntTZSearchStruct& rcStruct, const Int iSearchX, const Int iSearchY, const UChar ucPointNr, const UInt uiDistance )
{UInt  uiSad;Pel*  piRefSrch;//  参考图像Y分量的起始地址piRefSrch = rcStruct.piRefY + iSearchY * rcStruct.iYStride + iSearchX;//-- jclee for using the SAD function pointer// 该函数主要职能是设置计算SAD的函数指针m_pcRdCost->setDistParam( pcPatternKey, piRefSrch, rcStruct.iYStride,  m_cDistParam );// fast encoder decision: use subsampled SAD when rows > 8 for integer ME// 判断是否使用快速编码if ( m_pcEncCfg->getUseFastEnc() ){if ( m_cDistParam.iRows > 8 ){m_cDistParam.iSubShift = 1;}}// Y分量setDistParamComp(0);  // Y component// distortionm_cDistParam.bitDepth = g_bitDepthY;// 计算saduiSad = m_cDistParam.DistFunc( &m_cDistParam );// motion cost// 考虑上mv本身带来的开销uiSad += m_pcRdCost->getCost( iSearchX, iSearchY );// 更新最佳值 if( uiSad < rcStruct.uiBestSad ){rcStruct.uiBestSad      = uiSad;rcStruct.iBestX         = iSearchX;rcStruct.iBestY         = iSearchY;rcStruct.uiBestDistance = uiDistance;rcStruct.uiBestRound    = 0;rcStruct.ucPointNr      = ucPointNr;}
}

亚像素精度运动估计

入口函数

入口函数是xPatternSearchFracDIF,流程如下:

1、调用xExtDIFUpSamplingH,产生1/2精度的插值像素块
2、调用xPatternRefinement,进行1/2精度的亚像素运动估计
3、调用xExtDIFUpSamplingQ,产生1/4精度的插值像素块
4、调用xPatternRefinement,进行1/4精度的亚像素运动估计

Void TEncSearch::xPatternSearchFracDIF(TComDataCU* pcCU,TComPattern* pcPatternKey,Pel* piRefY,Int iRefStride,TComMv* pcMvInt,TComMv& rcMvHalf,TComMv& rcMvQter,UInt& ruiCost,Bool biPred)
{//  Reference pattern initialization (integer scale)TComPattern cPatternRoi;Int         iOffset    = pcMvInt->getHor() + pcMvInt->getVer() * iRefStride;cPatternRoi.initPattern( piRefY +  iOffset,NULL,NULL,pcPatternKey->getROIYWidth(),pcPatternKey->getROIYHeight(),iRefStride,0, 0 );//  Half-pel refinementxExtDIFUpSamplingH ( &cPatternRoi, biPred );rcMvHalf = *pcMvInt;   rcMvHalf <<= 1;    // for mv-costTComMv baseRefMv(0, 0);// 1/2精度的亚像素运动估计ruiCost = xPatternRefinement( pcPatternKey, baseRefMv, 2, rcMvHalf   );m_pcRdCost->setCostScale( 0 );xExtDIFUpSamplingQ ( &cPatternRoi, rcMvHalf, biPred );baseRefMv = rcMvHalf;baseRefMv <<= 1;rcMvQter = *pcMvInt;   rcMvQter <<= 1;    // for mv-costrcMvQter += rcMvHalf;  rcMvQter <<= 1;// 1/4精度的亚像素运动估计ruiCost = xPatternRefinement( pcPatternKey, baseRefMv, 1, rcMvQter );
}

亚像素精度的运动估计函数

1、初始化,设置率失真代价计算的函数指针以及参数
2、s_acMvRefineH和s_acMvRefineQ是HEVC预定义的两个数组,保存了偏移坐标偏移量,专门用于亚像素精度的运动估计
3、根据iFrac参数选取s_acMvRefineH或者s_acMvRefineQ作为偏移数组
4、遍历偏移数组,对于每一个偏移值,把它加到已经存在的最优MV上,然后进行率失真代价计算,更新最优值
5、最后得到最优的MV

UInt TEncSearch::xPatternRefinement( TComPattern* pcPatternKey,TComMv baseRefMv,Int iFrac, TComMv& rcMvFrac )
{UInt  uiDist;UInt  uiDistBest  = MAX_UINT;UInt  uiDirecBest = 0;Pel*  piRefPos;Int iRefStride = m_filteredBlock[0][0].getStride();m_pcRdCost->setDistParam( pcPatternKey, m_filteredBlock[0][0].getLumaAddr(), iRefStride, 1, m_cDistParam, m_pcEncCfg->getUseHADME() );const TComMv* pcMvRefine = (iFrac == 2 ? s_acMvRefineH : s_acMvRefineQ);for (UInt i = 0; i < 9; i++){TComMv cMvTest = pcMvRefine[i];cMvTest += baseRefMv;Int horVal = cMvTest.getHor() * iFrac;Int verVal = cMvTest.getVer() * iFrac;piRefPos = m_filteredBlock[ verVal & 3 ][ horVal & 3 ].getLumaAddr();if ( horVal == 2 && ( verVal & 1 ) == 0 ){piRefPos += 1;}if ( ( horVal & 1 ) == 0 && verVal == 2 ){piRefPos += iRefStride;}cMvTest = pcMvRefine[i];cMvTest += rcMvFrac;setDistParamComp(0);  // Y componentm_cDistParam.pCur = piRefPos;m_cDistParam.bitDepth = g_bitDepthY;uiDist = m_cDistParam.DistFunc( &m_cDistParam );uiDist += m_pcRdCost->getCost( cMvTest.getHor(), cMvTest.getVer() );if ( uiDist < uiDistBest ){uiDistBest  = uiDist;uiDirecBest = i;}}// mv更新rcMvFrac = pcMvRefine[uiDirecBest];return uiDistBest;
}

产生1/2精度的插值像素块

Void TEncSearch::xExtDIFUpSamplingH( TComPattern* pattern, Bool biPred )
{Int width      = pattern->getROIYWidth();Int height     = pattern->getROIYHeight();Int srcStride  = pattern->getPatternLStride();Int intStride = m_filteredBlockTmp[0].getStride();Int dstStride = m_filteredBlock[0][0].getStride();Short *intPtr;Short *dstPtr;Int filterSize = NTAPS_LUMA;Int halfFilterSize = (filterSize>>1);Pel *srcPtr = pattern->getROIY() - halfFilterSize*srcStride - 1;m_if.filterHorLuma(srcPtr, srcStride, m_filteredBlockTmp[0].getLumaAddr(), intStride, width+1, height+filterSize, 0, false);m_if.filterHorLuma(srcPtr, srcStride, m_filteredBlockTmp[2].getLumaAddr(), intStride, width+1, height+filterSize, 2, false);intPtr = m_filteredBlockTmp[0].getLumaAddr() + halfFilterSize * intStride + 1;  dstPtr = m_filteredBlock[0][0].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width+0, height+0, 0, false, true);intPtr = m_filteredBlockTmp[0].getLumaAddr() + (halfFilterSize-1) * intStride + 1;  dstPtr = m_filteredBlock[2][0].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width+0, height+1, 2, false, true);intPtr = m_filteredBlockTmp[2].getLumaAddr() + halfFilterSize * intStride;dstPtr = m_filteredBlock[0][2].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width+1, height+0, 0, false, true);intPtr = m_filteredBlockTmp[2].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[2][2].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width+1, height+1, 2, false, true);
}

产生1/4精度的插值像素块

Void TEncSearch::xExtDIFUpSamplingQ( TComPattern* pattern, TComMv halfPelRef, Bool biPred )
{Int width      = pattern->getROIYWidth();Int height     = pattern->getROIYHeight();Int srcStride  = pattern->getPatternLStride();Pel *srcPtr;Int intStride = m_filteredBlockTmp[0].getStride();Int dstStride = m_filteredBlock[0][0].getStride();Short *intPtr;Short *dstPtr;Int filterSize = NTAPS_LUMA;Int halfFilterSize = (filterSize>>1);Int extHeight = (halfPelRef.getVer() == 0) ? height + filterSize : height + filterSize-1;// Horizontal filter 1/4srcPtr = pattern->getROIY() - halfFilterSize * srcStride - 1;intPtr = m_filteredBlockTmp[1].getLumaAddr();if (halfPelRef.getVer() > 0){srcPtr += srcStride;}if (halfPelRef.getHor() >= 0){srcPtr += 1;}m_if.filterHorLuma(srcPtr, srcStride, intPtr, intStride, width, extHeight, 1, false);// Horizontal filter 3/4srcPtr = pattern->getROIY() - halfFilterSize*srcStride - 1;intPtr = m_filteredBlockTmp[3].getLumaAddr();if (halfPelRef.getVer() > 0){srcPtr += srcStride;}if (halfPelRef.getHor() > 0){srcPtr += 1;}m_if.filterHorLuma(srcPtr, srcStride, intPtr, intStride, width, extHeight, 3, false);        // Generate @ 1,1intPtr = m_filteredBlockTmp[1].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[1][1].getLumaAddr();if (halfPelRef.getVer() == 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 1, false, true);// Generate @ 3,1intPtr = m_filteredBlockTmp[1].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[3][1].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 3, false, true);if (halfPelRef.getVer() != 0){// Generate @ 2,1intPtr = m_filteredBlockTmp[1].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[2][1].getLumaAddr();if (halfPelRef.getVer() == 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 2, false, true);// Generate @ 2,3intPtr = m_filteredBlockTmp[3].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[2][3].getLumaAddr();if (halfPelRef.getVer() == 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 2, false, true);}else{// Generate @ 0,1intPtr = m_filteredBlockTmp[1].getLumaAddr() + halfFilterSize * intStride;dstPtr = m_filteredBlock[0][1].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 0, false, true);// Generate @ 0,3intPtr = m_filteredBlockTmp[3].getLumaAddr() + halfFilterSize * intStride;dstPtr = m_filteredBlock[0][3].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 0, false, true);}if (halfPelRef.getHor() != 0){// Generate @ 1,2intPtr = m_filteredBlockTmp[2].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[1][2].getLumaAddr();if (halfPelRef.getHor() > 0){intPtr += 1;}if (halfPelRef.getVer() >= 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 1, false, true);// Generate @ 3,2intPtr = m_filteredBlockTmp[2].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[3][2].getLumaAddr();if (halfPelRef.getHor() > 0){intPtr += 1;}if (halfPelRef.getVer() > 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 3, false, true);  }else{// Generate @ 1,0intPtr = m_filteredBlockTmp[0].getLumaAddr() + (halfFilterSize-1) * intStride + 1;dstPtr = m_filteredBlock[1][0].getLumaAddr();if (halfPelRef.getVer() >= 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 1, false, true);// Generate @ 3,0intPtr = m_filteredBlockTmp[0].getLumaAddr() + (halfFilterSize-1) * intStride + 1;dstPtr = m_filteredBlock[3][0].getLumaAddr();if (halfPelRef.getVer() > 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 3, false, true);}// Generate @ 1,3intPtr = m_filteredBlockTmp[3].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[1][3].getLumaAddr();if (halfPelRef.getVer() == 0){intPtr += intStride;}m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 1, false, true);// Generate @ 3,3intPtr = m_filteredBlockTmp[3].getLumaAddr() + (halfFilterSize-1) * intStride;dstPtr = m_filteredBlock[3][3].getLumaAddr();m_if.filterVerLuma(intPtr, intStride, dstPtr, dstStride, width, height, 3, false, true);
}

HM编码器代码阅读(30)——帧间预测之AMVP模式(五)运动估计相关推荐

  1. HM编码器代码阅读(16)——帧间预测之AMVP模式(四)预测MV的获取

    帧间预测的原理 AMVP的原理 帧间预测的实质就是为当前的PU在参考帧中寻找一块最相似块(相似度的判断准则有SAD等方法).但是参考图像通常都比较大,我们直接去搜索的话就太费时了,应该使用某种方法在参 ...

  2. HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程

    帧间预测的原理 AMVP的原理 帧间预测的实质就是为当前的PU在参考帧中寻找一块最相似块(相似度的判断准则有SAD等方法).但是参考图像通常都比较大,我们直接去搜索的话就太费时了,应该使用某种方法在参 ...

  3. HM编码器代码阅读(14)——帧间预测之AMVP模式(二)predInterSearch函数

    简介     predInterSearch主要的工作是ME(运动估计)和MC(运动补偿).     函数中有一个bTestNormalMC变量,它表示是否进行正常的MC过程,正常的MC过程就是进行M ...

  4. HM编码器代码阅读(15)——帧间预测之AMVP模式(三)xGetBlkBits函数

    GetBlkBits函数的主要功能是计算使用某种PU划分模式的时候,该种模式占用的比特数 Void TEncSearch::xGetBlkBits( PartSize eCUMode, Bool bP ...

  5. HM编码器代码阅读(32)——帧间预测之AMVP/Merge模式(七)encodeResAndCalcRdInterCU函数:残差计算、变换量化

    encodeResAndCalcRdInterCU 原理和细节 经过运动估计.运动补偿,我们得到了MV以及参考块,那么接下来是计算残差.计算MVD,然后对系数进行变换.量化. encodeResAnd ...

  6. HM编码器代码阅读(31)——帧间预测之AMVP/Merge模式(六)运动补偿

    运动补偿 原理 说实话一直很难理解运动补偿中"补偿"二字的意思,在参考了 http://blog.csdn.net/hevc_cjl/article/details/8457642 ...

  7. HM编码器代码阅读(38)——帧内预测(五)帧内预测之正式的预测操作

    正式的预测操作 在前面的操作中,我们已经得到了模式候选列表,但是我们的目的是要得到一个最优的模式,因此我们还需要对这个列表中的模式进行遍历,对于每一个模式,进行预测操作,为了计算率失真代价还必须进行变 ...

  8. HM编码器代码阅读(14)——帧间预測之AMVP模式(二)predInterSearch函数

    简单介绍     predInterSearch基本的工作是ME(运动预计)和MC(运动补偿).     函数中有一个bTestNormalMC变量.它表示是否进行正常的MC过程,正常的MC过程就是进 ...

  9. HM代码阅读1: 帧间预测函数Void TEncSearch::predInterSearch()

    AMVP理论知识简单回顾(含GPB) MVP主要是为了给当前PU提供一个运动矢量的预测,可加快ME的计算速度以及提升准确性.并且在后续编码中也只用编码MVD,减少了传输bit数. HM中获取每个参考图 ...

最新文章

  1. LeetCode OJ:Pascal's TriangleII(帕斯卡三角II)
  2. Android Studio问题集锦
  3. silverlight, 双击事件
  4. 深度学习核心技术精讲100篇(五)-通过CTR预估对比深度学习模型(deepfm)梯度提升模型(catboost)
  5. 关系型数据库表结构的两个设计技巧
  6. PHP非对称加密:RSA (RSA/ECB/PKCS1Padding)+base64_encode/bin2hex加密
  7. 计算机窗口设置不合并,电脑下边的任务栏不合并怎么办
  8. HTTP / HTTPS抓包工具-Fiddler
  9. 【转】Android 如何获取SDCard 内存(二)
  10. macOS安装mysql后,进入mysql出现command not found
  11. 不是有效的函数或过程名_什么是函数?
  12. linux和Dos下换行符转换
  13. Mybatis 中经典的 9 种设计模式!面试可以吹牛了!
  14. 山东大学往年c语言期末试题及答案,山东大学历年C语言题库.pdf
  15. 双屏幕切换成单屏,软件不显示的问题与解决方法(总结全网)
  16. rpm -ivh rpm包名
  17. Manjaro安装以及美化教程
  18. powershell和cmd区别
  19. Python 把两张图片拼起来
  20. Moloch安装与使用

热门文章

  1. php微信自动回复接口,微信接口自动回复
  2. easeOutBack的贝塞尔曲线参数
  3. 小爱(小米),天猫精灵(阿里),小度(百度),小艺(华为)智能家居系统选择
  4. 图像在计算机中通过什么方式表示_线性代数在数据科学中的十大强大应用(二)...
  5. uniApp图片加水印
  6. 《图像处理实例》 之 局部极值提取
  7. 淘宝API应用开发小试
  8. java/php/net/python驾校学员管理系统设计
  9. oracle ora-3136,ORA-3136 错误解决 .
  10. QT 自定义带图片背景半透明阴影窗口