算术编码JM实现
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实现的更多相关文章
- 算术编码Arithmetic Coding-高质量代码实现详解
关于算术编码的具体讲解我不多细说,本文按照下述三个部分构成. 两个例子分别说明怎么用算数编码进行编码以及解码(来源:ARITHMETIC CODING FOR DATA COIUPRESSION): ...
- X264-libx264编码库
X264编码库libx264实现真正的视频编解码,该编解码算法是基于块的混合编码技术,即帧内/帧间预测,然后对预测值变换.量化,最后熵编码所得. 编码帧的类型分为I帧(x264_type_i).P帧( ...
- H264编码技术
H.264的目标应用涵盖了眼下大部分的视频服务,如有线电视远程监控.交互媒体.数字电视.视频会议.视频点播.流媒体服务等.H.264为解决不同应用中的网络传输的差异.定义了两层:视频编码层(VCL:V ...
- 【转载】视频编码(H264概述)
一视频编码介绍 1.1 视频压缩编码的目标 1)保证压缩比例 2)保证恢复的质量 3)易实现,低成本,可靠性 1.2 压缩的出发点(可行性) 1)时间相关性 在一组视频序列中,相邻相邻两帧只有极少的不 ...
- H264 编码详解
H264 编码详解(收集转载) (1) x264_param_default( x264_param_t *param ) 作用: 对编码器进行参数设定 cqm:量化表相关信息 csp: ...
- 【视频编解码·学习笔记】7. 熵编码算法:基础知识 & 哈夫曼编码
一.熵编码概念: 熵越大越混乱 信息学中的熵: 用于度量消息的平均信息量,和信息的不确定性 越是随机的.前后不相关的信息,其熵越高 信源编码定理: 说明了香农熵越信源符号概率之间的关系 信息的熵为信源 ...
- 视频基础知识:浅谈视频会议中H.264编码标准的技术发展
浅谈视频会议中H.264编码标准的技术发展 浅谈视频会议中H.264编码标准的技术发展 数字视频技术广泛应用于通信.计算机.广播电视等领域,带来了会议电视.可视电话及数字电视.媒体存储等一系列应用,促 ...
- JPEG编码(一)
JPEG编码介绍. 转自:http://blog.chinaunix.net/uid-20451980-id-1945156.html JPEG(Joint Photographic Experts ...
- H264编码技术[3]
H.264的目标应用涵盖了目前大部分的视频服务,如有线电视远程监控.交互媒体.数字电视.视频会议.视频点播.流媒体服务等.H.264为解决不同应用中的网络传输的差异.定义了两层:视频编码层(VCL:V ...
随机推荐
- spring beans源码解读之--XmlBeanFactory
导读: XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力.从本质上讲,XmlBeanFactory等 ...
- 用NodeJs实现延迟调用,规避定时任务的闭包问题
很多人在用NodeJs的setTimeout(callback, delay[, arg][, ...])编写定时任务时,习惯上直接操作callback外部的对象object(闭包的特点).这样做有一 ...
- Vim程序编辑器
Vim的三种模式: 1) 一般模式 以 vi 打开一个档案就直接进入一般模式了(这是默认的模式).在这个模式中, 你可以使用『上下左右』按键来移动光标,你可以使用『删除字符』或『删除整行』来处理档案内 ...
- Python爬虫获取知乎图片
前段时间想抓点知乎问题中的图片,了解了下爬虫,发现还是Python的简单方便,于是做了点尝试. #coding=utf-8 import urllib import re def getHtml(ur ...
- 单点登录SSO原理
最近接触了一点单点登录的知识,有一点理解,记录一下.有些问题并没有找到完美的解决方法,还需要找点已有框架来看看. 欢迎留言探讨. 1 概念 1.1 概念及理解 有一个网上广为流传的 ...
- 在picture library中取某一图片的大图、小图
public static string GetPicThumbnail(SPFile file, string type) { string thumbnail = "" ...
- jmeter中webdriver插件,进行自动化压测
1.下载JMeterPlugins-WebDriver-1.1.2 2.将JMeterPlugins-WebDriver-1.1.2\lib\ext中的*.jar拷贝到D:\apache-jmeter ...
- JS 同源策略
对于任何基于WEB的应用,最重要的就是安全性.JS中有各种安全检查以防止恶意脚本攻击你的机器,其中一些特定的安全手段在各种浏览器中都有采用.如:Mozilla有个完全独特的完全模型,涉及到了签署脚本和 ...
- string的一些更改发
/* String 类: 1.1字符串的长度 int c =对象.length(); 1.2字符串某个字符的位置 int index=对象.indexOf("字母") ...
- Observer 模式
Observer模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变.最常见的一个例子就是:对同一组数据进行 ...