顔認証のクラウドサービスMercury Cloudで遊んでみる – 顔検知 –

2021.11.01

Mercury Cloudという顔認証のサービスを触る機会があったのでご紹介します。

顔認証のユースケースとしては、 オンラインサービスの本人確認、チケットレス入場(スタジアムやイベント会場など)、鍵解錠などが挙げられます。

Mercury Cloudは、既存のサービスでも簡単に顔認証を追加できるようなAPIを提供しているサービスです。 APIを実行するロジックは自身で追加しないといけません。

特徴として

  • 高速で顔を認識
  • 高精度な認証(マスク着用時も高い認証を誇る)
  • 2000万人の顔データ登録が可能
  • 顔の特徴から年齢や性別などの属性の識別が可能

といったものが挙げられています。

用意されているAPIですが、大きく分けると

  • 顔検知
    • 画像内の顔を検出し顔枠に外接する長方形の座標を返します
  • 顔比較
    • 2つの画像の中にあるそれぞれ最大の顔を検出し、これら2つの顔が同じ人物であるかどうかを確認
  • 顔検索
    • 特徴データベースに登録されているすべての顔特徴の中からアップロードした画像から検出された顔を検索し、最も近い結果を返します

があります。

本記事では顔検知を試していきたいと思います。

使ってみる

base uri: https://<<domain.com>>/openapi/face/v1

domain.com っは、サービス地域によって変わってきます。サービス開始時の連絡メールにこのドメイン部分は記載されています。

認証について

全てのAPIでクライアントの検証のためAPI認証トークンが必須となっています。

各APIを呼び出す際に、2つのヘッダー(x-dateとAuthorization)が必要です。それらが含まれていない場合、401のHTTPエラーコードが表示されます。

x-date

RFC-7231形式でのUTC日付と時刻の形式を使用します

Fri, 09 Jul 2021 01:51:02 GMT

Authorization

指定されたAPIパス、HTTPメソッド、アプリID、アクセスキー、およびシークレットキーに基づいて生成されます。特徴DBに関連する一部のAPIではDB IDも必要で、 以下のフォーマットに従う必要があります。

hmac username="{Access_key}", algorithm="hmac-sha256", headers="x-date request-line", signature="{Signature}"

Authorizationヘッダーの作成 に作成手順が詳しく記載されています。

アプリID、アクセスキー、およびシークレットキーはサービス開始時の連絡時に記載されているものを使用します。

これらの情報は安全な箇所に保管し、他人に開示してはいけない情報となっています。

ヘッダー作成ツールサンプル がドキュメントに用意されていますので、これを利用して作成することができます。

サンプル実行例)

画像について

Mercury Cloud APIはHTTPリクエストで、base64でエンコードされたバイナリを使用して画像データを送信します。

例)

def base64_encode_file(file_path):
    handle = open(file_path, "rb")
    raw_bytes = handle.read()
    handle.close()
    return base64.b64encode(raw_bytes).decode("utf-8")
  • 画像のフォーマットは、JPG、PNG、BMP、TIFF、またはGIF
  • 画像のファイルサイズは、8MB未満
  • 有効な顔サイズは、32✕32ピクセル以上
  • バッチアップロードがサポートされている顔検知APIと顔特徴追加APIでは、1回のAPI呼び出しでの画像の数量は16以下

以上の基準を守る必要があります。

基本的に画像の画質が高いほど精度が高くなりますが、ファイルサイズが大きいほどAPIの呼び出し時間が長くなります。

APIを呼び出す前に、高品質・正面・鮮明な画像を、有効な顔サイズが200x200ピクセル以上を確保しながら、画像をトリミングして200KB未満に圧縮すること

が推奨されていました。

顔検知API

URL

https://{domain}/openapi/face/v1/{app_id}/detect

Parameters

  • x-date (必須)
    • API認証時使用されるRFC-7231フォーマットのUTC日時文字列のヘッダーパラメータ
    • 例: Wed, 03 Mar 2021 02:11:37 GMT
  • app_id (必須)
    • テナントの下のアプリケーションIDを指定するURLパラメータ

Request body

指定された画像のバイナリデータ(base64でエンコード)

{
  "images": [
    {
      "data": "/9j/4AAQSk...ZJRgABAQEA"
    }
  ]
}

実行例

認証ヘッダーの作成、画像のbase64でエンコードを行った後、顔検知APIのURLにパラメータをつけて実行します。

アクセス例)

