THETAの中でOpenCVを動かす【プレビューフレーム応用編】

## はじめに
こんにちは、リコーの@roohii_3です。

これまでTHETAの中でOpenCVを動かすことに関して記事を書いてきました。

* [THETAの中でOpenCVを動かす][NDK] (←NDKを使った例)
* [THETAの中でOpenCVを動かす【プレビューフレーム取得編】][preview] (←前回の記事)

本記事は「[THETAの中でOpenCVを動かす【プレビューフレーム取得編】][preview]」の応用編です。
**プレビューフレームから「動体検知」し、何か動くものを認識したら写真を撮る**、といったようにTHETAを監視カメラ的なものに改造してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304709/71d0fd32-98cb-1eb0-683a-156b002c4f2f.png)

コードは以下で公開しています。
[https://github.com/theta-skunkworks/theta-plugin-opencv-detection-sample][code]
※ 実装はいろいろと手を抜いてます。流用する場合はご注意を…。


## 開発環境
* OpenCV Android pack ver. 3.4.5
* Android Studio ver. 3.3+
* gradle ver. 5.1.1
* RICOH THETA V Firmware ver. 3.00.1


## 実装
事前準備・OpenCVの導入手順などは[前回記事][preview]の「事前準備」を見てください。
実装は、[前回のコード][prevcode]をベースに**動体認識**、**静止画撮影**の処理を追加しています。

### 動体認識
動体認識アルゴリズムは以下のような流れです。
それぞれの処理はOpenCVのチュートリアルに載っているようなものです。
対象言語はPythonですが、[OpenCV-Pythonチュートリアル（日本語訳版）](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/index.html#)も参考になります。

1. [背景差分](https://docs.opencv.org/3.4.5/d1/dc5/tutorial_background_subtraction.html)
2. 二値化
3. [モルフォロジー（ノイズ除去）](https://docs.opencv.org/3.4.5/d3/dbe/tutorial_opening_closing_hats.html)
4. [領域検出](https://docs.opencv.org/3.4.5/da/d0c/tutorial_bounding_rects_circles.html)
5. [バウンダリーボックスの取得](https://docs.opencv.org/3.4.5/da/d0c/tutorial_bounding_rects_circles.html)

実際にどう実装しているかは、[GitHub](https://github.com/theta-skunkworks/theta-plugin-opencv-detection-sample/blob/master/app/src/main/java/com/theta360/opencvdetection/MainActivity.java)から `MainActivity.java` の `onCameraFrame()` メソッドを見てみてください。


### 静止画撮影

静止画撮影処理の部分は、OpenCVの `tutorial-3-cameracontrol` と [THETA Plug-in: CameraAPI Capture Plugin][cameraapisample] を参考にしました。
どちらとも `takePicture()` と `Camera.PictureCallback` という静止画撮影に必要なメソッドの実装がされていますが、本記事のコードでは `ThetaController.java` で実装しています。

#### Camera APIパラメータ
[前回記事][preview]でも触れたように、THETAには独自のCamera APIパラメータがあります。
たとえば撮影時には、以下のパラメータを設定しています。

```java:
Camera.Parameters params = mCamera.getParameters();

// 画像サイズ
params.setPictureSize(5376, 2688);

// 静止画撮影モード
params.set("RIC_SHOOTING_MODE", "RicStillCaptureStd");

// 自動露出モード
params.set("RIC_EXPOSURE_MODE", "RicAutoExposureP");

// スティッチングの種類を"DynamicStitching"に設定
params.set("RIC_PROC_STITCHING", "RicDynamicStitchingAuto");
```

##### スティッチング
上のコード例の一番最後にあるように、ここでは「スティッチングの種類」のパラメータに"`RicDynamicStitchingAuto`"を設定しています。
これは被写体に応じて精密なスティッチングを行うものですが、少し処理時間がかかります。
「スティッチングは簡易的で良いので、処理時間を速くしたい」という方は"`RicStaticStitching`"を使うと良いと思います。

他にもスティッチングの種類を示すパラメータがあるので、作るものに応じて使い分けてみてください。
パラメータ値と特徴を以下にまとめておきます。
（参考: [THETA Plug-inドキュメント > Camera API](https://api.ricoh/docs/theta-plugin-reference/camera-api/)）

| パラメータ値 | 特徴 |
|---|---|
| “RicNonStitching” | スティッチングしない（魚眼画像が横に２枚並んだ状態になる）。 |
| “RicStaticStitching” | 簡易的にスティッチングを行う。 |
| “RicDynamicStitchingAuto” | 被写体に応じて精密にスティッチングを行う。<br>ちょっと処理時間がかかる。 |
| “RicDynamicStitchingSave” | 精密にスティッチングするときの「スティッチングの仕方（つなぐ位置など）」の情報を保持する。<br>連続撮影するときに便利。<br>１枚目はこのパラメータ値を使い、２枚目以降は“`RicDynamicStitchingLoad`”を使うようにすると、1枚目と同じ具合にスティッチングできる。 |
| “RicDynamicStitchingLoad” | “`RicDynamicStitchingSave`”と合わせて使う。 |

##### 画像サイズ
ここでは `THETA V` 用のプラグインを想定していますが、`THETA Z1` では画像サイズを `6720x3360` に設定する必要があります。

```java:
// Z1で動かすときは、6720x3360 を設定する
params.setPictureSize(6720, 3360);
```

また、スティッチングの種類を示すパラメータに“`RicNonStitching`”を設定したときも、画像サイズが変わります。
`THETA V`は`5792x2896`、`THETA Z1`は`7296x3648`を設定します。

#### 注意点
記事の最初に書いたように、本記事のコードはいろいろと手を抜いてます。
たとえば…

* 静止画にExif情報が付与されない
* THETAが動いたり、露出が変わるだけでも敏感に動体認識が反応してしまう
* 認識処理～撮影～画像保存が単一スレッド上で行われている（本来であれば、保存など処理が重い部分は別スレッドに分けるべし）
* 容量がいっぱいになっても撮り続けようとする（本来であれば、ストレージ容量に応じて撮影制限する必要あり）
* （たぶん他にもある）

もし本コードを何かに流用する場合は、その辺りも注意して実装お願いします…。

## 実行
一番最初にプラグインを動かすときは、[Vysor](https://www.vysor.io/)等を使って**パーミッションの設定**をします。
※ [プラグインストア](https://pluginstore.theta360.com/)からインストールしたプラグインは、パーミッション設定は不要です。

### パーミッションの設定
[Vysor](https://www.vysor.io/)等を使ってSettingsアプリを開き、"Motion Detector > Permissions"より、"Camera"、"Storage"の許可をONにします。
![permissions.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304709/712a5520-01ad-e652-ed09-633dcfdce112.jpeg)

### 実行してみた様子
Vysorを使ってプラグインを実行したときは、リアルタイムで動体認識の様子を見られるようにしました。
緑色の四角で囲まれた部分が動体認識した箇所です。
![screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304709/15246c28-297b-c474-162d-fc125af29a36.jpeg)
THETAの前で手を振ってみましたが、ちゃんと手が認識されました。

#### 処理速度など
OpenCVの出力文によると、**27～30fps**くらい出ていました。
![fps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304709/c09ff7d6-cd02-8350-1843-381ace9acdb0.png)
今回はプレビューのフレームサイズを最小の`640x320`にしました。フレームサイズによってはフレームレートが下がってしまいます。作るものに応じてフレームサイズを調整すると良いと思います。
フレームサイズを変更するには、"`RIC_SHOOTING_MODE`"のパラメータ値を変更します。
パラメータ値は[THETA Plug-in ドキュメント > Camera API](https://api.ricoh/docs/theta-plugin-reference/camera-api/)の`Shooting Mode`を参考にしてください。

```example.java
Camera.Parameters params = mCamera.getParameters();
// プレビューのフレームサイズを 1920x960 に設定
params.set("RIC_SHOOTING_MODE", "RicMoviePreview1920");
```

#### 保存ファイル
PCにTHETAを繋いで中身を確認すると、"DCIM"の下に"MotionDetector"フォルダができています。
その中に **[1]動体認識したときの撮影画像** と、**[2]認識したものを矩形で示した画像** が保存されます。
![save_folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304709/f17a95b2-c3d7-2b6d-cd69-2b1c2ea507c0.png)
※ 例によって手抜き実装なので、[1]、[2]の画像が保存されるタイミングがズレてます…。

## おわりに
THETAの中で、果たしてリアルタイム画像処理なんてできるのかな？、と思っていましたが案外動いちゃいました。
THETA＋Computer Visionに興味ありなユーザーさんがもしいたら、本記事が参考になれば幸いです。


### 参考
* [Docs / THETA Plug-in > Camera API](https://api.ricoh/docs/theta-plugin-reference/camera-api/)
* [THETA Plug-in: CameraAPI Capture Plugin][cameraapisample]
* OpenCV Tutorials：
    * [How to Use Background Subtraction Methods](https://docs.opencv.org/3.4.5/d1/dc5/tutorial_background_subtraction.html)
    * [More Morphology Transformations ](https://docs.opencv.org/3.4.5/d3/dbe/tutorial_opening_closing_hats.html)
    * [Creating Bounding boxes and circles for contours ](https://docs.opencv.org/3.4.5/da/d0c/tutorial_bounding_rects_circles.html)
* [OpenCV-Python-Tutorials（日本語訳版）](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/index.html)
* [OpenCV Java documentation](https://docs.opencv.org/3.4.5/javadoc/index.html)

### RICOH THETAプラグインパートナープログラムについて
THETAプラグインに興味を持たれた方がいれば、以下の記事もぜひご覧ください。

* [THETAプラグインパートナープログラムはじめました](https://qiita.com/mrtakada/items/8f3bbf8d283c26899473)
* [【THETAプラグイン開発】プラグインパートナープログラムに登録してみよう](https://qiita.com/mShiiina/items/55d98f366e650ca42251)

[RICOH THETAプラグイン開発者コミュニティ](https://qiita.com/organizations/theta-plugin)では、他にも記事を書いています。
RICOH THETAプラグインについては[こちら](https://api.ricoh/products/theta-plugin/)。興味を持たれた方は[twitter](https://twitter.com/thetaplugin)のフォローと[THETAプラグイン開発コミュニティ(slack)](https://join.slack.com/t/thetadeveloper/shared_invite/enQtNzcxODI2MTk2ODg0LWY5YmU2ZTIxNGYwNzlmNWZjMWMyMWFlMzY3ZmQ3NGU3ZjZiZjI0Njk4MGIxMDU5NDJiMzQ4Y2FkNDEzYmFlNGU)への参加もぜひどうぞ｡


[NDK]:https://qiita.com/roohii_3/items/63a546169981f41967df
[preview]:https://qiita.com/roohii_3/items/f96918cdb52fd84e1cea
[prevcode]:https://github.com/theta-skunkworks/theta-plugin-opencv-preview-sample
[code]:https://github.com/theta-skunkworks/theta-plugin-opencv-detection-sample
[cameraapisample]:https://github.com/ricohapi/theta-plugin-camera-api-sample