CU的劃分、地址以及索引

CU的劃分、地址以及索引



    在看HM源碼的時候,最蛋疼的一件事就是被CU的劃分以及它們的地址搞懵。爲了搞清楚,仔細研究了一下源碼,有什麼錯誤請指出。爲了弄清楚這個問題,必須要對掃描順序、深度等概念有所理解。



掃描順序


    HEVC中對像素塊的掃描方式有兩種:Raster和Zscan

    Raster掃描方式:從上到下,從左到右進行掃描,是最直觀也是最容易理解的方式
    Zscan掃描方式:因爲CU是遞歸劃分的,因此爲了方便處理,HM中使用了Z掃描,也就是從左上角按照Z字形掃描到右下角。下面是Zscan掃描的一個例子:





CU的劃分


    CU按照四叉樹的方式自上而下,進行遞歸劃分,由於使用了這種遞歸的方式,因此HEVC中必須要使用Zscan的掃描方式。




深度


    1、深度等於0時,CU的尺寸是64x64,該CU下面4x4小塊的數量是256
    2、深度等於1時,CU的尺寸是32x32,該CU下面4x4小塊的數量是64
    3、深度等於2時,CU的尺寸是16x16,該CU下面4x4小塊的數量是16
    4、深度等於3時,CU的尺寸是8x8,該CU下面4x4小塊的數量是4



HEVC中對掃描順序以及地址的處理



初始化掃描順序


    1、把每一個LCU都劃分爲4x4的小塊,分別以Raster和Zscan的方式對4x4的小塊進行編號(也就是索引)

    2、把Raster到Zscan的編號映射存儲在g_auiRasterToZscan數組中

    3、把Zscan到Raster的編號映射存儲在g_auiZscanToRaster數組中

    4、把Raster索引到像素地址的映射放在g_auiRasterToPelX和g_auiRasterToPelY中

extern       UInt   g_auiZscanToRaster[ MAX_NUM_SPU_W*MAX_NUM_SPU_W ]; // 
extern       UInt   g_auiRasterToZscan[ MAX_NUM_SPU_W*MAX_NUM_SPU_W ];
Void         initZscanToRaster ( Int iMaxDepth, Int iDepth, UInt uiStartVal, UInt*& rpuiCurrIdx );
Void         initRasterToZscan ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth         );
extern       UInt   g_auiRasterToPelX[ MAX_NUM_SPU_W*MAX_NUM_SPU_W ];
extern       UInt   g_auiRasterToPelY[ MAX_NUM_SPU_W*MAX_NUM_SPU_W ];
Void         initRasterToPelXY ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth );


// 計算Z型掃描的塊索引
Void initZscanToRaster ( Int iMaxDepth, Int iDepth, UInt uiStartVal, UInt*& rpuiCurrIdx )
{
	// 偏移
	Int iStride = 1 << ( iMaxDepth - 1 );

	// 已經到達最大深度,遞歸結束
	if ( iDepth == iMaxDepth )
	{
		rpuiCurrIdx[0] = uiStartVal;
		rpuiCurrIdx++;
	}
	else
	{
		// 尚未到達最大深度,使用遞歸繼續往下處理
		Int iStep = iStride >> iDepth;
		initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal,                     rpuiCurrIdx );
		initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep,               rpuiCurrIdx );
		initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep*iStride,       rpuiCurrIdx );
		initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep*iStride+iStep, rpuiCurrIdx );
	}
}

Void initRasterToZscan ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth )
{
	UInt  uiMinCUWidth  = uiMaxCUWidth  >> ( uiMaxDepth - 1 );
	UInt  uiMinCUHeight = uiMaxCUHeight >> ( uiMaxDepth - 1 );

	UInt  uiNumPartInWidth  = (UInt)uiMaxCUWidth  / uiMinCUWidth;
	UInt  uiNumPartInHeight = (UInt)uiMaxCUHeight / uiMinCUHeight;

	for ( UInt i = 0; i < uiNumPartInWidth*uiNumPartInHeight; i++ )
	{
		g_auiRasterToZscan[ g_auiZscanToRaster[i] ] = i;
	}
}

