北岳より間ノ岳を望む


Date/Time: 2014:09:15 06:59:51
Camera: PENTAX
Model: PENTAX K-5 II s
Exporsure Time: 1/500
FNumber: 5.6
Aperture Value: 5.0
Focal Length: 21.0

Close

y2blog » JavaScriptでEXIFデータを読み込む方法

10

28

2009

JavaScriptでEXIFデータを読み込む方法

EXIFデータをJavaScriptで読み込む方法について


最近のコンシュマー向けデジタルカメラでは、その殆どがJPEG形式の画像データを記録するようになっており、画像データと共に撮影日時や露出などの情報などがEXIFという規格に従って記録されています.EXIFという規格は日本のカメラメーカーや電機メーカーなどが中心となって制定された規格で、その仕様書は “Exchangeable image file format for digital still cameras: Exif Version 2.2” (JEITA CP-3451  2002年4月 英文PDF書類 ) として一般に公開されていますので、EXIFのデータ構造を知るには無くてはならないドキュメントです.
【追記】EXIFのVersion 2.3 に関するPDFドキュメント(日本語版)が下記のURLで参照可能です.

デジタルスチルカメラ用画像ファイルフォーマット規格 Exif 2.3』(2010年4月26日制定)

EXIFのタグデータの内容を参照したり、編集するには通常Adobe LightRoom, Apple Aperture などの専用アプリケーションを用いるのが一般的ですが、WEBの世界では単純にJPEG画像ファイルに含まれている EXIF タグデータの内容を表示したり、GPSデータの情報などを用いてGoogle Map などと連携するのが一般的な用途だと思います.


サーバ側でWEBアプリケーションを開発する場合には、PHPを用いるのが一般的ですが、幸いなことにPHPにはJPEG画像データからEXIFデータを読み込む機能が最初から備わっています.


例)  $exifdata = @ exif_read_data( $url, “EXIF” );

  ( @はPHPのエラーメッセージを抑制するための指示子 )


PHPが使える場合は話は簡単なのですが、サーバ側でアプリケーションを動かさずにクライアントサイドだけで処理を行わなければならない場合には、JavaScript を使うのが一般的です.


PHPと同じようなイメージでEXIFデータをJavaScript側で処理すれば良いだけだろうと高をくくっていたら、JavaScriptにはJPEG画像ファイルに含まれるEXIFデータにアクセスする簡単な方法がないことがわかりました.


単に私がJavaScriptに不慣れで熟知していないだけという面もありますが、通常のJavaScriptの範疇ではどうやってEXIF情報を読み出せば良いのか皆目検討がつきませんでした.

Javascript EXIF Readerについて


