プログラミングを解説したり、音楽の紹介とか、スポーツ・趣味の紹介とか。1人でも見てくれる人がいる限り、更新を続けます。
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
C#言語では、ビットマップファイル(bmpファイル)のピクセル値を取り込む際、BitmapクラスのGetPixelメソッドを使用するのがわかりやすいです。このページの記事でも画像編集や画像処理のため、画像データ取得に使ってきました。
しかし、実はこのGetPixelメソッド、すっごく遅いです。画像処理をするときも、処理の速度より、このピクセルデータ取り込みに悩んでいる人も多いと思います。そこで、この記事では、私の知っている高速にビットマップのピクセル値を取り込む方法をいくつか紹介し、速度比較をしてみたいと思います。


私の知っているビットマップデータ取得方法は次の4つです。
上2つは、Windowsアプリで使われるクラスライブラリであるSystem.Drawing.dllを使います。下2つは、WPFアプリで使われるPresentationCore.dllとWindowsBase.dllを使い、名前空間はSystem.Windows.Media.Imagingです。

1.BitmapクラスのGetPixelメソッドで取得する方法
最もわかりやすい方法だと思いますが、速度がとても遅いようです。

2.BitmapDataをMarshalクラスのCopyメソッドで取得する方法

アンマネージなメモリポインタからマネージ配列にデータをコピーするもので、とても高速に動作します。

3.WPFのBitmapImageクラスのCopyPixelsメソッドを使った場合
WPFアプリは、GUIにアニメーションを使えたり、ちょっとこだわったアプリケーションを作るときに使うもので、Visual StudioのWindowsアプリとは区別されてますが、上にあげたdllを追加されていればライブラリ内のクラスを使うことができます。
この方法ちょっとまだわからないことがあって、ビットマップファイルを読み込む際に、なぜかRGBのファイルなのにARGBで読み込まれてしまいます。なので、1バイト多く読み込む必要があり、少しタイムロスします。

4.WPFのBmpBitmapDecoderからBitmapSourceを作る場合
BmpBitmapDecoderを使えばRGBで取り込めるようになり、高速に取り込めます。

○実験
下記のプログラムで上記1〜4までの方法のパフォーマンスを計測
入力画像には、1000x1000の24ビットカラー画像を使用する。
時間計測には、DateTimeを使う。
4回実行したときの平均時間。
実行には、Releaseビルドで、デバッグなし。

○結果
  1. 1785 ms
  2. 8 ms
  3. 25 ms
  4. 10ms


やっぱりGetPixel遅すぎですね〜。今まで、このブログでこのメソッド使い続けてましたが変えたほうがいいかな〜。
2番目のポインタを使う方法はやっぱり最速となりました。でも、ポインタを意識しなくても良い4番目の方法もなかなかの結果が出ています。

今回作成したC#のプログラムを紹介します。


// C#のビットマップピクセル値取得速度比較
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;