// 光柵掃描順序時,根據索引,定位出像素位置
Void initRasterToPelXY ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth )
{
	UInt    i;

	UInt* uiTempX = &g_auiRasterToPelX[0];
	UInt* uiTempY = &g_auiRasterToPelY[0];

	UInt  uiMinCUWidth  = uiMaxCUWidth  >> ( uiMaxDepth - 1 );
	UInt  uiMinCUHeight = uiMaxCUHeight >> ( uiMaxDepth - 1 );

	UInt  uiNumPartInWidth  = uiMaxCUWidth  / uiMinCUWidth;
	UInt  uiNumPartInHeight = uiMaxCUHeight / uiMinCUHeight;

	uiTempX[0] = 0; uiTempX++;
	for ( i = 1; i < uiNumPartInWidth; i++ )
	{
		uiTempX[0] = uiTempX[-1] + uiMinCUWidth; uiTempX++;
	}
	for ( i = 1; i < uiNumPartInHeight; i++ )
	{
		memcpy(uiTempX, uiTempX-uiNumPartInWidth, sizeof(UInt)*uiNumPartInWidth);
		uiTempX += uiNumPartInWidth;
	}

	for ( i = 1; i < uiNumPartInWidth*uiNumPartInHeight; i++ )
	{
		uiTempY[i] = ( i / uiNumPartInWidth ) * uiMinCUWidth;
	}
};




根據掃描順序得到地址



得到Zscan掃描順序


    這個很簡單,由於CU是按照四叉樹的方式進行遞歸處理的,因此CU被處理的順序就是Zscan掃描順序。下面是Zscan掃描順序的一個示意圖。



根據Zscan順序得到Raster掃描順序

    得到Zscan掃描順序之後還不夠啊,我們需要知道某個塊的地址才能對對它進行處理。獲取地址需要Raster掃描順序,可以直接使用g_auiZscanToRaster數組把Zscan順序轉換成Raster順序。


根據Raster掃描順序得到CU的地址


    使用g_auiRasterToPelX和g_auiRasterToPelY就可以把Raster掃描順序轉換成相對地址了。但是這個地址只是4x4小塊的地址,我們需要的是某個CU的地址,可以按照下面的方式進行處理:

    1、開始的時候已知slice的起始地址、每個LCU的偏移地址,這些在初始化的時候都是確定。

    2、根據CU左上角的4x4塊的地址可以算出CU在圖像中的地址了。

    3、另外補充一點:根據深度可以確定CU的尺寸



在TEncCu中和CU有關的信息


    TEncCu中有下面一些屬性是需要注意的

	TComDataCU**            m_ppcBestCU; //每個深度下的最優CU,用於存儲最優值,注意TComDataCU只有CU的信息,不存放數據
	TComDataCU**            m_ppcTempCU;  //每個深度下的臨時CU,這個是用於計算,每次計算完成之後都要和最優值進行比較,然後交換    
	UChar                   m_uhTotalDepth; //總的深度
	TComYuv**               m_ppcPredYuvBest; //每個深度下最優的預測YUV
	TComYuv**               m_ppcResiYuvBest; //每個深度下最優的殘差YUV
	TComYuv**               m_ppcRecoYuvBest; //每個深度下最優的重建YUV
	TComYuv**               m_ppcPredYuvTemp; //每個深度下臨時的預測YUV,用於計算過程,計算完成後和最優的進行比較,然後交換
	TComYuv**               m_ppcResiYuvTemp; //每個深度下臨時的殘差YUV,同上
	TComYuv**               m_ppcRecoYuvTemp; //每個深度下臨時的重建YUV,同上
	TComYuv**               m_ppcOrigYuv;    //YUV數據,在編碼之前從TComPicYuv中得到,TComPicYuv在TComPic中
    1、bestCU用於存放某個深度上CU的最優劃分信息

    2、tempCU用於編碼過程,每編碼完成一個CU就將tempCU與bestCU進行比較,如果有需要就更新bestCU

    3、TComDataCU存放了CU中所有4x4小塊的信息!



在編碼過程中CU的地址和索引


LCU的地址


    在函數compressCU中,LCU被初始化,它根據傳進來的TComPicSym的LCU信息,調用initCU進行初始化,得到LCU的地址等信息。這麼做是因爲在編碼的過程中不對TComPicSym的LCU進行直接操作,需要複製它的信息到臨時的對象中,編碼完成後再把最優的信息複製回TComPicSym中

    m_ppcBestCU表示某個深度下最優的CU信息,主要目的是存放最優的信息;m_ppcTempCU表示某個深度下臨時的CU信息,它被用於編碼操作,編碼完成之後,再根據需要把最優信息保存到m_ppcBestCU中。

Void TEncCu::compressCU( TComDataCU*& rpcCU )//TComPicSym
{
	// initialize CU data
	// CU初始化
	// 初始化最佳CU和臨時CU
	m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
	m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );

	// analysis of CU
	// 進行CU的切割
	// 起始的深度是0,即最開始進去的是LCU
	xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 );
}
    1、開始的時候rpcCU表示一個LCU
    2、利用LCU的信息,對深度等於0的bestCU和tempCU進行初始化,m_ppcBestCU的下標表示深度
    3、調用xCompressCU


    initCU主要用於LCU的初始化,普通CU的初始化不使用它

