TesseractとPytesseractによる日本語PDFのOCR処理
Tesseract OCR v4とpytesseractを使ってPDFから日本語テキストを抽出し、出力をきれいにするために必要な正規化処理についても扱います。
Tesseract OCR v4とそのPythonラッパーであるpytesseractを使うと、PDFから日本語テキストを抽出できます。
題材とするのは、太宰治による、現在パブリックドメインとなっている作品「走れメロス」です。
必要なもの
始める前に、以下のライブラリがインストールされていることを確認してください。
さらに、Tesseract OCR自体もインストールしておく必要があります。この記事では、Dockerを使ってセットアップしています。詳細は公式リポジトリの手順を参照してください。
構築
Pythonスクリプトの作成
import refrom datetime import datetime
import pdf2imageimport pytesseract
def to_images(pdf_path: str, first_page: int = None, last_page: int = None) -> list: """ Convert a PDF to a PNG image.
Args: pdf_path (str): PDF path first_page (int): First page starting 1 to be converted last_page (int): Last page to be converted
Returns: list: List of image data """
print(f'Convert a PDF ({pdf_path}) to a png...') images = pdf2image.convert_from_path( pdf_path=pdf_path, fmt='png', first_page=first_page, last_page=last_page, ) print(f'A total of converted png images is {len(images)}.') return images
def to_string(image) -> str: """ OCR an image data.
Args: image: Image data
Returns: str: OCR processed characters """
print(f'Extract characters from an image...') return pytesseract.image_to_string(image, lang='jpn')
def normalize(target: str) -> str: """ Normalize result text.
Applying the following: - Remove new line. - Remove spaces between Japanese characters.
Args: target (str): Target text to be normalized
Returns: str: Normalized text """
result = re.sub('\n', '', target) result = re.sub('([あ-んア-ン一-鿐])\s+((?=[あ-んア-ン一-鿐]))', r'\1\2', result) return result
def save(result: str) -> str: """ Save the result text in a text file.
Args: result (str): Result text
Returns: str: Text file path """
path = 'result.txt' with open(path, 'w') as f: f.write(result) return path
def main() -> None: start = datetime.now() result = ''
images = to_images('run-melos.pdf', 1, 2) for image in images: result += to_string(image) result = normalize(result) path = save(result)
end = datetime.now() duration = end.timestamp() - start.timestamp()
print('----------------------------------------') print(f'Start: {start}') print(f'End: {end}') print(f'Duration: {int(duration)} seconds') print(f'Result file path: {path}') print('----------------------------------------')
if __name__ == '__main__': main()Dockerfileの作成
6行目のrun-melos.pdfはこちらからダウンロードできます。
FROM python:3.10
WORKDIR /usr/src/appCOPY app.py ./COPY requirements.txt ./COPY run-melos.pdf ./RUN apt update && apt install -y poppler-utils tesseract-ocr tesseract-ocr-jpn \ && pip install -r requirements.txt
CMD ["python", "app.py"]# CMD ["/bin/sh", "-c", "while :; do sleep 10; done"]テスト
このスクリプトは、pdf2imageでPDFをPNG画像データに変換し、Tesseract OCRとpytesseractで画像から文字を抽出し、その結果をテキストファイルに保存します。
NAME=pytesseract-sampledocker build -t $NAME .docker run --name $NAME $NAME
# You can see OCR processing result in `result.txt`.docker cp $NAME:/usr/src/app/result.txt ./less result.txt
# Clean updocker container rm $NAMEdocker image rm $NAME結果
結果は以下からダウンロードできます。
まとめ
パブリックドメインの日本語PDFをpdf2imageとTesseractに通してDockerコンテナ内で処理したところ、元のテキストとほぼ一致する読みやすいテキストが抽出できました。pytesseract.image_to_stringに渡すlang='jpn'引数がTesseractの日本語学習済みモデルを有効にする鍵ですが、読みやすい出力を得るにはnormalizeステップの正規表現も重要です。これはTesseractが日本語の文字の間に挿入しがちな空白を取り除くもので、これがないと、認識自体は正確であっても抽出されたテキストが断片的に見えてしまいます。「走れメロス」の元テキストとの比較は、ざっと目を通すだけでなく実際に突き合わせて行う価値があります。見た目の似た漢字同士の誤読は、軽く確認しただけでは見逃しやすいためです。今回のPDFはスキャンではなく、デジタルで生成されたクリーンなパブリックドメインテキストであるため、この精度はほぼ最良のケースを示していると言えます。解像度の低い、あるいはスキャンされた元資料であれば、同程度の結果を得るためにはOCRの前に画像の前処理が必要になるでしょう。
Related posts
PythonでOPC UAの変数ノードを監視する
opcua-asyncioを使ってOPC UAの変数ノードの変更を購読し、ポーリングではなくサーバーからのプッシュ更新にクライアントが反応するようにします。
インターネット接続なしで Python パッケージをインストールする
インターネットに接続できない環境向けに、別のマシンで pip download したパッケージを転送し、--find-links と --no-index でオフラインインストールする方法。
EC2上でProxy.pyを軽量HTTPプロキシとして動かす
Proxy.pyにはそれ自体の認証機構がないため、EC2インスタンス上で動かしつつSSHトンネル経由で安全にアクセスする方法です。

保守性とテストしやすさのための依存性注入
密結合なTypeScriptクラスを、モックでテストできるクラスへとリファクタリングする。給与計算機、システムクロック、SESを対象に依存性注入を適用する。
Jasmine でモックオブジェクトのプロパティをスパイする
モックオブジェクトのプロパティをスパイする際に発生する Jasmine の "already been spied upon" エラーを、Object.getOwnPropertyDescriptor で回避する方法。
