Quantcast
Channel: 午後わてんのブログ
Viewing all articles
Browse latest Browse all 420

BitmapSourceでの画像処理速度、unsafe、ポインタ、ビルドモード

$
0
0

WPFのBitmapSourceの画像処理速度向上するためにいろいろ試してみた
初めて使ったunsafeでポインタが一番効果があった

画像処理の内容は色の反転、RGBそれぞれの値を逆にするだけのもので
50だったら255-50=205にする
単純な軽い処理


イメージ 1
15種類、基準になるのは処理1で

結果
イメージ 2
ポインタを使ったら1.926秒が0.874秒
約2倍速くなった

イメージ 4


使った画像は
イメージ 3
1024x768ピクセル
なぜこの画像を選んだのかコレガワカラナイ

PC環境は
OS Windows 10 Home 64bit
CPUAMD PhenomⅡ X3 720 @3.0GHz
メモリ DDR2 PC-800 8GB
っていう組んでから10年目になるパソコン

ビルドの設定?
イメージ 7
Releaseでx64


デザイン画面
イメージ 5


基準になるコードはこれ

private void ColorReverce1()
{
var wb = new WriteableBitmap(OriginBitmap);
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);
long p;
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
p = y * stride + x * 4;
//RGBを反転、アルファ値はそのまま
pixels[p] = (byte)(255 - pixels[p]);
pixels[p + 1] = (byte)(255 - pixels[p + 1]);
pixels[p + 2] = (byte)(255 - pixels[p + 2]);
}
}

wb.WritePixels(new Int32Rect(0, 0, w, h), pixels, stride, 0);
MyImage.Source = wb;
}


OriginBitmapが元になる画像でBitmapSource
PixelFormatはPbgra32
これからWriteableBitmapを作ってCopyPixelsからbyte配列を作って
画像処理でRGBを反転、最後に確認用にImageコントロールの
MyImageに表示して完了
これを100回繰り返した時間を計測
なので実際の処理時間は1/100になる

時間計測はStopwatch
イメージ 6



イメージ 11
↑普通↓ポインタ使用
イメージ 12
見た感じだとほとんど一緒だねえ、赤いところがbyte型ポインタ?の変数を使っているところ

イメージ 13
WriteableBitmapのBackBufferってのからポインタを取得できる
バックバッファーってのは後の画面、処理用の画面ってとこかなあ
対するのはフロント、メインとかで前の画面で実際に見えている画面
処理の途中は見えない後で行って結果だけを表示する感じ

ポインタってのはメモリのアドレスみたいな感じ
そのデータの先頭のメモリのアドレス
今回だとbyte型のポインタを使うからbyte* ptrってすると
ptrがbyte型のポインタになるみたいで
配列のインデックスみたいにptr[0]ってすると先頭のアドレスから1byte分のデータになって、ptr[1]だと先頭から1byte分進んだアドレスにある1byte分のデータ
ptr[2]だと先頭から2byte分進んだアドレスにある1byte分のデータ
かなあ

WriteableBitmap.Lockメソッドでメモリのロック
OSはメモリに入っているデータを最適な状態にしようと移動しているらしい
ハードディスクのデフラグみたいな感じかしら
処理中に勝手に移動されたら困るから、その対象から一時的に外して移動されないようにするのがWriteableBitmapのLockメソッドで、これを使った後は必ずUnlockメソッドを実行してもとに戻す



ポインタを使う前準備

プロジェクトのビルドのモードをunsafeってのにする必要があるみたいで
イメージ 8
ソリューションエクスプローラーで
アプリの名前あたりを右クリックのメニューでプロパティ

プロパティ画面
イメージ 9
ビルドタブにあるアンセーフコードの許可にチェックを入れる
これでコードでunsafeってのが使えるようになって
unsafeが使えるとポインタが使えるようになる
もしこうしないでunsafeって書いても
イメージ 10
って使えない
最初はこれがなんのことかわからなかった




処理2
イメージ 14
Pixelsのインデックス計算を少し変更
530行目でp = y * stride + x * 4;
ってしてたのを
527行目とで分けた、少し計算量が減ったはずなんだけどタイムは変わらずだった
1.926秒が1.882秒

処理3
イメージ 15
処理1のx,yの2重になっていたループを1重ループに変更
1.926秒が1.784秒
1割弱速くなった
ループ処理の合計は同じなんだけど速くなったから
forは減らしたほうがいいみたい


処理4
イメージ 16
処理3のRGBの各値変更のところをforにしたら遅くなった
1.784秒が1.927秒


