ctus:H.266/VVC的编码结构和块划分

一、将一帧图像划分成CTUs

VVC中一帧图像分成许多编码树单元(CTU)。CTU的概念与HEVC的相同。对于一帧具有三通道的图像,CTU由一个N×N的亮度样本块和两个相应的色度样本块组成。图1显示了一个图片被划分为CTU的示例。

在VVC中为了适应4k、8k等视频编码的需要,CTU中的亮度块的最大允许尺寸被指定为128×128(HEVC是64×64),注意亮度变换块的最大尺寸为64×64。

Figure1.Example of a picture divided into CTUs 

二、将图像划分成subpictures、slices、tiles

一帧图像可以分为一个或多个tile行和一个或多个tile列。一个Tile是CTU的一个序列,覆盖了图像的一个矩形区域。

一个slice由整数个完整的tiles或图像的tile内连续完整的CTU行组成。

支持两种slice模式,即raster-scan slice mode 和rectangular slice mode。在raster-scan slice mode下,slice包含图片的raster scan中的完整tiles序列。在rectangular slice mode中,slice包含多个共同构成图片的矩形区域的完整的tiles,或包含共同形成图片的矩形区域的一个tile的多个连续完整CTU行。

subpictures包含一个或多个slices,这些slices共同覆盖图片的矩形区域。

Figure2.Example of a picture partitioned into tiles and reaster-scan slices

​图2显示了图像的raster-scan slice分区的示例,其中图像被分为12个tiles和3个raster-scan slices。

Figure3.Example of a picture partitioned into tiles and rectangular slices

​图3显示了图像的rectangular slices分区的示例,其中将图像分为24个tiles(6个tile列和4个tile行)和9个矩形slice。

Figure4.Example of a picture partitioned into 4 tiles and 4 rectangular slices

​图4显示了图像被分为tiles和rectangular slices,其中该图像分为4个tiles(2个tile列和2个tile行)和4个rectangular slices。

Figure5.Example of a picture partitioned into 28 subpictures

​图5显示了一帧图像的子图像(subpicture)分割的示例,其中一幅图像被分割成18个tiles,左侧12个tiles,每一个tile覆盖4×4 CTU的一个slice;右侧6个tiles,每一个tile覆盖2个垂直堆叠的2×2 CTU的slice,总共产生24个slices和24个不同尺寸的子图(subpictures)(每个slice是一个subpicture),其中一幅图片被分成28个不同尺寸的subpictures。

三、CTU的划分

在HEVC中,利用四叉树结构(表示为编码树)将CTU划分为多个CU,以适应不同的局部特征。在子CU水平上决定是使用帧间(时间)预测还是帧内(空间)预测来编码图像区域。

每个子CU可以进一步划分成一个、两个或四个PU,取决于PU的类型。在一个PU内部的像素点应用相同的预测方式进行预测,并且将相关信息以PU为单元发送到解码器。在应用基于PU划分类型的预测过程获得残差块之后,可以根据与CU的编码树类似的另一个四叉树结构将子CU划分为多个变换单元(Tus)。HEVC结构的一个重要特点是具有CU、PU、TU等多重划分概念。

在VVC中,具有使用二叉和三叉分割结构的嵌套多类型树的四叉树替换了多个分区单元类型的概念,即它删除了CU,PU和TU概念的区别,统一采用编码单元(coding unit,CU)的概念,除非当前CU的尺寸大于VVC所支持的最大变换块的尺寸(即此时会将CU划分为TU),并支持更多形状的CU,在编码树结构中,CU可以是正方形或矩形。

编码树单元(CTU)首先会进行四叉树划分,然后利用多类型树结构进一步划分四叉树的叶节点。如图6所示,在多类型树结构中有四种分裂类型:垂直二叉划分(SPLIT_BT_VER)、水平二叉划分(SPLIT_BT_HOR)、垂直三叉划分(SPLIT_TT_VER)和水平三叉划分(SPLIT_TT_HOR)。多类型树的叶节点被称为编码单元(CUs),并且除非CU大于最大变换块尺寸,否则该划分直接用于预测和变换处理而不进行任何进一步划分。这意味着,在大多数情况下,CU、PU和TU在具有嵌套多类型树编码块结构的四叉树中的块大小相同。

