視頻編碼的目的是為了壓縮原始視頻,壓縮的主要思路是從空間、時間、編碼、視覺等幾個主要角度去除冗余信息。由于 H.264 出色的數據壓縮比率和視頻質量,成為當前市場上最為流行的編解碼標準。而 H.265 是在 H.264 的基礎上,保證相同視頻質量的同時,視頻流的碼率還可以減少50%。隨著H.265編碼格式越來越流行,本文將主要介紹 H.265 的編碼原理,以下是 H.265 的編碼框架流程圖。
01
編碼結構
VCL:Video Coding Layer,主要包括視頻壓縮引擎和圖像分塊的語法定義,原始視頻在 VCL 層,被編碼成視頻數據。簡單版本的編碼過程如下:
將每一幀的圖像分塊,將塊信息添加到碼流中;
對單元塊進行預測編碼,幀內預測生成殘差,幀間預測進行運動估計和運動補償;
對殘差進行變換,對變換系數進行量化、掃描。
對量化后的變換系數、運動信息、預測信息等進行熵編碼,形成壓縮的視頻碼流輸出。
NAL:Network Abstraction Layer,主要定義數據的封裝格式,把 VCL 產生的視頻數據封裝成一個個 NAL 單元的數據包,適配不同的網絡環(huán)境并傳輸。
02
分塊
從編碼順序和結構上講,H.265首先將一個視頻劃分成若干個序列,一個序列劃分成若干個圖像組(GOP),每一個GOP代表一組連續(xù)的視頻幀。H.265 在對圖像做預測編碼和變換編碼時,會先對圖像進行劃分,劃分方式是四叉樹。在劃分四叉樹時,會將整個視頻幀劃分成若干個正方形的編碼樹塊(CTB),CTB 可以繼續(xù)劃分成編碼塊(CB),CB 還可以劃分為預測塊(PB)和變換塊(TB)。因此,H.265對視頻的結構劃分如下圖所示:
同一位置處的一個亮度 CB 和兩個色度 CB ,加上一些相應的語法元素,組成一個編碼單元(CU)。CU 是決定進行幀內預測、幀間預測、Skip/Merge模式的單元。
同一位置處的一個亮度 CTB 和兩個色度 CTB ,加上一些相應的語法元素,和包含的 CU ,組成一個編碼樹單元(CTU)。CTU 相當于 H.264 中的宏塊,區(qū)別是 CTU 的尺寸是由編碼器制定,最大可以支持到 64x64,最小可以支持到 16x16。而宏塊的大小固定為 16x16。
一個 CTU 在進行編碼時,按照深度優(yōu)先的順序進行 CU 編碼,像數據結構中的四叉樹一樣,一個大的方塊代表父節(jié)點,里面有四個小方塊分別代表四個子節(jié)點。
03
預測
視頻的本質是由一系列連續(xù)的視頻幀組成,在單個視頻幀內部和多個視頻幀之間都存在大量的冗余。從空間的角度看,單個視頻幀內部的像素點之間的像素值相差很小。從時間的角度看,兩個連續(xù)的視頻幀之間也有很多相同的像素點。預測編碼就是基于圖像統計特性進行數據壓縮的一種方法,利用了圖像在時間和空間上的相關性,通過已經重建的像素數據預測當前正在編碼的像素。
3.1 幀內預測
幀內預測是指用于預測的像素和當前正在編碼的像素都在同一個視頻幀內,并且一般都在鄰近的區(qū)域內。由于鄰近的像素之間有很強的相關性,像素值一般都非常接近,發(fā)生突變的概率非常小,差值都是0或者非常小的數。所以,幀內預測編碼后傳輸的是預測值和真實值之間的差值,即0附近的值,叫做預測誤差或殘差,這樣就用較少的比特傳輸,達到壓縮的效果。
H.265幀內預測編碼以塊為單位,使用相鄰已經重建的塊的重建值對正在編碼的塊進行預測。預測分量分為亮度和色度兩個,對應的預測塊分別是亮度預測塊和色度預測塊。為了適應高清視頻的內容特征,提高預測精度,H.265采用了更加豐富的預測塊尺寸和預測模式。
H.265亮度預測塊的尺寸在4*4到32*32之間,所有尺寸的預測塊都有35種預測模式,這些預測模式可以分為3類:平面(Planar)模式、直流(DC)模式和角度(Angular)模式。
Planar模式:亮度模式0,適用于像素值變換緩慢的區(qū)域,例如像素漸變的場景。對預測塊中的每個像素都使用不同的預測值。預測值等于:該像素在水平和垂直兩個方向線性插值的平均值。
DC模式:亮度模式1,適用于圖像的大面積平坦區(qū)域,該模式對預測塊中的所有像素都使用相同的預測值。
如果預測塊是正方形,預測值等于左邊和上邊的參考像素的平均值;
如果預測塊是長方形,預測值等于長的那一邊的平均值;
角度模式:亮度模式2~34,總共33個預測方向,其中模式10是水平方向,模式26是垂直方向。角度模式每個像素的預測值都是從對應預測方向前已經重建的像素集的樣值進行水平或垂直方向偏移角度預測。
由于彩色視頻中,相同位置的色度信號和亮度信號的特征類似,因此色度預測塊和亮度預測塊的預測模式也類似。H.265中色度預測塊的預測模式有Planar模式、垂直模式、水平模式、DC模式和導出模式5種:
Planar模式:色度模式0,和亮度模式0一樣。
垂直模式:色度模式1,和亮度模式26一樣。
水平模式:色度模式2,和亮度模式10一樣。
DC模式:色度模式3,和亮度模式1一樣。
導出模式:色度模式4,采用和對應亮度預測塊相同的預測模式。如果對應的亮度預測塊模式是0、1、10、26中的一種,則替換為模式34。
3.2 幀間預測
幀間預測是指用于預測的像素和當前正在編碼的像素不在同一個視頻幀內,但是一般在相鄰或附近的位置。一般情況下,幀間預測編碼的壓縮效果要比幀內預測好,主要原因是視頻幀之間的相關性非常強。如果視頻幀中的運動物體變化速度很慢,那么視頻幀之間的像素差值也就很小,時間冗余度就非常大。
幀間預測評估運動物體運動狀況的方法是運動估計,它的主要思想就是對預測塊從參考幀的給定范圍中搜索匹配塊,計算匹配塊和預測塊之間的相對位移,該相對位移就是運動矢量。得到運動矢量后,需要對預測修正,也就是運動補償。將運動矢量輸入到運動補償模塊,"補償"參考幀,即可得到當前編碼幀的預測幀。預測幀和當前幀的差,就是幀間預測誤差。
如果幀間預測只用到了前一幀圖像,就稱為前向幀間預測或單向預測。該預測幀也就是P幀,P幀可以參考前面的I幀或者P幀。
如果幀間預測不僅用到了前一幀圖像預測當前塊,還用到了后一幀圖像,那么就是雙向預測。該預測幀也就是B幀,B幀可以參考前面的I幀或P幀和后面的P幀。
由于P幀需要參考前面的I幀或P幀,而B幀需要參考前面I幀或P幀和后面的P幀,如果在一個視頻流中,先到了B幀,而依賴的I幀、P幀還沒有到,那么該B幀還不能立即解碼,那么應該怎么保證播放順序呢?其實,在視頻編碼時,會生成PTS和DTS。通常情況下,編碼器在生成一個I幀后,會向后跳過幾個幀,用前面的I幀作為參考幀對P幀編碼,I幀和P幀之間的幀被編碼為B幀。推流的視頻幀順序在編碼的時候就已經按照I幀、P幀、B幀的依賴順序編好了,收到數據后直接解碼即可。所以,不可能先收到B幀,再收到依賴的I幀和P幀。
PTS:Presentation Time Stamp,顯示時間戳,告訴播放器在什么時間顯示這一幀。
DTS:Decoding Time Stamp,解碼時間戳,告訴播放器在什么時間解碼這一幀。
04
變換
變換編碼是指將圖像中的空間域信號映射變換到頻域(頻率域),然后對生成的變換系數編碼。由于在空間域中,數據之間的相關性比較大,經過預測編碼后的殘差變化較小,存在大量的數據冗余,在圖像中亮度值變化緩慢的平坦區(qū)域特別明顯。而變換為頻域后,會將空間域分散分布的殘差數據轉換成集中分布,可以降低相關性,減少數據冗余,從而達到去除空間冗余的目的。
在H.265中,一個編碼塊(CB)可以通過四叉樹劃分成若干個預測塊(PB)和變換塊(TB)。由于從 CB 到 TB 之間的四叉樹劃分主要是為了殘差的變換運算,因此這種四叉樹又稱為殘差四叉樹(RQT)。如下圖所示,就是一個 RQT 劃分實例,將一個 32*32 的殘差 CB 劃分成13個不同大小的 TB 。
每個 TB 的大小有四種,分別是從 4*4、8*8、16*16、32*32,每個 TB 都對應一個整數變換系數矩陣。大尺寸的 TB 適用于圖像亮度值變化緩慢的平坦區(qū)域,小尺寸的 TB 適用于圖像亮度值變化劇烈的復雜區(qū)域。所有尺寸都可以使用離散余弦變換(DCT)變換。另外,對于 4*4 的幀內預測亮度殘差塊,還可以使用離散正弦變換(DST)。
由于幀內預測編碼是基于左邊和上邊已經編碼塊的數據,因此預測塊距離已編碼塊越近,相關性越強,預測誤差越?。痪嚯x已編碼塊越遠,相關性越小,預測誤差越大。預測誤差的這種數據分布特征和 DST 的正弦基函數 sin 非常相似,起始點最小,然后逐漸變大。但是因為 DST 計算量比 DCT 大,需要增加更多的變換類型標識,因此 DST 僅用于 4*4的幀內預測亮度殘差塊。
05
量化
由于變換編碼只是將圖像數據從空間域矩陣轉換為頻域的變換系數矩陣,矩陣的系數個數和數據量都沒有減少。要想壓縮數據,還需要對頻域中的統計特征進行量化和熵編碼。
常見的量化方法可以分為**標量量化(SQ)和矢量量化(VQ)**兩類:
標量量化:將圖像中的數據劃分成若干個區(qū)間,然后在每個區(qū)間用一個值代表這個區(qū)間內所有樣點的取值。
矢量量化:將圖像中的數據劃分成若干個區(qū)間,然后在每個區(qū)間用一個代表矢量代表這個區(qū)間的所有矢量取值。
由于矢量量化引入了多個像素之間的關聯,并且使用了概率的方法,一般壓縮率比標量量化高。但是由于其計算復雜度高,所以目前廣泛使用的量化方法是標量量化。
量化的壓縮率取決于劃分的區(qū)間大小,即量化步長。量化步長越大,表示量化越粗,對應的視頻碼率越低,失真越大;量化步長越小,表示量化越細,對應的視頻碼率越高,失真越小。
H.265量化時是以**變換單元(TU)為基本單位,處理對象包括 TU 中的亮度分量和色度分量。H.265采用了非線性標量量化,通過量化參數(QP)**控制每個編碼塊的量化步長,QP 和量化步長的關系近似呈指數關系。QP 是個整數,亮度分量的 QP 值范圍是 0~51,色度分量的亮度 QP 值范圍是0~45。QP 值在0~29范圍時,亮度分量和色度分量的量化步長相等,從QP=30開始,兩者開始產生差異。QP 和量化步長的關系如下圖所示:
編碼端的量化過程可以簡單理解為是每個 DCT 變換系數除以量化步長得到量化值。在解碼端對應的反量化過程就是量化值乘以量化步長得到 DCT 變化系數值。
06
熵編碼
熵編碼是指在編碼過程中按熵原理不丟失任何信息的編碼。量化是一種有損的壓縮方式,而熵編碼是用更緊湊的方式標記和原數據之間的映射關系,屬于無損壓縮。常見的熵編碼有香農(Shannon)編碼、哈夫曼(Huffman)編碼、算術(Arithmetic)編碼、游程編碼等。
6.1 哈夫曼編碼
哈夫曼編碼是一種變長編碼,即不同字符的編碼長度是變化的。該編碼利用字符出現的概率構造哈夫曼二叉樹,目標是讓出現概率大的字符編碼時用短碼(距離根節(jié)點近),概率小的字符編碼時用長碼(距離根節(jié)點遠),從而讓平均碼字長度最短。
碼字:字符經過哈夫曼編碼后得到的編碼。
例:字符A、B、C、D、E、F對應的出現的概率分別是0.32、0.22、0.18、0.16、0.08、0.04。哈夫曼樹的構造過程如下:
選擇概率最小的 E、F 作為葉子節(jié)點,計算 E、F 的概率和作為它們父節(jié)點;
將父節(jié)點的值與剩下的 A、B、C、D 概率值排序,再選擇最小的兩個樹求和;
重復以上過程;
最終構造出來的哈夫曼二叉樹如下圖所示:
左節(jié)點的路徑為0,右節(jié)點的路徑為1,求得A、B、C、D、E、F的編碼結果:
字符 | A | B | C | D | E | F |
概率 | 0.32 | 0.22 | 0.18 | 0.16 | 0.08 | 0.04 |
碼字 | 11 | 01 | 00 | 101 | 1001 | 1000 |
碼字長度 | 2 | 2 | 2 | 3 | 4 | 4 |
平均碼字長度 = 0.32*2 + 0.22*2 + 0.18*2 + 0.16*3 + 0.08*4 + 0.04*4 = 2.4bit
6.2 算術編碼
雖然哈夫曼編碼在理論上可以獲得最佳編碼結果,但是在實際編碼中,由于計算機處理的最小數據單位是1bit,對于包含小數點的碼字長度只能按照整數處理,所以實際編碼效果往往略遜于理論編碼效果。在圖像壓縮領域,通常使用算術編碼代替哈夫曼編碼。不過,算術編碼的理論基礎和哈夫曼編碼是一致的,都是概率大的字符用短碼,概率小的字符用長碼。
算術編碼分為固定模式算術編碼、自適應算術編碼(AAC)、二進制算術編碼、自適應二進制算術編碼(CABAC)等,H.265 中使用了 CABAC 。此處將只介紹固定模式算術編碼流程:
統計輸入的符號序列中各個字符和出現的概率;
按照概率分布,將[0, 1)區(qū)間劃分成多個子區(qū)間,每個子區(qū)間代表一個字符,子區(qū)間的大小代表字符出現的概率;所有子區(qū)間大小的和等于1;假設該字符的區(qū)間范圍為 [L, H);
設置初始變量low=0, high=1,不斷讀取符號序列中的每個字符,找到該字符對應的區(qū)間范圍 [L, H),更新low和high的值:
low = low + (high - low) * L
high = low + (high - low) * H
遍歷完符號序列后,得到最終的low和high,轉換二進制形式輸出得到編碼數據;
例:輸入符號序列是 ADBCD,統計各個字符出現的概率:
字符 | 出現次數 | 出現概率 | 概率區(qū)間 |
A | 1 | 0.2 | [0, 0.2) |
B | 1 | 0.2 | [0.2, 0.4) |
C | 1 | 0.2 | [0.4, 0.6) |
D | 2 | 0.4 | [0.6, 1) |
遍歷第一個字符 A 時,low = 0, high = 1, L = 0, H = 0.2
low = low + (high - low) * L = 0
high = low + (high - low) * H = 0.2
遍歷第二個字符 D 時,low = 0, high = 0.2, L = 0.6, H = 1
low = low + (high - low) * L = 0.12(注:此處計算的low不代入下面計算high值的公式中)
high = low + (high - low) * H = 0.2
遍歷第三個字符 B 時,low = 0.12,high = 0.2,L = 0.2,H = 0.4
low = low + (high - low) * L = 0.136
high = low + (high - low) * H = 0.152
遍歷第四個字符 C 時,low = 0.136,high = 0.152,L = 0.4,H = 0.6
low = low + (high - low) * L = 0.1424
high = low + (high - low) * H = 0.1456
遍歷第五個字符D時,low = 0.1424,high = 0.1456,L = 0.6,H = 1
low = low + (high - low) * L = 0.14432
high = low + (high - low) * H = 0.1456
得到最后的[low, high)區(qū)間是[0.14432, 0.1456),在這個區(qū)間內取任意值轉二進制后都是對 "ADBCD"的算術編碼。對應的編碼流程可以簡化到下面這張圖中:
07
環(huán)路濾波
由于 H.265 采用分塊編碼,在圖像反量化、反變換重建的時候,會存在一些失真效應,例如塊效應、振鈴效應。為了解決這些問題,H.265 采用了環(huán)路濾波技術,其中包括去方塊濾波(DBF)和樣點自適應補償(SAO)。
DBF 作用于邊界像素,用于解決塊效應。塊效應是指一些相鄰編碼塊邊界處的灰度值存在明顯的不連續(xù)性,產生塊效應主要有兩個原因:
編碼器對殘差的DCT變換和量化是基于塊的,忽略了塊與塊之間的相關性,導致塊之間的處理不一致;
幀間預測運動補償塊的不完全匹配,存在誤差;而編碼時的預測參考幀通常來自這些重建圖像,導致待預測圖像失真;
DBF 針對邊界類型采用強濾波、弱濾波或者不處理,邊界類型的判定是由邊界像素梯度閾值和邊界塊的量化參數決定的。DBF 處理時,先對整個圖像的垂直邊緣進行水平濾波,然后對水平邊緣進行垂直濾波。濾波過程實際上就是對像素值進行修正的過程,讓方塊看起來不那么明顯。H.264 中也存在 DBF 技術,但是應用于 4*4 大小的處理塊,而 H.265 中應用于 8*8 大小的處理塊。
SAO 是 H.265 新引入的對重建圖像的誤差補償機制,用于改善振鈴效應。振鈴效應是指圖像的灰度值劇烈變化產生的震蕩,產生振鈴效應主要原因是DCT變換后高頻信息丟失。SAO 的原理就是通過對重構曲線的波峰像素添加負值補償,波谷添加正值補償,從而減小高頻信息的失真。和 DBF 只作用于邊界像素不同,SAO 作用于塊中所有的像素。
08
小結
本文從 H.265 整體編碼流程的角度,介紹了 H.265 編碼涉及到的分塊、預測、變換、量化、編碼、環(huán)路濾波等技術點。通過了解這些編碼原理,為我們后續(xù)進一步學習音視頻開發(fā)技術奠定扎實的基礎。
---------- END ----------

這里有 一個優(yōu)質的C++學習圈 等你加入。