// 實際上這是針對LCU的初始化,對於普通的CU,初始化使用initSubCU
Void TComDataCU::initCU( TComPic* pcPic, UInt iCUAddr )
{
	// 當前CU所屬的圖像
	m_pcPic              = pcPic;
	// 當前CU所屬的slice
	m_pcSlice            = pcPic->getSlice(pcPic->getCurrSliceIdx());
	// CU的地址
	m_uiCUAddr           = iCUAddr;
	// LCU在圖像中的實際像素地址
	m_uiCUPelX           = ( iCUAddr % pcPic->getFrameWidthInCU() ) * g_uiMaxCUWidth;
	m_uiCUPelY           = ( iCUAddr / pcPic->getFrameWidthInCU() ) * g_uiMaxCUHeight;
	m_uiAbsIdxInLCU      = 0;
	// 總的RD消耗
	m_dTotalCost         = MAX_DOUBLE;
	// 總的失真
	m_uiTotalDistortion  = 0;
	// 總的比特數
	m_uiTotalBits        = 0;
	// 總的二進制數
	m_uiTotalBins        = 0;

	// 一幀圖像最多可以分成256個CU(即深度是4,2^4 * 2^4 = 256)
	m_uiNumPartition     = pcPic->getNumPartInCU();

	// m_sliceStartCU初始化之後全都是0
	for(Int i=0; i<pcPic->getNumPartInCU(); i++)
	{
		if(pcPic->getPicSym()->getInverseCUOrderMap(iCUAddr)*pcPic->getNumPartInCU()+i>=getSlice()->getSliceCurStartCUAddr())
		{
			// 設置條帶開始的CU的地址放到一個數組中
			m_sliceStartCU[i]=getSlice()->getSliceCurStartCUAddr();
		}
		else
		{
			// 獲取slice中的所有CU,存放到一個數組中
			m_sliceStartCU[i]=pcPic->getCU(getAddr())->m_sliceStartCU[i];
		}
	}

	for(Int i=0; i<pcPic->getNumPartInCU(); i++)
	{
		if(pcPic->getPicSym()->getInverseCUOrderMap(iCUAddr)*pcPic->getNumPartInCU()+i>=getSlice()->getSliceSegmentCurStartCUAddr())
		{
			m_sliceSegmentStartCU[i]=getSlice()->getSliceSegmentCurStartCUAddr();
		}
		else
		{
			// 獲取所有的條帶片段中的CU,存放到數組中
			m_sliceSegmentStartCU[i]=pcPic->getCU(getAddr())->m_sliceSegmentStartCU[i];
		}
	}

	// getInverseCUOrderMap 這個函數是根據CU的地址獲取CU的順序索引
	Int partStartIdx = getSlice()->getSliceSegmentCurStartCUAddr() - pcPic->getPicSym()->getInverseCUOrderMap(iCUAddr) * pcPic->getNumPartInCU();

	// 元素的個數
	Int numElements = min<Int>( partStartIdx, m_uiNumPartition );
	for ( Int ui = 0; ui < numElements; ui++ )
	{
		TComDataCU * pcFrom = pcPic->getCU(getAddr());
		m_skipFlag[ui]   = pcFrom->getSkipFlag(ui);
		m_pePartSize[ui] = pcFrom->getPartitionSize(ui);
		m_pePredMode[ui] = pcFrom->getPredictionMode(ui);
		m_CUTransquantBypass[ui] = pcFrom->getCUTransquantBypass(ui);
		m_puhDepth[ui] = pcFrom->getDepth(ui);
		m_puhWidth  [ui] = pcFrom->getWidth(ui);
		m_puhHeight [ui] = pcFrom->getHeight(ui);
		m_puhTrIdx  [ui] = pcFrom->getTransformIdx(ui);
		m_puhTransformSkip[0][ui] = pcFrom->getTransformSkip(ui,TEXT_LUMA);
		m_puhTransformSkip[1][ui] = pcFrom->getTransformSkip(ui,TEXT_CHROMA_U);
		m_puhTransformSkip[2][ui] = pcFrom->getTransformSkip(ui,TEXT_CHROMA_V);
		m_apiMVPIdx[0][ui] = pcFrom->m_apiMVPIdx[0][ui];;
		m_apiMVPIdx[1][ui] = pcFrom->m_apiMVPIdx[1][ui];
		m_apiMVPNum[0][ui] = pcFrom->m_apiMVPNum[0][ui];
		m_apiMVPNum[1][ui] = pcFrom->m_apiMVPNum[1][ui];
		m_phQP[ui]=pcFrom->m_phQP[ui];
		m_pbMergeFlag[ui]=pcFrom->m_pbMergeFlag[ui];
		m_puhMergeIndex[ui]=pcFrom->m_puhMergeIndex[ui];
		m_puhLumaIntraDir[ui]=pcFrom->m_puhLumaIntraDir[ui];
		m_puhChromaIntraDir[ui]=pcFrom->m_puhChromaIntraDir[ui];
		m_puhInterDir[ui]=pcFrom->m_puhInterDir[ui];
		m_puhCbf[0][ui]=pcFrom->m_puhCbf[0][ui];
		m_puhCbf[1][ui]=pcFrom->m_puhCbf[1][ui];
		m_puhCbf[2][ui]=pcFrom->m_puhCbf[2][ui];
		m_pbIPCMFlag[ui] = pcFrom->m_pbIPCMFlag[ui];
	}

	// 第一個元素
	Int firstElement = max<Int>( partStartIdx, 0 );

	// 元素的個數
	numElements = m_uiNumPartition - firstElement;

	if ( numElements > 0 )
	{
		memset( m_skipFlag          + firstElement, false,                    numElements * sizeof( *m_skipFlag ) );

		memset( m_pePartSize        + firstElement, SIZE_NONE,                numElements * sizeof( *m_pePartSize ) );
		memset( m_pePredMode        + firstElement, MODE_NONE,                numElements * sizeof( *m_pePredMode ) );
		memset( m_CUTransquantBypass+ firstElement, false,                    numElements * sizeof( *m_CUTransquantBypass) );
		memset( m_puhDepth          + firstElement, 0,                        numElements * sizeof( *m_puhDepth ) );
		memset( m_puhTrIdx          + firstElement, 0,                        numElements * sizeof( *m_puhTrIdx ) );
		memset( m_puhTransformSkip[0] + firstElement, 0,                      numElements * sizeof( *m_puhTransformSkip[0]) );
		memset( m_puhTransformSkip[1] + firstElement, 0,                      numElements * sizeof( *m_puhTransformSkip[1]) );
		memset( m_puhTransformSkip[2] + firstElement, 0,                      numElements * sizeof( *m_puhTransformSkip[2]) );
		memset( m_puhWidth          + firstElement, g_uiMaxCUWidth,           numElements * sizeof( *m_puhWidth ) );
		memset( m_puhHeight         + firstElement, g_uiMaxCUHeight,          numElements * sizeof( *m_puhHeight ) );
		memset( m_apiMVPIdx[0]      + firstElement, -1,                       numElements * sizeof( *m_apiMVPIdx[0] ) );
		memset( m_apiMVPIdx[1]      + firstElement, -1,                       numElements * sizeof( *m_apiMVPIdx[1] ) );
		memset( m_apiMVPNum[0]      + firstElement, -1,                       numElements * sizeof( *m_apiMVPNum[0] ) );
		memset( m_apiMVPNum[1]      + firstElement, -1,                       numElements * sizeof( *m_apiMVPNum[1] ) );
		memset( m_phQP              + firstElement, getSlice()->getSliceQp(), numElements * sizeof( *m_phQP ) );
		memset( m_pbMergeFlag       + firstElement, false,                    numElements * sizeof( *m_pbMergeFlag ) );
		memset( m_puhMergeIndex     + firstElement, 0,                        numElements * sizeof( *m_puhMergeIndex ) );
		memset( m_puhLumaIntraDir   + firstElement, DC_IDX,                   numElements * sizeof( *m_puhLumaIntraDir ) );
		memset( m_puhChromaIntraDir + firstElement, 0,                        numElements * sizeof( *m_puhChromaIntraDir ) );
		memset( m_puhInterDir       + firstElement, 0,                        numElements * sizeof( *m_puhInterDir ) );
		memset( m_puhCbf[0]         + firstElement, 0,                        numElements * sizeof( *m_puhCbf[0] ) );
		memset( m_puhCbf[1]         + firstElement, 0,                        numElements * sizeof( *m_puhCbf[1] ) );
		memset( m_puhCbf[2]         + firstElement, 0,                        numElements * sizeof( *m_puhCbf[2] ) );
		memset( m_pbIPCMFlag        + firstElement, false,                    numElements * sizeof( *m_pbIPCMFlag ) );
	}

	UInt uiTmp = g_uiMaxCUWidth*g_uiMaxCUHeight;
	if ( 0 >= partStartIdx ) 
	{
		m_acCUMvField[0].clearMvField();
		m_acCUMvField[1].clearMvField();
		memset( m_pcTrCoeffY , 0, sizeof( TCoeff ) * uiTmp );
#if ADAPTIVE_QP_SELECTION
		memset( m_pcArlCoeffY , 0, sizeof( Int ) * uiTmp );  
#endif
		memset( m_pcIPCMSampleY , 0, sizeof( Pel ) * uiTmp );
		uiTmp  >>= 2;
		memset( m_pcTrCoeffCb, 0, sizeof( TCoeff ) * uiTmp );
		memset( m_pcTrCoeffCr, 0, sizeof( TCoeff ) * uiTmp );
#if ADAPTIVE_QP_SELECTION  
		memset( m_pcArlCoeffCb, 0, sizeof( Int ) * uiTmp );
		memset( m_pcArlCoeffCr, 0, sizeof( Int ) * uiTmp );
#endif
		memset( m_pcIPCMSampleCb , 0, sizeof( Pel ) * uiTmp );
		memset( m_pcIPCMSampleCr , 0, sizeof( Pel ) * uiTmp );
	}
	else 
	{
		TComDataCU * pcFrom = pcPic->getCU(getAddr());
		m_acCUMvField[0].copyFrom(&pcFrom->m_acCUMvField[0],m_uiNumPartition,0);
		m_acCUMvField[1].copyFrom(&pcFrom->m_acCUMvField[1],m_uiNumPartition,0);
		for(Int i=0; i<uiTmp; i++)
		{
			m_pcTrCoeffY[i]=pcFrom->m_pcTrCoeffY[i];
#if ADAPTIVE_QP_SELECTION
			m_pcArlCoeffY[i]=pcFrom->m_pcArlCoeffY[i];
#endif
			m_pcIPCMSampleY[i]=pcFrom->m_pcIPCMSampleY[i];
		}
		for(Int i=0; i<(uiTmp>>2); i++)
		{
			m_pcTrCoeffCb[i]=pcFrom->m_pcTrCoeffCb[i];
			m_pcTrCoeffCr[i]=pcFrom->m_pcTrCoeffCr[i];
#if ADAPTIVE_QP_SELECTION
			m_pcArlCoeffCb[i]=pcFrom->m_pcArlCoeffCb[i];
			m_pcArlCoeffCr[i]=pcFrom->m_pcArlCoeffCr[i];
#endif
			m_pcIPCMSampleCb[i]=pcFrom->m_pcIPCMSampleCb[i];
			m_pcIPCMSampleCr[i]=pcFrom->m_pcIPCMSampleCr[i];
		}
	}

	// Setting neighbor CU
	// 設置相鄰CU
	m_pcCULeft        = NULL;			// 左邊的CU
	m_pcCUAbove       = NULL;			// 上面的CU
	m_pcCUAboveLeft   = NULL;		// 左上方的CU
	m_pcCUAboveRight  = NULL;		// 右上方的CU

	// 位置數組,有兩個元素
	m_apcCUColocated[0] = NULL;
	m_apcCUColocated[1] = NULL;

	// 獲取一幀圖片中,橫向上CU的個數
	UInt uiWidthInCU = pcPic->getFrameWidthInCU();

	// 分別獲取當前CU各個方向上相鄰的CU

	// 如果CU地址不爲圖片上橫向上CU個數的整數倍,那麼它的左邊有CU
	if ( m_uiCUAddr % uiWidthInCU )
	{
		m_pcCULeft = pcPic->getCU( m_uiCUAddr - 1 );
	}

	// 如果CU地址不爲圖像縱向上CU個數的整數倍,那麼它的上方存在CU
	if ( m_uiCUAddr / uiWidthInCU )
	{
		m_pcCUAbove = pcPic->getCU( m_uiCUAddr - uiWidthInCU );
	}

	// 如果左邊有CU,上方有CU,那麼當前CU的上面有CU
	if ( m_pcCULeft && m_pcCUAbove )
	{
		m_pcCUAboveLeft = pcPic->getCU( m_uiCUAddr - uiWidthInCU - 1 );
	}

	// 如果上邊有CU,而且當前CU不是當前行最後一個CU,那麼它的右上角有CU
	if ( m_pcCUAbove && ( (m_uiCUAddr%uiWidthInCU) < (uiWidthInCU-1) )  )
	{
		m_pcCUAboveRight = pcPic->getCU( m_uiCUAddr - uiWidthInCU + 1 );
	}

	// getNumRefIdx從參考圖片數組(數組0或1)獲取參考圖片的數量
	if ( getSlice()->getNumRefIdx( REF_PIC_LIST_0 ) > 0 )
	{
		m_apcCUColocated[0] = getSlice()->getRefPic( REF_PIC_LIST_0, 0)->getCU( m_uiCUAddr );
	}

	if ( getSlice()->getNumRefIdx( REF_PIC_LIST_1 ) > 0 )
	{
		m_apcCUColocated[1] = getSlice()->getRefPic( REF_PIC_LIST_1, 0)->getCU( m_uiCUAddr );
	}
}


普通CU的地址

    xCompressCU中,下列語句用於初始化下一層的子CU

UChar       uhNextDepth         = uiDepth+1;
TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth];
TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth];

// 進一步的分割,當前CU又被劃分成爲4個子CU

// 對子CU進行初始化,計算座標索引等等,然後把父親CU的信息複製給子CU
// 另外,tempSubCU會和rpcTempCU一樣調用initEstData,進行清理,把舊的數據清除掉
for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )
{
	// 對子CU進行初始化,計算座標索引等等,然後把父親CU的信息複製給子CU
	pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.

	pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.

	// ***
}

    普通的CU初始化的時候不使用initCU,而是以父親CU作爲參數調用initSubCU進行初始化。

    1、計算當前CU在父親CU中以partition(4x4大小的塊)爲單位的偏移

    2、計算當前CU的地址,利用父親CU的地址和偏移量可以算出當前CU的地址

    3、計算當前CU在LCU中的Zscan順序的索引,先獲取父親CU的Zscan索引,再加上偏移就可以得到當前CU的Zscan索引

    4、計算當前CU的座標,利用父親CU的座標、當前CU的尺寸和當前CU在父親CU中的索引可以計算

    5、最後請注意:LCU的相關信息通過initCU已經設置完成,因此對於32x32的CU,可以直接利用LCU調用initSubCU進行初始化!

