视频的压缩编码的最后一步是熵编码,而熵编码的方法有很多。其中有一类编码叫做算术编码,算术编码的思路实在是精美绝伦,因此写此文记录一下。
与很多其他编码思想一样,如哈夫曼编码,算术编码的本质思想也是对于高频的字符进行短编码,即是高频的字符占用的字节少。
比如字符串AAABBCAAACAABAAACB,其中A出现的频率最高,那么希望编码的时候表达A的字符占用的字节最少。这也是类似哈夫曼编码的核心做法。对于算术编码来说,本质思想也是如此,但是算法编码使用的编码方法和过程,却是非常的独特。
假如我们要压缩这样一段字符:AABABCABAB
如果不进行压缩,则这个字符串所占空间大小为20个bit;其中A字符出现频率是50%,B字符出现频率是40%,C字符出现频率是10%;那么算术编码会对0-1的区间对A,B,C进行一个范围划分:
A:[0,0.5]
B:[0.5,0.9]
C:[0.9,1.0]
接下来我们需要对字符串的每一个字符依次根据它所在的区间再对A,B,C进行一个范围划分。过程如下:
(1)、字符串的第一个字符是A,则我们需要对A的区间[0,0.5]来进行一个区间划分:
A:[0,0.25]
B:[0.25,0.45]
C:[0.45,0.5]
(2)、字符串的第二个字符还是A,则我们需要对新的A区间[0,0.25]来进行一个区间划分:
A:[0,0.125]
B:[0.125,0.225]
C:[0.225,0.25]
(3)、第三个字符是B,那我们这次就需要对新的B区间[0.125,0.225]来进行一个区间划分:
A:[0.125,0.175]
B:[0.175,0.215]
C:[0.215,0.225]
(4)、重复上述的步骤,一直到最后一个字符,如下:
当前字符 | 当前目标区间 |
A | [0, 0.5] |
A | [0,0.25] |
B | [0.125,0.225] |
A | [0.125,0.175] |
B | [0.15,0.17] |
C | [0.168,0.17] |
A | [0.168,0.169] |
B | [0.1685,0.1689] |
A | [0.1685,0.1687] |
B | [0.1686,0.16868] |
(5)、完成上述操作后,最终区间为[0.1686,0.16868],我们在这个区间内任意选一个小数,就可以作为最终的编码小数。我们还需要对小数转成二进制,才是最终存储的格式。因为我们要尽可能地最短压缩,所以我们希望从这个区间里选择一个二进制表示出来最短的小数。这里我们选定为:0.16864013671875,二进制为:0.00101011001011,去掉整数位 0 以及小数点后,最终的二进制编码为 00101011001011,bit 长度为 14 bit,比不压缩的20bit,减少了6bit。
解码过程与编码过程是相逆的。大致如下:
(1)、获取到存储信息00101011001011,加上小数点还原后为.00101011001011,对应的十进制编码小数是 0.16864013671875。
(2)、我们先从初始区间中定位第一个字符:
A:[0,0.5]
B:[0.5,0.9]
C:[0.9,1.0]
因为小数0.16864013671875落在A的区间中,所以第一个字符可以确定为A
(3)、接下来定位第二个字符:
第二个区间是:
A:[0,0.25]
B:[0.25,0.45]
C:[0.45,0.5]
小数0.16864013671875仍然是落在A的区间中,因此第二个字符也是A
(4)、接下来第三个字符:
第三个区间是:
A:[0,0.125]
B:[0.125,0.225]
C:[0.225,0.25]
看到0.16864013671875是落在B的区间中,因此第三个字符是B
(5)、以此类推,最终就可以根据小数0.16864013671875将整个字符串解析出来
这就是算术编码的解码过程。
算法编码为什么能够压缩数据呢?
算术编码的压缩本质,就是在保留字符排列顺序的同时,对于更高频出现的字符,也就是概率更大的字符,赋予更大的小数区间。
算术编码的目的,是要在最终的目标区间内,找一个二进制最短的小数作为最终编码。
那么怎么去找到这样一个目标区间呢?最终目标区间的范围更大,可容纳的小数精度就越低,意味者我们最终的二进制编码更短。比如在低精度的 [0.1, 0.2) 和 高精度的 [0.1111111111, 0.1111111112) 之间各找一个最短编码的二进制进行比较,肯定是 [0.1, 0.2) 中找到的的最短二进制编码更短。
所以算术编码的实现途径就是:尽量使最终目标区间的范围更大。由于高频字符出现次数多,区间较大,而低频字符出现次数少,区间小。所以在遍历完所有字符之后,我们最终得到目标区间就更大,也就是小数精度更低。
通过上述讲解,我们就大体了解了什么是算术编码,以及算术编解码的过程。
而算法编码的原理可以总结为:
1、为了使最终二进制编码更短,就需要使得最终目标区间的范围更大。
2、为了使最终目标区间的范围更大,就需要赋予高频字符更大的区间,低频字符更小的区间。
算法编码的步骤过程,以及原理,我们都能去了解和掌握。但是令人想不到的是,这算法编码的这种方式,是如何想到的呢?这点不得不佩服算法编码的发明者。