LoginSignup
45
44

More than 3 years have passed since last update.

Androidで OpenCV 4を使う方法とカメラライブビューの表示

Last updated at Posted at 2019-05-25

この記事について

カメラ画像をネガポジ反転して出力するだけの簡単なAndroidアプリを作ります。
カメラ入力にはOpenCV4を使用します。
カメラ画像を用いて何らかのアプリを作るためのベースとなるプロジェクトとして利用できます。
この記事では、OpenCV Managerは使わずにAPK内にOpenCVライブラリを含む方法で説明します。

image.png

追記(2020/07/18) 最近のAndroid Studio (またはAndroid SDK) では、この方法は使えないようです。Camera2 APIかCamera Xを使った方が良いです。 CameraXを使う方法はこちらにまとめました。
https://qiita.com/iwatake2222/items/c0ebe6d84afdef57aab3

開発環境

  • Windows 10 64-bit
  • Android Studio 3.3.2
  • 開発言語: Java
  • OpenCV 4.1.0
  • 対象とするデバイス: Galaxy S7

事前準備

Android Studioのインストール

adbのあるフォルダにパスを通しておくと、色々と便利かも。(本記事では特に使用しません)

OpenCVのダウンロード

https://github.com/opencv/opencv/releases
からopencv-4.1.0-android-sdk.zip をダウンロードして、適当な場所に展開しておきます。バージョンは好きなのを選んでください。

以後、展開先のパスを{OpenCV-android-sdk} として説明を記載します。

自分のプロジェクトとapkにOpenCVを取り込む

新規プロジェクトの作成

  • Android Studioを開く
  • Start a new Android Studio project をクリック
  • Empty Activity を選び、Next
  • アプリケーション名などを設定する
    • 試すだけなら全部デフォルトで良いです
    • 今回は、開発言語にJava、Minimum API levelはAPI 21としました
    • リリースしたりする予定がある場合は、package nameをちゃんと決めてください
  • Android端末をUSBでPCに接続します
  • メニューバーのRun -> Run'App' を選び、Hello Worldが出力されるデフォルト状態のアプリが、Android端末で起動すればOKです

01.png

