21
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【画像処理】ヘッダ情報の確認とバイナリエディタからの画像編集(JPEG編)

Last updated at Posted at 2021-04-15

ヘッダ情報の確認とバイナリエディタからの画像編集(JPEG編)
前回の記事→「画像ビューア(ペイント・GIMP)でみる画像ファイル形式の違い

前回までの講座でファイル形式の違いについてある程度理解できたと思います。そこで今回は、画像ビューアがファイルそのものからどのような情報を読み込んでいるのかを確認するために、jpg構造の理解とバイナリエディタを使ってjpgファイル形式の画像内部をみていきます。

ファイル形式ごとのフォーマット

そもそも画像ファイルには、画像ビューアで画面に表示されているデータ以外にも、画像の幅・高さ、画像そのもの以外のデータサイズ、圧縮方式など様々な情報が含まれています。それらのフォーマットはファイル形式によって異なり、jpgフォーマットの内容を解説した後にバイナリエディタを使って具体的に中身を見ていきます。

jpgへの変換手順

jpgファイルの構造を理解するためには、まずjpgへの変換手順を理解する必要があります。手順は大きく分けて以下の4つです。

  1. RGB-YCbCr変換
  2. DCT変換(離散コサイン変換)
  3. 量子化
  4. エントロピー符号化(ランレングス・ハフマン圧縮)

jpg圧縮の基本的な概念は「人間の眼にわからないように情報量を減らす」ことです。そこで人間の眼の性質に着目し、2種類のデータ削減方法を組み合わせて行っています。
輝度の変化には敏感で、色の変化には鈍感という性質を利用した、明るさを変えずに色の変化情報のみを減らす方法と図柄の緩やかな変化には敏感で、図柄の細かく急激な変化には鈍感という性質を利用した、低周波成分を変えずに高周波成分のみを減らす方法の2つです。

上記4つの手順では、これらの性質を利用した圧縮が行われています。
それでは、具体的に見ていきます。

1. RGB-YCbCr変換

ここでは「明るさを変えずに色の変化情報のみを減らす方法」が実装されています。
画像は基本的にRGB(Red, Green, Blue)の色空間で表されますが、そこから輝度と色の変化情報を取り出すために、以下の式を使ってYCbCr値に変換します。

色空間
Y (0.2990 * R) + (0.5870 * G) + (0.1140 * B)
Cb -(0.1687 * R) - (0.3313 * G) + (0.5000 * B) + 128
Cr (0.5000 * R) - (0.4187 * G) - (0.0813 * B) + 128

Yは輝度、Cbは輝度と青レベルの差、Crは輝度と赤レベルの差です。

次に、Y(輝度)はそのままで、Cb(輝度と青レベルの差)とCr(輝度と赤レベルの差)だけを間引く、クロマサブサンプリングとよばれる方法で、色の変化情報のみを間引いていきます。この間引き方にも様々な種類がありますが、ここでは代表的なものを紹介します。各サンプリング比は、Y成分を基準として、CbCr成分の比率を表しています。ちなみに、Y成分のことをルミナンスといい、CbCr成分のことをクロマといいます。色成分のみを間引きして、サンプリングするので、クロマサブサンプリングと呼ばれています。
例えば、「1.1 入力画像(16×32)」の場合、YCbCr変換後に各サンプリング比でサンプリングすると、以下のようになります。

1.1 入力画像(16×32)
サンプリング比 Y(輝度) サンプリング後CbCr(色成分) サンプリング後YCbCr
4:4:4
4:2:2
4:2:0
4:1:1

上記図のように、サンプリング比によって色の変化が間引かれているのがわかります。このようにして明るさを変えずに色の変化情報のみを減らしています。
また、この時点で今後のデータ処理に使われるMCU(Minimum Coded Unit)という単位に分割されます。MCUは基本的に8*8ピクセル単位の画像データをYCbCr成分に分けて保存したもので、この時に上記の色成分に対するサンプリングが行われることでMCUに保存する画像データを減らすことができています。
例えば、上記の表のサンプリング比4:4:4の場合、8×8ピクセルの画像データはY(輝度)が8色、サンプリング後CbCr(色成分)が8色あります。よって、Y(8) + Cb(8) + Cr(8) = 24ブロックできることになります。

サンプリング比 Y(輝度)色数 Cb(青レベル)色数 Cr(赤レベル)色数 合計ブロック数
4:4:4 8 8 8 24
4:2:0 8 2 2 12

しかし、サンプリング比4:2:0の場合、8×8ピクセルの画像データはY(輝度)が8色、サンプリング後CbCr(色成分)が2色あります。つまり、Y(8) + Cb(2) + Cr(2) = 12ブロックできることになり、結果として12ブロック分のデータを減らすことができるのです。