Figure6.Multi-type tree splitting modes CTU_LEVEL = 0, //CTU级 CU_QUAD_SPLIT, //四叉树划分 CU_HORZ_SPLIT, //水平二叉树 CU_VERT_SPLIT, //垂直二叉树 CU_TRIH_SPLIT,//水平三叉树 CU_TRIV_SPLIT,//垂直三叉树

 图7说明了嵌套多类型树编码树结构的四叉树中分区划分信息的信令机制。编码树单元(CTU)被视为四叉树的根,首先用四叉树结构进行划分。每个四叉树的叶节点(当足够大以允许它的时候)被一个多类型树结构进一步划分。在具有嵌套多类型树编码树结构的四叉树中,对于每个CU节点,发送第一个标志(split_cu_flag)以指示该节点是否被进一步划分。如果当前CU节点是四叉树CU节点,则会发送第二个标志(split_qt_flag),表示是QT划分还是MTT划分。当使用MTT划分模式对节点进行分区时,需要发送第三个标志(mtt_split_cu_vertical_flag)以指示划分方向,然后发送第四个标志(mtt_split_cu_binary_flag)以指示划分是二叉树划分还是三叉树划分。根据mtt_split_cu_vertical_flagmtt_split_cu_binary_flag的值,推导出了cu的多类型树划分模式(MttSplitMode),如表1所示。

   Figure7.Splitting flags signalling in quadtree with nested multi-type tree coding tree structure

               Table 1 – MttSplitMode derviation based on multi-type tree syntax elements

MttSplitMode

mtt_split_cu_vertical_flag

mtt_split_cu_binary_flag

SPLIT_TT_HOR

0

0

SPLIT_BT_HOR

0

1

SPLIT_TT_VER

1

0

SPLIT_BT_VER

1

1

解码端划分标志的解析代码如下所示:

PartSplit CABACReader::split_cu_mode( CodingStructure& cs, Partitioner &partitioner ){ RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__SPLIT_FLAG, partitioner.currArea().blocks[partitioner.chType].size(), partitioner.chType ); PartSplit mode = CU_DONT_SPLIT; bool canNo, canQt, canBh, canBv, canTh, canTv; partitioner.canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv ); //判断当前分区是否可用进行各种划分 bool canSpl[6] = { canNo, canQt, canBh, canBv, canTh, canTv }; unsigned ctxSplit = 0, ctxQtSplit = 0, ctxBttHV = 0, ctxBttH12 = 0, ctxBttV12; DeriveCtx::CtxSplit( cs, partitioner, ctxSplit, ctxQtSplit, ctxBttHV, ctxBttH12, ctxBttV12, canSpl ); bool isSplit = canBh || canBv || canTh || canTv || canQt; if( canNo && isSplit ) { isSplit = m_BinDecoder.decodeBin( Ctx::SplitFlag( ctxSplit ) ); //解码split_cu_flag,是否进行划分 } DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d split=%d\n", ctxSplit, isSplit ); if( !isSplit ) { return CU_DONT_SPLIT; //不进行划分 } const bool canBtt = canBh || canBv || canTh || canTv; bool isQt = canQt; if( isQt && canBtt ) { isQt = m_BinDecoder.decodeBin( Ctx::SplitQtFlag( ctxQtSplit ) ); //解码split_qt_flag,是否进行四叉树划分 } DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d qt=%d\n", ctxQtSplit, isQt ); if( isQt ) { return CU_QUAD_SPLIT; //四叉树划分 } const bool canHor = canBh || canTh; bool isVer = canBv || canTv; if( isVer && canHor ) { isVer = m_BinDecoder.decodeBin( Ctx::SplitHvFlag( ctxBttHV ) ); // 解码mtt_split_cu_vertical_flag,是否是垂直划分 } const bool can14 = isVer ? canTv : canTh; // 三叉树划分 bool is12 = isVer ? canBv : canBh; //二叉树划分 if( is12 && can14 ) { is12 = m_BinDecoder.decodeBin( Ctx::Split12Flag( isVer ? ctxBttV12 : ctxBttH12 ) ); //是否是二叉树划分 } // 多类型树划分 if (isVer && is12) { mode = CU_VERT_SPLIT; //垂直二叉树 } else if (isVer && !is12) { mode = CU_TRIV_SPLIT; // 垂直三叉树 } else if (!isVer && is12) { mode = CU_HORZ_SPLIT;//水平二叉树 } else { mode = CU_TRIH_SPLIT; //水平三叉树 } DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctxHv=%d ctx12=%d mode=%d\n", ctxBttHV, isVer ? ctxBttV12 : ctxBttH12, mode ); return mode;}

