神奇的H.264算法
很開眼的一篇文章:https://sidbala.com/h-264-is-magic/
本文是它的譯文,不會逐字翻譯,只挑有趣或重要的部分。
H.264是視頻壓縮編解碼器標準,它的目標只有一個:減少傳輸視頻所需的寬帶。網(wǎng)絡視頻、藍光視頻、手機、無人機等等,都會使用H.264,可以說,它無處不在。
為何需要壓縮
視頻由幀構成,而幀其實就是圖片,而圖片其實就是一個三維矩陣(用opencv2讀取一張圖片,打印一下便知道其結構)。
一個 60fps 的視頻,如果未壓縮,那么它每一秒的大小為:,即一個50GB的藍光光盤只能存2分鐘左右的視頻,這幾乎是不可處理的。
所以,我們需要壓縮。
為什么選擇H.264

上圖是我從蘋果官網(wǎng)截圖獲得的圖片,基于這個圖片,我制作了2個文件:
蘋果官網(wǎng)主頁截圖PNG文件,大小為1015KB 基于同一張?zhí)O果官網(wǎng)主頁截圖,利用H.264制作的時長5秒,幀率60fps的視頻文件,大小為175KB
沒錯,5秒的視頻文件體積更小。
視頻每秒60fps,總共5秒,即有300幀(300個圖片矩陣),即視頻比PNG圖片多存在了300倍的數(shù)據(jù),但文件大小是PNG圖片的五分之一。
怎么做到的?H.264會將視頻中不重要的數(shù)據(jù)全部丟掉,只留下重要的數(shù)據(jù),即H.264是一種有損數(shù)據(jù)壓縮算法,而PNG是一種無損壓縮算法。
那H.264怎么判斷哪些數(shù)據(jù)是重要的?哪些是不重要的?嗯,這便是H.264的關鍵。
H.264實際怎么做的
左邊是原圖,右邊是有損壓縮后的圖片,觀察右邊圖片,蘋果筆記本的音響孔,這些孔在原圖(左邊)是清晰的,而有損壓縮后,就模糊了。這圖,經(jīng)過有損壓縮后,大小變?yōu)樵镜?%,而類似音響孔這種影響,肉眼要仔細觀察,才能看出差異。
頻域(Frequency Domain)
在信息論中,熵用于量化一段內(nèi)容所含的信息量。
很多時候,內(nèi)容表現(xiàn)形式不同,但信息量卻一樣,即熵沒有變化,一個典型的例子,我們可以使用二進制來表示一段數(shù)據(jù),也可以實驗十六進制來表示同一段數(shù)據(jù),雖然二進制與十六進制形式不同,將兩者的熵沒有改變,我們可以將任何數(shù)據(jù)的這種形式轉換稱為完美的無損轉換。
沒錯,圖像這類數(shù)據(jù)也可以進行無損轉換。
想象一下,你可以將任何隨空間或時間變化的數(shù)據(jù)集(比如圖像的亮度值)都轉為不同的坐標空間,假設現(xiàn)在我們有頻率坐標系(不是x-y坐標系),現(xiàn)在FreqX與FreqY是坐標軸,我們可以將圖像無損的轉換為頻率坐標系中,即將圖像換了一種表示形式且熵沒有改變。
如下圖,我們將蘋果筆記本圖片轉為頻率坐標的表示

看到頻率坐標系中的內(nèi)容,其中高亮的點是圖像中具有高信息含量的數(shù)據(jù),我們可以基于頻率坐標的展示,看出圖像中重要的部分與不太重要的部分,將不太重要的部分丟棄,則實現(xiàn)圖像的有損壓縮,如下圖所示:

從圖中可知,我們丟棄頻率坐標中邊緣信息越多,圖像就越小,從而實現(xiàn)了圖像的壓縮。
H.264利用了這個技巧來實現(xiàn)視頻中幀圖像的壓縮,但這還不夠。
色度抽樣
基于科學研究發(fā)現(xiàn),人類眼睛相應的腦區(qū)不太擅長分辨顏色的細節(jié),我們對亮度變化敏感,但對顏色的變化不敏感,如果我們丟棄圖像中的部分顏色,我們是感受不出變化的,所以我們需要一些方法來合理的丟棄圖像中的部分顏色信息,以此進一步壓縮圖像。
在電視信號中,R+G+B顏色數(shù)據(jù)被無損轉為Y+Cb+Cr的形式來展示數(shù)據(jù),其中Y表示亮度(本質(zhì)是黑白亮度),Cb和Cr的顏色成分,RGB轉YCbCr,其信息熵是沒有變化的。
為啥電視不直接使用RGB,而轉一層,使用YCbCr呢?其實是歷史原因。
早期只有黑白電視,我們只需要使用Y信號便可完成數(shù)據(jù)的傳輸,隨后,彩色電視問世,進入彩色電視與黑白電視共存的年代,如果彩色電視使用RGB,那么就需要弄2個獨立的數(shù)據(jù)流,這很麻煩(成本、維護上都麻煩)。
聰明的工程師決定將顏色信息編碼進Cb和Cr中,并將其與Y信息一同傳輸,這樣黑白電視只能看Y信息,而彩色電視內(nèi)部將YCbCr轉為RGB顯示則可。
因為人眼對亮度變化敏感,但對顏色變化不敏感,所以我們可以將Cb、Cr壓縮,從而將圖像大小再減少一半,而人眼卻看不出差別。
運動估計與補償
前面幾種壓縮方式都是針對幀內(nèi)的,即針對圖像的,而視頻還有另外一個大的壓縮空間,那便是幀間信息(幀與幀之間)。
H.264采用運動估計與補償?shù)姆绞絹韷嚎s幀間信息,從而極大減小視頻的大小。
首先,我們知道,視頻由一系列幀按順序排列而成,而這些幀,有很大一部分信息是冗余的,比如我們拍攝30 fps的視頻,那么抽出視頻中的1秒,可以發(fā)現(xiàn)有30張圖像,這30張圖像,內(nèi)容通常是相近的,是否有辦法將這些冗余的信息丟棄掉來減少視頻大小呢?
當然有,運動估計與補償便是解決這個問題的一種方案,為了理解,我從其他文章中(譯文之外的文章)選了一個例子。
現(xiàn)在有一個視頻,記錄了臺球的運動。

