撮影情報をプログラムで取得する

写真ネタが続いたので、ちょっとプログラミングネタを。といっても写真絡みであるのは間違いない。デジタルカメラで撮影すると、写真の画像ファイル(JPEG形式)にEXIFという撮影時の情報が記録される。これは非常に便利なもので、EXIF情報を解釈するソフトを使うと、写真の整理などがかんたんにできてしまうし、あとから撮影条件などを確認するのにも便利だ。今回は、このEXIF情報をC#などのプログラムから扱うにはどうするか、どいうことをまとめてみた。

EXIF:Exchangeable Image File=交換可能なイメージファイルの意

まず環境を、.NET Framework上と想定しておきたい。すると使用言語はVisual BasicやC#ということになるのだが、個人的に使用頻度の高いC#にしておきたい。Visual Basicでもほぼ同じ書きかたで使えるはずである。

.NET Framework上では、EXIF情報を扱うのは非常にカンタンで、System.Drawing名前空間のBitmapオブジェクトを使うだけである。Bitmapオブジェクトに画像をロードし、PropertyItemプロパティを使ってアクセスするだけである。PropertyItemはSystem.Drawing.Imagingのコレクション型であるので、foreach構文を使って各要素を取得することになる。

System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(filename);
foreach (System.Drawing.Imaging.PropertyItem item in bmp.PropertyItems)
{
………
}

System.Drawing.Imaging.PropertyItemは、Id, Type, Valueという3つのプロパティから構成される。IdはEXIF情報を識別する番号であり、Typeは情報の形式を、Valueはデータそのものを表す。

Idは膨大であるが、以下のサイトが非常に参考になる。実際に、どの情報を引っ張ってくるかはIdがわからないとどうしようもないので、資料と首っ引きになる。

Exif file format: Exif file format
Exif Tags: EXIF Tags (英語)

ちなみにTypeを抜粋すると、以下のようになっている。

Typeプロパティの値 データの型
1 Byte
2 ASCII形式でエンコードされたByteオブジェクトの配列
3 16ビット整数
4 32ビット整数
5 有理数を表す2つのByteオブジェクトの配列
6 未使用
7 未使用
8 未使用
9 SLong
10 SRational

上記のコードの省略部分では、Idを判別し、Typeに応じてValueを処理することになる。ちなみに、代表的な情報は文字列と整数値であり、前者ではたとえばカメラモデル、後者はたとえばISO感度が該当する。これを取り出すコードを書いてみると、こんな感じになる。

    //カメラモデル情報を取得
    if (item.Id == 0x0110 )
    {
        string val = System.Text.Encoding.ASCII.GetString(item.Value);
        string model = val.Trim(new char[] { '\0' });
    }
    //ISO感度情報を取得
    if (item.Id == 0x8827)
    {
        short val = BitConverter.ToInt16(item.Value, 0);
        short iso = val;
    }
    ………

あるいは、Typeプロパティの値に応じて処理してもいい。

    //エンコードされたASCII文字列を取得
    string str;
    short int16;
    if (item.Type == 2 )
    {
        str = System.Text.Encoding.ASCII.GetString(item.Value);
    }
    //16ビット整数値を取得
    if (item.Type == 3)
    {
        int16 = BitConverter.ToInt16(item.Value, 0);
    }
    ………

バイト列データの文字列への変換には、System.Text.Encoding.ASCIIクラスのGetStringメソッドを使える。16ビット整数への変換にはBitConverterクラスのToInt16メソッドが使える。自前で書くと面倒な処理も、一発で済ませられるのが.NET Frameworkの持つ膨大なクラスライブラリのメリットだ。

処理が終わったら、bmpオブジェクトをDisposeすればいい。

bmp.Dispose();

ちなみに日付情報は文字列で入っているなど、その形式には注意しなければならないので、上記のページを参考にしてひとつずつ処理をしていった方が確実である。

コメント