一、实验目的
1、理解图像编码的概念:
2、掌握霍夫曼编码技术
二、实验内容:
选取一段不少于3000字的英文材料,统计各字符出现的次数,实现HuMiman 编码,以及对编码结果的解码。
三、实验要求:
1)输出每个字符出现的次数和编码,并存储文件(Huffmnan.txt)。2)在 Huffman 编码后,英文文章编码结果保存到文件中(code.dat),编码结果必须是二进制形式,即01的信息用比特位表示,不能用字符“0 和“1表示。
3)实现解码功能
霍夫曼编码(Huffman Coding)和费诺编码(Fano Coding)都是用于数据压缩的变长编码方法,在数字通信、数据存储等领域有广泛应用。
霍夫曼编码
原理
霍夫曼编码基于数据出现的概率来构建最优二叉树(霍夫曼树),出现概率高的数据用较短的编码表示,出现概率低的数据用较长的编码表示,从而实现数据的压缩。
编码步骤
- 统计频率:统计输入数据中每个符号出现的频率或概率。
- 构建霍夫曼树:
- 将每个符号作为一个单独的节点,节点的权值为该符号的频率。
- 重复以下步骤,直到所有节点合并为一个根节点:
- 从节点集合中选取权值最小的两个节点。
- 创建一个新的内部节点,其权值为这两个节点权值之和,将这两个节点作为新节点的左右子节点。
- 将新节点加入节点集合,并移除原来的两个节点。
- 生成编码表:从根节点开始,向左遍历标记为 0,向右遍历标记为 1,直到到达叶子节点,得到每个符号的霍夫曼编码。
优缺点
- 优点:
- 是一种最优变长编码,能达到理论上的最小平均编码长度,压缩效率高。
- 编码过程简单,易于实现。
- 缺点:
- 需要额外的存储空间来存储霍夫曼树或编码表,以便解码时使用。
- 对数据的统计特性依赖较大,如果数据的概率分布发生变化,编码效率会降低。
费诺编码
原理
费诺编码也是一种变长编码方法,它通过将符号集合按照概率大小排序,然后递归地将集合分成两个子集,使得两个子集的概率之和尽可能接近,为每个子集分配不同的编码前缀,逐步生成每个符号的编码。
编码步骤
- 统计频率:统计输入数据中每个符号出现的频率或概率。
- 排序符号:将符号按照概率从大到小排序。
- 划分集合:
- 将排序后的符号集合划分为两个子集,使得两个子集的概率之和尽可能接近。
- 为第一个子集的所有符号编码前缀添加 0,为第二个子集的所有符号编码前缀添加 1。
- 递归处理:对每个子集重复步骤 3,直到子集中只有一个符号为止。
优缺点
- 优点:
- 编码过程相对直观,容易理解。
- 不需要像霍夫曼编码那样构建复杂的树结构。
- 缺点:
- 不是最优变长编码,平均编码长度通常比霍夫曼编码长,压缩效率相对较低。
- 当符号数量较多时,划分集合使得概率之和接近的操作可能会比较复杂。
具体代码如下:
% 读取英文材料
try
fileID = fopen('english_text.txt', 'r');
text = fread(fileID, '*char')';
fclose(fileID);
catch ME
fprintf('读取文件时出错: %s\n', ME.message);
return;
end
% 统计各字符出现的次数
uniqueChars = unique(text);
counts = zeros(size(uniqueChars));
for i = 1:length(uniqueChars)
counts(i) = sum(text == uniqueChars(i));
end
% 构建 Huffman 树和编码表
symbols = 1:length(uniqueChars);
probabilities = counts / sum(counts);
[dict, ~] = huffmandict(symbols, probabilities);
% 输出每个字符出现的次数和编码,并存储到文件 (Huffman.txt)
try
fileID = fopen('Huffman.txt', 'w');
for i = 1:length(uniqueChars)
code = dict{i};
fprintf(fileID, 'Character: %c, Frequency: %d, Code: ', uniqueChars(i), counts(i));
for j = 1:length(code)
fprintf(fileID, '%d', code(j));
end
fprintf(fileID, '\n');
end
fclose(fileID);
catch ME
fprintf('写入 Huffman.txt 文件时出错: %s\n', ME.message);
return;
end
% 将字符转换为对应的符号索引
symbolIndices = zeros(size(text));
for i = 1:length(text)
index = find(uniqueChars == text(i));
symbolIndices(i) = index;
end
% 进行 Huffman 编码
encodedData = huffmanenco(symbolIndices, dict);
% 将编码结果保存到文件 (code.dat),以二进制形式
try
fileID = fopen('code.dat', 'wb');
fwrite(fileID, encodedData, 'ubit1');
fclose(fileID);
catch ME
fprintf('写入 code.dat 文件时出错: %s\n', ME.message);
return;
end
% 读取编码文件
try
fileID = fopen('code.dat', 'rb');
encodedDataRead = fread(fileID, '*ubit1');
fclose(fileID);
catch ME
fprintf('读取 code.dat 文件时出错: %s\n', ME.message);
return;
end
% 将编码数据转换为 double 类型
encodedDataRead = double(encodedDataRead);
% 进行 Huffman 解码
decodedIndices = huffmandeco(encodedDataRead, dict);
% 将解码后的符号索引转换为字符
decodedText = uniqueChars(decodedIndices);
% 验证解码结果
if isequal(decodedText, text)
disp('解码成功!');
else
disp('解码失败。');
end
每个字符的出现次数和对应的 Huffman 编码如下:
编码方法 | 霍夫曼编码 | 费诺编码 |
---|---|---|
编码原理 | 基于概率构建最优二叉树 | 递归划分符号集合 |
压缩效率 | 高,理论最优 | 相对较低 |
编码复杂度 | 构建霍夫曼树较复杂 | 相对简单 |
解码复杂度 | 需要存储霍夫曼树或编码表 | 相对简单 |
应用场景 | 广泛应用于各种数据压缩领域 | 适用于对编码复杂度要求低、压缩效率要求不高的场景 |