AWS Rekognition を LINE Bot で使う-1

Pocket

やりたいこと

  • LINE に画像をアップロードした際に、その画像が何なのか Bot に答えてもらう
  • 構成もコードも なるべくシンプル に作る

構成イメージ

  • こんな感じで、シンプルにやってみます。

構成

ポイント

  • 前提として Messaging API が利用できるものとします。
    • LINE Bot の導入などは、ネット上にたくさん記事があるので省略します。
  • セキュリティのため API Gateway を立て、そちらのエンドポイントURLを LINE の Webhook URL に設定します。
  • Lambda 「一般設定」 → 「タイムアウト」 より、 Lambda の処理時間に余裕を持たせるようにしましょう。
    • 画像の送受信が発生するため、デフォルトの3秒だとちょっと不安…
  • CloudWatch は今回言及しませんが、開発する際のログ確認でお世話になります

コード

  • 出来上がったコード(Node.js)がこちらです(Lambda へそのままデプロイできるようにしています)
  • 以下4つの キーやシークレットは必要に応じて発行し Lambda 環境変数 で設定してください。
    • CH_SECRET ・・・ LINE Messaging API チャネルシークレット
    • CH_TOKEN ・・・ LINE Messaging API チャネルアクセストークン
    • AWS_KEY ・・・ AWS ユーザ アクセスキー
    • AWS_SECRET ・・・ AWS シークレットアクセスキー

index.js

const axios = require('axios').default;
const line = require('@line/bot-sdk');
const Rekognition = require('aws-sdk').Rekognition;

// LINE クライアントインスタンス生成
const client = new line.Client({ channelSecret: process.env.CH_SECRET, channelAccessToken: process.env.CH_TOKEN });

// Rekognition インスタンス生成
const rek = new Rekognition({ accessKeyId: process.env.AWS_KEY, secretAccessKey: process.env.AWS_SECRET });

/**
 * LINE Bot リプライを生成する
 */
const makeReply = async (event) => {
    if (event?.message?.type === 'image') {
        // ★1. LINE API を利用し、メッセージの画像コンテンツを取得
        const imgReqOpt = {
            url: `https://api-data.line.me/v2/bot/message/${event.message.id}/content`,
            method: 'get',
            headers: {
                'Authorization': `Bearer ${process.env.CH_TOKEN}`,
                'Content-Type': 'image/jpeg'
            },
            responseType: 'arraybuffer',
        };

        // ★2. arraybuffer として取得したバイト配列を AWS Rekognition detectLabels API に指定する
        const imgBytes = (await axios.get(imgReqOpt.url, imgReqOpt)).data;
        const imgLabels = (await rek.detectLabels({
            Image: {
                Bytes: imgBytes
            }
        }).promise()).Labels;

        // ★3. 取得したラベル(画像判定結果)を元に、リプライを返す
        await client.replyMessage(event.replyToken, { type: 'text', text: `${imgLabels.map(il => il.Name).join(', ')}` });

    } else {
        // 画像以外のメッセージ: 無視
        return Promise.resolve(null);
    }
};

/**
 * AWS Lambda エントリポイント
 */
exports.handler = async (event) => {
    // メッセージの処理
    for (let ev of JSON.parse(event.body)?.events) {
        await makeReply(ev);
    }

    // Lambda 処理結果 を返す
    return { statusCode: 200, body: 'OK' };
};

デプロイ

  1. 以下のような package.json を作り npm i をローカルで実行
    {
        "name": "xxx",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "start": "node ."
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
            "@line/bot-sdk": "^7.5.2",
            "aws-sdk": "^2.1307.0",
            "axios": "^1.2.6"
        }
    }
    
  2. node_modules ディレクトリ含め、すべてのファイルをZIP圧縮する
  3. Lambda 「コードソース」 → 「アップロード元」 → 「.zipファイル」と辿り、ZIPファイルをアップロードする

使ってみる

適当な 海の写真 を投げてみます。

結果

(英語ではありますが)ちゃんと結果が返ってきました!

Summer, Sea, Water, Outdoors, Nature, Waterfront, Scenery, Handrail, Beach, Shoreline, Coast, Land, Guard Rail, Boat, Vehicle, Transportation, Person, Bird, Animal, Railing, Landscape, Horizon, Sky, Path, Boardwalk, Bridge, Pier, Promontory

Sea, Water, Waterfront, Beach といった海に関連する単語や Handrail, Railing などの構造物を表す単語、 Summer といった季節なども判定できるのですね・・・。

結果としてそれっぽい判定結果を得ることができました!

まとめ

  • 普段使っている LINE でお手軽に画像判定を試すことができました。
  • Bot という枠組みで画像判定を行っているので、アイデア次第で色々と面白い機能を足すことができるのかもしれません。
    • 次回はその辺りの取っ掛かりを、少しだけ掘り下げてみたいですね。。。
Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です