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.   
  9.         for (k=0;k<blkSize;k++)  
  10.         {  
  11.             for (l=0;l<blkSize;l++)  
  12.             {  
  13.                 pDst[k*dstStride+l] = dcval;  
  14.             }  
  15.         }  
  16.     }  
  17.         //......  
  18. }  
在这个函数中可以看到,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.   
  6.     if (bAbove)  
  7.     {  
  8.         for (iInd = 0;iInd < iWidth;iInd++)  
  9.         {  
  10.             iSum += pSrc[iInd-iSrcStride];  
  11.         }  
  12.     }  
  13.     if (bLeft)  
  14.     {  
  15.         for (iInd = 0;iInd < iHeight;iInd++)  
  16.         {  
  17.             iSum += pSrc[iInd*iSrcStride-1];  
  18.         }  
  19.     }  
  20.   
  21.     if (bAbove && bLeft)  
  22.     {  
  23.         pDcVal = (iSum + iWidth) / (iWidth + iHeight);  
  24.     }  
  25.     else if (bAbove)  
  26.     {  
  27.         pDcVal = (iSum + iWidth/2) / iWidth;  
  28.     }  
  29.     else if (bLeft)  
  30.     {  
  31.         pDcVal = (iSum + iHeight/2) / iHeight;  
  32.     }  
  33.     else  
  34.     {  
  35.         pDcVal = pSrc[-1]; // Default DC value already calculated and placed in the prediction array if no neighbors are available  
  36.     }  
  37.   
  38.     return pDcVal;  
  39. }  
在该函数中,编码器通过判断上方和左方参考像素是否有效而选择将相应的数据(指针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.   
  5.     Int k, l, bottomLeft, topRight;  
  6.     Int horPred;  
  7.     Int leftColumn[MAX_CU_SIZE], topRow[MAX_CU_SIZE], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];  
  8.     UInt blkSize = width;  
  9.     UInt offset2D = width;  
  10.     UInt shift1D = g_aucConvertToBit[ width ] + 2;  
  11.     UInt shift2D = shift1D + 1;  
  12.   
  13.     // Get left and above reference column and row  
  14.     for(k=0;k<blkSize+1;k++)  
  15.     {  
  16.         topRow[k] = pSrc[k-srcStride];  
  17.         leftColumn[k] = pSrc[k*srcStride-1];  
  18.     }  
  19.   
  20.     // Prepare intermediate variables used in interpolation  
  21.     bottomLeft = leftColumn[blkSize];  
  22.     topRight   = topRow[blkSize];  
  23.     for (k=0;k<blkSize;k++)  
  24.     {  
  25.         bottomRow[k]   = bottomLeft - topRow[k];  
  26.         rightColumn[k] = topRight   - leftColumn[k];  
  27.         topRow[k]      <<= shift1D;  
  28.         leftColumn[k]  <<= shift1D;  
  29.     }  
  30.   
  31.     // Generate prediction signal  
  32.     for (k=0;k<blkSize;k++)  
  33.     {  
  34.         horPred = leftColumn[k] + offset2D;  
  35.         for (l=0;l<blkSize;l++)  
  36.         {  
  37.             horPred += rightColumn[k];  
  38.             topRow[l] += bottomRow[l];  
  39.             rpDst[k*dstStride+l] = ( (horPred + topRow[l]) >> shift2D );  
  40.         }  
  41.     }  
  42. }  
首先从参考数据中获取的是顶行和左列的数据,并记录一下左下角和右上角的两个像素值。然后计算底行和右列的数据,方法是用左下角的像素减去 顶行相应位置的像素得到底行,右上角的像素减去左列相应位置的像素得到右列。预测块中每个像素的数据,就是对应的四个边的像素值的平均。

