Bitmapクラスを用いることでビットマップ・JPEG・GIF・PNGなど様々なフォーマットの画像を読み込み操作することができます。 さらに、 BitmapクラスとGraphicsクラスを用いて描画処理などを施したイメージをファイルに保存することもできます。 このとき、画像フォーマットや品質などを指定して保存することが出来ます。

フォーマットを指定して画像を保存する

Bitmap.SaveメソッドImageFormatを指定すると、JPEGやPNGといった画像フォーマットを指定して保存することが出来ます。

フォーマットを指定して画像を保存する
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // ファイルからBMP形式の画像を読み込む
    using (var image = Bitmap.FromFile("source.bmp")) {
      // PNGで保存
      image.Save("imageformat.png", ImageFormat.Png);

      // JPEGで保存
      image.Save("imageformat.jpg", ImageFormat.Jpeg);

      // GIFで保存
      image.Save("imageformat.gif", ImageFormat.Gif);

      // BMPで保存
      image.Save("imageformat.bmp", ImageFormat.Bmp);
      image.Save("imageformat-default.bmp");
    }
  }
}
フォーマットを指定して画像を保存する
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    ' ファイルからBMP形式の画像を読み込む
    Using image As Image = Bitmap.FromFile("source.bmp")
      ' PNGで保存
      image.Save("imageformat.png", ImageFormat.Png)

      ' JPEGで保存
      image.Save("imageformat.jpg", ImageFormat.Jpeg)

      ' GIFで保存
      image.Save("imageformat.gif", ImageFormat.Gif)

      ' BMPで保存
      image.Save("imageformat.bmp", ImageFormat.Bmp)
      image.Save("imageformat-default.bmp")
    End Using
  End Sub
End Class
入力画像
source.bmp
実行結果
ImageFormat.Png(PNG形式)
ImageFormat.Jpeg(JPEG形式)
ImageFormat.Gif(GIF形式)
ImageFormat.Bmp(BMP形式)
ImageFormat指定なし(BMP形式)

上記の例のように、Bitmap.Saveメソッドの第一引数に出力するファイル名、第二引数に出力する画像のフォーマットを指定します。 画像フォーマットにはここで使用したもの以外にもいくつかあります(アイコン形式やWMF形式など)。 なお、第二引数を指定しない場合、つまり画像フォーマットを指定しないで保存した場合はファイル名の拡張子に関わらずビットマップ形式で保存されます。

パラメータを指定して画像を保存する

画像を保存する際にフォーマットを指定するほかにも、JPEGでは品質(画質)を設定して出力したいという場合があります。 そういった場合には、画像をエンコードする際のパラメータを渡すためにEncoderParameterクラスを使います。

品質(画質)の指定

EncoderParameterクラスを使う例として、まずは品質を指定してJPEGを保存してみます。 次の例では、EncoderParameterクラスを使って品質パラメータを指定してJPEGを保存しています。

品質を指定して画像を保存する
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // ファイルからBMP形式の画像を読み込む
    using (var image = Bitmap.FromFile("source.bmp")) {
      // 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);

      // 品質のパラメータに値25を設定する (品質25で保存する)
      parameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)25);

      // コーデック情報とパラメータを指定して保存
      image.Save("quality-25.jpg", jpegCodecInfo, parameters);
    }
  }
}
品質を指定して画像を保存する
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    ' ファイルからBMP形式の画像を読み込む
    Using image As Image = Bitmap.FromFile("source.bmp")
      ' 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)

      ' 品質のパラメータに値25を設定する (品質25で保存する)
      parameters.Param(0) = New EncoderParameter(Encoder.Quality, CLng(25))

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

Bitmap.Saveメソッドには引数でEncoderParameterを渡すことが出来るバージョンが用意されていて、それを使うことで品質を指定して保存することが出来るようになります。 ただその場合は、画像のフォーマットを指定するのにImageFormatではなくImageCodecInfoを指定する必要があります。 そのため、まずは保存したい画像フォーマットに該当するImageCodecInfoを取得します。

