Flickrで三ヶ月になった赤ちゃんの写真をAzureで顔認識して奥さんにLINEで通知してみた
やったこと
普段はスマホアプリエンジニアですが、ゴールデンウィークだったのでFlickr + Azure Face API + Integromat + Azure Functions + LINE NotifyでFlickrの写真の顔認識をしてLINE Notify APIで奥さんに共有するのを作ってみました。
なお、Flickrが有料会員なの以外は無料枠で構成。
全体の構成はこんな感じ。
IntegromatのScenarioはこんな感じ。
Azure FunctionからAzure Face APIを操作しているCodeはここに置いておきました。
それ*oogle Photoで元々出来んじゃね?
- 15年近くFlickrを使っていて有料課金もずっとしてた
- *oogleさん、肌色多めの子供の写真があると事前警告なしでいきなりアカウントごとBanしてくるらしい[1][2]
Flickrの運営がどう判断するのかは分からないけど、もうちょっと穏便そう?(危なそうな写真はSafety levelを設定していた方が良さそう)
https://www.flickr.com/help/forum/en-us/72157624170020268/#reply72157624179451086 - エンジニアだし自分でやりたい
Azure Functions使えるならばIntegromatは不要では?
- FlickrとLINEのOAuth2.0のログイン関連部分を作るのが面倒、Integromatならtokenのrefreshも勝手にしてくれる
- Integromatちょっと興味があった
Azure Face API
ググった限り、米国では近年Face Recognitionと人種問題の絡みで炎上してIBMが研究を停止したり[3][4]していて、Face Detection APIの提供をしているところはいくつかあるものの、Face Recognition APIを提供しているのは主だったところはMicrosoftしかない模様。
Face Detection -> 写真の中に顔があるかの判定
Face Recognition -> 写真に写っているのが誰かの判定
一分間に20回以下の呼び出しなら月30,000回まで無料らしい[5]。
MicrosoftのクイックスタートとPython SDKがわざわざ日本語で準備してくれてるし良く出来ていて、それ通りやればほとんど困らなかった。
クイックスタートに書いてある通りですが、
事前準備
-
person_group
を作る - その中に
person
を作ってperson_id
を適当にメモしておく(Nameからperson_id
を取るAPIもあるみたいだけど、面倒なので)
face_client.person_group.create(
person_group_id=PERSON_GROUP_ID, name=PERSON_GROUP_ID, recognition_model=RECOGNITION_MODEL)
kid = face_client.person_group_person.create(PERSON_GROUP_ID, "Kid")
print(f'Kid={kid}')
- 教師画像を用意して
person_id
に登録する(正面で一人で写っている写真ならば、特に顔の範囲をcropしなくても良いみたい)
face_client.person_group_person.add_face_from_url(
PERSON_GROUP_ID, person_id, url, detection_model=DETECTION_MODEL)
-
person_group
をtrain
して、学習が終わるのを待つ。6枚ほど教師画像を用意したけど、すぐ終わるらしく、状態を確認するコードを書いて確認する頃には終わってた。
face_client.person_group.train(PERSON_GROUP_ID)
status = face_client.person_group.get_training_status(PERSON_GROUP_ID)
print(f'status={status}')
認識の流れ
-
detect_with_url
で画像内の顔を探してきて
faces = face_client.face.detect_with_url(
url=url, detection_model=DETECTION_MODEL, recognition_model=RECOGNITION_MODEL)
if not faces:
return None
face_ids = list(map(lambda x: x.face_id, faces))
- 見つかった顔を
face_client.face.identify(face_ids, PERSON_GROUP_ID)
でperson_group
内の顔と一致するか判定
results = face_client.face.identify(face_ids, PERSON_GROUP_ID)
if not results:
print(f'No matching face identified')
return None
candidates = []
for result in results:
if len(result.candidates) > 0:
# Append top most candidate only
candidate = result.candidates[0]
print(f'candidate={candidate}')
candidates.append(candidate)
-
candidate
の結果が返ってくるのであれば、そのperson_id
が探したい人と一致しているか判定
result = any(candidate.person_id == KID_PERSON_ID for candidate in candidates)
という感じです。
唯一ちょっとはまったのは、認識精度を高めるために新しいversionのdetection_model
とrecognition_model
を指定した方が良いらしい[6]ものの、person_group.create
で指定していたrecognition_model
を
face_client.person_group.create(
person_group_id=PERSON_GROUP_ID, name=PERSON_GROUP_ID, recognition_model=RECOGNITION_MODEL)
face.detect_with_url
にも同じように指定しないといけないところを忘れて、defaultのrecogntion_01
が使われてエラーを吐いたのに悩んだぐらい。ちゃんと指定したら動いた。
detected_faces = face_client.face.detect_with_url(
url=url, detection_model=DETECTION_MODEL, recognition_model=RECOGNITION_MODEL)
全体的なコードはこちら。
肝心な認識精度ですが、わざと他の赤ちゃんの画像を入れてfalse positiveが出るかとかは試してないものの、抱っこされて顔の一部が隠れていてもピントがぼけててもバッチリ検出されてUse case的には何ら問題ない感じ。
Azure Functions
Azure Face APIを使うならAzure Functionsが良いのだろうという安直な発想でチョイス。初めてちゃんと触ったけど、公式Youtube見たら特に困らなかった。ちょっと気をつけたのはauthLevel=function
に設定して匿名アクセスをブロックしたぐらい[7]?Azure Functions Extensionがめっちゃ便利。
一回vscodeのAzure Accountの状態がおかしくなってExtension削除して再インストールしたけど"No subscriptions were found"ってなって何でじゃと思ったけど、ログアウトしてログインしたら直った。Extension再インストールしてもアカウント情報消えなくて、ログアウトのGUIも無くてCommand Pallete経由でないとログアウト出来ないのは驚いたけど。
Azure Functionsの呼び出し関連の処理は大した事やっていませんが、このあたりです。
https://github.com/francais-harry/face-api-azure-function/blob/master/face_identification/init.py
Integromat
IFTTT的だけど、多段で色々Moduleをつなげられたり、条件分岐とかを書けるサービス。FlickrやLINEなど連携できるサービスも多くて便利。個々のModuleを実行するのをOperationと言い、月額料金によって(ModuleをつなげたScenarioの実行回数ではなく)Operationや転送データ量の上限が変わる。
今回のScenarioの場合は、Scenario一回あたりLoopの回り方によっては20以上のOperationを消費するので、Pollingは数時間に一回程度が無料アカウント(Operation月1000回が上限[8])だと限界。一日一回でも良いかも。
Azure Face APIは無料枠だと一分間に20回まで[5:1]なので、Flickr watch photos triggerを一回に最大20 photosに制限しているのと、デジタル一眼で撮った高解像度の画像を送ったところFace APIが「大きすぎる」とエラーを吐いたので、Integromat側でFlickrのSuffixのSpecに従って、少し小さめの画像のURLになるようreplace
で文字置き換えをしてます(API reference)。あと、Azure Functionsの呼び出しにはx-functions-key
を指定して匿名アクセスをブロックしてます[7:1]。
LINE連携についてはIntegromatのメニューにいっぱい項目が出てきますが、"Notify API"の"Send a Notification"を選んでおけば簡単で良いかと思います。
頑張ればLINE公式アカウントを自分で作成してIntegromatと連携できるみたいですが、単純な通知であればLINE Notifyで十分かと。
あと、子供の画像を20枚Uploadすると20回連続でLINEの通知が来るのがうざかったのでText aggregatorを使ってURLを一つのTextにまとめてます。公式Youtubeが参考になりました。
まとめ
という訳で、無事にゴールデンウィーク中にFlickrに上げた写真から子供の写真を検知してLINEに通知を飛ばして奥さんと共有する仕組みを作れました。工夫したところとしては「赤ちゃんの世話もしないで休み中ずっとパソコンいじってる!」と思われないよう、
- 奥さんに最初に作りたいものを説明
- 毎日の進捗報告
- デモをして成果報告
と、Product Ownerとの情報共有を欠かさないようにしました。無事、「私の写真もそこに入れたい」という貴重なBacklog itemの追加を頂いたのと、Integromat側の対応が出来ていないのか動画がハンドリングできていないので、そのあたりを時間があれば何とかしたい。(「お腹空いた!」「おむつ気持ち悪い!」「眠い!」でランダムな割り込み処理をしてくるのを相手しながらは想像以上に大変でした・・・眠いなら寝れば良いのに・・・)
-
https://nlab.itmedia.co.jp/nl/articles/1306/25/news042.html ↩︎
-
https://www.theverge.com/2020/6/8/21284683/ibm-no-longer-general-purpose-facial-recognition-analysis-software ↩︎
-
https://sitn.hms.harvard.edu/flash/2020/racial-discrimination-in-face-recognition-technology/ ↩︎
-
https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/face-api/ ↩︎ ↩︎
-
https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/releasenotes#new-face-api-detection-model ↩︎
-
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=csharp#api-key-authorization ↩︎ ↩︎
Discussion