首页 > 代码库 > 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标准之熵编码部分