次にEncoderParameterとそれを格納するためのEncoderParametersを作成します。 EncoderParametersのコンストラクタでは、指定したいパラメータの数を指定します。 ここでは品質だけを指定できればよいので1を指定します。 そして、EncoderParameters.ParamプロパティにEncoderParameterを設定していきます。 品質を設定するには、EncoderParameterのコンストラクタでEncoder.Qualityと品質の数値を指定します。 品質は0から100の範囲の数値を指定することが出来ます。 なお、品質の数値を指定する際、値はInt64(long/Long)型でなければならない点に注意してください。 Int32(int/Integer)型で指定した場合はArgumentExceptionがスローされます。

最後に、取得しておいたImageCodecInfoとパラメータを格納したEncoderParametersをBitmap.Saveメソッドに渡して呼び出すことで、指定した品質で画像が保存されることになります。

実行結果
source.bmp(入力画像)
出力される画像

コーデック情報の取得方法

上記の例では目的のImageCodecInfoを取得するためにFormatIDプロパティを参照していましたが、MimeTypeプロパティと比較することで、画像のMIMEタイプからも目的のImageCodecInfoを取得することが出来ます。

以下の例では、MIMEタイプが"image/jpeg"のImageCodecInfoを取得し、そのFormatIDがImageFormat.Jpeg.Guidと同じとなっているか調べて目的のものが取得できたか確認しています。

MIMEタイプからImageCodecInfoを取得する
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    ImageCodecInfo jpegCodecInfo = null;

    foreach (var codecInfo in ImageCodecInfo.GetImageEncoders()) {
      // MimeTypeが"image/jpeg"のものを探す
      if (codecInfo.MimeType == "image/jpeg") {
        jpegCodecInfo = codecInfo;
        break;
      }
    }

    Console.WriteLine(jpegCodecInfo.FormatID);
    Console.WriteLine(ImageFormat.Jpeg.Guid);
  }
}
MIMEタイプからImageCodecInfoを取得する
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    Dim jpegCodecInfo As ImageCodecInfo = Nothing

    For Each codecInfo As ImageCodecInfo In ImageCodecInfo.GetImageEncoders()
      ' MimeTypeが"image/jpeg"のものを探す
      If codecInfo.MimeType = "image/jpeg" Then
        jpegCodecInfo = codecInfo
        Exit For
      End If
    Next

    Console.WriteLine(jpegCodecInfo.FormatID)
    Console.WriteLine(ImageFormat.Jpeg.Guid)
  End Sub
End Class
実行結果
b96b3cae-0728-11d3-9d7b-0000f81ef32e
b96b3cae-0728-11d3-9d7b-0000f81ef32e

JPEGだけでなく、PNGなら"image/png"、GIFなら"image/gif"、ビットマップなら"image/bitmap"などとすることで同様に取得できます。

プログレッシブ/インターレースの指定

Encoder.RenderMethodEncoderValue.RenderProgressive/EncoderValue.RenderNonProgressiveのパラメータを指定することで、プログレッシブ/非プログレッシブの指定をすることが出来るようになっています。 ただし、ドキュメント上で「GDI+ バージョン 1.0 では使用されません。」と記載されていて、実際には機能せず、指定された内容に関わらずベースラインJPEGが出力されるようです。 これはMono+libgdiplusでも同様に機能しないようです。

また、Encoder.ScanMethodEncoderValue.ScanMethodInterlaced/EncoderValue.ScanMethodNonInterlacedのパラメータで、インターレース/非インターレースの指定が出来料になっていますが、これも同様にJPEGでは機能しないようです。 ただ、インターレースPNGを出力することは出来るようです。

以下のコードは意図した通りには機能しませんが、パラメータを指定する例の一つとして掲載しておきます。

