face.comの顔認識APIとim.kayac.comを使ってPCに人(の顔)が近付いたらiPod touchに知らせる

face.comの顔認識APIが公開された(1年で70億枚の顔写真をスキャンしたFace.comが顔認識APIを無料で一般公開 | TechCrunch Japan)。珍しいAPIなので、何に使えるか考えて、とりあえず、カメラに近付いた人の顔の認識に使ってみることにした。

ただ近付いたと分かるだけでもつまらないので、Arduinoの状態をim.kayac.comに送信してiPod touchで確認する - DiaryExceptionのネタを流用して、顔が近付いたらiPod touchに知らせる。

少し長いエントリーになったが、以下の4つの章に分けられる。

画像アップローダの設置

face.comの顔認識APIは画像のURLを引数に取るため、PC(ローカル)にある画像を解析対象とさせることは出来ない。そこで、予め画像アップローダを設置し、PCからURLで参照出来る位置に画像をアップロードして顔認識APIに読ませる。

アップローダとなるCGIスクリプト(Python)は以下のようにした。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import cgi
import os.path
import sys

print "Content-Type: text/html\n"

html = """

<html>
<head>
  <meta  http-equiv="content-type" content="text/html; charset=utf-8" />
  <title>File uploader</title>
</head>
<body>
<h1>File uploader</h1>
<form action="%s" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
  <input type="submit" />
</form>
</body>
</html>"""

form = cgi.FieldStorage()
if "file" in form:
    item = form["file"]
    if item.file:
        out = open(os.path.join('uploader_images', item.filename), "wb")
        out.write(item.file.read())
        out.close()
print html % sys.argv[0]

このスクリプトを、face.comが参照出来るWebサーバに設置する。スクリプトと同じ場所に、uploader_imagesという名前のディレクトリを作り、CGIディレクトリ内にファイルを書き込めるようにパーミッションを変更しておく。

face.comとim.kayac.comのアカウント登録(APIキー取得)

Logo.com (@heylogoapp) • Instagram photos and videosim.kayac.comでアカウント登録をすることが出来る。

face.comはアプリケーション登録を適当にしておけば、API keyとAPI Secretを取得出来る。

iPod touchにはim.kayac.comのアプリを入れておくこと。

PCに接続したカメラからカメラ画像を読み取り、画像をアップローダに転送、face.comで顔認識結果を取得し、もし顔があればim.kaya.comでiPod touchに知らせる

Webカメラの画像を取得するために、バーコードリーダ/ライタと同じくProcessingのライブラリを用いた。WebカメラにはiSightを用いる。

スクリプトJythonで書き、外部ライブラリとしてProcessingの他にJSONIC - simple json encoder/decoder for javaを使っている。

画像アップローダに画像を転送するために、横着をしてcurlコマンドを使っている。Macならデフォルトで入っているので気にしない。

#!/opt/usr/bin/jython
# -*- coding: utf-8 -*-

from processing.core import PApplet
from processing.video import Capture
from net.arnx.jsonic import JSON
from javax.swing import JFrame, JPanel, BoxLayout
from java.util import Timer, TimerTask
from java.io import File
from javax.imageio import ImageIO
import os
import time
import urllib
import hashlib

CHECK_INTERVAL = 30  # (sec)
# カメラ画像をチェックする時間間隔 / 短すぎるとface.comのAPIアクセス回数制限に引っかかる

UPLOADER_URL = "http://MYCGISERVER/uploader.py"
# アップローダのURL
UPLOADER_IMAGEURL = "http://MYCGISERVER/uploader_images/%s"
# アップローダに転送された画像の位置 / %sに画像ファイル名が入る

FACE_URL = "http://api.face.com/faces/detect.json"
FACE_APIKEY = "YOUR_FACE_COM_API_KEY"
FACE_APISECRET = "YOUR_FACE_COM_API_SECRET"

IMKAYACCOM_USER = "YOUR_IM_KAYAC_COM_USER"
IMKAYACCOM_SECRETKEY = "YOUR_IM_KAYAC_COM_SECRET_KEY"

## im.kayac.com
# im.kayac.com -> iPod touch

