Help us understand the problem. What is going on with this article?

THETAプラグインでライブプリビューを扱いやすくする

はじめに

リコーの @KA-2 です。

弊社ではRICOH THETAという全周囲360度撮れるカメラを出しています。
RICOH THETA VRICOH THETA Z1は、OSにAndroidを採用しています。Androidアプリを作る感覚でTHETAをカスタマイズすることもでき、そのカスタマイズ機能を「プラグイン」と呼んでいます(詳細は本記事の末尾を参照)。

今回は、hogehoge認識と共に、認識結果に合わせた露出補正や、認識をトリガーに撮影なども行うTHETAプラグインが作成しやすくなる"土台"のプロジェクトファイル一式を作成したので紹介します。
2020年2月15日~2月16日に開催されたTMMF2020(Tsukuba Mini Maker Faire 2020)で、デモンストレーションしていた「Tensor Flow "Lite" でObject Detectionして、カメラが固定されていても映像だけがバナナをトラッキングする事例」※末尾にアニメGIFあり※も、この土台から作られています。(余談:こちらの記事はTensor Flow "Mobile"の事例です)

「はやくTesor Flow Liteの事例も紹介して!」という方々、しばしお待ちを。
まずは「他の応用も広がる土台」を抑えていただいてから、3月末~4月初旬を目処にそちらの記事も公開予定です。土台を抑えてからのほうが応用が広がることうけあいです。

Android Camera APIとWeb API内部アクセス、ライブプリビューの違い

前提として、以前抜粋した「THETAプラグインのしくみ」「撮影アプリ(com.theta360.receptor)」について少しだけ掘り下げて説明します。とはいえ、荒い粒度の大枠説明ですのでご安心を。

Android Camera APIのライブプリビュー

Android Camera APIについては、Googleのドキュメントを参照してください。
こちらも全体像がつかみやすいと思います。
Android Camera APIのTHETA固有事項については、弊社のドキュメントを参照してください。

概要は下図のとおり。

スライド1.JPG

[補足]
Android Camera APIを使ってプレビューフレーム取得する過去事例は以下です。

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

上記記事の例では、OpenCVも併せて利用できるようにしています。WebUIに映像を出す点は省略しています。
本記事の例をベースにOpenCVを利用したり、上記記事の例をベースとしてWebUIに映像を出すことは可能です。

Web API内部アクセスのライブプリビュー

本記事で「Web API」と略しているのは「RICOH THETA API v2.1」のことです。

Web APIライブプリビューのフォーマット指定については、こちらを参照してください。
Web APIライブプリビューの開始についてはこちらを参照してください。

概要は下図のとおり。

スライド2.JPG

それぞれの特徴まとめ

それぞれの特徴を比較すると下表のようになります。

Android Camera API Web API 内部アクセス
1 通信オーバーヘッド
(圧縮と解凍含む)
ほぼなし
最終結果はYCrCb形式
通信オーバヘッドあり
圧縮と解凍も生じる
解凍後はBitmapクラス
2 扱える画像サイズ 640x320
1024x512
1920x960
3840x1920
640x320
1024x512
1920x960
3 フレームレート Max 30fps
低速側が柔軟
Max 30fps or Min 8fps
1920x960は8fpsのみ
4 扱いやすさ
(コードの書きやすさ、
デバッグ容易性)
難易度高い 簡単

ここで「ライブプリビューを受け取る」という局部性能だけで実施手段を決定すると、人によっては後の作業で泣きをみる(場合によっては挫折してしまう・・・)方も居るかもしれません。以降にもう少し判断材料となる説明をしますね。

「連続フレームが取得できたあとどうする?」が大切になります。
漠然と試してみるのなら以降の説明は後で読むとして、Web API内部アクセスから試してみることをおすすめします。

通信オーバヘッドについて

この項目は素直にみて頂いてOKです。撮影に関する全てをTHETAプラグインが引き受けるわけですから、性能が出る(無駄な演算パワーを使わない)のは Android Camera APIをダイレクトに使う方法です。

Android Camera APIで受け取れるデータ形式がYCrCb形式である点は問題にならないと思います。Bitmapクラス(のたとえばARGB8888形式)への変換は、さほど時間がかからない処理、と納得して頂けると思います。

画像サイズとフレームレート

この2つの項目は合わせてみる必要があります。

まず、Web API内部アクセスの事情を補足します。目を引くのは、1920x960のフレームレートが8fpsで頭打ちしていることかと。Web APIの内部アクセスができるとはいえ、仕様の上限は「外部機器へWi-Fi通信でライブプリビューを送ること」を念頭に決定されているためと思われます。実態にあわない数値だけの上限だと意味がなくなってしまいますので・・・。
そして、Android Camera API で 3840x1920の30fpsを選択したらどうなるかの説明が必要です。受け取れますが、その大きさのフレーム全てに、目的とする何らかの画像処理をかけたならば、結局フレームレートを落とさざるえなくなると思います。
「独自処理のために空いている演算パワー」があまりない状態です。

利用可能な演算パワーの範疇で、画像サイズとフレームレートを少しづつ落として折り合いをつける(妥協点を探る)ことになります。これを考えると、Web API内部アクセスで受け取れるライブプリビューが、大きく劣っているわけではなくなります。おそらくココ、一番大切なところです。
(Web API内部アクセスのライブプリビュー利用でTensor Flow LiteのObject Detectionがぼちぼち動いているわけでして、ぜんぜんダメというわけではなさそうでしょ?)

扱いやすさ

Android Camera APIをバリバリと使いこなせる方はこの項目は無視して良いと思います。
しかし、まだAndroidに不慣れな私には・・・結構手数が多いと感じます。コード量増加だけでなくデバッグも手間取りそうです。

Web API内部アクセスは、「性能出しはほどほどに、試作→実証検証→修正のサイクルを速く回し、成果物全体として必要な要件を洗い出すとき」や、「それほど複雑な画像処理が必要ない時」に役立つと思います。作成物を誰かに使ってもらえる状態にするまでが速いのです。

そして、ライブプリビュー以外でも、撮影に関する諸操作をWeb API内部アクセスで行う以下のような事例がそろっています。
音通信ライブラリChirpを利用してTHETAを操る
こういった点からも、やりたいことが短い時間でできるのはWeb API内部アクセスです。

ソースコード

こちらのプロジェクト一式を参照してください。
雛型として使うだけならREADME.mdをみるだけで十分かと思います。(今回は丁寧めに書いたつもりです)
必要に応じて以下の解説を参照してください。

ファイル構成

THETA Plug-in SDKをベースに作成しました。
既に公開済みのAutomatic Face Blur BETA (以降、自動顔ぼかしβ版)のソースコードから必要最小限の事項にしぼって移植を行っています。達成すべきことが異なる部分もあり、少しアレンジも加えてあります。

「theta-plugin-extendedpreview\app\src\main」配下のフォルダ構成は以下となっています。

theta-plugin-extendedpreview\app\src\main
├assets           // WebUIのHTMLとJavaScriptがあります。
└java\com\theta360
 ├pluginapplication
 │ ├model       // THETA Plug-in SDKのままです。
 │ ├network     // 過去事例と同じようにHttpConnector.javaに1つメソッドを追加しただけです。
 │ ├oled        // Oled.javaのみ(OLED描画ライブラリ記事参照 https://qiita.com/KA-2/items/b16fd6adc6db7db0fb8e)
 │ ├task        // GetLiveViewTask.java、MjisTimeOutTask.javaが新規です。TakePictureTask.javaは変更していません。
 │ └view        // MOTION JPEGのフレーム分割クラスMJpegInputStream.javaがあります。(https://github.com/ricohapi/theta-automatic-face-blur-plugin のファイル流用です)
 └extendedpreview   // MainActivity.javaとWebServer.javaがあります。

