1. 程式人生 > >HEVC學習-幀內預測-initAdiPattern函式

HEVC學習-幀內預測-initAdiPattern函式

#define     MAX_CU_DEPTH             6                          // log2(LCUSize) 最大CU深度為6
#define     MAX_CU_SIZE             (1<<(MAX_CU_DEPTH))         // maximum allowable size of CU, surely 64? (not 1<<7 = 128)最大CU_size
#define     MIN_PU_SIZE              4
#define     MIN_TU_SIZE              4
#define     MAX_TU_SIZE             32
#define     MAX_NUM_SPU_W           (MAX_CU_SIZE/MIN_PU_SIZE)   // maximum number of SPU in horizontal line水平方向上SPU的最大數目
#define     SCALING_LIST_REM_NUM     6

注:(ps:1<<7表示二進位制的1向左左移7位即10000000=128)(原諒我是個小白,大佬盡情鄙視)
二、對於影象步長值(stride value)的解釋
先貼一段HM程式碼中的原始碼

  // Access starting position of YUV transform unit buffer by pix offset for square & non-square blocks;對於正方形和非正方形塊,通過pix偏移訪問YUV轉換單元緩衝區的起始位置
  Pel*         getAddrPix                 (const ComponentID id, const UInt iPixX, const UInt iPixY )       { return m_apiBuf[id] + iPixY * getStride(id) + iPixX; }
  const Pel*   getAddrPix                 (const ComponentID id, const UInt iPixX, const UInt iPixY ) const { return m_apiBuf[id] + iPixY * getStride(id) + iPixX; }

  //  Get stride value of YUV buffer;得到YUV緩衝區域的步長值
  UInt         getStride                  (const ComponentID id) const { return m_iWidth >> getComponentScaleX(id);   }
  UInt         getHeight                  (const ComponentID id) const { return m_iHeight >> getComponentScaleY(id);  }
  UInt         getWidth                   (const ComponentID id) const { return m_iWidth >> getComponentScaleX(id);   }
  ChromaFormat getChromaFormat            ()                     const { return m_chromaFormatIDC; }
  UInt         getNumberValidComponents   ()                     const { return ::getNumberValidComponents(m_chromaFormatIDC); }
  UInt         getComponentScaleX         (const ComponentID id) const { return ::getComponentScaleX(id, m_chromaFormatIDC); }
  UInt         getComponentScaleY         (const ComponentID id) const { return ::getComponentScaleY(id, m_chromaFormatIDC); }

};

下面對影象步長值(stride value)進行解釋
當視訊影象儲存在記憶體時,影象的每一行末尾也許包含一些擴充套件的內容,這些擴充套件的內容隻影響影象如何儲存在記憶體中,但是不影響影象如何顯示出來。Stride 就是這些擴充套件內容的名稱,Stride 也被稱作 Pitch,如果影象的每一行畫素末尾擁有擴充套件內容,Stride 的值一定大於影象的寬度值,就像下圖所示:

在這裡插入圖片描述
兩個緩衝區包含同樣大小(寬度和高度)的視訊幀,卻不一定擁有同樣的 Stride 值,如果你處理一個視訊幀,你必須在計算的時候把 Stride 考慮進去。
另外,一張影象在記憶體中有兩種不同的儲存序列(arranged),對於一個從上而下儲存(Top-Down) 的影象,最頂行的畫素儲存在記憶體中最開頭的部分,對於一張從下而上儲存(Bottom-Up)的影象,最後一行的畫素儲存在記憶體中最開頭的部分,下面圖示展示了這兩種情況:
在這裡插入圖片描述


一張從下而上的影象擁有一個負的 Stride 值,因為 Stride 被定義為[從一行畫素移動到下一行畫素時需要跨過多少個畫素],僅相對於被顯示出來的影象而言;而 YUV 影象永遠都是從上而下表示的,以及任何包含在 Direct3D Surface 中的影象必須是從上而下,RGB 影象儲存在系統記憶體時通常是從下而上。尤其是視訊變換,特別需要處理不同 Stride 值的影象,因為輸入緩衝也許與輸出緩衝不匹配。
三、具體程式碼註釋實現(如果有大佬能夠指出錯誤或者解答疑惑,小弟萬分感謝)

