要らなくなったiPhoneで玄関監視をした
実家では、人が入ってきたら分かるように、こういうドアアラームを使っているのだけど
電池の消耗が激しくて10日に1度ぐらいエネループを充電しなければならず、とても面倒だと父親が言っていた。 それなら、要らなくなった古いiPhoneをビデオカメラ代わりにして、それをMacに転送して、Macでリアルタイム画像解析してやればいいのでは? と思って、システムを作ってみました。
全体の流れ
そのスクリーンショットを解析し、玄関のドアが開いていると判断できれば、チャイム等を鳴らす。
です。 以下で詳しく説明していきます。 (詳しい説明が要らない場合には、最下部のソースコードを直接見てください。)
手順1
Reflectorは1アカウント1200円ぐらいするちょっと高めのアプリですが、パソコンに繋いだ(音質の良い)スピーカーでiPhoneに入っている音楽をAirPlayで流す使い方もできたりするので、しっかり使えば12000円ぐらいの価値はあると思います…笑
手順2
上の説明そのままです。
手順3
一番面倒だったのはこのiPhoneのウィンドウのスクリーンショットだけを撮る部分です。
普通に画面全体のスクリーンショットを撮ると
となりますが、これでは周りの画像が解析の邪魔になるので、今回欲しいのは、以下のiPhoneの部分だけのスクリーンショット。
usage: screencapture [-icMPmwsWxSCUtoa] [files] -c force screen capture to go to the clipboard -C capture the cursor as well as the screen. only in non-interactive modes -d display errors to the user graphically -i capture screen interactively, by selection or window control key - causes screen shot to go to clipboard space key - toggle between mouse selection and window selection modes escape key - cancels interactive screen shot -m only capture the main monitor, undefined if -i is set -M screen capture output will go to a new Mail message -o in window capture mode, do not capture the shadow of the window -P screen capture output will open in Preview -s only allow mouse selection mode -S in window capture mode, capture the screen not the window -t<format> image format to create, default is png (other options include pdf, jpg, tiff and other formats) -T<seconds> Take the picture after a delay of <seconds>, default is 5 -w only allow window selection mode -W start interaction in window selection mode -x do not play sounds -a do not include windows attached to selected windows -r do not add dpi meta data to image -l<windowid> capture this windowsid -R<x,y,w,h> capture screen rect files where to save the screen capture, 1 file per screen
とのことで、特定のウィンドウのスクリーンショットを撮るには
-l<windowid> capture this windowsid
のオプションを使えば良さそう。しかしwindowid
をどうやって調べればよいのか…
How do I find the windowid to pass to screencapture -l? を見ると、Apple純正のアプリでは
screencapture -l$(osascript -e 'tell app "Safari" to id of window 1') test.png
のようなコマンドで、特定のアプリのウィンドウのwindowidを渡せるようですが、ReflectorはApple純正のアプリじゃないし、無理…
GUIのQuartz Debugアプリを使えばwindowidが分かるようだけど、それではプログラムに組み込めないし…
ということで、原点に戻ってCocoaを使って取得します。RubyからCocoaを叩きたいので、
require 'osx/cocoa'
として、
all_windows = OSX::CGWindowListCopyWindowInfo((OSX::KCGWindowListOptionAll | OSX::KCGWindowListOptionOnScreenOnly | OSX::KCGWindowListExcludeDesktopElements), OSX::KCGNullWindowID);
とすれば、現在表示しているwindowの全ての情報が取得できました。 (rbenvでRuby管理してたら、RubyCocoaが上手く使えなかったので、その解決方法は YosemiteでRubyCocoaを使う に書きました。)
あとはこの中から欲しいwindowidを抜き出して、コマンドラインからスクリーンショットを撮るだけです。 (詳細は下部のソースコード)
手順4
得られたiPhoneのスクリーンショットから、どうやってドアが開いている、もしくは人が入ってきたのを判断するかという部分ですが 1. 顔認識をする 2. 動作検出をする 辺りが簡単にできるかなと思いました。
1に関しては過去の記事にも書いたようにOpen-CVを使えば簡単に顔認識ができます。 ので、実際に実装してみたのだけど、落ち込んだ気分の人が(頭を下げたまま)玄関から入ってくると顔が認識されないのと、家から出て行く場合には後頭部しか映らないので、そういった場合には検出ができませんでした。
ということで、動作検出の方に方針を切り替えます。
具体的には、1分毎に"元画像"を保存して、2秒ごとに"現在の画像"を撮って、"元画像"と"現在の画像"を比較して変化が大きければ、玄関が開いたもしくは人が写っていると判断するようにしました。
1分毎に"元画像"を変更しているのは、うちの玄関が
こういうドアで外の光が透過して、朝と夜とか、晴れと曇りで明るさが変わってしまうので、それに対応するためです。
2つの画像の比較はImageMagickのコマンドラインツールを使って
$ composite -compose difference original.png now.png diff.png $ identify -format "%[mean]" diff.png
とすると、最初のコマンドでdiff.pngに差分の画像が生成されて、次のコマンドでどれだけの差分があったかを数値で出力します。 (詳しいことは2枚の画像のdiff(差分)を超簡単に調べる方法を参照)
あとは、"元画像"を変更する時に、それがちょうどドアが開いている時でないことのチェックとか、 何かが起きてずっと動作検出状態になってしまっても、復帰するような工夫も一応しておきました。 (細かい部分はソースコード見てくれ〜)
ソースコード
ここまで4時間ちょっとで完成して、仕事後帰宅した父親にKyokoさんの声で
「ぴんぽおん、ぴんぽおん、ぴんぽおん、ぴんぽおん。(父親の名前)さん、おかえりなさい。私はげんかんしです。今日から玄関の監視を担当します、げんかんしです。よろしくおねがいします。」
と大音量で流したら、 「これは要らない」 と言われて、結局このシステムは採用されませんでした。 (画像解析にCPUを意外と使うから、電気代が結構掛かるかもと説明したのが採用されなかった主な理由ですが…)