作为一个视频编码小白,最近开始着手啃HEVC帧间预测的代码,想用博客记录一下自己的学习过程,也想与大家分享、交流一下。

HEVC代码的学习主要是参考两位大神岳麓吹雪、NB_vol_1的博客以及HM参考软件。
两位大神的关于HEVC帧间部分的博客如下:
HEVC代码学习
HM编码器代码阅读(33)——帧间预测的总结
而HM软件的安装配置可参考HEVC代码:HM使用+码流分析教程

应接下来研究的需要(想将自己用其他方式获取的MV直接喂入HEVC),先对HEVC帧间预测部分MV的获取、传递和存储方式进行总结,摸清楚在HEVC中MV的传输路径。

目录

  • 帧间预测基本架构
  • 涉及的主要Class/Struct
    • TComDataCU
    • TComCUMvField
    • TComMvField
    • TComMv
    • AMVPInfo
  • 帧间预测入口函数
  • Inter模式(AMVP)
    • 入口函数——xCheckRDCostInter
      • predInterSearch
  • Merge模式
    • 入口函数—xCheckRDCostMerge2Nx2N
      • getInterMergeCandidates

帧间预测基本架构

下面是我暂时总结的帧间部分跟MV有关的基本结构,忽略了很多细节的部分,主要是帧间预测部分中关键函数的调用关系。

涉及的主要Class/Struct

在学习代码的过程中,我发现如果想更好的理清楚HM的代码结构,那么对其中各种Class的定义及调用关系的学习至关重要。
关于Class的学习可以参考HM参考网站HEVC Test Model (HM) HM-16.18——Class List

下面我总结出了帧间预测中与MV相关的几个重要Class,有助于更好的理解MV信息在帧间预测过程中如何获取、传递以及存储。

TComDataCU

官方定义:CU data structure class.
是HM中CU的数据类型,其中定义了非常多CU内涉及到的变量及函数。
该Class内与MV有关的变量及函数总结如下(根据自己的进度持续更新):

// 将pcCU的MV及其参考信息赋给rcMvField
static Void   getMvField( const TComDataCU* pcCU, UInt uiAbsPartIdx, RefPicList eRefPicList, TComMvField& rcMvField );// 获取AMVP候选列表
Void          fillMvpCand( const UInt uiPartIdx, const UInt uiPartAddr, const RefPicList eRefPicList, const Int iRefIdx, AMVPInfo* pInfo ) const;// 获取Merge候选列表
Void          getInterMergeCandidates( UInt uiAbsPartIdx, UInt uiPUIdx, TComMvField* pcMFieldNeighbours, UChar* puhInterDirNeighbours, Int& numValidMergeCand, UInt& numSpatialMergeCandidates , Int mrgCandIdx = -1) const;// MV信息
TComCUMvField m_acCUMvField[NUM_REF_PIC_LIST_01];     ///< array of motion vectors.
// 获取MV信息
TComCUMvField* getCUMvField( RefPicList e )  { return &m_acCUMvField[e];                  }// 帧间预测方向
UChar*        m_puhInterDir;                          ///< array of inter directions
// 设置帧间预测方向
Void          setInterDirSubParts( UInt uiDir,  UInt uiAbsPartIdx, UInt uiPartIdx, UInt uiDepth );// 最优MVP索引
SChar*        m_apiMVPIdx[NUM_REF_PIC_LIST_01];       ///< array of motion vector predictor candidates
// 获取最优MVP索引
SChar*        getMVPIdx( RefPicList eRefPicList ) { return m_apiMVPIdx[eRefPicList];           }
// 设置/存储最优MVP索引
Void          setMVPIdxSubParts( Int iMVPIdx, RefPicList eRefPicList, UInt uiAbsPartIdx, UInt uiPartIdx, UInt uiDepth );// 有效MVP候选数量
SChar*        m_apiMVPNum[NUM_REF_PIC_LIST_01];       ///< array of number of possible motion vectors predictors
// 获取有效MVP候选数量
SChar*        getMVPNum( RefPicList eRefPicList ) { return m_apiMVPNum[eRefPicList];           }
// 设置/存储有效MVP候选数量Void         setMVPNumSubParts( Int iMVPNum, RefPicList eRefPicList, UInt uiAbsPartIdx, UInt uiPartIdx, UInt uiDepth );

