HEVC 高級運動向量預測技術(AMVP)


代碼版本: VTHEVCDec

函數: fillMvpCand()

時間: 2015/8/26

作者: lb

 

高級運動向量預測技術(AMVP)利用空間、時間上運動向量的相關性,分別建立空域候選列表以及時域候選列表,再從候選列表中選取最終的MVP。

 

1、空域候選列表的建立


       空域候選列表需要從上圖5個參考塊中選出兩個候選MV。分別是a0a1中選出一個候選MVb0,b1,b2中選出一個候選MV

其中左側候選塊的選擇順序爲a0->a1->scaled a0->scaled a1scaled爲比例伸縮模式。一旦前一個塊的MV可用,即選擇其爲候選MV,停止左側後續塊的判斷。

      上側塊的選擇順序爲(scaled b0->scaled b1->scaled b2)b0->b1->b2,同理,一旦有一個塊可用,停止後續塊的判斷。 上側塊的比例伸縮模式之所以用括號括起來,因爲它跟普通非比例伸縮模式是一個二選一的過程,當它滿足以下條件時:左側的a0,a1塊均滿足 參考塊不存在或者存在時其預測模式不是幀內預測,則採用比例伸縮模式。反之,則採用普通模式。

      比例伸縮模式(scaled)是當參考塊存在,當前Pu的參考圖像跟參考塊的參考圖像不一致時才選用的方式。具體計算方法如下公式所示,curMV爲當前Pu的候選MVtdtb分別爲當前塊與參考塊到他們的參考圖像間的距離,colMV 爲參考塊的mv


2、時域候選列表的建立

   時域候選列表最多提供一個候選MV,時域候選列表的建立過程利用了鄰近已編碼圖像中對應參考塊位置的Pu的運動信息。它用的技術方式即爲上述的比例伸縮模式(scaled),只是參考塊的 位置不一樣。參考塊的位置如下圖所示:


   從上圖可以看出參考塊位置分別爲H。若H位置的同位Pu(鄰近已編碼圖像對應位置Pu)不可用,則用位置的同位Pu代替。

3、AMVP候選列表的建立流程

   整個函數的流程可用下圖表示:

   


附上該函數代碼:

 

