System.Drawing.Bitmap画像を使った高速な画像処理プログラミング その2

 

.Net Frameworkベンチマークの公開にはややこしいルールがあるようなので、
ベンチマーク結果を公開する代わりにベンチマークを取れるプログラムを公開します。
こうにすれば画像処理プログラムの雛形としても使えますし…

 

<bench1.cs : SetPixel, GetPixel を使う方法>
using System;
using System.Drawing;
using System.Runtime.InteropServices;

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  public static void Main(string[ ] args){
    Bitmap bmp = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    for (int j = 0; j < bmp.Height; ++j){
      for (int i = 0; i < bmp.Width; ++i){
        Color c = bmp.GetPixel(i,j) ;
        byte r = c.R < TH ? (byte)0 : (byte)255, g = c.G < TH ? (byte)0 : (byte)255, b = c.B < TH ? (byte)0 : (byte)255;
        bmp.SetPixel(i,j, Color.FromArgb(r,g,b)) ;
      }
    }
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

  

<bench2.cs : LockBits, UnlockBits, BitmapData を使う方法>
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

 

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  static void CopyBmp(Bitmap src, Bitmap dst){
    Graphics g = Graphics.FromImage(dst) ;
    g.DrawImageUnscaled(src,0,0) ;
    g.Dispose() ;
  }

  public static void Main(string[ ] args){
    Bitmap bmp_ = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    Bitmap bmp = new Bitmap(bmp_.Width, bmp_.Height, PixelFormat.Format24bppRgb);
    CopyBmp(bmp_, bmp);
    bmp_.Dispose();
    BitmapData bd = bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
    int stride = bmp.Width * 3;
    byte[ ] data = new byte[stride * bmp.Height];
    Marshal.Copy(bd.Scan0, data, 0, data.Length);
    for (int j = 0, ji = 0; j < bmp.Height; ++j, ji += stride){
      for (int i = 0; i < bmp.Width * 3; i += 3){
        int p = i + ji;
        data[p] = data[p] < TH ? (byte)0 : (byte)255; // R
        data[p+1] = data[p+1] < TH ? (byte)0 : (byte)255; // G
        data[p+2] = data[p+2] < TH ? (byte)0 : (byte)255; // B
      }
    }
    Marshal.Copy(data, 0, bd.Scan0, data.Length);
    bmp.UnlockBits(bd);
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

 

 

<bench3.cs : MemoryStream にBMPファイルを書き出す方法>
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

 

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  static void CopyBmp(Bitmap src, Bitmap dst){
    Graphics g = Graphics.FromImage(dst) ;
    g.DrawImageUnscaled(src,0,0) ;
    g.Dispose() ;
  }

  static int readint32(MemoryStream ms){
    byte b = new byte[4];
    ms.Read(b, 0, 4);
    return (int)b[0] + (int)b[1] * 0x100 + (int)b[2] * 0x10000 + (int)b[3] * 0x1000000;
  }

  public static void Main(string args){
    Bitmap bmp_ = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    Bitmap bmp = new Bitmap(bmp_.Width, bmp_.Height, PixelFormat.Format24bppRgb);
    CopyBmp(bmp_, bmp);
    bmp_.Dispose();
    MemoryStream ms = new MemoryStream();
    bmp.Save(ms, ImageFormat.Bmp);
    ms.Seek(10, SeekOrigin.Begin);
    int startRaw = readint32(ms);
    ms.Seek(startRaw, SeekOrigin.Begin);
    int stride = ((bmp.Width * 3 + 3) / 4) * 4;
    byte[] data = new byte[stride * bmp.Height];
    ms.Read(data, 0, data.Length);
    for (int j = 0, ji = (bmp.Height - 1) * stride; j < bmp.Height; ++j, ji -= stride){
      for (int i = 0; i < bmp.Width * 3; i += 3){
        int p = i + ji;
        data[p] = data[p] < TH ? (byte)0 : (byte)255; // B
        data[p+1] = data[p+1] < TH ? (byte)0 : (byte)255; // G
        data[p+2] = data[p+2] < TH ? (byte)0 : (byte)255; // R
      }
    }
    ms.Seek (startRaw, SeekOrigin.Begin) ;
    ms.Write (data, 0, data.Length);
    bmp.Dispose();
    bmp_ = new Bitmap(ms);
    bmp = new Bitmap(bmp_);
    ms.Dispose();
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

  

3例ともSystem.Runtime.InteropServices名前空間を参照していますが、3番目については、timeGetTimeのためだけに使っていますので、その部分を外せばSystem.Runtime.InteropServicesの参照を外すことができます。