📹

ソラカメ + Azure Custom Vision + Azure Functions + Tensorflowで鍵の閉め忘れを検知する

2022/12/19に公開

本記事はSORACOM Advent Calendar 2022の19日目の記事です。

SORACOM Advent Calendar2022は、ソラカメ、特に2022年の秋にリリースされたソラカメAPIを使った記事が多く掲載されています。
多分に漏れず、私もソラカメAPIを使った記事を書こうと思います。

はじめに

私は昔からドアの鍵を閉めたかとても心配になる人で、家から出るときには何度もドアをガチャガチャし(お隣さんから苦情が来たことも)、ちょっと離れては再度戻ってきてを数回繰り返すこともしばしばです。
これまでもこれを解決するためにはIoT的にセンサーを付けて見張るのが良いのか、スマートロックを買うべきなのか、結局最後は目で見るのが一番安心だからカメラで監視するのがいいのか…などと長年あれこれ検討はしてはいたものの結局実行には移せていませんでした。

そんな中、2022年にソラカメがリリースされ、早速リリース直後に購入して玄関を見張る位置に設置してみました。ソラカメで撮影している動画を外出先からもアプリで簡単に確認でき、これだけでも十分に役に立っていましたが、せっかくソラカメAPIでスナップショット画像を取得できるようになったから、これを機械学習にかけて検知と通知ができるのでは?と考えました。

で、それをどういうアーキテクチャで作るかなーと考えていたところ、ソラコムのmickが全く同じネタで記事を書こうとしているという話をもくもく会で聞きました。完全にネタ被った…とは思ったものの、mickはAWSで実装しているとのことなので、同じことをAzureでやったらどうなるか?というのであればいいかなと思ってそのまま進めることにしました。

ちなみにmickの記事は以下となります。ぜひ本ブログと比較してみてください。

ソラカメ + Lobe + AWS Lambda + Tensorflow Lite で鍵の閉め忘れを検知する

アーキテクチャ

今回のアーキテクチャは以下になります。

