[Swift] AWS Amplify Rekognitionを使って顔認識させる

（割り込みアドベントしましたm(_ _ )m.）

# 前置き

以前、GoogleのFirebaseを使用して
「[[Swift]Firebase MLKitを使って動画の顔認識をさせる](https://qiita.com/H_Crane/items/337c94ff506f5a10ede8)」
のようなことを行ってみました。

今回はAWSで提供されている「**AWS Rekognition**」を使用して、同じようなことができるのでは、と思い試してみました。

## AWS Rekognitionとは？

公式サイトのデモから

![スクリーンショット 2020-03-08 16.36.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/9ca9a61f-3333-d769-3218-bbb0e8c78ca2.png "公式サイトのデモ画像")

> Amazon Rekognition では、機械学習の専門知識を必要とせずに、実績のある高度にスケーラブルなっ深層学習テクノロジーを使用して、アプリケーションに画像およびビデオ分析を簡単に追加できるようになります。
> Amazon Rekognition を使用すると、画像と動画の物体、人物、テキスト、シーン、活動を特定し、不適切なコンテンツを検出できます。Amazon Rekognition は、非常に正確な顔分析および顔検索機能も備えています。これを使用して、さまざまなユーザー検証、人数のカウント、および公共安全のユースケースで顔を検出、分析、比較できます。

とあるように、画像に対してAmazonが保有する学習データを簡単に使える機能になります。


# 導入

２つの方法で実装が可能です。
（１） **AWS Amplify**を使って設定を簡単に行う
（２） デフォルトのSDKを使う（自分でAWSを設定していく）

AWSを実際使うとなると**設定周りのハードルが高く**なってしまいます。
アプリで使用するにしても、以下のような認証部分の設定は最低限必要になってきます。

![cognito-diagram-800w-opt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/310cc1fd-099c-c07e-518c-ae7945f1e2cb.jpeg)

AWS Amplifyではその部分を**まるっとCLIで行ってくれる**ため、アプリ開発者はその部分に注力しなくて済むのです。
今回はせっかくなので、どちらの方法でも実装方法を説明していきたいと思います。

## 前提

### AWSのアカウント作成

まずは前提としてアカウントが必要になるので、[公式](https://aws.amazon.com/jp/)から作成しましょう。

![スクリーンショット 2020-03-07 20.53.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/7cd9cb58-4a03-19ee-63ff-a004974a71ea.png "アカウント作成流れの図")

作成自体は流れに沿って入力していけば完了します。
（無料で使用できる期間はありますが、登録にあたりクレジットカード登録は必須なので用意しておきましょう）

## （１） AWS Amplifyで実装する


![Screen-Shot-2019-09-30-at-10.40.56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/640483b6-4b94-31e7-b96e-27e5b1ea723b.jpeg)

コマンドラインツールを使うことで、AWSの管理画面側をほとんど触らずに済ますことができます。
以下で体験してみましょう。

### 1. AWSの設定

#### ① Amplifyの準備

必要なものをインストールします。以下のサイトを参考にすると良いでしょう。
- [AWS Amplify CLIの使い方〜インストールから初期セットアップまで〜](https://qiita.com/Junpei_Takagi/items/f2bc567761880471fd54)

#### ② 認証機能の準備

アプリで使用可能にするため、認証機能を追加します。

```
$ amplify add auth
```

terminalでの質問は、以下の選択肢を選べば大丈夫です。
![スクリーンショット 2020-03-09 0.46.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/c5e74702-7123-ec61-4429-c29437373cfc.png)

#### ③ Rekognitionの準備

Rekognitionの機能を追加します。

```
$ amplify add predictions
```

terminalでの質問は、以下の選択肢を選べば大丈夫です。
![スクリーンショット 2020-03-09 0.50.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/9c99214b-fa37-0c9f-a173-92838e04eee7.png)

今回は特定の機能（Identity Entities）を使用する前提で進めています。
**それ以外の機能を使用したい場合は、こちらで別のものを選択してください。**
選択したものしか使用許可が出ないため、アプリ上でAPIを叩いてもエラーが返ってきます。

#### ④ 設定の反映

AWS側に設定を反映します。

```
$ amplify add auth
```

また、生成されている``amplifyconfiguration.json``をXcodeのプロジェクトに追加しておきましょう。
（場所はどこでも良く、Xcode上にリファレンスがついている必要があります。）

これで準備は完了です。

### 2. アプリ側の実装

#### ① Podの準備

適当なプロジェクト作成し、CocoaPodsを導入しましょう。

```
  pod 'Amplify'
  pod 'AmplifyPlugins'
  pod 'AWSPluginsCore'
  pod 'CoreMLPredictionsPlugin'
  pod 'AWSPredictionsPlugin'
  pod 'AWSMobileClient', '~> 2.12.2'
```

バージョン指定をしています。
現状では``AWSPredictionsPlugin``が最新のものに対応しておらずで、エラーが出てしまうためです。

#### ② AWSのセットアップ

起動した時にAWSのセットアップを行います。

```.swift
import Amplify
import AWSMobileClient
import AWSPredictionsPlugin

@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let apiPlugin = AWSPredictionsPlugin()
        do {
            try Amplify.add(plugin: apiPlugin)
            try Amplify.configure()
        } catch {
            print("Failed to configure Amplify \(error)")
        }

        return true
}
```

問題がある場合は起動時にコンソールでエラーが出てきます。
（jsonが読み込めていない場合など）

#### ③ 「顔認識」をしてみる

先ほどの設定で``Identity Entities``を指定したので、それを呼び出してみます。

```.swift
import Amplify
```

```.swift
guard let path = Bundle.main.path(forResource: name, ofType: "jpg") else { return }
let url = URL(fileURLWithPath: path)
        
_ = Amplify.Predictions.identify(type: .detectEntities, image: url, listener: { [weak self] event in
    switch event {
    case .completed(let result):
        debugPrint("----completed----", "\(result)")

    case .failed(let error):
        debugPrint("----failed----", error.errorDescription)

    case .notInProcess, .inProcess, .unknown:
        debugPrint("----other----", event.description)
    }
})
```

プロジェクト内にあるjpgファイルを読み込み使用しています。

<img width="837" alt="スクリーンショット 2020-03-09 2.29.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/30e54885-04e3-d257-2276-1d4988e6806b.png">

うまくいけば、省略していますが、顔から推察されるデータがこのように返却されてきます。
コード自体は数行で済みとても簡単でしたね！


## （２） デフォルトのSDKを使って実装する

アプリをスケールさせるとなった時には、やはり細かい設定を触れた方がメリットも大きいです。
それでは、管理画面を触りながら実装していく方法をみていきましょう。

### 1. AWSの設定

#### ① IDプールの作成

AWSサービスへの**アクセスを許可する**ために、任意のIDを発行（作成）します。
（このIDは、あとでアプリ側で設定することになるものです。）

![edi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/7dd9380f-7f13-7af9-3b5c-b040e84f208f.png "IDプールの作成流れの図")


① [ホーム画面](https://us-east-2.console.aws.amazon.com/console/home)の検索窓で「**Cognito**」と検索し選択します
② 「**IDプール管理**」を選択します
③ 適当に「プール名」を付け、チェックボックスにチェックを入れ、「プールを作成」を押します※
④ 特に何もせず「許可」を押します

完了すると、セットアップコードとともに自分のIDが表示されているはずです。※
（以下はiOSの場合）

![スクリーンショット 2020-03-07 13.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/d4efb0a1-2f7a-5781-5341-07c738371d46.png)

他の画面でも閲覧可能ですが、探すのが面倒になるので一旦こちらで入手できる**IDを控えておきましょう**。


※
AWS Cognitoを使用することで、サインアップ・サインインの機能を実装できます。
今回はその機能は実装せずに**Rekognitionの機能だけを使用したい**ので、チェックを入れることで回避します。

※
ドキュメントのコードが少し古いので、そのまま使用することはできないので注意が必要です。

#### ② Rekognitionを使用する権限を与える

先ほど作成したIDに、RekognitionのIAMポリシー（権限）をアタッチ（付与）していきます。※
（IDとは管理画面が違うので注意してください。）

![スクリーンショット 2020-03-07 21.46.16 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/26345cdd-2777-9a5a-0c13-7838c68546ba.png)


① [IAMポリシーコンソール](https://console.aws.amazon.com/iam/home)から「ポリシー」を選択します
② 検索窓から「**AmazonRekognitionReadOnly**」を絞り込みます。
③ 絞り込んだものにチェックをつけ、「ポリシーアクション」から「アタッチ」を選択します
④ 「Cognito_xxx **Unauth_Role**」にチェックをつけ、「ポリシーのアタッチ」を押します（xxxは作成したプール名が入る）

AWS側の設定はこれで完了です。

※
AWSでは各機能のアクセス権のことをポリシーという名称で表しており、それをIAMで管理します。
（IAM = Identity and Access Management）

- [IAM入門～ロールとグループとポリシーの違い,設計・設定手順について](https://milestone-of-se.nesuke.com/sv-advanced/aws/iam-policy-role-group/)
- [AWSのIAMロールとポリシーの違い](https://qiita.com/montama/items/90bb8a3973d101be4690)

### 2. アプリ側の実装

#### ① Podの準備

適当なプロジェクト作成し、[CocoaPodsを導入](https://qiita.com/ShinokiRyosei/items/3090290cb72434852460)しましょう。

```Podfile
pod 'AWSRekognition'
```

AWS Rekognitionを使用するために、Podfileに記載してインストールします。

#### ② AWSのセットアップ

起動した時にAWSのセットアップを行います。

```AppDelegate.swift
import UIKit
import AWSCore

@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let credentialsProvider = AWSCognitoCredentialsProvider(
            regionType: .USEast2,
            identityPoolId: "xxxxxx" // set your pool id
        )
        let configuration = AWSServiceConfiguration(
            region: .USEast2,
            credentialsProvider: credentialsProvider
        )
        AWSServiceManager.default().defaultServiceConfiguration = configuration
        
        return true
    }
}
```

（regionは人によって違う可能性があります）

**identityPoolId**は各自が作成したものをセットしてください。
先ほどは、CIが設定してくれたものを自前で行っています。


#### ③ 「顔認識」をしてみる

##### （１） 「顔認識」をしてみる

プロジェクト上にある画像をImage Literalでおいて読み込んでいます。
（先ほどと同じくBundleを使用しても可能です。ただし、変換が必要になります。）

```.swift
import AWSRekognition
```

```.swift
private var rekognitionObject: AWSRekognition?

private func detectFaces(with image: UIImage) {
    let image: UIImage = /* image literal */

    let rekognitionImage = AWSRekognitionImage()
    rekognitionImage?.bytes = image.jpegData(compressionQuality: 0.5)
    
    guard let frequest = facesRequest else { return }
    
    rekognitionObject = AWSRekognition.default()
    rekognitionObject?.detectFaces(frequest) { response, error in
        if let r = response {
            debugPrint("----success----", r)
            
        } else if let e = error {
            debugPrint("----failure----", e)
            
        }
    }
}
```

このあたりは、先ほどのAmplifyとはメソッド名が少し変わりましたが、大体同じような仕様になっています。

##### （２） 「有名人の認識」をしてみる

せっかくなので、先ほどとは違う機能を試してみます。

```.swift
import AWSRekognition
```

```.swift
private var rekognitionObject: AWSRekognition?

private func recognizeCelebrities() {
    // 画像のリクエスト作成
    let image: UIImage = /* image literal */

    let rekognitionImage = AWSRekognitionImage()
    rekognitionImage?.bytes = image.jpegData(compressionQuality: 0.5)

    let rekognitionRequest = AWSRekognitionRecognizeCelebritiesRequest()
    rekognitionRequest?.image = rekognitionImage
    
    guard let request = rekognitionRequest else { return }
    
    // リクエスト
    rekognitionObject = AWSRekognition.default()
    rekognitionObject?.recognizeCelebrities(request) { result, error in
        if let r = result {
            debugPrint("----success----", r)
        
        } else if let e = error {
            debugPrint("----failure----", e)
        
        }
    }
}
```

試しにドナルド・トランプの画像を使用してみたところ

<img width="1805" alt="co.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/4ccc7724-04c5-239b-cd7e-ec93fd1e2542.png">


ちゃんと**画像が判定されている**ことがわかります。

AWSの設定がうまくいってないと、以下のようなエラーが返ってきます。

```
Error Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=10 "(null)" 
UserInfo={__type=ResourceNotFoundException, message=IdentityPool 'xxxxxx’ not found.}
```
（この場合は権限が設定されていないというエラー）

# あとがき

公式のリファレンスではIAMについての記述が少しわかりずらく、管理画面を操作するのに若干苦労しましたが、、
firebaseと比べると、サーバーとの通信が走る分１秒ほど体感速度は遅いようにも感じます。

普通に設定すると面倒だなぁ、、
とは思いつつも、**アプリをスケールさせる前提**で機能を使うにはとても良いような気がします。
AWS Amplifyを使用することでサクっとアプリを開発。
大きくなったらちゃんと設定運用するといった手法が取れるのは、firebaseよりもメリットなような気はします。

撮影している動画の顔をリアルタイムに行えるかどうか、の検証は時間があれば行いたいと思いますが、、
おそらく細切れにした画像をいくつもリクエストすることになり、無料枠がすぐに埋まってしまいそうな気がしている、、笑


# その他

- [動作するリポジトリ](https://github.com/crane-hiromu/AWS_Rekognition_Swift/tree/master)

## 参考

- [Amazon Rekognition Celebrity Detection Ios](https://github.com/aws-samples/amazon-rekognition-celebrity-detection-ios/blob/starter-app/README.md)
- [Using Amazon Rekognition to detect celebrities in an iOS app](https://aws.amazon.com/jp/blogs/mobile/amazon-rekognition-detects-celebrities-in-ios-app/)
- [Amplify Framework Predictions](https://aws-amplify.github.io/docs/ios/predictions)
- [Amazon Cognito とは](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/what-is-amazon-cognito.html)