大量の活動写真から我が子の写真を見つけ出す


「こどもの日頃の成長の写真を共有します」と大量の写真をダウンロードするリンクが送られてきました。楽しみにしてダウンロードはしてみたものの数百枚もある写真を１枚１枚確認して、我が子の写っているものだけをピックアップするのは、もはや楽しみとは言えない、、、
そんな不純な動機ですが、Rekognitionにある程度の選別を助けてもらうことにしました。

# Amazon Rekognition

Amazon のコンピュータビジョン科学者が開発した深層学習テクノロジに基づいたサービスで、機械学習の知識がなくても、画像や映像の分析をできるという優れたサービスです。
今回は、この中でも「イメージ間の顔の比較」の機能をPythonで利用してみます。
詳細は [公式ドキュメント][1] を参照してください。

# イメージ間の顔の比較

プログラムを作成する前に、まずはマネージメントコンソールで機能を確認しました。
直感的に画面をみることで、おおよそのことは理解できますね。探す対象の人物写真(Reference face)に我が子の顔の写った写真を指定して、比較対象の写真(Comparison faces)としてダウンロードさせてもらった数百枚ある写真を１枚ずつ指定すればうまくいきそうです。
斜め写真や影のある写真でも認識できるというのは、現実問題としてはかなりありがたいです。

下記は、マネージメントコンソールでAmazon RekognitionのFace comparisonを選択した画面です。
![rekognition.png](https://qiita-image-store.s3.amazonaws.com/0/284457/4e2081b0-cfa9-5801-f9be-e077920c94ac.png)

# Pythonでつくってみる

とはいえ、マネージメントコンソールの画面のとおりにクラスを作って、boto3をそのまんま使っているだけな気もしますが、、、まぁ、こんな感じでしょうか。
今回は、S3に写真の画像ファイルをアップロードしてから処理を行いましたが、バイナリーで画像ファイルを都度送信して比較処理を行うこともできます。

``` Python
# -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import unicode_literals
import boto3

class ReferenceFaceImage:
    def __init__(self, source_image_dict=None):
        self.source_image_dict = source_image_dict
        self.__client = boto3.client('rekognition')

    @staticmethod
    def s3_dict(bucket, name, version=None):
        image_dict = dict()
        image_dict.update({'Bucket': bucket})
        image_dict.update({'Name': name})
        if version is not None:
            image_dict.update({'Name': version})
        s3_object = dict()
        s3_object.update({'S3Object': image_dict})
        return s3_object

    def exception(func):
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                print('Exception occurred: {args}'.format(args=e.args))
        return wrapper

    @exception
    def compare(self, target_image_dict):
        print('Compare: Target={target} Source={source}'.format(target=target_image_dict, source=self.source_image_dict))
        res = self.__client.compare_faces(SourceImage=self.source_image_dict, TargetImage=target_image_dict)

        # 一致率を出力
        if len(res.get('FaceMatches')) > 0:
            similarity = 0
            for s in res.get('FaceMatches'):
                if similarity < s.get('Similarity'):
                    similarity = s.get('Similarity')
            print('Matched({similarity})'.format(similarity=similarity))
        else:
            print('NotMatched')
        return res

if __name__ == '__main__':
    source_bucket_name = 'my-source-bucket'
    source_key = 'rekognition/sample/Reference.png' # 参照の顔写真
    source_image_dict = RekognitionFaceImage.s3_dict(bucket=source_bucket_name, name=source_key)

    target_bucket_name = 'my-target-bucket'
    target_key = 'rekognition/many-pics/Comparison-000001.JPG'  # 検索対象の写真
    target_image_dict = RekognitionFaceImage.s3_dict(bucket=target_bucket_name, name=target_key)

    # 実際の処理
    face_image = ReferenceFaceImage(source_image_dict=source_image_dict)
    face_image.compare(target_image_dict=target_image_dict)  # 実際には、この処理をループで実行する

```

# 実際に処理をしてみると...

比較処理1回あたり、2〜3秒で処理が戻ってきます。なんだか良い感じ。。。

ところが、ある程度進んだ途中でエラー  `An error occurred (InvalidParameterException) when calling the CompareFaces operation: Request has invalid parameters`  が出てしまいます。

調べてみると、なるほど顔が写っていない写真（美味しそうなメロンですが...）が混じっていますね。こういう写真は、事前に顔の有無を確認(`detect_faces処理`)してから、比較処理をすることもできそうです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/284457/57c89bd9-8d1d-096c-393f-220e6caeda41.png" width=60%>

# 結果

結果として、写真を判定して我が子の写った写真を抽出することができました。
顔が半分だけ写ったような写真でも判定されていることには驚きです。おおよそですが `Similarity` が `95%` を超えると間違いなく我が子が写っている写真でした。

活動写真だけに条件が厳しいものも多かったことと、やはり子どもの顔は判定が難しいということもあるのかなぁと思いますが、その部分は写真を見ながら分類するのも一つの楽しみかもしれないです。少なくとも、数百枚から探し出すという途方もない作業からは開放されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/284457/42e2780a-372c-3331-b137-bd45e53b8599.png" width=70%>
600枚近くの写真の中に50枚近くの顔が写っていない写真が含まれていました。


# 参考資料

[Amazon Rekognition とは][1]
[1]:https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/what-is.html
[Amazon Rekognitionで2つの画像から顔を検出・比較する][2]
[2]:https://dev.classmethod.jp/cloud/make-full-use-rekognition/