图8显示了用四叉树和嵌套的多类型树编码块结构划分为多个CU的CTU,其中粗体块边表示四叉树分区,其余边表示多类型树分区。嵌套多类型四叉树划分的使得CU的形状更能适应图像的内容。CU的亮度分量的尺寸可以和CTU一样大,最小为4x4。对于4:2:0YUV视频格式,最大色度分量的CB大小为64×64,最小色度CB块由16个像素组成。

  Figure 8– Example of quadtree with nested multi-type tree coding block structure

​在VVC中,最大支持的亮度变换块尺寸大小为64×64,最大支持色度变换块尺寸大小为32×32。当CB的宽度或高度大于最大变换宽度或高度时,CB在水平方向和/或垂直方向上自动划分,以满足在该方向上的变换尺寸限制。

下面是嵌套多类型树编码树方案的四叉树相关的SPS层语法参数:

  • CTU size: the root node size of a quaternary tree
  • CTU size: 四叉树的根节点的尺寸大小
  • MinQTSize: 允许的最小四叉树叶节点尺寸大小
  • MaxBtSize: 允许的最大二叉树根节点尺寸大小
  • MaxTtSize: 允许的最大三叉树根节点尺寸大小
  • MaxMttDepth: 从四叉树叶分割的多类型树的最大允许层次深度
  • MinBtSize: 允许的最小二叉树叶子节点尺寸大小
  • MinTtSize: 允许的最小三叉树叶子节点尺寸大小

举嵌套多类型树编码树结构的四叉树的一个例子:

对于4:2:0的YUV序列,CTU的默认大小是128×128(即有一个128×128的亮度CTB和两个64×64的色度CTB),MinQTSize被设置为16×16,MaxBtSize被设置为128×128,MaxTtSize被设置为64×64,MinBtSize和MinTtSize(宽度和高度)被设置为4×4,最大深度设为4。

首先将CTU进行四叉树(QT)划分,生成四叉树叶节点。四叉树叶节点的大小在16×16(即MinQTSize)到128×128(即CTU大小)之间。如果QT叶节点是128×128,则不会被二叉树和三叉树进一步划分,因为其大小超过例了MaxBtSize和MaxTtSize(64×64)。如果四叉树子节点大小不是128×128,四叉树叶节点可以被多类型树进一步划分,此时,四叉树叶节点也是多类型树的根节点,其多类型树深度(mttDepth)为0。当多类型树深度达到MaxMttDepth(4)时,不考虑进一步的划分。当多类型树节点的宽度等于MinBtSize且小于或等于2*MinTtSize时,不考虑进一步的水平划分。类似地,当多类型树节点的高度等于MinBtSize且小于或等于2*MinTtSize时,不考虑进一步的垂直划分。

在VVC中,编码树方案支持亮度和色度具有单独的块划分结构。目前,对于P和B slices,同一个CTU中的亮度和色度 CTBs必须具有相同的编码树结构。然而,对于I slices,亮度和色度可以有单独的块树结构。当亮度和色度分量的划分结构不同时,亮度CTB被一种编码树结构划分为Cus,色度CTB被另一种编码树结构划分为chroma Cus。这意味着I片中的CU可能仅包含一个亮度分量的编码块(CB)或者仅包含两个色度分量的编码块(CB)组成,而P或B片中的CU始终由所有三个颜色分量的编码块组成,除非视频是单色的。

四、图像边界的CU划分

