|
|||||
<このサンプルの概要> .NETのBitmap処理は遅いので高速化するサンプル(C#.NET)を作ってみました。 この方法で通常のBitmapで32秒掛かった処理が4秒で出来るようになりました。 C#.NETでBitmap処理を高速化する方法には次の2つがあります。 1.System.Runtime.InteropServices.Marshalを使用する方法 2.アンセーフコード(unsafe)を使用する方法 ※アンセーフコード(unsafe)を使用する方法の方が若干速いと思います。 ※2つのサンプルがありますので比較出来ます。 このサンプルのポイントとなるキーワードは以下の通りです。 (1)System.Drawing.Imaging.BitmapData.Scan0 (2)System.Drawing.Imaging.BitmapData.Stride (3)GetPixel (4)SetPixel (5)unsafe ※アンセーフコード(unsafe)を使用する方法 (6)LockBits ※アンセーフコード(unsafe)を使用する方法 (7)UnlockBits ※アンセーフコード(unsafe)を使用する方法 VB.NETのサンプルはこちらです。 Bitmap処理を高速化するサンプル(VB.NET) ★Bitmap処理を高速化するためのクラス(BitmapPlus.cs) ※System.Runtime.InteropServices.Marshalを使用する方法 using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace BitmapPlus { /// <summary> /// Bitmap処理を高速化するためのクラス /// </summary> class BitmapPlus { /// <summary> /// オリジナルのBitmapオブジェクト /// </summary> private Bitmap _bmp = null; /// <summary> /// Bitmapに直接アクセスするためのオブジェクト /// </summary> private BitmapData _img = null; /// <summary> /// コンストラクタ /// </summary> /// <param name="original"></param> public BitmapPlus(Bitmap original) { // オリジナルのBitmapオブジェクトを保存 _bmp = original; } /// <summary> /// Bitmap処理の高速化開始 /// </summary> public void BeginAccess() { // Bitmapに直接アクセスするためのオブジェクト取得(LockBits) _img = _bmp.LockBits(new Rectangle(0, 0, _bmp.Width, _bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb); } /// <summary> /// Bitmap処理の高速化終了 /// </summary> public void EndAccess() { if (_img != null) { // Bitmapに直接アクセスするためのオブジェクト開放(UnlockBits) _bmp.UnlockBits(_img); _img = null; } } /// <summary> /// BitmapのGetPixel同等 /// </summary> /// <param name="x">X座標</param> /// <param name="y">Y座標</param> /// <returns>Colorオブジェクト</returns> public Color GetPixel(int x, int y) { if (_img == null) { // Bitmap処理の高速化を開始していない場合はBitmap標準のGetPixel return _bmp.GetPixel(x, y); } // Bitmap処理の高速化を開始している場合はBitmapメモリへの直接アクセス IntPtr adr = _img.Scan0; int pos = x * 3 + _img.Stride * y; byte b = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 0); byte g = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 1); byte r = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 2); return Color.FromArgb(r, g, b); } /// <summary> /// BitmapのSetPixel同等 /// </summary> /// <param name="x">X座標</param> /// <param name="y">Y座標</param> /// <param name="col">Colorオブジェクト</param> public void SetPixel(int x, int y, Color col) { if (_img == null) { // Bitmap処理の高速化を開始していない場合はBitmap標準のSetPixel _bmp.SetPixel(x, y, col); return; } // Bitmap処理の高速化を開始している場合はBitmapメモリへの直接アクセス IntPtr adr = _img.Scan0; int pos = x * 3 + _img.Stride * y; System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 0, col.B); System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 1, col.G); System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 2, col.R); } } } ★Bitmap処理を高速化するためのクラス(BitmapPlus.cs) ※アンセーフコード(unsafe)を使用する方法 // ★★★注意★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ // このサンプルではunsafeを使用しているため、プロジェクトのプロパティで // アンセーフコードの許可を設定する必要があります。 // ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace BitmapPlus { /// <summary> /// Bitmap処理を高速化するためのクラス /// </summary> class BitmapPlus { /// <summary> /// オリジナルのBitmapオブジェクト /// </summary> private Bitmap _bmp = null; /// <summary> /// Bitmapに直接アクセスするためのオブジェクト /// </summary> private BitmapData _img = null; /// <summary> /// コンストラクタ /// </summary> /// <param name="original"></param> public BitmapPlus(Bitmap original) { // オリジナルのBitmapオブジェクトを保存 _bmp = original; } /// <summary> /// Bitmap処理の高速化開始 /// </summary> public void BeginAccess() { // Bitmapに直接アクセスするためのオブジェクト取得(LockBits) _img = _bmp.LockBits(new Rectangle(0, 0, _bmp.Width, _bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb); } /// <summary> /// Bitmap処理の高速化終了 /// </summary> public void EndAccess() { if (_img != null) { // Bitmapに直接アクセスするためのオブジェクト開放(UnlockBits) _bmp.UnlockBits(_img); _img = null; } } /// <summary> /// BitmapのGetPixel同等 /// </summary> /// <param name="x">X座標</param> /// <param name="y">Y座標</param> /// <returns>Colorオブジェクト</returns> public Color GetPixel(int x, int y) { if (_img == null) { // Bitmap処理の高速化を開始していない場合はBitmap標準のGetPixel return _bmp.GetPixel(x, y); } unsafe { // Bitmap処理の高速化を開始している場合はBitmapメモリへの直接アクセス byte* adr = (byte*)_img.Scan0; int pos = x * 3 + _img.Stride * y; byte b = adr[pos + 0]; byte g = adr[pos + 1]; byte r = adr[pos + 2]; return Color.FromArgb(r, g, b); } } /// <summary> /// BitmapのSetPixel同等 /// </summary> /// <param name="x">X座標</param> /// <param name="y">Y座標</param> /// <param name="col">Colorオブジェクト</param> public void SetPixel(int x, int y, Color col) { if (_img == null) { // Bitmap処理の高速化を開始していない場合はBitmap標準のSetPixel _bmp.SetPixel(x, y, col); return; } unsafe { // Bitmap処理の高速化を開始している場合はBitmapメモリへの直接アクセス byte* adr = (byte*)_img.Scan0; int pos = x * 3 + _img.Stride * y; adr[pos + 0] = col.B; adr[pos + 1] = col.G; adr[pos + 2] = col.R; } } } } ★テスト用フォーム(Form1.cs) namespace BitmapPlus { public partial class Form1 : Form { private void button1_Click(object sender, EventArgs e) { // Bitmapオブジェクト生成 Bitmap bmp; if (pictureBox1.Image == null) { // Bitmapを新規生成 bmp = new Bitmap(512,512); } else { // BitmapをpictureBoxから取得 bmp = new Bitmap(pictureBox1.Image); } // Bitmap処理の高速化開始 BitmapPlus bmpP = new BitmapPlus(bmp); bmpP.BeginAccess(); for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { // Bitmapの色取得 //Color bmpCol = bmp.GetPixel(i, j); Color bmpCol = bmpP.GetPixel(i, j); if (bmpCol.R == (byte)0) { // Bitmapの色設定 //bmp.SetPixel(i, j, Color.FromArgb(255, 255, 0, 0)); bmpP.SetPixel(i, j, Color.FromArgb(255, 255, 0, 0)); } else { // Bitmapの色設定 //bmp.SetPixel(i, j, Color.FromArgb(255, 0, 0, 0)); bmpP.SetPixel(i, j, Color.FromArgb(255, 0, 0, 0)); } } } // Bitmap処理の高速化終了 bmpP.EndAccess(); pictureBox1.Image = bmp; } } } |