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 }