高级运动矢量预测模式(Advanced Motion Vector Prediction,AMVP)
AMVP模式是H.265/HEVC中提出的新的MV预测技术,H.266/VVC仍采用了该技术,并在HEVC的基础上进行了改进。AMVP利用空域和时域的运动矢量的相关性,为当前PU建立了候选预测MV列表,编码端从其中选出最优的预测MVP,并对MVP进行差分编码(即通过运动搜索获得真正的MV,将运动矢量差进行编码(MV-MVP));解码端通过建立相同的列表,仅需要预测MVP在列表中的索引和运动向量残差MVD即可计算出当前PU的MV。
AVMP与Merge模式的区别:
- Merge模式是直接将通过空域和时域MV的相关性获得的预测MVP作为最终的MV,而不存在MVD;AMVP模式是以相关性得到预测MVP作为搜索起点,通过运动搜索获得更准确的MVP,然后再将预测的MVP和搜索得到的MV之间的差值MVD进行编码
- Merge模式仅需要传最佳预测MV在候选列表中的索引;AMVP模式除了需要传最佳MVP在候选列表的索引,还需要对运动矢量差MVD进行编码
- Merge模式和AMVP模式的候选列表长度不同(Merge模式的候选列表长度为6,AMVP模式的候选列表长度为2),构建MVP候选列表的方式也不同
AMVP的候选列表长度为2,候选MV总共有4种类型:
- 基于相邻块的空域MVP
- 基于同位块的时域MVP
- 基于历史信息构建的HMVP
- 零MV
一、AMVP的候选列表构建
空域相邻MV
VVC的AMVP模式规定只能从当前PU的左侧和上方各产生一个候选MV,最多允许2个空域候选MV,左侧的检查顺序是A1->A0,上侧的检查顺序是B1->B0->B2。
时域相邻MV
AMVP的时域候选MV推导和Merge模式相同,构建过程可以参考:扩展的Merge模式
HMVP候选
如果空域和时域候选MV推导完成之后,候选列表长度仍不足2时,需要添加HMVP候选,最多添加一个HMVP候选,使用HMVP列表中最新的HMVP候选。其中HMVP列表的构建和Merge模式中的HMVP列表的构建类似。
零MV
当进行完上述三种候选过程后,AMVP候选列表长度仍不足2,需要使用(0,0)MV进行填补。
代码及注释如下:(基于VTM10.0)
/** Constructs a list of candidates for AMVP (See specification, section "Derivation process for motion vector predictor candidates")
* \param uiPartIdx
* \param uiPartAddr
* \param eRefPicList
* \param iRefIdx
* \param pInfo
*/
void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo)
{
CodingStructure &cs = *pu.cs;
AMVPInfo *pInfo = &amvpInfo;
pInfo->numCand = 0;
if (refIdx < 0)
{
return;
}
//======================================获取空域MV====================================
//-- Get Spatial MV
Position posLT = pu.Y().topLeft();
Position posRT = pu.Y().topRight();
Position posLB = pu.Y().bottomLeft();
{ //获取当前PU左侧的MV,检查顺序:左下->左侧
bool bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, *pInfo );
if( !bAdded )
{
bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, *pInfo );
}
}
// Above predictor search
{//获取当前PU上侧MV,检查顺序:右上->上->左上
bool bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, *pInfo );
if( !bAdded )
{
bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, *pInfo );
if( !bAdded )
{
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, *pInfo );
}
}
}
for( int i = 0; i < pInfo->numCand; i++ )
{
pInfo->mvCand[i].roundTransPrecInternal2Amvr(pu.cu->imv);//精度转换
}
if( pInfo->numCand == 2 )
{
if( pInfo->mvCand[0] == pInfo->mvCand[1] ) //去除冗余项,如果前两个MV相同,则删除后一个
{
pInfo->numCand = 1;
}
}
// 获取时域MV候选
if (cs.picHeader->getEnableTMVPFlag() && pInfo->numCand < AMVP_MAX_NUM_CANDS && (pu.lumaSize().width + pu.lumaSize().height > 12))
{
// Get Temporal Motion Predictor
const int refIdx_Col = refIdx;
Position posRB = pu.Y().bottomRight().offset(-3, -3);
const PreCalcValues& pcv = *cs.pcv;
Position posC0;//bottomRight.offset(1,1)
bool C0Avail = false;
Position posC1 = pu.Y().center();
Mv cColMv;
// 判断当前CU右下是否超出图片边界
bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight);
const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos());
if (curSubPic.getTreatedAsPicFlag())
{
boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() &&
(posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom());
}
if (boundaryCond)
{
int posYInCtu = posRB.y & pcv.maxCUHeightMask;
if (posYInCtu + 4 < pcv.maxCUHeight)
{
posC0 = posRB.offset(4, 4);
C0Avail = true;
}
}
if ( ( C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdx_Col, false ) ) || getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdx_Col, false ) )
{
cColMv.roundTransPrecInternal2Amvr(pu.cu->imv);
pInfo->mvCand[pInfo->numCand++] = cColMv;
}
}
// 如果候选列表长度仍小于2,则将HMVP加入到列表中
if (pInfo->numCand < AMVP_MAX_NUM_CANDS)
{
const int currRefPOC = cs.slice->getRefPic(eRefPicList, refIdx)->getPOC();
addAMVPHMVPCand(pu, eRefPicList, currRefPOC, *pInfo);
}
if (pInfo->numCand > AMVP_MAX_NUM_CANDS)
{
pInfo->numCand = AMVP_MAX_NUM_CANDS;
}
// 添加零MV
while (pInfo->numCand < AMVP_MAX_NUM_CANDS)
{
pInfo->mvCand[pInfo->numCand] = Mv( 0, 0 );
pInfo->numCand++;
}
for (Mv &mv : pInfo->mvCand)
{
mv.roundTransPrecInternal2Amvr(pu.cu->imv);
}
}
二、运动估计
H.266/VVC的参考软件平台VTM中使用TZSearch搜索算法进行运动估计。