第三种预测模式,即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.   
  7.     // Map the mode index to main prediction direction and angle  
  8.     assert( dirMode > 0 ); //no planar  
  9.     Bool modeDC        = dirMode < 2;  
  10.     Bool modeHor       = !modeDC && (dirMode < 18);  
  11.     Bool modeVer       = !modeDC && !modeHor;  
  12.     Int intraPredAngle = modeVer ? (Int)dirMode - VER_IDX : modeHor ? -((Int)dirMode - HOR_IDX) : 0;//计算当前模式同水平/垂直模式之间的角度差  
  13.     Int absAng         = abs(intraPredAngle);  
  14.     Int signAng        = intraPredAngle < 0 ? -1 : 1;  
  15.   
  16.     // Set bitshifts and scale the angle parameter to block size  
  17.     Int angTable[9]    = {0,    2,    5,   9,  13,  17,  21,  26,  32};  
  18.     Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle  
  19.     Int invAngle       = invAngTable[absAng];  
  20.     absAng             = angTable[absAng];  
  21.     intraPredAngle     = signAng * absAng;  
  22.     // ......  
  23.         // Do angular predictions  
  24.     else  
  25.     {  
  26.         Pel* refMain;  
  27.         Pel* refSide;  
  28.         Pel  refAbove[2*MAX_CU_SIZE+1];  
  29.         Pel  refLeft[2*MAX_CU_SIZE+1];  
  30.   
  31.         // Initialise the Main and Left reference array.  
  32.         if (intraPredAngle < 0)  
  33.         {  
  34.             for (k=0;k<blkSize+1;k++)  
  35.             {  
  36.                 refAbove[k+blkSize-1] = pSrc[k-srcStride-1];  
  37.             }  
  38.             for (k=0;k<blkSize+1;k++)  
  39.             {  
  40.                 refLeft[k+blkSize-1] = pSrc[(k-1)*srcStride-1];  
  41.             }  
  42.             refMain = (modeVer ? refAbove : refLeft) + (blkSize-1);  
  43.             refSide = (modeVer ? refLeft : refAbove) + (blkSize-1);  
  44.   
  45.             // Extend the Main reference to the left.  
  46.             Int invAngleSum    = 128;       // rounding for (shift by 8)  
  47.             for (k=-1; k>blkSize*intraPredAngle>>5; k--)  
  48.             {  
  49.                 invAngleSum += invAngle;  
  50.                 refMain[k] = refSide[invAngleSum>>8];  
  51.             }  
  52.         }  
  53.         else  
  54.         {  
  55.             for (k=0;k<2*blkSize+1;k++)  
  56.             {  
  57.                 refAbove[k] = pSrc[k-srcStride-1];  
  58.             }  
  59.             for (k=0;k<2*blkSize+1;k++)  
  60.             {  
  61.                 refLeft[k] = pSrc[(k-1)*srcStride-1];  
  62.             }  
  63.             refMain = modeVer ? refAbove : refLeft;  
  64.             refSide = modeVer ? refLeft  : refAbove;  
  65.         }  
  66.   
  67.         if (intraPredAngle == 0)  
  68.         {  
  69.             for (k=0;k<blkSize;k++)  
  70.             {  
  71.                 for (l=0;l<blkSize;l++)  
  72.                 {  
  73.                     pDst[k*dstStride+l] = refMain[l+1];  
  74.                 }  
  75.             }  
  76.   
  77.             if ( bFilter )  
  78.             {  
  79.                 for (k=0;k<blkSize;k++)  
  80.                 {  
  81.                     pDst[k*dstStride] = Clip3(0, (1<<bitDepth)-1, pDst[k*dstStride] + (( refSide[k+1] - refSide[0] ) >> 1) );  
  82.                 }  
  83.             }  
  84.         }  
  85.         else  
  86.         {  
  87.             Int deltaPos=0;  
  88.             Int deltaInt;  
  89.             Int deltaFract;  
  90.             Int refMainIndex;  
  91.   
  92.             for (k=0;k<blkSize;k++)  
  93.             {  
  94.                 deltaPos += intraPredAngle;  
  95.                 deltaInt   = deltaPos >> 5;  
  96.                 deltaFract = deltaPos & (32 - 1);  
  97.   
  98.                 if (deltaFract)  
  99.                 {  
  100.                     // Do linear filtering  
  101.                     for (l=0;l<blkSize;l++)  
  102.                     {  
  103.                         refMainIndex        = l+deltaInt+1;  
  104.                         pDst[k*dstStride+l] = (Pel) ( ((32-deltaFract)*refMain[refMainIndex]+deltaFract*refMain[refMainIndex+1]+16) >> 5 );  
  105.                     }  
  106.                 }  
  107.                 else  
  108.                 {  
  109.                     // Just copy the integer samples  
  110.                     for (l=0;l<blkSize;l++)  
  111.                     {  
  112.                         pDst[k*dstStride+l] = refMain[l+deltaInt+1];  
  113.                     }  
  114.                 }  
  115.             }  
  116.         }  
  117.   
  118.         // Flip the block if this is the horizontal mode  
  119.         if (modeHor)  
  120.         {  
  121.             Pel  tmp;  
  122.             for (k=0;k<blkSize-1;k++)  
  123.             {  
  124.                 for (l=k+1;l<blkSize;l++)  
  125.                 {  
  126.                     tmp                 = pDst[k*dstStride+l];  
  127.                     pDst[k*dstStride+l] = pDst[l*dstStride+k];  
  128.                     pDst[l*dstStride+k] = tmp;  
  129.                 }  
  130.             }  
  131.         }  
  132.     }  
  133. }  
在图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指针指向的数据又是如何获取的?且听下回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值