はじめに
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
MinGW-w64のインストール
https://qiita.com/yomei_o/items/2e706a5fa3ac5ffc3a50
WindowsでOpenCVをMinGW-w64でビルド
https://qiita.com/yomei_o/items/4d794536e24ffc12e29d
Mac
macでも以下のような感じでインストールできます。
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
#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})
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モデルが公開されています。
このモデルは最近リリースされたOpevCV4.10のDNNモジュールで動くようになりました。
これを使えばたった数十行で簡単な自動運転のプログラムを作れます。
DeepLabV3-Plus-MobileNet
https://aihub.qualcomm.com/compute/models/deeplabv3_plus_mobilenet
OpenCVのDNNモジュールを使ったMNISTでした。