for(i=0;i<lx*ly;i++)
{
//ip中存储各像素灰度值
a=(int)ip[i];
b=a/2;
//当前像素对应的格雷码
GrayCode=a^b;
//把格雷码存入8个比特平面对应的数组
for(j=0;j<8;j++)
{
iip[j][i]=GrayCode%2;
GrayCode/=2;
}
}
3.对各个比特平面进行扫描,取p=q=4,各比特平面中p*q小块如果为全0块,开头一位放2,如果为全1块,开头一位放3,而混合块开头一位是0或1,编码时可以相区别。同时统计全0块和全1块出现的次数,返回0或1,编码时可以区别处理。下面为进行扫描的函数。
int scan()
{
int zero,one,i,j,z,k,binary,sign;
zero=one=0;
for(i=0;i<(ly/p);i++)
{
for(j=0;j<(lx/q);j++)
{
binary=iip[I][i*p*lx+j*q]; sign=0;
//判断是否为混合块,一旦有不相等的数值,跳出循环
for(z=0;z<p;z++)
{
for(k=0;k<q;k++)
{
if(binary!=iip[I][(i*p+z)*lx+(j*q+k)])
{
sign=1;break;
}
}
if(sign==1) break;
}
//是全0块或全1块
if((z==p)&&(k==q))
{
if(binary==0)
{
zero++;//0计数加1
iip[I][i*p*lx+j*q]=2;//全0块标志
}
else
{
one++;//1计数加1
iip[I][i*p*lx+j*q]=3;//全1块标志
}
}
}
}
if(one>zero) return 1;
else return 0;
}
4.开始编码并写入文件。
编码从高位比特平面到低位比特平面进行。对于全0块或全1块,如果出现概率高,分配1比特码字0;如果出现概率低,分配2比特码字11。如果是混合块,先写入前缀码10,再写入该块具体图案。由于读写文件的最小单位是字节,而编码是不定长的,所以我们把每32位编码对应为一个unsigned int 型数据,依次把各个unsigned int 型数据写入文件。最后几位编码如果不够32位,我们把这几位编码的右端补0,凑够32位后写入文件。解码时,当所有位平面解码完成后,我们就不再继续向下读数据文件,所以上面右端补0的做法不影响解码的正确性。以下为编码并写入文件的程序。
//创建压缩文件
CFile MyFile1("D:\\test.dat",CFile::modeCreate|CFile::modeWrite);
nCode=nN=0;
//编码从高位比特平面到低位比特平面
for(i1=7;i1>=0;i1--)
{
for(i=0;i<(ly/p);i++)
{
for(j=0;j<(lx/q);j++)
{
bi=iip[i1][i*p*lx+j*q];
//该块为全0或全1块且出现的概率高,分配1比特码字0
if(((bi==2)&&(c[i1]==0))||((bi==3)&&(c[i1]==1)))
{
nCode+=0;nN++;
if(nN==32)
{
MyFile1.Write((void*)(&nCode),4);
nN=0;nCode=0;
}
else
nCode<<=1;
}
//该块为全0或全1块且出现的概率低,分配2比特码字11
else if(((bi==2)&&(c[i1]==1))||((bi==3)&&(c[i1]==0)))
{
for(l=0;l<2;l++)
{
nCode+=1;
nN++;
if(nN==32)
{
MyFile1.Write((void*)(&nCode),4);
nN=0;
nCode=0;
}
else
nCode<<=1;
}
}
//混合块先写入前缀码10,再写入该块具体图案
else
{
//写入10
for(l=0;l<2;l++)
{
nCode+=(1-l);nN++;
if(nN==32)
{
MyFile1.Write((void*)(&nCode),4);
nN=0;nCode=0;
}
else
nCode<<=1;
}
//写入该块具体图案
for(z=0;z<p;z++)
{
for(k=0;k<q;k++)
{
nCode+=iip[i1][(i*p+z)*lx+j*q+k];nN++;
if(nN==32)
{
MyFile1.Write((void*)(&nCode),4);
nN=0;nCode=0;
}
else
nCode<<=1;
}//k
}//z
}//else
}//j
}//i
}//i1
MyFile1.Close();
5.由压缩文件解码,重建图像。
解码为编码的逆过程,以下为解码的程序。
CFile MyFile2("D:\\test.dat",CFile::modeRead);
while(1)
{
MyFile2.Read((void*)(&nCode1),4);
n1=31;
//把编码拆入数组
while(n1>=0)
{
st[n1--]=nCode1%2;nCode1/=2;
}
for(i=0;i<32;i++)
{
if(read==1)
{
oop[num][n++]=st[i];
if(n==p*q)
{
n=0;num++;read=0;
if(num==((lx/q)*(ly/p)))
{
trans();//一个比特平面解码完成,存入数组相应位置
if(nu==-1)
{
sig=1;break;
}
else
{
num=0;s1=c[nu];s2=1-s1;
}
}
}
}
else if(con==1)
{
if(st[i]==1)
{
oop[num++][0]=s2+2;
if(num==((lx/q)*(ly/p)))
{
trans();//一个比特平面解码完成,存入数组相应位置
if(nu==-1)
{
sig=1;break;
}
else
{
num=0;s1=c[nu];s2=1-s1;
}
}
}
else
{
read=1;
}
con=0;
}
else
{
if(st[i]==0)
{
oop[num++][0]=s1+2;
if(num==((lx/q)*(ly/p)))
{
trans();//一个比特平面解码完成,存入数组相应位置
if(nu==-1)
{
sig=1; break;
}
else
{
num=0;s1=c[nu];s2=1-s1;
}
}
}
else
con=1;
}
}//i
if(sig==1) break;
}
MyFile2.Close();
以下为解码过程中用到的一个转换函数,作用为把各个比特平面的数组全0块,全1块全部填入0或1,混合块填入具体图案。
void trans()
{
int d1,d2,i,j,z,k;
for(i=0;i<(ly/p);i++)
{
for(j=0;j<(lx/q);j++)
{
d1=oop[i*(lx/q)+j][0];
//全0块填入0或1
if((d1==2)||(d1==3))
{
for(z=0;z<p;z++)
{
for(k=0;k<q;k++)
{
op[nu][(i*p+z)*lx+j*q+k]=d1-2;
}
}
}
//混合块填入具体图案
else
{
for(z=0;z<p;z++)
{
for(k=0;k<q;k++)
{
d2=oop[i*(lx/q)+j][z*q+k];
op[nu][(i*p+z)*lx+j*q+k]=d2;
}
}
}
}//j
}//i
nu--;
}
6.将格雷码转为二进制码,并存入图像数组。
相应于(1)中的多项式,8比特的二进制码可由下式得到。
(4)
对应的程序如下。
for(i=0;i<lx*ly;i++)
{
//转换为二进制
nValue=nValue1=op[7][i];
for(j=6;j>=0;j--)
{
nValue1=(op[j][i])^nValue1;
nValue<<=1;
nValue+=nValue1;
}
//存入图像数组
op1[i]=nValue;
}
6.显示解码后图像(本文中代码省略)。
三、 结语
对256*256像素(64K)的几幅灰度图像进行测试,结果如下。
sun |
tree |
baboon |
lena |
cat |
34.8K |
53.4K |
61.4K |
50.4K |
52.3K |
对512*512像素(256K)的几幅灰度图像进行测试,结果如下。
goldhill |
lena |
barbara |
boat |
baboon |
207K |
189K |
212K |
208K |
243K |
对于上述结果可以进一步处理。如果一个编码块中比特数值大部分为0,只有很少的1,利用人的视觉心理特性,可将该块视为全0块处理。此时视觉上可能感觉不到明显的失真而
予以接受。反之也可将1很多而0很少的块视为全1块处理。这种利用视觉心理特性的编码,尤其在低位的比特平面上使用,不会引起明显的可察觉失真(因其产生的客观失真量小),可收到较好效果。上述处理使全0块全1块出现的概率p(0)p(1)上升,从而提高压缩效率。
四、参考文献
[1]冈萨雷斯. 数字图像处理. 北京:电子工业出版社,2003
[2]张春田,苏育挺,张静. 数字图像压缩编码. 北京:清华大学出版社,2006