皆さんはじめまして、HRです。
今回はLineBotからAlibabaクラウドのImageSearchを使って、画像検索機能を利用してみました。
早速、実現方法について簡単に記載させていただきます。
①まずLineBotを作成するために、下記のURLからLineDevelopersアカウントを作成します。
アカウント作成後、LineBotを一つ作成し、友達を追加します。
LineDevelopersの下記情報をメモして、後ほどLineBotAPIを呼び出しする際利用します。
- (1)Channel secret
- (2)Channel access token (long-lived)
②Alibabaクラウド上で、ImageSearchインスタンスをデプロイ方法は下記のドキュメントを参照します。
③ImageSearchのインスタンス作成後、下記のドキュメントに従って、事前に画像をインプットします。
④今回利用するコードは下記になります。簡単に説明させていただきます。
利用するライブラリは以下になります。
import os import re import oss2 import base64 from io import BytesIO from PIL import Image import aliyunsdkimagesearch.request.v20190325.AddImageRequest as AddImageRequest import aliyunsdkimagesearch.request.v20190325.DeleteImageRequest as DeleteImageRequest import aliyunsdkimagesearch.request.v20190325.SearchImageRequest as SearchImageRequest from aliyunsdkcore.client import AcsClient from flask import Flask from flask import request from flask import abort from linebot import LineBotApi from linebot import WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent from linebot.models import TextMessage from linebot.models import TextSendMessage from linebot.models import ImageMessage from linebot.models import ImageSendMessage
後ほどコードをECSにデプロイするため、ECSの環境変数は以下のものを設定します。
まずはLineBotAPIの呼び出しに必要なトークン情報です。
# LineBot Access Token CHANNEL_ACCESS_TOKEN = os.environ["CHANNEL_ACCESS_TOKEN"] CHANNEL_SECRET = os.environ["CHANNEL_SECRET"]
次にImageSearchを利用するための必要なトークン情報も設定します。
- (1)Alibabaクラウドにアクセスに必要なアクセスキーとシークレットトークン
- (2)OSSにアクセスするためのURL、バケット名、バケットディレクトリ
- (3)ImageSearchインスタンスのURL、Bot名、リージョン名
# AliCloud Access Token ALICLOUD_ACCESS_KEY = os.environ["ALICLOUD_ACCESS_KEY"] ALICLOUD_ACCESS_SECRET = os.environ["ALICLOUD_ACCESS_SECRET"] ALICLOUD_REGION = os.environ["ALICLOUD_REGION"] ALICLOUD_BUCKET_NAME = os.environ["ALICLOUD_BUCKET_NAME"] ALICLOUD_BUCKET_URL = os.environ["ALICLOUD_BUCKET_URL"] ALICLOUD_BUCKET_DIR = os.environ["ALICLOUD_BUCKET_DIR"] ALICLOUD_IMGSEARCH_URL = os.environ["ALICLOUD_IMGSEARCH_URL"] ALICLOUD_IMGSEARCH_BOT = os.environ["ALICLOUD_IMGSEARCH_BOT"]
さて、本体のLineBotからImageSearchのAPIを呼びだしする部分は以下になります。
line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN) handler = WebhookHandler(CHANNEL_SECRET) auth = oss2.Auth(ALICLOUD_ACCESS_KEY, ALICLOUD_ACCESS_SECRET) bucket = oss2.Bucket(auth, ALICLOUD_BUCKET_URL, ALICLOUD_BUCKET_NAME) # create AcsClient instance client = AcsClient(ALICLOUD_ACCESS_KEY, ALICLOUD_ACCESS_SECRET, ALICLOUD_REGION) # callback() @app.route("/callback", methods=['POST']) def callback(): # Get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # Get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # Handle webhook body try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK' # ImageMessage Handler @handler.add(MessageEvent, message=ImageMessage) def handle_message(event): # get binary image message_id = event.message.id message_content = line_bot_api.get_message_content(message_id) image_bin = BytesIO(message_content.content) # search image request = SearchImageRequest.SearchImageRequest() request.set_endpoint(ALICLOUD_IMGSEARCH_URL) request.set_InstanceName(ALICLOUD_IMGSEARCH_BOT) encoded_pic_content = base64.b64encode(image) request.set_PicContent(encoded_pic_content) response = client.do_action_with_exception(request) # get string result data = str(response) # get image name imgName = (re.search("[a-z]*_[0-9]*_[0-9]*", data)).group(0) # get image on oss url = bucket.sign_url('GET', ALICLOUD_BUCKET_DIR + imgName + '.' + imgFormat, 60) # reply image message line_bot_api.reply_message( event.reply_token, ImageSendMessage( original_content_url = url, preview_image_url = url ) ) @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text) )
iPhoneで取得した画像はサイズだと直接ImageSearchに送信できないため、画像をリサイズします。
※今回は画像サイズを一律(250,800)にしています。
# resize image pil_img = Image.open(image_bin) re_img = pil_img.resize((250,800)) # get binary image output = BytesIO() re_img.save(output, format=imgFormat) image = output.getvalue()
Flaskサーバを外部公開するため、下記のように記載します。
if __name__ == "__main__": callback() app.run(debug=False, host='0.0.0.0', port=443)
⑤作成したプログラムをECSにデプロイし、実行します。
⑥最後はLineDevelopersのWebHookURLに今回公開するサイトのURLを登録して構築完了です!
それでは実際動作する際のイメージを確認してみましょう。
(1)作成したImageSearchのBotを選択しチャット画面を開きます。
(2)携帯のカメラを起動して、検索したい画像を写真撮影して送信すれば、一番類似の画像が返ってくることが確認できました。
まとめ
以上でiPhoneから画像を撮影し、LineBotを経由でImageSearchで画像検索ができました〜
ImageSearchとLineBotを結合することで、ImageSearchの利用もよりユーザーに近ずくことができると考えて今回のデモを作成しました。
ぜひ皆さんも実装してみてはいかがでしょうか。
今回は画像の検索のみ実装しましたが、また第2弾では画像の登録や削除も実装してみます!