「本サンプルで できること」を考えると、THETA Plug-in SDKから変わったファイルが少ないと感じて頂けると思います。わりとシンプルなんですよ。

以降ではソースコード一式を読み解くためのガイド的な説明を記載します。

MOTION JPEGのフレーム分割

主として参照すべきファイルは以下2ファイルです。

GetLiveViewTask.javaが、ライブプリビューに関するWeb APIの通信を行っているタスク本体です。
自動顔ぼかしβ版では2つのタスクにわかれていた処理を1タスクにまとめています。

MJpegInputStream.javaが、MOTION JPEGのストリームをJPEGのフレームに分解するクラスです。
自動顔ぼかしβ版のままです。フォルダ構成が異なるのでpackage定義は書き換えてあります。

THETAプラグイン内Live-Preview処理の維持

主として参照すべきファイルは以下1ファイルです。
MjisTimeOutTask.java

本サンプルでは、自動顔ぼかしβ版と異なり、常時ライブプリビューのフレーム取得を行うようにしました。その際、Wi-Fiの接続状態が変化すると、撮影アプリがポートを閉じずにライブプリビュー出力を停止する現象がみられたので、それをリカバリーするための処理です。
レアなケースではありますが、本サンプル動作中にWi-Fi接続状態変化があると1秒程度フレーム取得が行われない期間がありますがご容赦ください。

Webサーバーの役割

本サンプルのWebサーバーには以下の3つの役割があります。

  • assetsフォルダに仕込んだファイルをブラウザからの要求に応じて返す。
  • RICOH THETA API v2.1 のコマンドを仲介する。
    (ただしcamera.getLivePreviewは無効コマンドとして扱います)
  • Web UIなどにライブプリビューを表示するため、拡張したコマンドの処理を行う。
    拡張したコマンドの仕様はREADME.mdを参照してください。

主として参照すべきファイルは以下1ファイルです。
WebServer.java

ブラウザへの表示(HTMLとJavaScript)

theta-plugin-extendedpreview\app\src\main\assets配下の以下ファイルがWeb UIを構成するファイルです。
露出補正、TakePictureが行えますが、全てJavaScriptからRICOH THETA API v2.1を実行することで実現しています。このような方法でTHETAを操る方法もありますのでお見知りおきを。

theta-plugin-extendedpreview\app\src\main\assets
├img\theta_logo.jpg   // ブラウザのアイコン用画像で省略可能です。
├js\preview.js        // JavaScriptファイルです。
└index.html           // HTMLファイルです。

自動顔ぼかしβ版のWeb UIでも使っているJavaScriptの3DライブラリThree.jsを使いこなすとTHETA基本アプリのようなミラーボール表示なども行えます。

Web UIがTHETAプラグインのサーバーからフレームを取得する頻度はJavaScriptで調節します。公開したファイルはREADME.mdにあるように30ms間隔になっており高頻度ですので、作成するプラグインにあわせて調節してください。Web UIがTHETAプラグインに与える負荷を減らせます。

フレーム分割後データに独自の画像処理を行うときの推奨箇所

主として参照すべきファイルは以下1ファイルです。
MainActivity.java

以下の「**** OLED display sample code ****」と記事への転記を省略した部分を、独自の処理に置き換えることを推奨します。
このスレッドに所望のコードを書くことで、記述されたコードの処理時間がフレーム取得間隔より長くなっても、撮影アプリからのフレーム読み取りが滞ることがありません。

MainActivity.java
    //==============================================================
    // OLED Thread
    //==============================================================
    public void drawOledThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int outFps=0;
                long startTime = System.currentTimeMillis();
                Bitmap beforeBmp = null;

                while (mFinished == false) {

                    byte[] jpegFrame = latestLvFrame;
                    if ( jpegFrame != null ) {


                       **** OLED display sample code ****


                        outFps++;
                    } else {
                        try {
                            Thread.sleep(33);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    long curTime = System.currentTimeMillis();
                    long diffTime = curTime - startTime;
                    if (diffTime >= 1000 ) {
                        Log.d(TAG, "[OLED]" + String.valueOf(outFps) + "[fps]" );
                        startTime = curTime;
                        outFps =0;
                    }

                }
            }
        }).start();
    }

