[總結]HEVC熵編碼和HM具體實現

HEVC熵編碼

熵編碼原理

熵編碼的基本要求

  • 唯一可譯編碼:碼字序列能被唯一地譯碼
  • 非續長代碼:任何碼字都不是其他碼字的續長

定長熵編碼

  • 每個編碼符號分配等長的碼字
  • HEVC中定長編碼主要應用於NAL單元頭、Slice分割頭和參數集編碼
  • HEVC中規定f(n)f(n)描述語法元素的固定 nn比特的預定義值

變長熵編碼 VLC

霍夫曼編碼

  • 高概率符號分配短碼
  • 低概率符號分配長碼

指數Golomb編碼

  • 結構明確,較霍夫曼編碼簡單
  • 編碼規則
    • Code=[M個0][1][攜帶信息的M位數據INFO]Code = \text{[M個0][1][攜帶信息的M位數據INFO]}
    • 待編碼符號的根據出現概率排列,當前符合的索引爲 nn
    • M=floor[log2(n+1)],      INFO=n+12MM = \text{floor}[\log_2(n+1)],\;\;\;\text{INFO} = n + 1 - 2^M

算術編碼AC

  • 解決了現實計算機中整數位編碼問題,算法中只有加法和移位運算
  • 自適應算術編碼:符號初始概率爲均勻分佈,編碼過程不斷調整
  • 二進制算術編碼:信源爲二進制化後的符號
  • 基於上下文的自適應二進制算術編碼CABAC:上下文模型 + 自適應二進制算術編碼

HEVC的CABAC

HEVC的CABAC特點

  • 採用二進制算術編碼,將所有語法元素轉換爲二進制符號串,無乘法運算,降低計算複雜度
  • 上下文建模:根據已編碼語法元素爲待編碼語法元素建立概率模型並自適應進行概率估計和模型更新
  • 旁路編碼:假定二進制0/1等概分佈進行二進制算術編碼

HEVC的CABAC框架

常規編碼
旁路編碼
更新上下文模型
輸入符號
二進制化
上下文模型
旁路二進制編碼
常規二進制編碼
編碼輸出

二進制化

  • 一元碼 U與截斷一元碼 TU:二進制化 mvp_idxdelta_qp等語法元素
  • 截斷Rice碼 TR
  • k階指數Golomb碼 EGK:二進制化 mvd等語法元素
  • 定長碼 FL

上下文模型

變量定義

  • 6比特量化概率狀態索引 pStateIdx=σ\text{pStateIdx} =\sigma1比特最大概率符號值 valMPS=ω\text{valMPS} = \omega
  • 上下文模型狀態 (σ,ω)(\sigma, \omega) 僅需要7比特數即可表示
  • 量化參數 QPQP
  • 起始類型 initTpye\text{initTpye},上下文索引 ctxIdx\text{ctxIdx},上下文初始值 initValue\text{initValue}
  • 限幅函數 $ \text{Clip3} (a,b,c) $ 將 cc 的值限制在 aabb 之間

上下文關係

  1. 根據已編碼相鄰塊(左側和上方)語法元素對當前語法元素進行模型預測

  2. 侷限於對塊和子塊類型應用,第 nn比特根據前面已編碼的 n1n-1比特所採用的模型進行模型預測

  3. 僅用於殘差數據編碼,根據待編碼的殘差係數在掃描路徑中的位置進行模型預測

  4. 僅用於殘差數據編碼,根據已編碼係數幅度中某個特定幅度出現次數作爲參考進行模型預測

上下文模型初始化

  • 上下文模型初始化負責將概率區間復原到 (0,1)(0,1)併爲上下文模型確定初始值 (ω,σ)(\omega, \sigma)
  • 根據量化參數 QPQP 查找上下文模型的經驗分佈,並作爲上下文模型初始值
