🐾

FlutterのmacOSアプリからC++のOpenCVを呼び出す

2022/06/29に公開

概要

 macOS上のFlutterから、C++のOpenCVをFFI経由で呼び出す設定です。Flutter3でmacOSのアプリもstableとなったようですが、Flutter2のときに動作確認した手順なので、Flutter2(あえてFlutter3を避ける必要はないと思いますが)でも動作する思います。

環境

 本記事での実行環境です。

> sw_vers
ProductName:	macOS
ProductVersion:	12.4
> flutter --version
Flutter 3.1.0-9.0.pre • channel dev • https://github.com/flutter/flutter.git
Framework • revision f28e570c8c (2 weeks ago) • 2022-06-14 13:39:33 -0500
Engine • revision 74ee6b5afd
Tools • Dart 2.18.0 (build 2.18.0-165.1.beta) • DevTools 2.14.0

 Flutter自体は、brewなどでインストールできるので、インストールしてください。

brew install flutter

手順

1. プロジェクト作成

 Flutterのプロジェクトを作成して、サンプルのアプリが起動ができるか確認します。

flutter create --platforms macos .
flutter run

 問題なく起動できれば、以下のUIが確認できるはずです。

2. OpenCVのxcframework生成

 OpenCVのソースから、xcframeworkを生成します。OpenCV付属のコマンドを実行するだけで生成してくれます。

git clone --depth 1 https://github.com/opencv/opencv.git
python3 opencv/platforms/apple/build_xcframework.py --macos_archs x86_64 --build_only_specified_archs --out ./build_xcframework

 ビルドが終了すると、build_xcframeworkディレクトリにopencv2.xcframeworkが生成されているはずです。それをRunner.xcodeprojがあるディレクトリへコピーします。Runner.xcodeprojはflutter createで生成されるディレクトリにあります。

3. OpenCVのC++コード作成

 C++にてCV_VERSIONを呼び出す以下のデモ関数を作成し、my_opencv.cppとして保存します。ここではmain.dartと同じlibディレクトリに保存します。

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

extern "C" {
    __attribute__((visibility("default"))) __attribute__((used))
    const char* version() {
        return CV_VERSION;
    }
}

4. FFIのコード作成

 FFIとして呼び出すDartのコードを以下のように作成し、my_opencv.cppと同じディレクトリに保存します。

import 'dart:ffi' as ffi;
import 'dart:io';
import 'package:ffi/ffi.dart';

typedef _CVersionFunc = ffi.Pointer<Utf8> Function();
typedef _VersionFunc = ffi.Pointer<Utf8> Function();

ffi.DynamicLibrary _openDynamicLibrary() {
  return ffi.DynamicLibrary.process();
}

ffi.DynamicLibrary _lib = _openDynamicLibrary();

final _VersionFunc _version =
    _lib.lookup<ffi.NativeFunction<_CVersionFunc>>('version').asFunction();

String opencvVersion() {
  return _version().toDartString();
}

5. Runner.xcodeprojの編集

 Runner.xcodeprojを起動して、以下2つの操作をします。

  1. Frameworksにopencv2.xcframeworkを追加
  • RunnerのGeneralでAdd Filesからopencv2.xcframeworkを追加
  • Do Not Embedを選択
  1. OpenCVのC++コードの追加
  • Runnerの下にmy_opencv.cppを追加
  • Create folder references(デフォルト)を選択してFinishで追加

 この時点で以下の画像のチェック部分と同じようになります。

6. FFIの追加

 pubspec.yamlにffiを追加します。

ffi: ^2.0.1

7. OpenCVの呼び出し

 main.dartを修正して、作成したデモ関数であるopencvVersion()を呼び出します。
 以下のようにimportでmy_oepncvをロードすれば、opencvVersion()関数を呼び出せるようになるので、適当なボタン(以下だとShow version)を追加して、クリックするとopencvVersion()を呼び出すように編集すると、以下のようにOpenCVのバージョンが表示されると思います。

import 'my_opencv.dart';

最後に

 FlutterでのFFIについては、昔から苦労されている方が多いですが、公開されている記事などは限られているので、同じことをやろうとしてる人は、mediumだったり、zennだったりで同じ記事を読んだのではないかと思います。自分もそうでした。時期や課題が違ったためか、同じ手順を試したのですが、なかなかうまくいかず、今回の記事の手順となりました。Flutterのtemplateを使用する手順などでもうまくいくなど、他のやり方についても知りたいので、知っている方がいれば教えて下さい。また、この手法もFlutter、Dart、FFIのバージョンが新しくなった場合は、そのまま適用できない可能性はあるので、その際も教えていただけると助かります。

Discussion