Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

概要

Raspberry Pi でSIM経由で通信する顔認証システムを作成し、認証結果を kintone で管理するシステムを構築します。
今回は、Raspberry Pi 内に保管した画像を使って顔が同じかを比較判定します。
cf.jpg

顔認証システム構築シリーズ

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を自在に操ろう ~入門編~」の情報を参考にすると良いでしょう。

AWS 側の設定

IAM で AmazonRekognitionFullAccess 権限を持つプログラムのみからアクセスするユーザを追加し、アクセスキーを保管します。
ユーザの追加やアクセスキーについては、「アクセスキーについて」の情報を参考にすると良いでしょう。

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
テスト画像の Other01.jpg, Other02.jpg, Other04.jpg, Other05.jpg は、「ぱくたそ」のフリー素材を利用させていただきました。

その他にも、以下の面白い結果を得ることができました。
- 顔検出で 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/

yukataoka
若い頃にUターンしたIT屋で、出身は高知県仁淀川町。IoTとクラウドを活用した地域課題解決などに挑戦。 コミュニティ活動の傍ら、サイボウズ公認 kintone エバンジェリスト も務める複業者。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away