AWS SAMでAWS Lambdaを構築し、Amazon Rekognitionを使った写真解析AIのLINE Botを作成してみた!【サーバーレスアプリケーション】

AWS SAMでAWS Lambdaを構築し、Amazon Rekognitionを使った写真解析AIのLINE Botを作成してみた!【サーバーレスアプリケーション】

AWS Lambda と Amazon Rekognitionを利用した写真解析AIのLINE Botを作成してみました。
アプリとしては、AWS Lambdaを使ったサーバーレス構成になっているので、マネしやすいかなと思います。

目次

LINE Developerの設定

LINE Developerのアカウント設定は、先に完了させておいてください。
https://developers.line.biz/ja/」こちらから登録できます。

まずは、プロバイダーを作成していきます。
画面の「作成」ボタンを選択しましょう。

プロバイダー名は、任意で構いません。今回は、テスト用なので適当に付けました。

下記の画面に遷移できれば、プロバイダー登録は完了です。
このまま、チャネルの登録をしますので、「Messaging API」を選択しましょう。

新規チャネル作成画面が表示されるため、情報を入力していきます。

内容は、下記のような感じです。基本的には、ご自身の内容を入力していただければOKです。

  • チャンネルの種類:Messaging API
  • プロバイダー:先ほど作成したプロバイダー
  • 会社・事業者の所在国・地域:日本
  • チャネルアイコン:任意
  • チャネル名:任意
  • チャネル説明:任意
  • 大業種:任意
  • 小業種:任意
  • メールアドレス:ご自身のメールアドレス
  • プライバシーポリシーURL:任意
  • サービス利用規約:任意

入力が完了したら、利用規約を読んで同意し、「作成」ボタンを選択します。

下記のようにチャネル設定画面が表示されたら、チャネル作成は完了です。

この画面の下のほうに、「チャネルシークレット」という文字列がありますが、後で利用しますのでメモしておきます。

次に、Message API設定に移動します。
ここのQRコードは、BOTを友達登録する際に利用できます。

この画面の下の方に、「チャネルアクセストークン」がありますので、これもメモしておきます。

応答メッセージも変更しておきます。
下記のように、「Webhook」のみを有効にしておきましょう。

これで、LINE Developerでの設定は、一旦終わりです。
あとで、Webhookの設定だけ別途行います。

AWS CLIの設定

ここは、AWSのユーザーは既に作成済みであることとして、解説しておきます。

aws configure
  • AWS Access Key ID:AWSユーザーのアクセスキーID
  • AWS Secret Access Key:AWSユーザーのシークレットアクセスキー
  • Default region name:ap-northeast-1
  • Default output format:json

設定内容はこれだけです。

AWS SAMで利用するソース等を作成

では、AWS SAMを利用してAWS LambdaとAmazon API Gatewayを作成していきます。
AWS SAMに関しては、今回は詳しく説明しません。

まずディレクトリ構成は、このような形でお願いします。

├─ src
│  ├─ mylinebot.py
│  └─ requirements.txt
└─ template.json

まずは、「mylinebot.py」になります。
Lambdaの処理の中身です。言語は、pythonを利用しています。

"""
Line Bot
"""

import os

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage, ImageMessage,
)

import boto3

handler = WebhookHandler(os.getenv('LINE_CHANNEL_SECRET'))
line_bot_api = LineBotApi(os.getenv('LINE_CHANNEL_ACCESS_TOKEN'))

client = boto3.client('rekognition')


def lambda_handler(event, context):
    headers = event["headers"]
    body = event["body"]

    # get X-Line-Signature header value
    signature = headers['x-line-signature']

    # handle webhook body
    handler.handle(body, signature)

    return {"statusCode": 200, "body": "OK"}


def all_happy(result):
    # 検出した顔がすべてHappyならTrueを返却
    for detail in result["FaceDetails"]:
        if most_confident_emotion(detail["Emotions"]) != "HAPPY":
            return False
    return True


def most_confident_emotion(emotions):
    """
    複数ある感情のうち、もっとも確度が高い感情を返却する
    :param emotions:
    :return:
    """
    max_conf = 0
    result = ""
    for e in emotions:
        if max_conf < e["Confidence"]:
            max_conf = e["Confidence"]
            result = e["Type"]
    return result


@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    """ TextMessage handler """
    input_text = event.message.text

    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=input_text))


@handler.add(MessageEvent, message=ImageMessage)
def handle_image_message(event):
    # ユーザから送られてきた画像を一時ファイルとして保存
    message_content = line_bot_api.get_message_content(event.message.id)
    # lambdaで保存できるディレクトリは「/tmp」配下のみ
    file_path = "/tmp/sent-image.jpg"
    with open(file_path, 'wb') as fd:
        for chunk in message_content.iter_content():
            fd.write(chunk)

    # Rekognition で感情分析
    with open(file_path, 'rb') as fd:
        sent_image_binary = fd.read()
        response = client.detect_faces(Image={"Bytes": sent_image_binary}, Attributes=["ALL"])

    print(response)

    # 応答するメッセージを決める
    if all_happy(response):
        message = "みんな楽しそうですね!!"
    else:
        message = "もう一度写真を取り直してみてはいかがでしょうか。"

    # 応答を返す
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=message)
    )

    # file_pathの画像を削除
    os.remove(file_path)