TComCUMvField

官方定义:class for motion information in one CU
该Class用来表示一个CU内的MV信息。主要包括MV、MVD、参考帧索引、AMVP候选列表
协作图:

该Class定义如下:

class TComCUMvField
{private:TComMv*   m_pcMv;  // MVTComMv*   m_pcMvd; // MVDSChar*    m_piRefIdx; // 参考帧索引UInt      m_uiNumPartition;AMVPInfo  m_cAMVPInfo; // AMVP候选列表template <typename T>Void setAll( T *p, T const & val, PartSize eCUMode, Int iPartAddr, UInt uiDepth, Int iPartIdx );public:TComCUMvField() : m_pcMv(NULL), m_pcMvd(NULL), m_piRefIdx(NULL), m_uiNumPartition(0) {}~TComCUMvField() {}// ------------------------------------------------------------------------------------------------------------------// create / destroy// ------------------------------------------------------------------------------------------------------------------Void    create( UInt uiNumPartition );Void    destroy();// ------------------------------------------------------------------------------------------------------------------// clear / copy// ------------------------------------------------------------------------------------------------------------------Void    clearMvField();Void    copyFrom( TComCUMvField const * pcCUMvFieldSrc, Int iNumPartSrc, Int iPartAddrDst );Void    copyTo  ( TComCUMvField* pcCUMvFieldDst, Int iPartAddrDst ) const;Void    copyTo  ( TComCUMvField* pcCUMvFieldDst, Int iPartAddrDst, UInt uiOffset, UInt uiNumPart ) const;// ------------------------------------------------------------------------------------------------------------------// get// ------------------------------------------------------------------------------------------------------------------TComMv const & getMv    ( Int iIdx ) const { return  m_pcMv    [iIdx]; }   // 获取MVTComMv const & getMvd   ( Int iIdx ) const { return  m_pcMvd   [iIdx]; }   // 获取MVDInt            getRefIdx( Int iIdx ) const { return  m_piRefIdx[iIdx]; }   // 获取参考帧索引AMVPInfo* getAMVPInfo () { return &m_cAMVPInfo; }  // 和获取AMVP候选列表// ------------------------------------------------------------------------------------------------------------------// set// ------------------------------------------------------------------------------------------------------------------// 以下四个函数的设置方式都是将第一个值(如rcMv)赋给当前CU对应值(如m_pcMv)Void    setAllMv     ( TComMv const & rcMv,         PartSize eCUMode, Int iPartAddr, UInt uiDepth, Int iPartIdx=0 );  // 设置/存储MVVoid    setAllMvd    ( TComMv const & rcMvd,        PartSize eCUMode, Int iPartAddr, UInt uiDepth, Int iPartIdx=0 );  // 设置/存储MVDVoid    setAllRefIdx ( Int iRefIdx,                 PartSize eMbMode, Int iPartAddr, UInt uiDepth, Int iPartIdx=0 );  // 设置/存储参考帧索引Void    setAllMvField( TComMvField const & mvField, PartSize eMbMode, Int iPartAddr, UInt uiDepth, Int iPartIdx=0 );  // 同时设置/存储MV及参考帧索引Void setNumPartition( Int iNumPart ){m_uiNumPartition = iNumPart;}Void linkToWithOffset( TComCUMvField const * src, Int offset ){m_pcMv     = src->m_pcMv     + offset;m_pcMvd    = src->m_pcMvd    + offset;m_piRefIdx = src->m_piRefIdx + offset;}#if REDUCED_ENCODER_MEMORYVoid compress(SChar *pePredMode, const SChar* pePredModeSource, const Int scale, const TComCUMvField &source);
#elseVoid compress(SChar* pePredMode, Int scale);
#endif
};

TComMvField

官方定义:class for motion vector with reference index
该Class表示带有参考帧索引信息的MV
(疑问:为什么不直接用TComCUMvField的MV和参考帧索引呢?而是额外定义了这个Class?)

协作图:

该Class定义如下:

