理論
熵編碼
熵編碼是一種無損編碼,按照熵原理不丟失任何信息的編碼。
常見的熵編碼有:香濃(Shannon)編碼、哈夫曼(Huffman)編碼、算術(arithmetic)編碼、指數哥倫布(exponential Golomb)編碼、基於上下文的自適應變長編碼(CAVLC:Context-based Adaptive Variable Length Coding )、基於上下文的自適應二進制算術編碼(CABAC:Context-based Adaptive Binary Arithmetic Coding )。
在H264中,主要使用三種編碼方式,exp-golomb、CAVLC、CABAC。
因爲在項目中,需要解析sps,而sps RBSP語法元素主要用到了0階exp-golomb,下面介紹0階exp-golomb。
指數哥倫布編碼
指數哥倫布碼(Exponential-Golomb coding)是一種無損數據壓縮方法。
Exp-golomb編碼規則(文字描述版本)
說明:
codenum 表示 待編碼的數 x;
codeword 表示 編碼後的數;codeword的構成=[prefix][suffix]
prefix 也叫做 leadingzerobits; 二進制數
suffix 也是二進制數
編碼步驟
- codenum+1 並將其值寫成二進制形式,用suffix表示;
- 數suffix的位數,用M表示;
- prefix 由M-1個0組成;
以codenum=5爲例,
- codenum+1=6,即suffix = 0b110;
- prefix = 0b00;
- codeword = 0b00110;
我們知道,在計算機中,以字節作爲最小存儲單位(字節對齊),而字節由8個bit組成。10進制數5在計算機中的存儲爲0b00000101,可以看出實際的有效位數只有3位,另外的5位沒有用,但還是佔用了5個bit的存儲空間。所以爲了更高效地利用物理存儲空間,工程師們就想,有沒有什麼方式既能正確的表示數據又能更省存儲空間呢?那就需要設計某種“規則”,使用這種“規則”重新表示數據。而這種“規則”用專業術語表達就叫做編碼。
前面提到的指數哥倫布編碼就是萬千編碼方式中的一種,通過exp-golomb coding,將0b00000101壓縮成了0b00110,從8位壓縮到了5位,節省了3位。
從上面講解Exp-golomb編碼規則中,並沒有看到和指數相關的東西。那爲什麼這玩意叫指數哥倫布編碼呢?
其實,上面講解的編碼規則是一種特殊情況(指數k=0)。k階指數哥倫布編碼的一般規則是
- x+-1 使用 0階指數哥倫布編碼;
- 數suffix的位數,用M表示;
- prefix 由M-1個0組成;
以codenum=5, k=1爲例,
- ,6的0階指數哥倫布編碼爲00111;
- 刪除 1 個前導 0;
- codeword 爲 0b0111;
Elias gamma coding
Elias γ code or Elias gamma code is a universal code encoding positive integers developed by Peter Elias.[1]:197, 199 It is used most commonly when coding integers whose upper-bound cannot be determined beforehand.
elias gamma coding 使用 個bit來編碼表示數值。例如,數字5 使用 5個bit來表示;數字10使用7個bit來表示。
編碼規則
x 爲 待編碼數
- 找N,N爲x的2的指數的最大冪次;,即$2^N\le x < 2^{N+1} $;
- N個前導0 bits;
- 接着將x表示成2進制;
- 將#2和#3的二進制數組合 即爲編碼後的數;
以x=5爲例:
- N = 2;;
- 2個前導0,00;
- x = 0b101
- codeword = 0b00101
實踐
sps的數據語法格式如下
從sps的語法中可以看到,主要使用了4種描述子,u(1),u(n),ue(v),se(v)。
接下來,分別介紹這四種描述子及其代碼實現。
在h264標準中,有一個讀取比特流的語法函數,read_bits(n)
read_bits( n ) reads the next n bits from the bitstream and advances the bitstream pointer by n bit positions. When n is
equal to 0, read_bits( n ) is specified to return a value equal to 0 and to not advance the bitstream pointer
read_bits的作用就是 從當前比特流位置開始,讀取n個比特,同時比特流指針向前移動n位。如果n爲0,read_bits返回0,且比特流指針不移動。
Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded. Syntax elements coded as te(v) are truncated ExpGolomb-coded. The parsing process for these syntax elements begins with reading the bits starting at the current location in the bitstream up to and including the first non-zero bit, and counting the number of leading bits that are equal to 0.
This process is specified as follows:
leadingZeroBits = -1
for( b = 0; !b; leadingZeroBits++ )
b = read_bits( 1 )
在h264的語法元素中,ue(v),me(v),se(v)都是使用指數哥倫布編碼;te(v)使用截斷指數哥倫布編碼。解碼過程爲 從當前比特流位置開始讀,直到遇到第一個非0bit位,即1位,然後計算前導0的個數N(注意:比特流的指針移動到了第一個非0bit的下一位);接着再向後讀取N個bit並得到這個N個bit對應的數值;再按照公式解出原數值。
例如,5的exp-golomb coded number是 00110,第一個比特值1的前面有2個前導0,即leadingZeroBits=2;接着,第一個比特值1後的兩個比特值爲10,也就是10進制的2;所以 ,這樣就解出原數值5了。
從table9-1可以看出,"prefix"即是leadingZeroBits對應的前導0個數,"suffix"即是codeNum對應的比特值,由表示,i的範圍是[0, leadingZeroBits-1],可以是0也可以是1。
u(1)
u(1)就相當於 read_bits(1)。它的值是0或1。
u(n)
unsigned integer using n bits. When n is “v” in the syntax table, the number of bits varies in a manner
dependent on the value of other syntax elements. The parsing process for this descriptor is specified by the return
value of the function read_bits( n ) interpreted as a binary representation of an unsigned integer with most
significant bit written first.
u(n) 就相當於 read_bits(n)。u(n)的返回值是n個bit對應的正整數。注意:most significant bit written first。
ue(v)
unsigned integer Exp-Golomb-coded syntax element with the left bit first. The parsing process for this
descriptor is specified in clause 9.1
ue(v)是使用0階指數哥倫布編碼的值,所以解析sps相應的語法元素時,要使用0階指數哥倫布對應的解碼規則進行解碼。ue(v)的實現原理即是前面提到的。ue(v)的值即是codeNum。
se(v)
se(v)的值 就是 在ue(v)的值的基礎上,按照計算得到。
更直觀點的描述就是,se(v)對應的語法元素的值 ,從1開始,每相鄰的兩個數爲一對,低序號的值爲正數,高序號的值爲負數,依次升序排列,如下表所示。
reference:
https://en.wikipedia.org/wiki/Exponential-Golomb_coding
ITU-T H.264 (V12) 2017-04-13
https://baike.baidu.com/item/%E7%86%B5%E7%BC%96%E7%A0%81/3303605