VTM1.0代码阅读:Partitioner类_矛盾统一的博客-CSDN博客
H.266/VVC-VTM代码学习24-根据当前块位置与尺寸确定隐藏划分模式getImplicitSplit()_liaojq2020的博客-CSDN博客
tryModeMaster()这个函数判断当前m_ComprCUCtxList栈顶中的模式是否要跳过的,为真的话,则运行nextmod()函数,跳过
ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
// Fast checks, partitioning depended
if (cuECtx.isHashPerfectMatch && encTestmode.type != ETM_MERGE_SKIP && encTestmode.type != ETM_INTER_ME && encTestmode.type != ETM_AFFINE && encTestmode.type != ETM_MERGE_GEO)
{
#if GDR_ENABLED // disable hash perfect match when GDR is on
if (!m_encCfg.getGdrEnabled())
{
return false;
}
#else
return false;
#endif
}
ComprCUCtx :用来存储ctu中的一个cu在compress过程中的各种数据
第一个if语句:如果是 hash perfect match为真,且不为ETM_MERGE_SKIP,ETM_INTER_ME,ETM_AFFINE,ETM_MERGE_GEO,且GDR关闭,则返回一个false。
若GDR开启,则不执行 hash perfect match,也返回一个false。执行nextmode()
// if early skip detected, skip all modes checking but the splits
if( cuECtx.earlySkip && m_pcEncCfg->getUseEarlySkipDetection() && !isModeSplit( encTestmode ) && !( isModeInter( encTestmode ) ) )
{
return false;
}
const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );
const bool isBoundary = implicitSplit != CU_DONT_SPLIT;
if( isBoundary && encTestmode.type != ETM_SPLIT_QT )
{
return getPartSplit( encTestmode ) == implicitSplit;
}
else if( isBoundary && encTestmode.type == ETM_SPLIT_QT )
{
return partitioner.canSplit( CU_QUAD_SPLIT, cs );
}
第一个if语句:如果earlyskip开启且当前不为划分和帧间,则跳过
getImplicitSplit():解析放在下面。运行时按F11进入,直接F12进入的那个函数好像不对
isBoundary:判断当前划分模式是否有隐式分割限制,不是的话为true
getPartSplit():将当前栈顶的待测试模式传入,如果不为划分模式,则返回一个CU_DONT_SPLIT,表示当前测试模式不是划分模式
第二个if语句:如果isBoundary为真且当前测试模式不为QT,则比较getPartSplit()和implicitSplit,不相同则为假
所以返回一个false
if( !tryModeMaster( m_ComprCUCtxList.back().testModes.back(), cs, partitioner ) )
{
nextMode( cs, partitioner );
}
最终执行nextmode,把intra从栈顶去掉
这其中的getImplicitSplit()十分重要,主要是判断当前CU是否有隐式分割模式,如果有,就不会运行intra预测模式。以下是这个函数的解析
1. getImplicitSplit()解析
PartSplit QTBTPartitioner::getImplicitSplit( const CodingStructure &cs )
{
if( m_partStack.back().checkdIfImplicit )
{
return m_partStack.back().implicitSplit;
}
PartSplit split = CU_DONT_SPLIT;
if( split == CU_DONT_SPLIT )
{
const bool isBlInPic = cs.picture->Y().contains( currArea().Y().bottomLeft() );
const bool isTrInPic = cs.picture->Y().contains( currArea().Y().topRight() );
const CompArea& area = currArea().Y();
const unsigned maxBtSize = cs.pcv->getMaxBtSize( *cs.slice, chType );
const bool isBtAllowed = area.width <= maxBtSize && area.height <= maxBtSize && currMtDepth < (cs.pcv->getMaxBtDepth(*cs.slice, chType) + currImplicitBtDepth);
const unsigned minQtSize = cs.pcv->getMinQtSize( *cs.slice, chType );
const bool isQtAllowed = area.width > minQtSize && area.height > minQtSize && currBtDepth == 0;
if( !isBlInPic && !isTrInPic && isQtAllowed )
{
split = CU_QUAD_SPLIT;
}
else if( !isBlInPic && isBtAllowed && area.width <= MAX_TB_SIZEY )
{
split = CU_HORZ_SPLIT;
}
else if( !isTrInPic && isBtAllowed && area.height <= MAX_TB_SIZEY )
{
split = CU_VERT_SPLIT;
}
else if( !isBlInPic || !isTrInPic )
{
split = CU_QUAD_SPLIT;
}
if (CS::isDualITree(cs) && (currArea().Y().width > 64 || currArea().Y().height > 64))
{
split = CU_QUAD_SPLIT;
}
if( (!isBlInPic || !isTrInPic) && split == CU_DONT_SPLIT )
{
split = CU_QUAD_SPLIT;
}
}
m_partStack.back().checkdIfImplicit = true;
m_partStack.back().isImplicit = split != CU_DONT_SPLIT;
m_partStack.back().implicitSplit = split;
return split;
}
这里面用到的是一个vector容器,是PartLevel的堆栈,记录每一层划分树的情况。
m_partStack中的checkdIfImplicit表示当前CU有没有隐式分割模式,implicitSplit表示当前的隐式分割模式
1.1 m_partStack堆栈中的数据在initCtu()中被设定
m_partStack的初始化在compressCtu()中的initCtu()里,先清空,再通过PartLevel()赋初值。
void QTBTPartitioner::initCtu( const UnitArea& ctuArea, const ChannelType _chType, const Slice& slice )
{
#if _DEBUG
m_currArea = ctuArea;
#endif
currDepth = 0;
currTrDepth = 0;
currBtDepth = 0;
currMtDepth = 0;
currQtDepth = 0;
currSubdiv = 0;
currQgPos = ctuArea.lumaPos();
currQgChromaPos = ctuArea.chromaFormat != CHROMA_400 ? ctuArea.chromaPos() : Position();
currImplicitBtDepth = 0;
chType = _chType;
m_partStack.clear();
m_partStack.push_back( PartLevel( CTU_LEVEL, Partitioning{ ctuArea } ) );
treeType = TREE_D;
modeType = MODE_TYPE_ALL;
}
1.1.1 m_partStack初始设定
初始化后默认checkdIfImplicit为false,implicitSplit为CU_DONT_SPLIT.
PartLevel::PartLevel( const PartSplit _split, Partitioning&& _parts )
: split ( _split )
, parts ( std::forward<Partitioning>( _parts ) )
, idx ( 0u )
, checkdIfImplicit ( false )
, isImplicit ( false )
, implicitSplit ( CU_DONT_SPLIT )
, firstSubPartSplit ( CU_DONT_SPLIT )
, canQtSplit ( true )
, qgEnable ( true )
, qgChromaEnable ( true )
, modeType ( MODE_TYPE_ALL )
{
}
1.1.2 设定隐式分割模式
H.266/VVC-VTM代码学习22-partitioner类判断划分模式是否可用函数partitioner.canSplit_liaojq2020的博客-CSDN博客
然后在initCULevel()中有加入如TT,BT划分模式的partitioner.canSplit( CU_TRIV_SPLIT, cs )函数,bool类型的canSplit()
bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs )
{
const CompArea area = currArea().Y();
const unsigned maxTrSize = cs.sps->getMaxTbSize();
bool canNo, canQt, canBh, canTh, canBv, canTv;
canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
switch( split )
{
case CTU_LEVEL:
THROW( "Checking if top level split is possible" );
return true;
break;
case TU_MAX_TR_SPLIT:
return area.width > maxTrSize || area.height > maxTrSize;
break;
case SBT_VER_HALF_POS0_SPLIT:
case SBT_VER_HALF_POS1_SPLIT:
case SBT_HOR_HALF_POS0_SPLIT:
case SBT_HOR_HALF_POS1_SPLIT:
case SBT_VER_QUAD_POS0_SPLIT:
case SBT_VER_QUAD_POS1_SPLIT:
case SBT_HOR_QUAD_POS0_SPLIT:
case SBT_HOR_QUAD_POS1_SPLIT:
return currTrDepth == 0;
break;
case CU_QUAD_SPLIT:
return canQt;
case CU_DONT_SPLIT:
return canNo;
case CU_HORZ_SPLIT:
return canBh;
case CU_VERT_SPLIT:
return canBv;
case CU_TRIH_SPLIT:
return canTh;
case CU_TRIV_SPLIT:
return canTv;
case CU_MT_SPLIT:
return ( canBh || canTh || canBv || canTv );
case CU_BT_SPLIT:
return ( canBh || canBv );
break;
default:
THROW( "Unknown split mode" );
return false;
break;
}
return true;
}
这里面有一个void类型的canSplit()函数,从这里面判断是否在m_partStack中添加隐式分割模式
void QTBTPartitioner::canSplit( const CodingStructure &cs, bool& canNo, bool& canQt, bool& canBh, bool& canBv, bool& canTh, bool& canTv )
{
const PartSplit implicitSplit = m_partStack.back().checkdIfImplicit ? m_partStack.back().implicitSplit : getImplicitSplit( cs );
const unsigned maxBTD = cs.pcv->getMaxBtDepth( *cs.slice, chType ) + currImplicitBtDepth;
const unsigned maxBtSize = cs.pcv->getMaxBtSize ( *cs.slice, chType );
const unsigned minBtSize = cs.pcv->getMinBtSize ( *cs.slice, chType );
const unsigned maxTtSize = cs.pcv->getMaxTtSize ( *cs.slice, chType );
const unsigned minTtSize = cs.pcv->getMinTtSize ( *cs.slice, chType );
const unsigned minQtSize = cs.pcv->getMinQtSize ( *cs.slice, chType );
canNo = canQt = canBh = canTh = canBv = canTv = true;
bool canBtt = currMtDepth < maxBTD;
// the minimal and maximal sizes are given in luma samples
const CompArea& area = currArea().Y();
const CompArea *areaC = (chType == CHANNEL_TYPE_CHROMA) ? &(currArea().Cb()) : nullptr;
PartLevel& level = m_partStack.back();
const PartSplit lastSplit = level.split;
const PartSplit parlSplit = lastSplit == CU_TRIH_SPLIT ? CU_HORZ_SPLIT : CU_VERT_SPLIT;
// don't allow QT-splitting below a BT split
if( lastSplit != CTU_LEVEL && lastSplit != CU_QUAD_SPLIT ) canQt = false;
if( area.width <= minQtSize ) canQt = false;
if( areaC && areaC->width <= MIN_DUALTREE_CHROMA_WIDTH ) canQt = false;
if( treeType == TREE_C )
{
canQt = canBh = canTh = canBv = canTv = false;
return;
}
if( implicitSplit != CU_DONT_SPLIT )
{
canNo = canTh = canTv = false;
canBh = implicitSplit == CU_HORZ_SPLIT;
canBv = implicitSplit == CU_VERT_SPLIT;
if (areaC && areaC->width == 4) canBv = false;
if( !canBh && !canBv && !canQt ) canQt = true;
return;
}
if( ( lastSplit == CU_TRIH_SPLIT || lastSplit == CU_TRIV_SPLIT ) && currPartIdx() == 1 )
{
canBh = parlSplit != CU_HORZ_SPLIT;
canBv = parlSplit != CU_VERT_SPLIT;
}
if( canBtt && ( area.width <= minBtSize && area.height <= minBtSize )
&& ( ( area.width <= minTtSize && area.height <= minTtSize ) ) )
{
canBtt = false;
}
if( canBtt && ( area.width > maxBtSize || area.height > maxBtSize )
&& ( ( area.width > maxTtSize || area.height > maxTtSize ) ) )
{
canBtt = false;
}
if( !canBtt )
{
canBh = canTh = canBv = canTv = false;
return;
}
if( area.width > maxBtSize || area.height > maxBtSize )
{
canBh = canBv = false;
}
// specific check for BT splits
if( area.height <= minBtSize ) canBh = false;
if( area.width > MAX_TB_SIZEY && area.height <= MAX_TB_SIZEY ) canBh = false;
if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE ) canBh = false;
if( area.width <= minBtSize ) canBv = false;
if( area.width <= MAX_TB_SIZEY && area.height > MAX_TB_SIZEY ) canBv = false;
if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE || areaC->width == 4)) canBv = false;
if( modeType == MODE_TYPE_INTER && area.width * area.height == 32 ) canBv = canBh = false;
if( area.height <= 2 * minTtSize || area.height > maxTtSize || area.width > maxTtSize )
canTh = false;
if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY ) canTh = false;
if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE*2 ) canTh = false;
if( area.width <= 2 * minTtSize || area.width > maxTtSize || area.height > maxTtSize )
canTv = false;
if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY ) canTv = false;
if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE * 2 || areaC->width == 8)) canTv = false;
if( modeType == MODE_TYPE_INTER && area.width * area.height == 64 ) canTv = canTh = false;
}
在第一句,如果当前为m_partStack的初始值,checkdIfImplicit必定为false,则又会调用一次getImplicitSplit().因为checkdIfImplicit为false,所以第一if语句不执行
PartSplit QTBTPartitioner::getImplicitSplit( const CodingStructure &cs )
{
if( m_partStack.back().checkdIfImplicit )
{
return m_partStack.back().implicitSplit;
}
PartSplit split = CU_DONT_SPLIT;
if( split == CU_DONT_SPLIT )
{
//检查当前CU是否在图片中
const bool isBlInPic = cs.picture->Y().contains( currArea().Y().bottomLeft() );
const bool isTrInPic = cs.picture->Y().contains( currArea().Y().topRight() );
//基本参数获得
const CompArea& area = currArea().Y();
const unsigned maxBtSize = cs.pcv->getMaxBtSize( *cs.slice, chType );
const bool isBtAllowed = area.width <= maxBtSize && area.height <= maxBtSize && currMtDepth < (cs.pcv->getMaxBtDepth(*cs.slice, chType) + currImplicitBtDepth);
const unsigned minQtSize = cs.pcv->getMinQtSize( *cs.slice, chType );
const bool isQtAllowed = area.width > minQtSize && area.height > minQtSize && currBtDepth == 0;
//根据参数判断要加入的隐式分割模式
if( !isBlInPic && !isTrInPic && isQtAllowed )
{
split = CU_QUAD_SPLIT;
}
else if( !isBlInPic && isBtAllowed && area.width <= MAX_TB_SIZEY )
{
split = CU_HORZ_SPLIT;
}
else if( !isTrInPic && isBtAllowed && area.height <= MAX_TB_SIZEY )
{
split = CU_VERT_SPLIT;
}
else if( !isBlInPic || !isTrInPic )
{
split = CU_QUAD_SPLIT;
}
//当前为dual——tree且当前CU的宽度或高度大于64,则添加四叉树为隐式分割模式
if (CS::isDualITree(cs) && (currArea().Y().width > 64 || currArea().Y().height > 64))
{
split = CU_QUAD_SPLIT;
}
if( (!isBlInPic || !isTrInPic) && split == CU_DONT_SPLIT )
{
split = CU_QUAD_SPLIT;
}
}
m_partStack.back().checkdIfImplicit = true; //设为true
m_partStack.back().isImplicit = split != CU_DONT_SPLIT; //若当前split不为CU_DONT_SPLIT,则为帧,表示有隐式分割
m_partStack.back().implicitSplit = split; //设置隐式分割模式,放入m_partStack堆栈最后一位
return split;
}
关于128 X 128的CTU块为什么不能直接作预测,推测:
一个大于64 X 64的CU要进行隐式分割,因为最大变换单元为64X64
ISP默认设置是true
推测:因为ISP和变换单元等的限制,在CTU-Level时要进行一个QT隐式分割,先把CU变小。所以要把Intra模式从栈顶去掉