curl -X 'POST' 
  'https://<<domain>>/openapi/face/v1/<<app_id>>/detect' 
  -H 'accept: application/json' 
  -H 'x-date: <<RFC-7231フォーマットのUTC日時>>' 
  -H 'Authorization: hmac username="<<api_key>>", algorithm="hmac-sha256", headers="x-date request-line", signature="<<signature>>"' 
  -H 'Content-Type: application/json' 
  -d '{
  "images": [
    {
      "data": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/~~~~~~~~~~sABFEdIMSMMQhNS/p19B6v0H+C/ouXGX6HrA0R"
    }
  ]
}'
画像1

  • レスポンス
{
  "trace_id": "f035eca5057ff86f81407e6fa494abed",
  "results": [
    {
      "code": 80303,
      "message": "No face found.",
      "internal_code": 303
    }
  ],
  "batches": [
    {
      "faces": []
    }
  ]
}

顔が検知できなかったといういうレスポンスが返ってきた例です

画像2

  • レスポンス
{
  "trace_id": "af32b826835ff2d152609c03e5e77920",
  "results": [
    {
      "code": 80303,
      "message": "No face found.",
      "internal_code": 303
    }
  ],
  "batches": [
    {
      "faces": []
    }
  ]
}

イラストだと顔が検知できないようです

画像3

  • レスポンス
{
  "trace_id": "a29ceb0182508c5c012fb209c62c1b57",
  "results": [
    {
      "code": 0,
      "message": "Success.",
      "internal_code": 0
    }
  ],
  "batches": [
    {
      "faces": [
        {
          "quality": 0.99826276,
          "rectangle": {
            "top": 220,
            "left": 162,
            "width": 203,
            "height": 196
          },
          "angle": {
            "yaw": 17.226185,
            "pitch": 8.400441,
            "roll": 15.522398
          },
          "landmarks": [
            {
              "x": 163,
              "y": 277
            },
            {
              "x": 167,
              "y": 289
            },
            ~~~~~~~,
            {
              "x": 269,
              "y": 255
            }
          ],
          "attributes": {
            "age_lower_limit": {
              "type": 0,
              "category": "age_lower_limit",
              "value": 23
            },
            "age_up_limit": {
              "type": 0,
              "category": "age_up_limit",
              "value": 33
            },
            "cap_style": {
              "type": 2,
              "category": "HAT_STYLE_TYPE_NONE",
              "value": 0.999972
            },
            "gender_code": {
              "type": 2,
              "category": "MALE",
              "value": 0.999675
            },
            "glass_style": {
              "type": 2,
              "category": "GLASSES_STYLE_TYPE_NONE",
              "value": 0.9972076
            },
            "mustache_style": {
              "type": 2,
              "category": "MUSTACHE_STYLE_TYPE_NONE",
              "value": 0.97730595
            },
            "respirator_color": {
              "type": 2,
              "category": "COLOR_TYPE_NONE",
              "value": 0.99573535
            },
            "st_age": {
              "type": 2,
              "category": "ST_ADULT",
              "value": 1
            },
            "st_expression": {
              "type": 2,
              "category": "ST_CALM",
              "value": 0.99880713
            },
            "st_helmet_style": {
              "type": 2,
              "category": "ST_HELMET_STYLE_TYPE_NONE",
              "value": 0.9993663
            },
            "st_respirator": {
              "type": 0,
              "category": "",
              "value": 0
            }
          },
          "rotated": 1
        }
      ]
    }
  ]
}

顔検知が成功すると、上記のようなレスポンスが返ってきます。

st_ageがST_ADULTなので大人、gender_codeMALEなので男性, respirator_colorCOLOR_TYPE_NONEなのでマスク着用無 と推測されたようです。

あってますね、すごい。

属性に関して詳しくは

表:属性

を参照。

画像4

  • レスポンス
{
    "trace_id": "f97f4e32e12b0c9d2df3f4c8f6f4ccb1",
    "results": [
        {
            "code": 0,
            "message": "Success.",
            "internal_code": 0
        }
    ],
    "batches": [
        {
            "faces": [
                {
                    "quality": 0.9982797,
                    "rectangle": {
                        "top": 150,
                        "left": 181,
                        "width": 130,
                        "height": 136
                    },
                    "angle": {
                        "yaw": 18.2098,
                        "pitch": 7.520368,
                        "roll": -15.592735
                    },
                    "landmarks": [
                        {
                            "x": 198,
                            "y": 162
                        },
                        ~~~~~~~~~~~
                        {
                            "x": 262,
                            "y": 178
                        }
                    ],
                    "attributes": {
                        "age_lower_limit": {
                            "type": 0,
                            "category": "age_lower_limit",
                            "value": 27
                        },
                        "age_up_limit": {
                            "type": 0,
                            "category": "age_up_limit",
                            "value": 37
                        },
                        "cap_style": {
                            "type": 2,
                            "category": "HAT_STYLE_TYPE_NONE",
                            "value": 0.98707956
                        },
                        "gender_code": {
                            "type": 2,
                            "category": "MALE",
                            "value": 0.99992156
                        },
                        "glass_style": {
                            "type": 2,
                            "category": "GLASSES_STYLE_TYPE_NONE",
                            "value": 0.9942698
                        },
                        "mustache_style": {
                            "type": 2,
                            "category": "MUSTACHE_STYLE_TYPE_NONE",
                            "value": 0.67222404
                        },
                        "respirator_color": {
                            "type": 2,
                            "category": "COLOR_TYPE_OTHER",
                            "value": 0.9999087
                        },
                        "st_age": {
                            "type": 2,
                            "category": "ST_ADULT",
                            "value": 1
                        },
                        "st_expression": {
                            "type": 2,
                            "category": "ST_HAPPY",
                            "value": 0.85142446
                        },
                        "st_helmet_style": {
                            "type": 2,
                            "category": "ST_HELMET_STYLE_TYPE_NONE",
                            "value": 0.99641335
                        },
                        "st_respirator": {
                            "type": 0,
                            "category": "",
                            "value": 0
                        }
                    },
                    "rotated": 1
                }
            ]
        }
    ]
}

