HEVC幀內預測HM實現

1、具體代碼位置

角度預測的代碼路徑:TLibCommon\TComPrediction.cpp  xPredIntraAng()


2、代碼流程

 

        const Bool       bIsModeVer = (dirMode >= 18);
        const Int        intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX :  -((Int)dirMode - HOR_IDX);
        const Int        absAngMode = abs(intraPredAngleMode);
        const Int        signAng = intraPredAngleMode < 0 ? -1 : 1;
        const Bool       edgeFilter = bEnableEdgeFilters && isLuma(channelType) &&  (width <= MAXIMUM_INTRA_FILTERED_WIDTH) && (height <= MAXIMUM_INTRA_FILTERED_HEIGHT);
        // Set bitshifts and scale the angle parameter to block size
        static const Int angTable[9] = { 0,    2,    5,   9,  13,  17,  21,  26,  32  };
        static const Int invAngTable[9] = { 0, 4096, 1638, 910, 630, 482, 390, 315,  256 }; // (256 * 32) / Angle
        Int invAngle = invAngTable[absAngMode];
        Int absAng = angTable[absAngMode];
        Int intraPredAngle = signAng * absAng;

 

 

結合之前的角度圖 和  角度表。 上面一段代碼的功能就是 根據模式來 取 intraPredAngle 角度。

首先以 18 爲分界 分水平 和垂直, 大於等於18 垂直, 小於水平。

水平和 垂直 分類的角度中 又可以以10 爲分界 分爲 水平向上 向下, 垂直又可以以26爲分界分爲垂直向左 向右

水平的2-17 從10 往兩邊角度增加相同的即 2 5 9 13 17 21 26 32。 垂直的18-34 從26 往兩邊角度增加相同的即 2 5 9 13 17 21 26 32。

所以只要定義這個 就可以把所有模式的角度給計算出來。

intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX : -((Int)dirMode -  HOR_IDX);

這個就是計算距離 10 和 26 的差值,absAngMode 這個是距離的絕對值, signAng這個就是符合 是向上 向下 向左 還是向右。


                const Int refMainOffsetPreScale = (bIsModeVer ? height : width) - 1;

                const Int refMainOffset = height - 1;

                for (Int x = 0; x < width + 1; x++)

                {

                        refAbove[x + refMainOffset] = pSrc[x - srcStride - 1];

                }

                for (Int y = 0; y < height + 1; y++)

                {

                        refLeft[y + refMainOffset] = pSrc[(y - 1)*srcStride - 1];

                }

                refMain = (bIsModeVer ? refAbove : refLeft) + refMainOffset;

                refSide = (bIsModeVer ? refLeft : refAbove) + refMainOffset;

                // Extend the Main reference to the left.

                Int invAngleSum = 128;       // rounding for (shift by 8)

                for (Int k = -1; k > (refMainOffsetPreScale + 1)*intraPredAngle >> 5;  k--)

                {

                        invAngleSum += invAngle;

                        refMain[k] = refSide[invAngleSum >> 8];

                }

inAngle 的作用, 相當於 是一個算法定點化的一個過程。

這邊實際就是之前說過的映射的過程,refMain 主參考邊的數組,對於垂直類的模式,主參考邊是refAbove ,水平類的就是refLeft。

主參考邊的數組確定之後,剩下的一個就是副參考 refSide。

最後的循環就是要將refSide 映射到 refMain。 也就是下面這個公式,就是將refSide一一映射到refMain。

 


    

            Pel *pDsty = pDst;

                for (Int y = 0, deltaPos = intraPredAngle; y < height; y++, deltaPos  += intraPredAngle, pDsty += dstStride)

                {

                        const Int deltaInt = deltaPos >> 5;

                        const Int deltaFract = deltaPos & (32 - 1);

                        if (deltaFract)

                        {

                               // Do linear filtering

                               const Pel *pRM = refMain + deltaInt + 1;

                               Int lastRefMainPel = *pRM++;

                               for (Int x = 0; x < width; pRM++, x++)

                               {

                                      Int thisRefMainPel = *pRM;

                                      pDsty[x + 0] = (Pel)(((32 -  deltaFract)*lastRefMainPel + deltaFract * thisRefMainPel + 16) >> 5);

                                      lastRefMainPel = thisRefMainPel;

                               }

                        }

                        else

                        {

                               // Just copy the integer samples

                               for (Int x = 0; x < width; x++)

                               {

                                      pDsty[x] = refMain[x + deltaInt + 1];

                               }

                        }

                }

上面這個就是參考像素獲取的代碼。 最外層的循環 是 y = 0 到 height -1。deltaPos每次都是增加intraPredAngle,pDsty 也是增加dstStride。

算出deltaInt 這個用來確定位置,另外一個則是權重。

refMain 指針移動deltaInt + 1 個字節 pRM指向這個位置。 然後先把這個位置的值取出來賦值給lastRefMainPel,pRM 向前移動一位。

然後在內層循環裏面。塊中行掃描。  thisRefMainPel 取 pRM 當前指向的值。  預測值 就取lastRefMainPel和thisRefMainPel 的權重。

然後將thisRefMainPel 賦值給lastRefMainPel, pRM 向前移動一位 thisRefMainPel 取pRM。 這樣循環。

index的變化 簡化就是外面循環加intra_pred_angle,算delta_int,裏面的循環delta_int的基礎上面取累積。

           for (int j = 0; j < iHeight; j++) {

                      delta_pos = delta_pos + intra_pred_angle;

                      delta_int = delta_pos >> 5;

                      for (int i = 0; i < iWidth; i++) {

                             int ref_main_index = i + delta_int;

                             int ref_side_index = ref_main_index + 1;

                             saveDistanceMatri(distanMatri, i, j, ref_main_index,  ref_side_index);

                      }

              }







         if (!bIsModeVer)

         {

                for (Int y = 0; y < height; y++)

                {

                        for (Int x = 0; x < width; x++)

                        {

                               pTrueDst[x*dstStrideTrue] = pDst[x];

                        }

                        pTrueDst++;

                        pDst += dstStride;

                }

         }


   

x方向和y方向之間的關係

垂直方向是按行掃描過去  然後得到的結果一個個存放在 pDsty中。 

一行都是相差1的。

水平方向的 是需要按列是相差1的。  但是現在 按照行做完,pDsty 中卻是行相差1, 所有應該行列轉置一下

 

 

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