前回からだいぶ時間が経ってしまいましたが、実際にRubyMotionからOpenCVを使えるようにしていきます(`・ω・´)
前回紹介したとおりOpenCVはiOS用のビルドもできるのでXcode/Objective-Cで開発する場合はCocoapodsを利用すれば非常に簡単です。
RubyMotionから通常のstaticライブラリ(Framework)を利用する際には、Framework内のC/Objective-Cのヘッダファイルから定数や構造体、Objective-Cのクラス情報などをビルド時にBridge Supportというファイルに出力して、RubyMotionのはそこに定義があれば呼び出せるようになります。
RubyMotionのビルドではC++は解釈できないのでBridge Supportを作るときに読み込むヘッダファイルはCまたはObjective-Cのヘッダファイルである必要があります。RubyMotionに気づかれないようにC++部分を覆い隠すラッパーを作成していきます。
まずは最小限のラッパーを実装して動かしてみるところまでを目標にします。
サンプルは画像をグレイスケールに変換するフィルタとします。実はOpenCV for iOSの使い方 -- Qiitaのパク...いやインスパイアされてます。
Matの変換関数はMotionCVというObjective-Cのクラスのクラスメソッドとして実装することにしました。
UIImage
<=> cv::Mat
の変換も先ほどのサンプルをそのまま利用させていただいています。
cv::Matを直接見せないようにMotionMatというObjective-Cクラスでラップすることにします。
これをOpenCVのビルド環境に組み込むのですが、リポジトリを観察した結果modules
の中に置くのが良さそうです。modules/motion
というディレクトリを作成して、他のmoduleに習って下記のようにソースを配備します。
modules/motion/
├── include
│ └── opencv2
│ └── motion
│ ├── MotionCV.h
│ └── MotionMat.h
└── src
├── MotionCV.mm
└── MotionMat.mm
ヘッダファイルはビルドの設定が書かれているcmake/OpenCVModule.cmake
の中でパスがinclude/opencv2/${name}/*.h
と指定されているため、ディレクトリ構成を変更するとframework/Headersに入って来ません。
続いてこれらを利用するサンプルプログラムを作っていきます。
表示する部分はRubyMotionで次のようになります。
ほぼ、先ほどのページのサンプルをMotionCV/MotionMatを使用してRubyMotionに書き換えただけです。
次にRakefileに最低限必要な設定です。
追加したiOSのFrameworkはOpenCVが内部で使用しているため必要です。また-lc++
もOpenCVが内部で標準C++ライブラリを使用しているため必要となります。
vendor_project
の設定に見慣れないforce_load
というオプションを指定しています。これを指定しないとduplicate symbol ...
というエラーがたくさん出てコンパイルできません。
Duplicate symbol error when linking AdMob SDK in RubyMotion -- StackOverflowにこの回避方法が載っていたのでやってみたら先に進めるようになりました(・ω・)ノ
さて、これでやっとうまくいくかと思いきや、まだクラスやメソッドが見つからないというエラーで終了します。確かにBridge Supportファイルを検索しても入っていません。
Bridge Supportファイルの生成はデフォルトではvendorプロジェクト(OpenCVの場合vendor/opencv2.framework)内の"*/.{c,m,cpp,cxx,mm,h}"
にマッチする全てのファイルが対象になりますので、何もしないとOpenCVのC APIのヘッダファイル全てが対象になると思われます。
ところがOpenCVのC APIにはC++のテンプレート構造体やINLINEマクロが大量に含まれていて、ほとんど処理されず空振りしているようです。
エラーになるのならばわかり易かったのですが、どうやらBridge Support出力は無視して進むように見えます。おそらく使えるものだけでも簡単に使えるようにしようという対応かと想像します
少々面倒ですが、サンプルプログラムのRakefile内に対象とすべきヘッダファイルをリストすることにします。
さて、これでもまだうまく行きません(笑)ビルドは成功するも実行時にMotionCVやMotionMatのメソッドが見つからないといって落ちます。
ビルドされたスタティックライブラリをstrings
コマンドでのぞいてみると確かに存在しないようです。
OpenCVのcmake/OpenCVModule.cmake
をもう一度よくみてみると、
:
file(GLOB lib_srcs "src/*.cpp")
:
こういう設定がありました。本来ならモジュール内のCMake設定ファイルを用意して変更すべきなのでしょうが、暫定的にここにsrc/*.mm
を追加してビルドしました。
iPhoneSimulatorで動いた様子

長いこと試行錯誤した結果RubyMotionTokyo meetup#4で動かすことができるようになったので写真はその時のものです。
実はラッパープログラムの方にも自分では結構長い時間分からなかったミスがあったのを@watson1978さんが見つけてくれたのも大きかったですヽ(´ー`)ノ
OpenCVのRubyMotion向けラッパーはMotionCV
という名前でhttps://github.com/iwazer/opencvのmotioncv
ブランチで開発していますので、興味のある方はチェックしてみてください。
利用するサンプルプログラムもGitHubに上げてあります。
まだ、これに毛が生えただけの状態ですが...