処理5
イメージ 17
ここからポインタを使用した処理になる
処理1のポインタ版+ビット反転版
1.926秒が0.786秒
2倍以上速くなった
ビット反転
~を付けるとビット反転
p[0]が0000 0000だったときに~を付けて~p[0]にすると
~p[0]は1111 1111になる
ちょうど色の反転に使えたのでこれも初めて使ってみた

処理6
イメージ 18
処理5をビット反転じゃなくて普通の引き算版
つまり処理1のポインタ版
1.926秒が0.874秒
画像処理でポインタを使うときはこれが基本になりそう
ビット反転版より遅いけど何回か計測すると逆転することもあったので
普通の引き算もビット反転も変わらないかもと、このときは思っていた

処理7
イメージ 19
処理6のLockしない版
0.874秒→0.889秒
ほとんど変わらないのでこれは使う意味がない

処理8
イメージ 20
処理6のパラレル版、yループをパラレルに変更で
0.874秒→0.973秒
なぜか遅くなった
でもこれはもっと重たい画像処理なら処理6より速くなるはず
…だと思う

処理9
イメージ 21
処理6のx,yでの2重ループを1重ループにしたもの
0.874秒→0.898秒
速くなると思ったけど変わらず、意外

処理10
イメージ 22
処理9の改変
0.878秒→1.405秒
思った以上に遅くなった
PixelFormat.Pbgra32はアルファ値もあるけど
色反転の処理ではアルファ値は変更したくないのでifで判定して弾いている
その処理が重たいみたい、回数が多いからねえ
でもこれでパラレル版にできる、パラレルなら速くなるはず

処理11
イメージ 23
処理10のパラレル版
1.405秒→2.209秒
速くなると思ったのに遅くなった、しかもかなり遅い
アルファ値がじゃま、ifをなくしたい

処理12
イメージ 24
処理11を改変
アルファ値がじゃまならPixelFormatを変更すればいいじゃない
FormatConvertedBitmapを使ってBgr24に変更して
色変反転処理を軽くしたら
2.209秒→1.938秒
速くなったけど…遅い
PixelFormat変換部分が重たいみたい

処理13
イメージ 25
処理6の改変
0.874秒→1.264秒と遅くなった
処理6ではWriteableBitmapのBackBufferからのポインタだったのを
もとのBitmapSourceからCopyPixelsで得たbyte配列のポインタに変更しただけ
なんだけど遅くなったねえ
CopyPixelsと最後のBitmapSource.Createが重たいのかなあ

処理14
イメージ 26
処理9のビット反転版
0.898秒→0.723秒
今回試した中で最速になった!
普通の引き算よりビット反転のほうが速いのかなあ

処理15
イメージ 27
処理8のビット反転版
0.973秒→0.849秒
やっぱりビット反転のほうが速いみたい

DebugとReleaseと32bitと64bit


イメージ 29
いつもはDebug+AnyCPUっていうモードでコードを書いて

イメージ 30
できたなあってときだけRelease+x64にしているんだけど
このモードの違いで処理時間がかなり変わるのに気づいた
多少は違うだろうなあと思っていたけど多少じゃなくて
イメージ 28
5倍以上差が出るのもあった
できたときはRelease+x64でいいんだけど、作っている途中はどんな設定にしたらいいのかなあ、いままでビルドの設定を気にしたことなかったからさっぱりわからん
けどCPUもOSも64bitだから32bit優先はないなあ

イメージ 31


完走した感想
  • ポインタ使ったほうが速い
  • ビット反転のほうが引き算より速い
  • forでのループ総数がおなじなら2重より1重のほうが速い
  • 今回みたいに軽い画像処理ならパラレルは使わなくてもいい

コード




参照したところ
WriteableBitmapの画素をポインタから操作する - schima.hatenablog.com
http://schima.hatenablog.com/entry/20100918/1284817562
今回のコードはこちらを見よう見まねで書いた

.NETによる画像処理の高速化Tips:unsafe編(改稿:2015/11/08)
https://qiita.com/Nuits/items/da8c11e5b284ad6cb90a
WPFじゃないけどbyte配列のポインタの使い方はこちらから



関連記事
2018/3/26
処理速度比較、画像の使用色数を数える、重複なしのリストのHashSetも速いけど配列+ifも速かった ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15430276.html



2018/3/15
Parallelクラスを使ってもっと速く減色 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15413665.html






Viewing all articles
Browse latest Browse all 420

Trending Articles