2. DCT変換(離散コサイン変換)

続いて、MCU内の8×8ピクセルの画像データ(ブロック)にフォーカスしていきます。
ここでは、各ブロックに対してDCT変換1を行い、DCT変換後画像例のように64段階の周波数成分に分解して左上が低周波成分、右下が高周波成分になるように並べていきます。

DCT変換後画像例

低周波成分は画像内変化の小さいところが集まり、高周波成分は画像内変化の大きいところが集まります。
この時に左上端の部分はコサイン関数が絡まないので、とくに「直流(DC)成分」と呼び、その他の部分を「交流(AC)成分」と呼びます。
そうして並べ替えられたデータは、次の工程である量子化、そして符号化によって圧縮されていきます。
つまり、DCT変換時点ではデータの圧縮は行われずに単に変換のみが行われているため、ここでのデータ量に変化はないものの、続く量子化・符号化工程において効率的な圧縮を実現するための重要な工程となります。

3. 量子化

ここでは、DCT変換された各ブロックの高周波成分を間引きすることで、次の工程の符号化で効率的な圧縮ができるようにすることが目的です。端的に言えば、DCT変換されたブロックを同じ大きさの量子化テーブルで割るだけです。

量子化テーブル(Y成分用) 量子化テーブル(CbCr成分用)

「DCT変換後画像例」のように、DCT変換されたブロックは左上端に低周波成分、右下端に高周波成分が集まっています。そのため量子化の工程では、高周波成分の間引きを考え、左上端に小さい値、右下端に大きい値が来るように量子化テーブルが構成されています。
この量子化テーブルを利用してDCT変換されたブロックを割ることでブロック全体の値を小さくします。この時に出た余りは切り捨てられるため、とくに高周波成分の値に多く0が出ます。
次の工程ではデータ値の登場頻度をもとに符号を決めるため、ここで0の部分が多く出るほどそれだけ情報を間引くことができます。また、ここで切り捨てた余りが大きいほどデコード時の画像劣化が大きくなるために、jpgの品質調整が量子化テーブルの値調整によって行われていることがわかります。
また、上記2つの量子化テーブルのようにYCbCrのY成分には小さい値を、CbCr成分には大きい値を割り当てて輝度値の劣化を抑えるなど、各成分ごとにも品質調整がなされています。

4. エントロピー符号化(ランレングス・ハフマン圧縮)

最後の工程がこの符号化になります。
ここでは、ハフマン符号化とランレングス符号化を組み合わせて、圧縮を行っています。また直流成分と交流成分で記録する順番が異なりますが、基本的な処理方法は同じです。

まず、直流成分は隣接するブロック間(MCUの前後ブロック間)の平均輝度値は似たような値をとるので、1つ前のブロックの直流成分との差分を抽出してその値に対してハフマン符号化を行っていきます。
この時、DCT変換後に量子化されたブロックには幅広い範囲の値が記録されているため、このままハフマン符号化を適用するのは効率的ではありません。そのため、値の取りうる範囲を16区間に分割し、その区間番号とその区間の何番目の値に対応するのかを記録することで効率的なハフマン符号化を行います。

次に、交流成分はここまでの工程で左上端に低周波成分、右下端に高周波成分が集まっています。さらに、高周波成分には0が多く並んでいる状態です。これを活用して最初にランレングス符号化を行います。ランレングス符号化は、同じ数字が連続しているほど小さく圧縮できるため、データを見ていく順番も一工夫されていて、そのスキャン方法をジグザグスキャンといい、左上端から右下端に向かってジグザグに進んでいき、できる限り0を並べてスキャンできるようにしています。

ジグザグスキャン

その後、直流成分と同じように各値に区間番号とその区間の何番目の値に対応するのかを記録してハフマン符号化を行います。
また、この時にどのデータがどのハフマン符号と対応しているかを記載したハフマンテーブルというものが作られ、デコード時にこのテーブルをもとに復号化されます。

以上、4つの手順によってjpg画像が作られています。
ここまででjpgの変換手順について説明しました。つづいて、そのファイル構造について説明していきます。

jpgのファイル構造

jpgの全体的なファイル構造は次のようになっています。

jpgの構造

先頭にSOIマーカ(FFD8)、最後にEOIマーカ(FFD9)というファイルの始まりと終わりを示すものがあり、その間に画像そのもののデータとそれ以外のデータが挟まれています。2

ここにある複数のセグメントは、画像そのもの以外のデータを格納している部分で、その構造は次のようになっています。

