HEVC中一共定义了35中帧内编码预测模式,编号分别以0-34定义。其中模式0定义为平面模式(INTRA_PLANAR),模式1定义为均值模式(INTRA_DC),模式2~34定义为角度预测模式(INTRA_ANGULAR2~INTRA_ANGULAR34),分别代表了不同的角度。
最简单的Intra_DC模式,DC模式适用于大面积平摊区域,当前预测值可由其左侧和上方(不包含左上角,左下方和右上方)参考像素的平均值得到。该模式同角度预测模式实现在同一个函数Void TComPrediction::xPredIntraAng(...)中:
Void TComPrediction::xPredIntraAng( Int bitDepth,
const Pel* pSrc, Int srcStride,
Pel* pTrueDst, Int dstStrideTrue,
UInt uiWidth, UInt uiHeight, ChannelType channelType,
UInt dirMode, const Bool bEnableEdgeFilters
)
{
Int width=Int(uiWidth);
Int height=Int(uiHeight); // Map the mode index to main prediction direction and angle
assert( dirMode != PLANAR_IDX ); //no planar
const Bool modeDC = dirMode==DC_IDX; // Do the DC prediction
if (modeDC)
{
const Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height); for (Int y=height;y>;y--, pTrueDst+=dstStrideTrue)
{
for (Int x=; x<width;) // width is always a multiple of 4.
{
pTrueDst[x++] = dcval;
}
}
}
else // Do angular predictions
{
//........ }
}
在这个函数中可以看到,Intra_DC模式中所有预测块的像素值都是同一个值dcval,这个值是由一个函数predIntraGetPredValDC计算得到:
Pel TComPrediction::predIntraGetPredValDC( const Pel* pSrc, Int iSrcStride, UInt iWidth, UInt iHeight)
{
assert(iWidth > && iHeight > );
Int iInd, iSum = ;
Pel pDcVal; for (iInd = ;iInd < iWidth;iInd++)
{
iSum += pSrc[iInd-iSrcStride];//左列参考像素总和
}
for (iInd = ;iInd < iHeight;iInd++)
{
iSum += pSrc[iInd*iSrcStride-];//上方行参考像素总和
} pDcVal = (iSum + iWidth) / (iWidth + iHeight);//取平均值 return pDcVal;
}
其次是Planar模式,该模式定义在xPredIntraPlanar函数中。适用于像素值缓慢变化的区域,Planar使用水平和垂直方向的两个线性滤波器,并将二者的平均值作为当前像素的预测值。Planar能够使像素平缓变化,与其他模式相比能够提升视频的主管质量。
Void TComPrediction::xPredIntraPlanar( const Pel* pSrc, Int srcStride, Pel* rpDst, Int dstStride, UInt width, UInt height )
{
assert(width <= height); Int leftColumn[MAX_CU_SIZE+], topRow[MAX_CU_SIZE+], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
UInt shift1Dhor = g_aucConvertToBit[ width ] + ;
UInt shift1Dver = g_aucConvertToBit[ height ] + ; // Get left and above reference column and row
for(Int k=;k<width+;k++)//记录顶行数据
{
topRow[k] = pSrc[k-srcStride];
} for (Int k=; k < height+; k++)//记录左列数据
{
leftColumn[k] = pSrc[k*srcStride-];
} // Prepare intermediate variables used in interpolation
Int bottomLeft = leftColumn[height];//左下角像素值
Int topRight = topRow[width];//右上角像素值
/*计算底行的数据,方法是用左下角的像素减去顶行相应位置的像素得到底行。*/
for(Int k=;k<width;k++)
{
bottomRow[k] = bottomLeft - topRow[k];
topRow[k] <<= shift1Dver;
}
/*计算右列的数据,方法是用右上角的像素减去左列相应位置的像素得到右列。*/
for(Int k=;k<height;k++)
{
rightColumn[k] = topRight - leftColumn[k];
leftColumn[k] <<= shift1Dhor;
} const UInt topRowShift = ; // Generate prediction signal
for (Int y=;y<height;y++)
{
Int horPred = leftColumn[y] + width;
for (Int x=;x<width;x++)
{
horPred += rightColumn[y];//竖直方向(x,y)预测值
topRow[x] += bottomRow[x]; Int vertPred = ((topRow[x] + topRowShift)>>topRowShift);//水平方向(x,y)预测值
rpDst[y*dstStride+x] = ( horPred + vertPred ) >> (shift1Dhor+);//预测像素是水平和垂直两个方向预测值得平均值
}
}
}
最后是角度预测,mode=2~34时采用角度预测模式。实现的方式在xPredIntraAng中:
Void TComPrediction::xPredIntraAng( Int bitDepth,
const Pel* pSrc, Int srcStride,
Pel* pTrueDst, Int dstStrideTrue,
UInt uiWidth, UInt uiHeight, ChannelType channelType,
UInt dirMode, const Bool bEnableEdgeFilters
)
{
Int width=Int(uiWidth);
Int height=Int(uiHeight); // Map the mode index to main prediction direction and angle
assert( dirMode != PLANAR_IDX ); //no planar
const Bool modeDC = dirMode==DC_IDX; // Do the DC prediction
if (modeDC)
{
const Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height); for (Int y=height;y>;y--, pTrueDst+=dstStrideTrue)
{
for (Int x=; x<width;) // width is always a multiple of 4.
{
pTrueDst[x++] = dcval;
}
}
}
else // Do angular predictions
{
const Bool bIsModeVer = (dirMode >= );
const Int intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX : -((Int)dirMode - HOR_IDX);
const Int absAngMode = abs(intraPredAngleMode);
const Int signAng = intraPredAngleMode < ? - : ;
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[] = {, , , , , , , , };
static const Int invAngTable[] = {, , , , , , , , }; // (256 * 32) / Angle
Int invAngle = invAngTable[absAngMode];
Int absAng = angTable[absAngMode];
Int intraPredAngle = signAng * absAng; Pel* refMain;
Pel* refSide; Pel refAbove[*MAX_CU_SIZE+];
Pel refLeft[*MAX_CU_SIZE+]; // Initialize the Main and Left reference array.
if (intraPredAngle < )
{
const Int refMainOffsetPreScale = (bIsModeVer ? height : width ) - ;
const Int refMainOffset = height - ;
for (Int x=;x<width+;x++)
{
refAbove[x+refMainOffset] = pSrc[x-srcStride-];
}
for (Int y=;y<height+;y++)
{
refLeft[y+refMainOffset] = pSrc[(y-)*srcStride-];
}
refMain = (bIsModeVer ? refAbove : refLeft) + refMainOffset;
refSide = (bIsModeVer ? refLeft : refAbove) + refMainOffset; // Extend the Main reference to the left.
Int invAngleSum = ; // rounding for (shift by 8)
for (Int k=-; k>(refMainOffsetPreScale+)*intraPredAngle>>; k--)
{
invAngleSum += invAngle;
refMain[k] = refSide[invAngleSum>>];
}
}
else
{
for (Int x=;x<*width+;x++)
{
refAbove[x] = pSrc[x-srcStride-];
}
for (Int y=;y<*height+;y++)
{
refLeft[y] = pSrc[(y-)*srcStride-];
}
refMain = bIsModeVer ? refAbove : refLeft ;
refSide = bIsModeVer ? refLeft : refAbove;
} // swap width/height if we are doing a horizontal mode:
Pel tempArray[MAX_CU_SIZE*MAX_CU_SIZE];
const Int dstStride = bIsModeVer ? dstStrideTrue : MAX_CU_SIZE;
Pel *pDst = bIsModeVer ? pTrueDst : tempArray;
if (!bIsModeVer)
{
std::swap(width, height);
} if (intraPredAngle == ) // pure vertical or pure horizontal
{
for (Int y=;y<height;y++)
{
for (Int x=;x<width;x++)
{
pDst[y*dstStride+x] = refMain[x+];
}
} if (edgeFilter)
{
for (Int y=;y<height;y++)
{
pDst[y*dstStride] = Clip3 (, (( << bitDepth) - ), pDst[y*dstStride] + (( refSide[y+] - refSide[] ) >> ) );
}
}
}
else
{
Pel *pDsty=pDst; for (Int y=, deltaPos=intraPredAngle; y<height; y++, deltaPos+=intraPredAngle, pDsty+=dstStride)
{
const Int deltaInt = deltaPos >> ;
const Int deltaFract = deltaPos & ( - ); if (deltaFract)
{
// Do linear filtering
const Pel *pRM=refMain+deltaInt+;
Int lastRefMainPel=*pRM++;
for (Int x=;x<width;pRM++,x++)
{
Int thisRefMainPel=*pRM;
pDsty[x+] = (Pel) ( ((-deltaFract)*lastRefMainPel + deltaFract*thisRefMainPel +) >> );
lastRefMainPel=thisRefMainPel;
}
}
else
{
// Just copy the integer samples
for (Int x=;x<width; x++)
{
pDsty[x] = refMain[x+deltaInt+];
}
}
}
} // Flip the block if this is the horizontal mode
if (!bIsModeVer)
{
for (Int y=; y<height; y++)
{
for (Int x=; x<width; x++)
{
pTrueDst[x*dstStrideTrue] = pDst[x];
}
pTrueDst++;
pDst+=dstStride;
}
}
}
}
帧内预测是在TComPrediction::predIntraAng(...)实现:
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 )
{
const ChannelType channelType = toChannelType(compID);
const TComRectangle &rect = rTu.getRect(isLuma(compID) ? COMPONENT_Y : COMPONENT_Cb);
const Int iWidth = rect.width;
const Int iHeight = rect.height; assert( g_aucConvertToBit[ iWidth ] >= ); // 4x 4
assert( g_aucConvertToBit[ iWidth ] <= ); // 128x128
//assert( iWidth == iHeight ); Pel *pDst = piPred; // get starting pixel in block
const Int sw = ( * iWidth + ); if ( bUseLosslessDPCM )//如果预测方式为垂直或水平,则bUseLosslessDPCM =1
{
const Pel *ptrSrc = getPredictorPtr( compID, false );
// Sample Adaptive intra-Prediction (SAP)
if (uiDirMode==HOR_IDX)//竖直方向预测
{
// left column filled with reference samples
// remaining columns filled with piOrg data (if available).
for(Int y=; y<iHeight; y++)
{
piPred[y*uiStride+] = ptrSrc[(y+)*sw];
}
if (piOrg!=)
{
piPred+=; // miss off first column
for(Int y=; y<iHeight; y++, piPred+=uiStride, piOrg+=uiOrgStride)
{
memcpy(piPred, piOrg, (iWidth-)*sizeof(Pel));
}
}
}
else // VER_IDX 水平方向预测
{
// top row filled with reference samples
// remaining rows filled with piOrd data (if available)
for(Int x=; x<iWidth; x++)
{
piPred[x] = ptrSrc[x+];
}
if (piOrg!=)
{
piPred+=uiStride; // miss off the first row
for(Int y=; y<iHeight; y++, piPred+=uiStride, piOrg+=uiOrgStride)
{
memcpy(piPred, piOrg, iWidth*sizeof(Pel));
}
}
}
}
else //平面和角度预测
{
const Pel *ptrSrc = getPredictorPtr( compID, bUseFilteredPredSamples ); if ( uiDirMode == PLANAR_IDX )
{
xPredIntraPlanar( ptrSrc+sw+, sw, pDst, uiStride, iWidth, iHeight );
}
else
{
// Create the prediction
TComDataCU *const pcCU = rTu.getCU();
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
const Bool enableEdgeFilters = !(pcCU->isRDPCMEnabled(uiAbsPartIdx) && pcCU->getCUTransquantBypass(uiAbsPartIdx));
#if O0043_BEST_EFFORT_DECODING
const Int channelsBitDepthForPrediction = rTu.getCU()->getSlice()->getSPS()->getStreamBitDepth(channelType);
#else
const Int channelsBitDepthForPrediction = rTu.getCU()->getSlice()->getSPS()->getBitDepth(channelType);
#endif
xPredIntraAng( channelsBitDepthForPrediction, ptrSrc+sw+, sw, pDst, uiStride, iWidth, iHeight, channelType, uiDirMode, enableEdgeFilters ); if( uiDirMode == DC_IDX )
{
xDCPredFiltering( ptrSrc+sw+, sw, pDst, uiStride, iWidth, iHeight, channelType );
}
}
} }