fix
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Measure how legible each page of the photocopied manual is.
|
||||
|
||||
check_legibility.py copied.pdf [crisp.pdf]
|
||||
|
||||
OCRs every page (tesseract) and scores it against the PDF's own invisible text
|
||||
layer, which is the known-correct text: the score is the share of true words
|
||||
that OCR recovers. If a crisp render is given, its score is the baseline (OCR
|
||||
is imperfect on monospace tables), and a page is judged by how much the copying
|
||||
cost it. Needs tesseract and poppler's pdftotext/pdftoppm. Development tool;
|
||||
the build does not run it.
|
||||
"""
|
||||
import difflib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def words(text):
|
||||
return re.findall(r"[A-Za-z0-9][A-Za-z0-9'.\-]*", text)
|
||||
|
||||
|
||||
def ocr_scores(pdf, dpi=130):
|
||||
"""Return {page: (recall, truth_word_count)} for each page of pdf."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmp = Path(tmp)
|
||||
subprocess.run(["pdftoppm", "-r", str(dpi), "-gray", "-png", pdf, str(tmp / "p")], check=True)
|
||||
images = sorted(tmp.glob("p-*.png"))
|
||||
|
||||
def score(img):
|
||||
n = int(img.stem.split("-")[1])
|
||||
truth = words(subprocess.run(["pdftotext", "-f", str(n), "-l", str(n), pdf, "-"],
|
||||
capture_output=True, text=True, check=True).stdout)
|
||||
got = words(subprocess.run(["tesseract", str(img), "-", "--psm", "6"],
|
||||
capture_output=True, text=True, env={**os.environ, "OMP_THREAD_LIMIT": "1"}).stdout)
|
||||
m = difflib.SequenceMatcher(None, truth, got, autojunk=False)
|
||||
hit = sum(b.size for b in m.get_matching_blocks())
|
||||
return n, (hit / max(1, len(truth)), len(truth))
|
||||
|
||||
with ThreadPoolExecutor(4) as pool:
|
||||
return dict(pool.map(score, images))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
copied = ocr_scores(sys.argv[1])
|
||||
crisp = ocr_scores(sys.argv[2]) if len(sys.argv) > 2 else {}
|
||||
print("page words crisp copied loss")
|
||||
losses = []
|
||||
for n in sorted(copied):
|
||||
c, w = copied[n]
|
||||
b = crisp.get(n, (1.0, w))[0]
|
||||
losses.append(b - c)
|
||||
flag = " <-- check" if b - c > 0.10 else ""
|
||||
print(f"{n:4d} {w:6d} {b:5.0%} {c:5.0%} {b - c:+5.0%}{flag}")
|
||||
tot = sum(w for _, w in copied.values())
|
||||
print(f"\nmean copied recall {sum(c * w for c, w in copied.values()) / tot:.1%}"
|
||||
+ (f", mean crisp {sum(c * w for c, w in crisp.values()) / tot:.1%}" if crisp else ""))
|
||||
Reference in New Issue
Block a user