コンテンツにスキップ

Top

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モジュールが出ているはず。

チェックを入れると関連付けられる。

エラー対応する

これで本来の設定は終わりだが、必ずと言ってよいほどエラーが出るはず。

1
2
3
4
5
6
7
8
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の

1
   <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" />

1
   <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のライブラリをロードしないといけません。

1
2
3
    static {
        System.loadLibrary("opencv_java3");
    }

Java用のチュートリアルで、

1
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

としていたりするので最初真似したのですが、エラーで落ちました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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は適当に拾ってきた画像に置き換えてくだせい。

あとがき

プロジェクトを作るたびにこれはいくら何でもしんどい。
うまいこと一気にやるシェルかなんかを作らないと。

以上。