セグメント構造

セグメントの先頭にデータ(パラメータ)の種類を表すマーカと呼ばれるものが必ずあり、その後ろにセグメントの長さ、パラメータが格納されています。

必須セグメント

jpgには様々なセグメントが格納されていますが、その中でも画像の表示に必ず必要となるセグメントが4つあります。
DQTセグメント、DHTセグメント、SOFセグメント、SOSセグメントです。
これらはどのjpgファイルにも必ずあるセグメントで、それぞれ重要な役割を持っているので、1つずつ見ていきます。

DQT(Define Quantization Table) -- 量子化テーブル定義

DQTはjpgファイルの画質に関するデータが記録されているセグメントで、ひとつのセグメントで複数の量子化テーブルを定義することが出来ます。具体的にはjpg変換手順の量子化で用いた量子化係数を保存したテーブルが定義してあり、そのマーカはFFDBです。また、輝度成分ブロックと色成分ブロックで別々の量子化テーブルを使うことが多いので、ここでは複数のテーブルとなることが多いです。
テーブルは固定長でテーブル番号が4種類(0~3)あります。また、DQTは後述するSOFセグメントから参照されます。

DQTの構造
名称 サイズ 内容
Lq 2バイト セグメント長
Pq 4ビット(上位) 量子化テーブル精度 (8bit or 16bit)
Tq 4ビット(下位) 量子化テーブル番号 (0~3)
Q-0~63 Pq=8bit 64バイト Pq=16bit 128バイト 64個の量子化係数

DHT(Define Huffman Table) -- ハフマンテーブル定義

DHTはjpgファイルのハフマン圧縮、復元時に使用するデータが記録されているセグメントで、ひとつのセグメントで複数のハフマンテーブルを定義出来ます。具体的にはjpg変換手順のハフマン符号化で用いたハフマン符号との対応を保存したテーブルが定義してあり、そのマーカはFFC4です。また、輝度成分ブロックと色成分ブロックごとに直流成分・交流成分があり、別々のハフマンテーブルを使うことが多いので、ここではDQTと同じように複数のテーブルとなることが多いです。
テーブルは可変長でテーブル番号が4種類(0~3)、テーブルクラス(DCとAC)が2種類、計8種類あります。また、DHTは後述するSOSセグメントから参照されます。

DHTの構造
名称 サイズ 内容
Lh 2バイト セグメント長
Tcn 4ビット(上位) ハフマンテーブルクラス (DC成分 or AC成分)
Thn 4ビット(下位) ハフマンテーブル番号 (0~3)
L1~L16 16バイト ハフマンテーブルビット配分数
V1~V16 L1~L16の合計x1バイト ハフマンテーブル定義データ
DC成分:データビット数
AC成分:上位4ビット ランレングス数
     下位4ビット データビット数

SOF(Start Of Frame) -- フレームヘッダ

SOFはjpgファイルの種類や画像サイズなど重要なパラメータが記録されているセグメントで、ひとつのjpgファイルやjpg構造に必ず一つ記録されます。
SOFは圧縮形式や画像形式によってマーカが異なり、全部で13種類のマーカが有ります。jpgは様々な圧縮方式があり、その中でもベースライン方式とプログレッシブ方式が一般的に使われています。ベースライン方式ならFFC0、プログレッシブ方式ならFFC2がマーカに利用されます。各色成分ごとに使用している量子化テーブルの対応付けをここでしているため、ここのセグメントからDQTセグメントを参照しています。

SOFの構造
名称 サイズ 内容
Lf 2バイト セグメント長
P 1バイト 成分数
Y 2バイト 画像縦サイズ
X 2バイト 画像横サイズ
Nf 1バイト 成分数
Cn 1バイト 成分ID
Hn 4ビット(上位) 水平サンプリング値
Vn 4ビット(下位) 垂直サンプリング値
Tqn 1バイト 対応量子化テーブル番号

SOS(Start Of Scan) -- スキャンヘッダ

SOSはイメージデータの先頭に記録されるセグメントで、後に続くイメージデータの成分などの内容が記録されています。マーカはFFDAです。
各色成分ごとに使用しているハフマンテーブルの対応付けをここでしているため、ここのセグメントからDHTセグメントを参照しています。

SOSの構造
名称 サイズ 内容
Ls 2バイト セグメント長
Ns 1バイト 成分数
Csn 1バイト 成分ID
Tdn 4ビット(上位) DC成分ハフマンテーブル番号
Tan 4ビット(下位) AC成分ハフマンテーブル番号
Ss 1バイト 量子化係数開始番号
Se 1バイト 量子化係数終了番号
Ah 4ビット(上位) 前回のスキャンの係数値分割シフト量(最初のスキャンは0)
Ai 4ビット(下位) 係数値分割シフト量(最後のスキャンは0)

