在之前的学习中,主要对CCP的整体代码思路进行了学习,下面将继续对TEncSearch::xIntraCodingTUBlock中CCP的参数和CCP其它相关函数进行研究。
一、CCP参数
1、 m_crossComponentPredictionEnabledFlag:CCP可用的标识。
TAppEncCfg::parseCfg会将读入的cfg文件中的CrossComponentPrediction的值赋给m_crossComponentPredictionEnabledFlag,默认情况下m_crossComponentPredictionEnabledFlag是false。
TAppEncCfg::xCheckParameter会对其进行检测,只有当YUV444时,m_crossComponentPredictionEnabledFlag才可以为真。
if(m_crossComponentPredictionEnabledFlag && (m_chromaFormatIDC != CHROMA_444))
{
fprintf(stderr, "****************************************************************************\n");
fprintf(stderr, "** WARNING: Cross-component prediction is specified for 4:4:4 format only **\n");
fprintf(stderr, "****************************************************************************\n");
m_crossComponentPredictionEnabledFlag = false;
}
有两个相关的函数,分别实现对m_crossComponentPredictionEnabledFlag的取值和赋值操作。
Bool getCrossComponentPredictionEnabledFlag () const { return m_crossComponentPredictionEnabledFlag; }
Void setCrossComponentPredictionEnabledFlag (const Bool value) { m_crossComponentPredictionEnabledFlag = value; }
在运行CCP之前会通过pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag(),来获取m_crossComponentPredictionEnabledFlag进行判断,只有当值为true时,CCP才可用。另外在反变换TComTrQuant::invRecurTransformNxN、CCP编码TEncSbac::codeCrossComponentPrediction、重构色度TEncSearch::xRecurIntraCodingLumaQT、解码器分析CCP信息TDecSbac::parseCrossComponentPrediction等函数中都会对m_crossComponentPredictionEnabledFlag进行判断。
2、bUseCrossCPrediction:也是CCP的可用标识。
与m_crossComponentPredictionEnabledFlag的区别在于,它是计算得到的,如Void TEncSearch::xIntraCodingTUBlock中,通过如下计算定义bUseCrossCPrediction。const Bool bUseCrossCPrediction = isChroma(compID) && (uiChPredMode == DM_CHROMA_IDX) && checkCrossCPrediction;
3、m_reconBasedCrossCPredictionEstimate:指示使用重构亮度残差还是原始亮度残差预测。
取值为true时,使用重构亮度残差预测;取值为false时,使用原始亮度残差预测。
TAppEncCfg::parseCfg会将读入的cfg文件中的ReconBasedCrossCPredictionEstimate的值赋给m_reconBasedCrossCPredictionEstimate,默认情况下m_crossComponentPredictionEnabledFlag是false。
有两个相关的函数,分别实现对m_reconBasedCrossCPredictionEstimate的取值和赋值操作。
Bool getUseReconBasedCrossCPredictionEstimate () const { return m_reconBasedCrossCPredictionEstimate; }
Void setUseReconBasedCrossCPredictionEstimate (const Bool value) { m_reconBasedCrossCPredictionEstimate = value; }
4、bUseReconstructedResidualForEstimate:指示使用重构亮度残差还是原始亮度残差。
直接由getUseReconBasedCrossCPredictionEstimate获取m_reconBasedCrossCPredictionEstimate来赋值,然后决定lumaResidualForEstimate的值,进而决定由重构亮度残差还是原始亮度残差计算α。
Pel *const lumaResidualForEstimate = bUseReconstructedResidualForEstimate ? reconstructedLumaResidual : encoderLumaResidual;
在TEncSearch::xIntraCodingTUBlock的生成CCP预测中,如果不使用CCP,且bUseReconstructedResidualForEstimate为false时,会对原始亮度残差进行存储。
5、m_crossComponentPredictionAlpha[MAX_NUM_COMPONENT]:存储α值的数组。
有三个相关函数,其中两个函数用于对m_crossComponentPredictionAlpha数组进行取值:
Char* getCrossComponentPredictionAlpha( ComponentID compID ) { return m_crossComponentPredictionAlpha[compID]; }
Char getCrossComponentPredictionAlpha( UInt uiIdx, ComponentID compID ) { return m_crossComponentPredictionAlpha[compID][uiIdx]; }
一个函数给m_crossComponentPredictionAlpha写入计算得到的α值。
Void TComDataCU::setCrossComponentPredictionAlphaPartRange( Char alphaValue, ComponentID compID, UInt uiAbsPartIdx, UInt uiCoveredPartIdxes )
{
memset((m_crossComponentPredictionAlpha[compID] + uiAbsPartIdx), alphaValue, (sizeof(Char) * uiCoveredPartIdxes));
}
二、相关函数
之前学习了TEncSearch::xIntraCodingTUBlock下直接调用的TEncSearch::xCalcCrossComponentPredictionAlpha、TComTrQuant::crossComponentPrediction、TEncSearch::xStoreCrossComponentPredictionResult三个函数,下面将对其它与CCP相关的函数进行学习。
1、TDecSbac::parseCrossComponentPrediction
位于解码器中的,用于检测CCP信息,根据二进制比特信息计算α值。由于一直在看编码器端的实现,对于解码器没有接触,因此很多看不懂。如果有错误请指正。
Void TDecSbac::parseCrossComponentPrediction( TComTU &rTu, ComponentID compID )
{
TComDataCU *pcCU = rTu.getCU();
if( isLuma(compID) || !pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() ) //检测CCP是否可用,不可用直接返回
{
return;
}
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
if (!pcCU->isIntra(uiAbsPartIdx) || (pcCU->getIntraDir( CHANNEL_TYPE_CHROMA, uiAbsPartIdx ) == DM_CHROMA_IDX))
{
Char alpha = 0;
UInt symbol = 0;
DTRACE_CABAC_VL( g_nSymbolCounter++ )
DTRACE_CABAC_T("\tparseCrossComponentPrediction()")
DTRACE_CABAC_T( "\tAddr=" )
DTRACE_CABAC_V( compID )
DTRACE_CABAC_T( "\tuiAbsPartIdx=" )
DTRACE_CABAC_V( uiAbsPartIdx )
#if RExt__DECODER_DEBUG_BIT_STATISTICS
TComCodingStatisticsClassType ctype(STATS__CABAC_BITS__CROSS_COMPONENT_PREDICTION, (g_aucConvertToBit[rTu.getRect(compID).width] + 2), compID);
#endif
ContextModel *pCtx = m_cCrossComponentPredictionSCModel.get(0, 0) + ((compID == COMPONENT_Cr) ? (NUM_CROSS_COMPONENT_PREDICTION_CTX >> 1) : 0);
m_pcTDecBinIf->decodeBin( symbol, pCtx[0] RExt__DECODER_DEBUG_BIT_STATISTICS_PASS_OPT_ARG(ctype) ); //用symbol记录alpha量化后的值
if(symbol != 0) //symbol=0即α=0,此时不进行CCP预测
{
// Cross-component prediction alpha is non-zero.
UInt sign = 0; //表示α的符号
m_pcTDecBinIf->decodeBin( symbol, pCtx[1] RExt__DECODER_DEBUG_BIT_STATISTICS_PASS_OPT_ARG(ctype) );
if (symbol != 0)
{
// alpha is 2 (symbol=1), 4(symbol=2) or 8(symbol=3).
// Read up to two more bits
xReadUnaryMaxSymbol( symbol, (pCtx + 2), 1, 2 RExt__DECODER_DEBUG_BIT_STATISTICS_PASS_OPT_ARG(ctype) );
symbol += 1;
}
m_pcTDecBinIf->decodeBin( sign, pCtx[4] RExt__DECODER_DEBUG_BIT_STATISTICS_PASS_OPT_ARG(ctype) );
alpha = (sign != 0) ? -(1 << symbol) : (1 << symbol); //计算α
}
DTRACE_CABAC_T( "\tAlpha=" )
DTRACE_CABAC_V( alpha )
DTRACE_CABAC_T( "\n" )
pcCU->setCrossComponentPredictionAlphaPartRange( alpha, compID, uiAbsPartIdx, rTu.GetAbsPartIdxNumParts( compID ) ); //存储α值
}
}
2、TEncSbac::codeCrossComponentPrediction
用于编码CCP信息,主要是编码得到二进制化的α。个人对于CABAC不够了解,对该代码的理解程度很差,之后将对CABAC进行学习,然后再回头看这段代码。
Void TEncSbac::codeCrossComponentPrediction( TComTU &rTu, ComponentID compID )
{
TComDataCU *pcCU = rTu.getCU();
if( isLuma(compID) || !pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() ) //判断CCP是否可用
{
return;
}
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
if (!pcCU->isIntra(uiAbsPartIdx) || (pcCU->getIntraDir( CHANNEL_TYPE_CHROMA, uiAbsPartIdx ) == DM_CHROMA_IDX))
{
DTRACE_CABAC_VL( g_nSymbolCounter++ )
DTRACE_CABAC_T("\tparseCrossComponentPrediction()")
DTRACE_CABAC_T( "\tAddr=" )
DTRACE_CABAC_V( compID )
DTRACE_CABAC_T( "\tuiAbsPartIdx=" )
DTRACE_CABAC_V( uiAbsPartIdx )
Int alpha = pcCU->getCrossComponentPredictionAlpha( uiAbsPartIdx, compID ); //取α值
ContextModel *pCtx = m_cCrossComponentPredictionSCModel.get(0, 0) + ((compID == COMPONENT_Cr) ? (NUM_CROSS_COMPONENT_PREDICTION_CTX >> 1) : 0);
m_pcBinIf->encodeBin(((alpha != 0) ? 1 : 0), pCtx[0]); //根据α是否等于0确定编码模式
if (alpha != 0) //α=0时不进行CCP预测
{
static const Int log2AbsAlphaMinus1Table[8] = { 0, 1, 1, 2, 2, 2, 3, 3 };
assert(abs(alpha) <= 8); //限定α范围要小于等于8
if (abs(alpha)>1) //α大于1和等于1分别编码
{
m_pcBinIf->encodeBin(1, pCtx[1]);
xWriteUnaryMaxSymbol( log2AbsAlphaMinus1Table[abs(alpha) - 1] - 1, (pCtx + 2), 1, 2 );
}
else
{
m_pcBinIf->encodeBin(0, pCtx[1]);
}
m_pcBinIf->encodeBin( ((alpha < 0) ? 1 : 0), pCtx[4] );
}
DTRACE_CABAC_T( "\tAlpha=" )
DTRACE_CABAC_V( pcCU->getCrossComponentPredictionAlpha( uiAbsPartIdx, compID ) )
DTRACE_CABAC_T( "\n" )
}
}