如在HEVC中所做的,当树节点块的一部分超过底部或右侧图片边界时,树节点块被强制划分,直到每个编码CU的所有样本都位于图片边界内。VVC中应用了以下划分规则:

  • 如果树节点块的任何部分超出图片底部或右侧边界,则不允许TT拆分。
  • 如果树节点块的任何部分超出图片底部或右侧边界,并且由于块大小限制而不允许任何QT和BT划分,则强制使用QT划分模式划分该块。
  • 否则,如果树节点块的一部分超出底部和右侧图片边界,
    • 如果块是QT节点,并且块的大小大于最小QT大小,则强制使用QT划分模式划分块。
    • 否则,将强制使用“SPLIT_BT_HOR”模式划分块
  • 否则,如果树节点块的一部分超出底部图片边界
    • 如果该块是QT节点,并且该块的大小大于最小QT大小,并且该块的大小大于最大BT大小,则强制使用QT模式划分该块。
    • 否则,如果该块是QT节点,并且该块的大小大于最小QT大小,并且该块的大小小于或等于最大BT大小,则该块将强制使用QT划分模式或SPLIT_BT_HOR进行划分,并发送split_qt_flag标志,以指定这种情况下的划分模式。
    • 否则(该块是BTT节点或该块的大小小于或等于最小QT大小),该块将被强制以SPLIT_BT_HOR划分。
  • 否则,如果树节点块的一部分超出了右侧的图片边界
    • 如果该块是QT节点,并且该块的大小大于最小QT大小,并且该块的大小大于最大BT大小,则强制使用QT模式划分该块
    • 否则,如果该块是QT节点,并且该块的大小大于最小QT大小,并且该块的大小小于或等于最大BT大小,则该块将被迫使用QT模式或SPLIT_BT_VER拆分,并向发送标志split_qt_flag,以指定这种情况下的划分模式
    • 否则(该块是BTT节点或该块的大小小于或等于最小QT大小),该块将被迫以SPLIT_BT_VER拆分

五、CU划分冗余的限制

四叉树具有嵌套多类型树编码块结构,提供了一种高度灵活的块划分结构。由于多类型树支持的划分类型不同,不同的划分模式可能导致相同的编码块结构。在VVC中,一些冗余的划分模式是不允许的。

图10说明了二叉树和三叉树的冗余划分模式。如图10所示,在一个方向上连续的两次的二叉树划分,和三叉树划分后的再对中间进行相同方向的二叉树划分具有相同的编码块结构。在这种情况下,VVC禁止了三叉树划分后,再对同方向上对中间分区进行二叉树划分。此限制适用于所有图片中的Cus。

Figure 10–Redundant splitting patterns of binary tree split and ternary tree split cases

当如上所述禁止划分时,相应语法元素的信令将被修改以考虑禁止的情况。例如,当识别图10中的任何情况时(即对于中间分区的CU禁止二叉树划分),语法元素mtt_split_cu_binary_flag(指定分割是二叉树划分还是三叉树划分)不发出信号,而是由解码器推断为等于0。

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 ); const unsigned maxBTD = cs.pcv->getMaxBtDepth( *cs.slice, chType ) + currImplicitBtDepth;//最大多类型树划分深度 3 const unsigned maxBtSize = cs.pcv->getMaxBtSize ( *cs.slice, chType );// 最大二叉树划分尺寸 32 const unsigned minBtSize = cs.pcv->getMinBtSize ( *cs.slice, chType );// 最小二叉树划分尺寸 4 const unsigned maxTtSize = cs.pcv->getMaxTtSize ( *cs.slice, chType );// 最大三叉树划分尺寸 32 const unsigned minTtSize = cs.pcv->getMinTtSize ( *cs.slice, chType );// 最小三叉树划分尺寸 4 const unsigned minQtSize = cs.pcv->getMinQtSize ( *cs.slice, chType );// 最小四叉树划分尺寸 8 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; // 如果上一级的划分不是CTU级或者四叉树划分,则禁止四叉树划分 if( area.width <= minQtSize ) canQt = false;//如果当前宽度小于最小四叉树划分尺寸,则禁止四叉树划分 // 如果当前是色度块,且色度宽度小于等于4,则禁止四叉树划分 if( areaC && areaC->width <= MIN_DUALTREE_CHROMA_WIDTH ) canQt = false; if( treeType == TREE_C ) { // treeC情况下,禁止进一步划分(防止出现色度小块) 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; // 对于小于16个像素的色度块,禁用水平二叉树划分 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; // 对于小于16个像素的色度块或者宽度等于4的色度块,禁用二叉树垂直划分 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;}

