Edited at

C# WPF Exifの回転情報を取得して画像を表示する

More than 1 year has passed since last update.


はじめに

WPF で画像ビューワを作っていて、タイトルの通りのところでつまったので、まとめておきます。


画像ファイルを読み取って Image コントロールに表示する。

Image という名前の Image コントロールが Form に配置されていて、 path 変数に画像のファイルパスが入っている状態だとして

  Image.Source =  new BitmapImage(new Uri(path));

これで表示することができます。簡単です。

ですが、罠がありまして、ファイル閲覧中はファイル削除できないとか、そういうロックされる動作になります。

画像ビューアとして使い勝手悪いので、改善します。


画像ファイルのロックを解除する。

ここに書いてあるとおりにやればうまくいきます。

ファイルロックのかからない画像表示方法

http://neareal.net/index.php?Programming%2F.NetFramework%2FWPF%2FWriteableBitmap%2FLoadReleaseableBitmapImage


Exif の回転情報を読み取る

英語ですがこちらに載っています。

JPEG "Orientation" from EXIF for automatic rotation

https://social.msdn.microsoft.com/Forums/en-US/f2e4f23c-f6fd-4474-b47d-b4280f094c8f/jpeg-orientation-from-exif-for-automatic-rotation?forum=wpf

10サイトくらい調べましたが、正確に書かれていて動作するのは、ここの情報くらいです。


画像に回転や左右上下反転の加工をする

方法 : BitmapImage に変換を適用する | Microsoft Docs

https://docs.microsoft.com/ja-jp/dotnet/framework/wpf/graphics-multimedia/how-to-apply-a-transform-to-a-bitmapimage

このあたりを参考にしました。


ソース

全体的に、再利用しやすいようにライブラリとして、ラッピングしました。


//----------------------------------------
//BitmapSorce
//----------------------------------------

//単純なImageコントロールへの画像のセット
BitmapSource GetImageSourceLock(string path,
Rotation rotation = Rotation.Rotate0) {
var bmpImage = new BitmapImage(new Uri(path));
bmpImage.Rotation = rotation;
return bmpImage;
}

//画像ファイルロックをしないコントロールへの画像のセット
BitmapSource GetImageSource(string path,
Rotation rotation = Rotation.Rotate0) {
var bmpImage = new BitmapImage();
FileStream stream = File.OpenRead(path);
bmpImage.BeginInit();
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.StreamSource = stream;
bmpImage.Rotation = rotation;
bmpImage.EndInit();
stream.Close();
return bmpImage;
}

//画像ファイルロックをしないコントロールへの画像のセット
//Exif情報を読み取って自動で回転や左右上下反転を行う
BitmapSource GetImageSourceAutoExifOrientate(string path) {
var bmpImage = new BitmapImage();
FileStream stream = File.OpenRead(path);
bmpImage.BeginInit();
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.StreamSource = stream;

var metaData = (BitmapFrame.Create(stream).Metadata) as BitmapMetadata;

stream.Position = 0;
bmpImage.EndInit();
stream.Close();

string query = "/app1/ifd/exif:{uint=274}";
if (!metaData.ContainsQuery(query)) {
return bmpImage;
}

switch (Convert.ToUInt32(metaData.GetQuery(query))) {
case 1:
return bmpImage;
case 3:
return TransformBitmap(
bmpImage,
new RotateTransform(180));
case 6:
return TransformBitmap(
bmpImage,
new RotateTransform(90));
case 8:
return TransformBitmap(
bmpImage,
new RotateTransform(270));
case 2:
return TransformBitmap(
bmpImage,
new ScaleTransform(-1, 1, 0, 0));
case 4:
return TransformBitmap(
bmpImage,
new ScaleTransform(1, -1, 0, 0));
case 5:
return TransformBitmap(
TransformBitmap(
bmpImage,
new RotateTransform(90)
),
new ScaleTransform(-1, 1, 0, 0));
case 7:
return TransformBitmap(
TransformBitmap(
bmpImage,
new RotateTransform(270)
),
new ScaleTransform(-1, 1, 0, 0));
}
return bmpImage;
}

BitmapSource TransformBitmap(BitmapSource source, Transform transform) {
var result = new TransformedBitmap();
result.BeginInit();
result.Source = source;
result.Transform = transform;
result.EndInit();
return result;
}

void ShowImage(string path) {
if (File.Exists(path)) {
Image.Source = GetImageSourceAutoExifOrientate(path);
} else {
Image.Source = null;
}
}

ShowImage が画面表示する部分です。次のようになっています。

  Image.Source = GetImageSourceAutoExifOrientate(path);

ここを、下記のようにしても動作させることができます。

  Image.Source = GetImageSourceLock(path);

  Image.Source = GetImageSource(path);

もちろん、Exif 情報は読み取りません。外部から回転方向を指定できますが、あんまり意味がないかもしれません。実装上、作りやすかったからつけただけの機能です。