1. 引言
经过上一章的学习,我们学会看描述子。这时候我们就会发现,在语法中,除了简单的 u(n),i(n) 这种读取固定长度的二进制解析方法之外,用的更多的还有ue(v),se(v)这些。

只有学会了这些,我们可以一路顺利的解析完 SPS,PPS,SEI,Slice header,一直到slice data的位置也才会被一个ae(v)挡住。

ae(v)是CAVLC的范畴,我们暂时不管。
ue(v)和se(v)其实就是今天的重点,H264中熵编码的指数哥伦布编码。
所以今天这里,我们就把ue(v)和se(v)解决,这样的话,在下一篇,我们就可以去真正的解析一些完整的NAL,看看他们的参数究竟是什么含义了。
今天的文章,主要集中在这么几个问题上:
- 什么是熵编码?
- 什么是指数哥伦布编码?
- 指数哥伦布编码的编码步骤?
- 指数哥伦布编码的解码步骤?
2. 什么是熵编码?
在H264中,使用了预测编码,变换编码,熵编码等多种编码手段。
其中,预测编码,变换编码等都作用于从YUV图转换成 白皮书中参数的过程,熵编码主要用于从这些参数转换成高效率的码流。

简单聊一下熵编码,
熵是表示系统混乱关系的一种度量,熵越高就表示系统越混乱。
在信息中,熵越高则表示信息中的规律越少,信息越杂乱。
而熵编码的作用就是让我们能用尽量短的码字去逼近他的熵极限。
那什么是熵极限呢?打个很简单的比方。
面对一串未知的字符串,我们需要尽可能短的传输它,但是是未知的,规律不明确,我们怎么传输呢?那很多人会选择使用ascii码来传输,每个字符一个字节。
那如果对于一串已知规律的字符串,比如大家知道字符串中只有0和1两个字符,同样需要尽可能短的传输它,那大家会怎么传输呢,最简单的肯定是用bit0代表0,bit1代表1,对吧,这就是用很短的码字来实现了编码。
从一个字节变成了一个比特,实现了大量的压缩,而且是无损的。
熵编码就是如此,根据已知的规律,将那些高概率的符号用更短的码字来表示,讲那些低概率的符号用长码字来表示。实现信息的压缩。
区别就在于,有些编码方法中,码字和码表是固定的,有些则不是,即同样的码字,在不同的上下文里,代表的符号可能不一样。
3. 什么是指数哥伦布编码
Exp-Golomb编码是一种变长编码, 并且无需事先建立和存储码表。
其实通俗来说,就是把1,2,3,4,5,6……这些数字映射成1串固定的比特流,只不过数字不同,阶数不同,对应的比特的长度和数值也不同。当阶数确定时,数字对应的码表就是确定的。不同的阶数对应不同的码表。
只不过,指数哥伦布的变换关系是有规律的,无需事先建立和存储码表。按照规律进行实时的解码和编码即可,这也是它的一个优势。
在h264中使用了0阶的指数哥伦布编码,0~3阶的典型码表如下:

下面都以0阶指数哥伦布来介绍。
3. 指数哥伦布编码 的 编码步骤
指数哥伦布编码的码字可以分为3部分,“前缀 + 1 + 后缀”。
前缀码就是 N个0。
0阶指数哥伦布的编码步骤是:
(1)将code_num 加上1
(2)将code_num + 1写为二进制的形式
(3)计算二进制的比特个数 M,并在前面加上M-1个0,得到编码码字。
以前面码表中的3举例。
3的码字是 00100’b。
(1) 3+1 = 4
(2) 4的二进制是 100’b
(3) 100’b 是3个bit,所以在前面加上(3-1 = 2)个0,得到 00100。
4. 指数哥伦布编码 的 解码
4.1 原理
因为指数哥伦布是变长的,每个码字的长度不同,我们如何对码流进行切分,来进行解码呢?
这就涉及到指定码表时的一个规则,“短码字不能是长码字的前缀码”。
比如0节的指数哥伦布,
0的码字是比特1’b。其他的码字则没有一个是以1开头的。
1的码字是010’b,其他的码字也没有一个是以010开头的。
就是二叉树中,找取一些叶子节点来做码字,这样一旦匹配上,则不会出现截断错误的问题。
这样的弊端也很明显,就是有些短码字完全被浪费了,无法被使用。

可以看出来,在0阶的指数哥伦布中,第一层只有1个有效码字,第二层2个,第三层4个,第4层8个……
4.2 步骤
上面已知了编码方式,解码方式就是倒叙跑一遍。
0阶指数哥伦布的解码步骤是:
- 数出前缀的0的个数M (leadingZeroBits)。
- 取出后面挨着的M+1 位bit
- 将取出的数-1,得到最终值。
还是以3为例来解码,3的码字是 00100’b.
- 数出前缀的0的个数 M=2.
- 取出后面挨着的 3位bit: 100’b
- 将取出的数减去1,即 (100’b = 4)- 1 = 3。
这样就完成了解码。
4.3 UE(n),SE(n),TE(n),ME(n)
上面介绍了指数哥伦布的解码,但是实际上,h264使用了4种指数哥伦布,它们是怎么使用上面介绍的方法呢?
4.3.1 UE(n) 无符号指数哥伦布
UE通常被称为无符号指数哥伦布编码。
它只能解出>=0 的无符号实数。
解码步骤如上文的4.2所示
4.3.2 SE(n) 有符号指数哥伦布
SE被称为有符号指数哥伦布编码。
我们可以先调用UE把比特读成一个无符号数,再映射成一个有符号数。
映射规则如下:
| UE | SE |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 2 | -1 |
| 3 | 2 |
| 4 | -2 |
| 5 | 3 |
| 6 | -3 |
| 7 | 4 |
| … | … |
4.3.3 ME(n) 映射指数哥伦布
ME就是先用UE读出codenum,然后按照白皮书中对应的映射表,映射成另外一个数即可。
用的不多,在读取CBP的时候用,主要有些固定的大数字,映射成小的数,编码进来。
解码时候再映射回去。
具体的映射表可以在白皮书212页检索。
根据表格我们可以看到,映射关系主要受3个参数影响
- CodeNum: ReadUe解出来的码字
- Mb_type: 根据Mb的类型确定是intra还是inter,intra16x16不需要,从类型就能直接获取
- ChromaArrayType: 从SPS中可以获取这个值

4.3.4 TE(n) 截断指数哥伦布
TE又叫截断指数哥伦布,因为它的编解码中间是截断的。
在宏块预测中使用了这种编码。
编码
codenum = 0, 码字 = 1‘b
codenum = 1,码字 = 0’b
codenum > 1, 码字= UE,和UE的编码方式一致
解码
解码一样是编码的逆序。
但是因为是多段的,所以要注意一点。
首先需要判断的是语法元素的取值范围,假定为[0, x], x≥1。根据x的取值情况,语法元素根据下面不同情况进行解析:
若x>1,解析方法同ue(v)相同;
若x=1,语法元素值等同于下一位bit值的取反。
注意,是根据语法元素的取值范围来定的,而不是根据语法元素的真实值来定的,因为在解码过程中,真实值无从得知,只能根据解码元素的范围来选定解码区间。

585

被折叠的 条评论
为什么被折叠?



