導入
お久しぶりです。戌亥です。晴れてセーフィー株式会社新卒エンジニアとなりました。(参考)
研修で動画の仕組みに関して学ぶ機会があったのですが、そのうちの画像圧縮に関してイメージしづらい部分があると感じました。同期エンジニアで勉強会があり、今回はそこで話した内容を一部加筆修正して解説しようと思います。
コンピュータでの画像の扱い方
画像圧縮の前に画像がデータとして扱われていることを知らない方向けに、画像がおおよそどのようにデータとして扱われているかを説明します。
説明のために画像を拡大して見ることができるアプリをGeminiで作成しました。
画像を入力し、クリックした地点を8×8マスで拡大してみることができます。拡大だけでなく、RGB値で表示するモードとバイナリ値で表示するモードがあります。

こちらが画像を拡大した時の図です。左の画像の小さくオレンジ色になっている(セーフィーのロゴの嘴)部分が拡大されたものが右のグリッド図になります。
一般的に画像は0~255の256段階のRGB(Red, Green, Blue)値を各画素ごとにもっています。以下の画像は拡大したグリッド図をRGBの値で表したものです。

コンピュータは0と1で解釈するのでデータ量は2進数で考える必要があります。この0または1をビットと言う単位で考えます。
256は2進数で28なので0~255を表すためには8ビットが必要です。画像において、このビット数を減らすのが画像圧縮です。
YUVとは
YUVとはRGBやHSVなどと同じ色空間(色の表し方)の1つです。
人の眼の「輝度信号に対して敏感」と「色差信号に対して鈍感」という特性に合わせた表し方で、輝度信号Yと青色成分の色差U(または)と赤色成分の色差V(または
)で構成されています。
ディスプレイに表示させる際やカメラで撮影した生のデータはRGBですが、この「色差信号に対して鈍感」という特性から、YUVに変換させることでビット数を減らすことができます。
これを体験できるアプリをGeminiを使って作りました。
ここに画像ファイルを上げると、YUVそれぞれの層がどのような役割をしているかが直感的にわかります。またこちらのスキップ数というのは、Yで縦×横、Uで縦×横、Vで縦×横とそれぞれ2次元の画素を持っています。これを何個ずつ進むかというものです。
例えばスキップ数が1であればその層は完全な情報です。スキップ数が2だと1つ飛ばして、飛ばしたマスはその前の値で埋められるので、同じ画素値が2つ連続することになります。そうした時の全体のビット数はYが100%、Uが50%、Vが50%となります。つまりビット数は2/3で67%ほどになります。これはYUV 4:2:2と呼ばれる方式と同じです。
YUV 4:2:0と呼ばれる水平・垂直方向にそれぞれ1つ飛ばしていく方式ではYが100%、Uが25%、Vが25%となりビット数は1/2となります。
アプリの方で試してもらうとわかる通り、Yをスキップするとすぐにわかりますが、UとVを少し飛ばしただけでは気づきません。こうして色差信号を減らしていくと圧縮することができます。
ITU-R BT.601というアナログ信号とデジタル信号を相互に変換するための規格があります。これによると、RGBとYUVの関係式は式1のように定義されています。
式1:RGB to YUV
離散コサイン変換(DCT)
DCTは厳密には圧縮方法ではなく、圧縮のために必要な処理の1つです。変換のために画像に周波数の考えを導入します。


右のようなのっぺりとした濃淡の変化が少ない画像を低周波な画像、唐草模様や左の濃淡の激しい画像を高周波な画像といいます。自然画では高周波な画像が少なく、高周波成分を削ってしまってもあまり影響を与えません。そこでDCTを行って画像を周波数にしてから高周波成分を削ることで画質にあまり影響を与えずに画像を圧縮できます。
DCTの具体的な手法の前に、フーリエ変換という概念をお話しします。「全ての周期信号は正弦波の合成で表現できる」という考えがあり、これに基づき波形が永遠に続くと仮定して合成元の正弦波を導けます。ざっくりいうとこれがフーリエ変換であり、波形がどの周波数のどのくらいの強度の正弦波で構成されているかがわかるというものです。
DCTの式は式2で示すことができますが、勉強していた当時、これを見てもよくわかりませんでした。
式2:Image to Power

DCTはフーリエ変換と似たことをやっており、画像の各層を2次元の波だと考え、ブロック単位で細かく分けた画像を周波数成分に分解します。
例えば8×8マス単位でDCTを行う場合、まず0~7Hzの波形を作ります。
そうすると下記の図のような8つの波形ができます。

図:0~7Hzの離散波形
それをもとに下記の図のような基底関数画像といわれるグレースケールの画像を作成します。これは2つの波の該当する部分を8×8の各マスで掛け算します。

図:基底関数画像(u, v)= (5, 2)
このような画像を0~7Hzの8パターンを縦×横の計64パターンを用意します。
これは8×8マス単位でDCTを行うのであればどんな画像に対してでも同じ64パターンの画像を使います。
下記の図のように、生成した64パターンの画像と対象のブロックとの内績を取った値がDCT変換後の画素値となります。

図:DCT変換
DCT変換後の画像はブロック単位(この場合は8×8の画像)になります。分割した数だけDCT画像(係数)があるのでこの状態では圧縮されていません。画像が周波数情報に変わっただけです。
これを体験できるアプリも同様にGeminiを使って作りました。
画像を入力し、クリックして範囲を指定するとその部分に対してDCT係数を計算することができます。出力されたDCT係数に対して全範囲を選択して逆DCTをすることで、元の画像に戻すことができます。当然範囲を限定して逆DCTをかけると圧縮によって元の画像とは少し違う結果となります。出力された表を見るとその画像の局所的なDCT係数がわかりますが、各係数がその周波数の影響を与える大きさだと思っていただけるといいです。ぜひいろんな条件で試してみてください。(逆DCT結果の画像がグレースケールなのはYUVのY成分のみで計算しているためです。)
まとめ
動画圧縮の仕組みについてYUVとDCTを用いる部分について簡単なアプリを使って解説しました。YUVを用いることで色差情報を削ることができ、DCTを用いて画像を周波数成分にすることで画質に影響を与えない情報に絞って削ることができることがわかりました。大学で習っている当初はあまり何に使うのか理解できなかったフーリエ変換などの概念も、割と様々な場面で出てくることが多いので、昔の自分にしっかりやっておくよう忠告したいところです。
また私が調べていたときは、出力結果画像の差でみることができるYUVはまだしも、DCTに関しては64パターンの謎の画像(基底関数画像)の意味がずっとよくわからなかったので、今回のアプリで誰かの理解の助けになるとうれしく思います。
余談ですが、最近買った「H.264/AVC教科書」が図も交えてしっかり書かれていて個人的におすすめです。この記事で動画圧縮の仕組み等に興味を持ってくださった方は読んでみてもいいかもしれません。