/*============================================
**函數名:	fillMvpCand
**功能描述:	獲取當前PU的候選MV列表
**創建人:	lmx
**時間	:  20150601
**輸入參數:pCu							--- 當前CU結構體
iPartIdx					--- PU索引
uiPartAddr					--- PU左上角索引值(以最小TU爲單位,相對所在CU的左上角位置)	
eRefPicList					--- 參考幀鏈表
iRefIdx						--- 當前PU所使用的參考幀索引
**輸出參數:
**返回值:	
**版本號: 	
**修改記錄:	
**==========================================*/
void fillMvpCand (DataCu *pCu,UInt32 iPartIdx, UInt32 uiPartAddr, RefPicList eRefPicList, Int32 iRefIdx, AMVPINFO* pSrcfo )
{
	UInt8 bAddedSmvp = false;   //!< 左側兩個參考塊任意一個可用且模式爲幀間預測時,爲真

	//-- Get Spatial MV, 空域(2個)
	UInt32 uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;
	UInt8 bAdded = false;

	/*    __        _____
		 |B2|______|B1|B0|
		 |         | 
		 |	       |	
		 |curent PU|
	   __|		   |
	  |A1|_________|
	  |A0|
	*/
	DataCu *tmpCU_BL = NULL;    //!< A0
	DataCu *tmpCU_L  = NULL;    //!< A1
	DataCu *tmpCU_AR = NULL;    //!< B0
	DataCu *tmpCU_A  = NULL;    //!< B1
	DataCu *tmpCU_AL = NULL;    //!< B2

	UInt32 idx;  //!< Z掃描:當前預測PU左下角參考TU的索引(相對它所在的Cu)
	UInt32 idxTmp;
	Slice *pSlice = pCu->pSlice;

	pSrcfo->N = 0;
	
	if (iRefIdx < 0)
	{
		return;
	}

	//獲取當前PU本身的左上角、右上角、左下角TU座標(以最小TU爲單位,相對Pu所在CTU的左上角位置)
	deriveLeftRightTopIdx(pCu,iPartIdx, &uiPartIdxLT, &uiPartIdxRT);
	deriveLeftBottomIdx(pCu,iPartIdx,&uiPartIdxLB);

	//!< A0,返回當前Pu左下角參考塊(非Pu本身)所在Cu
	//!< 左側產生一個候選MV, 選擇順序爲 BELOW_LEFT -> LEFT
	tmpCU_BL	= getPuBelowLeft(pCu,&idx,uiPartIdxLB,true); 
	bAddedSmvp	= (tmpCU_BL != NULL) && (tmpCU_BL->pPePredMode[idx] == MODE_INTER); // 參考塊爲幀內預測

	//!< A1,返回當前Pu左側參考塊所在Cu
	tmpCU_L = getPuLeft(pCu,&idxTmp,uiPartIdxLB,true,true);
	if (!bAddedSmvp)
	{
		bAddedSmvp = (tmpCU_L != NULL) && (tmpCU_L->pPePredMode[idxTmp] == MODE_INTER);
	}

	if (tmpCU_BL)
	{
		bAdded = xAddMVPCand(pCu,tmpCU_BL,pSrcfo,eRefPicList,iRefIdx, idx);
	}
	if (!bAdded && tmpCU_L)
	{
		bAdded = xAddMVPCand(pCu,tmpCU_L, pSrcfo, eRefPicList,iRefIdx, idxTmp);
	}

	//!<  若左側的兩個MV不可用,則通過MV的比例伸縮查找,選擇順序爲BELOW_LEFT -> LEFT
	if(!bAdded)
	{
		if (tmpCU_BL)
		{
			bAdded = xAddMVPCandOrder(pCu,tmpCU_BL, pSrcfo, eRefPicList, iRefIdx, idx);
		}
		
		if (!bAdded  && tmpCU_L)
		{
			xAddMVPCandOrder(pCu, tmpCU_L,pSrcfo, eRefPicList, iRefIdx, idxTmp );
		}
	}

	//!< 左側的兩個參考塊均滿足--不可用或預測模式不是幀間預測,才採用MV的比例縮放
	if(!bAddedSmvp)
	{
		tmpCU_AR = getPuAboveRight(pCu,&idx, uiPartIdxRT,true);
		if (tmpCU_AR)
		{
			bAdded = xAddMVPCandOrder(pCu,tmpCU_AR, pSrcfo, eRefPicList, iRefIdx, idx);
		}
		
		if (!bAdded)
		{
			tmpCU_A = getPuAbove(pCu,&idx, uiPartIdxRT,true,false,true);
			if (tmpCU_A)
			{
				bAdded = xAddMVPCandOrder(pCu, tmpCU_A,pSrcfo, eRefPicList, iRefIdx, idx);
			}
		}

		if(!bAdded)
		{
			tmpCU_AL = getPuAboveLeft(pCu,&idx, uiPartIdxLT,true);
			if (tmpCU_AL)
			{
				xAddMVPCandOrder(pCu,tmpCU_AL, pSrcfo, eRefPicList, iRefIdx, idx);
			}
		}
	}
	else
	{
		bAdded = false;

		//!< B0, 上方預測塊產生一個候選MV,選擇順序爲 ABOVE_RIGHT -> ABOVE -> ABOVE_LEFT
		tmpCU_AR = getPuAboveRight(pCu,&idx, uiPartIdxRT,true);
		if (tmpCU_AR)
		{
			bAdded	 = xAddMVPCand(pCu,tmpCU_AR,pSrcfo, eRefPicList,iRefIdx, idx);
		}

		if (!bAdded)
		{	//!< B1
			tmpCU_A = getPuAbove(pCu,&idx, uiPartIdxRT,true,false,true);
			if (tmpCU_A)
			{
				bAdded = xAddMVPCand(pCu,tmpCU_A,pSrcfo, eRefPicList, iRefIdx, idx);
			}
		}

		if(!bAdded)
		{	//!< B2
			tmpCU_AL = getPuAboveLeft(pCu,&idx, uiPartIdxLT,true);
			if (tmpCU_AL)
			{
				xAddMVPCand(pCu,tmpCU_AL,pSrcfo, eRefPicList,iRefIdx, idx);
			}
			
		}
	}

	if ( pSrcfo->N == 2 )
	{  //!< 合併空域相同候選MV
		if ( (pSrcfo->mvCand[ 0 ].hor == pSrcfo->mvCand[ 1 ].hor) && (pSrcfo->mvCand[ 0 ].ver == pSrcfo->mvCand[ 1 ].ver) )  
		{
			pSrcfo->N = 1;
		}
	}

	if (pSlice->enableTMVPFlag)  //!< TMVP ,時域候選MV
	{
		PicSym *pPicSym = pCu->pPic->pPicSym;

		const UInt32 numPartInCtuWidth  = pPicSym->numPartInCtuWidth;
		const UInt32 numPartInCtuHeight = pPicSym->numPartInCtuHeight;

		const DataCu *tmpCtu = pPicSym->pPicCtuArray[pCu->ctuRsAddr];
		const Sps    *tmpSps = pCu->pSlice->pSps;

		Int32 iRefIdx_Col = iRefIdx;	//!< 當前PU的參考幀索引
		MvInfo cColMv;
		UInt32 uiPartIdxRB;
		UInt32 uiAbsPartIdx;
		UInt32 uiAbsPartAddr;			//!< 參考塊在所在CTU的Z掃描位置
		UInt32 uiTmpResi, uiTmpQuo;     //!< 餘數, 商
		Int32 ctuRsAddr = -1;			//!< 參考塊所在CTU在圖像中的光柵掃描位置


		//!< 獲取右下角參考Tu的位置
		deriveRightBottomIdx(pCu, iPartIdx, &uiPartIdxRB );
		uiAbsPartAddr = pCu->absZIdxInCtu + uiPartAddr;  //!< z掃描:當前PU相對CTU的位置
		uiAbsPartIdx = g_auiZscanToRaster[uiPartIdxRB];  //!< uiPartIdxRB 是相對CTU的位置

		uiTmpResi = uiAbsPartIdx % numPartInCtuWidth;
		uiTmpQuo  = uiAbsPartIdx / numPartInCtuWidth;
		
		//----  co-located RightBottom Temporal Predictor (H) ---//

		if (  ( ( tmpCtu->uiCUPelX + g_auiRasterToPelX[uiAbsPartIdx] + pPicSym->minCUWidth ) < tmpSps->picWidthInLumaSamples )  // image boundary check, 判斷當前Pu所在的CTU是不是處於圖像的右邊界
			&& ( ( tmpCtu->uiCUPelY + g_auiRasterToPelY[uiAbsPartIdx] + pPicSym->minCUHeight ) < tmpSps->picHeightInLumaSamples ) )
			//!< 判斷當前Pu所在的CTU是不是處於圖像的下邊界
		{
			if ( ( uiTmpResi < numPartInCtuWidth - 1 ) &&  // is not at the last column of CTU
				( uiTmpQuo < numPartInCtuHeight - 1 ) )  // is not at the last row    of CTU
			{    //!< 右下角Tu不在CTU的最後一列最後一行,說明右下角TU的右下角參考塊也在當前CTU
				uiAbsPartAddr = g_auiRasterToZscan[ uiAbsPartIdx + numPartInCtuWidth + 1 ];
				ctuRsAddr = pCu->ctuRsAddr;
			}
			else if ( uiTmpResi < numPartInCtuWidth - 1 )  // is not at the last column of CTU But is last row of CTU
			{
				uiAbsPartAddr = g_auiRasterToZscan[ (uiAbsPartIdx + numPartInCtuWidth + 1) % pPicSym->tuNumCtu ];
			}
			else if ( uiTmpQuo < numPartInCtuHeight - 1 ) // is not at the last row of CTU But is last column of CTU
			{
				uiAbsPartAddr = g_auiRasterToZscan[ uiAbsPartIdx + 1 ];
				ctuRsAddr = pCu->ctuRsAddr + 1;
			}
			else //is the right bottom corner of CTU
			{
				uiAbsPartAddr = 0;
			}
		}
		if ( ctuRsAddr >= 0 && xGetColMVP(pCu, eRefPicList, ctuRsAddr, uiAbsPartAddr, &cColMv, iRefIdx_Col ) )
		{
			pSrcfo->mvCand[pSrcfo->N++] = cColMv;
		}
		else  //!< 若Current Pu 的右下角參考塊的同位PU不存在,則取Current PU 的中間Tu塊作爲同位參考塊
		{
			UInt32 uiPartIdxCenter;
			xDeriveCenterIdx(pCu, iPartIdx, &uiPartIdxCenter );
			if (xGetColMVP(pCu, eRefPicList, pCu->ctuRsAddr, uiPartIdxCenter,  &cColMv, iRefIdx_Col ))
			{
				pSrcfo->mvCand[pSrcfo->N++] = cColMv;
			}
		}
		//----  co-located RightBottom Temporal Predictor  ---//
	}

	if (pSrcfo->N > AMVP_MAX_NUM_CANDS)			//!< 保留候選列表中的前兩個作爲最終的候選MV
	{
		pSrcfo->N = AMVP_MAX_NUM_CANDS;
	}

	while (pSrcfo->N < AMVP_MAX_NUM_CANDS)		//!< 若候選MV長度不足,用(0,0) 補充
	{
		pSrcfo->mvCand[pSrcfo->N].hor = 0;
		pSrcfo->mvCand[pSrcfo->N].ver = 0;
		pSrcfo->N++;
	}

	return ;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章