![]() |
OpenCV 5.0.0
Open Source Computer Vision
|
画像処理では毎秒膨大な数の演算を扱うため、コードが正しい解を返すだけでなく、それを最速で返すことも必須である。そこでこの章では、次のことを学ぶ:
OpenCV のほかに、Python も実行時間の計測に役立つ time モジュールを提供している。別のモジュール profile は、コード中の各関数がどれだけの時間を要したか、関数が何回呼び出されたかなど、コードに関する詳細なレポートを得るのに役立つ。しかし IPython を使っていれば、これらの機能はすべて使いやすい形で統合されている。重要なものをいくつか見ていく。詳細については 追加資料 の節のリンクを参照すること。
cv.getTickCount 関数は、ある基準イベント(マシンの電源が入った瞬間など)からこの関数が呼び出された瞬間までのクロックサイクル数を返す。したがって、関数の実行前後にこれを呼び出せば、関数の実行に使われたクロックサイクル数が得られる。
cv.getTickFrequency 関数は、クロックサイクルの周波数、すなわち毎秒のクロックサイクル数を返す。よって実行時間を秒単位で求めるには、次のようにすればよい:
次の例で実演する。以下の例では、5 から 49 までの奇数サイズのカーネルでメディアンフィルタリングを適用する。(結果がどのような見た目になるかは気にしなくてよい。それはここでの目的ではない):
OpenCV の関数の多くは SSE2、AVX などを用いて最適化されている。最適化されていないコードも含まれている。したがって、システムがこれらの機能をサポートしていれば(ほぼすべての最新プロセッサがサポートしている)、それらを活用すべきである。これはコンパイル時に既定で有効になっている。そのため OpenCV は、有効なら最適化されたコードを、そうでなければ最適化されていないコードを実行する。cv.useOptimized() で有効・無効を確認でき、cv.setUseOptimized() で有効・無効を切り替えられる。簡単な例を見てみよう。
ご覧のとおり、最適化されたメディアンフィルタリングは最適化されていない版より2倍高速である。ソースを調べると、メディアンフィルタリングが SIMD 最適化されていることがわかる。そのため、これを使ってコードの先頭で最適化を有効にできる(既定で有効になっている点は覚えておくこと)。
似たような2つの処理の性能を比較したくなることがある。IPython にはこれを行うためのマジックコマンド timeit がある。より正確な結果を得るためにコードを複数回実行する。これも単一行のコードの計測に適している。
たとえば、次の加算演算 x = 5; y = x**2、x = 5; y = x*x、x = np.uint8([5]); y = x*x、y = np.square(x) のうちどれが優れているか分かるだろうか。IPython シェルで timeit を使って確かめてみよう。
x = 5 ; y = x*x が最速であり、Numpy と比べて約 20 倍高速であることがわかる。配列の生成も考慮すれば、最大 100 倍まで高速になることもある。すごいだろう? (Numpy 開発者がこの問題に取り組んでいる)
もう1つ例を試してみよう。今回は、同じ画像に対する cv.countNonZero() と np.count_nonzero() の性能を比較する。
見てのとおり、OpenCV の関数は Numpy の関数より約 25 倍高速である。
性能計測、プロファイリング、行単位のプロファイリング、メモリ計測など、性能を測るためのマジックコマンドはほかにもいくつかある。いずれも十分に文書化されている。そのため、ここではそれらのドキュメントへのリンクのみを示す。関心のある読者は試してみることを勧める。
Python と Numpy の最大限の性能を引き出すための手法やコーディング方法がいくつかある。ここでは関連するものだけを挙げ、重要な情報源へのリンクを示す。ここで注意すべき主な点は、まずアルゴリズムを単純な形で実装することである。動作するようになったらプロファイルを取り、ボトルネックを見つけて最適化する。
これらの操作をすべて行ってもコードがまだ遅い場合、あるいは大きなループの使用が避けられない場合は、Cythonのような追加ライブラリを使って高速化する。