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繼續編碼下一個非零系數。更新過程可以用下面代碼表示:
1 if(SuffixLength == 0) 2 SuffixLength++; 3 else if (abs(Level) > (3 << (SuffixLength -1)) && SuffixLength < 6) 4 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
三、附表: