HEVC2HM-16.7编码一个CU(帧内部分) 2.帧内预测各种模式实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HEVC2HM-16.7编码一个CU(帧内部分) 2.帧内预测各种模式实现相关的知识,希望对你有一定的参考价值。

      HEVC中一共定义了35中帧内编码预测模式,编号分别以0-34定义。其中模式0定义为平面模式(INTRA_PLANAR),模式1定义为均值模式(INTRA_DC),模式2~34定义为角度预测模式(INTRA_ANGULAR2~INTRA_ANGULAR34),分别代表了不同的角度。

技术分享

     最简单的Intra_DC模式,DC模式适用于大面积平摊区域,当前预测值可由其左侧和上方(不包含左上角,左下方和右上方)参考像素的平均值得到。该模式同角度预测模式实现在同一个函数Void TComPrediction::xPredIntraAng(...)中:

  1 Void TComPrediction::xPredIntraAng(       Int bitDepth,
  2                                     const Pel* pSrc,     Int srcStride,
  3                                           Pel* pTrueDst, Int dstStrideTrue,
  4                                           UInt uiWidth, UInt uiHeight, ChannelType channelType,
  5                                           UInt dirMode, const Bool bEnableEdgeFilters
  6                                   )
  7 {
  8   Int width=Int(uiWidth);
  9   Int height=Int(uiHeight);
 10 
 11   // Map the mode index to main prediction direction and angle
 12   assert( dirMode != PLANAR_IDX ); //no planar
 13   const Bool modeDC        = dirMode==DC_IDX;
 14 
 15   // Do the DC prediction
 16   if (modeDC)
 17   {
 18     const Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height);
 19 
 20     for (Int y=height;y>0;y--, pTrueDst+=dstStrideTrue)
 21     {
 22       for (Int x=0; x<width;) // width is always a multiple of 4.
 23       {
 24         pTrueDst[x++] = dcval;
 25       }
 26     }
 27   }
 28   else // Do angular predictions
 29   {
 30   //........ 31   }
 32  }

     在这个函数中可以看到,Intra_DC模式中所有预测块的像素值都是同一个值dcval,这个值是由一个函数predIntraGetPredValDC计算得到:

 1 Pel TComPrediction::predIntraGetPredValDC( const Pel* pSrc, Int iSrcStride, UInt iWidth, UInt iHeight)
 2 {
 3   assert(iWidth > 0 && iHeight > 0);
 4   Int iInd, iSum = 0;
 5   Pel pDcVal;
 6 
 7   for (iInd = 0;iInd < iWidth;iInd++)
 8   {
 9     iSum += pSrc[iInd-iSrcStride];//左列参考像素总和
10   }
11   for (iInd = 0;iInd < iHeight;iInd++)
12   {
13     iSum += pSrc[iInd*iSrcStride-1];//上方行参考像素总和
14   }
15 
16   pDcVal = (iSum + iWidth) / (iWidth + iHeight);//取平均值
17 
18   return pDcVal;
19 }

     其次是Planar模式,该模式定义在xPredIntraPlanar函数中。适用于像素值缓慢变化的区域,Planar使用水平和垂直方向的两个线性滤波器,并将二者的平均值作为当前像素的预测值。Planar能够使像素平缓变化,与其他模式相比能够提升视频的主管质量。

 1 Void TComPrediction::xPredIntraPlanar( const Pel* pSrc, Int srcStride, Pel* rpDst, Int dstStride, UInt width, UInt height )
 2 {
 3   assert(width <= height);
 4 
 5   Int leftColumn[MAX_CU_SIZE+1], topRow[MAX_CU_SIZE+1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
 6   UInt shift1Dhor = g_aucConvertToBit[ width ] + 2;
 7   UInt shift1Dver = g_aucConvertToBit[ height ] + 2;
 8 
 9   // Get left and above reference column and row
10   for(Int k=0;k<width+1;k++)//记录顶行数据
11   {
12     topRow[k] = pSrc[k-srcStride];
13   }
14   
15   for (Int k=0; k < height+1; k++)//记录左列数据
16   {
17     leftColumn[k] = pSrc[k*srcStride-1];
18   }
19 
20   // Prepare intermediate variables used in interpolation
21   Int bottomLeft = leftColumn[height];//左下角像素值
22   Int topRight   = topRow[width];//右上角像素值
23   /*计算底行的数据,方法是用左下角的像素减去顶行相应位置的像素得到底行。*/
24   for(Int k=0;k<width;k++)
25   {
26     bottomRow[k]  = bottomLeft - topRow[k];
27     topRow[k]     <<= shift1Dver;
28   }
29   /*计算右列的数据,方法是用右上角的像素减去左列相应位置的像素得到右列。*/
30   for(Int k=0;k<height;k++)
31   {
32     rightColumn[k]  = topRight - leftColumn[k];
33     leftColumn[k]   <<= shift1Dhor;
34   }
35 
36   const UInt topRowShift = 0;
37 
38   // Generate prediction signal
39   for (Int y=0;y<height;y++)
40   {
41     Int horPred = leftColumn[y] + width;
42     for (Int x=0;x<width;x++)
43     {
44       horPred += rightColumn[y];//竖直方向(x,y)预测值
45       topRow[x] += bottomRow[x];
46 
47       Int vertPred = ((topRow[x] + topRowShift)>>topRowShift);//水平方向(x,y)预测值
48       rpDst[y*dstStride+x] = ( horPred + vertPred ) >> (shift1Dhor+1);//预测像素是水平和垂直两个方向预测值得平均值
49     }
50   }
51 }

       最后是角度预测,mode=2~34时采用角度预测模式。实现的方式在xPredIntraAng中:

  1 Void TComPrediction::xPredIntraAng(       Int bitDepth,
  2                                     const Pel* pSrc,     Int srcStride,
  3                                           Pel* pTrueDst, Int dstStrideTrue,
  4                                           UInt uiWidth, UInt uiHeight, ChannelType channelType,
  5                                           UInt dirMode, const Bool bEnableEdgeFilters
  6                                   )
  7 {
  8   Int width=Int(uiWidth);
  9   Int height=Int(uiHeight);
 10 
 11   // Map the mode index to main prediction direction and angle
 12   assert( dirMode != PLANAR_IDX ); //no planar
 13   const Bool modeDC        = dirMode==DC_IDX;
 14 
 15   // Do the DC prediction
 16   if (modeDC)
 17   {
 18     const Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height);
 19 
 20     for (Int y=height;y>0;y--, pTrueDst+=dstStrideTrue)
 21     {
 22       for (Int x=0; x<width;) // width is always a multiple of 4.
 23       {
 24         pTrueDst[x++] = dcval;
 25       }
 26     }
 27   }
 28   else // Do angular predictions
 29   {
 30     const Bool       bIsModeVer         = (dirMode >= 18);
 31     const Int        intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX :  -((Int)dirMode - HOR_IDX);
 32     const Int        absAngMode         = abs(intraPredAngleMode);
 33     const Int        signAng            = intraPredAngleMode < 0 ? -1 : 1;
 34     const Bool       edgeFilter         = bEnableEdgeFilters && isLuma(channelType) && (width <= MAXIMUM_INTRA_FILTERED_WIDTH) && (height <= MAXIMUM_INTRA_FILTERED_HEIGHT);
 35 
 36     // Set bitshifts and scale the angle parameter to block size
 37     static const Int angTable[9]    = {0,    2,    5,   9,  13,  17,  21,  26,  32};
 38     static const Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle
 39     Int invAngle                    = invAngTable[absAngMode];
 40     Int absAng                      = angTable[absAngMode];
 41     Int intraPredAngle              = signAng * absAng;
 42 
 43     Pel* refMain;
 44     Pel* refSide;
 45 
 46     Pel  refAbove[2*MAX_CU_SIZE+1];
 47     Pel  refLeft[2*MAX_CU_SIZE+1];
 48 
 49     // Initialize the Main and Left reference array.
 50     if (intraPredAngle < 0)
 51     {
 52       const Int refMainOffsetPreScale = (bIsModeVer ? height : width ) - 1;
 53       const Int refMainOffset         = height - 1;
 54       for (Int x=0;x<width+1;x++)
 55       {
 56         refAbove[x+refMainOffset] = pSrc[x-srcStride-1];
 57       }
 58       for (Int y=0;y<height+1;y++)
 59       {
 60         refLeft[y+refMainOffset] = pSrc[(y-1)*srcStride-1];
 61       }
 62       refMain = (bIsModeVer ? refAbove : refLeft)  + refMainOffset;
 63       refSide = (bIsModeVer ? refLeft  : refAbove) + refMainOffset;
 64 
 65       // Extend the Main reference to the left.
 66       Int invAngleSum    = 128;       // rounding for (shift by 8)
 67       for (Int k=-1; k>(refMainOffsetPreScale+1)*intraPredAngle>>5; k--)
 68       {
 69         invAngleSum += invAngle;
 70         refMain[k] = refSide[invAngleSum>>8];
 71       }
 72     }
 73     else
 74     {
 75       for (Int x=0;x<2*width+1;x++)
 76       {
 77         refAbove[x] = pSrc[x-srcStride-1];
 78       }
 79       for (Int y=0;y<2*height+1;y++)
 80       {
 81         refLeft[y] = pSrc[(y-1)*srcStride-1];
 82       }
 83       refMain = bIsModeVer ? refAbove : refLeft ;
 84       refSide = bIsModeVer ? refLeft  : refAbove;
 85     }
 86 
 87     // swap width/height if we are doing a horizontal mode:
 88     Pel tempArray[MAX_CU_SIZE*MAX_CU_SIZE];
 89     const Int dstStride = bIsModeVer ? dstStrideTrue : MAX_CU_SIZE;
 90     Pel *pDst = bIsModeVer ? pTrueDst : tempArray;
 91     if (!bIsModeVer)
 92     {
 93       std::swap(width, height);
 94     }
 95 
 96     if (intraPredAngle == 0)  // pure vertical or pure horizontal
 97     {
 98       for (Int y=0;y<height;y++)
 99       {
100         for (Int x=0;x<width;x++)
101         {
102           pDst[y*dstStride+x] = refMain[x+1];
103         }
104       }
105 
106       if (edgeFilter)
107       {
108         for (Int y=0;y<height;y++)
109         {
110           pDst[y*dstStride] = Clip3 (0, ((1 << bitDepth) - 1), pDst[y*dstStride] + (( refSide[y+1] - refSide[0] ) >> 1) );
111         }
112       }
113     }
114     else
115     {
116       Pel *pDsty=pDst;
117 
118       for (Int y=0, deltaPos=intraPredAngle; y<height; y++, deltaPos+=intraPredAngle, pDsty+=dstStride)
119       {
120         const Int deltaInt   = deltaPos >> 5;
121         const Int deltaFract = deltaPos & (32 - 1);
122 
123         if (deltaFract)
124         {
125           // Do linear filtering
126           const Pel *pRM=refMain+deltaInt+1;
127           Int lastRefMainPel=*pRM++;
128           for (Int x=0;x<width;pRM++,x++)
129           {
130             Int thisRefMainPel=*pRM;
131             pDsty[x+0] = (Pel) ( ((32-deltaFract)*lastRefMainPel + deltaFract*thisRefMainPel +16) >> 5 );
132             lastRefMainPel=thisRefMainPel;
133           }
134         }
135         else
136         {
137           // Just copy the integer samples
138           for (Int x=0;x<width; x++)
139           {
140             pDsty[x] = refMain[x+deltaInt+1];
141           }
142         }
143       }
144     }
145 
146     // Flip the block if this is the horizontal mode
147     if (!bIsModeVer)
148     {
149       for (Int y=0; y<height; y++)
150       {
151         for (Int x=0; x<width; x++)
152         {
153           pTrueDst[x*dstStrideTrue] = pDst[x];
154         }
155         pTrueDst++;
156         pDst+=dstStride;
157       }
158     }
159   }
160 }

      帧内预测是在TComPrediction::predIntraAng(...)实现:

 1 Void TComPrediction::predIntraAng( const ComponentID compID, UInt uiDirMode, Pel* piOrg /* Will be null for decoding */, UInt uiOrgStride, Pel* piPred, UInt uiStride, TComTU &rTu, const Bool bUseFilteredPredSamples, const Bool bUseLosslessDPCM )
 2 {
 3   const ChannelType    channelType = toChannelType(compID);
 4   const TComRectangle &rect        = rTu.getRect(isLuma(compID) ? COMPONENT_Y : COMPONENT_Cb);
 5   const Int            iWidth      = rect.width;
 6   const Int            iHeight     = rect.height;
 7 
 8   assert( g_aucConvertToBit[ iWidth ] >= 0 ); //   4x  4
 9   assert( g_aucConvertToBit[ iWidth ] <= 5 ); // 128x128
10   //assert( iWidth == iHeight  );
11 
12         Pel *pDst = piPred;
13 
14   // get starting pixel in block
15   const Int sw = (2 * iWidth + 1);
16 
17   if ( bUseLosslessDPCM )//如果预测方式为垂直或水平,则bUseLosslessDPCM =1
18   {
19     const Pel *ptrSrc = getPredictorPtr( compID, false );
20     // Sample Adaptive intra-Prediction (SAP)
21     if (uiDirMode==HOR_IDX)//竖直方向预测
22     {
23       // left column filled with reference samples
24       // remaining columns filled with piOrg data (if available).
25       for(Int y=0; y<iHeight; y++)
26       {
27         piPred[y*uiStride+0] = ptrSrc[(y+1)*sw];
28       }
29       if (piOrg!=0)
30       {
31         piPred+=1; // miss off first column
32         for(Int y=0; y<iHeight; y++, piPred+=uiStride, piOrg+=uiOrgStride)
33         {
34           memcpy(piPred, piOrg, (iWidth-1)*sizeof(Pel));
35         }
36       }
37     }
38     else // VER_IDX  水平方向预测
39     {
40       // top row filled with reference samples
41       // remaining rows filled with piOrd data (if available)
42       for(Int x=0; x<iWidth; x++)
43       {
44         piPred[x] = ptrSrc[x+1];
45       }
46       if (piOrg!=0)
47       {
48         piPred+=uiStride; // miss off the first row
49         for(Int y=1; y<iHeight; y++, piPred+=uiStride, piOrg+=uiOrgStride)
50         {
51           memcpy(piPred, piOrg, iWidth*sizeof(Pel));
52         }
53       }
54     }
55   }
56   else //平面和角度预测
57   {
58     const Pel *ptrSrc = getPredictorPtr( compID, bUseFilteredPredSamples );
59 
60     if ( uiDirMode == PLANAR_IDX )
61     {
62       xPredIntraPlanar( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight );
63     }
64     else
65     {
66       // Create the prediction
67             TComDataCU *const pcCU              = rTu.getCU();
68       const UInt              uiAbsPartIdx      = rTu.GetAbsPartIdxTU();
69       const Bool              enableEdgeFilters = !(pcCU->isRDPCMEnabled(uiAbsPartIdx) && pcCU->getCUTransquantBypass(uiAbsPartIdx));
70 #if O0043_BEST_EFFORT_DECODING
71       const Int channelsBitDepthForPrediction = rTu.getCU()->getSlice()->getSPS()->getStreamBitDepth(channelType);
72 #else
73       const Int channelsBitDepthForPrediction = rTu.getCU()->getSlice()->getSPS()->getBitDepth(channelType);
74 #endif
75       xPredIntraAng( channelsBitDepthForPrediction, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, channelType, uiDirMode, enableEdgeFilters );
76 
77       if( uiDirMode == DC_IDX )
78       {
79         xDCPredFiltering( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, channelType );
80       }
81     }
82   }
83 
84 }

 

以上是关于HEVC2HM-16.7编码一个CU(帧内部分) 2.帧内预测各种模式实现的主要内容,如果未能解决你的问题,请参考以下文章

AVS3帧间预测

十五帧内编码:2预测当前块的预测模式

ffmpeg编码h264只包含I帧P帧的方法

十五帧内编码:1帧内编码的基本原理

H265

PCS2021:监控视频编码基于实例分割的背景参考帧生成