熵编码

 熵:熵表示的是不确定性的量度
   假设有3600个数,N=3600
            其中最大的数为10,最小的数为-10,那么3600个数中一共会出现P = 21个数
            每个数出现的概率 Pi = Ni / N
            比如 0 一共有1800个,那么 0 的Pi = 0.5
            平均熵 m  = -∑p(xi)log2P(xi)
            假设平均熵 = 2 就表示每个数需要2个bit来表示
            总熵 = m * N 表示这段数据经过压缩后需要多少bit来存储。

        算术编码:符号出现的概率越大对应于区间愈宽,可用较短码字表示;符号出现概率越小对应于区间愈窄,需要较长码字表示。
            举例:比如出现的概率0>1>18,那么查表用表中1来表示0,用表中1 0 来表示1,用表中 1 0 0 1来表示18
            用这种方法来达到压缩的目的。

   k阶指数哥伦布编码:
    例:x = 22, k = 2
编码:
 1. x的二进制为:10110
 2. 右移k位:101
 3. 101+1 = 110
 4. 110是三位数,l(x) = 3 - 1 = 2  前缀: 110
 5. 后缀:l(x)个1 + k个0组成(1100), 然后 10110 - 1100 = 1010
 6. 结果:前缀+后缀 1101010
解码:
    例:1101010
 1. 往后找0
 2. 0之前两个1,  所以l(x) = 2
 3. 找l(x)+k位,结果为4,1010
 4. l(x)个1 + k个0组成(1100)
 5. 1010 + 1100 = 10110 = 22

CABAC:这种基于上下文的自适应的算术编码系统对其语法元素进行编码和解码。
1. 上下文建模 
  在开始编码一个新的片时,都会对每个上下文模型指定相应的一对变量(m, n),并根据m, n的值计算出每个上下文模型对应的初始概率状态state与最大概率符号MPS的值。在开始编码一个新的片时,都会对每个上下文模型指定相应的一对变量(m, n),并根据m, n的值计算出每个上下文模型对应的初始概率状态state与最大概率符号MPS的值。

     由qp,m,n。上下文类型(mv,bcp,等),上下文初始化表来决定state和MPS的值
     state:index into state-table CP  0~63 状态表索引
         MPS:Least Probable Symbol 0/1 CP
       
包括:cat:0~6  选取上下文初始化表  idc:0~2 cabac类型  qp:0~51  Erange=510 区间范围   fbf

2. 算术编码
   获取数据的绝对值和正负号。
  根据左边块和上边块得到ctxinc和数据的综合sum
  判断sum的值来分成好几种情况
  根据ctxinc查表得出state和MPS的值

biari_encode_symbol函数分析

void biari_encode_symbol(EncodingEnvironmentPtr eep, signed short symbol, BiContextTypePtr bi_ct )

主要功能:通过使用相关上下文的概率估计对一个二进制符号进行算术编码。

输入:编码环境指针,待编码符号,上下文管理变量

从编码环境中读取当前区间的下限与范围(range = Erange, low = Elow)。

根据上下文管理变量中的概率状态与当前区间的范围,查表得到LPS的相关子区间范围(rLPS = rLPS_table_64x4[bi_ct->state][(range>>6) & 3])。

如果正在使用CABAC进行编码(cabac_encoding = 1),则上下文管理变量中的计数器加1(bi_ct->count++)。

把所有非零的待编码符号值为1。

计算新的区间范围(range -= rLPS)。

根据待编码符号判断出现的情况是MPS还是LPS。如果是LPS(symbol != bi_ct->MPS),更新区间的下限与范围(low += range,range = rLPS),此时如果概率状态为0,互换LPS与MPS。如果是MPS(symbol = bi_ct->MPS),不再改变已得到的区间。

无论是LPS还是MPS,最后都要更新概率状态(bi_ct->state = AC_next_state_LPS_64 [bi_ct->state],bi_ct->state = AC_next_state_MPS_64[bi_ct->state])。

在biariencode.h中可以查到相应的二维的LPS区间范围表格rLPS_table_64x4[64][4]与概率更新表格AC_next_state_MPS_64[64]与AC_next_state_LPS_64[64]。

如果要输出的区间范围小于28(range < QUARTER),则需作如下重整化处理。

如果下限大于等于29(low >= HALF),使用文件中的put_one_bit_plus_outstanding函数来输出1个或多个比特,然后更新下限(low -= HALF)。

如果下限小于28(low < QUARTER),使用文件中的put_one_bit_plus_outstanding函数来输出1个或多个比特。

如果下限处于28与29之间(QUARTER<=low< HALF),更新下限(low -= QUARTER)。

同时更新区间的下限与范围(low <<= 1, range <<= 1)。

再次检测区间范围,如果小于28,需不断进行重整化处理直至其值大于等于28

最后更新编码环境(Erange = range, Elow = low, eep->C++)。

biari_encode_symbol_eq_prob函数分析

void biari_encode_symbol_eq_prob(EncodingEnvironmentPtr eep, signed short symbol)

主要功能:对一个等概率的二进制符号(p(symbol) = 0.5)进行算术编码。

输入:编码环境指针,待编码符号。

初始化下限(low = (Elow<<1))。

如果编码符号为1(symbol != 0),更新下限(low += Erange)。

接着作重整化处理。

如果下限大于等于210(low >= ONE),使用biariencode.c文件中的put_one_bit_plus_ outstanding函数来输出1个或多个比特,然后更新下限(low -= ONE)。

如果下限小于29(low < HALF),使用biariencode.c文件中的put_one_bit_plus_ outstanding函数来输出1个或多个比特。

如果下限处于29与210之间(HALF<=low<ONE),更新下限(low -= HALF)。

最后更新编码环境(Elow = low, eep->C++)。

biari_encode_symbol_final函数分析

void biari_encode_symbol_final(EncodingEnvironmentPtr eep, signed short symbol)

主要功能:终止前对最后一个符号进行算术编码。

输入:编码环境指针,待编码符号。

从编码环境中读取当前区间的下限与范围(range = Erange -2, low = Elow)。

如果待编码符号为1,更新当前区间的下限与范围(range = 2, low += range)。

接着作重整化处理。(与biari_encode_symbol函数中的重整化处理部分相同。)

最后更新编码环境(Erange = range, Elow = low, eep->C++)。



CAVLC:cavlc用于ziazag扫描后的4*4或者2*2块中变换系数编码,

1、编码系数个数和零序列(coeff_token):
coeff_token = <TotalCoeff, TrailingOnes>;
TotalCoeff = 编码非零系数总数; [0 , 16]
TrailingOnes = 特殊处理的+/-1个数; [0 , 3]  注:拖尾系数,1和-1的个数,如果1和-1的个数超过了3个,以3个计,剩下的1和-1算做非零系数。

2、           编码每个TrailingOne的符号:

倒序,从高频开始向前编码TrailingOne符号,每个符号一位,0为正,1为负,最多为3个。

3、           编码余下非零系数的幅值:

编码顺序:倒序,从高频开始向前编码直到DC系数,每个幅值码字level[i]包含一个幅值前缀level_prefix和一个幅值后缀level_suffix。

level[i]:

Level为实际系数的幅值。但有个例外:

当TrailingOnes<3时,那么被编码的第一个非T1 幅值肯定不是+/-1(否则将算作T1)。这个幅值如果为负就加1(如果为正就减1),例如+/-2 被映射成+/-1, +/-3映射成+/-2进行编码,这样可以用较短的变长码.

levelCode:

 如果level[i]为正, levelCode = (Level[i] << 1) - 2;

如果 level[i]为负, levelCode = -(Level[i] << 1) - 1;

计算level_prefix:

编码时:

level_prefix = levelCode / (1 << suffixLength);

根据level_prefix查标准表9-6得到码字

解码时:

从比特流的当前位置开始读取,计算为0的leading bits数量。0的长度即为level_prefix值,对应关系可察看标准文档中表9-6。

计算suffixLength: 

suffixLength为0-6 比特,其长度是自适应变换的。

suffixLength 增长过程:

1、             初始化suffixLength = 0 ; 如果有超过10个非零系数,并且少于3个TailingOnes,这时suffixLength = 1;

2、           编码最高频的非零系数;

       3、           如果这个系数的幅值大于一定的阈值,则增长suffixLength
          
  
当前suffixLength
增加suffixLength的系数阈值
0
0
1
3
2
6
3
12
4
24
5
48
6
N/A
  


if(suffixLength == 0)更新suffixLength函数如下:

++suffixLength;

else if(level[i] > (3<<suffixLength-1) && suffixLength < 6)

++suffixLength;

计算levelSuffixSize: (后缀是长度为levelSuffixSize的无符号整数)

除了以下两种情况levelSuffixSize等于suffixLength:

1、             level_prefix == 14 && suffixLength == 0 时, levelSuffixSize = 4;

2、           level_prefix >= 15 时,levelSuffixSize = level_prefix – 3;

4、             编码最后一个非零系数前零的个数 

使用VLC编码最高频非零系数前所有零的个数

total_zeros:既为最高非零系数前所有零的个数;编码表见标准表9-7;表9-8;表9-9

5、             编码每个零游程

zerosLeft: 当前系数之前所有的零的个数

run_before: 紧接当前系数前的零个数

[转]CAVLC编码过程详解——Sunrise

CAVLC
编码过程 
假设有一个4*4数据块 

0, 3, -1, 0, 
0, -1, 1, 0, 
1, 0, 0, 0, 
0, 0, 0, 0 

数据重排列:0,3,0,1,-1,-1,0,1,0…… 


1) 初始值设定: 
非零系数的数目(TotalCoeffs) = 5; 
拖尾系数的数目(TrailingOnes)= 3; 
最后一个非零系数前零的数目(Total_zeros) = 3; 
变量NC=1; 
(说明:NC值的确定:色度的直流系数NC=-1;其他系数类型NC值是根据当前块左边4*4块的非零系数数目(NA)当前块上面4*4块的非零系数数目(NB)求得的,见毕厚杰书P120表6.10) 
suffixLength = 0; 
i = TotalCoeffs = 5; 

2) 编码coeff_token: 
查标准(BS ISO/IEC 14496-10:2003)Table 9-5,可得: 
If (TotalCoeffs == 5 && TrailingOnes == 3 && 0 <= NC < 2) 
coeff_token = 0000 100; 
Code = 0000 100; 

3) 编码所有TrailingOnes的符号: 
逆序编码,三个拖尾系数的符号依次是+(0),-(1),-(1); 
即: 
TrailingOne sign[i--] = 0; 
TrailingOne sign[i--] = 1; 
TrailingOne sign[i--] = 1; 
Code = 0000 1000 11; 

4) 编码除了拖尾系数以外非零系数幅值Levels: 
过程如下: 
(1)将有符号的Level[ i ]转换成无符号的levelCode; 
如果Level[ i ]是正的,levelCode = (Level[ i ]<<1) – 2;  
如果Level[ i ]是负的,levelCode = - (Level[ i ]<<1) – 1; 
(2)计算level_prefix:level_prefix = levelCode / (1<<suffixLength); 
查表9-6可得所对应的bit string; 
(3)计算level_suffix:level_suffix = levelCode % (1<<suffixLength); 
(4)根据suffixLength的值来确定后缀的长度; 
(5)suffixLength updata: 
If ( suffixLength == 0 ) 
suffixLength++; 
else if ( levelCode > (3<<suffixLength-1) && suffixLength <6) 
suffixLength++; 

回到例子中,依然按照逆序,Level[i--] = 1;(此时i = 1) 
levelCode = 0;level_prefix = 0; 
查表9-6,可得level_prefix = 0时对应的bit string = 1; 
因为suffixLength初始化为0,故该Level没有后缀; 
因为suffixLength = 0,故suffixLength++; 
Code = 0000 1000 111; 
编码下一个Level:Level[0] = 3; 
levelCode = 4;level_prefix = 2;查表得bit string = 001; 
level_suffix = 0;suffixLength = 1;故码流为0010; 
Code = 0000 1000 1110 010; 
i = 0,编码Level结束。 

5)编码最后一个非零系数前零的数目(TotalZeros): 
查表9-7,当TotalCoeffs = 5,total_zero = 3时,bit string = 111; 
Code = 0000 1000 1110 0101 11; 

6) 对每个非零系数前零的个数(RunBefore)进行编码: 
i = TotalCoeffs = 5;ZerosLeft = Total_zeros = 3;查表9-10: 
依然按照逆序编码 
ZerosLeft =3, run_before = 1 run_before[4]=10; 
ZerosLeft =2, run_before = 0 run_before[3]=1; 
ZerosLeft =2, run_before = 0 run_before[2]=1; 
ZerosLeft =2, run_before = 1 run_before[1]=01; 
ZerosLeft =1, run_before = 1 run_before[0]不需要码流来表示 
Code = 0000 1000 1110 0101 1110 1101; 
编码完毕。

H.264中CAVLC解码过程详解

实例解析

{0 3 -1 0

0 -1 1 0

1 0 0 0

0 0 0 0} NC = 1

编码后得到输出码流为:0000 1000 1110 0101 1110 1101

解码详细过程如下:

1.       根据Coeff_token和NC查表(见标准表9-5),得到非零系数数目TotalCoeffs和拖尾系数数目TrailingOnes

NC = 1选择对应的表,Coeff_token为0000100,查表得到TotalCoeffs=5 TrailingOnes="3"

输出序列:无

2.       解析拖尾系数

由第一步得到拖尾系数有3个,输入拖尾系数符号编码码流011,得到两个拖尾系数由先到后是1,-1,-1

输出序列:1,-1,-1

3.       解析除拖尾系数外的非零系数的幅值(level)

(1)       确定后缀长度SuffixLength

(2)       根据码流查表9-6得到前缀LevelPrefix

(3)       根据前缀和后缀,得到LevelCode=(levelprefix<<suffixlength)+levelsuffix

(4)       Levelcode为偶数 level=(level+2)/2

Levelcode为奇数 level=(-level-1)/2

(5)       根据设定的阈值确定是否update Suffixlegth

回到例子中,按照逆序

i=0,Sufixlegth=0,查表9-6,1对应的前缀levelprefix=0,levelcode=0,计算得到level=1,i++

i=1,sufixlegth=1,查表0010对应的前缀levelprefix=2,计算levelcode=4,level=3,i++

i=2>=TotalCoeffs-TrailingOnes,除拖尾系数外的非零系数解析完毕

输出序列:3,1,-1,-1,1

4.       解析每个非零系数前零的个数

根据TotalCoeffs=5和输入码流111查表9-7得到TotalZeros=3

初始i=TotalCoeffs-1=4,zeroleft=TotalZeros=3,5个非零系数前零的数目解析如下:

i=4,zeroleft=3,根据码流10查表9-10,runbefor=1,输出序列:3,1,-1,-1,0,1

i=3,zeroleft=3-1=2,根据码流1查表runbefore=0,输出序列:3,1,-1,-1,0,1

i=2,zeroleft=2-0=2,根据码流1查表runbefore=0,输出序列:3,1,-1,-1,0,1

i=1,zeroleft=2-0=2,根据码流01查表runbefore=1,输出序列:3,0,1,-1,-1,0,1

i=0,zeroleft=2-1=1,输出序列:0,3,0,1,-1,-1,0,1

5.       解码完毕,将剩下的元素用0补齐,反序排列就可以得到4*4矩阵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值