OpenCV 3.0 の拡張モジュール群 opencv_contrib を iOS で利用する

OpenCV 3.0 で、SIFT / SURFといった局所特長量抽出アルゴリズムを利用したくて、

- http://docs.opencv.org/3.0-rc1/d7/d66/tutorial_feature_detection.html

公式チュートリアルの[ここ](http://docs.opencv.org/3.0-rc1/d7/d66/tutorial_feature_detection.html)を見てたら、

```
#include "opencv2/xfeatures2d.hpp"
```

とあるものの、そんなヘッダは手元の opencv2.framework にはない。。


調べてみると、OpenCV 3.0 では SIFT/SURF などのモジュールは "extra modules" として以下の別リポジトリで管理されてる模様。

- https://github.com/Itseez/opencv_contrib


##公式に説明されている opencv_contrib のビルド方法

opencv_contrib リポジトリの README では、"How to build" として次のような方法が書いてありました。

```
$ cd <opencv_build_directory>
$ cmake -DOPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules <opencv_source_directory>
$ make -j5
```

cmake の引数にオプションで opencv_contrib のパスを渡してビルドしましょう、とのこと。


##iOS で用いるにあたっての問題

ただ、iOS向けフレームワークをビルドする場合、**直接 cmake コマンドをたたいてたわけじゃなくて、次のようにあらかじめ用意されている python スクリプトを実行**してたわけです。

```
python opencv/platforms/ios/build_framework.py ios
```

- [OpenCV for iOSの使い方](http://qiita.com/shu223/items/3ff48a8bd6edd910e780)

じゃあこのスクリプトをいじって cmake 実行時にオプション渡すようにするかー、と思って中身を見てみたけどけっこう複雑。。

cmake の勉強とかはまたの機会でいいので、今は手っ取り早くビルドしたい・・・どうしたものか・・・


##解決方法その1: 全部入りフレームワーク作成

こういう記事が見つかりました。

- http://xfanzone.me/opencv-for-ios-en.html

> You can just copy the wanted modules to the modules folder inside opencv source folder, and then run build_framework.py as usual. If everything is OK, this will produce the customized framework after a few minutes.


**使いたい contrib モジュールのソースを `opencv/modules` フォルダにコピーしてビルドスクリプトを実行する** という力技。


さすがにもうちょいスマートな方法はないものか、と探してみると OpenCV のフォーラムでも同様の回答。

- http://answers.opencv.org/question/40392/how-to-build-opencv_contrib-module-for-ios/

> But inferring from the cmake file, you can copy the module you want (ximgproc in your case) to opencv/modules. Then run build_framework.py as usual.


じゃあこれでいいか、ということで `opencv_contrib/modules` 配下のソースを全部 `opencv/modules` にコピーし、スクリプト実行 → 無事 **全部入りフレームワーク** が生成されました。


##解決方法その2 `contrib` オプションを利用

本記事を書いてから『OpenCVプログラミングブック』の著者である [@dandelion1124](https://twitter.com/dandelion1124) さんより下記のようなご助言をいただき、

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr"><a href="https://t.co/W4jD2OaKtO">https://t.co/W4jD2OaKtO</a> を読むとわかりますがbuild_framework.pyの--contribオプションでopencv_contribのパス指定するだけでいけますね． <a href="https://t.co/XO371cnHnf">https://t.co/XO371cnHnf</a></p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/597222259040735232">2015, 5月 10</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

次のようにビルドスクリプトを実行してみました。

```
$ python opencv/platforms/ios/build_framework.py --contrib ./opencv_contrib ios
```

すると、

```
CMake Error at cmake/OpenCVUtils.cmake:764 (add_library):
  Cannot find source file:

    {実行パス}/ios/build/iPhoneOS-armv7/modules/xfeatures2d/opencl_kernels_xfeatures2d.cpp
```

というエラーが出てスクリプトが止まります。

同様のエラーで困っている人はいるようですが、

- http://code.opencv.org/issues/3944
- https://github.com/Itseez/opencv_contrib/issues/91

ここに書かれているように `-DBUILD_opencv_contrib_world` オプションをOFFにしてみたり削除してみたりしてみてもダメ、そもそも opencl_kernels_xfeatures2d.cpp なんてファイルは GitHub どころか web のどこにもない。。

諦めようかと思った矢先、ふと思いついて出力先である ios フォルダを削除して再度ビルドスクリプトを実行してみたところ・・・うまくいきました！

build フォルダに生成されるファイル群がキャッシュとして使われていたようです。

ちなみに `contrib` オプションを利用すると、生成されるフレームワーク名は "opencv2_contrib.framework" になります。

###opencv2_contrib.framework 利用方法

単体で利用するとエラーになるので、

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/shu223">@shu223</a> どうも公式側でopencv_contribを有効にした形でのiOS向けframeworkにバグがあることを認識出来ていない可能性があるのでticket発行したいと思います！（私もframeworkがビルドできたので安心してました・・・）</p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/597525291938910208">2015, 5月 10</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

諦めて前述の力技でビルドした方を使うか、と思っていたところ、dandelion さんより以下のツイート。

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/shu223">@shu223</a> よくよく考えるとcontribオプションありなしでビルドした２つのフレームワークをインポートするという使い方な気がしてきました。帰ってから試してみます。</p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/597533360362115072">2015, 5月 10</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

contrib オプションありなしの2つのフレームワークを用意して下記のように試してみると、

```
#import <opencv2/opencv.hpp>
#import <opencv2/videoio/cap_ios.h>
#import <opencv2_contrib/xfeatures2d.hpp>
using namespace cv;
using namespace cv::xfeatures2d;
```

ビルドが通って処理もうまくいきました！

が、実は xfeatures2d.hpp の

```
#include "opencv2/xfeatures2d/nonfree.hpp"
```

というところでエラーが出たので、手動で

```
#include "opencv2_contrib/xfeatures2d/nonfree.hpp"
```

と修正して使用しました。結局、これを自動化するようにビルドスクリプトを修正する必要はありそうです。


##解決方法その3

またも [@dandelion1124](https://twitter.com/dandelion1124) に情報提供いただきました。（2015.8.23）

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">opencv_contribも含んだopencv2.framework生成方法について&#10;まとめました．　opencv_contribを有効にしたopencv2.framework(for iOS)生成方法 <a href="https://t.co/SO0hs8xODW">https://t.co/SO0hs8xODW</a></p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/635358681631059968">2015, 8月 23</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

[opencv_contribを有効にしたopencv2.framework(for iOS)生成方法 · atinfinity/lab Wiki](https://github.com/atinfinity/lab/wiki/opencv_contrib%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%97%E3%81%9Fopencv2.framework(for-iOS)%E7%94%9F%E6%88%90%E6%96%B9%E6%B3%95)