namespace BitmapLoadSpeedTest
{
class Program
{
static void Main(string[] args)
{
DateTime start = DateTime.Now;
DateTime end = DateTime.Now;

// 一つ目の方法
// bitmapクラスでGetPixelメソッドを使った場合
start = DateTime.Now;
Bitmap bitmap1 = new Bitmap("test1.bmp");
byte[] data1 = new byte[bitmap1.Width * bitmap1.Height * 3];
for (int i = 0; i < bitmap1.Height; i++)
{
for (int j = 0; j < bitmap1.Width; j++)
{
// ピクセル値取得
Color color = bitmap1.GetPixel(j, i);
data1[i * bitmap1.Width * 3 + j * 3] = color.B;
data1[i * bitmap1.Width * 3 + j * 3 + 1] = color.G;
data1[i * bitmap1.Width * 3 + j * 3 + 2] = color.R;
}
}
end = DateTime.Now;
Console.WriteLine((end - start).TotalMilliseconds.ToString());


// 二つ目の方法
// bitmapクラスでMarshalクラスのCopyメソッドを使った場合
start = DateTime.Now;
Bitmap bitmap2 = new Bitmap("test2.bmp");

// LockBitsメソッドでBitmapDataの参照を取得
BitmapData bitmapData =
bitmap2.LockBits(
new Rectangle(0, 0, bitmap2.Width, bitmap2.Height),
ImageLockMode.ReadWrite,
bitmap2.PixelFormat);
// bitmapDataポインタ取得
IntPtr bitmapPtr = bitmapData.Scan0;

byte[] data2 = new byte[bitmap2.Width * bitmap2.Height * 3];

// MarshalクラスのCopyメソッドで配列にコピー
Marshal.Copy(
bitmapPtr, data2, 0, bitmap2.Width * bitmap2.Height * 3);

// bitmapのUnlock
bitmap2.UnlockBits(bitmapData);
end = DateTime.Now;
Console.WriteLine((end - start).TotalMilliseconds.ToString());



// 三つ目の方法
// WPFのBitmapImageクラスのCopyPixelsメソッドを使った場合
start = DateTime.Now;

// BitmapImageインスタンス作成
BitmapImage bmpImage = new BitmapImage(
new Uri("test3.bmp", UriKind.Relative));
byte[] data3 =
new byte[bmpImage.PixelWidth * bmpImage.PixelHeight * 4];

// CopyPixelsメソッドで配列にコピー
bmpImage.CopyPixels(data3, bmpImage.PixelWidth * 4, 0);
end = DateTime.Now;
Console.WriteLine((end - start).TotalMilliseconds.ToString());



// 四つ目の方法
// WPFのFileStreamとBmpBitmapDecoderからBitmapSourceを作る場合
start = DateTime.Now;

// BmpBitmapDecoderでビットマップ取り込み
BmpBitmapDecoder decoder =
new BmpBitmapDecoder(
new Uri("test4.bmp", UriKind.Relative),
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);

// ピクセル値を取り出すためBitmapSource作成
BitmapSource bitmapSource = decoder.Frames[0];
byte[] data4 =
new byte[bitmapSource.PixelWidth * bitmapSource.PixelHeight * 3];

// CopyPixelsメソッドで配列にコピー
bitmapSource.CopyPixels(data4, bitmapSource.PixelWidth * 3, 0);
end = DateTime.Now;
Console.WriteLine((end - start).TotalMilliseconds.ToString());
}
}
}

ソースコードは、以下のURLから
http://colnagow61s.me.land.to/BitmapLoadSpeedTest.zip

↓面白かった
パターン認識と機械学習 上 - ベイズ理論による統計的予測パターン認識と機械学習 上 - ベイズ理論による統計的予測
元田 浩 栗田 多喜夫 樋口 知之

シュプリンガー・ジャパン株式会社 2007-12-10
売り上げランキング : 19435
おすすめ平均

Amazonで詳しく見る


C#クックブック 第3版C#クックブック 第3版
鈴木 幸敏

オライリージャパン 2008-08-25
売り上げランキング : 17468
おすすめ平均

Amazonで詳しく見る
コメント
この記事へのコメント
どうも、始めまして! 近所(FC2)でXNAのゲーム開発をしてますKaru_gamoと申します。bmpの解析大変、勉強になりました。というか、こるなごさんの記事はどれもハイレベルなので、一気読みさせていただきましたwww

仙台にお住まいなんですね〜。ラーメンの記事を見て行ってみようかな…と思ったら…うぅ遠い…もし仙台に行く用事があるときにはぜひ活用させていただきます!

これを機にリンクさせていただいてもよろしいでしょうか??
2009/01/10(土) 09:09 | URL | Karu_gamo #GCA3nAmE[ 編集]
はじめまして、Karu_gamoさん

XNAは最近中断していますが、今の作業が終わったらまた始めると思うので、相互リンクOKです。

よろしくお願いします。
2009/01/10(土) 17:05 | URL | こるなご #-[ 編集]
ちなみに住んでる所は米沢市(山形県)です
2009/01/10(土) 17:06 | URL | こるなご #-[ 編集]
確かにGetPixelは遅いですね〜
自分も早い方法を探していたところでした。
わかりやすく書かれていました。
参考にしました。
2009/03/13(金) 17:55 | URL | windy #Qa9hNd.E[ 編集]
コメントを投稿する
URL:
Comment:
Pass:
秘密: 管理者にだけ表示を許可する
 
トラックバック
この記事のトラックバックURL
http://thorshammer.blog95.fc2.com/tb.php/254-38022f1e
この記事にトラックバックする(FC2ブログユーザー)
この記事へのトラックバック
QLOOKアクセス解析