AWS Rekognition の Detecting Unsafe Content 機能を使う [SDK] [Python3]

「[scrapy でクローラーを実装し、画像を収集してみる](https://qiita.com/paper2/items/bb4fe0f78ea1dbd974b5)」の続きです。
画像収集したドメインがいかがわしい画像を扱っているかを AWS Rekognition で判別してみます。**あくまでもフィルタリング用途で使うのであって、見つけるために使うわけではありません。**

本当はドメインを集める scrapy のコードに埋め込んで全部自動化する予定でしたが、 Rekognition 無料枠が毎月5000枚なのですぐ越してしまうと思いドメインのフォルダを指定して、そのフォルダ内のみチェックするようにします。まあ本当の目的は AWS Rekognition を使ってみることだったのでこれでいいです笑


# 参考サイト
- [Limits in Amazon Rekognition - Amazon Rekognition](https://docs.aws.amazon.com/rekognition/latest/dg/limits.html)
- [【Amazon Rekognition】料金（人工知能・機械学習の画像認識・画像解析） | AWS](https://aws.amazon.com/jp/rekognition/pricing/)
- [公式ドキュメント](https://docs.aws.amazon.com/rekognition/latest/dg/rekognition-dg.pdf)

# AWS Rekognition の判別結果
公式ドキュメントの引用です。トップレベルとセカンドレベルでカテゴリわけされています。セカンドレベルの`Sexual Activity`が何かわかりませんが、基本的に裸の人がいると`Explicit Nudity`になります。水着とかを着ていると`Suggestive`になります。何でもないと何も返ってきません。つまり今回~~探す~~フィルタリングするのは`Explicit Nudity`です。
![Screen Shot 2018-01-23 at 20.13.37.png](https://qiita-image-store.s3.amazonaws.com/0/230552/bf7cd6a0-b1e0-d9ed-3511-70a0f099d0e3.png)


# ソースコード
以下ソースコードです。


``` rekognition.py
# -*- coding: utf-8 -*-
import boto3
import sys
import os
import imghdr
from PIL import Image

def check_nudity(file_path):
    client = boto3.client('rekognition', region_name='us-east-1')

    # 形式を確認
    if imghdr.what(file_path) not in ['png', 'jpeg']:
        return False

    # 80 * 80 以下ははじく
    im = Image.open(file_path)
    width, height = im.size
    if width < 80 or height < 80:
        return

    # バイナリ形式で渡す
    img = open(file_path, 'rb').read()
    response = client.detect_moderation_labels(
        Image={
            'Bytes': img
        }
    )
    print(os.path.basename(file_path), response)

    # Nudity と判別されたら True を返す
    for label in response['ModerationLabels']:
        if label['Name'].find('Nudity') > 0:
            return True
    return False


if __name__ == '__main__':
    argvs = sys.argv
    argc = len(argvs)

    dir_path = argvs[1]

    files = os.listdir(dir_path)

    for filename in files:
        if check_nudity(dir_path + "/" + filename):
            print('これはいかがわしいドメインです!!（ファイル: ' + filename + ')')
            sys.exit(0)
    print('いかがわしくありません、、、、')
```
参考サイトに書いてありますが、`detect_moderation_labels()`を利用するために制限があるので注意しましょう。80*80 より小さいとダメとかで最初引っかかりました。

# 実行結果

``` shell
$python detect_moderation_labels /scrapy/tutorial/imgs/example.com
c0c4ead9531e9a244a26d15aacea9a375f9f8b8b.jpg {'ModerationLabels': [], 'ResponseMetadata': {'RequestId': '086c4992-002e-11e8-8d0f-012f12111acb', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/x-amz-json-1.1', 'date': 'Tue, 23 Jan 2018 11:10:33 GMT', 'x-amzn-requestid': '086c4992-002e-11e8-8d0f-012f12111acb', 'content-length': '23', 'connection': 'keep-alive'}, 'RetryAttempts': 0}}
eb0aa62cc6296348e7224cec086f95d8934b7ce9.jpg {'ModerationLabels': [], 'ResponseMetadata': {'RequestId': '0960bbc9-002e-11e8-9f7b-0fd5b391a951', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/x-amz-json-1.1', 'date': 'Tue, 23 Jan 2018 11:10:35 GMT', 'x-amzn-requestid': '0960bbc9-002e-11e8-9f7b-0fd5b391a951', 'content-length': '23', 'connection': 'keep-alive'}, 'RetryAttempts': 0}}
1877121b6e0eb4b09b9a489f05068aa4ad0ee2e0.jpg {'ModerationLabels': [], 'ResponseMetadata': {'RequestId': '0a2ff2c2-002e-11e8-a67c-bfcb0a424ae5', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/x-amz-json-1.1', 'date': 'Tue, 23 Jan 2018 11:10:37 GMT', 'x-amzn-requestid': '0a2ff2c2-002e-11e8-a67c-bfcb0a424ae5', 'content-length': '23', 'connection': 'keep-alive'}, 'RetryAttempts': 0}}
35f7fa2ff7f57987630514c4739b96ee0b16fbc1.jpg {'ModerationLabels': [{'Confidence': 64.89798736572266, 'Name': 'Explicit Nudity', 'ParentName': ''}, {'Confidence': 64.89798736572266, 'Name': 'Sexual Activity', 'ParentName': 'Explicit Nudity'}], 'ResponseMetadata': {'RequestId': '0b0f5614-002e-11e8-ac71-05fdd8d8affb', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/x-amz-json-1.1', 'date': 'Tue, 23 Jan 2018 11:10:38 GMT', 'x-amzn-requestid': '0b0f5614-002e-11e8-ac71-05fdd8d8affb', 'content-length': '185', 'connection': 'keep-alive'}, 'RetryAttempts': 0}}
これはいかがわしいドメインです!!（ファイル: 35f7fa2ff7f57987630514c4739b96ee0b16fbc1.jpg)
```
これで Scrapy と AWS Rekognition でいかがわしいサイトを探すことができます！！

# 複数の写真を合成して分析してみる
おまけです。冒頭でも説明しましたが、AWS Rekognition で分析しようとすると一枚約一円かかります。個人的にはまあまあ高い気もします。ということで一枚の画像にたくさん分析したい画像を詰めれば節約できるのではないかと思いやってみました。

結果**うまく判別されませんでした**。機械学習さんはそういうのは判別できないのですかね。48枚の画像の中に１枚だけいかがわしい画像をいれてみましたがばれませんでした。ふむ、、、、フィルタリングで本気で使う場合にもこういう抜け穴があると少し危ないかもしれませんね。

# まとめ
簡単に機械学習の恩恵を受けれる AWS Rekognition を利用しました。何枚か試してみたけど基本は正確に判別できたのですごいなと思いました。実際にユースケースでもあげられているような SNS のプロフィールなどに不適切な画像を載せることを防いだりと色々な使い道があると感じました。