HM编码器代码阅读(16)——帧间预测之AMVP模式(四)预测MV的获取

帧间预测的原理


AMVP的原理

    帧间预测的实质就是为当前的PU在参考帧中寻找一块最相似块(相似度的判断准则有SAD等方法)。但是参考图像通常都比较大,我们直接去搜索的话就太费时了,应该使用某种方法在参考图像中确定一个搜索起始点,然后再该搜索起始点的周围进行搜索,那么就能降低搜索的时间了。
    AMVP模式就提供了这样一种方法,在开始搜索之前,先为当前PU预测出一个MV,这个预测的MV就被称为MVP,预测的MV可以直接从空域或者时域上的相邻块直接得到,因为相邻块有多个,因此MVP也会有多个,这些MVP组成了MVP候选列表,我们需要从中选择最优的一个来作为实际的MVP,得到MVP之后,我们根MVP(MVP实际也是一个MV)来确定搜索的起始点,然后在搜索起始点的附近按照某种方法做搜索,最后得到一个最优的MV,这个MV就是实际MV,MV确定了参考块的位置,参考块与当前PU相减得到残差,达到了数据压缩的目的;
同时MV与MVP相减得到MV残差也就是MVD,也能够达到数据压缩的目的。
    它的大致工作流程是:
    1、根据某种方法获取MVP候选列表
    2、从候选列表中选出最优的一个MVP
    3、根据MVP确定运动估计的起始点
    4、在起始点附近,按照某种方法进行搜索
    5、搜索完毕之后得到最优的MV
    6、由MV确定参考块在参考图像中的位置
    7、参考块减去当前块(PU)得到残差块
    8、MV减去MVP得到MVD
    9、通过7和8两个步骤就能达到数据压缩的目的

merge模式的原理

    帧间预测的目的就是要得到一个MV(运动向量),然后根据该MV确定参考块在参考图像中的位置,
但是由于临近块的相似性(比如当前块和临近块都属于同一个物体,在镜头移动的时候,它们移动的距离和方向当然是相同的),因此很多时候我们并不需要去计算MV,我们把相邻块的MV直接当作当前块的MV。和AMVP相似,我们通过相邻块得到一个MVP候选列表,从中选出最优的一个MVP作为当前块的MV,然后根据该MV直接确定参考块的位置,确定了参考块之后就能计算残差了。因为MVP和MV相同,因此不存在MVD,因此编码的时候只需要编码MV(MVP)在候选列表中索引即可,不再需要编码MVD,解码可以按照类似的方法构造MVP候选列表,然后依据传送过来的索引就能得到MV了。
    工作流程是:
    1、根据某种方法获取MVP候选列表
    2、从候选列表中选出最优的一个MVP,同时得到该MVP在候选列表中的索引
    3、把该MVP作为当前块的MV
    4、根据MV确定参考块在参考图像中的位置
    5、参考块减去当前块得到残差块
    6、因为MVP和MV相同,因此没有MVD,只需把残差系数和MVP的索引传给解码器就行了



skip模式的原理

    skip模式是merge模式的一种特例。按照merge模式得到MV之后,如果编码器根据某种方法判断了当前块和参考块基本一样,那么不需要传输残差数据,只需要传送MV的索引和一个标志,来表明当前块可以直接从参考块得到。

xEstimateMvPredAMVP的工作流程

AMVP中MVP的获取就是通过xEstimateMvPredAMVP这个函数得到的
1、判断bFilled标识,该标识表示MVP候选列表是否已经建立好
2、如果bFilled是false,通过fillMvpCand获取MVP候选列表;否则,不需要重新建立MVP候选列表
3、先把MVP候选列表中的第一个MVP作为最优的MVP
4、如果候选列表中MVP的数量小于等于1,那么直接把步骤3选出的MVP返回
5、如果bFilled是true,表示MVP候选列表是原来已经建立好的,那么直接根据PU的相关信息得到最优的MVP,然后返回
6、遍历MVP候选列表,选出代价最小的MVP作为当前PU的MVP,并设置相关信息,然后返回

#if ZERO_MVD_EST
Void TEncSearch::xEstimateMvPredAMVP( TComDataCU* pcCU, TComYuv* pcOrgYuv, UInt uiPartIdx, RefPicList eRefPicList, Int iRefIdx, TComMv& rcMvPred, Bool bFilled, UInt* puiDistBiP, UInt* puiDist  )
#else
Void TEncSearch::xEstimateMvPredAMVP( TComDataCU* pcCU, TComYuv* pcOrgYuv, UInt uiPartIdx, RefPicList eRefPicList, Int iRefIdx, TComMv& rcMvPred, Bool bFilled, UInt* puiDistBiP )
#endif
{
	AMVPInfo* pcAMVPInfo = pcCU->getCUMvField(eRefPicList)->getAMVPInfo();

	TComMv  cBestMv;
	Int     iBestIdx = 0;
	TComMv  cZeroMv;
	TComMv  cMvPred;
	UInt    uiBestCost = MAX_INT;
	UInt    uiPartAddr = 0;
	Int     iRoiWidth, iRoiHeight;
	Int     i;

	// 得到此次分割的索引和大小
	pcCU->getPartIndexAndSize( uiPartIdx, uiPartAddr, iRoiWidth, iRoiHeight );
	
	// Fill the MV Candidates
	// 获取MVP(即预测的MV),MVP存放在pcAMVPInfo中
	if (!bFilled)
	{
		pcCU->fillMvpCand( uiPartIdx, uiPartAddr, eRefPicList, iRefIdx, pcAMVPInfo );
	}

	// initialize Mvp index & Mvp
	// 最优MVP的默认索引是0
	iBestIdx = 0;
	
	// 最优MVP默认是列表的第一个
	cBestMv  = pcAMVPInfo->m_acMvCand[0];
#if !ZERO_MVD_EST

	// 如果MVP候选列表中MVP的数量是0或者1
	if (pcAMVPInfo->iN <= 1)
	{
		// 得到了最优的MVP
		rcMvPred = cBestMv;

		pcCU->setMVPIdxSubParts( iBestIdx, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));
		pcCU->setMVPNumSubParts( pcAMVPInfo->iN, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));

		// 如果参考列表是list1(表示这是B slice)
		if(pcCU->getSlice()->getMvdL1ZeroFlag() && eRefPicList==REF_PIC_LIST_1)
		{
#if ZERO_MVD_EST
			(*puiDistBiP) = xGetTemplateCost( pcCU, uiPartIdx, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, rcMvPred, 0, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight, uiDist );
#else
			(*puiDistBiP) = xGetTemplateCost( pcCU, uiPartIdx, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, rcMvPred, 0, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight);
#endif
		}
		// 返回
		return;
	}
#endif  
	// 如果MVP候选列表已经填充完毕,那么直接返回MVP就行了
	// 注意:fillMvpCand并不会修改bFilled的值,bFilled的值是函数传进来的
	if (bFilled)
	{
		assert(pcCU->getMVPIdx(eRefPicList,uiPartAddr) >= 0);
		// 选择相应的MVP
		rcMvPred = pcAMVPInfo->m_acMvCand[pcCU->getMVPIdx(eRefPicList,uiPartAddr)];
		return;
	}

	m_cYuvPredTemp.clear();
#if ZERO_MVD_EST
	UInt uiDist;
#endif
	//-- Check Minimum Cost.
	// 遍历每一个MVP,选出代价最小的那个
	for ( i = 0 ; i < pcAMVPInfo->iN; i++)
	{
		UInt uiTmpCost;
#if ZERO_MVD_EST
		uiTmpCost = xGetTemplateCost( pcCU, uiPartIdx, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, pcAMVPInfo->m_acMvCand[i], i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight, uiDist );
#else
		uiTmpCost = xGetTemplateCost( pcCU, uiPartIdx, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, pcAMVPInfo->m_acMvCand[i], i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight);
#endif      
		if ( uiBestCost > uiTmpCost )
		{
			uiBestCost = uiTmpCost;
			cBestMv   = pcAMVPInfo->m_acMvCand[i];
			iBestIdx  = i;
			(*puiDistBiP) = uiTmpCost;
#if ZERO_MVD_EST
			(*puiDist) = uiDist;
#endif
		}
	}

	m_cYuvPredTemp.clear();

	// Setting Best MVP
	rcMvPred = cBestMv;
	pcCU->setMVPIdxSubParts( iBestIdx, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));
	pcCU->setMVPNumSubParts( pcAMVPInfo->iN, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));
	return;
}


MVP候选列表的建立

AMVP模式的候选列表,列表的长度是2,建立流程如下
     (1)空域列表的建立。假设当前PU的左下角是A0,左侧是A1,左上角是B2,上方是B1,右上角是B0。当前PU的左侧和上方需要各产生一个候选MV。对于左侧的候选MV的筛选,处理顺序是A0->A1->scaled A0->scaled A1;对上侧的候选MV的筛选,处理的顺序是B0->B1->B2(如果这几个都不存在,那么继续处理-> scaled B0-> scaled B2)。对于左侧(上方)来说,只要找到一个候选MV,就不继续处理后面的候选者了
     (2)时域列表的建立。与空域情况不同,时域候选列表不能直接使用候选块的运动信息,需要根据当前帧和参考帧之间的位置关系做相应的伸缩调整。时域最多只能提供一个候选MV。如果此时候选列表的候选MV的数量还不足2个,那么需要填充零向量。

AMVP模式的MVP候选列表是通过fillMvpCand函数建立的

Void TComDataCU::fillMvpCand ( UInt uiPartIdx, UInt uiPartAddr, RefPicList eRefPicList, Int iRefIdx, AMVPInfo* pInfo )
{
	TComMv cMvPred;
	Bool bAddedSmvp = false;

	pInfo->iN = 0;  
	if (iRefIdx < 0)
	{
		return;
	}

	//-- Get Spatial MV
	UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;
	// 在宽度(横向)上有多少个分割数
	UInt uiNumPartInCUWidth = m_pcPic->getNumPartInWidth();
	Bool bAdded = false;

	// 该函数调用的三个参数是,当前分割的索引,左下角分割的索引,右上角分割的索引
	deriveLeftRightTopIdx( uiPartIdx, uiPartIdxLT, uiPartIdxRT );
	deriveLeftBottomIdx( uiPartIdx, uiPartIdxLB );
	// 空域候选MV的选择方式
	// 要找出两个候选MV
	// 假设 左下角a0,左边a1,右上角b0,上方b1,左上角b2
	// 首先 按照a0->a1->scale a0->scale a1的顺序找到一个候选MV,只要找到一个就不再继续判断后面
	// 然后 按照scale b0->scale b1->scale b2->b0->b1->b2的顺序再找出一个候选MV,只有在这个顺序中找到一个,就不再判断后面的了

	/*
	比例伸缩模式(scaled)是当参考块存在,当前Pu的参考图像跟参考块的参考图像不一致时才选用的方式(因为是空域MV选择,即参考块和当前块在同一帧中,因此才会出现
	当前块和参考块参考的图像是否一致这种问题;如果是时域MV候选,那么当前块和参考块的参考图像肯定是不一致的)。
	具体计算方法如下公式所示,curMV为当前Pu的候选MV,td与tb分别为当前块与参考块到他们的参考图像间的距离,colMV 为参考块的mv。
	*/


	TComDataCU* tmpCU = NULL;
	UInt idx;
	// 得到左下角的相邻的cu
	tmpCU = getPUBelowLeft(idx, uiPartIdxLB);
	bAddedSmvp = (tmpCU != NULL) && (tmpCU->getPredictionMode(idx) != MODE_INTRA);

	// 没过没有左下角的
	if (!bAddedSmvp)
	{
		// 得到左边的相邻的cu
		tmpCU = getPULeft(idx, uiPartIdxLB);
		bAddedSmvp = (tmpCU != NULL) && (tmpCU->getPredictionMode(idx) != MODE_INTRA);
	}

	// Left predictor search
	// 添加到候选列表中
	bAdded = xAddMVPCand( pInfo, eRefPicList, iRefIdx, uiPartIdxLB, MD_BELOW_LEFT);
	if (!bAdded) 
	{
		// 左边相邻的cu
		bAdded = xAddMVPCand( pInfo, eRefPicList, iRefIdx, uiPartIdxLB, MD_LEFT );
	}

	if(!bAdded)
	{
		bAdded = xAddMVPCandOrder( pInfo, eRefPicList, iRefIdx, uiPartIdxLB, MD_BELOW_LEFT);
		if (!bAdded) 
		{
			xAddMVPCandOrder( pInfo, eRefPicList, iRefIdx, uiPartIdxLB, MD_LEFT );
		}
	}

	// Above predictor search
	// 右上角
	bAdded = xAddMVPCand( pInfo, eRefPicList, iRefIdx, uiPartIdxRT, MD_ABOVE_RIGHT);

	if (!bAdded) 
	{
		// 上方
		bAdded = xAddMVPCand( pInfo, eRefPicList, iRefIdx, uiPartIdxRT, MD_ABOVE);
	}

	if(!bAdded)
	{
		// 左上角
		xAddMVPCand( pInfo, eRefPicList, iRefIdx, uiPartIdxLT, MD_ABOVE_LEFT);
	}

	if (!bAddedSmvp)
	{
		bAdded = xAddMVPCandOrder( pInfo, eRefPicList, iRefIdx, uiPartIdxRT, MD_ABOVE_RIGHT);
		if (!bAdded) 
		{
			bAdded = xAddMVPCandOrder( pInfo, eRefPicList, iRefIdx, uiPartIdxRT, MD_ABOVE);
		}

		if(!bAdded)
		{
			xAddMVPCandOrder( pInfo, eRefPicList, iRefIdx, uiPartIdxLT, MD_ABOVE_LEFT);
		}
	}

	// 判断列表中候选者的数量,如果等于2,那么进入下面的判断
	if ( pInfo->iN == 2 )
	{
		if ( pInfo->m_acMvCand[ 0 ] == pInfo->m_acMvCand[ 1 ] )
		{
			pInfo->iN = 1;
		}
	}

	// 判断是否启用时域的MVP功能(TMVP中的T即时域的意思),如果启用了,那么选择时域候选MV
	// 选择的参考块的位置位于 (当前cu所在的LCU的右下角的相邻cu),注意这是位置上,因为实际上参考块在另一个帧
	// 如果不存在,那么直接用该位置的mv即可——若H位置的同位Pu(邻近已编码图像对应位置Pu)不可用,则用位置的同位Pu代替
	// 缩放的方式和上面的一样
	if ( getSlice()->getEnableTMVPFlag() )
	{
		// Get Temporal Motion Predictor
		Int iRefIdx_Col = iRefIdx;
		TComMv cColMv;
		UInt uiPartIdxRB;
		UInt uiAbsPartIdx;  
		UInt uiAbsPartAddr;

		deriveRightBottomIdx( uiPartIdx, uiPartIdxRB );
		uiAbsPartAddr = m_uiAbsIdxInLCU + uiPartAddr;

		//----  co-located RightBottom Temporal Predictor (H) ---//
		uiAbsPartIdx = g_auiZscanToRaster[uiPartIdxRB];
		Int uiLCUIdx = -1;
		if ( ( m_pcPic->getCU(m_uiCUAddr)->getCUPelX() + g_auiRasterToPelX[uiAbsPartIdx] + m_pcPic->getMinCUWidth() ) >= m_pcSlice->getSPS()->getPicWidthInLumaSamples() )  // image boundary check
		{
		}
		else if ( ( m_pcPic->getCU(m_uiCUAddr)->getCUPelY() + g_auiRasterToPelY[uiAbsPartIdx] + m_pcPic->getMinCUHeight() ) >= m_pcSlice->getSPS()->getPicHeightInLumaSamples() )
		{
		}
		else
		{
			if ( ( uiAbsPartIdx % uiNumPartInCUWidth < uiNumPartInCUWidth - 1 ) &&           // is not at the last column of LCU 
				( uiAbsPartIdx / uiNumPartInCUWidth < m_pcPic->getNumPartInHeight() - 1 ) ) // is not at the last row    of LCU
			{
				uiAbsPartAddr = g_auiRasterToZscan[ uiAbsPartIdx + uiNumPartInCUWidth + 1 ];
				uiLCUIdx = getAddr();
			}
			else if ( uiAbsPartIdx % uiNumPartInCUWidth < uiNumPartInCUWidth - 1 )           // is not at the last column of LCU But is last row of LCU
			{
				uiAbsPartAddr = g_auiRasterToZscan[ (uiAbsPartIdx + uiNumPartInCUWidth + 1) % m_pcPic->getNumPartInCU() ];
			}
			else if ( uiAbsPartIdx / uiNumPartInCUWidth < m_pcPic->getNumPartInHeight() - 1 ) // is not at the last row of LCU But is last column of LCU
			{
				uiAbsPartAddr = g_auiRasterToZscan[ uiAbsPartIdx + 1 ];
				uiLCUIdx = getAddr() + 1;
			}
			else //is the right bottom corner of LCU                       
			{
				uiAbsPartAddr = 0;
			}
		}
		
		// 对MV进行缩放处理
		if ( uiLCUIdx >= 0 && xGetColMVP( eRefPicList, uiLCUIdx, uiAbsPartAddr, cColMv, iRefIdx_Col ) )
		{
			// 存进MVP列表候选列表中
			pInfo->m_acMvCand[pInfo->iN++] = cColMv;
		}
		else 
		{
			UInt uiPartIdxCenter;
			UInt uiCurLCUIdx = getAddr();
			xDeriveCenterIdx( uiPartIdx, uiPartIdxCenter );
			if (xGetColMVP( eRefPicList, uiCurLCUIdx, uiPartIdxCenter,  cColMv, iRefIdx_Col ))
			{
				pInfo->m_acMvCand[pInfo->iN++] = cColMv;
			}
		}
		//----  co-located RightBottom Temporal Predictor  ---//
	}

	if (pInfo->iN > AMVP_MAX_NUM_CANDS)
	{
		pInfo->iN = AMVP_MAX_NUM_CANDS;
	}

	// 如果没有达到指定的数量,就填充0向量
	while (pInfo->iN < AMVP_MAX_NUM_CANDS)
	{
		pInfo->m_acMvCand[pInfo->iN].set(0,0);
		pInfo->iN++;
	}

	// MVP候选列表中MV存放的顺序是先空域MV(左边的MV,然后上方的MV)然后再时域MV
	return ;
}






评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值