【H.264/AVC视频编解码技术详解】二十一、熵编码(6):CABAC的上下文环境

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shaqoneal/article/details/82194594

《H.264/AVC视频编解码技术详解》视频教程已经在“CSDN学院”上线,视频中详述了H.264的背景、标准协议和实现,并通过一个实战工程的形式对H.264的标准进行解析和实现,欢迎观看!

“纸上得来终觉浅,绝知此事要躬行”,只有自己按照标准文档以代码的形式操作一遍,才能对视频压缩编码标准的思想和方法有足够深刻的理解和体会!

链接地址:H.264/AVC视频编解码技术详解

GitHub代码地址:点击这里

一、CABAC中的概率模型

1.1 为什么采用概率模型

在CABAC编码中,对每一个符号(即经过二值化的语法元素中的每一个bit值),都会经过一个构建模型阶段,即为该符号分配一个概率分布模型。在随后的编码阶段,算术编码器内核会根据这个概率模型进行编码,生成输出的二进制比特流。由于这个概率模型直接决定了编码阶段的性能,因此在构建概率模型时,应尽可能地使该模型可以符合符号的统计情况,并且在编码的过程中实时更新。

在H.264编码的实际应用中,为了尽可能降低编码的运算量,CABAC定义了基于查表的的概率模型。每当编码完一个bit值需要更新索引时,编码器不再是通过计算更新概率,而是通过查表来获取最新的上下文。

1.2 CABAC上下文模型的定义

我们已经知道,在CABAC中所有的信息都会转换为二进制数据进行编码,即0和1两种字符。因此在CABAC中,概率模型实际上只包含有一个概率——P(a),另外一个元素P(b)实际上等于1-P(a),其中a=1,b=0或相反。无论二者关系如何,总有一个值的概率不小于0.5,另一个值的概率不超过0.5。其中,概率不大于0.5的字符定义为LPS(Least Probability Symbol),概率不小于0.5的字符定义为MPS(Most Probability Symbol),分别用于表示当前状态下的小概率字符以及大概率字符。

在1.1节中可知,对于CABAC执行过程中的每一个状态,其MPS和LPS的概率通过一个预定义的表格来进行查询和转移,因此上下文模型中还需要一个索引值来表示当前在表格中所处的位置。因此在实现中,上下文模型可以用如下结构体表示:

//! struct for context management
typedef struct
{
  unsigned short state;         // index into state-table CP  
  unsigned char  MPS;           // Least Probable Symbol 0/1 CP
} BiContextType;

typedef BiContextType *BiContextTypePtr;

二、 上下文模型索的初始化与更新

在这一章节中,我们以mb_type为例,来说明如何获取解析该语法元素上下文的过程。

2.1 上下文模型的初始化

初始化CABAC上下文模型是编码器初始化的重要步骤,其本质就是针对上节定义的上下文模型结构BiContextType的初始值。初始化上下文模型的部分在标准文档的9.3.1.1节中定义。其中规定的初始化上下文模型的方法如下式表示:

preCtxState = Clip3( 1, 126, ( ( m ∗ Clip3( 0, 51, SliceQPY ) ) >> 4 ) + n )
if( preCtxState <= 63 ) {
    pStateIdx = 63 − preCtxState
    valMPS = 0
} else {
    pStateIdx = preCtxState − 64
    valMPS = 1
}

从上式中可知,如果希望初始化上下文模型的变量pStateIdx和valMPS,我们所必须的数据有SliceQPY、m与n等3个数值。其中我们所必须的数据有SliceQPY表示当前slice的亮度分量的量化参数初始值,可以通过pps结构中的pic_init_qp和SliceHeader中的slice_qp_delta值计算。m和n的值通过查表获取,实际查询的表格由语法元素类型、帧类型和cabac_init_idc决定。

不同的语法元素之间采用的初始上下文模型索引值不同,下表中列出了不同的语法元素在不同的帧类型下所采用的上下文索引ctxIdx的范围,以及初始化时查找m和n的表:

例如在初始化I帧的宏块类型mb_type这个语法元素的上下文模型时,参考表9-12:

2.2 上下文模型的更新

