FlutterからOpenCVを使う(Android編)
tl;dr
Flutterは小ぎれいなアプリが作れるマルチブラットフォームなフレームワークで大変素晴らしいです。ただ、どのプラットフォームにとってもあくまでも「よそ者」なので、ときどきそのプラットフォーム固有の開発環境のコード(例: AndroidならKotlinなりJavaなりで書いたコード)と連携させたくなります。そんなときに使うのが platform channel という機能です。
この記事ではAndroid OSで、platform channelを使う方法を解説します。単に Kotlinコードを呼び出すだけでも良いのですが、せっかくだから実際に役立ちそうな例としてOpenCVを使ってみます。
OpenCV
「flutter pub add cvなんとか」 じゃダメなんですか?
OpenCV は公式対応しているのが C++, Java, Python のみです。(MATLAB, Octaveでも使えるよという風のうわさもありますが、ちょっと別系統なので置いときましょう。) Androidの場合は Kotlin でもいけます。Flutter でもいくつかの OpenCV パッケージが出ていますが、いずれも(OpenCV的には)非公式なものばかりで、全ての機能が使えるわけではありません。そんなときに platform channel を使うと便利です。
- Flutterプロジェクトに公式OpenCV を追加する。
myapp/android/app/build.gradleに以下を追記します。(myapp/android/build.gradle じゃないので注意!)
dependencies {
implementation 'org.opencv:opencv:4.10.0' // バージョンは適当に最新のものを
}
- OpenCVをロードするコードを書く。
myapp/android/app/src/main/kotlin/com/example/myapp/MainActivity.kt を次のようにします。とりあえずここまでやって flutter run してみましょう。エラーが出なければok.
// これらの import を追加(あとで必要な分もまとめて書いてます)
import androidx.annotation.NonNull
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.os.Bundle
import android.util.Log
import org.opencv.android.OpenCVLoader
import org.opencv.core.Core
import org.opencv.core.Mat
import org.opencv.core.CvType
import org.opencv.core.Scalar
import org.opencv.core.MatOfDouble
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
OpenCVLoader.initDebug() // false だったらエラーなのでその処理書いても良いです
}
}
platform channel
開通させる
MainActivityの configureFlutterEngine メソッドを override します。
正直何をやっているのかぼくも理解してませんが、とにかくこうやると invert2x2という名前でkotlin側のメソッド呼び出しができるようになります。さしあたりデバッグプリントで試してみましょう。
private val CH = "com.example.myapp/channel"
override fun configureFlutterEngine(@NonNull engine: FlutterEngine) {
super.configureFlutterEngine(engine)
MethodChannel(engine.dartExecutor.binaryMessenger, CH)
.setMethodCallHandler { call, result: MethodChannel.Result ->
if (call.method == "invert2x2" ) {
println("invoked!")
result.success(0)
}
}
}
dart側から呼び出す
main.dartなどを編集してチャネルを繋げます。flutter create myappとしたときに自動生成されるひな型コードだったらこんな感じです。先ほど作った invert2x2メソッドに引数としてMapを渡しています。
import 'package:flutter/services.dart';
// (中略)
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
static const platform = MethodChannel('com.example.myapp/channel');
void _incrementCounter() async { // async にする
var ret = await platform.invokeMethod('invert2x2', {
'arg1': [1, 2, 3, 4]
});
print(ret);
setState(() {
_counter++;
});
}
flutter run して + ボタン(fab)をクリックしてみましょう。期待通りに print されていればokです。
引数 > OpenCV機能 > 戻り値
それではOpenCVで逆行列を求めてみましょう。invert2x2メソッドを次のようにアップデートします。
if (call.method == "invert2x2" ) {
val args = call.argument<List<Double>>("arg1")!!
val m1 = Mat(2, 2, CvType.CV_64F)
val m2 = Mat()
m1.put(0, 0, args[0])
m1.put(0, 1, args[1])
m1.put(1, 0, args[2])
m1.put(1, 1, args[3])
Core.invert(m1, m2)
result.success(listOf(m2[0, 0], m2[0, 1], m2[1, 0], m2[1, 1]))
}
fab をクリックすると I/flutter (28546): [[-2.0], [1.0], [1.5], [-0.5]] なんて感じに [1, 2; 3, 4]の逆行列が求まりました!