Skip to content

Commit 871a9e1

Browse files
hBouananeclaude
andcommitted
Merge v1.0.0: OCRSmith becomes a document forge
Lands the full nine-PR series (#2-#10) on main. The v0 engine produced one image with one string. v1 produces whole pages - multi-column, with titles, tables, figures, forms and running headers - degraded to look like a real capture, and emits ground truth for recognition, detection, layout analysis, document-to-markup and table structure from the same render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 parents 1de38ee + ab1cf7d commit 871a9e1

140 files changed

Lines changed: 11509 additions & 3597 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,104 @@ All notable changes to this project are documented here. The format follows
44
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to
55
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [Unreleased]
7+
## [1.0.0] - 2026-08-12
8+
9+
The first release of OCRSmith as a document forge rather than a line-image generator.
10+
The v0 engine produced one image with one string; v1 produces whole pages with word-level
11+
boxes, typed layout regions, table structure and markup ground truth, and validates every
12+
page before it reaches the dataset.
813

914
### Added
1015

16+
- `ocrsmith.quality`: validation and statistics.
17+
- Six validators for the failures that do not raise — blank pages, washed-out text,
18+
boxes off the canvas, overlapping blocks, illegibly small lines, empty labels. Each
19+
explains itself, so a rejection rate is diagnosable rather than merely alarming.
20+
- The generation pipeline runs them as a gate; a shard that rejects more than
21+
`quality.max_rejection_rate` aborts, because that is a configuration problem and a
22+
shard full of holes would hide it behind a plausible-looking dataset.
23+
- `DatasetStats` accumulates the distributions that define a corpus — templates, capture
24+
conditions, direction, region types, alphabet, line-height percentiles — in constant
25+
memory, and renders a dataset-card fragment.
26+
- `ocrsmith.evaluation`: CER, WER, normalised edit similarity, TEDS-style table similarity
27+
and IoU-thresholded detection P/R/F1, plus a harness that scores a model's predictions
28+
against a generated benchmark. Diacritic sensitivity is an explicit argument rather than
29+
a silent convention.
30+
- `ocrsmith.domain.page_from_dict` and friends: annotations round-trip, so validation,
31+
statistics and evaluation all read the same records the writers produce.
32+
- CLI: `validate`, `stats` and `evaluate`.
33+
- `ocrsmith.pipeline`: streaming generation.
34+
- `SampleFactory.create(index)` derives a per-sample seed from `(config.seed, index)`
35+
and yields one annotated `Sample` per page. Any sample can be regenerated on its own,
36+
without replaying the run that produced it.
37+
- `iter_samples` is a generator end to end, so a ten-million-page run holds one page in
38+
memory at a time.
39+
- Work is sharded, and each worker *writes* its own shard rather than shipping images
40+
back through a pickle queue. Completed shards are marked, so an interrupted job
41+
resumes instead of restarting.
42+
- `ocrsmith.datasets.writers`: six output formats behind one `SampleSink` protocol —
43+
`jsonl`, `parquet`, `webdataset`, `coco` (word/line/region instances), `paddleocr`
44+
(detection labels plus line crops) and `chat` (image + instruction + Markdown answer for
45+
vision-language fine-tuning). Sinks write incrementally, so a killed run leaves valid
46+
shards rather than one truncated file.
47+
- `ocrsmith.config`: a single validated `GenerationConfig` describing a whole corpus, with
48+
every per-sample choice expressed as a distribution. Dotted-key CLI overrides
49+
(`--set run.workers=8`) and a JSON-serialisable payload that crosses the process
50+
boundary intact.
51+
- New `ocrsmith` CLI (typer + rich): `generate`, `preview`, `fonts`, `doctor`,
52+
`show-config`. `doctor` reports whether this machine can produce correct Arabic; the CLI
53+
forces UTF-8 output so a cp1252 Windows console cannot crash a tool whose entire purpose
54+
is Arabic text.
55+
- `ocrsmith.core.degradations`: capture-condition modelling, where every degradation takes
56+
the annotation as well as the image and returns both.
57+
- Geometric: `Rotation` and `PerspectiveWarp` derive an explicit forward point mapping
58+
and push the whole annotation through it, so boxes track the ink instead of drifting
59+
silently. Rotated words gain polygons.
60+
- Photometric: gaussian noise, paper grain, defocus and motion blur, brightness,
61+
contrast, JPEG artefacts, downscaling, ink spread/erosion, bleed-through, shadow,
62+
vignette, glare, stains and folds.
63+
- Five presets — `clean`, `scan`, `photo`, `fax`, `archive` — ordered along the physical
64+
capture chain, so a corpus can be composed by capture condition rather than by
65+
undifferentiated noise.
66+
- `ocrsmith.core.documents`: a document engine that produces full pages, not single lines.
67+
- `DocumentBuilder` / `DocumentContent` separate *what a document says* from *how it
68+
looks*, so the same content laid out in one column or two yields identical markup
69+
ground truth.
70+
- `PageSpec.from_paper` derives the canvas from paper size and DPI, with margins,
71+
multi-column layout (right-first for RTL) and reserved header/footer bands.
72+
- `DocumentRenderer` pours blocks down columns and onto further pages, splitting prose
73+
and moving structure whole. Nothing is clipped, nothing overlaps, and pages can be
74+
streamed one at a time.
75+
- `TableRenderer` emits per-cell boxes, content-derived column widths, in-cell wrapping,
76+
RTL column mirroring, and five border styles.
77+
- `TypographySampler` samples one coherent font family per document with per-role sizes
78+
and spacing, rather than a random face per block.
79+
- Six document genres (article, report, newspaper, letter, form, invoice) behind a
80+
weighted `TemplateRegistry`.
81+
- `ocrsmith.core.rendering`: a text renderer that emits pixels and their annotation in
82+
the same pass, so the two cannot disagree.
83+
- Words are drawn individually, which yields exact per-word boxes and is typographically
84+
safe for Arabic (letters never join across a space).
85+
- `visual_word_order` implements word-level bidi run reordering, so mixed
86+
Arabic/Latin/digit lines are drawn in the right place while the label stays logical.
87+
- Wrapping is lossless: no word is silently dropped, oversized words are broken rather
88+
than overflowed, and lines that do not fit the height budget are *reported* so the
89+
label can be trimmed to exactly what was drawn.
90+
- `TextStyle` covers alignment (including natural RTL), line/word spacing, stroke,
91+
underline, strikethrough, synthetic bold/italic, and baseline/word-gap jitter, with an
92+
injectable `random.Random` so any sample is reproducible.
93+
- `ocrsmith.text`: a text subsystem that owns everything happening to a string before it
94+
becomes pixels.
95+
- Script and base-direction detection (`detect_script`, `detect_direction`).
96+
- `NormalizationPolicy` — opt-in, idempotent transforms for diacritics, tatweel,
97+
alef/ya/ta-marbuta unification, numeral systems and whitespace. Each one changes the
98+
label, so each one is explicit and recorded.
99+
- Bidi/shaping with two interchangeable backends: `TransparentShaper` when Pillow has
100+
Raqm, `ReshaperBidiShaper` (arabic-reshaper + python-bidi) otherwise. Both keep the
101+
logical string as the label so datasets are identical across machines.
102+
- Font glyph coverage via fontTools `cmap` (`supports_text`, `fonts_supporting`), so a
103+
font that cannot draw a character is rejected instead of silently emitting tofu.
104+
11105
- Development tooling: ruff lint/format configuration, pytest configuration with
12106
`pythonpath = ["src"]` (so a clean clone is testable without installing), coverage
13107
settings, and a GitHub Actions CI matrix over Linux/Windows and Python 3.10/3.12.
@@ -22,8 +116,19 @@ All notable changes to this project are documented here. The format follows
22116
- `HuggingFaceTextLoader` accepts any iterable of mappings, streams records via
23117
`iter_texts`, and defers the `datasets` import to call time.
24118

119+
### Removed
120+
121+
- The v0 engine and everything coupled to it: `OCRSmithEngine`, `core/app.py`, the four
122+
`*Manager` classes, and the `augmentation`, `text_renderers`, `text_placement`,
123+
`backgrounds` and `fonts` strategy packages. Their responsibilities now live in
124+
`core/rendering`, `core/documents`, `core/degradations`, `core/fonts.py`,
125+
`core/backgrounds.py` and `pipeline/`, with word-level ground truth throughout.
126+
- `ocrsmith.utils`, whose remaining helpers had no callers.
127+
25128
### Fixed
26129

27130
- `wrap_text_by_pixels` raised `TypeError` when `layout.max_width` / `layout.max_height`
28131
were unset, which made every sample fail for configs that omit them.
29132
- Whitespace-only input no longer produces a zero-sized canvas.
133+
134+
[1.0.0]: https://github.com/atlasia-ma/OCRSmith/releases/tag/v1.0.0

0 commit comments

Comments
 (0)