predIntraAng()
在模式决策过程中,此函数被循环调用,其主要步骤为:
通过上层获取当前要预测的方向,若是Planar模式则进入函数xPredIntraPlanar()
,若是DC则进入函数xPredIntraDc()
, 若为角度模式则进入xPredIntraAng()
。
同时,判断是否采用PDPC,并采取相关措施。
void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu, const bool useFilteredPredSamples )
{
const ComponentID compID = MAP_CHROMA( compId );
const ChannelType channelType = toChannelType( compID );
const Int iWidth = piPred.width;
const Int iHeight = piPred.height;
const UInt uiDirMode = PU::getFinalIntraMode( pu, channelType );
CHECK( g_aucLog2[iWidth] < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );
CHECK( g_aucLog2[iWidth] > 7, "Size not allowed" );
CHECK( iWidth != iHeight && !pu.cs->pcv->rectCUs, "Rectangular block are only allowed with QTBT" );
const Int srcStride = ( iWidth + iHeight + 1 );
#if HEVC_USE_HOR_VER_PREDFILTERING
const Bool enableEdgeFilters = !(CU::isRDPCMEnabled( *pu.cu ) && pu.cu->transQuantBypass);
#endif
//参考块左上角首地址
Pel *ptrSrc = getPredictorPtr( compID, useFilteredPredSamples );
//************************************************ 判断是否使用PDPC
#if JEM_TOOLS
#if HM_PDPC_AS_IN_JEM //PM: reproducing JEM behavior (however, is boundary condition for QTBT off case really useful?)
bool pdpcCondition = (pu.cs->sps->getSpsNext().isIntraPDPC() && pu.cu->pdpc && (pu.cs->pcv->rectCUs || (pu.cu->lumaPos().x && pu.cu->lumaPos().y))) || (pu.cs->sps->getSpsNext().isPlanarPDPC() && (uiDirMode == PLANAR_IDX));
#else
bool pdpcCondition = ((pu.cs->sps->getSpsNext().isIntraPDPC()) && pu.cu->pdpc) || (pu.cs->sps->getSpsNext().isPlanarPDPC() && (uiDirMode == PLANAR_IDX));
#endif
//************************************************ 使用PDPC
if( pdpcCondition )
{
int idxW = std::min( 4, (int)g_aucLog2[iWidth] - 1 );
int idxH = std::min( 4, (int)g_aucLog2[iHeight] - 1 );
#if HEVC_USE_PART_SIZE
if( !pu.cs->pcv->only2Nx2N )
{
CHECK( idxW != idxH, "Non-square partitions not supported by this config" );
if( pu.cu->partSize == SIZE_NxN && idxW == 1 ) { idxW = idxH = 0; }
}
#endif
const int *pPdpcParWidth;
const int *pPdpcParHeight;
//获取参数
if( pu.cs->sps->getSpsNext().isPlanarPDPC() )
{
pPdpcParWidth = g_pdpcParam[idxW];
pPdpcParHeight = g_pdpcParam[idxH];
}
else
{
pPdpcParWidth = g_pdpc_pred_param[idxW][g_intraMode65to33AngMapping[uiDirMode]];
pPdpcParHeight = g_pdpc_pred_param[idxH][g_intraMode65to33AngMapping[uiDirMode]];
}
const int *pPdpcParMain = (iWidth < iHeight) ? pPdpcParHeight : pPdpcParWidth;
const int srcStride = iWidth + iHeight + 1;
const int doubleSize = iWidth + iHeight;
Pel* piRefVector = m_piTempRef + doubleSize;
Pel* piLowpRefer = m_piFiltRef + doubleSize;
//未滤波行/列
for( int j = 0; j <= doubleSize; j++ ) { piRefVector[ j] = ptrSrc[j]; }
for( int i = 1; i <= doubleSize; i++ ) { piRefVector[-i] = ptrSrc[i*srcStride]; }
if( pPdpcParMain[5] != 0 )
{
//参考像素的滤波
xReferenceFilter( doubleSize, pPdpcParMain[4], pPdpcParMain[5], piRefVector, piLowpRefer );
// copy filtered ref. samples back to ref. buffer
//滤波后的行/列
for( int j = 0; j <= doubleSize; j++ ) { ptrSrc[j] = piLowpRefer[ j]; }
for( int i = 1; i <= doubleSize; i++ ) { ptrSrc[i*srcStride] = piLowpRefer[-i]; }
}
const ClpRng& clpRng( pu.cu->cs->slice->clpRng(compID) );
switch( uiDirMode )
{
//HEVC的planar预测
case( PLANAR_IDX ): xPredIntraPlanar( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, *pu.cs->sps ); break;
// including DCPredFiltering
case( DC_IDX ): xPredIntraDc ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType, false ); break;
#if HEVC_USE_HOR_VER_PREDFILTERING
//获取由预测模式索引标识的角度模式预测方向的预测样本,若预测像素投影在参考像素之间,需要进行线性插值
default: xPredIntraAng ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType,
uiDirMode, clpRng, enableEdgeFilters, *pu.cs->sps, false ); break;
#else
default: xPredIntraAng ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType,
uiDirMode, clpRng, *pu.cs->sps, false ); break;
#endif
}
//************************************************ PDPC相关步骤
if( pPdpcParMain[5] != 0 )
{
//PDPC所需未滤波数据
// copy unfiltered ref. samples back to ref. buffer for weighted prediction
for( int j = 0; j <= doubleSize; j++ ) { ptrSrc[j] = piRefVector[ j]; }
for( int i = 1; i <= doubleSize; i++ ) { ptrSrc[i*srcStride] = piRefVector[-i]; }
}
int scale = (g_aucLog2[iWidth] + g_aucLog2[iHeight] < 10) ? 0 : 1;
int parShift = 6; //normalization factor
int parScale = 1 << parShift;
int parOffset = 1 << (parShift - 1);
//PDPC公式
for( int y = 0; y < iHeight; y++ )
{
int shiftRow = y >> scale;
int coeff_Top = pPdpcParHeight[2] >> shiftRow;
int coeff_offset = pPdpcParHeight[3] >> shiftRow;
for( int x = 0; x < iWidth; x++ )
{
int shiftCol = x >> scale;
int coeff_Left = pPdpcParWidth[0] >> shiftCol;
int coeff_TopLeft = (pPdpcParWidth[1] >> shiftCol) + coeff_offset;
int coeff_Cur = parScale - coeff_Left - coeff_Top + coeff_TopLeft;
int sampleVal = (coeff_Left* piRefVector[-y - 1] + coeff_Top * piRefVector[x + 1] - coeff_TopLeft * piRefVector[0] + coeff_Cur * piPred.at( x, y ) + parOffset) >> parShift;
piPred.at( x, y ) = ClipPel( sampleVal, clpRng );
}
}
}
else
//************************************************ 不用PDPC
#endif
{
switch( uiDirMode )
{
case( DC_IDX ): xPredIntraDc ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType ); break; // including DCPredFiltering
case( PLANAR_IDX ): xPredIntraPlanar( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, *pu.cs->sps ); break;
#if HEVC_USE_HOR_VER_PREDFILTERING
default: xPredIntraAng ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType, uiDirMode,
pu.cs->slice->clpRng( compID ), enableEdgeFilters, *pu.cs->sps ); break;
#else
default: xPredIntraAng ( CPelBuf( ptrSrc, srcStride, srcStride ), piPred, channelType, uiDirMode,
pu.cs->slice->clpRng( compID ), *pu.cs->sps ); break;
#endif
}
}
}