Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

PythonでGoogle Cloud Visionを使った顔検出

- はじめに -

前回の記事でGoogle Cloud VisionAPIキーを発行しました。

そのAPIキーを使って、Pythonを使った顔検出(Face Detector)をやります。

顔認識じゃないです。顔検出です。


- Google APIのFACE_DETECTIONになげるやつ -

以下Pythonコードです。
変数を変更します。
api_keyに発行したAPIキーを入れます。
とりあえずこのスクリプトでSample.jpgから顔検出が出来ると思います。

#! /usr/bin/python
# -*- coding: utf-8 -*-
'''Gooogle Cloud Vision APIに画像を投げるやつ.'''

import base64
import cv2
from requests import Request, Session
import json

# ここにAPIキーを入れる
api_key = ''
# このスクリプトを単体で実行する場合はここにファイルパスを指定
sample_img_path = 'sample.jpg'
# 検出する顔の数の最大数 (多いほどレスポンスが返ってくるのが遅い)
max_results = 8
# DISCOVERY_URLは現時点(2016/08)でこれしかないのでこのまま
DISCOVERY_URL = 'https://vision.googleapis.com/v1/images:annotate?key='


# cv画像と画像ファイルへのPathと検出最大数が引数
def facedetector_gcv(img, image_path, max_results):
    # 通信不良等を考慮してTry...expectしておく
    try:
        # base64convert用に読み込む
        image = open(image_path, 'rb').read()

        # 顔を検出するやつのResponse作成
        str_headers = {'Content-Type': 'application/json'}
        batch_request = {'requests': [{'image': {'content': base64.b64encode(image)}, 'features': [{'type': 'FACE_DETECTION', 'maxResults': max_results, }]}]}

        # セッション作ってリクエストSend
        obj_session = Session()
        obj_request = Request("POST", DISCOVERY_URL + api_key, data=json.dumps(batch_request), headers=str_headers)
        obj_prepped = obj_session.prepare_request(obj_request)
        obj_response = obj_session.send(obj_prepped, verify=True, timeout=180)

        # Responseからjsonを抽出
        response_json = json.loads(obj_response.text)
        # 返り値用
        s = ''
        # 'faceAnnotations'があれば顔あり
        if 'faceAnnotations' in response_json['responses'][0]:
            faces = response_json['responses'][0]['faceAnnotations']

            # 画像情報
            s += image_path + ' '
            # OpenCVで矩形を書き込み
            for face in faces:
                # 0と2が両端の番地
                x = face['fdBoundingPoly']['vertices'][0]['x']
                y = face['fdBoundingPoly']['vertices'][0]['y']
                x2 = face['fdBoundingPoly']['vertices'][2]['x']
                y2 = face['fdBoundingPoly']['vertices'][2]['y']
                cv2.rectangle(img, (x, y), (x2, y2), (0, 0, 255), thickness=10)
                # 矩形情報を保存
                s += (str(x) + ' ' + str(y) + ' ' + str(x2) + ' ' + str(y2) + ' ')

        # 矩形が書き込まれた画像とs = 'file_name x1 y1 x2 y2'
        # 顔が無ければsは空
        return img, s

    except:
        return img, ""


if __name__ == '__main__':
    # 画像読み込み
    img = cv2.imread(sample_img_path)

    # Goog;e API
    img, s = facedetector_gcv(img, sample_img_path, max_results)

    # 画像出力
    cv2.imwrite('output_' + sample_img_path, img)

    # 矩形情報出力
    f = open('./rect.txt', 'w')
    f.write(s)
    f.close()

PEP8ガン無視で横に長くてスマン。


- テスト -

フリー画像で顔があって可愛いと言えば河村友歌ちゃんかなと思い用意しました。

これを入力にします。
f:id:vaaaaaanquish:20160808145629j:plain


こうなります。
f:id:vaaaaaanquish:20160808145702j:plain


