熵编码(二)-指数哥伦布熵编码

116 阅读4分钟

熵编码系列目录
熵编码(一)-熵编码概述
熵编码(二)-指数哥伦布熵编码

1. 前言

通过熵编码系列第一篇文章熵编码(一)-熵编码概述,我们了解了熵编码的概念和分类。熵编码包含了香农-范诺编码、霍夫曼编码、指数哥伦布熵编码,CAVLC、CABAC等。 本篇文章中主要介绍指数哥伦布熵编码,主要应用于SPS&PPS的句法元素解析。

2. 指数哥伦布熵编码

指数哥伦布熵编码是变长编码, 一个数值可以随着数值的不同而有不同的比特数,包含:无符号指数哥伦布熵编码,有符号指数哥伦布熵编码,映射指数哥伦布熵编码,截断指数哥伦布熵编码。 指数哥伦布熵编码是一种比较简单的编码方法,可以拓展至K阶,即K阶指数哥伦布熵编码。在H264白皮书中使用的是0阶指数哥伦布熵编码,即K=0。因此下述文章中除非特殊标明是K阶,描述的都是0阶指数哥伦布熵编码。理解了0阶指数哥伦布熵编码,自然也就理解了K阶指数哥伦布熵编码

3. 无符号指数哥伦布熵编码

无符号指数哥伦布熵编码和熵解码流程如下图 无符号指数哥伦布熵编码&熵解码步骤图

3.1. 无符号指数哥伦布熵编码

无符号指数哥伦布码表

无符号指数哥伦布熵编码步骤:

  1. 将code_num+1,转化为二进制序列
  2. 计算二进制序列比特位数M, 令前置补0的比特数leading_zero_bits=M-1,
  3. 在二进制序列前面增加leading_zero_bits个0, 得到encode_num

3.2. 无符号指数哥伦布熵解码 ue(v)

无符号指数哥伦布熵解码步骤:

  1. 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
  2. 读取leading_zero_bits+1的二进制序列,获取无符号二进制数值D
  3. 解码得到code_num=D-1
uint32_t md_bits_rue(BitContext *bc) {
	int leading_zero_bits = 0;
	/* md_bits_r1: read one bit */
	while(!md_bits_r1(1)) {
	    leading_zero_bits++;
	}
	return (1 << leading_zero_bits) + md_bits_r1(leading_zero_bits) - 1;
}

上述代码写法是因为md_bits_r1(1)不为0时,此时指针已经指向非0比特位的下一个比特位.

3.3. K阶无符号指数哥伦布熵编码

K阶无符号指数哥伦布熵编码&熵解码步骤图

由上述的0阶流程,可以拓展至k阶,只需要把第一步稍微变动一点 K阶无符号指数哥伦布熵解码步骤:

  1. code_num+2^k转化为二进制序列(0阶)
  2. 计算二进制序列比特位数M, 令前置补0的比特数leading_zero_bits=M-1,
  3. 在二进制序列前面增加leading_zero_bits个0, 得到encode_num

可以发现0阶指数哥伦布熵编码就是K阶指数哥伦布熵编码K=0的特殊形式

3.4. K阶无符号指数哥伦布熵解码 ue_k(v)

K阶无符号指数哥伦布熵解码步骤:

  1. 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
  2. 读取leading_zero_bits+1的二进制序列,获取无符号二进制数值D
  3. 解码得到code_num=D-2^k
uint32_t md_bits_rue_k(BitContext *bc, k) {
	int leading_zero_bits = 0;
	/* md_bits_r1: read one bit */
	while(!md_bits_r1(1)) {
	    leading_zero_bits++;
	}
	return (1 << leading_zero_bits) + md_bits_r1(leading_zero_bits) - (1 << k);
}

4. 有符号指数哥伦布熵编码

4.1. 有符号指数哥伦布熵编码

有符号指数哥伦布熵编码步骤:

  1. 将code_num取绝对值并转化为二进制序列
  2. 在二进制序列后增加一位符号位:0表示整数,1表示负数
  3. 计算二进制序列比特M, 令前置补0的比特数leading_zero_bits=M-1
  4. 在二进制序列前面增加leading_zero_bits个0,得到encode_num

4.2. 有符号指数哥伦布熵解码 se(v)

image.png

有符号指数哥伦布熵解码步骤:

  1. 先进行无符号指数哥伦布解码ue(v),得到code_num=k
  2. 有符号指数哥伦布语法元素值se(v)=(-1)^(code_num) * ((codec_num+1) >> 1)
int md_bits_rse(BitContext *bc) {
	uint32_t code_num = md_bits_rue(bc);
	return pow(-1, code_num + 1) * ((code_num + 1) >> 1);
}

5. 参考文档

本篇文章参考T-REC-H.264官方协议文档实现指数哥伦布熵编码&熵解码