HEVC的算数编码
H.264和HEVC采用了CABAC以极大幅度地提高了算法的压缩比率。由于H.264中所采用的算法存在较强的数据依赖性,因此算法的数据吞吐量收到一定的限制。在HEVC中采用的CABAC算法充分考虑了算法的压缩比率和并行化程度。经过优化的CABAC可以在保持相当高的压缩比率的同时,显著提高处理速度并降低了硬件处理的需求。
1、CABAC熵编码
熵编码是视频编码的最后一步和解码的第一步所使用的一种无损编码。熵编码所处理的对象,是在前期的预测、变换阶段所产生的一系列语法元素(Syntax Elements),包括预测模式和残差数据等。这些语法元素描述了CU,PU,TU和LF等多种语法元素的特性。对CU,有块结构信息以及帧内/帧间预测模式;对PU,描述了帧内预测模式和运动信息等;对TU,主要包含残差信息的变换系数等;LF语法元素在每一个最大编码单元LCU中传输一次,描述了环路滤波中SAO的类型和偏移量。CABAC主要包括三大步骤,即二值化、上下文建模和算数编码。
(1)二值化。
二值化的作用是将语法元素映射为二进制符号(bin)。HEVC中的二值化采用几种不同的方式,与H.264类似,主要有一元(Unary),截断一元(Truncated Unary),k阶指数哥伦布编码(EGK)和定长(Fixed Length)。这几种不同模式的区别体现在将某个无符号整数N二值化的结果的不同,具体如下图所示:
该表中的例子表述了大部分情况所采用的二值化方法,另外可能存在多种方法的组合,以及一些特定化的二值化方法。在实际应用中,具体采用哪一种二值化模式取决于语法元素的类型,或者之前处理过的语法元素的值以及条带参数中的设置。
(2)上下文模型
CABAC之所以在压缩比率上可以取得巨大提高,关键就是因为上下文模型的引入为编码过程提供了精确的概率估计。CABAC采用的上下文模型是高度自适应的,不同二进制码元采用的模型不同,而且可以依据之前处理的二值化码流进行模型更新。每一个bin的上下文模型的选择依据包括语法元素类型、bin的位置、亮度/色度和相邻块信息等。CABAC的概率模型采用7bit结构,其中6bit的概率状态位和1bit的最大概率模型位,在HEVC中其概率模型更新方法与H.264的类似,而改进了上下文选择的逻辑以提高数据处理效率。
(3)算数编码
算数编码是一种基于区间的递归划分的熵编码方法。一个初始化为[0,1]的区间根据bin的概率分布划分为两个子区间,并且依照bin的取值选取两个区间之一。该区间更新为选择的子区间,并进行下一次分割,依此循环往复。为防止下溢出,当区间长度小于某个值时,停止递归并重新进行区间归一化。
在编码的过程中,可以使用概率估计(上下文编码)和等概率模式(旁路编码)。旁路编码中,区间划分由某个偏移量实现,而上下文编码的bin需查表。HEVC的编码过程与H.264类似。
2、CABAC的输出瓶颈
下图为CABAC解码器中的反馈环路。由该图中可以看出主要存在一下几种反馈机制:
(1)递归区间分割中的区间范围更新;(2)精确概率估计对上下文的更新;(3)上下文的选择依赖于语法元素的类型;(4)上下文的选择依赖于bin在语法元素中的位置。
在上图中,1、2环路比较简单,因此对吞吐量没有造成过大影响。如果某一个bin的上下文依赖于并行处理的另一个bin,则会造成较大影响。随着并行bin个数的增加,“预测运算”量将会呈指数增长。因此,吞吐量的瓶颈主要在于上下文选择过程中的依赖性。
3、HEVC的CABAC所做的改进
为提高数据的吞吐量,HEVC主要在前期的CABAC的基础上做了如下改进:
(1)减少上下文编码的bin数量;上下文编码所带来的数据依赖性限制了数据吞吐量,而旁路编码由于没有因为上下文选择带来的数据依赖问题所以更适于并行处理。另外,由于不需要查表,旁路模式的算数编码过程也更为简单。
(2)聚合旁路编码的bin;比特流中连续出现的一串旁路编码bin可以在同一个循环中进行处理。
(3)聚合相同上下文的bin;参考相同上下文的bin聚合在一起,可以减少“预测运算”,同时减少上下文切换次数。
(4)减少上下文选择的依赖性;由于上下文选择过程中的数据依赖性,循环解码多个bin需要大量“预测操作”;减少此类依赖性可以简化上下文选择的过程并减少“预测操作”数量。
(5)减少bin的总量;主要考虑降低算数编码本身的负载;
(6)减少解析的依赖性;尽量隔离CABAC解析同其他所有的处理过程;
(7)减少内存需求;减少内存的需求也就是降低了内存的存取时间;
4、编码预测单元PU
PU中主要包含了帧内和帧间预测信息,并取得了比H.264更高的编码效率。
(1)对运动信息的编码
该部分主要包括减少解析块合并的依赖、减少运动矢量预测的依赖、减少上下文编码bin和减小内存需求。
①减小块合并的依赖:
HEVC中的块合并功能可以通过时空邻域信息获取预测方向、参考索引和运动矢量等运动信息。在该模式中,merge_idx表示该从哪一个候选中获取运动信息,该变量采用截断一元编码。理论上其最大长度cMax应设为PU中用于合并的候选列表的长度。但是这需要建立列表以获取表长度,增加解析过程的依赖性。另外,建立候选块列表所需的运算量也相当大,因此该方法效果不佳。
在HEVC中cMax在条带头中标记,并不依赖与表的长度。为了弥补定长cMax带来的效率损失,在表长度不足的情况下,将联合/零合并候选块加入表中。
②减小运动矢量预测的依赖:
在未开启块合并模式下,运动矢量由邻域块的MV进行预测,并通过MVP和MVD记录。在H.264中采用了单一MV预测,采用左、上和右上三个方向的邻域块的中值作为运动矢量预测值;在HEVC中采用了“先进运动矢量预测”(AMVP)的方法,从时间和空间邻域中选定多个候选块数据,经过优化处理,保留两个较优的候选块。mvp−l0−flag标识哪一个候选的MV选作了MVP,并且即使列表中只有一个候选块该标识也会存在。默认候选为0矢量,因此列表永不为空。
③减少上下文编码的bin:HEVC中,上下文编码的bin被极大减少,对MVD的编码,只有前两个bin是上下文编码,其余是一阶指数哥伦布编码bin的旁路编码。
④减少内存需求:HEVC通过改进上下文选择的逻辑,减少了行缓存大小。通过对比每个邻域块的MV信息和阈值16的比较来选择上下文,可以将每个邻域块的MV缓存需求减少1bit。
⑤聚合旁路编码bin:mvd的x和y分量的旁路编码bin将聚合在一起进行处理,以最大化快速旁路编码的效率。
(2)帧内模式的编码
HEVC采用了新的最大概率模式提高编码效率,采用定长为3的由左、上邻域块构建的候选表(可以加入DC、平面等候补选择)。如果采用最大概率模式,使用mpm_idx变量说明采用了哪一个候选。
与帧间模式的编码类似,CABAC编码帧内信息曹勇了减少上下文编码bin和聚合旁路编码bin等技术。
5、编码变换单元TU
在CABAC中,变换系数的位置以“关键度表”的形式编码,关键度表表明了非零系数的位置。系数的level信息只在大于1的系数信息中包含,而所有非零系数信息都包含系数的符号。
(1)关键度表:
在H.264中,会传递一个标志位significant_coeff_flat(SCF)来指示每一个非零系数(采用之字形扫描),随后会传递一个标志last_significant_coeff_flag指示当前是否是最后一个SCF。对4×4和8×8TU中不同位置以及bin是否表示SCF和LSCF,采用不同的上下文。SCF和LSCF是交错分布的,因此在上下文选择过程中,H.264的算法存在大量的二进制依赖关系。
为了减小这种依赖关系,HEVC采用了减少参考邻域的做法,在HM模型不同的阶段,采用了10、8、6、5个等不同的参考邻域。在HM2.0及其以前的版本中,由于关键度表采用之字形扫描,所以取消对角方向的块作为参考邻域会造成比较明显的影响。
在HM4.0模型中,采用了对角线扫描的方式处理SCF。此方法对编码效率有一定的影响,但是却解除了SCF处理中的依赖关系。在HM7.0中,对16×16和32×32TU进行了分割处理,将TU分割成了4×4的子块,并引入了coded_sub_block_flag这个参数。在HM8.0中8×8TU也被分割成了4×4子块,因此之后所有的TU都是基于4×4子块组成的。
在对8×8、16×16和32×32TU的处理中,每个块都被划分为DC、低频区和高频区三部分,每部分都采用不同的上下文。同时为了节约内存,16×16和32×32的SCF共享内存空间。
对于处理帧内预测的CU时,引入了一种“依赖模式系数扫描”(MDCS),根据帧内预测模式选择水平、垂直和对角线扫描。
“最末位”编码:
由于H.264的SCF和LSCF相互交错,它们之间存在较强的相互依赖性。主要解决方法有:将数个SCF组合,并为这N个SCF传递一个LSCF;或者避免将SCF和LSCF交替出现;以及将一个LSCF分离成x,y两个分量传输。“最末位”由一个前缀和后缀两部分组成。前缀对x和y分别采用cMax的截断一元编码码,以TU的宽度和高度为上下文;后缀采用定长旁路编码。
编码子块标志位(CSBF):
每一个TU被分割成4×4大小的子块。CSBF用于标识某个子块是否包含非零系数。若该参数为1,则该子块中含有SCF;反之,若子块中仅仅包含0系数,则不存在SCF的值。该方法在HM7.0中扩展,用以降低16×16和32×32TU的上下文选择中的依赖性,并且在HM8.0中应用到了8×8TU中。同时,分别采用了四种模式,将不同位置的邻域子块中的元素同上下文模型的选择结合,以解除对其的依赖性。
(2)系数的等级和符号
在H.264中,系数等级由两部分组成。前14位由截断一元二值化生成,采用上下文编码,剩余位由0阶指数哥伦布编码产生,采用旁路编码。在系数等级编码完成后,采用旁路编码处理系数的符号。
①等级编码:
在HEVC中,系数等级仅仅有前两位(coeff_abs_level_greater1_flag和coeff_abs_level_greater2_flag)采用上下文编码,剩余部分(coeff_abs_level_ramaining)采用旁路编码。这样可以极大程度地降低了整个CABAC中上下文编码的码子数量。coeff_abs_level_ramaining的前缀的二值化方法采用一元码,后缀则采用定长码,码长取决于前缀的值。同时,定长码子的数目还依赖于cRiceParam这个参数,该参数根据之前的非零系数的等级变化。coeff_abs_level_ramaining的最大取值为32.
对于每一个4×4子块而言,每一个子块最多包含8个coeff_abs_level_greater1_flag和1个coeff_abs_level_greater2_flag。减少这两个flag的数量将有效减少上下文的的数量。
对系数等级进行上下文选择,主要经过以下步骤:
1)对子块中的16个系数进行扫描;——>2)根据前一个子块中大于1的系数的个数,为当前子块选定上下文集合;——>3)根据当前块内的1系数,在集合中选择上下文模型。
在HM3.0中,每个上下文集合包含5个上下文,共6种集合备选;低频/非低频部分,亮度/色度分量,以及coeff_abs_level_greater1_flag和coeff_abs_level_greater2_flag均需要不同的上下文,因此共需要120种不同的上下文模型。而发展到了HM6.0,所需的上下文模型减少到了仅需要30个。
②符号编码:
每一个4×4子块内的16个系数的符号被聚合在一起处理,这些系数的符号位码元位于coeff−abs−level−remaining的码元之前,并且在该元素解码完成之后可以立刻获取。
除此之外,还采用了数据隐藏的方法提高编码的效率。若一个子块内非0系数的个数超过了某一阈值,则符号位可由非零系数个数的奇偶性推测。