OKI「AE2100」向け OpenCV DNNモジュールの使い方

<font color="Red">**※この記事はAE2100のOpenVINOコンテナ(ubuntu_openvino_2021.4.1.tar)を対象としています。**</font>

# 要約

この記事ではOpenCVに付属するDNNモジュールの解説を行い、開発環境上でDNNモジュールを用いたサンプルソースのビルドを行い、AE2100上で動作確認するまでの方法を解説します。

# はじめに

OKIのAIエッジコンピュータ「AE2100」
の標準コンテナには、ディープラーニングの推論環境であるOpenVINOがインストールされています。また、OpenVINOにはコンピュータビジョンライブラリであるOpenCVが付属しています。

そのためAE2100において、DeepLearningの推論処理を実装する場合、OpenVINO InferenceEngine APIを用いる方法と、OpenCV DNNモジュール APIを利用する方法があります。

本記事では、OpenCV DNNモジュールAPIを使ったサンプルコードの動作方法について解説します。

# OpenCV　DNNモジュールについて


OpenCVは、画像処理を簡潔に行うことができるAPIが用意されたライブラリです。
そのなかでも、DNNモジュールは各DeepLearningフレームワークで作成されたモデルを用いてDeepLearningの推論処理を行う機能が実装されています。 　

OpenCV DNNモジュールはAE2100のMyriad Xを使用するためのHDDLプラグインをOpenVINO2021.4.1以降からサポートしています。


以下のDeepLearningフレームワークのモデルに対応しています。

|  バックエンド  |  説明 |
| ---- | ---- |
|  DNN_BACKEND_OPENCV  |  汎用プロセッサ向け(X86, ARM等)  |
| DNN_BACKEND_INFERENCE_ENGINE  |  AE2100を含むIntel社製品プロセッサ向け  | 
| DNN_BACKEND_HALIDE |  汎用プロセッサ向け(X86, ARM等)  |
| DNN_BACKEND_CUDA |  NVIDIA製品プロセッサ向け |

OpenCVのDNNモジュールを利用することにより、多様なデバイスを用いて推論処理を動かすことが可能で、移植性が高いソースコードを記述することができます。  
AE2100においては、OpenVINO InferenceEngineをバックエンドに指定することで、.xml .bin形式のモデルを、CPU,GPU,HDDL(MyriadX)にて推論処理を行うことが可能です。

本記事では、OpenVINO InferenceEngineをバックエンドに指定し、CPUデバイスを用いた識別処理のサンプルコードを動作させます。



# 開発環境について

まず、開発環境でOpenCVを使ったプログラムのビルドをおこない、AE2100で実行するための環境を構築します。

開発環境の構築は「AE2100 シリーズ SDK 取扱説明書 ―DeepLearning 編―」2.1章 ユーザー開発環境の構築 を参考にしてください。

開発環境のメモリは4GB以上をご用意ください。
メモリが少ないと、ビルドが途中で止まってしまうことがあるのでご注意ください。



# モデルファイル・推論画像の準備

モデルファイルは、Tensorflowが公開している1000クラス学習済み識別モデルを使用します。  
モデルファイルの準備については、「AE2100 シリーズ SDK 取扱説明書 ―DeepLearning 編―」　2.2章 モデルの変換を参照してください。
  
推論に使用する画像については、OpenVINOに付属する自動車が写っているサンプル画像を使用します。  

 下記コマンドでサンプルソース実行に必要なファイルをコピーします。
 ``` console
# cp /opt/intel/openvino/deployment_tools/model_optimizer/inception_v3_2016_08_28_frozen.bin .
# cp 
/opt/intel/openvino/deployment_tools/model_optimizer/inception_v3_2016_08_28_frozen.xml .
# cp /opt/intel/openvino/deployment_tools/demo/car_1.bmp .
# cp 
/opt/intel/openvino/deployment_tools/model_optimizer/imagenet_slim_labels.txt ./inception_v3_2016_08_28_frozen.txt
 ```

# サンプルコードの用意
サンプルコードは、以下から入手してください。
- [opencv/classification.cpp at 4.x opencv/opencv  GitHub](https://github.com/opencv/opencv/blob/4.x/samples/dnn/classification.cpp)

また、引数の読み込み処理が記述されているヘッダファイルを入手します。
- [opencv/common.hpp at 4.x opencv/opencv  GitHub](https://github.com/opencv/opencv/blob/4.5.3-openvino-2021.4.1/samples/dnn/common.hpp)  

本サンプルコードは、推論結果を画面に表示する処理になっているため、ウィンドウ出力の無効化と、画像の出力処理を追記します。

classification.cppについて、以下の103、167行目の処理をコメントアウトします。

```cpp 
//namedWindow(kWinName, WINDOW_NORMAL);
```
```cpp
//imshow(kWinName, frame);
```
コメントアウトした167行目の処理の直後に以下の処理を追記します。
```cpp
//imshow(kWinName, frame);
printf("%s\n",label.c_str());
break;
```
# サンプルコードの解説


96~98行目の処理によって、モデルファイルの読み込みを行います。

net.setPreferableBackendにて、今回指定するバックエンドのIDを指定します。このbackendIdという変数は、プログラムの実行時に引数で与えることができ、OpenVINO InferenceEngineを示す"2"を指定します。

net.setPreferableTargetでは、推論処理を行うデバイスを指定します。この値も、プログラムの引数から与えることができます。開発環境上ではCPUで処理を行いますので、そのIDとなる“0”を指定します。

```cpp
Net net = readNet(model, config, framework);
    net.setPreferableBackend(backendId);
    net.setPreferableTarget(targetId);
```
130~137行目の以下の処理で、入力画像をnet.setInput関数に入力できる形式に変換します。

```cpp
    blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight), mean, swapRB, crop);
    // Check std values.
    if (std.val[0] != 0.0 && std.val[1] != 0.0 && std.val[2] != 0.0)
    {
        // Divide blob by std.
        divide(blob, std, blob);
    }
```
141行目の処理で、入力画像をモデルに入力します。
```cpp
    net.setInput(blob);
```
147行目の処理で、入力画像データの推論処理を行います。
```cpp
    Mat prob = net.forward();       
```

148~151行目の処理で、最もスコア値が高いクラスのデータを取得します。
```cpp
    Point classIdPoint;
    minMaxLoc(prob.reshape(1, 1), 0, &confidence, 0, &classIdPoint);
    classId = classIdPoint.x;
```


# プログラムのmakeについて
OpenCVのプログラムをビルドするには、以下のパスにある以下のライブラリをリンクさせる必要があります。  

ライブラリパス：
- /opt/intel/openvino/opencv/lib

ライブラリsoファイル：  
- opencv_core.so  
- opencv_imgcodecs.so  
- opencv_imgproc.so  
- opencv_videoio.so  
- opencv_highgui.so  
- opencv_video.so  
- opencv_dnn.so  

サンプルコードを実行するMakefileは以下のようになります。
 ```Makefile
######################################################################
#
# Macro
# 
######################################################################

TARGET			=	example_dnn_classification
CPPFILE			+=	./classification.cpp 
CXX				=	g++
COMPILEFLAGS	+=	-std=c++11

######################################################################
#
# Module's
#
######################################################################

INCLUDE			+=  -I"/opt/intel/openvino/opencv/include/"

ENGINE_LIBS		=   -L"/opt/intel/openvino/opencv/lib"\
                     -lopencv_core\
                     -lopencv_imgcodecs\
                     -lopencv_imgproc\
                     -lopencv_videoio\
                     -lopencv_highgui\
                     -lopencv_video\
                     -lopencv_dnn

######################################################################
#
# Make execution
#
######################################################################

$(TARGET):
	$(CXX) -o $@ $(CPPFILE) $(ENGINE_LIBS) $(INCLUDE) $(COMPILEFLAGS)
 ```

 以上のMakefileをmake実行します。
 以下のファイルが生成されていると成功です。
```
example_dnn_classification
```

# サンプルコードの実行
下記のコマンドを実行します。
 ```
./example_dnn_classification --model=inception_v3_2016_08_28_frozen.bin --config=inception_v3_2016_08_28_frozen.xml --classes=inception_v3_2016_08_28_frozen.txt --mean="0 0 0" --width=299 --height=299 --rgb=false　--input=car_1.bmp --backend=2 --target=0 
 ```

実行すると以下の様に、最もスコアが高かったラベルと、そのクラスのスコアが出力されます。今回、車の画像を用いた場合は、以下のような結果を得ることができます。  
```
minivan: 0.5530
```
  




# AE2100での実行について

本記事で紹介した方法で作成したプログラムをAE2100上のOpenVINOコンテナにデプロイすることで、AE2100上で推論処理をすることができます。  
主な手順は、「AE2100 シリーズ SDK 取扱説明書 ―DeepLearning 編―」2.4章 AE2100 へのデプロイ を参考にしてください。  
プログラムを実行する際に、--targetの引数を3に変更することで、AE2100に搭載されているMyriadXを利用した推論処理を行うことができます。ぜひ試してみてください。


# まとめ

今回はOpenVINOコンテナ上にOpenCV DNNモジュールを用いたサンプルコードが動作するまでの手順を紹介しました。
OpenCVのサンプルコードの中には、AE2100搭載のMyriadXの性能を十分に発揮できる、非同期処理を実装した[物体検出のサンプルプログラム](https://github.com/opencv/opencv/blob/4.5.3-openvino-2021.4.1/samples/dnn/object_detection.cpp)があります。
これらのコードを参考にし、AE2100ソフトウェアの開発に役立ててみてください。