レンダリング方式・スキャン方式を指定して画像を保存する
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // ファイルからBMP形式の画像を読み込む
    using (var image = Bitmap.FromFile("source.bmp")) {
      // JPEGのコーデック情報を取得
      ImageCodecInfo jpegCodecInfo = null;

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

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

      // RenderMethodにRenderProgressive(プログレッシブ)を指定する
      parameters.Param[0] = new EncoderParameter(Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
      // RenderMethodにScanMethodInterlaced(インターレース)を指定する
      parameters.Param[1] = new EncoderParameter(Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
      // 品質に80を指定する
      parameters.Param[2] = new EncoderParameter(Encoder.Quality, (long)80);

      // パラメータとコーデック情報を指定して保存
      image.Save("test.jpg", jpegCodecInfo, parameters);
    }
  }
}
レンダリング方式・スキャン方式を指定して画像を保存する
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    ' ファイルからBMP形式の画像を読み込む
    Using image As Image = Bitmap.FromFile("source.bmp")
      ' 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(3)

      ' RenderMethodにRenderProgressive(プログレッシブ)を指定する
      parameters.Param(0) = New EncoderParameter(Encoder.RenderMethod, CInt(EncoderValue.RenderProgressive));
      ' RenderMethodにScanMethodInterlaced(インターレース)を指定する
      parameters.Param(1) = New EncoderParameter(Encoder.ScanMethod, CInt(EncoderValue.ScanMethodInterlaced));
      ' 品質に80を指定する
      parameters.Param(2) = new EncoderParameter(Encoder.Quality, CLng(80));

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

画像の回転情報の指定

Encoder.TransformationEncoderValue.TransformRotate90などのパラメータを指定することで、JPEGファイルで使われる画像の回転情報を含めて保存することが出来るようになっています。 これを指定することで、画像を表示する際に90度や270度回転させて正しい方向に向けるといったことが出来ます。 詳しくはEXIFの情報に基づいて画像を回転するで解説しています。

その他のパラメータ

この他にもEncoderクラスEncoderValue列挙体で指定できるパラメータには様々なものがありますが、画像フォーマットやパラメータの値によって動作する/しないものがまちまちになっています。 どのような組み合わせの場合に動作するか、どういった結果になるかといった点についてはKING OF SQUID - JAPANESE SECTION グラフィックス TIPS § イメージエンコーディング §が非常に詳しくまとまっています。

画像のフォーマットを判定する

ImageCodecInfoではエンコーダだけでなく、デコーダに関する情報も取得することが出来ます。 GetImageDecodersメソッドではサポートされているデコーダを取得することが出来ます。

また、Bitmapクラスの基底クラスであるImageクラスにはRawFormatプロパティがあり、これを参照することで元になった画像のフォーマットを知ることが出来ます。

以下の例では、RawFormatプロパティを参照して読み込んだ画像ファイルのフォーマットを判定しています。 なお、この方法ではいったん画像を全て読み込んでから判定を行うため、効率的ではありません。 あくまで例であることを書き添えておきます。

画像のフォーマットを判定する
using System;
using System.Drawing;
using System.Drawing.Imaging;

class Sample {
  static void Main()
  {
    // 判定する画像を読み込む
    using (var image = Bitmap.FromFile("test.jpg")) {
      ImageCodecInfo decoderInfo = null;

      foreach (var codecInfo in ImageCodecInfo.GetImageDecoders()) {
        // 一致するFormatIDを持つImageCodecInfoを探す
        if (image.RawFormat.Guid == codecInfo.FormatID) {
          decoderInfo = codecInfo;
          break;
        }
      }

      Console.WriteLine(decoderInfo.FormatDescription);
      Console.WriteLine(decoderInfo.CodecName);
    }
  }
}
画像のフォーマットを判定する
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging

Class Sample
  Shared Sub Main()
    ' 判定する画像を読み込む
    Using image As Image = Bitmap.FromFile("test.jpg")
      Dim decoderInfo As ImageCodecInfo = Nothing

      For Each codecInfo As ImageCodecInfo In ImageCodecInfo.GetImageDecoders()
        ' 一致するFormatIDを持つImageCodecInfoを探す
        If image.RawFormat.Guid = codecInfo.FormatID Then
          decoderInfo = codecInfo
          Exit For
        End If
      Next

      Console.WriteLine(decoderInfo.FormatDescription)
      Console.WriteLine(decoderInfo.CodecName)
    End Using
  End Sub
End Class
JPEGファイルの場合の実行結果
JPEG
Built-in JPEG Codec
PNGファイルの場合の実行結果
PNG
Built-in PNG Codec