C#プログラム画像処理

【C#】画像の輝度値の取得設定速度の比較

C#で画像の輝度値を取得/設定を行う場合は、GetPixel、SetPixelメソッドを使うと遅いのでLockBits~UnlockBitsでポインタをむき出して輝度値の取得/設定を行うのが定番となっていますが、自分自身でGetPixel、SetPixelメソッドを使った事が無かったので、いくつかの方法で処理時間を比較してみました。

 

【評価環境】

OS Windows10 64bit ver.1809
CPU Intel Core i7-7700K 4.2GHz
メモリ 32GB
.NET Framework 4.5.2
プラットフォーム Any CPU(32ビットを優先)
評価画像 jpeg 6000×4000画素 24bitカラー

です。

 

評価プログラムとしては、カラー画像の輝度値をR,G,Bそれぞれ取得し、値を明暗反転して輝度値を入れなおすプログラムを作成しています。

 

処理結果は、以下のようにポジ→ネガ→ポジ→ネガと繰り返すようになっています。

C# 画像の輝度値の取得設定時間C# 画像の輝度値の取得設定時間

 

処理時間は処理を5回行い平均した時間となります。

 

 

GetPixel、SetPixelを使った方法

平均処理時間は27439msec

本当に遅かった。。

しかもGetPixel、SetPixelではモノクロ画像(Format8bppIndexed)に対応していないので、モノクロ画像も多く扱う画像処理には不向きとなります。

 

LockBits~UnlockBitsでポインタ(Scan0)を取得し、データを配列にコピーしてから処理を行い、結果を元にコピーしなおす方法

平均処理時間は101msec

さすがにGetPixel、SetPixelよりはぜんぜん速いです。

 

MarshalクラスのReadByte、WriteByteメソッドでポインタ(Scan0)に直接、値を読み書きする方法

平均処理時間は273.6msec

この方法に少し期待していたのですが、配列にコピーした方が速かった。。

 

unsafeを使ったポインタ(Scan0)を直接読み書きする方法

平均処理時間は25.4msec

やっぱりunsafeはできれば使いたくないのですが、ここまで速いと使うのもあり??

ポインタを使うなら、C言語のライブラリにしておきたい気もしますが、C#だけで完結できるのもちょっと惹かれます。

 

ポインタで読み書きし、Parallel.Forをつかって処理を並列化する方法

平均処理時間は11.4msec

もう、unsafeは使うしかないでしょ!というレベルですが、やっぱり抵抗がある。。

 

処理を呼び出す側のプログラム

 

まとめ

それぞれの処理時間をまとめると以下の通りでした。

 

方法 処理時間(msec)
GetPixel、SetPixel 27439
LockBits、UnlockBitsで配列を介して処理 101
MarshalクラスのReadByte、WriteByte 273.6
unsafeのポインタで参照 25.4
unsafeのポインタの並列処理 11.4

 

結局GetPixel、SetPixelは論外でした。

あとは好みというか、ポリシーというか、意見の分かれるところだと思いますが、ポインタで処理するのは、やはり魅力です。

 

(2019.11.8追記)

.NET Framework4.5.232ビットを優先で評価を行っていたので、この部分を変えると処理速度に差が出るか?確認しました。

 

.NET Framework 4.7.2  32ビットを優先

方法 処理時間(msec)
GetPixel、SetPixel 27293.4
LockBits、UnlockBitsで配列を介して処理 101.8
MarshalクラスのReadByte、WriteByte 290.8
unsafeのポインタで参照 24.4
unsafeのポインタの並列処理 9.8

 

.NET Framework 4.7.2  32ビットを優先なし

方法 処理時間(msec)
GetPixel、SetPixel 21281.2
LockBits、UnlockBitsで配列を介して処理 85.4
MarshalクラスのReadByte、WriteByte 213.6
unsafeのポインタで参照 27.2
unsafeのポインタの並列処理 9.2

 

となりました。

ということで、.NET Frameworkの4.5.2と4.7.2の差はほとんどありませんでしたが、「32ビットを優先」のあるなしでは優先しない(64bitで動作)方が少し速い結果となりました。

 

関連記事

【C#】Bitmap画像データのメモリ構造
Bitmap画像の輝度値を参照するには、こちらのページ↓ で、Bitmap画像のメモリの値を参照するにはBitmapオブジェクトをLockBitsでロックし、BitmapDataのScan0プロパティを参照すれば、良いと書きましたが、このS...

 

画像処理のためのC#テクニックへ戻る

コメント

タイトルとURLをコピーしました