Web制作・アプリ開発をコストパフォーマンスで考えるなら

React NativeにOpenCVを導入するための手順

こんにちは。graphy事業部の遠藤です。 React Nativeを使ったアプリ開発でOpenCVを導入する機会がありましたので、 今回はその導入手順について詳しく説明していきたいと思います。

はじめに

react-native initコマンドで、プロジェクト作成すると以下のようなフォルダ構成になっていると思います。

$ tree -L 1
.
├── App.js
├── __tests__
├── android
├── app.json
├── babel.config.js
├── index.js
├── ios
├── metro.config.js
├── node_modules
├── package-lock.json
└── package.json

iosフォルダがXcodeプロジェクトになっており、androidフォルダがAndroidアプリのフォルダ構成になっていて、 OpenCVの導入にはそれぞれのプロジェクトのファイルを追加修正する必要があるので順番に説明していきたいと思います。

OpenCVの導入 iOS編

OpenCVのライブラリは公式ページから入手でき、iOS用を入手したい場合はiOS packと書かれているリンクを参照します。 ですが、iOS packから入手できるライブラリはOpenCVの全モジュールを含んだ状態でビルドされており、サイズもなかなかのデカさとなってます。

使わないモジュールを含まずにビルドしたい場合は、Sourcesのリンクからソースをダウンロードし、自分で必要なモジュールのみに絞ってビルドすることが可能です。 Sourcesからダウンロードしたzipファイルを解凍すると以下のような構成になっており、platforms/iosフォルダにビルド用のスクリプトbuild_framework.pyがおいてあるので、それを使ってビルドすることができます。

$ tree -L 1
.
├── 3rdparty
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── apps
├── cmake
├── data
├── doc
├── include
├── modules
├── platforms
│       ├── android
│       ├── ios
│       ...
└── samples

以下のように出力先(out)を指定し、オプションで必要なモジュールを指定してビルドできます。ビルド完了までだいぶ時間がかかります。 ビルドが完了すると、出力先に成果物としてopencv2.frameworkが生成されていると思います。

$ python build_framework.py out \
    --without video --without videoio --without videostab \
    --without features2d --without objdetect --without flann --without ml \
    --without cudaarithm --without cudabgsegm --without cudacodec --without cudafeatures2d  \
    --without cudafilters --without cudaimgproc --without cudalegacy --without cudaobjdetect  \
    --without cudaoptflow --without cudastereo --without cudawarping --without cudev \
    --without highgui --without viz \
    --without superres --without photo

次にXcodeプロジェクトにopencv2.frameworkを追加します。 追加できたら、Build Settings > Framework Search Pathsopencv2.frameworkが配置されているフォルダへのパスが設定されているか確認します。 Xcode Settings

ここまで設定ができればOpenCVのヘッダーを読み込んでコンパイルできるようになっていると思います。

// main.m
#import <opencv2/opencv.hpp> // ここを追加

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  }
}

OpenCVの導入 Android編

こちらは公式ページAndroid packのリンクからライブラリを入手することができます。 Androidの方も同様に、platforms/androidフォルダにbuild_sdk.pyがありますので、これを利用してモジュールを選択してビルドを行うことができます。 Androidはビルド前にいくつか設定が必要になります。

Android SDK/NDKへのパスを環境変数に設定しておきます。 SDK/NDKはAndroid Studioを使ってインストールが可能です。

export ANDROID_SDK=$HOME/Library/Android/sdk
export ANDROID_NDK=$ANDROID_SDK/ndk-bundle

次に、build_sdk.pyでは利用しているビルドツールがいくつかあるので事前にインストールしておく必要があります。

$ brew install cmake ninja ccache

また、build_sdk.pyの中身を少し修正する必要がありました。OpenCVの必要なモジュールのみに絞るための設定です。 iOSの場合と違って、なぜかAndroidの方はコマンドラインの引数として与える形になってないようです。

## build_sdk.py
def build_library(self, abi, do_install):
    cmd = ["cmake", "-GNinja"]
    cmake_vars = dict(
        CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
        INSTALL_CREATE_DISTRIB="ON",
        WITH_OPENCL="OFF",
        WITH_IPP=("ON" if abi.haveIPP() else "OFF"),
        WITH_TBB="ON",
        BUILD_EXAMPLES="OFF",
        BUILD_TESTS="OFF",
        BUILD_PERF_TESTS="OFF",
        BUILD_DOCS="OFF",
        BUILD_ANDROID_EXAMPLES="ON",
        INSTALL_ANDROID_EXAMPLES="ON",
        BUILD_LIST="core,imgproc,imgcodecs,java", // この行を追加した。
    )

そして、スクリプトを叩いてビルド開始です。iOSと比べるとAndroidの方はビルドにすごく時間がかかります。

$ python build_sdk.py out

ビルド完了したら、出力先に以下のような構成のフォルダが出力されます。 公式ページからダウンロードできるAndroid packの構成のものと同じ形です。

$ tree -L 3
.
├── LICENSE
├── README.android
├── samples
└── sdk
          ├── build.gradle
          ├── etc
          ├── java
          └── native

次にAndroidプロジェクトにOpenCVを追加していきます。 sdkフォルダをAndroidプロジェクトに追加します。次にsdkだと分かりづらいのでOpenCVという名前にリネームします。 フォルダの中身でbuild.gradle, javanative/libsフォルダは以外は不要なので取り除いています。 Android Settings

続いてgradleの設定になります。

// settings.gradle
rootProject.name = 'sample'
// 以下の2行を追加
include ':opencv'
project(':opencv').projectDir = new File(rootProject.projectDir, './OpenCV')
include ':app'
// build.gradle
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    implementation "com.facebook.react:react-native:+"

    implementation project(':opencv') // この行を追加
}

最後にOpenCVの読み込みのためのコードを追加すれば、OpenCVの導入完了です。

// MainApplication.java
public class MainApplication extends Application implements ReactApplication {
  static {
    if (!OpenCVLoader.initDebug()) {
      // Handle initialization error
    }
  }
  ...

ハマりポイント

Xcodeやビルドツールのバージョンが違うと、ここまで載せた通りの手順の中でエラーになってしまう部分があるかもしれません。 私が作業行っている時にcmakeやxcodebuild周りでエラーにハマりました。 Issuesを眺めてみるとビルドエラーに関する情報と解決方法が載っていることが多いので、困ったときは一度検索してみることをおすすめします。

まとめ

今回はOpenCVのビルドから設定までの流れをまとめました。 React Nativeプロジェクトではライブラリ導入を行う場合、npmreact-native linkで両OSへの設定が完了してしまうことが多いので、 両OSそれぞれ対応するのに時間がかかりました。

また、ビルドから設定までまとまっている情報が意外となかったので、今回まとめるきっかけになりました。 OpenCVを導入したい方の助けになれば幸いです。