1. 程式人生 > >33、編碼一個CU(幀內部分)2、幀內預測各種模式的實現

33、編碼一個CU(幀內部分)2、幀內預測各種模式的實現

HEVC中一共定義了35中幀內編碼預測模式,編號分別以0-34定義。其中模式0定義為平面模式(INTRA_PLANAR),模式1定義為均值模式(INTRA_DC),模式2~34定義為角度預測模式(INTRA_ANGULAR2~INTRA_ANGULAR34),分別代表了不同的角度。具體的示意圖如標準文件的圖8-1所示:


這三大類的預測方法均有實現的程式碼。首先看最簡單的Intra_DC模式,該模式同角度預測模式實現在同一個函式Void TComPrediction::xPredIntraAng(...)中:

  1. Void TComPrediction::xPredIntraAng(Int bitDepth, Int* pSrc, Int srcStride, Pel*& rpDst, Int dstStride, UInt width, UInt height, UInt dirMode, Bool blkAboveAvailable, Bool blkLeftAvailable, Bool bFilter )  
  2. {  
  3.         //......
  4.         // Do the DC prediction
  5.     if (modeDC)  
  6.     {  
  7.         Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height, blkAboveAvailable, blkLeftAvailable);  
  8.         for (k=0;k<blkSize;k++)  
  9.         {  
  10.             for (l=0;l<blkSize;l++)  
  11.             {  
  12.                 pDst[k*dstStride+l] = dcval;  
  13.             }  
  14.         }  
  15.     }  
  16.         //......
  17. }  
在這個函式中可以看到,Intra_DC模式中所有預測塊的畫素值都是同一個值dcval,這個值是由一個函式predIntraGetPredValDC計算得到:
  1. Pel TComPrediction::predIntraGetPredValDC( Int* pSrc, Int iSrcStride, UInt iWidth, UInt iHeight, Bool bAbove, Bool bLeft )  
  2. {  
  3.     Int iInd, iSum = 0;  
  4.     Pel pDcVal;  
  5.     if (bAbove)  
  6.     {  
  7.         for (iInd = 0;iInd < iWidth;iInd++)  
  8.         {  
  9.             iSum += pSrc[iInd-iSrcStride];  
  10.         }  
  11.     }  
  12.     if (bLeft)  
  13.     {  
  14.         for (iInd = 0;iInd < iHeight;iInd++)  
  15.         {  
  16.             iSum += pSrc[iInd*iSrcStride-1];  
  17.         }  
  18.     }  
  19.     if (bAbove && bLeft)  
  20.     {  
  21.         pDcVal = (iSum + iWidth) / (iWidth + iHeight);  
  22.     }  
  23.     elseif (bAbove)  
  24.     {  
  25.         pDcVal = (iSum + iWidth/2) / iWidth;  
  26.     }  
  27.     elseif (bLeft)  
  28.     {  
  29.         pDcVal = (iSum + iHeight/2) / iHeight;  
  30.     }  
  31.     else
  32.     {  
  33.         pDcVal = pSrc[-1]; // Default DC value already calculated and placed in the prediction array if no neighbors are available
  34.     }  
  35.     return pDcVal;  
  36. }  
在該函式中,編碼器通過判斷上方和左方參考畫素是否有效而選擇將相應的資料(指標pSrc指向的資料)累加到iSum中,並對這些參考資料取平均返回。所以,在DC模式下,所有預測畫素值都是同一個值,也即參考資料的均值,這也是DC模式命名的由來。

第二種預測模式時平面模式,該模式定義在xPredIntraPlanar函式中。

  1. Void TComPrediction::xPredIntraPlanar( Int* pSrc, Int srcStride, Pel* rpDst, Int dstStride, UInt width, UInt height )  
  2. {  
  3.     assert(width == height);  
  4.     Int k, l, bottomLeft, topRight;  
  5.     Int horPred;  
  6.     Int leftColumn[MAX_CU_SIZE], topRow[MAX_CU_SIZE], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];  
  7.     UInt blkSize = width;  
  8.     UInt offset2D = width;  
  9.     UInt shift1D = g_aucConvertToBit[ width ] + 2;  
  10.     UInt shift2D = shift1D + 1;  
  11.     // Get left and above reference column and row
  12.     for(k=0;k<blkSize+1;k++)  
  13.     {  
  14.         topRow[k] = pSrc[k-srcStride];  
  15.         leftColumn[k] = pSrc[k*srcStride-1];  
  16.     }  
  17.     // Prepare intermediate variables used in interpolation
  18.     bottomLeft = leftColumn[blkSize];  
  19.     topRight   = topRow[blkSize];  
  20.     for (k=0;k<blkSize;k++)  
  21.     {  
  22.         bottomRow[k]   = bottomLeft - topRow[k];  
  23.         rightColumn[k] = topRight   - leftColumn[k];  
  24.         topRow[k]      <<= shift1D;  
  25.         leftColumn[k]  <<= shift1D;  
  26.     }  
  27.     // Generate prediction signal
  28.     for (k=0;k<blkSize;k++)  
  29.     {  
  30.         horPred = leftColumn[k] + offset2D;  
  31.         for (l=0;l<blkSize;l++)  
  32.         {  
  33.             horPred += rightColumn[k];  
  34.             topRow[l] += bottomRow[l];  
  35.             rpDst[k*dstStride+l] = ( (horPred + topRow[l]) >> shift2D );  
  36.         }  
  37.     }  
  38. }  
首先從參考資料中獲取的是頂行和左列的資料,並記錄一下左下角和右上角的兩個畫素值。然後計算底行和右列的資料,方法是用左下角的畫素減去頂行相應位置的畫素得到底行,右上角的畫素減去左列相應位置的畫素得到右列。預測塊中每個畫素的資料,就是對應的四個邊的畫素值的平均。

第三種預測模式,即mode=2~34時採用角度預測模式。實現的方式在xPredIntraAng中:

  1. Void TComPrediction::xPredIntraAng(Int bitDepth, Int* pSrc, Int srcStride, Pel*& rpDst, Int dstStride, UInt width, UInt height, UInt dirMode, Bool blkAboveAvailable, Bool blkLeftAvailable, Bool bFilter )  
  2. {  
  3.     Int k,l;  
  4.     Int blkSize        = width;  
  5.     Pel* pDst          = rpDst;  
  6.     // Map the mode index to main prediction direction and angle
  7.     assert( dirMode > 0 ); //no planar
  8.     Bool modeDC        = dirMode < 2;  
  9.     Bool modeHor       = !modeDC && (dirMode < 18);  
  10.     Bool modeVer       = !modeDC && !modeHor;  
  11.     Int intraPredAngle = modeVer ? (Int)dirMode - VER_IDX : modeHor ? -((Int)dirMode - HOR_IDX) : 0;//計算當前模式同水平/垂直模式之間的角度差
  12.     Int absAng         = abs(intraPredAngle);  
  13.     Int signAng        = intraPredAngle < 0 ? -1 : 1;  
  14.     // Set bitshifts and scale the angle parameter to block size
  15.     Int angTable[9]    = {0,    2,    5,   9,  13,  17,  21,  26,  32};  
  16.     Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle
  17.     Int invAngle       = invAngTable[absAng];  
  18.     absAng             = angTable[absAng];  
  19.     intraPredAngle     = signAng * absAng;  
  20.     // ......
  21.         // Do angular predictions
  22.     else
  23.     {  
  24.         Pel* refMain;  
  25.         Pel* refSide;  
  26.         Pel  refAbove[2*MAX_CU_SIZE+1];  
  27.         Pel  refLeft[2*MAX_CU_SIZE+1];  
  28.         // Initialise the Main and Left reference array.
  29.         if (intraPredAngle < 0)  
  30.         {  
  31.             for (k=0;k<blkSize+1;k++)  
  32.             {  
  33.                 refAbove[k+blkSize-1] = pSrc[k-srcStride-1];  
  34.             }  
  35.             for (k=0;k<blkSize+1;k++)  
  36.             {  
  37.                 refLeft[k+blkSize-1] = pSrc[(k-1)*srcStride-1];  
  38.             }  
  39.             refMain = (modeVer ? refAbove : refLeft) + (blkSize-1);  
  40.             refSide = (modeVer ? refLeft : refAbove) + (blkSize-1);  
  41.             // Extend the Main reference to the left.
  42.             Int invAngleSum    = 128;       // rounding for (shift by 8)
  43.             for (k=-1; k>blkSize*intraPredAngle>>5; k--)  
  44.             {  
  45.                 invAngleSum += invAngle;  
  46.                 refMain[k] = refSide[invAngleSum>>8];  
  47.             }  
  48.         }  
  49.         else
  50.         {  
  51.             for (k=0;k<2*blkSize+1;k++)  
  52.             {  
  53.                 refAbove[k] = pSrc[k-srcStride-1];  
  54.             }  
  55.             for (k=0;k<2*blkSize+1;k++)  
  56.             {  
  57.                 refLeft[k] = pSrc[(k-1)*srcStride-1];  
  58.             }  
  59.             refMain = modeVer ? refAbove : refLeft;  
  60.             refSide = modeVer ? refLeft  : refAbove;  
  61.         }  
  62.         if (intraPredAngle == 0)  
  63.         {  
  64.             for (k=0;k<blkSize;k++)  
  65.             {  
  66.                 for (l=0;l<blkSize;l++)  
  67.                 {  
  68.                     pDst[k*dstStride+l] = refMain[l+1];  
  69.                 }  
  70.             }  
  71.             if ( bFilter )  
  72.             {  
  73.                 for (k=0;k<blkSize;k++)  
  74.                 {  
  75.                     pDst[k*dstStride] = Clip3(0, (1<<bitDepth)-1, pDst[k*dstStride] + (( refSide[k+1] - refSide[0] ) >> 1) );  
  76.                 }  
  77.             }  
  78.         }  
  79.         else
  80.         {  
  81.             Int deltaPos=0;  
  82.             Int deltaInt;  
  83.             Int deltaFract;  
  84.             Int refMainIndex;  
  85.             for (k=0;k<blkSize;k++)  
  86.             {  
  87.                 deltaPos += intraPredAngle;  
  88.                 deltaInt   = deltaPos >> 5;  
  89.                 deltaFract = deltaPos & (32 - 1);  
  90.                 if (deltaFract)  
  91.                 {  
  92.                     // Do linear filtering
  93.                     for (l=0;l<blkSize;l++)  
  94.                     {  
  95.                         refMainIndex        = l+deltaInt+1;  
  96.                         pDst[k*dstStride+l] = (Pel) ( ((32-deltaFract)*refMain[refMainIndex]+deltaFract*refMain[refMainIndex+1]+16) >> 5 );  
  97.                     }  
  98.                 }  
  99.                 else
  100.                 {  
  101.                     // Just copy the integer samples
  102.                     for (l=0;l<blkSize;l++)  
  103.                     {  
  104.                         pDst[k*dstStride+l] = refMain[l+deltaInt+1];  
  105.                     }  
  106.                 }  
  107.             }  
  108.         }  
  109.         // Flip the block if this is the horizontal mode
  110.         if (modeHor)  
  111.         {  
  112.             Pel  tmp;  
  113.             for (k=0;k<blkSize-1;k++)  
  114.             {  
  115.                 for (l=k+1;l<blkSize;l++)  
  116.                 {  
  117.                     tmp                 = pDst[k*dstStride+l];  
  118.                     pDst[k*dstStride+l] = pDst[l*dstStride+k];  
  119.                     pDst[l*dstStride+k] = tmp;  
  120.                 }  
  121.             }  
  122.         }  
  123.     }  
  124. }  
