全文検索に引っかかるように、pdf文書にOCR処理をしてテキスト付きpdf化します。
複数ファイルをまとめて処理します。
印刷した活字をキーワードで検索する様な使い方であれば、精度も問題無いと感じました。
今回、追加の学習などはしていませんが、素のままでは手書き文字の変換は難しいですね。
手書きの日本語は「がんばりましょう」な感じでしたが、数字は思った以上に読み取ってくれました。
#作業環境
Windows10 Pro 64bit
Python3.7 / Anaconda
#処理の概要
今回のやり方ではpdfファイルから直接OCR処理できる訳ではありません。今回主役となるtesseractでは、png, jpg等の画像ファイルからでないとテキスト付きpdfが得られないので、下の様に迂回します。
○変換したいpdf
→ページごとに分割 & png化
→OCR処理
→バラバラのテキスト付きpdf
→1つのpdfファイルに結合
「変換したいpdf」が複数ある場合は、これを繰り返します。
#作業手順
「最新のポルシェが最良のポルシェである」の言葉に従い、一番下の各記事を参考にして、それぞれ現時点で最新っぽいVer.をインストールしました。ポルシェに乗ったことはありません。
1)poppler-0.68.0、poppler-data-0.4.10言語ファイルの追加、環境変数pathの設定
ー パスを通すというのが「???」で、理解に時間が掛かりました。
2)tesseract-OCR 5.0.0 alpha/2021-05-06
ー LSTMとか素敵!
3)pipで以下を導入。
ー jupyter内でpipが使えるんですね。
pip install pdf2image # --> 1.15.1
pip install fitz # --> 0.0.1.dev2
pip install PyMuPDF # --> 1.18.14
当初、情報が多かったことから、終盤のpdfファイルの結合は PyPDF2 で検証を進めていましたが、最後の最後で「ファイル名に日本語が含まれているとエラーになる」ことがわかり、fitz(PyMuPDF) に変更しました。
#構成とコード
OCR処理したいpdfファイルを "file_in" に配置、pdfOCR.ipynbを実行すると "operate" で作業し "ocr_out" に出力します(出力処理後、"operate" 下のファイルは削除)。
.
┣ pdfOCR.ipynb(プログラム本体)
┣ file_in(対象ファイル投下用)
┣ operate(作業用)
┗ ocr_out(OCR後のpdf出力用)
import os
import glob
import pdf2image
import fitz
input_dir = 'file_in\\'
operate_dir = 'operate\\'
output_dir = 'ocr_out\\'
only_pdf = '*.pdf' #pdfに限定
only_png = '*.png'
all_f = '*.*'
input_dir_pdf = input_dir + only_pdf
operate_dir_png = operate_dir + only_png
operate_dir_pdf = operate_dir + only_pdf
operate_dir_all = operate_dir + all_f
input_files = glob.glob(input_dir_pdf) #入力pdfファイルのリスト取得
for ipf in input_files:
images = pdf2image.convert_from_path(ipf) #dpi=200
file_name = os.path.splitext(ipf)[0]
for index, image in enumerate(images): #ページごとに分割してpng保存 --> operate_dir
img_fn = file_name + '-' + str('{:0=4}'.format(index+1)) #-0000
img_fn_op = img_fn.replace(input_dir,operate_dir)
image.save(img_fn_op + '.png')
png_files = glob.glob(operate_dir_png) #作業用ディレクトリ内pngのリスト取得
for pnf in png_files: #OCR処理のメイン:png --> pdf+OCR
pdf_name = os.path.splitext(pnf)[0]
!tesseract {pnf} {pdf_name} -l jpn+eng --psm 3 pdf
pdf_files = glob.glob(operate_dir_pdf) #作業用ディレクトリ内pdfのリスト取得
merge_fns=[] #入出力複数ファイル対応用の箱
for iof in input_files: #入力ファイル名 --> 出力ファイル名をつくる
fna = os.path.splitext(iof)[0]
merge_fn = fna + '_OCR_.pdf'
merge_fn_out = merge_fn.replace(input_dir, output_dir)
merge_fns.append(merge_fn_out)
for mfn in merge_fns: #同じファイル名のpdfを結合
mf = os.path.splitext(mfn)[0][:-5]
mfc = mf.replace(output_dir, '')
mg_f = fitz.open() #結合先
for pff in pdf_files:
pf = os.path.splitext(pff)[0][:-5]
pfc = pf.replace(operate_dir, '')
if mfc == pfc:
cmb_f = fitz.open(pff) #結合するページ
mg_f_lp = len(mg_f)
cmb_f_lp = len(cmb_f)
mg_f.insertPDF(cmb_f, from_page=0, to_page=cmb_f_lp, #結合するファイルの最初から最後までを
start_at=mg_f_lp) #結合先の最後のページに続けて
cmb_f.close()
mg_f.save(mfn)
for rmvf in glob.glob(operate_dir_all): #作業用ディレクトリのファイル削除
if os.path.isfile(rmvf):
os.remove(rmvf)
tesseractでは、jpn+eng → eng+jpn にすると精度が変わるかもしれません。また文章の抽出ではなく、キーワード検索のレベルであれば、psm6より3の方が良さそうでした。それほど深く追求した訳ではないですけど。
変換したいpdfファイルは、10,000ページ未満/ファイル を想定してます。
#備考
元のファイル名にスペースが含まれているとtesseractのエラーが出ます。事前に処理しておきましょう。
#参考
ありがたや、ありがたや。
Poppler : PDFのコマンドラインツール
Windows 10でPath環境変数を設定/編集する
Tesseract at UB Mannheim
PythonとTesseract OCRで文字認識
PythonでPDFを複数まとめて自動結合!日本語ファイルを1つに集約
スキャンしたpdfファイルにOCR処理を行い,テキスト埋め込みpdfを作成する
Pythonでファイル・ディレクトリを削除するos.remove, shutil.rmtreeなど
Qiita Markdown 書き方 まとめ