/// class for motion vector with reference index
class TComMvField
{private:TComMv    m_acMv;  // MVInt       m_iRefIdx;  // 参考帧索引public:TComMvField() : m_iRefIdx( NOT_VALID ) {}// 设置MV及其参考帧索引信息Void setMvField( TComMv const & cMv, Int iRefIdx ){m_acMv    = cMv;m_iRefIdx = iRefIdx;}// 单独设置参考帧索引Void setRefIdx( Int refIdx ) { m_iRefIdx = refIdx; }// 获取MVTComMv const & getMv() const { return  m_acMv; }TComMv       & getMv()       { return  m_acMv; }Int getRefIdx() const { return  m_iRefIdx;       }Int getHor   () const { return  m_acMv.getHor(); }Int getVer   () const { return  m_acMv.getVer(); }
};

TComMv

官方定义:basic motion vector class
即MV的Class,内部主要包括其水平和垂直分量

主要内容:

private:Short m_iHor;     ///< horizontal component of motion vectorShort m_iVer;     ///< vertical component of motion vectorpublic:// 设置水平/垂直变量Void  set       ( Short iHor, Short iVer)     { m_iHor = iHor;  m_iVer = iVer;            }Void  setHor    ( Short i )                   { m_iHor = i;                               }Void  setVer    ( Short i )                   { m_iVer = i;                               }// 获取水平/垂直变量Int   getHor    () const { return m_iHor;          }Int   getVer    () const { return m_iVer;          }Int   getAbsHor () const { return abs( m_iHor );   }Int   getAbsVer () const { return abs( m_iVer );   }

AMVPInfo

官方定义:parameters for AMVP
该结构体表示AMVP候选列表,其内容包括三个:AMVP候选列表m_acMvCand、列表内有效候选数量iN、列表内空域候选数量numSpatialMVPCandidates

typedef struct _AMVPInfo
{TComMv m_acMvCand[ AMVP_MAX_NUM_CANDS ];  ///< array of motion vector predictor candidatesInt    iN;                                ///< number of motion vector predictor candidates
#if MCTS_ENC_CHECKUInt   numSpatialMVPCandidates;
#endif
} AMVPInfo;

帧间预测入口函数

帧间预测部分的入口函数是xCompressCU,其主要作用是完成块划分,确定最优预测模式。主要可以分为:
1.帧间预测xCheckRDCostInter——Inter模式、xCheckRDCostMerge2Nx2N——Merge模式
2.帧内预测xCheckRDCostIntra
3.PCM模式xCheckIntraPCM

xCompressCU的学习可参考博客:
HEVC代码学习11:xCompressCU函数
HM编码器代码阅读(12)——CU编码

Inter模式(AMVP)

入口函数——xCheckRDCostInter

Inter模式(AMVP模式)的入口函数为xCheckRDCostInter,被xCompressCU调用。
该函数主要流程如下:
(1)调用predInterSearch,进行ME(运动估计)和MC(运动补偿)。
(2)调用encodeResAndCalcRdInterCU,根据预测值计算残差,然后进行TU的划分、变换、量化等操作,并计算RD cost。
(3)调用xCheckBestMode选择最好的模式。

xCheckRDCostInter的学习可参考博客:
HEVC代码学习12:xCheckRDCostInter函数
HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程

predInterSearch

predInterSearch的作用是进行ME(运动估计)和MC(运动补偿)。
predInterSearch的学习可参考博客:
HEVC代码学习13:predInterSearch函数
HM编码器代码阅读(14)——帧间预测之AMVP模式(二)predInterSearch函数

该函数的主要流程如下:
对当前CU下所有PU逐一进行以下操作(PU地址索引为ipartIdx):
(1)遍历参考列表(iRefList)以及参考列表中所有参考帧(参考帧索引iRefIdxTemp),进行以下操作:

  • 调用xEstimateMvPredAMVP获取最优MVP,以及最优MVP在候选列表中的索引、MVP候选列表中候选数量。调用代码如下:
 // 获取最优MVPxEstimateMvPredAMVP( pcCU, pcOrgYuv, iPartIdx, eRefPicList, iRefIdxTemp, cMvPred[iRefList][iRefIdxTemp], false, &biPDistTemp);// 获取最优MVP索引
aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->getMVPIdx(eRefPicList, uiPartAddr);
// 获取MVP候选列表中候选数量
aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->getMVPNum(eRefPicList, uiPartAddr);

xEstimateMvPredAMVP函数的详细情况将单独写一篇文章进行总结,文章链接如下:
HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)

