VTM7.0中,帧内预测的角度预测的入口函数为predIntraAng函数,该函数主要是用于进行传统的帧内预测(Planar、DC、角度预测),然后对Planar和DC模式使用PDPC(其余角度模式的PDPC在xPredIntraAng函数中进行)
void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu)
{
const ComponentID compID = MAP_CHROMA( compId );
const ChannelType channelType = toChannelType( compID );
const int iWidth = piPred.width;
const int iHeight = piPred.height;
#if JVET_P0641_REMOVE_2xN_CHROMA_INTRA
CHECK(iWidth == 2, "Width of 2 is not supported");
#endif
#if JVET_P0059_CHROMA_BDPCM
//获取预测模式
const uint32_t uiDirMode = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : !isLuma(compId) && pu.cu->bdpcmModeChroma ? BDPCM_IDX : PU::getFinalIntraMode(pu, channelType);
#else
const uint32_t uiDirMode = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : PU::getFinalIntraMode( pu, channelType );
#endif
CHECK( floorLog2(iWidth) < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );
CHECK( floorLog2(iWidth) > 7, "Size not allowed" );
const int srcStride = m_refBufferStride[compID];
const int srcHStride = 2;
const CPelBuf & srcBuf = CPelBuf(getPredictorPtr(compID), srcStride, srcHStride);//参考像素
const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));
switch (uiDirMode)
{
case(PLANAR_IDX): xPredIntraPlanar(srcBuf, piPred); break;//Planar模式
case(DC_IDX): xPredIntraDc(srcBuf, piPred, channelType, false); break;//DC模式
#if JVET_P0059_CHROMA_BDPCM
case(BDPCM_IDX): xPredIntraBDPCM(srcBuf, piPred, isLuma(compID) ? pu.cu->bdpcmMode : pu.cu->bdpcmModeChroma, clpRng); break;
#else
case(BDPCM_IDX): xPredIntraBDPCM(srcBuf, piPred, pu.cu->bdpcmMode, clpRng); break;
#endif
default: xPredIntraAng(srcBuf, piPred, channelType, clpRng); break;
}
//PDPC,这里进对Planar和DC做,其余角度模式的PDPC在xPredIntraAng函数中进行
if (m_ipaParam.applyPDPC)
{
PelBuf dstBuf = piPred;
const int scale = ((floorLog2(iWidth) - 2 + floorLog2(iHeight) - 2 + 2) >> 2);
CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");
if (uiDirMode == PLANAR_IDX || uiDirMode == DC_IDX)//PLANAR和DC的PDPC
{
for (int y = 0; y < iHeight; y++)
{
const int wT = 32 >> std::min(31, ((y << 1) >> scale)); //32是2的5次方。即超过5就会变为0,不再受上影响
const Pel left = srcBuf.at(y + 1, 1);
for (int x = 0; x < iWidth; x++)
{
const int wL = 32 >> std::min(31, ((x << 1) >> scale));
const Pel top = srcBuf.at(x + 1, 0);
const Pel val = dstBuf.at(x, y);
dstBuf.at(x, y) = val + ((wL * (left - val) + wT * (top - val) + 32) >> 6);
}
}
}
}
}
xPredIntraAng函数主要进行传统的角度预测,并对预测像素进行平滑滤波或者插值处理(和模式有关)。
将角度预测模式分为以下:
如果角度模式属于A,不进行滤波处理,直接将参考像素作为预测像素。
如果角度模式属于B,使用[1, 2, 1]滤波器对参考像素进行滤波,直接将滤波后的值作为预测像素不进行插值处理。
如果角度模式属于C,对落到非整数位置的参考像素进行插值处理(不再进行滤波)。
获得预测像素之后,在还需要对以下几种模式进行PDPC操作:
垂直模式(模式50),水平模式(模式18),左下角对角线模式和与其相邻的8个模式(模式2~10),右上角对角线模式和与其相邻的8个模式(模式58~66)。
/** Function for deriving the simplified angular intra predictions.
*
* This function derives the prediction samples for the angular mode based on the prediction direction indicated by
* the prediction mode index. The prediction direction is given by the displacement of the bottom row of the block and
* the reference row above the block in the case of vertical prediction or displacement of the rightmost column
* of the block and reference column left from the block in the case of the horizontal prediction. The displacement
* is signalled at 1/32 pixel accuracy. When projection of the predicted pixel falls inbetween reference samples,
* the predicted value for the pixel is linearly interpolated from the reference samples. All reference samples are taken
* from the extended main reference.
*/
//NOTE: Bit-Limit - 25-bit source
// pSrc参考像素 pDsit预测像素
void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const ClpRng& clpRng)
{
int width =int(pDst.width);
int height=int(pDst.height);
const bool bIsModeVer = m_ipaParam.isModeVer;//predMode >= DIA_IDX
const int multiRefIdx = m_ipaParam.multiRefIndex;//多参考行索引
const int intraPredAngle = m_ipaParam.intraPredAngle;//偏移值
const int invAngle = m_ipaParam.invAngle;
Pel* refMain;
Pel* refSide;
Pel refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//上侧参考像素
Pel refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//左侧参考像素
// Initialize the Main and Left reference array.
// 初始化参考像素
// 偏移值offset小于0,即模式为19~49
// 对于垂直类模式(34~49),需要将左侧参考行映射到上方参考行
// 对于水平类模式(18~33),需要将上方参考行映射到左侧参考行
if (intraPredAngle < 0)
{
for (int x = 0; x <= width + 1 + multiRefIdx; x++)
{
refAbove[x + height] = pSrc.at(x, 0);
}
for (int y = 0; y <= height + 1 + multiRefIdx; y++)
{
refLeft[y + width] = pSrc.at(y, 1);
}
// 如果是垂直类模式(34~49),则主要侧为正上方参考行,次要侧为正左侧参考行
// 如果是水平类模式(19~33),则主要侧为正左侧参考行,次要侧为正上方参考行
refMain = bIsModeVer ? refAbove + height : refLeft + width;//主要侧
refSide = bIsModeVer ? refLeft + width : refAbove + height;//次要侧
// Extend the Main reference to the left.
// 将次要侧参考行投影到主要侧参考行
int sizeSide = bIsModeVer ? height : width;
for (int k = -sizeSide; k <= -1; k++)
{
refMain[k] = refSide[std::min((-k * invAngle + 256) >> 9, sizeSide)];
}
}
else// 偏移值大于等于0,只需要用到上方参考像素或者左侧参考像素
{
for (int x = 0; x <= m_topRefLength + multiRefIdx; x++)
{
refAbove[x] = pSrc.at(x, 0);
}
for (int y = 0; y <= m_leftRefLength + multiRefIdx; y++)
{
refLeft[y] = pSrc.at(y, 1);
}
// 垂直类模式,主参考行为上方参考行
// 水平类模式,主参考行为左侧参考行
refMain = bIsModeVer ? refAbove : refLeft;
refSide = bIsModeVer ? refLeft : refAbove;
// Extend main reference to right using replication
// 使用复制将主参考行向右扩展,即填充Segment A和Segment F
const int log2Ratio = floorLog2(width) - floorLog2(height);
const int s = std::max(0, bIsModeVer ? log2Ratio : -log2Ratio);
const int maxIndex = (multiRefIdx << s) + 2;
const int refLength = bIsModeVer ? m_topRefLength : m_leftRefLength;
const Pel val = refMain[refLength + multiRefIdx];
for (int z = 1; z <= maxIndex; z++)
{
refMain[refLength + multiRefIdx + z] = val;
}
}
// swap width/height if we are doing a horizontal mode:
// 如果是水平类模式则交换width和height
if (!bIsModeVer)
{
std::swap(width, height);
}
Pel tempArray[MAX_CU_SIZE * MAX_CU_SIZE];
const int dstStride = bIsModeVer ? pDst.stride : width;
Pel * pDstBuf = bIsModeVer ? pDst.buf : tempArray;
// compensate for line offset in reference line buffers
// 补充多参考行中的偏移
refMain += multiRefIdx;
refSide += multiRefIdx;
Pel *pDsty = pDstBuf;
if( intraPredAngle == 0 ) // pure vertical or pure horizontal 水平模式和垂直模式(18和50)
{
for( int y = 0; y < height; y++ )
{
for( int x = 0; x < width; x++ )
{
pDsty[x] = refMain[x + 1];//直接投影
}
//使用PDPC技术
if (m_ipaParam.applyPDPC)
{
const int scale = (floorLog2(width) + floorLog2(height) - 2) >> 2;
const Pel topLeft = refMain[0];
const Pel left = refSide[1 + y];
for (int x = 0; x < std::min(3 << scale, width); x++)
{
const int wL = 32 >> (2 * x >> scale);
const Pel val = pDsty[x];
pDsty[x] = ClipPel(val + ((wL * (left - topLeft) + 32) >> 6), clpRng);
}
}
pDsty += dstStride;
}
}
else//对于其余模式
{
for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y> 5;
const int deltaFract = deltaPos & 31;
//对于除水平、垂直、模式2、34、66之外的模式需进行使用高斯插值滤波器进行插值处理
if ( !isIntegerSlope( abs(intraPredAngle) ) )
{
if( isLuma(channelType) )//亮度块
{
const bool useCubicFilter = !m_ipaParam.interpolationFlag;
#if JVET_P0599_INTRA_SMOOTHING_INTERP_FILT
const TFilterCoeff intraSmoothingFilter[4] = {TFilterCoeff(16 - (deltaFract >> 1)), TFilterCoeff(32 - (deltaFract >> 1)), TFilterCoeff(16 + (deltaFract >> 1)), TFilterCoeff(deltaFract >> 1)};
const TFilterCoeff* const f = (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : intraSmoothingFilter;
#else //!JVET_P0599_INTRA_SMOOTHING_INTERP_FILT
const TFilterCoeff *const f =
(useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : g_intraGaussFilter[deltaFract];
#endif //JVET_P0599_INTRA_SMOOTHING_INTERP_FILT
for (int x = 0; x < width; x++)
{
Pel p[4];
p[0] = refMain[deltaInt + x];
p[1] = refMain[deltaInt + x + 1];
p[2] = refMain[deltaInt + x + 2];
p[3] = refMain[deltaInt + x + 3];
Pel val = (f[0] * p[0] + f[1] * p[1] + f[2] * p[2] + f[3] * p[3] + 32) >> 6;
pDsty[x] = ClipPel(val, clpRng); // always clip even though not always needed
}
}
else //色度块
{
// Do linear filtering
// 线性滤波
for (int x = 0; x < width; x++)
{
Pel p[2];
p[0] = refMain[deltaInt + x + 1];
p[1] = refMain[deltaInt + x + 2];
pDsty[x] = p[0] + ((deltaFract * (p[1] - p[0]) + 16) >> 5);
}
}
}//if ( !isIntegerSlope( abs(intraPredAngle) ) )
else
{
// 对于模式2, DIA_IDX, VDIA_IDX,直接将滤波后的参考像素作为预测像素
// Just copy the integer samples
for( int x = 0; x < width; x++ )
{
pDsty[x] = refMain[x + deltaInt + 1];
}
}
if (m_ipaParam.applyPDPC)//使用PDPC技术
{
const int scale = m_ipaParam.angularScale;
int invAngleSum = 256;
for (int x = 0; x < std::min(3 << scale, width); x++)
{
invAngleSum += invAngle;
int wL = 32 >> (2 * x >> scale);
Pel left = refSide[y + (invAngleSum >> 9) + 1];
pDsty[x] = pDsty[x] + ((wL * (left - pDsty[x]) + 32) >> 6);
}
}
}
}
// Flip the block if this is the horizontal mode
// 如果这是水平模式,翻转块
if( !bIsModeVer )
{
for( int y = 0; y < height; y++ )
{
for( int x = 0; x < width; x++ )
{
pDst.at( y, x ) = pDstBuf[x];
}
pDstBuf += dstStride;
}
}
}