H.264中,4x4的像素块经过变换和量化之后,低频信号集中在左上角,大量高频信号集中在右下角。左边的低频信号相对数值较大,而右下角的大量高频信号都被量化成0、1和-1;变换量化后的残差信息有一定的统计特性和规律。

  CAVLC(Context-based Adaptive Variable-Length Code):基于上下文的可变长度编码,是H.264中进行4x4像素块进行熵编码的方法,基本(baseline)档次中只能使用CAVLC,只有主要档次和扩展档次才能使用CABAC(见笔记:熵编码之CABAC)。

一、对于经过Zigzag扫描(Z扫描)后的4x4像素残差,编码过程包括:

A、非零系数数目(TotalCoeffs)和拖尾系数数目(TrailingOnes)的编码:

  拖尾系数:就是指Z扫描后,末尾高频信号中出现连续1或-1的个数(中间可以隔任意多个0),拖尾系数最多有5个。当连续1或-1的个数超过3个,只有最后3个1或-1是拖尾系数,其他的当作普通的非零系数。

  TotalCoeffs和TrailingOnes通过查表方式进行编码,H.264针对TotalCoeffs和TrailingOnes,提供了4张变长表和1张定长表(部分见附表9-5)。编码表的选择是由NC确定的,NC的值是由上下文信息确定的。对于色度直流信号,NC=-1;对于其他的NC,根据当前块左边4x4块和上面4x4块的非零系数的个数A和B决定。如表一:其中X表示该块与当前块属于同一slice且可用。根据NC选择编码表的策略如表二:

表一
A(左边块) B(上面块) NC
X   X     (A+B)/2
X   A
X B
0
表二
NC 编码表编号
0,1 变长表1
2,3 变长表2
4,5,6,7 变长表3
>=8 定长表
-1 变长表4

  通过NC确定所选择的表之后,将编码的二进制输出。

B、每一个拖尾系数的符号正负性编码(按照Z扫面结果的逆序编码):

  按照逆序对每一个拖尾系数的符号进行编码,用0表示1(正)、1表示-1(负),将编码结果输出

C、除拖尾系数外的每一个非零系数幅值(Level,包含正负号信息)编码(按照Z扫描结果的逆序编码):

  拖尾系数幅值的编码包括前缀Level_prefix和后缀Level_suffix。另外编码过程中suffixLength基于上下文信息,根据Level_suffix和Level实时更新。具体过程如下:

  1、设置初始SuffixLength:

    当TotalCoeffs>10且TrailingOnes<=1时,SuffixLength设为1;否则设为0;

  2、将有符号的Level转换成无符号的LevelCode:

    若Level > 0:LevelCode = (Level << 1)-2;

    若Level < 0:LevelCode = -(Level << 1) - 1;

    这样解码时,就可以根据LevelCode的奇偶性判断Level的正负性,从在根据LevelCode解码出有符号的Level。

  3、计算Level_prefix 和 Level_suffix:

    Level_prefix = LevelCode / (1 << SuffixLength);

    Level_suffix = LevelCode% (1 << SuffixLength);

  4、编码Level_prefix和Level_suffix:

    编码Level_prefix是通过查标准表9-6(部分见附表9-6),编码得到的是前缀码,所以解码时可以即使、唯一译码。Level_suffix的编码就是Level_suffix的二进制无符号形式。然后将Level_prefix和Level_suffix的编码结果依次输出,但当SuffixLength=0时,没有Level_suffix,不需要输出。

  5、更新SuffixLength的值,回到步骤2继续编码下一个非零系数。更新过程可以用下面代码表示:

 if(SuffixLength == )
SuffixLength++;
else if (abs(Level) > ( << (SuffixLength -)) && SuffixLength < )
SuffixLength++;

  即:当SuffixLength为0时,SuffixLength加1;当SuffixLength达到6之后不再更新SuffixLength;当SuffixLength在1和6之间,如果当前已编码的非零系数的绝对值(abs(Level))大于给定的阈值S,那么SuffixLength增1,其中阈值S的大小为:S = 3 * 2 ^ (SuffixLength-1) = 3<<(SuffixLength-1)。

D、最后一个非零系数前0的数目(TotalZeros)编码:查找标准表9-7(部分见附表9-7)~9-9

E、每一个非零系数前连续0的数目(RunBefore)编码(按照Z扫面结果的逆序编码):查找标准表9-10(部分见附表9-10)

  编码过程中,ZerosLeft表示当前编码非零系数左边所有0的个数,对于最后一个(逆序的最后一个)非零系数前0的个数不需要编码。  

二、例如:对于4x4的残差块,如下图:

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

  经过Z扫描得到序列:0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0。

  对该序列进行编码如下:

  1、初始值设定:

    TotalCoeffs = 5;TrailingOnes = 3;TotalZeros = 3;假设 NC = 1;SuffixLength = 0;最终编码输出码流为out。

  2、编码TotalCoeffs和TrailingOnes:

    查附表9-5得,TotalCoeffs = 5、TrailingOnes = 3和0 <= NC <2时;编码结果为:0000 100。

    此时out = 0000 100。

  3、编码拖尾系数符号:

    拖尾系数为1,-1,-1(扫描逆序),对应的符号编码为0,1,1。所以此时out = 0000 1000 11。

  4、编码每个非拖尾非零系数的幅值Level:

    需要编码的非零系数有1,3(Z扫描逆序),初始i=2,过程如下:

    Level[i--] = 1; 得到LevelCode = 0,Level_prefix = 0,没有Level_suffix(因为SuffixLength=0),查表9-6得编码结果为1。此时out = 0000 1000 111。

    然后更新SuffixLength,SuffixLength++得SuffixLength=1;

    Level[i--] = 3;得到LevelCode=4,Level_prefix = 2,Level_suffix = 0,查表9-6得Level_prefix编码结果为001,然后Level_suffix的二进制表示为0。

    out = 0000 1000 1110 010。此时i=0,此步骤编码结束。

  5、编码TotalZeros:查表9-7得编码结果为编码结果为111,此时out = 0000 1000 1110 0101 11。

  6、编码每一个非零系数前的连续o的数目:有四个非零系数前连续0的个数要编码,初始i=5。查表9-10的编码过程如下:

    ZerosLeft = 3, RunBefore = 1,Level[i--]=1,编码结果为10;

    ZerosLeft = 2, RunBefore = 0,Level[i--]=-1,编码结果为1;

    ZerosLeft = 2, RunBefore = 0,Level[i--]=-1,编码结果为1;

    ZerosLeft = 2, RunBefore = 1,Level[i--]=1,编码结果为01;

    ZerosLeft = 1, RunBefore = 1,Level[i--]=3,此时对应第一个非零系数,不需要编码;

  最后输出结果为 out = 0000 1000 1110 0101 1110 1101。

  相应解码如下:读入out = 0000 1000 1110 0101 1110 1101

  1、解码TotalCoeffs和TrailingOnes:

    初始NC=1,根据表9-5,可以从0000100中解码得到TotalCoeffs=5;TrailingOnes=3。这里因为表9-5的编码可以及时、唯一译码,所以遇到的第一个合法的01串就是TotalCoeffs和TrailingOnes编码的结果。

  2、解码拖尾系数:此时out = 0 1110 0101 1110 1101

    由TrailingOnes=3知,有3个拖尾系数,所以对应的正负号编码为011,所以3个拖尾系数是1,-1,-1。所以解码输出 in是 -1,-1,1(因为编码是逆序的)。

  3、解码除拖尾系数外的非零系数:此时out = 10 0101 1110 1101

    由TotalCoeffs=5;TrailingOnes=3知除拖尾系数外,还有两个非零系数。初始SuffixLength=0,所以根据表9-6(及时、唯一译码)解码如下:

    SuffixLength=0  查表得比特串为1,level_prefix=0,LevelCode=0(偶数),Level=1,没有Level_suffix;(消耗码流0)

    SuffixLength=1  查表得比特串为001,level_prefix=2,LevelCode=4(偶数),Level=3,Level_suffix=0;(消耗码流0010)  

    所以输出 in是 3,1,-1,-1,1

  4、解码每个非零系数前0的个数:此时out = 1 1110 1101

    TotalCoeffs = 5;根据表9-7解码得 TotalZeros=3,对应码流:111。

    然后查表9-10,得到每一个非零系数前连续0的个数,过程如下:此时out = 10 1101

    TotalZeros=3,根据码流查表得10对应 RunBefore=1,in是 3,1,-1,-1,0,1

   TotalZeros=3-1=2,根据码流查表得1对应 RunBefore=0,in是 3,1,-1,-1,0,1

    TotalZeros=2-0=2,根据码流查表得1对应 RunBefore=0,in是 3,1,-1,-1,0,1

    TotalZeros=2-0=2,根据码流查表得01对应 RunBefore=1,in是 3,0,1,-1,-1,0,1

    TotalZeros=2-1=1,out码流解码完,所以TotalZeros=1表示3之前的0数目,in是 0,3,1,-1,-1,0,1

    然后在结尾补0组成16个残差系数,得解码结果0,3,1,-1,-1,0,1,0,0,0,0,0,0,0,0,0

三、附表:

  