Void TComDataCU::initSubCU( TComDataCU* pcCU, UInt uiPartUnitIdx, UInt uiDepth, Int qp )
{
	assert( uiPartUnitIdx<4 );

	// partition(也就是4x4大小的塊)爲單位的偏移
	// pcCU->getTotalNumPart()>>2表示當前CU中partition的數量是父親CU的1/4,因爲它的寬和高都是父親CU的一半
	// 乘以uiPartUnitIdx的目的是計算當前CU在父親CU中的偏移
	UInt uiPartOffset = ( pcCU->getTotalNumPart()>>2 )*uiPartUnitIdx;

	m_pcPic              = pcCU->getPic();
	m_pcSlice            = m_pcPic->getSlice(m_pcPic->getCurrSliceIdx());
	
	// 父親CU的地址,利用父親CU的地址和偏移可以計算出當前CU的地址
	m_uiCUAddr           = pcCU->getAddr();
	
	// pcCU->getZorderIdxInCU()得到父親CU在LCU中的Zscan順序的索引,加上偏移之後得到當前CU在LCU中的索引
	m_uiAbsIdxInLCU      = pcCU->getZorderIdxInCU() + uiPartOffset;

	// 計算當前CU的座標(左上角)
	// pcCU->getCUPelX()得到父親CU的座標,利用當前CU的尺寸以及它在父親CU中索引,計算出當前CU的座標
	m_uiCUPelX           = pcCU->getCUPelX() + ( g_uiMaxCUWidth>>uiDepth  )*( uiPartUnitIdx &  1 );
	m_uiCUPelY           = pcCU->getCUPelY() + ( g_uiMaxCUHeight>>uiDepth  )*( uiPartUnitIdx >> 1 );

	m_dTotalCost         = MAX_DOUBLE;
	m_uiTotalDistortion  = 0;
	m_uiTotalBits        = 0;
	m_uiTotalBins        = 0;
	m_uiNumPartition     = pcCU->getTotalNumPart() >> 2;

	Int iSizeInUchar = sizeof( UChar  ) * m_uiNumPartition;
	Int iSizeInBool  = sizeof( Bool   ) * m_uiNumPartition;

	Int sizeInChar = sizeof( Char  ) * m_uiNumPartition;
	memset( m_phQP,              qp,  sizeInChar );

	memset( m_pbMergeFlag,        0, iSizeInBool  );
	memset( m_puhMergeIndex,      0, iSizeInUchar );
	memset( m_puhLumaIntraDir,    DC_IDX, iSizeInUchar );
	memset( m_puhChromaIntraDir,  0, iSizeInUchar );
	memset( m_puhInterDir,        0, iSizeInUchar );
	memset( m_puhTrIdx,           0, iSizeInUchar );
	memset( m_puhTransformSkip[0], 0, iSizeInUchar );
	memset( m_puhTransformSkip[1], 0, iSizeInUchar );
	memset( m_puhTransformSkip[2], 0, iSizeInUchar );
	memset( m_puhCbf[0],          0, iSizeInUchar );
	memset( m_puhCbf[1],          0, iSizeInUchar );
	memset( m_puhCbf[2],          0, iSizeInUchar );
	memset( m_puhDepth,     uiDepth, iSizeInUchar );

	UChar uhWidth  = g_uiMaxCUWidth  >> uiDepth;
	UChar uhHeight = g_uiMaxCUHeight >> uiDepth;
	memset( m_puhWidth,          uhWidth,  iSizeInUchar );
	memset( m_puhHeight,         uhHeight, iSizeInUchar );
	memset( m_pbIPCMFlag,        0, iSizeInBool  );
	for (UInt ui = 0; ui < m_uiNumPartition; ui++)
	{
		m_skipFlag[ui]   = false;
		m_pePartSize[ui] = SIZE_NONE;
		m_pePredMode[ui] = MODE_NONE;
		m_CUTransquantBypass[ui] = false;
		m_apiMVPIdx[0][ui] = -1;
		m_apiMVPIdx[1][ui] = -1;
		m_apiMVPNum[0][ui] = -1;
		m_apiMVPNum[1][ui] = -1;
		if(m_pcPic->getPicSym()->getInverseCUOrderMap(getAddr())*m_pcPic->getNumPartInCU()+m_uiAbsIdxInLCU+ui<getSlice()->getSliceSegmentCurStartCUAddr())
		{
			m_apiMVPIdx[0][ui] = pcCU->m_apiMVPIdx[0][uiPartOffset+ui];
			m_apiMVPIdx[1][ui] = pcCU->m_apiMVPIdx[1][uiPartOffset+ui];;
			m_apiMVPNum[0][ui] = pcCU->m_apiMVPNum[0][uiPartOffset+ui];;
			m_apiMVPNum[1][ui] = pcCU->m_apiMVPNum[1][uiPartOffset+ui];;
			m_puhDepth  [ui] = pcCU->getDepth(uiPartOffset+ui);
			m_puhWidth  [ui] = pcCU->getWidth(uiPartOffset+ui);
			m_puhHeight  [ui] = pcCU->getHeight(uiPartOffset+ui);
			m_puhTrIdx  [ui] = pcCU->getTransformIdx(uiPartOffset+ui);
			m_puhTransformSkip[0][ui] = pcCU->getTransformSkip(uiPartOffset+ui,TEXT_LUMA);
			m_puhTransformSkip[1][ui] = pcCU->getTransformSkip(uiPartOffset+ui,TEXT_CHROMA_U);
			m_puhTransformSkip[2][ui] = pcCU->getTransformSkip(uiPartOffset+ui,TEXT_CHROMA_V);
			m_skipFlag[ui]   = pcCU->getSkipFlag(uiPartOffset+ui);
			m_pePartSize[ui] = pcCU->getPartitionSize(uiPartOffset+ui);
			m_pePredMode[ui] = pcCU->getPredictionMode(uiPartOffset+ui);
			m_CUTransquantBypass[ui] = pcCU->getCUTransquantBypass(uiPartOffset+ui);
			m_pbIPCMFlag[ui]=pcCU->m_pbIPCMFlag[uiPartOffset+ui];
			m_phQP[ui] = pcCU->m_phQP[uiPartOffset+ui];
			m_pbMergeFlag[ui]=pcCU->m_pbMergeFlag[uiPartOffset+ui];
			m_puhMergeIndex[ui]=pcCU->m_puhMergeIndex[uiPartOffset+ui];
			m_puhLumaIntraDir[ui]=pcCU->m_puhLumaIntraDir[uiPartOffset+ui];
			m_puhChromaIntraDir[ui]=pcCU->m_puhChromaIntraDir[uiPartOffset+ui];
			m_puhInterDir[ui]=pcCU->m_puhInterDir[uiPartOffset+ui];
			m_puhCbf[0][ui]=pcCU->m_puhCbf[0][uiPartOffset+ui];
			m_puhCbf[1][ui]=pcCU->m_puhCbf[1][uiPartOffset+ui];
			m_puhCbf[2][ui]=pcCU->m_puhCbf[2][uiPartOffset+ui];

		}
	}
	UInt uiTmp = uhWidth*uhHeight;
	memset( m_pcTrCoeffY , 0, sizeof(TCoeff)*uiTmp );
#if ADAPTIVE_QP_SELECTION  
	memset( m_pcArlCoeffY , 0, sizeof(Int)*uiTmp );
#endif
	memset( m_pcIPCMSampleY , 0, sizeof( Pel ) * uiTmp );
	uiTmp >>= 2;
	memset( m_pcTrCoeffCb, 0, sizeof(TCoeff)*uiTmp );
	memset( m_pcTrCoeffCr, 0, sizeof(TCoeff)*uiTmp );
#if ADAPTIVE_QP_SELECTION
	memset( m_pcArlCoeffCb, 0, sizeof(Int)*uiTmp );
	memset( m_pcArlCoeffCr, 0, sizeof(Int)*uiTmp );
#endif
	memset( m_pcIPCMSampleCb , 0, sizeof( Pel ) * uiTmp );
	memset( m_pcIPCMSampleCr , 0, sizeof( Pel ) * uiTmp );
	m_acCUMvField[0].clearMvField();
	m_acCUMvField[1].clearMvField();

	if(m_pcPic->getPicSym()->getInverseCUOrderMap(getAddr())*m_pcPic->getNumPartInCU()+m_uiAbsIdxInLCU<getSlice()->getSliceSegmentCurStartCUAddr())
	{
		// Part of this CU contains data from an older slice. Now copy in that data.
		UInt uiMaxCuWidth=pcCU->getSlice()->getSPS()->getMaxCUWidth();
		UInt uiMaxCuHeight=pcCU->getSlice()->getSPS()->getMaxCUHeight();
		TComDataCU * bigCU = getPic()->getCU(getAddr());
		Int minui = uiPartOffset;
		minui = -minui;
		pcCU->m_acCUMvField[0].copyTo(&m_acCUMvField[0],minui,uiPartOffset,m_uiNumPartition);
		pcCU->m_acCUMvField[1].copyTo(&m_acCUMvField[1],minui,uiPartOffset,m_uiNumPartition);
		UInt uiCoffOffset = uiMaxCuWidth*uiMaxCuHeight*m_uiAbsIdxInLCU/pcCU->getPic()->getNumPartInCU();
		uiTmp = uhWidth*uhHeight;
		for(Int i=0; i<uiTmp; i++)
		{
			m_pcTrCoeffY[i]=bigCU->m_pcTrCoeffY[uiCoffOffset+i];
#if ADAPTIVE_QP_SELECTION
			m_pcArlCoeffY[i]=bigCU->m_pcArlCoeffY[uiCoffOffset+i];
#endif
			m_pcIPCMSampleY[i]=bigCU->m_pcIPCMSampleY[uiCoffOffset+i];
		}
		uiTmp>>=2;
		uiCoffOffset>>=2;
		for(Int i=0; i<uiTmp; i++)
		{
			m_pcTrCoeffCr[i]=bigCU->m_pcTrCoeffCr[uiCoffOffset+i];
			m_pcTrCoeffCb[i]=bigCU->m_pcTrCoeffCb[uiCoffOffset+i];
#if ADAPTIVE_QP_SELECTION
			m_pcArlCoeffCr[i]=bigCU->m_pcArlCoeffCr[uiCoffOffset+i];
			m_pcArlCoeffCb[i]=bigCU->m_pcArlCoeffCb[uiCoffOffset+i];
#endif
			m_pcIPCMSampleCb[i]=bigCU->m_pcIPCMSampleCb[uiCoffOffset+i];
			m_pcIPCMSampleCr[i]=bigCU->m_pcIPCMSampleCr[uiCoffOffset+i];
		}
	}

	m_pcCULeft        = pcCU->getCULeft();
	m_pcCUAbove       = pcCU->getCUAbove();
	m_pcCUAboveLeft   = pcCU->getCUAboveLeft();
	m_pcCUAboveRight  = pcCU->getCUAboveRight();

	m_apcCUColocated[0] = pcCU->getCUColocated(REF_PIC_LIST_0);
	m_apcCUColocated[1] = pcCU->getCUColocated(REF_PIC_LIST_1);
	memcpy(m_sliceStartCU,pcCU->m_sliceStartCU+uiPartOffset,sizeof(UInt)*m_uiNumPartition);
	memcpy(m_sliceSegmentStartCU,pcCU->m_sliceSegmentStartCU+uiPartOffset,sizeof(UInt)*m_uiNumPartition);
}

    在xCompressCU中,下面四條語句用於獲取bestCU的座標

UInt uiLPelX   = rpcBestCU->getCUPelX();							//  (left)
UInt uiRPelX   = uiLPelX + rpcBestCU->getWidth(0)  - 1;		//  (right)
UInt uiTPelY   = rpcBestCU->getCUPelY();							// 	(top)
UInt uiBPelY   = uiTPelY + rpcBestCU->getHeight(0) - 1;		// 	(buttom)
    1、getCUPelX返回CU的左上角的X座標
    2、getWidth(0)返回當前CU的寬度,參數是深度值,0表示返回的是當前CU的寬度,而不是其子CU的寬度
    3、getCUPelY返回CU左上角的Y座標
    4、getHeight(0)返回當前CU的高度,參數是深度值,0表示返回的是當前CU的高度,而不是其子CU的高度







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