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# で画像の中に一致するパターンがあるか探す

C# で画像のりサイズを行う

正確には、Bitmap クラスで表される画像を、縦横比を維持したまま任意の矩形内に収まるようにリサイズして返すメソッド。
適当なコードなので、頭悪いかもしれない。

// 参照設定が無い場合は、そっちも必要。
using System.Drawing;
public static Bitmap ResizeImage(Bitmap image, double dw, double dh)
{
    double hi;
    double imagew = image.Width;
    double imageh = image.Height;

    if ((dh / dw) <= (imageh / imagew))
    {
        hi = dh / imageh;
    }
    else
    {
        hi = dw / imagew;
    }
    int w = (int)(imagew * hi);
    int h = (int)(imageh * hi);

    Bitmap result = new Bitmap(w, h);
    Graphics g = Graphics.FromImage(result);
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
    g.DrawImage(image, 0, 0, result.Width, result.Height);

    return result;
}