WPFで画像メタデータを操作する

画像

私は最近、.NETプラットフォーム、C#言語、およびWindows Presentation Foundationに精通することにしました。

勉強の過程で(そしてパイロットプロジェクトの開発過程で常に言語と技術を勉強します)、私はかなりの落とし穴と微妙な点に出会いました。 誰もがhabrasocietyと共有したい(多くの新しいWPF開発者が望んでいると思う)が、結果のhabratopikのボリュームが大きすぎるので、イメージメタデータから始めることにしました。 このテーマに関する情報は、英語を話すインターネットでも十分ではありません。







一般に、さまざまな形式の画像にメタデータを含めることができますが、例としてJPEGについて説明します。 彼と働いた。 他の形式の場合、違いは小さいと思います。



メタデータの種類





最初に、画像に含まれるメタデータのタイプを確認しましょう。 おそらく誰もがこれを知っていますが、念のために、私はあなたに教えます:





WPFメタデータの原則





WPFでメタデータを操作するには、BitmapEncoder、BitmapDecoder、BitmapSource、BitmapFrame、BitmapMetadata、InPlaceMetadataWriterクラスが使用されます。

BitmapEncoderクラスとBitmapDecoderクラスには、特定の画像形式で作業できる子孫があります。 私の場合、JpegBitmapEncoderとJpegBitmapDecoder。

InPlaceMetadataWriterクラスは、ファイルをトランスコードせずに、メタデータを直接インプレースで変更するために使用されます。

データの読み取りと書き込みには2つの方法があります。階層メタデータタグ名を操作するGetQuery / SetQuery関数を使用するか、BitmapMetadataクラスのフィールドを使用してメタデータに簡単にアクセスします。

BitmapMetadataクラスのフィールドを介してメタデータにアクセスする場合、WICは、異なる標準のメタデータで、最初のXMP、次にIPTCおよびEXIFの順序で対応するフィールドを見つけようとします。 BitmapMetadataクラスのフィールドを介してタグを記述する場合、WICはそれらをXMP形式で書き込みます。



メタデータの読み取り





次に、ファイルからメタデータを読み取ることができる既製の関数の例を示します。



  1. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  2. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  3. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  4. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  5. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  6. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  7. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  8. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  9. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  10. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );



  11. FileStream f = File .Open( "test.jpg" , FileMode.Open); BitmapDecoder decoder = JpegBitmapDecoder.Create(f, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[ 0 ].Metadata; // string title = metadata.Title; // XMP string xmptitle = ( string )metadata.GetQuery( @"/xmp/<xmpalt>dc:title" ); // EXIF string exiftitle = ( string )metadata.GetQuery( @"/app1/ifd/{ushort=40091}" ); // IPTC string iptctitle = ( string )metadata.GetQuery( @"/app13/irb/8bimiptc/iptc/object name" );







ここのすべては非常にシンプルで透明なので、すぐに録音に行きます。



メタデータの記録





  1. BitmapMetadata md = new BitmapMetadata"jpg" );
  2. md.SetQuery( @ "/ xmp / <xmpalt> dc:title" 、xmptitle);
  3. md.SetQuery( @ "/ app1 / ifd / {ushort = 40091}" 、exiftitle);
  4. md.SetQuery( @ "/ app13 / irb / 8bimiptc / iptc /オブジェクト名" 、iptctitle);
  5. BitmapFrame frame = BitmapFrame.Create(decoder.Frames [ 0 ]、decoder.Frames [ 0 ] .Thumbnail、md、decoder.Frames [ 0 ] .ColorContexts);
  6. BitmapEncoderエンコーダー= 新しい JpegBitmapEncoder ();
  7. encoder.Frames.Add(フレーム);
  8. FileStream of = File .Open( "test2.jpg" 、FileMode.Create、FileAccess.Write);
  9. encoder.Save(of);
  10. of.Close();




コードは、メタデータを読み取るフラグメントの続きとして機能します。 3つのすべてのメタデータ形式でメタデータにタイトルを書き込むことにより、元のファイルのコピーを作成します。



オンサイトのメタデータ編集





これまで、私は一般的に非常によく文書化された簡単なことを言ってきましたが、ここではすべてがすでにより複雑です。 公式文書(MSDN)の例は間違っており、一般的に物事の実際の状態とは意味が反対です。

インプレースメタデータを編集するには、InPlaceBitmapMetadataWriterクラスのオブジェクトを作成する必要があります。



  1. InPlaceBitmapMetadataWriterライター;
  2. writer = decode.Frames [ 0 ] .CreateInPlaceBitmapMetadataWriter();




その後、通常のBitmapMetadataと同様に、SetQueryを呼び出して必要なメタデータを設定します。

変更を保存するには、TrySave()メソッドを呼び出して、変更を元のストリームに保存しようとします。 記録しようとしても成功する場合としない場合があります。 成功した場合、メソッドはtrueを返し、そうでない場合はfalseを返します。

変更の書き込みを妨げる可能性のある最も一般的な間違いは、メタデータに十分な空き領域がないことです。 原則として、新たに撮影されたすべての写真にはメタデータに十分なスペースが含まれていないため、メタデータの編集をその場で使用し始めるには、ファイルのコピーを作成し、特別なパディングフィールドを使用してメタデータを追加し、その後の変更のために空きスペースを残してください。 これを行うには、ファイルを開き、目的のフレームとそのメタデータのクローンを作成し、いくつかのリクエストを実行します。



  1. BitmapFrame frame =(BitmapFrame)decode.Frames [ 0 ] .Clone();
  2. BitmapMetadataメタデータ=(BitmapMetadata)decode.Frames [ 0 ] .Metadata.Clone();
  3. metadata.SetQuery( "/ app1 / ifd / PaddingSchema:Padding"2048 );
  4. metadata.SetQuery( "/ app1 / ifd / exif / PaddingSchema:Padding"2048 );
  5. metadata.SetQuery( "/ xmp / PaddingSchema:Padding"2048 );
  6. BitmapFrame newframe = BitmapFrame.Create(frame、frame.Thumbnail、metadata、original.Frames [ 0 ] .ColorContexts);




その後、エンコーダーでフレームをエンコードし、目的のストリームに書き込むだけで十分です。その結果、後でメタデータを編集するための空きスペースが画像に表示されます。

通常、2048バイトのパディング値で十分です。 さらに必要な場合は、より大きな値を指定できます。



クエリ文字列





SetQuery / GetQueryメソッドを研究する誰もが合理的な質問を持っていると思います-シンプルで直感的とは言えないこれらのクエリ文字列のすべてをどこで入手できるのでしょうか?

MSDNで広範囲に検索した結果、 リストが見つかりました。 おそらくすべての必要な要求があります。 不足しているものは、原則として、類推によってコンパイルすることができ、多くの例があります:)



微妙さと落とし穴









おわりに





一般に、WPFを使用したメタデータの操作はかなり複雑で混乱しているように見えました。 記載されているほとんどすべての落とし穴は、デバッグとグーグルで数時間かかり、これに関する情報はどこにもなく、症状は時々非常に奇妙です。 公式文書(MSDN)はこの質問をうまくカバーしておらず、一部の場所では完全に間違っています。

この収集された情報が、WPFを介してメタデータを操作し、時間を節約する必要がある人々に役立つことを願っています:)



PSコメント(どこかで間違いを犯した場合)と、私が会ったことのない、または言及するのを忘れた落とし穴の説明を見て喜んでいます。



PPS WPFについて書き続けるべきですか、それとも古くから知られているものを書いていますか?



All Articles