JEI_Import_Documents-App/scripts/ocr_invoices.py

88 lines
2.9 KiB
Python
Raw Normal View History

"""
OCR scanned PDFs using Tesseract + PyMuPDF (no poppler needed).
Reads each unparseable PDF page-by-page, renders to image, OCRs the image,
and saves the text to data/review/ocr/<invoice_no>.txt.
Then merges with the text-based extraction by writing OCR'd text back into
a fake pdfplumber-compatible page object via monkey-patching.
Run: python scripts/ocr_invoices.py
"""
import os, sys, subprocess, json
from pathlib import Path
import fitz # PyMuPDF
ROOT = Path(__file__).resolve().parent.parent
INVOICES = ROOT / "invoices"
OCR_DIR = ROOT / "data" / "review" / "ocr"
# Find tesseract
TESSERACT = None
for p in ["C:/Program Files/Tesseract-OCR/tesseract.exe", "C:/Program Files (x86)/Tesseract-OCR/tesseract.exe"]:
if os.path.exists(p):
TESSERACT = p
break
if not TESSERACT:
sys.exit("tesseract.exe not found - install via winget install UB-Mannheim.TesseractOCR")
OCR_DIR.mkdir(parents=True, exist_ok=True)
def ocr_page_image(img_bytes: bytes) -> str:
"""OCR a single PNG image, return extracted text."""
# Write to temp file
tmp_in = OCR_DIR / "_tmp_in.png"
tmp_out = OCR_DIR / "_tmp_out"
tmp_in.write_bytes(img_bytes)
try:
result = subprocess.run(
[TESSERACT, str(tmp_in), str(tmp_out), "-l", "eng"],
capture_output=True, text=True, timeout=60,
)
out_file = tmp_out.with_suffix(".txt")
if out_file.exists():
return out_file.read_text(encoding="utf-8", errors="replace")
return ""
finally:
tmp_in.unlink(missing_ok=True)
(tmp_out.with_suffix(".txt")).unlink(missing_ok=True)
def ocr_pdf(pdf_path: Path) -> list[str]:
"""Return list of OCR'd text per page."""
texts = []
with fitz.open(pdf_path) as doc:
for page_num, page in enumerate(doc, start=1):
# Render at 3x for better OCR accuracy
mat = fitz.Matrix(3, 3)
pix = page.get_pixmap(matrix=mat)
img_bytes = pix.tobytes("png")
text = ocr_page_image(img_bytes)
texts.append(text)
print(f" {pdf_path.name} page {page_num}/{len(doc)}: {len(text)} chars")
return texts
def main():
pdfs = sorted(p for p in INVOICES.rglob("*.pdf") if "_non_jei" not in p.parts)
errors = 0
for pdf in pdfs:
try:
out_dir = OCR_DIR / pdf.stem.strip()
out_dir.mkdir(exist_ok=True)
# OCR all pages
texts = ocr_pdf(pdf)
for i, text in enumerate(texts, start=1):
(out_dir / f"page_{i:03d}.txt").write_text(text, encoding="utf-8")
# Combined file
combined = "\n=== PAGE BREAK ===\n".join(texts)
(OCR_DIR / f"{pdf.stem.strip()}.txt").write_text(combined, encoding="utf-8")
except Exception as e:
errors += 1
print(f" ERR {pdf.name}: {e}")
print(f"\nDone. OCR text in {OCR_DIR}. Errors: {errors}")
if __name__ == "__main__":
main()