HEVC代码学习:帧间预测——MVP过程中MV的获取、传递及存储
作为一个视频编码小白,最近开始着手啃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
的总结中已知,getMvField
是TComDataCU
中定义的一个用于获取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的获取、传递及存储相关推荐
- HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)
HEVC帧间预测在AMVP模式下是依靠xEstimateMvPredAMVP函数获取预测MV(MVP)的. 这部分内容的学习还可以参考这两篇博客: HEVC代码学习15:AMVP相关函数 HM编码器代 ...
- H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng
H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar 下一篇:H.266/VVC-VTM代码学习-帧内 ...
- Overview of HEVC之5 帧间预测
预测块(PB)的划分:与帧内预测的CB相比, HEVC为帧间预测的CB提供了更多的PB划分形状: PART_2N×2N的划分模式表示CB不划分:PART_2N×N的划分模式表示CB水平划分成两个相等尺 ...
- HEVC亮度分量帧内预测模式代码详解
作者:66 (转载请注明出处) 从我自身的学习历程来看,建议大家先把理论扎实了再去理解代码,代码里也有注释,与理论的地方一对应加上一点C编程基础很容易就能理解. 我们先了解相关理论,此处可参考这一篇博 ...
- 【十六】 H.266/VVC | VVC中帧间预测技术详细总结 | 所有帧间预测技术代码汇总
前言 帧间预测是影响视频编码性能的关键环节之一,H.266/VVC帧间预测在传统只能应对简单的平移运动的基础上,采用了仿射运动模型,可以描述更加复杂的缩放.旋转等运动.为了更好的发挥合并模式(Me ...
- 【VTM10.0代码学习】帧间预测xCheckRdCostMerge2N*2N
xCheckRdCostMerge函数在xCompressCU中被调用,这里包括了常规Merge模式.CIIP模式和MMVD模式,GPM在xCheckRDCostGeo函数中.也就是说常规Merg ...
- HEVC代码学习19:MV、MVD、MVP概念解析
在代码阅读中,可以看到MV.MVD.MVP三个概念,在开始学习的时候就很糊涂,一直买具体来看下,也找不到具体在哪里有讲解,现在来关注学习一下. 先来逗比一下,名词解析: MVP--most valua ...
- HEVC帧间预测流程梳理
HEVC帧间预测流程 前言:最近在琢磨hevc理论知识,看着那本书绕过去绕过来咋也没把流程想通,去找了师兄说耽误他一分钟,结果叭叭叭了六小时哈哈哈哈. 最后还是感谢师兄给我解答问题,我写这篇文章主要是 ...
- HEVC参考软件HM源码分析--帧间预测(1)--xCompressCU
本文首先对HM中帧间预测的基本流程作简要介绍,接着对代码中关键变量的用途作出说明,最后以源代码+注释的形式进行具体分析. 备注:这位大神的博客对楼主帮助很大,解决了我的不少疑惑,最后才能顺利写下这篇博 ...
最新文章
- EventBus VS Spring Event
- 计算机网络-信道的极限容量
- error: #5: cannot open source input file core_cm3.h: No such file or directory
- 实现权限控制_在 Go 语言中使用 casbin 实现基于角色的 HTTP 权限控制
- Machine Learning On Spark——基础数据结构(二)
- 【Python】pyCryptodome模块实现AES加密、解密
- 基于环境气象因素影响的异常就诊量预测
- 今天我要批判技术管理者
- 管理系统页面脚手架(一)
- 在SQL Server 2016中使用动态数据屏蔽来保护敏感数据
- python 用mysqldb方式操作数据库
- matlab初学者教程_初学者的Hibernate教程
- delphi if多个条件_【会计职场】老会计带你玩转Excel,IF函数的使用方法大全!小白必看!...
- c++构造函数分类说明
- 关于uuid与自增列的选择
- datastage连接mysql库_Datastage 8.5 连接远程Oracle 数据库
- DSP 2812: 使用C++封装外设时钟控制
- Oracle PO ER Model
- python资本市场财务数据分析_不懂财务数据分析?教你一分钟看懂财务报表
- 元胞自动机:森林火灾模拟(Python:numpy、seaborn)