この記事について
この記事は、以下のような人に役立ちます。つまり、私のことです。
- 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 にチェックを入れるようにしてください。
OpenCV for Android をダウンロード
http://opencv.org/releases.html から、3.2.0のAndroid packをダウンロードします。zipファイルなので、適当な場所に解凍しておきます。
OpenCVモジュールの導入
インポート
作成したプロジェクトで、File > New > Import Module...
から、OpenCVをインポートします。インポートするモジュールのパスは、<OpenCVForAndroidを解凍した場所>/sdk/java
です。正しくパスを指定していれば、以下のようにopenCVLibrary320
というモジュール名が入るはずです。
また、オプションには全てチェックを入れていました。とくに意味はないかもしれません。
build.gradleの設定
app/build.gradle
のdependencies
ブロックにインポートしたOpenCVモジュールを追加します。
dependencies {
compile project(':openCVLibrary320')
}
また、インポート時のopenCVLibrary320
モジュールのAndroid SDKバージョンがかなり低いので、以下のようにapp
モジュールと同じものを使います。SDKバージョンが低いままだと、javaからOpenCVを呼び出すときにエラーになりました。
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.gradle
のandroid
ブロックに以下の設定を追加します。
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の設定ファイルがあるはずです。その設定ファイルを以下のように書いてください。
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.gradle
のandroid.defaultConfig.externalNativeBuild.cmake
ブロックにarguments
を追加してください。
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DpathToProject:STRING=" + project.rootProject.getProjectDir().absolutePath
}
}
}
}
説明
arguments "-DpathToProject:STRING=" + project.rootProject.getProjectDir().absolutePath
CmakeLists.txtで使える変数の定義をしています。pathToProject
という変数にプロジェクトの絶対パスを入れています。これによって、コードの変更なしにCIでのビルドができます。
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
を返すメソッドが書かれたファイルがあるので、そのファイルを以下のように書き換えます。
#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
で動作確認をします。
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
クラスを利用しています。
参考文献
- http://stackoverflow.com/questions/38958876/can-opencv-for-android-leverage-the-standard-c-support-to-get-native-build-sup/3900881
- https://developer.android.com/studio/projects/add-native-code.html
- http://blog.wagavulin.jp/entry/2011/11/27/222642
- http://stackoverflow.com/questions/38764787/android-studio-cmake-access-environement-variable