アーキテクチャ

  1. 「open」「close」の2つに分類する推論モデルをAzure Custom Visionで作成する
  2. Azure Functionsで定期的にソラカメAPIを呼んでその時の画像を取得(C#で実装)
  3. 取得した画像をAzure Blob Storageに保存
  4. 別のFunctionアプリをAzure FunctionsのAzure Blob Storageトリガーで起動し、Tensorflowで推論して画像の鍵が開いてるかどうかをチェックする(pythonで実装)
  5. チェックした結果、「open」だったらLINE Notifyで通知する

モデルの作成

Custom Visonのページからサインインし、プロジェクトを作成します。

鍵の開閉状態によって画像を分類するので、Project TypesはClassification、Classification TypesはMulticlass(Single tag per image)とします。
推論プログラムはpythonで実装しますので、ドメインはGeneral(compact)、Export CapabilitiesはBasic platformsとします。

プロジェクト作成

Resourcesの「new」から学習に使うリソースを作成しますが、今回はPricing TierはF0にして無料で学習させます。

準備できたら画像をアップロードして、タグをつけていきます。以下のキャプチャのように、複数の画像に同一のタグをつけてアップロードも可能です。

画像のアップロード

最初は、玄関全体の監視をしているソラカメから取得した画像をそのまま使って学習させてみました。しかし、判定に使う鍵部分が画像に占める割合があまりにも小さいため、学習に失敗ないしは精度が上がらないという状態が続きました。
そこで、判定に使う部分だけを切り出すことにしました。切り出しはImageMagickconvertコマンドを使って以下のように行いました。128x128の画像を(650,378)の位置から取り出す感じですね。

for i in *.jpg; do
	convert $i -crop 128x128+650+378 crop/$i
done

これでopen/closeの2つのタグについて20枚ずつ画像を準備して3時間ほど学習させたらまずまずの精度になりました。
学習できたら「Performance」→該当のIteration→「Export」とクリックして「TensorFlow」形式でダウンロードします。

モデルのエクスポート

ダウンロードしたzipファイルを展開し、含まれているlabels.txtmodels.pbを取得します。

TimerTriggerファンクションの作成

次は定期的にSORACOM APIにアクセスして画像を取得するプログラムを作成します。
AWS LambdaであればSORACOM cliが使えるLambda Layerが使えるのですが、残念ながらAzureでは使えません。
powershellで書いて都度SORACOM cliをダウンロードして実行する/SORACOM cliが使えるコンテナをFunctionsで使うといった方法も考えましたが、せっかくなので自前でAPIを呼ぶように実装することにしました。

当初、APIを呼ぶ部分をC#向けSDKっぽくなるように独自に設計して実装を進めていたのですが、今後APIが増えたときのことを考えるとOpenAPIの定義から自動生成する方が楽だということに気づいて、OpenAPI Generatorを使うことにしました。
SORACOM APIのOpenAPI定義は https://users.soracom.io/swagger/soracom-api.ja.yaml にあるので、以下のようにしてC#用のクライアントを出力します。

% curl -OL https://users.soracom.io/swagger/soracom-api.ja.yaml
% docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/soracom-api.ja.yaml -g csharp-netcore -o /local/out/

これでクライアントプログラムがカレントディレクトリのoutディレクトリ以下に出力されるので、ファンクション用のプロジェクトファイルにout/src/Org.OpenAPITools/Org.OpenAPITools.csprojをリンクします。

ソースコードは以下に置いていますので参考にしてください。

https://github.com/kenichiro-kimura/soracam-image-crawler-function

なお、このクライアント、今回利用しているソラカメ用のAPIは問題なく動いたのですが、SIM関連のAPIではJSONのデシリアライズ部分でエラーが出てうまく動きませんでした。認証周りもちょっと書き方が冗長になってしまうので、そのうちまとめて修正しようかと思ってます。

BlobTriggerファンクションの作成

続いてブロブトリガーで動く、Tensorflowを用いる推論プログラムをpythonで作成します。

基本的にはこちらのチュートリアルのプログラムをそのまま持ってきて、Functionsの流儀に合わせて修正して、blobとのやりとりを足してあげれば動くのですが、Azure Functionsで動かすにはいくつか注意点があります。

  • 従量課金プランでは動かない
    従量課金プランではストレージもメモリも足りないようで、デプロイ時にエラーになりました。
    AppService PlanのB2で動いたことは確認しましたが、B2でもたまにデプロイエラーになるのでその時はB3などにしてみてください。なお、メモリが足りないのはデプロイ時だけで、動き始めたらB1でOKでした(ストレージが足りないのでF1には落とせません)。
  • pip freezeで作ったrequirements.txtを使わない
    Azure Functions での Python エラーのトラブルシューティングにも記載がありますが、pip freezeで作成したrequirements.txtは、「現在の環境」(pipコマンドを実行した開発環境)で使用しているモジュール名とバージョンが記載されます。そうすると、Azure Functionsの実行環境とのCPUアーキテクチャやOSの差異で動かなくなるケースがあります。
    動かなくなるのは依存関係で入るモジュールについてなので、requirements.txtには依存元(本来必要なもの)だけをモジュール名だけ記載し、デプロイ時にAzure Functionsと同じコンテナを使ってそこでpip installが動くようにすることで、適切なものが自動選択されるようにします。
  • デプロイにGitHub Actionsを使う
    上記のとおり、デプロイ時にAzure Functionsと同じコンテナを使わないと適切なモジュールが選択されません。そのためにはGitHub Actionsでデプロイするのが一番楽です。
    Functionsのポータルの「デプロイセンター」でGitHubからデプロイすることを選択すると、自動でGitHub Actionsのyamlファイルを作成して該当のリポジトリにコミットしてくれます。
    私のリポジトリには、私の環境用に自動作成されたyamlが含まれていますので、forkして使われる場合はご自身の設定で上書きしてください。
  • ローカルのライブラリに依存するモジュールを使わない
    opencv-pythonはローカルにインストールされたライブラリに依存するため、そのままではFunctions上でうまく動かないので除外しました(無理矢理動かす方法もあって、それで動くのも確認しましたがあまりスマートではないのでやめました)。
    元のコードでは画像のリサイズをしているだけなので、そこをpillowのresizeメソッドに書き換えましたが、問題なく動作しているようです。

この辺りに注意しつつ作成したソースコードは以下で公開しています。リポジトリにmodels.pbを含めたくなかったので、実行時にBlobから取得するようにしています。

https://github.com/kenichiro-kimura/blobtrigger-azure-function-tensorflow/

なお、ここまで作って何とか動くようになった時点で

  • 従量課金ではなくAppService PlanでFunctions使うなら、デプロイであれこれ苦労せずともコンテナでいいのでは?
  • コンテナならそもそもプログラム書かないでもCustom VisionからエクスポートできるコンテナイメージでREST APIを使えるからそれでいいのでは?
  • 間欠的にコンテナを動かすならAzure Container Instancesでいいのでは?

とか色んなことを思ったのですが、せっかく作ったのでひとまずこれで動かすことにします。

動かしてみた

では、早速動かしてみましょう。

LINE Notify

無事に届きました!

まとめ

ソラカメAPIで取得した画像を使って、Azureで鍵の閉め忘れの検知を実現してみました。
mickの記事のAWS版とも比較しつつ皆さんも試して頂ければと思います。

もうちょっと頑張ると従量課金プランでも動かせるかも知れませんので、引き続き色々試してみようと思います。
また、実際にしばらく動かしてみると判定の精度がもう一つでしたので、この辺りの改善を楽にできるような手立ても考えたいです。

皆さんのお役に立てば幸いです。

GitHubで編集を提案

Discussion