C#のBitmapとImageの違い・変換・使い方をサンプルコードで徹底解説

はじめに

C#で画像を扱うときによく登場するのが、BitmapImageです。どちらも画像を表すクラスなので、はじめて使うと「C#のBitmapとImageは何が違うのか」「ImageからBitmapへ変換するにはどうすればよいのか」「PictureBoxに表示するときはどちらを使えばよいのか」と迷いやすいポイントです。

結論からいうと、Imageは画像全般を扱うための抽象的な基底クラスで、Bitmapはその派生クラスです。つまり、BitmapImageの一種として扱えます。一方で、ピクセル単位の操作や画像加工を行う場合は、Bitmapを使う場面が多くなります。Microsoftのドキュメントでも、ImageBitmapMetafileの派生クラスに機能を提供する抽象基底クラス、Bitmapはピクセルデータで定義された画像を扱うためのクラスとして説明されています。Microsoft Learn+1

この記事では、C#のBitmapImageの違い、相互変換、基本的な使い方、画像加工のサンプルコード、Disposeやファイルロックなどの注意点まで、実務でつまずきやすい内容をまとめて解説します。

1. C#のBitmapとImageとは?まず押さえる基本

1-1. BitmapとImageの位置づけ

C#で画像処理を行う場合、Windows Formsなどでは主にSystem.Drawing名前空間のImageBitmapを使います。

大まかな位置づけは次のとおりです。

クラス役割
Image画像全般を表す抽象的な基底クラス
Bitmapピクセルデータを持つ具体的な画像クラス
Metafileベクター形式のメタファイルを扱うクラス
Graphics画像や画面に文字・図形を描画するためのクラス

Imageは画像を読み込む、保存する、サイズを取得するなど、画像全般に共通する機能を持っています。一方、Bitmapはピクセルデータを持つ画像として扱えるため、GetPixelSetPixelを使ったピクセル操作、Graphicsによる描画、リサイズ、形式変換などでよく使われます。

1-2. System.Drawing.Imageとは

System.Drawing.Imageは、画像を表す基底クラスです。Image自体は抽象クラスなので、通常は次のようにnew Image()で直接インスタンス化することはできません。

C#
// これはできない
// Image image = new Image();

画像ファイルをImageとして読み込む場合は、Image.FromFileImage.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を継承したクラス

BitmapImageを継承しています。C#の定義で見ると、Bitmapは次のような関係です。

C#
public sealed class Bitmap : Image
{
}

Microsoftのドキュメントでも、BitmapSystem.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とは限らないため、安全に扱うならasisを使う方法もあります。

C#
if (image is Bitmap bmp)
{
Console.WriteLine(bmp.GetPixel(0, 0));
}

1-5. 「C# bitmap image」で検索する人が知りたいこと

「C# bitmap image」で検索する人が知りたい内容は、主に次のようなものです。

知りたいこと代表的な疑問
違いBitmapImageは何が違うのか
変換ImageBitmapに変換するにはどうするのか
表示PictureBox.Imageにはどちらを設定するのか
保存JPEG、PNG、BMPで保存する方法
加工リサイズ、文字描画、ピクセル操作をしたい
エラー対策ファイルロックやGDI+エラーを解決したい

特に実務では、「画像を読み込むだけならImageでよいのか」「加工するならBitmapに変換すべきか」「保存時にエラーが出るのはなぜか」という点でつまずきやすいです。

2. C#のBitmapとImageの違い

2-1. クラス構造の違い

ImageBitmapの最大の違いは、クラス構造です。

C#
Image
├─ Bitmap
└─ Metafile

Imageは画像の共通機能を提供する基底クラスです。一方、BitmapImageを継承した具体的な画像クラスです。

そのため、次のように考えると理解しやすくなります。

C#
// Image型として画像を扱う
Image image = Image.FromFile("sample.jpg");

// Bitmap型として画像を扱う
Bitmap bitmap = new Bitmap("sample.jpg");

Imageは抽象的な型なので、画像を表示するだけ、保存するだけ、サイズを取得するだけなら十分です。しかし、ピクセル操作や画像加工を行うならBitmapを使う方が自然です。

2-2. 使えるメソッド・プロパティの違い

ImageBitmapでは、共通して使える機能と、Bitmapでしか使えない機能があります。

機能ImageBitmap
幅・高さの取得
画像保存
複製
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);

このようにGetPixelSetPixelを使えば、画像の色を1ピクセルずつ変更できます。

ただし、GetPixelSetPixelはわかりやすい反面、大きな画像では遅くなりやすいです。大量のピクセルを高速に処理する場合は、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なので、ImageBitmapも設定できます。Microsoftのドキュメントでも、PictureBox.ImageSystem.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でも、幅と高さはWidthHeightSizeで取得できます。

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.PngImageFormat.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に変換する

ファイルロックを避けたい場合は、FileStreamMemoryStreamを使って画像データを読み込み、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型として扱う基本

BitmapImageを継承しているため、特別な変換処理をしなくてもImage型として扱えます。

C#
using System.Drawing;

Bitmap bitmap = new Bitmap("sample.jpg");

Image image = bitmap;

この代入はアップキャストです。BitmapImageの一種なので、安全に代入できます。

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.ImageImage型なので、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);

このように、BitmapGraphicsを組み合わせると、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);

