OpenCV と Intel Movidius Neural Compute Stick のSDKを連携させた。

![nps_electric_guitar_s.png](https://qiita-image-store.s3.amazonaws.com/0/105738/053d8061-5d67-c961-e1eb-02414344372c.png)
サンプルでの認識対象のエレキギター　(縮小版）

#はじめに

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

* Intel Movidius Neural Compute Stick (NCS)に関しては、説明致しません。[日経のテクノロジーの記事](http://techon.nikkeibp.co.jp/atcl/feature/15/122200045/110600223/)等を参考にしてください。
また、SDKの環境構築は、私の[OpenCV 備忘録の記事](https://iwaki2009.blogspot.jp/2017/11/intel-movidius-neural-compute-stick.html)を参考にしてください。

* 今回は、SDKに添付されているC++のサンプルコードを、OpenCVのAPIを使用して書き換える話ですが、まじめに説明すると非常に長くなるので、概略のみとなります。図表もありませんので、おもしろくはないと思います。


#サンプルコードの概略
使用するサンプルコードは、[ncsdk/examples/caffe/SqueezeNet/cpp/run.cpp](https://github.com/movidius/ncsdk/tree/v1.10.01.01/examples/caffe/SqueezeNet/cpp) です。
サンプルコードは、以下のファイルにより構成されています。

+ build.sh (ビルドスクリプト)
+ fp16.c (float と **fp16** の変換ソース）
+ fp16.h (上記のヘッダーファイル)
+ run.cpp (サンプルプログラム本体)
+ stb_image.h (画像ファイルを読み込むための汎用ライブラリのソース)
+ stb_image_resize.h (画像のサイズを変更するための汎用ライブラリのソース)

OpenCV の API で書き換えると、fp16.c, fp16.h, stb_image.h, stb_image_resize.h の４つのファイルは不要になります。

## ソースの説明
プロフラムは、以下の順で処理が行われています。

1. デバイス初期化
2. グラフデータの読み込み
3. グラフデータを NCS に転送
4. <font color="Blue">画像データの読み込みと変換</font>
4.1. <font color="Blue">ファイルの読み込み</font>
4.2. <font color="Blue">ファイルのリサイズ</font>
4.3. <font color="Blue">unsigned char から float への変換</font>
4.4. <font color="Blue">RGB各画素値より平均値を減算し、RとBを入れ替える</font>
4.5. <font color="Blue">float から **fp16** への変換</font>

5. **fp16** 形式のデータをNCSに転送
6. **fp16** 形式で結果を取得 
7. <font color="Blue">**fp16** から float への変換</font>
8. <font color="Blue">最大値のindexの探索</font>
9. 最大値とindexの表示
10. デバイスクローズ

* 上記で、青色で記載した 4.1-4.5,7,8 の処理を OpenCV の APIで置き換えます。
* 以下に、該当する部分のみ OpenCV の API で書き換えたソースを掲載します。
* 冒頭にOpenCVのインクルードファイルが必要です。

```c++
#include <opencv2/opencv.hpp>
```

###画像データの読み込み処理
4.1-4.5 の処理を書き換えたものです。

```c++
	cv::Mat mat = cv::imread(IMAGE_FILE_NAME);
	cv::resize(mat, mat, cv::Size(networkDim, networkDim));
	mat.convertTo(mat, CV_32F);
	mat -= cv::Scalar(0.40787054*255.0, 0.45752458*255.0, 0.48109378*255.0);
	cv::convertFp16(mat, mat16);
	half* imageBufFp16 = (half*)mat16.data;
```
注：OpenCVでは画像の並びがBGRなので入れ替える必要はない

###結果から最大値の取得
7,8 の処理を書き換えたものです。

```c++
	cv::Mat mat16res(cv::Size(1, numResults), CV_16SC1, resultData16);
	cv::Mat mat32;
	cv::convertFp16(mat16res, mat32);

	double maxVal;
	cv::Point maxLoc;
	cv::minMaxLoc(mat32, NULL, &maxVal, NULL, &maxLoc);
	printf("Index of top result is: %d\n", maxLoc.y);
	printf("Probability of top result is: %f\n", maxVal);
```
###ビルドコマンド
```
 g++ run.cpp -o run -lmvnc `pkg-config --cflags opencv` `pkg-config --libs opencv`
```

##結果
* オリジナルのソースをOpenCV の API に変更することにより、コードがすっきりとし読みやすくなったが

* オリジナルの計算結果
Index of top result is: 546
Probability of top result is: <font color="Red">0.969727</font>
に対して、
* OpenCV版では、
Index of top result is: 546
Probability of top result is: <font color="Red">0.991211</font>
となり indexは同じであるが、最大値が異なる結果となった。

##補足
* [cv::convertFp16(OpenCV)](https://docs.opencv.org/3.3.1/d2/de8/group__core__array.html#ga9c25d9ef44a2a48ecc3774b30cb80082) と floattofp16(fp16.c) や fp16tofloat(fp16.c) は、特定のデータで結果が同じになることを別途確認している。
* 結果が異なる理由は、cv::resize と stbir_resize_uint8 のリサイズ結果を合わせることができなかったためと推測している。

##最後に
* ここで省略した内容は、ブログの方に補足として書いていく予定である。
* 明日は、dandelion1124 さんの番です。タイトルは未定ですが楽しみにしています。