在之前的学习中,主要对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的可用标识。
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" ) } }