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

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);
      }
    }
  }
}
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class MainClass
  Shared Sub Main()
    ' 元の画像を開く
    Using origin As New Bitmap("origin.jpg")
      Dim rotation As RotateFlipType = RotateFlipType.RotateNoneFlipNone

      ' 画像に付与されているEXIF情報を列挙する
      For Each item As PropertyItem In origin.PropertyItems
        If item.Id <> &H0112 Then Continue For

        ' IFD0 0x0112; Orientationの値を調べる
        Select Case item.Value(0)
          Case 3
            ' 時計回りに180度回転しているので、180度回転して戻す
            rotation = RotateFlipType.Rotate180FlipNone
          Case 6
            ' 時計回りに270度回転しているので、90度回転して戻す
            rotation = RotateFlipType.Rotate90FlipNone
          Case 8
            ' 時計回りに90度回転しているので、270度回転して戻す
            rotation = RotateFlipType.Rotate270FlipNone
        End Select
      Next

      ' 元の画像を複製する
      Using rotated As Bitmap = DirectCast(origin.Clone(), Bitmap)
        ' 指定された角度だけ画像を回転する
        rotated.RotateFlip(rotation)

        ' BMP形式で保存
        rotated.Save("rotated.bmp", ImageFormat.Bmp)
      End Using
    End Using
  End Sub
End Class

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

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

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);
    }
  }
}
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    ' 元の画像を開く
    Using origin As Image = Bitmap.FromFile("origin.jpg")
      ' JPEGのコーデック情報(ImageCodecInfo)を取得
      Dim jpegCodecInfo As ImageCodecInfo = Nothing

      For Each codecInfo As ImageCodecInfo In ImageCodecInfo.GetImageEncoders()
        ' FormatIdがJpegのものを探す
        If codecInfo.FormatID = ImageFormat.Jpeg.Guid Then
          jpegCodecInfo = codecInfo
          Exit For
        End If
      Next

      ' エンコーダに渡すパラメータを設定する
      Dim parameters As New EncoderParameters(1)

      ' 画像を270度回転した状態で表示させるようにする
      parameters.Param(0) = New EncoderParameter(Encoder.Transformation, CLng(EncoderValue.TransformRotate270))

      ' コーデック情報とパラメータを指定して保存
      origin.Save("transformed.jpg", jpegCodecInfo, parameters)
    End Using
  End Sub
End Class

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