Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

エッジAIカメラ「S+ Camera Basic」でソラコムサンタを検知する

SORACOMアドベントカレンダー 2020、アンカー担当の松下(ニックネーム: Max)です。
12/24 7:00pm JST 時点で、私を残すだけとなりました。そしてまだ、12/25は5時間ほど残っています。
image.png
極めて順調に毎日投稿される記事に多大なるプレッシャー感謝を感じつつ、日々生活しておりましたが、なんとかまとめ上げられそうで喜ばしい限りです!

今年もソラコムサンタがやってくるきた

IoTプラットフォームを提供しているSORACOMには、この時期になると活発になるメンバーがいます。それが ソラコムサンタ です。

ソラコムサンタは SNSやフォームにお寄せいただいた、SORACOMサービスの機能追加や改善リクエストを具現化してプレゼントしており、ソラコムメンバーの行動指針であるリーダーシップステートメントの中でも、特に "Customer Centric" 、 "Likability" そして "Deliver Results" を体現している存在です。

今年も「ソラコムサンタより愛をこめて 2020」というブログと共に、数多くのプレゼントをくれたソラコムサンタです。ありがとう!

ソラコムサンタを検知したい、カメラで。

私は2017年3月にソラコムメンバーとなりましたが、2015年の時点で既にいらっしゃったにも関わらず、ソラコムサンタ殿のご尊顔を拝見したことがありません。そもそも、彼なのか彼女なのか、そしてアカウント名も不明なのです。
※2016年って書いてたけど、2015年から在籍されていました!!

仮にも「サンタ」を名乗っているわけですから、おそらくはサンタクロースの眷属と推察できるわけですが、そうなると、もしかすると我が家にも訪れるかもしれない!?という期待が出てくるわけです。

昨今はテレワークで自宅に籠りっぱなしの私ではありますが、常に警戒するのは正直しんどい。そこで考えたのはカメラによる検知ですが、いつ現れるかわからない存在を相手に常に画像をクラウドにアップし続けるのも、テレカン用の帯域を確保しておきたい身としては気が引けました。

そこで、エッジコンピューティングですよ。

「ソラコムサンタを検知したら、その時お知らせしてくれれば、、、」
まさにエッジコンピューティングが役立つ時です。しかも画像分析は、今一番熱い!
これがアンカーである12/25のブログにふさわしい!と書き始めたわけです。ただし、いまは12/24 8:00pm JSTですが。

構成

前置きがだいぶ長くなりましたが、構成です。
Lobeでモデルを作りTensorFlow形式でexportした後、そのモデルを使ってS+ Camera Basic上で推論を行い、結果をデータ収集・蓄積サービス「SORACOM Harvest Files」で確認します。

Lobe について

私は機械学習とかそういった方面には全くスキルがありません。TensorFlowは聞いたことがありますが、TensorFlow.jsやTensorFlow Liteとの違いはわかりませんし、ハイパーほにゃららと聞けばハイパーオリンピックを連想するくらいのレベルです。

そんな私でも画像分類ができる神アプリが現れました。それがLobeです。
分類1つあたり5枚の画像を用意するだけで、それなりに動いてくれるという「ああ、ついに21世紀が来た」と感じる、そんなデスクトップアプリなのです。まあ、21世紀も20年経ちますけど。

Lobeの試用の様子はラベル当たり5枚の画像があれば始められる「Lobe」をご覧いただくとして、ここでのポイントはLobeで学習したモデルをexportできることにあります。
Exportできる形式は 2020年12月時点で6種類(うち1つはLocalでのHTTP REST API)あり、その中にTensorFlowも入っています。

S+ Camera Basic について

S+ Camera Basicは「カメラ + Linuxベースコンピュータ + セルラー通信」が一体化した、通信するエッジAIカメラです。通信にスマートフォンで使われるセルラー通信が採用されており、Wi-Fiを始めとしたネットワーク環境を用意することなく、データをクラウドとやり取りできます。

ただ単純にカメラとして動作させることも可能ですが、Linuxベースコンピュータですのでプログラムを実行することも可能です。また、このプログラムの書き換えもセルラー通信を経由して行えるので、常に最新のプログラムや機械学習のモデルを適用することができます。

S+ Camera BasicではPython 3が利用できます。また、HTTPクライアントのrequestsや画像処理のPillow、数値計算で定番のnumpy、そして機械学習ライブラリのTensorFlowも導入済みです。(すべてのライブラリはこちらをご覧ください)
TensorFlowによる推論が標準で可能という事は、Lobeでexportしたモデルを手間なくS+ Camera Basic上で動かすことができるのが、今回のポイントとなります。

