C#のBitmapとImageの違い・変換・使い方をサンプルコードで徹底解説
はじめに
C#で画像を扱うときによく登場するのが、BitmapとImageです。どちらも画像を表すクラスなので、はじめて使うと「C#のBitmapとImageは何が違うのか」「ImageからBitmapへ変換するにはどうすればよいのか」「PictureBoxに表示するときはどちらを使えばよいのか」と迷いやすいポイントです。
結論からいうと、Imageは画像全般を扱うための抽象的な基底クラスで、Bitmapはその派生クラスです。つまり、BitmapはImageの一種として扱えます。一方で、ピクセル単位の操作や画像加工を行う場合は、Bitmapを使う場面が多くなります。Microsoftのドキュメントでも、ImageはBitmapやMetafileの派生クラスに機能を提供する抽象基底クラス、Bitmapはピクセルデータで定義された画像を扱うためのクラスとして説明されています。Microsoft Learn+1
この記事では、C#のBitmapとImageの違い、相互変換、基本的な使い方、画像加工のサンプルコード、Disposeやファイルロックなどの注意点まで、実務でつまずきやすい内容をまとめて解説します。
1. C#のBitmapとImageとは?まず押さえる基本
1-1. BitmapとImageの位置づけ
C#で画像処理を行う場合、Windows Formsなどでは主にSystem.Drawing名前空間のImageやBitmapを使います。
大まかな位置づけは次のとおりです。
| クラス | 役割 |
|---|---|
Image | 画像全般を表す抽象的な基底クラス |
Bitmap | ピクセルデータを持つ具体的な画像クラス |
Metafile | ベクター形式のメタファイルを扱うクラス |
Graphics | 画像や画面に文字・図形を描画するためのクラス |
Imageは画像を読み込む、保存する、サイズを取得するなど、画像全般に共通する機能を持っています。一方、Bitmapはピクセルデータを持つ画像として扱えるため、GetPixelやSetPixelを使ったピクセル操作、Graphicsによる描画、リサイズ、形式変換などでよく使われます。
1-2. System.Drawing.Imageとは
System.Drawing.Imageは、画像を表す基底クラスです。Image自体は抽象クラスなので、通常は次のようにnew Image()で直接インスタンス化することはできません。
C#// これはできない
// Image image = new Image();
画像ファイルをImageとして読み込む場合は、Image.FromFileやImage.FromStreamを使います。
C#using System.Drawing;
Image image = Image.FromFile("sample.jpg");
Console.WriteLine(image.Width);
Console.WriteLine(image.Height);
image.Dispose();
Imageには、次のような画像共通のプロパティやメソッドがあります。
| メンバー | 内容 |
|---|---|
Width | 画像の幅 |
Height | 画像の高さ |
Size | 画像サイズ |
RawFormat | 画像形式 |
Save | 画像を保存 |
Clone | 画像を複製 |
Dispose | リソースを解放 |
Image.FromFileは指定したファイルからImageを作成できますが、読み込んだImageを破棄するまでファイルがロックされる点に注意が必要です。Microsoftのドキュメントにも、Imageが破棄されるまでファイルがロックされたままになると記載されています。Microsoft Learn
1-3. System.Drawing.Bitmapとは
System.Drawing.Bitmapは、ピクセルデータを持つ画像を扱うためのクラスです。Bitmapは画像ファイルから作成することも、幅と高さを指定して空の画像として作成することもできます。
C#using System.Drawing;
Bitmap bitmap = new Bitmap("sample.jpg");
Console.WriteLine(bitmap.Width);
Console.WriteLine(bitmap.Height);
Color color = bitmap.GetPixel(0, 0);
Console.WriteLine(color);
bitmap.Dispose();
空のBitmapを作成する例は次のとおりです。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
g.DrawRectangle(Pens.Blue, 10, 10, 100, 50);
bitmap.Save("output.png");
Bitmapはピクセル単位の画像を扱うクラスなので、画像加工や描画処理で特によく使われます。
1-4. BitmapはImageを継承したクラス
BitmapはImageを継承しています。C#の定義で見ると、Bitmapは次のような関係です。
C#public sealed class Bitmap : Image
{
}
Microsoftのドキュメントでも、BitmapはSystem.Drawing.Imageを継承するクラスとして定義されています。Microsoft Learn
そのため、BitmapオブジェクトはImage型の変数に代入できます。
C#Bitmap bitmap = new Bitmap("sample.jpg");
Image image = bitmap; // OK。BitmapはImageとして扱える
逆に、Image型の変数をBitmapとして扱いたい場合は、実体がBitmapであればキャストできます。
C#Image image = new Bitmap("sample.jpg");
Bitmap bitmap = (Bitmap)image; // 実体がBitmapならOK
ただし、すべてのImageが必ずBitmapとは限らないため、安全に扱うならasやisを使う方法もあります。
C#if (image is Bitmap bmp)
{
Console.WriteLine(bmp.GetPixel(0, 0));
}
1-5. 「C# bitmap image」で検索する人が知りたいこと
「C# bitmap image」で検索する人が知りたい内容は、主に次のようなものです。
| 知りたいこと | 代表的な疑問 |
|---|---|
| 違い | BitmapとImageは何が違うのか |
| 変換 | ImageをBitmapに変換するにはどうするのか |
| 表示 | PictureBox.Imageにはどちらを設定するのか |
| 保存 | JPEG、PNG、BMPで保存する方法 |
| 加工 | リサイズ、文字描画、ピクセル操作をしたい |
| エラー対策 | ファイルロックやGDI+エラーを解決したい |
特に実務では、「画像を読み込むだけならImageでよいのか」「加工するならBitmapに変換すべきか」「保存時にエラーが出るのはなぜか」という点でつまずきやすいです。
2. C#のBitmapとImageの違い
2-1. クラス構造の違い
ImageとBitmapの最大の違いは、クラス構造です。
C#Image
├─ Bitmap
└─ Metafile
Imageは画像の共通機能を提供する基底クラスです。一方、BitmapはImageを継承した具体的な画像クラスです。
そのため、次のように考えると理解しやすくなります。
C#// Image型として画像を扱う
Image image = Image.FromFile("sample.jpg");
// Bitmap型として画像を扱う
Bitmap bitmap = new Bitmap("sample.jpg");
Imageは抽象的な型なので、画像を表示するだけ、保存するだけ、サイズを取得するだけなら十分です。しかし、ピクセル操作や画像加工を行うならBitmapを使う方が自然です。
2-2. 使えるメソッド・プロパティの違い
ImageとBitmapでは、共通して使える機能と、Bitmapでしか使えない機能があります。
| 機能 | Image | Bitmap |
|---|---|---|
| 幅・高さの取得 | ○ | ○ |
| 画像保存 | ○ | ○ |
| 複製 | ○ | ○ |
| PictureBoxへの表示 | ○ | ○ |
GetPixel | × | ○ |
SetPixel | × | ○ |
| 空画像の作成 | × | ○ |
| ピクセル形式指定 | × | ○ |
たとえば、Image型のままではGetPixelは使えません。
C#Image image = Image.FromFile("sample.jpg");
// これはできない
// Color color = image.GetPixel(0, 0);
GetPixelを使いたい場合は、Bitmapに変換します。
C#using Image image = Image.FromFile("sample.jpg");
using Bitmap bitmap = new Bitmap(image);
Color color = bitmap.GetPixel(0, 0);
Console.WriteLine(color);
2-3. 画像の読み込み・保存での違い
画像を読み込むだけなら、Image.FromFileでもnew Bitmapでも可能です。
C#Image image = Image.FromFile("sample.jpg");
Bitmap bitmap = new Bitmap("sample.jpg");
Image.FromFileは戻り値がImage型です。画像形式に応じて適切なImage派生オブジェクトが作成されます。
C#using Image image = Image.FromFile("sample.jpg");
image.Save("copy.png", System.Drawing.Imaging.ImageFormat.Png);
new Bitmapは戻り値がBitmap型です。ピクセル操作や加工を前提にするなら、最初からBitmapとして読み込むと扱いやすくなります。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.SetPixel(0, 0, Color.Red);
bitmap.Save("edited.png", System.Drawing.Imaging.ImageFormat.Png);
ただし、どちらの方法でもファイルを開いたまま扱う場合は、Disposeを忘れないことが重要です。
2-4. ピクセル操作が必要な場合の違い
ピクセル操作が必要な場合は、基本的にBitmapを使います。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color original = bitmap.GetPixel(x, y);
int gray = (original.R + original.G + original.B) / 3;
Color newColor = Color.FromArgb(gray, gray, gray);
bitmap.SetPixel(x, y, newColor);
}
}
bitmap.Save("gray.png", System.Drawing.Imaging.ImageFormat.Png);
このようにGetPixelとSetPixelを使えば、画像の色を1ピクセルずつ変更できます。
ただし、GetPixelとSetPixelはわかりやすい反面、大きな画像では遅くなりやすいです。大量のピクセルを高速に処理する場合は、LockBitsを使う方法や、ImageSharp、SkiaSharpなどの画像処理ライブラリを検討します。
2-5. BitmapとImageはどちらを使うべきか
使い分けの目安は次のとおりです。
| やりたいこと | おすすめ |
|---|---|
| 画像を読み込んで表示するだけ | Image |
| PictureBoxに表示する | ImageまたはBitmap |
| 幅・高さを取得する | Image |
| 画像を保存する | ImageまたはBitmap |
| ピクセルを取得・変更する | Bitmap |
| 文字や図形を描画する | Bitmap + Graphics |
| リサイズ・形式変換する | Bitmap |
| Windows Formsで簡単に画像を扱う | System.Drawing.Bitmap |
| LinuxやmacOSを含むサーバー処理 | ImageSharpやSkiaSharpを検討 |
単純な表示や保存ではImage、加工やピクセル操作ではBitmapと考えると実務で迷いにくくなります。
3. BitmapとImageの基本的な使い方
3-1. 画像ファイルをImageとして読み込む
画像ファイルをImageとして読み込むには、Image.FromFileを使います。
C#using System.Drawing;
using Image image = Image.FromFile("sample.jpg");
Console.WriteLine($"幅: {image.Width}");
Console.WriteLine($"高さ: {image.Height}");
Console.WriteLine($"サイズ: {image.Size}");
Image.FromFileは、BMP、GIF、JPEG、PNG、TIFFなどの形式に対応しています。Microsoftのドキュメントでも、GDI+の組み込みエンコーダー・デコーダーがこれらの形式をサポートすることが説明されています。Microsoft Learn
3-2. 画像ファイルをBitmapとして読み込む
画像をBitmapとして読み込むには、Bitmapのコンストラクターを使います。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap("sample.jpg");
Console.WriteLine($"幅: {bitmap.Width}");
Console.WriteLine($"高さ: {bitmap.Height}");
Color color = bitmap.GetPixel(10, 10);
Console.WriteLine($"(10, 10)の色: {color}");
画像加工をする予定がある場合は、最初からBitmapとして読み込む方がシンプルです。
3-3. PictureBoxに画像を表示する
Windows Formsで画像を表示する場合は、PictureBoxコントロールを使うことが多いです。PictureBox.Imageプロパティの型はSystem.Drawing.Imageなので、ImageもBitmapも設定できます。Microsoftのドキュメントでも、PictureBox.ImageはSystem.Drawing.Image型のプロパティとして定義されています。Microsoft Learn
C#using System;
using System.Drawing;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.Image = Image.FromFile("sample.jpg");
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
}
}
Bitmapを設定する場合も同じです。
C#Bitmap bitmap = new Bitmap("sample.jpg");
pictureBox1.Image = bitmap;
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
PictureBoxSizeMode.Zoomを使うと、画像の縦横比を保ったまま表示領域に合わせて拡大・縮小できます。
3-4. 画像サイズ・幅・高さを取得する
ImageでもBitmapでも、幅と高さはWidth、Height、Sizeで取得できます。
C#using Image image = Image.FromFile("sample.jpg");
int width = image.Width;
int height = image.Height;
Size size = image.Size;
Console.WriteLine($"幅: {width}");
Console.WriteLine($"高さ: {height}");
Console.WriteLine($"サイズ: {size}");
画像処理では、リサイズ前の確認、アスペクト比の計算、サムネイル生成などで画像サイズをよく使います。
アスペクト比を保ったまま幅を300pxにする場合は、次のように計算できます。
C#using Image image = Image.FromFile("sample.jpg");
int newWidth = 300;
int newHeight = image.Height * newWidth / image.Width;
Console.WriteLine($"新しいサイズ: {newWidth} x {newHeight}");
3-5. 画像を保存する
画像を保存するには、Saveメソッドを使います。
C#using System.Drawing;
using System.Drawing.Imaging;
using Image image = Image.FromFile("sample.jpg");
image.Save("output.png", ImageFormat.Png);
Bitmapでも同じように保存できます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.Save("output.jpg", ImageFormat.Jpeg);
bitmap.Save("output.png", ImageFormat.Png);
bitmap.Save("output.bmp", ImageFormat.Bmp);
保存形式を指定しない場合、拡張子だけでは期待どおりの形式にならないことがあるため、ImageFormat.PngやImageFormat.Jpegのように明示する方が安全です。
4. ImageからBitmapへ変換する方法
4-1. new Bitmap(Image)で変換する基本コード
ImageからBitmapへ変換する基本は、new Bitmap(image)です。
C#using System.Drawing;
using Image image = Image.FromFile("sample.jpg");
using Bitmap bitmap = new Bitmap(image);
Color color = bitmap.GetPixel(0, 0);
Console.WriteLine(color);
この方法を使うと、Imageとして読み込んだ画像をBitmapとして扱えるようになります。
4-2. Image.FromFileで読み込んだ画像をBitmapに変換する
Image.FromFileの戻り値はImage型です。ピクセル操作をしたい場合は、次のようにBitmapへ変換します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Image image = Image.FromFile("sample.jpg");
using Bitmap bitmap = new Bitmap(image);
bitmap.SetPixel(0, 0, Color.Red);
bitmap.Save("edited.png", ImageFormat.Png);
ただし、このコードではimageが破棄されるまで元ファイルがロックされる可能性があります。ファイルロックを避けたい場合は、読み込み後にコピーを作成して、元のImageをすぐに破棄します。
C#using System.Drawing;
Bitmap bitmap;
using (Image image = Image.FromFile("sample.jpg"))
{
bitmap = new Bitmap(image);
}
// この時点で元のImageはDispose済み
// bitmapは独立した画像として扱える
using (bitmap)
{
bitmap.Save("copy.png");
}
4-3. Streamから読み込んでBitmapに変換する
ファイルロックを避けたい場合は、FileStreamやMemoryStreamを使って画像データを読み込み、Bitmapとしてコピーする方法もあります。
C#using System.Drawing;
using System.IO;
Bitmap LoadBitmapWithoutFileLock(string path)
{
byte[] bytes = File.ReadAllBytes(path);
using MemoryStream ms = new MemoryStream(bytes);
using Image image = Image.FromStream(ms);
return new Bitmap(image);
}
使い方は次のとおりです。
C#using Bitmap bitmap = LoadBitmapWithoutFileLock("sample.jpg");
bitmap.Save("copy.png");
この方法では、画像データを一度バイト配列として読み込んでいるため、元ファイルをロックし続ける問題を避けやすくなります。
4-4. 変換時に注意すべきファイルロック
Image.FromFileで読み込んだ画像は、Disposeされるまでファイルがロックされます。そのため、次のようなコードでは、読み込み元ファイルを削除・上書きしようとしたときにエラーになることがあります。
C#Image image = Image.FromFile("sample.jpg");
// この時点ではsample.jpgがロックされている可能性がある
File.Delete("sample.jpg"); // エラーになる可能性
正しくは、usingでスコープを限定します。
C#using (Image image = Image.FromFile("sample.jpg"))
{
Console.WriteLine(image.Size);
}
// ここではDispose済み
File.Delete("sample.jpg");
また、PictureBox.Imageに直接Image.FromFileの結果を設定した場合も、PictureBoxが画像を保持している間はファイルをロックすることがあります。
C#pictureBox1.Image = Image.FromFile("sample.jpg");
ファイルロックを避けたい場合は、コピーしたBitmapを設定します。
C#using (Image image = Image.FromFile("sample.jpg"))
{
pictureBox1.Image = new Bitmap(image);
}
4-5. ImageをBitmapに変換する実用サンプル
次のサンプルは、画像ファイルを読み込み、Bitmapに変換し、中央に文字を描画してPNGで保存する例です。
C#using System.Drawing;
using System.Drawing.Imaging;
void AddTextToImage(string inputPath, string outputPath, string text)
{
using Image image = Image.FromFile(inputPath);
using Bitmap bitmap = new Bitmap(image);
using Graphics g = Graphics.FromImage(bitmap);
using Font font = new Font("Arial", 32, FontStyle.Bold);
using Brush brush = new SolidBrush(Color.Red);
SizeF textSize = g.MeasureString(text, font);
float x = (bitmap.Width - textSize.Width) / 2;
float y = (bitmap.Height - textSize.Height) / 2;
g.DrawString(text, font, brush, x, y);
bitmap.Save(outputPath, ImageFormat.Png);
}
AddTextToImage("sample.jpg", "output.png", "C# Bitmap Image");
Imageとして読み込んだ後、Bitmapに変換することで、Graphics.FromImageを使って画像に文字を描画できます。
5. BitmapからImageへ変換する方法
5-1. BitmapをImage型として扱う基本
BitmapはImageを継承しているため、特別な変換処理をしなくてもImage型として扱えます。
C#using System.Drawing;
Bitmap bitmap = new Bitmap("sample.jpg");
Image image = bitmap;
この代入はアップキャストです。BitmapはImageの一種なので、安全に代入できます。
5-2. キャストによる変換
BitmapからImageへ明示的にキャストすることもできます。
C#Bitmap bitmap = new Bitmap("sample.jpg");
Image image = (Image)bitmap;
ただし、明示的なキャストを書かなくても代入できるため、通常は次の書き方で十分です。
C#Image image = bitmap;
5-3. メソッドの戻り値をImageにする場合
メソッドの戻り値をImageにして、内部ではBitmapを作成することもできます。
C#using System.Drawing;
Image CreateSampleImage()
{
Bitmap bitmap = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
g.DrawEllipse(Pens.Blue, 50, 50, 200, 100);
return bitmap;
}
使う側ではImageとして受け取れます。
C#using Image image = CreateSampleImage();
image.Save("sample.png");
ただし、この場合も戻り値を受け取った側がDisposeする責任を持ちます。
5-4. PictureBox.ImageへBitmapを設定する
PictureBox.ImageはImage型なので、Bitmapをそのまま設定できます。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
pictureBox1.Image = new Bitmap(bitmap);
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
注意点として、using Bitmap bitmapで作成した画像をそのままPictureBox.Imageに設定し、usingを抜けて破棄してしまうと、表示時に問題が起きることがあります。
避けたいコードは次のような例です。
C#using (Bitmap bitmap = new Bitmap("sample.jpg"))
{
pictureBox1.Image = bitmap;
}
// bitmapはDispose済みなので、PictureBox側で問題になる可能性がある
PictureBoxに表示させ続ける場合は、表示用に別のインスタンスを渡します。
C#using (Bitmap bitmap = new Bitmap("sample.jpg"))
{
pictureBox1.Image = new Bitmap(bitmap);
}
すでにPictureBox.Imageに別の画像が設定されている場合は、古い画像をDisposeしてから差し替えるとメモリリークを防げます。
C#Image? oldImage = pictureBox1.Image;
pictureBox1.Image = new Bitmap("sample.jpg");
oldImage?.Dispose();
5-5. BitmapからImageへ変換する実用サンプル
次のサンプルは、画像をリサイズするメソッドの戻り値をImageにする例です。
C#using System.Drawing;
using System.Drawing.Drawing2D;
Image ResizeImage(string path, int width, int height)
{
using Image original = Image.FromFile(path);
Bitmap resized = new Bitmap(width, height);
using Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(original, 0, 0, width, height);
return resized;
}
使い方は次のとおりです。
C#using Image image = ResizeImage("sample.jpg", 300, 200);
image.Save("resized.png");
戻り値はImageですが、実体はBitmapです。呼び出し側はImageとして保存や表示ができ、必要であればBitmapにキャストしてピクセル操作もできます。
6. Bitmapで画像を加工するサンプルコード
6-1. Bitmapを作成して画像を描画する
Bitmapは、既存ファイルから読み込むだけでなく、空の画像として作成できます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(400, 300);
using Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
g.DrawLine(Pens.Black, 0, 0, 400, 300);
g.DrawRectangle(Pens.Blue, 50, 50, 200, 100);
bitmap.Save("drawing.png", ImageFormat.Png);
このように、BitmapとGraphicsを組み合わせると、C#で画像を生成できます。
6-2. Graphicsを使って文字や図形を描く
画像に文字を描画する例です。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(500, 200);
using Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
using Font font = new Font("Arial", 36, FontStyle.Bold);
using Brush brush = Brushes.DarkBlue;
g.DrawString("C# Bitmap", font, brush, 30, 60);
bitmap.Save("text.png", ImageFormat.Png);
既存画像に文字を重ねる場合は、次のようにします。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("sample.jpg");
using Graphics g = Graphics.FromImage(bitmap);
using Font font = new Font("Arial", 28, FontStyle.Bold);
using Brush brush = new SolidBrush(Color.Red);
g.DrawString("Sample", font, brush, 20, 20);
bitmap.Save("with_text.png", ImageFormat.Png);
画像に透かし文字、日付、ユーザー名、ラベルなどを入れる処理でよく使う書き方です。
6-3. GetPixelとSetPixelでピクセルを操作する
Bitmapでは、GetPixelで色を取得し、SetPixelで色を変更できます。
次の例では、画像をグレースケールに変換します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("sample.jpg");
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color color = bitmap.GetPixel(x, y);
int gray = (color.R + color.G + color.B) / 3;
Color grayColor = Color.FromArgb(color.A, gray, gray, gray);
bitmap.SetPixel(x, y, grayColor);
}
}
bitmap.Save("gray.png", ImageFormat.Png);
色を反転する場合は次のようにします。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color color = bitmap.GetPixel(x, y);
Color inverted = Color.FromArgb(
color.A,
255 - color.R,
255 - color.G,
255 - color.B
);
bitmap.SetPixel(x, y, inverted);
}
}
bitmap.Save("inverted.png", System.Drawing.Imaging.ImageFormat.Png);
小さな画像やサンプルコードではGetPixelとSetPixelで十分ですが、大きな画像を大量に処理する場合は処理速度に注意が必要です。
6-4. 画像をリサイズする
画像をリサイズするには、新しいサイズのBitmapを作成し、Graphics.DrawImageで描画します。
C#using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
void ResizeImage(string inputPath, string outputPath, int width, int height)
{
using Image original = Image.FromFile(inputPath);
using Bitmap resized = new Bitmap(width, height);
using Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(original, 0, 0, width, height);
resized.Save(outputPath, ImageFormat.Png);
}
ResizeImage("sample.jpg", "resized.png", 300, 200);
アスペクト比を維持してリサイズする場合は、元画像の比率を使って高さを計算します。
C#void ResizeKeepAspect(string inputPath, string outputPath, int newWidth)
{
using Image original = Image.FromFile(inputPath);
int newHeight = original.Height * newWidth / original.Width;
using Bitmap resized = new Bitmap(newWidth, newHeight);
using Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(original, 0, 0, newWidth, newHeight);
resized.Save(outputPath, System.Drawing.Imaging.ImageFormat.Png);
}
6-5. JPEG・PNG・BMP形式で保存する
保存形式を指定するには、System.Drawing.Imaging.ImageFormatを使います。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.Save("output.jpg", ImageFormat.Jpeg);
bitmap.Save("output.png", ImageFormat.Png);
bitmap.Save("output.bmp", ImageFormat.Bmp);
bitmap.Save("output.gif", ImageFormat.Gif);
bitmap.Save("output.tiff", ImageFormat.Tiff);
形式ごとの特徴は次のとおりです。
| 形式 | 特徴 |
|---|---|
| JPEG | 写真向き。非可逆圧縮。透過は不可 |
| PNG | 透過対応。画面キャプチャやロゴ向き |
| BMP | 非圧縮に近くファイルサイズが大きい |
| GIF | 色数が少ない画像や簡易アニメーション向き |
| TIFF | 高品質画像やスキャン画像で使われることがある |
実務では、写真はJPEG、透過画像や加工後の画像はPNGで保存することが多いです。
7. Bitmap・Imageを使うときの注意点
7-1. Disposeでリソースを解放する
ImageやBitmapは、内部でGDI+などのネイティブリソースを使用します。そのため、使い終わったらDisposeでリソースを解放する必要があります。
C#Image image = Image.FromFile("sample.jpg");
// 画像処理
image.Dispose();
Disposeを忘れると、ファイルがロックされたままになったり、メモリ使用量が増えたり、GDI+関連のエラーにつながることがあります。
7-2. using文で安全に扱う
Disposeを確実に呼び出すには、using文を使うのが基本です。
C#using Image image = Image.FromFile("sample.jpg");
Console.WriteLine(image.Width);
Console.WriteLine(image.Height);
古い書き方では次のように書けます。
C#using (Image image = Image.FromFile("sample.jpg"))
{
Console.WriteLine(image.Width);
Console.WriteLine(image.Height);
}
Bitmap、Graphics、Font、BrushなどもDisposeが必要なオブジェクトです。
C#using Bitmap bitmap = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(bitmap);
using Font font = new Font("Arial", 20);
using Brush brush = new SolidBrush(Color.Black);
g.DrawString("Hello", font, brush, 10, 10);
bitmap.Save("hello.png");
7-3. Image.FromFileのファイルロック問題
Image.FromFileを使うと、Imageを破棄するまでファイルがロックされます。これはC#でBitmapやImageを使うときに非常によくあるトラブルです。Microsoftのドキュメントにも、Imageが破棄されるまでファイルがロックされることが明記されています。Microsoft Learn
問題になりやすいコードは次の例です。
C#pictureBox1.Image = Image.FromFile("sample.jpg");
// 後でsample.jpgを上書きしようとして失敗することがある
対策としては、読み込んだ画像をコピーしてから元のImageを破棄します。
C#using (Image image = Image.FromFile("sample.jpg"))
{
pictureBox1.Image = new Bitmap(image);
}
また、既存のPictureBox.Imageを差し替える場合は、古い画像を解放します。
C#Image? old = pictureBox1.Image;
using (Image image = Image.FromFile("sample.jpg"))
{
pictureBox1.Image = new Bitmap(image);
}
old?.Dispose();
7-4. GDI+エラーが発生する主な原因
C#で画像保存時に次のようなエラーが出ることがあります。
GDI+ で汎用エラーが発生しました。
主な原因は次のとおりです。
| 原因 | 対処 |
|---|---|
| 保存先フォルダが存在しない | Directory.CreateDirectoryで作成する |
| 保存先に書き込み権限がない | 権限を確認する |
| 読み込み元と同じファイルへ上書きしている | 別名で保存してから置き換える |
| 画像オブジェクトがファイルをロックしている | Disposeする |
| 拡張子と保存形式が不一致 | ImageFormatを明示する |
保存先フォルダが存在しない場合の対策例です。
C#string outputPath = @"C:\temp\images\output.png";
string? directory = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.Save(outputPath, System.Drawing.Imaging.ImageFormat.Png);
同じファイルへ直接上書きしたい場合は、一度別ファイルに保存してから置き換える方法が安全です。
C#string inputPath = "sample.jpg";
string tempPath = "sample_temp.jpg";
using (Bitmap bitmap = new Bitmap(inputPath))
{
// 加工処理
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
File.Delete(inputPath);
File.Move(tempPath, inputPath);
7-5. 大きい画像を扱うときのメモリ対策
大きな画像を扱う場合は、メモリ使用量に注意が必要です。画像は圧縮ファイルとしては小さくても、メモリ上ではピクセルデータとして展開されます。
たとえば、幅4000px、高さ3000pxの画像は1200万ピクセルです。32bit ARGBなら1ピクセルあたり4バイトなので、単純計算で約48MBのメモリを使います。複数枚を同時に処理すると、すぐに大きなメモリ使用量になります。
対策は次のとおりです。
| 対策 | 内容 |
|---|---|
使い終わったらすぐDisposeする | 不要な画像を保持しない |
| 同時に大量の画像を開かない | 1枚ずつ処理する |
| 必要以上にコピーしない | new Bitmapの作りすぎに注意 |
| リサイズしてから処理する | 大きい画像のまま加工しない |
| サーバー処理では別ライブラリも検討 | ImageSharpやSkiaSharpを検討 |
特にWebアプリやバッチ処理で大量の画像を扱う場合は、BitmapやImageを長時間保持しない設計が重要です。
8. Windows Forms・WPF・.NETでの違い
8-1. Windows FormsでBitmapとImageを使う場合
Windows Formsでは、System.Drawing.ImageやSystem.Drawing.Bitmapを自然に使えます。PictureBox、Graphics、Bitmapの組み合わせは、Windows Formsの画像表示・画像加工でよく使われる構成です。
C#using System.Drawing;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
using Image image = Image.FromFile("sample.jpg");
pictureBox1.Image = new Bitmap(image);
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
}
}
PictureBoxは、bitmap、metafile、icon、JPEG、GIF、PNGなどのグラフィックを表示する用途で使われるコントロールです。Microsoftのドキュメントでも、表示したい画像をImageプロパティに設定すると説明されています。Microsoft Learn
8-2. WPFのBitmapImageとの違い
WPFでは、System.Drawing.Bitmapではなく、System.Windows.Media.Imaging.BitmapImageやBitmapSourceを使うのが一般的です。
C#using System;
using System.Windows.Media.Imaging;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri("sample.jpg", UriKind.RelativeOrAbsolute);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
WPFのImageコントロールに表示する場合は、Sourceプロパティへ設定します。
C#myImage.Source = bitmapImage;
ここで注意したいのは、WPFのImageコントロールとSystem.Drawing.Imageは別物だということです。
| 種類 | 名前空間 | 用途 |
|---|---|---|
System.Drawing.Image | System.Drawing | GDI+系の画像クラス |
System.Drawing.Bitmap | System.Drawing | GDI+系のビットマップ画像 |
BitmapImage | System.Windows.Media.Imaging | WPFで画像を読み込むクラス |
Imageコントロール | System.Windows.Controls | WPF画面に画像を表示するUI部品 |
WPFのBitmapImageは、XAMLで画像を読み込む用途に最適化されたBitmapSource派生クラスとして説明されています。Microsoft Learn
8-3. System.Drawing.Commonの注意点
.NET Framework時代は、System.DrawingはWindowsデスクトップアプリで広く使われてきました。しかし、.NET 6以降ではSystem.Drawing.Commonの扱いに注意が必要です。
Microsoftの互換性情報では、System.Drawing.CommonはWindows固有のライブラリとして扱われ、.NET 6ではWindows以外のOSでコンパイル警告が出ること、実行時にPlatformNotSupportedExceptionが発生する場合があることが説明されています。Microsoft Learn
つまり、次のような用途では注意が必要です。
| 用途 | 注意点 |
|---|---|
| Windows Forms | 基本的に使いやすい |
| WPF | WPF標準のBitmapImageなどを優先 |
| ASP.NET Core on Linux | System.Drawing.Commonは避けるのが無難 |
| Docker Linuxコンテナ | ImageSharpやSkiaSharpを検討 |
| クロスプラットフォームアプリ | System.Drawing以外も検討 |
Windows専用のデスクトップアプリであればBitmapやImageは使いやすい選択肢ですが、LinuxサーバーやmacOSを含む環境では別のライブラリを検討する方が安全です。
8-4. .NET 6以降で画像処理を行う場合
.NET 6以降で画像処理を行う場合は、まず実行環境を確認します。
| 実行環境 | 選択肢 |
|---|---|
| Windows Forms | System.Drawing.Bitmapを使いやすい |
| Windows専用ツール | System.Drawingで問題ないことが多い |
| WPF | BitmapImage、BitmapSourceを優先 |
| Linuxサーバー | ImageSharpやSkiaSharpを検討 |
| Docker | ImageSharpやSkiaSharpを検討 |
| MAUIなどクロスプラットフォーム | SkiaSharpなどを検討 |
Windows専用アプリなら、この記事で紹介しているBitmapとImageのコードがそのまま役立ちます。一方で、Webサーバーやクラウド環境で画像処理を行う場合は、System.Drawing.Commonに依存しない設計を考えることが重要です。
8-5. ImageSharpやSkiaSharpを使う選択肢
クロスプラットフォームで画像処理を行う場合は、ImageSharpやSkiaSharpが候補になります。
ImageSharpは、.NET向けの高性能な画像処理ライブラリで、マネージドAPIで画像の読み込み、調査、処理、保存を行えるライブラリとして説明されています。Six Labors
SkiaSharpは、GoogleのSkiaをベースにした.NET向けのクロスプラットフォーム2DグラフィックスAPIで、画像、図形、テキストなどの描画に利用できます。Mono
使い分けの目安は次のとおりです。
| ライブラリ | 向いている用途 |
|---|---|
System.Drawing.Bitmap | Windows Forms、Windows専用ツール |
WPF BitmapImage | WPFアプリでの画像表示 |
| ImageSharp | サーバーサイド画像処理、リサイズ、変換 |
| SkiaSharp | クロスプラットフォーム描画、2Dグラフィックス |
Windows Formsアプリで画像を表示・加工する程度ならBitmapで十分です。しかし、今後LinuxやDockerで動かす可能性があるなら、最初からImageSharpやSkiaSharpを検討すると移行コストを抑えやすくなります。
9. よくあるエラーと解決方法
9-1. 「使用中のためファイルにアクセスできません」の原因と対処
このエラーは、画像ファイルがImageやBitmapによってロックされているときによく発生します。
よくある原因は次のコードです。
C#pictureBox1.Image = Image.FromFile("sample.jpg");
// 後でsample.jpgを上書きしようとして失敗
対処方法は、画像をコピーしてから使うことです。
C#using (Image image = Image.FromFile("sample.jpg"))
{
pictureBox1.Image = new Bitmap(image);
}
さらに、差し替え前の画像を解放します。
C#Image? oldImage = pictureBox1.Image;
using (Image image = Image.FromFile("sample.jpg"))
{
pictureBox1.Image = new Bitmap(image);
}
oldImage?.Dispose();
ファイルを削除・上書きする前に、該当するImageやBitmapがすべてDisposeされているか確認しましょう。
9-2. 「GDI+ で汎用エラーが発生しました」の原因と対処
「GDI+ で汎用エラーが発生しました」は原因がわかりにくいエラーですが、多くの場合は保存処理まわりに原因があります。
代表的な原因は次のとおりです。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
// 読み込み元と同じファイルへ保存しようとしている
bitmap.Save("sample.jpg");
対処方法として、別名で保存します。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.Save("sample_output.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
また、保存先ディレクトリが存在しない場合もエラーになります。
C#string outputPath = @"C:\temp\images\output.png";
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
using Bitmap bitmap = new Bitmap("sample.jpg");
bitmap.Save(outputPath, System.Drawing.Imaging.ImageFormat.Png);
権限不足、ファイルロック、パスの間違い、保存形式の不一致も確認しましょう。
9-3. Bitmap保存時に上書きできない場合の対処
読み込んだ画像を同じパスにそのまま保存しようとすると、ファイルがロックされて上書きできないことがあります。
避けたい例です。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
// 同じファイルへ保存
bitmap.Save("sample.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
安全な方法は、一時ファイルへ保存してから置き換えることです。
C#string path = "sample.jpg";
string tempPath = "sample_temp.jpg";
using (Bitmap bitmap = new Bitmap(path))
{
// 加工処理
bitmap.Save(tempPath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
File.Delete(path);
File.Move(tempPath, path);
また、読み込み時にコピーを作っておく方法もあります。
C#Bitmap bitmap;
using (Image image = Image.FromFile("sample.jpg"))
{
bitmap = new Bitmap(image);
}
using (bitmap)
{
bitmap.Save("sample.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
ただし、同じファイルへ保存する設計はトラブルが起きやすいため、実務では出力先を別ファイルにする方が安全です。
9-4. null参照エラーが出る場合の確認ポイント
PictureBox.Imageや画像パスがnullのとき、NullReferenceExceptionが発生することがあります。
たとえば、次のようなコードです。
C#int width = pictureBox1.Image.Width;
pictureBox1.Imageがnullの場合、このコードはエラーになります。
対処方法は、事前にnullチェックを行うことです。
C#if (pictureBox1.Image != null)
{
int width = pictureBox1.Image.Width;
int height = pictureBox1.Image.Height;
Console.WriteLine($"{width} x {height}");
}
else
{
Console.WriteLine("画像が設定されていません。");
}
ファイルパスが空の場合もチェックしましょう。
C#string path = "";
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
{
using Image image = Image.FromFile(path);
Console.WriteLine(image.Size);
}
else
{
Console.WriteLine("画像ファイルが見つかりません。");
}
9-5. 画像形式が対応していない場合の対処
Image.FromFileで画像を読み込むとき、対応していない形式や壊れた画像を指定すると例外が発生します。
C#try
{
using Image image = Image.FromFile("sample.unknown");
Console.WriteLine(image.Size);
}
catch (OutOfMemoryException)
{
Console.WriteLine("画像形式が対応していない、または画像ファイルが壊れている可能性があります。");
}
catch (FileNotFoundException)
{
Console.WriteLine("ファイルが見つかりません。");
}
Image.FromFileは、画像形式が無効、またはGDI+が対応していないピクセル形式の場合にOutOfMemoryExceptionをスローすることがあります。これは名前からメモリ不足のように見えますが、実際には画像形式の問題で発生する場合もあります。Microsoft Learn
実務では、次の点を確認します。
| 確認項目 | 内容 |
|---|---|
| ファイルが存在するか | File.Existsで確認 |
| 拡張子が正しいか | .jpg、.pngなど |
| ファイルが壊れていないか | 画像ビューアで開けるか確認 |
| 対応形式か | BMP、JPEG、PNGなど |
| 権限があるか | 読み取り権限を確認 |
10. BitmapとImageに関するよくある質問
10-1. BitmapとImageはどちらが高速か
単純に「BitmapとImageのどちらが高速か」とは言い切れません。Imageは基底クラスで、Bitmapは具体的な画像クラスだからです。
画像の幅や高さを取得するだけなら、大きな差を意識する場面はほとんどありません。
C#using Image image = Image.FromFile("sample.jpg");
Console.WriteLine(image.Width);
Console.WriteLine(image.Height);
一方で、ピクセル操作をするならBitmapが必要です。
C#using Bitmap bitmap = new Bitmap("sample.jpg");
Color color = bitmap.GetPixel(0, 0);
ただし、GetPixelやSetPixelは便利ですが高速ではありません。大量のピクセルを処理する場合は、LockBitsや画像処理ライブラリの利用を検討します。
10-2. BitmapとImageは相互変換できるか
相互変換できます。
ImageからBitmapへ変換するには、new Bitmap(image)を使います。
C#using Image image = Image.FromFile("sample.jpg");
using Bitmap bitmap = new Bitmap(image);
BitmapからImageへは、そのまま代入できます。
C#Bitmap bitmap = new Bitmap("sample.jpg");
Image image = bitmap;
理由は、BitmapがImageを継承しているからです。
10-3. Image.FromFileとnew Bitmapの違いは何か
Image.FromFileはImage型として画像を読み込みます。
C#Image image = Image.FromFile("sample.jpg");
new BitmapはBitmap型として画像を読み込みます。
C#Bitmap bitmap = new Bitmap("sample.jpg");
違いをまとめると次のとおりです。
| 書き方 | 戻り値 | 向いている用途 |
|---|---|---|
Image.FromFile | Image | 表示、サイズ取得、保存 |
new Bitmap | Bitmap | ピクセル操作、描画、加工 |
画像を表示するだけならImage.FromFileでも十分です。加工するならnew Bitmap、またはImageからBitmapへの変換を使います。
10-4. Bitmapをコピーするにはどうすればよいか
Bitmapをコピーするには、new Bitmap(original)を使います。
C#using Bitmap original = new Bitmap("sample.jpg");
using Bitmap copy = new Bitmap(original);
copy.Save("copy.png", System.Drawing.Imaging.ImageFormat.Png);
Cloneを使う方法もあります。
C#using Bitmap original = new Bitmap("sample.jpg");
using Bitmap copy = (Bitmap)original.Clone();
copy.Save("copy.png", System.Drawing.Imaging.ImageFormat.Png);
ファイルロックを避けたい場合にも、コピーはよく使われます。
C#Bitmap bitmap;
using (Image image = Image.FromFile("sample.jpg"))
{
bitmap = new Bitmap(image);
}
using (bitmap)
{
bitmap.Save("copy.png");
}
10-5. C#で画像処理するならBitmapで十分か
Windows FormsやWindows専用のデスクトップアプリで、画像表示、リサイズ、文字描画、簡単な加工を行う程度なら、Bitmapで十分なケースは多いです。
ただし、次のような場合は別の選択肢も検討した方がよいです。
| 条件 | 検討したい選択肢 |
|---|---|
| Linuxサーバーで画像処理する | ImageSharp、SkiaSharp |
| Dockerで動かす | ImageSharp、SkiaSharp |
| 大量の画像を高速処理する | ImageSharp、SkiaSharp、専用処理 |
| WPFアプリで表示中心 | BitmapImage、BitmapSource |
| Windows Forms中心 | Bitmap、Image |
特に.NET 6以降でWindows以外の環境に対応する場合は、System.Drawing.Commonのサポート範囲に注意が必要です。Windows専用の画像処理ならBitmapは扱いやすいですが、クロスプラットフォーム前提ならImageSharpやSkiaSharpを検討するのが現実的です。Microsoft Learn+2
Six Labors+2
まとめ
C#のBitmapとImageは似ていますが、役割は異なります。Imageは画像全般を扱う基底クラスで、Bitmapはピクセルデータを持つ具体的な画像クラスです。BitmapはImageを継承しているため、BitmapをImageとして扱うことは簡単にできます。
基本的な使い分けは、画像の表示やサイズ取得だけならImage、ピクセル操作や画像加工をするならBitmapです。
C#// Imageとして読み込む
using Image image = Image.FromFile("sample.jpg");
// Bitmapとして読み込む
using Bitmap bitmap = new Bitmap("sample.jpg");
// ImageからBitmapへ変換
using Bitmap converted = new Bitmap(image);
// BitmapはImageとして扱える
Image image2 = bitmap;
実務で特に重要なのは、Disposeとファイルロックです。Image.FromFileで読み込んだ画像は、破棄されるまでファイルがロックされるため、usingを使って確実にリソースを解放しましょう。
C#using (Image image = Image.FromFile("sample.jpg"))
{
using Bitmap bitmap = new Bitmap(image);
bitmap.Save("output.png", System.Drawing.Imaging.ImageFormat.Png);
}
また、Windows FormsではBitmapとImageを扱いやすい一方で、.NET 6以降のクロスプラットフォーム環境ではSystem.Drawing.Commonの扱いに注意が必要です。Windows専用アプリならBitmapとImageで十分な場面が多いですが、Linux、Docker、クラウド、Webサーバーで画像処理を行う場合は、ImageSharpやSkiaSharpなどのライブラリも選択肢に入れるとよいでしょう。

