H.266/VVC-VTM代码学习22-partitioner类判断划分模式是否可用函数partitioner.canSplit

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()

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值