HEVC學習(五) —— 幀內預測系列之三
阿新 • • 發佈:2019-01-22
今天主要介紹幀內預測一個很重要的函式initAdiPattern,它的主要功能有三個,(1)檢測當前PU的相鄰樣點包括左上、上、右上、左、左下鄰域樣點值的可用性,或者說檢查這些點是否存在;(2)參考樣點的替換過程,主要實現的是JCTVC-J1003即draft 8.4.4.2.2的內容,主要由函式fillReferenceSamples來完成,這個在之前的文章已經討論過了;(3)相鄰樣點即參考樣點的平滑濾波,主要實現draft 8.4.4.2.3的內容。話不多說,下面給出initAdiPattern的實現和我個人的一些註釋,供大家參考。
Void TComPattern::initAdiPattern( TComDataCU* pcCU, UInt uiZorderIdxInPart, UInt uiPartDepth, Int* piAdiBuf, Int iOrgBufStride, Int iOrgBufHeight, Bool& bAbove, Bool& bLeft, Bool bLMmode ) {//! bLMmode is usually false Pel* piRoiOrigin; Int* piAdiTemp; UInt uiCuWidth = pcCU->getWidth(0) >> uiPartDepth; //!< CU的寬度 UInt uiCuHeight = pcCU->getHeight(0)>> uiPartDepth; //!< CU的高度 UInt uiCuWidth2 = uiCuWidth<<1; 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]; //!< 用於存放4個方向上的相鄰樣點值的可用性, 4 x 32 + 1 Int iNumIntraNeighbor = 0; //!< 給可用鄰塊進行計數 UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB; //! 獲取當前PU左上角LT,右上角RT以及左下角LB 以4x4塊為單位的Zorder pcCU->deriveLeftRightTopIdxAdi( uiPartIdxLT, uiPartIdxRT, uiZorderIdxInPart, uiPartDepth ); pcCU->deriveLeftBottomIdxAdi ( uiPartIdxLB, uiZorderIdxInPart, uiPartDepth ); iUnitSize = g_uiMaxCUWidth >> g_uiMaxCUDepth; iNumUnitsInCu = uiCuWidth / iUnitSize; iTotalUnits = (iNumUnitsInCu << 2) + 1; // Top + RightTop + Left + LeftBottom + LeftTop = iNumUnitsInCu + iNumUnitsInCu + iNumUnitsInCu + iNumUnitsInCu + 1 //! 掃描順序是從左下到左上,再從左上到右上 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 ); bAbove = true; bLeft = true; uiWidth=uiCuWidth2+1; uiHeight=uiCuHeight2+1; if (((uiWidth<<2)>iOrgBufStride)||((uiHeight<<2)>iOrgBufHeight)) { return; } //! piRoiOrigin指向當前PU左上角 piRoiOrigin = pcCU->getPic()->getPicYuvRec()->getLumaAddr(pcCU->getAddr(), pcCU->getZorderIdxInCU()+uiZorderIdxInPart); piAdiTemp = piAdiBuf; fillReferenceSamples ( pcCU, piRoiOrigin, piAdiTemp, bNeighborFlags, iNumIntraNeighbor, iUnitSize, iNumUnitsInCu, iTotalUnits, uiCuWidth, uiCuHeight, uiWidth, uiHeight, iPicStride, bLMmode); Int i; // generate filtered intra prediction samples Int iBufSize = uiCuHeight2 + uiCuWidth2 + 1; // left and left above border + above and above right border + top left corner = length of 3. filter buffer UInt uiWH = uiWidth * uiHeight; // number of elements in one buffer //! 下面所進行的工作主要是對參考樣點進行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,因畫圖的時候發生了遺漏,特此說明)
(轉載請註明出處。)