Android で OpenCV と　MobleNet を使って 物体検出する

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

### MobleNet

MoblieNetは、モバイル用に軽量化された　Neural Network　のフレームワークです。

- [速くて軽くて精度の良い、MobileNetのポイントをまとめてみた]
(https://qiita.com/simonritchie/items/f6d6196b1b0c41ca163c)

- [MobileNet(v1,v2,v3)を簡単に解説してみた]
(https://qiita.com/omiita/items/77dadd5a7b16a104df83)

### OpenCV の MobleNet


OpenCV 3.3 から対応している。
cv::dnn モジュールと Java ラッパーが用意されている。

[opencv : Deep Neural Networks (dnn module)](https://docs.opencv.org/master/d2/d58/tutorial_table_of_content_dnn.html)

[opencv : Class Dnn](https://docs.opencv.org/3.4/javadoc/org/opencv/dnn/Dnn.html)

### mobilenet-objdetect

OpenCVのレポジトリにある Android 用のサンプルコードです。
今回はこれを試す。

- [opencv : How to run deep networks on Android](https://docs.opencv.org/3.4/d0/d6c/tutorial_dnn_android.html)

- [github : opencv android mobilenet-objdetect](https://github.com/opencv/opencv/tree/master/samples/android/mobilenet-objdetect)

### 学習済みデータ

上記のサンプルコードには、学習済みデータとして、
下記の２つのファイルが必要です。
- 構成ファイル MobileNetSSD_deploy.prototxt
- 重みファイル MobileNetSSD_deploy.caffemodel

下記からダウンロードする。
https://github.com/chuanqi305/MobileNet-SSD

検出できる物体は下記の20種類。

- aeroplane (飛行機)
- bicycle (自転車)
- bird (鳥)
- boat (小型船)
- bottle (瓶)
- bus (バス)
- car (車)
- cat (猫)
- chair (椅子)
- cow (乳牛)
- diningtable (食卓)
- dog (犬)
- horse (馬)
- motorbike (オートバイ)
- person (人物)
- pottedplant (鉢植え)
- sheep (羊)
- sofa (長椅子)
- train (列車)
- tvmonitor (テレビ受像機)

### アプリを作成する

まず、下記を読んでください。
[Android　で OpenCV のサンプル camerapreview を試す](https://qiita.com/ohwada/items/53305d29f33bd14e6683)

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

Neural Network を取得する。

```
public class MainActivity extends CameraActivity implements CvCameraViewListener2 {

public void onCameraViewStarted(int width, int height) {
    // 学習済みモデルを設定する
    String proto = getPath("MobileNetSSD_deploy.prototxt", this);
    String weights = getPath("MobileNetSSD_deploy.caffemodel", this);
    // Neural Network を取得する
    mNet = Dnn.readNetFromCaffe(proto, weights);
}

private  String getPath(String fileName, Context context) {
        // Assets フォルダーのファイルを外部記憶にコピーする
        // 詳細 略
```

物体を検出する。

```
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    final int IN_WIDTH = 300;
    final int IN_HEIGHT = 300;
    final double MEAN_VAL = 127.5;

    Mat frame = inputFrame.rgba();

    // 物体検出する物体の大きさを設定する
    Mat blob = Dnn.blobFromImage(frame,         IN_SCALE_FACTOR,
     new Size(IN_WIDTH, IN_HEIGHT),
    new Scalar(MEAN_VAL, MEAN_VAL, MEAN_VAL), 
    /*swapRB*/false, /*crop*/false);
    mNet.setInput(blob);

    // フォワードパスを実行し、物体を検出する
    Mat detections = mNet.forward();
```

### アプリを実行する

アプリが起動すると、
カメラのアクセス許可を要求する。
![opencv_permission.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/68677c41-b04e-8fa8-740b-c20c04729ebc.png)

スクリーンショット
猫を検出した例
![opencv52_cat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/85ad6bd5-27d6-5649-fde9-32752a2e9f53.png)

馬、人物、車、犬　を同時に検出した例
![opencv52_mix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/5487b923-077e-875e-7ec7-752f56e0045a.png)

いろいろ試してみた。
１つの学習済みデータで、
20種類の物体がリアルタイムに検出できた。
クラウドを使わないモバイルアプリとしては、優秀だね。

サンプル画像
https://github.com/chuanqi305/MobileNet-SSD/tree/master/images

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