运动补偿

原理

说实话一直很难理解运动补偿中“补偿”二字的意思,在参考了 http://blog.csdn.net/hevc_cjl/article/details/8457642 和百度百科以及分析的源代码之后,我大致猜测了一下它的功能:
    百科上说“运动补偿是通过先前的局部图像来预测、补偿当前的局部图像,它是减少帧序列冗余信息的有效方法”,通过前面的运动估计我们得到了MV(运动向量),大部分情况下MV是亚像素精度的,MV的作用就是定位参考块在参考帧中的位置,但是亚像素的MV定位出来的位置是没有像素点的(亚像素就是指该位置在两个像素之间),换句话说非整像素精度的MV定位出来的地方没有像素点(即没有像素块),那么我们需要使用现有的像素点去构造亚像素点,这样通过MV找到位置才有参考块。运动补偿实际干的就是这么回事它通过MV和现有的像素块去构造一个亚像素块,这个新被创建出来的像素块就是当前PU的参考块。这样,得到了MV和参考块之后就可以进行后续的工作了。

运动补偿入口函数

motionCompensation()完成了运动补偿的工作;

motionCompensation()调用了xPredInterUni()完成了单向预测的运动补偿;而调用xPredInterBi()完成了双向预测的运动补偿,它实际调用xPredInterBi和xWeightedPredictionBi来完成相应的工作。其中xPredInterUni()调用xPredInterBlk()完成一个分量块的运动补偿。而xPredInterBlk()调用了TComInterpolationFilter类的filterHor()和filterVer()完成了亚像素的插值工作。

motionCompensation的流程:
    1、如果指明了PU,那么只对这个PU进行处理,如果没有指明PU,那么对CU下面的所有PU进行处理。
    2、对于一个PU,如果指定了参考列表,那么表示进行单向运动补偿(双向运动补偿可以通过两次单向操作来完成);如果没有指定参考列表,那么默认进行双向运动补偿,但是在操作之前先确认PU两个方向上的参考帧是否相同,如果相同,表示只有一个参考帧那么它实际还是进行单向运动补偿,否则使用双向运动补偿。
    3、无论是单向运动补偿还是双向运动补偿,都需要在亚像素插值工作完成之后,检测是否需要进行加权预测,相关的加权操作是在xWeightedPredictionUni中完成的,这个函数根据权重参数对目标像素块进行权重转换,对每一个像素通过一个公式去重新计算它的值。单向预测的运动补偿中,xWeightedPredictionUni跟在xPredInterUni函数的后面,在双向预测的运动补偿中,xWeightedPredictionUni在xPredInterBi函数里面。

下面的它的流程图和代码:

Void TComPrediction::motionCompensation ( TComDataCU* pcCU, TComYuv* pcYuvPred, RefPicList eRefPicList, Int iPartIdx )
{Int         iWidth;Int         iHeight;UInt        uiPartAddr;// 如果PU的索引是有效值,那么直接处理该PU,然后返回if ( iPartIdx >= 0 ){pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iWidth, iHeight );// 有效的参考列表,即明确的标明了使用哪个参考列表,那么就在对应的方向上进行单向预测if ( eRefPicList != REF_PIC_LIST_X ){// 先进行插值操作if( pcCU->getSlice()->getPPS()->getUseWP()){xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred, true ); // 最后一个参数指明是否为双向预测}else{xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );}// 加权预测if ( pcCU->getSlice()->getPPS()->getUseWP() ){xWeightedPredictionUni( pcCU, pcYuvPred, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );}}// 没有指明明确的参考列表,那么判断PU两个方向上的参考帧是否一样else{// 如果PU的两个参考列表是相同的,即它们的运动是一致的// 那么直接使用单向预测if ( xCheckIdenticalMotion( pcCU, uiPartAddr ) ){xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, pcYuvPred );}// 否则使用双向预测else{xPredInterBi  (pcCU, uiPartAddr, iWidth, iHeight, pcYuvPred );}}return;}// 否则处理CU下的所有PUfor ( iPartIdx = 0; iPartIdx < pcCU->getNumPartitions(); iPartIdx++ ){pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iWidth, iHeight );if ( eRefPicList != REF_PIC_LIST_X ){if( pcCU->getSlice()->getPPS()->getUseWP()){xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred, true );}else{xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );}if ( pcCU->getSlice()->getPPS()->getUseWP() ){xWeightedPredictionUni( pcCU, pcYuvPred, uiPartAddr, iWidth, iHeight, eRefPicList, pcYuvPred );}}else{if ( xCheckIdenticalMotion( pcCU, uiPartAddr ) ){xPredInterUni (pcCU, uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, pcYuvPred );}else{xPredInterBi  (pcCU, uiPartAddr, iWidth, iHeight, pcYuvPred );}}}return;
}

