简单短序列的算术编码的MATLAB实现.docx
《简单短序列的算术编码的MATLAB实现.docx》由会员分享,可在线阅读,更多相关《简单短序列的算术编码的MATLAB实现.docx(7页珍藏版)》请在冰豆网上搜索。
![简单短序列的算术编码的MATLAB实现.docx](https://file1.bdocx.com/fileroot1/2023-3/29/cd8adc63-33da-457c-b539-adec6dcfddd6/cd8adc63-33da-457c-b539-adec6dcfddd61.gif)
简单短序列的算术编码的MATLAB实现
简单短序列的算术编码的MATLAB实现
正确实现的算术编码算法压缩能力Shannond定理描述的理论极限,是目前已知的压缩能力最强的无损压缩算法。
不过,由于算术编码算法的实现比较复杂,使用它作为默认压缩算法的应用程序还相当少。
在Unix平台上非常流行的bzip2(这个工具有命令行模式的Windows版本)使用的就是经过修改的算术编码算法。
目前为止还没有使用算术编码作为默认压缩算法的Windows应用程序,WinRAR和WinIMP能够支持bzip2的解压。
除此之外,在最新的JPEG标准中也用到了经过修改的算术编码压缩算法,但JPEG所用的那种算法受专利保护,因此使用时必须获得授权。
在之后的文章会很好的研究这个算法的实现:
现在给出一个简单的实例:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%算术编码过程实例ssbm.m
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
about={...
'本实例说明:
'
'字符串不能太长,程序不加判断,请注意溢出;'
'本实例只限定少数字符串abcde;'
'实例只是说明一下算术编码过程。
'};
disp(about);
str=input('请输入编码的字符串(本程序仅仅是一个实例,请仅输入abcde):
');
l=0;r=1;d=1;
%初始间隔
%程序限定字符为:
a、b、c、d、e
p=[0.20.30.10.150.25];
%字符的概率分布,sum(p)=1
n=length(str);
disp('abcde')
disp(num2str(p))
fori=1:
n
switchstr(i)
case'a'
m=1;
case'b'
m=2;
case'c'
m=3;
case'd'
m=4;
case'e'
m=5;
otherwise
error('请不要输入其它字符!
');
end
%判断字符
pl=0;pr=0;
forj=1:
m-1
pl=pl+p(j);
end
forj=1:
m
pr=pr+p(j);
end
%概率统计
l=l+d*pl;
r=l+d*(pr-pl);
strl=strcat('输入第',int2str(i),'符号的间隔左右边界:
');
disp(strl);
formatlong
disp(l);disp(r);
d=r-l;
end
运行过程如下:
'本实例说明:
'
'字符串不能太长,程序不加判断,请注意溢出;'
'本实例只限定少数字符串abcde;'
'实例只是说明一下算术编码过程。
'
请输入编码的字符串(本程序仅仅是一个实例,请仅输入abcde):
'aaabded'
abcde
0.20.30.10.150.25
输入第1符号的间隔左右边界:
0
0.200000000000000
输入第2符号的间隔左右边界:
0
0.040000000000000
输入第3符号的间隔左右边界:
0
0.008000000000000
输入第4符号的间隔左右边界:
0.001600000000000
0.004000000000000
输入第5符号的间隔左右边界:
0.003040000000000
0.003400000000000
输入第6符号的间隔左右边界:
0.003310000000000
0.003400000000000
输入第7符号的间隔左右边界:
0.003364000000000
0.003377500000000
%I=imread('001.bmp')
%imshow(I);
clear
I=[33113312;23313232;12333312];
%I=[111100101110];
[m,n]=size(I);
%第一列为灰度值,第二列为个数,第三列为概率百分数,应该也可以用imhist
table=tabulate(I(); %注意的是,tabulate要求I的元素必须为非负整数
%否则,以采用如下方法求解
%如[123;122],则统计出结果1是2个,2是3个,3是1个
%sortM=sort(M();
%uniqueM=([diff(sortM);1]>0);
%count=[sortM(uniqueM)diff(find([1;uniqueM]))]
%即color,p如下所示
color=table(:
1)';
p=table(:
3)'/100;
%计算上下限
csump=cumsum(table(:
3)');
allLow=[0,csump(1:
end-1)/100];
allHigh=csump/100;
numberlow=0;
numberhigh=1;
fork=1:
m
forkk=1:
n
data=I(k,kk);
low=allLow(data==color);
high=allHigh(data==color);
range=numberhigh-numberlow;
tmp=numberlow;
numberlow=tmp+range*low;
numberhigh=tmp+range*high;
end
end
%theresultrange
fprintf('算术编码范围下限为%16.15f\n\n',numberlow);
fprintf('算术编码范围上限为%16.15f\n\n',numberhigh);
%decoding
Mat=zeros(m,n);
fork=1:
m
forkk=1:
n
indx=numberlow indx=[indx1];
ind=diff(indx);
ind=logical(ind);
Mat(k,kk)=color(ind);
low=allLow(ind);
high=allHigh(ind);
range=high-low;
numberlow=numberlow-low;
numberlow=numberlow/range;
end
end
fprintf('原矩阵为:
\n')
disp(I);
fprintf('\n');
fprintf('解码矩阵:
\n');
disp(Mat);
附
算术编码与译码原理:
1、编码过程
算术编码方法是将被编码的一则消息或符号串(序列)表示成0和1之间的一个间隔(Interval),即对一串符号直接编码成[0,1]区间上的一个浮点小数。
符号序列越长,编码表示它的间隔越小,表示这一间隔所需的位数就越多。
信源中的符号序列仍然要根据某种模式生成概率的大小来减少间隔。
可能出现的符号概率要比不太可能出现的符号减少范围小,因此,只正加较少的比特位。
在传输任何符号串之前,0符号串的完整范围设为[0,1]。
当一个符号被处理时,这一范围就依据分配给这一符号的那一范围变窄。
算术编码的过程,实际上就是依据信源符号的发生概率对码区间分割的过程。
举例说明如下:
假设一则消息“static_tree”具有如下的概率分布:
字符 概率
---------------------------------------------------------------
_(space) 0.1
a 0.1
e 0.3
r 0.1
s 0.1
t 0.3
下面用算术编码方法给该消息编码。
一旦字符的概率已知,就沿着“概率线”为每一个单独的符号设定一个范围,哪一个被设定到哪一段范围并不重要,只要编码和解码都以同样方式进行就可以,这里所用的6个字符被分配的范围(range)如下:
字符 概率 范围
_(space) 0.1 0≤r<0.1
a 0.1 0.1≤r<0.2
e 0.3 0.2≤r<0.5
r 0.1 0.5≤r<0.6
s 0.1 0.6≤r<0.7
t 0.3 0.7≤r<1.0
----------------------------------------------------------------
对“state_tree”的算术编码过程为:
(1)初始化时,被分割的范围range=high-low=[0,1),下一个范围的低、高端分别由下式计算:
Low=low+range×rangelow
High=low+range×rangehigh
其中等号右边的low为上一个被编码字符的范围低;rangelow和rangehigh分别为被编码符号已给定的字符出现概率范围的low和high。
(2)对消息第一字符s编码:
s的rangelow=0.6, s的rangehigh=0.7因此,下一个区间的low和high为:
Low=low+range×rangelow=0+1×0.6=0.6
High=low+range×rangehigh=0+1×0.7=0.7
Range=high-low=0.7-0.6=0.1
S将区间[0,1)=>[0.6,0.7)
(3)对第二个字符t编码,使用的新生范围为[0.6,0.7),因为t的rangelow=0.7,rangehigh=1.0,因此下一个low,high分别为
Low=0.6+0.1×0.7=0.67
High=0.6+0.1×1.0=0.70
Range=0.7-0.67=0.03
t将[0.6,0.7)=>[0.67,0.70)
(4)对第三个字符a编码,在新生成的[0.67,0.70)中进行分割,因为a的rangelow=0.10,rangehigh=0.2,因此下一个low,high分别为
Low=0.67+0.03×0.1=0.673
High=0.67+0.03×0.2=0.676
Range=0.676-0.673=0.003
a将[0.67,0.70)=>[0.673,0.676)
(5)对第四个字符t编码,在新生成的[0.673,0.676)上进行分割。
因为t的rangelow=0.70,rangehigh=1.0,则下一个low,high分别为
Low=0.673+0.003×0.7=0.6751
High=0.673+0.003×1.0=0.676
Range=0.0009
t将[0.673,0.676)=>[0.6751,0.676)
同理得到下面各字符e,_,s,t,r,e,e编码所得到的范围分别为[0.67528,0.67555),[0.67528,0.675307),[0.6752989,0.675307),[0.67530295,0.67530376),[0.675303112,0.675303355),[0.6753031606,0.6753032335)
将编码后的区间范围综合如下:
上述编码过程可以用下面的区间分割过程表示:
2、解码过程
解码是编码的逆过程,了解了编码过程后,理解解码过程的操作就相对容易了。
通过编码,最后的下界值0.6753031606就是消息“state_tree”的唯一编码。
然后解码时,通过判断哪一个符号能拥有我们已编码的消息落在的空间来找出消息中的第一个符号。
由于0.6753031606落在[0.6,0.7)之间,因此可解得第一个符号是s。
在解出s后,由于我们知道s的范围的上界和下界,利用编码的逆作用,首先去掉s的下界值0.6,得0.0753031606,然后用s的范围range=0.1去除所得到的0.0753031606,即得到0.753031606,接着找出它所在的区间,就是t的原来范围[0.7,1.0)。
去掉t的下界值0.7,得0.053031606,然后用t的range=0.3除该数得出0.17677202,该值所属范围就是字符a……如此操作下去便得到消息的准确译码。
综述,可以得到解码公式为:
(Number-rangelow)/range=>number
其中,number为字符串的编码。