Amazon Rekognitionで2つの画像から顔を検出・比較する

2018.05.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

中村です。

今日は、Amazon Rekognition(以下、Rekognition)のお話です。 AWS CLIに用意されている、Rekognition APIを使ってみます。

Amazon Rekognition では、画像分析と動画分析をアプリケーションに簡単に追加できます。 Rekognition API に画像または動画を与えるだけで、このサービスが対象物、人、テキスト、シーン、アクティビティ、それに不適切なコンテンツまで検出します。 Amazon Rekognition はまた極めて正確な顔分析と顔認識を行います。顔を検出、分析、比較して、多岐にわたるユーザー検証、カタログ作成、人数計数、公共安全のユースケースに使えます。

compare-faces

ソース画像から検出される顔とターゲット画像から検出される最大100の顔を比較する。

ソース画像に複数の顔が検出される場合は、一番大きい顔を使い比較します。

まず、S3にソース画像とターゲット画像をS3にアップロードします。 使用したフリー素材はこちらです。(※サイズは全て、1600x1066)

AWS CLIではこのコマンドで比較ができます。

$ aws rekognition compare-faces --source-image '{"S3Object": {"Bucket":"test-rekognition-blog","Name":"source.jpg"}}' --target-image '{"S3Object": {"Bucket":"test-rekognition-blog","Name":"target.jpg"}}'

上記のコマンドを実行すると、下記のデータがJSONで返ってきます。

  • FaceMatches
  • SourceImageOrientationCorrection
  • TargetImageOrientationCorrection
  • UnmatchedFaces
  • SourceImageFace

FaceMatches / UnmatchedFacesには、ソース画像の顔とマッチした顔・マッチしなかった顔のデータが返されます。 SourceImageOrientationCorrection / TargetImageOrientationCorrectionには、Exif メタデータに向きの情報がない場合Rekognitionが推定した画像の向きが入ります。 Rekognitionを使用した一般的なアプリケーションでは、認識した画像を正しい向きで表示する場合があるため提供されています。 SourceImageFaceは、比較元の画像の顔の位置・信頼性が返されます。

また、 FaceMatches / UnmatchedFacesには、共通して下記のデータが入ります。

プロパティ名 内容
BoundingBox 認識した顔を囲うバウンディングボックスの位置
Confidence 認識結果の信頼性
Pose 顔の姿勢を表したもの

  • roll: ロール
  • pitch: ピッチ
  • yaw: ヨー軸
Quality
  • brightness: 顔の明るさ
  • sharpness: 顔の鮮明さや鮮鋭さ
Landmarks 顔の部位の位置でX,Y軸とタイプ

  • eyeLeft/eyeRight: 目・nose: 鼻・mouseLeft/mouseRight: 口
  • X: LandMarkの左上からの画像の幅の比率
  • Y: LandMarkの左上からの画像の高さの比率

実際のJSONデータはこのようになります。

Matchした場合

{
    "FaceMatches": [
        {
            "Face": {
                "BoundingBox": {
                    "Width": 0.234375,
                    "Top": 0.15572232007980347,
                    "Left": 0.4806250035762787,
                    "Height": 0.35178235173225403
                },
                "Confidence": 99.99999237060547,
                "Pose": {
                    "Yaw": 1.0256012678146362,
                    "Roll": -4.608410358428955,
                    "Pitch": -0.8010402321815491
                },
                "Quality": {
                    "Sharpness": 99.9305191040039,
                    "Brightness": 54.11868667602539
                },
                "Landmarks": [
                    {
                        "Y": 0.2782706916332245,
                        "X": 0.5543986558914185,
                        "Type": "eyeLeft"
                    },
                    {
                        "Y": 0.2687712609767914,
                        "X": 0.6395020484924316,
                        "Type": "eyeRight"
                    },
                    {
                        "Y": 0.3594965934753418,
                        "X": 0.5966022610664368,
                        "Type": "nose"
                    },
                    {
                        "Y": 0.4383445382118225,
                        "X": 0.5707676410675049,
                        "Type": "mouthLeft"
                    },
                    {
                        "Y": 0.4353347420692444,
                        "X": 0.623863935470581,
                        "Type": "mouthRight"
                    }
                ]
            },
            "Similarity": 96.0
        }
    ],
    "SourceImageOrientationCorrection": "ROTATE_0",
    "TargetImageOrientationCorrection": "ROTATE_0",
    "UnmatchedFaces": [],
    "SourceImageFace": {
        "BoundingBox": {
            "Width": 0.18187500536441803,
            "Top": 0.1463414579629898,
            "Left": 0.43562498688697815,
            "Height": 0.2729831039905548
        },
        "Confidence": 99.99734497070312
    }
}

