※追記:もっと簡単に実行できるように改良した(Geminiに改良してもらった)。
NDLOCR-LiteでOCRして、透明テキストの付いたPDFを作成(端末だけで実行ver2)
国立国会図書館(NDL)がGPUを必要としないOCRツール NDLOCR-Liteアプリケーション を公開した。 https://github.com/ndl-lab/ndlocr-lite
これを利用すると、紙ベースで書類等をスキャンしたPDFデータをOCRできるだけでなく、PDFに透明テキストをつけることができる。
※以前の記事(NDLOCR-LiteでOCRして、透明テキストの付いたPDFを作成)の方法は、コマンドライン(Powershell)とNDLOCR-LiteのGUIを行ったり来たりしながらやらないといけなかったので、今回はコマンドラインのみでできるようにする。
※ただし、縦書きOCRでしか試していないので、横書きOCRがうまく機能するかは試していないので、あしからず。
下準備
1.NDLOCR-Lite ダウンロード
- NDLOCR-Liteアプリケーションのリポジトリのリリースページより、右上の緑の「Code」>Download zip でzipファイルをダウンロードして展開するか、gitでダウンロード。
- ※リリースされたばかりでバージョンアップが激しいので、こまめにリリースを確認する。
- Zipを展開したできたフォルダを日本語(全角文字)を含まない場所に配置。
- ※NDLOCR-Liteの使い方に「デスクトップアプリケーションを利用する際には、日本語(全角文字)を含まないパスにアプリケーションを配置してください。全角文字を含む場合に起動しないことがあります。」という注意事項がある。
- 「コマンドラインからの利用」を読んで、必要なpythonモジュール(
pip install -r requirements.txtのところ)をインストール。pip install -r requirements.txtがうまく行かないときは、いろいろ自分で頑張って…。
2.PDFTKのインストール
Windowsの場合
Powershellで以下を実行でインストール。
winget install --id=PDFLabs.PDFtk.Server
Macの場合
ターミナルで
brew install pdftk-java
Linux(Ubuntu)の場合
sudo apt install pdftk-java
3. python、PyMuPDFライブラリのインストール
初心者がpythonを入れるのは、PCのMicrosoft Storeからインストールのが一番楽だと思う。
PyMuPDFライブラリは、Powershellで以下を実行してインストール。
pip install pymupdf
4. PDFをpngにするスクリプト(pdftopng.py)を作成
PDFを各ページごとのpng画像にするためのスクリプト。なお、ChatGPTに作ってもらった。
メモ帳などのテキストエディタで以下の内容を pdftopng.py として保存。
import sys
import os
import fitz
from multiprocessing import Pool, cpu_count
def convert_pages(args):
pdf_path, pages, outdir = args
doc = fitz.open(pdf_path)
for page_num in pages:
page = doc.load_page(page_num)
pix = page.get_pixmap(matrix=fitz.Matrix(2,2), alpha=False)
pix.save(f"{outdir}/image-{page_num+1:04d}.png")
doc.close()
def main():
if len(sys.argv) < 2:
print("使い方: python3 pdftopng.py ファイル名.pdf")
sys.exit()
pdf = sys.argv[1]
if not pdf.lower().endswith(".pdf"):
pdf += ".pdf"
outdir = os.path.join("OCR", "image")
os.makedirs(outdir, exist_ok=True)
doc = fitz.open(pdf)
total_pages = len(doc)
doc.close()
cores = cpu_count()
page_lists = [[] for _ in range(cores)]
for i in range(total_pages):
page_lists[i % cores].append(i)
tasks = [(pdf, pages, outdir) for pages in page_lists if pages]
with Pool(cores) as p:
p.map(convert_pages, tasks)
if __name__ == "__main__":
main()
5. PDFにJSONファイルの文字情報を重ねたり、PDFやテキストを結合するなどのスクリプト(makeOCR_PDF.py)を作成
メモ帳などのテキストエディタで、以下の内容を「makeOCR_PDF.py」として保存。
これはGeminiに作成してもらった。
※ただし、縦書きOCRでしか試していないので、横書きOCRがうまく機能するかは試していないので、あしからず。
import json
import os
import sys
import glob
import subprocess
from PIL import Image
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib.colors import blue
# --- 基本設定 ---
TARGET_DIR = './OCR/image/'
# 親ディレクトリ基準の保存先
PDF_FINAL_DIR = '../OCR_PDF/'
TXT_FINAL_DIR = '../OCR_TXT/'
def create_pdf_from_json(json_path, img_path, output_path):
"""個別PDF生成ロジック"""
try:
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
img = Image.open(img_path)
img_w, img_h = img.size
c = canvas.Canvas(output_path, pagesize=(img_w, img_h))
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiMin-W3', isVertical=True))
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5', isVertical=False))
c.drawImage(img_path, 0, 0, width=img_w, height=img_h)
c.setFillColor(blue, alpha=0.0) # 透明設定
for item in data['contents'][0]:
text = item['text']
bbox = item['boundingBox']
x_coords = [p[0] for p in bbox]; y_coords = [p[1] for p in bbox]
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
width, height = x_max - x_min, y_max - y_min
if height > width: # 縦書き
font_size = width * 0.7
x_pdf = x_max - (font_size * 0.7)
y_pdf = img_h - y_min
c.setFont('HeiseiMin-W3', font_size)
c.drawString(x_pdf, y_pdf, text)
else: # 横書き
font_size = height * 0.7
x_pdf = x_min
y_pdf = img_h - y_max
c.setFont('HeiseiKakuGo-W5', font_size)
c.drawString(x_pdf, y_pdf, text)
c.save()
return True
except Exception as e:
print(f"Error: {e}")
return False
def main():
# 1. 引数のチェック
if len(sys.argv) < 2:
print("使用法: python3 make.py [出力ファイル名]")
print("例: python3 make.py sample_01")
return
output_name = sys.argv[1] # コマンドラインからの引数
# 保存先ディレクトリの準備
abs_pdf_dir = os.path.abspath(os.path.join(TARGET_DIR, PDF_FINAL_DIR))
abs_txt_dir = os.path.abspath(os.path.join(TARGET_DIR, TXT_FINAL_DIR))
os.makedirs(abs_pdf_dir, exist_ok=True)
os.makedirs(abs_txt_dir, exist_ok=True)
# 2. 個別PDFの生成
json_files = sorted(glob.glob(os.path.join(TARGET_DIR, "image-*.json")))
if not json_files:
print("対象ファイルが見つかりません。")
return
for json_file in json_files:
base = os.path.splitext(os.path.basename(json_file))[0]
img_file = os.path.join(TARGET_DIR, f"{base}.png")
pdf_out = os.path.join(TARGET_DIR, f"{base}.pdf")
if os.path.exists(img_file):
create_pdf_from_json(json_file, img_file, pdf_out)
# 3. PDFの結合 (pdftk)
print(f"PDFを結合中: {output_name}.pdf")
combined_pdf_path = os.path.join(abs_pdf_dir, f"{output_name}.pdf")
subprocess.run(f"pdftk *.pdf cat output \"{combined_pdf_path}\"", shell=True, cwd=TARGET_DIR)
# 4. テキストの結合 (PowerShell)
print(f"テキストを結合中: {output_name}.txt")
combined_txt_path = os.path.join(abs_txt_dir, f"{output_name}.txt")
ps_command = f'Get-Content -Encoding UTF8 .\\*.txt | Out-File -FilePath "{combined_txt_path}" -Encoding UTF8'
subprocess.run(["powershell", "-Command", ps_command], cwd=TARGET_DIR)
# 5. 後片付け (削除)
print("中間ファイルを削除中...")
extensions = ['*.pdf', '*.txt', '*.png', '*.json', '*.xml']
for ext in extensions:
for f in glob.glob(os.path.join(TARGET_DIR, ext)):
os.remove(f)
print(f"完了しました。出力先:\n PDF: {combined_pdf_path}\n TXT: {combined_txt_path}")
if __name__ == "__main__":
main()
6. ディレクトリ構成
ディレクトリ(フォルダ)構成を以下のようにする。
任意ディレクトリ
├─入力.pdf
├─pdftopng.py
├─makeOCR_PDF.py
└─OCR
├─image
├─OCR_PDF
└─OCR_TXT
任意ディレクトリにOCRをしたいPDF、pdftopng.py、makeOCR_PDF.pyを入れ、
その下位ディレクトリにOCRフォルダ、
さらに下位ディレクトリにimageフォルダ、OCR_PDFフォルダ、OCR_TXTフォルダを配置する。
作業手順
1.PDF→png
PDFやpdftopng.pyが入っているフォルダ内で右クリック、「ターミナルで開く」でPowerShellを起動。以下を実行。 pdfが各ページごとのpngファイルに変換され、OCR>imageフォルダに入る。
python3 pdftopng.py "入力.pdf"
"入力.pdf"のところは、""(ダブルクォーテーションマーク)の中にカーソルを持ってきて、pdfファイルをドラッグ・アンド・ドロップをすればよい。
2.NDLOCR-LiteでOCR
ターゲットフォルダ(--sourcedir)OCR>imageフォルダで、出力データ保存先(--output)も同じOCR>imageフォルダで、OCR。
python3 "D:\D_Program_Files\ndlocr-lite-master\src\ocr.py" --sourcedir "D:\Documents\test\OCR\image" --output "D:\Documents\test\OCR\image"
D:\D_Program_Files\ndlocr-lite-master\src\ocr.pyやD:\Documents\test\OCR\imageのところは、自分の環境に合わせて適宜修正を。
3.PDFにJSONファイルの文字情報を重ねたり、PDFやテキストを結合するなど
OCR後、以下を実行。
OCR_PDFフォルダに透明テキスト付PDFが、OCR_TXTフォルダに結合されたテキストファイルが入って、imageフォルダ内のpdf、txt、png、json、xmlファイルが削除される。
python3 makeOCR_PDF.py 出力ファイル名
おまけ:テキスト内のキーワード検索
「OCR-TXT」フォルダ内の全てのtxtファイルを対象に、一瞬でキーワード検索できる。
「OCR-TXT」フォルダ内で右クリック、「ターミナルで開く」でPowerShellを起動。以下のように実行。
Windowsの場合
Select-String "検索したいキーワード" *.txt | Format-Table Filename, LineNumber, Line
and検索の例
Select-String "師範學校" *.txt | Select-String "鹿兒島" | Format-Table Filename,LineNumber,Line
MacやLinuxの場合
grep --color=always -n -H "検索したいキーワード" *.txt | column -t -s ":"
and検索の例
grep --color=always -n -H "師範學校" *.txt | grep --color=always -n -H "鹿兒島" | column -t -s ":"
余談
ターミナルのフォントはUDEV Gothicを使うと良い。
yuru7/udev-gothic: UDEV Gothic