VVC中的编码块的划分与HEVC中有很大的不同。VVC中专门的一个函数来实现划分xCheckModeSplit。在void EncCu::xCheckModeSplit中的会进行具体的QT.BT.TT的划分,
在xCompressCU函数中 do{ 尝试帧间、帧内各种预测模式,
当遍历完所有可用模式后,调用xCheckModeSplit进行cu特定块的划分
else if( isModeSplit( currTestMode ) )
{
xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode
, tempMotCandLUTs
, bestMotCandLUTs
, partitioner.currArea()
);
}
}while()
在xCompressCU这个函数也会调用xCheckModeSplit这里面的划分结果,首先先看一个EncTestMode,会得到具体的CU划分模式
ETM_SPLIT_QT,
ETM_SPLIT_BT_H,
ETM_SPLIT_BT_V,
ETM_SPLIT_TT_H,
ETM_SPLIT_TT_V
enum EncTestModeType
{
#if JVET_M0253_HASH_ME
ETM_HASH_INTER,
#endif
ETM_MERGE_SKIP,
ETM_INTER_ME,
ETM_AFFINE,
ETM_MERGE_TRIANGLE,
ETM_INTRA,
ETM_IPCM,
ETM_SPLIT_QT,
ETM_SPLIT_BT_H,
ETM_SPLIT_BT_V,
ETM_SPLIT_TT_H,
ETM_SPLIT_TT_V,
ETM_POST_DONT_SPLIT, // dummy mode to collect the data from the unsplit coding
#if REUSE_CU_RESULTS
ETM_RECO_CACHED,
#endif
ETM_TRIGGER_IMV_LIST,
ETM_IBC, // ibc mode
ETM_IBC_MERGE, // ibc merge mode
ETM_INVALID
};
enum EncTestModeOpts
{
ETO_STANDARD = 0, // empty (standard option)
ETO_FORCE_MERGE = 1<<0, // bit 0 (indicates forced merge)
ETO_IMV_SHIFT = 1, // bits 1-3 (imv parameter starts at bit 1)
ETO_IMV = 7<<ETO_IMV_SHIFT, // bits 1-3 (imv parameter uses 3 bits)
ETO_DUMMY = 1<<5, // bit 5 (dummy)
ETO_INVALID = 0xffffffff // bits 0-31 (invalid option)
};
static void getAreaIdx(const Area& area, const PreCalcValues &pcv, unsigned &idx1, unsigned &idx2, unsigned &idx3, unsigned &idx4)
{
idx1 = (area.x & pcv.maxCUWidthMask) >> MIN_CU_LOG2;
idx2 = (area.y & pcv.maxCUHeightMask) >> MIN_CU_LOG2;
idx3 = gp_sizeIdxInfo->idxFrom( area.width );
idx4 = gp_sizeIdxInfo->idxFrom( area.height );
}
EncModeCtrl控制是否应测试特定模式
EncModeCtrl-抽象类,指定模式控制的一般流程
struct EncTestMode
{
EncTestMode()
: type( ETM_INVALID ), opts( ETO_INVALID ), qp( -1 ), lossless( false ) {}
EncTestMode( EncTestModeType _type )
: type( _type ), opts( ETO_STANDARD ), qp( -1 ), lossless( false ) {}
EncTestMode( EncTestModeType _type, int _qp, bool _lossless )
: type( _type ), opts( ETO_STANDARD ), qp( _qp ), lossless( _lossless ) {}
EncTestMode( EncTestModeType _type, EncTestModeOpts _opts, int _qp, bool _lossless )
: type( _type ), opts( _opts ), qp( _qp ), lossless( _lossless ) {}
EncTestModeType type;
EncTestModeOpts opts;
int qp;
bool lossless;
};
inline bool isModeSplit( const EncTestMode& encTestmode )
{
switch( encTestmode.type )
{
case ETM_SPLIT_QT :
case ETM_SPLIT_BT_H :
case ETM_SPLIT_BT_V :
case ETM_SPLIT_TT_H :
case ETM_SPLIT_TT_V :
return true;
default:
return false;
}
}
inline bool isModeNoSplit( const EncTestMode& encTestmode )
{
return !isModeSplit( encTestmode ) && encTestmode.type != ETM_POST_DONT_SPLIT;
}
inline bool isModeInter( const EncTestMode& encTestmode ) // perhaps remove
{
return ( encTestmode.type == ETM_INTER_ME
|| encTestmode.type == ETM_MERGE_SKIP
|| encTestmode.type == ETM_AFFINE
|| encTestmode.type == ETM_MERGE_TRIANGLE
#if JVET_M0253_HASH_ME
|| encTestmode.type == ETM_HASH_INTER
#endif
);
}
inline PartSplit getPartSplit( const EncTestMode& encTestmode )
{
switch( encTestmode.type )
{
case ETM_SPLIT_QT : return CU_QUAD_SPLIT;
case ETM_SPLIT_BT_H : return CU_HORZ_SPLIT;
case ETM_SPLIT_BT_V : return CU_VERT_SPLIT;
case ETM_SPLIT_TT_H : return CU_TRIH_SPLIT;
case ETM_SPLIT_TT_V : return CU_TRIV_SPLIT;
default: return CU_DONT_SPLIT;
}
}
inline EncTestMode getCSEncMode( const CodingStructure& cs )
{
return EncTestMode( EncTestModeType( (unsigned)cs.features[ENC_FT_ENC_MODE_TYPE] ),
EncTestModeOpts( (unsigned)cs.features[ENC_FT_ENC_MODE_OPTS] ),
false);
}
接下来总体看看这部分代码:
xCheckModeSplit函数在xCompressCU中被调用,xCheckModeSplit中的会进行具体的QT.BT.TT的划分,,encTestMode可以获取具体需要test的划分模式。
xCheckModeSplit在依据划分模式,将当前tempCS区域划分为相应的多个子cu区域之后,对每个子cu分别调用xCompressCU函数进行处理;
获取各个子cu的最优划分和最优模式,递归返回之后将bestSubCS的最优数据copy给tempCS,然后进行下一个子cu的递归处理,获取最优模式;
再将bestSubCS的最优数据copy给tempCS,直至xCompressCU处理完所有子cu之后,tempCS就包含encTestMode划分模式的最优数据;
最后xCheckBestMode比较tempCS和bestCS的cost,最优模式存入bestCS。
void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode
, LutMotionCand* &tempMotCandLUTs
, LutMotionCand* &bestMotCandLUTs
, UnitArea parArea
)
{
const int qp = encTestMode.qp;
const PPS &pps = *tempCS->pps;
const Slice &slice = *tempCS->slice;
const bool bIsLosslessMode = false; // False at this level. Next level down may set it to true.
const int oldPrevQp = tempCS->prevQP[partitioner.chType];
const uint32_t currDepth = partitioner.currDepth;
const unsigned wParIdx = gp_sizeIdxInfo->idxFrom(parArea.lwidth());
const unsigned hParIdx = gp_sizeIdxInfo->idxFrom(parArea.lheight());
if (tempCS->chType == CHANNEL_TYPE_LUMA)
tempCS->slice->copyMotionLUTs(tempMotCandLUTs, m_pSplitTempMotLUTs[wParIdx][hParIdx]);
const PartSplit split = getPartSplit( encTestMode );
CHECK( split == CU_DONT_SPLIT, "No proper split provided!" );
tempCS->initStructData( qp, bIsLosslessMode );
m_CABACEstimator->getCtx() = m_CurrCtx->start;
const TempCtx ctxStartSP( m_CtxCache, SubCtx( Ctx::SplitFlag, m_CABACEstimator->getCtx() ) );
#if JVET_M0421_SPLIT_SIG
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() ) );
#else
const TempCtx ctxStartBT( m_CtxCache, SubCtx( Ctx::BTSplitFlag, m_CABACEstimator->getCtx() ) );
#endif
m_CABACEstimator->resetBits();
#if JVET_M0421_SPLIT_SIG
m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
#else
if( partitioner.getImplicitSplit( *tempCS ) != CU_QUAD_SPLIT )
{
if( partitioner.canSplit( CU_QUAD_SPLIT, *tempCS ) )
{
m_CABACEstimator->split_cu_flag( split == CU_QUAD_SPLIT, *tempCS, partitioner );
}
if( split != CU_QUAD_SPLIT )
{
m_CABACEstimator->split_cu_mode_mt( split, *tempCS, partitioner );
}
}
#endif
const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );
#if JVET_M0428_ENC_DB_OPT
tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
if (!tempCS->useDbCost)
CHECK(bestCS->costDbOffset != 0, "error");
const double cost = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;
#else
const double cost = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) );
#endif
m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag, ctxStartSP );
#if JVET_M0421_SPLIT_SIG
m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitQtFlag, ctxStartQt );
m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitHvFlag, ctxStartHv );
m_CABACEstimator->getCtx() = SubCtx( Ctx::Split12Flag, ctxStart12 );
#else
m_CABACEstimator->getCtx() = SubCtx( Ctx::BTSplitFlag, ctxStartBT );
#endif
#if JVET_M0428_ENC_DB_OPT
if (cost > bestCS->cost + bestCS->costDbOffset)
#else
if( cost > bestCS->cost )
#endif
{
xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
return;
}
#if JVET_M0170_MRG_SHARELIST
#if JVET_M0483_IBC
if ((!slice.isIntra() || slice.getSPS()->getIBCFlag())
#else
if (!slice.isIntra()
#endif
&& tempCS->chType == CHANNEL_TYPE_LUMA
)
{
tempCS->slice->copyMotionLUTs(tempMotCandLUTs, tempCS->slice->getMotionLUTs());
}
int startShareThisLevel = 0;
const uint32_t uiLPelX = tempCS->area.Y().lumaPos().x;
const uint32_t uiTPelY = tempCS->area.Y().lumaPos().y;
int splitRatio = 1;
CHECK(!(split == CU_QUAD_SPLIT || split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT
|| split == CU_TRIH_SPLIT || split == CU_TRIV_SPLIT), "invalid split type");
splitRatio = (split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT) ? 1 : 2;
bool isOneChildSmall = ((tempCS->area.lwidth())*(tempCS->area.lheight()) >> splitRatio) < MRG_SHARELIST_SHARSIZE;
if ((((tempCS->area.lwidth())*(tempCS->area.lheight())) > (MRG_SHARELIST_SHARSIZE * 1)))
{
m_shareState = NO_SHARE;
}
if (m_shareState == NO_SHARE)//init state
{
if (isOneChildSmall)
{
m_shareState = GEN_ON_SHARED_BOUND;//share start state
startShareThisLevel = 1;
}
}
#if JVET_M0483_IBC
if ((m_shareState == GEN_ON_SHARED_BOUND) && (!slice.isIntra() || slice.getSPS()->getIBCFlag()))
#else
if ((m_shareState == GEN_ON_SHARED_BOUND) && (!slice.isIntra()))
#endif
{
#if JVET_M0170_MRG_SHARELIST
tempCS->slice->copyMotionLUTs(tempCS->slice->getMotionLUTs(), tempCS->slice->m_MotionCandLuTsBkup);
m_shareBndPosX = uiLPelX;
m_shareBndPosY = uiTPelY;
m_shareBndSizeW = tempCS->area.lwidth();
m_shareBndSizeH = tempCS->area.lheight();
m_shareState = SHARING;
#endif
}
m_pcInterSearch->setShareState(m_shareState);
setShareStateDec(m_shareState);
#endif
partitioner.splitCurrArea( split, *tempCS );
m_CurrCtx++;
tempCS->getRecoBuf().fill( 0 );
#if JVET_M0427_INLOOP_RESHAPER
tempCS->getPredBuf().fill(0);
#endif
AffineMVInfo tmpMVInfo;
bool isAffMVInfoSaved;
m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, isAffMVInfoSaved);
do
{
......
} while( partitioner.nextPart( *tempCS ) ); //得到下一个子块
partitioner.exitCurrSplit();
这部分的代码的详细理解可以参照这篇博客:https://blog.csdn.net/gq0323/article/details/103721171