その他セグメント

jpgは上記で説明した4つのセグメント以外にも、通常格納されているセグメントがあります。カメラで撮影した時の状況・情報を記録したExif情報やデータの欠落時にその影響を最小限にするためのセグメントなどがあります。

COM(comment) -- コメントセグメント

画像ファイルに埋め込むことができるテキストデータを定義するセグメントで、SOIマーカ・EOIマーカ間であれば自由にテキストデータを埋め込むことができます。マーカはFFFEです。
基本的には加工を行ったソフトの名前・バージョンやコピーライトが書き込まれています。

COMの構造
名称 サイズ 内容
Lc 2バイト セグメント長
Cmn 1バイト×文字数 コメント(ASCIIテキスト)

APP0~15(Application) -- アプリケーションセグメント

アプリケーション側で自由に規定できるセグメントで、ここには各OSごとの差異を吸収するためのJFIF情報や撮影時の情報が記録されたExif情報などがありますが、ここではAPP0(JFIFフォーマット)とAPP1(Exifフォーマット)のみを取り上げます。

APP0(JFIFフォーマット)セグメント

APP0は各プラットフォーム(OS等)との互換を取るための情報が記録されているので、大半のjpgファイルに含まれています。ここで重要なのがASCII文字で書かれている"JFIF"という文字です。ビューアは、この文字を見てjpg画像であるかを判断しています。マーカはFFE0です。

APP0の構造
名称 サイズ 内容
La 2バイト セグメント長
Id 5バイト ASCII文字で"JFIF"+NULL
Ver 2バイト JFIFのバージョン
U 1バイト 解像度単位(0:単位なし 1:dpi 2:dpcm)
Xd 2バイト 横解像度
Yd 2バイト 縦解像度
Xt 1バイト サムネイル横サイズ
Yt 1バイト サムネイル縦サイズ
RGB 3バイト 1ピクセルのRGBデータが全ピクセル数分

APP1(Exifフォーマット)セグメント

APP1はデジタルカメラの撮影情報が記録されているセグメントで、大半のカメラの出力ファイルに含まれています。ここには撮影条件や撮影場所などの情報が記録されていますが、カメラの機種やメーカーによって異なることがあります。
マーカはFFE1です。

APP1の構造
名称 サイズ 内容
La 2バイト セグメント長
Id 6バイト ASCII文字で"Exif"+NULL
Th 2バイト Tiffヘッダ
Tid 2バイト Tiff識別コード
Io 4バイト 0th IFDへのポインタ

APP2(カラープロファイル)セグメント

PhotoshopやGIMPなどのソフトでカラープロファイルを記録する項目を選んだ場合、このセグメントが記録され、撮影や編集時の色を忠実に再現させるためのデータが入っています。このセグメントのデータを利用するには、それに対応したソフトや機器が必要ですが、プロ向けの規格なので一般的には使うことはないと思われます。マーカはFFE2です。

APP2の構造

構造はその規格ごとに異なり、そのアーキテクチャを知らなければ理解が難しいです。ISO 15076-1で標準規格化されているものを紹介します。
以下のサイトで必要な参照色空間とデータ構造(タグ)の説明がなされています。気になる方はどうぞ。

国際標準化団体(International Color Consortium)
ISO 15076-1

DRI(Define Restart Interval) -- リスタートインタバル定義

画像データの部分的な破損が全体に波及するのを防ぐために、jpgファイル作成時に一定の間隔でRST(restart)マーカが埋め込まれる場合があります。RSTマーカはMCUの間に一定間隔で置かれていて、その区間でデータの破損があった場合は次のRSTマーカまで読み飛ばします。
DRIはそのRSTマーカがどのくらいの間隔で置かれているかを定義しているセグメントで、マーカはFFDDです。

DRIの構造
名称 サイズ 内容
Lr 2バイト セグメント長
Ri 2バイト リスタートインタバル間隔

ここまででひと通りjpgの構造を説明しました。次はそれらの情報を活用して本当に説明通りの構造になっているかを確認します。

バイナリエディタからの画像編集

ここではバイナリエディタを使って、実際に画像の中身を確認していきます。
今回は、GIMPを使用して16×16の黒で塗りつぶした画像を使用します。

1.1 サンプル画像

上記の画像をバイナリエディタを使って見てみると、以下のようになります。

1.1サンプル画像
  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 	