ちなみに、転記を省略したサンプルコードは、以下3つの処理のいずれかが動作するようになっており、どの処理を動かすかは WLANボタンで切り替えることができます。

  • OLEDに2値化したプリビューフレームを約30fpsで表示する
  • OLEDに2値化したエッジ抽出に近しいプリビューフレームを約8fpsで表示する
  • OLEDに2値化したコマ間差分で変化があった画素を約8fpsで表示する

01_OLED_sample.gif

どの処理もjavaで記述したので処理が遅いです。画像処理で速度を求める場合には、NDK(C++)で記述し、場合によってはOpenMPによる並列処理の指示なども記載するとよいです。

独自の画像処理結果をWeb UIに表示する

MainActivity.javaの以下コールバックルーチンを変更することで、Web UIに画像処理後の映像を表示することができます。

MainActivity.java
        @Override
        public byte[] getLatestFrame() {
            return latestLvFrame;
        }

応用事例

今回公開した範疇でも、すでにこんな応用ができますという事例を紹介します。

CLモードで複数ブラウザにライブプリビューを同時表示

THETAをCLモードでローカルネットワークに接続している場合、同一ネットワーク上の複数のデバイスにライブプレビューを表示することもできます。ある一つの機器から操作を行うと他の機器にも結果が反映されます。

03_WebUI_CL_sample.gif

この事例では、「ご家庭のとある場所を、複数の人がランダムなタイミングで、スマホなどを用いて簡単に様子を伺える」のような見守り系システムを簡単に構築できるのではないでしょうか? (THETAは給電しながらの連続動作もしますし)
「ご家庭」を「工場」、「お店」、「会社」などに置き換えて考えてもよさそうです。
映像を見るだけでなく、「○○認識などと組み合わせて過去1時間の△△履歴」みたいなものをTHETA内に保持し、映像とあわせてみれてもよさそうです。
もちろん、クラウド連携なども組み合わせることもできます。

M5 Stackにライブプリビューを表示

このプラグインによって、THETAのライブプリビューを表示できる機器の幅が広がりました。
M5 Stackのような、マイコンに分類される廉価な機器でも、実用的なフレームレートで表示できます。一見普通そうですが、M5 Stackの通信速度、CPU処理能力、RAM容量の少なさからすると結構ヤルじゃんということなのです。

00_M5StackPreview.gif

詳しくはGitHubのREADME_jp.mdを参照してください。(英語版README.mdへのリンクもあります。)
本記事とは別の記事1本分相当のがっつり解説を書かせて頂きました。

M5 Stackの日本正規代理店は SWITCH SCIENCEさんです。色々な情報へのリンクがあります。
M5 Stack初心者がお試しする場合はこちらの環境セットアップからはじめると良さそうです。

この例が示すところは、「サードパーティ機器メーカーさんが、THETAプラグイン(ソフトウェア)とセットのオプション(ハードウェア)をお安く作ることができる」という事です。
既存のTHETA用でない機器もTHETA用にできたりするわけです。腕に覚えのあるかたはご自身でどんどん拡張できます。

GitHubに書ききれなかったM5ファン向けな色々はコチラ!(クリックして開いてください)

着手の経緯

THETAプラグインに関するこのQiita活動が始動する直前、2018年5月14日に行われた「M5Stackユーザーミーティング vol.1(記念すべき第1回!)」のLTに登壇し、「M5 StackにTHETAのライブビューを表示しようとして失敗した話」をしました。その状態が以下です。

201905_SC_PREVIEW.gif

「壊れる」「遅い」「止まる(THETAに止められる)」の三重苦です。
THETAプラグインを介在させれば解決できるなと思いつつ、関連するTHETAプラグインに着手できていなかったので、およそ1年8ヶ月ほど放置しておりました。今回チャンスがきてリベンジ成功です!

