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. WPF组件开发之组件的基类

    之前在网上看到很多关于组件开发的资料,但真正可以用到框架内的却很少.今天贴出自己做的组件,并适合大部分框架的代码. 组件开发需要先做出组件的基类,然后由其他的各类组件去继承这个基类,下面是组件基类的代 ...

  2. ARCGIS二维三维互动

    当对三维模型进行操作时(如导航.平移)二维地图自动跟进. private void Synckron() { m_pGlobe = this._GlobeControl.Globe; m_pMap = ...

  3. Base64工具类

    public final class AbBase64 { /** The Constant base64EncodeChars. */ private static final char[] bas ...

  4. 【Android】isEmpty()

    对字符串进行非空判断,可以一次性进行两种空值的判断.当传入的字符串等于null或者等于空字符串的时候这个方法都会返回true,从而不需要单独去判断这两种空值,再利用逻辑运算符连接起来.

  5. div a块状布局

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  6. Ganglia3.6.0,nginx+php搭建gweb,监控Hadoop2.2 和 Hbase0.98.1

    环境:CentOS6.5 Hadoop2.2.0 Hbase0.98.1 服务端(master): 安装 ganglia ganglia-devel ganglia-gmetad ganglia-gm ...

  7. NSAttributedString用法

    以前看到这种字号和颜色不一样的字符串,想出个讨巧的办法就是“¥150”一个UILabel,“元/位”一个UILabel.今天翻看以前的工程,command点进UITextField中看到[attrib ...

  8. LCS最长公共子序列HDU1159

    最近一直在学习算法,基本上都是在学习动态规划以及字符串.当然,两者交集最经典之一则是LCS问题. 首先LCS的问题基本上就是在字符串a,b之间找到最长的公共子序列,比如 YAOLONGBLOG 和 Y ...

  9. 中文翻译:pjsip教程(三)之ICE stream transport的使用

    1:pjsip教程(一)之PJNATH简介 2:pjsip教程(二)之ICE穿越打洞:Interactive Connectivity Establishment简介 3:pjsip教程(三)之ICE ...

  10. __init__ __new__区别

    请运行代码: class A: def __init__(self): print "A.__init" def __new__(self): print "A.__ne ...