在上一节中提到,CABAC为了提高编码效率,需要实时更新。概率模型实时更新的过程中,不仅仅是MPS(或LPS)的概率在更新,甚至是MPS和LPS本身所代表的符号也可能会互换。下图表示了CABAC的上下文模型在编码过程中的刷新关系:

上图表示了概率模型在CABAC编码过程中的更新原理。LPS的概率取值区间[0,0.5]被划分为64个概率值,分别对应了索引值为0\~63的各种状态,其中在编码过程中可供更新概率模型使用的状态为0\~62。在CABAC编码过程中,概率模型处在上图的某一个点的位置上。此时根据当前处理的二进制字符的取值,采取不同的模型更新方法:

  • 当前字符为MPS,沿实线部分向右更新;此时LPS的概率趋向于越来越小,如果MPS持续出现,则LPS概率最终会更新到索引值为62的状态(此时LPS的概率达到预定义的最小值),而后保持在该状态不再变化,直到一个LPS出现;
  • 当前字符为LPS,沿虚线部分向左更新,此时LPS的概率趋向于越来越大,当索引值降为0时,LPS的概率达到最大的0.5;如果下一个字符依然为LPS,则认为LPS的实际概率已经超过0.5,LPS与MPS对调,继续按当前规则更新。

三、上下文索引的推导方法

3.1 二值化过程的“副产品”

针对每个语法元素推导上下文模型索引是CABAC的重要步骤之一。为了实现上下文索引的推导,需要语法元素二值化步骤中提供的部分数据。在标准文档的9.3.2节“二值化过程”中说明,二值化过程的输出结果,除了语法元素的二值化码流之外,还包括三个值:maxBinIdxCtx、ctxIdxOffsetbypassFlag

  • maxBinIdxCtx:该语法元素索引的上限,通常也可认为以最多多少个bit位来表示二值化后的语法元素;
  • ctxIdxOffset:即context index offset,该值作为推导上下文索引的数据来源;
  • bypassFlag:在满足一定条件下,该标志位设为1,表示该语法元素以bypass模式进行编码;

在二值化的过程中,上述3个值根据语法元素的类型在表9-34中查表确定。例如,我们以I帧的mb_type这一语法元素为例:

由该表的规定可知,对于I Slice宏块的mb_type而言,maxBinIdxCtx和ctxIdxOffset统一规定为6和3。

3.2 推导上下文模型索引的过程

推导上下文模型索引的过程定义在标准协议文档的9.3.3.1节。这个过程中的输入数据主要包括二值化所生成的maxBinIdx和ctxIdxOffset等,输出数据为上下文模型的索引ctxIdx。除此之外,还需要使用另外一个中间变量ctxIdxInc(即ctxIdx increments,上下文索引增量)。表9-39列出了ctxIdxInc与ctxIdxOffset和binIdx的对应关系:

从上表中可知,对于语法元素mb_type,ctxIdxInc可能的取值有0~7,计算过程在9.3.3.1.1.3和9.3.3.1.2中说明。

3.2.1 ctxIdxInc的计算方法

在标准文档的9.3.3.1.1.3节,针对语法元素mb_type,ctxIdxInc的计算方法定义如下:

为了计算ctxIdxInc,需要定义两个值condTermFlagA和condTermFlagB,分别与当前宏块的左侧和上方两个相邻宏块相关联。对于某个相邻宏块,condTermFlagA或condTermFlagB的计算方法为:

  • 当满足如下条件时,condTermFlagA或condTermFlagB设为0:
    1. 该宏块不可得;
    2. ctxIdxOffset为0,且该相邻宏块为SI类型;
    3. ctxIdxOffset为3,且该相邻宏块为I_NxN类型;
    4. ctxIdxOffset为27,且该相邻宏块为B_Skip或B_Direct_16x16类型;
  • 否则该值设为1。

当condTermFlagA和condTermFlagB全部计算完成后,ctxIdxInc的计算方式为二者的和:

ctxIdxInc = condTermFlagA + condTermFlagB;

3.2.2 ctxIdx的计算方法

最终,ctxIdx的值由ctxIdxOffset和ctxIdxInc计算得到:

ctxIdx = ctxIdxOffset + ctxIdxInc;

猜你喜欢

转载自blog.csdn.net/shaqoneal/article/details/82194594