小さな画像やサンプルコードではGetPixelSetPixelで十分ですが、大きな画像を大量に処理する場合は処理速度に注意が必要です。

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でリソースを解放する

ImageBitmapは、内部で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);
}

BitmapGraphicsFontBrushなども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#でBitmapImageを使うときに非常によくあるトラブルです。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アプリやバッチ処理で大量の画像を扱う場合は、BitmapImageを長時間保持しない設計が重要です。

8. Windows Forms・WPF・.NETでの違い

8-1. Windows FormsでBitmapとImageを使う場合

Windows Formsでは、System.Drawing.ImageSystem.Drawing.Bitmapを自然に使えます。PictureBoxGraphicsBitmapの組み合わせは、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.BitmapImageBitmapSourceを使うのが一般的です。

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.ImageSystem.DrawingGDI+系の画像クラス
System.Drawing.BitmapSystem.DrawingGDI+系のビットマップ画像
BitmapImageSystem.Windows.Media.ImagingWPFで画像を読み込むクラス
ImageコントロールSystem.Windows.ControlsWPF画面に画像を表示する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基本的に使いやすい
WPFWPF標準のBitmapImageなどを優先
ASP.NET Core on LinuxSystem.Drawing.Commonは避けるのが無難
Docker LinuxコンテナImageSharpやSkiaSharpを検討
クロスプラットフォームアプリSystem.Drawing以外も検討

Windows専用のデスクトップアプリであればBitmapImageは使いやすい選択肢ですが、LinuxサーバーやmacOSを含む環境では別のライブラリを検討する方が安全です。

8-4. .NET 6以降で画像処理を行う場合

.NET 6以降で画像処理を行う場合は、まず実行環境を確認します。

実行環境選択肢
Windows FormsSystem.Drawing.Bitmapを使いやすい
Windows専用ツールSystem.Drawingで問題ないことが多い
WPFBitmapImageBitmapSourceを優先
LinuxサーバーImageSharpやSkiaSharpを検討
DockerImageSharpやSkiaSharpを検討
MAUIなどクロスプラットフォームSkiaSharpなどを検討

Windows専用アプリなら、この記事で紹介しているBitmapImageのコードがそのまま役立ちます。一方で、Webサーバーやクラウド環境で画像処理を行う場合は、System.Drawing.Commonに依存しない設計を考えることが重要です。

8-5. ImageSharpやSkiaSharpを使う選択肢

クロスプラットフォームで画像処理を行う場合は、ImageSharpやSkiaSharpが候補になります。

ImageSharpは、.NET向けの高性能な画像処理ライブラリで、マネージドAPIで画像の読み込み、調査、処理、保存を行えるライブラリとして説明されています。Six Labors

SkiaSharpは、GoogleのSkiaをベースにした.NET向けのクロスプラットフォーム2DグラフィックスAPIで、画像、図形、テキストなどの描画に利用できます。Mono

使い分けの目安は次のとおりです。

ライブラリ向いている用途
System.Drawing.BitmapWindows Forms、Windows専用ツール
WPF BitmapImageWPFアプリでの画像表示
ImageSharpサーバーサイド画像処理、リサイズ、変換
SkiaSharpクロスプラットフォーム描画、2Dグラフィックス

Windows Formsアプリで画像を表示・加工する程度ならBitmapで十分です。しかし、今後LinuxやDockerで動かす可能性があるなら、最初からImageSharpやSkiaSharpを検討すると移行コストを抑えやすくなります。

9. よくあるエラーと解決方法

9-1. 「使用中のためファイルにアクセスできません」の原因と対処

このエラーは、画像ファイルがImageBitmapによってロックされているときによく発生します。

よくある原因は次のコードです。

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();

ファイルを削除・上書きする前に、該当するImageBitmapがすべて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.Imagenullの場合、このコードはエラーになります。

対処方法は、事前に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);

ただし、GetPixelSetPixelは便利ですが高速ではありません。大量のピクセルを処理する場合は、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;

理由は、BitmapImageを継承しているからです。

10-3. Image.FromFileとnew Bitmapの違いは何か

Image.FromFileImage型として画像を読み込みます。

C#
Image image = Image.FromFile("sample.jpg");

new BitmapBitmap型として画像を読み込みます。

C#
Bitmap bitmap = new Bitmap("sample.jpg");

違いをまとめると次のとおりです。

書き方戻り値向いている用途
Image.FromFileImage表示、サイズ取得、保存
new BitmapBitmapピクセル操作、描画、加工

画像を表示するだけなら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+2Six Labors+2

まとめ

C#のBitmapImageは似ていますが、役割は異なります。Imageは画像全般を扱う基底クラスで、Bitmapはピクセルデータを持つ具体的な画像クラスです。BitmapImageを継承しているため、BitmapImageとして扱うことは簡単にできます。

基本的な使い分けは、画像の表示やサイズ取得だけなら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ではBitmapImageを扱いやすい一方で、.NET 6以降のクロスプラットフォーム環境ではSystem.Drawing.Commonの扱いに注意が必要です。Windows専用アプリならBitmapImageで十分な場面が多いですが、Linux、Docker、クラウド、Webサーバーで画像処理を行う場合は、ImageSharpやSkiaSharpなどのライブラリも選択肢に入れるとよいでしょう。