OpenCVを取り込む

  • Import Module
    • メニューバーのFile -> `New -> Import Module を選ぶ
    • Source directory に、先ほどダウンロード・展開した、{OpenCV-android-sdk}/sdk を指定
      • OpenCV 3.x以前だと{OpenCV-android-sdk}/sdk/java を指定?
    • Module name がデフォルトだとsdk となり分かりづらいので、適当にopencv としておきます
    • その他設定はデフォルトのままで、NextFinish

02.png

  • 依存関係の設定
    • メニューバーのFile -> Project Structure を選ぶ
    • 左側のapp を選び、Dependencies タブを選ぶ
    • 右上の+(add) をクリックし、3: Module dependenciy を選ぶ
    • opencv を選らんで、OK

03.png

  • (不要) OpenCVライブラリ(soファイル)のコピー

    • 以前だと、OpenCVライブラリ(soファイル)を、自分でjniLibsにコピーする必要があったのですが、今では(OpenCV4以降??)不要なようです
  • (不要) AndroidManifest, build.gradleの修正

    • 以前だと、OpenCVのAndroidManifest.xml 内で、targetSdkVersion 等が指定されているのを削除したり、build.gradle 内で各種sdkVersion(compileSdkVersion 等)をappとそろえる必要がありました。
    • 今では(OpenCV4以降??)不要なようです

OpenCVのロード

OpenCVを使用するActivityのコード(プロジェクト生成時にEmpty Activityを選び、何も設定を変えていなかったら、app\src\main\java\com\example\myapplication\MainActivity.java )を以下のように編集します。

実際には、どちらか片方で十分です。実プロダクトでは、System.loadLibrary だけを使うと思います。

MainActivity.java
public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("opencv_java4");     // 追加
    }

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

        OpenCVLoader.initDebug();   // 追加
    }
}

この状態でRunして、Logcatに以下のようなログが出たらOKです。

Logcat出力
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: General configuration for OpenCV 4.1.0 =====================================
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:   Version control:               4.1.0
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:   Platform:
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     Timestamp:                   2019-04-07T19:03:43Z
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     Host:                        Linux 4.15.0-47-generic x86_64
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     Target:                      Android 1 aarch64
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     CMake:                       3.6.0-rc2
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     CMake generator:             Ninja
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     CMake build tool:            /opt/android/android-sdk.gradle/cmake/3.6.4111459/bin/ninja
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper:     Configuration:               Release

なんか、Logcatの上の方を見ていると、OpenCV/StaticHelper: OpenCV error: Cannot load info library for OpenCV こんなエラーが出ているのですが、動作には影響がありませんでした。

カメラライブビュー表示

ここまでで、OpenCVを取り込んだプロジェクトを準備できたので、カメラのライブビューを表示するアプリケーションを作ってみようと思います。

パーミッションの設定

カメラを使うための設定をします

マニフェストの設定

まず、「このアプリケーションはカメラを使うよ」と宣言しておく必要があります。

app/src/main/AndroidManifest.xml に以下の3行を追加します。追加するのは、<manifest> の直下です。既にある<application> と同列に追加します。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <!--↓追加 -->
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <!--↑追加-->

    <application
        android:allowBackup="true"
省略
    </application>

</manifest>

許可をもらう

実は、マニフェストの設定だけでも事足ります。
しかし、これだけだと、ユーザがアプリの管理画面から権限設定をする必要があります。通常のユーザはその画面がどこにあるかも知りません。

なので、アプリの方から自発的に「許可をください」と要求画面を出します。

以下のようなコードで、もしも許可設定されていなかったら、許可要求画面を出すようにします。呼び出し元のコードは後程示します。

MainActivity.java
    public static boolean getPermissionCamera(Activity activity) {
        if (ContextCompat.checkSelfPermission(
                activity,
                android.Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            String[] permissions = new String[]{Manifest.permission.CAMERA};
            ActivityCompat.requestPermissions(
                    activity,
                    permissions,
                    0);
            return false;
        } else {
            return true;
        }
    }

CameraViewの配置

レイアウトに、CameraViewを追加します。layout/activity_main.xml を開き、Textモードで、org.opencv.android.JavaCameraView を追加します。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--↓追加-->
    <org.opencv.android.JavaCameraView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        tools:layout_editor_absoluteX="0dp"
        tools:layout_editor_absoluteY="0dp"
        app:show_fps="true"/>
    <!--↑追加-->
</android.support.constraint.ConstraintLayout>

コード全文

コード全文を以下に示します。

  • CameraBridgeViewBase.CvCameraViewListener インターフェイスを継承します。
    • MainActivity に、implements CameraBridgeViewBase.CvCameraViewListener を追加後、Alt+Enterで適当にimportを追加してもらいます。再度、Alt+Enterで、Implement Methods をして必要な関数を追加してもらいます
  • CameraBridgeViewBase を宣言して、先ほどレイアウトに配置したJavaCameraView に割り当てます。setCvCameraViewListener でこのActivityを指定して、先ほど追加したonCameraViewStartedonCameraViewStoppedonCameraFrame を呼んでもらいます。
  • onCameraFrame は毎フレーム呼ばれる関数です。引数(inputFrame )にカメラからの入力画像が入り、戻り値に表示したいMat を設定します。
    • このonCameraFrame はUIスレッドとは別スレッドで動いているので、ここで画像処理等多少重たい処理をやっても大丈夫そうです。Best Practiceかどうかは分かりませんが。
    • 今回はネガポジ反転してみました
    • onCameraFrame での処理が重くなると、描画のfpsも当然遅くなります。これは実装者にとってはうれしいことです。onCameraFrame 内で重い処理をやっている最中に、再度onCameraFrame 関数が呼ばれることはないようです。なので、リエントラントにする必要はなさそうです。
MainActivity.java
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener {
    private CameraBridgeViewBase m_cameraView;

    static {
        System.loadLibrary("opencv_java4");
    }

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

        getPermissionCamera(this);
//        OpenCVLoader.initDebug();
        m_cameraView = findViewById(R.id.camera_view);
        m_cameraView.setCvCameraViewListener(this);
        m_cameraView.enableView();
    }

    public static boolean getPermissionCamera(Activity activity) {
        if (ContextCompat.checkSelfPermission(
                activity,
                android.Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            String[] permissions = new String[]{Manifest.permission.CAMERA};
            ActivityCompat.requestPermissions(
                    activity,
                    permissions,
                    0);
            return false;
        } else {
            return true;
        }
    }

    @Override
    public void onCameraViewStarted(int width, int height) {

    }

    @Override
    public void onCameraViewStopped() {

    }

    @Override
    public Mat onCameraFrame(Mat inputFrame) {
        // ここで何らかの画像処理を行う
        // 試しに、ネガポジ反転してみる
        Core.bitwise_not(inputFrame, inputFrame);
        return inputFrame;
    }
}

機種や、デバイスの向き、設定によっては画面が回転していたりする可能性があります。それらの調整をやるとコードと説明が複雑になるので省略しています。
同様に、onDestoryやonResumeでの終了、復帰も省略しています。

おわりに

  • AndroidでOpenCV4を使う手順をまとめました
  • OpenCV4を使ってカメラプレビューを取得、表示する手順をまとめました
45
44
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
45
44