H.264---CABAC---第四步--算術編碼


轉自:https://www.cnblogs.com/TaigaCon/p/5304563.html

算術編碼是基於區間划分的,普通的概率划分需要使用到多位乘法。CABAC的算術編碼為了降低計算復雜度,並便於硬件實現,采取了如下一些方法:

  1. 總是估計小概率符號LPS(pLPS<0.5pLPS<0.5)的概率,並將其概率離散化成64個不同概率狀態。概率估計轉換成基於表格的概率狀態的轉換(見初始化部分的描述)。
  2. 使用9bit的變量R與10bit的L表示當前區間,其中L為區間的起點,R為區間長度
  3. 每當輸入新符號時,會對區間的起點LL以及區間的長度RR進行更新,在前面的二進制算術編碼時,我們已經得知兩者的更新方法,其中RL的更新包含了浮點數乘法Rip,為了降低運算復雜度,CABAC把乘法換算成了查表的形式。換算方法如下:
    • 離散化的狀態pStateIdx代表了符號的概率p
    • 9個bit的區間長度RR通過(R>>6)&3(R>>6)&3被量化成2個bit,即{0,1,2,3}(因為RR總是大於等於2828小於2929,在后面的歸一化可以看出來)
    • 有了上述兩個離散的變量,區間更新所需要的乘法就能轉換成查表操作,表格請查看標准9.3.3.2中的第一個表格。
  4. 在算術編碼的過程中,盡管是同一上下文,但是概率並不是固定的,每次輸入一個新符號都會改變相應上下文的概率,也就是會進行狀態轉換(見初始化部分的描述)
  5. 對近似均勻分布的語法元素,在編碼和解碼時選擇旁路(bypass)模式,可以免除上下文建模,提高編解碼的速度。
  6. 由於編碼區間是有限位表示的,因此在輸入一個符號進行區間更新后,需要進行重歸一化以保證編碼精度

 

算術編碼過程

image

該過程可分為5個步驟

  1. 通過當前編碼器區間范圍R得到其量化值ρ作為查表索引,然后利用狀態索引pStateIdx與ρ進行查表得出RLPS的概率區間大小。
  2. 根據要編碼的符號是否是MPS來更新算術編碼中的概率區間起點L以及區間范圍R
  3. pStateIdx==0表明當前LPS在上下文狀態更新之前已經是0.5的概率,那么此時還輸入LPS,表明它已經不是LPS了,因此需要進行LPS、MPS的轉換
  4. 更新上下文模型概率狀態
  5. 重歸一化,輸出編碼比特。

 

重歸一化分析

 

image

 

在CABAC編碼過程中,在輸入符號后,進行區間更新,接下來就是重歸一化過程。下面就以[0,210)[0,210)表示區間[0,1)[0,1)為例,分析重歸一化過程

    (請注意,在該過程中,[0,210)[0,210)只起到輔助作用,實際的區間為RR)

  1. R<28R<28的情況,如果L<28L<28,則可知R+L<29R+L<29,那么可以確定編碼區間[L,L+R)[L,L+R)在區間[0,0.5)[0,0.5)上,用二進制表示這個區間即為0.0x0.0x,因此輸出00(只記錄小數點后面的二進制)。最后用[0,210)[0,210)來表示區間[0,0.5)[0,0.5),也就是將原本的[0,29)[0,29)線性擴增到[0,210)[0,210)
  2. R<28R<28的情況,如果L29L⩾29,那么就可以確定編碼區間[L,L+R)[L,L+R)在區間[0.5,1)[0.5,1)上,用二進制表示這個區間即為0.1x0.1x,因此輸出11。最后用[0,210)[0,210)來表示區間[0.5,1)[0.5,1),也就是將原本的[29,210)[29,210)線性擴增到[0,210)[0,210)
  3. R<28R<28的情況,如果28L<2928⩽L<29,則28<R+L<29+2828<R+L<29+28,編碼區間[L,L+R)[L,L+R)可能在[0,0.5)[0,0.5)區間內,也有可能跨越[0,0.5)[0,0.5)與[0.5,1)[0.5,1)兩個區間,即可能是0.01x0.01x,也可能是0.10x0.10x。此時可以先暫緩輸出,用[0,210)[0,210)來表示區間[0.25,0.75)[0.25,0.75),也就是將原本的[28,28+29)[28,28+29)線性擴增到[0,210)[0,210),然后進入重歸一化的下一個循環繼續判斷。
  4. R28R⩾28的情況,無法通過L<28L<28來確定編碼區間,需要通過輸入下一個符號來對RR與LL進行更新后再繼續進行判斷,因此當前符號的編碼流程結束。由於這個原因,因此在一個符號編碼結束后,另一個符號編碼開始前,總是28R<2928⩽R<29。