H.264学习笔记5——熵编码之CAVLC的更多相关文章

  1. H.264学习笔记1——相关概念

    此处记录学习AVC过程中的一些基本概念,不定时更新. frame:帧,相当于一幅图像,包含一个亮度矩阵和两个色度矩阵. field:场,一帧图像,通过隔行扫描得到奇偶两场,分别称为顶场和底场或奇场和偶 ...

  2. H.264学习笔记之一(层次结构,NAL,SPS)

    一 H.264句法 1.1元素分层结构 H.264编码器输出的Bit流中,每个Bit都隶属于某个句法元素.句法元素被组织成有层次的结构,分别描述各个层次的信息. 图1 H.264分层结构由五层组成,分 ...

  3. H.264学习笔记

    1.帧和场的概念 视频的一场或一帧可用来产生一个编码图像.通常,视频帧可以分成两种类型:连续或隔行视频帧.我们平常看的电视是每秒25帧,即每秒更换25个图像,由于视觉暂留效应,所以人眼不会感到闪烁.每 ...

  4. 02:H.264学习笔记

    H.264组成 1.网络提取层 (Network Abstraction Layer,NAL) 2.视讯编码层 (Video Coding Layer,VCL) a.H.264/AVC影像格式阶层架构 ...

  5. H.264学习笔记6——指数哥伦布编码

    一.哥伦布码 哥伦布码就是将编码对象分能成等间隔的若干区间(Group),每个Group有一个索引值:Group Id. >对于Group Id采用二元码编码: >对于Group内的编码对 ...

  6. H.264学习笔记4——变换量化

    A.变换量化过程总体介绍 经过帧内(16x16和4x4亮度.8x8色度)和帧间(4x4~16x16亮度.4x4~8x8色度)像素块预测之后,得到预测块的残差,为了压缩残差信息的统计冗余,需要对残差数据 ...

  7. H.264学习笔记3——帧间预测

    帧间预测主要包括运动估计(运动搜索方法.运动估计准则.亚像素插值和运动矢量估计)和运动补偿. 对于H.264,是对16x16的亮度块和8x8的色度块进行帧间预测编码. A.树状结构分块 H.264的宏 ...

  8. H.264学习笔记2——帧内预测

    帧内预测:根据经过反量化和反变换(没有进行去块效应)之后的同一条带内的块进行预测. A.4x4亮度块预测: 用到的像素和预测方向如图: a~f是4x4块中要预测的像素值,A~Q是临块中解码后的参考值. ...

  9. 每天进步一点点------H.264学习 (一)

    分三个阶段学习1.第一个阶段: 学习H.264,首先要把最基本最必要的资料拿在手里.这些资料包括:标准文档+测试模型+经典文章,在本FTP中能找到.首先看 <H.264_MPEG-4 Part ...

随机推荐

  1. 发布Java桌面程序

    我拿了一份桌面工具的开源代码,修修改改,在elipse上运行,感觉良好,但到了发布应用程序,就傻眼了.我居然不知道咋发布! 呵呵,不愧是Java小白! 如果是微软阵营,直接就编译成exe了.但java ...

  2. 2016/04/26 流程 数据库lcdb 四个表 1,用户表users 2,流程表(设定有哪些流程)liucheng 3,流程发起者表(记录谁发起到哪里) 4,流程经过的人员表 flowpath (order排序)

    流程:      十一 个页面 1,denglu.php(登录) <!DOCTYPE html> <html lang="en"> <head> ...

  3. 2016-2017 ACM-ICPC Southwestern European Regional Programming Contest (SWERC 2016) D.Dinner Bet 概率DP+排列组合

    题目链接:点这里 题意: 1~N标号的球 现在A有C个,B有C个 每次可以随机得到D个不同的球(1~N);问你A或B中的C个球都出现一次的 期望次数 题解: dp[i][j][k]表示 随机出现了i个 ...

  4. java8--NIO(java疯狂讲义3复习笔记)

    NIO采用内存映射文件的方式处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念),通过这种方式来进行输入输出比传统的 ...

  5. Common non-standard response fields

    https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#cite_note-52 Common non-standard response f ...

  6. Linux文件锁【转】

    本文转载自:http://blog.csdn.net/dragon_li_chen/article/details/17147911 一.文件锁的分类: 翻阅参考资料,你会发现文件锁可以进行很多的分类 ...

  7. 第十八周 Leetcode 72. Edit Distance(HARD) O(N^2)DP

    Leetcode72 看起来比较棘手的一道题(列DP方程还是要大胆猜想..) DP方程该怎么列呢? dp[i][j]表示字符串a[0....i-1]转化为b[0....j-1]的最少距离 转移方程分三 ...

  8. Python基础第八天

    一.内容

  9. vue 生命周期钩子 过滤器 计算属性

    每一个Vue实例在被创建之前都要经过一系列的初始化过程.例如,实例需要配置数据观测.编译模板.挂载实例到DOM,然后在数据变化时更新DOM,在这个过程中,实例也会调用一些生命周期钩子,这就给我们提供了 ...

  10. UI: 网易新闻实现

      #pragma mark-(AppDelegate.H文件)-------------------------------------------------------------------- ...