TesseractとPytesseractによる日本語PDFのOCR処理

TesseractとPytesseractによる日本語PDFのOCR処理

Tesseract OCR v4とpytesseractを使ってPDFから日本語テキストを抽出し、出力をきれいにするために必要な正規化処理についても扱います。

Takahiro Iwasa
5 min read

Tesseract OCR v4とそのPythonラッパーであるpytesseractを使うと、PDFから日本語テキストを抽出できます。

題材とするのは、太宰治による、現在パブリックドメインとなっている作品「走れメロス」です。

必要なもの

始める前に、以下のライブラリがインストールされていることを確認してください。

さらに、Tesseract OCR自体もインストールしておく必要があります。この記事では、Dockerを使ってセットアップしています。詳細は公式リポジトリの手順を参照してください。

構築

Pythonスクリプトの作成

app.py
import re
from datetime import datetime
import pdf2image
import 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こちらからダウンロードできます。

Dockerfile
FROM python:3.10
WORKDIR /usr/src/app
COPY 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 OCRpytesseractで画像から文字を抽出し、その結果をテキストファイルに保存します。

Terminal window
NAME=pytesseract-sample
docker 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 up
docker container rm $NAME
docker image rm $NAME

結果

結果は以下からダウンロードできます。

まとめ

パブリックドメインの日本語PDFをpdf2imageとTesseractに通してDockerコンテナ内で処理したところ、元のテキストとほぼ一致する読みやすいテキストが抽出できました。pytesseract.image_to_stringに渡すlang='jpn'引数がTesseractの日本語学習済みモデルを有効にする鍵ですが、読みやすい出力を得るにはnormalizeステップの正規表現も重要です。これはTesseractが日本語の文字の間に挿入しがちな空白を取り除くもので、これがないと、認識自体は正確であっても抽出されたテキストが断片的に見えてしまいます。「走れメロス」の元テキストとの比較は、ざっと目を通すだけでなく実際に突き合わせて行う価値があります。見た目の似た漢字同士の誤読は、軽く確認しただけでは見逃しやすいためです。今回のPDFはスキャンではなく、デジタルで生成されたクリーンなパブリックドメインテキストであるため、この精度はほぼ最良のケースを示していると言えます。解像度の低い、あるいはスキャンされた元資料であれば、同程度の結果を得るためにはOCRの前に画像の前処理が必要になるでしょう。

About the author

Takahiro Iwasa

Takahiro Iwasa

Software Developer

This blog shares technical notes from hands-on projects—architecture, implementation, and AWS service integrations.