インストーラーのダウンロード
- UB-Mannheim Tesseract Wiki にアクセスします。
tesseract-ocr-w64-setup-v5.x.x.xxx.exe(64bit版)をダウンロードしてください。
インストール時の注意点
- インストーラーを起動して進めます。
- 重要:
Additional script dataの選択画面で、「Japanese」関係の項目にチェックを入れてください(これがないと日本語が読めません)。Japanese scriptとJapanese vertical(縦書き) などがあります。
- インストール先(パス)をメモしておいてください(通常は
C:\Program Files\Tesseract-OCRです)。
pip install pytesseract pillow pymupdf
import fitz # PyMuPDF
import pytesseract
from PIL import Image
import io
import tkinter as tk
from tkinter import filedialog
import os
# =======================================================
# 【重要】Tesseractのインストール先
# ※環境に合わせて書き換えてください
# =======================================================
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
def mark_exact_text_in_pdf(input_path, output_path, target_text):
"""
画像化されたPDFから「文字単位」で位置を特定し、
検索した文字部分だけをピンポイントで青枠で囲む関数
"""
print("処理を開始します。文字単位の解析には時間がかかります...")
# 検索語句からスペースを除去(OCRの仕様上、スペースは無視されるため合わせる)
clean_target = target_text.replace(" ", "").replace(" ", "")
target_len = len(clean_target)
if target_len == 0:
print("検索文字が空です。")
return
try:
doc = fitz.open(input_path)
total_found = 0
# 拡大率(高解像度にして認識精度を上げる)
zoom = 2.0
mat = fitz.Matrix(zoom, zoom)
for page_num, page in enumerate(doc):
print(f"{page_num + 1}ページ目を解析中...")
# 1. ページを画像データに変換
pix = page.get_pixmap(matrix=mat)
img_data = pix.tobytes("png")
img = Image.open(io.BytesIO(img_data))
img_w, img_h = img.size
# 2. 文字単位でOCRを実行 (image_to_boxes)
# 返り値の形式: "char x1 y1 x2 y2 page" (1行に1文字)
# ※注意: Tesseractのbox出力は座標原点が「左下」です
box_data = pytesseract.image_to_boxes(img, lang='jpn+eng')
# データをパースしてリスト化
chars_info = []
for line in box_data.splitlines():
b = line.split()
if len(b) < 6: continue
char = b[0]
# 座標変換 (左下原点 -> 左上原点)
# x1, y1(下), x2, y2(上)
x1 = int(b[1])
y1_bottom = int(b[2])
x2 = int(b[3])
y2_top = int(b[4])
# 画像座標(左上原点)に変換
# boxのy1は下からの距離なので、上からの距離は H - y2
real_y1 = img_h - y2_top
real_y2 = img_h - y1_bottom
chars_info.append({
"char": char,
"rect": fitz.Rect(x1, real_y1, x2, real_y2)
})
# 3. 全文字を結合した文字列を作る
full_text_stream = "".join([c["char"] for c in chars_info])
# 4. 文字列の中から検索語句を探す
start_index = 0
while True:
# 見つかった場所のインデックスを取得
idx = full_text_stream.find(clean_target, start_index)
if idx == -1:
break # これ以上見つからない
# 5. 見つかった文字たちの枠を結合して一つの枠にする
# 対象となる文字ごとのRectを取得
target_rects = [c["rect"] for c in chars_info[idx : idx + target_len]]
# 最初の文字の枠を基準に、後続の文字の枠を合体(union)させる
union_rect = target_rects[0]
for i in range(1, len(target_rects)):
union_rect |= target_rects[i] # 枠の結合演算
# 6. PDF上の座標に戻す(拡大率で割る)
final_rect = fitz.Rect(
union_rect.x0 / zoom,
union_rect.y0 / zoom,
union_rect.x1 / zoom,
union_rect.y1 / zoom
)
# 青枠描画
page.draw_rect(final_rect, color=(0, 0, 1), width=1.5)
total_found += 1
# 次の検索へ (1文字進める)
start_index = idx + 1
if total_found > 0:
doc.save(output_path)
print(f"\n完了! '{target_text}' を {total_found} 箇所囲いました。")
print(f"保存先: {output_path}")
else:
print(f"\n'{target_text}' は見つかりませんでした。")
doc.close()
except Exception as e:
print(f"\nエラーが発生しました: {e}")
import traceback
traceback.print_exc()
def select_pdf_file():
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(
title="PDFファイルを選択(精密検索版)",
filetypes=[("PDFファイル", "*.pdf")]
)
return file_path
# --- メイン処理 ---
if __name__ == "__main__":
input_file = select_pdf_file()
if input_file:
print(f"選択されたファイル: {input_file}")
search_word = input("検索して囲みたい文字列を入力してください: ")
if search_word:
base_name, ext = os.path.splitext(input_file)
output_file = f"{base_name}_exact_marked{ext}"
mark_exact_text_in_pdf(input_file, output_file, search_word)
else:
print("文字が入力されませんでした。")
else:
print("ファイルが選択されませんでした。")

コメント