以前に、PDFからテキスト抽出をしてデータフレームにする方法を紹介しました。
ですが、実際には紙の資料をスキャンしてPDF化した資料も多々あり、その場合は純粋なテキスト抽出ができません。
そこで今回は、OCRという画像から文字を読み取る方法で、スキャンされた資料のPDFからテキストを抽出する方法を紹介します。
処理の流れ
今回の処理の一連の流れは、
- PDFの各ページをそれぞれ画像に変換
- OCRで各画像から文字を読み取る
- ページ内での文字の位置情報も含めたデータフレームに変換
です。
事前準備
Tesseractのインストール
画像からのテキスト抽出には、Tesseractというオープンソースのソフトウェアを用います。光学式文字認識エンジンであるTesseractは、オープンソースながら精度も十分と評判です。
Tesseractのインストール手順は、以下サイトが非常に参考になります(Windows)。
https://gammasoft.jp/blog/tesseract-ocr-install-on-windows/#download
読み取るPDF
今回読み取るPDFは、2012年のとある論文とします。
このPDFではそのままテキスト抽出もできますが、今回は画像からOCRでテキスト抽出していきます。
プログラム
コード全容
まずは、今回のプログラム全容を紹介します。
import os
import pathlib
from pdf2image import convert_from_path
import sys
import pyocr
import pyocr.builders
import pandas as pd
#画像ごとにOCR実行
def exe_ocr(imgs):
# tesseract-OCRのパスを通す
tessera_path = "C:\Program Files\Tesseract-OCR"
# pathsepは環境変数に追加するときの区切り;
os.environ["PATH"] += os.pathsep + str(tessera_path)
tools = pyocr.get_available_tools()
if len(tools) == 0:
print("No OCR tool found")
sys.exit(1) # 引数1は終了ステータスで1を返す
tool = tools[0]
df = pd.DataFrame()
index = 0
#画像の数(ページ数)ごとの処理
for i, img in enumerate(imgs):
#OCR実行。WordBoxBuilderでは座標も取得可能
word_boxes = tool.image_to_string(
img,
lang="eng",
builder=pyocr.builders.WordBoxBuilder(tesseract_layout=6)
)
#文字の塊ごとに分けてデータフレームに変換
for box in word_boxes:
df_page = pd.DataFrame({
"x_start":box.position[0][0],
"x_end" :box.position[1][0],
"y_start":box.position[0][1],
"y_end" :box.position[1][1],
"text" :box.content,
"page" :i+1},index=[index]
)
df = df.append(df_page)
index += 1
print(word_boxes)
return df
##ここから
if __name__ == '__main__':
# PDFファイルのパス
pdf_path = pathlib.Path('./input/pxc3882784.pdf')
#PDFの各ページを画像に変換
imgs = convert_from_path(pdf_path)
df = exe_ocr(imgs)
PDFを画像に変換
convert_from_path()で指定したパスのPDFファイルをページごとの画像に変換します。このとき画像はnumpy配列として格納されます。このとき、PDFのパスはpathlib.Path()形式で渡す必要があります。
ちなみに画像を1つずつ出力すると、以下のように1ページずつ分けられているのがわかります。
OCR実行
関数exe_ocrの内部でOCRを実行します。
まずはTesseractのパスを一時的に環境変数に登録します。
# tesseract-OCRのパスを通す
tessera_path = "C:\Program Files\Tesseract-OCR"
# pathsepは環境変数に追加するときの区切り;
os.environ["PATH"] += os.pathsep + str(tessera_path)
toolsには呼び出したTesseractのモジュールを格納します。ここで、Tesseractをうまく呼び出せない場合は処理終了としています。
tools = pyocr.get_available_tools()
if len(tools) == 0:
print("No OCR tool found")
sys.exit(1) # 引数1は終了ステータスで1を返す
**OCRに使用するtoolはtoolsの0番目を指定します。
OCRは画像1枚(1ページ)ずつ行います。toolの image_to_string()メソッドでOCRを実行します。このとき、builderに pyocr.builders.WordBoxBuilder()を指定することで、テキストだけでなくその座標情報も取得することができます。
今回は英語の論文なので、lang=”eng”としています。日本語の場合は”jpn”としてください。
#OCR実行。WordBoxBuilderでは座標も取得可能
word_boxes = tool.image_to_string(
img,
lang="eng",
builder=pyocr.builders.WordBoxBuilder(tesseract_layout=9)
)
tesseract_layout
pyocr.builders.WordBoxBuilder()ではtesseract_layoutを設定しています。これはテキスト読み取りルールの指定パラメータで、デフォルトは3です。このパラメータによって読み取り精度も大きく変わってくるようです。
0 | Orientation and script detection (OSD) only. |
1 | Automatic page segmentation with OSD. |
2 | Automatic page segmentation, but no OSD, or OCR |
3 | Fully automatic page segmentation, but no OSD. (Default) |
4 | Assume a single column of text of variable sizes. |
5 | Assume a single uniform block of vertically aligned text. |
6 | Assume a single uniform block of text. |
7 | Treat the image as a single text line. |
8 | Treat the image as a single word. |
9 | Treat the image as a single word in a circle. |
10 | Treat the image as a single character. |
ちなみに、これの公式ドキュメントはどこかにあるんですか??
データフレームに変換
ここからは、必要な情報をデータフレームにまとめていきます。
テキスト本文とテキストを囲った時の座標、ページ番号をともにまとめていきます。
#文字の塊ごとに分けてデータフレームに変換
for box in word_boxes:
df_page = pd.DataFrame({
"x_start":box.position[0][0],
"x_end" :box.position[1][0],
"y_start":box.position[0][1],
"y_end" :box.position[1][1],
"text" :box.content,
"page" :i+1},index=[index]
)
df = df.append(df_page)
index += 1
**空のデータフレームを予め作成しています。
x座標はstart(左)→end(右)、y座標はstart(下)→end(上)を示します。こうして作成したデータフレームは以下のようになります。
テキスト情報とともに座標、ページ番号が出力されています。
まとめ
スキャンされた資料のPDFファイルからテキスト情報を収集する方法を紹介しました。古い資料のPDFなどはこの手のものが多いので、こうやってテキスト抽出ができると便利だと思います。
座標等と組合わせれば、単純なテキスト分析だけでなく、さらに可用性が増しそうです^^
今回のように簡単に実装できるので、是非お試しあれ!
ではでは👋