Skip to the content.

ステップ2. 顔認証のWeb APIを作成する(スマートフォンとAWSサービスを用いた画像認識サービスの構築する)

当コンテンツは、エッジデバイスとしてスマートフォン、クラウドサービスとしてAWSを利用し、エッジデバイスとクラウド間とのデータ連携とAWSサービスを利用した画像認識を体験し、IoT/画像認識システムの基礎的な技術の習得を目指す方向けのハンズオン(体験学習)コンテンツ「スマートフォンとAWSサービスを用いた画像認識サービスの構築する」の一部です。

ステップ2. 顔認証のWeb APIを作成する

ステップ2アーキテクチャ図

ステップ2では、アップロードされた画像を分析し、「事前登録済みの人物が写っているか、写っている場合は誰かを判定する」機能を持ったWeb APIを作成し、デバイス側からAPIを呼び出す仕組みを構築します。

まずは、「Amazon Rekognition(以下、Rekognition)」を利用して「コレクション」を作成し、認識対象となる顔を登録します。 次に、作成したコレクションを使って顔の認識を行う「AWS Lambda(以下、Lambda)」を作成します。 さいごに、作成したLambdaにHTTPでアクセスするために「Amazon API Gateway(以下、API Gateway)」を作成します。


目的

概要


<Web APIとは?>

HTTPプロトコルを利用してインターネットを介したアプリケーション間のやりとりを行うためのインターフェースです。 Web APIの代表的な実装方式として、RESTとSOAPが存在しており、今回作成するWeb APIはREST APIとなります。

様々な公開Web APIが提供されていることや、わかりやすいデータのやり取りから、Web APIを利用してモノリシックなシステムをマイクロサービス化することが現在の潮流となっています。

<AWS Lambdaとは?>

AWS Lambdaは、サーバーをプロビジョニングしたり管理する必要なくコードを実行できるサーバーレスコンピューティングサービスです。これは他のAWSサービスをトリガーとして実行することができ、実際に処理にかかった時間やリクエスト数に対して従量課金されます。Lambda関数はJavaやPythonをはじめとした一般的なプログラミング言語で作成することができます。

より詳しく知りたい場合は公式サイトをご確認ください。

<Amazon API Gatewayとは?>

Amazon API Gatewayは、開発者があらゆる規模でAPIの公開、保守、モニタリング、セキュリティ保護、運用を簡単に行えるフルマネージドサービスです。 セキュアで信頼性の高いAPIを大規模に稼働させるために、差別化にはつながらない面倒な作業を処理する従量制のサービスです。 AWS、または他のウェブサービス、AWSクラウドに保存されているデータにアクセスするAPIを作成できます。ユースケースとしては、モバイルアプリやWebアプリケーションのバックエンドAPIとして、API GatewayとLambdaを連携させて、サーバレスなREST API環境を構築するケースでよく使われます。

より詳しく知りたい場合は公式サイトをご確認ください。

<APIキー認証とは?>

API Gatewayがサポートする認証方式の1つで、所定のキー(文字列)をHTTPヘッダ(x-api-keyヘッダ)に含めたリクエストであればアクセスを許可、無ければアクセスを拒否する機能です。APIキーの発行・管理は、API Gateway側で行われます。 また、ユーザごとにAPIキーを発行し、ユーザごとに呼び出し回数に制限をかけるというような使用法も可能です。 ただし、APIキーはヘッダー解析などをされることで漏洩する可能性がありますので、認証の唯一の手段としてAPIキーを使用することはベストプラクティスではありません。 認証機能が必要な場合は、IAMロール、Lambdaオーソライザー、または Amazon Cognitoユーザープールを使用するようにしてください。 https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-api-usage-plans.html

2-1. Lambdaを作成

ここでは、スマホからAPI Gatewayを通して受け取った顔画像が、ステップ1で作成したRekognitionのコレクションに登録済みの人物とマッチするかどうか、マッチする場合は誰であるかリターン値として返すLambdaファンクションを作成します。

2-1-1. Lambda関数を作成する

「基本的なLambdaアクセス権限」を指定することで、Lambda実行時のログをCloudWatch Logsにアップロードするためのロールが自動的に付与されます。 次のステップで、Lambdaのソースコードから使用するAWSサービスに対する必要な権限をカスタムで追加します。

2-1-2. Lambdaに必要な権限を付与する

2-1-2_4 2-1-2_5

2-1-3. Pythonの関数コードを作成する

Lambdaが実行するPythonコードを作成します。

  # Rekognitionで作成したコレクション名を入れてください
  COLLECTION_ID = '{collection_id}'
  # Rekognitionで作成したコレクション名を入れてください
  COLLECTION_ID = 'yamada-authentication-collection'

