PILのImageの画素に(x,y)座標でアクセスする速度を計測した。
画素へのアクセス方法
実験方法
1024x1024ピクセルのカラー画像の各画素をモノクロ2値化する処理を上記の方法1~6で実装し、処理時間を計測した。
- スクリプト : PixelAccessTest.py · GitHub
- 使用した画像 : 1024x1024ピクセル, 24ビットカラーのBMP画像 (こちら)
実験結果
方法 | 処理時間[msec] |
---|---|
方法1 | 1717 |
方法2 | 382 |
方法3 | 340 |
方法4 | 534 |
方法5 | 2899 |
方法6(初回) | 369 |
方法6(2回目以降) | 3.7 |
※ 方法6に関しては、初回の関数呼び出し時にJITコンパイルされるので、初回だけ実行時間が長くなる。
【方法1】
Image.getpixel/putpixel は、画素の値を取得/設定する。
(x, y)でアクセスでき、最も素直な方法だが、かなり遅い。
【方法2】
Image.load は、画素アクセス用のオブジェクトを返す。
方法1よりかなり速く、[x, y] でアクセスできるので面倒もない。
【方法3】
Image.getdata/putdata は、画素の値の1次元配列を取得/設定する。
方法2よりも少し速いが、[y * (1ラインの画素数) + x]でアクセスするのが少し面倒。
【方法4】
Image.tobytes/frombytes は、画像データのバイト配列を取得/設定する。
[(y * (1ラインの画素数) + x) * (1画素のバイト数)]でアクセスするのが面倒なうえ、速くもない。
【方法5】
numpy.asarray / Image.fromarray は、 画像データ ⇔ NumPy配列 の変換をおこなう。
[y, x] でアクセスできる。( [x, y] でないことに注意 )
しかし、NumPy配列はfor文で回すとかえって低速であり、この例では方法1より遅くなった。
【方法6】
NumbaによるJITコンパイルで方法5を高速化した。
制約や注意点は多々あるものの、けた違いな高速化ができた。
結論
パフォーマンをそれほど求めないのであれば、方法2(Image.load) か 方法3(Image.getdata/putdata) が良さそう。手間をかけてもパフォーマンスを求めるのであれば 方法6(NumPy + Numba)が圧倒的に高速。
参考