一個視頻,展開后,就是一系列的幀:

有了視頻后,第一步,要對視頻中的相似幀分組,那怎么判斷某些幀相似呢?
H.264編碼器會按順序逐次抽取兩個相鄰幀,然后按宏塊進行對比,計算兩幀相似度。
這里涉及到宏塊這個新概念,什么是宏塊?其實就是H.264處理圖像時的窗口大小,比如有一張圖:

H.264默認會按大小劃分出一個宏塊,當然,你也可以按、等大小來劃分。

H.264編碼器通過宏塊掃描與宏塊搜索來判斷兩個幀之間的相似度,然后將相似的幀分為一組。
對同一組幀,會做運動估計與補償。
首先,拿出同一組的相鄰兩幀。通過宏塊掃描,發(fā)現(xiàn)圖像中有物體,便在另一幀圖像相同位置的周圍進行搜索,如果在另外一幀的圖像中,也找到該物體,則可以計算出物體的運行矢量。
如上圖,計算出相鄰兩幀圖像中臺球的相差位置,計算出臺球的運行的方向和距離。
H.264編碼器依次把每一幀中球移動的距離和方向都記錄下來,如下圖:

計算出運動矢量后,將相同部分的數(shù)據(jù)丟棄,剩余的數(shù)據(jù)便是補償數(shù)據(jù),對于這一組幀,我們只需要存儲完整的第一幀數(shù)據(jù)(稱為I幀)和補償數(shù)據(jù)便可以還原出完整原始數(shù)據(jù)了,通過這種做法,幀間大量冗余數(shù)據(jù)被壓縮。
運動估計與補償算法壓縮了視頻大小,但也會帶來一些小問題,比如:
當我們在視頻網(wǎng)站上瀏覽視頻時,如果錯過了一段內(nèi)容,想往回點擊,再次播放時,網(wǎng)站通常會停頓幾秒。這些內(nèi)容剛剛已經(jīng)緩存下來了,只是我沒看,想倒回去看,為何還會停頓,直接讀緩存信息不就行了?
當你跳轉視頻到某個任意幀時,H.264的解碼器必須重新做所有的計算,從而得到運動矢量和補償數(shù)據(jù),并將這些數(shù)據(jù)加到你當前幀中。這個計算壓力是比較大的,所以會停頓一下,從而影響你的體驗。
熵編碼器(Entory Coder)
剛剛的描述中,我們簡化了獲取運動矢量和補償數(shù)據(jù)的過程,實際上,H.264編碼器將幀分組后,會將組中的第一幀作為I幀,然后使用兩種方式去獲取運動矢量,一種是P幀,一種是B幀。
P幀只會與前一幀進行對比來獲得數(shù)據(jù),而B幀會對前后的幀都進行對比來獲得數(shù)據(jù)。
在實際的視頻中,幀變化時,很可能會出現(xiàn)幾個宏塊掃描對比得出的運動矢量是相同的情況,這便造成了數(shù)據(jù)冗余。
熵編碼器會處理這種數(shù)據(jù)冗余的情況,這是一種無損轉換,不會損失數(shù)據(jù),但這種轉換減少了存儲相同數(shù)據(jù)所需要的空間,從而減小視頻大小。
結尾
如果原始視頻的分辨率是,時長5秒,每秒60幀,那么原始視頻的大小為:,而壓縮后,視頻會變?yōu)?75kb,真讓人驚嘆,真的就是魔法。
H.264有幾十年的研究歷史,本文只是簡單化的描述了其中的工作,但有很多細節(jié)并沒有展示,H.264也是經(jīng)過多年發(fā)展,慢慢優(yōu)化成當前形態(tài)的。
我是二兩,我們下篇文章見。
本文參考:
[H.264 is Magic] https://sidbala.com/h-264-is-magic/ [視頻壓縮原理] https://github.com/733gh/Android-Notes/blob/master/android/%E8%A7%86%E9%A2%91%E5%8E%8B%E7%BC%A9%E5%8E%9F%E7%90%86.md
掃碼即可加我微信
--End-- 1、想領取贈書,加我微信,朋友圈不定期送書;
2、想咨詢學習,加我微信,每次咨詢僅9.9元;
3、更多需求(學習 代碼 視頻剪輯),都可以加我微信,歡迎咨詢。
掃碼即可加我微信
分享
收藏
點贊
在看




