CAVLC


在H.264標准中,CAVLC(Context-based Adaptive Variable Length Coding)被用於亮度和色度殘差數據編碼。在標准的碼流結構中,CAVLC編碼方式描述為ce(v)。如果在編碼時采用CAVLC,那么盡管在DCT時是以8x8塊為單位進行的,在進行CAVLC時也會強制采用4x4塊為單位進行編碼(請參考h.264語法結構分析中的redisual_luma部分)。在進行熵編碼之前,需要把4x4塊的矩陣中的元素按照一定順序重新排列成大小為16的序列,這部分工作請參考h.264 scanning process for transform coefficients,熵編碼就是以這些序列為基礎進行編碼的。

 

CAVLC可分為五個部分

  1. 編碼非零系數的數目(TotalCoeffs)、拖尾系數(TrailingOnes)的數目
  2. 對每個拖尾系數(TrailingOnes)的符號進行編碼
  3. 對除了拖尾系數之外的非零系數(level)進行編碼
  4. 對最后一個非零系數前面零的數目(TotalZeros)進行編碼
  5. 對每個非零系數前面連續的零的個數(RunBefore)進行編碼

 

 

一. 編碼TotalCoeffs,TrailingOnes

TotalCoeffs:非零系數數目。變換系數level中所有不為0的level的數目。

TrailingOnes:拖尾系數的數目。指的是矩陣重排列后,序列末尾連續出現的$\pm 1$的個數(中間可以間隔任意多個0)。如果$\pm 1$的個數大於3個,則只有最后三個$\pm 1$會被視為拖尾系數,其余的被視為普通的非零系數。

我們用一個例子來解釋TotalCoeffs以及TrailingOnes

image

在上述例子中,共有5個非零系數,因此TotalCoeffs為5;其中有四個非零系數都是$\pm 1$,只有最后三個被視為拖尾系數,因此TrailingOnes為3。

 

TotalCoeffs與TrailingOnes是一起進行編碼的,編碼通過查表的方式進行,碼表請參考標准中的表9-5。

用來編碼TotalCoeffs與TrailingOnes的碼表,需要根據變量nC的值進行選擇。除了色度的直流系數(Chroma DC)之外,其它系數類型的nC值是根據當前塊左邊4x4塊的非零系數數目(nA)和當前塊上邊4x4塊的非零系數數目(nB)求得。nC的求值過程見下表。其中“X”表示與當前塊同屬於一個slice並可用。

上邊的塊(nB) 左邊的塊(nA) nC
X X (nA+nB)/2
X - nB
- X nA
- - 0

另外,如果輸入的系數是色度的直流系數(而且視頻格式為4:2:0),nC=-1;如果輸入的系數是色度的直流系數(而且視頻格式為4:2:2),nC=-2。

 

原理上說,nC代表了當前塊的相鄰塊非零系數的情況,由於塊與塊之間的相關性,當我們知道了相鄰塊的非零系數的情況,就有很大概率知道了當前塊非零系數的情況。有了概率,h.264標准在這里引入了huffman編碼,不過huffman的編碼過程不用我們自己處理,h.264標准已經根據實驗得到的數據組合成了碼表9-5。

 

 

二. 對TrailingOnes的符號進行編碼

在第一步時,對於TrailingOnes,我們只編碼了它的個數。在這一步,我們對TrailingOnes的符號進行編碼。對每個拖尾系數需要用一個bit表示它的符號。標准規定用0表示“+”,用1表示“-”。編碼順序是按照掃描逆序進行,也就是從高頻數據開始。

image

 

 

三. 對除了拖尾系數之外的非零level進行編碼

除了拖尾系數之外的非零level可能會有多個,編碼順序與上述TrailingOnes的一樣,也是逆序進行。

image

 

對於每個level,編碼結果包括兩部分:前綴(level_prefix)和后綴(level_suffix)。

image

其中前綴的碼值需要根據level_prefix的值去參照標准中的表9-6,后綴的碼值就是level_suffix的二進制。

 

如何計算level_prefix以及level_suffix的這部分比較麻煩,我們有必要照着標准分析。

