HM編碼器程式碼閱讀(32)——幀間預測之AMVP/Merge模式(七)encodeResAndCalcRdInterCU函式:殘差計算、變換量化
encodeResAndCalcRdInterCU
原理和細節
經過運動估計、運動補償,我們得到了MV以及參考塊,那麼接下來是計算殘差、計算MVD,然後對係數進行變換、量化。
encodeResAndCalcRdInterCU函式就是幀間預測之後,專門用來計算殘差、進行變換量化的函式。但是,除了變換量化之外,encodeResAndCalcRdInterCU函式中還有熵編碼方面的處理,我們都知道熵編碼是在encodeSlice(xEncodeCU)中進行的,為什麼會這樣呢,這樣做不就導致熵編碼進行了多次嗎?為了搞清楚這個問題,我們需要了解下面的一些概念:
1、compressSlice/compressCU,這些函式的作用是模式選取,目的是選取最優的編碼模式,使得率失真代價/位元代價最優。具體的操作就是利用率失真優化的原理,選取LCU到CU的最優劃分模式,CU到PU的最優劃分模式,PU的最優預測模式,CU到TU的最優劃分模式,最優的QP引數,使得率失真代價/位元代價最優,但是為了算位元代價(bitCost),必須要經過熵編碼這一步驟才能得到,而熵編碼之前又有變化量化等步驟,因此compressSlice實際上是把各種各樣的模式進行組合,然後每一個組合進行一個完整的編碼操作(預測、變換、量化、熵編碼),根據率失真代價/位元代價來選取最優的模式組合!
2、encodeSlice/encodeCU,經過了compressSlice之後,我們已經得到了最優的劃分模式、預測模式等引數,同時也得到了變換量化之後的係數既然已經得到了最優模式和變換系數,那麼最後的一步就是對這些模式和係數進行熵編碼,得到最終的位元率,因此encodeCU的功能就是熵編碼。
encodeResAndCalcRdInterCU的流程
encodeResAndCalcRdInterCU實際就是計算PU的殘差,然後進行變換量化,同時通過熵編碼來計算率失真代價/位元代價來得到最優QP引數,這就是為什麼encodeResAndCalcRdInterCU中還會有熵編碼處理的原因。該函式執行完畢之後,變換量化、反變換反量化就已經處理好了,同時還選取了最優的QP引數。所以,這個函式的核心實際是變換量化!
/* ** 估計殘差的編碼代價 ** 主要是進行變換量化以及熵編碼,然後選出最優的模式 */ Void TEncSearch::xEstimateResidualQT( TComDataCU* pcCU, UInt uiQuadrant, UInt uiAbsPartIdx, UInt absTUPartIdx, TComYuv* pcResi, const UInt uiDepth, Double &rdCost, UInt &ruiBits, UInt &ruiDist, UInt *puiZeroDist ) { // 刪除無關緊要程式碼*************** // 完整檢查的標誌 Bool bCheckFull; if ( SplitFlag && uiDepth == pcCU->getDepth(uiAbsPartIdx) && ( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ) ) { bCheckFull = false; } else { bCheckFull = ( uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() ); } // 是否檢查split標誌 const Bool bCheckSplit = ( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ); // 完整檢查模式 if( bCheckFull ) { // 刪除無關緊要程式碼*************** // 是否使用了率失真優化的量化 if (m_pcEncCfg->getUseRDOQ()) { // 估算位元數 m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA ); } // 刪除無關緊要程式碼*************** /*【==============變換量化開始===================】*/ // Y分量變換和量化 m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrY, #endif trWidth, trHeight, uiAbsSumY, TEXT_LUMA, uiAbsPartIdx ); // 刪除無關緊要程式碼*************** if( bCodeChroma ) { if (m_pcEncCfg->getUseRDOQ()) { m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA ); } // 刪除無關緊要程式碼*************** // 變換和量化(U分量) m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrU, #endif trWidthC, trHeightC, uiAbsSumU, TEXT_CHROMA_U, uiAbsPartIdx ); // 刪除無關緊要程式碼*************** // 變換和量化(V分量) m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrV, #endif trWidthC, trHeightC, uiAbsSumV, TEXT_CHROMA_V, uiAbsPartIdx ); // 刪除無關緊要程式碼*************** } /*【==============變換量化結束===================】*/ /*【==============變換系數以及其他資訊熵編碼開始===================】*/ m_pcEntropyCoder->resetBits(); // 對CBF進行編碼(Y分量) m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode ); // 對殘差係數進行編碼(Y分量) m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA ); // 刪除無關緊要程式碼*************** // 對色度部分進行編碼 if( bCodeChroma ) { // 刪除無關緊要程式碼*************** // 對CBF進行編碼(U分量) m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode ); // 對係數進行編碼(U分量) m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U ); // 刪除無關緊要程式碼*************** // 對CBF進行編碼(V分量) m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode ); // 對係數進行編碼(V分量) m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V ); // 刪除無關緊要程式碼*************** } /*【==============變換系數以及其他資訊熵編碼結束===================】*/ // 刪除無關緊要程式碼*************** /*【==============反量化、反變換開始===================】*/ // 如果Y分量絕對係數和大於0 // 開始執行反量化、反變換 if( uiAbsSumY ) { // 刪除無關緊要程式碼*************** // 反變換、反量化(Y分量) m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(), pcCoeffCurrY, trWidth, trHeight, scalingListType );//this is for inter mode only const UInt uiNonzeroDistY = m_pcRdCost->getDistPart(g_bitDepthY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getLumaAddr( absTUPartIdx ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(), pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride(), trWidth,trHeight ); if (pcCU->isLosslessCoded(0)) { uiDistY = uiNonzeroDistY; } else { // 刪除無關緊要程式碼*************** m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode ); // 刪除無關緊要程式碼*************** } } // 否則,如果是跳過Y分量 else if( checkTransformSkipY ) { // 刪除無關緊要程式碼*************** // 編碼Y分量的CBF的0係數 m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** // 如果對色度部分進行了變換量化,那麼需要對色度部分進行反變換反量化 if( bCodeChroma ) { // 刪除無關緊要程式碼*************** // 如果對U分量進行了變換量化 if( uiAbsSumU ) { // 刪除無關緊要程式碼*************** // 反變換、反量化(U分量) m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType ); const UInt uiNonzeroDistU = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCbAddr( absTUPartIdxC), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcResi->getCbAddr( absTUPartIdxC), pcResi->getCStride(), trWidthC, trHeightC , TEXT_CHROMA_U ); if(pcCU->isLosslessCoded(0)) { uiDistU = uiNonzeroDistU; } else { // 刪除無關緊要程式碼*************** m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode ); // 刪除無關緊要程式碼*************** } } // 否則(即變換量化沒有效果或者不夠優?) else if( checkTransformSkipUV ) { // 刪除無關緊要程式碼*************** // 編碼CBF的0係數 m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** // 對V分量進行處理(過程同U分量) if( uiAbsSumV ) { // 刪除無關緊要程式碼*************** // 反變換反量化(V分量) m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType ); const UInt uiNonzeroDistV = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCrAddr( absTUPartIdxC ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcResi->getCrAddr( absTUPartIdxC ), pcResi->getCStride(), trWidthC, trHeightC , TEXT_CHROMA_V ); if (pcCU->isLosslessCoded(0)) { uiDistV = uiNonzeroDistV; } else { // 刪除無關緊要程式碼*************** // 對CBF的0係數進行編碼 m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode ); // 刪除無關緊要程式碼*************** } } // 如果跳過了UV分量 else if( checkTransformSkipUV ) { // 刪除無關緊要程式碼*************** // 對CBF的0係數進行編碼 m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode ); // 刪除無關緊要程式碼*************** } if( !uiAbsSumV ) { // 刪除無關緊要程式碼*************** } } /*【==============反量化、反變換結束===================】*/ // 刪除無關緊要程式碼*************** /*【==============TransformSkip模式的變換量化、反變換反量化、熵編碼 開始===================】*/ if( checkTransformSkipY ) { // 刪除無關緊要程式碼*************** if (m_pcEncCfg->getUseRDOQTS()) { // 估算位元數 m_pcEntropyCoder->estimateBit( m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA ); } // 刪除無關緊要程式碼*************** // 變換和量化 m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrY, #endif trWidth, trHeight, uiAbsSumTransformSkipY, TEXT_LUMA, uiAbsPartIdx, true ); pcCU->setCbfSubParts( uiAbsSumTransformSkipY ? uiSetCbf : 0, TEXT_LUMA, uiAbsPartIdx, uiDepth ); if( uiAbsSumTransformSkipY != 0 ) { m_pcEntropyCoder->resetBits(); // 對CBF的0係數進行編碼 m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode ); // 對殘差係數進行編碼 m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA ); // 刪除無關緊要程式碼*************** // 反變換、反量化 m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(), pcCoeffCurrY, trWidth, trHeight, scalingListType, true ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** } // 如果對色度部分進行編碼,但是跳過了UV分量(這是什麼情況?) if( bCodeChroma && checkTransformSkipUV ) { // 刪除無關緊要程式碼*************** if (m_pcEncCfg->getUseRDOQTS()) { // 位元數估算 m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA ); } // 刪除無關緊要程式碼*************** // 變換量化(U分量) m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrU, #endif trWidthC, trHeightC, uiAbsSumTransformSkipU, TEXT_CHROMA_U, uiAbsPartIdx, true ); // 刪除無關緊要程式碼*************** // 變換量化(V分量) m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV, #if ADAPTIVE_QP_SELECTION pcArlCoeffCurrV, #endif trWidthC, trHeightC, uiAbsSumTransformSkipV, TEXT_CHROMA_V, uiAbsPartIdx, true ); // 刪除無關緊要程式碼*************** // 如果跳過了U分量 if( uiAbsSumTransformSkipU ) { // 刪除無關緊要程式碼*************** // 對係數進行編碼 m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode ); m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U ); // 刪除無關緊要程式碼*************** // 反變換反量化 m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType, true ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** // 如果跳過了V分量 if( uiAbsSumTransformSkipV ) { // 刪除無關緊要程式碼*************** // 對係數進行編碼 m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode ); m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V ); // 刪除無關緊要程式碼*************** // 反變換反量化 m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType, true ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** } /*【==============TransformSkip模式的變換量化、反變換反量化、熵編碼 結束===================】*/ // 刪除無關緊要程式碼*************** if( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ) { // 編碼分割標誌? m_pcEntropyCoder->encodeTransformSubdivFlag( 0, 5 - uiLog2TrSize ); } if( bCodeChroma ) { m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode ); m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode ); } m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode ); m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA ); if( bCodeChroma ) { m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U ); m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V ); } // 刪除無關緊要程式碼*************** } // code sub-blocks // 對TU的四個子TU進行計算(使用遞迴的方式) if( bCheckSplit ) { // 刪除無關緊要程式碼*************** for( UInt ui = 0; ui < 4; ++ui ) { UInt nsAddr = uiAbsPartIdx + ui * uiQPartNumSubdiv; // 遞迴呼叫 xEstimateResidualQT( pcCU, ui, uiAbsPartIdx + ui * uiQPartNumSubdiv, nsAddr, pcResi, uiDepth + 1, dSubdivCost, uiSubdivBits, uiSubdivDist, bCheckFull ? NULL : puiZeroDist ); } // 刪除無關緊要程式碼*************** // 對殘差四叉樹進行編碼 xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, true, TEXT_LUMA ); xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_LUMA ); xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_U ); xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_V ); // 刪除無關緊要程式碼*************** } // 刪除無關緊要程式碼*************** }
估算殘差的率失真代價和位元代價
入口函式是:xEstimateResidualQT
流程如下:
一、首先看到CheckFull標誌,如果為真,就對各種模式進行測試,估計他們的率失真代價和位元代價等,主要步驟如下:
1、分別對Y、U、V三個分量進行變換和量化,transformNxN最後的引數是false,表示不是TransformSkip模式
2、對Y、U、V三個分量的變換系數以及其他的資訊進行熵編碼,計算出率失真代價以及位元代價
3、對Y、U、V三個分量進行反變換反量化
4、如果是TransformSkip模式,那麼在TransformSkip模式下,重新執行步驟1、2、3,但是transformNxN最後的引數是true,表示是TransformSkip模式
5、判斷比較得出最優的模式
二、檢測CheckSplit標識,如果為真,那麼進行子TU的遞迴處理,並對當前TU進行熵編碼
1、對四個子TU遞迴呼叫xEstimateResidualQT
2、對Y分量呼叫xEncodeResidualQT,進行熵編碼,bSubdivAndCbf引數是true,表示計算當前TU劃分成4個子TU時的代價,另外如果bSubdivAndCbf引數是true,那麼會在xEncodeResidualQT裡面計算U、V分量的率失真代價/位元代價
3、對Y、U、V三個分量呼叫xEncodeResidualQT進行熵編碼,bSubdivAndCbf引數是false,表示計算當前TU不劃分成子TU時的代價
/*
** 估計殘差的編碼代價
** 主要是進行變換量化以及熵編碼,然後選出最優的模式
*/
Void TEncSearch::xEstimateResidualQT( TComDataCU* pcCU, UInt uiQuadrant, UInt uiAbsPartIdx, UInt absTUPartIdx, TComYuv* pcResi, const UInt uiDepth, Double &rdCost, UInt &ruiBits, UInt &ruiDist, UInt *puiZeroDist )
{
// 刪除無關緊要程式碼***************
// 完整檢查的標誌
Bool bCheckFull;
if ( SplitFlag && uiDepth == pcCU->getDepth(uiAbsPartIdx) && ( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ) )
{
bCheckFull = false;
}
else
{
bCheckFull = ( uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() );
}
// 是否檢查split標誌
const Bool bCheckSplit = ( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) );
// 完整檢查模式
if( bCheckFull )
{
// 刪除無關緊要程式碼***************
// 是否使用了率失真優化的量化
if (m_pcEncCfg->getUseRDOQ())
{
// 估算位元數
m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );
}
// 刪除無關緊要程式碼***************
/*【==============變換量化開始===================】*/
// Y分量變換和量化
m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrY,
#endif
trWidth, trHeight, uiAbsSumY, TEXT_LUMA, uiAbsPartIdx );
// 刪除無關緊要程式碼***************
if( bCodeChroma )
{
if (m_pcEncCfg->getUseRDOQ())
{
m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );
}
// 刪除無關緊要程式碼***************
// 變換和量化(U分量)
m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrU,
#endif
trWidthC, trHeightC, uiAbsSumU, TEXT_CHROMA_U, uiAbsPartIdx );
// 刪除無關緊要程式碼***************
// 變換和量化(V分量)
m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrV,
#endif
trWidthC, trHeightC, uiAbsSumV, TEXT_CHROMA_V, uiAbsPartIdx );
// 刪除無關緊要程式碼***************
}
/*【==============變換量化結束===================】*/
/*【==============變換系數以及其他資訊熵編碼開始===================】*/
m_pcEntropyCoder->resetBits();
// 對CBF進行編碼(Y分量)
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
// 對殘差係數進行編碼(Y分量)
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
// 刪除無關緊要程式碼***************
// 對色度部分進行編碼
if( bCodeChroma )
{
// 刪除無關緊要程式碼***************
// 對CBF進行編碼(U分量)
m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
// 對係數進行編碼(U分量)
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
// 刪除無關緊要程式碼***************
// 對CBF進行編碼(V分量)
m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
// 對係數進行編碼(V分量)
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
// 刪除無關緊要程式碼***************
}
/*【==============變換系數以及其他資訊熵編碼結束===================】*/
// 刪除無關緊要程式碼***************
/*【==============反量化、反變換開始===================】*/
// 如果Y分量絕對係數和大於0
// 開始執行反量化、反變換
if( uiAbsSumY )
{
// 刪除無關緊要程式碼***************
// 反變換、反量化(Y分量)
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(), pcCoeffCurrY, trWidth, trHeight, scalingListType );//this is for inter mode only
const UInt uiNonzeroDistY = m_pcRdCost->getDistPart(g_bitDepthY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getLumaAddr( absTUPartIdx ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),
pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride(), trWidth,trHeight );
if (pcCU->isLosslessCoded(0))
{
uiDistY = uiNonzeroDistY;
}
else
{
// 刪除無關緊要程式碼***************
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode );
// 刪除無關緊要程式碼***************
}
}
// 否則,如果是跳過Y分量
else if( checkTransformSkipY )
{
// 刪除無關緊要程式碼***************
// 編碼Y分量的CBF的0係數
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
// 如果對色度部分進行了變換量化,那麼需要對色度部分進行反變換反量化
if( bCodeChroma )
{
// 刪除無關緊要程式碼***************
// 如果對U分量進行了變換量化
if( uiAbsSumU )
{
// 刪除無關緊要程式碼***************
// 反變換、反量化(U分量)
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType );
const UInt uiNonzeroDistU = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCbAddr( absTUPartIdxC), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
pcResi->getCbAddr( absTUPartIdxC), pcResi->getCStride(), trWidthC, trHeightC
, TEXT_CHROMA_U
);
if(pcCU->isLosslessCoded(0))
{
uiDistU = uiNonzeroDistU;
}
else
{
// 刪除無關緊要程式碼***************
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode );
// 刪除無關緊要程式碼***************
}
}
// 否則(即變換量化沒有效果或者不夠優?)
else if( checkTransformSkipUV )
{
// 刪除無關緊要程式碼***************
// 編碼CBF的0係數
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
// 對V分量進行處理(過程同U分量)
if( uiAbsSumV )
{
// 刪除無關緊要程式碼***************
// 反變換反量化(V分量)
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType );
const UInt uiNonzeroDistV = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCrAddr( absTUPartIdxC ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
pcResi->getCrAddr( absTUPartIdxC ), pcResi->getCStride(), trWidthC, trHeightC
, TEXT_CHROMA_V
);
if (pcCU->isLosslessCoded(0))
{
uiDistV = uiNonzeroDistV;
}
else
{
// 刪除無關緊要程式碼***************
// 對CBF的0係數進行編碼
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode );
// 刪除無關緊要程式碼***************
}
}
// 如果跳過了UV分量
else if( checkTransformSkipUV )
{
// 刪除無關緊要程式碼***************
// 對CBF的0係數進行編碼
m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode );
// 刪除無關緊要程式碼***************
}
if( !uiAbsSumV )
{
// 刪除無關緊要程式碼***************
}
}
/*【==============反量化、反變換結束===================】*/
// 刪除無關緊要程式碼***************
/*【==============TransformSkip模式的變換、量化、熵編碼 開始===================】*/
if( checkTransformSkipY )
{
// 刪除無關緊要程式碼***************
if (m_pcEncCfg->getUseRDOQTS())
{
// 估算位元數
m_pcEntropyCoder->estimateBit( m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );
}
// 刪除無關緊要程式碼***************
// 變換和量化
m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrY,
#endif
trWidth, trHeight, uiAbsSumTransformSkipY, TEXT_LUMA, uiAbsPartIdx, true );
pcCU->setCbfSubParts( uiAbsSumTransformSkipY ? uiSetCbf : 0, TEXT_LUMA, uiAbsPartIdx, uiDepth );
if( uiAbsSumTransformSkipY != 0 )
{
m_pcEntropyCoder->resetBits();
// 對CBF的0係數進行編碼
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
// 對殘差係數進行編碼
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
// 刪除無關緊要程式碼***************
// 反變換、反量化
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(), pcCoeffCurrY, trWidth, trHeight, scalingListType, true );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
}
// 如果對色度部分進行編碼,但是跳過了UV分量(這是什麼情況?)
if( bCodeChroma && checkTransformSkipUV )
{
// 刪除無關緊要程式碼***************
if (m_pcEncCfg->getUseRDOQTS())
{
// 位元數估算
m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );
}
// 刪除無關緊要程式碼***************
// 變換量化(U分量)
m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrU,
#endif
trWidthC, trHeightC, uiAbsSumTransformSkipU, TEXT_CHROMA_U, uiAbsPartIdx, true );
// 刪除無關緊要程式碼***************
// 變換量化(V分量)
m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
pcArlCoeffCurrV,
#endif
trWidthC, trHeightC, uiAbsSumTransformSkipV, TEXT_CHROMA_V, uiAbsPartIdx, true );
// 刪除無關緊要程式碼***************
// 如果跳過了U分量
if( uiAbsSumTransformSkipU )
{
// 刪除無關緊要程式碼***************
// 對係數進行編碼
m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
// 刪除無關緊要程式碼***************
// 反變換反量化
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType, true );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
// 如果跳過了V分量
if( uiAbsSumTransformSkipV )
{
// 刪除無關緊要程式碼***************
// 對係數進行編碼
m_pcEntropyCoder->encodeQtCbf ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
// 刪除無關緊要程式碼***************
// 反變換反量化
m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType, true );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
}
/*【==============TransformSkip模式的變換、量化、熵編碼 結束===================】*/
// 刪除無關緊要程式碼***************
if( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) )
{
// 編碼分割標誌?
m_pcEntropyCoder->encodeTransformSubdivFlag( 0, 5 - uiLog2TrSize );
}
// 前面的是為了選出最優模式,下面對係數進行編碼
/*【==============前面的好幾次變換量化熵編碼,只是為了選取最優的編碼模式,現在根據最優的編碼模式進行編碼 開始===================】*/
// 對色度部分CBF進行編碼
if( bCodeChroma )
{
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
}
// 對亮度的CBF進行編碼
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
// 編碼亮度部分的係數
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
// 對色度部分的係數進行編碼
if( bCodeChroma )
{
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
}
/*【==============前面的好幾次變換量化熵編碼,只是為了選取最優的編碼模式,現在根據最優的編碼模式進行編碼 結束===================】*/
// 刪除無關緊要程式碼***************
}
// code sub-blocks
// 對TU的四個子TU進行計算(使用遞迴的方式)
if( bCheckSplit )
{
// 刪除無關緊要程式碼***************
for( UInt ui = 0; ui < 4; ++ui )
{
UInt nsAddr = uiAbsPartIdx + ui * uiQPartNumSubdiv;
// 遞迴呼叫
xEstimateResidualQT( pcCU, ui, uiAbsPartIdx + ui * uiQPartNumSubdiv, nsAddr, pcResi, uiDepth + 1, dSubdivCost, uiSubdivBits, uiSubdivDist, bCheckFull ? NULL : puiZeroDist );
}
// 刪除無關緊要程式碼***************
// 對殘差四叉樹進行編碼
xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, true, TEXT_LUMA );
xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_LUMA );
xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_U );
xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_V );
// 刪除無關緊要程式碼***************
}
// 刪除無關緊要程式碼***************
}
對Cbf、殘差係數進行熵編碼
/*
** xEncodeResidualQT的主要功能是對Cbf、殘差係數進行熵編碼
*/
Void TEncSearch::xEncodeResidualQT( TComDataCU* pcCU, UInt uiAbsPartIdx, const UInt uiDepth, Bool bSubdivAndCbf, TextType eType )
{
assert( pcCU->getDepth( 0 ) == pcCU->getDepth( uiAbsPartIdx ) );
// 模式
const UInt uiCurrTrMode = uiDepth - pcCU->getDepth( 0 );
const UInt uiTrMode = pcCU->getTransformIdx( uiAbsPartIdx );
// 對當前TU是否會繼續向下分割
const Bool bSubdiv = uiCurrTrMode != uiTrMode;
const UInt uiLog2TrSize = g_aucConvertToBit[pcCU->getSlice()->getSPS()->getMaxCUWidth() >> uiDepth]+2;
if( bSubdivAndCbf && uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() && uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) )
{
// 編碼向下分割的標誌
m_pcEntropyCoder->encodeTransformSubdivFlag( bSubdiv, 5 - uiLog2TrSize );
}
assert( pcCU->getPredictionMode(uiAbsPartIdx) != MODE_INTRA );
// 如果bSubdivAndCbf是true,那麼對U、V分量進行處理,Y分量在前面已經處理了
if( bSubdivAndCbf )
{
const Bool bFirstCbfOfCU = uiCurrTrMode == 0;
// 編碼UV分量的CBF(Y分量已經在呼叫這個函式之前處理完了)
if( bFirstCbfOfCU || uiLog2TrSize > 2 )
{
if( bFirstCbfOfCU || pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode - 1 ) )
{
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode );
}
if( bFirstCbfOfCU || pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode - 1 ) )
{
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode );
}
}
else if( uiLog2TrSize == 2 )
{
assert( pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode ) == pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode - 1 ) );
assert( pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode ) == pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode - 1 ) );
}
}
// 如果不在繼續向下分割,那麼直接編碼YUV三個分量的係數,否則需要對子TU遞迴呼叫
if( !bSubdiv )
{
const UInt uiNumCoeffPerAbsPartIdxIncrement = pcCU->getSlice()->getSPS()->getMaxCUWidth() * pcCU->getSlice()->getSPS()->getMaxCUHeight() >> ( pcCU->getSlice()->getSPS()->getMaxCUDepth() << 1 );
//assert( 16 == uiNumCoeffPerAbsPartIdxIncrement ); // check
const UInt uiQTTempAccessLayer = pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() - uiLog2TrSize;
TCoeff *pcCoeffCurrY = m_ppcQTTempCoeffY [uiQTTempAccessLayer] + uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx;
TCoeff *pcCoeffCurrU = m_ppcQTTempCoeffCb[uiQTTempAccessLayer] + (uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx>>2);
TCoeff *pcCoeffCurrV = m_ppcQTTempCoeffCr[uiQTTempAccessLayer] + (uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx>>2);
Bool bCodeChroma = true;
UInt uiTrModeC = uiTrMode;
UInt uiLog2TrSizeC = uiLog2TrSize-1;
if( uiLog2TrSize == 2 )
{
uiLog2TrSizeC++;
uiTrModeC --;
UInt uiQPDiv = pcCU->getPic()->getNumPartInCU() >> ( ( pcCU->getDepth( 0 ) + uiTrModeC ) << 1 );
bCodeChroma = ( ( uiAbsPartIdx % uiQPDiv ) == 0 );
}
if( bSubdivAndCbf )
{
m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
}
else
{
if( eType == TEXT_LUMA && pcCU->getCbf( uiAbsPartIdx, TEXT_LUMA, uiTrMode ) )
{
Int trWidth = 1 << uiLog2TrSize;
Int trHeight = 1 << uiLog2TrSize;
// 對Y分量的係數進行編碼
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
}
if( bCodeChroma )
{
Int trWidth = 1 << uiLog2TrSizeC;
Int trHeight = 1 << uiLog2TrSizeC;
// 對U分量的係數進行編碼
if( eType == TEXT_CHROMA_U && pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode ) )
{
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_CHROMA_U );
}
// 對V分量的係數進行編碼
if( eType == TEXT_CHROMA_V && pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode ) )
{
m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_CHROMA_V );
}
}
}
}
else
{
if( bSubdivAndCbf || pcCU->getCbf( uiAbsPartIdx, eType, uiCurrTrMode ) )
{
const UInt uiQPartNumSubdiv = pcCU->getPic()->getNumPartInCU() >> ((uiDepth + 1 ) << 1);
// 對子TU遞迴呼叫該函式
for( UInt ui = 0; ui < 4; ++ui )
{
xEncodeResidualQT( pcCU, uiAbsPartIdx + ui * uiQPartNumSubdiv, uiDepth + 1, bSubdivAndCbf, eType );
}
}
}
}
至此,幀間預測的AMVP模式全部講解完畢!