UnityからOpenCVを利用する(iOS)
Jun 7, 2016 · ios · unityopencvobjc
UnityからiOSのネイティブプラグイン経由でOpenCV
でカメラを利用するサンプル
内容としては、
- OpenCV経由で端末のカメラ画像を取得
- 取得した画像をOpenCVで処理(シンプルにグレースケールへ変換)
- ネイティブプラグイン経由でUnity側(C#)へ渡す
- Unity側でテクスチャを生成して表示する
といった流れになる
手順
OpenCVのフレームワークの準備
公式のダウンロードページから
for iOS
のVer.2系をダウンロードする
(サンプルではVer.2.4.13)ダウンロードしたファイルを解凍して
opencv2.framework
を取り出しておく
なお、Ver.2を利用したのは、作成時点ではVer.3だとエラーが出てしまったので・・・ 多分ソースからビルドするかバージョン上がれば、Ver.3系でも問題ないかと
iOSネイティブ部分のソースを作成
OpenCVがC++なので、言語はObjective-C++
を利用する
(残念ながらSwiftはC++を扱うことができないのでObjC一択)
それに伴い、忘れずにファイルの拡張子を.mm
にしておくこと
ただその場合、ヘッダファイル(.hpp
)はUnityがプラグインのファイルとして認識してくれないので、
今回は一つのファイルに収まるように書いている
(通常の.h
はちゃんと認識してくれるのに・・・)
プラグインの呼び出しは以下の感じ
[iOS]
OpenCV(C++)での処理
|
ObjC++のクラスでラップ
|
Cの関数でラッパークラスをエクスポート
|
[Unity]
C#でDLLimportして呼び出し
ObjC++で一旦ラップしているのは、OpenCV部分の開発は別にXCode上で行う為。 ただ、場合によってはメソッド呼び出しが遅いのでそこは注意
ネイティブ側のソースの全文は以下の通り
通常のOpenCV周りの処理(4〜51行目あたり)
ややこしいのが、C++はARC管理外の為、手動でメモリ管理が必要。 今回はメモリの確保/破棄をラッパークラスの初期化/破棄と合わせて、 ラッパークラスの生存期間とOpenCVのオブジェクトの生存期間が一致するようにしてある
カメラの画像の取得は、初期化と同時にcv::VideoCapture
を生成し、
その後は毎フレーム*camera >> img
で画像を読み込んでいる
この時、取得された画像のフォーマットはBGR
なのがポイント。
最終的にUnityのテクスチャのサイズとフォーマットRGBA
に変換してUnity側へ渡す
(Unity側ではARGB
の表記なので、最初それで変換してハマってた・・・)
プラグインのエントリポイントを用意(54〜77行目あたり)
用意するのはラッパークラスVideoCapture
の生成、毎フレーム呼び出す用、破棄の3つのCの関数
これを55〜59行目のように宣言してC#へエクスポートする。
このファイルはC++の扱いになっているのでextern "C"
が必要
(逆に言うと、C#からはこれ以外が見えない状態となっている)
関数の本体は61行目以降にあるように、単純にブリッジしているだけ
Unityでネイティブプラグイン作成
Unityにネイティブのソース類を組み込む
Assets
直下にPlugins
フォルダを作る- 作成した
Plugins
フォルダにiOSネイティブ用のソースファイルを入れる
Unityのエディタ上のインスペクタの設定を念のため確認Select platforms for plugin
:iOSPlatform settings
:チェック不要
- 同じく
Plugins
フォルダにダウンロードしておいたopencv2.framework
を丸ごと入れる
Unity上ではフォルダとして認識されて、中にいろいろあればOK
コンポーネントの作成
ネイティブの画像データからテクスチャを生成し、
そのテクスチャを指定したRenderer
のマテリアルにセットするコンポーネントを作成
当然、ネイティブプラグイン部分は、iOS上での実行時しか動かないので、
該当部分は#if UNITY_IOS
〜#endif
でエディタ実行時にエラーにならないようにしておく
ネイティブ側で準備したエントリポイントを利用する為にインポートの宣言(16〜26行目あたり)が必要
Cの関数宣言をそのままC#での宣言にするが、同じデータ型がC#にない場合は対応するデータ型を代わりに指定する
(今回だとvoid*
がIntPtr
)
なお、構造体をやりとりするような場合はマーシャリング
が必要となるので結構面倒
宣言すれば後は通常のメソッドと同じ様に利用できる。ただし、C側と引数などが異なっていた場合は、 実行時にエラーとなるので注意
後は、コンポーネントのライフサイクルと、キャプチャ用とテクスチャのオブジェクトのライフサイクルを一致させればOK
シーン作成
- キャプチャしたテクスチャ用の
Material
を作成Shader
でUnlit/Texture
を選択
(キャプチャ画像にライトが反映して光らないように)
Quad
でテクスチャを貼るオブジェクトを作成- Scaleを
X:3, Y:4, Z:1
にする
(数値はキャプチャ画像のアスペクト比と合わせる) Material
に1で作成したマテリアルをセット
- Scaleを
- プラグインのコンポーネントを追加
- プラグインの
renderTarget
に自身をセット
- プラグインの
感想
サンプルでは出てこないけど、C#のアンマネージドの辺りが、ObjCやSwiftに比べると結構大変な気がした。 慣れてないからかも知れないけど、特にメモリ周りやマーシャリングはいずれちゃんと勉強しないと
あと、OpenCV周りの細かい挙動もまだ把握しきれていないので調べたい
開発環境
- OS X 10.11.5
- Unity 5.3.4f1
- Xcode 7.3.1
- iOS 9.3.2
- iPhone 6+
ソース
ただし、上記ソースにはopencv2.framework
が含まれていない
(Githubの100MB制限にかかってしまったので)
動かすには、OpenCVのフレームワークの準備
の項目で準備した
opencv2.framework
を手動でUnityOpenCV/Assets/Plugins/
直下に追加する必要がある。