单向预测的运动补偿

Void TComPrediction::xPredInterUni ( TComDataCU* pcCU, UInt uiPartAddr, Int iWidth, Int iHeight, RefPicList eRefPicList, TComYuv*& rpcYuvPred, Bool bi )
{Int         iRefIdx     = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr );           assert (iRefIdx >= 0);TComMv      cMv         = pcCU->getCUMvField( eRefPicList )->getMv( uiPartAddr );pcCU->clipMv(cMv);xPredInterLumaBlk  ( pcCU, pcCU->getSlice()->getRefPic( eRefPicList, iRefIdx )->getPicYuvRec(), uiPartAddr, &cMv, iWidth, iHeight, rpcYuvPred, bi );xPredInterChromaBlk( pcCU, pcCU->getSlice()->getRefPic( eRefPicList, iRefIdx )->getPicYuvRec(), uiPartAddr, &cMv, iWidth, iHeight, rpcYuvPred, bi );
}

对亮度块进行亚像素插值工作

Void TComPrediction::xPredInterLumaBlk( TComDataCU *cu, TComPicYuv *refPic, UInt partAddr, TComMv *mv, Int width, Int height, TComYuv *&dstPic, Bool bi )
{Int refStride = refPic->getStride();  Int refOffset = ( mv->getHor() >> 2 ) + ( mv->getVer() >> 2 ) * refStride;Pel *ref      = refPic->getLumaAddr( cu->getAddr(), cu->getZorderIdxInCU() + partAddr ) + refOffset;Int dstStride = dstPic->getStride();Pel *dst      = dstPic->getLumaAddr( partAddr );Int xFrac = mv->getHor() & 0x3;Int yFrac = mv->getVer() & 0x3;if ( yFrac == 0 ){m_if.filterHorLuma( ref, refStride, dst, dstStride, width, height, xFrac,       !bi );}else if ( xFrac == 0 ){m_if.filterVerLuma( ref, refStride, dst, dstStride, width, height, yFrac, true, !bi );}else{Int tmpStride = m_filteredBlockTmp[0].getStride();Short *tmp    = m_filteredBlockTmp[0].getLumaAddr();Int filterSize = NTAPS_LUMA;Int halfFilterSize = ( filterSize >> 1 );m_if.filterHorLuma(ref - (halfFilterSize-1)*refStride, refStride, tmp, tmpStride, width, height+filterSize-1, xFrac, false     );m_if.filterVerLuma(tmp + (halfFilterSize-1)*tmpStride, tmpStride, dst, dstStride, width, height,              yFrac, false, !bi);    }
}

双向预测运动补偿

