AWS Amplify iOSで画像から顔の検出と分析を行う #reinvent

RekognitionとCoreMLを併用し、画像から顔を検出しよう

re:Invent 2019の期間中、ネイティブアプリ用の新しいSDKAmplify iOSAmplify Androidがプレビューにて公開されました。

その中で、iOSでは Prediction(予測機能) について CoreMLフレームワークとの組み合わせ が実装されています。ML/AI系の機能を、AWSサービスとiOS Frameworkを組み合わせ、非常に高い精度の結果セットが得られるようになっています。

CoreMLフレームワークおよびCoreML VisionフレームワークはiOSの機械学習用のフレームワークで、学習モデルなどを扱う際に開発者が専門的な知識を必要とせずに扱えるように補助するフレームワークです。CPU、GPU、ニューラルエンジンを活用し、予測の作成、モデルのトレーニングや微調整をすべてユーザーのデバイス上で行うようになっています。

本記事では、Amplify iOSを使って画像から顔を検出する機能を試してみました。

AWSサービスとしてはAmazon Rekognitionを利用する形となります。Amplify iOSを通して使うことで、ローカル(オフライン)での実行も可能になります。

インストール

それではまずはインストールしていきます。なお、AmplifyのiOSアプリ向けプロジェクトはすでに作成済みの前提で進めます。また、今回は対話形式でのインストールが必要なためAmplify CLIを利用します。

まずは predictions というプラグインを追加します。

$ amplify add predictions

対話形式で、どのような機能を作りたいか問われます。

? Please select from one of the categories below : Identify
? What would you like to identify? : Identify Entities
? Provide a friendly name for your resource : identifyEntitiesec505499
? Would you like use the default configuration? : Default Configuration
? Who should have access? : Auth and Guest users

Prediction機能は以下のように分類されているので、自分の作りたい機能に合わせて設定します。本記事では Identify を選択しています。

? Please select from one of the categories below
❯ Identify
  Convert
  Interpret
  Infer
  Learn More

Identify を選択すると ? What would you like to identify? と、どのような種類の解釈を行いたいのか問われます。今回は Identify Labels としました。

? What would you like to identify?
  Identify Text
❯ Identify Entities
  Identify Labels

また、最後の Who should have access?Auth and Guest users を選び、Cognito User Poolsの作成を行うようにします。これはAWSリソースへのアクセスを行うため必須になります。

Successfully added resource identifyEntitiesec505499 locally

ローカルでの設定が完了したので amplify push でAWSリソースを作成します。

$ amplify push

| Category    | Resource name            | Operation | Provider plugin   |
| ----------- | ------------------------ | --------- | ----------------- |
| Predictions | identifyEntitiesec505499 | Create    | awscloudformation |
| Api         | amplifyDatasource        | No Change | awscloudformation |
| Auth        | amplifysample93d65ae8    | No Change | awscloudformation |

以上でAWSリソースの作成は完了です。

次に AWSPredictionsPlugin というPodを追加します。Podfile 全体としては以下のようになります。

target 'AmplifySample' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for AmplifySample
  pod 'amplify-tools'
  pod 'Amplify'
  pod 'AWSPluginsCore'
  pod 'AWSPredictionsPlugin'
  pod 'AWSMobileClient', '~> 2.12.0'
  pod 'AmplifyPlugins/AWSAPIPlugin'
end

最後に pod install を実行して終わりです。

$ pod install --repo-update

実装

今回は画像からオブジェクトの検出を行いたいだけなので、iOSアプリでは画面なしで実装します。

まずは AppDelegate でセットアップする処理を書きます。

import UIKit
import Amplify
import AWSPredictionsPlugin
import AmplifyPlugins

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let apiPlugin = AWSAPIPlugin(modelRegistration: AmplifyModels())
        let predictionsPlugin = AWSPredictionsPlugin()
        do {
            try Amplify.add(plugin: apiPlugin)
            try Amplify.add(plugin: predictionsPlugin)
            try Amplify.configure()
            print("Amplify initialized")
        } catch {
            print("Failed to configure Amplify \(error)")
        }
        return true
    }

}

次に ViewController で画像からオブジェクトの検出を行う処理を実装します。detectEntities() というメソッドを用意しました。

import UIKit
import Amplify

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        detectEntities()
    }
    
    func detectEntities() {
        guard let path = Bundle.main.path(forResource: "sample-image-entity", ofType: "jpg") else { return }
        let image = URL(fileURLWithPath: path)
        let options = PredictionsIdentifyRequest.Options(defaultNetworkPolicy: .auto, uploadToRemote: false)
        _ = Amplify.Predictions.identify(type: .detectEntities, image: image, options: options, listener: { (event) in
            switch event {
            case .completed(let result):
                let data = result as! IdentifyEntityMatchesResult
                print(data)
            case .failed(let error):
                print(error)
            default:
                print("")
            }
        })
    }

}

対象の画像は URL 型で指定します。ローカルまたはWeb上の画像から指定します。上記は sample-image-entity.jpg という画像ファイルをXcodeプロジェクト内に追加した場合のコードです。オフラインでの動作を試したい場合はXcodeプロジェクトに追加するようにしてください。

試してみる

それでは実行してみましょう。写真は以下のような写真を使いました。Pexels の Bernard Lee さんによる写真をいただきました。御礼申し上げます。

コンソールログに以下のように出力されるかと思います(見やすいようにインデントなどを整形しています)。landmarks には検出した顔の各パーツの種類別の座標情報が含まれています。Amplify.LandmarkType.leftEye や 、 Amplify.LandmarkType.noseAmplify.LandmarkType.outerLips といった種類の座標が取得できていることが分かります。

IdentifyEntitiesResult(
  entities: [
    Amplify.Entity(
      boundingBox: (0.4098570942878723, 0.5645298361778259, 0.18871554732322693, 0.2830733060836792), 
      landmarks: [
        Amplify.Landmark(
          type: Amplify.LandmarkType.allPoints, 
          points: [
            (0.17537660896778107, 0.6361929178237915), 
            (0.3476901352405548, 0.6726177334785461), 
            (0.22914546728134155, 0.6293135285377502),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.faceContour, 
          points: [
            (0.8277578353881836, 0.7898500561714172), 
            (0.8541383147239685, 0.6917181015014648), 
            (0.8671514391899109, 0.5894176959991455),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.leftEye, 
          points: [
            (0.17537660896778107, 0.6361929178237915), 
            (0.21634075045585632, 0.6784913539886475), 
            (0.29351523518562317, 0.6946832537651062),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.rightEye, 
          points: [
            (0.7177664041519165, 0.774705708026886), 
            (0.6674187779426575, 0.7944667935371399), 
            (0.5984203815460205, 0.7718154788017273),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.leftEyebrow, 
          points: [
            (0.053776077926158905, 0.7238196730613708), 
            (0.16826969385147095, 0.8264898061752319), 
            (0.3205413520336151, 0.852995753288269), 
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.rightEyebrow, 
          points: [
            (0.7612065672874451, 0.9083396792411804), 
            (0.6390267610549927, 0.9404084086418152), 
            (0.5183915495872498, 0.9013070464134216),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.nose, 
          points: [
            (0.48520657420158386, 0.6750242710113525), 
            (0.42751747369766235, 0.5210459232330322), 
            (0.45271211862564087, 0.43232858180999756), 
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.noseCrest, 
          points: [
            (0.4623622000217438, 0.7444325685501099), 
            (0.48520657420158386, 0.6750242710113525), 
            (0.5094031691551208, 0.60334312915802), 
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.medianLine, 
          points: [
            (0.4623622000217438, 0.7444325685501099), 
            (0.48520657420158386, 0.6750242710113525), 
            (0.5094031691551208, 0.60334312915802), 
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.outerLips, 
          points: [
            (0.4402713179588318, 0.3170118033885956), 
            (0.48036128282546997, 0.3520199656486511), 
            (0.5234674215316772, 0.37910303473472595),
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.innerLips, 
          points: [
            (0.5035750865936279, 0.32198426127433777), 
            (0.5711224675178528, 0.3403124213218689), 
            (0.6263250708580017, 0.35332754254341125), 
            【…以下略】
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.leftPupil, 
          points: [
            (0.25228697061538696, 0.6655845046043396)
          ]), 
        Amplify.Landmark(
          type: Amplify.LandmarkType.rightPupil, 
          points: [
            (0.638219952583313, 0.7655141949653625)
          ])
      ], 
      ageRange: nil, 
      attributes: nil, 
      gender: nil, 
      metadata: Amplify.EntityMetadata(
        confidence: 1.0, 
        pose: Amplify.Pose(pitch: 0.0, roll: 0.0, yaw: 0.0)
      ), 
      emotions: nil
    )
  ]
)

明示的にオフラインで使いたい場合は、実行する際のオプション設定を変更します。

let options = PredictionsIdentifyRequest.Options(defaultNetworkPolicy: .offline, uploadToRemote: false)

なおAmazon Rekognition Collectionを併用することも可能です。事前に顔情報(Collection)を登録しておき collectionIdamplifyconfiguration.json にセットすることで、特定の顔を画像から検索することができます。

画像からスピーディに顔検出する仕組みを作ろう

顔の検出・分析機能は、画像ビューアアプリやカメラアプリなど、写真を取り扱うアプリに非常に有用な機能になると思います。本記事で紹介した機能を活用したスマートなアプリを作ってみたいですね!