Python で Amazon Rekognition を動かしてみた

# 背景
「家に不在時でも〇〇が家に来たら〇〇を行う」
こんな事ができたらなぁと考える事がありました。

例えば
　・ヤクルトレディが来たらオートロック解除
　・宅配業者ならオートロック解除
　・N〇Kならブラックリスト登録
でもどうやって判断するんだ？画像分析ってなんか難しそうやなぁ

と言うことで今回は、やりたい事の一端に活用できそうな **Amazon Rekognition** を動かしてみます。
また、今後応用できるように **Python** で動作できる様にします。 

# 流れ
①**Python** 実行
②**Amazon S3** にImageを格納
③**Amazon Rekognition** で要素を分析
こんな流れでいきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/4f488290-7d76-1b8d-574e-23683344b506.png" width="700">
# 環境
Windows 10 Enterprise 上でWSL2を動かしています。
Ubuntu、Pythonを利用しています。

### Version
```
$cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"

$python3 -V
Python 3.8.10

>wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Running         2
```
# Amazon Rekognitionとは？
アップロードした画像を元に各種分析を行ってくれるディープラーニングベースの視覚分析サービスです。
ここに画像をアップロードさせます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/126705fe-80d3-4e5a-152a-401a2d389e73.png" width="500">

例えばラベル検出だと何が写ってるのか判定してくれますね。
>イメージ内の物体、概念、シーン、アクションに自動的にラベルを付け、信頼スコアを提供します
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/fbe22f52-d5e1-0c0a-425c-06d565ae97fd.png" width="500">

他にも"顔の分析"や複数の分析がありますが、今回はラベル検出を試してみます。

https://aws.amazon.com/jp/rekognition/

# Amazon S3とは？
一言で言うとオンラインストレージです。
基本的なストレージの使い方と一緒でバックアップデータとして使えます。
また、AWSのサービスを使うS3を参照させれるのでこちらを利用していきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/1aac12c7-0327-aaa9-5437-3969c5a243d1.png" width="500">
>Amazon Simple Storage Service (Amazon S3) は、業界をリードするスケーラビリティ、データ可用性、セキュリティ、およびパフォーマンスを提供するオブジェクトストレージサービスです。

https://aws.amazon.com/jp/s3/


# 画像の用意

我が家に居た丁度いい熊人形
今回はシンプルに、端末内に保存された以下の画像を利用します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/87c8701b-5d0b-e241-bb8a-7032a25f7121.jpeg" width="500">
応用する場合は、監視カメラの映像を画像で保存しアップロードする方法になると思います。
それはまた別の機会に…