Void TComPrediction::xPredInterBi ( TComDataCU* pcCU, UInt uiPartAddr, Int iWidth, Int iHeight, TComYuv*& rpcYuvPred )
{TComYuv* pcMbYuv;Int      iRefIdx[2] = {-1, -1};// 执行两次单向预测的运动补偿,就可以完成双向预测的运动补偿了for ( Int iRefList = 0; iRefList < 2; iRefList++ ){RefPicList eRefPicList = (iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0);iRefIdx[iRefList] = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr );if ( iRefIdx[iRefList] < 0 ){continue;}assert( iRefIdx[iRefList] < pcCU->getSlice()->getNumRefIdx(eRefPicList) );pcMbYuv = &m_acYuvPred[iRefList];// 单向预测的运动补偿if( pcCU->getCUMvField( REF_PIC_LIST_0 )->getRefIdx( uiPartAddr ) >= 0 && pcCU->getCUMvField( REF_PIC_LIST_1 )->getRefIdx( uiPartAddr ) >= 0 ){xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv, true );}else{if ( ( pcCU->getSlice()->getPPS()->getUseWP()       && pcCU->getSlice()->getSliceType() == P_SLICE ) || ( pcCU->getSlice()->getPPS()->getWPBiPred() && pcCU->getSlice()->getSliceType() == B_SLICE ) ){xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv, true );}else{xPredInterUni ( pcCU, uiPartAddr, iWidth, iHeight, eRefPicList, pcMbYuv );}}}// 加权预测if ( pcCU->getSlice()->getPPS()->getWPBiPred() && pcCU->getSlice()->getSliceType() == B_SLICE  ){xWeightedPredictionBi( pcCU, &m_acYuvPred[0], &m_acYuvPred[1], iRefIdx[0], iRefIdx[1], uiPartAddr, iWidth, iHeight, rpcYuvPred );}  else if ( pcCU->getSlice()->getPPS()->getUseWP() && pcCU->getSlice()->getSliceType() == P_SLICE ){xWeightedPredictionUni( pcCU, &m_acYuvPred[0], uiPartAddr, iWidth, iHeight, REF_PIC_LIST_0, rpcYuvPred ); }else{xWeightedAverage( &m_acYuvPred[0], &m_acYuvPred[1], iRefIdx[0], iRefIdx[1], uiPartAddr, iWidth, iHeight, rpcYuvPred );}
}

加权预测

单向加权预测

// getWpScaling的作用就是设置权重table的参数
// addWeightUni根据权重参数对目标像素块进行权重转换,即对每一个像素通过一个公式去重新计算它的值
Void TComWeightPrediction::xWeightedPredictionUni( TComDataCU* pcCU, TComYuv* pcYuvSrc, UInt uiPartAddr, Int iWidth, Int iHeight, RefPicList eRefPicList, TComYuv*& rpcYuvPred, Int iRefIdx)
{ wpScalingParam  *pwp, *pwpTmp;if ( iRefIdx < 0 ){iRefIdx   = pcCU->getCUMvField( eRefPicList )->getRefIdx( uiPartAddr );}assert (iRefIdx >= 0);if ( eRefPicList == REF_PIC_LIST_0 ){getWpScaling(pcCU, iRefIdx, -1, pwp, pwpTmp);}else{getWpScaling(pcCU, -1, iRefIdx, pwpTmp, pwp);}addWeightUni( pcYuvSrc, uiPartAddr, iWidth, iHeight, pwp, rpcYuvPred );
}

双向加权预测

/*
** 双向加权预测
*/
Void TComWeightPrediction::xWeightedPredictionBi( TComDataCU* pcCU, TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, Int iRefIdx0, Int iRefIdx1, UInt uiPartIdx, Int iWidth, Int iHeight, TComYuv* rpcYuvDst )
{wpScalingParam  *pwp0, *pwp1;TComPPS         *pps = pcCU->getSlice()->getPPS();assert( pps->getWPBiPred());// getWpScaling的作用就是设置权重table的参数getWpScaling(pcCU, iRefIdx0, iRefIdx1, pwp0, pwp1);// addWeightUni根据权重参数对目标像素块进行权重转换,即对每一个像素通过一个公式去重新计算它的值if( iRefIdx0 >= 0 && iRefIdx1 >= 0 ){addWeightBi(pcYuvSrc0, pcYuvSrc1, uiPartIdx, iWidth, iHeight, pwp0, pwp1, rpcYuvDst );}else if ( iRefIdx0 >= 0 && iRefIdx1 <  0 ){addWeightUni( pcYuvSrc0, uiPartIdx, iWidth, iHeight, pwp0, rpcYuvDst );}else if ( iRefIdx0 <  0 && iRefIdx1 >= 0 ){addWeightUni( pcYuvSrc1, uiPartIdx, iWidth, iHeight, pwp1, rpcYuvDst );}else{assert (0);}
}

