正所谓工欲善其事,必先利其器;我们下面要解析vps,sps,pps,少不了的会有两点:1、对照spec看语法元素;2根据描述解析语法元素;那么这里我要总结的是,如何解析这些语法元素;因为vps,sps,pps的信息,单独看每个有啥含义,在这里看是没有太多用处的;目前我们只能说了解个大概;因为这些信息是为了后面的解码服务的;所以这里我们要看具体是怎么解析的;
首先这里我要粘贴一下书上的描述子的描述(这里有点绕。。。但是没错):
那么这里面就出现了几个概念需要理解:
1、基于上下文的自适应的二元算数编码:ae(v)这里指的是可以理解为对于265就是cabac编码
2、其他的就是哥伦布编码以及其他等等;
所以,这节最主要的还是要看熵编码这一块的知识,就知道如何进行熵解码
一、
首先我们应该先看看普通的变长编码(非定长的,VLC,看一下首字母就知道怎么回事了)是怎么编码的;来引出我们的概念:
常见的就是哈夫曼编码,说道哈夫曼编码,其实在这里了解一下即可;说道哈夫曼编码就离不开哈夫曼树,直接举例,会比较好理解一些:
假设ABCD,加权是0.2,0.3,0.1,0.4,那么我们怎么画哈夫曼树呢;首先我们找最小的两个;也就是A,C。
小概率的放到左边,大概率的放到右边,那么就会合成一个E,E就是0.3,然后E再和B对比,然后组成F,F是0.6,然后再和D对比那么D放到左边,F放到右边,所以就会有下图:
画的太丑了,但是很明显就是编码后,A是111,C是110,B是10,D是0,所以比较简单;
举例如果是ACBA,那么编码出来的就是11111010111
二、
上面是代表的是变长编码;但是算数编码是不一样的;变长编码,如哈夫曼编码,那么会是要对每个码字都要进行率先给一个码字;如A是111,C是110,那么每个字符,至少也得分配一个码字;但是算数编码不是的;他是要对整个码率进行编码;这就出现了概率空间的概念了举个例子,比如AA,我可能用1一个bit就代替了,那么如果使用边长编码的话,那么就会出现1111116个bit,这样是不是感觉算数编码很节省?
上面说了这么多,其实没啥用,如果不举例,其实什么都看不懂;我们还是按照上面的概率,上面ACBA举例;那么如果是算数编码,那么应该是怎么样的呢?
如下图:
1、首先我们说,第一个码字是A,那么就是落在的概率区间就是[0,0.2]
2、然后第二个码字是C,那么C是在A的基础之上的,所以C落在[0.1~0.12],C过后,概率区间只有0.02大小了
3、然后是B,那么B是在0.02x0.2以后,所以结合前面就是0.104~0.11之间;此时,概率区间只有0.006大小了
4、最后是A,那么A又是从0开始的,所以输出的码率下限就是0.104,那么我们输出0.104,就能代表是ACBA这个码字了
我们整理为公式的话输出的就是0+0.2x0.5+0.2x0.1x0.2 = 0.104
那比如如果我们现在有了一个小数,能否解码出来呢?答案是必须且唯一的;要不学习算数编码,无法解码有什么用呢?
假设是一个0.105,那么0.105在0~0.2之间,也就是说第一个是A
然后我们现在看编码的这个公式0+0.2x0.5+0.2x0.1x0.2 = 0.104
说明什么呢?说明下一步主要看第二加的内容区间。所以为(0.105-0)/0.2 = 0.525,是C
以此类推,(0.525-0.5)/0.1 = 0.25,是B
(0.25-0.2)/0.3是0.166,是A
也就是最终为ACBA;
那么我们上面编码不是0.104嘛,为啥0.105也是ACBA呢?
因为ACBA最后一个是落在0.006x0.2 = 0.0012也就是说,在0.104~0.1052之间的任意数都是0可以的;
三、
下面要看一下哥伦布指数编码;哥伦布指数编码,属于变长编码的范畴;那么我们从上面可以看出我们学习了变长编码,算数编码;为什么,我们要回过头来,看哥伦布指数编码呢?
首先,我们要看哥伦布指数编码在265中的作用:
1、slice以上大多会用到哥伦布指数编码(sps,vps,pps,ue(v),se(v))
2、在slice层,有一个参数要用1阶哥伦布指数编码去二值化
这两点是不是很晕?其实还好,我大体描述一下是什么意思:
对于那些sps,vps这些,我们需要哥伦布编码的方式编码;就是这些值用哥伦布指数编码编成0101的码字即可;
但是对于slice编码的话,就像上面描述的;用变长编码是不是用的码字特别多?是不是需要用算数编码?但是算数编码需要三步:1、二值化;2、建立概率模型;3、算数编码;此时,变长编码。也就是1阶哥伦布指数编码就会用到了
但是这里我得说一句,一阶哥伦布指数编码去二值化,情况很少,大部分我们不用这种方式;slice层只有一个地方会用到;所以了解即可;
终于要进入正题了;就是如何进行哥伦布指数编码:
首先要看一下,无符号0阶哥伦布指数编码:
这里我是在不想写那么多了,但是我只是总结一下;其中,图片中codenum指的是要编码的语法元素;如果要进行哥伦布指数编码,那么有两部分组成:
1、前缀0,前缀0的话,计算就是M那个公式;
2、实际的码字,就是codenum+1对应的二进制;
如果编程的话,没有以2为底的函数的话,可以用两个以10为底的log相除
是不是很简单,下面还有表格,都是例子;
然后再看有符号的哥伦布指数编码
其中V是指的要编码的语法元素;是不是很巧妙?通过奇偶就能区分了正负;可以看出,正数都是变成了奇数后再去编码,负数和0变成了偶数去编码;
最后看一下1阶哥伦布指数编码,再强调一点,这个只是为了二值化,所以会用1阶哥伦布指数编码
这里贴了一段程序,如下:
代码是不是很容易就可以看懂?
其中synval代表的是要编码的语法元素;要首先求一个绝对值;然后定义个结束的标志位;然后k初始化为1,因为是1阶哥伦布指数编码;然后循环输出0和1,然后输出几个0,或者几个1,最终结束;
举例14,按照上述的逻辑输出为11100000,也就是224
讲到这里,我们其实自适应的二元算数编码还没有接触。但是起码;sps这些你能解析了;因为他们大部分是哥伦布编码;这节写的有点多,所以后面再写cabac