JPEG画像の圧縮アルゴリズムについて解説します。
JPEGとは規格策定団体(ジョイント・フォトグラフィック・エクスパーツ・グループ)の名称であり、正しくはJFIF画像(JPEG・ファイル・インターチェンジ・フォーマット)と言うべきですが、現状数あるJPEG策定規格でほぼ唯一JFIF画像のみが採用されており、JPEG画像と呼んでも殆ど差障りが無いようです。
JPEG形式は、策定団体の名前どおり、写真の効率良い圧縮を行うためのフォーマットです。
JPEG形式では多くの場合、圧縮の際にデータの一部を切捨てる事で圧縮効率を高めると言うアルゴリズムを採用しております。従って、JPEG形式は圧縮された画像を展開した場合、元通りにはならない不可逆圧縮となります。
尚、以下のJPEG画像に関する説明は最もよく使われているベースライン方式を想定して解説したものですが、他の方式(特にプログレッシヴJPEG形式)も圧縮の際のデータの扱いが異なるだけで、大きな違いがある訳ではありません。
JPEG画像の圧縮アルゴリズムは、GIF画像やPNG画像に較べて遙かに複雑です。
具体的には、JPEG画像の圧縮アルゴリズムは、以下のようになります。
以下、それぞれの処理について解説していきましょう。
RGB形式はピクセルのデータを赤・緑・青の光三原色の値に変換する方式ですが、JPEG圧縮ではこれをYCrCb方式、すなわち輝度・赤方向の色相・青方向の色相の三要素に変換します。
この方が処理がし易いからです。
具体的な変換公式はこうなります。
JPEG画像では輝度情報、色相情報(赤方向及び青方向)とも8ピクセルズ四方のブロック単位で圧縮/展開されます。
一般に人間の目は細かな輝度の違いには敏感ですが、色相の細かな違いは見落とし易いという性質があります。
このため、多くのJPEG画像では、圧縮データの削減のために色相情報は1ピクセルおきに間引きして取得しております。
つまり、多くの場合、色相情報のブロックは輝度情報のブロックより少なく取得されます。
このため、対応する画像上の領域に合わせて、輝度情報ブロック4に対して色相情報ブロックは赤方向青方向とも1ずつを対応させ(4:1:1でサンプリングして)、これを処理の一単位(MCU)とします。
具体的には縦横4ブロック(16ピクセルズ四方)の輝度情報に対して、色相情報は縦横とも1ピクセルおきにデータを取得して1ブロックに見立てます。
尚、稀に輝度ブロック4に対して色相情報ブロックを2ずつとする形式(4:2:2でのサンプリング)や、輝度ブロック4に対して色相情報ブロックを4ずつとする形式(4:4:4でのサンプリング)も利用されます。
尚、これらの方式は一部のディジタルカメラでのみ採用されているようです。
具体的には4:1:1でのサンプリングでは、一単位(MCU)について、左上の輝度情報ブロック, 右上の輝度情報ブロック, 左下の輝度情報ブロック, 右下の輝度情報ブロック, 赤方向の色相情報ブロック(縦横とも1ピクセルおきに間引きしたデータ)及び青方向の色相情報ブロック(縦横とも1ピクセルおきに間引きしたデータ)の順に六個のブロックに分割されます。
JPEG方式のアルゴリズムでは、形式に従ってMCUに分割されますが、実際の処理は8ピクセルズ四方に分けられたブロック単位です。
例えば4:1:1サンプリング形式なら、1MCUに対して六つのブロックが生じますが、それぞれが個別に処理される事になります。
DCT変換とは離散フーリエ変換の一種で、8ピクセルズ四方(64個)のデータを周波数分解するものです。
こうする事で、一見無秩序なピクセルの並びも、整然としたデータの並びに変換されるのです。
JPEG画像の圧縮で用いられている具体的なDCT変換の公式は、以下の通りです。
変換前のピクセル値をs(x,y)、変換後の値をS(u,v)としたとき、
S(u,v)=1/4 C(u)C(v) Σx=0..7Σy=0..7 (s(x,y) cos((2x+1)uπ/16) cos((2y+1)vπ/16))
一方、JPEG画像の展開で用いられているIDCT変換(逆DCT変換)の公式は、以下の通りです。
逆変換前のピクセル値をS(x,y)、逆変換後の値をs(u,v)としたとき、
s(x,y)=1/4 Σu=0..7Σv=0..7 C(u)C(v) (S(u,v) cos((2x+1)uπ/16) cos((2y+1)vπ/16))
DCT変換によって、64ピクセルズの値は64段階の周波数成分に分解されますが、このとき、周波数が高ければ高いほど細かなピクセルの変化を表すものと理解すればよいでしょう。
ただ、高周波成分は実際には一般人には目に付かないほどの細かい情報であり、このため、次の量子化で大抵削られてしまいます。
量子化とは専門的な用語ですが、要するに細かい誤差を切捨てる事です。
具体的なやり方は実に単純で、整数割算、すなわち整数で割って端数を切落す事で実現します。
実際には64ピクセルズ毎に割る数を定義しており、その情報を量子化テーブルとして与えておきます。
DCT変換されたデータも8ピクセルズ四方のデータとなります。
そこで、左上隅のデータを直流成分(DC成分)、残りの63個のデータを交流成分(AC成分)として取扱います。
直流成分は前のブロックの直流成分との差を情報として取扱い、直流成分に続いて交流成分をジグザグスキャンと呼ばれる規則に従って一列に並べ直します。
ジグザグスキャンとは、64個のデータが縦横8つに並べられているものを、ジグザグ状にスキャンしていくと言うものです。
具体的には以下のようになります。
以下、右下の63番成分にたどり着くまで繰り返します。
JPEG方式のアルゴリズムでのエントロピー符号化(圧縮)にはハフマン符号が使われます。
ハフマン符号はPNG画像形式に採用されているデフレート圧縮でも使われておりますが、頻度の高いデータに短いビット数の符号を割当てる事で全体のビット数を減らすと言う形で圧縮する方法です。
一般にハフマン符号を用いて圧縮する形式では、予めハフマン符号を仕様で定義してそれを使う事で処理を単純化出来るものです。
しかしながら、JPEG方式では固定ハフマン符号が仕様で定義されておらず、従って個別にハフマン符号を定義しなければなりません。
また、個別にハフマン符号を定義した場合、符号表を添付しないとどう復元すればよいか分からず、一方では添付した符号表の大きさが圧縮効率を下げる事にもなります。
ハフマン符号は直流成分用と交流成分用を用意します。
更に多くのエンコーダは輝度成分と色相成分で異なるハフマン符号表セット(直流成分用と交流成分用の組)を用意している場合が多いですが、同じ符号表セットを使っても構いません。
実際の符号化は、以下のようになります。
直流成分はデータの値のビット数を調べ、データの上の方の無駄なビットを削ります。
そして、そのビット数をハフマン符号で表現してデータの前に付けます。
つまり、「ハフマン符号」「データ」という形になります。
交流成分は前に連続しているゼロデータの個数を数え、ゼロで無いデータのビット数に対して直流成分同様ビット数を調べてデータのビット数を減らします。
交流成分のハフマン符号は、前に続いているゼロデータの個数とゼロで無いデータのビット数を組合わせたものとなります。
尚、ゼロデータが16個連続する場合はZRL符号と呼ばれる特別な符号を発行して16個分のゼロデータを表現します。この場合、更にゼロデータが後続していても、新たにゼロから数え直します。
先行するゼロデータが15個以内の場合は「先行するゼロデータ数と後続するデータのビット数を表すハフマン符号」「後続するゼロで無いデータ」と言う形に符号化されます。
また、一番後ろの方でゼロデータが連続する場合、一番最後のゼロでないデータを符号化した後にEOB符号と呼ばれる特別なを発行する事で符号化を打切る事が出来ます。
すなわち、交流成分は63個のデータを漏れなく符号化するか、一番最後の非ゼロデータを符号化した後にEOB符号を発行するかのどちらかとなります。
JPEG画像の展開アルゴリズムは、圧縮手順の逆を辿ればいい訳ですが、ファイル形式との絡みで以下のようになります。
画像データを以下の要領で輝度情報のブロック、色相情報のブロックの順に展開する。