查表確定上下文索引值和初始值
  • 根據Slice的類型確定起始類型 initTpye\text{initTpye}

    • KaTeX parse error: Expected '}', got '_' at position 12: \text{slice_̲type == I: ini…
    • KaTeX parse error: Expected '}', got '_' at position 12: \text{slice_̲type == P: init…
    • KaTeX parse error: Expected '}', got '_' at position 12: \text{slice_̲type == B: init…
  • 根據語法元素和起始類型 initTpye\text{initTpye}查表確定上下文索引 ctxIdx\text{ctxIdx}和初始值 initValue\text{initValue}

確定概率狀態索引和大概率符號值
  • 導出比特長度爲 4 的變量 slopeIdx=initValue>>4,  offsetIdx=initValue&15\text{slopeIdx} = \text{initValue} >> 4, \;\text{offsetIdx} = \text{initValue}\, \& \,15

  • 計算中間變量 m=slopeIdx×545,  n=offsetIdx<<316m = \text{slopeIdx} \times 5 - 45,\;n = \text{offsetIdx}<<3 - 16

  • 計算當前Slice的量化參數 KaTeX parse error: Expected '}', got '_' at position 35: …26 + \text{init_̲qp_minus26} + \…

  • 導出狀態概率索引和最大概率符號值
    preCtxState=Clip3(1,126,[m×Clip3(0,51,SliceQPy)]>>4+n)ω=valMPS=preCtxState63  ?  0  :1σ=pStateIdx=valMPS==1  ?  preCtxState64  :63preCtxState \begin{matrix} \begin{aligned} &\text{preCtxState}=\text{Clip3}\Big(1, 126, \big[m\times \text{Clip3}(0, 51, \text{SliceQPy})\big]>>4+n\Big) \\ &\omega = \text{valMPS} = \text{preCtxState} \le 63 \;?\;0\;:1 \\ &\sigma = \text{pStateIdx} = \text{valMPS} == 1 \;?\;\text{preCtxState}-64\;:63-\text{preCtxState} \\ \end{aligned} \end{matrix}

上下文模型更新

  • 小概率符號的概率pσ{0.01875,0.5}p_\sigma \in\{0.01875,0.5\}由有限的64個索引值 σ\sigma 表徵

  • pσp_\sigma的導出公式爲 pσ=αpσ1, where α=(0.018750.5)1630.95p_\sigma = \alpha \cdot p_{\sigma-1},\text{ where } \alpha = \big(\frac{0.01875}{0.5}\big)^{\frac{1}{63}} \approx 0.95

  • 概率索引值 σ\sigma 自適應更新規則

    • 出現 MPS\text{MPS} 事件:若 σ<62\sigma < 62,則 σ=σ+1\sigma=\sigma+1,否則 σ\sigma 保持不變
    • 出現 LPS\text{LPS} 事件:若 σ>0\sigma > 0,則 σ=σ1\sigma=\sigma-1,否則 σ\sigma 保持不變且 valMPS\text{valMPS}valLPS\text{valLPS} 互換
    • σ=63\sigma = 63 固定用於對編碼結束判斷進行編碼

常規二進制編碼

變量定義

  • 當前二進制符號 binVal{0,1}\text{binVal} \in \{0,1\}

  • 量化區間下限 $L,; \text{where } len(L) = 10 $ ,量化區間長度爲 R(28,29)R \in (2^8, 2^9)

  • 量化範圍索引 qRangeIdx=ρ\text{qRangeIdx} =\rho,量化概率索引 qStateIdx=σ\text{qStateIdx} =\sigma

  • 小概率符號 LPSLPS,大概率符號 MPSMPS

  • 大概率符號區間長度 RMPSR_{MPS},小概率符號區間長度 RLPSR_{LPS} (RMPSR_{MPS}在前,RLPSR_{LPS}在後)

  • 非確定延時移出高位值 bitOutstanding\text{bitOutstanding}

基本步驟

  1. 子分當前概率區間

    • 當前區間範圍 RR 由量化值 Q(R)Q(R) 近似,且需要滿足 28<R<292^8 < R < 2^9
    • 將區間範圍等分爲四個部分,由量化範圍索引 ρ=(R>>6)&3\rho = (R >> 6 ) \& 3 表示
    • 根據參數 (ρ,σ)(\rho,\sigma) 作爲索引查找表 rangeTabLps,取得 LPSLPSRLPSR_{LPS}
    • 計算 RMPS=RRLPSR_{MPS} = R - R_{LPS}
  2. 確定新概率區間

    • 判斷當前二進制符號 binVal\text{binVal} 是大概率符號 MPSMPS 還是小概率符號 LPSLPS
    • binVal==MPS\text{binVal} == MPS,則量化區間更新爲自 LL 開始,長度爲 RMPSR_{MPS}
    • binVal==LPS\text{binVal} == LPS,則量化區間更新爲自 L+RMPSL + R_{MPS}開始,長度爲 RLPSR_{ LPS}
  3. 上下文模型概率狀態更新

    • 概率索引值 σ\sigma 自適應更新
    • 根據 σ\sigma 計算小概率符號的概率 pσp_\sigma
    • 根據 pσp_\sigma 計算最大概率符號值 ω\omega
  4. LLRR 重歸一化:將 RR 重新調整到 (28,29)(2^8, 2^9)範圍內以滿足區間劃分要求,並同時調整 RR 的下限值 LL

    • 重歸一化過程可能輸出一或多個比特,即LL的最終值的高位,作爲算術編碼的輸出

      N
      Y
      N
      Y
      N
      Y
      Y
      開始
      R < 256 ?
      結束
      L < 256 ?
      輸出比特 0
      L < 512 ?
      L -= 512
      輸出比特 1
      L -=256
      bitOutstanding++
      L = L << 1, R = R << 1
    • Step 1:根據 RR 的大小判斷是否需要重歸一化:若 R>28R > 2^8,則 左端點 R+LR+L 可以落在

      區間 (28,29)(2^8, 2^9)的任何位置,無法確定並輸出編碼的最高位,故無需重歸一化。否則轉到 Step 2

    • Step 2:根據 LL的值輸出編碼區間最高位並對 L,RL, R 進位,同時保證 L<29L<2^9。可分成三種情況:

      • R<28,L<28R < 2^8, L<2^8:可知右端點 R+L<29R +L<2^9,且編碼區間最高位爲0,寄存器左移輸出比特0並將 RRLL左移倍乘以保持精度
      • R<28,L>29R < 2^8, L>2^9:可知右端點 R+L>29R +L>2^9,且編碼區間最高位爲1,寄存器左移輸出比特1並將 RRLL左移倍乘以保持精度
      • R<28,28<L<29R < 2^8, 2^8<L<2^9:可知右端點 R+L(28,28+29)(28,29)R +L \in (2^8,2^8+2^9) \cup (2^8,2^9),且編碼區間最高兩位可能是1011,故無法確定最高有效位。通過移出高位,判斷次高位符號的方法確定移出的高位符號是01,並將 RRLL左移倍乘以保持精度

旁路編碼模式

  • 固定二進制符號01等概率,概率估計和更新過程均被旁路

  • 區間劃分約定0區間在前,且通過保持編碼區間長度RR不變,區間下限LL左移實現加倍

    Y
    N
    N
    Y
    Y
    N
    L = L << 1
    binVal == 0 ?
    L >= 1024 ?
    L = L + R
    L < 512 ?
    輸出比特 1
    L = L - 1024
    輸出比特 0
    L = L - 512
    結束

HM的熵編碼實現

HM與熵編碼有關的類

  • TEncEntropy:熵編碼的頂層包裝類,內部調用TEncEntropyIf類實現
  • TEncEntropyIf:熵編碼算法的抽象類
  • TEncSbac:CABAC熵編碼算法的實現類
  • TEncCavlc:CAVLC熵編碼算法的實現類
  • TEncBinIf:二進制編碼抽象類,負責實現熵編碼內部具體的二進制編碼細節
  • TEncBinCABAC:進行二進制編碼並將比特流寫入文件
  • TEncBinCABACCounter:只計算熵編碼的比特數而不將比特流寫入文件
  • ContextModel3DBuffer:上下文模型的包裝類
  • ContextModel:上下文模型

HM中熵編碼關鍵函數

  • 參數集熵編碼:

    • 參數集使用CAVLC編碼
    • TEncCavlc::encodeVPS()TEncCavlc::encodeSPS()TEncCavlc::encodePPS()
    • TEncGOP::compressGOP()中調用
  • 熵編碼入口函數:TEncSlice::encodeSlice()

    1. 初始化熵編碼器,設置CABAC熵編碼器爲當前的熵編碼器
    2. 加載熵編碼器,初始化並加載上下文信息
    3. 遍歷片中的每一個LCU,進行如下操作:
      1. 定向比特流到輸出位置
      2. 重置熵編碼器,並更新上下文信息
      3. 如果使用SAO,則調用encodeSAOBlkParam()對SAO的參數進行編碼
      4. 調用encodeCtu()對CTU進行熵編碼
  • 熵編碼初始化函數:TEncSbac::resetEntropy()

    • 根據預定義的上下文模型初始化各個語法元素的上下文模型

    • 調用過程:

      TEncSbac::resetEntropy()
      ContextModel3DBuffer::initBuffer()
      ContextModel::init()
  • 常規編碼核心函數TEncBinCABAC::encodeBin()

    • 語法元素通過調用encodeBin()進行二進制編碼
    • 編碼當前比特位並將該比特對應的上下文模型設置爲已經編碼
    • 根據常規二進制編碼的基本步驟編碼並更新上下文模型
    • 嘗試寫入緩衝區
  • 旁路編碼核心函數TEncBinCABAC::encodeBinEP()

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