「画像認識処理が遅い」となげく前に

画層認識・機械学習が、これまでになかった勢いで新たなアルゴリズムが発表されている。
処理速度を別とすれば、実現したいことが実現できるアルゴリズムが既にオープンソースの実装が存在したりする。
でも、リアルタイムの処理が必要なことに対して、処理時間がかかりすぎることは、致命的だ。
たとえて言えば、明日の天気を予報するのに、１日以上かかっては、実用上意味を持たないのと同じだ。

画像認識処理（ここでは、機械学習系の画像認識も含める）が遅いとなげく前に、ぜひ次のことは試してみよう。

## チェック項目
### 処理する画像の解像度を減らすことはできないか？
 画像処理の大半は、処理する画素の総数に比例する場合が多い。
　縮小画像を用いて処理をすると、処理時間が短縮できることが多い。
　画像の幅・高さともに半分にすると、処理時間が1/4倍になることが多い。
 縦横をともに1/3倍に間引くことだけで、処理時間を1/9倍にできる。
　処理の詳細なコードをいじる必要はまったくない。
　そのことによって、得られた結果が十分に許容できるものであれば、縮小画像を用いるのが得策だ。

##### 縮小画像に対して処理をするのならば、撮影時点での解像度を減らせないか
- 撮影時の解像度が低くなっている方が、撮影・画像の転送を含めて高速化に寄与しないか。

##### 利用しているモデルファイルの解像度はいくらか
- 最近の機械学習では、デフォルトのモデルが想定している解像度が横20K 程度になっていることがある。
- 実際には、解像度ごとにモデルを作ってあって、低い解像度のモデルを使えばいいんだけど。
- 使用するモデルの解像度が高いほど、検出結果・セグメンテーションの結果が見栄えするので、解像度が高いモデルを使っていることがある。
- 解像度を減らしたモデルが提供されていないか、確認してみてください。


### 画像のうち処理する範囲を限定できないか？
　ロジックのわかりやすさのためには、画像の全領域に対して同じ処理を加える。
　しかし、本当に必要なのは、画像の中の部分領域であることがある。
　そのような場合には、その処理を部分領域に限定することで、処理時間を短縮できることがある。
　処理時間の短縮が、ロジックの単純さよりも必要性が上回るときには、処理する範囲を限定することを考えてみよう。

### 動画のフレームのうち、処理するフレームを減らすことはできないか？
30 fps の動画あったときに、処理の内容によっては、その全てのフレームを処理しなくても十分な場合がある。対象物の移動速度が高くない時は、 15 fps で十分かもしれない。
それだけで、必要な計算量が半減する。

### 動画を処理するタイミングを何らかの条件で絞り込めないか？
固定カメラの場合だと、画像の差分が生じていない時（つまり、画像に変化が生じていない時）には、検出処理をやり直す必要はない。ほとんど人もクルマも通らない場所にある固定カメラの場合だと、そのような絞り込みによって、負荷を減らすことができる。
画像の差分を生じていないときは、結果を通信するのも省略するような設計ができるかもしれない。


### 画像の色空間を変えたほうが処理が簡単になることがある。

### 処理を単純化することで、高速性を確保する常套手段
- 面積縮小ではなく、単純に画像を間引く。
- 線形補間ではなく、最近接点を用いる。
- 四捨五入ではなく、単純に切り捨てる。
ただ、これらの置き換えは、精度の劣化をもたらす割に、処理速度のボトルネックにはなっていない場合が多いから、何がボトルネックになっているのを確かめないうちは、採用を勧めない。


### 用途が同じアルゴリズムが複数ある場合には、精度が劣っても処理時間が少ないアルゴリズムで使い物になるものがないか確かめて見よう。

OpenCVに代表される画像認識のライブラリには、古い時代に開発されたものから、深層学習の進展によって最近加わったものまでたくさんの種類がある。
#### 古い時代に開発されたアルゴリズム
古い時代に開発されたアルゴリズムは、シングルCPUしか使えない時代で、メモリ空間の制約が厳しかったときに開発されて生き残っているアルゴリズムであることが多い。
精度という点では課題はあるものの、処理の軽量性という点では魅力的である**ことがある。**

#### ハードウェアの進展に合わせて並列処理の能力を高められたライブラリ
OpenCLやCudaなどで並列処理を活用することで、高速性を達成しているライブラリがある。そういったライブラリとハードウェアの組み合わせを試してみよう。

きちんとOpenCVのライブラリのビルドの条件を確かめて、ハードウェアを活用しきるものであるか確かめよう。
  
#### 深層学習によって逆に処理が軽くなっているかもしれない
古い時代の処理では、同じ画像を倍率を変えて、右上から左側にスキャンして行きながら、下のラインにうつって、右から左にスキャンを繰り返していくライブラリがあった。
しかも、検出対象物が違うと、違う対象について、同様の処理を繰り返すものだった。
深層学習以前の物体検出がそのようなライブラリだった。そのため、検出対象の種類が増えるとその分比例して処理時間が増えるものだった。（以前の物体検出アルゴリズムには無駄が多かったことがわかるだろう。）
　だから、スケールを変えて、何でも探索するようなアルゴリズムの場合には、深層学習を使った方が、処理時間が短い可能性が高い。
　OpenCVのHaarCascade検出器を使う顔検出、HOG-SVMを使う歩行者検出を使うよりは、素直に深層学習の顔検出・人物検出を使った方が、処理時間の点でも短い可能性がある。
　とくに深層学習のフレームワークは、各種デバイス上での高速化のための改良が続けられている。それらの恩恵にあづかりやすい。HaarCasCadeの検出器では、処理回数が一定しない部分があり、並列化の恩恵を受けにくいものになっている。（とはいっても、Haar Cascadeの顔検出器が画像認識・機械学習の可能性を切り開いてきた素晴らしい技術には違いない。）
　だから、深層学習で実装されている技術の方が高速化もしれない可能性も考えてほしい。



#### 機械学習の推論部分を他のデバイスに外出しすることでの高速性の確保
OpenVinoの場合には、[Neural Compute Stick 2](https://www.switch-science.com/catalog/4104/)を使うことで、OpenVinoでの推論処理をNeural Compute Stick 2　に外出しできることがわかっている。そうすると、CPU側の演算処理の時間をそれだけ減らすことができる。

#### 次の画像取得処理を、今の画像を処理している間に用意しておこう。
シングルタスクの考え方では、次のようなものだった。
- 画像を取得する。（その間は画像処理をしない）
- 画像処理をする。（その間は画像の取得をしない）

これでは、マルチコアの時代にもったいない。
trio などのライブラリを使って
画像処理をしている間に、次の画像を取得してしまっておこう。
これによって、スループットが向上できるはずだ。

## C/C++でのソースコードがある場合
二重ループ処理などがある場合には
OpenMPを利用するようにpragmaを記述するとよい。
それによって2重ループ処理が、マルチコアを使って速くなるはず。
[「並行コンピューティング技法　―実践マルチコア/マルチスレッドプログラミング」](https://www.oreilly.co.jp/books/9784873114354/)

## 機械学習結果の重みファイルは対象デバイス向けに変換してあるか
- 利用しているデバイスと、重みファイルを作成しているライブラリ(例: Pytorch, onnx)を組み合わせで調査してください。
- ３倍程度の高速化が比較的簡単にできることがあります。


## プロファイリングをしよう。
[Python プロファイラ]
(https://docs.python.org/ja/3/library/profile.html)

---
追記
## インストールしているOpenCVは以下の条件を満たしているか
- [ ] マルチコアならばOpenMPをサポートしたビルドになっているか

- [ ] NVIDIAのGPUが使える環境だったら、CUDAを有効化してビルドしているか。


[Pythonでの高速化の選択肢](https://qiita.com/nonbiri15/items/5e64ff73289385b9b663)

## 速い学習済みモデルが作られていないか
人検出・頭部検出・手検出などについていうと、集中的にモデルの改良を行なっている人がいる。
その人の最新のモデルを調べて見てください。
格段に、実行速度・検出性能が優れています。

他の分野の推論でも、もっと高速のモデルがでてないかを調べてみてください。