@handler.add(MessageEvent, message=TextMessage) は、Lineメッセージがテキストだった場合に、ハンドリングされます。
@handler.add(MessageEvent, message=ImageMessage) は、Lineメッセージで画像だった場合に、ハンドリングされます。
画像だった場合は、boto3を利用して、Amazon Rekognitionの「Detect Faces」を呼び出しています。

Detect Facesのレスポンスの「Emotions」が写真に写っている人達の感情情報が含まれる場所です。
この中に含まれる感情の一番確度が高い感情がHappyだった場合に、その人は笑顔だと判定しています。
すべての人がHappyだった場合、「みんな楽しそうですね!!」を返却、だれか一人でも笑顔ではない場合は、「写真を取り直してみてはいかがでしょうか。」と写真の撮り直しを提案しています。

次に「requirements.txt」です。
AWS Lambdaを作成するタイミングで、読み込みたいライブラリを列挙します。
今回は、line-bot-sdkとboto3だけでOKです。

line-bot-sdk
boto3

次に、「template.json」になります
この中身は詳しく説明しませんが、SAMで作成する「Amazon API Gateway」と「AWS Lambda」の情報が記載されています。
注意としては、最初にメモをしたLINEのチャネルアクセストークンとLINEのチャネルシークレットは、パラメータとしてSAMを実行するときに入力するようにしています。
これは、GITなどでソースを管理するときに、トークンが漏れないようにするためです。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Parameters": {
    "LineChannelAccessToken": {"Type": "String", "Description": "LINEのチャネルアクセストークン"},
    "LineChannelSecret": {"Type": "String", "Description": "LINEのチャネルシークレット"}
  },
  "Resources": {
    "EndPointFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Runtime": "python3.9",
        "CodeUri": "src",
        "Handler": "mylinebot.lambda_handler",
        "Environment": {"Variables": {
            "LINE_CHANNEL_ACCESS_TOKEN": {"Ref": "LineChannelAccessToken"},
            "LINE_CHANNEL_SECRET": {"Ref": "LineChannelSecret"}
        }},
        "Policies": [{"RekognitionDetectOnlyPolicy": {}}],
        "Events": {
          "API": {
            "Type": "Api",
            "Properties": {"Path": "/api_endpoint", "Method": "post"}
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiEndpointURL": {
      "Description": "API Endpoint URL",
      "Value": {"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${ServerlessRestApi.Stage}/api_endpoint"}
    }
  }
}

作成したソース等を使って、SAMを実行

sam buildコマンドで、AWS SAM テンプレートファイル、アプリケーションコード、および該当する言語固有のファイルと依存関係を処理させます。

sam build -t template.json

windowsで作業する場合は、dockerを起動し、「sam build –use-container -t template.json」を実行してください。

次に、sam buildで処理した結果を、sam deployでデプロイしていきます。
–guidedオプションを付与することで、ガイド付きのインタラクティブモードで実行できます。
また、この際に、LINEのアクセストークンなどParametersで設定した内容が聞かれるので、メモしていたものを記載するようにしてください。

sam deploy --guided
  • Stack Name:任意の名前(LineBotの名前になります。)
  • AWS Region:ap-northeast-1(ご自身のAWS RegionでOKです。)
  • Parameter LineChannelAccessToken:「LINE Developerの設定」で取得したチャネルアクセストークン
  • Parameter LineChanelSecret:「LINE Developerの設定」で取得したチャネルシークレット
  • Confirm changes before deploy:N(デフォルト)
  • Allow SAM CLI IAM creation:Y(デフォルト)
  • EndPointFunction may not have authorization defined, Is this okay?:Y(テスト用のため)
  • Save arguments to configuration file:Y(デフォルト)
  • SAM configuration file:samconfig.toml(デフォルト)
  • SAM configuration enviroment:default(デフォルト)

Outputsに下記が出力されているはず。

  • Key:ApiEndpointURL
  • Description:API Endpoint URL
  • Value:https://xxxxx

LINE DeveloperのWebhookを修正

最後に、先ほどのValueをLINE Developersの「Messaging API設定>Webhook URL」に設定します。

Webhookの利用もONにしておきましょう。

動確してみる

では、最初のLINE DeveloperのQRコードからLINE BOTを友達登録して、画像を送信してみましょう。
少し恥ずかしいので、写真は隠しましたが、ちゃんとメッセージの出しわけもうまくいっていそうでした!

まとめ

AWS Lambda と Amazon Rekognitionを利用した写真解析AIのLINE Botを作成してみました。
Amazon Rekognitionはほかにもいろいろできそうなので、機会があればもう少し詳しく触ってみたいなと思います。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

クラウド関連を勉強するために始めたブログです。
未経験の技術のため、広い心を持って見ていただけると嬉しく思います。
情報セキュリティを勉強するために、「Hack Lab. 256」もあります。
情報セキュリティに興味がある方は、ぜひ覗いて見てください!

目次