整理网址:http://www.cnblogs.com/TaigaCon/p/5304563.html
整理网址:http://blog.csdn.net/shakingwaves/article/details/52426244
表格生成:http://pressbin.com/tools/excel_to_html_table/index.html
在视频数据压缩种,按照压缩前后图像信息量是否有损失,可以将压缩方法分为两类:一类是无失真编码或熵编码;另一类是有限失真编码。
基于混合编码的视频压缩标准中,变换、预测后的量化处理属于有限失真编码,消除的是信源空间和时间的冗余度。而对量化后的预测残差变换系数形成的语法元素,采用的熵编码消除的是码字之间的冗余度,属于无失真编码。
信息熵是指信号源(信源)的信息量。设有一个离散信源,,它产生消息的概率是已知的,记为 ,,( ),则信息量定义为:
(bit) (1)
信息量仅反映了一个符号的信息量的大小,而信源都是由若干个符号所组成,如二进制信源由0和1两个符号,因此,用平均信息量,称为“熵”(entropy)来表示由多个符号所组成信源所携带的信息量,定义为:
上式取以2为底的对数时,单位是比特:
(比特/符号) (2)
定长编码也称为等长编码,即为每个编码符号分配一个等长比特的码字。常用的二进制码如表1:
在HEVC中,描述子*f(n)*表示有一个固定n比特的预定义值。具体到某一具体语法元素forbiden_zero_bit,它是一个f(1)码字,即1比特长度,其值为0。这种定长编码主要用于NAL单元头,slice分割头以及SPS/PPS中。
变长编码为各个编码符号分配的比特数不一定相等,常见的变长编码有哈夫曼、香浓、指数哥伦布编码等。变长编码的优势是编码的平均长度比定长编码短。例如概率分布为2的负幂次方的序列符号,具体如下:
图 3 概率分布为2的负幂次方的序列符号
对图3采用哈夫曼如图:
按照公式计算其平均码长为:
若采用定长编码,则需要3位二进制等长码。
传统编码是通过符号映射实现的。映射包含符号(symbol)与码字(codeword)两个要素,如下面的例子
symbol | e | h | l | o |
codeword | 00 | 01 | 10 | 11 |
1. 首先我们需要根据概率设定各符号在[0,1)上的初始区间,其中区间的起点为表中前面的符号的累计概率 |
symbol | e | h | l | o |
sum of probability | 0 | 0.1 | 0.1+0.2 | 0.1+0.2+0.3 |
interval | [0,0.1) | [0.1,0.3) | [0.3,0.6) | [0.6,1 |
2. 接下来我们需要根据符号的概率分割[0.1,0.3)上的区间,得到的结果如下 |
symbol | e | h | l | o |
interval | [0.1,0.12) | [0.12,0.16) | [0.16,0.22) | [0.22,0.3) |
3. 按照这种方式继续进行区间映射,最终“hello”映射到的区间是[0.10888,0.1096) |
映射区间 | 区间大小 | |
初始值 | [0,1) | 1 |
编码完h后 | [0.1,0.3) | 0.2 |
编码完e后 | [0.1,0.12) | 0.02 |
编码完l后 | [0.106,0.112) | 0.006 |
编码完l后 | [0.1078,0.1096) | 0.0018 |
编码完o后 | [0.10888,0.1096) | 0.00072 |
4. 从区间[0.10888,0.1096)中任取一个代表性的小数,如“0.109”就是编码“hello”后的输出值* |
.当处理符号 a k a_k ak时,区间 R R R宽度根据 a k a_k ak出现概率 p ( a k ) p(a_k) p(ak)而变窄,符号序列越长,相应的子区间越窄,编码的位数越多。
算术解码就只是需要判断代表性的小数在哪个区间,相应地就知道输入的符号了
二进制算术编码的编码方法跟算术编码是一样的,但是输入只有两个符号:“0”,“1”,也就是说输入的是二进制串。
除了是对二进制串进行编码这个特征外,二进制算术编码跟普通的算术编码还有一些区别,总体上可以按照如下进行描述:
CABAC采用的是二进制算术编码,在编码过程中需要传入二进制串,输出的也是二进制串。
在h.264标准中,CABAC在语法结构中用ae表示,它只用于编码slice_data中的语法元素(包括slice_data内部的子模块的语法元素)
CABAC实现分为四个部分
初始化执行于slice开始之前,另外如果在编码过程中某个宏块是PCM宏块,那么在PCM宏块之后,编码下一个宏块之前也需要进行初始化。
初始化主要工作就是确定所有上下文的初始MPS以及初始状态pStateIdx。求解方法如下
preCtxState = Clip3( 1, 126, ( ( m ∗ Clip3( 0, 51, SliceQPY ) ) >> 4 ) + n )
if( preCtxState <= 63 ) {
pStateIdx = 63 − preCtxState
valMPS = 0 ( 9-5)
} else {
pStateIdx = preCtxState − 64
valMPS = 1
}
上面的计算依赖于SliceQPY,m,n三个变量,其中不同的上下文索引(contex Index)对应不同的m、n,具体的m、n的取值请参考标准9.3.1中的各个表格。上下文索引是基于语法元素以及二值化后的二进制串的索引binIdx,我们将在下一小节进行阐述。
在CABAC的初始化过程的结果会得到所有上下文索引对应的MPS与pStateIdx的初始值。如果确定了MPS为“0”,那么LPS为“1”,反之如果MPS为“1”,那么LPS为“0”。状态pStateIdx是什么呢?
状态pStateIdx是LPS出现的概率 p L P S p_{LPS} pLPS的索引。在CABAC中规定了LPS的概率取值范围是 p L P S ∈ [ 0.01875 , 0.5 ] p_{LPS}\in [0.01875,0.5] pLPS∈[0.01875,0.5],由于LPS是小概率符号,因此它的概率肯定是小于0.5的,如果某个小概率符号在状态转换的过程中超出了0.5,此时我们就需要把MPS与LPS进行交换。
CABAC的状态机中共有64个状态,pStateIdx = 0,1,2,…,63,分别代表64个不同的概率,除了pStateIdx = 63外,其他的63个状态都满足上述状态转换规则,其中
{ p 0 L P S = 0.5 p 62 L P S = 0.01875 \left\{\begin{matrix} {p^{0}}_{LPS}=0.5 \qquad\\ {p^{62}}_{LPS}=0.01875 \end{matrix}\right. {p0LPS=0.5p62LPS=0.01875
结合4.1.2状态机的转换规则,我们可以得到状态转换参数
算术编码中最重要的要素就是符号的概率,CABAC是自适应的算术编码,也就是说符号的概率会随着符号的输入而改变,这种变化就是一种状态机,如果输入的是LPS的话,状态(概率)会怎样变化,如果输入的是MPS的话,状态(概率)又会怎么变化。CABAC的状态机转换的规则由HOWARD与VITTER的"exponential aging"模型借鉴而来,转换规则如下
p ( t + 1 ) L P S = { α ⋅ p t L P S i f a n M P S o c c u r s α ⋅ p t L P S + ( 1 − α ) i f a n L P S o c c u r s {p^{(t+1)}}_{LPS}= \left\{\begin{matrix} \alpha \cdot {p^{t}}_{LPS}\qquad\qquad & if\ an\ MPS\ occurs\\ \alpha \cdot {p^{t}}_{LPS} + (1-\alpha) &if\ an\ LPS\ occurs \end{matrix}\right. p(t+1)LPS={α⋅ptLPSα⋅ptLPS+(1−α)if an MPS occursif an LPS occurs
CABAC状态机的状态转换如下图(黑色实线代表输入的是MPS,红色虚线代表输入的是LPS),具体的pStateIdx变换请参考标准的表9-45
CABAC编码的是slice data中的语法元素,在进行算术编码前,需要把这些语法元素按照一定的方法转换成适合进行二进制算术编码的二进制串,这个转换的过程被称为二值化(binarization)。
二值化的方案共有7种
###4.2.1 一元码(Unary)
对于一个非二进制的无符号整数值符号 x ⩾ 0 x \geqslant 0 x⩾0,在CABAC中的一元码码字由 x x x个“1”位外加一个结尾的“0”组成,见下表。例如,输入的语法元素值为3,其二值化结果为110。解码器靠搜索一个"0"来判断何时语法元素结束。
一元码的变体,用在已知语法元素的最大值cMax的情况。对于 0 ⩽ x < c M a x 0\leqslant x < cMax 0⩽x<cMax的范围内的取值,使用一元码进行二值化。对于 x = c M a x x = cMax x=cMax,其二值化的二进制串全部由“1”组成,长度为cMax。对于 x > c M a x x>cMax x>cMax,则取 x = c M a z x=cMaz x=cMaz进行编码。例如,当cMax=5时,语法元素值为4的二进制串为11110,语法元素值为5的二进制串为11111。
在已知门限值cMax、莱斯参数R和语法元素值V的情况下,即可获得截断莱斯二元码串。截断莱斯码由前缀和后缀串接而成,前缀值P的计算方法为:
一、指数哥伦布编码映射关系
指数哥伦布码是一种压缩编码算法。指数哥伦布编码有四种映射关系,从V到code_num,其中,code_num是码字的编号,V是有符号数或无符号数。
ue:无符号整数指数哥伦布编码的语法元素,这种映射关系应用在宏快类型、参考帧索引等。映射关系为:code_num=V;
se:有符号整数指数哥伦布编码的语法元素,左位在先,这种映射关系应用在运动矢量插值、量化参数插值等。
映射关系为:
code_num 0 没有前缀码和INFO,code_num1 和 2 有1 bitINFO,code_num 3到6有两 bit INFO。每个哥伦布码字的长度为2M+1,并且基于code_num,每个codeword可以被重构,通过下面的公式:
三、K阶指数哥伦布码
K阶级指数哥伦布码如下表所示,表中给出了0阶、1阶、2阶和3阶的指数哥伦布码。
k阶哥伦布编码的逻辑结构:[(M-k) Zeros] [1] [M bits INFO]
四、k阶哥伦布码的编码过程
用来表示非负整数的k阶指数哥伦布码,可以通过下面的方式获得:
(1)将数字以二进制的形式写出,去掉最低的k个比特位,然后加1;
(2)计算剩下的比特数,将此数减1,即得前缀0的个数M-k;
五、k阶哥伦布码的解码过程
解析k阶指数哥伦布码时,首先从比特流的当前位置开始寻找第一个非零比特leadingZeroBits,然后根据公式计算出code_num的值:
具体哪个语法元素选择哪种二值化方案,请查看标准9.3.2中第一个表格。
在前面初始化的时候就出现了上下文这个概念,那么上下文所指的是什么?
以JM中的上下文结构体为例
//! struct for context management
struct bi_context_type
{
unsigned long count;
byte state; //uint16 state; // index into state-table CP
unsigned char MPS; // Least Probable Symbol 0/1 CP
};
上下文包含两个变量:MPS、pStateIdx(count只是用于计数)。在CABAC编码的过程中会碰到需要修改这两个值的情况(如上面的状态变换),这些修改都是以上下文为单位的。
语法元素在经过二值化后形成二进制串,二进制串中不同binIdx位置上的MPS(出现频率高的符号)可能会有所不同,并且概率也可能会不同,因此需要用一个概念来表示特定语法元素的二进制串中特定binIdx的MPS与pStateIdx,上下文就是这样的概念。
在h.264标准中,用一个上下文索引ctxIdx来代表上下文,ctxIdx的取值为0~1023,就是说h.264的上下文一共有1024个。
ctxIdx的计算方式分为两种
如果语法元素为coded_block_flag、significant_coeff_flag、last_significant_coeff_flag、coeff_abs_level_minus1,即残差系数部分的语法元素,则
ctxIdx = ctxIdxOffset + BlockCatOffset(ctxBlockCat) + ctxIdxInc(ctxBlockCat)
否则
ctxIdx = ctxIdxOffset + ctxIdxInc
其中的变量有
表9-25中,maxBinIdxCtx以及ctxIdxOffset列中的某些单元格含有前缀、后缀的字样,例如语法元素列的mb_type(P, SP slices only),其二进制序列类型中含有前缀以及后缀,具体参见9.3.2.5节的规定:在 P 和 SP 条带中的 I 宏块类型二进制码串,对应 mb_type 的值从 5 到 30,包含了前缀和后缀的串联,这里前缀包含了表 9-28 中规定的值为 1 的单个比特,后缀在 9-27 中规定,通过 mb_type 减 5 所得的值索引。
表9-30中当ctxIdxOffset取值为14时,binIdx取值为2时,ctxIdxInc可取2也可取3,具体参见9.3.3.1.2小节的第一个表格
表9-32中ctxIdxInc列的b1、b3对应表9-25中二进制序列的相关类型,具体参见9.3.2.5小节:
在残差系数部分,上下文是会根据不同的残差块类型做出不同选择的,BlockCatOffset就代表了不同的残差块类型的索引偏移,具体偏移值可以查看标准中的相关表格。
算术编码是基于区间划分的,普通的概率划分需要使用到多位乘法。CABAC的算术编码为了降低计算复杂度,并便于硬件实现,采取了如下一些方法:
在区间划分结束后,如果新的区间不在 [ 2 8 , 2 9 ) [2^8,2^9) [28,29)之内,即 R < 256 R<256 R<256,256是最大编码区间的 1 4 \frac{1}{4} 41,那么就需要进行重归一化操作(incremental output and interval expansion),输出一位或多位编码比特。重归一化过程如下图所示。
在CABAC编码过程中,在输入符号后,进行区间更新,接下来就是重归一化过程。下面就以 [ 0 , 2 10 ) [0,2^{10}) [0,210)表示区间 [ 0 , 1 ) [0,1) [0,1)为例,分析重归一化过程
在编码输出“0”或者“1”的阶段,用PutBit(B)表示
关于PutBit(B)的分析,参考上面重归一化的区间图,可以看到有三种情况
.另外,PutBit(B)不会编码第一个bit。原因是CABAC在初始化的时候,会以 [ 0 , 2 10 ) [0,2^{10}) [0,210)表示区间 [ 0 , 1 ) [0,1) [0,1),而在初始化区间时 R = 510 , L = 0 R=510,L=0 R=510,L=0,这意味着已经进行了第一次区间选择,区间为 [ 0 , 0.5 ) [0,0.5) [0,0.5),需要输出“0”。PutBit(B)在此阻止这个“0”的输出,这样就能得到正确的算术编码结果了。
有些语法元素在二值化后选择的可能不是上述的算术编码,而是旁路编码,具体情况请查看h.264标准9.3.2的第一个表格。旁路编码中,假设待编码的符号符合近似的均匀分布。下图给出了旁路模式下的编码过程。
旁路模式有几个特点:符号均匀分布,无需对 R R R进行量化与查表;每编码完一个符号后, R R R总是会小于 2 8 2^8 28,因此每次都需要对概率区间进行放大; 把 对 把对 把对L$的移位操作提到了前面,因此旁路编码的重归一化的区间可以看作由 [ 0 , 2 10 ) [0,2^{10}) [0,210)变成了 [ 0 , 2 11 ] [0,2^{11}] [0,211]。
在编码语法元素end_of_slice_flag(ctxIdx = 276)以及I_PCM mb_type时会调用EncodeTerminate这个编码过程。在EncodeTerminate中,采用的是pStateIdx = 63的状态,这个状态表示的是当前宏块是否为该slice的最后一个宏块的概率。在该状态下,对概率区间的划分跟概率区间量化值无关。在编码end_of_slice_flag以及I_PCM的mb_type时,概率区间固定为 R L P S = 2 R_{LPS} = 2 RLPS=2。如果当前宏块为slice的最后一个宏块(end_of_slice_flag = 1)或者当前编码为PCM宏块并且编码它的mb_type I_PCM时,结束CABAC编码,调用EncodeTerminate中的EncodeFlush。具体情况请参考标准中的9.3.4.5小节。
在编码完成slice的最后一个宏块后,将会调用字节填充过程。该过程会往NAL单元写入0个或者更多个字节(cabac_zero_word),目的是完成对NAL单元的封装(标准9.3.4.6)。这里有计算如下
如果k>0k>0,则需要将3字节长的0x000003添加到NAL单元kk次。这里的前两字节0x0000代表了cabac_zero_word,第三个字节0x03代表一个emulation_prevention_three_byte。
如果k⩽0k⩽0,则无需添加字节到NAL单元。
式子中的各个变量所代表的意思请查看标准