六、 Virtual pipeline data units (VPDUs)

虚拟管道数据单元(VPDUs)被定义为图像中不重叠的单元。在硬件解码器中,连续的vpdu由多个流水线级同时处理。在大多数流水线阶段,VPDU大小大致与缓冲区大小成正比,因此保持VPDU大小较小很重要。在大多数硬件解码器中,VPDU大小可以设置为最大变换块(TB)大小。然而,在VVC中,三叉树(TT)和二叉树(BT)的划分会导致VPDU大小的增加。

为了使VPDU的大小保持为64x64亮度块的大小,在VTM中应用了以下规范的分区限制(带语法信令修改),如图11所示:

  • 对于宽度或高度等于128的CU,或者宽高同时等于128的CU,不允许TT拆分。
  • 对于N≤64的128xN的CU(即宽度等于128,高度小于128),不允许水平BT。
  • 对于N≤64的Nx128的 CU(即高度等于128,宽度小于128),不允许垂直BT。

Figure 11 – Examples of disallowed TT and BT partitioning in VTM 

                                         

七、帧内色度划分和预测限制

在典型的硬件视频编码器和解码器中,由于相邻帧内块之间的样本处理数据依赖性,当图片具有更多小帧内块时,处理吞吐量下降。帧内块的预测器生成需要来自相邻块的顶部和左侧边界重构样本。因此,帧内预测必须逐块顺序地处理。

在HEVC中,最小的帧内CU的亮度分量是8x8的亮度块。最小帧内CU的亮度分量可以进一步划分成四个4x4亮度帧内预测单元(pu),但是最小帧内CU的色度分量不能进一步划分。因此,当处理4x4色度帧内块或4x4亮度帧内块时,出现最坏情况的硬件处理吞吐量。在VVC中,为了提高最坏情况下的吞吐量,通过限制帧内色度CB的划分来禁止小于16个像素的的色度帧内CB(大小2x2、4x2和2x4)和宽度小于4个色度像素的色度帧内CB(大小2xN)。

1. single coding tree

在single coding tree中,最小色度帧内预测单元(SCIPU)被定义为其色度块大小大于或等于16个色度像素并且具有至少一个子luma块小于64个luma像素的编码树节点,或者其色度块大小不是2xN并且具有至少一个子luma块为4xN的luma像素的编码树节点。

要求在每个SCIPU中,所有CB都是inter,或者所有CB都是非inter,即帧内或帧内块拷贝(IBC)。如果是非inter的SCIPU,则进一步要求非inter的SCIPU的色度不得进一步划分,并且允许SCIPU的亮度进一步划分。这样,去除小于16个色度像素或大小为2xN的色度帧内CB。此外,在非帧间SCIPU的情况下不应用色度缩放。这里,没有发送附加语法,并且可以通过SCIPU中的第一个亮度CB的预测模式来导出SCIPU是否是非帧间的。

如果当前Slice是I Slice,或者当前SCIPU在进一步划分一次后有一个4x4亮度分区,则SCIPU的类型推断为非inter(因为VVC中不允许inter 4x4);否则,在解析SCIPU中的Cus之前,由一个标志指示SCIPU的类型(inter或non-inter)。

2. dual tree in intra picture

对于帧内图像中的双树,通过分别禁用4xN和8xN色度分区的垂直二叉树划分和垂直三叉树来移除2xN帧内色度块。也通过分区限制删除大小为2x2、4x2和2x4的小chroam块。

此外,通过将图片宽度和高度考虑为max(8,MinCbSizeY)的倍数,考虑对图片大小的限制以避免图片角落的2x2/2x4/4x2/2xN内色度块。

相关推荐

相关文章