Android　で OpenCV のサンプル camerapreview を試す

[AndroidStudio で OpenCV をインポートする]
(https://qiita.com/ohwada/items/71eb18296aac46f9f5ee)
の続きです。

### 概要

Android 用 OpenCV　には、
８個のサンプルが同封されている。

そのうち　camerapreview　は、
基本となるサンプルなので、少し詳しく説明する。

OpenCV の Java API に関するドキュメントは 下記の JavaDoc 由来のものがある。

[OpenCV Java documentation]
(https://docs.opencv.org/master/javadoc/index.html)

しかし、 Android API に関するドキュメントはないようです。
ないようです
ソースコードを解読した。


### OpenCV の Mat

OpenCV　で扱う画像の形式です。

参考 [OpenCV の Mat ってやつの話](http://shimikel.hatenablog.com/entry/2015/07/25/190139)

参考 [cv::Matの基本処理](http://opencv.jp/cookbook/opencv_mat.html)

参考 [OpenCVのMatとbyte[]の相互変換](https://qwx.jp/opencv-interconversion-map-byte-array/)

### tutorial-1-camerapreview

カメラのプレビュー画像を表示するサンプルです。

下記の説明には、
Java API または Native camera API を使用するとある
Native camera API は、古いバージョンにはあったようだが、
最新バージョン 4.1.2  には、見当たらず。

[opencv.org : OpenCV4Android Samples](https://opencv.org/opencv4android-samples/)


参考 [NativeCameraViewはまだ存在しますか？](https://codeday.me/jp/qa/20190709/1215671.html)

### アプリを作成する。

(1) 新規のプロジェクトを作成する
File -> New -> New Project -> Empty Activity

(2) 前回の記事で作った Android のライブラリをインポートする
File -> New -> Import Module -> Import JAR / AAR Package

(3) 依存関係を設定する
 File -> Project Structure 

(4) AndroidManifest にカメラのアクセス許可を追加する。

```AndroidManifest.xml
    <uses-permission android:name="android.permission.CAMERA"/>
```

(5) レイアウトファイルに、JavaCameraView　またはJavaCamera2View　を配置する。

いずれも SurFaceView を継承している。
JavaCameraView　は、旧来の　Camera　API を使用する。
JavaCamera2View　は、API 21 からサポートされた Camera2　API を使用する。

```activity_main.xml
<FrameLayout　>
    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"
        android:id="@+id/camera_view"
        opencv:show_fps="true"
        opencv:camera_id="any" />
</FrameLayout>
```

属性 (attributes)
opencv:show_fps
true　のとき、ビューの左上に、描画速度 (fps)を表示する
opencv:camera_id
カメラを指定する
front: 前面カメラ
back: 背面カメラ
any: 第１カメラを自動判定


(6) CameraActivity を継承して MainActivity.java を作成する。


```MainActivity.java
public class MainActivity extends CameraActivity implements CvCameraViewListener2 {

public void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
        // カメラビューを取得する
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.camera_view);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
}

public void onResume()
    {
// OpenCV　をロードする
        if (OpenCVLoader.initDebug()) {
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
    }

}

private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
    public void onManagerConnected(int status) {
        // カメラビューを有効にする
        mOpenCvCameraView.enableView();
    }
}

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        // カメラ画像をカラーで表示する
        return inputFrame.rgba();
}
```

### OpenCVLoader

OpenCV ライブラリの初期化を行うヘルパークラスです。
下記のメソッドを持つ。


- public static boolean initDebug()
OpenCVライブラリをロードして初期化する。
初期化が成功した場合にtrueを返す。

- public static boolean initDebug(boolean InitCuda)
CUDA ランタイムライブラリをロードして、
OpenCVライブラリを初期化する。

- public static boolean initAsync(String Version, Context AppContext,
            LoaderCallbackInterface Callback)
OpenCV Engine service を使用してOpenCVライブラリをロードおよび初期化する。

参考 [wikipedia : CUDA](https://ja.wikipedia.org/wiki/CUDA)

参考 [github : OpenCV Engineサービス](https://github.com/opencv/opencv/tree/master/platforms/android/service/engine/src/org/opencv/engine)

### BaseLoaderCallback

OpenCVLoader　のコールバッククラスです。
２つのメソッドを持つ。

- public void onManagerConnected(int status)
OpenCVライブラリの初期化後に呼び出されるコールバックメソッド。
引数 status　初期化のステータス

-  public void onPackageInstall(final int operation, final InstallCallbackInterface callback)
パッケージのインストールが必要な場合に呼び出されるコールバックメソッド。
引数 operationは、
InstallCallbackInterface　の定数
引数 callbackは、
インストール（install）およびキャンセル（cancel）の２つメソッドを持つオブジェクト


初期化ステータス定数

- static final int SUCCESS = 0;
OpenCVの初期化が正常に終了しました。

- static final int MARKET_ERROR = 2;
Google Play（Google Play Market）を呼び出すことはできません。

- static final int INSTALL_CANCELED = 3;
OpenCVライブラリのインストールはユーザーによってキャンセルされました。

- static final int INCOMPATIBLE_MANAGER_VERSION = 4;
OpenCVマネージャーサービスのこのバージョンは、アプリと互換性がありません。
おそらく、サービスの更新が必要です。


- static final int INIT_FAILED = 0xff;
OpenCVライブラリの初期化に失敗しました。


### InstallCallbackInterface

OpenCV パッケージのインストールに関するコールバックインターフェイス。

3つのメソッドを持つ

- public void install();
インストールが承認されました。

- public void cancel();
インストールはキャンセルされます。

- public void wait_install();
パッケージのインストールを待ちます。

２つの定数を持つ

-  static final int NEW_INSTALLATION = 0;
新しいパッケージのインストールが必要です。

- static final int INSTALLATION_PROGRESS = 1;
パッケージのインストールが進行中です。

###　CvCameraViewListener2

CameraView　に関するListener クラス。

３つのメソッドを持つ。

- public void onCameraViewStarted(int width, int height)
このメソッドは、カメラのプレビューが開始されたときに呼び出される。
このメソッドが呼び出された後、
onCameraFrame（）コールバックを介して、カメラ画像が配信される。
引数は、配信されるカメラの幅（width）と高さ（height）

- public void onCameraViewStopped()
このメソッドは、何らかの理由でカメラのプレビューが停止したときに呼び出される。
このメソッドが呼び出された後、フレームはonCameraFrame（）コールバックを介したカメラ画像の配信は停止する。

- public Mat onCameraFrame(CvCameraViewFrame inputFrame)
このメソッドは、カメラ画像の配信を行う必要があるときに呼び出される。
引数 inputFrame　は、カメラ画像。
戻り値は、Mat形式で、画面に表示される。

###　CvCameraViewFrame

カメラデバイスから１つの画像を表す抽象クラスです。
onCameraFrame コールバック用に使用される。


２つのメソッドを持つ。

- public Mat rgba();
このメソッドは、カメラ画像を　カラー(RGBA）　のMat形式で返す。

- public Mat gray();
このメソッドは、カメラ画像を　グレースケール（single channel gray scale ）　のMat形式で返す。

###　camerapreview の処理の流れ

アプリが起動すると、
CameraActivity#onStart が呼ばれる。
Activity#requestPermissions を実行して、
カメラのアクセス許可を要求する。

JavaCamera2View#enableView が実行されると、
Camera2　API　と ImageReader　を使用して、
カメラデバイスからカメラ画像を取得する。
カメラ画像を OpenCV の Mat 形式に変換する。
カメラ画像が更新されると、
CvCameraViewListener2#onCameraFrame を呼び出して、
SurFaceView　に描画する。


少し詳しく処理の流れを追ってみた。


- JavaCamera2View#enableView
- CameraBridgeViewBase#enableView
- CameraBridgeViewBase#checkCurrentState
- CameraBridgeViewBase#processEnterState
- CameraBridgeViewBase#onEnterStartedState
- JavaCamera2View#connectCamera

- JavaCamera2View#initializeCamera
CameraManager#openCamera　を実行して、
カメラをオープンする。

- JavaCamera2View#createCameraPreviewSession()
ImageReader#newInstance を実行して、
ImageReader　を生成する。

- ImageReader.OnImageAvailableListener#onImageAvailable
JavaCamera2Frame(image) を実行して、
Android の　Image　を　OpenCV の Mat　に変換する。

- CameraBridgeViewBase#deliverAndDrawFrame

- CvCameraViewListener2#onCameraFrame を呼び出す。
戻り値の OpenCV の Mat　を　Android の　Bitmap　に変換する。
Bitmap　を　SurFaceView　の　Canvas　に描画する。


### アプリを実行する


analyze_apk　すると、
下記の２つが同封されていることが確認できる。
- libopencv_java4.so
- libc++_shared.so

![opencv42_analyze_apk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/5614389d-386e-c43d-da0f-5882387aa485.png)


アプリが起動すると、
カメラのアクセス許可を要求する。

![opencv42_permission.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/aa393232-25de-a619-8df3-7cac5c4d028f.png)


「許可」すると、カメラのプレビュー画像を表示する

![opencv42_preview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/3c8f1dce-f77a-4f7b-c9d1-a944e3c8aa37.png)

サンプルコードをgithub に公開した。
 https://github.com/ohwada/Android_Samples/tree/master/Opencv42