在圖8.1中可以看出,模式18的預測方向相當於對角線預測。所以以模式18為分界線,2~17分為水平模式(modeHor),18~33分為垂直模式(modeVer),這樣區分有利於減少程式碼的冗餘。另外,從該圖中也可以看出,模式10和26即相當於水平模式和垂直模式,在程式碼中也定義了兩個巨集HOR_IDX和VER_IDX表示,然後計算當前模式同水平/垂直模式之間的角度差,用intraPredAngle表示。intraPredAngle不同的取值對應的預測方向可以參考圖8-2: 圖中可見,intraPredAngle的取值可能出現正值或負值。當intraPredAngle取非負值時,垂直模式下只參考上方的參考點,水平模式下只參考左方的參考點;當intraPredAngle取負值的時候,refMain會依照refSide中的資料進行部分擴充,因此會同時參考左方和上方兩部分的參考點。當intraPredAngle為0的時候,表示預測模式為10或者26,這是也就是水平或者垂直模式,直接複製參考畫素的值就OK了;否則,會對角度做一個判斷,如果對應的是參考畫素中的整畫素點那麼就不需要進行計算,直接獲取該點資料;如果對應的不是整畫素點,那麼會按照相鄰兩點按照“距離”進行加權平均作為參考畫素點的值。

除此之外,這個函式還實現了對小於16×16尺寸塊實現濾波操作,以及水平模式時將預測矩陣進行轉置操作。

大致上Intra預測塊的生成方法就這樣了,下一個問題在於,參考畫素是如何來的?pSrc指標指向的資料又是如何獲取的?且聽下回。