dnnモジュール（Inference Engine backend）がONNXモデル読み込みサポートしたってよ

この記事は[OpenCV Advent Calendar 2023](https://qiita.com/advent-calendar/2023/opencv)の16日目の記事です。

## はじめに

<https://github.com/opencv/opencv/pull/21066>でdnnモジュール（Inference Engine backend）のONNXモデル読み込みがサポートされるようになりました。この記事ではこの機能について紹介していきます。

## OpenVINOにおけるモデル読み込み

OpenCVのdnnモジュールの話に入る前にOpenVINOにおけるモデル読み込みについて確認していきます。<https://docs.openvino.ai/2021.4/openvino_docs_IE_DG_ONNX_Support.html?sw_type=switcher-python>によると

> Starting with the 2020.4 release, OpenVINO™ supports reading native ONNX models. The `IECore.read_network()` method provides a uniform way to read models from IR or ONNX format, it is a recommended approach to reading models.

とあり、`OpenVINO 2020.4`の時点でONNXモデルの読み込みに対応していることがわかります。さらにOpenVINO Workflow（<https://docs.openvino.ai/2023.2/openvino_workflow.html>）を見ていきましょう。

![](https://docs.openvino.ai/2023.2/_images/ov_workflow_diagram_convenience.svg)

※上図は<https://docs.openvino.ai/2023.2/openvino_workflow.html>より引用

着目すべき点は以下の通りです。

- TensorFlow、ONNX、PaddlePaddleなどのモデル：`convert_model()`もしくは`read_model()`で読み込まれる
- OpenVINO IR：`read_model()`で読み込まれる

この図からもわかるようにOpenVINOはOpenVINO IRだけでなく、その他のDNNフレームワークのモデルもサポートしていることがわかります。

## 従来のInference Engine backend

ここまでの説明から、OpenVINO自体はONNXモデルも読み込めることがわかりました。それでは本題のOpenCV dnnモジュールについて確認していきます。

従来のdnnモジュール（Inference Engine backend）は、[cv2.dnn.readNetFromModelOptimizer](https://docs.opencv.org/4.8.0/d6/d0f/group__dnn.html#ga4f3b552113d2bff48a54e168791c448e) の

> * [in] xml	XML configuration file with network's topology.
> * [in] bin	Binary file with trained weights.

にあるように、OpenVINO IRを読み込むことが前提となっていました。そのため、手元にONNXモデルがあっても[Model Preparation — OpenVINO™ documentation](https://docs.openvino.ai/2023.2/openvino_docs_model_processing_introduction.html)にある手順でOpenVINO IRに変換する作業が必要になってきます。普段、OpenVINOを使わない人からするとこの変換作業が煩雑に感じてしまうかもしれません。

## 現行のInference Engine backend

<https://github.com/opencv/opencv/pull/21066>がマージされたので、dnnモジュール（Inference Engine backend）でONNXモデル読み込みがサポートされるようになりました。そのため、ONNXモデルがあれば、わざわざOpenVINO IRに変換せずともInference Engineの恩恵を受けられるようになりました。

## 効果検証

それではONNXモデルを入力とし、以下の2つのbackendを用いてinference時間を計測していきます。

- cv2.dnn.DNN_BACKEND_OPENCV
- cv2.dnn.DNN_BACKEND_INFERENCE_ENGINE

### 計測環境

計測環境は下表の通りです。

|名称|スペック、バージョン|
|---|---|
|CPU|Intel Core i7-9800X CPU @ 3.80GHz|
|メモリ|32GB
|OS|Ubuntu 22.04|
|Python|3.10.12|
|opencv-python|4.8.1.78|
|openvino|2023.2.0|

今回、環境構築を簡略化するために<https://hub.docker.com/r/openvino/ubuntu22_dev>にある`openvino/ubuntu22_dev:2023.2.0`を用いました。

```
docker run -it --device /dev/dri:/dev/dri --rm openvino/ubuntu22_dev:2023.2.0 bash
```

### ソースコード

今回、[@UnaNancyOwen](https://twitter.com/UnaNancyOwen)さんの<https://qiita.com/UnaNancyOwen/items/650d79c88a58a3cc30ce>にあるYOLOXのinferenceコードをベースにすることにします。具体的なinferenceコードは<https://gist.github.com/UnaNancyOwen/802b724b46977115d94332b5971775b4>を参照ください。

ここでは下記の変更前後でinference時間がどのようになるかを見ていきます。

```diff
- self.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
+ self.setPreferableBackend(cv2.dnn.DNN_BACKEND_INFERENCE_ENGINE)
```

### 計測結果

計測結果は以下の通りです。たった1行の変更でそれなりに高速化が図れるのは嬉しいですね。

|backend|inference time(ms)|
|---|---:|
|cv2.dnn.DNN_BACKEND_OPENCV|134.383|
|cv2.dnn.DNN_BACKEND_INFERENCE_ENGINE|89.332|

### 出力結果

お馴染みの画像ですが<https://github.com/pjreddie/darknet/blob/master/data/dog.jpg>を入力にしたinferenceの結果は以下の通りです。

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/ccf6ec4d-f680-9d08-5317-cc62fea1f684.png)

## おわりに

本記事では、dnnモジュール（Inference Engine backend）でサポートされたONNXモデル読み込みについて紹介しました。OpenCVをIntel CPUで動かす場合にはぜひ使ってみてください。