Android Studio にOpenCVを導入する(Java)
AndroidでOpenCVを使いたい。
ので、その導入方法について述べる。
Java版について述べる。Kotlinは使ったこと無いので不明。
設定は同じだと思うけど。
また、OpenCVは3系について述べる。
4系でも同じだと思うけど。
OpenCVをダウンロード
https://opencv.org/releases/から使用したいOpenCVのバージョンのAndroidをダウンロードする。
今回は、OpenCV – 3.4.6 の Android をダウンロードした。
4系も同じはずだけど試していない。
ダウンロードしたzipは解凍しておく。
Android Studioでプロジェクトを作成
Android Studioを起動し、任意のEmpty Activityを作成する。
パッケージ名はcom.example.myapplicationにしておいたので、ソースをコピペする場合は合わせて。
LanguageはJava。
OpenCVモジュールをインポートする
File → New → Import ModuleでOpenCVモジュールを追加する。
具体的には以下の画面で、ダウンロードして展開したOpenCVのSDKのフォルダを指定する。
(展開したディレクトリ)/OpenCV-android-sdk/sdk/java
ライブラリを物理的にコピー
変なひと手間が発生する。
OpenCVの /(展開したディレクトリ)/OpenCV-android-sdk/sdk/native/libs 配下を
Android Studioプロジェクトの /プロジェクト名/app/src/main 配下に jniLibs というフォルダを作成し、
コピーしないといけない(フォルダ名は任意?)。
なんか面倒だし、もうちょっとおしゃれに出来ないものか?と思う。
グダグダ言わずにコピー。
OpenCVモジュールとappを関連付ける
File → Project Structure を立ち上げ、 Dependencies を選択。
appを選択し、ややわかりにくいが+ボタンを押し、3番のModule Dependenciesを選択すると、先ほどのOpenCVモジュールが出ているはず。
チェックを入れると関連付けられる。
エラー対応する
これで本来の設定は終わりだが、必ずと言ってよいほどエラーが出るはず。
ERROR: The minSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file.
Remove minSdkVersion and sync project
Affected Modules: openCVLibrary346
WARNING: The targetSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file.
Remove targetSdkVersion and sync project
Affected Modules: openCVLibrary346
原因はバージョンのミスマッチ。
OpenCVモジュールの build.gradle の compileSdkVersion と targetSdkVersion を
appモジュールのそれと合わせてください。
自分の場合は28だったのでどちらも28にあわせました。
あとはOpenCVモジュールの AndroidManifest.xmlの
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" />
を
<uses-sdk />
にしてください。
これで大丈夫なはずです。
サンプルコード
Java用のドキュメント
https://docs.opencv.org/3.4.6/javadoc/index.html
当たり前だが、ネット上ではC++かPythonのサンプルばかりでJavaのサンプルなんて殆ど無い。
ので、作るの結構大変。
Java用のチュートリアル
https://opencv-java-tutorials.readthedocs.io/en/latest/index.html
opencv_java3
MainActivity 内で OpenCVのライブラリをロードしないといけません。
static {
System.loadLibrary("opencv_java3");
}
Java用のチュートリアルで、
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
としていたりするので最初真似したのですが、エラーで落ちました。
2019-04-19 11:07:15.769 30066-30066/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 30066
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/base.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_resources_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/lib/arm64, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/base.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_dependencies_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_resources_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_0_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_1_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_2_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_3_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_4_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_5_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_6_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_7_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_8_apk.apk!/lib/arm64-v8a, /data/app/com.example.myapplication-fDcSfSSkFntiOrqnxzGmJQ==/split_lib_slice_9_apk.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]] couldn't find "libopencv_java346.so"
at java.lang.Runtime.loadLibrary0(Runtime.java:1011)
at java.lang.System.loadLibrary(System.java:1660)
at com.example.myapplication.MainActivity.<clinit>(MainActivity.java:29)
at java.lang.Class.newInstance(Native Method)
at android.app.Instrumentation.newActivity(Instrumentation.java:1178)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3191)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3411)
at android.app.ActivityThread.-wrap12(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1994)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7529)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
Core.NATIVE_LIBRARY_NAME の値を出力してみると opencv_java346 となっており、なんか問題があるかもです。
ちょっとダサいですが、直接 opencv_java3 と書きましょう。
画像を2値化するサンプルコード
MainActivity.java
package com.example.myapplication;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("opencv_java3");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
onImageScan();
}
private void onImageScan() {
Bitmap _bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pet);
Bitmap bmp = _bmp.copy(Bitmap.Config.ARGB_8888, true);
Mat mat = new Mat();
Utils.bitmapToMat(bmp, mat, true);
// グレースケール変換
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2GRAY);
// 2値化
Imgproc.threshold(mat, mat, 100.0, 255.0, Imgproc.THRESH_BINARY);
// Bitmapに変換するため、RGB形式に変換
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_GRAY2RGBA, 4);
Bitmap dst = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
// MatからBitmapへ
Utils.matToBitmap(mat, dst);
// 結果表示
ImageView iv1 = (ImageView)findViewById(R.id.imageView1);
iv1.setImageBitmap(bmp);
ImageView iv2 = (ImageView)findViewById(R.id.imageView2);
iv2.setImageBitmap(dst);
}
}
imageView1、imageView2は適当に作って、R.drawable.petは適当に拾ってきた画像に置き換えてくだせい。
あとがき
プロジェクトを作るたびにこれはいくら何でもしんどい。
うまいこと一気にやるシェルかなんかを作らないと。
以上。