Amazon Rekognition で顔が同じか比較判定し、感情などの情報を取得する

## 概要
Raspberry Pi でSIM経由で通信する顔認証システムを作成し、認証結果を kintone で管理するシステムを構築します。
今回は、Raspberry Pi 内に保管した画像を使って顔が同じかを比較判定します。
![cf.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/466e2660-6c37-3d75-92c9-4899cf295643.jpeg)

### 顔認証システム構築シリーズ
Raspberry Pi で顔を認識しファイルに保存する
https://qiita.com/yukataoka/items/7510217f4b6efdefff06

Amazon Rekognition で顔が同じか比較判定し、感情などの情報を取得する
https://qiita.com/yukataoka/items/3fde5d6b22255ff1aed9

Raspberry Pi でCO2センサを使う
https://qiita.com/yukataoka/items/a3b4065e8210b8f372ff

Raspberry Pi で温湿度気圧センサを使う
https://qiita.com/yukataoka/items/8f9046587c978e91f689

## 環境
前回の以下の内容を参照ください。
https://qiita.com/yukataoka/items/7510217f4b6efdefff06#%E7%92%B0%E5%A2%83

## Python boto3 環境の設定
boto3 は AWS (Amazon Web Services) を Python から操作するためのライブラリです。
pip3 で簡単にインストールできます。

```
$ sudo pip3 install boto3
```

