GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - SniperJF/zone-robust-ocr-align: Word-level alignment of OCR and ground truth under zoning and reading-order errors

BranchesTags
Open more actions menu

Latest commit

 

History

1 Commit
1 Commit

Folders and files

NameName
Last commit message
Last commit date
docs
docs
 
 
tests
tests
 
 
.gitignore
.gitignore
 
 
FROZEN_PARAMS.md
FROZEN_PARAMS.md
 
 
FROZEN_PARAMS_V2.md
FROZEN_PARAMS_V2.md
 
 
LICENSE
LICENSE
 
 
NOTICE
NOTICE
 
 
README.md
README.md
 
 
common_eval.py
common_eval.py
 
 
download_impact_tifs.py
download_impact_tifs.py
 
 
ext_tools.py
ext_tools.py
 
 
impact_data.py
impact_data.py
 
 
multiregion_align.py
multiregion_align.py
 
 
multiregion_v2.py
multiregion_v2.py
 
 
ocr_alignment_prototype.py
ocr_alignment_prototype.py
 
 
pyproject.toml
pyproject.toml
 
 
requirements.txt
requirements.txt
 
 
smoke_mr.py
smoke_mr.py
 
 
step1_eval.py
step1_eval.py
 
 
step3c_ablation.py
step3c_ablation.py
 
 
step3d_impact_eval.py
step3d_impact_eval.py
 
 
step4_stress.py
step4_stress.py
 
 
step4_trec_all.py
step4_trec_all.py
 
 
step4_trec_synth.py
step4_trec_synth.py
 
 
step5_realocr.py
step5_realocr.py
 
 
step6_impact_geogold.py
step6_impact_geogold.py
 
 
step7_longdoc.py
step7_longdoc.py
 
 
step8_v2_dev.py
step8_v2_dev.py
 
 
step8_v2_eval.py
step8_v2_eval.py
 
 
step9_wordgold.py
step9_wordgold.py
 
 

Repository files navigation

zone-robust-ocr-align

Word-level alignment of OCR output to ground-truth (GT) text when the OCR reading order is wrong. Zoning errors include interleaved columns, displaced blocks and swapped paragraphs. The repository also holds the CPU-only evaluation scripts behind the tables of the paper (see Citation).

Contents:

  • Aligners (pure Python, depend only on Levenshtein):
    • LocationAwareAligner: the 2019 location-aware heuristic of Fonseca Cacho & Taghva (step1_eval.py)
    • ReanchoringAligner: the 2019 heuristic plus n-gram re-anchoring after failure streaks (step1_eval.py)
    • SimpleRETAS ("RETAS-lite"): unique-word anchors plus local search (step1_eval.py)
    • MultiRegionAligner (multiregion v1): unique word/n-gram anchors, then greedy assignment of OCR chunks to GT windows, then local edit-distance fill (multiregion_align.py; frozen defaults in FROZEN_PARAMS.md)
    • MultiRegionAlignerV2 (multiregion v2): v1 plus faster chunk assignment, a long-document fallback and an anchor-scarcity fallback (multiregion_v2.py; frozen defaults in FROZEN_PARAMS_V2.md)
  • Synthetic zoning generator with exact word-level gold (step1_eval.generate_zoning_pair), plus metrics: precision, recall, F1, cascade length and recovery@N (step1_eval.score_alignment).
  • Evaluation scripts for each paper table (see Reproduce the paper).

