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

はじめに

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.