【Python】あれ？ dnnモジュールのONNX推論遅くねえ？ ※2021/12/14時点

:::note info
この記事はOpenCV Advent Calendar 2021の16日目の記事です。
:::

# 結論
遅いです（[ONNXランタイム](https://github.com/microsoft/onnxruntime)と比較して）
※デフォルトバックエンド & CPU推論

でも、バージョンアップ毎にどんどん早くなり、対応レイヤーも増えているので、今後に期待です🦔
また、readNet() シリーズは、統一された処理で整備されているため使いやすいです。今後に期待です🦔
そして、readNet() シリーズは、OpenCVをインストールすれば使用できるため使い勝手が良く、今後に期待です🦔 ※環境による


#### readNet() シリーズ
* readNet()<Br>※readNet()はモデルの拡張子や引数から以下のAPIを呼び分ける
* readNetFromCaffe()
* readNetFromDarknet()
* readNetFromModelOptimizer()
* readNetFromONNX()
* readNetFromTensorflow()
* readNetFromTorch()

# バージョン
以下のバージョンで確認しました。

```bash
pip show opencv-python

Name: opencv-python
Version: 4.5.4.60
(省略)
```

```bash
pip show onnxruntime

Name: onnxruntime
Version: 1.9.0
(省略)
```

# 計測環境
Google Colaboratory上でCPU推論を行い「%%time」や「%%timeit」を使用して推論時間を計測しています。
（本来はもっと厳密な方法と環境で計測すべきでしょうが、、、ご容赦を。。。🙇

<details><summary>参考：CPU情報</summary><div>

```bash
!cat /proc/cpuinfo

processor	: 0
vendor_id	: GenuineIntel
cpu family	: 6
model		: 79
model name	: Intel(R) Xeon(R) CPU @ 2.20GHz
stepping	: 0
microcode	: 0x1
cpu MHz		: 2199.998
cache size	: 56320 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat md_clear arch_capabilities
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
bogomips	: 4399.99
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

processor	: 1
vendor_id	: GenuineIntel
cpu family	: 6
model		: 79
model name	: Intel(R) Xeon(R) CPU @ 2.20GHz
stepping	: 0
microcode	: 0x1
cpu MHz		: 2199.998
cache size	: 56320 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 1
initial apicid	: 1
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat md_clear arch_capabilities
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
bogomips	: 4399.99
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:
```
</div></details>

# ONNXランタイム VS OpenCV DNNモジュール VGG16推論時間比較
CPUで推論した場合、ぼちぼち推論時間がかかるVGG16で比較してみます。
VGG16のONNXモデルは、[ONNX Model Zoo](https://github.com/onnx/models) の [vgg16-7.onnx](https://github.com/onnx/models/blob/master/vision/classification/vgg/model/vgg16-7.onnx?raw) を使用しました👀 

蜂(Bee)の画像で推論してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/8591e280-9d0d-c2dc-1e25-37d9901f2ff3.png)
※[ぱくたそ](https://www.pakutaso.com/)の画像を使用

以降のスクリプトは、[Qiita-AdventCalendar-20211216-01-OpenCV.ipynb](https://colab.research.google.com/drive/1bREZL84NrK8KN-kbcihlJLrHcRNxX8Jn?usp=sharing)でも公開しています。

#### ONNXランタイム
モデルをロードして

```python
import onnxruntime
onnx_session = onnxruntime.InferenceSession('vgg16-7.onnx')
```
画像の前処理をして

```python
import cv2 as cv
import numpy as np

# 前処理
input_image = cv.resize(image, dsize=(224, 224))
input_image = cv.cvtColor(input_image, cv.COLOR_BGR2RGB)

mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
input_image = (input_image / 255 - mean) / std

input_image = input_image.transpose(2, 0, 1).astype('float32')
input_image = input_image.reshape(-1, 3, 224, 224)

# 入力名・出力名取得
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name
```
推論時間計測します。

```python
%%time

# 推論
result = onnx_session.run([output_name], {input_name: input_image})
```
451msと出ました👀
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/2566ef89-c62e-ba8e-6093-03d1aabd51d9.png)

上位5クラスの推論結果を出してみます。

```python
result = np.array(result).squeeze()
print(np.argsort(result)[::-1][:5])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/2b84041b-a6eb-b08d-adbc-e209a6e73f38.png)
あっていそうですね👀
309：Bee(蜂)
94：hummingbird(ハチドリ)
308：fly(蝿)
95：jacamar(キツツキ目の鳥(キリハシ))
946：cardoon(菊科の植物(カルドン))

#### OpenCV dnnモジュール
モデルをロードして

```python
import cv2 as cv
net = cv.dnn.readNet('vgg16-7.onnx')
```
画像の前処理をして

```python
import numpy as np

# 前処理
input_image = cv.resize(image, dsize=(224, 224))
input_image = cv.cvtColor(input_image, cv.COLOR_BGR2RGB)

mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
input_image = (input_image / 255 - mean) / std
input_image = input_image.astype('float32')

input_blob = cv.dnn.blobFromImage(input_image)

# 入力BLOB設定
net.setInput(input_blob, 'data')
```
推論時間計測します。

```python
%%time

# 推論
result = net.forward('vgg0_dense2_fwd')
```
574msと出ました👀
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/9a510f30-f6ac-e595-0755-41b5194f3031.png)

上位5クラスの推論結果を出してみます。

```python
result = np.array(result).squeeze()
print(np.argsort(result)[::-1][:5])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/3309b54f-6334-d245-571c-5eea4cded037.png)
あっていそうですね👀
309：Bee(蜂)
94：hummingbird(ハチドリ)
308：fly(蝿)
95：jacamar(キツツキ目の鳥(キリハシ))
946：cardoon(菊科の植物(カルドン))

#### ONNXランタイム VS OpenCV dnnモジュール
dnnモジュールでの推論のほうが、若干遅いですね、、、👀

| モデル | ONNXランタイム | dnnモジュール |
|:-:|:-:|:-:|
| [VGG16](https://github.com/onnx/models/blob/master/vision/classification/vgg/model/vgg16-7.onnx?raw) | 451ms | 574ms(+27.2%) |

# ONNXランタイム VS OpenCV DNNモジュール その他いくつかのモデルの推論時間比較
さきほどは推論結果も確認する都合上「%%time」で計測していましたが、
今回はいくつかのモデルに対して「%%timeit -n 10」で計測します。
MobileNetV2がかなり遅いですね、、、👀


| モデル | ONNXランタイム | dnnモジュール |
|:-:|:-:|:-:|
| [VGG16](https://github.com/onnx/models/blob/master/vision/classification/vgg/model/vgg16-7.onnx) | 488ms | 616ms(+26.2%) |
| [MobileNetV2](https://github.com/onnx/models/blob/master/vision/classification/mobilenet/model/mobilenetv2-7.onnx) | 14.7ms | 36.1ms(+145.5%) |
| [ResNet50V1](https://github.com/onnx/models/blob/master/vision/classification/resnet/model/resnet50-v1-12.onnx) | 124ms | 167ms(+34.6%) |

上記の計測に使用したスクリプトは、[Qiita-AdventCalendar-20211216-02-OpenCV.ipynb](https://colab.research.google.com/drive/1BMEyeCPKcbNIhQ16EgQP-EwdrF6zmKiD?usp=sharing)で公開しています。

# ONNXランタイム VS OpenCV DNNモジュール 古いOpenCV(4.1.2.30)
比較するとdnnモジュールの推論のほうが遅いのですが、
昔はもっともっと遅かった記憶があります、、、🤔

というわけで若干古め(4.1.2.30)のOpenCVで試してみます。
明らかに4.5.4.60のほうが早くなっていますね👾

| モデル | ONNXランタイム | dnn(4.5.4.60) | dnn(4.1.2.30) |
|:-:|:-:|:-:|:-:|
| [VGG16](https://github.com/onnx/models/blob/master/vision/classification/vgg/model/vgg16-7.onnx) | 488ms | 616ms(+26.2%) | 867ms(+77.7%) |
| [MobileNetV2](https://github.com/onnx/models/blob/master/vision/classification/mobilenet/model/mobilenetv2-7.onnx) | 14.7ms | 36.1ms(+145.5%) | ロード失敗 |
| [ResNet50V1](https://github.com/onnx/models/blob/master/vision/classification/resnet/model/resnet50-v1-12.onnx) | 124ms | 167ms(+34.6%) | 244ms(+96.8%) |

上記の計測時に使用したスクリプトは、[Qiita-AdventCalendar-20211216-03-OpenCV.ipynb](https://colab.research.google.com/drive/1prJ9QuD_VUDImG1y8uTJpSK0PW9Qjj7k?usp=sharing)で公開しています。

4.1.2.30時点のdnnモジュールではONNX Model zooのMobileNetV2は、
以下のエラーが出て読み込みが出来ませんでした。

```python
error: OpenCV(4.1.2) /io/opencv/modules/dnn/src/layers/reshape_layer.cpp:141: 
error: (-215:Assertion failed) dstTotal != 0 in function 'computeShapeByReshapeMask'
```

# 試したかったけど OpenCV 4.5.4.60 のdnnモジュールで読みこめなかったモデルたち
#### yolov3-10.onnx
以下のエラーが発生して読み込めず。

```python
error: OpenCV(4.5.4) /tmp/pip-req-build-3129w7z7/opencv/modules/dnn/src/onnx/onnx_graph_simplifier.cpp:692: 
error: (-210:Unsupported format or combination of formats) Unsupported data type: BOOL in function 'getMatFromTensor'
```

#### yolov4.onnx
以下のエラーが発生して読み込めず。

```python
error: OpenCV(4.5.4) /tmp/pip-req-build-3129w7z7/opencv/modules/dnn/src/onnx/onnx_importer.cpp:739: 
error: (-2:Unspecified error) in function 'handleNode'
```

#### MaskRCNN-10.onnx

以下のエラーが発生して読み込めず。

```python
error: OpenCV(4.5.4) /tmp/pip-req-build-3129w7z7/opencv/modules/dnn/src/onnx/onnx_importer.cpp:739: 
error: (-2:Unspecified error) in function 'handleNode'
```
以上。