突然ですが、JPEG の構造はだいたい下記のような感じになってます。
今回は libjpeg 等のライブラリを使わずに、JPEGファイルから画像の width と height を取り出したいと思います。この width と height の情報は、上図におけるフレームヘッダセグメントに入っています。
基礎知識
フレームヘッダセグメント
フレームヘッダセグメントの中身は、以下のとおりです。
データ | サイズ(bit) |
---|---|
フレーム開始マーカー | 16 |
フレームヘッダのサイズ(byte) | 16 |
サンプル精度 | 8 |
height | 16 |
width | 16 |
省略 |
あとは、サンプリングファクタとかが延々と続くんですが、今回は不要なので省略しています。
ヘッダセグメント
フレームヘッダ以外のヘッダセグメントには、EXIFデータやサムネイルなど、画像のデコードに直接関係ないメタデータが入っています。
JPEGでは、上図のとおり、複数個のヘッダセグメントが並んだ後に、フレームヘッダセグメントが登場します。(フレームヘッダセグメントが他のヘッダセグメントより先に来ることは無い)
つまり、ヘッダセグメントを読み飛ばしてから、フレームヘッダを読む、という戦略をとる必要があります。
各ヘッダセグメントの大きさは可変長で、それぞれセグメントのサイズが最初に書かれています。単位はバイトで、マーカーはサイズに含みません。
マーカー
上図の「◯◯マーカー」とは、16ビットの定数です。
今回登場する主なマーカーには以下のようなものがあります。
マーカー名 | 値 | 意味 |
---|---|---|
SOI | 0xffd8 | JPEGファイルの最初 |
EOI | 0xffd9 | JEPGファイルの最後 |
SOF0 | 0xffc0 | 基本DCT方式のフレーム開始 |
フレーム開始マーカーには、SOF0〜SOF15 までありますが、その中でも基本DCT方式がもっとも一般的に使われているようです。
実装
バイナリ操作なので C がよさそうですが、プロトタイピングなので、なんとなく Ruby で書きました。
コード全体は以下の gist にあります。
https://gist.github.com/3735311
以下は、 SOI マーカーから始まり、ヘッダセグメントを読み飛ばし( parse_extra )、フレームヘッダから width, height を抽出( parse_frame_header )している部分のコードです。
# 以下、jpeg_parser.rb の一部 def parse read 2 assert SOI parse_frame @data end def parse_frame read 2 parse_extra parse_frame_header end def parse_extra while marker?(current) && !sof?(current) context "#{inspect_marker(current)}" do length = read_int(2) - 2 log "length: #{length}" skip length end read 2 end end def parse_frame_header assert true, sof?(current) context "frame_header" do length = read_int(2) - 2 log "length: #{length}" skip 1 @data[:height] = read_int 2 @data[:width] = read_int 2 skip length - 5 end end
実行結果
jpeg_parser.rb の引数に jpg ファイルを与えて実行すると、以下のように、 width と height が出力されていることが分かります。
ちなみに、 ImageMagick を使ってコマンドラインで width height を出力するには、以下のようにします。
$ identify -format %w,%h input.jpg 1086,1453
同じ jpg ファイルを identify コマンドにかけると、同じ width height が得られていることが分かります。
参考文献
以下の本を参考にして実装しました。

- 作者: 橋本晋之介,アズウィ
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/12
- メディア: 単行本
- クリック: 35回
- この商品を含むブログ (17件) を見る
また、twitter 上で @yoya さんにいろいろアドバイスをいただきました。ありがとうございます。
@mirakui 案1) "\x00\x10".unpack('n');
— \助けよや/さん (@yoya) 9月 16, 2012
@mirakui そんな感じの図を昔作った事がありますslideshare.net/yoyayoya1/php-…(JPEG編:p23ー25、GIF,PNG編:p26)
— \助けよや/さん (@yoya) 9月 16, 2012