Raspberry PiとGoogle Cloud Vision APIでサンタの来訪検知システムを作ってみる

サンタ監視システム Google Cloud Platform
昨年の仕様
この記事はIoTLT Advent Calendar 2017 12/5の記事です。

1年は早いものでもう12月ですね。12月といえばクリスマスですが、みなさんはサンタクロースって知ってますか?僕は知ってます。

サンタクロース(英: Santa Claus[1])は、クリスマス・イヴ(クリスマスの前の夜)に良い子のもとへプレゼントを持って訪れるとされている人物。
サンタクロース – wikipedia

サンタクロースは良い子にプレゼントをくれるおじさんです。遠い昔、朝起きると枕もとにプレゼントを置いてくれていました。しかし、ある時期から悪い子認定されたようで、それっきりプレゼントをもらえなくなってしまいました。

あれから10年以上が経ち、僕は立派な社会人になりました。Webエンジニアとして、社会貢献も僅かながらも何らかの形でできていると思います。以前よりは良い子になった僕には、今年こそはサンタが来てくれるはずなのです。ということで、そんなサンタに出会う為、Webカメラで来訪が想定される場所を監視し、来訪したらSlackへ即座に通知を投げるシステムを作ります。

スポンサーリンク

サンタの来訪を検知する

サンタ監視システム
以下の一連の流れをNode.jsで制御して実現します。

  1. Raspberry Piに接続したWebカメラから写真撮影を行ない、画像を保存
  2. 保存した画像をGoogle Cloud Vision APIへPOSTし、サンタ判定を行う
  3. サンタが写っていた場合は撮影した画像と一緒にSlackへ送信

リアルタイムでの撮影→判定は難しいのとAPIの費用が半端ないので、運用としてはcronなどを使用して数秒に1度nodeのスクリプトを実行するイメージです。

Raspberry Pi

今回はRaspberry Pi3 Model Bを使用しました。Bluetoothやwifi、その他インタフェースが充実しているので買ってすぐ色々できたりします。今回はUSB接続でWebカメラを接続してサンタの写真を撮影します。

初期で入っているNode.jsはアンインストールし、v8.9.1で実装を行ないました。
Raspberry Pi3 Model B

Raspberry Pi3 Model B

Raspberry PiからWebカメラでサンタの写真を撮る

まず、窓(決め打ち)から入ってくるサンタクロースの写真を撮影しなければならないので、ラズパイでWebカメラを扱えるようにします。Webカメラについては手元にあったLogicool C270を使用します。コスパの良いカメラとして有名ですね。いったんUSB接続しておきます。

Raspberry PiとC270を接続

Raspberry PiとC270

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なのでいっぱいまで指定しました。
fswebcamで撮影

fswebcamで撮影

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
        }
      ]
    }
  ]
}

Cloud Vision API – Detecting Labels

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を直で叩いたのと変わらないやろ!って言われたらそれまでですが)

サンタを撮影

ガチサンタを撮影
画像元:https://www.atpress.ne.jp/news/53453

‘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番目は写真が切れていますが、サンタ判定ができています。

サンタのslack通知

サンタのslack通知

あとはこれを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で数秒単位の実行を想定していましたが、センサーなど使用すれば必要な時だけ撮影ができますね。別の機会にしっかり作り込みたいと思います。

Google Cloud PlatformRaspberry PiWebAPI
スポンサーリンク
この記事が気に入ったら
いいね!しよう
最新情報をお届けします。
この記事を書いた人

スパイスファクトリー株式会社 Webエンジニア。フロントエンドやWebサイトの高速化が得意です。インフラ・バックエンドも一通りやってます。
個人的なお仕事のご依頼や情報交換などはお問い合わせまたはTwitterにメンションをお願いします。

ShoheiTaiをフォローする
このエントリーが役に立ったらシェアをお願いします!
イケてないコード – Webエンジニアのブログ

コメント

タイトルとURLをコピーしました