標准規定了CAVLC解碼的步驟如下,我們能根據這些步驟逆推出它的編碼步驟

  1. The syntax element level_prefix is decoded as specified in clause 9.2.2.1.
  2. The variable levelSuffixSize is set as follows:
    • If level_prefix is equal to 14 and suffixLength is equal to 0, levelSuffixSize is set equal to 4.
    • Otherwise, if level_prefix is greater than or equal to 15, levelSuffixSize is set equal to level_prefix − 3.
    • Otherwise, levelSuffixSize is set equal to suffixLength.
  3. The syntax element level_suffix is decoded as follows:
    • If levelSuffixSize is greater than 0, the syntax element level_suffix is decoded as unsigned integerrepresentation u(v) with levelSuffixSize bits.
    • Otherwise (levelSuffixSize is equal to 0), the syntax element level_suffix is inferred to be equal to 0.
  4. The variable levelCode is set equal to ( Min( 15, level_prefix ) << suffixLength ) + level_suffix.
  5. When level_prefix is greater than or equal to 15 and suffixLength is equal to 0, levelCode is incremented by 15.
  6. When level_prefix is greater than or equal to 15( 16 is the same ), levelCode is incremented by (1<<( level_prefix − 3 )) − 4096.
  7. When the index i is equal to TrailingOnes( coeff_token ) and TrailingOnes( coeff_token ) is less than 3, levelCode is incremented by 2.
  8. The variable levelVal[ i ] is derived as follows:
    • If levelCode is an even number, levelVal[ i ] is set equal to ( levelCode + 2 ) >> 1.
    • Otherwise (levelCode is an odd number), levelVal[ i ] is set equal to ( −levelCode − 1) >> 1.
  9. When suffixLength is equal to 0, suffixLength is set equal to 1.
  10. When the absolute value of levelVal[ i ] is greater than ( 3 << ( suffixLength − 1 ) ) and suffixLength is less than 6, suffixLength is incremented by 1.
  11. The index i is incremented by 1.

下面的分析過程會把這些步驟記為“上述步驟”

 

1. 有符號的level轉換為無符號的levelCode

我們知道level的值可能為正,也可能為負,是帶符號的,我們需要把它轉化成無符號的levelCode以供后面的計算。根據(上述步驟8),我們知道在進行CAVLC編碼時levelCode的計算方式如下:

如果$level$為正,$levelCode = (|level|-1)<<1$

如果$level$為負,$levelCode = (|level|-1)<<1+1$

因此有以下轉換

image

可以看到正的level變成了levelCode中的偶數部分,負的level變成了levelCode的奇數部分,及levelCode中的最低位的bit代表符號。

此外,存在一種需要調整levelCode大小的情況:如果當前編碼塊的拖尾系數的個數小於3(TrailingOnes < 3),那么需要對第一個進行編碼的非零非拖尾level的levelCode減去2(上述步驟7)。

 

 

2. levelCode的拆分

這部分我們首先需要了解這兩個變量的含義

levelSuffixSize: 后綴level_suffix的實際長度

suffixLength: 在某些情況下,levelSuffixSize等於這個變量的值,suffixLength也用於計算前綴的值,它貫穿於整個CAVLC編碼過程,會在一個宏塊開始編碼level時進行初始化(見下述標准中抓取的部分),並且在每個level編碼后進行更新。

  • If TotalCoeff( coeff_token ) is greater than 10 and TrailingOnes( coeff_token ) is less than 3, suffixLength is set
    equal to 1.
  • Otherwise (TotalCoeff( coeff_token ) is less than or equal to 10 or TrailingOnes( coeff_token ) is equal to 3),
    suffixLength is set equal to 0.

suffixLength會根據宏塊的非零系數以及拖尾系數的個數被初始化為0或者1;在每個level編碼后的更新,如果此時suffixLength為0,則suffixLength加1(上述步驟9),這意味着我們在對一個宏塊進行CAVLC時,最多只有一次是suffixLength為0;如果此時已被編碼的level大於( 3 << ( suffixLength − 1 ) ) ,suffixLength也會加1(上述步驟10)。

 

根據上述標准,在拆分levelCode上我們可以分成兩大類情況

1. suffixLength為0

其中可細分為三種情況進行編碼

①  如果$| level |<8$,那么levelSuffixSize為0,也就是沒有后綴level_suffix,這些level分別為$\pm 1,\pm 2,…,\pm 7$,這14個level由$level\_prefix = 0,1,2,…,13$表示。(上述步驟2第3條)

②  如果$8 \leqslant | level |<16$,那么levelSuffixSize為4,也就是后綴共有四個bit,也就是說后綴能表達16個level,由於此時固定了$level\_prefix=14$,因此這種情況能表達的level就是16個,即$\pm 8,\pm 9,…,\pm 15$。(上述步驟2第1條)

③  否則,$level\_prefix\geqslant 15$。這種情況下,levelSuffixSize的值為level_prefix-3(上述步驟2第2條)。此時levelCode在解碼端的計算方法為

$\begin{align*}
levelCode
&=\underbrace{ \left\lfloor 15, level\_prefix \right\rfloor << suffixLength  + level\_suffix}_{step.4} \quad \underbrace{+15}_{step.5}\quad \underbrace{+1<<(level\_prefix-3)-4096}_{step.6}\\
&=15<<0+level\_suffix+15+ 1<<(level\_prefix-3)-4096\\
&=level\_suffix+1<<(level\_prefix-3)-4066
\end{align*}$ 

其中level_prefix的值為$15,16,…$,在編碼端,levelCode已知,我們只需要按照遞增順序一個一個帶入level_prefix,並且保證level_suffix在其所占用 的bit的范圍內,即可得出合適的level_prefix與level_suffix。

 

JM18.6代碼如下

int writeSyntaxElement_Level_VLC1(SyntaxElement *se, DataPartition *dp, int profile_idc)
{
  int level  = se->value1;
  int sign   = (level < 0 ? 1 : 0);
  int levabs = iabs(level);

  if (levabs < 8)      //① level_prefix < 14        ((8-1)*2 - len(inf) = 14 - 1)
  {
    se->len = levabs * 2 + sign - 1;
    se->inf = 1;
  }
  else if (levabs < 16)    //② level_prefix = 14        (len - len(inf) = 19 - len(16) = 19 - 5 = 14)
  {
    // escape code1
    se->len = 19;
    se->inf = 16 | ((levabs << 1) - 16) | sign;
  }
  else     //③ level_prefix >= 15           (28 - len(4096) + numPrefix = 15 + numPrefix)
  {
    int iMask = 4096, numPrefix = 0;
    int levabsm16 = levabs + 2032;

    // escape code2   select level_prefix, which level_prefix = 15 + numPrefix
    if ((levabsm16) >= 4096)
    {
      numPrefix++;
      while ((levabsm16) >= (4096 << numPrefix))
      {
        numPrefix++;
      }
    }
   
    iMask <<= numPrefix;
    se->inf = iMask | ((levabsm16 << 1) - iMask) | sign;
  /*  caution : int levabs = iabs(level);
   *
   *    (levabsm16 << 1) - iMask
   *  = ((levabs + 2032) << 1) - (4096 << numPrefix)
   *  = ((iabs(level) + 2032) << 1) - (4096 << numPrefix)
   *  = ((iabs(level) - 1 + 2033) << 1) - (1 << levelSuffixSize)
   *  = ((iabs(level) - 1 + 2048 - 15) << 1) - (1 << levelSuffixSize)
   *  = ((iabs(level) - 1 - 15) << 1) + 4096 - (1 << levelSuffixSize)
   *  = ((iabs(level) - 1 - 15) << 1) - ((1 << levelSuffixSize) - 4096)
   *
   *  belonging to above,you can find the relationship from the spec
   */

    /* Assert to make sure that the code fits in the VLC */
    /* make sure that we are in High Profile to represent level_prefix > 15 */
    if (numPrefix > 0 && !is_FREXT_profile( profile_idc ))
    {
      //error( "level_prefix must be <= 15 except in High Profile\n",  1000 );
      se->len = 0x0000FFFF; // This can be some other big number
      return (se->len);
    }
    
    se->len = 28 + (numPrefix << 1);
  }

  symbol2vlc(se);

  writeUVLC2buffer(se, dp->bitstream);

  if(se->type != SE_HEADER)
    dp->bitstream->write_flag = 1;

#if TRACE
  if(dp->bitstream->trace_enabled)
    trace2out (se);
#endif

  return (se->len);
}

 

2. suffixLength為N (N = 1,2,3,4,5,6)

suffixLength不為0的情況下,可細分為以下兩種編碼過程

①  如果$levelCode<( 15<< suffixLength )$,為什么這里會有個15呢?因為15表明當前level所需要的level_prefix小於15,即(0~14)共15個數值。此時

$\begin{Bmatrix}
levelSuffixSize=suffixLength\qquad\qquad\qquad\qquad\qquad\quad & (step.2.1) \\
levelCode = level\_prefix<<suffixLength + level\_suffix & (step.4)
\end{Bmatrix}$

按照這兩個條件,得到

$\begin{Bmatrix}
level\_prefix &= &levelCode >> suffixLength \\
level\_suffix &= &levelCode \ \& \ \underbrace{11...1}_{suffixLength}
\end{Bmatrix}$

 

②  否則,表明編碼需要的level_prefix大於或等於15。$levelSuffixSize = level\_prefix-3 \quad $(上述步驟2第2條)。解碼規定了此時的levelCode為

$\begin{align*}
levelCode
&=\underbrace{ \left\lfloor 15, level\_prefix \right\rfloor << suffixLength  + level\_suffix}_{step.4} \quad \underbrace{+1<<(level\_prefix-3)-4096}_{step.6}\\
&=15<<suffixLength + level\_suffix + 1<<(level\_prefix-3)-4096
\end{align*}$ 

在編碼level端,levelCode與suffixLength已知,我們只需要按照遞增順序一個一個帶入level_prefix,並且保證level_suffix在其所占用 的bit的范圍內,即可得出合適的level_prefix與level_suffix。

 