作成プログラムの解説

  import json                      # JSONフォーマットのデータを扱うために利用します
  import boto3                     # AWSをPythonから操作するためのSDKのライブラリです
  from typing import Union, Tuple  # 変数や引数・返り値などの型定義に利用します
  from datetime import datetime    # この関数を動かした(=顔の認証を行なった)時間をtimestampとして作成するのに利用します
  import botocore.exceptions       # boto3の実行時に発生する例外クラスです
  import base64                    # base64フォーマットの画像データをRekognitionが認識できる形に変換するのに利用します
  # 画像データから不要な値を抜き出す(コンマがない場合は不要な値がないとし、全文字列を利用)
  image_base64str = image_base64str[image_base64str.find(',') + 1 :]
  return base64.b64decode(image_base64str)
  # RekognitionのClientを生成
  REKOGNITION_CLIENT = boto3.client('rekognition')
  # 画像とマッチする人物を特定する
  search_result = REKOGNITION_CLIENT.search_faces_by_image(
      CollectionId=self.collection_id,
      Image={
            'Bytes': image_binary
      },
      MaxFaces=self.max_faces,
      FaceMatchThreshold=threshold
  )
  # タイムスタンプ生成
  date_now = datetime.now()
  # 辞書型の変数定義
  payloads: dict = {}
  # タイムスタンプを設定
  payloads['timestamp'] = str(date_now.strftime('%Y-%m-%d %H:%M:%S'))
  # search_faces_by_imageの戻り値を設定
  payloads.update(search_result)
  # レスポンス用のBodyを生成
  body = json.dumps({'msg': msg, 'payloads': payloads})

2-1-4. Lambdaのテストを実行する

Lambdaの関数コードを保存したら、API Gatewayから渡されてくる想定のイベントデータを用意し、Lambdaに渡して、実際の動きをテストしてみましょう。

2-1-4_2

2-2. API Gatewayを作成する

デバイス側から作成したLambdaを使用するために、API GatewayでREST APIを作成します。

2-2-1. APIを作成する

2-2-2. APIにリソースを追加する

作成直後のAPIには、具体的なアクセス先のリソースが存在していない状態です。 APIにリソースを追加し、ステップ2-2で作成したLambdaを呼び出すAPIリソースを作成します。


<リソースとは?>

Amazon API Gatewayでは、API Gatewayリソースと呼ばれるプログラム可能なエンティティのコレクションとして REST API を構築できます。各 Resource エンティティは、Method リソースを 1 つ以上持つことができます。リクエストパラメーターと本文で表された Method は、クライアントが公開された Resource にアクセスするためのアプリケーションプログラミングインターフェイスを定義し、クライアントによって送信された受信リクエストを表します。 https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-create-api.html

<CORSとは?>

Web APIは、元来パブリックに公開されず、リソースのオリジンが共有された一つのサービスの内部機能として利用されていました。そのことから、オリジンが異なるリソースからのアクセスを受けた際、デフォルトではアクセスを拒否してしまいます。CORSを設定することで、アクセス元のリソースを意識せずに利用することができるようになります。 https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-cors.html


2-2-3. APIにメソッドを追加する

APIにはHTTPメソッドが必要です。 ステップ2-1-3で作成したLambda実行コードに必要なデータをAPI経由で渡す必要があるため、レスポンスボディが使用できるPOSTにします。

2-2-4. APIの統合先をセットする

APIメソッドを設定したら、バックエンドのエンドポイントに統合する必要があります。 バックエンドのエンドポイントは、統合エンドポイントとも呼ばれ、Lambda関数、HTTPウェブページ、または AWSのサービスアクションとして使用できます。 今回は、前ステップで作成したLambdaと統合しましょう。

<Lambdaプロキシ統合とは?>

Amazon API Gateway Lambdaプロキシ統合は、単一のAPIメソッドのセットアップでAPIを構築するシンプル、強力、高速なメカニズムです。 Lambdaプロキシ統合は、クライアントが単一のLambda関数をバックエンドで呼び出すことを可能にします。 クライアントがAPIリクエストを送信すると、API Gatewayは、統合されたLambda関数にリクエストをそのまま渡します。 このリクエストデータには、リクエストヘッダー、クエリ文字列パラメータ、URLパス変数、ペイロード、および API設定データが含まれます。 バックエンドLambda関数では、受信リクエストデータを解析して、返すレスポンスを決定します。

詳細は公式ドキュメントをご確認ください。

2-2-5. APIへのアクセスにAPIキーを必要とする設定にする

APIを公開すると、エンドポイントURLを知っている人は誰でもアクセス可能となります。 今回作成するAPIには、簡易な認証機能としてAPIキーによる認証を追加しましょう。

<OPTIONSメソッドとは?>

OPTIONSメソッドは、リソース作成時にCORSを有効にすることで自動的に作成されます。 このメソッドは、リソースに対するアクセスの際に、必ず最初に経由されます。 これにより、受け取ったリクエストのオリジンをOPTIONSのものと置き換えることで、オリジンの違いによるアクセス拒否を回避しています。

2-2-6. APIをデプロイする

作成したAPIは、デプロイすることで外部からアクセスすることが可能となります。 作成したAPIをデプロイしてみましょう。

ステージは、APIを公開後も安定してAPIのエンドポイントを供給しながら開発を行う上で必須の機能です。

<API Gatewayのステージとは?>

ステージは、デプロイに対して名前を付けたAPIのスナップショットとなります。 API Gatewayでは、ステージごとに別の設定やステージごとの変数を定義することが出来ます。 また、APIにアクセスするエンドポイントURLにはステージ名が含まれますので、例えば「v1.0」「v2.0」というステージを用意して 過去バージョンを保証したデプロイや、ステージを利用したカナリアリリースが可能となります。 詳細は公式サイトをご確認ください。

2-2-7. APIの使用量プランとAPIキーを作成する

APIキーに対して使用量プランを設定しましょう。 以下の説明を参考に紐付けるAPIキーでのアクセスの頻度を想定して、使用量を設定してください。


<使用量プランとは?>

API Gatewayでは、使用量プランに紐付けたAPIキーの利用頻度を測定・制限することが可能です。 APIキーは使用できる回数分だけ「トークンバケット」に補給され、ここにある分だけがAPIへのアクセスに利用できます。 ページ内項目の「スロットリング」の「レート」は1秒ごとにトークンバケットに登録されるAPIキーを、「バースト」はバケットに入るトークンの最大数を表しています。 「クォータ」はある期間中にAPIキーが利用できる回数を示します。 この他にも様々な方法でAPIの利用を監視・制限できます。 詳細は公式ドキュメントをご確認ください。


2-2-8. cURLでAPIをテストする

APIGAtewayが正常に稼働することを確認するために、cURLの curlコマンドを利用して、前のステップで作成したAPIにアクセスしてみましょう。

今回のケースでは、画像のBase64形式データの文字数が多く、コマンドラインでの直接入力は大変なため、shellファイルを修正してコマンドラインから実行します。


<cURLとは?>

URLの書き方でファイルの送受信が行えるオープンソースのコマンドラインツールです。 WebサイトやAPIサーバーのポート疎通確認によく利用され、HTTP/HTTPSの他にも様々なプロトコルに対応しています。コマンドラインで curlコマンドとして利用します。

<Base64とは?>

Base64とは、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にてマルチバイト文字やバイナリデータを扱うためのエンコード方式です。 具体的には、A–Z, a–z, 0–9 までの62文字と、記号2つ (+, /)、さらにパディング(余った部分を詰める)のための記号として = が用いられる。


2-2-8-1. Shellファイルを編集する

curlコマンドの解説

echoコマンドについて

curlコマンドについて

APIのエンドポイントの確認方法

AWSのコンソールで、ステップ2-2-6でデプロイしたAPIのステージを選択し、さらにリソースを選択すると、「URLの呼び出し」でAPIのリソースパスが表示されます。

2-2-8-1_1

APIキーの確認方法

AWSのコンソールで、ステップ2-2-7で作成したAPIキーを[表示]します。

2-2-8-1_2

sh curl_authentication_test_format.sh
sh curl_authentication_test_format.sh
{"msg": "[SUCCEEDED]Rekognition done", "payloads": {"timestamp": "2019-08-19 00:40:17", 
"SearchedFaceBoundingBox": {"Width": 0.24594193696975708, "Height": 0.45506250858306885, 
"Left": 0.21459317207336426, "Top": 0.1758822500705719}, "SearchedFaceConfidence": 99.99996185302734, 
"FaceMatches": [{"Similarity": 97.72643280029297, "Face": {"FaceId": "xxxxxx-xxxx-xxxx-xxxx-xxxxxx", 
"BoundingBox": {"Width": 0.40926501154899597, "Height": 0.44643399119377136, "Left": 0.2812440097332001, 
"Top": 0.12307199835777283}, "ImageId": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxx", "ExternalImageId": "Taro_Yamada", 
"Confidence": 100.0}}], "FaceModelVersion": "4.0", "ResponseMetadata": {"RequestId": "xxxxx-xxxx-xxxx-xxxx-xxxxxx", 
"HTTPStatusCode": 200, "HTTPHeaders": {"content-type": "application/x-amz-json-1.1", "date": "Mon, 19 Aug 2019 00:40:16 GMT", 
"x-amzn-requestid": "xxxxxx-xxxx-xxxx-xxxx-xxxxxx", "content-length": "537", "connection": "keep-alive"}, 
"RetryAttempts": 0}}}

