2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C#で画像処理:透明な領域を切り取る

Last updated at Posted at 2020-01-03

欲しいもの

物体のまわりに透明な領域がある画像から
1.jpg
物体が描かれている領域だけを
2.png
切り出してファイルに保存したい。
3.png

※画像はいらすとやの黒はんぺん

やること

  1. 画像ファイルを読み込む
  2. 透明でない領域を検出する
  3. 画像の一部を切り出す
  4. 画像を保存する

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オブジェクトの透明でない領域の矩形を検出する。

4.png

ピクセルの色の検出は、画像をバイト列に変換して値を参照することでできる。

PixelFormat.Format32bppArgb フォーマットでは1ピクセル4バイト、BGRAの順に1バイトずつ入る。※リトルエンディアンの場合

例えば次のようなサイズ 2x2 の画像があった場合、
6.png
こんなバイト列になる。
7.png

透明でないピクセルの上、下、左、右(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 が生成された。
5.png

ソースコード全体

GitHub

参考

2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?