1年は早いものでもう12月ですね。12月といえばクリスマスですが、みなさんはサンタクロースって知ってますか?僕は知ってます。
サンタクロース(英: Santa Claus[1])は、クリスマス・イヴ(クリスマスの前の夜)に良い子のもとへプレゼントを持って訪れるとされている人物。
サンタクロース – wikipedia
サンタクロースは良い子にプレゼントをくれるおじさんです。遠い昔、朝起きると枕もとにプレゼントを置いてくれていました。しかし、ある時期から悪い子認定されたようで、それっきりプレゼントをもらえなくなってしまいました。
あれから10年以上が経ち、僕は立派な社会人になりました。Webエンジニアとして、社会貢献も僅かながらも何らかの形でできていると思います。以前よりは良い子になった僕には、今年こそはサンタが来てくれるはずなのです。ということで、そんなサンタに出会う為、Webカメラで来訪が想定される場所を監視し、来訪したらSlackへ即座に通知を投げるシステムを作ります。
サンタの来訪を検知する
- Raspberry Piに接続したWebカメラから写真撮影を行ない、画像を保存
- 保存した画像をGoogle Cloud Vision APIへPOSTし、サンタ判定を行う
- サンタが写っていた場合は撮影した画像と一緒にSlackへ送信
リアルタイムでの撮影→判定は難しいのとAPIの費用が半端ないので、運用としてはcronなどを使用して数秒に1度nodeのスクリプトを実行するイメージです。
Raspberry Pi
今回はRaspberry Pi3 Model Bを使用しました。Bluetoothやwifi、その他インタフェースが充実しているので買ってすぐ色々できたりします。今回はUSB接続でWebカメラを接続してサンタの写真を撮影します。
Raspberry PiからWebカメラでサンタの写真を撮る
まず、窓(決め打ち)から入ってくるサンタクロースの写真を撮影しなければならないので、ラズパイでWebカメラを扱えるようにします。Webカメラについては手元にあったLogicool C270を使用します。コスパの良いカメラとして有名ですね。いったんUSB接続しておきます。
fswebcamでコマンドから写真撮影
fsphil/fswebcamを使うと簡単にWebカメラを扱えるようになります。コマンド一発で写真が撮れるスグレモノです。Raspberry Piへssh接続してインストール。
$ sudo apt-get -y install fswebcam
以下コマンドで撮影ができます。
$ fswebcam -D 2 -r 1280x720 capture.jpg
- -D 2
- コマンド実行から撮影までの時間を遅らせます。秒指定。色々調べてみましたが、 即時撮影より遅延撮影の方が画質が良かったりエラーにならなかったりするようです。(実際、遅延撮影でエラーはなくなりました。)
- -r 1280×720
- 解像度を指定します。今回使用しているC270は最大1280×720なのでいっぱいまで指定しました。
Webカメラを接続してこのコマンドを叩くだけで画像が撮れました!
Node.jsからfswebcamを叩く
これを実際にNode.jsで実装すると以下のようになります。単純にspawnでコマンド実行するだけです。
撮影に失敗した場合は後述のCloud Vision APIへリクエストさせたくないので、codeで判定して処理を行ないます。
const IMAGE_FILE_PATH = './captured.jpeg'
const webcam = spawn('fswebcam', ['-D', '2', '-r', '1280x720', IMAGE_FILE_PATH])
webcam.on('close', code => {
if(code === 0) {
// 撮影成功時の処理
}
})
Google Cloud Vision APIでサンタを判定する
今回は撮影された人がサンタだった場合だけSlackへ通知するようにします。これでサンタ以外の人が突然部屋に侵入しても通知されなくなるので、夜もゆっくり眠れますね!
前述のfswebcamで撮影した画像をGoogle Cloud Vision APIへPOSTして、サンタかどうかを判定します。
Google Cloud Vision APIの設定
Google Cloud Vision APIを使用するには、Google Cloud Platformへの登録が必要になります。最初は無料試用期間として12ヶ月有効な300ドル分のクレジットが配布されます。また、GCPの各サービスには無料枠もあるので、是非試してください。
設定の詳細については本記事で割愛しますが、必要となるのは以下の情報です。
- projectId
- プロジェクトを作成すると生成されます。
- サービスアカウントキーのファイル
- IAMやAPI Consoleの認証情報の欄から作成します。任意のアカウントを作成し、json形式の認証ファイルをダウンロードします。
Google Cloud Vision API – Detecting Labels
Google Cloud Vision APIには複数のfeature(機能)がありますが、今回はDetecting Labelsを使用します。画像に写っているオブジェクトに対してラベル付けをしてくれるものです。これにより画像から特定の物を判別することができます。
以下の公式サンプルでは犬の画像を判別しています。パラメータに含まれているmidはオブジェクトを一意に識別するIDです。Google Knowledge Graph Search APIで使われています。
{ "responses": [ { "labelAnnotations": [ { "mid": "/m/0bt9lr", "description": "dog", "score": 0.97346616 }, { "mid": "/m/09686", "description": "vertebrate", "score": 0.85700572 }, { "mid": "/m/01pm38", "description": "clumber spaniel", "score": 0.84881884 }, { "mid": "/m/04rky", "description": "mammal", "score": 0.847575 }, { "mid": "/m/02wbgd", "description": "english cocker spaniel", "score": 0.75829375 } ] } ] }
Node.jsからVisionAPIへリクエスト・サンタ判定
Node.jsへの実装はGoogle Cloud Client Library for Node.jsを使用しました。Node.jsから様々なAPIへリクエストを投げることができます。visionAPIへのリクエストもgoogle-cloudドキュメントに記載されています。
const GCLOUD_ACCOUNT_KEY_FILE = process.env.npm_package_config_gcloud_service_account_file
const GCLOUD_PROJECT_ID = process.env.npm_package_config_gcloud_project_id
const IMAGE_FILE_PATH = './captured.jpeg'
const DETECTION_ENTITY_ID = '/m/027g6wt' // サンタクロースのmid
const client= require('google-cloud')
const vision = client.vision({
projectId: GCLOUD_PROJECT_ID,
keyFilename: GCLOUD_ACCOUNT_KEY_FILE
})
vision.labelDetection({ source: { filename: IMAGE_FILE_PATH} })
.then((results) => {
const labels = results[0].labelAnnotations
labels.some(label => {
if(label.mid === DETECTION_ENTITY_ID) {
// サンタと判定された場合の処理
// 今回はSlackへの画像アップを行う
return true
}
})
})
サンタ判定成功時に行うSlack通知+画像アップロードはnode-slack-uploadを使ってみました。
const SLACK_API_TOKEN = process.env.npm_package_config_slack_api_token
const SLACK_CHANNEL = '@shouhei.tai'
const IMAGE_FILE_PATH = './captured.jpeg'
const fs = require('fs')
const Slack = require('node-slack-upload')
const slack = new Slack(SLACK_API_TOKEN)
slack.uploadFile({
file: fs.createReadStream(IMAGE_FILE_PATH),
filetype: 'auto',
title: 'サンタクロース',
initialComment: 'サンタクロースの来訪を検知しました',
channels: SLACK_CHANNEL
})
実際にやってみた
エンジニアたるもの、品質を担保するためにテストしなければなりません。
サンタになりきる
僕がテストデータになりましょう。事前に友人からサンタローブを借りてテストしました。
この撮影した写真をCloud Vision APIに投げると以下のレスポンスが。
[
{ mid: '/m/047vlmn',
locale: '',
description: 'outerwear',
score: 0.7418337464332581,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/0dnr7',
locale: '',
description: 'textile',
score: 0.7367467284202576,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/07c97b',
locale: '',
description: 'fur clothing',
score: 0.6937162280082703,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/0cnmr',
locale: '',
description: 'fur',
score: 0.6757779717445374,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/05r655',
locale: '',
description: 'girl',
score: 0.6461390852928162,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/0250x',
locale: '',
description: 'costume',
score: 0.596626341342926,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/01k5yg',
locale: '',
description: 'robe',
score: 0.5136777758598328,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] }
]
こっちは体張ってんのに偽サンタであることを完全に見抜かれてます。色的にはサンタのそれっぽくなっているのにcostumeという判定が出ているのはかなりの精度。Googleを欺くのは難しそうです。
本物っぽいサンタ
ガチのサンタコスが手に入れば話は別かもしれませんが、今回は用意する時間がなかったのでカメラ経由でサンタの画像を読み取ってみます。
(Cloud Vision APIを直で叩いたのと変わらないやろ!って言われたらそれまでですが)
‘santa claus’の文字列が返ってきています!
[ { mid: '/m/081pkj',
locale: '',
description: 'event',
score: 0.7535479068756104,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/027g6wt',
locale: '',
description: 'santa claus',
score: 0.7529886364936829,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/02h7lkt',
locale: '',
description: 'fictional character',
score: 0.6336227655410767,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] },
{ mid: '/m/01vq3',
locale: '',
description: 'christmas',
score: 0.5926483869552612,
confidence: 0,
topicality: 0,
boundingPoly: null,
locations: [],
properties: [] } ]
santa claus, christmas, event等様々なラベルが返ってきました! イベントとか雰囲気的なものも判定できるんですね。。Googleすごい。。
Slackへの通知もうまくいってました。2番目は写真が切れていますが、サンタ判定ができています。
あとはこれをcron登録してクリスマスに配置するだけですね!
まとめ
- Raspberry PiとWebカメラの連携はfswebcamで簡単にできる!
- Google Cloud Vision APIの精度は非常に高い。見えているオブジェクトから、場の雰囲気的なものまで正確に取得できている。12/24, 12/25の本番稼働にも耐えうるレベルの精度。
Google Cloud Vision APIの精度には正直驚きました。今回はサンタだけでしたが、様々なオブジェクトに対応できるのでCloud Vision APIとカメラの連携は色々おもしろいことができそうです!是非試してみてください!
今回使用したソースコードは以下に置いてあります。かなり雑です。
tai-sho/adv-iotlt-santa-detection
今回作ったものについて、実際の運用はcronで数秒単位の実行を想定していましたが、センサーなど使用すれば必要な時だけ撮影ができますね。別の機会にしっかり作り込みたいと思います。
コメント