今回は半分ネタでソラコムサンタ風を学習させていますが、この構成自体は汎用的に使えるのではないでしょうか。

ソラコムサンタって、どんな格好だろう?🤔

そういえばソラコムサンタってどんな格好なんでしょうね?眷属ですから、いわゆる「あの恰好」ではあるの思うのですが、色にはこだわってるんじゃないかな。青色に少し緑がかかった色 "チェレステ" (通称 #ソラコム色) かなあと。
ということで想像して衣装を用意してみました。今回はこれを基に学習させることにします。まあ、ご本人の画像が無いものですから。

image.png

Lobeでモデル作成

Lobeでのモデル作成は マイクロソフトが公開した機械学習モデルの訓練を容易にできる「Lobe」を試してみた。 に詳しく載っていますので、ごらんください。

「私(Max)」「ソラコムサンタ(Soracom Santa)」「何もなし(None)」の3つにしました。それぞれ60枚くらい撮っています。
image.png
image.png
image.png

S+ Camera Basicの開発 / 最小構成を試す

S+ Camera Basicでの開発を行ってみます。
といっても、実際はPython 3の開発を行うのと変わりありません。私は Windows 10 で WSL2 に Ubuntu 20.04 の環境を使用しました。

開発環境の準備

開発環境の構築手順はユーザードキュメントに記載されています。
この中で

Python の runtime 環境の構築完了後、SORACOM Mosaic に必要な Python のモジュールをインストールします。Python のモジュールのリストはこちらになります。こちらを pip を用いてインストールしてください。

とありますが、ここの具体的な作業としてはSORACOM Mosaic Python module listrequirements.txt として保存したうえで

$ pip install -r requirements.txt

とします。
もしエラーが発生したら、以下の対処方法があります。

Could not find a version that satisfies the requirement tflite-runtime...

requirements.txt から tflite-runtime==2.1.0.post1 を削除(もしくはコメントアウト)してから再度 pip install -r requirements.txt を実行しましょう。

`tensorflow 1.14.0 has requirement tensorboard<1.15.0,>=1.14.0, but you'll have tensorboard 2.0.2 which is incompatible.

必須の対処ではありませんが requirements.txttensorboard==2.0.2tensorboard==1.14.0 と変更してから再度 pip install -r requirements.txt を実行しましょう。

You are using pip version 19.0.3, however version 20.3.3 is available.

こちらも必須の対処ではありませんが、気持ちの問題でアップデートしたければ pip install --upgrade pip で解消できます。

FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated;

こちらもwarningですが気になるようでしたら numpy のバージョン調整を行えばOKです。

$ pip uninstall numpy==1.17.2
$ pip install numpy==1.16.6

その後のjqやSORACOM CLIのインストールは「あったら便利」程度であり、必須ではありません。

最終的なディレクトリ構成はこのようになりました。
mosaic_app_logging_test はこの後で解説します。

.
├── mosaic_app_logging_test
│   └── CameraApp0
│       ├── CameraApp0
│       ├── PreSetup
│       └── info.json
├── python
└── requirements.txt

アルゴリズム開発 / まずはログに出力

いきなり画像活用をしたいところですが、ここは落ち着いてまず「ログに出力」するところから始めましょう。今後の開発の基礎知識になるからです。

ログは標準出力/標準エラー出力に出力されたものになります。 SORACOM Mosaic コンソール(WebUI)の "Download logs" で得ることができます。

フォルダ構成、ファイル作成(CameraApp0, PreSetup, info.json)

フォルダ構成は先に紹介した通りです。アルゴリズムに関わる部分のみ再度抜き出しています。

.
├── mosaic_app_logging_test
    └── CameraApp0
        ├── CameraApp0
        ├── PreSetup
        └── info.json

mosaic_app_logging_test という名称は任意です。

S+ Camera Basicのアルゴリズム1開発にはユーザーサイトにも書かれているように、必須のファイルが存在しますが、必要なのは CameraApp0, PreSetup, info.json の3ファイルです。

これら3つのファイルを CameraApp0 というフォルダに置きます。この名称は固定となります。CameraApp0(フォルダ)以下の構成は自由にしていただいてOKです。後述するモデルもこの中に作っていく事になります。

※ CameraApp0 はファイルとフォルダの2つの名前で使われていますが単体で CameraApp0(フォルダ) と明示していなければファイルと読んでください。

CameraApp0

CameraApp0 がエントリーポイントです。
ユーザーサイトに書かれている CameraApp0.py はエントリーポイントから呼び出されているだけなので CameraApp0 だけで済んでしまう場合は不要です。ここからのコードは全部 CameraApp0 で完結するように書いてあります。

S+ Camera Basicのアルゴリズム無限ループ実装が基本となります。ここでは while True を使いましたが、threadingを使っても良いでしょう。

Uptimeを3秒毎に標準出力(= ログ)へ出力する例です。
注意点はバッファリングのFlushです。これを行わないと、いつまで経ってもログに出力されて来ません。2 (参考: https://qiita.com/mmsstt/items/469a9346ce545709f53c)

mosaic_app_logging_test/CameraApp0/CameraApp0
#!/opt/soracom/python/bin/python

import time

started_at = time.time()
while True:
    payload = {"uptime_s": int(time.time() -started_at)}
    print(payload, flush=True)
    time.sleep(3)

ローカルでの動作確認をしておきましょう。

PreSetup

PreSetup は CameraApp0 実行前の hook スクリプトです。
今回は何もしません。ログ(標準出力)に出力しておしまいです。

mosaic_app_logging_test/CameraApp0/PreSetup
#!/bin/bash
echo "Output from PreSetup"
exit 0

ここまででお気づきになったかもしれませんが、CameraApp0 や PreSetup は冒頭のshebangで指定されたもので動くため、ほかのランタイムで動かすことも可能ではあります。

info.json

このアルゴリズムに関するメタ情報を記載します。
SORACOM Mosaic コンソールで表示されるので、わかりやすい名称を書くのが良いでしょう。
image.png

デプロイ

3つのファイルが揃ったらデプロイです。
デプロイスクリプトが提供されていますが、何がされているのかを確認するためにもステップを紹介します。

$ cd mosaic_app_logging_test/
$ chmod 740 CameraApp0/{CameraApp0,PreSetup}
$ tar czvf mosaic_app_logging_test.tgz CameraApp0/

このtgzファイル(ファイル名は任意)をSORCOM Harvest Filesにアップロードし、そのアップロードパスをSORACOM Mosaicの "Install new algorithm..." で指定すると、S+ Camera Basic から SORACOM Harvest Files上の tgzファイルを取得し、適用されるといった流れになります。

tgz作成時の注意点は chmod です。CameraApp0 や PreSetup は実行フラグが付いている必要があります。特に WSL の方でPOSIXのフラグが扱えないファイルシステムの場合はご注意ください。

SORACOM Harvest Files の /Max/mosaic_app/mosaic_app_logging_test.tgz とアップロードしたら、SORACOM Mosaic では http://private.harvest-files.soracom.io/Max/mosaic_apps/mosaic_app_logging_test.tgz と指定する関係となっています。

SORACOM Harvest Filesの様子

image.png

SORACOM Mosaic コンソール側の指定

image.png

特に info.json でバージョン(PkgVersion)を更新しておくと、ちゃんと適用されたかの確認がわかりやすいのでお勧めです。

ログの確認

SORACOM Mosaic コンソールの "Download logs" で取得できます。
テキストファイルです。お好きなエディタでどうぞ。

よりもっとダイレクトに確認したい方は トラブルシューティング に記載されている手順で tail -f を使えるようにしてください。

これにて、アルゴリズム開発の流れとデバッグ環境は揃った事になります。

S+ Camera Basicの開発 / TensorFlow で推論

ここからはTensorFlowによる推論です。いきなりジャンプアップした気もしますが大丈夫です。
すでにLobeでモデルを作成しているため、

  • Lobeからのモデルのexport
  • コーディングとテストとデプロイ

と、まあ簡単(?!)ですね。

LobeからのモデルのexportとSORACOM Harvest Filesへの保存

Lobe から TensorFlow 形式で export すると Objects TensorFlow というフォルダが作られます。この中にモデルとサンプルコードまで(!)入っています。

このモデルが入ったフォルダをアルゴリズムの中に同梱したかったのですが、思いのほかサイズが大きく(私は100MBくらいあった)、アルゴリズムインストールでタイムアウトが起こってしまいます。
そのため SORACOM Harvest Files にZIPでアップロードしておき、PreSetupでダウンロード&展開することにしています。

※PreSetup毎にダウンロードを行うので、通信速度や量は特に気をつけてください。

コーディングとテストとデプロイ

Objects TensorFlow/example/tf_example.py は import 可能なように作られています。さすが。ぜひ、流用させていただきましょう。
ここからは Objects TensorFlowCameraApp0(フォルダ)内に存在する前提で書いています。

tf_example.py と等価の処理なら、以下のように書くことができます。

inference_example.py
#!/opt/soracom/python/bin/python

import os
import sys
from PIL import Image

sys.path.append('Objects TensorFlow/example')
import tf_example

target = sys.argv[1]
if not os.path.isfile(target):
    print(f"Couldn't find image file {target}")
    exit(255)

image = Image.open(target)
# convert to rgb image if this isn't one
if image.mode != "RGB":
    image = image.convert("RGB")
model = tf_example.Model()
model.load()
outputs = model.predict(image)
print(f"Predicted: {outputs}")
"""
e.g.)
$ python this_file.py image.jpg
Predicted: {'Prediction': 'Nothing', 'Confidences': [0.0, 0.0, 1.0]}

これを基に S+ Camera Basic のカメラから読み込んで判定させ続けるコード(CameraApp0)は以下の通りです。
※今回は mosaic_app_inference_by_tensorflow としています。

Soracom Santa か Max を検出したら、その時の画像を SORACOM Harvest Files にアップロードします。判定は Prediction の部分です。

mosaic_app_inference_by_tensorflow/CameraApp0/CameraApp0
#!/opt/soracom/python/bin/python

import os
import sys
from PIL import Image
import time
import io
import requests

print("START: mosaic_app_inference_by_tensorflow", flush=True)

sys.path.append('Objects TensorFlow/example')
import tf_example

requests.get("http://localhost:8080/v1/stopCameraCapture")
print("START: waiting for cameraState(10s)", flush=True)
time.sleep(10)
requests.put("http://localhost:8080/v1/cameraState", json={"width": 1280, "height": 720})
r = requests.get("http://localhost:8080/v1/startCameraCapture")
if r.status_code != 200:
    print(f"Failed /v1/startCameraCapture (HTTP {r.status_code}), exited.", flush=True)
    exit
print("START: waiting for camera(5s)", flush=True)
time.sleep(5)

print("Initialized. Go the loop.", flush=True)

while True:
    started_at = time.time()
    r = requests.get("http://localhost:8080/v1/cameraJpegImage")
    if r.status_code != 200:
        print(f"Failed /v1/cameraJpegImage (HTTP {r.status_code}), next.", flush=True)
        break
    image = Image.open(io.BytesIO(r.content))
    # convert to rgb image if this isn't one
    if image.mode != "RGB":
        image = image.convert("RGB")
    model = tf_example.Model()
    model.load()
    outputs = model.predict(image)
    print(f"Predicted: {outputs}", flush=True)
    process_time = time.time() - started_at
    print(f"Elapsed: {process_time} s", flush=True)

    if outputs["Prediction"] == "Soracom Santa" or outputs["Prediction"] == "Max":
        print("Dareka-Hakken !?", flush=True)
        virtIO = io.BytesIO()
        image.save(virtIO, format="JPEG")
        r = requests.post("http://harvest-files.soracom.io", data=virtIO.getvalue(), headers={"Content-Type":"image/jpeg"})
        print("Save to SORACOM Harvest Files", flush=True)
        if r.status_code != 200:
            print(f"Failed (HTTP {r.status_code}), next.", flush=True)

PreSetup は、SORACOM Harvest Filesからのダウンロードとunzip処理を加えました。(同じファイルだったらダウンロードしないとか、ダウンロード後のハッシュ検証とかは省きました、、ごめん)

mosaic_app_inference_by_tensorflow/CameraApp0/PreSetup
#!/bin/bash

echo $PWD
echo "Cleanup"
rm -rf "Objects TensorFlow"
rm -f Objects%20TensorFlow.zip
echo "Download and Extract"
curl -O http://private.harvest-files.soracom.io/Max/mosaic_apps/Objects%20TensorFlow.zip
unzip -x Objects%20TensorFlow.zip
echo "done."

あとはデプロイするだけです!

$ cd mosaic_app_inference_by_tensorflow/
$ chmod 740 CameraApp0/{CameraApp0,PreSetup}
$ tar czvf mosaic_app_inference_by_tensorflow.tgz CameraApp0/

さあ、いつでも来やがれ、ソラコムサンタ!

Predicted: {'Prediction': 'None', 'Confidences': [0.0, 0.0, 1.0, 0.0]}
Elapsed: 28.12995481491089 s
Predicted: {'Prediction': 'Max', 'Confidences': [0.0, 1.0, 0.0, 0.0]}
Elapsed: 28.623439073562622 s
Dareka-Hakken !?
Save to SORACOM Harvest Files
Predicted: {'Prediction': 'Soracom Santa', 'Confidences': [0.0, 1.1005343888624412e-28, 0.0, 1.0]}
Elapsed: 27.988136529922485 s
Dareka-Hakken !?
Save to SORACOM Harvest Files

おっと、誰か発見していますね!2件あります。
image.png
ダウンロードしてみると、たしかに私と、私の扮するソラコムサンタの姿が!
image.png
動いてくれて良かったです!

ご覧の通り、推論に30秒近くかかっています。S+ Camera BasicはCoral Edge TPUに対応しているので、こういったもので底上げは出来るかなと思います。(もちろんそれに合わせた実装をする必要もありますが)

まとめ (というか、オチ)

ソラコムサンタ風の私」は、無事検出できました。
Lobe上でテストした後にエッジへデプロイできるのは、手軽ですし検証性も上がって良かったと個人的に思っています。

まあ本物のソラコムサンタが現れたら検出できるかと言われれば「知らないものは推論できない」というのがオチですね (^^;;
機械学習がアツい昨今ではありますが、銀の弾丸ではない事を改めて認識しました。
もちろん、私はソラコムサンタじゃありませんからね!!!

モデルもクラウドも、利用して「迅速に回す」

さて、特にモデルは1回作って終わりではありません。
機械学習は正解が見えづらいからこそ、作って試すサイクルを高速に回す事が重要です。モデル更新のために現場に行くような運用では、高速に回すことは不可能でしょう。だからこそ通信を活用していく、特に設置の状況に左右されることの無い安定した環境は不可欠です。

クラウド側のシステムも閉じることなく、様々な可能性に対応できるべきです。
今回はSORACOM Harvest Filesに画像をアップするだけとなりましたが、S+ Camera Basic自体はSORACOMのサービスに閉じることなく、他のクラウドサービス連携が可能となっています。
たとえば reuqests を使ってSaaSの HTTP REST APIを呼び出すといった構成もできます。検出したらリモート雲台を操作してフォーカスを当てるといった、そういう事も組み合わせによって実現できるでしょう。

また、その時はデータ転送支援の「SORACOM Beam」やクラウドアダプタ「SORACOM Funnel」、FaaSを直接呼び出せる「SORACOM Funk」を経由すると、より簡単に外部サービス連携ができますが、その時に一番楽をして成果を得るようにすることが重要ではないでしょうか。

充実している「サンプルアルゴリズム」

ここまで書いておいて「ズコーッ」的な紹介ですが。
S+ Camera Basicには目的別のアルゴリズムが用意されており、ボタン一発でインストールが可能です。パフォーマンスもTensorFlowを使って自力でやるよりもよほど良さそうです。

特に12/24にリリースされたばかりの顔認識アルゴリズムは、画像の調整からラベル付けのUIまでそっろた仕組みとして、今回の解決にも使えたかもしれません。こっちを使えばよかったかな?

ご興味があれば、試してみてください。使用感のフィードバックもお待ちしております!

いかがだったでしょうか?

実はIoTLT アドベントカレンダー 2020 でもアンカー担当で、同時進行がかなり厳しかったのですが、「KeiganMotorによるリモート雲台」と同時にやってしまうというアイデアで、なんとかかき揚げ(!)ました。

こんな感じで KeiganMotor に S+ Camera Basic を取り付けて、画角調整もできます。詳しくは SORACOMとモーターモジュール "KeiganMotor" で作る「どこでもリモート雲台」 もご覧ください。
remote-undai.gif
最後はS+ Camera Basicの雲台として利用したKeiganMotorのご挨拶で〆たいと思います。これもさっきのブログの使いまわしですが「流用して、迅速に回す」!
よいお年を.gif
良いお年を! (^^/~

EoT


  1. いわゆるアプリケーションの事です。 

  2. いやー、これはハマった。でも社内のSlackで聞いてみたら速攻でわかった! 

ma2shita
(松下 享平) IoT通信プラットフォーム「ソラコム」のエバンジェリストで年間140回以上の講演を通じ #IoT の事例や技術情報を日々紹介。LPWAの選び方や共著で「公式ガイドブック SORACOM プラットフォーム」を書いています。2020年の #AWS IoT Heroです。"Max"はニックネームです、フォローやメッセージはお気軽に! Twitterは @ma2shita です。
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