日常風景の写真撮影に,コンパクトデジタルカメラから,Android端末やiPhoneに切り替わって,何年になるでしょうか.
いずれにせよ,撮った写真は,1台のPCのハードディスクに集約しています.ファイル名に日本語があればASCII文字のみとなるよう変換し,撮影年月日でフォルダに振り分けます.撮影年月日は,ファイル名ではなく,Exif情報の中のDateTimeタグを参照します.
なのですが,先月から,その振り分けでエラーが発生していました.振り分けは自作のRubyスクリプトで,都合によりそのソースコードは非公開とします.ともあれエラーの発生状況を再現してみます.
2つのファイルがあります.もともとは長いファイル名ですが,簡単のため1.jpg,2.jpgとします.Rubyでは,gem install exifrのコマンドで,exifrライブラリをインストール済みです.
irbコマンドを使って,日時情報を見てみます.まずは1.jpgのほう.
$ irb irb(main):001:0> require "exifr" => true irb(main):002:0> ex = EXIFR::JPEG.new("1.jpg"); nil => nil irb(main):003:0> ex.date_time => 2017-04-03 06:29:45 +0900 irb(main):004:0> ex.date_time.class => Time irb(main):005:0> exit
途中の「; nil」について,書かなかったら,exに代入されるオブジェクトの内容が表示されます.バイナリデータもあり,膨大な出力となります.
次に,2.jpgのほうですが,こちらは自作の振り分けスクリプトでエラーが発生するものです.
$ irb irb(main):001:0> require "exifr" => true irb(main):002:0> ex = EXIFR::JPEG.new("2.jpg"); nil => nil irb(main):003:0> ex.date_time => "2017:04:03 06:30:00.284" irb(main):004:0> ex.date_time.class => String irb(main):005:0> exit
エラーになったという2.jpgでは,日時情報は「.284」(秒未満の値?)がついた文字列です.それに対し1.jpgでは,日時情報がTimeのインスタンスとなっています.
Exifの仕様に,RubyのTimeクラスが入っているというのは,おかしな話なので,ここはおそらくexifrライブラリがお節介をしているのでしょう.DateTimeタグの値はあくまでバイト列であり,その値を,容易な操作でTimeに変換できるのであれば,そのインスタンスに,そうでなければStringのインスタンスにしている,と解釈すればすっきりします.
年月日を獲得する処理は,これまで,次のように記述していました.
dt = ex.date_time ymd = dt.strftime("%Y%m%d")
これを以下のように書き換えました.
dt = ex.date_time case dt when Time ymd = dt.strftime("%Y%m%d") when String ymd = dt.gsub(/\D/,"")[0,8] else raise end
ちなみに1.jpgはiPhoneのカメラ(標準アプリ)で,2.jpgはMicrosoft Pixで撮影したものです.標準アプリだと,GPS関連の情報も,Exifとして入っています.上で「; nil」を書いて中身を見ませんでしたが,中身を見てみると,終わりのほうに「@exif=」から始まって詳細を知ることができ,1.jpgと2.jpgとで,gps関連の有無も確認できました.
Microsoft Pixは,無音で撮影したいときや,詳細なメタ情報が不要なときに,活用することとし,家族写真など,日常の風景は,標準アプリのカメラのほうが良さそうです.