複数の顔画像が入った写真もテストしてみます。
こんな感じでmax_resultsで指定した数までは顔検出できます。
f:id:vaaaaaanquish:20160808151204j:plain


- レスポンス -

レスポンスで返ってくるのは以下の通りです。

{
"boundingPoly": 顔の矩形位置(4頂点のx,y座標)

"fdBoundingPoly": 耳なども含めた顔の矩形位置(4頂点のx,y座標)

"landmarks": 以下のパーツの位置、三次元座標軸(x,y,z)
[
    "UNKNOWN_LANDMARK" : 謎
    "LEFT_EYE" : 左目
    "RIGHT_EYE" : 右目
    "LEFT_OF_LEFT_EYEBROW" : 左眉の左端
    "RIGHT_OF_LEFT_EYEBROW" : 左眉の右端
    "LEFT_OF_RIGHT_EYEBROW" : 右眉の左端
    "RIGHT_OF_RIGHT_EYEBROW" : 右眉の右端
    "MIDPOINT_BETWEEN_EYES" : 両目の中心
    "NOSE_TIP" : 鼻の頂点
    "UPPER_LIP" : 上唇
    "LOWER_LIP" : 下唇
    "MOUTH_LEFT" : 口全体の左端
    "MOUTH_RIGHT" : 口全体の右端
    "MOUTH_CENTER" : 口中央
    "NOSE_BOTTOM_RIGHT" : 鼻の下右側
    "NOSE_BOTTOM_LEFT" : 鼻の下左側
    "NOSE_BOTTOM_CENTER" : 鼻の下中央
    "LEFT_EYE_TOP_BOUNDARY" : 左目中央上境界
    "LEFT_EYE_RIGHT_CORNER" : 左目右ライン
    "LEFT_EYE_BOTTOM_BOUNDARY" : 左目中央下境界
    "LEFT_EYE_LEFT_CORNER" : 左目左ライン
    "LEFT_EYE_PUPIL" : 左目瞳
    "RIGHT_EYE_TOP_BOUNDARY" : 右目中央上境界
    "RIGHT_EYE_RIGHT_CORNER" : 右目右ライン
    "RIGHT_EYE_BOTTOM_BOUNDARY" : 右目中央下境界
    "RIGHT_EYE_LEFT_CORNER" : 右目左ライン
    "RIGHT_EYE_PUPIL" : 右目瞳
    "LEFT_EYEBROW_UPPER_MIDPOINT" : 左眉中間点座標
    "RIGHT_EYEBROW_UPPER_MIDPOINT" : 右眉中間点座標
    "LEFT_EAR_TRAGION" : 右耳
    "RIGHT_EAR_TRAGION" : 左耳
    "FOREHEAD_GLABELLA" : おでこ
    "CHIN_GNATHION" : アゴ、下顎下縁正中点
    "CHIN_LEFT_GONION" : アゴの左側
    "CHIN_RIGHT_GONION" : アゴの右側
]

"rollAngle" : 画像の回転角度
"panAngle" : 顔の左右回転角度,
"tiltAngle" : 顔の左右回転角度,
"detectionConfidence" : 顔検出信頼度
"landmarkingConfidence" : landmarksの信頼度

以下感情推定や状態推定
UNKNOWN	判定不能。
VERY_UNLIKELY, UNLIKELY, POSSIBLE, LIKELY, VERY_LIKELYで返ってくる。

"joyLikelihood" : 楽しそう
"sorrowLikelihood" : 悲しそう
"angerLikelihood" : 怒ってそう
"surpriseLikelihood" : 驚いてそう
"underExposedLikelihood" : 肌を露出してそう
"blurredLikelihood" : ぼやけてそう
"headwearLikelihood" : 帽子とか付けてそう
}

返ってきすぎ。
感情推定や状態推定はちょっと精度悪くてあんまり信用できないです。
ただ、横顔や顔が途切れてても結構返ってくるのですごい精度です。
後Landmarkが返ってきたりするのも学習用データ作りが捗りますね。


- 精度に関する知見 -

