算数游程编码
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
算术编码与游程编码
1 .课题描述
1.理解和掌握算术编码和游程编码的基本原理。
2.编程实现算术编码和游程编码的基本流程。
3.了解算术编码和游程编码的优缺点。
4.分析实验结果。
2 信源编码的相关介绍
编码实质上是对信源的原始符号按一定规则进行的一种变换。
编码可分为信源编码和信道编码。
信源编码:以提高通信有效性为目的的编码。
通常通过压缩信源的冗余度来实现。
采用的一般方法是压缩每个信源符号的平均比特数或信源的码率。
即同样多的信息用较少的码率传送,使单位时间内传送的平均信息量增加,从而提高通信的有效性。
•信源编码理论是信息论的一个重要分支,其理论基础是信源编码的两个定理。
–无失真信源编码定理:是离散信源/数字信号编码的基础;
–限失真信源编码定理:是连续信源/模拟信号编码的基础。
•信源编码的分类:
–离散信源编码:独立信源编码,可做到无失真编码;
–连续信源编码:独立信源编码,只能做到限失真信源编码;
–相关信源编码:非独立信源编码。
信源编码的作用之一是设法减少码元数目和降低码元速率,即通常所说的数据压缩:作用之二是将信源的模拟信号转化成数字信号,以实现模拟信号的数字化传输。
最原始的信源编码就是莫尔斯电码,另外还有ASCII码和电报码都是信源编码。
但现代通信应用中常见的信源编码方式有:Huffman编码、算术编码、L-Z编码,这三种都是无损编码,另外还有一些有损的编码方式。
信源编码的目标就是使信源减少冗余,更加有效、经济地传输,最常见的应用形式就是压缩。
另外,在数字电视领域,信源编码包括通用的MPEG—2编码和H.264(MPEG—Part10 AVC)编码等相应地,信道编码是为了对抗信道中的噪音和衰减,通过增加冗余,如校验码等,来提高抗干扰能力以及纠错能力。
•
3 . 算术编码
3.1算术编码算法
算术编码在图像数据压缩标准中扮演了重要的角色, 是无损压缩的一种
算术编码中用0和1之间的实数进行编码, 该编码用到两个基本的参数Α符号的概率和它的编码间隔
信源符号的概率决定了压缩编码的效率, 也决定了编码过程中信源符号的间隔, 而这些间隔包含在0到1之间编码过程中的间隔决定了符号压缩后的输出算术编码的编码过程如
下Α算术编码把一个信源集合表示为实数线上的0 到1 之间的一个区间。
这个集合中的每个元素都要用来缩短这个区间。
信源集合的元素越多,所得到的区间就越小,当区间变小时,就需要更多的数位来表示这个区间,这就是区间作为代码的原理。
算术编码首先假设一个信源的概率模型,然后用这些概率来缩小表示信源集的区间。
]设英文元音字母采用固定模式符号概率分配如下:
设编码的数据串为eai 。
令high 为编码间隔的高端,low 为编码间隔的低端, range 为编码间隔的长度,rangelow 为编码字符分配的间隔低端,rangehigh 为编码字符分配的间隔高端。
初始high=1,low=0,range=high-low, 一个字符编码后新的low 和high 按下式计算: ·low =low+range × rangelow
·high =low+range×rangehigh
(1) 在第一个字符e 被编码时,e 的rangelow=0.2,rangehigh=0.5, 因此:
low=0 + 1 × 0.2 =0.2
high=0 + 1 × 0.5 =0.5
range=high-low=0.5-0.2=0.3
此时分配给e 的范围为[0.2,0.5] 。
(2)第二个字符 a 编码时使用新生成范围[0.2,0.5],a 的rangelow=0, rangehigh=0.2, 因此:
low=0.2 十0.3 × 0=0.2
high=0.2 +0.3 × 0.2=0.26
range=0.06
范围变成[0.2,0.26] 。
(3) 对下一个字符i 编号,i 的rangelow=0.5,rangehigh=0.6, 则:
low=0.2 +0.06 × 0.5=0.23
high=0.2 +0.06 × 0.6=0.236
即用[0.23,0.236] 表示数据串eai, 如果解码器知道最后范围是
[0.23,0.236 ]这一范围, 它马上可解得一个字符为e, 然后依次得到惟一解a, 即最终得到eai 。
算术编码的算法思想如下:
(1)对一组信源符号按照符号的概率从大到小排序,将[0,1)设为当前分析区间。
按信源符号的概率序列在当前分析区间划分比例间隔。
(2)检索“输入消息序列”,锁定当前消息符号(初次检索的话就是第一个消息符号)。
找到当前符号在当前分析区间的比例间隔,将此间隔作为新的当前分析区间。
并把当前分析区
间的起点(即左端点)指示的数“补加”到编码输出数里。
当前消息符号指针后移。
(3)仍然按照信源符号的概率序列在当前分析区间划分比例间隔。
然后重复第二步。
直到“输入消息序列”检索完毕为止。
(4)最后的编码输出数就是编码好的数据。
3.2算术编码的特点
1)不必预先定义概率模型, 自适应模式具有独特的优点;
2)信源符号概率接近时, 建议使用算术编码, 这种情况下其效率高于Huffman 编码; 3)算术编码绕过了用一个特定的代码替代一个输入符号的想法, 用一个浮点输出数值代替一个流的输入符号, 较长的复杂的消息输出的数值中就需要更多的位数。
4)算术编码实现方法复杂一些, 但JPEG 成员对多幅图像的测试结果表明, 算术编码比Huffman 编码提高了5% 左右的效率, 因此在JPEG 扩展系统中用算术编码取代Huffman 编码。
4. 算术编码的C程序实现
4.1 程序设计
#include <iostream.h>
#include<math.h>
#include <stdio.h>
#define M 100
#define N 4
class suanshu
{
int count,length;
char number[N],n;
long double chance[N],c;
char code[M];
long double High,Low,high,low,d;
public:
suanshu()
{High=0;Low=0;}
void get_number();
void get_code();
void coding();
~suanshu(){}
};
void suanshu::get_number()
{
cout<<"please input the number."<<endl;
for(int i=0;i<N;i++)
{
cin>>n;
number[i]=n;
}
cout<<"please input the chance."<<endl;
for(int i=0;i<N;i++)
{
cin>>c;
chance[i]=c;
}
if(i==20)
cout<<"the number is full."<<endl;
count=i;
}
void suanshu::get_code()
{
cout<<"please input the code’s length:";
cin>>length;
while(length>=M)
{
cout<<"the length is too larger,please input a smaller one."; cin>>length;
}
for(int i=0;i<length;i++)
{
cin>>code[i];
}
}
void suanshu::coding()
{
long double tp;//从区间取数
long double x=0.0;
int i,j=0;
for(i=0;i<count;i++)
if(code[0]==number[i]) break;
while(j<i)
Low+=chance[j++];
d=chance[j];
High=Low+d;
for(i=1;i<length;i++)
for(j=0;j<count;j++)
{
if(code[i]==number[j])
{
if(j==0)
{
low=Low;
high=Low+chance[j]*d;
High=high;
d*=chance[j];
}
else
{
float chance_l=0.0;
for(int k=0;k<=j-1;k++)
chance_l+=chance[k];
low=Low+d*chance_l;
high=Low+d*(chance_l+chance[j]);
Low=low;
High=high;
d*=chance[j];
}
}
else continue;
}
cout<<"the result is:"<<Low<<high<<endl; tp=(low+high)/2.0;//取区间的中点编码printf("\n选取的小数为:%.17f\n",tp);
for(j=0;j<n;j++)
{
tp=tp*2;
if(tp>1.0)
{
tp=tp-1;
code[j]=1;
}
else
code[j]=0;
}
printf("编码为:\n");//输出编码
for(i=0;i<n;i++)
{
printf("%d",code[i]);
x+=code[i]*pow(2,(-i-1));
}
printf("\n恢复的小数为:%.17f",x);
if((x>low)&&(x<high))//判断恢复的是否成功printf("\n恢复的小数仍在允许区间\n");
}
int main()
{
suanshu a;
a.get_number();
a.get_code();
a.coding();
return 0;
}
4.2 运行结果
3 游程编码
3.1 游程编码算法
编码的基本原理是:用一个符号值或串长代替具有相同值的连续符号(连续符号构成了一段连续的“行程”。
行程编码因此而得名),使符号长度少于原始数据的长度。
只在各行或者各列数据的代码发生变化时,一次记录该代码及相同代码重复的个数,从而实现数据的压缩。
在m元序列中,可能m种游程,连着出现m种符号ar的游程,其长度L(r)就是‘r’游程长度,这是一个随机变量。
用L(r)也可构成游程序列但是这种变换必须再加一些符号,才能成为一一对应或可逆的。
游程长度编码的主要思想是将一个相同值的连续申用其值和申长(重复的个数)的数对
二元组来替代。
例如,在图像编码中,可以定义沿特定方向上具有相同灰度值的相邻像素为一轮,其延续的长度称之为延续的行程,即游程。
游程终点位置由前一游程终点的相对距离确定,这样就可以由灰度游程串来表示图像数据。
例如,若沿水平方向有一串M 个像素具有相同的灰度N,则按游程长度编码后,只传递两个值(N,M)就可以代替这M 个像素的M 个灰度值NJ简单来说,游程长度编码的主要任务是统计连续相同字符的个数,解码时要根据字符及连续相同字符的个数,恢复原来的数据。
3.1 游程编码特点
游程编码仍是变长码,有其固有的缺点,及需要大量的缓冲和优质的信道。
此外,编程长度1可以从一直到无限,这在码字的选择和码表的建立方面都有困难,实际应用是尚需采用某些措施来改进。
一般情况下游程长度越长,其概率越小,这在以前的计算中也可以看见,而且将随着长度的增大渐进向零。
对于小概率的码字,其长度为达到概率匹配或较长,损失不会太大,也就是对平均码字长度影响较小。
再按哈夫曼编码或其他方法处理以达到压缩码率的目的。
4. 游程编码的C程序实现
程序设计
#include<iostream>
using namespace std;
void create(int a[20][20],int i,int j)
{int m,n,x;
cout<<"请输入:"<<endl;
for(m=0;m<i;m++)
for(n=0;n<j;n++)
{ cin>>x;
a[m][n]=x;
}
a[0][0]=5;a[0][1]=5;a[0][2]=5;a[0][3]=5;a[0][4]=4;a[0][5]=4;a[0][6]=4;a[0][7]=4;
a[1][0]=4;a[1][1]=4;a[1][2]=4;a[1][3]=6;a[1][4]=6;a[1][5]=2;a[1][6]=2;a[1][7]=2; a[2][0]=2;a[2][1]=2;a[2][2]=3;a[2][3]=3;a[2][4]=3;a[2][5]=3;a[2][6]=3;a[2][7]=7; a[3][0]=7;a[3][1]=7;a[3][2]=7;a[3][3]=7;a[3][4]=0;a[3][5]=0;a[3][6]=0;a[3][7]=0; a[4][0]=1;a[4][1]=1;a[4][2]=1;a[4][3]=1;a[4][4]=1;a[4][5]=1;a[4][6]=1;a[4][7]=1; a[5][0]=2;a[5][1]=2;a[5][2]=2;a[5][3]=2;a[5][4]=3;a[5][5]=3;a[5][6]=3;a[5][7]=3; a[6][0]=5;a[6][1]=5;a[6][2]=5;a[6][3]=5;a[6][4]=5;a[6][5]=5;a[6][6]=7;a[6][7]=7; a[7][0]=1;a[7][1]=1;a[7][2]=1;a[7][3]=1;a[7][4]=1;a[7][5]=1;a[7][6]=3;a[7][7]=3; cout<<"像素值为:"<<endl;
for(m=0;m<i;m++)
{
for(n=0;n<j;n++)
{
cout<<a[m][n]<<" ";
}
cout<<endl;
}
}
void show2(int a[20][20],int i,int j,int c[20],int & g,int v[20])
{ int m, n , w,k,l=0;
for(m=0;m<i;m++)
{
for(n=0;n<j;)
{ cout<<"("<<a[m][n]<<",";
w=1;
c[g++]=a[m][n];
for(k=n+1;k<j;k++)
if(a[m][n]==a[m][k]) w++;
else if(a[m][n]!=a[m][k] || w==j-n)
{ cout<<w<<")"<<" ";
v[l++]=w-1;
break;
}
n=k;
}
if(n=j-1)
{
cout<<w<<")"<<" ";
}
v[l++]=w-1;
}
cout<<endl;
}
void duishu(int a,int & i)
{ int k=0;
int b=a;
while (a/2!=0)
{a=a/2;
k++;
}
if(b%2==0) i=k;
else i=k+1;
}
void zhuanhuan1(int c[20],int d[20][20],int g,int m)//转换成二进制
{ int i,k,j=0;
for(i=0;i<g;i++)
for(k=0;k<m;k++)
d[i][k]=0;
for(i=0;i<g;i++)
for(k=m-1;k>=0;k--)
if(c[i]/2>=0)
{
d[i][k]=c[i]%2;
c[i]=c[i]/2;
}
else break;
}
void bianma(int d[20][20],int c[20],int f[20][20],int v[20],int g,int m,int n)//编码{ int i,k,j;
zhuanhuan1(c,d,g,m);
for(i=0;i<g;i++)
{
v[i]=v[i]+1;
}
zhuanhuan1(v,f,g,n);
cout<<" 编码结果为:"<<endl;
for(i=0;i<g;i++)
{ for(k=0;k<m;k++)
cout<<d[i][k]<<" ";
cout<<".";
for(j=0;j<n;j++)
cout<<f[i][j]<<" ";
cout<<endl;
}
}
void yima(int d[20][20],int f[20][20],int g,int m,int n) { int i,j,k=0,s=0,tmp=0,t=0;
int p[20];
int q[20] ;
cout<<"开始译码:"<<endl;
for(i=0;i<g;i++)
{ p[i]=tmp=0;
for(j=0;j<m;j++)
{k=m-1-j;
tmp=d[i][j];
while(k>0)
{ tmp*=2;
k--;
}
p[i]+=tmp;
}
if(d[i][j]%2!=0) p[i]=p[i]+d[i][m-1];
}
for(i=0;i<g;i++)
{ q[i]=tmp=0;
for(j=0;j<n;j++)
{k=n-1-j;
tmp=f[i][j];
while(k>0)
{ tmp*=2;
k--;
}
q[i]+=tmp;
}
if(f[i][j]%2!=0) q[i]=q[i]+f[i][n-1];
}
for(i=0;i<g;i++)
for(j=0;j<q[i]+1;j++)
{
cout<<p[i]<<" ";
t++;
if(t%8==0)
{
cout<<endl;
}
}
}
void main()
{ int i,j,h,g=0,m,n;
char x;
int a[20][20];
int c[20];
int v[20];
cout<<"请输入灰度:"<<endl; cin>>h;
duishu(h,m);
cout<<"请输入行数和列数:"<<endl; cin>>i>>j;
duishu(j,n);
create(a,i,j);
show2(a,i,j,c,g,v);
int d[20][20];
int f[20][20];
for(i=0;i<g;i++)
{
v[i]=v[i]-1;
}
bianma(d,c,f,v,g, m, n);
cout<<"译码请输入Y!";
cin>>x;
if(x=='Y') yima( d,f,g,m,n);
else cout<<endl;
}
4.2 运行结果
总结
通过做算术和游程编码,我对码的基本实现过程已经有了一个较好的了解,所以做这游程编
码的时候明显感觉比第一个做费诺要来的简单一些.不过通过这次实验又使我掌握了一种新的编码方法,并且使我对编码有一进一步的了解.本实验的的成功之处,主要在于对一基本上在编码和译码过程中的可能出现的错误和异常情况都做了一定的修补,即本程序的性能比较好.还有就是在查找信源符号编码结果时用的是二分查找的方法这样可以把查找长度提高到O(nlogn)这一数量级上这比你直接对读入的码进行二进制转化要快一些.通过对游程编码的原理我们可以看到当输入灰度级矩阵中各灰度值相同的数少的话,比如其中每一个游程都只有一位的长度,则事实上就相当于一个灰度值要用两个数值来编码,这样就相当于加倍编码,其效率相当的差.其次,即便是游程足够长,但用等长编码的效率并不是最好的,所以我觉得,可以对其编码进行一个改进. 特别是当灰度级和每行的个数比较接近进更是如此,即,对游程的统计结果中各符号的个数来一个计数,并求出相关码的概率,对个数和灰度值进行统一的变长编码,这样又可以改进其编码效率,就相当于来了一个两次压缩.不过这种方法有一个不足就是在灰度值和每行个数相差较大时其效率不会很高.所以,下次实验时要对这一方面进行相应的改进从而来实现更加高效率的编码.
参考文献:
【1】夏娜.信息编码与加密实践.合肥工业大学出版社,2008,8
【2】曹雪虹.信息论与编码.清华大学出版社,2009,2。