JPEG画像のフォーマットについて解説します。
JPEG画像のファイルはセグメントと呼ばれるブロックの集まりです。
セグメントとは、意味を持ったブロックの事で、以下の二つの形式があります。
以下の2オクテットからなる、セグメント開始の目印です。
つまり、特定のセグメントにはマーカ以外には内容がありません。
以下の構成になります。
以下の2オクテットからなる、セグメント開始の目印です。
セグメントの内容の長さを表します。実際にはL○自身の長さ(2オクテット)も含まれますので、必ず2以上の値になります。
尚、圧縮された画像データは、SOSセグメントの直後に配置される事となっております。 このデータはセグメントの形式ではありませんが、セグメントのマーカと紛らわしくなるため、十六進数で[FF]となるバイナリデータには直後に十六進数で[00]となるダミーのバイナリデータを付与する事となっております。
JPEG方式でのファイルフォーマットにおいては、以下の約束事があります。
複数オクテットからなるデータは、必ず上位オクテットから並びます(ビッグエンディアン)。
圧縮データのハフマン符号及び符号に続くバイナリデータは全て上位ビットから順に並べられ、8ビットごとにオクテットのデータとなります。
JPEG画像でのセグメントには幾つもの定義がありますが、画像展開に最低でも必要となるセグメントは以下の通りです。
JPEG画像の先頭に必ず現れるセグメントで、画像の開始を意味します。
マーカは十六進数で[FF][D8]となります。
APPセグメントには、圧縮方式などにより幾つもの定義がありますが、最低限必要なのはこのAPP0セグメントです。
APP0セグメントはSOIセグメントの直後に現れ、幾つかのデータを持ちますが、唯一必要なデータはセグメント内に書かれている「JFIF
」という文字列です。
この文字列を見て、JPEG画像である事を判断する事になります。
マーカは十六進数で[FF][E0]となります。
具体的な内容は、以下の通りです。
具体的にはアスキィ文字列「JFIF」と十六進数で[00]のバイナリです。
デコーダはこの文字列を見出して、JPEG画像ファイルと判断します。
一応、以下のように定義されております(いずれも十六進数でのバイナリとします)。
実際にはウェブ向けの画像では1インチ当たり72ピクセルズとされているので、[01]となります。
左上から右下へ、横方向にスキャンしたサムネイルのデータを各ピクセルごとに赤成分・緑成分・青成分の3オクテットで表します。
量子化テーブルを収めております。
量子化テーブルは、輝度データと色相データで異なったテーブルを採用する事が多いです。
具体的な形式は以下の通りです。
この後、以下の量子化テーブルデータが並びます。
上4ビットは8ビット型の量子化テーブルなら[0x]、16ビット型なら[1x]となりますが、ベースライン方式では8ビット型と限られており、依って常に[0x]となります。
下4ビットは量子化テーブル番号で、多くの場合[x0]から[x3]までとなります。
実際にはベースライン方式の場合、輝度成分に対しての量子化テーブルは[00]、色相成分に対してのものは[01]が当てられる事になっております。
この量子化データは、0番目が直流成分に対してのもの、1番目以降はジグザグスキャンによって並べ替えられた順序で並びます。
値は1だと量子化がなされず、最大値(8ビット型なら255)では完全に情報が破棄される事を意味します。
圧縮に用いたハフマン符号表を収めております。
ハフマン符号の体系は、多くの場合輝度の直流成分, 輝度の交流成分, 色相の直流成分及び色相の交流成分で異なっております。
具体的な形式は以下の通りです。
この後、以下のハフマン符号表データが並びます。
上4ビットは直流成分に対する符号表なら[0x]、交流成分に対するものなら[1x]となります。
直流成分と交流成分で符号表を共有する事は不可能なので、必ず双方の符号表が存在する事となります。
すなわち、如何なる場合でも最低二つの符号表が定義される事となります。
一方下4ビットは符号表番号で、多くの場合[x0]から[x3]までとなります。
実際には、輝度成分に対しての符号表は[00]、色相成分に対してのものは[01]が当てられる事になっております。
先頭から順に、1ビット符号の個数, 2ビット符号の個数, … , 16ビット符号の個数となります。
直流成分の場合は、バイナリで符号に続くバイナリデータのビット数(0〜11)となります。
交流成分の場合は、以下のようなバイナリになります。
尚、下位4ビットが0となる符号として、以下の特別な符号があります。
十六個のゼロデータが連続した場合。
後続するバイナリデータは無く、代りに新たなハフマン符号が後続します。
データ処理が途中で打切りになる場合。
通常、63個の交流成分を全て圧縮する事になっておりますが、後ろの方が全部ゼロデータとなっている場合、一番最後のゼロで無い成分までを取扱ってそこで打切りにする事が出来ます。
その場合、このEOB符号を置いて打切りとします。
SOFセグメントにもいろいろな定義がありますが、圧縮方式や画像形式により使われるセグメントが決まっております。
最もよく使われるハフマン符号圧縮では、以下のSOFセグメントが使われます。
いずれもフォーマットは同じで、画像の大きさや、どの量子化テーブル及びハフマン符号表を利用するかが書かれております。
具体的な内容は、以下の通りです。
言うまでも無く、画像の大きさを表します。
単位はピクセルです。
コンポーネントとは、輝度・色相の各成分の事と思って頂ければ良いでしょう。
カラー画像の場合は、輝度・青色相・赤色相の三つのコンポーネントがあり、従ってこの値は[03]となります。
一方グレイスケールの場合は、輝度しかないため、この値は[01]になります。
この後に、各コンポーネントごとの以下のデータが並びます。
上位4ビットが横方向の、下位4ビットが縦方向のサンプリング比率となります。
カラー画像で通常良く用いられている4:1:1方式では、輝度成分は[22]、色相成分は[11]となります。
これは、1MCU(処理単位となる画像を分割して出来たブロックで、4:1:1方式では16ピクセルズ四方)から輝度成分は縦横とも2ブロック(すなわち4ブロック)を、色相成分は縦横とも1ブロック(つまり1ブロック)をサンプリングする事となります。
DQTセグメントで定義された量子化テーブル番号が当てられます。
圧縮された画像データの前に付けられるセグメントです。
ベースライン方式では単一の画像データのみとなりますが、プログレッシヴ方式では処理を分割した組数だけ画像データが続き、従ってSOSセグメントも組数だけあります。
具体的な内容は、以下の通りです。
この後に、各コンポーネントごとの以下のデータが並びます。
上位4ビットが直流成分の、下位4ビットが交流成分の符号表番号となります。
番号はDHTセグメントで定義された番号となります。
カラー画像では大抵輝度成分は[00]、色相成分は[11]となりますが、しらぎくさいと実験室で公開しているJPEGエンコーダではどちらも[00]となります。
最後に、以下のデータが続きます。
具体的には
を表します(詳細は割愛します)。
DRIセグメントは、リスタート処理の周期(インターヴァル)を設定し、RSTnセグメントはデコーダがリスタートを行う事を表します。
圧縮過程において、直流成分は前に処理した直流成分との差分を取っていったものをハフマン符号化します。
この差分は次回の処理に引継がれ、どんどん累積していく事になります。
ところで、直流成分値によっては累積差分がハフマン符号で表現出来る範囲をオーヴァフロウする事があり得ますが、その場合生成された画像は正常な表示が出来なくなります。
このため、一定数のMCUを処理する毎に直流成分の累積差分をクリアする必要があり、この初期化動作をリスタートと言います。
具体的には、DRIセグメントセグメントで指定された数のMCU分の圧縮データごとにRSTnというセグメントを挿入します。
デコーダはRSTnセグメントを見出す度にリスタートを実行します。
DRIセグメントの内容は以下の通りです。
尚、RSTnセグメントはマーカのみで内容はありません。
マーカは十六進数で[FF][Dn]となります。
nは0から7までの順で使われ、7の次はまた0に戻ります。つまり、「0, 1, 2, …, 6, 7, 0, 1, 2, …」の順に番号が付けられる訳です。
nが正しくない場合の措置は未定義です。
DRIセグメントが設置されていない場合は、画像データ中にRSTnセグメントが現れる事はありません。
このセグメントは画像データ終了を意味しており、JPEGファイルの末尾に必須となっております。
このセグメントは内容を持たないセグメントであり、従ってマーカのみの2オクテットとなります。
マーカは十六進数で[FF][D9]となります。
ベースライン方式の場合、以下の順序で配置される事となります。
プログレッシヴ方式の場合、以下のようになります。
以下のブロックが分割された組数だけ繰り返されます。