OpenCVとdlibで顔検出をおこなった場合の知見を以前まとめました。

OpenCVは無駄な矩形を多く出力して、recallを上げてくるイメージです。
OpenCVでは、入力に顔画像の最小サイズを指定できますが、結局そのパラメータを調整すると顔がそのサイズ以下になった時に検出できないのでう~んという感じです。
ただ、追加の学習が非常に簡単に行えるので、精度向上を測るのはやりやすいですね。
Anime_Faceを代表に、色んな状況での顔検出に最適化する事ができます。

対してdlibは、デフォで顔検出に関してとても精度が高いです。
ただ、中身にHoGSVMを使っている事もあり、顔っぽい文字やロゴを検出してしまうのがネックです。
また、オブジェクト検出用の学習も出来るには出来るんですが、学習用の関数がまだまだ使いにくい事もネックですね。

対して、Google Cloud Visionは追加での学習こそ出来ませんが、ものすごい精度で検出してきます。
しかも無駄な矩形をほとんど返さないのが魅力的です。
デフォルトの検出器を適当にprecisionで言うなら OpenCVが0.3、dlibが0.6、Googleは0.98という感じです。
しかも状態情報もくれるのでサイコー。


- 料金体系 -

最後に料金体系はこんな感じです。

- ~1000枚 ~100万枚 ~500万枚 ~2000万枚
1000枚毎に Free $2.50 $1.50 $0.6
限度枚数投げると Free $2497.5 $8496.0 $16895.4

限度枚数投げるとというのは、例えば100万枚投げた場合、最初の1000枚はFreeの価格が適応されるので無料。その後の999000枚は$2.5/1000枚かかるので総額が

F + 999000 * 2.5 / 1000 = $2497.5

という感じです。その後も同様に計算して

F + $2497.5 + (3999000 * 1.50 / 1000) = $2497.5 + $5998.5 = $8496.0
F + $2497.5 + (3999000 * 1.50 / 1000) + (13999000 * 0.6 /1000)
                   = $8496.0 + $8399.4 = $16895.4

日本円にすると2000万枚投げたら大体180万円くらいですね。

ただ、今は初期登録で60日間使える$300を貰えるので、無料で$2.5/1000枚とFree分合わせて12万と1000枚投げられます。
12万枚かなりの精度で色んな情報が返ってくると思うと、課金まで行かなくても全然使えますね。

後、これ以上投げる場合の金額は相談してねって感じみたいです。

Googleの公式の金額のページはここ。相談先もここ。
https://cloud.google.com/vision/docs/pricing

他にもラベル検出(LABEL_DETECTION)、文字検出+OCR(TEXT_DETECTION)、 有害コンテンツ検出(SAFE_SEARCH_DETECTION)、各物体のランドマーク検出(LANDMARK_DETECTION)、ロゴマーク検出(LOGO_DETECTION)、色解析(IMAGE_PROPERTIES)が使えて、それぞれ大体同じ金額になると思います。
何故かラベル出すやつだけ高いです。企業にお金でも払ってるんでしょうか。


- おわりに -

人工知能だので画像認識系の機械学習技術が話題という事もあり、機械学習APIがポツポツ出てきてますが、精度や安定性はやっぱりGoogleには勝てないですね。
中身はDeep Learning、R-CNN辺りでしょうか。
1レスポンスで返ってくる辺り、感情推定なんかも同じネットワーク内でやってそうですね。
すごいぞGoogle。流石だGoogle
「とりあえず顔検出用の学習データが欲しい」という時に無料枠でとりあえず使えますね。
そのデータを使いながら、環境に対して最適化していけば良いと思います。
あと、他のAPIも使う記事を気が向けば書きます

一応フォルダ内の画像を全部Google APIに投げて「顔画像あり」「顔画像なし」に分けて矩形情報を保存するスクリプトを書きました。
上部の画像単体を投げるスクリプトも一緒にGitリポジトリに入れてますので以下参照。

github.com