平均加权预测

Void TComPrediction::xWeightedAverage( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, Int iRefIdx0, Int iRefIdx1, UInt uiPartIdx, Int iWidth, Int iHeight, TComYuv*& rpcYuvDst )
{if( iRefIdx0 >= 0 && iRefIdx1 >= 0 ){rpcYuvDst->addAvg( pcYuvSrc0, pcYuvSrc1, uiPartIdx, iWidth, iHeight );}else if ( iRefIdx0 >= 0 && iRefIdx1 <  0 ){pcYuvSrc0->copyPartToPartYuv( rpcYuvDst, uiPartIdx, iWidth, iHeight );}else if ( iRefIdx0 <  0 && iRefIdx1 >= 0 ){pcYuvSrc1->copyPartToPartYuv( rpcYuvDst, uiPartIdx, iWidth, iHeight );}
}

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

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

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

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

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

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

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

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

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

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

    运动估计 通过 点击打开链接 介绍的方法得到MVP之后,可以根据该MVP确定运动估计的搜索起点,然后进行运动估计 xMotionEstimation就是进行运动估计的入口函数     1.先进行一些初 ...

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

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

  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. 【译】node js event loop part 1.1
  2. 标题 计算机构自由度时主要步骤有哪些,2010年1月全国自考混凝土结构设计试题和答案...
  3. char 类型的常数_CHAR_MAX常数,带C ++示例
  4. pandas的自带数据集_pandas.DataFrame.sample随机抽样
  5. mysql fixed数据类型_fixed数据类型
  6. 【Debug】IAR在线调试时报错,Warning: Stack pointer is setup to incorrect alignmentStack,芯片使用STM32F103ZET6...
  7. 43_并发编程-管道
  8. 统计学习导论(ISLR) 第四章分类算法课后习题
  9. 笔记本电脑桌面计算机图标不见了怎么办,桌面图标不见了怎么办,教您电脑桌面图标不见了怎么办...
  10. 【人工智能】无人车系统仿真软件-PreScan
  11. Jedis远程连接阿里云 Failed to create socket
  12. 游戏运营的工作中是做什么
  13. pandas之DataFrame常用方法
  14. lattice开发错误集合
  15. GrapeCity Documents for PDF[GcPDF]
  16. 中国剩余定理(孙子定理)
  17. SAP FICO CBS接口-银企直连联盟收款功能开发说明书(包括测试样例、程序代码仅作参考,不保证一定可以运行)
  18. Vue 实现华视身份证读卡器功能
  19. 混合精度训练、分布式训练等训练加速方法
  20. 双圆环环布带系法图解_魔术(连环扣)两个环之间是怎样穿过去的

热门文章

  1. 【趣文】秦始皇与区块链竟然有关系
  2. [leetcode]322. 零钱兑换(Coin Change )C++代码实现
  3. 救救我吧,今年27岁,想转行学大数据开发
  4. 红楼梦人物出场顺序统计
  5. 设计模式——中介模式
  6. 均方误差——MSE 和标准差 的区别
  7. [PPTX解析] 图片效果算法篇:设置透明色
  8. ACM:搜索算法专题(1)——24点
  9. at命令不生效 linux_linux中at命令详解
  10. zoj 2975 Kinds of Fuwas(数学题)