VVC支持基于子块的时间运动矢量预测(SbTMVP)方法。 类似于HEVC中的时间运动矢量预测(temporal motion vector prediction, TMVP),SbTMVP使用同位图片中的运动场来改善当前图片中CU的运动矢量预测和Merge模式。 SbTVMP和TMVP使用的相同的同位图片。 (同位块:相邻已编码图像中对应位置的块)
SbTMVP在以下两个主要方面与TMVP不同:
SbTMVP中使用的子CU大小固定为8x8,SbTMVP模式仅适用于宽度和高度都大于或等于8的CU。另外,SbTMVP Merge候选的编码逻辑与其他Merge候选的编码逻辑相同,即,对于P或B片中的每个CU,执行附加RD检查以决定是否使用SbTMVP候选。
SbTMVP分两步预测当前CU内子CU的运动矢量:
Step1:检查下图中的空域相邻块A1。 如果A1具有将同位图片用作其参考图片的运动矢量(A1可用且其参考图像就是当前图像的同位图像),则将该运动矢量选择为要应用的运动偏移。 如果未识别到此类运动,则将运动偏移设置为(0,0)。
Step2:利用①中A1块的运动偏移,定位当前CU的参考同位块的位置。然后将当前CU以及其对应的同位图像中的同位块分割为8*8的子块,对于每个子块,获取对应同位子块的运动信息( 8*8 子块中心位置处)。在确定同位子CU的运动信息之后,以与HEVC的TMVP过程类似的方式,将其转换为当前子CU的运动矢量和参考帧索引,其中应用时间运动缩放以将时间运动矢量的参考图片与当前CU的参考图片对齐。
SbtMVP和Affine Merge的关系
在VVC中,SbTMVP和Affine Merge的候选是共存的,将SbTVMP得到的候选和仿射Merge候选共同组成基于子块的Merge候选列表。 通过序列参数集(SPS)标志启用/禁用SbTVMP模式。 如果启用了SbTMVP模式,则将SbTMVP预测变量添加为基于子块的Merge候选列表的第一项,然后是仿射Merge候选项。 基于子块的Merge列表的大小在SPS中用信号通知,并且基于子块的Merge列表的最大允许大小为5。
SbTMVP的相关代码及注释如下所示:(基于VTM10.0)
// 基于子块的TMVP技术if (sbTmvpEnableFlag && slice.getPicHeader()->getEnableTMVPFlag()){MergeCtx mrgCtx &#61; *affMrgCtx.mrgCtx;bool tmpLICFlag &#61; false;CHECK( mrgCtx.subPuMvpMiBuf.area() &#61;&#61; 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized" );mrgCtx.subPuMvpMiBuf.fill( MotionInfo() );int pos &#61; 0;// Get spatial MVconst Position posCurLB &#61; pu.Y().bottomLeft();MotionInfo miLeft;//leftconst PredictionUnit* puLeft &#61; cs.getPURestricted( posCurLB.offset( -1, 0 ), pu, pu.chType );const bool isAvailableA1 &#61; puLeft && isDiffMER(pu.lumaPos(), posCurLB.offset(-1, 0), plevel) && pu.cu !&#61; puLeft->cu && CU::isInter( *puLeft->cu );if ( isAvailableA1 ){ //A1块可用miLeft &#61; puLeft->getMotionInfo( posCurLB.offset( -1, 0 ) );// get Inter DirmrgCtx.interDirNeighbours[pos] &#61; miLeft.interDir;// get Mv from Left 获得A1块的MVmrgCtx.mvFieldNeighbours[pos <<1].setMvField( miLeft.mv[0], miLeft.refIdx[0] );if ( slice.isInterB() ){mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].setMvField( miLeft.mv[1], miLeft.refIdx[1] );}pos&#43;&#43;;}mrgCtx.numValidMergeCand &#61; pos;// 获得子PU的时域MV预测isAvailableSubPu &#61; getInterMergeSubPuMvpCand(pu, mrgCtx, tmpLICFlag, pos, 0);if ( isAvailableSubPu ){// 将子块的时域MV添加到Affine Merge列表中for ( int mvNum &#61; 0; mvNum <3; mvNum&#43;&#43; ){affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand <<1) &#43; 0][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 0].mv, mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 0].refIdx );affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand <<1) &#43; 1][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].mv, mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].refIdx );}affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] &#61; mrgCtx.interDirNeighbours[pos];affMrgCtx.affineType[affMrgCtx.numValidMergeCand] &#61; AFFINE_MODEL_NUM;affMrgCtx.mergeType[affMrgCtx.numValidMergeCand] &#61; MRG_TYPE_SUBPU_ATMVP;if ( affMrgCtx.numValidMergeCand &#61;&#61; mrgCandIdx ){return;}affMrgCtx.numValidMergeCand&#43;&#43;;// early terminationif ( affMrgCtx.numValidMergeCand &#61;&#61; maxNumAffineMergeCand ){return;}}}
其中&#xff0c;在getInterMergeSubPuMvpCand函数中获得子PU的MVP候选项
bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, bool& LICFlag, const int count, int mmvdList
)
{const Slice &slice &#61; *pu.cs->slice;const unsigned scale &#61; 4 * std::max
}
sbtMVP对应的mergetype是MRG_TYPE_SUBPU_ATMVP&#xff0c;相应的运动补偿函数为&#xff1a;
void InterPrediction::xSubPuMC( PredictionUnit& pu, PelUnitBuf& predBuf, const RefPicList &eRefPicList /*&#61; REF_PIC_LIST_X*/, const bool luma /*&#61; true*/, const bool chroma /*&#61; true*/)
{// compute the location of the current PU// 当前PU的位置和尺寸Position puPos &#61; pu.lumaPos();Size puSize &#61; pu.lumaSize();int numPartLine, numPartCol, puHeight, puWidth;{numPartLine &#61; std::max(puSize.width >> ATMVP_SUB_BLOCK_SIZE, 1u); // 行存在多少个子PUnumPartCol &#61; std::max(puSize.height >> ATMVP_SUB_BLOCK_SIZE, 1u); // 列存在多少个子PU//subPu 的宽度/高度&#xff0c;如果当前PU的宽度/高度大于8&#xff0c;则subPU的宽度/高度为8&#xff0c;否则subPU的宽度/高度和PU的宽度/高度相等puHeight &#61; numPartCol &#61;&#61; 1 ? puSize.height : 1 <
}