respirator_colorCOLOR_TYPE_OTHER となっているのでマスク着用有と判定されています。

あってますね。

画像5

  • レスポンス
{
    "trace_id": "4a731fb6205477d95f4493594fc542e7",
    "results": [
        {
            "code": 80303,
            "message": "No face found.",
            "internal_code": 303
        }
    ],
    "batches": [
        {
            "faces": []
        }
    ]
}

顔が小さいためか、顔検知はできませんでした。

画像6

  • レスポンス
{
    "trace_id": "22a81df7f812bf6b99d0593f5ce1f19d",
    "results": [
        {
            "code": 0,
            "message": "Success.",
            "internal_code": 0
        }
    ],
    "batches": [
        {
            "faces": [
                {
                    "quality": 0.9965526,
                    "rectangle": {
                        "top": 292,
                        "left": 45,
                        "width": 123,
                        "height": 142
                    },
                    "angle": {
                        "yaw": 59.729923,
                        "pitch": 4.07797,
                        "roll": 7.824665
                    },
                    "landmarks": [
                        {
                            "x": 46,
                            "y": 322
                        },
                        ~~~~~~~
                        {
                            "x": 79,
                            "y": 322
                        }
                    ],
                    "attributes": {
                        "age_lower_limit": {
                            "type": 0,
                            "category": "age_lower_limit",
                            "value": 16
                        },
                        "age_up_limit": {
                            "type": 0,
                            "category": "age_up_limit",
                            "value": 26
                        },
                        "cap_style": {
                            "type": 2,
                            "category": "HAT_STYLE_TYPE_NONE",
                            "value": 0.99567693
                        },
                        "gender_code": {
                            "type": 2,
                            "category": "FEMALE",
                            "value": 0.9466953
                        },
                        "glass_style": {
                            "type": 2,
                            "category": "GLASSES_STYLE_TYPE_NONE",
                            "value": 0.9986252
                        },
                        "mustache_style": {
                            "type": 2,
                            "category": "MUSTACHE_STYLE_TYPE_NONE",
                            "value": 0.9887771
                        },
                        "respirator_color": {
                            "type": 2,
                            "category": "COLOR_TYPE_NONE",
                            "value": 0.9993965
                        },
                        "st_age": {
                            "type": 2,
                            "category": "ST_ADULT",
                            "value": 1
                        },
                        "st_expression": {
                            "type": 2,
                            "category": "ST_CALM",
                            "value": 0.7430104
                        },
                        "st_helmet_style": {
                            "type": 2,
                            "category": "ST_HELMET_STYLE_TYPE_NONE",
                            "value": 0.9973989
                        },
                        "st_respirator": {
                            "type": 0,
                            "category": "",
                            "value": 0
                        }
                    },
                    "rotated": 1
                },
                {
                    "quality": 0.9761087,
                    "rectangle": {
                        "top": 341,
                        "left": 847,
                        "width": 75,
                        "height": 82
                    },
                    ~~~~~
                },
                {
                    "quality": 0.96427304,
                    ~~~~~~~~~
                },
                {
                    "quality": 0.9879387,
                    ~~~~~~
                }
            ]
        }
    ]
}

複数の顔を検知することも可能でした。

faces に検知した複数の顔の情報が格納されています。

st_expression には ST_CALMST_HAPPYなどで分類されていました。

最後に

顔認証クラウドサービスMercury Cloudの顔検知APIを使ってみました。

6枚の画像でしか試していませんが、全て顔の検知はできていましたね。 精度が高いと評価されているのもうなづけます。

今回は検知だけでしたが、次回は検知したデータを使い顔の比較を行ってみたいと思います。