TZSearch算法的步骤:
①确定搜索起始点:VVC中采用AMVP技术来确定起始搜索点,AMVP会给出若干个候选预测MV,编码器从中选择率失真代价最小的作为预测MV,并用其所指向的位置作为起始搜索点。
②以步长1开始,按照上图所示的菱形模板(或正方形模板)在搜索范围内进行搜索,其中步长以2的整数次幂的形式进行递增,选出率失真代价最小的点作为该步骤搜索的结果。
③若步骤②中得到的最优点对应的步长为1,则需要在该点的周围进行两点搜索,其主要目的是补充搜索最优点周围尚未搜索的点。如下图所示,若步骤②使用的是菱形模板,则最优点可能是2、4、5、7;若步骤二使用的是正方形模板,则最优点可能为1~8。两点搜索将会搜索图中与当前最优点距离最近的两个点。例如,若最优点为2,则会搜索a,b两个点;若最优点为6,则会搜素e,g两个点。
④若步骤②中得到的最优点对应的步长大于某个阈值,则以该最优点为中心,在一定范围内做全搜索,即搜索该范围内的所有的点,选择率失真代价最小的作为该步骤的最优点。
⑤以步骤四得到的最优点为新的起始的搜索点,重复步骤二到步骤四,细化搜索,当相邻两次细化搜索得到的最优点一致时停止细化搜索,此时得到的MV即为最终的MV。
三、对称MVD模式
对称MVD模式(symmetric MVD mode)是VVC中新提出的一种双向预测时MVD语法单元传输模式。在使用对称MVD模式时,在传输双向预测的运动信息时不需要传list0和list1中参考图像的索引和list1的MVD。这些信息可以在解码端生成。
对称MVD模式的解码过程如下:
1、在slice层,变量BiDirPredFlag, RefIdxSymL0和RefIdxSymL1按如下方式生成:
-若mvd_l1_zero_flag=1,则BiDirPredFlag=0
-否则,如果在list0中离当前图像最近的参考图像和在list1中离当前图像最近的参考图像分别是前向参考图像和后向参考图像对或分别是后向参考图像和前向参考图像对,且list0和list1的参考图片都是短期参考图片,则BiDirPredFlag=1。否则,BiDirPredFlag=0
2、在CTU层,如果CU是双向预测且BiDirPredFlag=1,则需要在码流中显示传输一个对称模式标识符来表明是否使用对称模式。
当对称模式标识符为真时,在码流中只需要传mvp_l0_flag, mvp_l1_flag和MVD0。list-0和list-1的参考索引分别被设置为等于该对参考图片。MVD1=(-MVD0)最终运动向量可由下式生成:
在解码端MVD1由MVD0的相反数生成,如下图所示。
在编码端进行对称MVD模式的运动估计时需要一个初始MV。这个初始MV是从单向运动搜索MV、双向运动搜索MV和AMVP list中选择率失真代价最小的MV得到。