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

1. 前言

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

2. 指数哥伦布熵编码

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

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

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

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

无符号指数哥伦布码表

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

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

总结:对于代编码的code_num,根据下面公式计算M和Info, info是一个M位数据的二进制码,编码码字长度为(2M+1)位:

M=floor(log2(codec_num+1))
Info=code_num+1-2M
encode_num=[M个0]+[1]+[Info]

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

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

  1. 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
  2. 读取leading_zero_bits+1的二进制序列,获取无符号二进制数值D
  3. 解码得到code_num=D-1

总结:对于代编码的encode_num, 读入M位以“1”结尾的0; 根据读入的M,读入接下来的M位Info二进制码, 最后根据下式还原code_num:

code_num = Info-1+2M

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+2k转化为二进制序列
  2. 计算二进制序列比特位数num_bits, 令前置补0的比特数leading_zero_bits=num_bits-1-k,
  3. 在二进制序列前面增加leading_zero_bits个0, 得到encode_num

总结:对于代编码的code_num,根据下面公式计算M和Info, info是一个M位数据的二进制码,编码码字长度为(2M+1-k)位:

M=floor(log2(code_num+2K))
Info=code_num+2K-2M
encode_num=[(M-k)个0]+[1]+[Info]

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

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

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

  1. 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
  2. 读取leading_zero_bits+1+k的二进制序列,获取无符号二进制数值D
  3. 解码得到code_num=D-2k
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 + k)) + md_bits_r1(leading_zero_bits + k) - (1 << k);
}

总结:对于待编码的encode_num, 读入M位以“1”结尾的0; 根据读取的M,读入接下来的(M+k)位Info二进制码, 最后根据下式还原code_num:

code_num = Info+2(M+k)-2k

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)

有符号指数哥伦布编码语法元素值se(v)和codeNum表

有符号指数哥伦布熵解码步骤:
1. 先进行无符号指数哥伦布解码ue(v),得到code_num=k
2. 有符号指数哥伦布语法元素值se(v)=(-1)^(code_num+1) * ((code_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官方协议文档实现指数哥伦布熵编码&熵解码

posted @ 2024-07-20 09:42  尹佑壮  阅读(70)  评论(0编辑  收藏  举报