>参考用：
[Cisco Meraki MV](https://www.cisco.com/c/m/ja_jp/meraki/products/security-cameras.html) の [Generate Device Camera Snapshot](https://developer.cisco.com/meraki/api-v1/#!generate-device-camera-snapshot) でカメラに写る映像をスナップショットとして保存できます。

# AWS IAM
IAMユーザを作成して利用する`AWS S3`、`AWS Rekognition`だけの権限を与えてあげましょう。
私は気付かなかったので以下の様にまるごと権限与えちゃってますが[ドキュメント](https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/labels-detect-labels-image.html)には
`AmazonRekognitionFullAccess` と `AmazonS3ReadOnlyAccess`があれば良いと記載されてますね。
>引用元：[開発者ガイド](https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/labels-detect-labels-image.html)
Create or update an IAM user with AmazonRekognitionFullAccess and AmazonS3ReadOnlyAccess permissions. For more information, see Step 1: Set up an AWS account and create an IAM user.

私のIAM↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/36dc9bf7-5040-8598-9a12-e0c15d030bd4.png" width="700">
「めんどくさっ！FullAccsecc持ってるルートユーザでええやろ！」
と適当に扱ってしまうと、クレカの請求がすごい事になる可能性があるので気を付けましょう。

AWSを利用するまでの工程やIAMユーザ作成については沢山記事がありますので割愛します。
ここではIAMについての解説と本当にあった怖い話を紹介しておきます。

https://qiita.com/tmknom/items/303db2d1d928db720888


https://qiita.com/mochizukikotaro/items/a0e98ff0063a77e7b694

# Local → Amazon S3
まずは **Amazon S3** にて格納する**バケット**を作成してください。
格納する箱だと思ってくれたらいいです。

[AWS SDK for Python (Boto3) ](https://aws.amazon.com/jp/sdk-for-python/)を利用して、AWSのサービスをPythonで簡単に利用させてもらいます。
>AWS SDK for Python (Boto3) を使用すると、AWS の使用を迅速に開始できます。Boto3 を使用することで、Python のアプリケーション、ライブラリ、スクリプトを AWS の各種サービス（Amazon S3、Amazon EC2、Amazon DynamoDB など）と容易に統合できます。

### ここからできるだけパーツ毎にコードを記載していきます。

**`※記載するコードについて`**
エラー処理やってなくね？とかこれ変じゃね？とかあったりと至らない部分が多々あると思いますが、生暖かい目で見てあげてください。
動けばええねんくらいでやらしてもらってます！


``` python:Amazon S3へのアップロード部分
def S3_upload(aws_bucket, aws_file, aws_key):
    print("S3にアップロード中")
    client = boto3.client('s3')
    client.upload_file(aws_file, aws_bucket, aws_key)
    return
```

[boto3.client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/boto3.html?highlight=boto3.client#boto3.client)の部分でサービスを指定してます。

**client.upload_file** の部分は [configparser](https://qiita.com/mimitaro/items/3506a444f325c6f980b2) で管理してます。
・**aws_file** 端末の画像格納FullPath
・**aws_key** アップロード後のファイル名  
・**aws_bucket** S3で作成したバケット名
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/7290c39c-496d-1d08-3f59-e21110a52cde.png" width="500">
# Amazon S3 → Amazon Rekognition
開発者ガイドを参考に要素を表示する部分を書いていきます。
利用するのは以下の要素ですね。
>・**aws_key** アップロード後のファイル名 
>・**aws_bucket** Amazon S3で作成したバケット名

https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/labels-detect-labels-image.html


``` python:ラベル検出部分
def detect_labels(aws_key, aws_bucket):
    print("ラベル検出中")
    client = boto3.client('rekognition')
    response = client.detect_labels(Image={'S3Object':{'Bucket':aws_bucket,'Name':aws_key}},
        MaxLabels=10)
    print()
    for label in response['Labels']:
        print ("Label: " + label['Name'])
        print ("Confidence: " + str(label['Confidence']))
        print ("Instances:")
        for instance in label['Instances']:
            print ("  Bounding box")
            print ("    Top: " + str(instance['BoundingBox']['Top']))
            print ("    Left: " + str(instance['BoundingBox']['Left']))
            print ("    Width: " +  str(instance['BoundingBox']['Width']))
            print ("    Height: " +  str(instance['BoundingBox']['Height']))
            print ("  Confidence: " + str(instance['Confidence']))
            print()

        print ("Parents:")
        for parent in label['Parents']:
            print ("   " + parent['Name'])
        print ("----------")
        print ()
    return len(response['Labels'])
```
`client = boto3.client('rekognition')`で rekognition を利用します。
`response['Labels']` でラベルの数だけ各要素の内容をprintさせてます。

# 実行
上記のコードを合体して実行します。
合体したコード↓

<details><summary>Amazon_Rekognition.py</summary>

```python:Amazon_Rekognition.py
import boto3
import configparser

def S3_upload(aws_bucket, aws_file, aws_key):
    print("S3にアップロード中")
    client = boto3.client('s3')
    client.upload_file(aws_file, aws_bucket, aws_key)
    return

def detect_labels(aws_key, aws_bucket):
    print("ラベル検出中")
    client = boto3.client('rekognition')
    response = client.detect_labels(Image={'S3Object':{'Bucket':aws_bucket,'Name':aws_key}},
        MaxLabels=10)
    print()
    for label in response['Labels']:
        print ("Label: " + label['Name'])
        print ("Confidence: " + str(label['Confidence']))
        print ("Instances:")
        for instance in label['Instances']:
            print ("  Bounding box")
            print ("    Top: " + str(instance['BoundingBox']['Top']))
            print ("    Left: " + str(instance['BoundingBox']['Left']))
            print ("    Width: " +  str(instance['BoundingBox']['Width']))
            print ("    Height: " +  str(instance['BoundingBox']['Height']))
            print ("  Confidence: " + str(instance['Confidence']))
            print()

        print ("Parents:")
        for parent in label['Parents']:
            print ("   " + parent['Name'])
        print ("----------")
        print ()
    return len(response['Labels'])

def gather_credentials():
    """Gather Meraki credentials"""
    conf_par = configparser.ConfigParser()
    try:
        conf_par.read('credentials.ini')
        aws_bucket = conf_par.get('aws', 'bucket')
        aws_file = conf_par.get('aws', 'file')
        aws_key = conf_par.get('aws', 'key')
    except:
        print('Missing credentials or input file!')
        sys.exit(2)
    return aws_bucket, aws_file, aws_key

if __name__ == "__main__":
    (AWS_BUCKET,AWS_FILE,AWS_KEY) = gather_credentials()
    USER_DATA = {
        'AWS_BUCKET': AWS_BUCKET,
        'AWS_FILE': AWS_FILE,
        'AWS_FILE': AWS_KEY
    }

    S3_upload(AWS_BUCKET,AWS_FILE,AWS_KEY)
    detect_labels = detect_labels(AWS_KEY, AWS_BUCKET)
    print(str(detect_labels) + "個ラベルがありました。")
```
</details>

実行！


```
(venv) hmasayuk@:codes$ python3 Amazon_Rekognition.py
S3にアップロード中
ラベル検出中

Label: Toy
Confidence: 92.86068725585938
Instances:
  Bounding box
    Top: 0.03305918350815773
    Left: 0.2504766285419464
    Width: 0.6258938908576965
    Height: 0.9372327327728271
  Confidence: 56.228912353515625

Parents:
----------

Label: Figurine
Confidence: 57.18489456176758
Instances:
Parents:
----------

Label: Plush
Confidence: 55.58042526245117
Instances:
Parents:
   Toy
----------

3個ラベルがありました。
(venv) hmasayuk@:codes$
```

良い感じに出力されました。

TOY の要素が92％あって、フィギュアやぬいぐるみも要素にありますね。
`Label: Toy`
`Confidence: 92.86068725585938`

# 確認
`Amazon S3`を確認しましょう。
無事にアップロードされてますね。
ちなみに、もう一度コードを実行して同じ名前でアップロードすると、ファイルが上書きされます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/7c9c63b9-877d-2a0e-5d10-20257805af7f.png" width="500">
`Amazon Rekognition`でどんな結果がでるのかブラウザから手動アップロードして確認してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1730261/84521462-f967-62a9-715f-fb2298283389.png" width="500">
Python実行時と出力は一緒ですね。よかった。
こっちは熊の人形範囲が数値じゃなくて線で表示されてますね。


# まとめ
初めてプログラミングの記事を書いたけど手ごわかった。
何をやってるのかわからない行や、無駄な行を調べながら書いていたので時間がかなりかかってしまった。
次は誰かの美しいコードやエラー処理方法を見て真似してから書きたいですね。

また、今回はラベル検出を試してみたが他にも分析方法があるのが分かった。
冒頭で話した「〇〇会社の人間」の分析・判定には少し近づいたと思います。
サービスを使いすぎると課金されてしまうので気をつけてくださいね。