C# だけで画像のグレースケール化の処理

Bitmap クラスには、GetPixel 、SetPixel というメソッドがあるので、それを使って一ピクセルずつ処理してみた。
が!!!

これがまたクソ遅い。半端じゃない。

なので、バイト列を操作して画像を操作する方法を検索して、出来たのでメモ。
ちなみに、ピクセル操作と、バイト列操作では、100 倍くらい処理時間が違う。
SetPixel、GetPixel を使って処理すると、9000ms かかるが、
バイト列を取り出して操作すると、90ms で終わる、といった具合。

int 型とかビットシフトとかで演算してるけど、浮動小数点の演算が得意な CPU があるとか無いとかで、下手に int でやるより float でやったほうが早いとの話もあるが、試してないのでよく分からない。

using System;
using System.Drawing; // 参照設定に System.Drawing を追加する必要アリ
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
// 画面を用意
Bitmap bmp = Bitmap.FromFile("hoge.jpg");

// 画像データをメモリ上に固定?
BitmapData bmpdata = bmp.LockBits(
    new Rectangle(0, 0, bmp.Width, bmp.Height),
    ImageLockMode.ReadWrite,
    PixelFormat.Format32bppArgb
);

// グレイスケール用のアレ
int rp = (int)(0.298912 * 1024);
int gp = (int)(0.586611 * 1024);
int bp = (int)(0.114478 * 1024);

// バイト配列にコピー
byte[] ba = new byte[bmp.Width * bmp.Height * 4];
Marshal.Copy(bmpdata.Scan0, ba, 0, ba.Length);

// 処理
int pixsize = bmp.Width * bmp.Height * 4;
for (int i = 0; i < pixsize; i += 4)
{
    // 画像のバイト列って、ARGB じゃなくて、BGRAになってるっぽい??
    // リトルエンディアンか。
    byte g = (byte)((bp * ba[i + 0] + gp * ba[i + 1] + rp * ba[i + 2]) >> 10);
    ba[i + 0] = g;      // ブルー
    ba[i + 1] = g;      // グリーン
    ba[i + 2] = g;      // レッド
    ba[i + 3] = 0xFF;   // アルファ
}

// 元のところに書き込む。
Marshal.Copy(ba, 0, bmpdata.Scan0, ba.Length);

bmp.UnlockBits(bmpdata);

こうして出来た Bitmap を表示したり保存すると、グレイスケール化できている事を確認出来る。

もし、もっと複雑な画像処理や、画像認識を行いたいという場合は、OpenCV を使うと良さそう。

schima.hatenablog.com – OpenCvSharpをつかう

C# で画像の中に一致するパターンがあるか探す

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください