1、算术编码简介
算术编码是一种无损数据压缩方法,也是一种熵编码的方法。和其它熵编码方法不同的地方在于,其他的熵编码方法通常是把输入的消息分割为符号,然后对每个符号进行编码。而算术编码是直接把整个输入的消息编码为一个数,一个满足(0.0 ≤ n < 1.0)的小数n。
算术编码用到两个基本的参数:符号的概率和它的编码间隔。信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0到1之间。
2、算术编码的算法思想如下:
(1)对一组信源符号按照符号的概率从大到小排序,将[0,1)设为当前分析区间。按信源符号的概率序列在当前分析区间划分比例间隔。(2)检索“输入消息序列”,锁定当前消息符号(初次检索的话就是第一个消息符号)。找到当前符号在当前分析区间的比例间隔,将此间隔作为新的当前分析区间。并把当前分析区间的起点(即左端点)指示的数“补加”到编码输出数里。当前消息符号指针后移。
(3)仍然按照信源符号的概率序列在当前分析区间划分比例间隔。然后重复第二步。直到“输入消息序列”检索完毕为止。
(4)最后的编码输出数就是编码好的数据。
3、在算术编码中需要注意几个问题:
(1)由于实际计算机的精度不可能无限长,运算中出现溢出是一个明显的问题,但多数及其都有16位,32位或者64位的精度,因此这个问题可以使用比例缩放方法解决。
(2)算术编码器对整个消息只产生一个码字,这个码字是在间隔[0,1)中的一个实数,因此译码器在接受到表示这个实数的所有位之前不能进行译码。
(3)算术编码是一种对错误很敏感的编码方法,如果有一位发生错误就会导致整个消息译错。
算术编码可以是静态的或者是自适应的。在静态算术编码中,信源符号的概率是固定的。在自适应算术编码中,信源符号的概率根据编码时符号出现的频率动态地进行修改,在编码期间估算信源符号概率的过程叫做建模。需要开发动态算术编码的原因是因为事前知道精确的信源概率是很难的,而且不切实际。当压缩消息时,不能期待一个算术编码器获得最大的效率,所能做的最有效的方法是在编码过程中估算概率。因此动态建模就成为确定编码器压缩效率的关键。
4、示例
算术编码器的编码解码过程可用例子演示和解释。
例1:假设信源符号为{A, B, C, D},这些符号的概率分别为{ 0.1, 0.4, 0.2,0.3 },根据这些概率可把间隔[0, 1]分成4个子间隔:[0, 0.1], [0.1, 0.5], [0.5, 0.7], [0.7, 1],其中[x,y]表示半开放间隔,即包含x不包含y。上面的信息可综合在表03-04-1中。
表03-04-1 信源符号,概率和初始编码间隔
符号 | A | B | C | D |
---|---|---|---|---|
概率 | 0.1 | 0.4 | 0.2 | 0.3 |
初始编码间隔 | [0, 0.1) | [0.1, 0.5) | [0.5, 0.7) | [0.7, 1] |
如果二进制消息序列的输入为:C A D A C D B。编码时首先输入的符号是C,找到它的编码范围是[0.5,0.7]。由于消息中第二个符号A的编码范围是[0, 0.1],因此它的间隔就取[0.5, 0.7]的第一个十分之一作为新间隔[0.5,0.52]。依此类推,编码第3个符号D时取新间隔为[0.514, 0.52],编码第4个符号A时,取新间隔为[0.514, 0.5146],…。消息的编码输出可以是最后一个间隔中的任意数。整个编码过程如图03-04-1所示。
图03-04-1 算术编码过程举例
这个例子的编码和译码的全过程分别表示在表03-04-2和表03-04-3中。
表03-04-2 编码过程
步骤 | 输入符号 | 编码间隔 | 编码判决 |
---|---|---|---|
1 | C | [0.5, 0.7] | 符号的间隔范围[0.5, 0.7] |
2 | A | [0.5, 0.52] | [0.5, 0.7]间隔的第一个1/10 |
3 | D | [0.514, 0.52] | [0.5, 0.52]间隔的最后一个1/10 |
4 | A | [0.514, 0.5146] | [0.514, 0.52]间隔的第一个1/10 |
5 | C | [0.5143, 0.51442] | [0.514, 0.5146]间隔的第五个1/10开始,二个1/10 |
6 | D | [0.514384, 0.51442] | [0.5143, 0.51442]间隔的最后3个1/10 |
7 | B | [0.5143836, 0.514402] | [0.514384,0.51442]间隔的4个1/10,从第1个1/10开始 |
8 | 从[0.5143876, 0.514402]中选择一个数作为输出:0.5143876 |
表03-04-3 译码过程
步骤 | 间隔 | 译码符号 | 译码判决 |
---|---|---|---|
1 | [0.5, 0.7] | C | 0.51439在间隔 [0.5, 0.7) |
2 | [0.5, 0.52] | A | 0.51439在间隔 [0.5, 0.7)的第1个1/10 |
3 | [0.514, 0.52] | D | 0.51439在间隔[0.5, 0.52)的第7个1/10 |
4 | [0.514, 0.5146] | A | 0.51439在间隔[0.514, 0.52]的第1个1/10 |
5 | [0.5143,0.51442] | C | 0.51439在间隔[0.514, 0.5146]的第5个1/10 |
6 | [0.514384,0.51442] | D | 0.51439在间隔[0.5143, 0.51442]的第7个1/10 |
7 | [0.51439,0.5143948] | B | 0.51439在间隔[0.51439,0.5143948]的第1个1/10 |
8 | 译码的消息:C A D A C D B |
在上面的例子中,我们假定编码器和译码器都知道消息的长度,因此译码器的译码过程不会无限制地运行下去。实际上在译码器中需要添加一个专门的终止符,当译码器看到终止符时就停止译码。
5、MATLAB代码
clear all;
clc;
in='00000011111010101';
pr=[0.5 0.5]; %各字符出现的概率
temp=[0.0 0.5 1.0];
orignal=temp;
n=length(in);
%编码
for i=1:n
width=temp(3)-temp(1);
w=temp(1);
switch in(i)
case '0'
m=1;
case '1'
m=2;
end
temp(1)=w+orignal(m)*width;
temp(3)=w+orignal(m+1)*width;
left=temp(1);
right=temp(3);
fprintf('left=%.6f',left);
fprintf(' ');
fprintf('right=%.6f\n',right);
end
encode=(temp(1)+temp(3))/2
%解码
decode=['0'];
for i=1:n
fprintf('tmp=%.6f\n',encode);
if(encode>=orignal(1)& encode<orignal(2))
decode(i)='0';
t=1;
elseif(encode>=orignal(2)& encode<orignal(3))
decode(i)='1';
t=2;
end
encode=(encode-orignal(t));
encode=encode/pr(t);
end
decode
参考:
算术编码的原理和MATLAB实现:http://blog.sina.com.cn/s/blog_4a8f0cbc01000b6e.html
行程编码和算术编码:https://www.cnblogs.com/pulas/archive/2012/02/22/2363314.html
图像压缩——算术编码:http://blog.csdn.net/u010798503/article/details/53291743