首页 > 代码库 > JPEG-LS extensions标准之熵编码部分
JPEG-LS extensions标准之熵编码部分
本文简要介绍了JPEG-LS扩展标准中的算术编码的流程及实现细节。更为详细的描述可以参考ITU-T T.870扩展标准。
算术编码流程
1.初始化
初始化主要包括Creg,Areg,Buf[2],Av[31],Th[30],对于MLcnt,LPScnt,MPSvalue的初始化在此不再重复。
其中Creg=0,Areg=0xff*0xff,unsigned char buf[2]为无符号数组,初始化为0.
Av,Th标准规定为固定值,如下:
int Av[31] = { 0x7ab6, 0x7068, 0x6678, 0x5ce2, 0x53a6, 0x4ac0, 0x4230, 0x39f4, 0x33fc, 0x301a, 0x2c4c, 0x2892, 0x24ea, 0x2156, 0x1dd6, 0x1a66, 0x170a, 0x13c0, 0x1086, 0x0d60, 0x0b0e, 0x0986, 0x0804, 0x0686, 0x050a, 0x0394, 0x027e, 0x01c6, 0x013e, 0x0100, 0x0000 }; int Th[30] = { 0x7800, 0x7000, 0x6800, 0x6000, 0x5800, 0x5000, 0x4800, 0x4000, 0x3c00, 0x3800, 0x3400, 0x3000, 0x2c00, 0x2800, 0x2400, 0x2000, 0x1c00, 0x1800, 0x1400, 0x1000, 0x0e00, 0x0c00, 0x0a00,0x0800, 0x0600, 0x0400, 0x0300, 0x0200, 0x0180, 0x0001 };
从第二步开始,相当于在实现AritmeticEncode(Bin,S)函数。
2.确定合适的Av值
不解释,直接上代码
//Search of suitable Av Prob = (LPScnt[S]<<16)/MLcnt[S]; for(Aindex=0;Aindex<30;++Aindex) if(Prob>Th[Aindex]) break; for(wct=0;Areg<(0x8000>>wct);++wct) ; if( (MLcnt[S]==MAXcnt)&&(LPScnt[S]==1)) Avd = 0x0002; else Avd = Av[Aindex]>>wct; Hd = 0x8000 >> wct;
3.更新Creg和Areg变量
不解释,直接上代码,这里变量Bin指的是输入算术编码器的待编码的字符,S也是通过从外部输入到算术编码的。
//Update of Creg and Areg Avd = Areg - Avd; if(Avd < Hd) Avd = (Avd+Hd)/2; if(Bin == MPSvalue[S]) Areg = Avd; else{ Creg = Creg + Avd; Areg = Areg - Avd; }
4.更新计数器
//Update of counters if(MLcnt[S] == MAXcnt){ if(Bin != MPSvalue[S]){ MLcnt[S] = (MLcnt[S]+1)/2+1; LPScnt[S] = (LPScnt[S]+1)/2+1; } else if(LPScnt[S]!=1){ MLcnt[S] = (MLcnt[S]+1)/2+1; LPScnt[S] = (LPScnt[S]+1)/2; } } else{ MLcnt[S]++; if(Bin!=MPSvalue[S]) LPScnt[S]++; } if(MLcnt[S]<LPScnt[S]*2){ LPScnt[S] = MLcnt[S]-LPScnt[S]; MPSvalue[S]=1-MPSvalue[S]; }
5.归一化Areg和Creg,并输出编码结果
//Renormalization of Areg and Creg and output data bit stream if(Areg<0x100){ if(Creg>=0xff * 0xff){ Creg -=0xff * 0xff; Buf[0]++; if(Buf[0]==0xff){ Buf[0]=0; Buf[1]++; } } AppendToBitStream(Buf[1],8); Buf[1] = Buf[0]; Buf[0] = ((Creg>>8)+Creg+1)>>8; Creg+=Buf[0]; Creg=((Creg&0xff)<<8)-(Creg&0xff); Areg=(Areg<<8)-Areg; }
AppendToBitStream(i,j)函数使用j bit将无符号整数i写入到输出码流中。这里由于j固定为8,故相当于直接向输出码流中写入1字节。
6.结束编码
当编码完所有像素后,编码器最后向输出码流写入4字节,分别为Buf[1],Buf[0],和Creg。
这里有一个疑问,Buf[1],Buf[0]都是一个字节没有什么可说的,但是Creg是int类型,理论上应该有32位,但是标准却规定Creg在最后的输出中只占有2个字节,那么一旦最终的Creg>65536怎么办?在实际的测试中,我确实发现按照标准的这种结尾的处理方式,会导致解码在最后几个bit出现错误(恢复数据不正确),但是无论怎么调整编码结束时对结尾的处理,都无法到达理想的效果,所以这里留下一个疑问,有待高人解答。是标准有什么隐含地方没说明,还是标准有问题?
上面就是整个算术编码的流程,至于其中的原理,标准没有说明,所以不用过多关心,我们只是用用就好。
下面简要介绍一下对应的解码流程。
解码流程跟编码流程有相同的部分,相同的部分代码不再贴出。
1.初始化,这里的初始化与编码初始化相同,唯一不同的是Creg的初始化值,在读入的已经编码的比特流中,前两个字节始终是0x00,可以舍去,第3个字节乘以0xFF再加上第4个字节得到的值作为Creg的初始化值。其他变量的初始化值与编码时相同。
2.确定合适的Av值,此段代码与编码完全一致。
3.恢复Bin
//detemination of Bin Avd = Areg - Avd; if(Avd < Hd) Avd = (Avd+Hd)/2; if(Creg < Avd){ Areg = Avd; Bin = MPSvalue[S]; } else{ Creg = Creg - Avd; Areg = Areg - Avd; Bin = 1 - MPSvalue[S]; }
经过此步,便恢复出一比特的Bin值。
4.更新计数器,此段代码与编码完全一致。
5.归一化Areg和Creg
if(Areg<0x100){ Creg = (Creg<<8)-Creg+GetByte(); Areg = (Areg<<8)-Areg; }
GetByte()函数表示从输入码流中读取一字节数据。
如上五步就是整个的算术解码过程。
JPEG-LS extensions标准之熵编码部分