熵编码(二)-指数哥伦布熵编码
1. 前言
通过熵编码系列第一篇文章熵编码(一)-熵编码概述,我们了解了熵编码的概念和分类。熵编码包含了香农-范诺编码、霍夫曼编码、指数哥伦布熵编码,CAVLC、CABAC等。
本篇文章中主要介绍指数哥伦布熵编码,主要应用于SPS&PPS的句法元素解析。
2. 指数哥伦布熵编码
指数哥伦布熵编码是变长编码, 一个数值可以随着数值的不同而有不同的比特数,包含:无符号指数哥伦布熵编码,有符号指数哥伦布熵编码,映射指数哥伦布熵编码,截断指数哥伦布熵编码。
指数哥伦布熵编码是一种比较简单的编码方法,可以拓展至K阶,即K阶指数哥伦布熵编码。在H264白皮书中使用的是0阶指数哥伦布熵编码,即K=0。因此下述文章中除非特殊标明是K阶,描述的都是0阶指数哥伦布熵编码。理解了0阶指数哥伦布熵编码,自然也就理解了K阶指数哥伦布熵编码
3. 无符号指数哥伦布熵编码
无符号指数哥伦布熵编码和熵解码流程如下图
3.1. 无符号指数哥伦布熵编码
无符号指数哥伦布熵编码步骤:
- 将code_num+1,转化为二进制序列
- 计算二进制序列比特位数num_bits, 令前置补0的比特数leading_zero_bits=num_bits-1,
- 在二进制序列前面增加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)
无符号指数哥伦布熵解码步骤:
- 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
- 读取leading_zero_bits+1的二进制序列,获取无符号二进制数值D
- 解码得到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阶无符号指数哥伦布熵编码
由上述的0阶流程,可以拓展至k阶,只需要把第一步稍微变动一点
K阶无符号指数哥伦布熵解码步骤:
- 将code_num+2k转化为二进制序列
- 计算二进制序列比特位数num_bits, 令前置补0的比特数leading_zero_bits=num_bits-1-k,
- 在二进制序列前面增加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阶无符号指数哥伦布熵解码步骤:
- 按位读取encode_num,直到非0的比特位为止,得到前置补0的数量leading_zero_bits
- 读取leading_zero_bits+1+k的二进制序列,获取无符号二进制数值D
- 解码得到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. 有符号指数哥伦布熵编码
有符号指数哥伦布熵编码步骤:
- 将code_num取绝对值并转化为二进制序列
- 在二进制序列后增加一位符号位:0表示正数,1表示负数
- 计算二进制序列比特M, 令前置补0的比特数leading_zero_bits=M-1
- 在二进制序列前面增加leading_zero_bits个0,得到encode_num
4.2. 有符号指数哥伦布熵解码 se(v)
有符号指数哥伦布熵解码步骤:
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官方协议文档实现指数哥伦布熵编码&熵解码