「壊れる」については、「ESP32の通信ライブラリがHTTP 1.1のchunked通信に完全には対応していない」ということを私が知らなかったためです。chunk先頭にあるデータサイズバイトが画像の中にちりばめられていたので、取り除くと普通に表示できました。
あとのことはTHETAプラグインとの合わせ技で解決しました。本件のREADME.md必見です!

その他 M5 Stack/ESP関連 お役立ち?情報

・ライブプリビューのことは関係なしに、ESPシリーズから最新のTHETAと接続する事例としても役立つと思われます。RICOH THETA API 2.0(THETA SやSCの旧ファームウエア)では事例が結構あったようですが、APIバージョンが変わって繋がらずに諦められた感がちょっとあります。HTTPヘッダに厳しくなったのと、ESPシリーズとの微妙なタイミング問題があるのでリトライの仕方を工夫するくらいがコツでしょうか。あとは新しいAPIのほうが楽です。

・前述の「M5Stackユーザーミーティング vol.1」のLTで私自身も希望していたバッテリー状態表示について、先人の方々が開拓してくださっていたので反映しました。値取得と表示は分離してあります。表示はスペースをとらずチラつきもなしです。流用歓迎ですので是非お使いください。

充電中、満充電判断は以下の公式情報を元に実装しています。
https://github.com/m5stack/M5-Schematic/blob/master/Core/IIC_IP5306_REG_V1.4.pdf

残電池容量については先人の以下情報を元に実装しています。

ぼやき

M5 StickC版も作りたいとは思っています。LCDが160x80pixで1フレームのデータサイズをM5Stack版より小さくできる点は、フレームレート高速化が期待できます。ライブラリが充実しておらず公式ライブラリではJPEG表示ができない点、充電管理ICがM5 Stackとは異なる点あたりはハマる所かもしれません。もうすこし時期を待つか・・・。

・気が向いたら(皆さんからの反応がよければ?)、ソースコード可読性重視で機能制限したサンプルではなく、リモコンとしてフル実装をしたいです。プラグイン側のストア公開も考えます。ですので、なんらか反応があるとうれしいです。「一般人向けのM5 Stack側バイナリ配布をどうするか問題」というのもあるのですが、THETAからM5Stackのファームを書き込むという荒業(USBシリアルは開発者登録済みの人向けか?Wi-Fi経由は最初のFWが…)もあるにはあるので。。。どう料理しようか色々悩み中です。

まとめ

処理速度をそれほど求めないならば、今後いろいろな派生ができて、扱いが容易なプリビューフレーム取得とWebUI表示を行うサンプルができあがりました。

というわけで、3月末くらいを目処に、この土台を使ってTHETA内部でTensorFlow LiteのObject Detectionを動かす記事を書きます!

05_BANANA_Tracking.gif

上に貼り付けたTMMF2020向けデモンストレーションでは横トラッキングだけですが、なんとか"全方位トラッキング"にならないか実験をはじめないと。どうなることやらですが、乞うご期待!

RICOH THETAプラグインパートナープログラムについて

THETAプラグインをご存じない方はこちらをご覧ください。
パートナープログラムへの登録方法はこちらにもまとめてあります。
QiitaのRICOH THETAプラグイン開発者コミュニティ TOPページ「About」に便利な記事リンク集もあります。
興味を持たれた方はTwitterのフォローとTHETAプラグイン開発者コミュニティ(Slack)への参加もよろしくおねがいします。

KA-2
普段は星空のある風景写真(なにかの表紙なったりもするレベル)を撮っているようです。 電子工作なんかも少しします。 ファーム屋(RTOS/制御屋/システム屋)、商品企画、星景写真撮影者(ASPJ正会員)、 肩書き色々。。。
iotlt
IoT縛りの勉強会です。 毎月イベントを実施しているので是非遊びに来てください! 登壇者を中心にQiitaでも情報発信していきます。 https://iotlt.connpass.com
https://iotlt.connpass.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした