H264-CAVLC(基于上下文可变长度编码)
CAVLC 是用来编码残差系数的,它充分利用了变换量化后系数的特性。
cavlc 编码语法
其中ce(v)表示使用的基于上下文的可变长度的熵编码
分析过程
- 所有的传输系数幅度值coeffLevel[ i ],都初始化为0,i 从0 到maxNumCoff-1
- 非零系数的数目TotalCoeff(coeff_token)和拖尾系数数目TrailingOnes(coeff_token)使用过分析coeff_token获得的
- 接下来的步骤:
- 如果非零系数为0,直接退出,下边的操作不再进行。
- 如果非零系数大于0,
a. 非零系数的大小通过分析trailing_ones_sign_flag, level_prefix和level_suffix获得。
b. 在每个非零系数前的0系数通过分析total_zeros和run_before获得。
编码非零系数和拖尾系数(coeff token)
coeff_token 编码了非零系数的数目(total-coeffs)和拖尾系数数目(T1),Total-coeffs的取值为0~16,T1的取值为0-3。如果末尾有超过3个+1/-1,则拖尾系数做多有3个,其他的当做普通系数处理。对于亮度block 共有4种表格可供选择,3个变长编码表和一个定长编码表。这些表格对(TotalCoeff, TrailingOnes) 数据对进行了编码。block左边nA和上边nB的block的非零系数的数目决定选择哪个编码表。由nA和nB计算获得nC,nC的取值决定选择哪个table。nC的映射关系如下表:
nC的计算:
- 如果左边和上边都可用,nC = (nA + nB)>> 2
- 如果左边可用,nC= nA
- 如果上边可用,nC = nB
- 如果都不可用,nC= 0
分析level 信息
处理拖尾系数的符号
逆序表示拖尾系数的符号,0表示+,1表示-
过程:
将index i初始化为0,当TrailingOnes(coeff_token)不等于0时,接下来的步骤会重复TrailingOnes(coeff_token)次来解码拖尾系数的符号:
-1bit 语法单元trailing_ones_sign_flag 如果是0 LevelVal[i] 被设置为1
- 否则LevelVal[i] 被设置0
index 加1.
编码剩余非零系数
level表示非零系数的符号和幅度,是以逆序进行编码,从高频系数向低频系数进行。
suffixLength 根据以下原则初始化:
- 如果TotalCoeff(coeff_token)大于10并且TrailingOnes(coeff_token)小于3, suffixlength设置为1.
- 否则TotalCoeff(coeff_token)小于等于10或者TrailingOnes等于3,suffixlength设置为0
接下来,当TotalCoeff(coeff_token) - TrailingOnes(coeff_token)不为0时,接下来的步骤重复TotalCoeff(coeff_token)-TrailingOnes(coeff_token)次来解码剩余的非零系数的level值:
1. 解码语法元素level_prefix
2. 如果level_prefix等于14并且suffixlength等于0,levelSuffixSize等于4
否则,如果level_prefix大于或者等于15,levelSuffixSzie 设置为level_prefix-3
否则,levelSuffixSize设置为suffixLength
3. 语法元素level_suffix按照如下方式解码:
a. 如果levelSuffixSize大于0,语法元素level_suffix解码为无符号整数u(v), bit长度为levelSuffixSize
b. levelSuffixSize为0,语法元素level_suffix置为0
4. levelCode 设置为(Min(15, level_prefix) << suffixLength) + level_suffix
5. 当level_prefix大于或者等于15 并且suffixlength等于0,levelCode 增加15
6. 当level_prefix 大于或者等于16,levelCode增加(1 <<(level_prefix -3)) -4096
7. 当index i 等于TrailingOnes(coeff_token)并且TrailingOnes(coeff_token)小于3时,levelcode增加2
8. 变量levelVal[i] 由下面的步骤导出:
- 如果levelCode是偶数,levelVal[i] 设置为(levelCode + 2) >> 1
- 如果levelCode是奇数,levelVal[i]设置为(-levelCode -1) >> 1
9. 当suffixLength等于0, suffixLength设置为1
10. 当levelVal[i]的绝对值大于(3<<(suffixLength-1))并且suffixLength小于6,suffixLength增加1
11. index i 增加1
下边是suffixlength的增加门限表。
level_prefix 的处理
level_prefix 是前缀码,从当前位置开始读取位置开始直到第一个非零bit位结束(包括此非零bit),计算前缀0的数目即为level_prefix的值。如下代码所示
leadingZeroBits = −1
for( b = 0; !b; leadingZeroBits++ )
b = read_bits( 1 )
level_prefix = leadingZeroBits
在Baseline, Constrained Baseline,Main和Extended profiles中,level_prefix的值不能超过15。
处理total_zeros
total_zeros表示最后一个非零系数之前的0的数目。
变量zeroleft由下面的过程得到:
- 如果非零传输系数TotalCoeff(coeff_token)等于最大传输系数数目maxNumCoeff,zeroleft设置为0.
- 否则即有为0的系数时,解码total_zeros, zerosleft设置为total_zeros的值。
变量tzVlcIndex设置为TotalCoeff(coeff_token)
解码total_zeros的由下边的过程完成:
-如果maxNumCoeff等于4,使用下表解码total_zeros
-否则,如果MaxNumCoeff等于8,使用下表解码total_zeros
-否则MaxNumCoeff不等于4并且不等于8,使用下面的解码表:
处理run_before
run_before表示每一个非零系数到下一个非零之间0的个数。runVal[i]使每个非零数前0的个数。接下来的步骤重复TotalCoeff(coeff_token) - 1次:
- 变量runVal[i]由下面进行推导:
- 如果zeros_left大于零,run_before根据下表解码
-否则(zerosleft等于0),runVal[i] 置为0 - zerosleft 中减去runVal[i],它的结果赋值给zerosleft。
- index i 增加1
最后zerosleft的值赋值给runVal[i]
根据runval填充level
coeffNum = −1
for( i = TotalCoeff( coeff_token ) − 1; i >= 0; i− − ) {
coeffNum += runVal[ i ] + 1
coeffLevel[ startIdx + coeffNum ] = levelVal[ i ]
}
例1
[
0
3
−
1
0
0
−
1
1
0
1
0
0
0
0
0
0
0
]
\begin{bmatrix} 0&3&-1&0\\ 0&-1&1&0\\ 1&0&0&0\\ 0&0&0&0 \end{bmatrix}
⎣⎢⎢⎡00103−100−11000000⎦⎥⎥⎤
zigzag排序后获得0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0
有TotalCoeffs=5,Totalzeros = 3, TrailingOnes = 3 分别是最后的(1, -1, -1)
T1 sign(N) 表示取拖尾系数的符号,N表示第N个非零系数
level(N)表示非零系数的level,N表示第N个非零系数
编码:
元素 | 值 | 码字 | 备注 |
---|---|---|---|
coeff_token | (5, 3) | 0000 100 | 查找表格(假设nC=0) |
T1 sign (4) | 1 | 0 | |
T1 sign (3) | -1 | 1 | |
T1 sign (2) | -1 | 1 | |
Level (1) | +1 (level prefix = 1; suffixLength = 0) | 1 | suffixlength的增加是后判定 if val > 0 levelval = val * 2 -2 else levelval = -(val * 2 ) -1 当拖尾系数小于3,第一个非拖尾系数levelval -=2 level_prefix_temp = levelval >>suffix_length level_suffix = levelval & (2suffix_length-1)最后根据level_prefix_temp查level_prefix表获得 |
Level (0) | +3 (level prefix = 001, suffixLength = 1) | 0010 | 根据suffixlength增加的判定,suffixlength++ 得到level_prefix_temp = 2,level_suffix = 0, level_prefix_temp查表得到level_prefix码字为001 |
TotalZeros | 3 | 111 | 最后一个非零系数之前的0的个数为3,非零系数总数为5,查表得到编码111 |
run before(4) | ZerosLeft = 3; run before =1 | 10 | 查表获取编码 |
run before(3) | ZerosLeft = 2; run before =0 | 1 | * |
run before(2) | ZerosLeft = 2; run before =0 | 1 | * |
run before(1) | ZerosLeft = 2; run before =1 | 01 | * |
run before(0) | ZerosLeft = 1; run before =0 | 1 | 不需要编码 |
获得的编码为:0000100011100101111011011
解码:
码字 | 元素 | 值 | 输出序列 | 备注 |
---|---|---|---|---|
0000100 | coeff_token | (5,3) | 空 | |
0 | T1 sign | + | 1 | |
1 | T1 sign | - | -1,1 | |
1 | T1 sign | - | -1,-1,1 | |
1 | level(1) | 1 | 1,-1,-1,1 | suffix_length初始化为0,level_prefix = 1,查表得到level_prefix_tmp = 0 level_val = level_prefix_temp << suffix_length + level_suffix if (level_val %2 == 0) level = (levelval + 2) >> 1 else level = (-levelval-1) >> 1 |
0010 | level(0) | 3 | 3,1,-1,-1,1 | suffix_length =1, level_prefix = 001, 查表得到level_prefix_temp = 2, 最后得到level = 3 |
10 | run_before(4) | 0 | 3,1,-1,-1,0,1 | 解码之前zeros_left= 3 |
1 | run_before(3) | 0 | 3,1,-1,-1,0,1 | 解码之前zeros_left= 2 |
1 | run_before(2) | 0 | 3,1,-1,-1,0,1 | 解码之前zeros_left= 2 |
01 | run_before(1) | 1 | 3,0,1,-1,-1,0,1 | 解码之前zeros_left= 2 |
1 | run_before(0) | 1 | 0,3,0,1,-1,-1,0,1 | 解码之前zeros_left= 1 |
最后得到的矩阵zigzag序列0,3,0,1,-1,-1,0,1
例2
[
−
2
4
0
−
1
3
0
0
0
−
3
0
0
0
0
0
0
0
]
\begin{bmatrix} -2&4&0&-1\\ 3&0&0&0\\ -3&0&0&0\\ 0&0&0&0 \end{bmatrix}
⎣⎢⎢⎡−23−3040000000−1000⎦⎥⎥⎤
zigzag排序后获得-2,4,3,-3,0,0,-1,0,0,0,0,0,0,0,0,0,0
有TotalCoeffs=5,Totalzeros = 2, TrailingOnes = 1 是最后的 -1
编码:
元素 | 值 | 码字 | 备注 |
---|---|---|---|
coeff_token | (5, 1) | 0000 000110 | 查找表格(假设nC=0) |
T1 sign (4) | -1 | 1 | |
level(3) | -3(level prefix = 0000 0001; suffixLength = 0) | 0001 | |
level (2) | 3(level prefix = 001; suffixLength = 1) | 0010 | |
Level (1) | 4 (level prefix = 0001; suffixLength = 1) | 00010 | if val > 0 levelval = val * 2 -2 else levelval = -(val * 2 ) -1 当拖尾系数小于3,第一个非拖尾系数levelval -=2 level_prefix_temp = levelval >>suffix_length level_suffix = levelval & (2suffix_length-1)最后根据level_prefix_temp查level_prefix表获得 |
Level (0) | -2 (level prefix = 1, suffixLength = 2) | 111 | 根据suffixlength增加的判定,suffixlength++ |
TotalZeros | 2 | 0011 | |
run before(4) | ZerosLeft = 2; run before =2 | 00 | 查表获取编码 |
run before(3) | ZerosLeft = 0; run before =0 | 不需要编码 |
获得的编码为:000000011010001001000010111001100
解码
码字 | 元素 | 值 | 输出序列 | 备注 |
---|---|---|---|---|
0000 000110 | coeff_token | (5,1) | 空 | |
1 | T1 sign | - | -1 | |
0001 | level(3) | -3 | -3,1 | suffix_length初始化为0,level_prefix = 0001,查表得到level_prefix_tmp = 3 level_val = level_prefix_temp << suffix_length + level_suffix 当拖尾系数数目小于3,第一个非拖尾系数level_val +=2 if (level_val %2 == 0) level = (levelval + 2) >> 1 else level = (-levelval-1) >> 1 |
0010 | level(2) | 3 | 3,-3,1 | suffix_length = 1 , level_prefix = 001, level_suffix=0,得到level = 3 |
00010 | level(1) | 4 | 4,3,-3,1 | suffix_length = 1,level_prefix = 0001, level_suffix = 0,得到level=4 |
111 | level(0) | -2 | -2,4,3,-3,1 | suffix_length =2, level_prefix =1, level_suffix=3,得到level= -2 |
0011 | TotalZeros | 2 | -2,4,3,-3,1 | |
00 | run_before(4) | 2 | -2,4,3,-3,0,0,1 | 解码之前zeros_left= 2 |
无 | run_before(3~0) | 无 | -2,4,3,-3, 0,0,1 | 解码之前zeros_left= 0 |
最后得到的矩阵zigzag序列-2,4,3,-3,0,0,-1
cavlc编码matlab 代码
function [bits] = enc_cavlc(data, nL, nU)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This script/function was created by
% Abdullah Al Muhit
% contact - almuhit@gmail.com
% website - https://sites.google.com/site/almuhit/
% Please use it at your own risk. Also, Please cite the following paper:
% A A Muhit, M R Pickering, M R Frater and J F Arnold, 揤ideo Coding using Elastic Motion Model and Larger Blocks,�IEEE Trans. Circ. And Syst. for Video Technology, vol. 20, no. 5, pp. 661-672, 2010. [Impact factor �3.18] [PDF]
% A A Muhit, M R Pickering, M R Frater and J F Arnold, 揤ideo Coding using Geometry Partitioning and an Elastic Motion Model,�accepted for publication in Journal of Visual Communication and Image Representation. [Impact factor �1.33] [PDF]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% CAVLC Encoder.
% takes in 4x4 block of residual data and produces output bits
% load table.mat;
%使用到的码表
global Table_coeff0 Table_coeff1 Table_coeff2 Table_coeff3
global Table_run Table_zeros
%返回的二进制串
bits = '';
% Convert 4x4 matrix data into a 1x16 data of zig-zag scan
%获取输入数据的维度
[row, col] = size(data);
% check the correct size of the block
if((row~=4)||(col~=4))
disp('Residual block size mismatch - exit from CAVLC')
return;
end
%zigzag排列的矩阵元素下标矩阵,16*2
scan = [1,1;1,2;2,1;3,1;2,2;1,3;1,4;2,3;3,2;4,1;4,2;3,3;2,4;3,4;4,3;4,4];
%按zigzag顺序排列输入矩阵到l
for i=1:16
m=scan(i,1);
n=scan(i,2);
l(i)=data(m,n); % l contains the reordered data
end
i_last = 16;
% find the last non-zero co-eff in reverse order
while ((i_last>0)&(l(i_last)==0))
i_last = i_last - 1;
end
i_total = 0; % Total non zero coefficients
i_total_zero = 0; % Total zeros
i_trailing = 0;
sign = ''; % find sign for trailing ones
idx = 1;
%% find level, trailing ones(sign), run and total zero values
while ((i_last>0)&(abs(l(i_last))==1)&(i_trailing<3))
level(idx)=l(i_last);
i_total = i_total + 1;
i_trailing = i_trailing + 1;
if l(i_last)==-1
sign = [sign '1'];
else
sign = [sign '0'];
end
run(idx) = 0;
i_last = i_last - 1;
while ((i_last>0)&(l(i_last)==0))
run(idx) = run(idx) + 1;
i_total_zero = i_total_zero + 1;
i_last = i_last - 1;
end
idx = idx + 1;
end
while (i_last>0)
level(idx)=l(i_last);
i_total = i_total + 1;
run(idx) = 0;
i_last = i_last - 1;
while ((i_last>0)&(l(i_last)==0))
run(idx) = run(idx) + 1;
i_total_zero = i_total_zero + 1;
i_last = i_last - 1;
end
idx = idx + 1;
end
%% Write coeff_token
%根据参数计算nC
% find n parameter (context adaptive)
if (nL>0)&(nU>0)
n = (nL + nU)/2;
elseif (nL>0)|(nU>0)
n = nL + nU;
else
n = 0;
end
% Coeff_token mapping
% Rows are the total coefficient(0-16) and columns are the trailing ones(0-3)
% TABLE_COEFF0,1,2,3 ARE STORED IN TABLE.MAT OR CAVLC_TABLES.M FILE
% Choose proper table_coeff based on n value
if 0<=n<2
Table_coeff = Table_coeff0;
elseif 2<=n<4
Table_coeff = Table_coeff1;
elseif 4<=n<8
Table_coeff = Table_coeff2;
elseif 8<=n
Table_coeff = Table_coeff3;
end
% Assign coeff_token and append it to output bits
% Here coeff_token is cell array so needs to be coverted to char
coeff_token = Table_coeff(i_total + 1,i_trailing + 1);
bits = [bits char(coeff_token)];
% If the total coefficients == 0 exit from this function
if i_total==0
return;
end
% Append sign of trailing ones to bits
if i_trailing>0
bits = [bits sign];
end
%% Encode the levels of remaining non-zero coefficients
% find the suffix length
if (i_total>10)&(i_trailing<3)
i_sufx_len = 1;
else
i_sufx_len = 0;
end
% loop
for i=(i_trailing + 1):i_total
if level(i)<0
i_level_code = -2*level(i) - 1;
else
i_level_code = 2*level(i) - 2;
end
if (i == i_trailing + 1)&(i_trailing<3)
i_level_code = i_level_code - 2;
end
if bitshift(i_level_code,-i_sufx_len)<14
level_prfx = bitshift(i_level_code,-i_sufx_len);
while(level_prfx>0)
bits = [bits '0'];
level_prfx = level_prfx - 1;
end
bits = [bits '1'];
if i_sufx_len>0
level_sufx = dec2bin(i_level_code,i_sufx_len);
x = length(level_sufx);
if x>i_sufx_len
level_sufx = level_sufx(x-i_sufx_len+1:x);
end
bits = [bits level_sufx];
end
elseif (i_sufx_len==0)&(i_level_code<30)
level_prfx = 14;
while(level_prfx>0)
bits = [bits '0'];
level_prfx = level_prfx - 1;
end
bits = [bits '1'];
level_sufx = dec2bin(i_level_code-14,4);
x = length(level_sufx);
if x>4
level_sufx = level_sufx(x-4+1:x);
end
bits = [bits level_sufx];
elseif (i_sufx_len>0)&(bitshift(i_level_code,-i_sufx_len)==14)
level_prfx = 14;
while(level_prfx>0)
bits = [bits '0'];
level_prfx = level_prfx - 1;
end
bits = [bits '1'];
level_sufx = dec2bin(i_level_code,i_sufx_len);
x = length(level_sufx);
if x>i_sufx_len
level_sufx = level_sufx(x-i_sufx_len+1:x);
end
bits = [bits level_sufx];
else
level_prfx = 15;
while(level_prfx>0)
bits = [bits '0'];
level_prfx = level_prfx - 1;
end
bits = [bits '1'];
i_level_code = i_level_code - bitshift(15,i_sufx_len);
if i_sufx_len==0
i_level_code = i_level_code - 15;
end
if (i_level_code>=bitshift(1,12))|(i_level_code<0)
disp('Overflow occured');
end
level_sufx = dec2bin(i_level_code,12);
x = length(level_sufx);
if x>12
level_sufx = level_sufx(x-12+1:x);
end
bits = [bits level_sufx];
end
if i_sufx_len==0
i_sufx_len = i_sufx_len + 1;
end
if ((abs(level(i)))>bitshift(3,i_sufx_len - 1))&(i_sufx_len<6)
i_sufx_len = i_sufx_len + 1;
end
end
%% Encode Total zeros
% Here Rows(1-16) are Total coefficient and colums(0-15) are total zeros
% Rearranged from the standard for simplicity
% Table_zeros is located in table.mat or cavlc_tables.m file
if i_total<16
total_zeros = Table_zeros(i_total,i_total_zero + 1);
bits = [bits char(total_zeros)];
end
%% Encode each run of zeros
% Rows are the run before, and columns are zeros left
% Table_run is located in table.mat or cavlc_tables.m file
i_zero_left = i_total_zero;
if i_zero_left>=1
for i=1:i_total
if (i_zero_left>0)&(i==i_total)
break;
end
if i_zero_left>=1
i_zl = min(i_zero_left,7);
run_before = Table_run(1 + run(i),i_zl);
bits = [bits char(run_before)];
i_zero_left = i_zero_left - run(i);
end
end
end