1.merge和AMVP
空域和时域相邻块的mv有一定的相关性,HEVC在mv预测方面提出merge和AMVP技术。merge和AMVP技术通过空域和时域已编码块构建候选mv列表,选取最优的一个当作当前PU的预测mv。merge不存在MVD,AMVP存在MVD,且候选列表的构建方式和长度均不同。skip是merge的一种特殊模式,区别在于传输时无残差。
merge:index + 残差
AMVP:index + MVD + 残差
skip:skip flag + index
merge:为当前PU构建一个mv候选列表,该列表包含5个候选mv(及其对应的参考图像)。变量这5个候选mv,通过率失真代价的计算,最终选取代价最小的作为最优mv。编/解码端按照相同的方式构建候选列表,编码器只需要传输索引,解码器就可从列表中得到当前PU的mv,大大节约了传输mv的比特数。
空域候选:A1->B1->B0->A0->(B2),选取4个
时域候选:H->(C3),选取一个,要进行放缩。
Bslice的PU存在两个mv,所以mv候选列列表需要提供两个预测mv。将mv候选列表中的前4个候选mv进行两两组合,得到B slice的组合列表,如下:
AMVP:传输索引+MVD,解码端用相同的方法构建候选列表,通过索引得到mvp,加上MVD得到mv。
空域:2个,左侧一个,上方一个。左侧:A0->A1->scaled A0 ->scaled A1。上方:B0->B1->B2->(->scaled B0->scaled B1->scaled B2)。检测到第一个可用的mv时,直接使用该mv作为预测候选mv,不再进行剩余步骤。
时域:与merge相同。
2.意义
1.大大节省了运动信息的编码比特数。
2.降低RDO复杂度。如:在RDO的PU选择最优帧间预测参数时,AMVP根据拉格朗日代价函数J,确定最优mv,再以该mv为起点得到mv,大大降低了复杂度。
3.代码
下面来看getInterMergeCandidates工作流程:
1.初始化准备工作。
2.建立空域候选列表,按顺序依次遍历A1-B1-B0-A0-B2位置PU,对每个位置进行以下操作,选出最多4个候选。
(1)检测是否有效
(2)若有效则写入候选列表记录其MV
(3)检测列表是否已满
3.建立时域候选列表。
4.为B Slice建立组合列表。
5.候选列表未满时,用0填充。
//! Construct a list of merging candidates 构造merge候选列表
Void TComDataCU::getInterMergeCandidates( UInt uiAbsPartIdx, UInt uiPUIdx, TComMvField* pcMvFieldNeighbours, UChar* puhInterDirNeighbours, Int& numValidMergeCand, Int mrgCandIdx )
{
/******************************************初始化准备工作**************************************/
UInt uiAbsPartAddr = m_absZIdxInCtu + uiAbsPartIdx; //当前CU的ZScan地址
Bool abCandIsInter[ MRG_MAX_NUM_CANDS ]; //MRG_MAX_NUM_CANDS最大候选数为5
for( UInt ui = 0; ui < getSlice()->getMaxNumMergeCand(); ++ui )
{
abCandIsInter[ui] = false;
pcMvFieldNeighbours[ ( ui << 1 ) ].setRefIdx(NOT_VALID);
pcMvFieldNeighbours[ ( ui << 1 ) + 1 ].setRefIdx(NOT_VALID);
}
numValidMergeCand = getSlice()->getMaxNumMergeCand(); //最大有效候选数为5
// compute the location of the current PU 计算当前PU的位置
Int xP, yP, nPSW, nPSH;
this->getPartPosition(uiPUIdx, xP, yP, nPSW, nPSH); //获取当前PU的索引、起始坐标、高和宽
Int iCount = 0;
UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;
PartSize cCurPS = getPartitionSize( uiAbsPartIdx ); //获取当前CU的分割模式
deriveLeftRightTopIdxGeneral( uiAbsPartIdx, uiPUIdx, uiPartIdxLT, uiPartIdxRT ); //左上右上块索引
deriveLeftBottomIdxGeneral( uiAbsPartIdx, uiPUIdx, uiPartIdxLB ); //左下块索引
/******************************************建立空域候选列表**************************************/
//left 左侧
UInt uiLeftPartIdx = 0;
TComDataCU* pcCULeft = 0;
pcCULeft = getPULeft( uiLeftPartIdx, uiPartIdxLB ); //获取左侧PU
//判断A1有效性
Bool isAvailableA1 = pcCULeft && //左侧PU是否有效
pcCULeft->isDiffMER(xP -1, yP+nPSH-1, xP, yP) && //左侧PU与当前PU是否在同一运动估计区域
!( uiPUIdx == 1 && (cCurPS == SIZE_Nx2N || cCurPS == SIZE_nLx2N || cCurPS == SIZE_nRx2N) ) && //分割模式判断
pcCULeft->isInter( uiLeftPartIdx ) ; //帧间预测是否有效
if ( isAvailableA1 ) //A1有效
{
abCandIsInter[iCount] = true;
// get Inter Dir
puhInterDirNeighbours[iCount] = pcCULeft->getInterDir( uiLeftPartIdx ); //获取帧间预测方向
// get Mv from Left
pcCULeft->getMvField( pcCULeft, uiL