文章目录

  • `EncCu::compressCtu`
  • `EncCu::xCompressCU`
  • 块划分`xCheckModeSplit`

主要参考自VTM3.0代码阅读:xCompressCU函数,针对新版本VTM做了部分补充。

EncCu::compressCtu

初始化partitioning manager QTBTPartitioner partitioner,这是VVC相比于HEVC增加的数据类型。

判断是否需要执行IBC模式(帧内块复制技术,屏幕内容编码的一种技术,参考区域不局限在相邻像素行,而是可以利用当前帧所有已编码区域,预测待编码CU)

初始化上下文指针,初始化乒乓buffertempCSbestCS,递归执行xCompressCU函数,对亮度分量进行预测。

判断是否开启了 CTU Dual Tree技术,如果开启,需要对色度分量进行一次预测。

注释后的代码如下

void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsigned ctuRsAddr, const int prevQP[], const int currQP[] )
{/*input params:cs: picture 级别的coding structurearea: 当前正编码CTU的区域ctuRsAddr: 当前正编码CTU的raster-scan顺序递归调用xCompressCU*/m_modeCtrl->initCTUEncoding( *cs.slice );cs.treeType = TREE_D;cs.slice->m_mapPltCost[0].clear();cs.slice->m_mapPltCost[1].clear();// init the partitioning managerQTBTPartitioner partitioner;partitioner.initCtu(area, CH_L, *cs.slice);// IBC:屏幕内容编码,refer to https://blog.csdn.net/BigDream123/article/details/125185687if (m_pcEncCfg->getIBCMode()){if (area.lx() == 0 && area.ly() == 0){m_pcInterSearch->resetIbcSearch();}m_pcInterSearch->resetCtuRecord();m_ctuIbcSearchRangeX = m_pcEncCfg->getIBCLocalSearchRangeX();m_ctuIbcSearchRangeY = m_pcEncCfg->getIBCLocalSearchRangeY();}// IBC优先通过哈希搜索的方式进行块匹配if (m_pcEncCfg->getIBCMode() && m_pcEncCfg->getIBCHashSearch() && (m_pcEncCfg->getIBCFastMethod() & IBC_FAST_METHOD_ADAPTIVE_SEARCHRANGE)){const int hashHitRatio = m_ibcHashMap.getHashHitRatio(area.Y()); // in percentif (hashHitRatio < 5) // 5%{m_ctuIbcSearchRangeX >>= 1;m_ctuIbcSearchRangeY >>= 1;}if (cs.slice->getNumRefIdx(REF_PIC_LIST_0) > 0){m_ctuIbcSearchRangeX >>= 1;m_ctuIbcSearchRangeY >>= 1;}}// init current context pointerm_CurrCtx = m_CtxBuffer.data();// tempCS和bestCS作为乒乓buffer(ping-pong buffer),即使用多个低速的数据预处理模块处理高速的数据输入流, e.g. 一个buffer写入数据的时候,另外一个buffer进行数据处理,// tempCS存储test模式的数据,bestCS存储当前最优模式CodingStructure *tempCS = m_pTempCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];CodingStructure *bestCS = m_pBestCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);tempCS->currQP[CH_L] = bestCS->currQP[CH_L] =tempCS->baseQP       = bestCS->baseQP       = currQP[CH_L];tempCS->prevQP[CH_L] = bestCS->prevQP[CH_L] = prevQP[CH_L];xCompressCU(tempCS, bestCS, partitioner); // 第一次调用xCompressCU对亮度分量进行预测cs.slice->m_mapPltCost[0].clear();cs.slice->m_mapPltCost[1].clear();// all signals were already copied during compression if the CTU was split - at this point only the structures are copied to the top level CSconst bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType), copyUnsplitCTUSignals,false, false, copyUnsplitCTUSignals, true);// CTU Dual-Tree: for HEVC, 亮度和色度的划分方式相同;for VVC, P&B Slice的亮度和色度划分方式相同, I Slice的亮度和色度划分方式不同, 即dual-tree// 大多数时候,亮度的纹理比色度更加细节, refer to https://blog.csdn.net/fangz142/article/details/123080611if (CS::isDualITree (cs) && isChromaEnabled (cs.pcv->chrFormat)){m_CABACEstimator->getCtx() = m_CurrCtx->start;partitioner.initCtu(area, CH_C, *cs.slice);cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);tempCS->currQP[CH_C] = bestCS->currQP[CH_C] =tempCS->baseQP       = bestCS->baseQP       = currQP[CH_C];tempCS->prevQP[CH_C] = bestCS->prevQP[CH_C] = prevQP[CH_C];xCompressCU(tempCS, bestCS, partitioner);  // 第二次调用xCompressCU对色度分量进行预测const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType),copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals, true);}if (m_pcEncCfg->getUseRateCtrl()){(m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr)).m_actualMSE = (double)bestCS->dist / (double)m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr).m_numberOfPixel;}// reset context states and uninit context pointerm_CABACEstimator->getCtx() = m_CurrCtx->start;m_CurrCtx                  = 0;// Ensure that a coding was found// Selected mode's RD-cost must be not MAX_DOUBLE.CHECK( bestCS->cus.empty()                                   , "No possible encoding found" );CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );CHECK( bestCS->cost             == MAX_DOUBLE                , "No possible encoding found" );}

EncCu::xCompressCU

xCheckModeSplit函数会调用xCompressCU尝试QT、BT和TT划分,tempCS用来存储当前compress区域各种模式下的数据,m_modeCtrl维护需要测试的mode,通过一个do-while循环,尝试各种划分模式。

void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
{/*xCheckModeSplit函数会调用xCompressCU,尝试更小的划分*/CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");// ============================== INITIALIZE ====================================== //uint32_t compBegin;uint32_t numComp;bool jointPLT = false; // palette模式if (partitioner.isSepTree( *tempCS )){if( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D ){compBegin = COMPONENT_Y;numComp = (tempCS->area.chromaFormat != CHROMA_400)?3: 1;jointPLT = true;}else{if (isLuma(partitioner.chType)){compBegin = COMPONENT_Y;numComp   = 1;}else{compBegin = COMPONENT_Cb;numComp   = 2;}}}else{compBegin = COMPONENT_Y;numComp = (tempCS->area.chromaFormat != CHROMA_400) ? 3 : 1;jointPLT = true;}SplitSeries splitmode = -1;uint8_t   bestLastPLTSize[MAX_NUM_CHANNEL_TYPE];Pel       bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT foruint8_t   curLastPLTSize[MAX_NUM_CHANNEL_TYPE];Pel       curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partitionfor (int i = compBegin; i < (compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);bestLastPLTSize[comID] = 0;curLastPLTSize[comID] = tempCS->prevPLT.curPLTSize[comID];memcpy(curLastPLT[i], tempCS->prevPLT.curPLT[i], tempCS->prevPLT.curPLTSize[comID] * sizeof(Pel));}// beginSlice&   slice      = *tempCS->slice;const PPS &pps      = *tempCS->pps;const SPS &sps      = *tempCS->sps;const uint32_t uiLPelX  = tempCS->area.Y().lumaPos().x;const uint32_t uiTPelY  = tempCS->area.Y().lumaPos().y;const ModeType modeTypeParent  = partitioner.modeType;const TreeType treeTypeParent  = partitioner.treeType;const ChannelType chTypeParent = partitioner.chType;const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );tempCS->splitRdCostBest = nullptr;m_modeCtrl->initCULevel( partitioner, *tempCS );  // 该函数对m_modeCtrl进行初始化,对需要test的各个模式进行压栈
#if GDR_ENABLED// DGR(Gradual Decoder Refresh)帧,将一个完整的I帧切片,分别放在前面的多个连续P帧中,可以避免传递I帧造成的网络流量抖动和堵塞// refer to https://blog.csdn.net/zg_xd/article/details/107870369if (m_pcEncCfg->getGdrEnabled()){bool isInGdrInterval = slice.getPicHeader()->getInGdrInterval();// 1.0 applicable to inter picture onlyif (isInGdrInterval){int gdrPocStart = m_pcEncCfg->getGdrPocStart();int gdrInterval = m_pcEncCfg->getGdrInterval();int gdrPeriod = m_pcEncCfg->getGdrPeriod();int picWidth = slice.getPPS()->getPicWidthInLumaSamples();int m1, m2, n1;int curPoc = slice.getPOC();int gdrPoc = (curPoc - gdrPocStart) % gdrPeriod;int begGdrX = 0;int endGdrX = 0;double dd = (picWidth / (double)gdrInterval);int mm = (int)((picWidth / (double)gdrInterval) + 0.49999);m1 = ((mm + 7) >> 3) << 3;m2 = ((mm + 0) >> 3) << 3;if (dd > mm && m1 == m2){m1 = m1 + 8;}n1 = (picWidth - m2 * gdrInterval) / 8;if (gdrPoc < n1){begGdrX = m1 * gdrPoc;endGdrX = begGdrX + m1;}else{begGdrX = m1 * n1 + m2 * (gdrPoc - n1);endGdrX = begGdrX + m2;if (picWidth <= endGdrX){begGdrX = picWidth;endGdrX = picWidth;}}bool isInRefreshArea = tempCS->withinRefresh(begGdrX, endGdrX);if (isInRefreshArea){m_modeCtrl->forceIntraMode();}else if (tempCS->containRefresh(begGdrX, endGdrX) || tempCS->overlapRefresh(begGdrX, endGdrX)){// 1.3.1 enable only vertical splits (QT, BT_V, TT_V)m_modeCtrl->forceVerSplitOnly();// 1.3.2 remove TT_V if it does not satisfy the conditionif (tempCS->refreshCrossTTV(begGdrX, endGdrX)){m_modeCtrl->forceRemoveTTV();}}if (tempCS->area.lwidth() != tempCS->area.lheight()){m_modeCtrl->forceRemoveQT();}if (!m_modeCtrl->anyPredModeLeft()){m_modeCtrl->forceRemoveDontSplit();}if (isInRefreshArea && !m_modeCtrl->anyIntraIBCMode() && (tempCS->area.lwidth() == 4 || tempCS->area.lheight() == 4)){m_modeCtrl->finishCULevel(partitioner);return;}}}
#endifif (partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra()&& (sps.getUseSBT() || sps.getExplicitMtsInterEnabled())){auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt*>( m_modeCtrl );int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;slsSbt->resetSaveloadSbt( maxSLSize );}m_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;m_CurrCtx->start = m_CABACEstimator->getCtx();if( slice.getUseChromaQpAdj() ){// TODO M0133 : double check encoder decisions with respect to chroma QG detection and actual encodeint lgMinCuSize = sps.getLog2MinCodingBlockSize() +std::max<int>(0, floorLog2(sps.getCTUSize()) - sps.getLog2MinCodingBlockSize() - int((slice.getCuChromaQpOffsetSubdiv()+1) / 2));if( partitioner.currQgChromaEnable() ){m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );}}else{m_cuChromaQpOffsetIdxPlus1 = 0;}// m_modeCtrl初始化后,如果没有可以test的模式,那么直接returnif( !m_modeCtrl->anyMode() ){m_modeCtrl->finishCULevel( partitioner );return;}DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cux", uiLPelX ) );DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuy", uiTPelY ) );DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuw", tempCS->area.lwidth() ) );DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuh", tempCS->area.lheight() ) );DTRACE( g_trace_ctx, D_COMMON, "@(%4d,%4d) [%2dx%2d]\n", tempCS->area.lx(), tempCS->area.ly(), tempCS->area.lwidth(), tempCS->area.lheight() );m_pcInterSearch->resetSavedAffineMotion();double bestIntPelCost = MAX_DOUBLE;if (tempCS->slice->getSPS()->getUseColorTrans()){tempCS->tmpColorSpaceCost = MAX_DOUBLE;bestCS->tmpColorSpaceCost = MAX_DOUBLE;tempCS->firstColorSpaceSelected = true;bestCS->firstColorSpaceSelected = true;}if (tempCS->slice->getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS)){tempCS->firstColorSpaceTestOnly = false;bestCS->firstColorSpaceTestOnly = false;tempCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;tempCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;bestCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;bestCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly){tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;}}double splitRdCostBest[NUM_PART_SPLIT];std::fill(std::begin(splitRdCostBest), std::end(splitRdCostBest), MAX_DOUBLE);if (tempCS->slice->getCheckLDC()){m_bestBcwCost[0] = m_bestBcwCost[1] = std::numeric_limits<double>::max();m_bestBcwIdx[0] = m_bestBcwIdx[1] = -1;}// ============================== MAIN LOOP ====================================== //do{for (int i = compBegin; i < (compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);tempCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];memcpy(tempCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));}EncTestMode currTestMode = m_modeCtrl->currTestMode();  // 获取需要test的模式currTestMode.maxCostAllowed = maxCostAllowed;if (pps.getUseDQP() && partitioner.isSepTree(*tempCS) && isChroma( partitioner.chType )){const Position chromaCentral(tempCS->area.Cb().chromaPos().offset(tempCS->area.Cb().chromaSize().width >> 1, tempCS->area.Cb().chromaSize().height >> 1));const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, tempCS->area.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, tempCS->area.chromaFormat));const CodingStructure* baseCS = bestCS->picture->cs;const CodingUnit* colLumaCu = baseCS->getCU(lumaRefPos, CHANNEL_TYPE_LUMA);if (colLumaCu){currTestMode.qp = colLumaCu->qp;}}#if SHARP_LUMA_DELTA_QP || ENABLE_QPA_SUB_CTUif (partitioner.currQgEnable() && ((m_pcEncCfg->getBIM()) ||
#if SHARP_LUMA_DELTA_QP(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()) ||
#endif(m_pcEncCfg->getSmoothQPReductionEnable()) ||
#if ENABLE_QPA_SUB_CTU(m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP())
#elsefalse
#endif)){if (currTestMode.qp >= 0){updateLambda (&slice, currTestMode.qp,#if WCG_EXT && ER_CHROMA_QP_WCG_PPSm_pcEncCfg->getWCGChromaQPControl().isEnabled(),#endifCS::isDualITree (*tempCS) || (partitioner.currDepth == 0));}}
#endifif( currTestMode.type == ETM_INTER_ME ){if( ( currTestMode.opts & ETO_IMV ) != 0 ){const bool skipAltHpelIF = ( int( ( currTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT ) == 4 ) && ( bestIntPelCost > 1.25 * bestCS->cost );if (!skipAltHpelIF){tempCS->bestCS = bestCS;xCheckRDCostInterIMV(tempCS, bestCS, partitioner, currTestMode, bestIntPelCost); // 整像素inter ME, 1 pel and 4 peltempCS->bestCS = nullptr;splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}}else{tempCS->bestCS = bestCS;xCheckRDCostInter( tempCS, bestCS, partitioner, currTestMode );  // 帧间inter ME模式tempCS->bestCS = nullptr;splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}}else if (currTestMode.type == ETM_HASH_INTER){xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode );splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if( currTestMode.type == ETM_AFFINE ){xCheckRDCostAffineMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode ); // Affine merge模式splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}
#if REUSE_CU_RESULTSelse if( currTestMode.type == ETM_RECO_CACHED ){xReuseCachedResult( tempCS, bestCS, partitioner );splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}
#endifelse if( currTestMode.type == ETM_MERGE_SKIP ){xCheckRDCostMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );  // merge模式CodingUnit* cu = bestCS->getCU(partitioner.chType);if (cu){cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;}splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if( currTestMode.type == ETM_MERGE_GEO ){xCheckRDCostMergeGeo2Nx2N( tempCS, bestCS, partitioner, currTestMode );  // mergeGeo模式splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if( currTestMode.type == ETM_INTRA ){if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS)){bool skipSecColorSpace = false;skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false));  // intra模式if ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) && !m_pcEncCfg->getRGBFormatFlag()){skipSecColorSpace = true;}if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly){xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true));}if (!tempCS->firstColorSpaceTestOnly){if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE){double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0;if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0])){tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;}}}else{CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped");}}else{xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false);}splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if (currTestMode.type == ETM_PALETTE){xCheckPLT( tempCS, bestCS, partitioner, currTestMode );splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if (currTestMode.type == ETM_IBC){xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if (currTestMode.type == ETM_IBC_MERGE){xCheckRDCostIBCModeMerge2Nx2N(tempCS, bestCS, partitioner, currTestMode);  // IBC模式splitRdCostBest[CTU_LEVEL] = bestCS->cost;tempCS->splitRdCostBest = splitRdCostBest;}else if( isModeSplit( currTestMode ) ){// QT,BT, TT划分if (bestCS->cus.size() != 0){splitmode = bestCS->cus[0]->splitSeries;}assert( partitioner.modeType == tempCS->modeType );int signalModeConsVal = tempCS->signalModeCons( getPartSplit( currTestMode ), partitioner, modeTypeParent );int numRoundRdo = signalModeConsVal == LDT_MODE_TYPE_SIGNAL ? 2 : 1;bool skipInterPass = false;for( int i = 0; i < numRoundRdo; i++ ){//change cons modesif( signalModeConsVal == LDT_MODE_TYPE_SIGNAL ){CHECK( numRoundRdo != 2, "numRoundRdo shall be 2 - [LDT_MODE_TYPE_SIGNAL]" );tempCS->modeType = partitioner.modeType = (i == 0) ? MODE_TYPE_INTER : MODE_TYPE_INTRA;}else if( signalModeConsVal == LDT_MODE_TYPE_INFER ){CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INFER]" );tempCS->modeType = partitioner.modeType = MODE_TYPE_INTRA;}else if( signalModeConsVal == LDT_MODE_TYPE_INHERIT ){CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INHERIT]" );tempCS->modeType = partitioner.modeType = modeTypeParent;}//for lite intra encoding fast algorithm, set the status to save inter coding infoif( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType == MODE_TYPE_INTER ){m_pcIntraSearch->setSaveCuCostInSCIPU( true );m_pcIntraSearch->setNumCuInSCIPU( 0 );}else if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType != MODE_TYPE_INTER ){m_pcIntraSearch->setSaveCuCostInSCIPU( false );if( tempCS->modeType == MODE_TYPE_ALL ){m_pcIntraSearch->setNumCuInSCIPU( 0 );}}// check QT、BT和TT划分xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode, modeTypeParent, skipInterPass, splitRdCostBest );tempCS->splitRdCostBest = splitRdCostBest;//recover cons modestempCS->modeType = partitioner.modeType = modeTypeParent;tempCS->treeType = partitioner.treeType = treeTypeParent;partitioner.chType = chTypeParent;if( modeTypeParent == MODE_TYPE_ALL ){m_pcIntraSearch->setSaveCuCostInSCIPU( false );if( numRoundRdo == 2 && tempCS->modeType == MODE_TYPE_INTRA ){m_pcIntraSearch->initCuAreaCostInSCIPU();}}if( skipInterPass ){break;}}
#if GDR_ENABLEDif (bestCS->cus.size() > 0 && splitmode != bestCS->cus[0]->splitSeries)
#elseif (splitmode != bestCS->cus[0]->splitSeries)
#endif{splitmode = bestCS->cus[0]->splitSeries;const CodingUnit&     cu = *bestCS->cus.front();cu.cs->prevPLT = bestCS->prevPLT;for (int i = compBegin; i < (compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);bestLastPLTSize[comID] = bestCS->cus[0]->cs->prevPLT.curPLTSize[comID];memcpy(bestLastPLT[i], bestCS->cus[0]->cs->prevPLT.curPLT[i], bestCS->cus[0]->cs->prevPLT.curPLTSize[comID] * sizeof(Pel));}}}else{THROW( "Don't know how to handle mode: type = " << currTestMode.type << ", options = " << currTestMode.opts );}} while( m_modeCtrl->nextMode( *tempCS, partitioner ) );  // m_modeCtrl->nextMode将当前mode出栈,返回bool值判断是否还有mode需要test// ============================== END LOOP ====================================== //// ============================== FINISH ====================================== //// Finishing CU, 将最优划分模式和最优划分方法都存储在bestCS中if( tempCS->cost == MAX_DOUBLE && bestCS->cost == MAX_DOUBLE ){//although some coding modes were planned to be tried in RDO, no coding mode actually finished encoding due to early termination//thus tempCS->cost and bestCS->cost are both MAX_DOUBLE; in this case, skip the following process for normal casem_modeCtrl->finishCULevel( partitioner );return;}// 更新参数// set context statesm_CABACEstimator->getCtx() = m_CurrCtx->best;// QP from last processed CU for further processing// copy the qp of the last non-chroma CUint numCUInThisNode = (int)bestCS->cus.size();if( numCUInThisNode > 1 && bestCS->cus.back()->chType == CHANNEL_TYPE_CHROMA && !CS::isDualITree( *bestCS ) ){CHECK( bestCS->cus[numCUInThisNode-2]->chType != CHANNEL_TYPE_LUMA, "wrong chType" );bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode-2]->qp;}else{bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;}if ((!slice.isIntra() || slice.getSPS()->getIBCFlag()) && partitioner.chType == CHANNEL_TYPE_LUMA&& bestCS->cus.size() == 1 && (CU::isInter(*bestCS->cus.back()) || CU::isIBC(*bestCS->cus.back()))&& bestCS->area.Y() == (*bestCS->cus.back()).Y()){const CodingUnit&     cu = *bestCS->cus.front();bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);CU::saveMotionInHMVP( cu, isIbcSmallBlk );   // 更新HMVP列表}// 将最优模式的重建像素copy给picturebestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));bestCS->picture->getRecoBuf(currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );m_modeCtrl->finishCULevel( partitioner );  // m_modelCtrl->finishCULevelif( m_pcIntraSearch->getSaveCuCostInSCIPU() && bestCS->cus.size() == 1 ){m_pcIntraSearch->saveCuAreaCostInSCIPU( Area( partitioner.currArea().lumaPos(), partitioner.currArea().lumaSize() ), bestCS->cost );}if (bestCS->cus.size() == 1) // no partition{CHECK(bestCS->cus[0]->tileIdx != bestCS->pps->getTileIdx(bestCS->area.lumaPos()), "Wrong tile index!");if (CU::isPLT(*bestCS->cus[0])){for (int i = compBegin; i < (compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));}bestCS->reorderPrevPLT(bestCS->prevPLT, bestCS->cus[0]->curPLTSize, bestCS->cus[0]->curPLT, bestCS->cus[0]->reuseflag, compBegin, numComp, jointPLT);}else{for (int i = compBegin; i<(compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));}}}else{for (int i = compBegin; i<(compBegin + numComp); i++){ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);bestCS->prevPLT.curPLTSize[comID] = bestLastPLTSize[comID];memcpy(bestCS->prevPLT.curPLT[i], bestLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));}}const CodingUnit&     cu = *bestCS->cus.front();cu.cs->prevPLT = bestCS->prevPLT;// Assert if Best prediction mode is NONE// Selected mode's RD-cost must be not MAX_DOUBLE.CHECK( bestCS->cus.empty()                                   , "No possible encoding found" );CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );CHECK( bestCS->cost             == MAX_DOUBLE                , "No possible encoding found" );
}

块划分xCheckModeSplit

该函数的主要部分是一个do-while循环,遍历子CU的区域,和x265相同的是,也会比较当前累计的cost和最优cost,用来进行提前终止。

该部分的代码和注释:

void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, const ModeType modeTypeParent, bool &skipInterPass, double *splitRdCostBest )
{const int qp                = encTestMode.qp;const Slice &slice          = *tempCS->slice;const int oldPrevQp         = tempCS->prevQP[partitioner.chType];const auto oldMotionLut     = tempCS->motionLut;  // LUT(Look Up Table) 用于熵解码
#if ENABLE_QPA_SUB_CTUconst PPS &pps              = *tempCS->pps;const uint32_t currDepth    = partitioner.currDepth;
#endifconst auto oldPLT           = tempCS->prevPLT;const PartSplit split = getPartSplit( encTestMode ); // 获取需要测试的划分模式const ModeType modeTypeChild = partitioner.modeType;  // 子节点的模式类型CHECK( split == CU_DONT_SPLIT, "No proper split provided!" );tempCS->initStructData( qp );m_CABACEstimator->getCtx() = m_CurrCtx->start;const TempCtx ctxStartSP( m_CtxCache, SubCtx( Ctx::SplitFlag,   m_CABACEstimator->getCtx() ) );const TempCtx ctxStartQt( m_CtxCache, SubCtx( Ctx::SplitQtFlag, m_CABACEstimator->getCtx() ) );const TempCtx ctxStartHv( m_CtxCache, SubCtx( Ctx::SplitHvFlag, m_CABACEstimator->getCtx() ) );const TempCtx ctxStart12( m_CtxCache, SubCtx( Ctx::Split12Flag, m_CABACEstimator->getCtx() ) );const TempCtx ctxStartMC( m_CtxCache, SubCtx( Ctx::ModeConsFlag, m_CABACEstimator->getCtx() ) );m_CABACEstimator->resetBits();m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );  // 设置划分CU的模式m_CABACEstimator->mode_constraint( split, *tempCS, partitioner, modeTypeChild ); // 设置模式限制double costTemp = 0;// m_pcEncCfg->getFastAdaptCostPredMode()   "Mode for split cost prediction, 0..2 (Default: 0)"if( m_pcEncCfg->getFastAdaptCostPredMode() == 2 ){int numChild = 3;if( split == CU_VERT_SPLIT || split == CU_HORZ_SPLIT ){numChild--;}else if( split == CU_QUAD_SPLIT ){numChild++;}int64_t approxBits = numChild << SCALE_BITS;const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.11 : 1.085 )+ ( isChroma( partitioner.chType ) ? 0.2 : 0.0 ); // 计算cost的因子,如果当前块的QP>30,设置为1.11,否则设置为1.085costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + approxBits + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;}else if ( m_pcEncCfg->getFastAdaptCostPredMode() == 1) {const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 )+ (isChroma(partitioner.chType) ? 0.2 : 0.0);costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;}else{// Defaultconst double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;}tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();if( !tempCS->useDbCost ){CHECK( bestCS->costDbOffset != 0, "error" );}const double cost = costTemp;  // 更新上下文参数m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag,   ctxStartSP );m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitQtFlag, ctxStartQt );m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitHvFlag, ctxStartHv );m_CABACEstimator->getCtx() = SubCtx( Ctx::Split12Flag, ctxStart12 );m_CABACEstimator->getCtx() = SubCtx( Ctx::ModeConsFlag, ctxStartMC );// 如果上下文预测的cost > bestCS.cost,则重置上下文状态并确认beseCSif (cost > bestCS->cost + bestCS->costDbOffset
#if ENABLE_QPA_SUB_CTU|| (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP() && (slice.getCuQpDeltaSubdiv() > 0) && (split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT) &&(currDepth == 0)) // force quad-split or no split at CTU level
#endif){xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );return;}const bool chromaNotSplit = modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTRA ? true : false; // 父节点的所有类型都ok,且子节点为MODE_TYPE_INTRA时, chromaNotSplit=Trueif( partitioner.treeType != TREE_D ){tempCS->treeType = TREE_L;}else{if( chromaNotSplit ){CHECK( partitioner.chType != CHANNEL_TYPE_LUMA, "chType must be luma" );tempCS->treeType = partitioner.treeType = TREE_L;}else{// 只有当色度要划分的时候,tempCS.treeType才是dual treetempCS->treeType = partitioner.treeType = TREE_D;}}partitioner.splitCurrArea( split, *tempCS ); // 根据划分模式确定划分区域, refer to https://blog.csdn.net/dfhg54/article/details/124828138// QG(Quantization group): 把一帧图像划分为固定大小的正方形像素块,大小由PPS指定,且必须处于CTU和最小CU之间,//                         同一个QG内的所有非零系数的CU共享一个QP; HEVC中使用相邻QG的信息预测当前QG内的QP, predQP = (QP_A + Qp_B + 1) >> 1bool qgEnableChildren = partitioner.currQgEnable(); // QG possible at children levelbool qgChromaEnableChildren = partitioner.currQgChromaEnable(); // Chroma QG possible at children levelm_CurrCtx++;// 将重建像素和预测像素都置零tempCS->getRecoBuf().fill( 0 );tempCS->getPredBuf().fill(0);AffineMVInfo tmpMVInfo;bool isAffMVInfoSaved;
#if GDR_ENABLEDAffineMVInfoSolid tmpMVInfoSolid;m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, tmpMVInfoSolid, isAffMVInfoSaved);
#elsem_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, isAffMVInfoSaved);
#endifBlkUniMvInfo tmpUniMvInfo;bool         isUniMvInfoSaved = false;if (!tempCS->slice->isIntra()){m_pcInterSearch->savePrevUniMvInfo(tempCS->area.Y(), tmpUniMvInfo, isUniMvInfoSaved);}do{const auto &subCUArea  = partitioner.currArea();  // 将测试的子区域的YCbCr分量的位置和宽高赋给subCUArea;// auto作为C/C++存储类型之一,自动存储期,即当退出程序块的时候自动销毁;// auto可以自动推断数据类型(编译期),可以代替复杂冗长的类型声明// auto类型不能被声明为返回值、形参 和 模板参数if( tempCS->picture->Y().contains( subCUArea.lumaPos() ) ){const unsigned wIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lwidth () );  // 返回宽高的对数作为索引值,例如宽=64,wIdx=6const unsigned hIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lheight() );CodingStructure *tempSubCS = m_pTempCS[wIdx][hIdx]; // 从已划分CU的wIdx和hIdx推出新的tempCsCodingStructure *bestSubCS = m_pBestCS[wIdx][hIdx];tempCS->initSubStructure( *tempSubCS, partitioner.chType, subCUArea, false );  // 初始化子CU的节点,将CTU级别的信息传输给子CU节点, refer to https://blog.csdn.net/dfhg54/article/details/124304502tempCS->initSubStructure( *bestSubCS, partitioner.chType, subCUArea, false );tempSubCS->bestParent = bestSubCS->bestParent = bestCS;double newMaxCostAllowed = isLuma(partitioner.chType) ? std::min(encTestMode.maxCostAllowed, bestCS->cost - m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist)) : MAX_DOUBLE;newMaxCostAllowed = std::max(0.0, newMaxCostAllowed);xCompressCU(tempSubCS, bestSubCS, partitioner, newMaxCostAllowed);  // 递归调用xCompressCUtempSubCS->bestParent = bestSubCS->bestParent = nullptr;// 子CU的cost是正无穷(?),结束划分if( bestSubCS->cost == MAX_DOUBLE ){CHECK( split == CU_QUAD_SPLIT, "Split decision reusing cannot skip quad split" );tempCS->cost = MAX_DOUBLE;tempCS->costDbOffset = 0;tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();m_CurrCtx--;partitioner.exitCurrSplit();xCheckBestMode( tempCS, bestCS, partitioner, encTestMode ); // 检查当前模式是否为最佳模式,若为最佳模式则与 bestCS 交换if( partitioner.chType == CHANNEL_TYPE_LUMA ){tempCS->motionLut = oldMotionLut;}return;}bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;  // 是否保存残差信号tempCS->useSubStructure( *bestSubCS, partitioner.chType, CS::getArea( *tempCS, subCUArea, partitioner.chType ),KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi, true ); // 将子CU的最优模式copy到tempCS中if( partitioner.currQgEnable() ){tempCS->prevQP[partitioner.chType] = bestSubCS->prevQP[partitioner.chType]; // 如果当前QG可用,使用子CU prevQP 更新当前 tempCS 的prevQP}// 循环遍历所有子CU,check是否有不相容的模式if( partitioner.isConsInter() ){for( int i = 0; i < bestSubCS->cus.size(); i++ ){CHECK(!CU::isInter(*bestSubCS->cus[i]), "all CUs must be inter mode in an Inter coding region (SCIPU)");}}else if( partitioner.isConsIntra() ){for( int i = 0; i < bestSubCS->cus.size(); i++ ){CHECK(CU::isInter(*bestSubCS->cus[i]), "all CUs must not be inter mode in an Intra coding region (SCIPU)");}}tempSubCS->releaseIntermediateData();bestSubCS->releaseIntermediateData();// 如果当前不在I Slice,而且限制只能test帧内if( !tempCS->slice->isIntra() && partitioner.isConsIntra() ){tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );// 如果计算到目前的tempCS的cost已经大于bestCS的cost,则提前终止块划分的尝试if( tempCS->cost > bestCS->cost ){tempCS->cost = MAX_DOUBLE;tempCS->costDbOffset = 0;tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();m_CurrCtx--;partitioner.exitCurrSplit();if( partitioner.chType == CHANNEL_TYPE_LUMA ){tempCS->motionLut = oldMotionLut;}return;}}}} while( partitioner.nextPart( *tempCS ) ); // 尝试所有的划分子CUpartitioner.exitCurrSplit();m_CurrCtx--;if( chromaNotSplit ){// 这部分代码需要refer to https://blog.csdn.net/dfhg54/article/details/124291272//Note: In local dual tree region, the chroma CU refers to the central luma CU's QP.//If the luma CU QP shall be predQP (no residual in it and before it in the QG), it must be revised to predQP before encoding the chroma CU//Otherwise, the chroma CU uses predQP+deltaQP in encoding but is decoded as using predQP, thus causing encoder-decoded mismatch on chroma qp.if( tempCS->pps->getUseDQP() ){//find parent CS that including all coded CUs in the QG before this nodeCodingStructure* qgCS = tempCS;bool deltaQpCodedBeforeThisNode = false;if( partitioner.currArea().lumaPos() != partitioner.currQgPos ){int numParentNodeToQgCS = 0;while( qgCS->area.lumaPos() != partitioner.currQgPos ){CHECK( qgCS->parent == nullptr, "parent of qgCS shall exsit" );qgCS = qgCS->parent;numParentNodeToQgCS++;}//check whether deltaQP has been coded (in luma CU or luma&chroma CU) before this nodeCodingStructure* parentCS = tempCS->parent;// 逐层遍历当前父节点的所有CU,检查deltaQP在当前节点之前是否已经编码for( int i = 0; i < numParentNodeToQgCS; i++ ){//checking each parentCHECK( parentCS == nullptr, "parentCS shall exsit" );for( const auto &cu : parentCS->cus ){if( cu->rootCbf && !isChroma( cu->chType ) ){deltaQpCodedBeforeThisNode = true;break;}}parentCS = parentCS->parent;}}//revise luma CU qp before the first luma CU with residual in the SCIPU to predQPif( !deltaQpCodedBeforeThisNode ){//get pred QP of the QGconst CodingUnit* cuFirst = qgCS->getCU( CHANNEL_TYPE_LUMA );CHECK( cuFirst->lumaPos() != partitioner.currQgPos, "First cu of the Qg is wrong" );int predQp = CU::predictQP( *cuFirst, qgCS->prevQP[CHANNEL_TYPE_LUMA] );//revise to predQPint firstCuHasResidual = (int)tempCS->cus.size();for( int i = 0; i < tempCS->cus.size(); i++ ){if( tempCS->cus[i]->rootCbf ){firstCuHasResidual = i;break;}}for( int i = 0; i < firstCuHasResidual; i++ ){tempCS->cus[i]->qp = predQp;}}}assert( tempCS->treeType == TREE_L );uint32_t numCuPuTu[6];tempCS->picture->cs->getNumCuPuTuOffset( numCuPuTu );tempCS->picture->cs->useSubStructure( *tempCS, partitioner.chType, CS::getArea( *tempCS, partitioner.currArea(), partitioner.chType ), false, true, false, false, false );if (isChromaEnabled(tempCS->pcv->chrFormat)){partitioner.chType = CHANNEL_TYPE_CHROMA;tempCS->treeType = partitioner.treeType = TREE_C;m_CurrCtx++;const unsigned   wIdx         = gp_sizeIdxInfo->idxFrom(partitioner.currArea().lwidth());const unsigned   hIdx         = gp_sizeIdxInfo->idxFrom(partitioner.currArea().lheight());CodingStructure *tempCSChroma = m_pTempCS2[wIdx][hIdx];CodingStructure *bestCSChroma = m_pBestCS2[wIdx][hIdx];tempCS->initSubStructure(*tempCSChroma, partitioner.chType, partitioner.currArea(), false);tempCS->initSubStructure(*bestCSChroma, partitioner.chType, partitioner.currArea(), false);tempCS->treeType = TREE_D;xCompressCU(tempCSChroma, bestCSChroma, partitioner);// attach chromaCS to luma CS and update costbool keepResi = KEEP_PRED_AND_RESI_SIGNALS;// bestCSChroma->treeType = tempCSChroma->treeType = TREE_C;CHECK(bestCSChroma->treeType != TREE_C || tempCSChroma->treeType != TREE_C, "wrong treeType for chroma CS");tempCS->useSubStructure(*bestCSChroma, partitioner.chType,CS::getArea(*bestCSChroma, partitioner.currArea(), partitioner.chType),KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, true, true);// release tmp resourcetempCSChroma->releaseIntermediateData();bestCSChroma->releaseIntermediateData();// tempCS->picture->cs->releaseIntermediateData();m_CurrCtx--;}tempCS->picture->cs->clearCuPuTuIdxMap( partitioner.currArea(), numCuPuTu[0], numCuPuTu[1], numCuPuTu[2], numCuPuTu + 3 );//recover luma tree statuspartitioner.chType = CHANNEL_TYPE_LUMA;partitioner.treeType = TREE_D;partitioner.modeType = MODE_TYPE_ALL;}else{if (!qgChromaEnableChildren) // check at deepest cQG level only{xCheckChromaQPOffset( *tempCS, partitioner );}}// Finally, generate split-signaling bits for RD-cost checkconst PartSplit implicitSplit = partitioner.getImplicitSplit( *tempCS );{bool enforceQT = implicitSplit == CU_QUAD_SPLIT;// LARGE CTU bugif( m_pcEncCfg->getUseFastLCTU() ){unsigned minDepth = 0;unsigned maxDepth = floorLog2(tempCS->sps->getCTUSize()) - floorLog2(tempCS->sps->getMinQTSize(slice.getSliceType(), partitioner.chType));if( auto ad = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner ) ){ad->setMaxMinDepth( minDepth, maxDepth, *tempCS );}if( minDepth > partitioner.currQtDepth ){// enforce QTenforceQT = true;}}if( !enforceQT ){m_CABACEstimator->resetBits();m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );partitioner.modeType = modeTypeParent;m_CABACEstimator->mode_constraint( split, *tempCS, partitioner, modeTypeChild );tempCS->fracBits += m_CABACEstimator->getEstFracBits(); // split bits}}tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );  // 增加划分标志的影响// Check Delta QP bits for splitted structureif( !qgEnableChildren ) // check at deepest QG level only{xCheckDQP(*tempCS, partitioner, true);}// If the configuration being tested exceeds the maximum number of bytes for a slice / slice-segment, then// a proper RD evaluation cannot be performed. Therefore, termination of the// slice/slice-segment must be made prior to this CTU.// This can be achieved by forcing the decision to be that of the rpcTempCU.// The exception is each slice / slice-segment must have at least one CTU.if (bestCS->cost != MAX_DOUBLE){}else{bestCS->costDbOffset = 0;}tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();if( tempCS->cus.size() > 0 && modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTER ){int areaSizeNoResiCu = 0;for( int k = 0; k < tempCS->cus.size(); k++ ){areaSizeNoResiCu += (tempCS->cus[k]->rootCbf == false) ? tempCS->cus[k]->lumaSize().area() : 0;}if( areaSizeNoResiCu >= (tempCS->area.lumaSize().area() >> 1) ){skipInterPass = true;}}splitRdCostBest[getPartSplit(encTestMode)] = tempCS->cost;// RD check for sub partitioned coding structure.xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );  // 比较tempCS和bestCS#if GDR_ENABLEDif (isAffMVInfoSaved){m_pcInterSearch->addAffMVInfo(tmpMVInfo, tmpMVInfoSolid);}
#elseif (isAffMVInfoSaved){m_pcInterSearch->addAffMVInfo(tmpMVInfo);}
#endifif (!tempCS->slice->isIntra() && isUniMvInfoSaved){m_pcInterSearch->addUniMvInfo(tmpUniMvInfo);}tempCS->motionLut = oldMotionLut;tempCS->prevPLT   = oldPLT;tempCS->releaseIntermediateData();tempCS->prevQP[partitioner.chType] = oldPrevQp;
}

