Azure cognitive serviceを使った文字起こしlinebot

#### **Azure cognitive serviceとは？** 
microsoftがネット上で提供しているプラットフォームazureの中のサービスの一つです。
データベースやアプリ開発など色々なサービスが提供されていますが、このcognitie serviceは特に画像認識やタグ付け、翻訳などの機能を自分のアプリに組み込むことができるサービスです。

https://azure.microsoft.com/ja-jp/services/cognitive-services/

今回はこのAzure serviceの内、computer visionというものを使って、文字起こしLINE BOTを作っていきます。


#### アウトライン
①LINE Developersの登録と設定
②Python＆Flaskでおうむ返しLINE　Botを作る
③Herokuにデプロイする
④HerokuとLINE Developersの設定
⑤LINEBotのおうむ返し機能を確認
⑥Azureアカウントを作成し、キーを取得する
⑦LINEBotを文字起こしBotに作り変える
⑧文字起こしBotになっているか確認


####使用したもの
Mac OS
Python3.6.5
Flask
Heroku
Azure cognitive service
Line developers



####①LINE Developersの登録と設定

**1.Line developersにLINEアカウントでログイン＆Messaging APIを選択**
[Line developers](https://developers.line.me/ja/services/messaging-api/)にアクセスし、**今すぐはじめよう**からログイン
![スクリーンショット 2018-11-04 21.28.24.png](https://qiita-image-store.s3.amazonaws.com/0/271893/33758279-7715-22fa-a239-15038d082ac4.png)
![スクリーンショット 2018-11-04 21.32.39.png](https://qiita-image-store.s3.amazonaws.com/0/271893/8fc2a941-bb3f-3db7-1322-f562ab39f3c9.png)


**2.新規プロバイダー作成のところで、プロバイダ名（提供者の名前のようなもの）を入力し、プロバイダーを作成**
![スクリーンショット 2018-11-04 21.44.13.png](https://qiita-image-store.s3.amazonaws.com/0/271893/ea8ac400-046b-472c-6228-f9e1765f7fac.png)


**3.新規チャネル（Botとして動くもの）を作成**
任意のアプリ名
任意の説明文
プラン:Developer Trial
![スクリーンショット 2018-11-04 21.49.12.png](https://qiita-image-store.s3.amazonaws.com/0/271893/22a356b7-de82-bc59-3296-3d3c270ffcd2.png)
大業種:個人
小業種:個人（その他）
メールアドレス:自分のメールアドレス
![スクリーンショット 2018-11-04 21.49.24.png](https://qiita-image-store.s3.amazonaws.com/0/271893/630daebf-662c-3a56-b86c-2b1062b985c8.png)
作成！

**4.アクセストークンの再発行**
チャネルの設定を開き、下の方にあるアクセストークンを再発行しておきます。
＊後にこのアクセストークンと上部にあるchannel secretを使用します。
![スクリーンショット 2018-11-04 22.22.20.png](https://qiita-image-store.s3.amazonaws.com/0/271893/9517b5c9-7ed8-b82c-5727-a780860ec241.png)

1回目Line developers上の作業は終了です！

####②Python＆Flaskでおうむ返しLINE　Botを作る
**1.自分がわかりやすいところにディレクトリを作り、LINEBotを作る**
[line-bot-sdk-python](https://github.com/line/line-bot-sdk-python)というサンプルコードを使っておうむ返し機能を作成します。


```index.py
import os
from flask import Flask, request, abort

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

app = Flask(__name__)

line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')


@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value　LINE上から送られたことを証明する署名を取得
    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　署名が正しいかを調べ、問題がなければhandle関数を呼ぶ
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

"""addメソッドを使ってどんなイベント（内容）を処理するかを指定する。今回はテキストなのでTextmessage
   次にline-bot-apiのreply_messageメソッドを呼ぶ（第一引数はイベントへの返答に必要なトークン(許可証のようなもの）、第二引数は返信用のオブジェクト（text=送られたメッセージのテキスト部分)
）"""
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

#このスクリプトが直接実行された時、アプリを動かす
if __name__ == "__main__":
    port = os.environ.get('PORT', 3333)
    app.run(
        host='0.0.0.0',
        port=port,
    )
```


**2.他のファイルを作成**
index.pyと同じディレクトリに以下の三つのファイルを作成


必要なモジュールを指定

```requirements.txt
line-bot-sdk==1.5.0
future==0.16.0
requests==2.14.2
Flask== 0.12.2
click==6.7
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==0.23
Werkzeug==0.12.2
bottle==0.12.9
certifi==2017.4.17
chardet==3.0.3
idna==2.5
oauthlib==2.0.2
urllib3==1.21.1
```

Herokuで起動するものを指定

Procfile

```procfile
web: python index.py
```

gitで管理したくないものを指定

.gitignore

```.gitignore
__pycache__
```

####③Herokuにデプロイする
Herokuアカウントを作成してある方は、以下コマンドでデプロイ
作成されていない方はまずアカウント作成から
[このサイト](https://programmingnavi.com/1438/)を参考までに

```
$ git init
$ git add .
$ git commit -m "My first commit"
$ heroku login
$ heroku create "アプリ名"
$ git push heroku master
```

####④HerokuとLINE Developersでの設定
Herokuのログイン後にアプリのsettingを開き、下部にあるDomainをコピー（アプリ名と同一なはず）
![スクリーンショット 2018-11-05 9.56.02.png](https://qiita-image-store.s3.amazonaws.com/0/271893/2b070cbb-74e5-9a6f-54fe-42132794a67b.png)

LINE Developersのチャネルの設定画面で、webhook URLの部分に先ほどのdomainに/callbackを足したものを設定する。加えて、webhookを利用するに変更。(webhook URLの接続確認はしなくて大丈夫です！)
これらの下部にある自動応対メッセージは利用しないに変更。
![スクリーンショット 2018-11-04 22.41.59.png](https://qiita-image-store.s3.amazonaws.com/0/271893/41620c35-7c87-09a4-62f0-c96b89a770c6.png)

①の４で見たアクセストークンとsecret channelをコピーして、Herokuアプリ設定画面のconfig varsに設定します。

![スクリーンショット 2018-11-05 10.05.38.png](https://qiita-image-store.s3.amazonaws.com/0/271893/c602a571-e6e0-9c2d-b023-5f840aaa9aa0.png)

アプリ側でこれらの変数を受け取れるようにしておきます。

```settings.py
import os

# LINE
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
```


index.pyも少し変更

```index.py
import settings
#アクセストークンとsecret channelを代入していた部分を変更
line_bot_api = LineBotApi(settings.YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(settings.YOUR_CHANNEL_SECRET)
```


####⑤LINEbotのおうむ返し機能を確認

LNEBotがおうむ返しをしてくれるか確認します。
LINE　developersのチャネル設定画面下部(アクセストークンやchannel secretを確認したところ)にBotのQRコードが載っているので、そこから友達登録をして何か文章を送ってみてください。
![スクリーンショット 2018-11-12 12.11.56.png](https://qiita-image-store.s3.amazonaws.com/0/271893/91170fb1-71ec-b4c8-1b7f-211f8da446ff.png)

文章がそのまま返ってくればOKです！

ここまでで作業の半分ほど終わりました。お疲れ様です！
あと半分も楽しんで作っていきましょう！


以下で、このおうむ返し機能の部分を文字起こし機能に作り変えていきます。




####⑥Azureアカウントを作成し、キーを取得する

そのためには、azureのそれぞれのサービスに対応するAPIキー（その機能を組み込むための自分専用の鍵のようなもの）を取得する必要があります。

**１.Azureアカウントを作成する**

この記事がわかりやすいです。
Azureアカウント登録方法↓
https://cloud.nissho-ele.co.jp/blog/azure_registration/

個人で使用するレベルでは基本的にお金はかかりません。
（無料アカウントを作成すると、22500円分のクレジットが付与され、これを使い切る又は１ヶ月経過すると従量課金制のアカウントにアップグレードする必要がある。）
https://azure.microsoft.com/ja-jp/free/free-account-faq/

**2.Azureポータルでの作業**
アカウントを作成すると、Azureポータルというサイトにアクセスできます。
![スクリーンショット 2018-11-04 17.59.42.png](https://qiita-image-store.s3.amazonaws.com/0/271893/2187bb4a-bb4a-da20-efe1-b548e004354f.png)

ログインすると一覧ページに飛ぶので、そこからリソースの作成を選択します。
![リソース作成.png](https://qiita-image-store.s3.amazonaws.com/0/271893/f508c468-a525-f73d-3e7a-3b59eaa83726.png)

そこで使いたいサービスを選択します。今回は文字起こしbotを作るので、画像認識ができるAI+machine learningのcomputer visionを選択します。
![サービス選択.png](https://qiita-image-store.s3.amazonaws.com/0/271893/658cdad1-3fe6-b5ab-db73-48f9fbb91396.png)

そうすると入力欄がでてくるので、任意の名前、サブスクリプション（無料版なのでFree trialのはず）、地域（東アジアを選択したが、レファレンスを読むと無料版には関係なさそう）、価格F0　freeを選択又は入力します。リソースグループもおそらく既存のものはないと思うので、任意の名前で作成してください。
![リソース詳細作成.png](https://qiita-image-store.s3.amazonaws.com/0/271893/47957693-f9f8-972b-25ac-fe0e85f89e08.png)

上で作成したリソースを選択するとキーを取得できます。
![スクリーンショット 2018-11-04 17.35.25.png](https://qiita-image-store.s3.amazonaws.com/0/271893/a6f7ca5e-ef34-882b-f301-0510f7b772f0.png)
![キー取得２.png](https://qiita-image-store.s3.amazonaws.com/0/271893/2acd378d-cded-c921-98d8-3db98a735f3f.png)

このように表示されるので、１か２のどちらかをコピーして控えておきます。
![スクリーンショット 2018-11-04 18.30.37.png](https://qiita-image-store.s3.amazonaws.com/0/271893/185055cd-b264-1644-e0c6-5ada288b679f.png)

このキーを先ほどのアクセストークンと同じようにHerokuのconfig varsに設定します。
![スクリーンショット 2018-11-05 10.05.38.png](https://qiita-image-store.s3.amazonaws.com/0/271893/907d2b4e-5a6c-141a-325a-b7d6d2707da8.png)

settings.pyにもキーを足します。

```settings.py
import os


# LINE
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
KEY1 = os.environ["KEY1"]
```





これでazure関係での操作は終わりです！お疲れ様です！


####⑦LINEBotを文字起こしBotに作り変える
新たにvision.pyを作成

```vision.py
import requests

import settings

#先ほどのキーを取ってくる
KEY1 = settings.KEY1

# locationを東アジアで登録した場合のendpoint
endpoint = 'https://eastasia.api.cognitive.microsoft.com/vision/v1.0/ocr'

def get_text_by_ms(image_url=None, image=None):
    if image_url is None and image is None:
        return '必要な情報が足りません'

    params = {'visualFeatures': 'Categories,Description,Color'}

    if image_url:
        headers = {
            'Ocp-Apim-Subscription-Key': KEY1,
            'Content-Type': 'application/json',
        }
        data = {'url': image_url}
        response = requests.post(
            endpoint,
            headers=headers,
            params=params,
            json=data
        )

    elif image is not None:
        headers = {
            'Ocp-Apim-Subscription-Key': KEY1,
            "Content-Type": "application/octet-stream"
        }
        response = requests.post(
            endpoint,
            headers=headers,
            params=params,
            data=image,
        )

    status = response.status_code
    data = response.json()

    if status != 200:

        print(status)

    text = ''
    for region in data['regions']:
        for line in region['lines']:
            for word in line['words']:
                text += word.get('text', '')
                if data['language'] != 'ja':
                    text += ' '
        text += '\n'

    if len(text) == 0:
        text += '文字が検出できませんでした'

    print('text:', text)
    return text
```

今回のjson形式のデータはこのような形だと思われます。
(jsonとは　Javascript Object Notationの略で、データを表現するための一記法)
　　　　　　　　文章dataからregionsキーに対応するひとかたまりの文章の値を取り出し、
    そのひとかたまり文章からlinesキーに対応する一文の値を取り出し、
    その一文からwordsキーに対応する一語句の値を取り出す
    その一語句からtextキーに対応する値を取り出す、何もなければ''を返す
    日本語以外は一語ごとに空白　日本語は１行ごとに改行
　　　　　　　　これによりjson形式のデータを文章に戻します
   

![スクリーンショット 2018-11-04 23.58.29.png](https://qiita-image-store.s3.amazonaws.com/0/271893/67578cc6-9ad8-74a5-7a61-5e6bc08ec510.png)


index.pyも書き換えます。

```index.py
import os
from io import BytesIO

from flask import Flask, abort, request
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (ImageMessage, MessageEvent, TextMessage,
                            TextSendMessage)

import settings
from vision import get_text_by_ms

app = Flask(__name__)


line_bot_api = LineBotApi(settings.YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(settings.YOUR_CHANNEL_SECRET)


@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)
    print("body:", body)

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

    except InvalidSignatureError as e:
        print("InvalidSignatureError:", e)
        abort(400)

    return 'OK'


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

    if (text.startswith('http')):
        image_text = get_text_by_ms(text)
        messages = [
            TextSendMessage(text=image_text),
        ]

    else:
        messages = [
            TextSendMessage(text=text),
            TextSendMessage(text='画像を送信するか、画像のURLを送ってみてね!'),
        ]

    reply_message(event, messages)


@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    print("handle_image:", event)

    message_id = event.message.id
    message_content = line_bot_api.get_message_content(message_id)

    image = BytesIO(message_content.content)

    try:
        image_text = get_text_by_ms(image=image)

        messages = [
            TextSendMessage(text=image_text),
        ]

        reply_message(event, messages)

    except Exception as e:
        reply_message(event, TextSendMessage(text='エラーが発生しました'))


def reply_message(event, messages):
    line_bot_api.reply_message(
        event.reply_token,
        messages=messages,
    )


if __name__ == "__main__":
    port = os.environ.get('PORT', 3333)
    app.run(
        host='0.0.0.0',
        port=port,
    )
```

もう一度Herokuにデプロイして完成！お疲れ様です！

```
$ git add .
$ git commit -m "My first commit"
$ heroku login
$ heroku create "アプリ名"
$ git push heroku master
```

####⑧先ほど友達登録したものが文字起こしBotになっているかを確認！
![スクリーンショット 2018-11-05 10.28.00.png](https://qiita-image-store.s3.amazonaws.com/0/271893/a5a3ca5a-b5ab-f986-31f3-8e67250f1484.png)

読みづらいところも多々あったと思いますが、最後まで読んでいただけて本当に嬉しいです。
ありがとうございました！

参考
https://qiita.com/mikan3rd/items/f0b6ff57dbcedc79a22d
https://datumstudio.jp/blog/azure-computer-vision-api%E3%81%A8face-api%E3%82%92python%E3%81%8B%E3%82%89%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B
https://qiita.com/krocks96/items/67f7510b36945eb9689b