LoginSignup
24
25

More than 5 years have passed since last update.

CMakeでAndroid向けのOpenCVを利用する

Posted at

この記事について

この記事は、以下のような人に役立ちます。つまり、私のことです。
- OpenCVを使ったAndroidアプリを作りたい
- OpenCV Managerは使いたくない
- Android Studio 2.2から使えるCmakeでOpenCVをビルドしたい

OpenCV Managerを使う場合、自分のアプリとは別にOpenCV Managerというアプリをユーザーにインストールさせる必要があるので、使いたくない方が多いのではと思います。また、Android.mkを利用する方法を紹介した記事はありますが、Android Studio 2.2から使えるようになったCmakeを利用する記事が見つからなかったので、「せっかくなら新しいやり方でやりたい」という方に向けて書いていきたいと思います。

環境

私の環境です。
- OS X 10.11.6 (15G1421)
- Android Studio 2.3.2
- OpenCV for Android 3.2.0
- Android NDK と CMake ダウンロード済み(まだの人は https://developer.android.com/studio/projects/add-native-code.html?hl=ja#download-ndk を参考にダウンロードしてください)

サンプルアプリ

新規プロジェクトを作成

Android Studio で新規プロジェクトを作成します。Include C++ support にチェックを入れるようにしてください。

Create new project.png

OpenCV for Android をダウンロード

http://opencv.org/releases.html から、3.2.0のAndroid packをダウンロードします。zipファイルなので、適当な場所に解凍しておきます。

OpenCVモジュールの導入

インポート

作成したプロジェクトで、File > New > Import Module...から、OpenCVをインポートします。インポートするモジュールのパスは、<OpenCVForAndroidを解凍した場所>/sdk/javaです。正しくパスを指定していれば、以下のようにopenCVLibrary320というモジュール名が入るはずです。
opencvlibrary320.png

また、オプションには全てチェックを入れていました。とくに意味はないかもしれません。
import options.png

build.gradleの設定

app/build.gradledependenciesブロックにインポートしたOpenCVモジュールを追加します。

app/build.gradle
dependencies {
    compile project(':openCVLibrary320')
}

また、インポート時のopenCVLibrary320モジュールのAndroid SDKバージョンがかなり低いので、以下のようにappモジュールと同じものを使います。SDKバージョンが低いままだと、javaからOpenCVを呼び出すときにエラーになりました。

openCVLibrary320/build.gradle
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 25
    }
}

ネイティブライブラリの導入

OpenCVのネイティブライブラリを導入します。コピペ先のディレクトリは各自好みで書き換えても問題ありません。

ライブラリ本体

OpenCVライブラリの本体となる.soファイルをコピーします。/src/main/jniLibsというディレクトリを作り、そこに<OpenCVForAndroidを解凍した場所>/sdk/native/libs内のディレクトリ(arm64-v8a, ..., x86_64)を全てコピーします。必要なのは.soファイルだけなので、.aファイルは消しても構いません。
つづいて、app/build.gradleandroidブロックに以下の設定を追加します。

app/build.gradle
android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
}

ヘッダーファイル

<OpenCVForAndroidを解凍した場所>/sdk/native/jni/includeディレクトリをプロクジェクトにコピーします。今回は、/src/main/jniLibsにコピーしました。これで、/src/main/jniLibs/includeにOpenCVライブラリのヘッダーファイルが入りました。

CmakeLists.txt の設定

新規プロジェクト作成時にInclude C++ supportにチェックを入れていると、app/CmakeLists.txtというCmakeの設定ファイルがあるはずです。その設定ファイルを以下のように書いてください。

app/CmakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

# Your library
add_library( native-lib
             SHARED
             src/main/cpp/native-lib.cpp )

# OpenCV
add_library( lib_opencv SHARED IMPORTED )
include_directories(${pathToProject}/app/src/main/jniLibs/include)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

target_link_libraries( native-lib
                       lib_opencv )

また、app/build.gradleandroid.defaultConfig.externalNativeBuild.cmakeブロックにargumentsを追加してください。

app/build.gradle
android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DpathToProject:STRING=" + project.rootProject.getProjectDir().absolutePath
            }
        }
    }
}

説明

app/build.gradle
arguments "-DpathToProject:STRING=" + project.rootProject.getProjectDir().absolutePath

CmakeLists.txtで使える変数の定義をしています。pathToProjectという変数にプロジェクトの絶対パスを入れています。これによって、コードの変更なしにCIでのビルドができます。

app/CmakeLists.txt
add_library( lib_opencv SHARED IMPORTED )
include_directories(${pathToProject}/app/src/main/jniLibs/include)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

add_libraryコマンドでlib_opencvという名前でライブラリをインポートすることを宣言します。include_directoriesコマンドでヘッダーファイルを、set_target_propertiesでネイティブライブラリを指定します。ヘッダーファイルとネイティブライブラリの指定時にapp/build.gradleで定義したpathToProjectが使われています。また、${ANDROID_ABI}によってCPUの種類(arm64-v8a, ..., x86_64)ごとに読み込む.soファイルを変えます。

動作確認

ここまでの設定でOpenCVのライブラリを利用することができるようになりました。さっそく動作確認をしてみましょう。

C++コードから呼び出し

新規プロジェクト作成時にnative-lib.cppというStringを返すメソッドが書かれたファイルがあるので、そのファイルを以下のように書き換えます。

app/src/main/cpp/native-lib.cpp
#include <jni.h>
#include <string>
#include <opencv2/core.hpp>

extern "C"
JNIEXPORT jstring JNICALL
Java_example_com_androidcmakeopencv_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    cv::Mat mat;
    std::ostringstream oss;
    oss << "rows: " << mat.rows << ", cols: " << mat.cols;
    std::string str = oss.str();
    return env->NewStringUTF(str.c_str());
}

MatはOpenCVの基本的なクラスで、行列を表します。画像データをMatに変換して処理することが多いです。この例では、Matを初期化して行数と列数を表示できるようにしています。この初期化ではサイズ0の行列になるので、以下のような画面になれば成功です。

Javaコードからの呼び出し

新規プロジェクト生成時からあるMainActivity.javaで動作確認をします。

MainActivity.java
package example.com.androidcmakeopencv;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import org.opencv.core.Mat;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Mat mat = new Mat();
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText("rows: " + mat.rows() + ", cols: " + mat.cols());
    }

    // C++コードからの呼び出しで利用。この例では未使用
    public native String stringFromJNI();
}

こちらも、Matクラスの行数と列数を表示するコードですが、ネイティブライブラリではなくOpenCVのjavaモジュールが提供するMatクラスを利用しています。

参考文献

24
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
25