2017年10月16日月曜日

Raspberry PiとPython3とTwitterでOCRボットを作る

Raspberry PiでOCRを使うでRaspberry PiにTesseract-OCRをインストールしてOCRできるようにしたが、これとTwitterを利用して、Python3でOCRボットを作る。


環境


OSはRaspbian Jessie。



事前準備


以下の事項を済ませておく。


PythonラッパーPython-tesseractのインストール


PythonでTesseract-OCRを使うためにPythonラッパーのPython-tesseractをインストールする。ただその前に、libjpeg8-devと画像処理ライブラリのPillowをインストールしておく。libjpeg8-devはjpg画像を扱うのに必要。



続いてPython-tesseractのインストール。


Pillowとpytesseractのバージョンは以下の通り。



Python-tesseractを使ってみる


はじめにTesseract-OCRのパスを確認しておく。


使い方は簡単。tesseractのパスを設定して、OCRする画像を読み込むだけ。日本語OCRの精度に不安はあるが(Raspberry PiでOCRを使う)、とりあえずこれで進める。

ocr_sample.py
1
2
3
4
5
6
7
8
from PIL import Image
import pytesseract
 
# tesseractのパスを設定
pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
 
# text.pngという画像を日本語OCRして結果を出力
print(pytesseract.image_to_string(Image.open('test.png'), lang='jpn'))

Requests-OAuthlibによるStreaming APIの利用


PythonでOCRできるようになったので、次はTwitter APIを利用できるようにする。Python3でTwitterのデータを取得するではTweepyというTwitter APIのPythonラッパーを使ったが、ここではRequests-OAuthlibというHTTPライブラリのRequestsをサポートするOAuth認証ライブラリを使用する。Requests-OAuthlibはpipでインストール。


以下のバージョンがインストールされた。


Twitterが提供するAPIには、過去データを取得できるTwitter APIだけでなく、リアルタイムにデータ取得できるStreaming APIもある。Streaming APIを使ってリアルタイムに特定キーワードを含むツイートを検索してみる。Streaming APIを利用するサンプルはRequestsのページにあるので、これを参考に以下のようなサンプルコードを作成。

streaming_sample.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import json
from requests_oauthlib import OAuth1Session, OAuth1
 
# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'
 
# 認証情報の設定
tw = OAuth1Session(CONSUMER_KEY,
                client_secret=CONSUMER_SECRET,
                resource_owner_key=ACCESS_TOKEN,
                resource_owner_secret=ACCESS_TOKEN_SECRET)
 
# Twitterデータのリアルタイム検索
# 検索キーワードの指定
data={'track':'ラーメン'}
# Streaming APIを使うときはオプションstream=Trueを指定
r = tw.post(url, data=data, stream=True)
 
for line in r.iter_lines():
    if line:
        status = json.loads(line.decode('utf-8'))
 
        # ツイートを表示
        print(status['text'] + '\n')

上記コードをPython3で実行すると、以下のように「ラーメン」を含むツイートが表示される。



OCRボットの作成


要領としては、TwitterとPythonでRaspberry Piを再起動するでやったように、Twitterのダイレクトメッセージを使う。スマホから自分宛にダイレクトメッセージで画像を送信し、Raspberry Piで画像のURLを含むダイレクトメッセージを受信する。画像をダウンロードしてからOCRして、結果を自分宛に返信すると、スマホでOCRの結果を受け取れる。以下のコードを作成。

streaming_ocr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""
Twitterのダイレクトメッセージで送信した画像をOCRして返信する
"""
 
import os
import json
import uuid
from io import BytesIO
 
from requests_oauthlib import OAuth1Session, OAuth1
 
from PIL import Image
import pytesseract
 
# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'
 
# Twitterの認証情報でOAuth1Sessionオブジェクト作成
tw = OAuth1Session(CONSUMER_KEY,
                    client_secret=CONSUMER_SECRET,
                    resource_owner_key=ACCESS_TOKEN,
                    resource_owner_secret=ACCESS_TOKEN_SECRET)
 
# pytesseractのパスを設定
pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
 
def send_dm(sn, text):
    """
    ダイレクトメッセージの送信
    """
    data = {'screen_name':sn, 'text':text}
 
def dl(url):
    """
    ダイレクトメッセージに添付された画像ファイルのダウンロード
    """
    # ダイレクトメッセージに添付された画像のURLにHTTPリクエスト
    r = tw.get(url)
 
    # HTTPリクエストのレスポンスから画像データ作成
    img = Image.open(BytesIO(r.content))
 
    # 画像ファイルの拡張子を取得
    root, ext = os.path.splitext(url)
 
    # UUIDでユニークなファイル名を作成して画像を保存
    uname = str(uuid.uuid4())
    img.save(uname+ext)
 
    return uname+ext
 
def get_text(img_path):
    """
    pytesseractを使ってOCRした結果を返す
    """
    return pytesseract.image_to_string(Image.open(img_path), lang='jpn+eng')
 
def streaming():
    """
    Streaming APIでダイレクトメッセージを受信して、画像があればOCRして
    結果をダイレクトメッセージで送信する
    """
    # Stream APIを使うときはオプションstream=Trueを指定
    r = tw.get('https://userstream.twitter.com/1.1/user.json', stream=True)
 
    print('Ready to OCR')
    for line in r.iter_lines():
        if line:
            line = line.decode('utf-8')
        else:
            continue
 
        if 'direct_message' in line:
            # JSONから辞書型に変換
            dm = json.loads(line)['direct_message']
 
            # 送信者と受信者の一致をチェック
            # 他人からのDirect Messageに反応しないようにするため
            if dm['sender_screen_name'] == dm['recipient_screen_name']:
 
                if 'entities' in dm:
                    if 'media' in dm['entities']:
                        print('DM: {} from @{}'.format(dm['text'], dm['sender_screen_name']))
                        for media in dm['entities']['media']:
                            # 画像ファイルのURL
                            img_url = media['media_url']
 
                            # 画像をダウンロード
                            fname = dl(img_url)
 
                            # ダウンロードした画像をOCR
                            ocr_text = get_text(fname)
                            print(ocr_text)
 
                            # ダイレクトメッセージでOCRの結果を送信
                            send_dm(dm['sender_screen_name'], ocr_text)
 
                            # 画像ファイルの削除
                            os.remove(fname)
 
if __name__ == '__main__':
    streaming()

上記コードをPython3で実行し、スマホで撮った画像をTwitterのダイレクトメッセージで自分宛に送る。送ったのは以下2つの画像。解像度はそれぞれ4050 X 719と3898 X 850。



Twitterの遅延なのか数分遅れることがあるものの、ちゃんと動いている。ただ、OCRの精度はイマイチ。実用は難しいか・・・


0 件のコメント:

コメントを投稿