欲しいもの
物体のまわりに透明な領域がある画像から
物体が描かれている領域だけを
切り出してファイルに保存したい。
※画像はいらすとやの黒はんぺん
やること
- 画像ファイルを読み込む
- 透明でない領域を検出する
- 画像の一部を切り出す
- 画像を保存する
0. 準備
画像処理には System.Drawing
名前空間の API を使うので using しておく。
using System.Drawing;
.NET Framework の場合、System.Drawing
アセンブリへの参照を追加する必要あり。
.NET Core の場合、System.Drawing.Common
NuGet パッケージのインストールが必要。
dotnet add package System.Drawing.Common
Linux の場合、libgdiplus
も必要になるのでインストールしておく。
sudo apt install libgdiplus
1. 画像ファイルを読み込む
画像ファイルを読むには、System.Drawing.Bitmap クラスを使う。
var bmp = new Bitmap(filename);
Bitmapクラスは BMP, GIF, EXIF, JPG, PNG, TIFF 形式をサポートしている。
ただし今回は色に透明(アルファ値:不透明度)を含む画像が必要なので、下記のようなチェックを入れておく。
if (bmp.PixelFormat != PixelFormat.Format32bppArgb)
{
Console.WriteLine($"Not Format32bppArgb: {bmp.PixelFormat}");
return;
}
2. 透明でない領域を検出する
Bitmapオブジェクトの透明でない領域の矩形を検出する。

ピクセルの色の検出は、画像をバイト列に変換して値を参照することでできる。
PixelFormat.Format32bppArgb
フォーマットでは1ピクセル4バイト、BGRAの順に1バイトずつ入る。※リトルエンディアンの場合
例えば次のようなサイズ 2x2 の画像があった場合、
こんなバイト列になる。
透明でないピクセルの上、下、左、右(y0, y1, x0, x1)の座標を検出し、Rectangleオブジェクトを返すメソッド。
static Rectangle GetRect(Bitmap bmp)
{
// 画像のピクセルを byte[] にコピーする
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var bytes = Math.Abs(bmpData.Stride) * bmp.Height;
var rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
int x0 = bmp.Width;
int y0 = bmp.Height;
int x1 = 0;
int y1 = 0;
// 透明でないピクセルを探す
for (int i = 3; i < rgbValues.Length; i += 4)
{
// Aの値が0なら透明ピクセル
if (rgbValues[i] != 0)
{
int x = i / 4 % bmp.Width;
int y = i / 4 / bmp.Width;
if (x0 > x) x0 = x;
if (y0 > y) y0 = y;
if (x1 < x) x1 = x;
if (y1 < y) y1 = y;
}
}
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
}
ちなみに、Bitmap クラスにはピクセルの色を取得する GetPixel(int x, int y)
なんてメソッドも用意されているが、クソ遅いので LockBits
を使って高速化している。
3. 画像の一部を切り出す
Bitmap オブジェクトから Rectangle で指定した領域を切り出し、新しい Bitmap オブジェクトを生成するメソッド。
Bitmap Crop(Bitmap bmp, Rectangle rect)
{
var newbmp = new Bitmap(rect.Width, rect.Height);
using (var g = Graphics.FromImage(newbmp))
{
g.DrawImage(bmp, 0, 0, rect, GraphicsUnit.Pixel);
}
return newbmp;
}
4. 画像を保存する
newbmp.Save(filename);
ちょっと分かりづらいが、はんぺんの周りの領域が狭くなっている food_kuro_hanpen2.png が生成された。