基於上下文的自適應變長編碼CAVLC原理與流程

CAVLC -CAVLC概念

AVLC的全稱是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熵編碼處理流程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. 組合非零係數幅值和遊程信息,得到整個殘差數據塊




假設有一個 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 
 
查標準( BSISO/IEC 14496-10:2003 ) Table9-5 ,可得: 
If (TotalCoeffs == 5 &&TrailingOnes == 3 && 0<= NC < 2)
coeff_token = 0000 100;
Code output = 0000100; 

3
 ) 編碼所有 TrailingOnes 的符號: 
逆序編碼,三個拖尾係數的符號依次是+( 0 ),-( 1 ),-( 1 ); 
 :
TrailingOne sign[i--] = 0;
TrailingOne sign[i--] = 1;
TrailingOne sign[i--] = 1;
Code output = 0000100    011;
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 可得所對應的 bitstring  
( 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++;
     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 output = 0000 100011 1; 
編碼下一個Level:Level[0] = 3;
levelCode = 4;level_prefix = 2;查表得bit string = 001;
level_suffix = 0;suffixLength = 1;故碼流爲0010;
Code output = 0000 100011 1 0010 ; 
i = 0,編碼Level結束。

5)編碼最後一個非零係數前零的數目(TotalZeros):
查表9-7,當TotalCoeffs = 5,total_zero = 3時,bit string = 111;
Code output = 0000 100011 1 0010 111;

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] 最後一個非零係數不需要碼流來表示
Codeoutput = 0000 100 011 1 0010 111 10 1 1 01; 
編碼完畢。(CAVLC主要是查表,標準中的表是通過大量實驗的出來的!)

 

 

解碼過程:


接收碼流爲:0000 1000 1110 0101 1110 1101

計算NC = 1 
解碼詳細過程如下:
1.      根據Coeff_token和NC查表(見標準表9-5),得到非零係數數目TotalCoeffs和拖尾係數數目TrailingOnes
NC = 1選擇對應的表,Coeff_token爲0000100,查表得到TotalCoeffs=5TrailingOnes=3
輸出序列:無
2. 解析拖尾係數
由第一步得到拖尾係數有3個,輸入拖尾係數符號編碼碼流011,得到兩個拖尾係數由先到後是 -1,-1,1
output:-1,-1,1(反序輸出) 
3.      解析除拖尾係數外的非零係數的幅值(level)
(1)      確定後綴長度SuffixLength
(2)      根據碼流查表9-6得到前綴LevelPrefix
(3)      根據前綴和後綴,得到

             LevelCode=(levelprefix<<suffixlength)+levelsuffix
(4)      Levelcode爲偶數 level=(level+2)/2
               Levelcode爲奇數 level=(-level-1)/2
(5)      根據設定的閾值確定是否update Suffixlegth
回到例子中,按照逆序
i=0, Sufixlegth=0,查表9-6,1對應的前綴levelprefix=0,levelcode=0,

    計算得到level=1 , i++ , sufixlegth++(第一次都要加) 
i=1,sufixlegth=1,查表0010(3爲前綴,1位後綴 )對應的前綴     levelprefix=2,計算levelcode=4,level=3,i++
i=2 >=TotalCoeffs-TrailingOnes,除拖尾係數外的非零係數解析完畢
output:3,1,-1,-1,1 
4.      解析每個非零係數前零的個數
根據TotalCoeffs=5和輸入碼流111查表9-7得到TotalZeros=3
初始i=TotalCoeffs-1=4 ,zeroleft=TotalZeros=3 , 5個非零係數前零的數目解析如下:
i=4,zeroleft=3,根據碼流10,查表9-10,runbefor=1,

   輸出序列:3,1,-1,-1,0,1
i=3,zeroleft=3-1=2,根據碼流1,查表runbefore=0,

輸出序列:3,1,-1,-1,0,1
i=2,zeroleft=2-0=2,根據碼流1,查表runbefore=0,

輸出序列:3,1,-1,-1,0,1
i=1,zeroleft=2-0=2,根據碼流01,查表runbefore=1,

輸出序列:3,0,1,-1,-1,0,1
i=0,zeroleft=2-1=1,輸出序列:0,3,0,1,-1,-1,0,1
5. 解碼完畢,將剩下的元素用0補齊,反序排列就可以得到4*4矩陣。
6. 最後還原爲一個4*4數據塊
{
   0 , 3 , -1 , 0,
  0,   -1 ,1,   0,
   1,   0 , 0 , 0,
   0,   0 , 0 , 0
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章