これは、ビデオ技術に関するやさしい解説資料です。ソフトウェア開発者やエンジニアを対象にしていますが、誰でも理解できる解説にしたいと思っています。このアイディアは、ビデオ技術初学者のためのミニワークショップから生まれました。
できるだけ簡潔な言葉、多くの視覚的要素、具体的な例を使うことで、誰でもデジタルビデオの概念が理解できることを目標にしています。気軽に訂正や提案を送り、改善してください。
本資料にはハンズオンによる解説が含まれています。ハンズオンに取り組む方は、事前にdockerをインストールし、このレポジトリをクローンしておいてください。
git clone https://github.com/leandromoreira/digital_video_introduction.git
cd digital_video_introduction
./setup.sh
注意:
./s/ffmpeg
や./s/mediainfo
コマンドは、そのプログラムがDockerコンテナ上で実行されることを意味しています。コンテナの中には、必要な依存関係が全て含まれています。
ハンズオンはすべて、このレポジトリをクローンしたフォルダで実行してください。 jupyter examplesについては、./s/start_jupyter.sh
でサーバーを起動して、表示されるURLをブラウザで開いてください。
- DRMシステムの追加
- 1.0.0版のリリース
- 簡体字訳の追加
- FFmpeg oscilloscopeフィルターの例を追加
画像は、二次元マトリクスとして考えることができます。さらに次元を増やして三次元マトリクスにすることで画像の色を表現することも可能です。
画像の色を原色 (赤、緑、青)で表現すると、三つの平面を定義することになります。一つめが赤、二つ目が緑、そして三つ目が青です。
マトリクスのそれぞれの要素をピクセル (画素)と呼びます。一つのピクセルはその色の強度 (通常は数値)を表します。例えば、赤色のピクセルは、緑が0、青が0、赤が最大の強度により表現できます。ピンク色のピクセルも同様に3つの値で表現できます。0から255の数値で表現することにより、ピンクピクセルは赤=255、緑=192、青=203と定義できます。
色を表現する方法は、他にもたくさんあります。例えば、RGBモデルでは各ピクセルで3バイトを必要としますが、インデックスパレットは1バイトしか必要ありません。そういったモデルでは、色を表現するために三次元モデルを使わずに二次元モデルを使用できるでしょう。メモリを節約できますが、色の選択肢を狭めることになります。
例えば、下の画像をみてください。1番左の画像は色付けされており、他の画像は赤、緑、青の強度を表す平面です(グレートーンで表示しています)。
赤色が最も多く使われていることが分かります(左から二番目の顔の最も明るい部分)。一方青色 は服の一部とマリオの目にしかみられません(最後の顔) 。マリオのひげに対しては、どの色もあまり使われていない(最も暗い部分)ことが分かります。
各色の強度はビット深度とよばれる一定量のビットで表現されます。色(平面)ごとに8ビット(0から255の値で表現する)を使う場合、24ビット(8ビット x 3次元 R/G/B)の色深度を持つことになり、2の24乗種類の色を使えることが推測できます。
画像がどのように万物をビットとしてとらえるのかを学ぶと 良いでしょう
もう一つの画像のプロパティは 解像度です。解像度は長さあたりのピクセルの数です。解像度はよく幅 x 高さとして表現されます。例えば、下記は4×4の画像です。
画像やビデオの作業をする時にみるもう一つのプロパティは アスペクト比です。アスペクト比は、画像やピクセルの幅と高さの比率を表します。
動画や画像が16x9であると言うときは、たいてい画面アスペクト比 (DAR) のことを指します。しかし、個々のピクセルを様々な形状にすることができ、これを ピクセルアスペクト比 (PAR) といいます。
DVDの実際の解像度は704x480ですが、10:11のピクセルアスペクト比を持っているため、4:3のアスペクト比を保っています(704x10/480x11)。
最後に、ビデオを単位時間内のnフレームの並びとして定義でき、もう一つの特性と見ることができます。nはフレームレートもしくは秒間フレーム数 (FPS)です。
ビデオを表すために必要な秒間あたりのビット数はビットレートです。
ビットレート = 幅 x 高さ x ビット深度 x フレームレート
例えば、圧縮を全く使わないなら、フレームレートが30で、ピクセルあたりが24ビットで、解像度が480x240のビデオは、秒間あたり82,944,000ビットもしくは82.944 Mbps (30x480x240x24)が必要です。
ビットレートがほとんど一定なら、固定ビットレート(CBR)と呼ばれます。ビットレートが変動するなら、可変ビットレート (VBR)と呼ばれます。
このグラフはフレームが真っ黒の間はあまりビットを使わない、制約付きのVBRを示しています。
黎明期に、技術者たちが帯域幅を増やさずにビデオ画面で認識できるフレームレートを倍にする技術を思いつきました。この技術はインターレース動画として知られています。基本的には一つ目の「フレーム」で画面の半分を送り、次の「フレーム」で残りの半分を送ります。
今日では プログレッシブスキャン技術を使って画面に描画されます。プログレッシブは、動画を描画、保存、転送する手段の一つで、各フレームの全走査線を順番に描画します。
これで画像がどのようにデジタル表現されるのかが分かりました。色がどのように配置され、ビデオを表すのにどのくらいの秒間あたりのビットが必要なのか、それが固定なのか(CBR)可変なのか(VBR)、解像度とフレームレート、他にもインターレースやピクセルアスペクト比、その他のたくさんの用語を学びました。
圧縮なしにビデオを扱うことは不可能であることを学びました。解像度が720pで30fpsの場合、1時間のビデオ一つに278GB*が必要です。(PKZIP、Gzip、PNGで使われている)DEFLATE のような可逆圧縮アルゴリズム一つを使うだけでは必要な帯域幅を十分に削減できないため、ビデオを圧縮する別の方法を見つける必要があるのです。
* この数値は1280 x 720 x 24 x 30 x 3600 (幅、高さ、ピクセルあたりのビット数、フレームレート、秒単位での時間)を掛けることで算出しました
これを成し遂げるために、私たちの視覚の仕組みを利用することができます。私たちは色よりも明るさを見分けることが得意です。また、ビデオには少しの変化しかない多くの画像が含まれている時間的繰り返しがあります。それぞれのフレームもまた、多くの領域では同じか似ている色が使われている画像内の繰り返しがあります。
私たちの目は色よりも明るさにより敏感です。この画像をみてそれを自分自身で確認できます。
左側の正方形Aと正方形Bの色は同じであることが分からないとしても大丈夫です。私たちの脳は色よりも明暗に注意をはらうことで錯覚を起こさせているのです。右側では、同じ色のコネクターがあるため、私たち(私たちの脳)は、それらは実際は同じ色であるということを簡単に気づきます。
私たちの目がどのように機能するかの簡単な説明
眼は複雑な器官で、たくさんのパーツから成り立っていますが、主に錐体細胞と桿体細胞に関心があります。眼は 1億2000万の桿体細胞と600万の錐体細胞を含んでいるのです。
ものすごく簡単にするため、眼のパーツの機能のうち色と明るさに焦点を当てましょう。桿体細胞は主に明るさに対して責任を持っています。一方 錐体細胞は色に対して責任を持っています。異なった色素を持つ3種類の錐体があり、名前はS錐体(青)、M錐体(緑)、L錐体(赤)です。
私たちは錐体細胞(色)よりも多くの桿体細胞(明るさ)を持っているため、色よりも明暗をより識別することができることが推論できます。
コントラスト感度特性 (Contrast sensitivity functions)
実験心理学をはじめさまざまな分野の研究者が、人間の視覚について多くの理論を作り上げてきました。そのひとつが「コントラスト感度特性」です。コントラスト感度特性は光の時空間に関するものです。まず、観察者にある光を提示し、光を変化させていきます。この特性は、観察者が変化に気付いたと報告するまで、どれだけ光を変化させる必要があるかを示します。複数形の
functions
となっているのは、白黒だけでなく、カラーでも感度を測定できるためです。実験結果から、多くの場合、人間の目は色よりも明るさに敏感だとわかりました。
私たちは輝度(画像の明るさの度合い)により敏感であることが分かりました。この特性を利用しましょう。
RGBモデルを使ってカラー画像がどのように機能するのか最初に学びましたが、他のモデルも存在します。実は、輝度(明るさ)と色度(色)を別にするモデルが存在します。それはYCbCr*として知られています。
* 同じ分離を行うモデルはもっと存在します。
このカラーモデルは明るさを表すためにYを使い、2つの色チャンネルCb (青の色差)とCr (赤の色差)を使います。YCbCrはRGBから生成することができ、RGBに戻すこともできます。下の写真のように、このモデルを使ってフルカラーの画像を作ることができます。
中には 緑なしで色の全てを生成できるのかと異議を唱える方もいるでしょう。
この質問に答えるために、RGBからYCbCrへの変換を一通り説明します。ITU-Rグループ* によって推奨される BT.601標準 からの係数を使います。最初のステップは、輝度を計算することです。ITUに提案されている定数を使い、RGB値を置き換えます。
Y = 0.299R + 0.587G + 0.114B
輝度をえると次に、色を分ける (青の色差と赤の色差)ことができます。
Cb = 0.564(B - Y)
Cr = 0.713(R - Y)
それを戻すことができ、YCbCrを使って緑を得ることもできます。
R = Y + 1.402Cr
B = Y + 1.772Cb
G = Y - 0.344Cb - 0.714Cr
* グループや標準はデジタルビデオではよくでてきます。彼らは何が標準なのかを定義します。例えば4Kとは何か?どのフレームレート、解像度、カラーモデルを使うべきか?などです。
一般的にディスプレイ (モニター、テレビ、スクリーン等) は様々な方法で構成されたRGBモデルだけを利用します。下の写真でいくつか拡大したもの示しています。
画像は輝度と色度成分で表現することができるので、人間の視覚システムが色度よりも輝度に敏感であることを利用して情報を選択的に削減することができます。クロマサブサンプリングは色度の解像度を輝度の解像度より小さくする画像符号化技術です。
色度の解像度をどのくらい小さくすべきでしょうか?!解像度と結合 (最終的な色 = Y + Cb + Cr
)の仕方をどのようにするかを定義したいくつかの方式がすでに存在しています。
これらの方式はサブサンプリングシステムとして知られ、3つの部分からなる比a:x:y
として表現されます。これは a x 2
ブロックの輝度ピクセルに対する色度解像度を定義しています。
a
:横方向のサンプルの基本数。通常は4。x
:1ライン目のa
に現れる色信号サンプルの数。(a
に対する水平解像度)y
:1ライン目と2ライン目での変化数
4:1:0は例外で、
4 x 4
ブロックの輝度に1つの色信号サンプルだけを含んでいます。
現代のコーデックでよく使われる方式は4:4:4(サブサンプリング無し)、4:2:2、4:1:1、4:2:0、4:1:0、3:1:1です。
YCbCr 4:2:0結合
これがYCbCr 4:2:0を使って結合された画像の断片です。1ピクセルに12ビットしか使わないことに注意してください。
クロマサブサンプリングの主要な形式で符号化された同じ画像を見て下さい。一行目の画像は最終的なYCbCrで、二行目の画像は色度解像度を示しています。実に小さな劣化で素晴らしい結果です。
解像度が720pで30fpsのビデオを1時間ファイルに保存するためには278GBの領域が必要になることを先に計算しました。YCbCr 4:2:0を使うと、半分のサイズ(139 GB)*にすることができます。しかしまだ理想には程遠いです。
* 幅、高さ、ピクセルごとのビット数、フレームレートを掛けることによりこの値を計算しました。先に計算した時は24ビット必要でしたが、今は12ビットしか必要ありません。
ffmpegでYCbCrヒストグラムを調べることができます。 このシーンでは青の寄与率がより高くなっています。 ヒストグラムでそのことが示されます。
輝度や明るさ、ガンマ、色について解説している素晴らしい動画があります。ぜひご覧ください。
FFmpegのoscilloscope filterを使って、映像のYの強度を可視化できます。
ffplay -f lavfi -i 'testsrc2=size=1280x720:rate=30000/1001,format=yuv420p' -vf oscilloscope=x=0.5:y=200/720:s=1:c=1
次に時間的な冗長性の削減を試みましょう。しかし、その前にいくつかの基本的用語について押さえておきましょう。30 fpsの動画があり、下記が最初の4フレームであるとします。
青い背景のようにフレーム間にたくさんの繰り返しを見ることができます。背景はフレーム0からフレーム3まで変化しません。この問題に取り組むために、これらを3種類のフレームに抽象的に分類しましょう。
Iフレーム(参照、キーフレーム、イントラ)は自己完結的なフレームです。Iフレームは他に依存せずに描画することができ、静止画と似ています。最初のフレームは普通はIフレームですが、他の種類のフレームの間にも規則的にIフレームが挿入されています。
現在の画像は、ほとんど毎回1つ前のフレームを使って描画することができるという事実をPフレームは利用しています。例えば、2番目のフレームでは、ボールが前に動いたという変化しかありません。1つ前のフレームを参照し、そのフレームとの差分だけを用いてフレーム1を再構築できます。
Pフレームはより小さなデータしか使わないので、全体をIフレームは1つだけで、他は全てPフレームのビデオにエンコードしたらどうでしょう?
このビデオをエンコードした後、再生してビデオの前方にシークしてください。シーク先に移るのに時間がかかることに気づくでしょう。これは、描画のためにPフレームが参照フレームを必要とする (例えばIフレーム)ためです。
もう1つ手軽にできるテストとして、まず1つのI-Frameだけを使ってビデオをエンコードして、次に2秒間隔でIフレームを挿入してエンコードしてから、それぞれのエンコード結果のサイズを比較してください。
過去と未来のフレーム両方を参照したら、さらに高い圧縮率を得ることができるのではないでしょうか?!それがBフレームの基本です。
2つの方法でエンコードしてみましょう。1つはBフレーム付きで、もう一方はBフレームなしでエンコードして、ファイルサイズおよび画質を確認してください。
これらの異なる種類のフレームはより高い圧縮率を得るために使われます。次の節でどうやってこれが行われるのか見ていきます。しかし、今のところはIフレームは重く、Pフレームは比較的軽いですが、最も軽いのはBフレームと考えておいてよいでしょう。
時間的繰り返しを削減するために何ができるか見ていきましょう。この種の冗長性はインター予測という技術で解決することができます。
できるだけ少ないビットを使って、連続したフレーム0とフレーム1をエンコードしてみましょう。
1つできることとして、引き算があります。単純にフレーム0からフレーム1を引くと、エンコードする必要がある差分を得ることができます。
しかし、さらに少ないビットしか使わないもっとよい方法があると言ったらどうでしょう!まず、フレーム0
をきちんと定められた区画の集合であるとします。そしてフレーム0
のそれぞれのブロックをフレーム1
上にマッチさせようとします。.これは 動き推定として考えることができます。
"ブロック動き補償は現在のフレームを重ならないブロックに分けて、動き補償ベクトルはこれらのブロックがどこからのものかを表します (前回のフレームが重ならないブロックに分けられ、動き補償ベクトルはこれらのブロックがどこへ行くかを表すというのは、よくある誤解です)。 元のブロックは元のフレーム内で通常は重なり合います。ビデオ圧縮アルゴリズムの中には、 すでに送信された異なったいくつかのフレームの断片から現在のフレームを組み立てるものもあります。"
ボールがx=0、y=25
からx=6、y=26
へ移動したと推定することができます。xとyの値は動きベクトルです。ビットを節約するためのもう1つのさらなるステップは、前回のブロック位置と予測されるブロック位置との動きベクトルの差分だけをエンコードすることです。 最終的な動きベクトルはx=6 (6-0)、y=1 (26-25)
のようになります。
実際には、このボールは分割されてn個の区画にまたがるでしょう。しかし処理は同じです
フレーム上の物体は3D空間で移動します。ボールは背景の方に動くと小さくもなります。 完全にマッチするブロックを見つけられないことはよくあります。 推定画像と実際の画像を重ねると下記のようになります。
しかし、動き推定を適用すると、単純なフレーム差分手法を使うよりもエンコードするデータがより小さくなることが分かります。
この手法は全てのブロックに適用され、高い確率でボールは1つ以上のブロックに配置されます。 引用元: https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
jupyterを使ってこれらの概念を実験できます。
ffmpegでインター予測 (動きベクトル)付きのビデオを生成することができます。
または、Intel Video Pro Analyzer (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うこともできます。
ビデオの中の1つ1つのフレームを分析すると、たくさんの相関性のある領域が存在することが分かります。
例を通してみていきましょう。このシーンはほとんど青と白で構成されています。
これは Iフレーム
で、予測のために前のフレームを使えませんが、圧縮することはできます。赤いブロックを選択してエンコードしてみましょう。隣接する部分を見ると、その周りの色に傾向があることを推定することができます。
このフレームでは色が垂直方向に広がり続けることを予測してみます。未知のピクセルの色が隣接するピクセルの色を持つことを意味します。
予測が間違うかもしれません。そのため、この手法(イントラ予測)を適用してから、実際の値を引いて差分を生成する必要があります。その差分は予測前よりもさらに圧縮しやすいマトリクスになります。
他にも様々な予測方法があります。ここで紹介した方法は、直線状に並んだ領域の予測を行い、ブロックの上の行のピクセルをブロック内の各行にコピーしていました。ここにさらに角度成分を加え、左隣のピクセルの値も利用してブロック内のピクセル値を予測する方法もあります。また左隣と上隣のピクセル値の平均を利用する、DC予測という方法もあります。
ffmpegでマクロブロックとそれらの予測付きのビデオを生成することができます。それぞれのブロックの色の意味を理解するためにffmpegのドキュメントを調べてください。
またはIntel Video Pro Analyzer (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うことができます。
What? デジタルビデオを圧縮、解凍するソフトウェア / ハードウェアの部品。Why? 制限された帯域とストレージの下でより高い品質のビデオへの要求が、市場と社会で高まっているため。毎秒30フレーム、ピクセルあたり24ビット、解像度が480x240のビデオに必要な帯域を計算したのを覚えていますか。圧縮なしでは82.944 Mbpsでした。テレビやインターネットでHD/FullHD/4Kを配信するためには圧縮するしかありません。How? ここで主な技法について簡単に見ていきます。
コーデック 対 コンテナ
初心者がよく誤解することの1つに、デジタルビデオコーデックとデジタルビデオコンテナを混同するというものがあります。コンテナはビデオ(と音声もありえる)のメタデータとペイロードである圧縮されたビデオを包括するラッパーフォーマットとして考えることができます。
たいてい、ビデオファイルの拡張子はそのビデオコンテナを定義します。例えば、ファイル
video.mp4
はおそらく MPEG-4 Part 14 コンテナで、video.mkv
という名前のファイルはおそらく matroska です。コーデックとコンテナフォーマットを確実に調べるためには、ffmpegかmediainfoが使えます。
一般的なコーデックの内部動作に入って行く前に、いくつかの古いビデオコーデックについて少し理解するために過去を振り返ってみましょう。
ビデオコーデックであるH.261は1990年(厳密には1988年)に生まれました。H.261は64 kbit/sのデータレートで動作するように設計されました。クロマサブサンプリングやマクロブロックなどの考えをすでに使っていました。1995年に、H.263ビデオコーデック標準が発表され2001年まで拡張され続けました。
2003年にH.264/AVCの初版が完成しました。同じ年に On2 Technologies (旧Duck Corporation)という会社が、ロイヤリティーフリーで非可逆ビデオ圧縮の VP3と呼ばれるビデオコーデックをリリースしました。2008年にこの会社をGoogleが買収し、同じ年にVP8をリリースしました。2012年の12月にGoogleがVP9をリリースしました。VP9は(モバイルを含む)ブラウザ市場のおよそ¾でサポートされています。
AV1は新しいロイヤリティーフリーでオープンソースのビデオコーデックで、Alliance for Open Media (AOMedia)によって設計されました。AOMediaは複数の会社: Google、Mozilla、Microsoft、Amazon、Netflix、AMD、ARM、NVidia、Intel、Ciscoと他のいくつかの会社から成り立っています。リファレンスコーデックの初版 0.1.0が2016年4月7日に公開されました。
2015年の初期にGoogleがVP10の開発、Xiph (Mozilla)はDaalaの開発、CiscoはオープンソースでロイヤリティーフリーのThorと呼ばれるビデオコーデックの開発に取り組んでいました。
MPEG LAは、当初はHEVC (H.265)の年間ロイヤリティーの上限とH.264の8倍高いライセンス料を発表しましたが、すぐにルールを変更しました。:
- 年間ロイヤリティーの上限なし
- コンテンツ料金 (収入の0.5%)
- h264より10倍高い単位あたり料金
alliance for open mediaはハードウェアメーカー(Intel、AMD、ARM、Nvidia、Cisco)、コンテンツ配信 (Google、Netflix、Amazon)、ブラウザ開発(Google, Mozilla)、その他の会社によって作られました。
これらの会社にはロイヤリティーフリーのビデオコーデックという共通の目的があり、AV1はより 簡単な特許ライセンスで誕生しました。Timothy B. Terriberryが AV1の概念、ライセンスモデル、現状についての素晴らしいプレゼンテーションを行いました。この節はこのプレゼンテーションを元に書いています。
ブラウザーを使ってAV1コーデックを分析できることを知って驚くことでしょう。https://arewecompressedyet.com/analyzer/ を見てください。
追記: コーデックの歴史についてもっと学びたいなら、背後にあるビデオ圧縮の特許の基本を学ばなくてはなりません。
一般的なビデオコーデックの背後にある主な機構を紹介していきますが、これらの概念のほとんどは VP9、AV1、HEVCのような最新のコーデックでも役に立ち、使われています。物事をかなり単純にして説明することを理解してください。ときどき実際の例(だいたいはH.264)を使って、技法のデモを行います。
最初のステップは、いくつかのパーティション、サブパーティション、もっと細かい単位にフレームを分割することです。
しかしなぜ? たくさんの理由があります。例えば、画像を分割すると小さなパーティションを動きのある小さな部分に使い、より大きなパーティションを静的な背景に使って、予測をより正確に行うことができます。
通常、コーデックは、スライス(もしくはタイル)、マクロ(もしくは符号ツリーユニット)やたくさんのサブパーティションで パーティションを構成します。これらのパーティションの最大サイズは様々で、HEVCでは64x64、AVC16x16ですが、サブパーティションは4x4までです。
フレームはいくつかの形式に分けられているのを学んだことを覚えていますか?!さて、これらのアイデアをブロックに適用することもできます。つまりIスライス、Bスライス、Iマクロブロックなどを持つことができます。
Intel Video Pro Analyzer (有料ですが、最初の10フレームに制限された無料お試し版もあります)を使うこともできます。これが分析されたVP9パーティションです。
パーティションに分割すると、それらについて予測を行うことができます。インター予測のために、動きベクトルと差分を送信する必要があり、またイントラ予測のために、予測方向と差分を送信する必要があります。
差分ブロック (予測パーティション - 実際のパーティション
)を生成した後、それを変換することで画質をある程度保ったまま、どのピクセルを捨てられるかが分かるようになります。それにはいくつかの変換が存在します。
他の変換もありますが、離散コサイン変換(DCT)をしっかり見ていきます。DCTの主な特徴は:
- ピクセルブロックを同じサイズの周波数係数ブロックに変換する。
- エネルギーを偏らせて、空間的冗長性を削減しやすくする。
- 元に戻せる、またはピクセルに戻せる
2017年2月2日にCintra, R. J.とBayer, F. Mが14加算のみの画像圧縮用DCT近似変換という論文を発表しました。
上記の箇条書きの利点を全て理解しなかったとしても心配いりません。その本当の価値を見い出すためにいくつかの実験を試みます。
次のピクセルのブロック(8x8)を例にとりましょう:
これはブロック画像(8x8)を描画します:
このピクセルのブロックにDCTを適用すると係数のブロック (8x8)を得ます:
この係数のブロックを描画すれば、次の画像が得られるでしょう:
元の画像とは似ても似つかないことが分かり、最初の係数は他の係数とは大きく異なっていることにお気づきかもしれません。この最初の係数はDC係数として知られ、入力配列のサンプル全体を表します。平均に似た何かです。
この係数のブロックは高周波数成分を低周波数成分から切り離すという面白い特性を持っています。
画像では、エネルギーのほとんどは低周波に集中されます。それで画像を周波数成分に変換して高周波数係数を捨てれば、画質をそれほど犠牲にせずに画像を表現するのに必要なデータ量を削減できます。
周波数は信号がどれだけ速く変化するかを意味します。
では得られた知識を使って、元の画像をDCTを使って周波数(係数のブロック)に変換して、もっとも重要でない係数の部分を捨てる実験をしてみましょう。
まず、画像を周波数領域に変換します。
次に、係数の一部(67%)を捨てます。捨てるのはほとんどは右下の部分です。
最後に、この一部が捨てられた係数のブロックから画像を再生成し(元に戻せる必要があることを覚えておいてください)、元の画像と比較します。
この画像は元の画像と似ていますが、多くの相違点が発生しています。67.1875%を捨てましたが、 少なくとも元の画像に似ている画像を得ることができました。より賢い方法で係数を捨てて、もっと画質を良くすることもできたのですが、それは次のトピックで扱います。
それぞれの係数は画素全体を使って形成される
それぞれの係数は、1つの画素に直接マッピングしているわけではなく、画素全体の重み付き合計であることに留意することは重要です。下記の素晴らしいグラフは、1番目と2番目の係数が、それぞれのインデックスで異なる重みを使って、どのように計算されるかを示しています。
DCT基底ごとの単純な画像の形成を見てDCTを視覚化することもできます。例えば、下記はそれぞれの係数の重みを使って1つの文字が形成されていく過程です。
DCT変換を実験しましょう。
前のステップ (変換)で係数をいくつか捨てるときに、量子化のようなものを行いました。このステップでは、捨てる情報(損失部分)を選びます。単純な言葉でいうと、圧縮を成し遂げるために係数を量子化します。
どのように係数のブロックを量子化できるでしょうか?1つの単純な方法は、均一量子化でしょう。ブロックを単一値 (10) で割り、小数点を切り捨てます。
どのようにこの係数のブロックを元に戻せる(再量子化)でしょうか?同じ値 (10)を掛けることにより戻すことができます。
このやり方は最適な方法ではありません。それぞれの係数の重要度を考慮していないからです。 単一値の代わりに量子化マトリクスを使うことができます。このマトリクスでDCTの特性を活かすことができます。右下を一番量子化して、左上はあまり量子化しません。JPEGは似たやり方を使っています。ソースコード上でこのマトリクスを見ることができます。
量子化を実験しましょう。
データ(画像 ブロック/スライス/フレーム)を量子化した後、さらに可逆圧縮することができます。データ圧縮のためのたくさんの方法(アルゴリズム)が存在します。それらのいくつかを簡単に体験していきます。より深い理解のためには、この素晴らしい本Understanding Compression: Data Compression for Modern Developersを読むとよいでしょう。
記号のストリームを持っているとします: a、e、r、tとそれらの確率(0から1)がこのテーブルで表されています。
a | e | r | t | |
---|---|---|---|---|
確率 | 0.3 | 0.3 | 0.2 | 0.2 |
もっとも確率が高いものには(より小さな)ユニークなバイナリコードを、もっとも確率が低いものにはより大きなバイナリコードを割り当てることができます。
a | e | r | t | |
---|---|---|---|---|
probability | 0.3 | 0.3 | 0.2 | 0.2 |
binary code | 0 | 10 | 110 | 1110 |
ストリーム eatを圧縮してみましょう。それぞれの記号に8ビット使うと、圧縮なしで24ビット使うことになります。しかし、それぞれの記号をそのコードで置き換えると、スペースを節約できます。
まず記号eを符号化して10
になります。2つ目の記号aを符号化すると、足されて(算数の方法ではなく) [10][0]
となります。最後に3つ目の記号tを符号化すると、最終的な圧縮されたビットストリームは [10][0][1110]
もしくは1001110
となり、(もとより3.4倍小さなスペースである)7ビットしか使いません。
それぞれのコードはユニークな接頭符号を持つ必要があることに注意してください ハフマンがこれらの数字を見つけることを助けてくれます。いくつかの問題がありますが、この方法はいくつかのビデオコーデックでまだサポートしています。これは圧縮を必要とする多くのアプリケーションに有用なアルゴリズムです。
エンコーダーとデコーダーの両方がそのコードの記号テーブルを知らなくてはいけません。そのためテーブルも送信する必要があります。
記号: a, e, r, s, t のストリームを持っていて、それらの確率がこの表によって表されると仮定しましょう。
a | e | r | s | t | |
---|---|---|---|---|---|
probability | 0.3 | 0.3 | 0.15 | 0.05 | 0.2 |
この表を考慮に入れて、出現順にソートされた全ての可能な記号を含む範囲グラフを作ります。
さて、ストリーム eatを符号化してみましょう。最初の記号eを取り上げます。それは0.3以上0.6未満に位置しています。この部分範囲を取り上げ、それを再び同じ割合で分割します。
ストリームeatの符号化を続けましょう。次に記号aを取り上げます。これは0.3以上0.39未満に位置しています。そして、最後の記号 tを取り上げます。もう一度同じ処理を行うと0.354以上0.372未満という最終的な範囲を得ます。
最終範囲0.354以上0.372未満から1つの数値を取り上げる必要があります。0.36を取り上げましょう。しかしこの部分範囲内ならどんな数値を選んでもかまいません。この数値だけで、元のストリームeatを復元することができます。これは、ストリームを符号化する為に範囲の範囲に線を引くかのように捉えることができます。
逆過程 (別名 復号化) は同様に簡単で、数値0.36と元の範囲を使って、同じ処理を行い、この数値から符号化されたストリームを明らかにします。
最初の範囲で、その数値が1つの部分に一致し、それが最初の記号であることに気づきます。それから、その部分範囲を以前行ったようにまた分割します。すると0.36が記号aに合うことがわかり、同じ処理を繰り返すと、最後の記号であるtを見つけます(符号前のストリームeatを形成します)。
符号化と復号化の両方は、記号確率テーブルを知る必要があります。それでテーブルを送信する必要があります。
素晴らしいですよね。人々は本当に賢く、このような解決策を生み出しました。いくつかのビデオコーデックはこの技法を使っています。(もしくは少なくともオプションとして提供しています)。
この考えは、量子化ビットストリームを可逆圧縮する為のものです。間違いなくこの記事では伝えきれていない詳細、理由、トレードオフ等が山のようにあります。しかし、読者はデベロッパーとしてもっと学ぶべきです。より新しいコーデックは別のANSのようなエントロピー符号化アルゴリズムを使おうとしています。
1つはCABAC、もう一方はCAVLCを使って2つのストリームを生成し、生成する為にかかる時間と最終的なサイズを比較してみましょう。
これらのステップ全てを行った後、圧縮されたフレームとこれらのステップまでのコンテキストを一つにまとめる必要があります。 エンコーダによってなされた決定をデコーダへ明示的に知らせる必要があります。ビット深度、色空間、解像度、予測情報(動きベクトル、イントラ予測方向)、プロファイルレベル、フレームレート、フレームタイプ、フレーム数、その他多数です。
H.264ビットストリームについて表面的に学んでいきます。最初のステップは最小限のH.264 *ビットストリームを生成することです。このレポジトリとffmpegを使って、これができます。
./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264
* ffmpegは、デフォルトでSEI NALとして符号化された全パラメータを加えます。NALが何であるかは後ほど定義します。
このコマンドは、単一フレーム、64x64、色空間がyuv420、次の画像をフレームとして使っている生のh264ビットストリームを生成します。
AVC (H.264)標準は、情報が NAL (Network Abstraction Layer) と呼ばれる マクロフレーム (ネットワーク的には)で送信されることを定義しています。NALの主な目的は、"ネットワークフレンドリー"なビデオ表現を提供することです。この標準は、TV (ストリームベース)、インターネット(パケットベース)やその他で動作しなければなりません。
NALユニットの境界を定義する 同期マーカーがあります。それぞれの同期マーカーは、最初は0x00 0x00 0x00 0x01
で、それ以降は0x00 0x00 0x01
の値を持ちます。もし生成されたh264ビットストリーム上で16進ダンプを行えば、ファイルの最初の方に、少なくとも3つのNALを見つけることができます。
先に述べたとおり、デコーダは画像データだけではなく、動画、フレーム、色、使用されたパラメータ、その他の詳細を知る必要があります。それぞれのNALの最初のバイトは、そのカテゴリとタイプを定義します。.
NAL タイプID | 説明 |
---|---|
0 | 未定義 |
1 | 非IDRピクチャの符号化スライス |
2 | 符号化スライスデータパーティション A |
3 | 符号化スライスデータパーティション B |
4 | 符号化スライスデータパーティション C |
5 | IDRピクチャのIDR符号化スライス |
6 | SEI 付加拡張情報 |
7 | SPS シーケンスパラメータセット |
8 | PPS ピクチャパラメータセット |
9 | アクセスユニットデリミター |
10 | シーケンスの最後 |
11 | ストリームの最後 |
... | ... |
通常、ビットストリームの最初のNALはSPSです。このNALの種類は、プロファイル、レベル、解像度やその他の汎用エンコーディング変数を知らせる役割を持ちます。
最初の同期マーカーを飛ばすと、最初のバイトを復号化してNALのタイプが何かを知ることができます。
例えば、同期マーカーの最初のバイトは01100111
です。最初のビット (0
)はforbidden_zero_bitフィールドで、次の2ビット(11
)はnal_ref_idcフィールドで、このNALが参照フィールドかどうかを示します。残りの5ビット (00111
)はnal_unit_typeフィールドで、この例ではSPS (7) NALユニットです。
SPS NALの2バイト目(2進数=01100100、16進数=0x64、10進数=100
)はprofile_idcフィールドで、エンコーダが使ったプロファイルを示します。この例では、 制約付きハイプロファイル を使っています。これはB (双方向予測)スライスをサポートしないハイプロファイルです。
SPS NALについてH.264ビットストリーム仕様を読むと、パラメータ名、カテゴリ、説明の表に多くの値を見つけるでしょう。例えば、pic_width_in_mbs_minus_1
とpic_height_in_map_units_minus_1
フィールドについて見てみましょう。
パラメータ名 | カテゴリ | 説明 |
---|---|---|
pic_width_in_mbs_minus_1 | 0 | ue(v) |
pic_height_in_map_units_minus_1 | 0 | ue(v) |
ue(v): 符号なし整数 Exp-Golomb-coded
これらのフィールドの値に対してある計算をすると、解像度を得ることができます。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) です。さらに検査してみます:
仕様の情報を使い、スライスのタイプ (slice_type)、フレーム番号(frame_num)や他の重要なフィールドを復号することができます。
いくつかのフィールドの値を得るために(ue(v)、me(v)、se(v)、te(v)
)、それをExponential-Golombと呼ばれる特別なデコーダーを使って、デコードする必要があります。この方法は、デフォルト値が多いケースではたいてい、変数値を符号化するのにとても効率的です。
このビデオのslice_typeとframe_numの値は7 (Iスライス)と0 (最初のフレーム)です。
ビットストリームをプロトコルとして見ることができます。このビットストリームについてもっと学びたい、もしくは学ぶ必要があるなら、ITU H.264 spec.を参照してください。下記はマクロ図表で、ピクチャデータ(圧縮YUV)がどこに位置するかを示しています。
VP9ビットストリームやH.265 (HEVC)や、さらには新しいベストフレドである AV1 ビットストリームのビットストリームを探索することができます。それらはみんな似てますか?いいえ、しかし1つを学ぶと、他は簡単に理解できます。
単一フレームのビデオを生成し、mediainfoを使ってH.264ビットストリームを検査してみましょう。実際、h264 (AVC)ビットストリームをパースするソースコードを見ることもできます。
Intel Video Pro Analyzerを使うこともできます。有料ですが、最初の10フレームに制限された無料お試し版もあり、学習目的としては問題ありません。
多くの現代のコーデックが、これまで学んできた同じモデルを使っていることに気づくでしょう。実際のビデオコーデックのブロック図をみてみましょう。これは学んできた全てのステップを含んでいます。少なくともコーデック関連の発明や文献についてより理解することができるようになったことになります。
まず720p解像度で30fpsで1時間のビデオファイルを保存するのに139GBのストレージが必要になることを計算しました。ここで学んだ技法つまりインター予測、イントラ予測、変形、量子化、エントロピー符号化、その他を駆使して、ピクセルあたり0.031ビットで表現できると仮定すると、139GBに対したった367.82MBだけで同じ知覚画質のビデオを保存できます。
今回使用したビデオを元にピクセルあたり0.031ビットが必要になることを導きました。
今、コーデックの仕組みについてより理解しています。そのため、新しいコーデックがより高い解像度をより少ないビットでどのように配信できるのか簡単に理解できます。
AVCとHEVCを比較してみましょう。より多くのCPUサイクル(複雑さ)と圧縮率は、ほとんどいつでもトレードオフであることを心に止めておきましょう。
HEVCはAVCに比べて、より大きく、より多くのパーティション (と サブパーティション)のオプションを持っています。そしてより多くの方向性イントラ予測や角度イントラ予測、改善されたエントロピー符号化やその他を持っています。これら全ての改良のおかげで、H.265はH.264に比べて50%以上の圧縮をすることができるのです。
[TODO]
[TODO]
単純なトークンシステムを使ってコンテンツを保護することができます。トークンを持っていないユーザーはビデオをリクエストしようとしても、CDNが禁止します。一方有効なトークンを持つユーザーはそのコンテンツを再生することができます。これはたいていのウェブ認証システムとほとんど同じように動作します。
このトークンシステムの一人のユーザーが、あるユーザーにビデオをダウンロードさせて、それを配布させることもできます。DRM (デジタル著作権管理) システムを使ってこれを避けることができます。
実際の製品システムで、人々はしばしばこれら両方の技術を使って、認可と認証を提供します。
- FPS - FairPlay Streaming
- PR - PlayReady
- WV - Widevine
DRMはDigital rights management(デジタル著作権管理)を意味します。これは、ビデオやオーディオなどのデジタルメディアに著作権保護を提供する方法です。多くの場所で使われていますが、広くは受け入れられていません。
コンテンツ製作者(たいていはスタジオ)は、デジタルメディアの不正な再配布を防ぐために、 知的財産が複製されることから守りたいためです。
DRMの抽象的で一般的な形式を、とても単純な方法で説明していきます。
コンテンツC1 (例えば hlsやdashビデオストリーミング)とプレイヤーP1 (例えば shaka-clappr、exo-player、ios)がデバイスD1 (例えば スマートフォン、テレビ、タブレット、デスクトップ/ノートブック)上にあり、DRMシステムDRM1 (widevine、playready、FairPlayなど)を使っているとしましょう。
コンテンツC1は、システムDRM1からの対称鍵K1で暗号化され、暗号化コンテンツC'1を生成します。
デバイスD1のプレイヤーP1は2つの(非対称)鍵、秘密鍵PRK1 (この鍵は保護され1D1にしか知られていない)と公開鍵PUK1を持っています。
1保護される: この保護は、ハードウェアを介してなされます。例えば、この鍵は、特別な(読み取り専用)チップの中に保存されます。これは、復号を提供するブラックボックスのように働きます。もしくは(あまり安全でない)ソフトウェアによりなされます。DRM システムは、与えられたデバイスがどのタイプの保護を持っているかを知る方法を提供します。
プレイヤーP1がコンテンツC'1を再生したいとき、DRMシステムDRM1を使う必要があり、まず公開鍵PUK1を与えます。DRMシステムDRM1は クライアントの公開鍵PUK1で暗号化された鍵K1を返します。理論上、このレスポンスはD1だけが復号可能です。
K1P1D1 = enc(K1, PUK1)
P1は、DRMローカルシステム (それは特別なハードウェアかソフトウェアであるSOCもなりえる)を使います。このシステムは、秘密鍵PRK1を使って、コンテンツを復号することができます。K1P1D1からの対称鍵K1を復号化して、C'1を再生することができます。鍵がRAM上で外にさらされないのがベストです。
K1 = dec(K1P1D1, PRK1)
P1.play(dec(C'1, K1))
dockerがインストールされていることを確認して、./s/start_jupyter.sh
を実行し、ターミナル上の指示に従ってください。
- DEMUXED - 最後の2つのイベントプレゼンテーションをチェックすることができます。
ここに最高のコンテンツがあります。このテキストで見られる全ては、ここから抽出されたか、元になっているか、何か影響を受けています。この驚くべきリンク、本、動画などで、知識をより深くすることができます。
オンラインコースとチュートリアル:
- https://www.coursera.org/learn/digital/
- https://people.xiph.org/~tterribe/pubs/lca2012/auckland/intro_to_video1.pdf
- https://xiph.org/video/vid1.shtml
- https://xiph.org/video/vid2.shtml
- https://wiki.multimedia.cx
- https://mahanstreamer.net
- http://slhck.info/ffmpeg-encoding-course
- http://www.cambridgeincolour.com/tutorials/camera-sensors.htm
- http://www.slideshare.net/vcodex/a-short-history-of-video-coding
- http://www.slideshare.net/vcodex/introduction-to-video-compression-13394338
- https://developer.android.com/guide/topics/media/media-formats.html
- http://www.slideshare.net/MadhawaKasun/audio-compression-23398426
- http://inst.eecs.berkeley.edu/~ee290t/sp04/lectures/02-Motion_Compensation_girod.pdf
本:
- https://www.amazon.com/Understanding-Compression-Data-Modern-Developers/dp/1491961538/ref=sr_1_1?s=books&ie=UTF8&qid=1486395327&sr=1-1
- https://www.amazon.com/H-264-Advanced-Video-Compression-Standard/dp/0470516925
- https://www.amazon.com/High-Efficiency-Video-Coding-HEVC/dp/3319068946
- https://www.amazon.com/Practical-Guide-Video-Audio-Compression/dp/0240806301/ref=sr_1_3?s=books&ie=UTF8&qid=1486396914&sr=1-3&keywords=A+PRACTICAL+GUIDE+TO+VIDEO+AUDIO
- https://www.amazon.com/Video-Encoding-Numbers-Eliminate-Guesswork/dp/0998453005/ref=sr_1_1?s=books&ie=UTF8&qid=1486396940&sr=1-1&keywords=jan+ozer
ビットストリーム仕様:
- http://www.itu.int/rec/T-REC-H.264-201610-I
- http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=12904&lang=en
- https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
- http://iphome.hhi.de/wiegand/assets/pdfs/2012_12_IEEE-HEVC-Overview.pdf
- http://phenix.int-evry.fr/jct/doc_end_user/current_document.php?id=7243
- http://gentlelogic.blogspot.com.br/2011/11/exploring-h264-part-2-h264-bitstream.html
- https://forum.doom9.org/showthread.php?t=167081
- https://forum.doom9.org/showthread.php?t=168947
ソフトウェア:
- https://ffmpeg.org/
- https://ffmpeg.org/ffmpeg-all.html
- https://ffmpeg.org/ffprobe.html
- https://mediaarea.net/en/MediaInfo
- https://www.jongbel.com/
- https://trac.ffmpeg.org/wiki/
- https://software.intel.com/en-us/intel-video-pro-analyzer
- https://medium.com/@mbebenita/av1-bitstream-analyzer-d25f1c27072b#.d5a89oxz8
非ITUコーデック:
- https://aomedia.googlesource.com/
- https://github.com/webmproject/libvpx/tree/master/vp9
- https://ghostarchive.org/archive/0W0d8 (was: https://people.xiph.org/~xiphmont/demo/daala/demo1.shtml)
- https://people.xiph.org/~jm/daala/revisiting/
- https://www.youtube.com/watch?v=lzPaldsmJbk
- https://fosdem.org/2017/schedule/event/om_av1/
エンコードの概念:
- http://x265.org/hevc-h265/
- http://slhck.info/video/2017/03/01/rate-control.html
- http://slhck.info/video/2017/02/24/vbr-settings.html
- http://slhck.info/video/2017/02/24/crf-guide.html
- https://arxiv.org/pdf/1702.00817v1.pdf
- https://trac.ffmpeg.org/wiki/Debug/MacroblocksAndMotionVectors
- http://web.ece.ucdavis.edu/cerl/ReliableJPEG/Cung/jpeg.html
- http://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html
- https://prezi.com/8m7thtvl4ywr/mp3-and-aac-explained/
- https://blogs.gnome.org/rbultje/2016/12/13/overview-of-the-vp9-video-codec/
テスト用ビデオシーケンス:
- http://bbb3d.renderfarming.net/download.html
- https://www.its.bldrdoc.gov/vqeg/video-datasets-and-organizations.aspx
その他:
- https://github.com/Eyevinn/streaming-onboarding
- http://stackoverflow.com/a/24890903
- http://stackoverflow.com/questions/38094302/how-to-understand-header-of-h264
- http://techblog.netflix.com/2016/08/a-large-scale-comparison-of-x264-x265.html
- http://vanseodesign.com/web-design/color-luminance/
- http://www.biologymad.com/nervoussystem/eyenotes.htm
- http://www.compression.ru/video/codec_comparison/h264_2012/mpeg4_avc_h264_video_codecs_comparison.pdf
- https://web.archive.org/web/20100728070421/http://www.csc.villanova.edu/~rschumey/csc4800/dct.html (was: http://www.csc.villanova.edu/~rschumey/csc4800/dct.html)
- http://www.explainthatstuff.com/digitalcameras.html
- http://www.hkvstar.com
- http://www.hometheatersound.com/
- http://www.lighterra.com/papers/videoencodingh264/
- http://www.red.com/learn/red-101/video-chroma-subsampling
- http://www.slideshare.net/ManoharKuse/hevc-intra-coding
- http://www.slideshare.net/mwalendo/h264vs-hevc
- http://www.slideshare.net/rvarun7777/final-seminar-46117193
- http://www.springer.com/cda/content/document/cda_downloaddocument/9783642147029-c1.pdf
- http://www.streamingmedia.com/Articles/Editorial/Featured-Articles/A-Progress-Report-The-Alliance-for-Open-Media-and-the-AV1-Codec-110383.aspx
- http://www.streamingmediaglobal.com/Articles/ReadArticle.aspx?ArticleID=116505&PageNum=1
- http://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/
- https://cardinalpeak.com/blog/the-h-264-sequence-parameter-set/
- https://cardinalpeak.com/blog/worlds-smallest-h-264-encoder/
- https://codesequoia.wordpress.com/category/video/
- https://developer.apple.com/library/content/technotes/tn2224/_index.html
- https://en.wikibooks.org/wiki/MeGUI/x264_Settings
- https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming
- https://en.wikipedia.org/wiki/AOMedia_Video_1
- https://en.wikipedia.org/wiki/Chroma_subsampling#/media/File:Colorcomp.jpg
- https://en.wikipedia.org/wiki/Cone_cell
- https://en.wikipedia.org/wiki/File:H.264_block_diagram_with_quality_score.jpg
- https://en.wikipedia.org/wiki/Inter_frame
- https://en.wikipedia.org/wiki/Intra-frame_coding
- https://en.wikipedia.org/wiki/Photoreceptor_cell
- https://en.wikipedia.org/wiki/Pixel_aspect_ratio
- https://en.wikipedia.org/wiki/Presentation_timestamp
- https://en.wikipedia.org/wiki/Rod_cell
- https://it.wikipedia.org/wiki/File:Pixel_geometry_01_Pengo.jpg
- https://leandromoreira.com.br/2016/10/09/how-to-measure-video-quality-perception/
- https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
- https://softwaredevelopmentperestroika.wordpress.com/2014/02/11/image-processing-with-python-numpy-scipy-image-convolution/
- https://tools.ietf.org/html/draft-fuldseth-netvc-thor-03
- https://www.encoding.com/android/
- https://www.encoding.com/http-live-streaming-hls/
- https://web.archive.org/web/20150129171151/https://www.iem.thm.de/telekom-labor/zinke/mk/mpeg2beg/whatisit.htm
- https://www.lifewire.com/cmos-image-sensor-493271
- https://www.linkedin.com/pulse/brief-history-video-codecs-yoav-nativ
- https://www.linkedin.com/pulse/video-streaming-methodology-reema-majumdar
- https://www.vcodex.com/h264avc-intra-precition/
- https://www.youtube.com/watch?v=9vgtJJ2wwMA
- https://www.youtube.com/watch?v=LFXN9PiOGtY
- https://www.youtube.com/watch?v=Lto-ajuqW3w&list=PLzH6n4zXuckpKAj1_88VS-8Z6yn9zX_P6
- https://www.youtube.com/watch?v=LWxu4rkZBLw
- https://web.stanford.edu/class/ee398a/handouts/lectures/EE398a_MotionEstimation_2012.pdf
- https://sander.saares.eu/categories/drm-is-not-a-black-box/