算术编码不是简单的将每个信源符号映射成一个码字,而是对整个输入序列分配一个码字,所以平均意义上可以为每个信源符号分配长度小于1的码字。
算术编码操作简单,下面以一个实例讲解算术编码的原理:
设信源有a,b,c,d四种符号,概率分别为0.2,0.2,0.4,0.2,输入序列"abccd"。
符号 | a | b | c | d |
---|---|---|---|---|
概率 | 0.2 | 0.2 | 0.4 | 0.2 |
初始区间 | [0,0.2) | [0.2,0.4) | [0.4,0.8) | [0.8,1) |
-
第一个被压缩的符号为“a”,其初始间隔为[0, 0.2);
-
第二个被压缩的符号为“b”,由于前面的符号“a”的取值区间被限制在[0, 0.2)范围内,所以“b”的取值范围应在前一符号间隔[0, 0.2)的[0.2, 0.4)子区间内, 所以“b”的实际编码区间在[0.04, 0.08)之间。
-
第三个被压缩的符号为“c”,其编码取值范围应在[0.04, 0.08)区间的[0.4, 0.8)的子区间内,所以“c”的实际编码区间在[0.056, 0.072)之间。
-
第四个被压缩的符号为“c”,其编码取值范围应在[0.056, 0.072)区间的[0.4, 0.8)的子区间内,所以“c”的实际编码区间在[0.0624, 0.0688)之间。
-
第五个被压缩的符号为“d”,其编码取值范围应在[0.0624, 0.0688)区间的[0.8, 1)的子区间内,所以“c”的实际编码区间在[0.06752, 0.0688)之间。
-
至此,数据序列“abccd”已被描述为一个实数区间[0.06752, 0.0688),或者说在此区间内的任一实数值都唯一对应该数据序列。这样,就可以用该区间内的任意实数表示整个符号序列,如0.0688就可以用来表示整个符号序列。
整个过程如下图所示:
解码过程如下:
译码判决 | 译码区间 | 译码符号 |
---|---|---|
0.0688在[0,0.2)内 | [0,0.2) | a |
0.0688在[0,0.2)的第4个1/10区间内 | [0.04,0.08) | b |
0.0688在[0.04,0.08)的第7个1/10区间内 | [0.056,0.072) | c |
0.0688在[0.056,0.072)的第7个1/10区间内 | [0.0624,0.0688) | c |
0.0688在[0.0624,0.0688)的第10个1/10区间内 | [0.06752,0.0688) | d |
上面的例子假定编码器和解码器都知道消息的长度,所以解码过程不会无限进行下去。
算术编码存在以下几个问题:
-
实际的计算机精度不可能无限长,所以会出现溢出。这个问题可以通过比例缩放实现。
-
编码产生的码字是[0,1)间的一个实数,解码端必须收到实数的所有位后才能进行解码。
-
算术编码对错误敏感,如果有一位错误则整个消息都错误。
感兴趣的请关注微信公众号Video Coding