Matchしなかった場合

{
    "FaceMatches": [],
    "SourceImageOrientationCorrection": "ROTATE_0",
    "TargetImageOrientationCorrection": "ROTATE_0",
    "UnmatchedFaces": [
        {
            "BoundingBox": {
                "Width": 0.13696059584617615,
                "Top": 0.6600000262260437,
                "Left": 0.3789868652820587,
                "Height": 0.09187500178813934
            },
            "Confidence": 99.99980926513672,
            "Pose": {
                "Yaw": 15.426316261291504,
                "Roll": -3.8369193077087402,
                "Pitch": 19.897735595703125
            },
            "Quality": {
                "Sharpness": 99.99090576171875,
                "Brightness": 57.809112548828125
            },
            "Landmarks": [
                {
                    "Y": 0.6892076730728149,
                    "X": 0.42425626516342163,
                    "Type": "eyeLeft"
                },
                {
                    "Y": 0.6878476738929749,
                    "X": 0.47061899304389954,
                    "Type": "eyeRight"
                },
                {
                    "Y": 0.7005182504653931,
                    "X": 0.4575318396091461,
                    "Type": "nose"
                },
                {
                    "Y": 0.7273701429367065,
                    "X": 0.43246060609817505,
                    "Type": "mouthLeft"
                },
                {
                    "Y": 0.727500855922699,
                    "X": 0.47288259863853455,
                    "Type": "mouthRight"
                }
            ]
        },
        {
            "BoundingBox": {
                "Width": 0.024390242993831635,
                "Top": 0.949999988079071,
                "Left": 0.43714821338653564,
                "Height": 0.016249999403953552
            },
            "Confidence": 94.8503646850586,
            "Pose": {
                "Yaw": 15.95901870727539,
                "Roll": -2.701803207397461,
                "Pitch": 16.73355484008789
            },
            "Quality": {
                "Sharpness": 8.39514446258545,
                "Brightness": 79.75173950195312
            },
            "Landmarks": [
                {
                    "Y": 0.9560179710388184,
                    "X": 0.44508394598960876,
                    "Type": "eyeLeft"
                },
                {
                    "Y": 0.956104040145874,
                    "X": 0.4523526132106781,
                    "Type": "eyeRight"
                },
                {
                    "Y": 0.9594592452049255,
                    "X": 0.4502921998500824,
                    "Type": "nose"
                },
                {
                    "Y": 0.9637092351913452,
                    "X": 0.4477332830429077,
                    "Type": "mouthLeft"
                },
                {
                    "Y": 0.9637230634689331,
                    "X": 0.4524436593055725,
                    "Type": "mouthRight"
                }
            ]
        }
    ],
    "SourceImageFace": {
        "BoundingBox": {
            "Width": 0.4593749940395355,
            "Top": 0.09849905967712402,
            "Left": 0.4000000059604645,
            "Height": 0.6894934177398682
        },
        "Confidence": 99.99999237060547
    }
}

BoundingBoxのWidth・Top・Left・Heightは、実際の位置ではありません。画像全体のサイズとの比の値になります。

例えば、今回使っている画像は1600x1066なので、BoundingBoxの値を元に計算すると、

"BoundingBox": {
    "Width": 0.234375,
    "Top": 0.15572232007980347,
    "Left": 0.4806250035762787,
    "Height": 0.35178235173225403
}

BoundingBoxの位置は、 Width = 375 Top = 165.999993205 Left = 769.000005722 Height = 374.999986947 となります。 実際に画像に枠をつける等の作業が必要な時に、必要になると思います。

まとめ

いかがでしたでしょうか。 まだまだAPIがあるので、今後も1つずつ解説していきます。

ちなみにコンソールからも一連の流れを実行できます。 またJSONだけでなく、実際に検出された顔と部分も確認することができます。