OpenCVのDNNモジュールでMNISTをする。(C++編)

# はじめに

OpenCVにはDNNモジュールという、ニューラルネットワークのランタイムライブラリがあります。
このDNNモジュールはいろいろなonnxファイルを読み込んで推論することができます。

OpenCVのMNISTのC++のサンプルコードがないか探してみたんですが、見つからなかったので、今回DNNモジュールの使い方の勉強も兼ねてOpenCVでMNISTをやってみたいと思います。

# 準備
OpenCVを使うには事前にcmakeやC++コンパイラ、OpenCVがインストールされていないといけません。

### Linux
Linuxではapt-getでインストールできます。

```
apt-get install cmake make g++ gcc libopencv-dev
```

### Windows
Windowsでビルドするには以下のサイトをみてcmakeやC++コンパイラやOpenCVを準備をしてください。

WindowsにCmakeを導入
[https://qiita.com/matskeng/items/c466c4751e1352f97ce6](https://qiita.com/matskeng/items/c466c4751e1352f97ce6)

MinGW-w64のインストール
[https://qiita.com/yomei_o/items/2e706a5fa3ac5ffc3a50](https://qiita.com/yomei_o/items/2e706a5fa3ac5ffc3a50)

WindowsでOpenCVをMinGW-w64でビルド
[https://qiita.com/yomei_o/items/4d794536e24ffc12e29d](https://qiita.com/yomei_o/items/4d794536e24ffc12e29d)
  
### Mac
macでも以下のような感じでインストールできます。
[https://qiita.com/kai_kou/items/df335eb7ee78229ee46f](https://qiita.com/kai_kou/items/df335eb7ee78229ee46f)

# MNISTの学習済みONNXモデルの取得

MNISTの学習済みONNXモデルは以下のサイトからとってくることができます。
https://github.com/onnx/models/tree/main/validated/vision/classification/mnist

今回はmnist-8.onnxというものを使うことにしました。
https://github.com/onnx/models/blob/main/validated/vision/classification/mnist/model/mnist-8.onnx


# プログラムの作成


適当なディレクトリに「CMakeLists.txt」と「opencv_mnist.cpp」のファイルを置きます。

opencv_mnist.cpp
```c++
#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>

#include <opencv2/opencv.hpp>

int main()
{
 unsigned char* p;
 int x=-1, y=-1, n=-1;

 cv::Mat color = cv::imread("tegaki5.png");
 cv::Mat img;
 cv::cvtColor(color, img, cv::COLOR_BGR2GRAY);
 cv::resize(img, img, cv::Size(28, 28));
 img = ~img;

 cv::Mat inpBlob = cv::dnn::blobFromImage(img);
 cv::dnn::Net net = cv::dnn::readNetFromONNX("mnist-8.onnx");

 cv::imwrite("out.png",img);

 net.setInput(inpBlob);
 cv::Mat output = net.forward();

 int i;
 for (i = 0; i < 10; i++) {
  printf("%d : %f \n",i,output.at<float>(i));
 }

 return 0;
}

```

CMakeLists.txt
```
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX C)

find_package(OpenCV REQUIRED)

add_executable(test opencv_mnist.cpp)

target_include_directories(test PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries (test PRIVATE gdi32 comdlg32 ${OpenCV_LIBS})
```

画像は次のものを使用しました。
tegaki5.png
![tegaki5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3914335/6e93439c-5acb-281a-bed8-9501d37be3b0.png)

OpenCVのDNNモジュールの使い方は解説するまでもなくとてもシンプルです。
まず、以下のようにネットワークモデルを読み込みます。
```
net=cv::dnn::readNetFromONNX("mnist-8.onnx");
```
次に、イメージをfloatの配列に変換します。
```
blob=cv::dnn::blobFromImage(img);
```

データをネットにセットします。
```
net.setInput(blob);
```

そして、推論の実行をします。
```
cv::Mat output = net.forward();
```

# ビルド

LinuxやMacでは先ほど使ったディレクトリでシェルから以下のコマンドを実行すればプログラムがビルドされます。

```
mkdir build
cd build
cmake ..
make
```

Windowsでビルドする場合はこんな感じでビルドします。

```
mkdir build
cd build
set OpenCV_DIR=C:\prog\opencv-4.10.0\build\install
cmake -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -G "MinGW Makefiles" ..
make
```

ビルドが成功すると以下のような感じになります。

```
make
Consolidate compiler generated dependencies of target test
[ 50%] Building CXX object CMakeFiles/test.dir/opencv_mnist.cpp.obj
[100%] Linking CXX executable test
[100%] Built target test```
```
# プログラムの実行

それではビルドしたtestを実行してみます。
```
./test
0 : -1597.594116
1 : -2466.290771
2 : -1662.498535
3 : 727.113403
4 : -1446.075684
5 : 4535.136719
6 : 1075.813110
7 : -685.803711
8 : -195.729706
9 : 165.380707
```

「5」が一番確立が高く出ました。
このようにOpenCVのDNNモジュールはとても簡単にonnxを使うことができます。

# おまけ
OpevCVでonnxが使えるようになると、Semantic Segmentationなども簡単にできるようになります。
Qualcomm社のサイトでは自動運転用のSemantic Segmentationのonnxモデルが公開されています。
![s002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3914335/bd39029b-e300-38e1-91d1-09763047acf6.png)
このモデルは最近リリースされたOpevCV4.10のDNNモジュールで動くようになりました。
これを使えばたった数十行で簡単な自動運転のプログラムを作れます。

DeepLabV3-Plus-MobileNet
[https://aihub.qualcomm.com/compute/models/deeplabv3_plus_mobilenet](https://aihub.qualcomm.com/compute/models/deeplabv3_plus_mobilenet)

OpenCVのDNNモジュールを使ったMNISTでした。