HEVC學習-幀內預測-initAdiPattern函式
阿新 • • 發佈:2018-12-22
#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,因畫圖的時候發生了遺漏,特此說明)
本博文僅作小白學習記錄使用,所有文章出處均在開頭連結處可見,所有疑惑點均作了標註。歡迎各位大佬交流批評指正,侵刪。