複天一流:どんな手を使ってでも問題解決を図るブログ

宮本武蔵の五輪書の教えに従い、どんな手を使ってでも問題解決を図るブログです(特に、科学、数学、工学の問題についてですが)

画像処理にチャレンジ(part 2): jpegのヘッダー情報解読

前回のあらすじ

前回はBMP画像ファイルの読み込みに挑戦し、まずはヘッダーファイルという画像情報の記録に挑戦し、少しだけその「正体」を理解することができた。バイナリファイルの読み書きについての練習もできたし、実りの多い試みであった。

引き続いてBMPファイルの画像データそのものにアクセスし、加工してみようかと思ったのだが、ひとつアイデアが出てきてしまったので、最初にそちらを片付けてから本筋に戻ることにした。そのアイデアとはJPEGのヘッダー情報の読み取りの挑戦である!

もともと、デジカメのデータはjpegで記録することが多いので、画像処理したい対象はjpegpngであった。しかし「圧縮」という複雑な処理が施されているので、画像処理の素人には敷居の高い代物に見えた。しかし、よくよく考えるとヘッダー情報なら「圧縮」されているわけはないので、単なるバイナリファイルの読み取りで事済むはずじゃないだろうか?と思いたったというわけである。また、これまでに書き下したBMPヘッダーファイル読み取りプログラムは、少し書き換えればバイナリファイル一般に応用可能である。つまり、jpegに踏み込める上に、コードは再利用できるということで「一石二鳥」というわけである。

最初のマジックナンバー

jpegファイルはバイナリファイルなので、ファイルサイズ以内の読み取りサイズ(私のコード中でbyteSizeと定義されているもの)を指定してファイルをひらけば、なんらかのデータは読み取れるはずである。BMPの場合は最初14バイトがBITMAPFILEHEADERという「構造体」になっていたが、JPEGはどうなんだろうか?14バイト以上の画像データをどこかで拾ってきて、最初の14バイトを兎にも角にも読み取ってみよう。

拾ってきたのは、LA Dodgersの大谷選手の紹介HPに利用されていた画像である。今回のサンプルプログラムで読み取りたいのは、この画像のデータである。

MLBのLAドジャースのHPより拝借...
まずは何も考えずに最初の14バイトを(先日bmp用に書いたコードを使って)読み取ってみた。結果は次の通りである。

File found.
255 ff 
216 d8 
255 ff 
224 e0 
0 0 
16 10 
74 4a 
70 46 
73 49 
70 46 
0 0 
1 1 
1 1 
0 0 

読めた!読めたぞ!

最初の2バイトに"ff d8"というのがあって、これがBMPファイルのマジックナンバー"B M (0x42 0x4d)"に似た感じがある。続く"ff e0"もなんらかの記号のような印象を受ける。この数字を使って検索をかけてみると案の定であった。ただし、JPEGの場合には「魔法数」とは呼ばず、SOI(Start of Image)と呼ぶそうである。

JPEGのファイルであることを示す最初のマジックナンバー(魔法数)は、先頭2バイトの"ff d8"であった。つまり、最初の2バイトを読み取ると、BMPか、JPEGか識別できるというわけだ。これは後で画像処理アプリを開発するときに利用できそうな情報である。

JPEGには終わりのマークもある(EOI)

検索結果を読み進めると、BMPと違って、JPEGにはファイルの終わりを示すマークもあることがわかった。それはEOI(End of Image)と呼ばれる"0xff 0xd9"であるようだ。1バイトずつ読み取っていけば、最後にEOIが出てきて、そこでJPEGファイルが終わるということなんだろうか?この実験は後でやってみることにしよう。

ff e0の意味

JPEGの「魔法数」に相当するSOI、つまり0xff 0xd8の数字の次に記録されていたのが、0xff 0xe0であった。検索結果のなかにこの意味の解説も書かれていた。

これはAPPnと呼ばれるデータセグメントの先頭で、JPEGを作成したアプリケーションが付与したヘッダー情報になっているそうである。nの値は0から15まで(つまり16進数)の範囲をもっている。今回の大谷の画像で見つかったのはe0だからAPPnの最初のカテゴリーであるという意味であろう。

ff e0に続く次の2バイトはApp0セグメントの長さを(バイト単位で)示す情報で、大谷の画像では0x00 0x10つまり、16byteがApp0に割かれていることがわかる。次の5バイトは10 4a 46 49 46 00となっているが、これはASCIIコードになっているそうである。文字形式で印字してみると、JFIF\0と印字された。これは厳密な意味での”JPEG”画像データではなく、拡張されたJPEGという意味らしい。とはいえ、その拡張子はjpg, jpegを取ることが許されていて、緩い意味でのJPEG画像ファイルとして認識されているという。(これは知らなかったので勉強になった。)

JFIF\0の宣言文字に続くのは0と1の値であるが、これはなんらかの画像情報のデータであろう。

最初の試みとしてはこんなところであろう

ということで、jpegファイルもヘッダー部分は単なるバイナリデータであることが確認でき、私の未熟な技能でも読み取りが可能であることがわかった。手も足も出なかったJPEGファイルに一太刀浴びせることができたのは嬉しい限りである。JPEGのヘッダーファイルはBMPのそれよりも長く複雑であるそうだが、各種のマーク記号によって区切られており、セグメントごとに読み取ればなんとか処理できそうである。次はそちら方面をやってみようかと思っている。たとえば、JPEGのファイルサイズとか、撮影日時なんかのヘッダー情報が手に入れば、それはかなり実用面に近づくと思うのである。

おまけ(ファイルの終端)

JPEGの終端がff d9で終わっているか、まずは手っ取り早くxddコマンドを使って調べてみた。結果は次のとおりで、予想通りであった。