【本文主要分为前后两部分,前半部分基本是Vivienne Sze、Madhukar BudagaviGary和J. Sullivan所编著的《High Efficiency Video Coding (HEVC) ——Algorithms and Architectures》的第三章前半部分的笔记,后半部分是在HM-10.0中对Intra预测时块分割相应的代码研究。】
0、摘要
在基于块结构的混合编码框架中,每一帧图像被分割成多个像素结构的像素块(block),而一帧图像中多个像素块聚合成为一个条带结构(slice)并以此作为可独立解码的数据单位。新的编解码标准HEVC在遵循这一传统框架的同时,在图像-块级和块-像素级的像素数据分割方面提供了多种新的技术。本文首先说明了HEVC的四叉树像素分割结构:引入了CTB,
CB, PB, TB等结构的改进的预测和变换技术,然后评估了采用不同的配置(如在变换和预测中采用不同的块大小和树的深度)时HEVC的编码效率。这些评估实验结果表明,相比于H.264,HEVC所节省的码率中超过一半可归功于改进的预测/变换块结构。本文的第二部分叙述了主要用于数据并行处理和打包的改进图像分割方法,包括适用于并行处理的高层并行,如tiles和WPP等;此外,还叙述了一种新的概念即依赖性条带,此类的条带可作用于并行及超低延迟系统中。【限于篇幅和研究进度,这两部分暂时不做详述】
1、引言
HEVC这一编解码标准依然依据基于像素块的混合编码这一证明有效的框架而制定。在这一框架下,一帧图像首先被分割成多个像素块,然后这每一个像素块使用帧内或者帧间方法进行预测。帧内预测将去除某一帧图片内相邻像素块之间的空间冗余,而帧间估计利用的是不同帧之间的事件冗余。预测编码的冗余,即预测残差数据进行变换编码。变换编码消除的是一个像素块内部的空间冗余,其主要过程由去相关现行变换、对变换系数进行标量量化以及对量化后的变换系数进行熵编码。
2、预测和变换编码的像素块分割
在各种不同的编解码标准之间非常重要的区别之一是像素块分割模式集合的不同。一方面,选定的模式决定了当前像素块采用帧内还是帧间预测进行编码;另一方面,它还决定了一个给定的像素块是否进一步分割成更小的子块以进行预测或变换编码的方式。对于用作预测编码的像素块,通常会配合一些预测参数,如运动矢量或者帧内预测模式等。
混合编码结构的编码效率取决于多个方面,如亚像素差值滤波器的选择、熵编码的效率或者采用的环路滤波器等。然而使得新编码标准产生划时代提高的主要方法通常是增加编码一帧图像或者一个像素块时所支持的模式种类,主要包括更高精度的运动矢量、在选择图像顺序时更高的灵活性、更多的帧内预测模式、更多的运动矢量预测、支持更多的变换块的尺寸以及运动补偿预测时更多种类的块形状。本文中主要研究帧内预测、运动补偿预测和变换编码中不同的图像-块分割以及块-子块分割方法。在给定的像素块的情况下,预测或者变换时选择不同的子块分割模式需要在码率和失真度之间获取平衡。在将一个像素块分割成多个子块,对每一个子块选择最优预测参数时,通常可以减低预测误差的能量,但是增加了传输这些预测参数所需要的数据量。在考虑率失真的情况下,某一种分割方式是否最优取决于不同像素块的实际情况。在一个合适的编码判定算法中,增加支持的分割模式通常意味着需要增加更多的数据量来传输这些选择的模式,但是减低了由于编码预测残差产生的平均率失真代价。需要注意的是,编码器支持的分割模式越多,那么通常所要求的运算复杂度就越高。因此在设计编解码标准时,需仔细权衡考虑编码效率与运算复杂度之间的关系。
由于硬件计算能力的发展,新制定的编码标准所支持的模式种类相比原标准大幅增加。在制定HEVC时,考虑到高清和超高清视频的日渐流行,在进行运动补偿预测和变换编码时支持更大的块尺寸可以取得更好的效果。同时考虑到体现图像本身的特点,支持小尺寸的像素块依然有必要。为了兼顾二者的需求,HEVC采用了一种基于一种简单一致而又有效的四叉树结构的块分割方法。同时,这种四叉树结构的块分割方法还允许研究者在编码器中依据拉格朗日率失真代价来采用快速截止分割算法。
2.1、CTB和CTU
在H.264以及以前的标准中,视频呢序列中的每一帧被分割为宏块(macroblock)作为编码的基本单位。每一个宏块包含一个16×16的亮度块以及两个8×8的色度块(仅考虑4:2:0色度采样视频)。编码过程中,编码器为当前宏块选择一种编码的模式,该模式决定了宏块中所有的像素采用帧内预测还是帧间预测。根据不同标准的不同,每一个宏块选定了编码模式之后进一步确定如何分割成更小的子块进行编码。一个宏块所代表的16×16的像素块代表了可以作为判定编码模式的最大单位。MPEG-2和H.264等编码标准通常用于处理的是从QCIF到SD清晰度的视频,随着HD、UHD(如3840×2160甚至7680×4320分辨率)的日渐流行,亟需一种可以高效处理高清超高清视频的编码标准。在编码此类视频时,如果继续依照前代标准所定义的16×16大小的宏块作为判定编码模式的最大单位从率失真意义上来看是低效率的,因为对于高清视频而言,运动区域的大小可能远超过16×16像素大小。另一方面,更大的块尺寸对于低清晰度视频和运算能力不强的编码器会产生不利影响。因此HEVC包括了一种更加灵活的帧像素分割方法,可以将图片分割为多个大小不同的基本处理单元。
HEVC中,每一帧图像被分割成方形的CTB。每一个亮度CTB和其对应的两个色度CTB以及相应的语法结构组成一个CTU。一个CTU表示HEVC中一个最基本的编码单元,与之前的标准中的宏块概念类似。对于4:2:0采样的视频,一个亮度CTB区域为一个2^N*2^N大小的像素块,两个色度CTB都是2^(N-1)*2^(N-1)大小。参数N的取值可能为4、5、6,并由编码器写入码流的SPS中。这三种取值对应的CTU大小分别为16×16、32×32和64×64像素。更大尺寸的CTU通常可以获得更高的编码效率,但是可能会造成更高的编解码延迟。编码器在编码时根据需要选择适当的CTU边长来获得效率与低延迟的平衡。
2.2、CB和CU
在之前的标准中,一个宏块不仅仅用于分割视频的帧,而且表示编码器可以选择编码模式的基本处理单元。对于每个宏块,其内部所有的亮度和色度像素判定由帧内预测或者帧间预测进行编码。对于帧内和帧间这两种预测方法,一个宏块通常情况会进一步划分成更小的块以进行预测编码并赋予响应的预测参数。在应用广泛的H.264的high profile中,每个宏块供定义了三种帧内编码的模式,分别是Intra-4×4,Intra-8×8和Intra-16×16。在Intra4×4模式中,每一个宏块被分割成16个4×4大小的子块。每一个4×4的子块根据当前已经编码的相邻块的像素进行预测,而预测残差采用4×4大小的变换矩阵进行变换。当采用Intra8×8模式进行编码时,预测和量化处理均由8×8大小的块完成。对于Intra16×16模式,整个宏块的16×16像素矩阵利用已经编码的相邻块的像素值进行预测,对于16×16的预测残差,采用两步变换法(可认为是一种16×16变换的低复杂度改进技术)。对于这三种模式,色度像素块利用整个8×8大小的像素矩阵进行预测,残差采用一种8×8的两步变换法进行变换。
对于帧间预测,H.264定义了四种分割模式:Inter-16×16,Inter-16×8,Inter-8×16和Inter-8×8模式。对于Inter-16×16模式,宏块内所欲的亮度和色度像素都采用同一个运动参数集;对于Inter16×8和Inter8×16模式,宏块的亮度和色度块被分别水平和垂直地分割为两个相同尺寸的矩形块,二者分别赋予一个运动参数集。对于Inter8×8模式,整个宏块分割为4个大小均为8×8的子宏块,每个宏块或按照一整个8×8的块进行预测,被赋予一个运动参数集,或者进一步分割成4个4×4的子块。对于采用帧间预测的亮度分量残差数据,H.264
High profile支持基于4×4和8×8的变换矩阵,具体的选择在宏块层指定。如果整个宏块采用Inter8×8模式进行编码,并且至少一个子块进行了进一步划分,那么码流中不传输表示变换矩阵尺寸的语法元素,并规定此时会采用4×4大小的变换矩阵。帧间预测不支持跨像素块边界的变换。帧间预测的色度数据始终采用4×4的变换矩阵。下图是H.264所支持的帧内和帧间预测的宏块分割模式:
在HEVC中,视频的帧进行分割而成的基本处理单元大小为64×64像素(以亮度分量为考察对象)。如果直接引入H.264的方法会造成一些问题:从率失真的角度考虑,对大尺寸的像素块选择帧内或者帧间预测效率较差。对于P和B条带而言,通常多数像素更适合帧间预测处理,少数像素数据适用于帧内编码。如果标准只允许在CTU层次上选择编码模式,那么可能造成较大的编码效率损失。实验表明,允许在比16×16更低的层次上选择编码模式有助于提高编码效率;从另一方面考虑,在大尺寸的像素区模仿H.264的macroblock和sub-macroblock结构会造成语法结构的复杂化,而且如果在一个CTB中变换矩阵的大小不能改变,编码器将很难自适应处理图像的局部特征。
为了解决这些潜在的问题,HEVC定义了另一个数据处理结构——Coding Unit。每一个CTU可以分割为多个不同尺寸的CU,这样CTU便形成了一个树形结构,称作Coding Tree,表示CTU分割为CU的结构。同CTU类似,一个CU包含一个方形的亮度像素块,两个对应的色度块以及相应的附加语法结构。每一个CU中包含的亮度和色度像素矩阵乘坐Coding Block。在CTU分割为CU的过程中,亮度和色度分量总采用相同的方式,每一个2^N×2^N的亮度分量总是对应两个2^(N-1)×2^(N-1)的色度分量。下图表示一个CTU按照四叉树结构分割成CU的一个示例:
在CTU层,比特流中包含的一个名为split_cu_flag的标志位,该标志位表示当前CTU是整体作为一个CU还是进行进一步划分;如果进行进一步划分,每一个子块又包含一个split_cu_flag表示整体作为一个CU还是进一步划分;以此类推,直到没有一个子块被标记为继续划分。一个CU的最小尺寸在SPS中标记,其取值范围可以从8×8一直到CTU的大小。当一个子块的大小达到SPS所规定的最小CU大小时,则该子块自动不再进行下一次分割。因此,如果一个低复杂度编码器规定只采用某个大小以上的CU尺寸,那么可以在SPS中指定,这样可以避免编码多余的split_cu_flag的标识。在常用的编码器设置中,CU的大小通常设置为从8×8到64×64。
一个CTU中的CU按照深度优先进行编码,该顺序也被称作Z形扫描。这种方法保证了所有的CU(条带左上边界除外)的左侧和上方的CU都已经完成编码,所以这些CU都可以作为预测数据。每一帧图像的宽和高都必须是CU最小尺寸的整数倍,但是不需要是CTU大小的整数倍。在宽或高不是CTU整数倍的情况下,边界处的CTU自动进行分割直到子块的边界达到图像的边界为止。对于此类强制分割的情况,码流中不会编码split_cu_flag标志位,而最后强制分割产生的子块依然可以进行进一步分割。在图像边界之外的CU不会被编码。
每一个CU表示一个决定编码模式的单元,决定自身采用帧内编码或者帧间编码。从这个概念上看,CU同H.264中的宏块概念相似;而CU同宏块不同的是,CU存在可变尺寸。多个CU组成了一个CTU,CTU表示了帧像素数据分割的基本处理单位。对于帧内预测、帧间预测和预测残差的变换编码,一个CU可以按照树形结构进一步分割为更小的子块。
2.3、PB和PU
每一个CU都会在码流中写入表示预测模式的元素,这个预测模式表示当前CU是采用帧内或者是帧间编码。如果采用了帧内编码,亮度CB会从所支持的35中帧内预测模式中选择最适当的一种进行预测,并将选定的模式作为一个语法元素写入码流中。如果CU的大小为SPS规定的最小值,那么这个CB还可以被分割成4个等大小的子块,每个子块都有独立的帧内预测模式。对于两个色度分量,每一个CU从5个候选模式中选择一个适当帧内预测模式。
实际在处理过程中,标记了帧内预测模式的块并不一定进行实际的预测操作。一个CB可能被分割成多个TB,表示对预测残差进行二维变换的单位。从CB分割成TB的操作由另一个树形结构完成,CU表示这个树的根节点。如果一个亮度CB被分割成4个分别赋予帧内预测模式的子块,那么该CB同样会进行TB的分割以确保每一个TB内的像素采用的都是同一种预测方式。一个被赋予单一的帧内预测模式的块可能会被分割成多个的TB,实际的帧内预测会基于TB实现,因为随着像素点的距离增加其相关性会下降,而使用距离较近的参考数据会获得更好的预测效果。因此,一方面帧内预测的效率随着TB大小的增加而降低,另一方面变换编码的效率(给定比特率的均方误差的指标)随着TB的增大而提高。因此,支持将一个CB(或者标记帧内预测模式的块)分割成多个TB的特性提供了帧内预测和变换编码的效率之前的平衡。从另一个角度讲,对多个TB(实际进行帧内预测的块)编码同一个帧内预测模式可以节省传输多种预测模式的码率资源。
吐过一个CU使用帧间预测进行编码,亮度和色度CB分割成为多个PB。一个PB是采用相同的帧间预测运动参数的矩形像素块。其运动参数包括运动估计的个数(1或2)、参考帧索引和对于每个运动估计的运动矢量。CU的两个色度CB采用同亮度分量同样的分割方式。一个亮度PB、两个色度PB以及相关的语法元素构成一个PU。每一个PU在码流中写入一个运动参数集,用于亮度和色度PB的运动补偿预测。
HEVC支持8中CU分割成PU的方式。一个CU可以按照一整个PU进行帧间编码,也可以分割成两个或者四个PU进行处理。其中CU按整个PU编码的模式成为M×M模式;分成四个等大小PU的模式称为(M/2)×(M/2)模式,仅支持最小CU尺寸且该尺寸大于8×8的情况;对于将CU一分为二的方式,HEVC支持六种分割模式:其中包括(M/2)×M和M×(M/2)模式分别表示将一个CU水平和垂直一分二的分割方式,这两种称为对称分割模式;此外还支持4中非对称分割模式,其中一边是另一边的3倍大小,仅支持大于8×8的CU。在以上限制下,可以用作帧间预测的最小像素块大小为4×8或8×4,且限定只能采用单运动估计。如下图所示:
通常情况下支持的块分割模式越多,帧间编码的编码效率越高。然而更高的编码效率要求编码器遍历计算大量的分割模式,否则更多的模式造成的语法元素集合的扩张可能对编码效率造成不利影响。HEVC所采用的方式是二者的良好均衡。另外,在SPS中可以设置禁止非对称分割,此时分割模式的熵编码方法会做出调整所以剩余的模式会占用更少的比特。此种特性对低复杂度编码器具有较大意义。
在视频编码标准的发展过程中,提升编码效率的重要方式就是在帧间编码中可以支持更多种不同的像素块的尺寸。MPEG-2仅仅支持16×16一种大小的帧间预测块;H.263/MPEG-4 Visual 支持16×16及其8×8的划分;H.264支持4×4到16×16的块(包括矩形块)。在HEVC中,帧间预测块的概念向更大尺寸的块扩展,其范围从4×8或8×4一直到64×64。不同标准支持的块大小如下表:
2.4、RQT, TB和TU
如前文所述,一个CB可以分割成多个TB用于对预测残差进行变换编码。一个TB表示某种颜色分量的一个方形像素区,在此区域内采用相同大小的二维变换矩阵对其进行变换。由一个CB分割成TB的方式为一个四叉树结构,称作residual quardtree。通常情况下,亮度和色度的CB分割成的TB采用的同样的四叉树结构(存在一种例外情况)。
由于TB的存在可变尺寸,RQT可以使变换基本函数去自适应地对残差像素的空域/频域特性进行操作。更大的TB尺寸支持单元内更多的像素点,可以提供更高的频域分辨率;但是更小的TB尺寸的频域分辨率较低,可以提供较高的空间域分辨率。空间域/频域二者的关系可以通过编码器进行权衡,如采用拉格朗日优化技术等。
每一个残差四叉树由三个参数限制:树的最大深度dmax、允许的最小块的尺寸nmin和允许的最大块的尺寸nmax。nmin和nmax的取值范围为2-5,即TB的大小由4×4到32×32。树的最大深度dmax规定了可以继续划分为几层,如当dmax为1时,一个亮度CB可以按照一整个TB进行编码或者分为4个TB编码,且不允许进一步进行划分。
由于TB大小和树的深度的限制,在某些情况下编码器可以依据不同参数推断某些块位于四叉树的分支位置,即该块将被继续划分。例如,RQT根部的cu的亮度CB大小为64×64,最大深度dmax=0,最大变换块大小为32×32,即nmax=5,此时由于亮度CB块的大小大于允许的最大变换块的大小,则该亮度CB将被划分以满足对变换块尺寸的要求。
对于帧间编码,由于变换编码不支持跨过PB的边界,HEVC定义一种特别的情况,当RQT最大深度=0,且CU使用MC编码且涉及到多个PB,那么该CB将自动进行四等分;当dmax>0时RQT与PU的划分无关,这可能会导致一个CB内的多个PB公用一个TB。跨越PB的边界进行变换编码可能会造成部分CB编码效率的损失,但对其他CB而言则可能提高编码的效率。
由3.2.3所述,帧内预测CU的亮度和色度CB分割为TB不仅决定了残差变换操作的块大小,同时也决定了标记一个帧内预测模式的块的大小。如果亮度CB的大小等同于SPS中规定的最小CB尺寸,那么可能其四个等大小的子块每一个都会标记一个帧内预测的模式。再次情况下,一个TB不会覆盖整个CB,因为某个子块的相邻子块必须完全解码完成以作为参考数据。因此,如果一个CB分割成四个分别标记帧内预测模式的子块,那么同时也将分割为4个等大小的TB。而且每一个TB都可能被进一步分割。
当TB到达最小大小时,RQT到达一个叶子节点。另外,递归分割过程还会受到RQT最大深度的限制。两个条件必须同时满足。
在每一个CU中,仅存在一个RQT,该RQT决定了所有颜色分量的TB,因此决定了亮度和色度分量的子块划分。对于4:2:0采样的4×4的亮度TB是一个例外,因为这种情况下会导致产生两个2×2的色度TB,而这是标准所不支持的。因此,在一个RQT中允许分割一个8×8的亮度TB,但是不允许分割相应的两个4×4的色度TB。一个大于4×4大小或者4个4×4大小的亮度TB、对应的两个亮度TB以及相应的语法元素构成一个TU结构。
在参考代码HM中,这部分功能(暂时只考虑Intra模式)在函数xCompressCU中实现:
// further split if( bSubBranch && bTrySplitDQP && uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) { UChar uhNextDepth = uiDepth+1; TComDataCU* pcSubBestPartCU = m_ppcBestCU[uhNextDepth]; TComDataCU* pcSubTempPartCU = m_ppcTempCU[uhNextDepth]; for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ ) { pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // clear sub partition datas or init. pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // clear sub partition datas or init. Bool bInSlice = pcSubBestPartCU->getSCUAddr()+pcSubBestPartCU->getTotalNumPart()>pcSlice->getSliceSegmentCurStartCUAddr()&&pcSubBestPartCU->getSCUAddr()<pcSlice->getSliceSegmentCurEndCUAddr(); if(bInSlice && ( pcSubBestPartCU->getCUPelX() < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < pcSlice->getSPS()->getPicHeightInLumaSamples() ) ) { if( m_bUseSBACRD ) { if ( 0 == uiPartUnitIdx) //initialize RD with previous depth buffer { m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); } else { m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); } } #if AMP_ENC_SPEEDUP if ( rpcBestCU->isIntra(0) ) { xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, SIZE_NONE ); } else { xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, rpcBestCU->getPartitionSize(0) ); } #else xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth ); #endif rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // Keep best part data to current temporary data. xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth ); } else if (bInSlice) { pcSubBestPartCU->copyToPic( uhNextDepth ); rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); } } if( !bBoundary ) { m_pcEntropyCoder->resetBits(); m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true ); rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits if(m_pcEncCfg->getUseSBACRD()) { rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); } } rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); if( (g_uiMaxCUWidth>>uiDepth) == rpcTempCU->getSlice()->getPPS()->getMinCuDQPSize() && rpcTempCU->getSlice()->getPPS()->getUseDQP()) { Bool hasResidual = false; for( UInt uiBlkIdx = 0; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx ++) { if( ( pcPic->getCU( rpcTempCU->getAddr() )->getSliceSegmentStartCU(uiBlkIdx+rpcTempCU->getZorderIdxInCU()) == rpcTempCU->getSlice()->getSliceSegmentCurStartCUAddr() ) && ( rpcTempCU->getCbf( uiBlkIdx, TEXT_LUMA ) || rpcTempCU->getCbf( uiBlkIdx, TEXT_CHROMA_U ) || rpcTempCU->getCbf( uiBlkIdx, TEXT_CHROMA_V ) ) ) { hasResidual = true; break; } } UInt uiTargetPartIdx; if ( pcPic->getCU( rpcTempCU->getAddr() )->getSliceSegmentStartCU(rpcTempCU->getZorderIdxInCU()) != pcSlice->getSliceSegmentCurStartCUAddr() ) { uiTargetPartIdx = pcSlice->getSliceSegmentCurStartCUAddr() % pcPic->getNumPartInCU() - rpcTempCU->getZorderIdxInCU(); } else { uiTargetPartIdx = 0; } if ( hasResidual ) { #if !RDO_WITHOUT_DQP_BITS m_pcEntropyCoder->resetBits(); m_pcEntropyCoder->encodeQP( rpcTempCU, uiTargetPartIdx, false ); rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bits if(m_pcEncCfg->getUseSBACRD()) { rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); } rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); #endif Bool foundNonZeroCbf = false; rpcTempCU->setQPSubCUs( rpcTempCU->getRefQP( uiTargetPartIdx ), rpcTempCU, 0, uiDepth, foundNonZeroCbf ); assert( foundNonZeroCbf ); } else { rpcTempCU->setQPSubParts( rpcTempCU->getRefQP( uiTargetPartIdx ), 0, uiDepth ); // set QP to default QP } } if( m_bUseSBACRD ) { m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]); } Bool isEndOfSlice = rpcBestCU->getSlice()->getSliceMode()==FIXED_NUMBER_OF_BYTES && (rpcBestCU->getTotalBits()>rpcBestCU->getSlice()->getSliceArgument()<<3); Bool isEndOfSliceSegment = rpcBestCU->getSlice()->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && (rpcBestCU->getTotalBits()>rpcBestCU->getSlice()->getSliceSegmentArgument()<<3); if(isEndOfSlice||isEndOfSliceSegment) { rpcBestCU->getTotalCost()=rpcTempCU->getTotalCost()+1; } xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth); // RD compare current larger prediction }
最外层的bSubBranch表示当前CU是否被设置为CU分割提前截止,如果为真,则不会继续向下分割;bTrySplitDQP与另一个变量bTrySplit同fast encoder decision的设置有关;uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth则反映了上文中叙述的最大分割深度的问题。在这里,当前的cu在满足上文所述条件时,分别遍历四个子块,并对四个子块分别递归调用xCompressCU函数实现四叉树分割编码。