JavaScriptでEXIFタグを読み出す方法を色々と模索しているうちに、JavaScript 用のEXIFリーダーを公開しているサイトを見つけました.Jacob SeidelinさんのNIHILOGIC BLOG (http://www.nihilogic.dk/)というブログに、Javascript EXIF Readerに関する記事があります.


この記事中に、binaryajax.js と exif.js という2つのJavaScript プログラムがありますので、先ずはこの2つのJavaScriptソースコードを入手して下さい.( jQueryのplugin バージョン jQuery EXIF data plugin も用意されています)
このプログラムの簡単な例が Read EXIF data with Javascript に有ります.使い方はいたって簡単で、binaryajax.js と exif.js という2つのJavaScript プログラムをヘッダ部で予め宣言しておき、

<script type=”text/javascript” src=”binaryajax.js”></script>

<script type=”text/javascript” src=”exif.js”></script>
HTML body 部で、
<img src=”photo.jpg” id=”theTargetImage” exif=”true” />


<script type=”text/javascript”>

var imageObj = document.getElementById( “theTargetImage” );

var makerString = EXIF.getTag( imageObj, “Make” );

...

</script>
のように記述するだけです.


binaryajax.jsでは XMLHttpRequest() を用いて非同期にファイルのデータをバイナリーデータとして丸ごと読み取る処理を行っており、読み込んだデータの固まりをexif.js がEXIFデータ構造に基づいて様々なデータ処理を行うような作りになっています.


これらのJavaScriptプログラムに一通り目を通してみましたが、私のJavaScriptに関する知識や経験が貧弱だったために、最初は何がどうなっているのか良く分かりませんでした.コールバック関数が多用されていて、どのような順序で処理されて行くのかがとても分かり難いプログラムでした.


それらしい関数やメソッドを用いて、色々と試してみましたがどうもしっくり行きません.実際にデータが転送されるタイミングがリクエストを出した時点とは全く別に行われるため、通常のシーケンシャルな処理手順では上手く行きません.散々悩んだあげくに、プログラムの流れを追ってみたり、デバッガで処理の様子を観察しているうちに、Ajaxの要である非同期データ処理の仕組みがきちんと理解出来ていなかったのがいけなかったことに気付きました.


Ajaxの基本的な処理が理解出来ると、後のプログラムの解読は比較的簡単に事がすすみました.exif.js, binaryajax.js には最初からコールバック関数呼び出し用のフックが用意されているので、それらを上手く活用すれば良いのです.


デモページにあるような単純なアプリケーションにはそのままの状態でexif.jsを用いれば良いのですが、汎用的なJavaScriptのEXIFリーダプログラムとして使うにはexif.jsは少し問題があります.具体的な改造の手順については後ほど説明します.

Lightbox 2.0.4 と EXIF Readerを組み合わせる


WordPressなどのように、PHP側でEXIFデータを処理できる場合は良いのですが、JavaScript 単独でEXIFデータを読み出すことが出来た方が便利です.Lightbox 2.0.4上でEXIFのメタタグデータを表示するように改造したり、EXIFデータのGPSタグを利用して画像の撮影場所に関連したGoogleMap等の地図を呼び出すなどといった処理が可能になります.


JavaScriptで書かれた画像表示ライブラリとしてLightbox 2.0.4 がすっかり定着しており、このLightboxを改造して使用されている方も多いことと思います.そこで今回は、Lightbox 2.0.4上でEXIFタグデータを表示する方法について簡単に紹介します.


Lightbox自体の改造ついては今回は詳しくは触れませんが、exif.jsをLightboxから呼び出す部分を中心に説明します.Lightbox 2.0.4そのものの改造についての記事は、また別な機会に紹介したいと思います.


exif.jsのデモを見れば大凡の使い方は分かるかと思いますが、exif.jsでは 該当するページに含まれるimgタグをスキャンして、 属性データが exif=”true” となっている画像をページがロードされた時点で全て読み込んでEXIFタグを取り込んでいます.デモページではページ上の画像をクリックしたときにEXIFのタグ情報を取り出す処理をして、alert() を使って画面にEXIFタグデータを表示しています.

exif.jsを汎用的な JavaScriptアプリケーションとして用いるには少し使い勝手が悪いので、EXIFタグデータが必要になった時点でオンデマンドに取り出せるように変更します.
【exif.js側の修正】
1.exif.js の最後の方にある、下記の1行をコメントアウトにします.


//addevent( window, “load”, loadAllImages );  

これにより、ページがロードされた時点で強制的に該当する全ての画像データを読み込むことが無くなります.属性タグの exif=”true” という記述も不要です.


2.exif.jsの556行目辺りにある EXIF.getData メソッドを書き換えて、強制的に getImageData() を呼び出すように変更します.
修正前


EXIF.getData = function(oImg, fncCallback) 
{
	if (!oImg.complete) return false;
	if (!imageHasData(oImg)) {
		getImageData(oImg, fncCallback);
	} else {
		if (fncCallback) fncCallback();
	}
	return true;
}

修正後

EXIF.getData = function(oImg, fncCallback) 
{
	if (!oImg.complete) return false;

        getImageData(oImg, fncCallback);

//	if (!imageHasData(oImg)) {
//		getImageData(oImg, fncCallback);
//	} else {
//		if (fncCallback) fncCallback();
//	}
	return true;
}

以上で exif.js の修正作業は終了です.binaryajax.js については修正の必要はありません.
【追記: “binaryajax.js” はInternet Explorer 9.0 では修正が必要になります.詳細は『Internet Explorer 9 でのEXIF Reader』をご覧下さい.】

【 Lightbox 2.0.4 上で EXIFタグをキャプション欄に表示させる 】
話を簡単にするため、オリジナルの Lightbox 2.0.4 のコードに基づいて説明をします.Lightbox 2.0.4 のソースコードはダウンロードページからダウンロード可能です.


ソースコードの327行目辺りにキャプションを処理している部分 updateDetails があります.


    //
    //  updateDetails()
    //  Display caption, image number, and bottom nav.
    //
    updateDetails: function() {
    
        // if caption is not null
        if (this.imageArray[this.activeImage][1] != ""){
            this.caption.update(this.imageArray[this.activeImage][1]).show();
        }
        

    ...【以下省略】 


上記のソースコード中で、this.imageArray[this.activeImage][1] という部分が title属性の文字列が格納されている部分です.
<img src=”xxxxxxx.jpg” title=”撮影日時:” rel=”lightbox[test]” />
このキャプションの表示を、EXIFタグの撮影日時(“DateTimeOriginal”)タグの内容を付け加えてみます.本来ならば title属性の文字はそのままキャプション欄に表示して、別なキャプション欄を設けてそこに表示した方が良いのですが、LightBoxそのもののコードをかなり変更しなくてはならなくなるので、今回はより単純な例について説明します.

    updateDetails: function() {

        // if caption is not null
        if (this.imageArray[this.activeImage][1] != ""){

            var dateTimeString = "";

            // finding active lightbox image
            var theLightBoxImage = document.getElementById("lightboxImage");

            if ( theLightBoxImage ) {

                EXIF.getData( theLightBoxImage, function() {

                    // callback function (will be invoked after XHR finished loading)
                    dateTimeString = EXIF.getTag( theLightBoxImage, "DateTimeOriginal" );
  
                    //
                    // this.caption.update( this.imageArray[this.activeImage][1] + dateTimeString ).show();

                    // 【 訂正 (11/10 2009):  下記のように修正しました】

                    var theCaption = document.getElementById("caption");
                    theCaption.update( this.imageArray[this.activeImage][1] + dateTimeString ).show();
 

                } ); // end [ EXIF.getData() ] 

            } // end [ if ( theLightBoxImage ) ]

        } // end [ if (this.imageArray[this.activeImage][1] != "") ]


このサンプルコードの要点は、LightBox の画像がオーバーレイ表示された時点で、EXIFタグデータを読みに行きます.Lightbox2.0.4ではオーバーレイ表示された画像のid属性は “lightboxImage” になっていますので、
document.getElementById(“lightboxImage”);
を用いて、該当する画像データを選択します.


後はEXIF.getDataメソッドを呼び出し、第2引数にコールバック関数を記述します.このコールバック関数が、XMLHttpRequest()による非同期データ転送処理が終了した時点で呼び出され実行されます.EXIFタグを取り出す部分と、Lightboxの画面にキャプションを表示する処理をこのコールバック関数中に記述するというのが要点です.

【訂正(11/10 2009): 上記のコードに修正を加えました】


FireFoxではコールバック関数の中で、this を用いてキャプション欄のオブジェクトを参照することができませんでしたので、getElementById() でキャプション欄のオブジェクトを再度取得するように修正しました.



Lightbox 2.0.4 上で EXIFタグを表示させた例


Lightbox 2.0.4を改造して、キャプション欄を追加し、撮影日時、場所(GPSジオタグ情報)、撮影条件(露出条件、カメラモデル)などを表示させてみました.
【 追記:現在 このブログではLightbox2.0.4 から jQuery lightBox に変更しているので、実際に表示されている画像はjQuery lightBoxでの画像です. 】

苫小牧港上空 苫小牧港上空にて
鳥海山 鳥海山を望む
旭岳夫婦池 夫婦池と旭岳
須走口下山道 須走口下山道