はじめに
こんにちは。ソリューション開発部 農業開発チームの梅田です。
アンケート記事以来、約1ヶ月ぶりの再登板です。
今回は Xamarin.iOS から OpenCV を利用する方法をご紹介します。
Xamarin とは
Xamarin とは、.NET Framework の UNIX 互換実装である Mono をベースとしたアプリケーションを作成するための開発ツールであり、その開発元の企業名です。
企業としての Xamarin は2016年に Microsoft に買収され、ツールとしての Xamarin も OSS 化され、無料で使えるようになりました。
で、何ができるかというと、共通の C# のコードで、iOS / Android / Mac アプリの開発が行えます。(昔は Windows Phone もできたんだけどどこに行ったんだろう…)
同じロジックを Swift や Kotlin でそれぞれ実装する手間が省けるということですね。
クロスプラットフォーム開発の言語・フレームワークでは、最近では Kotlin や React Native など新しいものも続々と出てきており、大変アツい界隈です。
なぜ Xamarin?
実はオプティムの農業開発チーム、社内で唯一プロダクトに Xamarin を利用しているチームだったりします。
その中で、開発した Xamarin の iOS アプリで OpenCV を使って動画を処理する必要が出てきました。
そこで OpenCVSharp や OpenCV.NET、Emgu.CV などを検討したのですが、前者2つはモバイル非対応、Emgu.CV は有償ライセンス。
ということで、まずは本家のライブラリを使ってできないか、ということで調査を行いました。
その調査内容が、今回の記事のベースとなっています。
今回のサンプルアプリについて
今回はせっかくなので、最近リリースされた OpenCV 4 から追加された QR コード検出機能を例として、OpenCV を利用して QR コードの内容を読み取って表示する Xamarin.iOS アプリを作成してみます。
ちなみに、Xamarin.iOS / Android で QR コードを扱いたい場合は ZXing.Net.Mobile という素晴らしいライブラリが nuget にあるので、素直にそちらを使いましょう。
Xamarin.Forms 版もありますよ!
やってみる
0. OpenCV をダウンロード
まずは OpenCV のパッケージを公式サイトからダウンロードします。
今回は v4.1.0 の iOS Pack を使用します。
1. [Xcode] ライブラリを作成
まずは Xcode でスタティックライブラリを作成します。
"Cocoa Touch Static Library" を選択し、言語を "Objective-C" に設定。
Objective-C++ での開発なので、.m ファイルを .mm にリネームします。
プロジェクトに先ほどダウンロードした OpenCV のライブラリを追加します。 opencv2.framework という名前で追加されます。
.mm ファイルに実装を書いていきます。
QR コードの画像を UIImage で受け取って、検出結果を NSString で返すことにします。
iOS Pack は UIImage と cv::Mat の相互変換が一発でできて便利ですね。
一方、QR コードの認識は、cv::QRCodeDetector::detectAndDecode に Mat を渡すだけでできてしまいます。
#ifdef __cplusplus #import <opencv2/opencv.hpp> #import <opencv2/objdetect.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/highgui.hpp> #import <opencv2/imgproc.hpp> #import "OpenCV.h" #include <iostream> #include <vector> using namespace cv; using namespace std; #endif @implementation OpenCV -(NSString *)detectQr:(UIImage *)image { Mat input; UIImageToMat(image, input); QRCodeDetector qrDecoder = QRCodeDetector(); NSString *result = @"Not detected"; vector<cv::Point> points; if(!input.empty()) { string data = qrDecoder.detectAndDecode(input, points); if(data.length() > 0) { result = [NSString stringWithUTF8String:data.c_str()]; } return result; } @end
ここまで完了したら、ビルドターゲットを "Generic iOS Device" に設定してビルドすると、lib[プロジェクト名].a
ファイルが出力されます。
2. [Visual Studio] Xamarin.iOS プロジェクトを作成
次に Visual Studio でソリューションを作成し、Xamarin.iOS プロジェクトを追加します。
今回は Xamarin 公式のレシピからカメラロールから画像・動画を選択して表示するサンプルを拝借しました。
やや古いですが、iOS 12 でも問題なく動きます。
今回はシミュレータ用のライブラリをビルドしていないので、実機でのデバッグを行います。
このままだと Provisioning Profile がないとエラーが出てしまうので、この Xamarin.iOS プロジェクトと同じ Bundle Identifier を持つ iOS アプリを Xcode で作り、デプロイしておきましょう。
こちらなどが参考になります。
3. [Visual Studio] バインディングライブラリを追加
最後に、2. で作成したソリューションに バインディングライブラリを追加します。
"iOS > ライブラリ > バインディングライブラリ (C#)" を選択します。
"ネイティブ参照" を右クリックし、"ネイティブ参照の追加" から、0. でダウンロードした OpenCV のパックと 1. で作成したスタティックライブラリを追加します。
追加した参照を右クリックし、"プロパティ" を開きます。
OpenCV のパックは "Kind" に "Framework" を選択し、"Is C++" にチェックを入れます。
スタティックライブラリは "Kind" は "Static" のまま、同じく "Is C++" にチェック。
ApiDefinition.cs に定義を追加します。
using Foundation; using UIKit; [BaseType(typeof(NSObject))] interface OpenCV { [Export("detectQr:")] NSString DetectQr(UIImage image); }
Export
のメソッド名は スタティックライブラリのメソッド名と合わせる必要があります。
また、引数がないメソッドの場合はコロンなし、引数がある場合は引数の数だけコロンが必要なので、うっかり忘れないように気をつけてください。
最後に、Xamarin.iOS プロジェクトの参照に、バインディングライブラリを追加します。
あとはこんな感じで呼び出すだけです。
private OpenCV openCV = new OpenCV(); resultTextView.Text = openCV.DetectQr(originalImage);
今回の場合は UIImage と NSString なので特に気にせず受け渡していますが、戻り値がアンマネージドな場合は、都度マーシャリングを行ってください。
試してみる
こちらのサイトで作成した QR コードを端末に保存して、カメラロールから呼び出してみます。
正常に認識できていれば「OPTiM Tech Blog」という文字が表示されるはずです。
無事、Xamarin.iOS から呼び出した OpenCV で QR コードを検出することができました!
おわりに
今回は、Objective-C++ で作成した OpenCV のライブラリを Xamarin.iOS で読み込み、利用する方法を紹介しました。
動画像処理のライブラリは、ネイティブで用意されている API やサードパーティ・個人作成のものだけでも十分揃っていると思いますが、どうしても OpenCV を使いたい、という需要は一定数あるのではないでしょうか。
それをわざわざ Xamarin で実装する必要があるかどうかは検討の余地がありますが、Xamarin ではこんなこともできるんだよ、ということが少しでも伝われば嬉しいです。
また、今回は Xamarin.Android での実装をご紹介できませんでしたので、次回は Android 側の実装や、Xamarin の真骨頂である "Bait and Switch" についてもご紹介したいと思います。
OPTiM ではクロスプラットフォーム技術に限らず、様々な新しい技術に関心のあるエンジニアを募集しています。
夏のインターンシップも募集中です。