xCompressCU是一个递归函数,对于每一个CU,该函数都会被调用,主要是计算当前CU编码之后代价,然后再计算当前CU的每一个子CU编码后的代价,和当前CU的编码代价相比较,用来决定是否对当前CU进行分割。这个函数太复杂啦,继续慢慢学习吧。
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth, UInt uiWidth, UInt uiHeight, UInt uiBTSplitMode DEBUG_STRING_FN_DECLARE(sDebug_), UInt uiSplitConstrain )
#else
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize )
#endif
#else
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth )
#endif
{
//每个深度的预测用的都是temp,预测完后跟best比较并交换。best保留作为当前深度的预测数据,
//而temp再次初始化。在下一深度的子CU预测中用的是subtemp,每预测完一个子CU,就跟subbest比较交换,
//再把subbest的数据复制到已经初始化的temp的相应位置。当temp获取完子CU的subbest的数据后,
//就代表了整个下一深度的数据,这时再与代表当前深度数据的best比较交换;
#if JVET_C0024_BT_RMV_REDUNDANT
rpcBestCU->setSplitConstrain( uiSplitConstrain );
rpcTempCU->setSplitConstrain( uiSplitConstrain );
Bool bQTreeValid = false;
#endif
TComPic* pcPic = rpcBestCU->getPic();
DEBUG_STRING_NEW(sDebug)
const TComPPS &pps=*(rpcTempCU->getSlice()->getPPS());
const TComSPS &sps=*(rpcTempCU->getSlice()->getSPS());
#if JVET_C0024_QTBT
// These are only used if getFastDeltaQp() is true,32;
const UInt fastDeltaQPCuMaxSize = Clip3(sps.getMinQTSize(rpcBestCU->getSlice()->getSliceType(), rpcBestCU->getTextType())
, sps.getCTUSize(), 32u);
#else
// These are only used if getFastDeltaQp() is true
const UInt fastDeltaQPCuMaxSize = Clip3(sps.getMaxCUHeight()>>sps.getLog2DiffMaxMinCodingBlockSize(), sps.getMaxCUHeight(), 32u);
#endif
// get Original YUV data from picture
#if JVET_C0024_QTBT
UInt uiWidthIdx = g_aucConvertToBit[rpcTempCU->getWidth(0)];//=5;
UInt uiHeightIdx = g_aucConvertToBit[rpcTempCU->getHeight(0)];//=5;
//从图像中复制原始YUV数据;
m_pppcOrigYuv[uiWidthIdx][uiHeightIdx]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() );
UInt uiPelXInCTU = rpcBestCU->getCUPelX() - rpcBestCU->getPic()->getCtu(rpcBestCU->getCtuRsAddr())->getCUPelX();
UInt uiPelYInCTU = rpcBestCU->getCUPelY() - rpcBestCU->getPic()->getCtu(rpcBestCU->getCtuRsAddr())->getCUPelY();
rpcBestCU->getPic()->setCodedBlkInCTU(false, uiPelXInCTU>> MIN_CU_LOG2, uiPelYInCTU>> MIN_CU_LOG2, uiWidth>> MIN_CU_LOG2, uiHeight>> MIN_CU_LOG2 );
#else
m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() );
#endif
// variable for Cbf fast mode PU decision
#if !JVET_C0024_QTBT
Bool doNotBlockPu = true;
#endif
Bool earlyDetectionSkipMode = false;
//获取当前最优CU的范围;
const UInt uiLPelX = rpcBestCU->getCUPelX();//左边的像素,0;
const UInt uiRPelX = uiLPelX + rpcBestCU->getWidth(0) - 1;//最右边的像素,127;
const UInt uiTPelY = rpcBestCU->getCUPelY();//上边的像素,0;
const UInt uiBPelY = uiTPelY + rpcBestCU->getHeight(0) - 1;//最下边的像素,127;
#if !JVET_C0024_QTBT
const UInt uiWidth = rpcBestCU->getWidth(0);
#endif
#if JVET_C0024_DELTA_QP_FIX
UInt uiQTWidth = sps.getCTUSize()>>uiDepth;//四叉树的宽度,128;
UInt uiQTHeight = sps.getCTUSize()>>uiDepth;//四叉树的高度,128;
UInt uiBTDepth = g_aucConvertToBit[uiQTWidth]-g_aucConvertToBit[uiWidth] + g_aucConvertToBit[uiQTHeight]-g_aucConvertToBit[uiHeight];//二叉树深度;
const UInt uiQTBTDepth = (uiDepth<<1) + uiBTDepth;//QTBT的深度是四叉树的深度加二叉树的深度;
const UInt uiMaxDQPDepthQTBT = pps.getMaxCuDQPDepth() << 1;
#endif
#if JVET_D0077_SAVE_LOAD_ENC_INFO
Bool bUseSaveLoad = m_pcEncCfg->getUseSaveLoadEncInfo() && uiWidthIdx > 0 && uiHeightIdx > 0;
Bool bUseSaveLoadSplitDecision = bUseSaveLoad && m_pcEncCfg->getUseSaveLoadSplitDecision();
#if COM16_C1046_PDPC_INTRA && !JVET_G0104_PLANAR_PDPC
ChannelType eChannelType = rpcBestCU->getTextType();
#endif
UInt uiZorderIdx = rpcBestCU->getZorderIdxInCtu();//当前最优CU在CTU中以Z扫描顺序的索引;
#endif
Int iBaseQP = xComputeQP( rpcBestCU, uiDepth );//计算QP,量化步长;
Int iMinQP;
Int iMaxQP;
Bool isAddLowestQP = false;
#if COM16_C806_EMT
Double dBestInterCost = MAX_DOUBLE;
Bool bEarlySkipIntra = false;
#if !JVET_C0024_QTBT
Bool bAllIntra = (m_pcEncCfg->getIntraPeriod()==1);
Double dIntra2Nx2NCost = MAX_DOUBLE;
#endif
#if !JVET_C0024_QTBT
Double dIntraNxNCost = MAX_DOUBLE;
#endif
#endif
#if JVET_C0024_DELTA_QP_FIX
const Char lastCodedQP = rpcBestCU->getCodedQP();
#endif
#if JVET_E0076_MULTI_PEL_MVD
m_dBestMvDPelCost[0] = m_dBestMvDPelCost[1] = m_dBestMvDPelCost[2] = MAX_DOUBLE/2;
#endif
const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();//3;
#if VCEG_AZ08_INTER_KLT
g_bEnableCheck = false;
#endif
/设置最大的QP和最小的QP/
#if JVET_C0024_DELTA_QP_FIX
if( uiQTBTDepth <= uiMaxDQPDepthQTBT )
#else
if( uiDepth <= pps.getMaxCuDQPDepth() )
#endif
{
Int idQP = m_pcEncCfg->getMaxDeltaQP();
iMinQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP-idQP );
iMaxQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP+idQP );
#if JVET_C0024_DELTA_QP_FIX
if( pps.getUseDQP() )
{
UInt uiCurrPartIdxInCtu = rpcBestCU->getZorderIdxInCtu();
rpcBestCU->setQuPartIdx( uiCurrPartIdxInCtu );
rpcTempCU->setQuPartIdx( uiCurrPartIdxInCtu );
rpcBestCU->setQuLastCodedQP( lastCodedQP );
rpcTempCU->setQuLastCodedQP( lastCodedQP );
}
#endif
}
else
{
iMinQP = rpcTempCU->getQP(0);
iMaxQP = rpcTempCU->getQP(0);
}
#if WCG_LUMA_DQP_CM_SCALE
Int targetQP = iBaseQP;
if (m_pcEncCfg->getUseLumaDeltaQp() )
{
if (uiQTBTDepth <= uiMaxDQPDepthQTBT)
{
m_LumaQPOffset = calculateLumaDQP(rpcTempCU, 0, m_pppcOrigYuv[uiWidthIdx][uiHeightIdx]); // keep using the same m_QP_LUMA_OFFSET in the same LCU
}
targetQP = Clip3(-sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - m_LumaQPOffset); // targetQP is used for control lambda,32;
iMinQP = targetQP;
iMaxQP = iMinQP; // make it same as MinQP to force encode choose the modified QP
}
#endif
if ( m_pcEncCfg->getUseRateCtrl() )
{
iMinQP = m_pcRateCtrl->getRCQP();
iMaxQP = m_pcRateCtrl->getRCQP();
}
// transquant-bypass (TQB) processing loop variable initialisation ---
const Int lowestQP = iMinQP; // For TQB, use this QP which is the lowest non TQB QP tested (rather than QP'=0) - that way delta QPs are smaller, and TQB can be tested at all CU levels.
if ( (pps.getTransquantBypassEnableFlag()) )
{
isAddLowestQP = true; // mark that the first iteration is to cost TQB mode.
iMinQP = iMinQP - 1; // increase loop variable range by 1, to allow testing of TQB mode along with other QPs
if ( m_pcEncCfg->getCUTransquantBypassFlagForceValue() )
{
iMaxQP = iMinQP;
}
}
#if VCEG_AZ06_IC
Bool bICEnabled = rpcTempCU->getSlice()->getApplyIC();
#if JVET_C0024_QTBT
#if VCEG_AZ06_IC_SPEEDUP
if (uiWidth * uiHeight <= 32 )
{
bICEnabled = false;
}
#endif
#endif
#endif
TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx());
#if COM16_C806_LARGE_CTU
#if JVET_C0024_QTBT//ucMaxDepth=4;
UChar ucMinDepth = 0 , ucMaxDepth = g_aucConvertToBit[pcSlice->getSPS()->getCTUSize()]
- g_aucConvertToBit[pcSlice->getSPS()->getMinQTSize(pcSlice->getSliceType(), rpcTempCU->getTextType())];
#else
UChar ucMinDepth = 0 , ucMaxDepth = ( UChar )pcSlice->getSPS()->getLog2DiffMaxMinCodingBlockSize();
#endif
if( m_pcEncCfg->getUseFastLCTU() )//enable fast method for large CTU;
{
rpcTempCU->getMaxMinCUDepth( ucMinDepth , ucMaxDepth , rpcTempCU->getZorderIdxInCtu() );
}
#endif
#if JVET_C0024_QTBT
Bool bBoundary = !( uiRPelX < sps.getPicWidthInLumaSamples() && uiBPelY < sps.getPicHeightInLumaSamples() );//判断当前CU是否在图像边界;
#else
const Bool bBoundary = !( uiRPelX < sps.getPicWidthInLumaSamples() && uiBPelY < sps.getPicHeightInLumaSamples() );
#endif
#if JVET_C0024_QTBT
Bool bForceQT = uiWidth > MAX_TU_SIZE;
if( bForceQT )
{
assert(uiWidth == uiHeight);
}
#endif
#if JVET_D0077_SAVE_LOAD_ENC_INFO
UChar saveLoadTag = m_pcPredSearch->getSaveLoadTag( uiZorderIdx, uiWidthIdx, uiHeightIdx );
UChar saveLoadSplit = ( (bUseSaveLoadSplitDecision && saveLoadTag == LOAD_ENC_INFO) ? m_pcPredSearch->getSaveLoadSplit(uiWidthIdx, uiHeightIdx) : 0 );
Double dCostTempBest = MAX_DOUBLE;//最优的临时代价;
Double dNonSplitCost = MAX_DOUBLE;//不分割的代价;
Double dHorSplitCost = MAX_DOUBLE;//水平分割的代价;
Double dVerSplitCost = MAX_DOUBLE;//垂直分割的代价;
#endif
if (!bBoundary
#if COM16_C806_LARGE_CTU
&& ucMinDepth <= uiDepth
#endif
#if JVET_C0024_QTBT
&& !bForceQT
#endif
#if JVET_D0077_SAVE_LOAD_ENC_INFO
&& !(saveLoadSplit & 0x01)
#endif
)
{
#if JVET_D0077_FAST_EXT
Bool bPrevSameBlockIsIntra = rpcBestCU->getPic()->getIntra(rpcBestCU->getZorderIdxInCtu(), uiWidth, uiHeight);
Bool bPrevSameBlockIsSkip = rpcBestCU->getPic()->getSkiped(rpcBestCU->getZorderIdxInCtu(), uiWidth, uiHeight);
#endif
//从最小的QP到最大的QP遍历每一个QP,对每一个QP执行下面的操作(目的是选出最优的QP)/
for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)
{
const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP);
if (bIsLosslessMode)//判断是否为无损模式,若是无损,则QP设置为最低值;
{
iQP = lowestQP;
}
#if WCG_LUMA_DQP_CM_SCALE
if (m_pcEncCfg->getUseLumaDeltaQp() && (uiQTBTDepth <= uiMaxDQPDepthQTBT))
{
getSliceEncoder()->updateLambda(pcSlice, targetQP);
}
#endif
m_cuChromaQpOffsetIdxPlus1 = 0;
if (pcSlice->getUseChromaQpAdj())
{
/* Pre-estimation of chroma QP based on input block activity may be performed
* here, using for example m_ppcOrigYuv[uiDepth] */
/* To exercise the current code, the index used for adjustment is based on
* block position
*/
#if JVET_C0024_QTBT
Int lgMinCuSize = g_aucConvertToBit[sps.getMinQTSize(pcSlice->getSliceType(), rpcBestCU->getTextType())] + MIN_CU_LOG2 +
std::max<Int>(0, g_aucConvertToBit[sps.getCTUSize()] - g_aucConvertToBit[sps.getMinQTSize(pcSlice->getSliceType(), rpcBestCU->getTextType())] - Int(pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth()));
#else
Int lgMinCuSize = sps.getLog2MinCodingBlockSize() +
std::max<Int>(0, sps.getLog2DiffMaxMinCodingBlockSize() - Int(pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth()));
#endif
m_cuChromaQpOffsetIdxPlus1 = ((uiLPelX >> lgMinCuSize) + (uiTPelY >> lgMinCuSize)) % (pps.getPpsRangeExtension().getChromaQpOffsetListLen() + 1);
}
//初始化当前CU的数据;
rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
#if VCEG_AZ07_IMV && !JVET_C0024_QTBT
m_setInterCand.clear();
for (Int n = 0; n < NUMBER_OF_PART_SIZES; n++)
{
#if JVET_C0024_QTBT
m_pppcTempCUIMVCache[n][uiWidthIdx][uiHeightIdx]->initEstData(uiDepth, iQP, bIsLosslessMode);
#else
m_ppcTempCUIMVCache[n][uiDepth]->initEstData(uiDepth, iQP, bIsLosslessMode);
#endif
}
#endif
/帧间模式选择//
// do inter modes, SKIP and 2Nx2N
if (rpcBestCU->getSlice()->getSliceType() != I_SLICE)//不是I_SLICE,即不进行帧内预测;
{
#if JVET_D0077_SAVE_LOAD_ENC_INFO
#if VCEG_AZ07_FRUC_MERGE
Bool bFastSkipFruc = (saveLoadTag == LOAD_ENC_INFO && 0 == m_pcPredSearch->getSaveLoadFrucMode(uiWidthIdx, uiHeightIdx));
#endif
#if VCEG_AZ07_IMV
Bool bFastSkipIMV = (saveLoadTag == LOAD_ENC_INFO && (!m_pcPredSearch->getSaveLoadIMVFlag(uiWidthIdx, uiHeightIdx) || m_pcPredSearch->getSaveLoadMergeFlag(uiWidthIdx, uiHeightIdx)));
#endif
Bool bFastSkipInter = (saveLoadTag == LOAD_ENC_INFO && m_pcPredSearch->getSaveLoadMergeFlag(uiWidthIdx, uiHeightIdx));
#if COM16_C1016_AFFINE
Bool bFastSkipAffine = (saveLoadTag == LOAD_ENC_INFO && !m_pcPredSearch->getSaveLoadAffineFlag(uiWidthIdx, uiHeightIdx));
#endif
#endif
#if VCEG_AZ06_IC
for (UInt uiICId = 0; uiICId < (bICEnabled ? 2 : 1); uiICId++)
{
Bool bICFlag = uiICId ? true : false;
#endif
// 2Nx2N
if (m_pcEncCfg->getUseEarlySkipDetection()) //使用earlyDetectionSkipMode;
{
#if VCEG_AZ06_IC
rpcTempCU->setICFlagSubParts(bICFlag, 0, uiDepth);
#endif 比较已有最佳模式与2N*2N Inter算出的TempRDcost的大小;
xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));
rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);//by Competition for inter_2Nx2N
}
// SKIP
#if VCEG_AZ06_IC
if (!bICFlag)
{
#endif
#if COM16_C1016_AFFINE
#if JVET_D0077_FAST_EXT
if (rpcTempCU->getSlice()->getSPS()->getUseAffine() && !bPrevSameBlockIsIntra
#if JVET_D0077_SAVE_LOAD_ENC_INFO
&& !bFastSkipAffine
#endif
)
#else
if (rpcTempCU->getSlice()->getSPS()->getUseAffine())
#endif
{//比较已有最佳模式与AffineMerge2Nx2N算出的tempRDcost的大小;
xCheckRDCostAffineMerge2Nx2N(rpcBestCU, rpcTempCU);//H.266新加的技术;
rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
}
#endif
//比较已有最佳模式与Merge2N*2N算出的TempRdcost大小;
xCheckRDCostMerge2Nx2N(rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode);//by Merge for inter_2Nx2N
rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
#if VCEG_AZ06_IC
}
#endif
#if VCEG_AZ07_FRUC_MERGE
#if JVET_D0077_FAST_EXT
if (rpcTempCU->getSlice()->getSPS()->getUseFRUCMgrMode() && !bPrevSameBlockIsIntra
#if JVET_D0077_SAVE_LOAD_ENC_INFO
&& !bFastSkipFruc
#endif
)
#else
if (rpcTempCU->getSlice()->getSPS()->getUseFRUCMgrMode())
#endif
{
#if VCEG_AZ06_IC
rpcTempCU->setICFlagSubParts(bICFlag, 0, uiDepth);
#endif //比较已有最佳模式与Merge2N*2NFRUC的TempRDcost的大小;
xCheckRDCostMerge2Nx2NFRUC(rpcBestCU, rpcTempCU, &earlyDetectionSkipMode);//也是H.266里新加的;
rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
}
#endif
#if JVET_C0024_QTBT
#if JVET_D0077_FAST_EXT
if (!m_pcEncCfg->getUseEarlySkipDetection() && !bPrevSameBlockIsSkip && !bPrevSameBlockIsIntra
#if JVET_D0077_SAVE_LOAD_ENC_INFO
&& !bFastSkipInter
#i