C#で画像処理・表示・保存を実装する方法|初心者向けにサンプルコードで解説

はじめに

C#では、画像ファイルの読み込みや画面表示だけでなく、リサイズ、トリミング、回転、色変換、文字入れ、保存など、さまざまな画像処理を実装できます。

ただし、C#の画像処理方法は、Windows Forms、WPF、ASP.NET Core、Linux上のWebアプリなど、実行環境によって異なります。特にSystem.Drawing.Commonは、.NET 6以降ではWindows向けライブラリとして扱われているため、クロスプラットフォーム開発ではImageSharpやSkiaSharpなどを検討する必要があります。Microsoft Learn+1

この記事では、初心者にも分かりやすいように、C#で画像を読み込み、表示、加工、保存するまでの基本をサンプルコード付きで解説します。

1. C#で画像処理を始める前に知っておきたい基礎知識

1-1. C#でできる画像処理の種類

C#では、主に次のような画像処理を実装できます。

  • JPEGやPNGなどの画像ファイルを読み込む

  • Windows FormsやWPFの画面に画像を表示する

  • 画像サイズを変更する

  • 画像の一部分を切り抜く

  • 画像を回転・反転する

  • グレースケールや白黒画像に変換する

  • 明るさやコントラストを調整する

  • 画像に文字、線、図形、ロゴを描画する

  • 加工した画像をJPEGやPNGで保存する

  • フォルダ内の画像を一括処理する

単純な画像表示であれば数行のコードで実装できます。一方、顔認識、物体検出、輪郭抽出、カメラ映像解析などの高度な処理には、OpenCV系のライブラリが適しています。

1-2. 画像の読み込み・表示・加工・保存の基本的な流れ

C#で画像処理を行うときは、一般的に次の流れで実装します。

  1. ファイルやURLから画像を読み込む

  2. ImageBitmapなどのオブジェクトとして保持する

  3. PictureBoxやWPFのImageコントロールに表示する

  4. リサイズや色変換などの加工を行う

  5. JPEG、PNG、BMPなどの形式で保存する

  6. 使用後の画像オブジェクトを破棄する

画像オブジェクトは、ファイルハンドルやネイティブメモリを使用することがあります。そのため、using文やDisposeメソッドを利用して、不要になったオブジェクトを確実に破棄することが重要です。

1-3. Bitmap・Image・ピクセル・解像度の基礎

System.Drawing.Imageは、画像を表す基本クラスです。JPEG、PNG、GIFなど、さまざまな形式の画像を読み込めます。

System.Drawing.BitmapImageを継承したクラスで、ピクセル単位の読み書きや描画処理に向いています。

C#
using System.Drawing;

using Image image = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap bitmap = new Bitmap(image);

Console.WriteLine($"幅: {bitmap.Width}px");
Console.WriteLine($"高さ: {bitmap.Height}px");
Console.WriteLine($"水平解像度: {bitmap.HorizontalResolution}dpi");
Console.WriteLine($"垂直解像度: {bitmap.VerticalResolution}dpi");

ピクセルは画像を構成する最小単位です。一般的なカラー画像では、各ピクセルに赤・緑・青・透明度を表すRGBA情報が保存されています。

解像度には、画像の縦横サイズを表すピクセル数と、印刷時の密度を表すdpiがあります。画面表示やWeb利用では、通常はピクセル数を重視します。

1-4. JPEG・PNG・GIF・BMPなど画像形式の違い

JPEGは写真に適した形式です。ファイルサイズを小さくできますが、保存時に画質が劣化する非可逆圧縮を使用します。透明部分は保存できません。

PNGは、文字やイラスト、スクリーンショットに適しています。可逆圧縮を使用し、透明度も保存できます。

GIFは256色までの画像に対応し、簡単なアニメーションを保存できます。ただし、通常のBitmapとして加工して保存すると、アニメーション情報が失われることがあります。

BMPは基本的に圧縮されないためファイルサイズが大きくなりますが、データ構造が比較的単純です。

用途に迷った場合は、写真にはJPEG、透明部分を含む画像やロゴにはPNGを選ぶとよいでしょう。

1-5. デスクトップ・Web・クロスプラットフォームで実装方法が異なる理由

Windows FormsやWPFはWindows向けのデスクトップ技術です。そのため、Windowsの描画機能と連携するSystem.Drawingを利用しやすい環境です。

一方、ASP.NET CoreアプリをLinuxサーバーやコンテナで動かす場合、Windows固有の描画機能には依存できません。.NET 6以降のSystem.Drawing.CommonはWindowsのみが正式なサポート対象であり、.NET 7では非Windows環境向けの互換スイッチも削除されています。Microsoft Learn+1

そのため、環境に応じて次のように使い分けます。

  • Windows Forms、WPF:System.Drawing、WPF Imaging

  • ASP.NET Core、Linux、macOS:ImageSharp、SkiaSharp

  • .NET MAUI:SkiaSharp、Microsoft.Maui.Graphics

  • 画像認識や映像解析:OpenCvSharpなど

2. C#の画像処理で使用するライブラリの選び方

2-1. System.Drawingで画像を扱う方法と適した用途

System.Drawingには、画像を扱うためのImageBitmapGraphicsColorなどのクラスがあります。

Windows Formsで画像処理を学ぶ場合は、標準的なAPIでサンプルも多いため、比較的取り組みやすい選択肢です。

適している用途は次のとおりです。

  • Windows専用のデスクトップアプリ

  • PictureBoxへの画像表示

  • 簡単なリサイズやトリミング

  • 文字や図形の描画

  • 既存の.NET Frameworkアプリの保守

ただし、LinuxやmacOSでも動かす必要があるアプリには適していません。

2-2. ImageSharpで画像を扱う方法と適した用途

ImageSharpは、マネージドコードで実装された.NET向け画像処理ライブラリです。画像の読み込み、加工、保存を統一されたAPIで記述できます。

基本的には、Image.Loadで読み込み、Mutateで加工し、Saveまたは形式別の保存メソッドで出力します。Six Labors ドキュメント+1

C#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

using var image = Image.Load("sample.jpg");

image.Mutate(context =>
{
context.Resize(800, 600);
context.Grayscale();
});

image.Save("result.png");

ImageSharpは、次のような用途に向いています。

  • ASP.NET Coreでアップロード画像を加工する

  • LinuxサーバーやDockerコンテナで画像処理を行う

  • Windows、macOS、Linuxで共通のコードを使用する

  • 画像処理コードを比較的簡潔に記述する

ImageSharpには利用条件が設定されているため、特に企業の商用プロジェクトでは導入前に公式ライセンスを確認してください。GitHub+1

2-3. SkiaSharpで画像を扱う方法と適した用途

SkiaSharpは、GoogleのSkiaを.NETから利用するためのクロスプラットフォーム対応2DグラフィックスAPIです。デスクトップ、モバイル、サーバーなど、複数の環境で画像や図形を描画できます。GitHub+1

画像処理だけでなく、次のような用途に適しています。

  • .NET MAUIアプリでのカスタム描画

  • 高品質な文字や図形の描画

  • クロスプラットフォームのグラフィックス処理

  • 画像のデコードやエンコード

  • キャンバスを使用した独自UIの描画

ネイティブライブラリへの依存があるため、実行環境に対応したパッケージの導入が必要になる場合があります。

2-4. OpenCV系ライブラリが必要になるケース

OpenCVは、画像処理やコンピュータービジョン向けの機能を豊富に備えたライブラリです。C#では、OpenCvSharpなどの.NETラッパーを利用できます。OpenCvSharpは、OpenCVの画像処理・コンピュータービジョン機能を.NETから使用できるクロスプラットフォーム対応ライブラリです。GitHub+1

次のような処理が必要な場合に検討します。

  • 顔や物体の検出

  • 輪郭抽出

  • エッジ検出

  • テンプレートマッチング

  • カメラ映像のリアルタイム処理

  • QRコードやマーカーの認識

  • ノイズ除去や高度なフィルター処理

単純なリサイズや文字入れだけであれば、OpenCVは機能が多すぎることがあります。必要な処理に合わせて選択しましょう。

2-5. 初心者はどの画像処理ライブラリを選ぶべきか

Windows Formsで画像処理の基本を学ぶ場合は、System.Drawingから始めると理解しやすいでしょう。

ASP.NET CoreやLinux環境を含むアプリを作る場合は、ImageSharpが分かりやすい選択肢です。

描画性能や.NET MAUIとの連携を重視する場合はSkiaSharp、画像認識やカメラ解析まで行う場合はOpenCvSharpが候補になります。

本記事では、基本的なサンプルをSystem.Drawingで示し、クロスプラットフォーム向けの処理にはImageSharpを使用します。

3. C#で画像処理を行う開発環境を準備する

3-1. Visual StudioでC#プロジェクトを作成する

Windows Formsで試す場合は、Visual Studioから次の手順でプロジェクトを作成します。

  1. Visual Studioを起動する

  2. 「新しいプロジェクトの作成」を選択する

  3. 「Windows Formsアプリ」を選択する

  4. 言語としてC#を選択する

  5. プロジェクト名と保存場所を設定する

  6. 使用する.NETのバージョンを選択する

コンソールだけで画像加工を試す場合は、「コンソールアプリ」でも構いません。

3-2. Windows Formsで画像表示画面を作成する

フォームデザイナーを開き、ツールボックスから次のコントロールを配置します。

  • PictureBox:画像の表示

  • Button:画像の読み込み

  • Button:画像の保存

  • OpenFileDialog:読み込むファイルの選択

  • SaveFileDialog:保存先の選択

PictureBoxの名前をpictureBox1、読み込みボタンの名前をloadButtonなどに変更しておくと、コードが分かりやすくなります。

3-3. WPFで画像表示画面を作成する

WPFでは、XAMLにImageコントロールを配置します。

XML
<Grid>
<Image x:Name="previewImage"
Stretch="Uniform"
Margin="10" />
</Grid>

Stretch="Uniform"を指定すると、画像の縦横比を保ったまま、コントロール内に収まるように表示されます。

3-4. NuGetから画像処理ライブラリをインストールする

ImageSharpを利用する場合は、NuGetパッケージマネージャーからSixLabors.ImageSharpをインストールします。

パッケージマネージャーコンソールでは、次のように実行できます。

PowerShell
Install-Package SixLabors.ImageSharp

.NET CLIを使用する場合は次のとおりです。

Bash
dotnet add package SixLabors.ImageSharp

SkiaSharpの場合は、使用するプロジェクトにSkiaSharpを追加します。

Bash
dotnet add package SkiaSharp

OpenCvSharpでは本体に加え、対象OSに応じたランタイムパッケージが必要になることがあります。

3-5. サンプル画像と保存先フォルダを準備する

プロジェクトの実行フォルダに、次のような構成でファイルを準備します。

プロジェクトフォルダ
├─ Images
│ └─ sample.jpg
└─ Output

コードでは、実行フォルダを基準にパスを作成できます。

C#
string inputPath = Path.Combine(
AppContext.BaseDirectory,
"Images",
"sample.jpg");

string outputDirectory = Path.Combine(
AppContext.BaseDirectory,
"Output");

Directory.CreateDirectory(outputDirectory);

相対パスは実行時のカレントディレクトリによって解釈が変わることがあります。確実にファイルを参照したい場合は、AppContext.BaseDirectoryや絶対パスを使用しましょう。

4. C#で画像ファイルを読み込む方法

4-1. Image.FromFileで画像を読み込むサンプルコード

Image.FromFileを使用すると、指定したファイルから画像を読み込めます。

C#
using System.Drawing;

string path = @"C:\Images\sample.jpg";

using Image image = Image.FromFile(path);

Console.WriteLine($"幅: {image.Width}");
Console.WriteLine($"高さ: {image.Height}");
Console.WriteLine($"形式: {image.RawFormat}");

Image.FromFileで読み込んだ画像は、破棄するまで元ファイルを参照する場合があります。読み込み後に元ファイルを削除・移動・上書きしたい場合は、後述するクローン方法を使用します。

4-2. Bitmapクラスで画像を読み込むサンプルコード

画像をピクセル単位で加工したい場合は、Bitmapとして読み込みます。

C#
using System.Drawing;

string path = @"C:\Images\sample.png";

using Bitmap bitmap = new Bitmap(path);

Color color = bitmap.GetPixel(0, 0);

Console.WriteLine(
$"R={color.R}, G={color.G}, B={color.B}, A={color.A}");

GetPixelSetPixelは簡単に使えますが、大きな画像をすべて処理する場合は遅くなります。

4-3. FileStreamを使って画像を読み込む方法

ストリームから画像を読み込む場合は、Image.FromStreamを使用します。

C#
using System.Drawing;

static Bitmap LoadBitmapFromStream(string path)
{
using FileStream stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.Read);

using Image loaded = Image.FromStream(stream);

return new Bitmap(loaded);
}

using Bitmap bitmap = LoadBitmapFromStream(@"C:\Images\sample.jpg");

Image.FromStreamで作成した画像は、元ストリームの内容を後から参照する可能性があります。そのため、ストリームを閉じる前にnew Bitmap(loaded)で複製しています。

4-4. ファイル選択ダイアログから画像を読み込む方法

Windows Formsでは、OpenFileDialogを使ってユーザーに画像を選択してもらえます。

C#
using System.Drawing;
using System.Windows.Forms;

private void LoadImage()
{
using OpenFileDialog dialog = new OpenFileDialog
{
Title = "画像を選択してください",
Filter =
"画像ファイル|*.jpg;*.jpeg;*.png;*.gif;*.bmp|" +
"すべてのファイル|*.*"
};

if (dialog.ShowDialog() != DialogResult.OK)
{
return;
}

Bitmap newImage = LoadBitmapWithoutLock(dialog.FileName);

Image? oldImage = pictureBox1.Image;
pictureBox1.Image = newImage;
oldImage?.Dispose();
}

private static Bitmap LoadBitmapWithoutLock(string path)
{
using Image source = Image.FromFile(path);
return new Bitmap(source);
}

新しい画像を設定する前に、以前の画像をDisposeすることでメモリリークを防ぎます。

4-5. URLから画像を取得して読み込む方法

URL上の画像は、HttpClientでバイトデータを取得してから読み込みます。

C#
using System.Drawing;
using System.Net.Http;

static async Task<Bitmap> LoadBitmapFromUrlAsync(
string imageUrl,
CancellationToken cancellationToken = default)
{
using HttpClient client = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};

byte[] bytes = await client.GetByteArrayAsync(
imageUrl,
cancellationToken);

using MemoryStream stream = new MemoryStream(bytes);
using Image source = Image.FromStream(stream);

return new Bitmap(source);
}

実際のアプリでは、HttpClientを処理のたびに生成するのではなく、共有インスタンスやIHttpClientFactoryを使用する方法が適しています。

URLを外部入力から受け取るWebアプリでは、SSRF対策、ファイルサイズ制限、タイムアウト、Content-Typeの検証なども必要です。

4-6. 読み込み時のファイルロックを回避する方法

元ファイルをロックせずに読み込みたい場合は、一度読み込んだ画像を別のBitmapへ複製します。

C#
using System.Drawing;

static Bitmap LoadImageWithoutFileLock(string path)
{
using FileStream stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite);

using Image source = Image.FromStream(stream);

return new Bitmap(source);
}

このメソッドが返したBitmapは、呼び出し側で破棄します。

C#
using Bitmap bitmap =
LoadImageWithoutFileLock(@"C:\Images\sample.jpg");

4-7. 存在しないファイルや未対応形式のエラーを処理する方法

画像の読み込みでは、ファイルが存在しない、アクセス権限がない、画像が破損しているといった問題が発生します。

C#
using System.Drawing;

static Bitmap? TryLoadBitmap(string path)
{
if (!File.Exists(path))
{
Console.WriteLine("指定されたファイルが存在しません。");
return null;
}

try
{
using Image source = Image.FromFile(path);
return new Bitmap(source);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("ファイルを読み取る権限がありません。");
}
catch (OutOfMemoryException)
{
// System.Drawingでは、画像形式が不正な場合にも
// OutOfMemoryExceptionが発生することがあります。
Console.WriteLine("画像が破損しているか、未対応の形式です。");
}
catch (ArgumentException)
{
Console.WriteLine("有効な画像ファイルではありません。");
}
catch (IOException ex)
{
Console.WriteLine($"ファイルの読み込みに失敗しました: {ex.Message}");
}

return null;
}

拡張子だけで画像かどうかを判断せず、実際にデコードできるか確認することが大切です。

5. C#で読み込んだ画像を画面に表示する方法

5-1. Windows FormsのPictureBoxに画像を表示する

Windows Formsでは、PictureBoxのImageプロパティに画像を設定します。

C#
private void ShowImage(string path)
{
Bitmap image = LoadImageWithoutFileLock(path);

Image? oldImage = pictureBox1.Image;
pictureBox1.Image = image;

oldImage?.Dispose();
}

フォームを閉じるときにも、表示中の画像を破棄します。

C#
protected override void OnFormClosed(FormClosedEventArgs e)
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = null;

base.OnFormClosed(e);
}

5-2. PictureBoxのSizeModeで表示サイズを調整する

PictureBoxの表示方法は、SizeModeプロパティで変更できます。

C#
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;

主な設定は次のとおりです。

  • Normal:画像を左上に原寸表示する

  • StretchImage:PictureBox全体に引き伸ばす

  • AutoSize:画像サイズに合わせてPictureBoxを変更する

  • CenterImage:画像を中央に表示する

  • Zoom:縦横比を維持して拡大・縮小する

縦横比を崩したくない場合はZoomが適しています。Microsoft Learn+1

5-3. WPFのImageコントロールに画像を表示する

WPFでは、BitmapImageを作成してImageコントロールのSourceに設定します。

C#
using System.Windows.Media.Imaging;

private static BitmapImage LoadBitmapImage(string path)
{
BitmapImage bitmap = new BitmapImage();

bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path, UriKind.Absolute);
bitmap.EndInit();
bitmap.Freeze();

return bitmap;
}

private void ShowImage(string path)
{
previewImage.Source = LoadBitmapImage(path);
}

BitmapCacheOption.OnLoadを指定すると、初期化時に画像全体がメモリへ読み込まれるため、読み込み後に元ストリームを閉じられます。Microsoft Learn+1

5-4. バイト配列やStreamから画像を表示する

WPFでバイト配列から画像を表示する場合は、MemoryStreamを利用します。

C#
using System.Windows.Media.Imaging;

private static BitmapImage CreateBitmapImage(byte[] bytes)
{
using MemoryStream stream = new MemoryStream(bytes);

BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();

return image;
}

Windows Formsでは、次のようにBitmapへ複製します。

C#
using System.Drawing;

private static Bitmap CreateBitmap(byte[] bytes)
{
using MemoryStream stream = new MemoryStream(bytes);
using Image source = Image.FromStream(stream);

return new Bitmap(source);
}

5-5. 画像の縦横比を維持して表示する

Windows Formsでは、PictureBoxのSizeModeZoomに設定します。

C#
pictureBox1.Dock = DockStyle.Fill;
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;

WPFでは、Stretch="Uniform"を指定します。

XML
<Image x:Name="previewImage"
Stretch="Uniform"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />

縦横比を維持したまま表示する場合、コントロールと画像の比率が異なると余白ができます。コントロール全体を埋める場合は、画像の一部を切り取る必要があります。

5-6. 大きな画像をスクロール・拡大縮小表示する

Windows Formsでは、PanelAutoScrollを有効にし、その中へPictureBoxを配置します。

C#
panel1.AutoScroll = true;

pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
pictureBox1.Image = LoadImageWithoutFileLock(
@"C:\Images\large-image.jpg");

倍率を指定して表示する場合は、元画像のサイズからPictureBoxの大きさを計算します。

C#
private double zoomRate = 1.0;

private void ApplyZoom()
{
if (pictureBox1.Image is null)
{
return;
}

pictureBox1.Width =
(int)Math.Round(pictureBox1.Image.Width * zoomRate);

pictureBox1.Height =
(int)Math.Round(pictureBox1.Image.Height * zoomRate);
}

表示倍率を変更するだけなら、元画像自体を何度もリサイズしない方が画質を維持できます。

6. C#で画像サイズを変更する方法

6-1. BitmapとGraphicsで画像をリサイズする

BitmapGraphics.DrawImageを使用して、指定サイズの画像を作成します。

C#
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

static Bitmap ResizeImage(
Image source,
int width,
int height)
{
if (width <= 0 || height <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(width),
"幅と高さは1以上にしてください。");
}

Bitmap result = new Bitmap(
width,
height,
PixelFormat.Format32bppArgb);

using Graphics graphics = Graphics.FromImage(result);

graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

graphics.DrawImage(
source,
new Rectangle(0, 0, width, height),
0,
0,
source.Width,
source.Height,
GraphicsUnit.Pixel);

return result;
}

呼び出し側では、元画像と生成画像の両方を破棄します。

C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap resized = ResizeImage(source, 800, 600);

resized.Save(@"C:\Images\resized.png");

6-2. 縦横比を維持して画像を縮小する

縦横比を維持するには、幅と高さの縮小率のうち、小さい方を使用します。

C#
static Bitmap ResizeToFit(
Image source,
int maxWidth,
int maxHeight,
bool allowEnlarge = false)
{
if (maxWidth <= 0 || maxHeight <= 0)
{
throw new ArgumentOutOfRangeException();
}

double widthRate = (double)maxWidth / source.Width;
double heightRate = (double)maxHeight / source.Height;
double rate = Math.Min(widthRate, heightRate);

if (!allowEnlarge)
{
rate = Math.Min(rate, 1.0);
}

int width = Math.Max(
1,
(int)Math.Round(source.Width * rate));

int height = Math.Max(
1,
(int)Math.Round(source.Height * rate));

return ResizeImage(source, width, height);
}
C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap resized = ResizeToFit(source, 1200, 800);

resized.Save(@"C:\Images\fit.png");

6-3. 指定サイズに合わせて余白を追加する

画像を変形させずに指定サイズへ合わせる場合は、画像の周囲に余白を追加します。

C#
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

static Bitmap ResizeWithPadding(
Image source,
int canvasWidth,
int canvasHeight,
Color backgroundColor)
{
double rate = Math.Min(
(double)canvasWidth / source.Width,
(double)canvasHeight / source.Height);

int width = Math.Max(
1,
(int)Math.Round(source.Width * rate));

int height = Math.Max(
1,
(int)Math.Round(source.Height * rate));

int x = (canvasWidth - width) / 2;
int y = (canvasHeight - height) / 2;

Bitmap result = new Bitmap(
canvasWidth,
canvasHeight,
PixelFormat.Format32bppArgb);

using Graphics graphics = Graphics.FromImage(result);

graphics.Clear(backgroundColor);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

graphics.DrawImage(
source,
new Rectangle(x, y, width, height));

return result;
}

透過余白にしたい場合は、背景色としてColor.Transparentを指定し、PNG形式で保存します。

6-4. サムネイル画像を作成する

サムネイル画像も、基本的には縦横比を維持したリサイズで作成できます。

C#
static Bitmap CreateThumbnail(
Image source,
int maxSize)
{
return ResizeToFit(
source,
maxSize,
maxSize,
allowEnlarge: false);
}
C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap thumbnail = CreateThumbnail(source, 300);

thumbnail.Save(
@"C:\Images\thumbnail.jpg",
System.Drawing.Imaging.ImageFormat.Jpeg);

元画像が小さい場合に拡大すると画像がぼやけるため、サムネイル作成では通常、拡大を行わない設定にします。

6-5. リサイズ時の画質を設定する

Graphicsでは、主に次のプロパティで描画品質を調整します。

C#
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;

graphics.SmoothingMode =
SmoothingMode.HighQuality;

graphics.PixelOffsetMode =
PixelOffsetMode.HighQuality;

graphics.CompositingQuality =
CompositingQuality.HighQuality;

高品質な設定ほど処理時間が増える傾向があります。大量の画像を処理する場合は、画質と処理速度のバランスを確認しましょう。

画像を極端に小さくした後、再び拡大しても元の細部は復元できません。複数サイズの画像を作る場合は、加工済み画像ではなく元画像から生成してください。

6-6. ImageSharpで画像をリサイズするサンプルコード

ImageSharpでは、ResizeOptionsを使って縦横比を維持したリサイズを実装できます。

C#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

static void ResizeWithImageSharp(
string inputPath,
string outputPath,
int maxWidth,
int maxHeight)
{
using var image = Image.Load(inputPath);

image.Mutate(context =>
{
context.Resize(new ResizeOptions
{
Size = new Size(maxWidth, maxHeight),
Mode = ResizeMode.Max
});
});

image.Save(outputPath);
}

ResizeMode.Maxでは、指定した範囲内に収まるように縦横比を維持してリサイズされます。ImageSharpでは、用途に応じて切り抜きや余白追加などのリサイズモードも選択できます。Six Labors ドキュメント+1

7. C#で画像を切り抜き・回転・反転する方法

7-1. 指定範囲で画像をトリミングする

指定した長方形の範囲を、新しいBitmapへ描画します。

C#
using System.Drawing;
using System.Drawing.Imaging;

static Bitmap CropImage(
Image source,
Rectangle cropArea)
{
Rectangle imageArea = new Rectangle(
0,
0,
source.Width,
source.Height);

Rectangle safeArea =
Rectangle.Intersect(imageArea, cropArea);

if (safeArea.Width <= 0 || safeArea.Height <= 0)
{
throw new ArgumentException(
"切り抜き範囲が画像の外側です。",
nameof(cropArea));
}

Bitmap result = new Bitmap(
safeArea.Width,
safeArea.Height,
PixelFormat.Format32bppArgb);

using Graphics graphics = Graphics.FromImage(result);

graphics.DrawImage(
source,
new Rectangle(0, 0, result.Width, result.Height),
safeArea,
GraphicsUnit.Pixel);

return result;
}
C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");

Rectangle area = new Rectangle(
x: 100,
y: 50,
width: 400,
height: 300);

using Bitmap cropped = CropImage(source, area);
cropped.Save(@"C:\Images\cropped.png");

7-2. 画像の中央部分を切り抜く

中央から指定サイズを切り抜く場合は、開始位置を計算します。

C#
static Bitmap CropCenter(
Image source,
int width,
int height)
{
width = Math.Min(width, source.Width);
height = Math.Min(height, source.Height);

int x = (source.Width - width) / 2;
int y = (source.Height - height) / 2;

return CropImage(
source,
new Rectangle(x, y, width, height));
}
C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap center = CropCenter(source, 600, 600);

center.Save(@"C:\Images\center.png");

7-3. 画像を90度・180度・270度回転する

RotateFlipメソッドを使用すると、90度単位で回転できます。

C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");
using Bitmap rotated = new Bitmap(source);

rotated.RotateFlip(RotateFlipType.Rotate90FlipNone);
rotated.Save(@"C:\Images\rotate90.png");

指定できる主な値は次のとおりです。

C#
RotateFlipType.Rotate90FlipNone
RotateFlipType.Rotate180FlipNone
RotateFlipType.Rotate270FlipNone

RotateFlipは対象のBitmap自体を変更します。元画像を残したい場合は、事前に複製してください。

7-4. 画像を左右・上下に反転する

左右反転は次のように実装します。

C#
using Bitmap bitmap =
new Bitmap(@"C:\Images\sample.jpg");

bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
bitmap.Save(@"C:\Images\flip-horizontal.png");

上下反転は次のとおりです。

C#
using Bitmap bitmap =
new Bitmap(@"C:\Images\sample.jpg");

bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
bitmap.Save(@"C:\Images\flip-vertical.png");

回転と反転を同時に行うRotateFlipTypeも用意されています。

7-5. 範囲外の座標指定によるエラーを防ぐ

切り抜き座標は、必ず画像サイズの範囲内に調整します。

C#
static Rectangle ClampRectangle(
Rectangle requested,
Size imageSize)
{
Rectangle imageArea = new Rectangle(
Point.Empty,
imageSize);

Rectangle result =
Rectangle.Intersect(requested, imageArea);

if (result.Width <= 0 || result.Height <= 0)
{
throw new ArgumentException(
"有効な画像範囲がありません。");
}

return result;
}

ユーザーがマウスで範囲を選ぶアプリでは、左から右へドラッグするとは限りません。開始点と終了点から、左上座標、幅、高さを正規化してから処理しましょう。

8. C#で画像の色や明るさを加工する方法

8-1. 画像をグレースケールに変換する

ColorMatrixを使用すると、画像全体をグレースケールに変換できます。

C#
using System.Drawing;
using System.Drawing.Imaging;

static Bitmap ConvertToGrayscale(Image source)
{
Bitmap result = new Bitmap(
source.Width,
source.Height,
PixelFormat.Format32bppArgb);

ColorMatrix matrix = new ColorMatrix(
new[]
{
new[] { 0.299f, 0.299f, 0.299f, 0f, 0f },
new[] { 0.587f, 0.587f, 0.587f, 0f, 0f },
new[] { 0.114f, 0.114f, 0.114f, 0f, 0f },
new[] { 0f, 0f, 0f, 1f, 0f },
new[] { 0f, 0f, 0f, 0f, 1f }
});

using ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(matrix);

using Graphics graphics = Graphics.FromImage(result);

graphics.DrawImage(
source,
new Rectangle(0, 0, result.Width, result.Height),
0,
0,
source.Width,
source.Height,
GraphicsUnit.Pixel,
attributes);

return result;
}

8-2. 画像を白黒の二値画像に変換する

二値化では、各ピクセルの明るさを計算し、しきい値より明るければ白、暗ければ黒にします。

C#
using System.Drawing;

static Bitmap ConvertToBinary(
Bitmap source,
byte threshold = 128)
{
Bitmap result = new Bitmap(
source.Width,
source.Height);

for (int y = 0; y < source.Height; y++)
{
for (int x = 0; x < source.Width; x++)
{
Color color = source.GetPixel(x, y);

int brightness =
(299 * color.R +
587 * color.G +
114 * color.B) / 1000;

Color newColor =
brightness >= threshold
? Color.White
: Color.Black;

result.SetPixel(x, y, newColor);
}
}

return result;
}

この方法は分かりやすい一方、GetPixelSetPixelを大量に呼び出すため、大きな画像では処理が遅くなります。

8-3. 明るさとコントラストを調整する

明るさとコントラストは、ColorMatrixでまとめて調整できます。

C#
using System.Drawing;
using System.Drawing.Imaging;

static Bitmap AdjustBrightnessAndContrast(
Image source,
float brightness,
float contrast)
{
brightness = Math.Clamp(brightness, -1f, 1f);
contrast = Math.Clamp(contrast, 0f, 3f);

float translation =
0.5f * (1f - contrast) + brightness;

ColorMatrix matrix = new ColorMatrix(
new[]
{
new[] { contrast, 0f, 0f, 0f, 0f },
new[] { 0f, contrast, 0f, 0f, 0f },
new[] { 0f, 0f, contrast, 0f, 0f },
new[] { 0f, 0f, 0f, 1f, 0f },
new[] { translation, translation, translation, 0f, 1f }
});

Bitmap result = new Bitmap(
source.Width,
source.Height,
PixelFormat.Format32bppArgb);

using ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(matrix);

using Graphics graphics = Graphics.FromImage(result);

graphics.DrawImage(
source,
new Rectangle(0, 0, result.Width, result.Height),
0,
0,
source.Width,
source.Height,
GraphicsUnit.Pixel,
attributes);

return result;
}

使用例は次のとおりです。

C#
using Image source = Image.FromFile(@"C:\Images\sample.jpg");

using Bitmap adjusted = AdjustBrightnessAndContrast(
source,
brightness: 0.1f,
contrast: 1.2f);

adjusted.Save(@"C:\Images\adjusted.png");

8-4. 透明度を変更する

画像全体の透明度は、アルファ成分を変更して描画します。

C#
using System.Drawing;
using System.Drawing.Imaging;

static Bitmap ChangeOpacity(
Image source,
float opacity)
{
opacity = Math.Clamp(opacity, 0f, 1f);

Bitmap result = new Bitmap(
source.Width,
source.Height,
PixelFormat.Format32bppArgb);

ColorMatrix matrix = new ColorMatrix
{
Matrix33 = opacity
};

using ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(matrix);

using Graphics graphics = Graphics.FromImage(result);

graphics.DrawImage(
source,
new Rectangle(0, 0, result.Width, result.Height),
0,
0,
source.Width,
source.Height,
GraphicsUnit.Pixel,
attributes);

return result;
}

透明度を維持する場合は、JPEGではなくPNGで保存します。

8-5. 背景色を透明にする

単色の背景を透明にする場合は、MakeTransparentを使用できます。

C#
using Bitmap bitmap =
new Bitmap(@"C:\Images\logo.png");

bitmap.MakeTransparent(Color.White);
bitmap.Save(
@"C:\Images\transparent-logo.png",
System.Drawing.Imaging.ImageFormat.Png);

アンチエイリアスが適用された画像では、背景との境界に白に近い色が残ることがあります。自然に背景を除去するには、色の許容範囲やマスク処理が必要です。

8-6. ピクセル単位で色を取得・変更する

特定の座標の色は、GetPixelで取得できます。

C#
using Bitmap bitmap =
new Bitmap(@"C:\Images\sample.png");

Color current = bitmap.GetPixel(10, 20);

Console.WriteLine(
$"A={current.A}, R={current.R}, " +
$"G={current.G}, B={current.B}");

色の変更にはSetPixelを使用します。

C#
bitmap.SetPixel(10, 20, Color.Red);

座標は0から始まります。x0以上Width - 1以下、y0以上Height - 1以下でなければなりません。

8-7. GetPixelとSetPixelが遅い場合の高速化方法

大量のピクセルを処理する場合は、Bitmap.LockBitsで画像データへまとめてアクセスします。

次の例では、32ビットARGB画像を高速にグレースケール化します。プロジェクト設定でunsafeコードを許可する必要があります。

C#
using System.Drawing;
using System.Drawing.Imaging;

static unsafe Bitmap ConvertToGrayscaleFast(Image source)
{
Bitmap result = new Bitmap(
source.Width,
source.Height,
PixelFormat.Format32bppArgb);

using (Graphics graphics = Graphics.FromImage(result))
{
graphics.DrawImageUnscaled(source, 0, 0);
}

Rectangle area = new Rectangle(
0,
0,
result.Width,
result.Height);

BitmapData data = result.LockBits(
area,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);

try
{
for (int y = 0; y < data.Height; y++)
{
byte* row =
(byte*)data.Scan0 + y * data.Stride;

for (int x = 0; x < data.Width; x++)
{
byte* pixel = row + x * 4;

byte blue = pixel[0];
byte green = pixel[1];
byte red = pixel[2];

byte gray = (byte)(
(299 * red +
587 * green +
114 * blue) / 1000);

pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
}
}
}
finally
{
result.UnlockBits(data);
}

return result;
}

LockBitsでは、ピクセル形式、1ピクセル当たりのバイト数、ストライド、色成分の並び順を正しく扱う必要があります。速度が必要ない処理では、まず安全で分かりやすい方法から実装しましょう。

9. C#で画像に文字や図形を描画する方法

9-1. Graphicsで画像に文字を描画する

画像へ文字を描画するには、Graphics.DrawStringを使用します。

C#
using System.Drawing;
using System.Drawing.Drawing2D;

static Bitmap DrawText(
Image source,
string text)
{
Bitmap result = new Bitmap(source);

using Graphics graphics = Graphics.FromImage(result);

graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;

using Font font = new Font(
"Yu Gothic UI",
32,
FontStyle.Bold,
GraphicsUnit.Pixel);

using Brush brush = new SolidBrush(Color.White);

graphics.DrawString(
text,
font,
brush,
new PointF(20, 20));

return result;
}

9-2. フォント・文字色・配置を指定する

文字を中央に配置する場合は、StringFormatを利用します。

C#
using Font font = new Font(
"Yu Gothic UI",
36,
FontStyle.Bold,
GraphicsUnit.Pixel);

using Brush brush =
new SolidBrush(Color.FromArgb(255, 255, 255));

using StringFormat format = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};

RectangleF area = new RectangleF(
0,
0,
bitmap.Width,
bitmap.Height);

graphics.DrawString(
"C# 画像処理",
font,
brush,
area,
format);

文字を読みやすくするには、影や縁取りを追加する方法もあります。

C#
graphics.DrawString(
text,
font,
Brushes.Black,
new PointF(x + 2, y + 2));

graphics.DrawString(
text,
font,
Brushes.White,
new PointF(x, y));

9-3. 線・四角形・円を描画する

Graphicsでは、線、四角形、楕円などを描画できます。

C#
using Bitmap bitmap =
new Bitmap(@"C:\Images\sample.jpg");

using Graphics graphics = Graphics.FromImage(bitmap);

using Pen redPen = new Pen(Color.Red, 4);
using Pen bluePen = new Pen(Color.Blue, 3);
using Brush fillBrush =
new SolidBrush(Color.FromArgb(100, Color.Yellow));

graphics.DrawLine(
redPen,
new Point(20, 20),
new Point(300, 200));

graphics.DrawRectangle(
bluePen,
new Rectangle(50, 50, 200, 120));

graphics.FillEllipse(
fillBrush,
new Rectangle(100, 100, 150, 150));

graphics.DrawEllipse(
redPen,
new Rectangle(100, 100, 150, 150));

bitmap.Save(@"C:\Images\shapes.png");

9-4. 画像に透かし文字を追加する

透明度を持つ色で文字を描画すると、透かしを作成できます。

C#
using System.Drawing;

static Bitmap AddTextWatermark(
Image source,
string watermarkText)
{
Bitmap result = new Bitmap(source);

using Graphics graphics = Graphics.FromImage(result);

float fontSize = Math.Max(
18f,
result.Width / 25f);

using Font font = new Font(
"Yu Gothic UI",
fontSize,
FontStyle.Bold,
GraphicsUnit.Pixel);

using Brush brush = new SolidBrush(
Color.FromArgb(110, 255, 255, 255));

SizeF textSize =
graphics.MeasureString(watermarkText, font);

float x =
result.Width - textSize.Width - 20;

float y =
result.Height - textSize.Height - 20;

graphics.DrawString(
watermarkText,
font,
brush,
x,
y);

return result;
}

9-5. 別の画像やロゴを重ね合わせる

ロゴ画像を右下へ重ねる例は次のとおりです。

C#
using System.Drawing;
using System.Drawing.Imaging;

static Bitmap AddLogo(
Image source,
Image logo,
float opacity = 0.7f)
{
opacity = Math.Clamp(opacity, 0f, 1f);

Bitmap result = new Bitmap(source);

int logoWidth = Math.Min(
logo.Width,
Math.Max(1, result.Width / 4));

int logoHeight = Math.Max(
1,
(int)Math.Round(
logo.Height *
((double)logoWidth / logo.Width)));

int margin = 20;
int x = result.Width - logoWidth - margin;
int y = result.Height - logoHeight - margin;

ColorMatrix matrix = new ColorMatrix
{
Matrix33 = opacity
};

using ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(matrix);

using Graphics graphics = Graphics.FromImage(result);

graphics.DrawImage(
logo,
new Rectangle(x, y, logoWidth, logoHeight),
0,
0,
logo.Width,
logo.Height,
GraphicsUnit.Pixel,
attributes);

return result;
}
C#
using Image source = Image.FromFile(@"C:\Images\photo.jpg");
using Image logo = Image.FromFile(@"C:\Images\logo.png");
using Bitmap result = AddLogo(source, logo);

result.Save(@"C:\Images\with-logo.png");

9-6. 日本語が文字化けする場合の対処法

日本語を描画する場合は、日本語グリフを持つフォントを指定します。

C#
using Font font = new Font(
"Yu Gothic UI",
32,
FontStyle.Regular,
GraphicsUnit.Pixel);

指定したフォントが実行環境に存在しない場合は、代替フォントに置き換えられたり、文字が四角形で表示されたりすることがあります。

特にサーバーやコンテナでは、日本語フォントがインストールされているとは限りません。実行環境へフォントを導入するか、ライセンスを確認したうえでアプリにフォントを同梱し、ライブラリの機能を使って読み込む必要があります。

ソースコード自体はUTF-8で保存し、入力文字列が正しいことも確認してください。

10. C#で加工した画像を保存する方法

10-1. Image.Saveで画像ファイルを保存する

最も簡単な保存方法は、Image.Saveへ保存先を指定する方法です。

C#
using Image image =
Image.FromFile(@"C:\Images\sample.png");

image.Save(@"C:\Images\copy.png");

同じ画像オブジェクトを、読み込んだ元ファイルと同じパスへ直接保存すると、ファイルロックやエンコーダーの問題が発生することがあります。上書きする場合は一時ファイルを利用しましょう。

10-2. JPEG・PNG・BMP形式を指定して保存する

保存形式はImageFormatで明示できます。

C#
using System.Drawing;
using System.Drawing.Imaging;

using Image image =
Image.FromFile(@"C:\Images\sample.png");

image.Save(
@"C:\Images\result.jpg",
ImageFormat.Jpeg);

image.Save(
@"C:\Images\result.png",
ImageFormat.Png);

image.Save(
@"C:\Images\result.bmp",
ImageFormat.Bmp);

JPEGでは透明部分を保存できません。透明画像をJPEGへ変換するときは、白などの背景色を描画してから保存します。

10-3. JPEGの画質を指定して保存する

JPEGの画質は、JPEGエンコーダーとEncoderParameterを指定して設定します。

C#
using System.Drawing;
using System.Drawing.Imaging;

static void SaveJpeg(
Image image,
string outputPath,
long quality)
{
quality = Math.Clamp(quality, 0L, 100L);

ImageCodecInfo jpegCodec =
ImageCodecInfo
.GetImageEncoders()
.First(codec =>
codec.FormatID == ImageFormat.Jpeg.Guid);

using EncoderParameters parameters =
new EncoderParameters(1);

parameters.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality,
quality);

image.Save(
outputPath,
jpegCodec,
parameters);
}
C#
using Image image =
Image.FromFile(@"C:\Images\sample.png");

SaveJpeg(
image,
@"C:\Images\quality-85.jpg",
85);

System.DrawingのJPEG品質には、通常0から100の値を指定します。値が低いほど圧縮率が高くなり、画質は低下します。Microsoft Learn+1

10-4. 透過情報を維持したままPNGで保存する

透明度を維持するには、アルファチャンネルを持つBitmapをPNG形式で保存します。

C#
using System.Drawing;
using System.Drawing.Imaging;

using Bitmap bitmap = new Bitmap(
500,
300,
PixelFormat.Format32bppArgb);

using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Transparent);

using Brush brush =
new SolidBrush(Color.FromArgb(128, Color.Red));

graphics.FillEllipse(
brush,
new Rectangle(50, 50, 200, 200));
}

bitmap.Save(
@"C:\Images\transparent.png",
ImageFormat.Png);

JPEGで保存するとアルファチャンネルは失われるため、透過画像にはPNGを使用してください。

10-5. 同名ファイルへの上書きを安全に行う

安全に上書きするには、同じフォルダへ一時ファイルを保存し、保存完了後に置き換えます。

C#
using System.Drawing;
using System.Drawing.Imaging;

static void SavePngSafely(
Image image,
string outputPath)
{
string fullPath =
Path.GetFullPath(outputPath);

string? directory =
Path.GetDirectoryName(fullPath);

if (string.IsNullOrEmpty(directory))
{
throw new ArgumentException(
"保存先フォルダを取得できません。",
nameof(outputPath));
}

Directory.CreateDirectory(directory);

string temporaryPath = Path.Combine(
directory,
$".{Guid.NewGuid():N}.tmp");

try
{
image.Save(
temporaryPath,
ImageFormat.Png);

File.Move(
temporaryPath,
fullPath,
overwrite: true);
}
finally
{
if (File.Exists(temporaryPath))
{
File.Delete(temporaryPath);
}
}
}

一時ファイルを同じフォルダへ作成することで、異なるドライブ間の移動による問題を避けやすくなります。

10-6. MemoryStreamやバイト配列へ画像を保存する

画像をデータベースやWebレスポンスへ渡す場合は、MemoryStreamへ保存します。

C#
using System.Drawing;
using System.Drawing.Imaging;

static byte[] ConvertToPngBytes(Image image)
{
using MemoryStream stream = new MemoryStream();

image.Save(stream, ImageFormat.Png);

return stream.ToArray();
}

JPEG画質を指定してバイト配列へ変換する場合は、保存先としてストリームを渡します。

C#
static byte[] ConvertToJpegBytes(
Image image,
long quality)
{
quality = Math.Clamp(quality, 0L, 100L);

ImageCodecInfo jpegCodec =
ImageCodecInfo
.GetImageEncoders()
.First(codec =>
codec.FormatID == ImageFormat.Jpeg.Guid);

using EncoderParameters parameters =
new EncoderParameters(1);

parameters.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality,
quality);

using MemoryStream stream = new MemoryStream();

image.Save(
stream,
jpegCodec,
parameters);

return stream.ToArray();
}

10-7. 保存先フォルダが存在しない場合の処理

画像を保存する前に、保存先フォルダを作成します。

C#
static void EnsureOutputDirectory(
string outputPath)
{
string fullPath = Path.GetFullPath(outputPath);

string? directory =
Path.GetDirectoryName(fullPath);

if (string.IsNullOrWhiteSpace(directory))
{
throw new ArgumentException(
"保存先フォルダが不正です。",
nameof(outputPath));
}

Directory.CreateDirectory(directory);
}

Directory.CreateDirectoryは、フォルダがすでに存在していても利用できます。

C#
string outputPath =
@"C:\Images\Output\result.png";

EnsureOutputDirectory(outputPath);

using Image image =
Image.FromFile(@"C:\Images\sample.jpg");

image.Save(
outputPath,
System.Drawing.Imaging.ImageFormat.Png);

11. C#で複数の画像を一括処理する方法

11-1. フォルダ内の画像ファイルを取得する

対象拡張子を限定して、フォルダ内の画像を取得します。

C#
static IEnumerable<string> GetImageFiles(
string directoryPath)
{
HashSet<string> supportedExtensions =
new HashSet<string>(
StringComparer.OrdinalIgnoreCase)
{
".jpg",
".jpeg",
".png",
".gif",
".bmp"
};

return Directory
.EnumerateFiles(
directoryPath,
"*.*",
SearchOption.TopDirectoryOnly)
.Where(path =>
supportedExtensions.Contains(
Path.GetExtension(path)));
}

サブフォルダも含める場合は、SearchOption.AllDirectoriesを指定します。ただし、アクセス権限のないフォルダが含まれる可能性があるため、例外処理が必要です。

11-2. 複数画像を一括でリサイズする

フォルダ内の画像を、縦横比を維持したまま一括で縮小する例です。

C#
using System.Drawing;
using System.Drawing.Imaging;

static void ResizeImagesInFolder(
string inputDirectory,
string outputDirectory,
int maxWidth,
int maxHeight)
{
if (!Directory.Exists(inputDirectory))
{
throw new DirectoryNotFoundException(
$"入力フォルダが存在しません: {inputDirectory}");
}

Directory.CreateDirectory(outputDirectory);

foreach (string inputPath in GetImageFiles(inputDirectory))
{
try
{
using Bitmap source =
LoadImageWithoutFileLock(inputPath);

using Bitmap resized = ResizeToFit(
source,
maxWidth,
maxHeight,
allowEnlarge: false);

string fileNameWithoutExtension =
Path.GetFileNameWithoutExtension(inputPath);

string outputPath = Path.Combine(
outputDirectory,
$"{fileNameWithoutExtension}.jpg");

SaveJpeg(
resized,
outputPath,
quality: 85);

Console.WriteLine(
$"処理完了: {inputPath}");
}
catch (Exception ex)
{
Console.WriteLine(
$"処理失敗: {inputPath} - {ex.Message}");
}
}
}

実行例は次のとおりです。

C#
ResizeImagesInFolder(
inputDirectory: @"C:\Images\Input",
outputDirectory: @"C:\Images\Output",
maxWidth: 1200,
maxHeight: 1200);

画像数が多い場合は並列処理も検討できますが、同時に多数の高解像度画像を展開するとメモリ使用量が急増します。最初は逐次処理で実装し、必要に応じて同時実行数を制限した並列処理へ変更するのが安全です。

まとめ

C#では、ImageBitmapGraphicsなどを使用して、画像の読み込み、表示、加工、保存を実装できます。Windows FormsではPictureBox、WPFではImageコントロールを使用すると、読み込んだ画像を画面へ表示できます。

画像サイズの変更、トリミング、回転、グレースケール変換、文字入れなどの基本処理は、System.Drawingだけでも実装可能です。ただし、画像オブジェクトはファイルやネイティブリソースを保持することがあるため、using文やDisposeによる適切な破棄が欠かせません。

Windows専用アプリではSystem.Drawingを利用しやすい一方、ASP.NET Core、Linux、macOSなどを含むクロスプラットフォーム開発では、ImageSharpやSkiaSharpが候補になります。顔認識、輪郭抽出、映像解析などの高度な画像処理が必要な場合は、OpenCvSharpなどのOpenCV系ライブラリを検討しましょう。