在HEVC中,当Slice header中的use_integer_mv_flag等于0时,编码快的MVD(CU的运动矢量和预测运动矢量之间的差值)以四分之一亮度像素精度进行编码。 在VVC中,引入了CU级自适应运动矢量精度(AMVR)方案。 AMVR允许以不同的精度对CU的MVD进行编码。
根据当前CU的模式(常规AMVP模式或仿射AVMP模式),可以如下自适应地选择当前CU的MVD:
- 常规AMVP模式:1/4亮度像素精度,1/2亮度像素精度,整数亮度像素精度或四倍亮度像素精度。
- 仿射AMVP模式:1/4亮度像素精度,整数亮度像素精度或1/16亮度像素精度。
只有当前CU具有至少一个非零MVD分量时,才会传输CU级像素精度。 如果所有MVD分量(即参考列表L0和参考列表L1的水平和垂直MVD)均为零,则MVD默认使用1/4亮度像素精度。
对于至少有一个非零MVD分量的CU,第一个标志位表示CU是否使用1/4亮度像素精度。如果第一个标志为0,则不需要传输其余的标志,当前CU的MVD使用1/4亮度像素精度。否则,
- 对于常规AMVP模式,需要第二个标志位以表示CU使用1/2亮度像素精度或其他MVD精度(整数或四倍亮度像素精度)。在1/2亮度像素精度下,使用6抽头插值滤波器而不是默认的8抽头插值滤波器。如果不是1/2亮度像素精度,则需要第三个标志用来指示当前CU使用整数亮度像素精度还是四倍亮度像素精度。
- 对于仿射AMVP模式,第二个标志用于指示是使用整数亮度像素精度还是1/16 亮度像素精度。
为了确保重建的MV具有同样的精度,在与MVD相加之前,CU的MVP(运动矢量预测)会四舍五入到与MVD相同的精度。MVP向零进行四舍五入(即负MVP向正无穷大舍入,正MVP向负无穷大舍入)。
编码器使用RD检查确定当前CU的MV精度。为了避免总是对每个CU的MVD执行四次精度检查,在VTM11中,除了1/4亮度像素精度外,其余MVD精度的RD检查只能在某些条件下执行。
- 对于普通AVMP模式,首先计算1/4亮度像素精度和整数亮度像素精度的RD Cost。然后将整数亮度像素精度的RD Cost与1/4亮度像素精度的RD Cost进行比较,以确定是否有必要进一步检验四倍亮度像素精度的RD Cost。如果1/4亮度像素精度的RD Cost远小于整数亮度像素精度,则跳过四倍亮度像素精度的RD检查。如果整数亮度像素精度的RD Cost显著大于先前测试的MVD精度的最佳RD Cost,则跳过1/2亮度像素精度的检查。
- 对于仿射AMVP模式,如果在检查仿射Merge/Skip模式、Merge/Skip模式、常规AMVP模式的1/4亮度像素精度和仿射AMVP模式1/4亮度像素精度的率失真代价后未选择仿射帧间模式,则不检查1/16亮度像素精度和整数亮度像素精度的仿射帧间模式。
此外,在1/16亮度像素级精度仿射帧间模式中,以1/4亮度像素精度仿射帧间模式得到的仿射参数作为搜索起点。
运动场存储
在VVC中,显式发出信号的运动矢量的最高精度为1/4像素精度。 在某些仿射帧间预测模式中,以1/16亮度像素精度导出运动矢量,以1/16精度执行运动补偿预测。 在内部运动场存储方面,所有运动矢量都以1 / 16亮度精度存储。
对于TMVP和SbTVMP使用的临时运动场存储,与HEVC中的16x16大小粒度相比,VVC以8x8大小粒度执行运动场压缩。
VTM中,MV的精度的定义:
enum MvPrecision
{
MV_PRECISION_4PEL = 0, // 4-pel
MV_PRECISION_INT = 2, // 1-pel, shift 2 bits from 4-pel
MV_PRECISION_HALF = 3, // 1/2-pel
MV_PRECISION_QUARTER = 4, // 1/4-pel (the precision of regular MV difference signaling), shift 4 bits from 4-pel
MV_PRECISION_SIXTEENTH = 6, // 1/16-pel (the precision of internal MV), shift 6 bits from 4-pel
MV_PRECISION_INTERNAL = 2 + MV_FRACTIONAL_BITS_INTERNAL,
};
几种模式的像素精度定义:
const MvPrecision Mv::m_amvrPrecision[4] = { MV_PRECISION_QUARTER, MV_PRECISION_INT, MV_PRECISION_4PEL, MV_PRECISION_HALF }; // for cu.imv=0, 1, 2 and 3
const MvPrecision Mv::m_amvrPrecAffine[3] = { MV_PRECISION_QUARTER, MV_PRECISION_SIXTEENTH, MV_PRECISION_INT }; // for cu.imv=0, 1 and 2
const MvPrecision Mv::m_amvrPrecIbc[3] = { MV_PRECISION_INT, MV_PRECISION_INT, MV_PRECISION_4PEL }; // for cu.imv=0, 1 and 2
精度转换的代码:
void changePrecision(const MvPrecision& src, const MvPrecision& dst)
{
const int shift = (int)dst - (int)src;
if (shift >= 0)
{
*this <<= shift;
}
else
{
const int rightShift = -shift;
const int nOffset = 1 << (rightShift - 1);
hor = hor >= 0 ? (hor + nOffset - 1) >> rightShift : (hor + nOffset) >> rightShift;
ver = ver >= 0 ? (ver + nOffset - 1) >> rightShift : (ver + nOffset) >> rightShift;
}
}