2-3. デバイスからプログラムでWeb APIを利用する

2-3-1. 顔認証を行うWeb APIにアクセスするプログラムを作成する

最後に、ステップ0で作成したWebアプリケーションから、前のステップで作成したAPIのリソースにアクセスするVue.jsのプログラムを作成します。

デプロイしたWebアプリケーションにはすでにAPIにアクセスするためのプログラムが組み込まれていますので、こちらの章ではコードを適宜引用しながら、APIにアクセスしている部分の解説を行います。

APIアクセス用のプログラムの解説

<template></template>タグ内の要素について

<script></script>タグ内の要素について

  import axios from 'axios';
APIへのアクセスに必要なパラメータを準備する

リクエストヘッダー情報

APIキーを、'x-api-key': 'xxxxxxxxxxxxxxxxxxxxxxxx'という形式で、ヘッダー情報に組み込みます。

また、やり取りするデータ形式を指定するため 'Content-type': 'application/json'も入れましょう。

this.apiKeyで、このコンポーネントが持つapiKeyプロパティの値を取得できます。ここでは、コードの30行目で画面から入力された値が最新値として代入されます。

this.apiKeyには画面生成時にCofigファイル(Mixins)の値を初期値として設定しています。https://github.com/IoTkyoto/iot-handson-rekognition/blob/master/webapp/src/mixins/ConfigMixin.js

// 画面作成時にコンポーネント内で利用する変数の初期値を設定する
created() {
  ・・・(省略)・・・
  this.apiKey = this.config.searchConfig.apiKey
  ・・・(省略)・・・
},

// ヘッダー情報を設定する
const config = {headers: {
  'Content-Type': 'application/json',
  'x-api-key': this.apiKey,
}};

リクエストボディ情報

ステップ2-1-3で作成したLambda関数コードが想定しているパラメータデータの値を用意します。

base64dataには、 createImage()関数内の FileReaderメソッドの実行結果として画像ファイルのbase64データ(data URI scheme形式)を利用します。

thresholdでは、this.thresholdで、コードの31行目で画面で選択された値を最新値として利用します。

// コンポーネント内で利用する変数の初期値を設定する
created() {
  ・・・(省略)・・・
  this.threshold = this.config.searchConfig.threshold
},

// リクエストボディ情報を作成する
const querydata = {
  'image_base64str': this.uploadedImage,
  'threshold': this.threshold,
};

// 入力されたファイルをFileReaderでbase64(data URI scheme)にエンコードする
createImage(file) {
  const reader = new FileReader();
  
  reader.onload = e => {
    // ここにファイルの読み込み処理(readAsDataURL())実行後の処理を記述
    // 以下は画面表示・APIアクセスに利用します
    this.uploadedImage = e.target.result;
  };
  // data URI scheme形式でファイルを読み込む
  reader.readAsDataURL(file);
},

最後に、axiosを利用してリクエストを投げます。

apiEndpointでは、this.apiEndpointで、コードの26行目で画面に入力された値を最新値として利用します。

リクエストボディのデータは JSON.stringify()メソッドでJSON形式に変換しましょう。

// コンポーネント内で利用する変数の初期値を設定する
data: () => ({
  // --他のデータ定義は省略--
  info: '',
  apiEndpoint: '',
}),

// APIエンドポイントのURLを構築する
const apiEndpoint = 
  'https://' + this.apiId + '.execute-api.' + this.region + '.amazonaws.com/'  
  + this.stage + '/' + this.resource

// API呼び出し
axios
  // APIエンドポイント、リクエストボディ、ヘッダーを引数にPOSTします
  .post(apiEndpoint, JSON.stringify(querydata), config)
  // APIアクセス成功時の処理です
  .then(response => {
      // ステップ2-1-3で作成した `payloads`内にあるRekognitionの結果データを取得します
      const faceMatches = response.data.payloads.FaceMatches;
      if (faceMatches.length == 0) {
        // 該当する人物がない場合に実行する処理を記述します
        this.noTarget = '人物を特定できませんでした'
      } else {
        // 該当する人物がいた場合に実行する処理を記述します
        this.createAuthenticationData(faceMatches[0]);
      }
      this.overlay = false;
  })
  // エラーハンドリングを実装します
  .catch(error => {
    this.errorMessage = error;
    this.overlay = false;
  });

2-3-2. スマホ画面から顔認証を実行する

前のステップで解説したWebアプリケーションを実際に動かして顔認識処理を実行します。

画像内に、Rekognitionのコレクションに登録した人物が発見された場合、マッチした人物と信頼度が表示されます。見つからなかった場合、「認識結果」のボックスに「人物を特定できませんでした」と表示されます。 2-3-2_7

また、AWSのコンソールに移動し、CloudWatchでAPIへのアクセス履歴やLambdaのログを確認しましょう。