cabac_switch

 

輸出

在編碼輸出“0”或者“1”的階段,用PutBit(B)表示

image

關於PutBit(B)的分析,參考上面重歸一化的區間圖,可以看到有三種情況

  1. 情況1,走PutBit(0),直接輸出“0”
  2. 情況2,走PutBit(1),直接輸出“1”
  3. 情況3,輸出可能為“10”或者“01”,因此不能直接輸出,走bitsOutstanding++的步驟。在下一次編碼符號時,符合情況2,走PutBit(1),此時bitsOutstanding = 1,因此輸出“10”

另外,PutBit(B)不會編碼第一個bit。原因是CABAC在初始化的時候,會以[0,210)[0,210)表示區間[0,1)[0,1),而在初始化區間時R=510L=0R=510,L=0,這意味着已經進行了第一次區間選擇,區間為[0,0.5)[0,0.5),需要輸出“0”。PutBit(B)在此阻止這個“0”的輸出,這樣就能得到正確的算術編碼結果了。

 

 

旁路(bypass)編碼

有些語法元素在二值化后選擇的可能不是上述的算術編碼,而是旁路編碼,具體情況請查看h.264標准9.3.2的第一個表格。旁路編碼中,假設待編碼的符號符合近似的均勻分布。下圖給出了旁路模式下的編碼過程。

旁路模式有幾個特點:符號均勻分布,無需對RR進行量化與查表;每編碼完一個符號后,RR總是會小於2828,因此每次都需要對概率區間進行放大;把對LL的移位操作提到了前面,因此旁路編碼的重歸一化的區間可以看作由[0,210)[0,210)變成了[0,211)[0,211)。

image

下面是旁路編碼的一個例子

 

 

編碼結束 EncodeTerminate

在編碼語法元素end_of_slice_flag以及I_PCM mb_type時(ctxIdx = 276)會調用EncodeTerminate這個編碼過程。

在EncodeTerminate中,采用的是pStateIdx = 63的狀態,這個狀態表示的是當前宏塊是否為該slice的最后一個宏塊的概率。在該狀態下,對概率區間的划分跟概率區間量化值無關。在編碼end_of_slice_flag以及I_PCM的mb_type時,概率區間固定為RLPS=2RLPS=2。如果當前宏塊為slice的最后一個宏塊(end_of_slice_flag = 1)或者當前編碼為PCM宏塊並且編碼它的mb_type I_PCM時,結束CABAC編碼,調用EncodeTerminate中的EncodeFlush。具體情況請參考標准中的9.3.4.5小節以及CABAC補充討論

image

 

在編碼完成slice的最后一個宏塊后,將會調用字節填充過程。該過程會往NAL單元寫入0個或者更多個字節(cabac_zero_word),目的是完成對NAL單元的封裝(標准9.3.4.6)。這里有計算如下

k=(3×(32×BinCountsInNALunitsRawMbBits×PicSizeInMbs1024)NumBytesInVclNalunits)/3k=⌈(⌈3×(32×BinCountsInNALunits−RawMbBits×PicSizeInMbs1024)⌉−NumBytesInVclNalunits)/3⌉

如果k>0k>0,則需要將3字節長的0x000003添加到NAL單元kk次。這里的前兩字節0x0000代表了cabac_zero_word,第三個字節0x03代表一個emulation_prevention_three_byte,這兩個語法元素請參考h.264語法結構分析的相關部分。

如果k0k⩽0,則無需添加字節到NAL單元。

式子中的各個變量所代表的意思請查看標准


免責聲明!

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



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