目錄
- 1 CAVLC概念
- 2 CAVLC原理
- 3 CAVLC編碼流程
- 4 CAVLC解碼流程
- 展開全部
- 1 CAVLC概念
- 2 CAVLC原理
- 3 CAVLC編碼流程
- 4 CAVLC解碼流程
- 收起
摘要 糾錯編輯摘要
CAVLC即基于上下文的自适應變長編碼。H.264标準中使用CAVLC對4*4子產品的亮度和色度殘差資料進行編碼。
CAVLC-CAVLC概念
CAVLC的全稱是Context-Adaptive Varialbe-Length Coding,即基于上下文的自适應變長編碼。CAVLC的本質是變長編碼,它的特性主要展現在自适應能力上,CAVLC可以根據已編碼句法元素的情況動态的選擇編碼中使用的碼表,并且随時更新拖尾系數字尾的長度,進而獲得極高的壓縮比。H.264标準中使用CAVLC對4×4子產品的亮度和色度殘差資料進行編碼。
CAVLC-CAVLC原理
在H.264标準編碼體系中,視訊圖像在經過了預測、變換及量化編碼後表現出如下的特性:4×4塊殘差資料塊比較稀疏,其中非零系數主要集中在低頻部分,而高頻系數大部分是零;量化後的資料經過zig-zag掃描,DC系數附近的非零系數值較大,而高頻位置上的非零系數值大部分是+1和-1;相鄰的4×4塊的非零系數的數目是相關的。CAVLC就是利用編碼後殘差資料的這些特性,通過自适應對不同碼表的選擇,利用較少的編碼資料對殘差資料進行無損的熵編碼,進一步減少了編碼資料的備援和相關性,提高了H.264的壓縮效率。
CAVLC-CAVLC編碼流程
視訊圖像在經過預測、變換和量化編碼後,需要經過Zig-zag掃描和重新的排序過程,為後序的CAVLC編碼進行準備。一個殘差資料塊的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熵解碼是上述CAVLC熵編碼的逆過程,CAVLC熵解碼的輸入資料是來自片層資料的比特流,解碼的基本機關是一個4×4的像素塊,輸出為包含4×4塊每個像素點所有幅值的序列。CAVLC解碼步驟如下:
1. 初始化所有的系數幅值
2. 解碼非零系數個數(TotalCoeff)和拖尾系數個數(TrailingOnes)。
3. 解碼拖尾系數符号(trailing_ones_sign_flag)
4. 解碼非零系數幅值
5. 解碼total_zeros和run_before
6. 組合非零系數幅值和遊程資訊,得到整個殘差資料塊
H.264 CAVLC編解碼詳解 2007-04-20 21:53
CAVLC: Context-Adaptive Variable-Length Coding 用于亮度和色度殘差資料的編碼. 經過變化量化後的殘差有如下特性:非零系數集中在低頻部分,而高頻系數大部分是零; 量化後的資料經Zig-Zag掃描後DC系數附近的非零系數數值較大,而高頻位置上的非零系數值大部分是+1和-1;相鄰4*4塊的非零系數的數位是相關的. CAVLC模型選擇主要展現:非零系數編碼所需表格的選擇和拖尾系數字尾長度的更新; 編碼過程 1.初始化 确定NC值 NC則與目前塊左邊非零系數數目NA和上面塊的非零系數數目NB求得的: NC = (NA+NB)/2 NA,NB都可以能是沒有的(處于邊緣時),此時以0計之 色度的直流系數NC=-1 NC值與表格的對應 0-1:Num-VLC0 2-3:Num-VLC1 4-7:Num-VLC2 >=8:FLC(定長表格) 2.編碼非零系數的數目(TotalCoeffs: 0-16)和拖尾系數數目(TrailingOnes: 0-3) 兩個數目的編碼過程是通過查表實作的,而表格的選擇是根據變量NC(Number Current,目前塊值); 3.編碼每個拖尾系數的符号 符号用一個比特表示 0表示+ 1表示- 4.編碼除了拖尾系數之外的非零系數的幅值Levels 過程: 1)将有符号的Level[i]轉換成無符号的LevelCode Level[i]為正, levelCode=(Level[i]<<1)-2; Level[i]為負, levelCode=-(Level[i]<<1)-1; 2)計算level_prefix level_prefix=levelCode/(1<<suffixLength), 查标準中的表9-6可得對應的bit_string 3)計算level_suffix level_suffix=levelCode%(1<<suffixLength) 4)根據suffixLength的值确定字尾的長度 5)更新suffixLength if(suffixLength == 0)++suffixLength; else if(levelCode > (3<<suffixLength-1) && suffixLength < 6)++suffixLength 5.編碼最後一個非零系數前零的數目TotalZeros 查标準中的表9-7 6.編碼每個非零系數前零的個數RunBefore 查标準中的表9-10 解碼過程 1.初始化 确定NC值 2.确定TotalCoeffs和TrailingOnes 根據NC值和coeff_token值查表9-5 3.根據TrailingOnes确定拖尾1的符号 4.解析除拖尾系數之外的非零系數的幅值(解碼Levels) 1)确定suffixLength初始值 if(TotalCoeffs>10 && TrailingOnes<3)suffixLength=1; else suffixLength=0; 2)計算得到Level[i] 先得到level_prefix(形式如{0}^*1: n個0後一個1) 根據suffixLength讀取編碼中的suffixLength位作為level_suffix levelCode=level_prefix<<suffixLength+level_suffix if(levelCode%2==0)Level[i]=(levelCode+2)/2 else Level[i]=(-levelCode-1)/2 3)更新suffixLength if(suffixLength == 0)++suffixLength; else if(levelCode > (3<<suffixLength-1) && suffixLength < 6)++suffixLength 5.根據得到的TotalCoeff數值和TotalZeros編碼值得到TotalZeros數值 6.解析每個非零系數前零的數目(解碼RunBefore) zeroleft = TotalZeros; i=TotalCoeffs-1; while(zeroleft>0 && i>0) { 根據zeroleft查找比對的run_before[i]值; i=i-1; zeroleft = zeroleft-runbefore[i]; if(zeroleft==0 || i==0) { run_before[i]=zeroleft; break; } } 例子:假定用了Num-VLC0表 4*4 Block { 0,3,-1,0; 0,-1,1,0; 1,0,0,0; 0,0,0,0; } Reodered block: 0,3,0,1,-1,-1,0,1,0,... TotalCoeffs=5 TotalZeros=3 TrailingOnes=3 編碼 Element Value Code coeff_token TotalCoeffs=5, T1s=3 0000100;根據coeff_token到TotalCoeff和TrailingOnes映射表 T1 sign (4) + 0 T1 sign (3) - 1 T1 sign (2) - 1 Level (1) +1 (use Level_VLC0) 1; Level (0) +3 (use Level_VLC1) 0010; TotalZeros 3 111;TotalCoeff和TotalZeros的映射表 run_before(4) ZerosLeft=3; run_before=1 10;run_before表 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=1 No code required; last coefficient. Decoding: Code Element Value Output array 0000100 coeff_token TotalCoeffs=5, T1s=3 Empty 0 T1 sign + 1 1 T1 sign - -1, 1 1 T1 sign - -1, -1, 1 1 Level +1 1, -1, -1, 1 0010 Level +3 3, 1, -1, -1, 1 111 TotalZeros 3 3, 1, -1, -1, 1 10 run_before 1 3, 1, -1, -1, 0, 1 1 run_before 0 3, 1, -1, -1, 0, 1 1 run_before 0 3, 1, -1, -1, 0, 1 01 run_before 1 3, 0, 1, -1, -1, 0, 1 |
[轉貼] CAVLC編碼過程詳解——Sunrise
謹以此文獻給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 == 5 && TrailingOnes == 3 && 0 <= NC < 2)
coeff_token = 0000 100;
Code = 0000 100;
3) 編碼所有TrailingOnes的符号:
逆序編碼,三個拖尾系數的符号依次是+(0),-(1),-(1);
即:
TrailingOne sign[i--] = 0;
TrailingOne sign[i--] = 1;
TrailingOne sign[i--] = 1;
Code = 0000 1000 11;
4) 編碼除了拖尾系數以外非零系數幅值Levels:
過程如下:
(1)将有符号的Level[ i ]轉換成無符号的levelCode;
如果Level[ i ]是正的,levelCode = (Level[ i ]<<1) – 2;
如果Level[ i ]是負的,levelCode = - (Level[ i ]<<1) – 1;
(2)計算level_prefix:level_prefix = levelCode / (1<<suffixLength);
查表9-6可得所對應的bit string;
(3)計算level_suffix:level_suffix = levelCode % (1<<suffixLength);
(4)根據suffixLength的值來确定字尾的長度;
(5)suffixLength updata:
If ( suffixLength == 0 )
suffixLength++;
else if ( levelCode > (3<<suffixLength-1) && suffixLength <6)
suffixLength++;
回到例子中,依然按照逆序,Level[i--] = 1;(此時i = 1)
levelCode = 0;level_prefix = 0;
查表9-6,可得level_prefix = 0時對應的bit string = 1;
因為suffixLength初始化為0,故該Level沒有字尾;
因為suffixLength = 0,故suffixLength++;
Code = 0000 1000 111;
編碼下一個Level:Level[0] = 3;
levelCode = 4;level_prefix = 2;查表得bit string = 001;
level_suffix = 0;suffixLength = 1;故碼流為0010;
Code = 0000 1000 1110 010;
i = 0,編碼Level結束。
5)編碼最後一個非零系數前零的數目(TotalZeros):
查表9-7,當TotalCoeffs = 5,total_zero = 3時,bit string = 111;
Code = 0000 1000 1110 0101 11;
6) 對每個非零系數前零的個數(RunBefore)進行編碼:
i = TotalCoeffs = 5;ZerosLeft = Total_zeros = 3;查表9-10:
依然按照逆序編碼
ZerosLeft =3, run_before = 1 run_before[4]=10;
ZerosLeft =2, run_before = 0 run_before[3]=1;
ZerosLeft =2, run_before = 0 run_before[2]=1;
ZerosLeft =2, run_before = 1 run_before[1]=01;
ZerosLeft =1, run_before = 1 run_before[0]不需要碼流來表示
Code = 0000 1000 1110 0101 1110 1101;
編碼完畢。
----------------------------------Sunrise------
[[i] 本帖最後由 firstime 于 2008-9-4 02:49 PM 編輯 [/i]]
davesliu 發表于 2006-11-20 12:03 PM
請問斑竹,老畢書上120頁表6.10中第2行和第三行的NA和NB是不是給弄反了?
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
倒數三個 +/-1
另:可結合本論壇文章“[url=http://bbs.chinavideo.org/viewthread.php?tid=1057][color=blue][b]CAVLC中的字首和字尾[/b][/color][/url]”學習。
[[i] 本帖最後由 firstime 于 2008-5-20 09:59 PM 編輯 [/i]]
achen 發表于 2007-1-17 05:50 PM
“如果Level[ i ]是負的,levelCode = - (Level[ i ]<<1) – 1”
假如Level[ i ]=-3,levelCode值是多少啊?
還有能不能詳細講解一下如何确定suffixLength的取值,它跟域值的關系怎麼确定?
謝謝!
[[i] 本帖最後由 firstime 于 2007-1-27 10:06 PM 編輯 [/i]]
dcfarmer 發表于 2007-1-28 11:22 AM
讨論
是JM86運作後的trace記錄
@160 Luma # c & tr.1s(0,0) vlc=0 #c=11 #t1=1 000000000001110 ( 11)
@175 Luma trailing ones sign (0,0) 0 ( 0)
@176 Luma lev (0,0) k=9 vlc=1 lev= -2 11 ( -1)
@178 Luma lev (0,0) k=8 vlc=1 lev= -1 11 ( -1)
@180 Luma lev (0,0) k=7 vlc=1 lev= 1 10 ( 1)
@182 Luma lev (0,0) k=6 vlc=1 lev= -5 000011 ( -5)
@188 Luma lev (0,0) k=5 vlc=2 lev=-11 00000101 (-11)
@196 Luma lev (0,0) k=4 vlc=3 lev= -3 1101 ( -3)
@200 Luma lev (0,0) k=3 vlc=3 lev= 3 1100 ( 3)
@204 Luma lev (0,0) k=2 vlc=3 lev= 3 1100 ( 3)
@208 Luma lev (0,0) k=1 vlc=3 lev=-12 001111 (-12)
@214 Luma lev (0,0) k=0 vlc=3 lev= 9 001000 ( 9)
上面這段是trace.txt中的記錄,根據前面的vlc=0 #c=11 #t1=1,就是trailing_ones 的個數是1個(@175 Luma trailing ones sign (0,0) 0 ( 0) )也能說明,但是在下面的level中,明明還要-1,1可以作為trailing_ones的,為什麼不把這兩個數當作trailing_ones呢??而是作為level呢????
dcfarmer 發表于 2007-1-28 11:40 AM
回答
接上面的問題,在群裡面讨論弄明白了
在CAVLC編碼中,從右往左看,算trailing_ones時,在右邊不能有abs()>1的數,還有算trailing_ones的個數的話,也不能被别的abs()>1的數隔斷,如果隔斷的話,隻能計數到隔斷數為止,而且要保證個數<=3。例如。。。。2,3,0,0,0,1,1,1,0,0,2 ,這個序列中triailing_ones是沒有的。還有。。。。。1,0,0,-1,3,1,0,0,0,0,算trailing_ones 個數時,隻能有一個也就是3後面的“1”算一個,3前面的-1和1由于被3隔斷,是以不能算入其中。
[[i] 本帖最後由 dcfarmer 于 2007-6-7 03:27 PM 編輯 [/i]]
zjh1004 發表于 2007-8-26 09:55 PM
ZerosLeft =1, run_before = 1 run_before[0]不需要碼流來表示
請問:為什麼不需要碼流來表示?
是因為是最後一位嗎?
查表知:此時 run_before[0]=0
timek 發表于 2007-9-11 06:35 PM
回複 #6 achen 的文章
Level[ i ]=-3,levelCode值是多少啊?
levelcode應該是3
grassland_fly 發表于 2007-9-12 03:47 PM
[size=12px]ZerosLeft =1, run_before = 1 run_before[0]不需要碼流來表示
請問:為什麼不需要碼流來表示?
是因為是最後一位嗎?
查表知:此時 run_before[0]=0[/size]
[size=12px] [/size]
[size=12px]---------------------------------------------------------[/size]
[size=12px] [/size]
[size=12px]I think it'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記錄
@160 Luma # c & tr.1s(0,0) vlc=0 #c=11 #t1=1 000000000001110 ( 11)
@175 Luma trailing ones sign (0,0) 0 ( 0)
@176 Luma lev (0,0) k=9 vlc=1 lev= -2 11 ( -1)
@178 Luma lev (0,0) k=8 vlc=1 lev= -1 11 ( -1)
@180 Luma lev (0,0) k=7 vlc=1 lev= 1 10 ( 1)
@182 Luma lev (0,0) k=6 vlc=1 lev= -5 000011 ( -5)
@188 Luma lev (0,0) k=5 vlc=2 lev=-11 00000101 (-11)
@196 Luma lev (0,0) k=4 vlc=3 lev= -3 1101 ( -3)
@200 Luma lev (0,0) k=3 vlc=3 lev= 3 1100 ( 3)
@204 Luma lev (0,0) k=2 vlc=3 lev= 3 1100 ( 3)
@208 Luma lev (0,0) k=1 vlc=3 lev=-12 001111 (-12)
@214 Luma lev (0,0) k=0 vlc=3 lev= 9 001000 ( 9)
請問上面這段TRACE記錄對應的16個系數序列是什麼?謝謝
boleaon 發表于 2007-11-8 04:46 PM
好貼...
kdjwang 發表于 2008-10-10 03:52 PM
回複 #10 timek 的文章
levelcode應該是5,level先乘2為-6,再取反減1
解碼時因為levelcode為奇數,level = (‐levelCode‐1)/2
libra811 發表于 2008-12-29 10:44 PM
剛學習,受用了。謝謝
peiyangpr 發表于 2009-1-4 05:28 PM
畢厚傑P123,6.8.6節中所給例子是否有誤?是否應如下編碼?
0 0 -1 0
5 2 0 0
3 0 0 0
1 0 0 0
資料重排:0,0,5,3,2,-1,0,0,0,1……
其中TotalCoeffs = 5, TrailingOnes = 2, Total_zeros = 5, NC = 3
編碼過程:
元素 數值 編碼
Coeff_token TotalCoeffs = 5, TrailingOnes = 2 0000101
Trailingones_sign_flag(1) + 0
Tranlingones_sign_flag(0) - 1
Level(2) 1 (suffixLenth = 0) 1
Level(1) 3 (suffixLength = 1) 0010
Level(0) 5 (suffixLength = 1) 000010
Total_zeros 5 101
Run_before(4) Zeroleft = 5, run_before = 3 010
Run_before(4) Zeroleft = 2, run_before = 0 1
Run_before(4) Zeroleft = 2, run_before = 0 1
Run_before(4) Zeroleft = 2, run_before = 0 1
Run_before(4) Zeroleft = 2, run_before = 2 無
最終編碼輸出:000010110010000010101010111
[[i] 本帖最後由 peiyangpr 于 2009-1-4 09:16 PM 編輯 [/i]]
residual_block_cavlc( coeffLevel, maxNumCoeff ) { C Descriptor
for( i = 0; i < maxNumCoeff; i++ )
coeffLevel[ i ] = 0
// coeff_token 指明了非零系數的個數,拖尾系數的個數。
coeff_token
if( TotalCoeff( coeff_token ) > 0 ) {
if( TotalCoeff( coeff_token ) > 10 && TrailingOnes( coeff_token ) <
3 )
suffixLength = 1
else
suffixLength = 0
for( i = 0; i < TotalCoeff( coeff_token ); i++ )
if( i < TrailingOnes( coeff_token ) ) {
// trailing_ones_sign_flag 拖尾系數的符号
- 如果trailing_ones_sign_flag = 0, 相應的拖尾系數是+1。
- 否則,trailing_ones_sign_flag =1,相應的拖尾系數是-1。
trailing_ones_sign_flag
level[ i ] = 1 – 2 * trailing_ones_sign_flag
} else {
// level_prefix and level_suffix 非零系數值的字首和字尾。
level_prefix
levelCode = ( level_prefix << suffixLength )
if( suffixLength > 0 | | level_prefix >= 14 ) {
level_suffix
levelCode += level_suffix
}
if( level_prefix = = 15 && suffixLength = = 0 )
levelCode += 15
if( i = = TrailingOnes( coeff_token ) &&
TrailingOnes( coeff_token ) < 3 )
levelCode += 2
if( levelCode % 2 = = 0 )
level[ i ] = ( levelCode + 2 ) >> 1
else
level[ i ] = ( –levelCode – 1 ) >> 1
if( suffixLength = = 0 )
suffixLength = 1
if( Abs( level[ i ] ) > ( 3 << ( suffixLength – 1 ) ) &&
suffixLength < 6 )
suffixLength++
}
if( TotalCoeff( coeff_token ) < maxNumCoeff ) {
// total_zeros 系數中 0 的總個數。
total_zeros
zerosLeft = total_zeros
} else
zerosLeft = 0
for( i = 0; i < TotalCoeff( coeff_token ) – 1; i++ ) {
if( zerosLeft > 0 ) {
run_before
run[ i ] = run_before
} else
run[ i ] = 0
zerosLeft = zerosLeft – run[ i ]
}
run[ TotalCoeff( coeff_token ) – 1 ] = zerosLeft
coeffNum = -1
for( i = TotalCoeff( coeff_token ) – 1; i >= 0; i-- ) {
coeffNum += run[ i ] + 1
coeffLevel[ coeffNum ] = level[ i ]
}
}
}
本文來自CSDN部落格,轉載請标明出處:http://blog.csdn.net/xfding/archive/2010/04/12/5477464.aspx