Bitmapクラスを用いることでビットマップ・JPEG・GIF・PNGなど様々なフォーマットの画像を読み込み操作することができます。 さらに、 BitmapクラスとGraphicsクラスを用いて描画処理などを施したイメージをファイルに保存することもできます。 このとき、画像フォーマットや品質などを指定して保存することが出来ます。
フォーマットを指定して画像を保存する
Bitmap.SaveメソッドでImageFormatを指定すると、JPEGやPNGといった画像フォーマットを指定して保存することが出来ます。
上記の例のように、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メソッドに渡して呼び出すことで、指定した品質で画像が保存されることになります。
コーデック情報の取得方法
上記の例では目的のImageCodecInfoを取得するためにFormatIDプロパティを参照していましたが、MimeTypeプロパティと比較することで、画像のMIMEタイプからも目的のImageCodecInfoを取得することが出来ます。
以下の例では、MIMEタイプが"image/jpeg"のImageCodecInfoを取得し、そのFormatIDがImageFormat.Jpeg.Guidと同じとなっているか調べて目的のものが取得できたか確認しています。
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);
}
}
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.RenderMethodとEncoderValue.RenderProgressive/EncoderValue.RenderNonProgressiveのパラメータを指定することで、プログレッシブ/非プログレッシブの指定をすることが出来るようになっています。 ただし、ドキュメント上で「GDI+ バージョン 1.0 では使用されません。」と記載されていて、実際には機能せず、指定された内容に関わらずベースラインJPEGが出力されるようです。 これはMono+libgdiplusでも同様に機能しないようです。
また、Encoder.ScanMethodとEncoderValue.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.TransformationとEncoderValue.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 Built-in JPEG Codec
PNG Built-in PNG Codec