一、前言
H264 視頻壓縮算法現在無疑是所有視頻壓縮技術中使用最廣泛,最流行的。隨着 x264/openh264 以及 FFmpeg 等開源庫的推出,大多數使用者無需再對 H264 的細節做過多的研究,這大降低了人們使用 H264 的成本。
但為了用好 H264,我們還是要對 H264 壓縮編碼的基本原理弄清楚才行。今天我們就來看看 H264 壓縮編碼的基本原理。
二、H264概述
H264 編碼器采用的是變換和預測的混合編碼法。由上圖所示,輸入的幀或場 Fn 以宏塊為單位被編碼器處理。首先,按幀內或幀間預測編碼的方法進行處理。
如果采用幀內預測編碼,其預測值 PRED(圖中用 P 表示)是由當前片中前面已編碼的參考圖像經運動補償( MC)后得出,其中參考圖像用 F’n-1 表示。為了提高預測精度,從而提高壓縮比,實際的參考圖像可在過去或未來(指顯示次序上)已編碼解碼重建和濾波的幀中進行選擇。
預測值 PRED 和當前塊相減后,產生一個殘差塊 Dn,經塊變換、量化后產生一組量化后的變換系數 X,再經熵編碼,與解碼所需的一些邊信息(如預測模式量化參數、運動矢量等)一起組成一個壓縮后的碼流,經 NAL(網絡自適應層)供傳輸和存儲用。
H264壓縮方法
H264 壓縮技術主要采用了以下幾種方法對視頻數據進行壓縮:
- 幀內預測壓縮,解決的是空域數據冗余問題。
- 幀間預測壓縮(運動估計與補償),解決的是時域數據冗徐問題。
- 整數離散余弦變換(DCT),將空間上的相關性變為頻域上無關的數據,然后進行量化。
- CABAC 熵編碼, 對量化后的系數進一步的壓縮。
三、H264編碼原理
下面我們就簡單的描述一下 H264 壓縮編碼數據的過程。通過攝像頭采集到的視頻幀(按每秒 30 幀算),被送到 H264 編碼器的緩沖區中。編碼器先要為每一幅圖片划分宏塊。
3.1 划分宏塊
宏塊是編碼標准的基本處理單元,通常它的大小也為 16x16 像素。16X16 的宏塊上可以划分出更小的子塊。子塊的大小可以是 8X16、 16X8、 8X8、 4X8、 8X4、 4X4。
H264 默認是使用 16X16 大小的區域作為一個宏塊,也可以划分成 8X8 大小的宏塊。
划分好宏塊后,計算宏塊的象素值。
以此類推,計算一幅圖像中每個宏塊的像素值,所有宏塊都處理完后如下面的樣子。
3.2 划分子塊
H264 對比較平坦的圖像使用 16X16 大小的宏塊。但為了更高的壓縮率,還可以在 16X16 的宏塊上更划分出更小的子塊。
子塊的大小可以是 8X16、 16X8、 8X8、 4X8、 8X4、 4X4,非常的靈活。
上幅圖中,紅框內的 16X16 宏塊中大部分是藍色背景,而三只鷹的部分圖像被划在了該宏塊內。為了更好的處理三只鷹的部分圖像,H264就在 16X16 的宏塊內又划分出了多個子塊。
這樣再經過幀內壓縮,可以得到更高效的數據。
下圖是分別使用 MPEG-2 和 H264 對上面宏塊進行壓縮后的結果。其中左半部分為 MPEG-2 子塊划分后壓縮的結果,右半部分為 H264 的子塊划壓縮后的結果,可以看出 H264 的划分方法更具優勢。
每張圖片的宏塊划分好后,就可以對 H264 編碼器緩存中的所有圖片進行分組了。
3.3 幀分組
對於視頻數據主要有兩類數據冗余,一類是時間上的數據冗余,另一類是空間上的數據冗余。.其中時間上的數據冗余是最大的。下面我們就先來說說視頻數據時間上的冗余問題。
為什么說時間上的冗余是最大的呢?假設攝像頭每秒抓取30幀,這30幀的數據大部分情況下都是相關聯的。也有可能不止30幀的的數據,可能幾十幀,上百幀的數據都是關聯特別密切的。
對於這些關聯特別密切的幀,其實我們只需要保存一幀的數據,其它幀都可以通過這一幀再按某種規則預測出來,所以說視頻數據在時間上的冗余是最多的。
為了達到相關幀通過預測的方法來壓縮數據,就需要將視頻幀進行分組。那么如何判定某些幀關系密切,可以划為一組呢?
我們來看一下例子,下面是捕獲的一組運動的台球的視頻幀,台球從右上角滾到了左下角。
通過宏塊掃描與宏塊搜索可以發現這兩個幀的關聯度是非常高的。進而發現這一組幀的關聯度都是非常高的。因此,上面這幾幀就可以划分為一組,即 H264 中的 序列(GOP)。其算法是:在相鄰幾幅圖像畫面中,一般有差別的像素只有 10% 以內的點,亮度差值變化不超過 2%,而色度差值的變化只有 1% 以內,我們認為這樣的圖可以分到一組。
在這樣一組幀中,經過編碼后,我們只保留第一帖的完整數據,其它幀都通過參考上一幀計算出來。我們稱第一幀為 IDR/I幀,其它幀我們稱為 P/B幀,這樣編碼后的數據幀組我們稱為 GOP。
所以如果場景一直沒什么變化,則一系列視頻幀中 I 幀的數量會很少。如果場景變換很復雜,一直在場景變換大的場景切換時就會有 I 幀出現。
3.4 運動估計與運動補償(幀間預測)
在 H264 編碼器中將幀分組后,就要計算幀組內物體的運動矢量了。還以上面運動的台球視頻幀為例,我們來看一下它是如何計算運動矢量的。
H264 編碼器首先按順序從緩沖區頭部取出兩幀視頻數據,然后進行宏塊掃描。當發現其中一幅圖片中有物體時,就在另一幅圖的鄰近位置(搜索窗口中)進行搜索。如果此時在另一幅圖中找到該物體,那么就可以計算出物體的運動矢量了。
通過上圖中台球位置相差,就可以計算出台圖運行的方向和距離。H264 依次把每一幀中球移動的距離和方向都記錄下來就成了下面的樣子:
運動矢量計算出來后,將相同部分(也就是綠色部分)減去,就得到了補償數據。我們最終只需要將補償數據進行壓縮保存,以后在解碼時就可以恢復原圖了。壓縮補償后的數據只需要記錄很少的一點數據。如下所示:
我們把運動矢量與補償稱為幀間壓縮技術,它解決的是視頻幀在時間上的數據冗余。在這一步我們獲取了 P/B 幀。
除了幀間壓縮,幀內也要進行數據壓縮,幀內數據壓縮解決的是空間上的數據冗余。下面我們就來介紹一下幀內壓縮技術。
3.5 幀內預測
人眼對圖象都有一個識別度,對低頻的亮度很敏感,對高頻的亮度不太敏感。所以基於一些研究,可以將一幅圖像中人眼不敏感的數據去除掉。這樣就提出了幀內預測技術。
H264 的幀內壓縮與 JPEG 很相似。一幅圖像被划分好宏塊后,對每個宏塊可以進行 9 種模式的預測。找出與原圖最接近的一種預測模式。
幀內預測后的圖像與原始圖像的對比如下:
然后,將原始圖像與幀內預測后的圖像相減得殘差值。
再將我們之前得到的預測模式信息一起保存起來,這樣我們就可以在解碼時恢復原圖了。效果如下:
經過幀內與幀間的壓縮后,雖然數據有大幅減少,但還有優化的空間。這一步主要是壓縮出 I 幀。
3.6 對殘差數據做DCT轉換
可以將殘差數據做整數離散余弦變換,去掉數據的相關性,進一步壓縮數據。
如下圖所示,左側為原數據的宏塊,右側為計算出的殘差數據的宏塊。
將殘差數據宏塊數字化后如下圖所示:
將殘差數據宏塊進行 DCT 轉換。
去掉相關聯的數據后,我們可以看出數據被進一步壓縮了。做完 DCT 后,還不夠,還要進行 CABAC 進行無損壓縮。
3.7 CABAC 壓縮
上面的幀內壓縮是屬於有損壓縮技術。也就是說圖像被壓縮后,無法完全復原。而 CABAC 屬於無損壓縮技術。
無損壓縮技術大家最熟悉的可能就是哈夫曼編碼了,給高頻的詞一個短碼,給低頻詞一個長碼從而達到數據壓縮的目的。
MPEG-2 中使用的 VLC 就是這種算法,我們以 A-Z 作為例子,A 屬於高頻數據,Z 屬於低頻數據。看看它是如何做的。
CABAC 也是給高頻數據短碼,給低頻數據長碼。同時還會根據上下文相關性進行壓縮,這種方式又比 VLC 高效很多。其效果如下:
現在將 A-Z 換成視頻幀,它就成了下面的樣子。
從上面這張圖中明顯可以看出采用 CACBA 的無損壓縮方案要比 VLC 高效的多。
四、小結
至此,我們就將 H264 的編碼原理講完了。本篇文章主要講了以下以點內容:
- 簡單介紹了 H264 中的一些基本概念。如 I/P/B 幀, GOP。
- 詳細講解了 H264 編碼的基本原理,包括:
- 宏塊的划分,以及子塊的划分。
- 幀分組,將相似的幾幀划分為一組,即 H264 中的 序列(GOP)。
- 運動估計與運動補償(幀間預測),獲取 P/B 幀。
- 幀內預測,獲取 I 幀。
- 對殘差數據做 DCT 轉換。
- CABAC 壓縮。
參考: