3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flask + LINE Messaging APIでの人工知能LINEボットの作り方

Last updated at Posted at 2020-01-22

#Flask + LINE Messaging APIでの人工知能LINEボットの作り方

ナカノヒトシさんの書籍

Python + LINEで作る人工知能開発入門 - Flask + LINE Messaging APIでの人工知能LINEボットの作り方

まあまあ面白かった。
サンプル演習の最後はGoogle Vision APIを使った顔認識でスタンプで顔隠すボットアプリ。この手のアプリはたくさんあるがLINEボットにすると使う方はラクかも。

そして最後に顔認識を複数対応させてみようという宿題を出される。書籍のアプリでは1人のみ対応。回答はないです簡単です自力でがんばりましょうと厳しいナカノさん。

少し苦労して回答作ったので参考までに。

ユーザの送った画像をGoogle Vison APIで顔検出しcat.pngで隠した合成写真をリプライ(顔複数対応)
#app.py

# ユーザの送った画像をGoogle Vison APIで顔検出しcat.pngで隠した合成写真をリプライ(顔複数対応)

import io
import os
import base64
import json
import requests
from flask import Flask, request, abort
from PIL import Image #Pillowをインストール pip3 install pillow

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


# LINEアクセストークンとアプリケーションシークレット
ACCESS_TOKEN = ''
SECRET = ''
# Google Vision APIキー
API_KEY = ''

app = Flask(__name__)

line_bot_api = LineBotApi(ACCESS_TOKEN)
handler = WebhookHandler(SECRET)


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/callback',methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)
    
    return 'OK'

@handler.add(MessageEvent,message=ImageMessage)
def handle_message(event):
    try:
        message_content = line_bot_api.get_message_content(event.message.id)
        # event.message.idを指定することで画像本体データを読み出せる
        # message_content.content #取得した画像ファイル本体

        image_base64 = base64.b64encode(message_content.content) #画像ファイルをbase64に変換

        #リクエストボディを作成(json.dumps()でJSONに変換してる)
        req_body = json.dumps({
            'requests': [{
                'image': {
                    'content': image_base64.decode('utf-8')
                },
                'features': [{
                    'type': 'FACE_DETECTION',
                    'maxResults': 20,
                }]
            }]
        })
                            # Vison APIのエンドポイント↓
        res = requests.post("https://vision.googleapis.com/v1/images:annotate?key=" + API_KEY, data=req_body)
        #print('res内容は、' + res.text)

        result = res.json()
    
        vertices = result["responses"][0]["faceAnnotations"]
        #print('vertices内容は、' + json.dumps(vertices)) 
        ## response内容は、レスポンス.jsonを参照.

        if vertices:
            print('取得できた')
            image_base = Image.open(io.BytesIO(message_content.content))
            for face in vertices:
                corner = face["boundingPoly"]['vertices'][0]
                print('cornerは、' + json.dumps(corner))
                print('face["boundingPoly"]["vertices"][1]["x"]は、' + json.dumps(face["boundingPoly"]['vertices'][1]["x"]))
                width = face["boundingPoly"]['vertices'][1]["x"] - face["boundingPoly"]['vertices'][0]["x"]
                height = face["boundingPoly"]['vertices'][2]["y"] - face["boundingPoly"]['vertices'][1]["y"]

                image_cover = Image.open('static/cat.png') # cat.pngはアルファチャンネル画像でないとダメ。ValueError: bad transparency maskエラー
                image_cover = image_cover.resize((width,height))
                image_base.paste(image_cover, (corner['x'],corner['y']), image_cover)
                # Image.paste(im, box=None, mask=None)
                print('forループおわり')

            image_base.save('static/' + event.message.id + '.jpg')


        line_bot_api.reply_message(
            event.reply_token,
            ImageSendMessage(
                    original_content_url = "https://hidden-savannah-xxxxx.herokuapp.com/static/" + event.message.id + ".jpg",
                    preview_image_url = "https://hidden-savannah-xxxxx.herokuapp.com/static/" + event.message.id + ".jpg"
            )
        )

    except:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="顔認識できませんでした(動物ダメです人間だけです。横顔やアップすぎるのも厳しいです")
        )


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))


######参照:
念の為、80行目のprint('vertices内容は、' + json.dumps(vertices))のレスポンス内容

[
	{
		"boundingPoly": {
			"vertices": [
				{
					"x": 917,
					"y": 318
				},
				{
					"x": 1174,
					"y": 318
				},
				{
					"x": 1174,
					"y": 616
				},
				{
					"x": 917,
					"y": 616
				}
			]
		},
		"fdBoundingPoly": {
			"vertices": [
				{
					"x": 971,
					"y": 396
				},
				{
					"x": 1163,
					"y": 396
				},
				{
					"x": 1163,
					"y": 588
				},
				{
					"x": 971,
					"y": 588
				}
			]
		},
		"landmarks": [
			{
				"type": "LEFT_EYE",
				"position": {
					"x": 1031.1968,
					"y": 456.0161,
					"z": 0.0003030986
				}
			},
			{
				"type": "RIGHT_EYE",
				"position": {
					"x": 1112.0862,
					"y": 460.92987,
					"z": 28.232975
				}
			},
			{
				"type": "LEFT_OF_LEFT_EYEBROW",
				"position": {
					"x": 1008.84607,
					"y": 436.544,
					"z": -1.8571037
				}
			},
			{
				"type": "RIGHT_OF_LEFT_EYEBROW",
				"position": {
					"x": 1060.1007,
					"y": 440.86813,
					"z": -7.585352
				}
			},
			{
				"type": "LEFT_OF_RIGHT_EYEBROW",
				"position": {
					"x": 1095.2485,
					"y": 442.76245,
					"z": 5.0468025
				}
			},
			{
				"type": "RIGHT_OF_RIGHT_EYEBROW",
				"position": {
					"x": 1131.141,
					"y": 444.30832,
					"z": 41.595203
				}
			},
			{
				"type": "MIDPOINT_BETWEEN_EYES",
				"position": {
					"x": 1075.8728,
					"y": 455.9283,
					"z": -1.5975293
				}
			},
			{
				"type": "NOSE_TIP",
				"position": {
					"x": 1080.8457,
					"y": 504.33997,
					"z": -20.247692
				}
			},
			{
				"type": "UPPER_LIP",
				"position": {
					"x": 1071.2343,
					"y": 531.5437,
					"z": -1.6211907
				}
			},
			{
				"type": "LOWER_LIP",
				"position": {
					"x": 1069.6505,
					"y": 551.9242,
					"z": 4.4038887
				}
			},
			{
				"type": "MOUTH_LEFT",
				"position": {
					"x": 1035.7985,
					"y": 538.815,
					"z": 8.222528
				}
			},
			{
				"type": "MOUTH_RIGHT",
				"position": {
					"x": 1101.0676,
					"y": 541.8905,
					"z": 30.981604
				}
			},
			{
				"type": "MOUTH_CENTER",
				"position": {
					"x": 1070.1655,
					"y": 541.40643,
					"z": 4.1978736
				}
			},
			{
				"type": "NOSE_BOTTOM_RIGHT",
				"position": {
					"x": 1092.8889,
					"y": 510.94235,
					"z": 16.238985
				}
			},
			{
				"type": "NOSE_BOTTOM_LEFT",
				"position": {
					"x": 1049.6199,
					"y": 507.50146,
					"z": 0.9902145
				}
			},
			{
				"type": "NOSE_BOTTOM_CENTER",
				"position": {
					"x": 1072.0765,
					"y": 515.82806,
					"z": -2.7877321
				}
			},
			{
				"type": "LEFT_EYE_TOP_BOUNDARY",
				"position": {
					"x": 1037.2472,
					"y": 452.2355,
					"z": -4.3320293
				}
			},
			{
				"type": "LEFT_EYE_RIGHT_CORNER",
				"position": {
					"x": 1047.4124,
					"y": 459.2465,
					"z": 6.317641
				}
			},
			{
				"type": "LEFT_EYE_BOTTOM_BOUNDARY",
				"position": {
					"x": 1030.3141,
					"y": 461.9699,
					"z": 0.34013578
				}
			},
			{
				"type": "LEFT_EYE_LEFT_CORNER",
				"position": {
					"x": 1018.07513,
					"y": 455.93164,
					"z": 2.3924496
				}
			},
			{
				"type": "LEFT_EYE_PUPIL",
				"position": {
					"x": 1034.6456,
					"y": 457.22366,
					"z": -1.4217875
				}
			},
			{
				"type": "RIGHT_EYE_TOP_BOUNDARY",
				"position": {
					"x": 1109.9236,
					"y": 456.6617,
					"z": 21.767094
				}
			},
			{
				"type": "RIGHT_EYE_RIGHT_CORNER",
				"position": {
					"x": 1119.8134,
					"y": 462.12448,
					"z": 38.996845
				}
			},
			{
				"type": "RIGHT_EYE_BOTTOM_BOUNDARY",
				"position": {
					"x": 1110.3936,
					"y": 466.81308,
					"z": 26.98832
				}
			},
			{
				"type": "RIGHT_EYE_LEFT_CORNER",
				"position": {
					"x": 1094.9646,
					"y": 462.28857,
					"z": 22.470396
				}
			},
			{
				"type": "RIGHT_EYE_PUPIL",
				"position": {
					"x": 1109.2263,
					"y": 461.79114,
					"z": 25.238665
				}
			},
			{
				"type": "LEFT_EYEBROW_UPPER_MIDPOINT",
				"position": {
					"x": 1037.4519,
					"y": 429.95596,
					"z": -10.386488
				}
			},
			{
				"type": "RIGHT_EYEBROW_UPPER_MIDPOINT",
				"position": {
					"x": 1116.0272,
					"y": 434.71762,
					"z": 18.003847
				}
			},
			{
				"type": "LEFT_EAR_TRAGION",
				"position": {
					"x": 954.1669,
					"y": 484.3548,
					"z": 76.21559
				}
			},
			{
				"type": "RIGHT_EAR_TRAGION",
				"position": {
					"x": 1119.6852,
					"y": 494.08078,
					"z": 135.9113
				}
			},
			{
				"type": "FOREHEAD_GLABELLA",
				"position": {
					"x": 1078.9543,
					"y": 441.30212,
					"z": -4.084726
				}
			},
			{
				"type": "CHIN_GNATHION",
				"position": {
					"x": 1062.5234,
					"y": 589.9864,
					"z": 16.94458
				}
			},
			{
				"type": "CHIN_LEFT_GONION",
				"position": {
					"x": 968.6994,
					"y": 536.28186,
					"z": 52.295383
				}
			},
			{
				"type": "CHIN_RIGHT_GONION",
				"position": {
					"x": 1117.5015,
					"y": 545.4246,
					"z": 105.74548
				}
			}
		],
		"rollAngle": 4.5907497,
		"panAngle": 19.758451,
		"tiltAngle": -3.1237326,
		"detectionConfidence": 0.91960925,
		"landmarkingConfidence": 0.5607769,
		"joyLikelihood": "VERY_UNLIKELY",
		"sorrowLikelihood": "VERY_UNLIKELY",
		"angerLikelihood": "VERY_UNLIKELY",
		"surpriseLikelihood": "VERY_UNLIKELY",
		"underExposedLikelihood": "VERY_UNLIKELY",
		"blurredLikelihood": "VERY_UNLIKELY",
		"headwearLikelihood": "LIKELY"
	},
・・・・・・・・・・以上でひとり分・・・・・・・・・・・・

]


#####こんな感じになる
GoogleVisionテスト.jpg


書籍の中でうまく行かなかったところは以下の通り。 [Flaskアプリをherokuにデプロイ(苦苦々)](https://qiita.com/atomyah/items/91196f5fda95e4b9c7a6)
参考:[PythonでGoogle Cloud Visionを使った顔検出](https://vaaaaaanquish.hatenablog.com/?page=1471257396)
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?