当前位置:文档之家› 算术编码

算术编码

实验三算术编码一、实验目的1、复习C++语言基本编写方法,熟悉VC编程环境。

2、复习算术编码基本流程, 学会调试算术编码编码程序。

3、根据给出资料,自学自适应0阶算术编、解码方法。

二、实验内容1.复习C++代码基本语法(类和虚函数等面向对象数据结构定义)2.根据实验提供的源代码,学习算术编码实现流程,培养实际动手调试能力和相应的编程技巧。

三、实验仪器、设备1.计算机-系统最低配置256M 内存、P4 CPU。

2.C++ 编程软件-Visual C++ 7.0 (Microsoft Visual Studio 2003)Visual C++ 8.0 (Microsoft Visual Studio 2005)四、实验原理1.算术编码基本原理是将编码消息表示成实数0 和1 之间的一个间隔,消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。

算术编码用到两个基本的参数:符号的概率和它的编码间隔。

信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0 到1 之间。

编码过程中的间隔决定了符号压缩后的输出。

如何解压缩呢?那就更简单了。

解压缩之前仍然假定三个字符的概率相等。

解压缩时面对的是二进制流 1010001111,先在前面加上 0 和小数点把它变成小数0.1010001111,也就是十进制 0.64。

这时我们发现 0.64 在分布图中落入字符 b 的区间内,立即输出字符 b,并得出三个字符新的概率分布。

类似压缩时采用的方法,我们按照新的概率分布划分字符 b 的区间。

在新的划分中,我们发现 0.64 落入了字符c 的区间,我们可以输出字符 c。

同理,我们可以继续输出所有的字符,完成全部解压缩过程。

2.小数存储方法如果信息内容特别丰富,我们要输出的小数将会很长很长,该如何在内存中表示如此长的小数呢?其实,没有任何必要在内存中存储要输出的整个小数。

从上面的例子可以知道,在编码的进行中,会不断地得到有关要输出小数的各种信息。

具体地讲,当我们将区间限定在 0.6390 - 0.6501 之间时,我们已经知道要输出的小数第一位(十进制)一定是 6,那么我们完全可以将 6 从内存中拿掉,接着在区间0.390 - 0.501 之间继续我们的压缩进程。

内存中始终不会有非常长的小数存在。

使用二进制时也是一样的,我们会随着压缩的进行不断决定下一个要输出的二进制位是 0 还是 1,然后输出该位并减小内存中小数的长度,具体可以参考E1/E2/E3放大原理,及它们之间关系的描述。

3.静态模型与自适应模型1)静态模型对信息 bccb 我们统计出其中只有两个字符,概率分布为 Pb = 0.5,Pc = 0.5。

在压缩过程中不必再更新此概率分布,每次对区间的划分都依照此分布即可,对上例也就是每次都平分区间。

这样,压缩过程可以简单表示为:输出区间的下限输出区间的上限------------------------------------------------------------------------压缩前 0.0 1.0输入 b 0.0 0.5输入 c 0.25 0.5输入 c 0.375 0.5输入 b 0.375 0.4375最后的输出区间在 0.375 - 0.4375 之间,甚至连一个十进制位都没有确定,也就是说,整个信息根本用不了一个十进制位。

2)自适应模型既然使用静态模型可以很好地接近熵值,为什么还要采用自适应模型呢?要知道,静态模型无法适应信息多样性,另外还有最重要的一点,对较长的信息,静态模型统计出的符号概率是该符号在整个信息中的出现概率,而自适应模型可以统计出某个符号在某一局部的出现概率或某个符号相对于某一上下文的出现概率,换句话说,自适应模型得到的概率分布将有利于对信息的压缩(可以说结合上下文的自适应模型的信息熵建立在更高的概率层次上,其总熵值更小),好的基于上下文的自适应模型得到的压缩结果将远远超过静态模型。

3)自适应模型的阶通常用“阶”(order)这一术语区分不同的自适应模型。

前面例子中采用的是 0阶自适应模型,该例子中统计的是符号在已输入信息中的出现概率,没有考虑任何上下文信息。

如果我将模型变成统计符号在某个特定符号后的出现概率,那么,模型就成为了 1 阶上下文自适应模型。

举个例子要对一篇英文文本进行编码,已经编码了 10000 个英文字符,刚刚编码的字符是 t,下一个要编码的字符是 h。

我们在前面的编码过程中已经统计出前 10000 个字符中出现了 113 次字母 t,其中有 47 个 t 后面跟着字母 h。

我们得出字符 h 在字符 t 后的出现频率是 47/113,我们使用这一频率对字符 h 进行编码,需要 -log2(47/113) = 1.266 bit。

对比 0 阶自适应模型,如果前 10000 个字符中 h 的出现次数为 82 次,则字符 h 的概率是82/10000,我们用此概率对 h 进行编码,需要 -log2(82/10000) = 6.930 bit。

考虑上下文因素的优势显而易见。

还可以进一步扩大这一优势,例如要编码字符 h 的前两个字符是 gt,而在已经编码的文本中 gt 后面出现 h 的概率是 80%,那么我们只需要 0.322 bit 就可以编码输出字符 h。

此时,使用的模型叫做 2 阶上下文自适应模型。

最理想的情况是采用 3 阶自适应模型。

此时,如果结合算术编码,对信息的压缩效果将达到惊人的程度。

采用更高阶的模型需要消耗的系统空间和时间至少在目前还无法让人接受,使用算术压缩的应用程序大多数采用 2 阶或 3 阶的自适应模型。

五、实验步骤项目文件建立步骤同实验二,下面列出对给定序列的算术编码步骤:步骤1:编码器在开始时将“当前间隔”[ L,H) 设置为[0,1)。

步骤2:对每一事件,编码器按步骤(a)和(b)进行处理(a)编码器将“当前间隔”分为子间隔,每一个事件一个。

(b)一个子间隔的大小与下一个将出现的事件的概率成比例,编码器选择子间隔对应于下一个确切发生的事件相对应,并使它成为新的“当前间隔”。

步骤3:最后输出的“当前间隔”的下边界就是该给定事件序列的算术编码。

六,实验代码// Ac_algo.cpp : 定义控制台应用程序的入口点。

//#include "stdafx.h"#include "ModelOrder0C.h"using namespace std;// signature: "ACMC" (0x434D4341, intel byte order)const int g_Signature = 0x434D4341;int __cdecl main(int argc, char *argv[]){cout << "Arithmetic Coding" << endl;if( argc != 3 ){cout << "Syntax: AC source target" << endl;return 1;}fstream source, target;ModelI* model;// choose model, here just order-0model = new ModelOrder0C;source.open( argv[1], ios::in | ios::binary );target.open( argv[2], ios::out | ios::binary );if( !source.is_open() ){cout << "Cannot open input stream";return 2;}if( !target.is_open() ){cout << "Cannot open output stream";return 3;}unsigned int signature;source.read(reinterpret_cast<char*>(&signature),sizeof(signature));if( signature == g_Signature ){cout << "Decoding " << argv[1] << " to " << argv[2] << endl;model->Process( &source, &target, MODE_DECODE );}else{cout << "Encoding " << argv[1] << " to " << argv[2] << endl;source.seekg( 0, ios::beg );target.write( reinterpret_cast<const char*>(&g_Signature),sizeof(g_Signature) );model->Process( &source, &target, MODE_ENCODE );}source.close();target.close();return 0;}八、思考题能否根据算法流程和C++源代码写出Matlab 下算术编码程序?。

相关主题