[C#] ビットマップにピクセル単位で高速にアクセスするには (GetPixel/SetPixel vs BitmapData 速度比較)

Bitmapクラスにはピクセル単位のアクセス関数 SetPixel/GetPixel が用意されているけど、ネットを見るとこれら関数はあまり速くないらしい。これら関数を使う代わりに、ビットマップデータをアンマネージ配列にコピーした 上で処理する方法が推奨されているみたい。画像処理ソフトを書く上で処理速度は重要だ。両者でどれぐらいの速度差があるのか調べてみた。

フルカラー画像を読み込んだ後、ピクセル単位にグレイスケールに変換するのに要した時間をミリ秒単位で計測。グレイスケールへの変換には、YCrCb変換のY値の計算式を使った。

方法1: SetPixel/GetPixelを使う

方法2: ビットマップデータをアンマネージ配列にコピーしてから処理する

方法3: ビットマップをシステムメモリにロックして直アクセスする (バイト単位)

方法2のMarshal.Copyのコストが掛かっているかも?という懸念から。

方法4: ビットマップをシステムメモリにロックして直アクセスする (ピクセル単位)

方法3では1ピクセルごとにMarshal.ReadByte/WriteByteが3回ずつ呼ばれるので、これら関数のオーバーヘッドが掛かっているかも?という懸念から。

テストに使ったデータ

画像1 lena.jpg (400×400ピクセル)
画像2 ff_x_e1_004.JPG (4896×3264ピクセル) – 富士フィルムサイトより拝借

フジノンレンズ XF18-55mmF2.8-4 R LM OIS : サンプル画像 | 富士フイルム
http://fujifilm.jp/personal/digitalcamera/x/fujinon_lens_xf18_55mmf28_4_r_lm_ois/sample_images/

テスト結果

処理時間は以下のようになった。方法3と4は、アンマネージ配列をアロケートしてデータをコピーするコストを考えた代替方法だったのだけど、画像が大きくなってもそのコストは大したことはなかったので気にする必要はなさそう。

# 処理内容 画像1(400×400ピクセル) 画像2(4896×3264ピクセル)
方法1 GetPixel/SetPixel関数を使用 597ms 28,187ms
方法2 アンマネージ配列にコピーした上で処理 10ms 268ms
方法3 システムメモリにロックして直アクセス (バイト単位) 19ms 523ms
方法4 システムメモリにロックして直アクセス (ピクセル単位) 15ms 400ms

環境: Windows 7 Ultimate SP1 (64bit), Intel Core i7 3.4GHz, RAM 16GB, Visual Studio 2012

3 thoughts on “[C#] ビットマップにピクセル単位で高速にアクセスするには (GetPixel/SetPixel vs BitmapData 速度比較)

  1. 参考になりました
    ですがDrawing以外の名前空間が必要だったのでまた調べる羽目に・・・
    こちらコメントにメモしておきます

    using System.Drawing;//Bitmap,Rectangleに必要
    using System.Drawing.Imaging;//Bitmapに必要
    using System.Runtime.InteropServices;//Marshalに必要

  2. 参考になりました。ありがとうございます。
    実際の画像データを参考に、RGBの値を確認したのですが、
    bufにはbgraの順で格納されているようです。したがって方法2ですと
    byte grey = (byte)(0.299 * buf[i+2] + 0.587 * buf[i+1] + 0.114 * buf[i]);
    になるかと思うのですがいかがでしょうか。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です