はじめに
この記事はドワンゴ Advent Calendar 2021 の6日目の記事です。
入社から5年、ドワンゴ Advent Calendar への参加も5年目となりました。 今回は去年の続きとして、動画コーデックとはどういうものか、どうやって動画を圧縮しているのかについて、ざっくり書いてみようと思います。📝
去年までの記事はこちら
ただしがき
私の動画コーデックについての知識は H.264/AVC など MPEG 系のものが中心となっています。
そのため本記事で触れる知識も MPEG 系のコーデックをベースとしたものになります。
初学者向けの、ざっくりした解説になります。 間違っている内容・表現も多々あると思いますが、なんとなく掴んでもらうための記事なのでご容赦ください🙇
この記事で扱うこと
- MPEG 系動画コーデックの処理の概要
この記事では扱わないこと
- コーデック・コンテナの違い (前記事参照)
- 動画コーデックの各処理の詳細・理論
- MPEG 系以外の動画コーデック
- 動画コーデック同士の比較
- 静止画コーデック
- 音声コーデック
コーデック
コーデックとは、映像、音声など、それぞれのメディアを符号圧縮するための規格です。 また、実際に符号化・復号を行うソフトウェア自体のことも指します。 データを圧縮して、転送容量や保存容量を削減しつつ、元の映像や音声の再現するための工夫が詰まっています。
コンテナとの違いについては去年のアドベントカレンダー記事をご覧ください。
以下では動画のコーデックについて触れていきます。
動画コーデックから見た動画
コンピュータ上における動画とは、時間の順に並んでいる画像の集まりのことです。 画像の順番や枚数は、現実世界における時間と対応付いていることが多いでしょう。
動画コーデックにとっては、動画は "時間の順に並んでいる画像の集まり" でしかありません。
画像が最初から何枚目に再生されるのか、何秒目に再生されるフレームなのかということはコンテナが扱う部分で、動画コーデックではほぼ考えません。
基本的には1枚の画(1フレーム)ごとに扱います。 1フレームの画像は、さらに扱いやすいようにブロックに分けます。 多くの処理はこのブロック単位に完結して行われます。 どのようにブロックに分割するのかは、動画を作るエンコーダの工夫のしどころで、実装により様々です。 多くの場合、色の変化が少ない部分は大きなブロック、色が細かく変化する部分は小さなブロックと、画像の性質を反映したような分割になります。
様々な最適化の工夫として、フレームを跨いだ処理が行われたり、上記のブロックとは別で用途に合わせて画面を分割するようなこともありますが、今回は説明を割愛します。
エンコードとデコード
ソフトウェアとしてのコーデックは、符号化・復号を行います。 以下では、
- 符号化 = エンコード
- 入力された動画に様々な処理を施し、圧縮して、0/1 のデータにすること
- 復号 = デコード
- エンコード結果の 0/1 のデータを読み、表示できる動画として解釈すること
として扱います。
エンコードを行うソフトウェアをエンコーダ、デコードを行うソフトウェアをデコーダと呼びます。
エンコード、デコードのそれぞれがどのような処理なのか、ざっくりとした書き方にしたものを以下に示します。
デコード・エンコードの処理を見比べつつ、デコーダ・エンコーダの気持ちになってみてください(?)
個々の処理内容、その処理を行う理由については後述します。
エンコードの流れ
- 1フレームごとに
- ブロックに分割する
- 1ブロックごとに
- ブロックの画像を予測した 予測画像 をつくる
- 予測誤差 (入力画像と予測画像との差分) を計算する
- 予測誤差を圧縮する
- 変換
- 量子化
- ブロックを 0/1 へ符号化
1フレームごとに最終的に出力される、エンコードされたファイルには、以下のような情報が含まれます。
- どのようにブロック分割するのか
- どのように予測したのか
- 圧縮された予測誤差
デコードの流れ
- 1フレームごとに
- どのようにブロック分割するのか を読む
- 1ブロックごとに
- どのように予測したのか を読み、予測画像をつくる
- 圧縮された 予測誤差 を読む
- 0/1 からブロックに復号
- 逆量子化
- 逆変換
- 予測画像 に 予測誤差 を加算して 出力画像 をつくる
コーデックが他にやっていること
上記の処理の流れはかなり省略したもので、実際には他にも様々な処理を行っています。 以下に例を示します。
- 動画全体に関わるような情報は、動画全体用として記録する(Sequence Parameter Set; SPS など)
- 1枚の画像全体に関わるような情報は、画像全体用として記録する(Picture Parameter Set; PPS など)
- データ量を多く使うフレームとそうでないフレームに分け、画像のきれいさを保ちながら全体としてデータ量を減らす(I, P, B ピクチャ)
- 同じようなデータを何度も書く必要がないように、ブロックの分け方や変数の省略などで工夫する
- 各変数などを 0/1 のビット列として書き出すとき、エントロピー符号などを使ってより短いビット列で書き出す
- ブロック境界が不自然に見えないように、フィルタリングを行う
- 人の目に違和感なく情報量を減らすために、コーデック中では RGB を YUV4:2:0 に変換して扱う
- 並列化するための仕組み(WPP)
- デコーダが持ってるメモリ量などを想定して、デコードできるようにエンコードする
- その他 N 個の工夫
なぜ〇〇をするのか
ただしがき再
以下において、白黒のブロックで画像を表している場合には、値が大きい方が黒色 だと思って読んでください。
通常、画像のデータは値が大きい方が白色を指すことが一般的です。
動画データにおいてもおおよそ同様です。
しかし、このブログ記事は白い背景であり、その背景に対しては黒い方が"値が大きい感"が出ると思ったため、イメージ画像として、値が大きい部分を黒色で表示します。
また、ブロックの大きさや含まれる画素数も適当なので、イメージ画像と思ってください 🙏
ブロック分割
動画の画面サイズは、SQCIF(128x96) や 4K, 8K など、大きさも縦横比も様々です。
大きな画像を処理する場合、たいてい計算量が大きくなるため並列化したくなります。
例えば 4x4~64x64 など決まった範囲の大きさに分割し、そのブロックごとに処理を分けることで、様々なサイズの画像に対応することができ、並列化しやすくなります。
また、画像の傾向も画面全体で同じではなく、部分によって異なることが多くあります。 傾向に合わせた分割を行い、傾向に合わせた圧縮方法を用いることで、より圧縮率を上げることができます。
予測・差分
あるブロック画像をエンコードすることを考えます。 このブロックを普通に記録しようとすると、すべての画素を1つずつ記録することになると思います。
ここで、仮にこのブロックの画像を予測した画像があらかじめ用意されていたとします。
予測が完全に当たった場合、"予測画像との差分は全部 0 である" と記録するだけで元のブロックを記録できます。
これは画素を1つずつ記録するよりも圧倒的に少ないデータ量であり、つまりこのブロックの情報を圧縮できたことになります。
この大幅な圧縮を狙うため、予測・差分の処理を行います。
変換・量子化
残念ながら予測が外れて差分が生じてしまった場合、そのままでは結局ブロック1つ分の画素をすべて覚えることになります。
予測差分のような画像は、変換と量子化を行うことで効率よく記録することができます。
予測の精度が良ければ、ブロックのほとんどが値が無いところ(予測が当たったところ) になり、値があるところ(予測の外れたところ) は一部だけになります。
全体的にほとんどが変化が無い画像に対して周波数変換を行うと、低周波領域(画像の左上側)にのみ値があり高周波領域は 0 である画像ができあがります。
動画コーデックでよく利用されている周波数変換はDCT, DSTなどです。
値のある低周波成分のみを記録し、値の無い部分については"残りは全部 0 である"と記録することで、ブロック1つ分よりも少ない画素数を覚えるだけで済みます。
周波数変換後、もとめられる画質に応じて量子化を行うことでさらに情報量を削減します。 ただし、量子化は非可逆な処理であることに注意です。
予測
前項において、謎の"予測画像"があるとして話を進めました。 しかしこいつはなんなのでしょう。 どこから出てくるのでしょう。
フレーム内予測
1枚の画像の中で、1つのブロックを予想してみましょう。
緑色のブロックは左側の画素の色が似ているので、その画素をコピーすることで予測ができます。
今回は真横にコピーしましたが、上の画素が似ている場合は真下にコピーしたり斜めにコピーしたりと、画像に合わせたコピー方法を選ぶと予測精度を上げることができます。
どの方向にコピーするのかという情報などが、予測に必要な情報として符号化されます。
フレーム外予測
次は別のフレームの画像も参照しながら、1つのブロックを予想します。
静止しているカメラの前を通る物体や、動いている物体をカメラで追いかけるときの背景などに着目すると、同じ形のもの異なる時刻に異なる場所に出現していることがわかります。
同じ形のものが写っているならば、それを参照することで予測ができます。
参照するフレームや、どの位置を参照するのかを指すベクトル(動きベクトル) などが、予測に必要な情報として符号化されます。
何を見て予測するのか
デコーダ
デコーダは自分が持っている情報の中で、一番元の画像に近い画像、すなわち出力画像を参照して予測します。
正確には、デコードからの出力画像は必要な分だけバッファに保存され、そのバッファを参照してフレーム外予測を行います。
エンコーダ
エンコーダは、予測に使う画像を手に入れることができません。
一見、入力画像がデコーダの出力画像と同じであるかのように見えます。 しかし、実際は量子化が非可逆な処理なので、量子化以前と以後でデータは変化してしまっており、最終的に出来上がる出力画像は最初の入力画像とは異なる画像になります。
違う画像を使うと違う予測結果になり結果がずれていってしまうため、出力画像の代わりに入力画像を予測に使うことはできません。 そのため、エンコーダではデコーダの動作をシミュレートし、デコーダ側と全く同じ出力画像つくります。 この画像を再構成画像と呼びます。
この再構成画像も必要な分だけバッファに保存し、そのバッファを参照してフレーム外予測を行います。
まとめ
- 基本的には動画は1枚ずつの画像について、ブロックごとに記録・読み出ししている。
- エンコードとデコードでは、同じような処理をひっくり返して行っている。
- エンコードは、入力画像に対して、予測差分、変換、量子化、符号化 を行う。
- デコードは、復号、逆量子化、逆変換、予測加算 を行って、出力画像を得る。
- エンコードでは、デコードの処理をシミュレートするため、逆量子化、逆変換、予測加算 も行う。
- 予測 にはフレーム内予測・フレーム外予測がある。
- フレーム内予測は、近い部分の色が似ているなど、画像の特徴を生かした予想である。
- フレーム外予測は、画像が時間的に変化するという動画としての特徴を生かした予測である。
おわりに
今回は主に H.264/AVC をベースとして動画コーデックの処理概要についてまとめてみました。
YUV 色表現や I, P, B ピクチャ、エントロピー符号化など、動画コーデックに必須の機能でありながら今回解説していない部分が多くあります。
今回説明していない部分についてはこちらの神資料が分かりやすいです。
H.264/AVC には、他にも圧縮・高画質化のための機能があり、また実装においてもそれぞれのエンコーダ・デコーダが工夫を凝らしています。 AV1 や VVC など新しい動画コーデックも出てきており、まだまだ奥深い世界なので、興味があればぜひ色々調べてみてください。
参考文献
- 映像情報メディア学会 編『総合マルチメディア選書 MPEG』 オーム社 1996
- 角野眞也 菊池義浩 鈴木輝彦 共編 『改訂3版 H.264/AVC教科書』インプレスR&D 2009
- 鈴木輝彦 高村誠之 中條健 共編 『H.265/HEVC教科書』インプレスジャパン 2013
- Recommendation H.264 https://www.itu.int/rec/T-REC-H.264/