JM18.6代碼如下

int writeSyntaxElement_Level_VLCN(SyntaxElement *se, int vlc, DataPartition *dp, int profile_idc)
{  
  int level  = se->value1;
  int sign   = (level < 0 ? 1 : 0);
  int levabs = iabs(level) - 1;  

  int shift = vlc - 1;        
  int escape = (15 << shift);    //level_prefix = 15

  if (levabs < escape)   //① level_prefix < 15
  {
    int sufmask   = ~((0xffffffff) << shift);
    int suffix    = (levabs) & sufmask;

    se->len = ((levabs) >> shift) + 1 + vlc;
    se->inf = (2 << shift) | (suffix << 1) | sign;
  }
  else  // ② 
  {
    int iMask = 4096;
    int levabsesc = levabs - escape + 2048;  //+2048 * 2 = +4096 , on decode endpoint, would -4096
    int numPrefix = 0;

    if ((levabsesc) >= 4096)  //level_prefix = 15 + numPrefix
    {
      numPrefix++;
      while ((levabsesc) >= (4096 << numPrefix))
      {
        numPrefix++;
      }
    }

    iMask <<= numPrefix;
    se->inf = iMask | ((levabsesc << 1) - iMask) | sign;
  /*  caution : levabs = iabs(level) - 1;  
   * 
   *    (levabsesc << 1) - iMask
   *  = ((levabs - escape + 2048) << 1) - (4096 << numPrefix)
   *  = ((iabs(level) - 1 - escape) << 1) + 4096 - (1 << levelSuffixSize)
   *  = ((iabs(level) - 1 - escape) << 1) - ((1 << levelSuffixSize) - 4096)
   *
   *  belonging to above,you can find the relationship from the spec
   */

    /* Assert to make sure that the code fits in the VLC */
    /* make sure that we are in High Profile to represent level_prefix > 15 */
    if (numPrefix > 0 &&  !is_FREXT_profile( profile_idc ))
    {
      //error( "level_prefix must be <= 15 except in High Profile\n",  1000 );
      se->len = 0x0000FFFF; // This can be some other big number
      return (se->len);
    }
    se->len = 28 + (numPrefix << 1);
  }

  symbol2vlc(se);

  writeUVLC2buffer(se, dp->bitstream);

  if(se->type != SE_HEADER)
    dp->bitstream->write_flag = 1;

#if TRACE
  if(dp->bitstream->trace_enabled)
    trace2out (se);
#endif

  return (se->len);
}

 

3. 更新suffixLength

上面我們說過,suffixLength貫穿整個宏塊非零level的編碼過程,它在一開始會被初始化,然后在每個level編碼完成后進行更新。更新遵循兩個規則:

  • 如果suffixLength為0,則更新為1。(上述步驟9)
  • 如果當前編碼的level有$|level|>( 3<< suffixLength -1 )$,並且$suffixLength<6$,則suffixLength++。(上述步驟10)

CAVLC在編碼非零level這部分,首先編碼順序就是從高頻到低頻進行,一般來說高頻level會比較小,低頻level較大,因此CAVLC中的做法就是令suffixLength初始化為0,或者1,后續基於上下文對suffixLength進行增加,這樣做是為了出現頻率更高的高頻的level占用更小的bit,這也符合變長編碼的思想。

 

 

 

四. 對TotalZeros進行編碼

TotalZeros:  最后一個非零系數前零的總數目,如下圖例子為TotalZeros = 3,碼值需要根據當前宏塊的TotalCoeffs以及TotalZeros查表得到。

需要注意的是,在對Chroma DC進行CAVLC時,Chroma DC塊可能為2x2塊(4:2:0)或者2x4塊(4:2:2),這些都是要查不同的表格的。

image

 

 

五. 對RunBefore進行編碼

RunBefore:  這個值是對於非零系數來說的,表示的是當前非零系數前面連續出現的0的個數

ZeroLeft:  這個值也是對於非零系數來說的,表示的是當前非零系數前的0的總個數,在一開始這個值會被賦值為TotalZeros。當ZeroLeft為0的時候意味着后續非零系數的RunBefore都為0,編碼結束。

我們根據RunBefore與ZeroLeft的值查表9-10得到碼值。編碼順序也是逆序進行,編碼次數最多為TotalCoeffs - 1。

image

如上述例子

編碼過程如下:

ZeroLeft = 3,RunBefore = 1,碼值為10

ZeroLeft = 2,RunBefore = 0,碼值為1

ZeroLeft = 2,RunBefore = 0,碼值為1

ZeroLeft = 2,RunBefore = 2,碼值為00

ZeroLeft = 0,RunBefore = 0,不需要碼流表示


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM