社会人研究者が色々頑張るブログ

pythonで画像処理やパターン認識をやっていきます

Python+tesseractでOCR

はじめに

TesseractはコマンドラインベースのOCR光学文字認識)ライブラリです。
前回、Ubuntuにインストールして色々遊んでみました。
nsr-9.hatenablog.jp

tesseractは単体だと単純なOCR機能しか提供されていませんが、様々な言語によるラッパーライブラリが公開されています。
もちろんpythonによるものも公開されているので、今回はpythonラッパーであるPyOCRを用いてちょっと複雑な使い方を試してみます。

f:id:nsr_9:20210812155405p:plain 今回はこんな感じの出力を得たいと思います。

PyOCRとは

PyOCR[1]はOCR用のラッパーライブラリです。
今回はtesseractのラッパーとしてしか使っていませんが、どうやら他のOCRも扱える仕組みになっているみたいです。(今度試してみます)
tesseractのコマンドライン版では画像を与えて認識結果を受け取る方法しかやり方が分からなかったのですが、ラッパーライブラリを用いることで認識した単語のBounding Boxを取得する事ができるみたいです。
*Bounding Boxは検出対象の矩形領域です。一般的に左上の座標(X1, Y1)と右下の座標(X2, Y2)で構成されます。

Bounding Boxが分かることで、認識ミスが生じた時にそれが「未検出」によるものなのか、それとも「読み取りミス」によるものなのか分析できるので、今回はBounding Boxを可視化してみたいと思います。

[1]https://gitlab.gnome.org/World/OpenPaperwork/pyocr

PyOCRのインストール

PyOCRはconda(もしくはpip)で簡単にインストールすることができます。
tesseract本体は別途インストールする必要があるので、以前の記事を参照しインストールしてください。
https://nsr-9.hatenablog.jp/entry/2021/08/09/151749

tesseract本体をインストール後、以下のコマンドでPyOCRを導入することができます。

conda install pyocr

(pipで入れる場合は、condaの部分をpipに変えてください)

PyOCRの使い方

pythonで画像を読み込み、OCRの結果を得るまでの簡単なコードを以下に記載します。

from PIL import Image
import sys
import pyocr


if __name__ == "__main__":
    tools = pyocr.get_available_tools() # インストール済みのOCRに応じて複数のインスタンスが読み込まれる
    tool = tools[0] # 多分tesseractが優先される?
    
    img = Image.open("image.png") #opencvじゃだめだった
    out_text = tool.image_to_string(img, lang="jpn")

非常に簡単に使えますね。以下の画像に対する出力例を示します。

入力画像 出力画像(out_textをprint)
f:id:nsr_9:20210809124839p:plain f:id:nsr_9:20210812155804p:plain

Bounding Boxの取得

pyocrはbuilderというオプションにより、様々な追加機能を利用する事ができます。
今回は、単語ごとのBouning Boxの取得にフォーカスを絞って利用してみます。
いきなりドスンとコードを載せてしまいます。

from PIL import Image
import sys
import pyocr


if __name__ == "__main__":
    tools = pyocr.get_available_tools() # インストール済みのOCRに応じて複数のインスタンスが読み込まれる
    tool = tools[0] # 多分tesseractが優先される?
    img = Image.open("image.png") #opencvじゃだめだった
    # Bounding Boxモードで読み込むという意思表示
    builder = pyocr.builders.WordBoxBuilder(tesseract_layout=6)
    boxies = tool.image_to_string(img, lang="jpn", builder=builder)

out_dataの中身は次のようになっています

for box in boxies:
    txt = box.content
    pos = box.position # ((x1, y1), (x2, y2))

txtが検出したBoxに対応した読み取りテキストで、posがBoxの座標になります。
また、検出したBounding Boxは以下のように可視化することができます。

入力画像 BoundigBoxの出力
f:id:nsr_9:20210809124839p:plain f:id:nsr_9:20210812160007p:plain

重複で検出してたり、連結が変な感じがしますが、tesseract内部でDPMatchingみたいな事をしてくれてるんですかね。
こういった実用に向けた実装の難しさはとても良く分かるので、便利なライブラリを公開してくれる人には頭が上がりませんね。

また、box.contentで取得できる文字認識結果をBounding Boxに表示してみます。
文字の書き込みはPILを用いて行います。

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import numpy as np
import pyocr


if __name__ == "__main__":
    img = Image.open("./img.jpg")
    tool = pyocr.get_available_tools()[0]
    builder = pyocr.builders.WordBoxBuilder(tesseract_layout=6)
    boxies = tool.image_to_string(img, lang="jpn", builder=builder)
    draw = ImageDraw.Draw(img)
    font_ttf = "font.ttf" #任意のフォントを指定してください
    draw.font = ImageFont.truetype(font_ttf, 10)
    
    for box in boxies:
        text = box.content
        pos = box.position
        draw.rectangle(pos, fill=None, outline=(255, 0, 255))
        draw.text((pos[0][0], pos[0][1]-10), text, (0, 0, 100))
    
    img.save("out.jpg")

出力結果は次のようになります。

入力画像 出力結果
f:id:nsr_9:20210809124839p:plain f:id:nsr_9:20210812155405p:plain

Bounding Boxの左上に文字認識結果を表示することができました。
これで認識性能を直感的に理解することができますね。

まとめ

Tesseractのラッパーライブラリを用いて、ちょっと複雑な使い方を試してみました。
単純にOCRするだけならばラッパーは特に必要ないのですが、色々なアプリケーションを作っていく上ではこの様なラッパーの導入は必要であると思います。
次はPyOCRを使って色々作っていきたいと思います。