H.266/VVC

编程入门 行业动态 更新时间:2024-10-28 08:17:53

H.266/<a href=https://www.elefans.com/category/jswz/34/1748086.html style=VVC"/>

H.266/VVC

H.266/VVC专栏传送

上一篇:H.266/VVC-VTM代码学习19-CU层确定测试模式函数initCULevel
下一篇:H.266/VVC-VTM代码学习21-对划分模式进行RDO的函数xCheckModeSplit

目录

  • H.266/VVC专栏传送
    • 前言
    • 一、函数作用
    • 二、函数详解

前言

VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。

本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。

VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)

一、函数作用

函数 xCompressCU 在 VTM10.0 的 EncCu.cpp 中,主要负责遍历 m_ComprCUCtxList 列表并调用相应的函数进行 RDO。通过了解、调整这部分代码,可以调整 encoder 进行 RD 测试的 mode、split、QP 等,从而实现提升编码性能。

建立 m_ComprCUCtxList 列表的过程请查阅 H.266/VVC-VTM代码学习19-CU层确定测试模式函数initCULevel

Intra 模式具体的 RDO 过程请查阅 H.266/VVC-VTM代码学习-帧内预测11-编码端亮度块模式选择estIntraPredLumaQT函数

二、函数详解

void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
{CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");
#if ENABLE_SPLIT_PARALLELISMCHECK( m_dataId != tempCS->picture->scheduler.getDataId(), "Working in the wrong dataId!" );if( m_pcEncCfg->getNumSplitThreads() != 1 && tempCS->picture->scheduler.getSplitJobId() == 0 ){if( m_modeCtrl->isParallelSplit( *tempCS, partitioner ) ){m_modeCtrl->setParallelSplit( true );xCompressCUParallel( tempCS, bestCS, partitioner );return;}}#endifuint32_t compBegin;uint32_t numComp;bool jointPLT = false;if (partitioner.isSepTree( *tempCS )){// 若当前不是 DualTree 且划分树类型不是 TREE_D// 即为 joint tree 时// 则起始分量为 Y,总分量数400时为1,否则为3,使用 jointPLTif( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D ){compBegin = COMPONENT_Y;numComp = (tempCS->area.chromaFormat != CHROMA_400)?3: 1;jointPLT = true;}else{// 若当前是 DualTree 且划分树类型是 TREE_D 且当前为亮度通道时// 即当前是 TREE_L// 则起始分量为 Y,总分量数为1if (isLuma(partitioner.chType)){compBegin = COMPONENT_Y;numComp = 1;}// 若当前是 DualTree 且划分树类型是 TREE_D 且当前为色度通道时// 即当前是 TREE_C// 则起始分量为 Cb,总分量数为2else{compBegin = COMPONENT_Cb;numComp = 2;}}}// 若当前不是 DualTree 且划分树类型是 TREE_D 时// 即为 joint tree 时// 则起始分量为 Y,总分量数400时为1,否则为3,使用 jointPLTelse{compBegin = COMPONENT_Y;numComp = (tempCS->area.chromaFormat != CHROMA_400) ? 3 : 1;jointPLT = true;}SplitSeries splitmode = -1;// 以下是用于存储 PLT mode 的相关变量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 partition// 遍历所有分量,为 PLT mode 的存储变量赋值for (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));}// 当前 sliceSlice&   slice      = *tempCS->slice;// 当前 ppsconst PPS &pps      = *tempCS->pps;// 当前 spsconst SPS &sps      = *tempCS->sps;// 当前块的左上角xconst uint32_t uiLPelX  = tempCS->area.Y().lumaPos().x;// 当前块的左上角yconst uint32_t uiTPelY  = tempCS->area.Y().lumaPos().y;// 父结点的模式类型(MODE_TYPE_ALL / MODE_TYPE_INTER / MODE_TYPE_INTRA)const ModeType modeTypeParent  = partitioner.modeType;// 父结点的划分树类型(TREE_D / TREE_L / TREE_C)const TreeType treeTypeParent  = partitioner.treeType;// 父结点的通道类型(CHANNEL_TYPE_LUMA / CHANNEL_TYPE_CHROMA)const ChannelType chTypeParent = partitioner.chType;// 获取当前区域const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );// CU 层初始化(获取 CU 层 RDO 的模式、划分等)m_modeCtrl->initCULevel( partitioner, *tempCS );// 对使用子块变换(sub-block transform,SBT)情况的初始化if( partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra() && ( sps.getUseSBT() || sps.getUseInterMTS() ) ){auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt*>( m_modeCtrl );int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;slsSbt->resetSaveloadSbt( maxSLSize );
#if ENABLE_SPLIT_PARALLELISMCHECK( tempCS->picture->scheduler.getSplitJobId() != 0, "The SBT search reset need to happen in sequential region." );if (m_pcEncCfg->getNumSplitThreads() > 1){for (int jId = 1; jId < NUM_RESERVERD_SPLIT_JOBS; jId++){auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt *>(m_pcEncLib->getCuEncoder(jId)->m_modeCtrl);slsSbt->resetSaveloadSbt(maxSLSize);}}
#endif}// 用于存储子块变换(sub-block transform,SBT)模式的 costm_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;// 上下文m_CurrCtx->start = m_CABACEstimator->getCtx();// if 0, then cu_chroma_qp_offset_flag will be 0, otherwise cu_chroma_qp_offset_flag will be 1.m_cuChromaQpOffsetIdxPlus1 = 0;// 若使用色度QP调整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() / 2));if( partitioner.currQgChromaEnable() ){m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );}}// 若 RDO 测试列表为空,则完成当前层次的 RDO,直接返回if( !m_modeCtrl->anyMode() ){m_modeCtrl->finishCULevel( partitioner );return;}// 以下是 RDO 前的初始化// 记录当前的位置、尺寸信息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() );// 初始化 Affine(仿射运动补偿预测)模式的相关变量m_pcInterSearch->resetSavedAffineMotion();double bestIntPelCost = MAX_DOUBLE;// 若使用色彩变换// 当前CS与最佳CS的 cost 均初始化为最大值,且选中第一色彩空间if (tempCS->slice->getSPS()->getUseColorTrans()){tempCS->tmpColorSpaceCost = MAX_DOUBLE;bestCS->tmpColorSpaceCost = MAX_DOUBLE;tempCS->firstColorSpaceSelected = true;bestCS->firstColorSpaceSelected = true;}// 若使用色彩变换,且不是双重intra树时// 当前CS与最佳CS均设置不止测试第一色彩空间,且两色彩空间的 Intra cost 初始化为最大值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;// 若当前 CS 的最佳父结点存在,且最佳父结点只测试第一色彩空间、// 则当前 CS 与最佳 CS 均只测试第一色彩空间if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly){tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;}}// 以下 do 循环开始遍历 m_ComprCUCtxList 中的测试模式(划分、模式、QP)do{// 遍历分量,将 PLT 的相关变量填充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();// 当前测试模式的允许最大 costcurrTestMode.maxCostAllowed = maxCostAllowed;// 若使用 DQP 且是 SepTree 且当前为色度分量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);// 若对应亮度位置存在,则当前测试模式 QP 值设定为对应 bestCS 亮度位置的 QP 值if (colLumaCu){currTestMode.qp = colLumaCu->qp;}}// 若当前 QG 可用,且使用亮度 delta QP 映射或(使用 perceptQPA,且不使用 rate control,且使用DQP) 
#if SHARP_LUMA_DELTA_QP || ENABLE_QPA_SUB_CTUif (partitioner.currQgEnable() && (
#if SHARP_LUMA_DELTA_QP(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()) ||
#endif
#if ENABLE_QPA_SUB_CTU(m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP())
#elsefalse
#endif)){
#if ENABLE_SPLIT_PARALLELISMCHECK( tempCS->picture->scheduler.getSplitJobId() > 0, "Changing lambda is only allowed in the master thread!" );
#endif// 若当前测试模式的 QP 值有效,则更新 lambdaif (currTestMode.qp >= 0){updateLambda (&slice, currTestMode.qp,#if WCG_EXT && ER_CHROMA_QP_WCG_PPSm_pcEncCfg->getWCGChromaQPControl().isEnabled(),#endifCS::isDualITree (*tempCS) || (partitioner.currDepth == 0));}}
#endif// 若当前测试模式是 INTERif( currTestMode.type == ETM_INTER_ME ){// imv parameter (bit 1-3) 不全为 0if( ( currTestMode.opts & ETO_IMV ) != 0 ){const bool skipAltHpelIF = ( int( ( currTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT ) == 4 ) && ( bestIntPelCost > 1.25 * bestCS->cost );// 若 parameter 为 100,且 bestCS->cost * 1.25 < bestIntPelCost 时,检查 InterIMV 的 RD costif (!skipAltHpelIF){tempCS->bestCS = bestCS;xCheckRDCostInterIMV(tempCS, bestCS, partitioner, currTestMode, bestIntPelCost);tempCS->bestCS = nullptr;}}// imv parameter (bit 1-3)全为 0// 则检查 Inter 的 RD costelse{tempCS->bestCS = bestCS;xCheckRDCostInter( tempCS, bestCS, partitioner, currTestMode );tempCS->bestCS = nullptr;}}// 若当前测试模式是 HASH INTER// 则检查 HashInter 的 RD costelse if (currTestMode.type == ETM_HASH_INTER){xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode );}// 若当前测试模式是 AFFINE// 则检查 Affine 的 RD costelse if( currTestMode.type == ETM_AFFINE ){xCheckRDCostAffineMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );}
#if REUSE_CU_RESULTS// 若当前测试模式是 RECO_CACHED// 则检查 Reuse Cached 的 RD costelse if( currTestMode.type == ETM_RECO_CACHED ){xReuseCachedResult( tempCS, bestCS, partitioner );}
#endif// 若当前测试模式是 MERGE_SKIP// 则检查 Merge 的 RD costelse if( currTestMode.type == ETM_MERGE_SKIP ){xCheckRDCostMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );CodingUnit* cu = bestCS->getCU(partitioner.chType);// 若 bestCS 的 cu 不使用 skip,则不使用 mmvd(Merge mode with MVD)if (cu)cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;}// 若当前测试模式是 MERGE_GEO// 则检查 Merge Geo 的 RD costelse if( currTestMode.type == ETM_MERGE_GEO ){xCheckRDCostMergeGeo2Nx2N( tempCS, bestCS, partitioner, currTestMode );}// 若当前测试模式是 INTRAelse if( currTestMode.type == ETM_INTRA ){// 若使用色彩变换,且不是 Intra Dual Treeif (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS)){// 不跳过第二色彩空间bool skipSecColorSpace = false;// 检查 Intra 的 RD cost,且使用 RGB 格式时才使用色彩变换skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false));// 若 cost 模式是 COST_LOSSLESS_CODING,且 slice 层 lossless 有效,且不使用 RGB 模式// 则跳过第二色彩空间if ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) && !m_pcEncCfg->getRGBFormatFlag()){skipSecColorSpace = true;}// 若不跳过第二色彩空间,且不止测试第一色彩空间// 则检查 Intra 的 RD cost,且不使用 RGB 格式时才使用色彩变换if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly){xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true));}// 若不止测试第一色彩空间if (!tempCS->firstColorSpaceTestOnly){// 若当前两个色彩空间存储的 intra cost 均不为正无穷if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE){// 使用 RGB:1.1     不使用 RGB:1.0double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0;// 若当前测试结果中第二色彩空间的 cost 值 > 第一色彩空间的 cost 值 * ratio// 则仅测试第一色彩空间if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0])){tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;}}}// 若只测试第一色彩空间else{// 第二色彩空间的 cost 不为正无穷则应当报错CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped");}}// 若不使用色彩变换,或是 Intra Dual Treeelse{// 不使用色彩变换的情况下测试 Intra RD costxCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false);}}// 若当前测试模式是 PALETTE// 则检查 PLT 的 RD costelse if (currTestMode.type == ETM_PALETTE){xCheckPLT( tempCS, bestCS, partitioner, currTestMode );}// 若当前测试模式是 IBC(Intra block copy)// 则检查 IBC 的 RD costelse if (currTestMode.type == ETM_IBC){xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);}// 若当前测试模式是 IBC_MERGE// 则检查 IBC_MERGE 的 RD costelse if (currTestMode.type == ETM_IBC_MERGE){xCheckRDCostIBCModeMerge2Nx2N(tempCS, bestCS, partitioner, currTestMode);}// 若当前测试模式划分模式(ETM_SPLIT_QT、ETM_SPLIT_BT_H、ETM_SPLIT_BT_V、ETM_SPLIT_TT_H、ETM_SPLIT_TT_V)else if( isModeSplit( currTestMode ) ){// 若当前 bestCS 的 cu 数量不为 0if (bestCS->cus.size() != 0){// 将划分模式设定为 bestCS 指定模式splitmode = bestCS->cus[0]->splitSeries;}assert( partitioner.modeType == tempCS->modeType );// 获得当前信号模式值(LDT_MODE_TYPE_INHERIT、LDT_MODE_TYPE_INFER、LDT_MODE_TYPE_SIGNAL)int signalModeConsVal = tempCS->signalModeCons( getPartSplit( currTestMode ), partitioner, modeTypeParent );// 若当前信号模式值为 LDT_MODE_TYPE_SIGNAL(Need to signal mode_constraint_flag, and the modeType of the region is determined by the flag),则 RDO 轮次为2,否则为1int numRoundRdo = signalModeConsVal == LDT_MODE_TYPE_SIGNAL ? 2 : 1;bool skipInterPass = false;// 遍历 RDO 轮次for( int i = 0; i < numRoundRdo; i++ ){//change cons modes// 若当前信号模式值为 LDT_MODE_TYPE_SIGNAL// 则当前测试模式类型在第一轮设置为 INTER,在第二轮设置为 INTRAif( 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;}// 若当前信号模式值为 LDT_MODE_TYPE_INFER// 则当前测试模式类型为 INTRAelse if( signalModeConsVal == LDT_MODE_TYPE_INFER ){CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INFER]" );tempCS->modeType = partitioner.modeType = MODE_TYPE_INTRA;}// 若当前信号模式值为 LDT_MODE_TYPE_INHERIT// 则当前测试模式类型为父结点模式类型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 info// 若父结点模式类型为 MODE_TYPE_ALL(可以尝试所有类型),且当前测试模式类型为 Inter// 则 SaveCuCostInSCIPU,且设置 NumCuInSCIPU 为0if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType == MODE_TYPE_INTER ){m_pcIntraSearch->setSaveCuCostInSCIPU( true );m_pcIntraSearch->setNumCuInSCIPU( 0 );}// 若父结点模式类型为 MODE_TYPE_ALL(可以尝试所有类型),且当前测试模式类型不为 Inter// 则 SaveCuCostInSCIPU 为 false,且当前测试模式 MODE_TYPE_ALL 时设置 NumCuInSCIPU 为0else if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType != MODE_TYPE_INTER ){m_pcIntraSearch->setSaveCuCostInSCIPU( false );if( tempCS->modeType == MODE_TYPE_ALL ){m_pcIntraSearch->setNumCuInSCIPU( 0 );}}// 测试划分模式 RD costxCheckModeSplit( tempCS, bestCS, partitioner, currTestMode, modeTypeParent, skipInterPass );//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;}}// 若划分模式不为 bestCS 指定模式if (splitmode != bestCS->cus[0]->splitSeries){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 ) );//// Finishing CU
#if ENABLE_SPLIT_PARALLELISMif( bestCS->cus.empty() ){CHECK( bestCS->cost != MAX_DOUBLE, "Cost should be maximal if no encoding found" );CHECK( bestCS->picture->scheduler.getSplitJobId() == 0, "Should always get a result in serial case" );m_modeCtrl->finishCULevel( partitioner );return;}#endifif( 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 && (bestCS->cus.back()->predMode == MODE_INTER || bestCS->cus.back()->predMode == MODE_IBC)&& 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 );}bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));bestCS->picture->getRecoBuf( currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );m_modeCtrl->finishCULevel( partitioner );if( m_pcIntraSearch->getSaveCuCostInSCIPU() && bestCS->cus.size() == 1 ){m_pcIntraSearch->saveCuAreaCostInSCIPU( Area( partitioner.currArea().lumaPos(), partitioner.currArea().lumaSize() ), bestCS->cost );}#if ENABLE_SPLIT_PARALLELISMif( tempCS->picture->scheduler.getSplitJobId() == 0 && m_pcEncCfg->getNumSplitThreads() != 1 ){tempCS->picture->finishParallelPart( currCsArea );}#endifif (bestCS->cus.size() == 1) // no partition{CHECK(bestCS->cus[0]->tileIdx != bestCS->pps->getTileIdx(bestCS->area.lumaPos()), "Wrong tile index!");if (bestCS->cus[0]->predMode == MODE_PLT){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" );
}

上一篇:H.266/VVC-VTM代码学习19-CU层确定测试模式函数initCULevel
下一篇:H.266/VVC-VTM代码学习21-对划分模式进行RDO的函数xCheckModeSplit

更多推荐

H.266/VVC

本文发布于:2024-03-12 08:33:46,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1731112.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:VVC

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!