数据压缩课程中的代码(有点多先放一点以后有空再说)
熵的计算
熵的定义在这里不加赘述直接上代码
% 计算信源的熵
% 输入:p 概率集合
% 输出:H 其对应的熵
function H=entropy(p)
if nargin==0
disp('输入的概率集合不能为空,请重新输入!!')
return
end
p=p(:); % 将p变成一维向量
% 检测p的合法性
for i=1:length(p)
if p(i)<0 | p(i)>1
disp('概率必须在0到1之间');
return
end
end
% 实际中概率之和不可能精确等于1
tol=1e-4; % 设定允许的精度
if abs(sum(p)-1)>tol
disp('概率之和必须为“1”且在允许的精度范围内');
return
end
% 计算熵
disp('信源的熵为:');
p(find(p==0))=[]; % 删除p中的0值,否则会出现NaN
H=0;
for i=1:length(p)
H=H-p(i)*log2(p(i));
end
算术编码和解码
简单介绍
根据信源可能发现的不同符号序列的概率,把[0,1]区间划分为互不重叠的子区间,子区间的宽度恰好是各符号序列的概率。这样信源发出的不同符号序列将与各子区间一一对应,因此每个子区间内的任意一个实数都可以用来表示对应的符号序列,这个数就是该符号序列所对应的码字。显然,一串符号序列发生的概率越大,对应的子区间就越宽,要表达它所用的比特数就减少,因而相应的码字就越短。
初始状态下 , 。在已知所有信源符号的概率的情况下对数据序列中的每一个符号按顺序进行以下操作:
其中 startB表示前一个的开始值, 表示本字符开始值, 表示本字符结束值, 表示本字符概率区间左端, 表示概率区间右端。
解码过程:开始的时候拿编码比较概率区间,得到第一个解析的信源码,接下来的编码采用如下处理:
每次处理完后比较概率区间,知道解析出所有的信源编码。
算术编码实现
clc;
clear;
%设定固定的概率
c = [8000,1600,3000,4400,12000,2500,1700,6400,8000,400,800,4000,3000,8000,8000,1700,500,6200,8000,9000,3400,1200,2000,400,2000,200];
c = c/sum(c);
str = input('请输入待编码的字符串 ','s');
start = 0;
L = 1;
k = 0;
%得出概率区间
matr = zeros(length(c),2);
for i = 1:length(c)
matr(i,1) = k;
k = k+c(i);
matr(i,2) = k;
end
%进行迭代计算
for i = 1: length(str)
midst = start;
start = midst + L*matr(str(i)-'a'+1,1);
endn = midst + L*matr(str(i)-'a'+1,2);
L = endn - start;
disp(sprintf ('%c\t%.30f\t%.30f\t%.30f',str(i),start,endn,L));
end
%输出编码
disp(sprintf ('输出的编码为:%.30f',start));
测试结果:
注意输出格式,否则有可能看到都为0的结果
disp(sprintf ('%c\t%.30f\t%.30f\t%.30f',str(i),start,endn,L));
解码实现
clc;
clear;
%%输入概率
c = [8000,1600,3000,4400,12000,2500,1700,6400,8000,400,800,4000,3000,8000,8000,1700,500,6200,8000,9000,3400,1200,2000,400,2000,200];
c = c/sum(c);
%%输入编码和字符数
code = input('请输入编码:');
num = input('请输入字符数');
%%得出区间
matr = zeros(length(c),2);
k = 0;
for i = 1:length(c)
matr(i,1) = k;
k = k+c(i);
matr(i,2) = k;
end
for i = 1:num
for i = 1:26
if code>=matr(i,1)-0.001&&code<=matr(i,2)-0.001
disp(char('a'+i-1)) ;
code = (code-matr(i,1))/c(i);
break
end
end
end
测试结果:
算术编码的解码需要输入解码后的字符数,否则会无限解码下去。当然也可以加容忍误差到达范围内停止解码。
LZW编码
原理
LZW编码的核心思想其实比较简单,就是把出现过的字符串映射到记号上,这样就可能用较短的编码来表示长的字符串,实现压缩。
LZW压缩算法的基本概念:LZW压缩有三个重要的对象:数据流、编码流和编译表。在编码时,数据流是输入对象(文本文件的据序列),编码流就是输出对象(经过压缩运算的编码数据);在解码时,编码流则是输入对象,数据流是输出对象;而编译表是在编码和解码时都须要用借助的对象。
LZW编码的算法步骤:
初始化:将所有单个字符按字母顺序初始化输入到字典中 读入第一个字符到S1
Step1:读入下一个字符存放到S2中
if S2 为空(表示读入到字符串尾了)
输出S1的index 结束
If S1+S2已经在字典中
S1+S2->S1
Repeat Step1
else (三步)
输出S1的index
S1+S2顺序添加到字典末尾
S2->S1
Repeat Step
LZW解码的算法步骤:
Step 读入编码到K中
if K不为空
查找字典中K对应的字符串
Repeat step
LZW实现
%%先输入单个字符集
%%再输入字符集组成的字符串
%%输出的是编码
%%然后输出根据编码和字典所得到的原来的信息
clc;
clear;
od = input('输入编码中的字符集合','s');
num = size(od,2);
for i = 1:num
lib(i) = struct('code',od(i),'num',dec2hex(i-1)); %index从0开始所以需要减1
end
str = input('输入待编码的字符串','s');
s1 = str(1);
cod = []; %存放码
num = num -1;
for i = 2:size(str,2)
s2 = str(i);
s = strcmp({lib.code},[s1,s2]);
ind = find(s==1);
if ~isempty(ind) %s1+s2在字典中
s1 = [s1,s2];
continue
else
s = strcmp({lib.code},s1); % 输出index s1 s1+s2顺序添加到末尾 s1=s2
ind = find(s==1);
cod = [cod,lib(ind).num];
num = num+1;
lib(num+1) = struct('code',[s1,s2],'num',dec2hex(num));
s1 = s2;
end
end
s = strcmp({lib.code},s1);
ind = find(s==1);
cod = [cod,lib(ind).num];
disp('输出的编码为');
disp(cod);
%%解码部分
decode = [];
for i = 1:size(cod,2)
s = strcmp({lib.num},cod(i));
ind = find(s==1);
decode = [decode,lib(ind).code];
end
disp('解码输出原来的信息为:');
disp(decode);
测试结果: