none
BitmapMetadataで0th以外のIFDのタグを取得する方法は? RRS feed

  • 質問

  • WindowsVistaで.NET Framework 3.5+VisualBasic2008で、Windows Imaging Componentを使ってTIFFやExifのタグを読み出そうとしています。

     

    BitmapMetadataクラスのGetQuery()で、0th IFDのタグを読み出すことは出来るのですが、1st以降のIFDを読み出すクエリ文字列の作り方がわかりません。どなたか、ご存じないでしょうか?

     

    0th IFDの方は、例えば、Makeタグならば以下の問合せでうまくいきます。

    /app1/ifd/{ushort=271}

     

    しかし、1st IFDの方は、例えば、ImageWidthタグを読もうと、以下の様にしてもnothingが帰ってきます。(記録されていないというオチでないことは確認済みです。)

    /app1/[1]ifd/{ushort=256}

     

    Googleなどで探しても、見つかるのは0th IFD以下ばかりで、1st(あるいは任意に追加された3個目以降)のIFDのタグの入出力方法は見つけられませんでした。

    2008年2月29日 14:36

回答

  •  やな さんからの引用

    今後の議論は『Exif規格に則ったJPEGファイルの1st IFDのタグを読み出すクエリー文字列は?』についてでお願いします。

    "/app1/{ushort=1}/"で始めれば1stIFDの値が取れるのではないでしょうか。

     

    調査手段

    正確な情報が得られなかったため、BitmapMetadataクラスがIEnumerable<string>を実装していることをヒントに次のようなコードで実験しました。

    ※コードはC#です。

    ※次のコードはあくまでテスト用に書き下ろしたものであり、解放処理が全く考慮されていません。

     

    Code Snippet

    string filename = @"1112.jpg";
    Stream jpgStream = new System.IO.FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    JpegBitmapDecoder jpgDecoder = new JpegBitmapDecoder(jpgStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    BitmapFrame jpgFrame = jpgDecoder.Frames[0];


    BitmapMetadata metadata = (BitmapMetadata)jpgFrame.Metadata;
    Debug.WriteLine("Enumerating at /");
    foreach (string str in metadata)
    {
        Debug.WriteLine(str);
    }

    BitmapMetadata metadata2 = (BitmapMetadata)metadata.GetQuery("/app1");
    Debug.WriteLine("Enumerating at /app1");
    foreach (string str in metadata2)
    {
        Debug.WriteLine(str);
    }

    BitmapMetadata metadata3 = (BitmapMetadata)metadata2.GetQuery("/{ushort=1}");
    Debug.WriteLine("Enumerating at /app1/{ushort=1}");
    foreach (string str in metadata3)
    {
        Debug.WriteLine(str);
    }

     

     

    テスト対象のJPEGはPaint Shop ProでEXIF情報つきとして保存したものです。

    以下のようなデバッグ出力が得られました。

     

    Code Snippet

    Enumerating at /
    /app0
    /app1
    Enumerating at /app1
    /{ushort=0}
    /{ushort=1}
    Enumerating at /app1/{ushort=1}
    /{}
    /{ushort=259}
    /{ushort=282}
    /{ushort=283}
    /{ushort=296}
    /{ushort=513}
    /{ushort=514}
    /{ushort=531}

     

     

    順序立てて"/"、"/app1"という形で掘り下げることで、前述の目的の文字列を得ることができました。

    実際にContainsQuery("/app1/{ushort=1}/{ushort=531}")でTrueが得られることが確認できています。

    2008年3月2日 15:08
    モデレータ

すべての返信

  •  やな さんからの引用

    BitmapMetadataクラスのGetQuery()で、0th IFDのタグを読み出すことは出来るのですが、1st以降のIFDを読み出すクエリ文字列の作り方がわかりません。どなたか、ご存じないでしょうか?

     

    0th IFDの方は、例えば、Makeタグならば以下の問合せでうまくいきます。

    /app1/ifd/{ushort=271}

    MSDNのMetadata Query Language Overviewあたりに書いてある構文例では通らないかな?

    http://msdn2.microsoft.com/en-us/library/aa968944(VS.85).aspx

    http://msdn2.microsoft.com/en-us/library/bb531154(VS.85).aspx

     

    そもそも、TIFFにはapp1という構造がありません。app1を省略したら読めるとかありませんか?

    #JPEGであれば、app1マーカー内にIFDがあります。

    2008年3月1日 14:17
    モデレータ
  • "Metadata Query Language Overview"にある、
    > If the example JPEG included a second App1 block with an embedded IFD block,
    > the expression "/[1]app1/ifd" would be used to access the second App1 block.
    というくだりから類推して、JPEGの1番目のAPP1に入っている2番目のIFD(1st-IFD)内のタグを指すつもりで
    > /app1/[1]ifd/{ushort=256}
    という問い合わせ文を作ったのですが、期待した値は返ってこなかったということです。

    > そもそも、TIFFにはapp1という構造がありません。app1を省略したら読めるとかありませんか?

    > #JPEGであれば、app1マーカー内にIFDがあります。

    『省略は誤解の元』ですね。TIFF-IFDについてもExifタグについてもバイナリエディタで直接解読する程度の知識はありますので、おっしゃりたいことは理解できます。

    TIFFで採用されたIFDは線形リスト的なデータ構造であって、Exif規格では2つのIFDを連結し、1つ目(0th IFD)に本画像の情報を、2つ目(1st IFD)にサムネイルの情報を格納する決まりになっています。また、IFDを3つ、4つと連結することは(Exifからは外れるが)IFDの構造上は可能です。(なお、ここで言う『IFD』は、IFD個数~次のIFDオフセットの範囲全体を指しています。/app1/ifd/{ushort=0}という表記でifdで1階層とっているのも同じ見かたによるものでしょう。)

    今知りたいことは、IFD構造を踏まえた上で、WICで2番目以後のIFDのタグをGetQuery(あるいはSetQuery)で問い合わせる場合の方法です。
    2008年3月1日 19:43
  • BitmapMetadataクラスのインスタンスはどこから取得されていますか?

    BitmapDecoderクラスですか?BitmapFrameクラスですか?

     

    JPEGも、TIFFも、確かBitmapFrameクラスあたりから取得することになったと記憶していますが、確認させて下さい。

     

    蛇足

    WICはネイティブからCOMとして呼び出すことができます。

    この場合、COMのインターフェース・クラス・メソッド等の名前と、.NET Frameworkでのクラス・メソッド等の名前は異なります。

    今回は.NET Frameworkのクラスを通して利用するという風に認識しています。
    2008年3月2日 2:51
    モデレータ
  •  Azulean さんからの引用

    JPEGも、TIFFも、確かBitmapFrameクラスあたりから取得することになったと記憶していますが、確認させて下さい。

     

    この前提で考えるに、TIFFの0th IFDは最初のBitmapFrameインスタンスのMetadataプロパティ、1st IFDは次のBitmapFrameインスタンスのMetadataプロパティに相当すると考えられます。

    BitmapDecoderインスタンスのFramesプロパティから次のフレームを読み込むことで、1st IFDを取得できるのではないかという推測です。

     

    いいわけ

    マルチページTIFFファイルのサンプルが手っ取り早く入手できればテストしてから投稿できたのですが、少し探る程度では見あたりませんでした。

    間違えていたらすみませんが、ご指摘下さい。

    #職場にはいくつかサンプルあるんだけどなぁ…

    2008年3月2日 2:55
    モデレータ
  • 混乱の元なので、いったんTIFFは忘れてください。
    今後の議論は『Exif規格に則ったJPEGファイルの1st IFDのタグを読み出すクエリー文字列は?』についてでお願いします。

    具体的なソースは後ほど投稿したいと思います。

    2008年3月2日 12:15
  •  やな さんからの引用

    今後の議論は『Exif規格に則ったJPEGファイルの1st IFDのタグを読み出すクエリー文字列は?』についてでお願いします。

    "/app1/{ushort=1}/"で始めれば1stIFDの値が取れるのではないでしょうか。

     

    調査手段

    正確な情報が得られなかったため、BitmapMetadataクラスがIEnumerable<string>を実装していることをヒントに次のようなコードで実験しました。

    ※コードはC#です。

    ※次のコードはあくまでテスト用に書き下ろしたものであり、解放処理が全く考慮されていません。

     

    Code Snippet

    string filename = @"1112.jpg";
    Stream jpgStream = new System.IO.FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    JpegBitmapDecoder jpgDecoder = new JpegBitmapDecoder(jpgStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    BitmapFrame jpgFrame = jpgDecoder.Frames[0];


    BitmapMetadata metadata = (BitmapMetadata)jpgFrame.Metadata;
    Debug.WriteLine("Enumerating at /");
    foreach (string str in metadata)
    {
        Debug.WriteLine(str);
    }

    BitmapMetadata metadata2 = (BitmapMetadata)metadata.GetQuery("/app1");
    Debug.WriteLine("Enumerating at /app1");
    foreach (string str in metadata2)
    {
        Debug.WriteLine(str);
    }

    BitmapMetadata metadata3 = (BitmapMetadata)metadata2.GetQuery("/{ushort=1}");
    Debug.WriteLine("Enumerating at /app1/{ushort=1}");
    foreach (string str in metadata3)
    {
        Debug.WriteLine(str);
    }

     

     

    テスト対象のJPEGはPaint Shop ProでEXIF情報つきとして保存したものです。

    以下のようなデバッグ出力が得られました。

     

    Code Snippet

    Enumerating at /
    /app0
    /app1
    Enumerating at /app1
    /{ushort=0}
    /{ushort=1}
    Enumerating at /app1/{ushort=1}
    /{}
    /{ushort=259}
    /{ushort=282}
    /{ushort=283}
    /{ushort=296}
    /{ushort=513}
    /{ushort=514}
    /{ushort=531}

     

     

    順序立てて"/"、"/app1"という形で掘り下げることで、前述の目的の文字列を得ることができました。

    実際にContainsQuery("/app1/{ushort=1}/{ushort=531}")でTrueが得られることが確認できています。

    2008年3月2日 15:08
    モデレータ
  • 階層毎にBitmapMetadataオブジェクトを取得する手は試していませんでした。なんだか連想配列みたいなアクセス方法ですね。

     

    なるほど、「/app1/{ushort=1}/{ushort=タグID}」で1st IFDを読めています。ありがとうございました。

     

    app1の方は{型=数値}という表記には置き換えられないようですが、IFDオフセットでのリンクとExifIFDPointerタグでのリンクは{型=数値}で表現できているようです。

     

    言い訳になりますが、英文説明に"/[0]app1/[0]ifd/..."という表記があって、複数のapp1を[1]app1、[2]app2で表現できると書かれていたので、当然、[1]ifd、[2]ifdという表記が成り立つものと思い込んでいました。

     

    ちなみにもう遅いですが、以下の様なソースでした。(例外処理は省略しました。)

     

    Dim ImagePath As String

    Dim QueryString As String

    Dim ImageMetadata As BitmapMetadata
    Dim ImageDecoder As BitmapDecoder

    ImageDecoder = BitmapDecoder.Create(New Uri(ImagePath, UriKind.Relative), BitmapCreateOptions.None, BitmapCacheOption.Default)
    ImageMetadata = ImageDecoder.Frames(0).Metadata

    textBox1.Text = Metadata.GetQuery(QueryString).ToString

    2008年3月3日 1:20
  • 現状、GetQuery("/app1/{ushort=1}/{ushort=1})」などのクエリで、APP1内包の0th-IFD、1st-IFDのタグを読み出せるようになりましたが、連続してGetQueryメソッドを発行していると、全体の1~2割のGetQueryがCOMException例外(ハンドルは無効です)で失敗します。(SetQueryでも発生するかどうかは未確認です。)

    失敗するタグに再現性はなく、同じ処理で1回目、2回目、3回目・・・と何度か試行してみると、COMException例外が発生するタグは異なっていることが多いのですが、なんとなく、Int64、UInt64、BitmapMetadataBlob、Stringで発生することが多いように感じられます。

    1回目、2回目では、それぞれでBitmapDecoderを取得しなおしていますが、例外が発生した場合にBitmapDecoderを取得しなおさず同じクエリ文字列でGetQueryメソッドを連続発行(リトライ)しても、やはり例外が発生して正常に取得できません。

    原因を特定するには何を調べれば良いでしょうか?

    2008年3月6日 3:52
  •  やな さんからの引用

    原因を特定するには何を調べれば良いでしょうか?

    書かれている内容では検討がつきません。

    もう少し細かい情報、あるいは再現させるソースコード・画像ファイルは明示できませんか?

     

    WICの利用はまだ広まっているとは言い難く、少しの情報ですぐに思い当たる人がいるかどうか定かではありません。

    解決への手がかりを得るためにも、なるべく多くの情報、あるいは再現環境を整えるようにしてみてはいかがでしょうか。

    2008年3月6日 14:40
    モデレータ
  • こんにちは。中川俊輔 です。

     

    Azuleanさん、回答ありがとうございます。

     

    やなさん、フォーラムのご利用ありがとうございます。

    その後いかがでしょうか?

    有用な情報と思われたため、勝手ながらAzuleanさんの回答へ回答済みチェックをつけさせていただきました。

    追加の質問等ありましたら、是非投稿してください!

     

    回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
    有用な情報と思われる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。

    やなさんはチェックを解除することもできますので、ご確認ください。

     

    それでは!

    2008年3月17日 2:23