00000000: FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 01 2C    .X.`..JFIF.....,
00000010: 01 2C 00 00 FF FE 00 15 49 20 6D 61 64 65 20 74    .,...~..I.made.t
00000020: 68 69 73 20 62 79 20 47 49 4D 50 FF E2 02 B0 49    his.by.GIMP.b.0I
00000030: 43 43 5F 50 52 4F 46 49 4C 45 00 01 01 00 00 02    CC_PROFILE......
00000040: A0 6C 63 6D 73 04 30 00 00 6D 6E 74 72 52 47 42    .lcms.0..mntrRGB
00000050: 20 58 59 5A 20 07 E5 00 04 00 02 00 00 00 1E 00    .XYZ..e.........
00000060: 28 61 63 73 70 4D 53 46 54 00 00 00 00 00 00 00    (acspMSFT.......
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00000080: 00 00 00 F6 D6 00 01 00 00 00 00 D3 2D 6C 63 6D    ...vV......S-lcm
00000090: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    s...............
000000a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
000000b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
000000c0: 0D 64 65 73 63 00 00 01 20 00 00 00 40 63 70 72    .desc.......@cpr
000000d0: 74 00 00 01 60 00 00 00 36 77 74 70 74 00 00 01    t...`...6wtpt...
000000e0: 98 00 00 00 14 63 68 61 64 00 00 01 AC 00 00 00    .....chad...,...
000000f0: 2C 72 58 59 5A 00 00 01 D8 00 00 00 14 62 58 59    ,rXYZ...X....bXY
00000100: 5A 00 00 01 EC 00 00 00 14 67 58 59 5A 00 00 02    Z...l....gXYZ...
00000110: 00 00 00 00 14 72 54 52 43 00 00 02 14 00 00 00    .....rTRC.......
00000120: 20 67 54 52 43 00 00 02 14 00 00 00 20 62 54 52    .gTRC........bTR
00000130: 43 00 00 02 14 00 00 00 20 63 68 72 6D 00 00 02    C........chrm...
00000140: 34 00 00 00 24 64 6D 6E 64 00 00 02 58 00 00 00    4...$dmnd...X...
00000150: 24 64 6D 64 64 00 00 02 7C 00 00 00 24 6D 6C 75    $dmdd...|...$mlu
00000160: 63 00 00 00 00 00 00 00 01 00 00 00 0C 65 6E 55    c............enU
00000170: 53 00 00 00 24 00 00 00 1C 00 47 00 49 00 4D 00    S...$.....G.I.M.
00000180: 50 00 20 00 62 00 75 00 69 00 6C 00 74 00 2D 00    P...b.u.i.l.t.-.
00000190: 69 00 6E 00 20 00 73 00 52 00 47 00 42 6D 6C 75    i.n...s.R.G.Bmlu
000001a0: 63 00 00 00 00 00 00 00 01 00 00 00 0C 65 6E 55    c............enU
000001b0: 53 00 00 00 1A 00 00 00 1C 00 50 00 75 00 62 00    S.........P.u.b.
000001c0: 6C 00 69 00 63 00 20 00 44 00 6F 00 6D 00 61 00    l.i.c...D.o.m.a.
000001d0: 69 00 6E 00 00 58 59 5A 20 00 00 00 00 00 00 F6    i.n..XYZ.......v
000001e0: D6 00 01 00 00 00 00 D3 2D 73 66 33 32 00 00 00    V......S-sf32...
000001f0: 00 00 01 0C 42 00 00 05 DE FF FF F3 25 00 00 07    ....B...^..s%...
00000200: 93 00 00 FD 90 FF FF FB A1 FF FF FD A2 00 00 03    ...}...{!..}"...
00000210: DC 00 00 C0 6E 58 59 5A 20 00 00 00 00 00 00 6F    \..@nXYZ.......o
00000220: A0 00 00 38 F5 00 00 03 90 58 59 5A 20 00 00 00    ...8u....XYZ....
00000230: 00 00 00 24 9F 00 00 0F 84 00 00 B6 C4 58 59 5A    ...$.......6DXYZ
00000240: 20 00 00 00 00 00 00 62 97 00 00 B7 87 00 00 18    .......b...7....
00000250: D9 70 61 72 61 00 00 00 00 00 03 00 00 00 02 66    Ypara..........f
00000260: 66 00 00 F2 A7 00 00 0D 59 00 00 13 D0 00 00 0A    f..r'...Y...P...
00000270: 5B 63 68 72 6D 00 00 00 00 00 03 00 00 00 00 A3    [chrm..........#
00000280: D7 00 00 54 7C 00 00 4C CD 00 00 99 9A 00 00 26    W..T|..LM......&
00000290: 67 00 00 0F 5C 6D 6C 75 63 00 00 00 00 00 00 00    g...\mluc.......
000002a0: 01 00 00 00 0C 65 6E 55 53 00 00 00 08 00 00 00    .....enUS.......
000002b0: 1C 00 47 00 49 00 4D 00 50 6D 6C 75 63 00 00 00    ..G.I.M.Pmluc...
000002c0: 00 00 00 00 01 00 00 00 0C 65 6E 55 53 00 00 00    .........enUS...
000002d0: 08 00 00 00 1C 00 73 00 52 00 47 00 42 FF DB 00    ......s.R.G.B.[.
000002e0: 43 00 03 02 02 03 02 02 03 03 03 03 04 03 03 04    C...............
000002f0: 05 08 05 05 04 04 05 0A 07 07 06 08 0C 0A 0C 0C    ................
00000300: 0B 0A 0B 0B 0D 0E 12 10 0D 0E 11 0E 0B 0B 10 16    ................
00000310: 10 11 13 14 15 15 15 0C 0F 17 18 16 14 18 12 14    ................
00000320: 15 14 FF DB 00 43 01 03 04 04 05 04 05 09 05 05    ...[.C..........
00000330: 09 14 0D 0B 0D 14 14 14 14 14 14 14 14 14 14 14    ................
00000340: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14    ................
00000350: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14    ................
00000360: 14 14 14 14 14 14 14 FF C2 00 11 08 00 10 00 10    ........B.......
00000370: 03 01 11 00 02 11 01 03 11 01 FF C4 00 15 00 01    ...........D....
00000380: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00000390: 08 FF C4 00 14 01 01 00 00 00 00 00 00 00 00 00    ..D.............
000003a0: 00 00 00 00 00 00 00 FF DA 00 0C 03 01 00 02 10    ........Z.......
000003b0: 03 10 00 00 01 95 00 07 FF C4 00 14 10 01 00 00    .........D......
000003c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 20 FF DA    ...............Z
000003d0: 00 08 01 01 00 01 05 02 1F FF C4 00 14 11 01 00    ..........D.....
000003e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 FF    ................
000003f0: DA 00 08 01 03 01 01 3F 01 1F FF C4 00 14 11 01    Z......?...D....
00000400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20    ................
00000410: FF DA 00 08 01 02 01 01 3F 01 1F FF C4 00 14 10    .Z......?...D...
00000420: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00000430: 20 FF DA 00 08 01 01 00 06 3F 02 1F FF C4 00 14    ..Z......?...D..
00000440: 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00000450: 00 20 FF DA 00 08 01 01 00 01 3F 21 1F FF DA 00    ...Z......?!..Z.
00000460: 0C 03 01 00 02 00 03 00 00 00 10 92 4F FF C4 00    ............O.D.
00000470: 14 11 01 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00000480: 00 00 20 FF DA 00 08 01 03 01 01 3F 10 1F FF C4    ....Z......?...D
00000490: 00 14 11 01 00 00 00 00 00 00 00 00 00 00 00 00    ................
000004a0: 00 00 00 20 FF DA 00 08 01 02 01 01 3F 10 1F FF    .....Z......?...
000004b0: C4 00 14 10 01 00 00 00 00 00 00 00 00 00 00 00    D...............
000004c0: 00 00 00 00 20 FF DA 00 08 01 01 00 01 3F 10 1F    ......Z......?..
000004d0: FF D9                                              .Y


jpg構造の確認

SOIマーカ・EOIマーカ

行数 マーカ 内容
00000000 FFD8 SOIマーカ
000004d0 FFD9 EOIマーカ

先頭行を見てみると、「FFD8」が最初に来ています。さらに、最終行は「FFD9」で終わっています。つまり、このファイルの先頭にSOIマーカ、最後にEOIマーカがそれぞれあることがわかります。

APP0(JFIFフォーマット)

行数 マーカ 内容
00000000 FFE0 APP0(JFIFフォーマット)マーカ
000004d0 FFD9 EOIマーカ

SOIマーカの隣にAPP0(JFIFフォーマット)のマーカ「FFE0」があります。その直後2バイトに「0010」が来ていますので、このセグメント長は16バイトだとわかります。次にその後ろ5バイトに「4A 46 49 46 00」が来て、ASCII文字に直してみると「J F I F □」となり、上記で説明したAPP0のフォーマットに一致していることから、このファイルがjpgであることがわかります。3

COM(comment)

行数 マーカ 内容
00000010 FFFE COM(コメント)マーカ

2行目にはCOMマーカがあり、画像作成時に設定した「I made this by GIMP」というコメントが記録されています。

APP2

行数 マーカ 内容
00000020 FFE2 APP2(カラープロファイル)マーカ

3行目にAPP2(カラープロファイル)マーカの「FFE2」があり、そこから46行目の「000002d0」までが1つのセグメントになっています。
構造はわかりませんが、「ICC_PROFILE」というASCII文字で始まっていることからその直後にカラーフォーマットのプロファイル形式やデータ構造が定義されていると考えられます。

必須セグメントの確認

必須セグメントは、DQTセグメント、DHTセグメント、SOFセグメント、SOSセグメントの4つがありましたが、これらが実際にあるか確認してみます。

DQTセグメント

行数 マーカ 内容
000002d0 FFDB DQTマーカ
00000320 FFDB DQTマーカ

DQTのマーカは「FFDB」です。「000002d0:」の行、「00000320:」行を見てみると「FFDB」マーカがあり、その後ろの2バイトはどちらとも「0043」でセグメント長が67バイトであることがわかります。続いて、その後ろには量子化テーブル精度と番号が4ビットで記載されていて、どちらも8bit精度で前者のテーブル番号が0、後者のテーブル番号が1であることがわかります。さらに、その後ろに64個の量子化係数が並んでいます。セグメント長が67バイト、セグメント長表現に2バイト、量子化テーブルで1バイト使っていることからもセグメント最後方の64ビットが量子化係数を表しているとわかります。(67バイト - (2バイト + 1バイト))

DHTセグメント

行数 マーカ 内容
00000370 FFC4 DHTマーカ
00000390 FFC4 DHTマーカ
000003b0 FFC4 DHTマーカ
000003d0 FFC4 DHTマーカ
000003f0 FFC4 DHTマーカ
00000410 FFC4 DHTマーカ
00000430 FFC4 DHTマーカ
00000460 FFC4 DHTマーカ
00000480 FFC4 DHTマーカ

DHTのマーカは「FFC4」です。「FFC4」マーカの後ろの2バイトはセグメント長を表しています。その後ろにはDC成分またはAC成分を表す4ビットとハフマンテーブル番号を表す4ビットがあり、続いて量子化されたデータに配分するビット数定義があります。

SOFセグメント

行数 マーカ 内容
00000360 FFC2 SOFマーカ(ハフマン符号化プログレッシブ方式)

SOFのマーカはDCT変換の方式によって異なりますが、今回はハフマン符号化プログレッシブ方式の「FFC2」が、「00000360」の行にあります。セグメント長が「0011」で17バイトです。YCbCr成分数が8で、画像縦、横ともに「0010」で16×16です。
ここで特に注目したいところは、最後の10バイト「03 01 11 00 02 11 01 03 11 01」です。成分数(YCbCr=3)、成分ID、水平サンプリング値・垂直サンプリング値、対応量子化テーブル番号を表しています。例えば、「01 11 00」は成分ID=1, 1:1サンプリング、量子化テーブル番号=00となります。
そこで、参照している量子化テーブル番号を見に行くと、テーブル番号00は小さい値(上位4ビットが0)が多く続いていますが、テーブル番号1は多くの部分を14が占めています。
このことから、Y成分には緩やかな量子化、CbCr成分にはある程度粗い量子化が行われていることがわかると思います。

SOSセグメント

行数 マーカ 内容
000003a0 FFDA SOSマーカ
000003c0 FFDA SOSマーカ
00000410 FFDA SOSマーカ
00000430 FFDA SOSマーカ
00000450 FFDA SOSマーカ
00000450 FFDA SOSマーカ
00000480 FFDA SOSマーカ
000004a0 FFDA SOSマーカ
000004c0 FFDA SOSマーカ

SOSのマーカは「FFDA」です。「FFDA」マーカの後ろの2バイトはセグメント長を表しています。
その後ろには成分数やDC・AC各成分に対応するハフマンテーブル番号が続いています。

これですべての必須セグメントが記録されていることがわかりました。続いて、バイナリエディタを使って画像を編集してみたいと思います。

jpg構造の編集

jpgではイメージデータが圧縮されているため、今回は表示に関係ないセグメントを消して、表示が変わらないように画像データサイズを小さくしてみようと思います。
具体的にはCOMセグメントとAPP2セグメントを削除して、ファイルの容量を減らしつつ、画像はそのまま維持できるようにしてみます。

1.2サンプル画像

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 	
00000000: FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 01 2C    .X.`..JFIF.....,
00000010: 01 2C 00 00 FF DB 00 43 00 03 02 02 03 02 02 03    .,...[.C........
00000020: 03 03 03 04 03 03 04 05 08 05 05 04 04 05 0A 07    ................
00000030: 07 06 08 0C 0A 0C 0C 0B 0A 0B 0B 0D 0E 12 10 0D    ................
00000040: 0E 11 0E 0B 0B 10 16 10 11 13 14 15 15 15 0C 0F    ................
00000050: 17 18 16 14 18 12 14 15 14 FF DB 00 43 01 03 04    ..........[.C...
00000060: 04 05 04 05 09 05 05 09 14 0D 0B 0D 14 14 14 14    ................
00000070: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14    ................
00000080: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14    ................
00000090: 14 14 14 14 14 14 14 14 14 14 14 14 14 14 FF C2    ...............B
000000a0: 00 11 08 00 10 00 10 03 01 11 00 02 11 01 03 11    ................
000000b0: 01 FF C4 00 15 00 01 01 00 00 00 00 00 00 00 00    ..D.............
000000c0: 00 00 00 00 00 00 00 08 FF C4 00 14 01 01 00 00    .........D......
000000d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF DA    ...............Z
000000e0: 00 0C 03 01 00 02 10 03 10 00 00 01 95 00 07 FF    ................
000000f0: C4 00 14 10 01 00 00 00 00 00 00 00 00 00 00 00    D...............
00000100: 00 00 00 00 20 FF DA 00 08 01 01 00 01 05 02 1F    ......Z.........
00000110: FF C4 00 14 11 01 00 00 00 00 00 00 00 00 00 00    .D..............
00000120: 00 00 00 00 00 20 FF DA 00 08 01 03 01 01 3F 01    .......Z......?.
00000130: 1F FF C4 00 14 11 01 00 00 00 00 00 00 00 00 00    ..D.............
00000140: 00 00 00 00 00 00 20 FF DA 00 08 01 02 01 01 3F    ........Z......?
00000150: 01 1F FF C4 00 14 10 01 00 00 00 00 00 00 00 00    ...D............
00000160: 00 00 00 00 00 00 00 20 FF DA 00 08 01 01 00 06    .........Z......
00000170: 3F 02 1F FF C4 00 14 10 01 00 00 00 00 00 00 00    ?...D...........
00000180: 00 00 00 00 00 00 00 00 20 FF DA 00 08 01 01 00    ..........Z.....
00000190: 01 3F 21 1F FF DA 00 0C 03 01 00 02 00 03 00 00    .?!..Z..........
000001a0: 00 10 92 4F FF C4 00 14 11 01 00 00 00 00 00 00    ...O.D..........
000001b0: 00 00 00 00 00 00 00 00 00 20 FF DA 00 08 01 03    ...........Z....
000001c0: 01 01 3F 10 1F FF C4 00 14 11 01 00 00 00 00 00    ..?...D.........
000001d0: 00 00 00 00 00 00 00 00 00 00 20 FF DA 00 08 01    ............Z...
000001e0: 02 01 01 3F 10 1F FF C4 00 14 10 01 00 00 00 00    ...?...D........
000001f0: 00 00 00 00 00 00 00 00 00 00 00 20 FF DA 00 08    .............Z..
00000200: 01 01 00 01 3F 10 1F FF D9                         ....?...Y

1.2 サンプル画像

COMセグメント(FFFE)とAPP2セグメント(FFE2)のセグメントを削除した結果が上の状態です。APP2セグメントで使用していた部分がイメージデータに比較して大きかったためファイルサイズを大幅に削減することができました。また、GIMPやその他の画像ビューアで正常に画像が表示できることを確認しました。

画像名 ファイルサイズ
1.1 サンプル画像 1,234バイト
1.2 サンプル画像 521バイト

さいごに

今回は、「ヘッダ情報の確認とバイナリエディタからの画像編集(JPEG編)」について解説しました。jpg画像の構造を知ることができたと思います。

それでは引き続きよろしくお願いいたします。

目次は以下の記事からご覧になれます。

  1. DCTはデータ列をcos関数の和で表現する形式に変換する処理です。フーリエ変換の仲間で、周期関数に三角関数を用いるところはフーリエ変換と同じですが、cos関数のみを用いるところが異なります。

  2. 厳密には、EOIマーカ以降は何が記録してあっても無視するので、必ずしもEOIマーカがファイルの最終位置にあるとは限りません。また、多くの画像ビューアでは画像データが破損している場合に備え、EOIマーカが無くても画像を表示出来るようになっています。ただし、SOIマーカは画像を表示するために必ず必要となります。

  3. 厳密にはJFIFフォーマット=jpgではありませんが、現在は大半のjpgがJFIFフォーマットを採用しているため、記事内ではJFIFフォーマット=jpgとします。

21
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?