最近は仕事で画像をいろいろと弄ることをやっているのですが、スマートフォンで撮影された画像を System.Drawing で処理させると向きがおかしくなるんですよね。原因は Exif の orientation 情報を正しく処理していないからなんですが、これがまあまあ面倒なのでメモしておきます。
Exif では画像の向きを持つタグは 0x0112 という ID が割り振られています。そして値は仕様書を読むと unsighed short みたいなので、そのあたり考慮しないといけない感じです。
http://www.media.mit.edu/pia/Research/deepview/exif.html
とまあ、色々と検索していたら値は 1-8 取ることが分かったので、適当に enum を定義しておきます。
public enum ExifOrientation : ushort { TopLeft = 1, TopRight = 2, BottomRight = 3, BottomLeft = 4, LeftTop = 5, RightTop = 6, RightBottom = 7, LeftBottom = 8 }
ぶっちゃけ、これだけ見てもどうなってるのかさっぱりわかりませんね。
実際に orientation の値からどのように回転させるかですが、酢酸先生が既にまとめてくれていたので、これを元に RotateFlipType にマッピングさせるだけのコードを書きました。
一応 System.Drawing.Bitmap クラスでは PropertyItems という配列に Exif とかのメタデータ周りが格納されるので、それを使って向きを保持するタグを取得できます。GetPropertyItem メソッドに ID を指定すると簡単に取れますが、こいつはタグが見つからなかった時に例外を投げるので LINQ 使ってフィルタリングします。
var bitmap = new Bitmap(@"C:\Users\shibayan\Documents\photo.jpg"); // 0x0112 = Orientation を保持するタグ ID var property = bitmap.PropertyItems.FirstOrDefault(p => p.Id == 0x0112); if (property != null) { var rotation = RotateFlipType.RotateNoneFlipNone; var orientation = (ExifOrientation)BitConverter.ToUInt16(property.Value, 0); // Exif 情報に従って画像を回転させる switch (orientation) { case ExifOrientation.TopLeft: break; case ExifOrientation.TopRight: rotation = RotateFlipType.RotateNoneFlipX; break; case ExifOrientation.BottomRight: rotation = RotateFlipType.Rotate180FlipNone; break; case ExifOrientation.BottomLeft: rotation = RotateFlipType.RotateNoneFlipY; break; case ExifOrientation.LeftTop: rotation = RotateFlipType.Rotate270FlipY; break; case ExifOrientation.RightTop: rotation = RotateFlipType.Rotate90FlipNone; break; case ExifOrientation.RightBottom: rotation = RotateFlipType.Rotate90FlipY; break; case ExifOrientation.LeftBottom: rotation = RotateFlipType.Rotate270FlipNone; break; } bitmap.RotateFlip(rotation); property.Value = BitConverter.GetBytes((ushort)ExifOrientation.TopLeft); bitmap.SetPropertyItem(property); }
やってることは極めてシンプルで、Exif の orientation 情報に従って RotateFlip メソッドを使い画像を回転させます。そして、元々の Exif が残っていると書き出した時にさらに回転してしまうので、TopLeft で上書きをしておきます。
これで正しく向きが処理された Bitmap を取得できるので、書き出すなり描画したりと色々と処理が出来るようになります。