def postImkayaccom(faces, imgurl):
    url = "http://im.kayac.com/api/post/" + IMKAYACCOM_USER
    msg = u""
    for face in faces:
        msg += u"「%s」" % face
    msg += u"がお前のPCに近付いているッ!"
    message = msg.encode("UTF-8")
    
    signature = hashlib.sha1(message+IMKAYACCOM_SECRETKEY).hexdigest()
    opt = urllib.urlencode({
        "message": message,
        "handler": imgurl,
        "sig": signature,
        })
    res = urllib.urlopen(url, opt)
    # print res

## Processing

WIN_W = 320
WIN_H = 240

class FacePanel(PApplet):
    def __init__(self):
        self.mC = None
    def setup(self):
        self.size(WIN_W, WIN_H)
        self.background(255)
        self.frameRate(12)
        self.mC = Capture(self, self.width, self.height, 12)

        class FaceCheck(TimerTask):
            def __init__(self, mc):
                super(FaceCheck, self).__init__()
                self.mC = mc
            def run(self):
                if self.mC.available():
                    IMAGE_FILENAME =  "out_" + time.strftime("%Y%m%d%H%M%S") + ".jpg"  # 画像ファイル名
                    out = File(IMAGE_FILENAME)
                    ImageIO.write(self.mC.getImage(), "jpg", out)
                    os.system("curl -F file=@" + IMAGE_FILENAME + " " + UPLOADER_URL)  # アップローダに画像を転送
                    print("Upload a shot")
                    params = urllib.urlencode({
                        "api_key": FACE_APIKEY,
                        "api_secret": FACE_APISECRET,
                        "urls": UPLOADER_IMAGEURL % IMAGE_FILENAME,
                    })
                    time.sleep(2)
                    request = urllib.urlopen(FACE_URL + "?%s" % params )
                    response = request.read()
                    json_data = JSON.decode(response)  # face.comからのレスポンスをJSONデータとして読み込み
                    faces = json_data["photos"][0]["tags"]
                    if len(faces) != 0:  # 認識される顔は0個以上
                        genders = []
                        for face in faces:
                            gender = face["attributes"]["gender"]["value"]  # 顔の性別を取得
                            genders.append(gender)
                        postImkayaccom(genders, UPLOADER_IMAGEURL % IMAGE_FILENAME)  # im.kayac.comで知らせる

        timer = Timer()
        timer.schedule(FaceCheck(self.mC), 0, CHECK_INTERVAL * 1000)

    def draw(self):
        if self.mC.available():
            self.mC.read()
            self.image(self.mC, 0, 0)

## main

mainframe = JFrame(title="FaceReader", resizable=0,
    defaultCloseOperation=JFrame.EXIT_ON_CLOSE)

frame = JPanel()
frame.setLayout(BoxLayout(frame, BoxLayout.Y_AXIS))

fpanel = FacePanel()
frame.add(fpanel)
fpanel.init()
while fpanel.defaultSize and not fpanel.finished:
    pass

mainframe.add(frame)
mainframe.pack()
mainframe.visible = 1

実行

実行するためには、外部ライブラリをCLASSPATHに追加しておく必要がある。

$ export CLASSPATH=/Applications/Processing.app/Contents/Resources/Java/libraries/video/library/video.jar:/Applications/Processing.app/Contents/Resources/Java/core.jar:./jsonic-1.2.0/jsonic-1.2.0.jar:$CLASSPATH
$ jython facetotouch.py -d32

動作チェックのために、自分の顔も使ったが、顔を晒すのは恥ずかしいので、本棚にあった眞鍋かをりのブログ本を使って動作チェックをした様子を公開する。

上記スクリプトでは、30秒毎にカメラ画像をチェックしている(クリティカルな場面では長過ぎる間隔だが)。しばらくすると、face.comから顔認識結果が返ってきて、眞鍋かをりの顔が認識されたので、iPod touchにそれを知らせるメッセージがプッシュされてくる。

im.kayac.comのアプリを開き、リストからメッセージをクリックすると、Safariで画像URLが開く。

face.comはWebアプリで顔認識APIを用いることを想定しているようだが、このようにデスクトップアプリでも使える可能性を示すことが出来た。