h.264标准中,CABAC的算术编码部分(9.3.4)只是一个参考,实际编码器中并不一定会按照它来实现,像JM中就有自己的算术编码实现方案。

在上篇文章CABAC中有详细的算术编码描述,在了解算术编码原理的基础上,下面分析JM18.6中的算术编码实现。

下图是JM方案编码的一个例子

结合上图的例子分析,JM的方案在下面几部分跟标准有差异

1. 初始化

把$[0,1)$用$[0,2^{26})$来表示,其中有9个bit为$R$,也就是说在初始化时,有

$R \cdot 2^{17} = (R_{MPS}+R_{LPS})\cdot 2^{17} = 2^{26}$

void arienco_start_encoding(EncodingEnvironmentPtr eep,
unsigned char *code_buffer,
int *code_len )
{
eep->Elow = 0; // low
eep->Echunks_outstanding = 0; //count of consecutive 0xffff
eep->Ebuffer = 0; //store the word
eep->Epbuf = -1; // to remove redundant chunk ^^ count of bytes to output
eep->Ebits_to_go = BITS_TO_LOAD + 1; // to swallow first redundant bit //n = 16 + 1 eep->Ecodestrm = code_buffer;
eep->Ecodestrm_len = code_len; eep->Erange = HALF; //0x1fe 510
}
2. 重归一化

当输入的是LPS时,会选择$R_{LPS}$作为下次进行符号编码的$R$,但是由于标准规定了$R \in [2^8,2^9)$,因此如果$R_{LPS}$小于$2^8$的话,需要对$R_{LPS}$向左移位。不同大小$R_{LPS}$需要移动不同的位数才能符号区间$[2^8,2^9)$,下面是$R_{LPS}$的范围对应的移位表格

RangeLPS Renorm Left Shift Bits
[0,7] 6
[8,15] 5
[16,31] 4
[32,63] 3
[64,127] 2
[128,255] 1

$R_{LPS}$进行左移,意味着作为增量的$2^{n}$需要减去相应的位,即

${R^{i}}_{LPS} \times 2^n = ({R^i}_{LPS}<<k) \times 2^{n-k}$

void biari_encode_symbol(EncodingEnvironmentPtr eep, int symbol, BiContextTypePtr bi_ct )
{
...
else //LPS
{
unsigned int renorm = renorm_table_32[(rLPS >> 3) & 0x1F]; //get k low += range << bl;
range = (rLPS << renorm);
bl -= renorm; // n = n - k if (!bi_ct->state)
bi_ct->MPS ^= 0x01; // switch MPS if necessary bi_ct->state = AC_next_state_LPS_64[bi_ct->state]; // next state if (low >= ONE) // output of carry needed
{
low -= ONE;
propagate_carry(eep);
} if( bl > MIN_BITS_TO_GO ) // n > 0 ,no need to save a word yet
{
eep->Elow = low;
eep->Erange = range;
eep->Ebits_to_go = bl;
return;
}
}
...
}

当输入的是MPS时,会选择$R_{MPS}$作为下次进行符号编码的$R$,但是标准规定了$R\in [2^8,2^9)$,因此如果$R_{MPS}$小于$2^8$的话,需要对$R_{LPS}$左移,不过这里只需要左移一位,因为MPS出现的概率是大于等于0.5的,所以有$2^8 \leqslant 2 \times R_{MPS} < 2^{9}$。最后还需要对n减去1。如果$R_{MPS}$大于或等于$2^8$的话就不需要执行这一步了。

void biari_encode_symbol(EncodingEnvironmentPtr eep, int symbol, BiContextTypePtr bi_ct )
{
...
if ((symbol != 0) == bi_ct->MPS) //MPS
{
bi_ct->state = AC_next_state_MPS_64[bi_ct->state]; // next state if( range >= QUARTER ) // no renorm
{
eep->Erange = range;
return;
}
else
{
range<<=1;
if( --bl > MIN_BITS_TO_GO ) // renorm once, no output //n = n - 1, n>0, no need to save a word yet
{
eep->Erange = range;
eep->Ebits_to_go = bl;
return;
}
}
}
...
}
3. 区间起点的计算方法

最终编码输出的是区间起点$L$,由上图可以看出,只有当输入符号位LPS时,L才会增大,有

$L_{i+1} = L_i + {R^i}_{MPS} \cdot 2^n$

在前面我们已经知道,随着编码的推进,$n$由17往0递减,当$n$为0时,由于$R$只有9bit,对于一共有26bit的$L$,除了进位之外,后面的计算是不会修改到$L$的高位17个bit的部分了。此时可以保存$L$高位的16bit。

保存下来的16bit数据可能会由于后续计算的进位而+1。需要注意的是,如果保存下来的16bit数据是0xffff,就会由于进位而溢出。解决方法是:每当需要保存的16bit数据为0xffff时,用一个计数器记录0xffff连续出现的次数,一旦碰到进位就把这些0xffff对应的位置置零,并且对它们前面的那个非0xffff的16bit数据+1

如果在连续出现的0xffff后并没有进位,而是接着保存非0xffff的16bit数据,这时就能将0xffff及其前面的数据一起输出

$L$的26bit数据中,剩下的10bit数据会被再次进行16bit的左移位(n=16),在下次编码符号时作为$L$继续处理。

void biari_encode_symbol(EncodingEnvironmentPtr eep, int symbol, BiContextTypePtr bi_ct )
{
...
else //LPS
{
low += range << bl;
... if (low >= ONE) // output of carry needed
{
low -= ONE;
propagate_carry(eep); //process carry "+1"
}
...
}
...
//n = 0, save a word
//renorm needed
eep->Elow = (low << BITS_TO_LOAD )& (ONE_M1); //ONE_M1 = 2^26 - 1
low = (low >> B_BITS) & B_LOAD_MASK; // mask out the 8/16 MSBs for output //B_BITS=10 if (low < B_LOAD_MASK) // no carry possible, output now// B_LOAD_MASK=0xFFFF
{
put_last_chunk_plus_outstanding(eep, low); //low != 0xFFFF
}
else // low == "FF.."; keep it, may affect future carry
{
++(eep->Echunks_outstanding); //low == 0xFFFF
}
} static forceinline void propagate_carry(EncodingEnvironmentPtr eep)
{
++(eep->Ebuffer); //+1
while (eep->Echunks_outstanding > 0)
{
put_one_word(eep, 0); //set 0xFFFF 0
--(eep->Echunks_outstanding);
}
} static inline void put_last_chunk_plus_outstanding(EncodingEnvironmentPtr eep, unsigned int l)
{
while (eep->Echunks_outstanding > 0)
{
put_one_word(eep, 0xFFFF); //it is 0xFFFF, no carry would affect them
--(eep->Echunks_outstanding);
}
put_one_word(eep, l); //new word
}

算术编码JM实现的更多相关文章

  1. 算术编码Arithmetic Coding-高质量代码实现详解

    关于算术编码的具体讲解我不多细说,本文按照下述三个部分构成. 两个例子分别说明怎么用算数编码进行编码以及解码(来源:ARITHMETIC CODING FOR DATA COIUPRESSION): ...

  2. X264-libx264编码库

    X264编码库libx264实现真正的视频编解码,该编解码算法是基于块的混合编码技术,即帧内/帧间预测,然后对预测值变换.量化,最后熵编码所得. 编码帧的类型分为I帧(x264_type_i).P帧( ...

  3. H264编码技术

    H.264的目标应用涵盖了眼下大部分的视频服务,如有线电视远程监控.交互媒体.数字电视.视频会议.视频点播.流媒体服务等.H.264为解决不同应用中的网络传输的差异.定义了两层:视频编码层(VCL:V ...

  4. 【转载】视频编码(H264概述)

    一视频编码介绍 1.1 视频压缩编码的目标 1)保证压缩比例 2)保证恢复的质量 3)易实现,低成本,可靠性 1.2 压缩的出发点(可行性) 1)时间相关性 在一组视频序列中,相邻相邻两帧只有极少的不 ...

  5. H264 编码详解

    H264 编码详解(收集转载) (1)       x264_param_default( x264_param_t *param ) 作用: 对编码器进行参数设定 cqm:量化表相关信息 csp: ...

  6. 【视频编解码·学习笔记】7. 熵编码算法:基础知识 & 哈夫曼编码

    一.熵编码概念: 熵越大越混乱 信息学中的熵: 用于度量消息的平均信息量,和信息的不确定性 越是随机的.前后不相关的信息,其熵越高 信源编码定理: 说明了香农熵越信源符号概率之间的关系 信息的熵为信源 ...

  7. 视频基础知识:浅谈视频会议中H.264编码标准的技术发展

    浅谈视频会议中H.264编码标准的技术发展 浅谈视频会议中H.264编码标准的技术发展 数字视频技术广泛应用于通信.计算机.广播电视等领域,带来了会议电视.可视电话及数字电视.媒体存储等一系列应用,促 ...

  8. JPEG编码(一)

    JPEG编码介绍. 转自:http://blog.chinaunix.net/uid-20451980-id-1945156.html JPEG(Joint Photographic Experts ...

  9. H264编码技术[3]

    H.264的目标应用涵盖了目前大部分的视频服务,如有线电视远程监控.交互媒体.数字电视.视频会议.视频点播.流媒体服务等.H.264为解决不同应用中的网络传输的差异.定义了两层:视频编码层(VCL:V ...

随机推荐

  1. ServletContext对象--三大域对象

    Servlet三大域对象的应用 request.session.application(ServletContext) ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器 ...

  2. yii2 验证码的使用

    @see  http://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html 以下根据 MVC 模型的顺序来添加代码 1. model 层, 或 ...

  3. Installing the .NET Framework 4.5, 4.5.1

    This article provides links for installing the .NET Framework 4.5 and 4.5.1 on your computer.  If yo ...

  4. Spring Mvc session拦截器实现

    Spring Mvc拦截器实现session过期跳转到登录页面 配置拦截器 <mvc:interceptors> <mvc:interceptor> <mvc:mappi ...

  5. 路E施工管理ERP系统

      前  景   目前公路工程由于点多.线长.面广.周期久.投资大等原因,管理很难到位,施工过程中存在着大量问题: 规章制度欠缺或不健全,即便是有好的规章制度,在施工过程中也往往形同虚设,不能与现场施 ...

  6. GridView分页的实现

    要在GridView中加入 //实现分页 AllowPaging="true" //一页数据10行 PageSize="10" // 分页时触发的事件 OnPa ...

  7. 从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作

    转载:http://space.itpub.net/17007506/viewspace-615570 笔者在闲暇时,偶尔会登录腾讯QQGame玩玩升级游戏.这确实是一款非常优秀的软件作品,腾讯的开发 ...

  8. Android NDK学习总结

    一.android NDK编程步骤 java文件中声明native方法. android工程根目录新建jni文件夹. 调用javah命令为第一步声明的native方法生成相应的.h头文件. 通过win ...

  9. Masonry等比缩放

    第一种: CGFloat width = CGRectGetWidth([[UIScreen mainScreen] bounds]);         CGFloat aspectRatio = 1 ...

  10. Fast Report Data Filter

    使用Data Filter两种方式:一种是 直接在Filter 属性里写表达式 ,另外一种就是在beforePrint 事件里写方法. 今天开发时遇到了一个Filter的问题,不知道是不是fast r ...