【AWS】S3+Lambda+Rekognitionで画像認識してみた

# はじめに

AWSの「Amazon Rekognition」を使って画像認識を試してみました。
以下の人物画像から顔を検出し、性別と微笑みの有無を判別しています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/487c00ca-c8a6-b804-f8f3-fe38a15e33c3.jpeg" width="300" />

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/743e48d3-ac38-e104-5524-ebc2f3eb7e8e.jpeg" width="300" />

なお、用いた画像は写真のフリー素材サイト「photoAC」からダウンロードしました。

https://www.photo-ac.com/

# 利用するAWSのサービス

* Lambda 
プログラムを実行するための環境です。今回はNode.jsを使用しています。
* Amazon S3
認識させたい画像を保存するためのオンラインストレージです。
* Amazon Rekognition
機械学習の専門知識を持っていなくても簡単に扱うことができる画像／動画分析サービスです。
* IAM
LambdaからS3とRekognitionにアクセスする権限を付与するために使用します。
* CloudWatch
Lambdaで実行した結果が出力されたログを確認するために使用します。

# 処理フロー

1. S3のコンソールから画像アップロード
2. 画像アップロードをトリガーとしてLambda関数を実行
3. S3に保存された画像データをRekognitionで画像解析

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/c17fd7eb-febf-021d-9f91-702570cb4c9e.jpeg" width="700" />

# 構築手順
※2023年4月末時点のAWSコンソールで作成しています

## 1. Lambdaで関数を作成する
Lambdaで「関数の作成」を開いて、新たな関数を作成します。
関数名は任意で構いません。ランタイムは「Node.js」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/a736d983-2acc-6bac-aa0e-d4eb5c2216b2.png" width="800" />

また今回は事前にIAMでロールを作成していないので、新たに実行ロールを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/96da86e4-e84c-9176-30a8-cc7eb3e1043b.png" width="800" />

## 2. IAMで権限を付与する

Lambdaで関数の作成が終わったら、IAMで権限の付与を行います。
先ほど新たに作成したロールがIAMに追加されていますので、そちらのロールを変更していきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/ee02ad4d-1c50-10c5-6255-7341d390af81.png" width="800" />

許可ポリシーの一覧を確認すると、Lambdaの権限が付与されていることが分かります。
今回はS3とRekognitionの権限が必要なので「ポリシーをアタッチ」します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/de0cd793-63ef-a292-a73f-36a002090dd5.png" width="800" />

まずは、S3の読み取り権限「AmazonS3ReadOnlyAccess」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/176daab4-f158-8040-2651-c1ccf2e10184.png" width="800" />

次に、Rekognitionの読み取り権限「AmazonRekognitionReadOnlyAccess」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/3c5c28d0-4424-48e7-5379-8a88f5900870.png" width="800" />

どちらも追加できたら、以下のような表示になります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/8276a046-6c7a-ca43-0da0-36faa3957c9d.png" width="800" />


## 3. S3にバケットを作成してトリガーを設定する

まず、認識させる画像を保存するためのバケットを作成します。
バケット名は任意で構いません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/091a0dba-7860-9788-66f6-f4142084d896.png" width="600" />

次にLambdaで関数のトリガーを設定します。
トリガーの設定を開いて、先ほど作成したバケットを選択します。
また今回は、画像がアップロードされたタイミングで実行したいので、イベントタイプは「すべてのオブジェクト作成イベント」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/dc827adc-4432-dcf2-5029-5128964f303e.png" width="800" />

## 4. Lambdaでレイヤーを作成する

Lambda関数（Node.js）からRekognitionを操作するため「@aws-sdk/client-rekognition」が必要となります。
今回はレイヤーを作成して関数に追加します。

自PCのターミナルで以下のコマンドを実行して、ライブラリがインストールされたフォルダを作成します。
ちなみにOSはWindows11で実行しています。

```powershell
mkdir aws-sdk
cd aws-sdk
npm install @aws-sdk/client-rekognition
```

インストールされたフォルダをzip形式に圧縮したら、再びAWSコンソールを開いてレイヤーの作成を行います。
レイヤーの名前は任意で構いません。作成したzipファイルをアップロードします。
また、互換性のあるアーキテクチャおよびランタイムは関数を作成する際に選択した値を入力します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/d49246b1-bcd1-d94c-f44a-7416d710b983.png" width="700" />

レイヤーの作成が完了したら関数にレイヤーを追加します。
関数の画面からレイヤーの追加を開いて、先ほど作成したレイヤーを選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/99fe4263-bb60-d236-a1cc-8d912fa9ed89.png" width="700" />


## 5. Lambdaでプログラムを記述する

Lambda関数で実行するプログラムを記述します。
実行内容は以下の通りです。
1. アップロードされたS3のオブジェクト情報を取得する
2. RekognitionのDetectFaces関数を用いて画像内の顔を検出する
3. 顔の情報から性別および微笑みの有無を取得してログに出力する

```javascript
import { RekognitionClient, DetectFacesCommand } from "@aws-sdk/client-rekognition";

const rekognitionClient = new RekognitionClient({});

export const handler = async (event) => {
    try {
        const bucket = event.Records[0].s3.bucket.name;
        const key = event.Records[0].s3.object.key;
        const s3Object = {
            Bucket: bucket,
            Name: decodeURIComponent(key.replace(/\+/g, " ")),
        };
        const rekognitionResponse = await rekognitionClient.send(
            new DetectFacesCommand({
                Image: { S3Object: s3Object },
                Attributes: ["ALL"],
            })
        );
        rekognitionResponse.FaceDetails.forEach((faceDetail) => {
            const gender = faceDetail.Gender.Value === "Male" ? "男性" : "女性";
            if (faceDetail.Smile.Value === true) {
                console.log(`${gender}は微笑んでいます`);
            } else {
                console.log(`${gender}は微笑んでいません`);
            }
        });
    } catch (err) {
        console.log("Error", err);
    }
};
```

プログラムの記述が終わったら「Deploy」ボタンをクリックします。
これでプログラムの実行準備が整いました。

# 実行結果

それでは実際にS3に画像をアップロードして、実行結果を確認してみます。
まずはこちらの男性が微笑んでいる画像をS3のバケットにアップロードします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/743e48d3-ac38-e104-5524-ebc2f3eb7e8e.jpeg" width="300" />

アップロードしてしばらくするとCloudWatchにログが出力されます。
作成したLambda関数のロググループに出力されているのでそちらを確認します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/d0f80ca1-28bd-4376-3cd8-3726add9e0c7.png" width="700" />

ログを確認してみると「男性は微笑んでいます」と出力されています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/8daf8c76-a9e1-8021-e2f2-ae6623be41b8.png" width="800" />

次にこちらの男性が微笑んでいない画像を同様にアップロードします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/487c00ca-c8a6-b804-f8f3-fe38a15e33c3.jpeg" width="300" />

ログを確認してみると「男性は微笑んでいません」と出力されています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/10b805e8-d430-8eda-4117-f53f7e620e34.png" width="800" />

# おわりに

なるべくシンプルな構成でRekognitionの画像認識を試してみました。
今回はRekognitionの顔検知機能を使用しましたが、まだまだ他の機能があるようです。
以下のサイトに呼び出し可能なAPIが記載されています。

https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/API_Reference.html

今後は使用しなかった他の機能を用いて、色々と試していきたいと考えています。