ocr_alignment_prototype.py is the original 2019-era prototype, kept for provenance. Its built-in demo has measurement bugs that step1_eval.py corrects (see that file's docstring), so do not use it for evaluation.

Install

Python 3.9+.

git clone https://github.com/sniperjf/zone-robust-ocr-align
cd zone-robust-ocr-align
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt          # Levenshtein
pip install pytest                       # optional, for tests

Run all scripts from the repository root. They import each other as flat modules.

Optional external tools

Only some evaluation scripts need these. None is needed for the aligners, the synthetic experiments or the tests. Each script that uses Java RETAS accepts --skip-java. The scripts that use genalog only as a baseline column accept --skip-genalog.

Tool Used by How to obtain How the scripts find it
Tesseract 5 with nld, deu, eng language data (5.5.3 was used) real-OCR scripts (Tables 4, 5, 8) distro package, conda-forge tesseract, or https://github.com/tesseract-ocr/tesseract; traineddata from https://github.com/tesseract-ocr/tessdata --tesseract, $TESSERACT, or tesseract on PATH. Language data: --tessdata or $TESSDATA_PREFIX
Java RETAS (Java 17 was used) Java RETAS baseline column git clone https://github.com/Early-Modern-OCR/RETAS && cd RETAS && javac *.java (commit 0193ea6 was used, with its stock config.txt: level=WORD, alignmentFormat=LINES). RETAS has its own GPL licence; it is not distributed here java: --java, $JAVA_HOME/bin/java, or java on PATH. RETAS: --retas-dir or $RETAS_HOME
Microsoft genalog (MIT) genalog baseline column; reference mapping for TREC-5 and real-OCR agreement git clone https://github.com/microsoft/genalog (commit 806a782 was used), pip install biopython, then export PYTHONPATH=/path/to/genalog:$PYTHONPATH. Only genalog.text is used, and it needs only biopython normal import genalog

Using the aligners

from step1_eval import tokenize
from multiregion_v2 import MultiRegionAlignerV2

gt  = tokenize("Left column text first. Then the right column follows here.")
ocr = tokenize("Left column Then the right text first. column follows here.")

res = MultiRegionAlignerV2().align(ocr, gt)   # frozen v2 defaults
res["mapping"]       # list: OCR token i -> GT token index, or -1 if unaligned
res["success_rate"]  # fraction of OCR tokens given any GT partner (not accuracy)

All aligners share the align(ocr_tokens, gt_tokens) -> dict interface and return mapping. To score against a known gold mapping:

from step1_eval import BASE_PARAGRAPHS, generate_zoning_pair, score_alignment
from multiregion_align import MultiRegionAligner

pair = generate_zoning_pair(BASE_PARAGRAPHS, error_rate=0.06,
                            zoning="column_interleave", severity="medium", seed=0)
res = MultiRegionAligner().align(pair.ocr, pair.gt)
print(score_alignment(res["mapping"], pair.gold, pair.break_positions)["f1"])

Smoke test (CPU, seconds, no data)

python smoke_mr.py          # runs every pure-Python aligner on synthetic zoning pairs
python -m pytest            # checks frozen defaults, generator gold, aligner sanity

Reproduce the paper

All scripts run on CPU. Each script in the table writes a JSON file and a short markdown summary to --results-dir (default results/, git-ignored). Every script takes --help. Data download and layout are described in docs/DATA.md. Defaults match the paper protocol unless a flag is shown.

Paper table Script and command Needs
Table 1: synthetic zoning F1 python step3c_ablation.py genalog (or --skip-genalog)
Table 2: cascade length / recovery@10 same run as Table 1 (step3c_ablation.json) as above
Table 3: IMPACT reading-order permutation (GT vs GT) python step3d_impact_eval.py --max-pages 40 --seed 0
Java RETAS row: add --with-java (see note 1)
IMPACT XML, genalog
Table 4: IMPACT real Tesseract vs geometry-derived gold python step6_impact_geogold.py --max-pages 200 --seed 42 --validate-n 20 --max-tokens 600 [--ids-file IDS]
v2 row: step8_v2_eval.py (Table 8 run)
IMPACT XML + TIFs, Tesseract, genalog, Java
Table 5: CC0 word-box gold python step9_wordgold.py word-gold corpora, Tesseract, genalog, Java
Table 6: stress tests python step4_stress.py Java (or --skip-java)
Table 7: long documents python step7_longdoc.py (v1, RETAS-lite, Java, profile)
v2 column: python step8_v2_eval.py --skip-impact --skip-trec (read output section longdoc_nonrep; boilerplate runs too)
step7: genalog up to 5k tokens (or --skip-genalog), Java (or --skip-java). v2 command: no external data
Table 8: v1 vs v2 held-out comparison python step8_v2_eval.py [--ids-file IDS] IMPACT XML + TIFs, Tesseract, TREC-5 originals, Java
Table 9: TREC-5 Confusion Track agreement F1 / matched rate python step4_trec_all.py --n-docs 30 --seed 0 --max-tokens 600 TREC-5, genalog, Java
Prose: TREC synthetic noise + column interleave (v1 / RETAS-lite / 2019 / Java) python step4_trec_synth.py TREC-5 originals, Java (or --skip-java)

Suggested order for the real-OCR tables: download the IMPACT XML, then download_impact_tifs.py --n 200 --seed 42, which writes the sampled ids to data/impact_kb/page_ids.txt. Then run step6_impact_geogold.py, followed by step8_v2_eval.py, passing both --ids-file data/impact_kb/page_ids.txt. step6 caches Tesseract TSV under results/tess_tsv/, and step8 reuses it. To run only the synthetic sections of step8_v2_eval.py, use --skip-impact --skip-trec.

Other scripts:

  • step1_eval.py: the full synthetic sweep (noise × severity × zoning) for the 2019, re-anchoring and RETAS-lite aligners. It also holds the shared aligners, generator and metrics. Its output directory is --out (default results/), not --results-dir.
  • step5_realocr.py: supplementary real-OCR run on IMPACT, scored only as agreement with genalog (no box gold). Table 4 uses step6 instead.
  • step8_v2_dev.py: the development sweep that chose the v2 defaults in FROZEN_PARAMS_V2.md (dev seeds 100–129). Its optional IMPACT sanity section needs the held-out test-200 page list as --ids-file, used only to exclude those pages; without --ids-file that section is skipped.
  • download_impact_tifs.py: fetches selected IMPACT TIFs by HTTP range request (see docs/DATA.md).

Notes:

  1. Table 3, Java RETAS row. In the paper this row came from a separate run on the same 40 pages (shuffle seed 0, the same selection as step3d_impact_eval.py). It scored Java RETAS through common_eval.run_aligner with the same score_alignment metric. --with-java repeats that procedure inside step3d_impact_eval.py. The published row was not produced by this exact script.
  2. Page lists are not shipped. The IMPACT scripts accept --ids-file. If you omit it, they draw the protocol sample (200 pages, seed 42) from your XML directory. If every XML page has a TIF in the KB zip, this equals the download script's sample, but it has not been checked against the paper's list. The paper's test-200 page list will be added if rights permit. See docs/DATA.md.
  3. Results that involve Tesseract depend on the Tesseract version and language data. Runtimes depend on hardware.
  4. The scripts that build the paper's LaTeX tables and figures are not included. They only reformat the JSON outputs listed above.

What the numbers do and do not show

  • Synthetic tables (1, 2, 6, 7). The gold is exact, but the zoning permutations and character noise are generated, so they are simpler than real layouts.
  • TREC-5 (Table 9) has no word-level alignment gold. Its numbers are agreement F1 with genalog's mapping (genalog scores 1.0 by construction) plus a matched rate (the share of OCR tokens given any partner). They do not measure correctness against human word-level gold.
  • IMPACT geometry gold (Tables 4, 8) is approximate. KB IMPACT PAGE XML has region boxes only, so word boxes are estimated by a proportional split and matched to Tesseract boxes. step6 reports an estimated gold error and a high-confidence subset.
  • Word-box gold (Table 5) uses human word boxes but covers only 14 pages of 16th–17th century print. It is still a box match with Tesseract.
  • The v1 and v2 hyperparameters were frozen before the held-out evaluations (FROZEN_PARAMS*.md). The scripts instantiate the aligners with those defaults.

Licence

Code: Apache License 2.0. See LICENSE and NOTICE. Copyright 2026 Jorge Ramón Fonseca Cacho.

Datasets and external tools keep their own licences and terms (see docs/DATA.md and the table above). None of them is redistributed here.

Citation

If you use this code, please cite:

@unpublished{fonsecacacho2026wordalign,
  author = {Jorge Ramón {Fonseca Cacho}},
  title = {Word-Level Alignment of OCR and Ground Truth under Zoning and Reading-Order Errors},
  note = {Manuscript under review},
  year = {2026}
}

About

Word-level alignment of OCR and ground truth under zoning and reading-order errors

Resources

Readme
Apache-2.0 license
Activity

Stars

0 stars

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Packages

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.