boto3 の基本的な使い方については、「[Python boto3 でAWSを自在に操ろう ~入門編~](https://qiita.com/kimihiro_n/items/f3ce86472152b2676004)」の情報を参考にすると良いでしょう。

## AWS 側の設定
IAM で AmazonRekognitionFullAccess 権限を持つプログラムのみからアクセスするユーザを追加し、アクセスキーを保管します。
ユーザの追加やアクセスキーについては、「[アクセスキーについて](https://aws.amazon.com/jp/developers/access-keys/)」の情報を参考にすると良いでしょう。

## Python3 のコード
AWS の rekognition を利用して、顔写真の比較結果を表示します。
また、顔検出から感情データの取得結果を表示します。

``` Faces.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import boto3
import json

class Faces:

    def __init__(self, access='', secret='' , region='ap-northeast-1'):
        self.client=boto3.client('rekognition',
            aws_access_key_id = access,
            aws_secret_access_key = secret,
            region_name = region
        )

    # 顔写真を比較する
    def Compare(self, sourceFile='source.jpg', targetFile='target.jpg'):
        
        imageSource = open(sourceFile,'rb')
        imageTarget = open(targetFile,'rb')
        
        response = self.client.compare_faces(SimilarityThreshold=70,
                                  SourceImage={'Bytes': imageSource.read()},
                                  TargetImage={'Bytes': imageTarget.read()})
        
        for faceMatch in response['FaceMatches']:
            similarity = faceMatch['Similarity']
            position = faceMatch['Face']['BoundingBox']
            confidence = faceMatch['Face']['Confidence']
            print('O ' + targetFile + ' face at ' +
                   str(position['Left']) + ' ' +
                   str(position['Top']) +
                   ' matches with ' + str(confidence) + '% confidence, ' + 
                   str(similarity) + '% similarity')
        
        for faceUnmatch in response['UnmatchedFaces']:
            position = faceUnmatch['BoundingBox']
            confidence = faceUnmatch['Confidence']
            print('X ' + targetFile + ' face at ' +
                   str(position['Left']) + ' ' +
                   str(position['Top']) +
                   ' unmatches with ' + str(confidence) + '% confidence')
        
        imageSource.close()
        imageTarget.close()
        
        return response['FaceMatches']
    
    # 顔検出から感情データを取得
    def Detect(self, sourceFile='source.jpg'):
        
        imageSource = open(sourceFile,'rb')
        response = self.client.detect_faces(Image={'Bytes': imageSource.read()},Attributes=['ALL'])
        
        print('Detected faces for ' + sourceFile)    
        for faceDetail in response['FaceDetails']:
            position = faceDetail['BoundingBox']
            print('The face at ' +
                  str(position['Left']) + ' ' +
                  str(position['Top']) +
                  '. The detected face is between ' + str(faceDetail['AgeRange']['Low']) +
                  ' and ' + str(faceDetail['AgeRange']['High']) + ' years old. ' +
                  ' Gender is ' + faceDetail['Gender']['Value'] + '. ')
            print('Emotions attributes:')
            print(json.dumps(faceDetail['Emotions'], indent=4, sort_keys=True))
        
        return response
    
if __name__ == "__main__":

    face = Faces('aws_access_key_id', 'aws_secret_access_key')
    print('================================')
    face.Compare('My01.jpg', 'My02.jpg')
    print('================================')
    face.Compare('My01.jpg', 'My03.jpg')
    print('================================')
    face.Compare('My03.jpg', 'My04.jpg')
    print('================================')
    face.Compare('My03.jpg', 'My05.jpg')
    print('================================')
    face.Compare('My03.jpg', 'Other01.jpg')
    print('================================')
    face.Compare('My03.jpg', 'Other02.jpg')
    print('================================')
    face.Compare('My03.jpg', 'Other03.jpg')
    print('================================')
    face.Compare('My03.jpg', 'Other04.jpg')
    print('================================')
    face.Compare('My03.jpg', 'Other05.jpg')
    print('================================')
    print('\n')
    print('--------------------------------')
    face.Detect('My01.jpg')
    print('--------------------------------')
    face.Detect('My02.jpg')
    print('--------------------------------')
    face.Detect('My03.jpg')
    print('--------------------------------')
    face.Detect('My04.jpg')
    print('--------------------------------')
    face.Detect('My05.jpg')
    print('--------------------------------')
    face.Detect('Other01.jpg')
    print('--------------------------------')
    face.Detect('Other02.jpg')
    print('--------------------------------')
    face.Detect('Other03.jpg')
    print('--------------------------------')
    face.Detect('Other04.jpg')
    print('--------------------------------')
    face.Detect('Other05.jpg')
    print('--------------------------------')
```

## 結果
以下のサンプルデータと結果から、2つの顔画像のマッチ、アンマッチ精度は相当高いようです。
こちらで公開できないですが、保有する写真から眼鏡顔を選んで比較しましたが、完ぺきにアンマッチを判定しました。
![list.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/f6404e83-818b-f184-e9d6-2a59553a0537.jpeg)
テスト画像の Other01.jpg, Other02.jpg, Other04.jpg, Other05.jpg は、「[ぱくたそ](https://www.pakutaso.com/ "ぱくたそ")」のフリー素材を利用させていただきました。

その他にも、以下の面白い結果を得ることができました。
- 顔検出で My01.jpg の背後にいる人を検知していますが、性別は間違えています。
- 顔検出で My02.jpg はHAPPYが 97.32% ですが、確かに朗らかな感じが強いです。
- 顔検出で My05.jpg はCALM(穏やかな)が 90.51% ですが、そんな感じでしょうか。
- 顔検出で Other03.jpg は息子ですが、祭りの恰好で性別を間違えています。獅子舞は検出されていません。

## 注意
Amazon Rekognition の 顔の比較 Webサイトで実行した場合のレスポンスの json と Python の boto3 で取得した json とはレイアウトは同じですが、結果のデータが異なります。
Webサイトでは、比較側の顔が複数ある場合 FaceMatches に全ての顔が分類され UnmatchedFaces は空となります。
boto3で取得した場合は、比較側の顔が複数ある場合でも顔が合致する顔が FaceMatches に、合致しない場合は UnmatchedFaces に分類されます。（顔が1つの場合でも同じ。）
さすがに、このような現象は許して欲しいですね。

```
================================
O My02.jpg face at 0.4581129848957062 0.07815402746200562 matches with 100.0% confidence, 99.81407928466797% similarity
================================
O My02.jpg face at 0.4581129848957062 0.07815402746200562 matches with 100.0% confidence, 99.81407928466797% similarity
================================
O My03.jpg face at 0.2972644567489624 0.3247520625591278 matches with 99.99977111816406% confidence, 97.09852600097656% similarity
================================
O My04.jpg face at 0.5282963514328003 0.5222135782241821 matches with 99.99996185302734% confidence, 98.2182388305664% similarity
================================
O My05.jpg face at 0.2938196659088135 0.2693774104118347 matches with 99.99998474121094% confidence, 97.79974365234375% similarity
================================
X Other01.jpg face at 0.39465853571891785 0.18935610353946686 unmatches with 99.99995422363281% confidence
================================
X Other02.jpg face at 0.35288524627685547 0.16854149103164673 unmatches with 100.0% confidence
================================
X Other03.jpg face at 0.3426309823989868 0.32221511006355286 unmatches with 99.9999771118164% confidence
================================
X Other04.jpg face at 0.4581896960735321 0.18310882151126862 unmatches with 100.0% confidence
================================
X Other05.jpg face at 0.3525817394256592 0.26986002922058105 unmatches with 100.0% confidence
================================

--------------------------------
Detected faces for My01.jpg
The face at 0.171476617455 0.124303229153. The detected face is between 29 and 45 years old.  Gender is Male. 
Emotions attributes:
[
    {
        "Confidence": 1.3882617950439453, 
        "Type": "CALM"
    }, 
    {
        "Confidence": 0.5102754831314087, 
        "Type": "SAD"
    }, 
    {
        "Confidence": 85.1082763671875, 
        "Type": "HAPPY"
    }, 
    {
        "Confidence": 3.5353944301605225, 
        "Type": "SURPRISED"
    }, 
    {
        "Confidence": 3.0688531398773193, 
        "Type": "DISGUSTED"
    }, 
    {
        "Confidence": 2.9705846309661865, 
        "Type": "ANGRY"
    }, 
    {
        "Confidence": 3.4183461666107178, 
        "Type": "CONFUSED"
    }
]
The face at 0.923280537128 0.317661702633. The detected face is between 20 and 38 years old.  Gender is Female. 
Emotions attributes:
[
    {
        "Confidence": 45.22486114501953, 
        "Type": "CONFUSED"
    }, 
    {
        "Confidence": 45.277984619140625, 
        "Type": "ANGRY"
    }, 
    {
        "Confidence": 46.153038024902344, 
        "Type": "CALM"
    }, 
    {
        "Confidence": 45.34050369262695, 
        "Type": "SURPRISED"
    }, 
    {
        "Confidence": 45.16493606567383, 
        "Type": "DISGUSTED"
    }, 
    {
        "Confidence": 48.69762420654297, 
        "Type": "SAD"
    }, 
    {
        "Confidence": 49.141056060791016, 
        "Type": "HAPPY"
    }
]
--------------------------------
Detected faces for My02.jpg
X The face at 0.458112984896 0.078154027462. The detected face is between 15 and 25 years old.  Gender is Male. 
Emotions attributes:
[
    {
        "Confidence": 0.37020349502563477, 
        "Type": "CONFUSED"
    }, 
    {
        "Confidence": 0.08613979816436768, 
        "Type": "SAD"
    }, 
    {
        "Confidence": 0.8607620596885681, 
        "Type": "SURPRISED"
    }, 
    {
        "Confidence": 0.602418065071106, 
        "Type": "DISGUSTED"
    }, 
    {
        "Confidence": 0.7131559252738953, 
        "Type": "ANGRY"
    }, 
    {
        "Confidence": 97.3235092163086, 
        "Type": "HAPPY"
    }, 
    {
        "Confidence": 0.04381405934691429, 
        "Type": "CALM"
    }
]
（中略）
--------------------------------
Detected faces for My05.jpg
X The face at 0.293819665909 0.269377410412. The detected face is between 35 and 52 years old.  Gender is Male. 
Emotions attributes:
[
    {
        "Confidence": 3.0812907218933105, 
        "Type": "CONFUSED"
    }, 
    {
        "Confidence": 0.8279056549072266, 
        "Type": "ANGRY"
    }, 
    {
        "Confidence": 90.508056640625, 
        "Type": "CALM"
    }, 
    {
        "Confidence": 2.1059083938598633, 
        "Type": "SURPRISED"
    }, 
    {
        "Confidence": 0.7003881931304932, 
        "Type": "DISGUSTED"
    }, 
    {
        "Confidence": 1.9108589887619019, 
        "Type": "SAD"
    }, 
    {
        "Confidence": 0.8655884265899658, 
        "Type": "HAPPY"
    }
]
（中略）
--------------------------------
Detected faces for Other03.jpg
X The face at 0.342630982399 0.322215110064. The detected face is between 9 and 14 years old.  Gender is Female. 
Emotions attributes:
[
    {
        "Confidence": 3.8436155319213867, 
        "Type": "SAD"
    }, 
    {
        "Confidence": 65.46427917480469, 
        "Type": "CALM"
    }, 
    {
        "Confidence": 17.642349243164062, 
        "Type": "CONFUSED"
    }, 
    {
        "Confidence": 2.095968723297119, 
        "Type": "DISGUSTED"
    }, 
    {
        "Confidence": 9.949064254760742, 
        "Type": "ANGRY"
    }, 
    {
        "Confidence": 0.7067313194274902, 
        "Type": "SURPRISED"
    }, 
    {
        "Confidence": 0.29799211025238037, 
        "Type": "HAPPY"
    }
]
（後略）
--------------------------------
```

## 参考サイト
Amazon Rekognition - Python Code Samples
https://gist.github.com/alexcasalboni/0f21a1889f09760f8981b643326730ff

イメージ間の顔の比較
https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/faces-comparefaces.html

イメージ内の顔の検出
https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/faces-detect-images.html

Amazon Rekognitionで2つの画像から顔を検出・比較する
https://dev.classmethod.jp/cloud/make-full-use-rekognition/