CAVLC即基于上下文的自适应变长编码。H.264标准中使用CAVLC对4*4模块的亮度和色度残差数据进行编码。
CAVLC的全称是Context-Adaptive Varialbe-Length Coding,即基于上下文的自适应变长编码。CAVLC的本质是变长编码,它的特性主要体现在自适应能力上,CAVLC可以根据已编码句法元素的情况动态的选择编码中使用的码表,并且随时更新拖尾系数后缀的长度,从而获得极高的压缩比。H.264标准中使用CAVLC对4×4模块的亮度和色度残差数据进行编码。
在H.264标准编码体系中,视频图像在经过了预测、变换及量化编码后表现出如下的特性:4×4块残差数据块比较稀疏,其中非零系数主要集中在低频部分,而高频系数大部分是零;量化后的数据经过zig-zag扫描,DC系数附近的非零系数值较大,而高频位置上的非零系数值大部分是+1和-1;相邻的4×4块的非零系数的数目是相关的。CAVLC就是利用编码后残差数据的这些特性,通过自适应对不同码表的选择,利用较少的编码数据对残差数据进行无损的熵编码,进一步减少了编码数据的冗余和相关性,提高了H.264的压缩效率。
视频图像在经过预测、变换和量化编码后,需要经过Zig-zag扫描和重新的排序过程,为后序的CAVLC编码进行准备。一个残差数据块的CAVLC熵编码的流程如图所示:
1、TotalCoeffs和TrailingOnes的编码
从码流的起始位置开始计算整个编码块中非零系数的数目(TotalCoeffs),非零系数的数目为从0-16,非零系数的数目被赋值给变量TotalCoeffs。
拖尾系数是指码流中正或者负1的个数(+/-1)。拖尾系数的数目(TrailingOnes)被限定在3个以内,如果+/-1的数目超过3个,则只有最后3个被视为拖尾系数,其余的被视为普通的非零系数,拖尾系数的数目被赋值为变量TrailingOnes。
2、判断计算nC值
nC(Number Current 当前块值)值的计算集中体现了CAVLC的基于上下文的思想,通过nC值选择不同H.264标准附录CAVLC码表。
3、查表获得coeff_token编码
根据之前编码和计算过程所得的变量TotalCoeffs、TrailingOnes和nC值可以查H.264标准附录CAVLC码表,即可得出coeff_token编码序列。
4、编码每个拖尾系数的符号:前面的coeff_token编码中已经包含了拖尾系数的总数,还需进一步对拖尾系数的符号进行编码。由于拖尾系数符合为正(+)或负(-),因此,在H.264标准中规定用0表示正1(+1)、1表示负1(-1)。当拖尾系数的数目超过3个只有最后3个被认定为拖尾系数,因此对符号的编码顺序应按照反向扫描的顺序进行。
5、编码除拖尾系数之外的非零系数的幅值(Levels)
非零系数的幅值(Levels)由两个部分组成:前缀(level_prefix)和后缀(level_suffix)。levelCode、levelSuffixsSize和suffixLength是编码过程中需要使用的三个变量,其中levelCode是中间过程中用到的无符号数,levelSuffixsSize表示后缀长度位数,suffixLength代表Level的码表序号。
6、编码最后一个非零系数前零的数目(TotalZeros)
TotalZeros指的是在最后一个非零系数前零的数目,此非零系数指的是按照正向扫描的最后一个非零系数。因为非零系数数目(TotalCoeffs)是已知,这就决定了TotalZeros可能的最大值。根据TotalCoeffs值,H.264标准共提供了25个变长表格供查找,其中编码亮度数据时有15个表格供查找,编码色度DC 2×2块(4:2:0格式)有3个表格、编码色度DC 2×4块(4:2:2格式)有7个表格。
7、编码每个非零系数前零的个数(RunBefore)
在CAVLC中,变量 ZerosLeft表示当前非零系数左边的所有零的个数,ZerosLeft的初始值等于TotalZeros。每个非零系数前零的个数(RunBefore)是按照反序来进行编码的,从最高频的非零系数开始。H.264标准中根据不同ZerosLeft和RunBefore,构建了RunBefore编码表格供编码查找使用。根据表格每编码完一个RunBefore,对ZerosLeft的值进行更新,继续编码下一个RunBefore,直至全部完成所有非零系数前零的个数的编码。当ZerosLeft=0即没有剩余0需要编码时或者只有一个非零系数时,均不需要再进行RunBefore编码。
CAVLC熵解码是上述CAVLC熵编码的逆过程,CAVLC熵解码的输入数据是来自片层数据的比特流,解码的基本单位是一个4×4的像素块,输出为包含4×4块每个像素点所有幅值的序列。CAVLC解码步骤如下:
1. 初始化所有的系数幅值
2. 解码非零系数个数(TotalCoeff)和拖尾系数个数(TrailingOnes)。
3. 解码拖尾系数符号(trailing_ones_sign_flag)
4. 解码非零系数幅值
5. 解码total_zeros和run_before
6. 组合非零系数幅值和游程信息,得到整个残差数据块
CAVLC: Context-Adaptive Variable-Length Coding CAVLC模型选择主要体现:非零系数编码所需表格的选择和拖尾系数后缀长度的更新; 编码过程 2.编码非零系数的数目(TotalCoeffs: 0-16)和拖尾系数数目(TrailingOnes: 0-3) 3.编码每个拖尾系数的符号 4.编码除了拖尾系数之外的非零系数的幅值Levels 5)更新suffixLength 5.编码最后一个非零系数前零的数目TotalZeros 6.编码每个非零系数前零的个数RunBefore 2.确定TotalCoeffs和TrailingOnes 3.根据TrailingOnes确定拖尾1的符号 4.解析除拖尾系数之外的非零系数的幅值(解码Levels) 2)计算得到Level[i] 3)更新suffixLength 5.根据得到的TotalCoeff数值和TotalZeros编码值得到TotalZeros数值 6.解析每个非零系数前零的数目(解码RunBefore) 例子:假定用了Num-VLC0表 Reodered block: 编码 Decoding: |
谨以此文献给QQ群“H.264乐园”和群里那些无私奉献的同行朋友!
也希望能对刚进入这个领域的朋友有所帮助,欢迎做过CAVLC的同行能批评指正!
编码过程:
假设有一个4*4数据块
{
0, 3, -1, 0,
0, -1, 1, 0,
1, 0, 0, 0,
0, 0, 0, 0
}
数据重排列:0,3,0,1,-1,-1,0,1,0……
1) 初始值设定:
非零系数的数目(TotalCoeffs) = 5;
拖尾系数的数目(TrailingOnes)= 3;
最后一个非零系数前零的数目(Total_zeros) = 3;
变量NC=1;
(说明:NC值的确定:色度的直流系数NC=-1;其他系数类型NC值是根据当前块左边4*4块的非零系数数目(NA)当前块上面4*4块的非零系数数目(NB)求得的,见毕厚杰书P120表6.10)
suffixLength = 0;
i = TotalCoeffs = 5;
2) 编码coeff_token:
查标准(BS ISO/IEC 14496-10:2003)Table 9-5,可得:
If (TotalCoeffs &#61;&#61; 5 && TrailingOnes &#61;&#61; 3 && 0 <&#61; NC <2)
coeff_token &#61; 0000 100;
Code &#61; 0000 100;
3&#xff09; 编码所有TrailingOnes的符号&#xff1a;
逆序编码&#xff0c;三个拖尾系数的符号依次是&#xff0b;&#xff08;0&#xff09;&#xff0c;&#xff0d;&#xff08;1&#xff09;&#xff0c;&#xff0d;&#xff08;1&#xff09;&#xff1b;
即:
TrailingOne sign[i--] &#61; 0;
TrailingOne sign[i--] &#61; 1;
TrailingOne sign[i--] &#61; 1;
Code &#61; 0000 1000 11;
4&#xff09; 编码除了拖尾系数以外非零系数幅值Levels&#xff1a;
过程如下&#xff1a;
&#xff08;1&#xff09;将有符号的Level[ i ]转换成无符号的levelCode&#xff1b;
如果Level[ i ]是正的&#xff0c;levelCode &#61; (Level[ i ]<<1) – 2;
如果Level[ i ]是负的&#xff0c;levelCode &#61; - (Level[ i ]<<1) – 1;
&#xff08;2&#xff09;计算level_prefix&#xff1a;level_prefix &#61; levelCode / (1<
&#xff08;3&#xff09;计算level_suffix&#xff1a;level_suffix &#61; levelCode % (1<
&#xff08;5&#xff09;suffixLength updata&#xff1a;
If ( suffixLength &#61;&#61; 0 )
suffixLength&#43;&#43;&#xff1b;
else if ( levelCode > (3<
回到例子中&#xff0c;依然按照逆序&#xff0c;Level[i--] &#61; 1&#xff1b;&#xff08;此时i &#61; 1&#xff09;
levelCode &#61; 0&#xff1b;level_prefix &#61; 0&#xff1b;
查表9-6&#xff0c;可得level_prefix &#61; 0时对应的bit string &#61; 1&#xff1b;
因为suffixLength初始化为0&#xff0c;故该Level没有后缀&#xff1b;
因为suffixLength &#61; 0&#xff0c;故suffixLength&#43;&#43;&#xff1b;
Code &#61; 0000 1000 111&#xff1b;
编码下一个Level&#xff1a;Level[0] &#61; 3&#xff1b;
levelCode &#61; 4&#xff1b;level_prefix &#61; 2&#xff1b;查表得bit string &#61; 001&#xff1b;
level_suffix &#61; 0&#xff1b;suffixLength &#61; 1&#xff1b;故码流为0010&#xff1b;
Code &#61; 0000 1000 1110 010&#xff1b;
i &#61; 0&#xff0c;编码Level结束。
5&#xff09;编码最后一个非零系数前零的数目&#xff08;TotalZeros&#xff09;&#xff1a;
查表9-7&#xff0c;当TotalCoeffs &#61; 5&#xff0c;total_zero &#61; 3时&#xff0c;bit string &#61; 111&#xff1b;
Code &#61; 0000 1000 1110 0101 11&#xff1b;
6&#xff09; 对每个非零系数前零的个数&#xff08;RunBefore&#xff09;进行编码&#xff1a;
i &#61; TotalCoeffs &#61; 5&#xff1b;ZerosLeft &#61; Total_zeros &#61; 3&#xff1b;查表9-10&#xff1a;
依然按照逆序编码
ZerosLeft &#61;3, run_before &#61; 1 run_before[4]&#61;10;
ZerosLeft &#61;2, run_before &#61; 0 run_before[3]&#61;1;
ZerosLeft &#61;2, run_before &#61; 0 run_before[2]&#61;1;
ZerosLeft &#61;2, run_before &#61; 1 run_before[1]&#61;01;
ZerosLeft &#61;1, run_before &#61; 1 run_before[0]不需要码流来表示
Code &#61; 0000 1000 1110 0101 1110 1101&#xff1b;
编码完毕。
----------------------------------Sunrise------
[[i] 本帖最后由 firstime 于 2008-9-4 02:49 PM 编辑 [/i]]
davesliu 发表于 2006-11-20 12:03 PM
请问斑竹&#xff0c;老毕书上120页表6.10中第2行和第三行的NA和NB是不是给弄反了&#xff1f;
vcforever 发表于 2006-12-7 11:06 PM
应该是弄反了
dmsd 发表于 2006-12-18 05:42 PM
请问trailing ones是指所有绝对值为1的系数的个数还是连续绝对值为1的系数的个数。
谢谢。
firstime 发表于 2006-12-18 11:29 PM
倒数三个 &#43;/-1
另&#xff1a;可结合本论坛帖子“[url&#61;http://bbs.chinavideo.org/viewthread.php?tid&#61;1057][color&#61;blue][b]CAVLC中的前缀和后缀[/b][/color][/url]”学习。
[[i] 本帖最后由 firstime 于 2008-5-20 09:59 PM 编辑 [/i]]
achen 发表于 2007-1-17 05:50 PM
“如果Level[ i ]是负的&#xff0c;levelCode &#61; - (Level[ i ]<<1) – 1”
假如Level[ i ]&#61;-3,levelCode值是多少啊&#xff1f;
还有能不能详细讲解一下如何确定suffixLength的取值&#xff0c;它跟域值的关系怎么确定&#xff1f;
谢谢&#xff01;
[[i] 本帖最后由 firstime 于 2007-1-27 10:06 PM 编辑 [/i]]
dcfarmer 发表于 2007-1-28 11:22 AM
是JM86运行后的trace记录
&#64;160 Luma # c & tr.1s(0,0) vlc&#61;0 #c&#61;11 #t1&#61;1 000000000001110 ( 11)
&#64;175 Luma trailing ones sign (0,0) 0 ( 0)
&#64;176 Luma lev (0,0) k&#61;9 vlc&#61;1 lev&#61; -2 11 ( -1)
&#64;178 Luma lev (0,0) k&#61;8 vlc&#61;1 lev&#61; -1 11 ( -1)
&#64;180 Luma lev (0,0) k&#61;7 vlc&#61;1 lev&#61; 1 10 ( 1)
&#64;182 Luma lev (0,0) k&#61;6 vlc&#61;1 lev&#61; -5 000011 ( -5)
&#64;188 Luma lev (0,0) k&#61;5 vlc&#61;2 lev&#61;-11 00000101 (-11)
&#64;196 Luma lev (0,0) k&#61;4 vlc&#61;3 lev&#61; -3 1101 ( -3)
&#64;200 Luma lev (0,0) k&#61;3 vlc&#61;3 lev&#61; 3 1100 ( 3)
&#64;204 Luma lev (0,0) k&#61;2 vlc&#61;3 lev&#61; 3 1100 ( 3)
&#64;208 Luma lev (0,0) k&#61;1 vlc&#61;3 lev&#61;-12 001111 (-12)
&#64;214 Luma lev (0,0) k&#61;0 vlc&#61;3 lev&#61; 9 001000 ( 9)
上面这段是trace.txt中的记录&#xff0c;根据前面的vlc&#61;0 #c&#61;11 #t1&#61;1&#xff0c;就是trailing_ones 的个数是1个&#xff08;&#64;175 Luma trailing ones sign (0,0) 0 ( 0) &#xff09;也能说明&#xff0c;但是在下面的level中&#xff0c;明明还要-1,1可以作为trailing_ones的&#xff0c;为什么不把这两个数当作trailing_ones呢&#xff1f;&#xff1f;而是作为level呢&#xff1f;&#xff1f;&#xff1f;&#xff1f;
dcfarmer 发表于 2007-1-28 11:40 AM
接上面的问题&#xff0c;在群里面讨论弄明白了
在CAVLC编码中&#xff0c;从右往左看&#xff0c;算trailing_ones时&#xff0c;在右边不能有abs&#xff08;&#xff09;>1的数&#xff0c;还有算trailing_ones的个数的话&#xff0c;也不能被别的abs&#xff08;&#xff09;>1的数隔断&#xff0c;如果隔断的话&#xff0c;只能计数到隔断数为止,而且要保证个数<&#61;3。例如。。。。2&#xff0c;3&#xff0c;0&#xff0c;0&#xff0c;0&#xff0c;1&#xff0c;1&#xff0c;1&#xff0c;0&#xff0c;0&#xff0c;2 &#xff0c;这个序列中triailing_ones是没有的。还有。。。。。1&#xff0c;0&#xff0c;0&#xff0c;&#xff0d;1&#xff0c;3&#xff0c;1&#xff0c;0&#xff0c;0&#xff0c;0&#xff0c;0&#xff0c;算trailing_ones 个数时&#xff0c;只能有一个也就是3后面的“1”算一个&#xff0c;3前面的&#xff0d;1和1由于被3隔断&#xff0c;所以不能算入其中。
[[i] 本帖最后由 dcfarmer 于 2007-6-7 03:27 PM 编辑 [/i]]
zjh1004 发表于 2007-8-26 09:55 PM
ZerosLeft &#61;1, run_before &#61; 1 run_before[0]不需要码流来表示
请问&#xff1a;为什么不需要码流来表示&#xff1f;
是因为是最后一位吗&#xff1f;
查表知&#xff1a;此时 run_before[0]&#61;0
timek 发表于 2007-9-11 06:35 PM
Level[ i ]&#61;-3,levelCode值是多少啊&#xff1f;
levelcode应该是3
grassland_fly 发表于 2007-9-12 03:47 PM
[size&#61;12px]ZerosLeft &#61;1, run_before &#61; 1 run_before[0]不需要码流来表示
请问&#xff1a;为什么不需要码流来表示&#xff1f;
是因为是最后一位吗&#xff1f;
查表知&#xff1a;此时 run_before[0]&#61;0[/size]
[size&#61;12px] [/size]
[size&#61;12px]---------------------------------------------------------[/size]
[size&#61;12px] [/size]
[size&#61;12px]I think it&#39;s because can know the vaule based on the value before. :)[/size]
generalair 发表于 2007-10-22 02:51 PM
2007-1-28 11:22 AM dcfarmer
讨论
是JM86运行后的trace记录
&#64;160 Luma # c & tr.1s(0,0) vlc&#61;0 #c&#61;11 #t1&#61;1 000000000001110 ( 11)
&#64;175 Luma trailing ones sign (0,0) 0 ( 0)
&#64;176 Luma lev (0,0) k&#61;9 vlc&#61;1 lev&#61; -2 11 ( -1)
&#64;178 Luma lev (0,0) k&#61;8 vlc&#61;1 lev&#61; -1 11 ( -1)
&#64;180 Luma lev (0,0) k&#61;7 vlc&#61;1 lev&#61; 1 10 ( 1)
&#64;182 Luma lev (0,0) k&#61;6 vlc&#61;1 lev&#61; -5 000011 ( -5)
&#64;188 Luma lev (0,0) k&#61;5 vlc&#61;2 lev&#61;-11 00000101 (-11)
&#64;196 Luma lev (0,0) k&#61;4 vlc&#61;3 lev&#61; -3 1101 ( -3)
&#64;200 Luma lev (0,0) k&#61;3 vlc&#61;3 lev&#61; 3 1100 ( 3)
&#64;204 Luma lev (0,0) k&#61;2 vlc&#61;3 lev&#61; 3 1100 ( 3)
&#64;208 Luma lev (0,0) k&#61;1 vlc&#61;3 lev&#61;-12 001111 (-12)
&#64;214 Luma lev (0,0) k&#61;0 vlc&#61;3 lev&#61; 9 001000 ( 9)
请问上面这段TRACE记录对应的16个系数序列是什么&#xff1f;谢谢
boleaon 发表于 2007-11-8 04:46 PM
好贴...
kdjwang 发表于 2008-10-10 03:52 PM
levelcode应该是5&#xff0c;level先乘2为-6&#xff0c;再取反减1
解码时因为levelcode为奇数&#xff0c;level &#61; (‐levelCode‐1)/2
libra811 发表于 2008-12-29 10:44 PM
刚学习&#xff0c;受用了。谢谢
peiyangpr 发表于 2009-1-4 05:28 PM
毕厚杰P123&#xff0c;6.8.6节中所给例子是否有误&#xff1f;是否应如下编码&#xff1f;
0 0 -1 0
5 2 0 0
3 0 0 0
1 0 0 0
数据重排&#xff1a;0&#xff0c;0&#xff0c;5&#xff0c;3&#xff0c;2&#xff0c;-1&#xff0c;0&#xff0c;0&#xff0c;0&#xff0c;1……
其中TotalCoeffs &#61; 5, TrailingOnes &#61; 2, Total_zeros &#61; 5, NC &#61; 3
编码过程&#xff1a;
元素 数值 编码
Coeff_token TotalCoeffs &#61; 5, TrailingOnes &#61; 2 0000101
Trailingones_sign_flag(1) &#43; 0
Tranlingones_sign_flag(0) - 1
Level(2) 1 (suffixLenth &#61; 0) 1
Level(1) 3 (suffixLength &#61; 1) 0010
Level(0) 5 (suffixLength &#61; 1) 000010
Total_zeros 5 101
Run_before(4) Zeroleft &#61; 5, run_before &#61; 3 010
Run_before(4) Zeroleft &#61; 2, run_before &#61; 0 1
Run_before(4) Zeroleft &#61; 2, run_before &#61; 0 1
Run_before(4) Zeroleft &#61; 2, run_before &#61; 0 1
Run_before(4) Zeroleft &#61; 2, run_before &#61; 2 无
最终编码输出&#xff1a;000010110010000010101010111
[[i] 本帖最后由 peiyangpr 于 2009-1-4 09:16 PM 编辑 [/i]]
residual_block_cavlc( coeffLevel, maxNumCoeff ) { C Descriptor
for( i &#61; 0; i
// coeff_token 指明了非零系数的个数&#xff0c;拖尾系数的个数。
coeff_token
if( TotalCoeff( coeff_token ) > 0 ) {
if( TotalCoeff( coeff_token ) > 10 && TrailingOnes( coeff_token ) <
3 )
suffixLength &#61; 1
else
suffixLength &#61; 0
for( i &#61; 0; i
- 如果trailing_ones_sign_flag &#61; 0, 相应的拖尾系数是&#43;1。
- 否则&#xff0c;trailing_ones_sign_flag &#61;1&#xff0c;相应的拖尾系数是-1。
trailing_ones_sign_flag
level[ i ] &#61; 1 – 2 * trailing_ones_sign_flag
} else {
// level_prefix and level_suffix 非零系数值的前缀和后缀。
level_prefix
levelCode &#61; ( level_prefix <
level_suffix
levelCode &#43;&#61; level_suffix
}
if( level_prefix &#61; &#61; 15 && suffixLength &#61; &#61; 0 )
levelCode &#43;&#61; 15
if( i &#61; &#61; TrailingOnes( coeff_token ) &&
TrailingOnes( coeff_token ) <3 )
levelCode &#43;&#61; 2
if( levelCode % 2 &#61; &#61; 0 )
level[ i ] &#61; ( levelCode &#43; 2 ) >> 1
else
level[ i ] &#61; ( –levelCode – 1 ) >> 1
if( suffixLength &#61; &#61; 0 )
suffixLength &#61; 1
if( Abs( level[ i ] ) > ( 3 <<( suffixLength – 1 ) ) &&
suffixLength <6 )
suffixLength&#43;&#43;
}
if( TotalCoeff( coeff_token )
total_zeros
zerosLeft &#61; total_zeros
} else
zerosLeft &#61; 0
for( i &#61; 0; i
run_before
run[ i ] &#61; run_before
} else
run[ i ] &#61; 0
zerosLeft &#61; zerosLeft – run[ i ]
}
run[ TotalCoeff( coeff_token ) – 1 ] &#61; zerosLeft
coeffNum &#61; -1
for( i &#61; TotalCoeff( coeff_token ) – 1; i >&#61; 0; i-- ) {
coeffNum &#43;&#61; run[ i ] &#43; 1
coeffLevel[ coeffNum ] &#61; level[ i ]
}
}
}
本文来自CSDN博客&#xff0c;转载请标明出处&#xff1a;http://blog.csdn.net/xfding/archive/2010/04/12/5477464.aspx