总之对于predInterSearch来说,上述三个语句执行后的返回结果为:

 //最优MV存入cMvPred[iRefList][iRefIdxTemp]cMvPred[iRefList][iRefIdxTemp] = cBestMv// 最优MVP索引存入CU中(执行xEstimateMvPredAMVP的结果),然后赋值给aaiMvpIdx[iRefList][iRefIdxTemp]aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->m_apiMVPIdx[eRefPicList][uiPartAddr]  // MVP候选数量存入CU中(执行xEstimateMvPredAMVP的结果),然后赋值给aaiMvpNum[iRefList][iRefIdxTemp] aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->m_apiMVPNum[eRefPicList][uiPartAddr]
  • 调用xMotionEstimation进行运动估计,以上面得到的最优MVP为起点进行搜索,最终得到最优MV以及其bits、cost。
    调用语句如下:
 xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList] [iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp,  uiCostTemp );

xMotionEstimation的详细情况将单独写一篇文章进行总结。文章链接如下:
(待更)

总之调用后的返回结果为:

  cMvTemp[iRefList][iRefIdxTemp] = 最优MVuiBitsTemp = 最优MV的bitsuiCostTemp = 最优MV的cost
  • 调用xCheckBestMVP更新最优MVP。在得到最优MV后,重新寻找最优MVP,并于之前的最优MVP比较bits,从而更新最优MVP。
    (这里有个疑问,更新的时候为什么比较bit而不是cost?是为接下来编码做准备吗?而且更新MVP的目的是什么?更新之后的MVP还有什么用?)

  • 更新每一参考列表下最优(cost最小)MV,以及其对应的参考帧索引。
    代码如下:

 if ( iRefList == 0 ){uiCostTempL0[iRefIdxTemp] = uiCostTemp;uiBitsTempL0[iRefIdxTemp] = uiBitsTemp;}if ( uiCostTemp < uiCost[iRefList] ){uiCost[iRefList] = uiCostTemp;uiBits[iRefList] = uiBitsTemp; // storing for bi-prediction// set motioncMv[iRefList]     = cMvTemp[iRefList][iRefIdxTemp];iRefIdx[iRefList] = iRefIdxTemp;}if ( iRefList == 1 && uiCostTemp < costValidList1 && pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp ) < 0 ){costValidList1 = uiCostTemp;bitsValidList1 = uiBitsTemp;// set motionmvValidList1     = cMvTemp[iRefList][iRefIdxTemp];refIdxValidList1 = iRefIdxTemp;}

因此在遍历完所有参考列表下的所有参考帧后,将得到该PU在所有参考列表[iRefList]下的最优MVcMv[iRefList]以及其对应的参考帧索引iRefIdx[iRefList]、bits、cost。

(2)B帧处理

(3)存储MV、MVD及其信息。

// B帧
if ( uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]){uiLastMode = 2;pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMv( cMvBi[0], ePartSize, uiPartAddr, 0, iPartIdx );pcCU->getCUMvField(REF_PIC_LIST_0)->setAllRefIdx( iRefIdxBi[0], ePartSize, uiPartAddr, 0, iPartIdx );pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMv( cMvBi[1], ePartSize, uiPartAddr, 0, iPartIdx );pcCU->getCUMvField(REF_PIC_LIST_1)->setAllRefIdx( iRefIdxBi[1], ePartSize, uiPartAddr, 0, iPartIdx );TempMv = cMvBi[0] - cMvPredBi[0][iRefIdxBi[0]];pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd    ( TempMv,                 ePartSize, uiPartAddr, 0, iPartIdx );TempMv = cMvBi[1] - cMvPredBi[1][iRefIdxBi[1]];pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd    ( TempMv,                 ePartSize, uiPartAddr, 0, iPartIdx );pcCU->setInterDirSubParts( 3, uiPartAddr, iPartIdx, pcCU->getDepth(0) );pcCU->setMVPIdxSubParts( aaiMvpIdxBi[0][iRefIdxBi[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( aaiMvpNum[0][iRefIdxBi[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPIdxSubParts( aaiMvpIdxBi[1][iRefIdxBi[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( aaiMvpNum[1][iRefIdxBi[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));uiMEBits = uiBits[2];}//P帧(List0)else if ( uiCost[0] <= uiCost[1] ){uiLastMode = 0;pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMv( cMv[0], ePartSize, uiPartAddr, 0, iPartIdx );pcCU->getCUMvField(REF_PIC_LIST_0)->setAllRefIdx( iRefIdx[0], ePartSize, uiPartAddr, 0, iPartIdx );TempMv = cMv[0] - cMvPred[0][iRefIdx[0]];pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd    ( TempMv,                 ePartSize, uiPartAddr, 0, iPartIdx );pcCU->setInterDirSubParts( 1, uiPartAddr, iPartIdx, pcCU->getDepth(0) );pcCU->setMVPIdxSubParts( aaiMvpIdx[0][iRefIdx[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( aaiMvpNum[0][iRefIdx[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));uiMEBits = uiBits[0];}// P帧(List1)else{uiLastMode = 1;pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMv( cMv[1], ePartSize, uiPartAddr, 0, iPartIdx );pcCU->getCUMvField(REF_PIC_LIST_1)->setAllRefIdx( iRefIdx[1], ePartSize, uiPartAddr, 0, iPartIdx );TempMv = cMv[1] - cMvPred[1][iRefIdx[1]];pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd    ( TempMv,                 ePartSize, uiPartAddr, 0, iPartIdx );pcCU->setInterDirSubParts( 2, uiPartAddr, iPartIdx, pcCU->getDepth(0) );pcCU->setMVPIdxSubParts( aaiMvpIdx[1][iRefIdx[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( aaiMvpNum[1][iRefIdx[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));uiMEBits = uiBits[1];}

以上代码简单来说即:

MV存入:            pcCU->m_acCUMVField[eRefPicList]->m_pcMv
MV参考帧索引存入:    pcCU->m_acCUMVField[eRefPicList]->m_piRefIdx
MVD信息存入:        pcCU->m_acCUMVField[eRefPicList]->m_pcMvd
帧间预测方向信息存入: pcCU->m_puhInterDir
最优MVP索引信息存入: pcCU->m_apiMVPIdx[eRefPicList]
MVP候选数量信息存入: pcCU->m_apiMVPNum[eRefPicList]

(4)对于非2Nx2N的分块,需要计算并合并他们的运动估计代价。

(5)调用motionCompensation进行运动补偿

Merge模式

入口函数—xCheckRDCostMerge2Nx2N

Merge模式与AMVP模式不同,其得到的MVP将直接作为MV(没有MVD),因此不需要再进行运动估计,直接进行运动补偿。

执行流程如下:
(1)调用getInterMergeCandidates获取MVP候选列表;
(2)调用motionCompensation进行运动补偿;
(3)调用encodeResAndCalcRdInterCU计算残差,变换量化,选出最优的QP参数;
(4)调用xCheckBestMode,比较选出最优MV和最优模式信息

xCheckRDCostMerge2Nx2N的学习可以参考博客:
HM编码器代码阅读(17)——帧间预测之merge模式(一)Merge模式的介绍以及相关函数
HEVC代码学习31:xCheckRDCostMerge2Nx2N函数

getInterMergeCandidates

该函数的作用是建立Merge模式下的MVP候选列表。

其处理流程如下:
(1)先建立空域候选列表。空域最多只能提供4个候选MV,候选的遍历顺序是A1->B1->B0->A0->B2,优先处理前面四个,如果前面四个有不满足条件的时,才处理B2。在遍历每一个相邻PU时,都进行以下操作:

  • 检测是否有效
  • 若有效则写入候选列表记录其MV
  • 检测列表是否已满

代码如下(以A1为例):

if ( isAvailableA1 )  // 检测是否有效,后面的PU在检测时会考虑之前PU的情况,所以检测flag会更复杂{abCandIsInter[iCount] = true;// get Inter Dir 获取帧间预测方向puhInterDirNeighbours[iCount] = pcCULeft->getInterDir( uiLeftPartIdx );// get Mv from Left 获取List0的MV信息,该函数的原理详见下面TComDataCU::getMvField( pcCULeft, uiLeftPartIdx, REF_PIC_LIST_0, pcMvFieldNeighbours[iCount<<1] );if ( getSlice()->isInterB() ) // 如果是B帧,再获取List1的MV信息{TComDataCU::getMvField( pcCULeft, uiLeftPartIdx, REF_PIC_LIST_1, pcMvFieldNeighbours[(iCount<<1)+1] );}// 空间候选数量标志 numSpatialMergeCandidates + 1if ( mrgCandIdx == iCount ){#if MCTS_ENC_CHECKnumSpatialMergeCandidates = iCount + 1;
#endifreturn;}// 候选数量+1iCount ++;}// early termination 检测列表是否已满,如果已满则直接结束列表的建立if (iCount == getSlice()->getMaxNumMergeCand()){#if MCTS_ENC_CHECKnumSpatialMergeCandidates = iCount;
#endifreturn;}

其中获取相邻PU的MV的函数是TComDataCU::getMvField,在上面关于TComDataCU的总结中已知,getMvFieldTComDataCU中定义的一个用于获取MV信息的函数,下面具体学习一下这个函数:

Void TComDataCU::getMvField ( const TComDataCU* pcCU, UInt uiAbsPartIdx, RefPicList eRefPicList, TComMvField& rcMvField )
{if ( pcCU == NULL )  // OUT OF BOUNDARY{TComMv  cZeroMv;rcMvField.setMvField( cZeroMv, NOT_VALID );return;}const TComCUMvField*  pcCUMvField = pcCU->getCUMvField( eRefPicList );rcMvField.setMvField( pcCUMvField->getMv( uiAbsPartIdx ), pcCUMvField->getRefIdx( uiAbsPartIdx ) );
}

setMvField的定义为:

  Void setMvField( TComMv const & cMv, Int iRefIdx ){m_acMv    = cMv;m_iRefIdx = iRefIdx;}

所以getMvField的返回结果为:

rcMvField.m_acMv = pcCU->m_acCUMvField[eRefPicList]->m_pcMv[uiAbsPartIdx];
rcMvField.m_iRefIdx = pcCU->m_acCUMvField[eRefPicList]->m_piRefIdx[uiAbsPartIdx];

即,将pcCU的MV和参考帧索引信息赋给rcMvField。具体到调用getMvField语句(以A1为例),就是将pcCULeft的List0的MV和参考帧索引信息赋值给pcMvFieldNeighbours[iCount<<1](偶数位),将pcCULeft的List1的MV和参考帧索引信息赋值给pcMvFieldNeighbours[(iCount<<1)+1](奇数位)。iCount表示当前选中的MVP在MVP候选列表中的位置。

    TComDataCU::getMvField( pcCULeft, uiLeftPartIdx, REF_PIC_LIST_0, pcMvFieldNeighbours[iCount<<1] );if ( getSlice()->isInterB() ) {TComDataCU::getMvField( pcCULeft, uiLeftPartIdx, REF_PIC_LIST_1, pcMvFieldNeighbours[(iCount<<1)+1] );}

(2)建立时域候选列表
(3)为B Slice建立组合列表
(4)候选列表未满时用0填充

xCheckRDCostMerge2Nx2N调用getInterMergeCandidates的代码吗如下:

#if MCTS_ENC_CHECKUInt numSpatialMergeCandidates = 0;rpcTempCU->getInterMergeCandidates( 0, 0, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand, numSpatialMergeCandidates );
#elserpcTempCU->getInterMergeCandidates( 0, 0, cMvFieldNeighbours,uhInterDirNeighbours, numValidMergeCand );
#endif

因此从getInterMergeCandidates返回的结果有:

cMvFieldNeighbours[] = MVP候选列表
uhInterDirNeighbours[] = 列表中对应的帧间预测方向
numValidMergeCand = 候选列表中有效候选数量
numSpatialMergeCandidates = 候选列表中空域候选数量

HEVC代码学习:帧间预测——MVP过程中MV的获取、传递及存储相关推荐

  1. HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)

    HEVC帧间预测在AMVP模式下是依靠xEstimateMvPredAMVP函数获取预测MV(MVP)的. 这部分内容的学习还可以参考这两篇博客: HEVC代码学习15:AMVP相关函数 HM编码器代 ...

  2. H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng

    H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar 下一篇:H.266/VVC-VTM代码学习-帧内 ...

  3. Overview of HEVC之5 帧间预测

    预测块(PB)的划分:与帧内预测的CB相比, HEVC为帧间预测的CB提供了更多的PB划分形状: PART_2N×2N的划分模式表示CB不划分:PART_2N×N的划分模式表示CB水平划分成两个相等尺 ...

  4. HEVC亮度分量帧内预测模式代码详解

    作者:66 (转载请注明出处) 从我自身的学习历程来看,建议大家先把理论扎实了再去理解代码,代码里也有注释,与理论的地方一对应加上一点C编程基础很容易就能理解. 我们先了解相关理论,此处可参考这一篇博 ...

  5. 【十六】 H.266/VVC | VVC中帧间预测技术详细总结 | 所有帧间预测技术代码汇总

    前言 ​ 帧间预测是影响视频编码性能的关键环节之一,H.266/VVC帧间预测在传统只能应对简单的平移运动的基础上,采用了仿射运动模型,可以描述更加复杂的缩放.旋转等运动.为了更好的发挥合并模式(Me ...

  6. 【VTM10.0代码学习】帧间预测xCheckRdCostMerge2N*2N

      xCheckRdCostMerge函数在xCompressCU中被调用,这里包括了常规Merge模式.CIIP模式和MMVD模式,GPM在xCheckRDCostGeo函数中.也就是说常规Merg ...

  7. HEVC代码学习19:MV、MVD、MVP概念解析

    在代码阅读中,可以看到MV.MVD.MVP三个概念,在开始学习的时候就很糊涂,一直买具体来看下,也找不到具体在哪里有讲解,现在来关注学习一下. 先来逗比一下,名词解析: MVP--most valua ...

  8. HEVC帧间预测流程梳理

    HEVC帧间预测流程 前言:最近在琢磨hevc理论知识,看着那本书绕过去绕过来咋也没把流程想通,去找了师兄说耽误他一分钟,结果叭叭叭了六小时哈哈哈哈. 最后还是感谢师兄给我解答问题,我写这篇文章主要是 ...

  9. HEVC参考软件HM源码分析--帧间预测(1)--xCompressCU

    本文首先对HM中帧间预测的基本流程作简要介绍,接着对代码中关键变量的用途作出说明,最后以源代码+注释的形式进行具体分析. 备注:这位大神的博客对楼主帮助很大,解决了我的不少疑惑,最后才能顺利写下这篇博 ...

最新文章

  1. EventBus VS Spring Event
  2. 计算机网络-信道的极限容量
  3. error: #5: cannot open source input file core_cm3.h: No such file or directory
  4. 实现权限控制_在 Go 语言中使用 casbin 实现基于角色的 HTTP 权限控制
  5. Machine Learning On Spark——基础数据结构(二)
  6. 【Python】pyCryptodome模块实现AES加密、解密
  7. 基于环境气象因素影响的异常就诊量预测
  8. 今天我要批判技术管理者
  9. 管理系统页面脚手架(一)
  10. 在SQL Server 2016中使用动态数据屏蔽来保护敏感数据
  11. python 用mysqldb方式操作数据库
  12. matlab初学者教程_初学者的Hibernate教程
  13. delphi if多个条件_【会计职场】老会计带你玩转Excel,IF函数的使用方法大全!小白必看!...
  14. c++构造函数分类说明
  15. 关于uuid与自增列的选择
  16. datastage连接mysql库_Datastage 8.5 连接远程Oracle 数据库
  17. DSP 2812: 使用C++封装外设时钟控制
  18. Oracle PO ER Model
  19. python资本市场财务数据分析_不懂财务数据分析?教你一分钟看懂财务报表
  20. 元胞自动机:森林火灾模拟(Python:numpy、seaborn)

热门文章

  1. python django前端重构_Django学习笔记(11)——开发图书管理页面
  2. 科普 | 通俗易懂的介绍云计算、大数据和人工智能
  3. ab压力测试命令及参数详解
  4. 图像处理-双边滤波和联合双边滤波
  5. 如何做动作技术分析?分析技术动作的软件哪个好?
  6. “AI 终有可能消灭人类!”
  7. 安装centos7操作系统,安装界面在选择语言时卡主
  8. 5. Qt5 实现Ftp功能
  9. 厕所里的尿池可以更小
  10. uni-app 微信小程序分享功能