端くれプログラマの備忘録 C#,画像処理 [C#] 画像からExif情報を削除するRemovePropertyItemメソッドの挙動がおかしい件

[C#] 画像からExif情報を削除するRemovePropertyItemメソッドの挙動がおかしい件

Bitmapクラス (Imageクラスから派生) のメソッドを眺めていたら、プロパティ項目を削除するメソッドを発見。

Image.RemovePropertyItem メソッド (System.Drawing)
http://msdn.microsoft.com/ja-jp/library/system.drawing.image.removepropertyitem(v=vs.110).aspx

Exif情報などメタデータはプロパティ項目として画像に格納されているので、画像ファイルを読み込んでこのメソッドでプロパティ項目を削除して保存しなおせば、Exif情報が削除された画像ファイルが作成できるはず。そう思って試してみたのだけどうまくいかない。画像ファイルを読み込んだBitmapインスタンスからはプロパティ項目を削除できるのだが、保存しなおした画像ファイルには削除したはずのプロパティ項目が復活しているのだ。おかしい。

テストコード

以下のコードで挙動を調べた。

// まずはExif情報付きのJPEGファイルを読み込む
Bitmap image = new Bitmap(@"C:\Temp\Exif test\ff_x_e1_001.JPG");
 
// プロパティ項目数をチェック(62個あった)
Console.WriteLine("(1) {0} properties", image.PropertyItems.Length);
 
// ----- テスト1 -------------------------------------------------------
// 特定のExif情報(orientation)だけ変更してみる
foreach (PropertyItem item in image.PropertyItems) {
    if (item.Id == 0x112) {
        item.Value[0]++;
        image.SetPropertyItem(item);
        break;
    }
}
// プロパティ項目数をチェック(変わらず62個あった)
Console.WriteLine("(2) {0} properties", image.PropertyItems.Length);
 
// この時点の画像を保存(Exif情報をダンプしたらorientationは変わっていた。期待通り)
image.Save(@"C:\Temp\Exif test\ff_x_e1_001a.JPG", ImageFormat.Jpeg);
 
// ----- テスト2 -------------------------------------------------------
// 特定のExif情報(orientation)だけ削除してみる
image.RemovePropertyItem(0x112);
 
// プロパティ項目数をチェック(1個減って61個になっていた。期待通り)
Console.WriteLine("(3) {0} properties", image.PropertyItems.Length);
 
// この時点の画像を保存(Exif情報をダンプしたら削除したはずのorientationが存在。おかしい!)
image.Save(@"C:\Temp\Exif test\ff_x_e1_001b.JPG", ImageFormat.Jpeg);
 
// ----- テスト3 -------------------------------------------------------
// 全てのメタ情報を削除してみる
while (image.PropertyItems.Length != 0) {
    int id = image.PropertyItems[0].Id;
    image.RemovePropertyItem(id);
}
 
// プロパティ項目数をチェック(0個になっていた。期待通り)
Console.WriteLine("(4) {0} properties", image.PropertyItems.Length);
 
// この時点の画像を保存(Exif情報をダンプしたら全項目が存在。おかしい!)
image.Save(@"C:\Temp\Exif test\ff_x_e1_001c.JPG", ImageFormat.Jpeg);

結果と個人的な推察

インスタンス上で変更したプロパティ情報は、保存したファイルにも反映される。しかし、削除したプロパティ情報は保存したファイルでは復活してしまう。これは.NETのバグだろうか。いや、文書には書かれていないけど、恐らくは仕様だろう。僕の予想は以下の通り。

画像データに加えてメタ情報が格納されることで画像ファイルは以前に増して肥大化している。極端な例だけど、わずか5×5ピクセルの画像に10GBのメタデータが付加されることだってあり得る。もしImageクラスに画像ファイルの内容を全て保持するようにすると、オブジェクトが肥大化してメモリを圧迫するのは明らか。そこでImageクラスの設計者は、クラスの機能に必要なデータだけを保持することにした。その弊害として、オブジェクトを別ファイルに書き出しすと、元ファイルにあったメタ情報が継承されないという問題が起こる。これを回避するために、ファイルを書き出す際には元ファイルからメタ情報を直接転記するようにしたのだろう。

僕の予想が当たっているかどうかはわからないけど、もし仕様ならばきちんと文書に記して欲しい。挙動がはっきりわからなければ使えない。