Void TComPattern::initAdiPattern(TComDataCU* pcCU,       //當前待處理的CU
	UInt uiZorderIdxInPart, //當前待處理的PU相對於pcCU的位置,以4*4塊為單位
	UInt uiPartDepth, //當前PU的深度(相對於當前的CU),非0即1
	Int* piAdiBuf,    //其指向的空間是為了儲存預測資料,這裡只是使用上一行,左一列來儲存參考點的資料
	Int iOrgBufStride, //m_iYuvExtStride = ((MAX_CU_SIZE  + 8) << 4);??
	Int iOrgBufHeight, //m_iYuvExtHeight  = ((MAX_CU_SIZE + 2) << 4);??
	Bool& bAbove,      //指示上面塊可否使用,根據程式碼除錯的過程看,這個是為了計算均值時使用的一個標記
	Bool& bLeft,       //指示左面塊可否使用,同上
	Bool bLMmode)     //
{
	Pel*  piRoiOrigin;//指向原始資料
	Int*  piAdiTemp;
	//當前PU的尺寸:先計算當前CU的尺寸,在根據uiPartDepth計算
	UInt  uiCuWidth = pcCU->getWidth(0) >> uiPartDepth;
	UInt  uiCuHeight = pcCU->getHeight(0) >> uiPartDepth;

	//參考點水平和豎直方向上的個數(比當前PU的尺寸大一倍)
	UInt  uiCuWidth2 = uiCuWidth << 1;//uiCuWidth2、uiCuHeight2分別等於二倍的當前CU的寬度和高度
	UInt  uiCuHeight2 = uiCuHeight << 1;
	UInt  uiWidth;
	UInt  uiHeight;
	//當前影象的跨度(比影象的寬稍微大點)
	Int   iPicStride = pcCU->getPic()->getStride();//影象跨度(步長)值要大於影象寬度

	Int   iUnitSize = 0;
	Int   iNumUnitsInCu = 0;
	Int   iTotalUnits = 0;
	Bool  bNeighborFlags[4 * MAX_NUM_SPU_W + 1];//MAX_NUM_SPU_W是水平方向上的SPU的數量,MAX_NUM_SPU_W=(MAX_CU_SIZE/MIN_PU_SIZE);儲存五個方向上的鄰域標誌位,即是否可用

	Int   iNumIntraNeighbor = 0;//用來統計可用鄰域的數目

	UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;

	//獲取當前PU左上角,右上角以及左下角的位置, 以4x4塊為單位的ZScanOrder   
	pcCU->deriveLeftRightTopIdxAdi(uiPartIdxLT, uiPartIdxRT, uiZorderIdxInPart, uiPartDepth);//指標指向左上角和右上角的4x4ZScanOrder
	pcCU->deriveLeftBottomIdxAdi(uiPartIdxLB, uiZorderIdxInPart, uiPartDepth);//指標指向左下角的4x4ZScanOrder

	iUnitSize = g_uiMaxCUWidth >> g_uiMaxCUDepth;
	iNumUnitsInCu = uiCuWidth / iUnitSize;
	iTotalUnits = (iNumUnitsInCu << 2) + 1;
	//bNeighborFlags存放的是參考點是否可用,依次存放左下,左,左上,上,右上5個區域的參考點可用性,即掃描順序為從左下到左上,再從左上到右上 
	bNeighborFlags[iNumUnitsInCu * 2] = isAboveLeftAvailable(pcCU, uiPartIdxLT);
	iNumIntraNeighbor += (Int)(bNeighborFlags[iNumUnitsInCu * 2]);
	iNumIntraNeighbor += isAboveAvailable(pcCU, uiPartIdxLT, uiPartIdxRT, bNeighborFlags + (iNumUnitsInCu * 2) + 1);
	iNumIntraNeighbor += isAboveRightAvailable(pcCU, uiPartIdxLT, uiPartIdxRT, bNeighborFlags + (iNumUnitsInCu * 3) + 1);
	iNumIntraNeighbor += isLeftAvailable(pcCU, uiPartIdxLT, uiPartIdxLB, bNeighborFlags + (iNumUnitsInCu * 2) - 1);
	iNumIntraNeighbor += isBelowLeftAvailable(pcCU, uiPartIdxLT, uiPartIdxLB, bNeighborFlags + iNumUnitsInCu - 1);//計算5個區域內的可用鄰域的個數

	bAbove = true;
	bLeft = true;
	//參考點在水平和豎直方向上的個數,+1是因為要記錄左上角的一個位置
	uiWidth = uiCuWidth2 + 1;
	uiHeight = uiCuHeight2 + 1;
	//這個if語句我也不知道幹嘛的,可能是為了讓大家看不懂而設定????
	if (((uiWidth << 2)>iOrgBufStride) || ((uiHeight << 2)>iOrgBufHeight))
	{
		return;
	}

	//獲得當前PU對應的原始資料的地址
	piRoiOrigin = pcCU->getPic()->getPicYuvRec()->getLumaAddr(pcCU->getAddr(), pcCU->getZorderIdxInCU() + uiZorderIdxInPart);
	piAdiTemp = piAdiBuf;

	//獲得當前PU的參考點(內部分為3種情況:1 所有塊都可用2所有塊都不可用3有部分塊可用)
	fillReferenceSamples(g_bitDepthY, piRoiOrigin, piAdiTemp, bNeighborFlags, iNumIntraNeighbor, iUnitSize, iNumUnitsInCu, iTotalUnits, uiCuWidth, uiCuHeight, uiWidth, uiHeight, iPicStride, bLMmode);


	//! 下面所進行的工作主要是對參考樣點進行3抽頭的濾波。piAdiBuf指向濾波前的參考樣點的首地址,在濾波前,先將所有參考樣點
	//! 拷貝到piFilterBuf指向的區域,經濾波後的樣點值儲存在piFilterBufN指向的區域,最終將濾波後的樣點值拷貝到piFilterBuf1
	//! 值得一提的是,最終的結果是,piAdiBuf指向的區域是未經濾波的樣點值,而piFilterBuf1指向的區域是經過濾波的樣點值,
	//! 兩者的地址相差uiWH = uiWidth * uiHeight = (uiCuWidth2 + 1) * (uiCuHeight2 + 1),這就解釋了在進行真正的幀內預測時,
	//! 在需要濾波時,指向piAdiBuf的指標需要加上uiWH的偏移量
	Int* piFilteredBuf1 = piAdiBuf + uiWH;        // 1. filter buffer
	Int* piFilteredBuf2 = piFilteredBuf1 + uiWH;  // 2. filter buffer
	Int* piFilterBuf = piFilteredBuf2 + uiWH;     // buffer for 2. filtering (sequential)
	Int* piFilterBufN = piFilterBuf + iBufSize;   // buffer for 1. filtering (sequential) //!<存放的是參考樣點經3抽頭濾波後的值

												  // draft 8.4.4.2.3 Filtering process of neighbouring samples
	Int l = 0;
	// left border from bottom to top
	for (i = 0; i < uiCuHeight2; i++)
	{
		piFilterBuf[l++] = piAdiTemp[uiWidth * (uiCuHeight2 - i)]; ////!< 左邊界,儲存順序為從下往上
	}
	// top left corner
	piFilterBuf[l++] = piAdiTemp[0];  //!< 左上邊界
									  // above border from left to right
	for (i = 0; i < uiCuWidth2; i++)
	{
		piFilterBuf[l++] = piAdiTemp[1 + i];  //!<上邊界,儲存順序為從左往右
	}

	// 1. filtering with [1 2 1]
	piFilterBufN[0] = piFilterBuf[0]; //!< 第1個點直接儲存,不濾波
	piFilterBufN[iBufSize - 1] = piFilterBuf[iBufSize - 1]; //!< 最後一個點也直接儲存,不濾波
	for (i = 1; i < iBufSize - 1; i++) //!< 對中間樣點值進行3抽頭[1 2 1] / 4 的平滑濾波
	{
		piFilterBufN[i] = (piFilterBuf[i - 1] + 2 * piFilterBuf[i] + piFilterBuf[i + 1] + 2) >> 2;
	}

	// fill 1. filter buffer with filtered values
	l = 0;
	for (i = 0; i < uiCuHeight2; i++)
	{
		piFilteredBuf1[uiWidth * (uiCuHeight2 - i)] = piFilterBufN[l++];  // left border from bottom to top //!< 左邊界
	}
	piFilteredBuf1[0] = piFilterBufN[l++]; //!< 左上邊界
	for (i = 0; i < uiCuWidth2; i++)
	{
		piFilteredBuf1[1 + i] = piFilterBufN[l++]; // above border from left to right //!< 上邊界
	}
}

在這裡插入圖片描述
(注,上圖中,uiWidth和uiHeight實際上對應的是程式碼中的uiCUWidth和uiCUHeight,因畫圖的時候發生了遺漏,特此說明)

本博文僅作小白學習記錄使用,所有文章出處均在開頭連結處可見,所有疑惑點均作了標註。歡迎各位大佬交流批評指正,侵刪。