LambdaとCloud Vision API で画像をテキストに変換してくれる LINE bot を作ってみた
こんにちは、リサリサです。
Lambda と Cloud Vision API で画像をテキストに変換してくれる LINE bot を作ってみました。
先日、画像データを S3バケットに置くと、それを文字抽出できるツールを作成したところ、誰でも使えるようにしてほしいとリクエストを頂いたので、スタート地点をS3ではなく、LINE にしてみました。
友達登録はこちらからできます。ただ、リサリサ個人のAWSアカウント、GoogleCloudアカウントで動いているので、たくさん使われると課金が心配…。おひとり様1日20回のみ使えますww
制限内でしたらどんどんご利用くださいませ。
作ったもの
下記記事で作ったものをLINE Messaging API用に少し作り替えました。
作ってみた
LINE の Messaging API を作成
詳細な作成手順はこちらの「前準備」の「LINE Developersで、Messaging APIを作成」をご覧いただければと思います。
LINE Developers から Messaging API を作ります。
レイヤーを作る
line-bot-sdk と google-cloud-vision を使っていくので、今回はレイヤーに固めてみます。
今回使ったバージョンはそれぞれ以下です。
- Python 3.6
- google-cloud-vision 2.3.2
- line-bot-sdk 1.19.0
Lambdaを作成する
以下で作成しました。
- ランタイム:Python 3.6
- ロール:自動作成されるもの
コードはこちら
import json import os import traceback from linebot import (LineBotApi, WebhookHandler) from linebot.models import (MessageEvent, TextMessage, TextSendMessage,TemplateSendMessage,PostbackAction,ButtonsTemplate,ImageSendMessage) from linebot.exceptions import (LineBotApiError, InvalidSignatureError) from google.cloud import vision import io def lambda_handler(event, context): try: print(json.dumps(event)) body = json.loads(event['body']) events = body['events'] line_bot_api = LineBotApi(channel_access_token=os.environ['ACCESS_TOKEN']) output_texts = [] # events には複数画像がまとめて送られてくることがあるので、画像枚数分ループする for event in events: # 画像データじゃなかったら処理skip if event['message']['type'] !='image': continue # LINEから画像データを取得 message_id = event['message']['id'] message_content = line_bot_api.get_message_content(message_id) # Lambda の /tmp/に一時保存 with open(f"/tmp/{message_id}.jpg", "wb") as f: f.write(message_content.content) with io.open(f"/tmp/{message_id}.jpg", 'rb') as image_file: content = image_file.read() # 文字起こし client = vision.ImageAnnotatorClient() # Cloud Vision API 呼び出し image = vision.Image(content=content) response = client.document_text_detection( image=image, image_context={'language_hints': ['ja']} ) # Cloud Vision APIのレスポンスからテキストデータを抽出 output_text = '' for page in response.full_text_annotation.pages: for block in page.blocks: for paragraph in block.paragraphs: for word in paragraph.words: for symbol in word.symbols: # 確度があまりに低い文字は拾わない。とりあえず0.3以上 if symbol.confidence > 0.3: output_text += symbol.text # 単語と単語の間にはスペース output_text += ' ' # 文と文の間には改行 output_text += '\n' if len(output_text) > 0: output_texts.append(output_text.rstrip('\n')) else: output_texts.append('ごめんね。読み取れる文字がなかったみたい。\n文字の入った画像データを送ってくれたら文字起こしするよ!') if len(output_texts) == 0: output_texts.append('ごめんね。画像データがなかったみたい。\n文字の入った画像データを送ってくれたら文字起こしするよ!') # メッセージ送信 print(output_texts) reply_messages = [] for output_text in output_texts: reply_messages.append(TextSendMessage(text=output_text)) line_bot_api.reply_message(event['replyToken'], reply_messages) return { 'statusCode': 200 } except: traceback.print_exc() return { 'statusCode': 500 }
API Gateway の API を作る
Lambda を LINEbotが叩けるように API Gateway で API を作って Lambda を紐づけていきます。
API Gateway の API を作って、POST メソッドを作って、デプロイしていきます。
API を作成
「API を作成」から、「REST API」を「構築」します。
それぞれ設定して「API の作成」をします。
POST メソッドの作成
「アクション」から「メソッドの作成」をします。
「POST」メソッドを作成します。
「Lambda 関数」で作った Lambda をこのメソッドに紐づけていきます。
「Lambda プロキシ統合の使用」を ON にするとリクエストの受け渡しなど良きに計らってくれるので、 ON にします。詳しく知りたい方は、下記記事をご覧いただければと思います。
紐づける際にこんな感じで、勝手に権限の追加をしてくれて便利です。
デプロイ
メソッドが完成したらデプロイします。
ステージ名を付けて、「デプロイ」します。
デプロイが完了すると、API の呼び出しのエンドポイントが発行されます。これを Messaging API に Webhook URL に設定してあげます。
Messaging API に Webhook URL を設定
LINE Developers から作った Messaging API の Webhook URL を設定します。これを設定することで、LINE に来たメッセージなどを Lambda で受け取ることができるようになります。
「Webhook URL」を「Edit」します。
先ほど取得したAPI の呼び出しのエンドポイントを貼り付けて「Update」します。
「Use webhook」を ON にします。
完成です!!!!
使ってみる
こんな感じになります。
最後に
今回のも楽しく作れました!たまに、え?それ画像で送ってくる?みたいな時によかったらご利用下さいませ。友達登録はこちらから!
この後に、無制限に使われてしまうと私のお財布が困るので、一人一日20枚までしか利用できない機能追加をしています(DynamoDBでユーザー管理)。余裕があったら、それもまた記事にしていきたいと思います。