OpenCV DNN moduleのiOSでの利用方法とその実行速度の比較

#はじめに
これは，OpenCV Advent Calendar 2016 8日目の記事です．関連記事は[目次](http://qiita.com/advent-calendar/2016/opencv)にまとめられています．また，OpenCV Advent Calendar 2015の22日目のdandelion1124さんの記事[iOS向けopencv2.framework生成方法 with opencv_contrib](http://qiita.com/dandelion1124/items/c250449c99775f096475)と合わせて見ると良いかもしれません．

さて，本記事は，タイトルにもあるようにOpenCV3.1系で実装されたDNNモジュールをiOS上で利用する方法，特に，DNNモジュールをiOSで使うためのOpenCVのフレームワーク化，また，その実効速度などのパフォーマンスについて，簡単にまとめた内容になっています．詳しい使い方までは言及しませんので，使用方法などの詳細は以下の記事が参考になります．

1. [OpenCV Deep Learningモジュール cv::dnn の紹介](http://qiita.com/wellflat/items/b32aff49846c30be8c1b)
2. [iOSでCaffeの学習済みモデルを使う](https://horiken4.wordpress.com/2015/10/17/ios%E3%81%A7caffe%E3%81%AE%E5%AD%A6%E7%BF%92%E6%B8%88%E3%81%BF%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86/)

**※上記2つの記事は2015年に書かれたもので最新のOpenCV3.1系のGithub上にあるコードとは若干異なるのに注意してください．**

#手順1. DNNモジュールを含んだiOS用OpenCVフレームワークの生成法
##opencv本体とopencv_contribを用意する
まず，次のOpenCV本体とDNNモジュールなどが含まれているopencv_contribリポジトリ(**いずれも必ずmaster branch**)をgit cloneしてきます．

```
git clone https://github.com/opencv/opencv
git clone https://github.com/opencv/opencv_contrib
```
ここで注意して欲しいのが，git cloneする際は必ずmaster branchのを使うようにしてください．DNNモジュールは3.1系から実装されたモジュールなのですが，以下の図にあるようにmaster以外の，例えば，Tagsから3.1.0のbranchのものを使ってフレームワークを生成する際に以下のエラーに出くわすと思います．

<img width="302" alt="スクリーンショット 2016-12-08 15.52.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/149625/54c2080f-ac4e-3922-b067-5c60b396af6c.png">

```vim:master以外を使った場合に出くわすエラー
Undefined symbols for architecture arm64:
 "_png_init_filter_functions_neon", referenced from: 
_png_read_filter_row in opencv2(pngrutil.o) ld: 
symbol(s) not found for architecture arm64 clang: error: 
linker command failed with exit code 1 (use -v to see invocation)
```
このエラーは，~/opencv-master/platforms/ios/**build_framework.py**に以下の違いがあるからです．

```py3:修正前
if arch.startswith("armv"):
cmakecmd.append("-DENABLE_NEON=ON")
```
```py3:修正後
if arch.startswith("armv") or arch.startswith("arm64"):
cmakecmd.append("-DENABLE_NEON=ON")
```

もちろん，master以外のbranchにあるものを使って上記の訂正をすれば良いのですが，面倒なので，masterのを使うようにしましょう．

##手順2. OpenCV DNNモジュールを含んだフレームワークの生成
OpenCVの標準モジュールのみをiOSで利用したい場合，簡単なのは[http://opencv.org/downloads.html](http://opencv.org/downloads.html)から下図の赤線で囲まれているもの(2.4系なら↑，3.1系なら↓)をダウンロードして，Xcodeにドラッグアンドドロップするだけで使えるようになります．
![framework.png](https://qiita-image-store.s3.amazonaws.com/0/149625/05dadd69-1dc6-82fa-63ea-ea1359098aef.png)
ただ，標準モジュールには残念ながらDNNモジュールは含まれていないので，自分でビルドする必要があるわけです．DNNモジュールを含んだフレームワークの生成は簡単で，~/opencv_contrib-master/modules/dnn/**CMakeLists.txt**の上から1~10行までの部分を以下のように書き換えます．デフォルトの状態だとDNNモジュールを含まない設定になっているので，DNNモジュールを含んでビルドできるように書き換えます．

```py:修正前
cmake_minimum_required(VERSION 2.8)

if(APPLE_FRAMEWORK OR WINRT
    OR AARCH64 # protobuf doesn't know this platform
)
  ocv_module_disable(dnn)
endif()

set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass")
set(OPENCV_MODULE_IS_PART_OF_WORLD OFF)
```

```py:修正後
cmake_minimum_required(VERSION 2.8)

set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass")
set(OPENCV_MODULE_IS_PART_OF_WORLD OFF)
```

ここまで終わったら，後は，

```py:生成コマンド
python ~/opencv-master/platforms/ios/build_framework.py --contrib=~/opencv_contrib-master ios
```
とすれば，iosディレクトリにopencv2.frameworkが生成されて一件落着です！

##補足１: 実機でのビルドエラー対処
下のようなエラーが出た場合の対処法です．

```
Error: Unspecified error (Can't create layer "conv1/7x7_s2" of 
type "Convolution") in cv::dnn::LayerData::getLayerInstance, 
file E:/opencv_contrib-3.1.0/modules/dnn/src/dnn.cpp, line 122
```
上の，~/**opencv_contrib-3.1.0**/modules/dnn/src/dnn.cpp, line 122にある通り，master branch以外を使ってフレームワークを生成した場合に出るエラーの1つです．master branchを使えばこのエラーがでなくなっているようです．上のエラーの解決は簡単で**cv::dnn::initModule();**を追加すればOKです．

```cpp:修正前
int main(int argc, char **argv)
{
    String modelTxt = "bvlc_googlenet.prototxt";
    String modelBin = "bvlc_googlenet.caffemodel";
    String imageFile = (argc > 1) ? argv[1] : "space_shuttle.jpg";
```

```cpp:修正後
int main(int argc, char **argv)
{
    cv::dnn::initModule();  //Required if OpenCV is built as static libs
    String modelTxt = "bvlc_googlenet.prototxt";
    String modelBin = "bvlc_googlenet.caffemodel";
    String imageFile = (argc > 1) ? argv[1] : "space_shuttle.jpg";
```

##補足２: deploy.prototxtの修正
OpenCV DNNモジュールでdeploy.prototxtを使うには以下のように書き換える必要があるので注意が必要です．

```cpp:修正前
name: "GoogleNet"
layer {
  name: "data"
  type: "Input"
  top: "data"
  input_param { shape: { dim: 10 dim: 3 dim: 224 dim: 224 } }
}
```

```cpp:修正後
name: "GoogleNet"
input: "data"
input_dim: 10
input_dim: 3
input_dim: 224
input_dim: 224
```

#OpenCV DNN moduleの実行速度の比較
詳細については[画像ラボ(日本工業出版)](http://www.nikko-pb.co.jp/products/list.php?category_id=14)(何月号かは追って追記します)に掲載予定なので，雑誌をお手にとっていただけると幸いです．ここでは，[13th Annual International Conference on Mobile and Ubiquitous Systems: Computing, Networking and Services(MOBIQUITOUS)のWorkshop](http://mobiquitous.org/2016/show/home)で発表したスライドの一部を[SlideShare](http://www.slideshare.net/ssuserc80e3e/opencv-dnn-module-vs-ours-method)にアップしましたので，そちらと雑誌で補完頂ければと思います．

![161128tanno_5.jpg](https://qiita-image-store.s3.amazonaws.com/0/149625/aa1d5864-41c5-00a4-6b56-4daa1cc8eca2.jpeg)

#おわりに
いつもお世話になってるQiitaに何か貢献を！ということで，今回の記事を書いてみましたが，初投稿ということもあり(~~言い訳~~)，支離滅裂感も否めません．．．
でもでも，また，何かOpenCVで面白いDNN系の実装があったら，取り上げてみたいのと，Advent Calendarはとても良い企画だと思っているので，機会があったらまた参加してみたいです！

私事ですが，[DeepLearning Advent Calendar 2016 8日目](http://qiita.com/advent-calendar/2016/deeplearning)にも参加しているので，見て頂ければ幸いです．本記事同様，**モバイル上でのDeepLearning**について自身の研究(画像認識)を踏まえて書いています．

また，[Chainer Advent Calendar 2016 23日目](http://qiita.com/advent-calendar/2016/chainer)にも参加していて，ここでは，**モバイル上でのStyle変換のお話．Chainerで学習したモデルをiOSで利用したアプリ**について自身の研究(スタイル変換)や各企業の動向を踏まえて書くつもりです．

またまた，[今年読んだ一番好きな論文2016 Advent Calendar 2016 23日目](http://www.adventar.org/calendars/1785)にも参加していて，2016年に読んだ論文の中で、**自分の中で一番インパクトのあった、面白かった論文を紹介する系のAdvent Calendar**なのですが，記事を投稿すると，なんと！**豪華な商品があたる**チャンスがあるそうなので，参加してみました．笑 12/15以降はすべて埋まっているみたいですが，それ以外はまだ空きがあるそうなので，ぜひぜひ，参加して豪華賞品を掴みましょう！
(**※大学院生限定なのでそこは注意してください！ただ，それ以外は縛りはなく，どの分野どんな論文でも良いみたいなので，気軽に参加できます．注意事項は本リンク先を御覧ください．**)

#謝辞
今回，OpenCV Advent Calendarのお誘いをしていただいた**dandelion1124**さんにこの場を借りてお礼申し上げます．

#参考
1. [Error while compiling iOS app with openCV framework on real device](http://stackoverflow.com/questions/35338568/error-while-compiling-ios-app-with-opencv-framework-on-real-device)
2. [Error while compiling iOS app with openCV framework on real device](http://stackoverflow.com/questions/35338568/error-while-compiling-ios-app-with-opencv-framework-on-real-device)
3. [OpenCVでニューラルネットワークのcaffeモデル読み込み](http://www.mogino.com/blog/2016/03/15/opencv%E3%81%A7%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AEcaffe%E3%83%A2%E3%83%87%E3%83%AB%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF/)