新人上路,请多多指教。 一直想写博客,今天才开始写第一篇。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这次来讲一下VTM9.0中关于CU划分的部分,主要是两个函数xCompressCU和xCheckModeSplit。
xCompressCU主要针对当前CU可能进行的预测划分模式,完成相应的操作。主要分为以下几种模式(后面两种暂时还没学习,后面如果接触到会补充)
1划分模式:四叉树划分,水平二叉树划分,垂直二叉树划分,水平三叉树划分,垂直三叉树划分。通过调用函数xCompressCU完成。
2帧内预测:调用函数xCheckRDCostIntra完成。
3帧间预测:。。。。
4屏幕内容编码:。。。。
xCompressCU函数流程:初始化,调用函数initCULevel(),获取当前CU可能的预测和编码模式,并且按照顺序推入栈中。然后进入一循环,改循环会不断调取栈顶模式,将CU按照相应模式进行编码或划分,直至栈内不储存模式为止。具体代码如下:
void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
{
/*************************************** 初始化 ****************************************/
CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");
#if ENABLE_SPLIT_PARALLELISM
CHECK( 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;
}
}
#endif
uint32_t compBegin;
uint32_t numComp;
bool jointPLT = false;
if (partitioner.isSepTree( *tempCS ))
{
#if JVET_Q0504_PLT_NON444
if( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D )
{
compBegin = COMPONENT_Y;
numComp = (tempCS->area.chromaFormat != CHROMA_400)?3: 1;
jointPLT = true;
}
else
{
#endif
if (isLuma(partitioner.chType))
{
compBegin = COMPONENT_Y;
numComp = 1;
}
else
{
compBegin = COMPONENT_Cb;
numComp = 2;
}
#if JVET_Q0504_PLT_NON444
}
#endif
}
else
{
compBegin = COMPONENT_Y;
#if JVET_Q0504_PLT_NON444
numComp = (tempCS->area.chromaFormat != CHROMA_400) ? 3 : 1;
#else
numComp = 3;
#endif
jointPLT = true;
}
SplitSeries splitmode = -1;
uint8_t bestLastPLTSize[MAX_NUM_CHANNEL_TYPE];
Pel bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT for
uint8_t curLastPLTSize[MAX_NUM_CHANNEL_TYPE];
Pel curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partition
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));
}
Slice& 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 );
m_modeCtrl->initCULevel( partitioner, *tempCS );//初始化所有编码划分模式,为CU设置可能的编码划分模式
if( partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra() && ( sps.getUseSBT() || sps.getUseInterMTS() ) )
{
auto slsSbt = dynamic_cast( m_modeCtrl );
int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;
slsSbt->resetSaveloadSbt( maxSLSize );
#if ENABLE_SPLIT_PARALLELISM
CHECK( 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(m_pcEncLib->getCuEncoder(jId)->m_modeCtrl);
slsSbt->resetSaveloadSbt(maxSLSize);
}
}
#endif
}
m_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;
m_CurrCtx->start = m_CABACEstimator->getCtx();
/************************* 处理色度QP ***************************/
m_cuChromaQpOffsetIdxPlus1 = 0;
if( slice.getUseChromaQpAdj() )
{
// TODO M0133 : double check encoder decisions with respect to chroma QG detection and actual encode
int lgMinCuSize = sps.getLog2MinCodingBlockSize() +
#if JVET_Q0468_Q0469_MIN_LUMA_CB_AND_MIN_QT_FIX
std::max(0, floorLog2(sps.getCTUSize()) - sps.getLog2MinCodingBlockSize() - int(slice.getCuChromaQpOffsetSubdiv() / 2));
#else
std::max( 0, sps.getLog2DiffMaxMinCodingBlockSize() - int( slice.getCuChromaQpOffsetSubdiv()/2 ) );
#endif
#if JVET_Q0267_RESET_CHROMA_QP_OFFSET
if( partitioner.currQgChromaEnable() )
{
m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );
}
#else
m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );
#endif
}
if( !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;
}
}
/***************************按照堆栈中的模式进行循环预测编码或划分,当循环结束时当前CU已选择最小的RDcost进行划分与编码****************************/
do//尝试当前编码器各种可用的模式:如skip,帧间,帧内,PCM等,进行预测及划分。
{
for (int i = compBegin; i < (compBegin + numCom