Python3でPyOCRを用いてPacerのシェア画像から歩数抽出
Pacer(iOS, Android)という歩数管理のアプリではTwitterやFacebookに歩数などの記録を画像としてシェアできます。今回はその画像から文字部分を抽出して文字として認識可能なように挑戦したもので、環境は Windows 7, Python 3.5.1 です。
Pacerでシェアした抽出対象の画像
- 当初種類1に分類している画像を利用していましたが、日付が入っていないので種類2に分類している画像を利用するようにしました。
iPhone SEでキャプチャした画像
種類1 | 種類2 |
---|---|
1.jpg | 2.jpg |
3.jpg | 4.jpg |
Windowsなので付属のペイントでルーラー
とグリッド線
を有効にして座標を確認
- 赤枠の部分が抽出対象です。
必要なアプリケーション及びPythonモジュールを用意
Tesseract-OCR
- 画像から文字列を抽出するにあたり、OCRを利用します。
https://github.com/tesseract-ocr/tesseract#installing-tesseract - 抽出対象が数字やアルファベットの場合、日本語辞書は必要ありません。
PyOCR, PIL, numpy
- Pythonから上記OCRを利用するモジュールpyocrを利用しています。
- 画像操作でPIL(Pillow)が必要になります。
- 画像を2値化する際numpyを利用しています。
- インストール時点のバージョンはそれぞれ pyocr (0.4.2), Pillow (3.4.2), numpy (1.11.2) です。
Windows
python -m pip install pyocr python -m pip install Pillow python -m pip install numpy
LinuxやmacOS
pip install pyocr pip install Pillow pip install numpy
コード
- 当初全体から抽出を試みたものの、どこからどこまでが何の情報かわからないので対象部分を切り抜いてから文字抽出を試みるのが良さそうです。
- カラー画像だと抽出精度が低くなるようなので2値化しています。
# -*- encoding: utf-8 -*- from PIL import Image, ImageDraw import numpy, os, pyocr, pyocr.builders, re, sys tools = pyocr.get_available_tools() if len(tools) == 0: sys.exit('Not Found OCR tools') MIN_COLOR = 0 MAX_COLOR = 255 def retrieveStr(image_path): result = {} # 画像読み込み img = Image.open(image_path) # 文字を認識しやすいよう閾値200で白か黒の2値に変換 array = numpy.asarray(img.convert('L')) array.flags.writeable = True w, h = img.size for y in range(h): for x in range(w): array[y, x] = MAX_COLOR if (array[y, x] > 200) else MIN_COLOR img = Image.fromarray(numpy.uint8(array)) positions = {} # 1ピクセル目のRGBで画像種類を判断 r, g, b = img.crop((0, 0, 1, 1)).convert('RGB').getpixel((0, 0)) if r == MAX_COLOR: # 活動時間 positions['active time'] = (190, 70, 440, 140) # カロリー positions['calories'] = (10, 70, 170, 140) # Km positions['km'] = (450, 70, 630, 140) # 歩数 positions['steps'] = (170, 280, 470, 390) else: # 活動時間 positions['active time'] = (220, 410, 420, 460) # カロリー positions['calories'] = (80, 410, 210, 460) # 日付 positions['date'] = (190, 130, 450, 165) # Km positions['km'] = (430, 410, 560, 460) # 歩数 positions['steps'] = (220, 220, 440, 300) # 日本語辞書を使わないので`年月日`を塗りつぶす dr = ImageDraw.Draw(img) # [(始点x,始点y),(終点x,終点y)],fill=塗つぶし色 dr.rectangle([(275, 130), (305, 160)], fill=MAX_COLOR) # 年 dr.rectangle([(340, 130), (365, 160)], fill=MAX_COLOR) # 月 dr.rectangle([(405, 130), (430, 160)], fill=MAX_COLOR) # 日 for label, pos in positions.items(): # 欲しい部分だけ画像を切り抜く im = img.crop(pos) # 拡大縮小 # im.thumbnail((im.size[0] / 3, im.size[1] / 3), Image.ANTIALIAS) # im = im.resize((im.size[0] * 3, im.size[1] * 3)) # 画像表示 # im.show() # 画像から文字抽出 text = tools[0].image_to_string( im, builder=pyocr.builders.TextBuilder(tesseract_layout=6) ) # バイト列をUTF-8でエンコードしてUTF-8の文字列にデコード text = text.encode('utf_8').decode('utf_8') # 時間を取り出して秒に変える if label == 'active time': at = re.split('[^\d]+', text)[:-1] hours = 0 if len(at) == 1 else int(at[0]) minutes = int(at[len(at) - 1]) text = (hours * 60 * 60) + (minutes * 60) # str -> int elif label == 'calories': text = int(text) # 日付整理 elif label == 'date': text = '-'.join(re.split('[^\d]+', text)) # str -> float elif label == 'km': text = float(text) # 数字以外の文字を詰める elif label == 'steps': text = int(''.join(re.split('[^\d]+', text))) result[label] = text return result if __name__ == '__main__': base_dir = os.path.dirname(__file__) if not base_dir: base_dir = '.' img_dir = base_dir + os.path.sep + 'img' + os.path.sep for i in range(4): res = retrieveStr(img_dir + str(i + 1) + '.jpg') for val in res.items(): print('{}: {}'.format(*val)) print()
結果
c:\>python C:\py\ocr\img.py steps: 17589 active time: 9300 km: 13.9 calories: 640 date: 2016-10-31 steps: 18709 active time: 10140 km: 15.0 calories: 685 steps: 42177 active time: 22080 km: 33.4 calories: 1449 date: 2016-11-01 steps: 11488 active time: 6720 km: 9.0 calories: 390 c:\>
参考