H.266/VVC专栏传送
上一篇:H.266/VVC-VTM代码学习21-对划分模式进行RDO的函数xCheckModeSplit
下一篇:H.266/VVC-VTM代码学习23-编码块RDO选择模式(Intra与Inter)initCULevel()
目录
前言
VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。
本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。
VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
一、函数作用
在 initCULevel 函数将各个划分模式加入 RDO 列表时,都有如下代码结构先调用 partitioner.canSplit 函数进行判断,函数返回真时才加入相对应的划分模式。
if( partitioner.canSplit( CU_VERT_SPLIT, cs ) )
{
// add split modes
for( int qp = maxQP; qp >= minQP; qp-- )
{
m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_BT_V, ETO_STANDARD, qp } );
}
m_ComprCUCtxList.back().set( DID_VERT_SPLIT, true );
}
二、函数详解
1. initCULevel 函数调用的 canSplit
bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs )
{
// 当前亮度区域
const CompArea area = currArea().Y();
// 最大 TB 尺寸
const unsigned maxTrSize = cs.sps->getMaxTbSize();
bool canNo, canQt, canBh, canTh, canBv, canTv;
// 通过调用该函数,获得当前是否能不划分、四叉划分、二叉水平、二叉垂直、三叉水平、三叉垂直
canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
// 对输入的划分类型进行判断
switch( split )
{
// 若当前划分模式为0,则抛出异常
case CTU_LEVEL:
THROW( "Checking if top level split is possible" );
return true;
break;
// 若当前划分模式为 TU_MAX_TR_SPLIT
// 则当前区域宽度或高度大于最大 TB 尺寸时,可以划分,否则不可以
case TU_MAX_TR_SPLIT:
return area.width > maxTrSize || area.height > maxTrSize;
break;
// 以下 SBT 划分时
// 均在当前 Tr 深度为 0 时可以划分,否则不可以
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;
// 以下的 CU 划分模式均直接返回调用 canSplit 函数得到的可否划分标志
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;
}
2. canSplit 函数内部调用获取 CU 划分模式是否可行的 canSplit
该函数主要根据块尺寸进行相应划分能否尝试的判断。
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 );
// 最大 BT 深度
const unsigned maxBTD = cs.pcv->getMaxBtDepth( *cs.slice, chType ) + currImplicitBtDepth;
// 最大 BT 尺寸
const unsigned maxBtSize = cs.pcv->getMaxBtSize ( *cs.slice, chType );
// 最小 BT 尺寸
const unsigned minBtSize = cs.pcv->getMinBtSize ( *cs.slice, chType );
// 最大 TT 尺寸
const unsigned maxTtSize = cs.pcv->getMaxTtSize ( *cs.slice, chType );
// 最小 TT 尺寸
const unsigned minTtSize = cs.pcv->getMinTtSize ( *cs.slice, chType );
// 最小 QT 尺寸
const unsigned minQtSize = cs.pcv->getMinQtSize ( *cs.slice, chType );
// 将划分标志初始化为 True
canNo = canQt = canBh = canTh = canBv = canTv = true;
// MT(multi-type tree)即 TT 与 BT 的统称,若当前 MT 深度小于最大 BT 深度,则该标志有效
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;
// 上次划分为 TH 则该变量为 BH 否则该变量为 BV
const PartSplit parlSplit = lastSplit == CU_TRIH_SPLIT ? CU_HORZ_SPLIT : CU_VERT_SPLIT;
// 不允许在 BT 划分之后进行 QT
// don't allow QT-splitting below a BT split
if( lastSplit != CTU_LEVEL && lastSplit != CU_QUAD_SPLIT ) canQt = false;
// 若当前区域宽度小于等于最小 QT 尺寸,则不进行 QT
if( area.width <= minQtSize ) canQt = false;
// 若当前色度区域存在,且色度区域宽度小于等于 4,则不进行 QT
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;
// 若当前色度区域存在且色度区域宽度等于4,则不进行 BV
if (areaC && areaC->width == 4) canBv = false;
// 若不BH、BV、QT,则测试 QT
if( !canBh && !canBv && !canQt ) canQt = true;
return;
}
// 若上一划分模式为 TH,且当前块索引为 1,则不测试 BH,测试 BV
// 若上一划分模式为 TV,且当前块索引为 1,则不测试 BV,测试 BH
if( ( lastSplit == CU_TRIH_SPLIT || lastSplit == CU_TRIV_SPLIT ) && currPartIdx() == 1 )
{
canBh = parlSplit != CU_HORZ_SPLIT;
canBv = parlSplit != CU_VERT_SPLIT;
}
// 若同时满足当前 MT 深度小于最大 MT 深度、当前区域宽高均小于等于最小 BT 尺寸、当前区域宽高均小于等于最小 TT 尺寸,则将 canBtt 设为 false
if( canBtt && ( area.width <= minBtSize && area.height <= minBtSize )
&& ( ( area.width <= minTtSize && area.height <= minTtSize ) ) )
{
canBtt = false;
}
// 若同时满足当前 MT 深度小于最大 MT 深度、当前区域宽或高大于最大 BT 尺寸、当前区域宽或高大于最大 TT 尺寸,则将 canBtt 设为 false
if( canBtt && ( area.width > maxBtSize || area.height > maxBtSize )
&& ( ( area.width > maxTtSize || area.height > maxTtSize ) ) )
{
canBtt = false;
}
// 若 canBtt 为 false,则不尝试 BT 与 TT
if( !canBtt )
{
canBh = canTh = canBv = canTv = false;
return;
}
// 若当前区域宽或高大于最大 BT 尺寸
// 则不尝试 BT
if( area.width > maxBtSize || area.height > maxBtSize )
{
canBh = canBv = false;
}
// specific check for BT splits
/*********************************************************/
// 若当前区域高小于等于最小 BT 尺寸,则不尝试 BH
if( area.height <= minBtSize ) canBh = false;
// 若当前区域宽大于最大 TB 尺寸,且当前区域高小于等于最大 TB 尺寸,则不尝试 BH
if( area.width > MAX_TB_SIZEY && area.height <= MAX_TB_SIZEY ) canBh = false;
// 若当前色度区域存在,且面积小于等于 16,则不尝试 BH
if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE ) canBh = false;
/*********************************************************/
// 若当前区域宽小于等于最小 BT 尺寸,则不尝试 BV
if( area.width <= minBtSize ) canBv = false;
// 若当前区域宽小于等于最大 TB 尺寸,且当前区域高大于最大 TB 尺寸,则不尝试 BV
if( area.width <= MAX_TB_SIZEY && area.height > MAX_TB_SIZEY ) canBv = false;
// 若当前色度区域存在,且面积小于等于 16 或宽为 4,则不尝试 BV
if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE || areaC->width == 4)) canBv = false;
/*********************************************************/
// 若为 INTER,且当前区域面积为 32,则不尝试 BT
if( modeType == MODE_TYPE_INTER && area.width * area.height == 32 ) canBv = canBh = false;
/*********************************************************/
// 若当前区域高小于等于两倍最小 TT 尺寸,或当前区域高大于最大 TT 尺寸,或当前区域宽大于最大 TT 尺寸
// 则不尝试 TH
if( area.height <= 2 * minTtSize || area.height > maxTtSize || area.width > maxTtSize )
canTh = false;
// 若当前区域宽大于最大 TB 尺寸,或当前区域高大于最大 TB 尺寸,则不尝试 TH
if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY ) canTh = false;
// 若当前色度区域存在,且面积小于等于 32,则不尝试 TH
if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE*2 ) canTh = false;
/*********************************************************/
// 若当前区域宽度小于等于两倍最小 TT 尺寸,或当前区域宽大于最大 TT 尺寸,或当前区域高大于最大 TT 尺寸
// 则不尝试 TV
if( area.width <= 2 * minTtSize || area.width > maxTtSize || area.height > maxTtSize )
canTv = false;
// 若当前区域宽大于最大 TB 尺寸,或当前区域高大于最大 TB 尺寸,则不尝试 TV
if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY ) canTv = false;
// 若当前色度区域存在,且面积小于等于 32 或 宽度为 8,则不尝试 TV
if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE * 2 || areaC->width == 8)) canTv = false;
/*********************************************************/
// 若为 INTER,且当前区域面积为 64,则不尝试 TT
if( modeType == MODE_TYPE_INTER && area.width * area.height == 64 ) canTv = canTh = false;
}
上一篇:H.266/VVC-VTM代码学习21-对划分模式进行RDO的函数xCheckModeSplit
下一篇:H.266/VVC-VTM代码学习23-编码块RDO选择模式(Intra与Inter)initCULevel()