EXIFのOrientationタグの値に基づいて画像を回転し、適切な方向に戻して再保存する方法。 また逆に、Orientationタグを設定して画像を回転した状態で保存する方法。

§1 Orientationタグから画像の方向を復元する

Bitmap.PropertyItemsプロパティを参照すると画像に付与されているEXIFの情報を取得することができる。 ID(PropertyItem.Id)が0x0112のタグがOrientationタグなので、このIDを持つタグの値(PropertyItem.Value)を参照することで画像の方向を知ることができる。 画像の回転にはImage.RotateFlipメソッドを使うことができる。

using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // 元の画像を開く
    using (var origin = new Bitmap("origin.jpg")) {
      var rotation = RotateFlipType.RotateNoneFlipNone;

      // 画像に付与されているEXIF情報を列挙する
      foreach (var item in origin.PropertyItems) {
        if (item.Id != 0x0112)
          continue;

        // IFD0 0x0112; Orientationの値を調べる
        switch (item.Value[0]) {
          case 3:
            // 時計回りに180度回転しているので、180度回転して戻す
            rotation = RotateFlipType.Rotate180FlipNone;
            break;
          case 6:
            // 時計回りに270度回転しているので、90度回転して戻す
            rotation = RotateFlipType.Rotate90FlipNone;
            break;
          case 8:
            // 時計回りに90度回転しているので、270度回転して戻す
            rotation = RotateFlipType.Rotate270FlipNone;
            break;
        }
      }

      // 元の画像を複製する
      using (var rotated = (Bitmap)origin.Clone()) {
        // 指定された角度だけ画像を回転する
        rotated.RotateFlip(rotation);

        // BMP形式で保存
        rotated.Save("rotated.bmp", ImageFormat.Bmp);
      }
    }
  }
}

上記の例では水平方向・垂直方向に反転している画像については考慮していない。 Orientationタグは次のいずれかの値を取りうる。

Orientationタグの値と画像の方向を元に戻すための処理
Orientationタグの値 方向を元に戻すために施す処理
1 不要(回転・反転なし)
2 水平方向に反転
3 時計回りに180度回転
4 垂直方向に反転
5 水平方向に反転+時計回りに270度回転
6 時計回りに90度回転
7 水平方向に反転+時計回りに90度回転
8 時計回りに270度回転

§2 Orientationタグを設定して保存する

逆に、Orientationタグを設定して画像を保存する方法。 EncoderParameterを使ってEncoder.Transformationと以下の値を指定することで、画像を回転(もしくは反転)された状態で表示されるように指定することが出来る。

Encoder.Transformationで指定できる値と画像の回転・反転方向
指定する値 画像の回転・反転方向
EncoderValue.TransformRotate90 時計回りに90度回転するようにする
EncoderValue.TransformRotate180 時計回りに180度回転するようにする
EncoderValue.TransformRotate270 時計回りに270度回転するようにする
EncoderValue.TransformFlipHorizontal 水平方向に反転するようにする
EncoderValue.TransformFlipVertical 垂直方向に反転するようにする
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // 元の画像を開く
    using (var origin = new Bitmap("origin.jpg")) {
      // JPEGのコーデック情報(ImageCodecInfo)を取得
      ImageCodecInfo jpegCodecInfo = null;

      foreach (var codecInfo in ImageCodecInfo.GetImageEncoders()) {
        // FormatIdがJpegのものを探す
        if (codecInfo.FormatID == ImageFormat.Jpeg.Guid) {
          jpegCodecInfo = codecInfo;
          break;
        }
      }

      // エンコーダに渡すパラメータを設定する
      var parameters = new EncoderParameters(1);

      // 画像を270度回転した状態で表示させるようにする
      parameters.Param[0] = new EncoderParameter(Encoder.Transformation, (long)EncoderValue.TransformRotate270);

      // コーデック情報とパラメータを指定して保存
      origin.Save("transformed.jpg", jpegCodecInfo, parameters);
    }
  }
}

なお、この方法を使ってもロスレスでの回転とはならない模様(保存時にJPEGへの再エンコードが行われる)。