第一部分: 處理影片和影像的基礎知識
什麼? 視訊編解碼器是一種壓縮和/或解壓縮數位視訊的軟體/硬體。
為了什麼? 儘管在頻寬和頻寬方面存在一定的限制
而在資料儲存空間方面,市場對視訊品質的要求也越來越高。 您還記得在上一篇文章中我們如何計算每秒 30 幀、每像素 24 位元、解析度為 480x240 所需的最小值嗎? 我們在未壓縮的情況下收到了 82,944 Mbit/s。 壓縮是目前將 HD/FullHD/4K 傳輸到電視螢幕和網路的唯一方法。 這是如何實現的? 現在我們簡單看一下主要方法。
我們訂婚了
視訊監控系統集成 和我們正在開發顯微斷層掃描儀 .
編解碼器與容器
新手常犯的一個錯誤是混淆數位視訊編解碼器和數位視訊容器。 容器是一定的格式。 包含視訊(也可能是音訊)元資料的包裝器。 壓縮視訊可以被視為容器有效負載。
通常,視訊檔案的副檔名指示其容器類型。 例如,檔案 video.mp4 可能是容器 MPEG-4 Part 14,並且最有可能的是名為 video.mkv 的文件
歷史上的位
在我們到達之前 怎麼樣?,讓我們深入了解歷史,以便更好地理解一些較舊的編解碼器。
視訊編解碼器 H.261 出現於 1990 年(技術上是 1988 年),其創建的目的是以 64 Kbps 的資料傳輸速率運行。 它已經使用了顏色子採樣、宏塊等想法。 影片編解碼標準於1995年發布 H.263,一直發展到2001年。
第一個版本於2003年完成 H.264 / AVC。 同年,TrueMotion 發布了其免費的有損視訊編解碼器,名為 VP3。 谷歌於 2008 年收購了該公司,並發布 VP8 同年。 2012年XNUMX月,Google發布 VP9,大約 XNUMX/XNUMX 的瀏覽器市場(包括行動裝置)都支援它。
AV1 是一個新的免費開源視訊編解碼器,由 開放媒體聯盟 (奧美傳媒),其中包括最著名的公司,例如:Google、Mozilla、Microsoft、Amazon、Netflix、AMD、ARM、NVidia、Intel 和 Cisco。 該編解碼器的第一個版本 0.1.0 於 7 年 2016 月 XNUMX 日發布。
AV1的誕生
2015 年初,Google正致力於 VP10Xiph(由 Mozilla 擁有)正在致力於 達拉,而思科製作了自己的免費視訊編解碼器,稱為 托爾.
然後 MPEG 洛杉磯 首次公佈的年度限額 HEVC (H.265),費用比 H.8 高 264 倍,但他們很快又改變了規則:
無年度限制,
內容費(收入的 0,5%)和
單價比H.10高264倍左右。
開放媒體聯盟 由來自不同領域的公司創建:設備製造商(英特爾、AMD、ARM、Nvidia、思科)、內容提供者(Google、Netflix、亞馬遜)、瀏覽器創建者(Google、Mozilla)等。
這些公司有一個共同的目標—免版稅視訊編解碼器。 然後出現 AV1 具有更簡單的專利許可。 Timothy B. Terryberry 做了一場令人驚嘆的演講,成為當前 AV1 概念及其許可模式的起源。
你會驚訝地發現你可以透過瀏覽器分析AV1編解碼器(有興趣的可以去
通用解碼器
讓我們看看通用視訊編解碼器的主要機制。 這些概念中的大多數都很有用,並且在現代編解碼器中使用,例如 VP9, AV1 и HEVC。 我警告你,解釋的許多事情都會被簡化。 有時,現實世界的範例(如 H.264)將用於演示這些技術。
第一步-分割影像
第一步是將框架分為幾個部分、小部分等等。
為了什麼? 原因有很多。 當我們分割圖像時,我們可以透過對小運動部分使用小部分來更準確地預測運動向量。 對於靜態背景,您可以將自己限制在較大的部分。
編解碼器通常將這些部分組織成部分(或區塊)、宏塊(或編碼樹塊)和多個子部分。 這些分割區的最大大小各不相同,HEVC 將其設定為 64x64,而 AVC 使用 16x16,子分割區最多可以分割為 4x4 大小。
您還記得上一篇文章中的框架類型嗎? 同樣的情況也適用於區塊,因此我們可以有 I 片段、B 區塊、P 巨集區塊等。
對於那些想要練習的人,請觀看圖像如何劃分為部分和子部分。 為此,您可以使用上一篇文章中已經提到的方法。
第二步-預測
一旦我們有了部分,我們就可以對它們進行占星預測。 為了 國際米蘭預測 必須轉移 運動向量 和其餘部分,對於 INTRA 預測,它被傳輸 預測方向 和其餘的。
第三步-轉型
一旦我們有了殘差塊(預測部分→真實部分),就可以對其進行變換,以便我們知道可以丟棄哪些像素,同時保持整體質量。 有一些轉換可以提供確切的行為。
雖然還有其他方法,但讓我們更詳細地看看它們。 離散餘弦變換 (DCT - 從 離散餘弦變換)。 DCT的主要功能:
- 將像素區塊轉換為大小相等的頻率係數區塊。
- 凝聚力量,幫助消除空間冗餘。
- 提供可逆性。
2 年 2017 月 14 日 辛特拉 R.J. (辛特拉,RJ)和拜耳 F.M. (Bayer FM) 發表了一篇關於影像壓縮的類 DCT 變換的文章,該變換僅需要 XNUMX 次加法。
如果您不了解每一點的好處,請不要擔心。 下面我們透過具體的例子來看看它們的真正價值。
我們以這個 8x8 的像素區塊為例:
該區塊被渲染成以下 8 x 8 像素影像:
對這個像素塊應用 DCT 並得到一個 8x8 的係數塊:
如果我們渲染這個係數區塊,我們將得到以下圖像:
正如您所看到的,它看起來不像原始圖像。 您可以看到第一個係數與所有其他係數非常不同。 第一個係數稱為 DC 係數,它表示輸入數組中的所有樣本,類似於平均值。
此係數區塊有一個有趣的特性:它將高頻成分與低頻成分分開。
在影像中,大部分功率集中在較低頻率,因此如果將影像轉換為其頻率成分並丟棄較高頻率係數,則可以減少描述影像所需的資料量,而無需犧牲太多影像品質。
頻率是指訊號變化的速度。
讓我們嘗試應用在測試案例中獲得的知識,使用 DCT 將原始影像轉換為其頻率(係數區塊),然後丟棄部分最不重要的係數。
首先我們將其轉換到頻域。
接下來,我們丟棄部分(67%)係數,主要是右下部分。
最後,我們從這個被丟棄的係數區塊中重建影像(記住,它必須是可逆的)並將其與原始影像進行比較。
我們看到它與原始圖像相似,但與原始圖像有許多差異。 我們扔掉了 67,1875%,但仍然得到了與原始版本類似的東西。 可以更仔細地丟棄係數以獲得品質更好的圖像,但這是下一個主題。
每個係數都是使用所有像素產生的
重要提示:每個係數並不會直接對應到一個像素,而是所有像素的加權和。 這個令人驚嘆的圖表顯示如何使用每個指數特有的權重來計算第一和第二係數。
您也可以嘗試透過查看基於 DCT 的簡單影像形成來視覺化 DCT。 例如,以下是使用每個係數權重產生的符號 A:
第四步-量化
在我們在上一步中丟棄一些係數之後,在最後一步(轉換)中,我們執行一種特殊形式的量化。 在此階段,遺失資訊是可以接受的。 或者,更簡單地說,我們將量化係數以實現壓縮。
如何量化一個係數區塊? 最簡單的方法之一是均勻量化,當我們取一個區塊時,將其除以一個值(除以 10)並對結果進行四捨五入。
我們可以反轉這組係數嗎? 是的,我們可以,乘以除以的相同值。
這種方法並不是最好的,因為它沒有考慮到每個係數的重要性。 人們可以使用量化器矩陣而不是單一值,並且該矩陣可以透過量化右下角的大部分和左上角的少數來利用 DCT 屬性。
步驟 5 - 熵編碼
一旦我們量化了數據(圖像塊、片段、幀),我們仍然可以對其進行無損壓縮。 有許多演算法方法可以壓縮資料。 我們將快速瀏覽其中的一些內容,為了更深入地了解您可以閱讀《理解壓縮:現代開發人員的資料壓縮》一書(“
使用VLC進行視訊編碼
假設我們有一個字元流: a, e, r и t。 此表顯示了每個字元在流中出現的頻率的機率(範圍從 0 到 1)。
a | e | r | t | |
---|---|---|---|---|
可能性 | 0,3 | 0,3 | 0,2 | 0,2 |
我們可以將唯一的二進位代碼(最好是小的二進位代碼)分配給最有可能的代碼,並將較大的代碼分配給不太可能的代碼。
a | e | r | t | |
---|---|---|---|---|
可能性 | 0,3 | 0,3 | 0,2 | 0,2 |
二進位代碼 | 0 | 10 | 110 | 1110 |
我們壓縮流,假設最終每個字元花費 8 位元。 如果不進行壓縮,每個字元將需要 24 位元。 如果將每個字元替換為其代碼,則可以節省成本!
第一步是對字元進行編碼 e,等於 10,第二個字元是 a,添加(不是以數學方式):[10][0],最後是第三個字符 t,這使得我們最終的壓縮位元流等於 [10][0][1110] 或 1001110,只需 7 位元(比原始空間少 3,4 倍)。
請注意,每個代碼必須是帶有前綴的唯一代碼。
編碼器和解碼器都必須能夠存取帶有二進位代碼的符號表。 因此,還需要發送一個表格作為輸入。
算術編碼
假設我們有一個字元流: a, e, r, s и t,其機率列於該表中。
a | e | r | s | t | |
---|---|---|---|---|---|
可能性 | 0,3 | 0,3 | 0,15 | 0,05 | 0,2 |
使用此表,我們將建立包含所有可能字元的範圍,並按最大數字排序。
現在讓我們對三個字元的流進行編碼: 吃.
首先選擇第一個字符 e,其範圍為 0,3 至 0,6(不含)。 我們採用這個子範圍並按照與之前相同的比例再次劃分它,但對於這個新範圍。
讓我們繼續編碼我們的串流 吃。 現在取第二個字符 a,位於從 0,3 到 0,39 的新子範圍內,然後取最後一個字符 t 再次重複相同的過程,我們得到從 0,354 到 0,372 的最終子範圍。
我們只需要在最後一個子範圍 0,354 到 0,372 之間選擇一個數字。 我們選擇 0,36(但您可以選擇此子範圍中的任何其他數字)。 只有有了這個數字,我們才能恢復原來的流。 就好像我們在範圍內畫一條線來編碼我們的流。
逆運算(即 解碼)同樣簡單:使用我們的數字 0,36 和初始範圍,我們可以運行相同的過程。 但現在,使用這個數字,我們可以識別使用這個數字編碼的流。
對於第一個範圍,我們注意到我們的數字對應於切片,因此這是我們的第一個字元。 現在我們按照與之前相同的過程再次劃分這個子範圍。 這裡可以看到0,36對應的符號 a,重複這個過程後我們得到了最後一個字符 t (形成我們的原始編碼流 吃).
編碼器和解碼器都必須有一個符號機率表,因此也有必要在輸入資料中發送它。
相當優雅,不是嗎? 想出這個解決方案的人真是太聰明了。 一些視訊編解碼器使用這種技術(或至少提供它作為一種選項)。
這個想法是無損壓縮量化的比特流。 當然,這篇文章缺少大量的細節、原因、權衡等。 但如果你是開發人員,你應該要了解更多。 新的編解碼器嘗試使用不同的熵編碼演算法,例如 ANS.
第 6 步 - 位元流格式
完成所有這些後,剩下的就是在所執行的步驟的上下文中解壓縮壓縮幀。 解碼器必須明確告知編碼器所做的決定。 必須向解碼器提供所有必要的資訊:位元深度、色彩空間、解析度、預測資訊(運動向量、定向幀間預測)、設定檔、等級、幀速率、幀類型、幀編號等等。
我們將快速瀏覽一下比特流 H.264。 我們的第一步是建立一個最小的 H.264 位元流(FFmpeg 預設會新增所有編碼選項,例如 塞納爾 ——我們稍後會知道它是什麼)。 我們可以使用我們自己的儲存庫和 FFmpeg 來完成此操作。
./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
該命令將產生原始位元流 H.264 一幀,64×64分辨率,色彩空間 YUV420。 在這種情況下,使用以下圖像作為框架。
H.264 比特流
規範 AVC (H.264)確定資訊將以宏幀(在網路意義上)發送,稱為 非酒精性脂肪酶 (這是網路抽象層級)。 NAL 的主要目標是提供「網路友善」的影片示範。 該標準應該適用於電視(基於串流)、網際網路(基於資料包)。
有一個同步標記來定義 NAL 元素的邊界。 每個同步令牌包含一個值 0x00 0x00 0x01, 除了第一個,它等於 0x00 0x00 0x00 0x01. 如果我們推出 十六進位轉儲 對於產生的 H.264 位元流,我們在檔案開頭至少標識了三個 NAL 模式。
如上所述,解碼器不僅必須知道影像數據,還必須知道影片、幀、顏色、使用的參數等等的詳細資訊。 每個 NAL 的第一個位元組定義其類別和類型。
NAL 類型標識符 | 描述 |
---|---|
0 | 未知類型 |
1 | 沒有 IDR 的編碼影像片段 |
2 | 編碼切片資料部分 A |
3 | 編碼切片資料部分 B |
4 | 編碼切片資料部分 C |
5 | IDR 影像的編碼 IDR 片段 |
6 | 有關 SEI 擴展的更多信息 |
7 | SPS 序列參數集 |
8 | PPS影像參數集 |
9 | 訪問分隔符 |
10 | 序列結束 |
11 | 線程結束 |
... | ... |
通常,位元流的第一個 NAL 是 PLC。 這種類型的 NAL 負責通知常見的編碼變量,例如設定檔、等級、解析度等。
如果我們跳過第一個同步標記,我們可以解碼第一個位元組以找出第一個 NAL 類型。
例如,同步令牌之後的第一個位元組是 01100111,其中第一位 (0) 位於字段 f 中orbidden_zero_bit。 接下來的 2 位 (11) 告訴我們這個字段 nal_ref_idc, 指示該 NAL 是否為參考欄位。 剩下的 5 位(00111) 告訴我們這個字段 nal_unit_type, 在本例中它是 SPS 區塊(7) 納爾。
第二個位元組(二進制=01100100, 十六進制=0x64, 十二月=100) 在 SPS NAL 中是該字段 設定檔_idc, 它顯示了編碼器使用的設定檔。 在本例中,使用了有限的高輪廓(即沒有雙向 B 段支援的高輪廓)。
如果你看一下比特流規範 H.264 對於SPS NAL,我們會發現很多參數名稱、類別和描述的值。 例如,讓我們看一下字段 pic_width_in_mbs_minus_1 и pic_height_in_map_units_minus_1.
參數名稱 | 類別 | 描述 |
---|---|---|
pic_width_in_mbs_minus_1 | 0 | 尿素(v) |
pic_height_in_map_units_minus_1 | 0 | 尿素(v) |
如果我們對這些欄位的值進行一些數學運算,我們將獲得解析度。 可以使用 1920 x 1080 來表示 pic_width_in_mbs_minus_1 值為 119 ((119 + 1) * macroblock_size = 120 * 16 = 1920)。 同樣,為了節省空間,我們沒有編碼 1920,而是使用 119。
如果我們繼續以二進位形式檢查我們創建的影片(例如: xxd -b -c 11 v/minimal_yuv420.h264),然後您可以轉到最後一個 NAL,即幀本身。
這裡我們看到它的前 6 個位元組值: 01100101 10001000 10000100 00000000 00100001 11111111。 由於已知第一個位元組指示 NAL 類型,在本例中 (00101)是一個IDR片段(5),然後你可以進一步探索它:
使用規範訊息,可以解碼片段類型(切片類型)和幀號(影格號)等重要領域。
取得某些欄位的值(ue(v), me(v), se(v)或 te(v)),我們需要使用基於的特殊解碼器來解碼片段
意 切片類型 и 影格號 該影片的 7(I 片段)和 0(第一幀)。
比特流可以被認為是一種協定。 如果你想了解更多關於比特流的信息,你應該參考規範 國際電聯H.264。 這是一個顯示影像資料所在位置的巨集圖(YUV 以壓縮形式)。
可以檢查其他位元流,例如 VP9, H.265 (HEVC)或甚至是我們新的最佳比特流 AV1。 它們都相似嗎? 不,但是一旦你至少理解了其中一個,要理解其餘的就容易多了。
想練習嗎? 探索 H.264 位元流
您可以產生單一畫面影片並使用 MediaInfo 檢查位元流 H.264。 事實上,沒有什麼可以阻止您查看分析比特流的源代碼 H.264 (AVC).
作為練習,您可以使用 Intel Video Pro Analyzer(我是否已經說過該程式是付費的,但有一個限制為 10 幀的免費試用版?)。
Обзор
請注意,許多現代編解碼器使用我們剛剛研究的相同模型。 這裡,我們看一下視訊編解碼器的框圖 托爾。 它包含我們經歷過的所有步驟。 這篇文章的重點是至少讓您更了解該領域的創新和文件。
先前計算出,儲存一個 139p 品質、720 fps 持續一小時的影片檔案需要 30 GB 磁碟空間。 如果您使用本文中討論的方法(幀間和內部預測、變換、量化、熵編碼等),那麼您可以實現(基於我們每個像素花費 0,031 位元的事實)相當長的視訊品質令人滿意,僅佔用367,82 MB,而不是139 GB 記憶體。
H.265如何達到比H.264更好的壓縮比?
現在我們對編解碼器的工作原理有了更多了解,就更容易理解較新的編解碼器如何以更少的位數提供更高的解析度。
如果我們比較 AVC и HEVC,值得記住的是,這幾乎總是在更大的 CPU 負載和壓縮比之間進行選擇。
HEVC 有更多的部分(和小節)選項 AVC、更多內部預測方向、改進的熵編碼等等。 所有這些改進均已完成 H.265 能夠壓縮50%以上 H.264.
第一部分: 處理影片和影像的基礎知識
來源: www.habr.com