之前学习了Cross-Component Prediction的理论,由于需要,先对其代码实现进行学习。
Cross-Component Prediction(CCP)被HEVC RExt采纳,主要用于帧内/帧间预测中消除颜色组件间的相关性,提高编码效率。随着HM16的发布,HEVC RExt分支版本并入了主线软件,其中包含了CCP。本文参考的代码是HM16.6。
思路关键点
HM16.6中,CCP主要用于帧内预测,是在TU块编码TEncSearch::xIntraCodingTUBlock函数中进行的。在完成了帧内预测得到残差信息后,根据情况进行α的计算、色度残差的预测和重构。
其中α的计算公式为α=∑(△x△y)/∑(△x)²。
cfg配置参数:
在cfg文件里RExt配置中,可以看到CCP参数
TU块中的CCP实现:
在TEncSearch::xIntraCodingTUBlock中,使用CCP预测和重构色度残差。
1、预测
在获取了残差信息后,进行了色度残差的预测,然后变换/量化。
当bUseCrossCPrediction为真时,执行xCalcCrossComponentPredictionAlpha计算α,如果等于0直接返回,否则执行TComTrQuant::crossComponentPrediction,使用重构亮度残差预测色度残差。
当bUseCrossCPrediction为假时,如果不使用重构残差进行预测,执行xStoreCrossComponentPredictionResult存储原始亮度残差。
if (pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
if (bUseCrossCPrediction)
{
if (xCalcCrossComponentPredictionAlpha( rTu, compID, lumaResidualForEstimate, piResi, uiWidth, uiHeight, MAX_CU_SIZE, uiStride ) == 0) //计算α
{
return;
}
TComTrQuant::crossComponentPrediction ( rTu, compID, reconstructedLumaResidual, piResi, piResi, uiWidth, uiHeight, MAX_CU_SIZE, uiStride, uiStride, false ); //使用重构亮度残差进行CCP预测
}
else if (isLuma(compID) && !bUseReconstructedResidualForEstimate)
{
xStoreCrossComponentPredictionResult( encoderLumaResidual, piResi, rTu, 0, 0, MAX_CU_SIZE, uiStride ); //存储原始亮度残差
}
2、重构
在反变换后,进行色度残差的重构。
当bUseCrossCPrediction为真时,TComTrQuant::crossComponentPrediction,使用重构亮度残差辅助重构色度残差。
当bUseCrossCPrediction为假时,执行xStoreCrossComponentPredictionResult。
Pel* pPred = piPred;
Pel* pResi = piResi;
Pel* pReco = piReco;
Pel* pRecQt = piRecQt;
Pel* pRecIPred = piRecIPred;
if (pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
if (bUseCrossCPrediction)
{
TComTrQuant::crossComponentPrediction( rTu, compID, reconstructedLumaResidual, piResi, piResi, uiWidth, uiHeight, MAX_CU_SIZE, uiStride, uiStride, true ); //进行色度残差重构
}
else if (isLuma(compID))
{
xStoreCrossComponentPredictionResult( reconstructedLumaResidual, piResi, rTu, 0, 0, MAX_CU_SIZE, uiStride ); //存储重构亮度残差。
}
}
3、调用的重要函数
(1)TEncSearch::xCalcCrossComponentPredictionAlpha代码如下,主要完成的是α=∑(△x△y)/∑(△x)²的计算。
Char
TEncSearch::xCalcCrossComponentPredictionAlpha( TComTU &rTu,
const ComponentID compID,
const Pel* piResiL,
const Pel* piResiC,
const Int width,
const Int height,
const Int strideL,
const Int strideC )
{
const Pel *pResiL = piResiL; //初始化
const Pel *pResiC = piResiC;
TComDataCU *pCU = rTu.getCU();
const Int absPartIdx = rTu.GetAbsPartIdxTU( compID );
const Int diffBitDepth = pCU->getSlice()->getSPS()->getDifferentialLumaChromaBitDepth();
Char alpha = 0;
Int SSxy = 0;
Int SSxx = 0;
for( UInt uiY = 0; uiY < height; uiY++ )
{
for( UInt uiX = 0; uiX < width; uiX++ )
{
const Pel scaledResiL = rightShift( pResiL[ uiX ], diffBitDepth ); //处理亮度和色度组件之间的bit深度差异,调整亮度残差
SSxy += ( scaledResiL * pResiC[ uiX ] ); //计算亮度残差与色度残差的乘积和SSxy
SSxx += ( scaledResiL * scaledResiL ); //计算亮度残差的平方SSxx
}
pResiL += strideL;
pResiC += strideC;
}
if( SSxx != 0 ) //当亮度残差的平方不为0时,计算α=SSxy/SSxx并量化
{
Double dAlpha = SSxy / Double( SSxx );
alpha = Char(Clip3<Int>(-16, 16, (Int)(dAlpha * 16))); //限定范围[-16,16]
static const Char alphaQuant[17] = {0, 1, 1, 2, 2, 2, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8};
alpha = (alpha < 0) ? -alphaQuant[Int(-alpha)] : alphaQuant[Int(alpha)]; //对α进行量化
}
pCU->setCrossComponentPredictionAlphaPartRange( alpha, compID, absPartIdx, rTu.GetAbsPartIdxNumParts( compID ) );
return alpha;
}
(2)TComTrQuant::crossComponentPrediction代码如下,主要进行的是色度残差的预测,定义了正向的预测和反向的重构:预测△y'=△y-(α*△x),重构△y'=△y+(α*△x)。
Void TComTrQuant::crossComponentPrediction( TComTU & rTu,
const ComponentID compID,
const Pel * piResiL,
const Pel * piResiC,
Pel * piResiT,
const Int width,
const Int height,
const Int strideL,
const Int strideC,
const Int strideT,
const Bool reverse )
{
const Pel *pResiL = piResiL; //初始化
const Pel *pResiC = piResiC;
Pel *pResiT = piResiT;
TComDataCU *pCU = rTu.getCU();
const Int alpha = pCU->getCrossComponentPredictionAlpha( rTu.GetAbsPartIdxTU( compID ), compID );
const Int diffBitDepth = pCU->getSlice()->getSPS()->getDifferentialLumaChromaBitDepth();
for( Int y = 0; y < height; y++ )
{
if (reverse) //reverse为ture时,进行重构
{
// A constraint is to be added to the HEVC Standard to limit the size of pResiL and pResiC at this point.
// The likely form of the constraint is to either restrict the values to CoeffMin to CoeffMax,
// or to be representable in a bitDepthY+4 or bitDepthC+4 signed integer.
// The result of the constraint is that for 8/10/12bit profiles, the input values
// can be represented within a 16-bit Pel-type.
#if RExt__HIGH_BIT_DEPTH_SUPPORT 高比特深度下
for( Int x = 0; x < width; x++ )
{
pResiT[x] = pResiC[x] + (( alpha * rightShift( pResiL[x], diffBitDepth) ) >> 3); //进行色度残差预测
}
#else
const Int minPel=std::numeric_limits<Pel>::min(); //取最小值和最大值
const Int maxPel=std::numeric_limits<Pel>::max();
for( Int x = 0; x < width; x++ )
{
pResiT[x] = Clip3<Int>(minPel, maxPel, pResiC[x] + (( alpha * rightShift<Int>(Int(pResiL[x]), diffBitDepth) ) >> 3)); //进行色度残差预测
}
#endif
}
else //reverse为false,正向预测
{
// Forward does not need clipping. Pel type should always be big enough.
for( Int x = 0; x < width; x++ )
{
pResiT[x] = pResiC[x] - (( alpha * rightShift<Int>(Int(pResiL[x]), diffBitDepth) ) >> 3); //进行色度残差预测
}
}
pResiL += strideL;
pResiC += strideC;
pResiT += strideT;
}
}
(3)TEncSearch::xStoreCrossComponentPredictionResult代码如下,具体功能还不太清楚,个人理解是存储亮度残差,需要继续学习。
Void
TEncSearch::xStoreCrossComponentPredictionResult( Pel *pResiDst,
const Pel *pResiSrc,
TComTU &rTu,
const Int xOffset,
const Int yOffset,
const Int strideDst,
const Int strideSrc )
{
const Pel *pSrc = pResiSrc + yOffset * strideSrc + xOffset;
Pel *pDst = pResiDst + yOffset * strideDst + xOffset;
for( Int y = 0; y < rTu.getRect( COMPONENT_Y ).height; y++ )
{
::memcpy( pDst, pSrc, sizeof(Pel) * rTu.getRect( COMPONENT_Y ).width );
pDst += strideDst;
pSrc += strideSrc;
}
}