VVC系列(三)xCompressCTU、xCompressCU和xCheckModeSplit解析相关推荐

  1. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  2. 数据库分库分表(sharding)系列(三) 关于使用框架还是自主开发以及sharding实现层面的考量...

    当团队对系统业务和数据库进行了细致的梳理,确定了切分方案后,接下来的问题就是如何去实现切分方案了,目前在sharding方面有不少的开源框架和产 品可供参考,同时很多团队也会选择自主开发实现,而不管是 ...

  3. sed修炼系列(三):sed高级应用之实现窗口滑动技术

    sed系列文章: sed修炼系列(一):花拳绣腿之入门篇 sed修炼系列(二):武功心法(info sed翻译+注解) sed修炼系列(三):sed高级应用之实现窗口滑动技术 sed修炼系列(四):s ...

  4. Web 开发人员和设计师必读文章推荐【系列三十】

    <Web 前端开发精华文章推荐>2014年第9期(总第30期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  5. 数据与广告系列三十二:重排ReRank,广告推荐算法链路上的背叛者,生态系统格局的重塑者...

    作者·黄崇远 『数据虫巢』 全文23138字 题图ssyer.com " 在推荐系统又或者计算广告中,重排ReRank明目张胆的把召回.粗排.精排几个链路逻辑辛苦生成的序给打乱,但却敢号称是 ...

  6. python爬虫入门教程(三):淘女郎爬虫 ( 接口解析 | 图片下载 )

    2019/10/28更新 网站已改版,代码已失效(其实早就失效了,但我懒得改...)此博文仅供做思路上的参考 代码使用python2编写,因已失效,就未改写成python3 爬虫入门系列教程: pyt ...

  7. 【第21天】SQL进阶-查询优化- performance_schema系列三:事件记录(SQL 小虚竹)

    回城传送–><32天SQL筑基> 文章目录 零.前言 一.练习题目 二.SQL思路 SQL进阶-查询优化- performance_schema系列三:事件记录 等待事件表 even ...

  8. [常微分方程的数值解法系列三] 改进欧拉法(预估校正法)

    改进欧拉法 简介 预估-校正 截断误差 例子 在惯性导航以及VIO等实际问题中利用IMU求解位姿需要对IMU测量值进行积分得到需要的位置和姿态,其中主要就是求解微分方程.但之前求解微分方程的解析方法主 ...

  9. [算法系列] 深入递归本质+经典例题解析——如何逐步生成, 以此类推,步步为营

    [算法系列] 深入递归本质+经典例题解析--如何逐步生成, 以此类推,步步为营 本文是递归系列的第三篇, 第一篇介绍了递归的形式以及递归设计方法(迭代改递归),;第二篇以递归为引子, 详细介绍了快排和 ...

  10. Kylin源码分析系列三—rowKey编码

    Kylin源码分析系列三-rowKey编码 注:Kylin源码分析系列基于Kylin的2.5.0版本的源码,其他版本可以类比. 1. 相关概念 前面介绍了Kylin中Cube构建的流程,但Cube数据 ...

最新文章

  1. maven 如何看jar是否被修改_如何在线修改jar文件
  2. About Instruments
  3. uva10160(dfs+状态压缩)
  4. 【渝粤教育】电大中专就业指导作业 题库
  5. java listen_JavaWeb之Filter、Listener
  6. 一加神秘新机入网:搭载天玑8100 首发长寿版150W超级闪充
  7. 简单详细的OD破解教程
  8. 前端开发:报错Error in created hook:”SyntaxError:Unexpected token u in JSON at position 0”…解决方法
  9. 上传项目源码至Nexus私服
  10. 因为计算机中丢失lua.dll,lua51.dll丢失修复
  11. tomcat闪退没有报错_越狱后直接换sileo商店附Sileo的部分报错解决办法
  12. Easy Less生成.ttss后缀文件的配置
  13. 这些题你hold住吗?
  14. FastReport.Net报表工具 vs RDL标准报表定义语言
  15. 教你在线免费PDF转Word,建议收藏
  16. tableau用数据分组_对数据进行分组
  17. EasyExcel表头校验,表内容校验
  18. iphone输入法换行_iphone打字怎么换行?iphone输入法换行教程
  19. 小程序云开发(一):新建云开发模板
  20. QQ 引流引发的灰色地带产业

热门文章

  1. 写口算用计算机作文600字,口算比赛作文600字
  2. Xshell——连接服务器
  3. 计算机专业的求职信英文版带翻译,[求职信英语作文带翻译]求职信的英语作文4篇...
  4. PHP遍历数组,分别将内容加入到table表格中
  5. 投入产出表之影响力系数和感应度系数的计算--基于Excel
  6. python使用www.ip138.com作为解析对象 获取公网ip及ip所属地区
  7. 计算机前置usb应用,usb前面不能用,教您解决电脑前置USB接口不能使用
  8. linux软件安装与卸载
  9. 百度人脸识别申请授权文件步骤要领
  10. 《德鲁克管理思想精要》读书笔记小结