Reconstruct clean, searchable documents from scrambled, degraded, bilingual scanned bundles.
Runs free & offline with local OCR. Add an Anthropic API key and it gets smarter — an LLM vision layer re-reads exactly the pages local OCR couldn't.
Real-world scanned legal/records bundles are a mess: pages from different documents are interleaved and shuffled, the same document appears in multiple copies, scans are degraded photocopies, pages are rotated, and the text is bilingual (e.g. English + Hindi/Devanagari). A naïve "split by blank page" tool falls apart — there is no positional signal left to split on.
scansmith treats it as a reconstruction problem: group scattered pages back into their true
documents by content, pick the clearest copy of every page, and emit one named, deduplicated,
searchable PDF per document, plus provenance reports and a JSON manifest.
flowchart LR
A[scanned<br/>bundle] --> B[ingest]
B --> C["OCR triage<br/>(Tesseract)"]
C -. "DEGRADED pages,<br/>only with an API key" .-> V["vision re-OCR<br/>(Claude)"]
V -.-> D
C --> D["segment<br/>content clustering"]
D --> E["order + dedup<br/>clearest copy wins"]
E --> F["embed<br/>invisible text layer"]
F --> G["emit<br/>named searchable PDFs<br/>+ reports + manifest"]
- Content clustering — pages link by shared rare signals that survive OCR noise: party names (idf-weighted, common surnames ignored), amounts, dates, certificate/case numbers (fuzzy-matched against OCR digit slips). Connected components become documents; title / clause numbering / signature cues order the pages.
- Copy dedup — perceptual image hash and text agreement must both match, so a duplicate collapses but two same-template pages never merge.
- Vision escalation (optional) — pages the local pass flags
DEGRADED(grainy photocopies, Devanagari without a language pack, handwriting) are re-read by Claude with a faithful-transcription prompt: Devanagari stays Unicode, unreadable spans become[illegible], signatures[signature]. No key → the offline result stands, hard pages stay flagged.
Everything is scored against generated ground truth (scansmith eval) — on the seeded
6-document demo bundle:
| metric | offline core | baseline |
|---|---|---|
| segmentation pairwise F1 | 0.947 | 0.000 ("1 page = 1 doc") |
| dedup precision / recall | 1.000 / 1.000 | recall 0 (no dedup) |
| OCR CER (legible Latin) | 0.001 | 1.0 (no text) |
Adding the vision layer (A/B on the same bundle):
| metric | offline | with vision* |
|---|---|---|
| segmentation F1 | 0.947 | 1.000 |
| documents found (truth: 6) | 7 | 6 |
| Devanagari CER | 0.80 | 0.00 |
| Devanagari pages legible | 0/2 | 2/2 |
* measured with the deterministic test harness (a seeded backend returning ground-truth text — this is how the escalation path is proven without network); a real vision model typically lands Devanagari CER around 0.1–0.2 on these pages.
The offline core cannot read the Hindi court order, so it fragments; the vision layer transcribes it faithfully and it snaps into one document. That delta is the layered design working.
pip install "scansmith[tesseract]" # plus the system tesseract-ocr binary (add the
# hin language pack for the Hindi documents)
scansmith gen-sample --out demo/ # fabricate a messy bilingual bundle + ground truth
scansmith process demo/bundle.pdf -o out/ # reconstruct into clean, searchable documents
scansmith eval out/ --truth demo/truth.json # metrics vs ground truth
# Optional smart layer — re-read the hard pages with Claude vision:
pip install "scansmith[llm]"
export ANTHROPIC_API_KEY=sk-... # or put SCANSMITH_ANTHROPIC_API_KEY in .env
scansmith process demo/bundle.pdf -o out_llm/ --llm anthropic --model claude-opus-4-8
scansmith eval out_llm/ --truth demo/truth.jsonRuns fully offline without a key; the smart layer only adds to what the local core produces.
Pluggable pipeline behind small protocols: local OCR engines implement OcrBackend, LLM vision
implements LlmBackend — so the tool degrades gracefully from "LLM-augmented" to "local only"
without changing the orchestration. All tests are deterministic and offline (the vision path is
proven with a seeded fake backend; one live test is API-key-gated). Full design:
docs/ARCHITECTURE.md.
Born from reconstructing a real 789-page, 4-file legal bundle (heavily scrambled, bilingual,
degraded) where local OCR failed on exactly the pages that mattered and LLM vision was the
reliable transcriber. No real documents are included here — everything above is synthetic
data from scansmith gen-sample, so the whole pipeline is reproducible and PII-free.
v1.0.0 — all milestones complete: M0 scaffold · M1 synthetic data · M2 local core · M3 LLM smart layer · M4 polish.
MIT © 2026 Yashvardhan Chaudhry

