Models & parity
The gen2 models
Section titled “The gen2 models”sceptre uses one CRAFT detector shared across languages, plus one gen2 CRNN recognizer per language group:
| Language | Model | Config value |
|---|---|---|
| — (detector) | craft_mlt_25k |
— |
| English | english_g2 |
english |
| Latin-script | latin_g2 |
latin |
| Chinese (simplified) | zh_sim_g2 |
chinese_simplified |
| Japanese | japanese_g2 |
japanese |
| Korean | korean_g2 |
korean |
| Cyrillic-script | cyrillic_g2 |
cyrillic |
| Telugu | telugu_g2 |
telugu |
| Kannada | kannada_g2 |
kannada |
All eight of EasyOCR’s gen2 (*_g2) recognizers are supported — the complete gen2 family — see
ADR 0002.
Source, verification, and caching
Section titled “Source, verification, and caching”Every ONNX artifact is a first-party export — built from EasyOCR’s weights by the sceptre export
pipeline and hosted on the xberg-io org on Hugging Face
(Apache-2.0, dynamic-width — no letterbox distortion). Each model’s SHA-256 is pinned in the
registry and verified against the downloaded bytes.
Downloads land in Hugging Face’s standard hub cache
(<root>/models--<owner>--<name>/snapshots/<rev>/<file>), so the library, the CLI, and any other
Hugging Face tooling on the machine share one store. The cache root resolves, in order:
HF_HUB_CACHE → HUGGINGFACE_HUB_CACHE → $HF_HOME/hub → ~/.cache/huggingface/hub, or the
ModelConfig::cache_dir override.
The registry also carries a revision field, which is the floating string "main". It is never
passed to the Hub and never used to resolve a cache path — the cache lookup enumerates every
snapshots/ subdirectory under the repo and returns the newest usable one, regardless of what
revision says. revision is carried purely as reporting metadata — its only reader outside the
registry is sceptre env --format json, which echoes it back in each model pin. The SHA-256 is
the only real pin. Derive CI cache keys from the digest set (see
Offline and CI), not from the revision.
The registry owner (xberg-io by default) can be overridden per config
(ModelConfig::registry_owner) to point at a mirror without any code change — the owner is
validated to a safe form before it reaches the cache path.
How parity is validated
Section titled “How parity is validated”The parity claim is scoped to a runtime: the ort backend on the CPU execution provider, which
is what model.accelerator defaults to. Every golden fixture and every published benchmark number
was produced there. The same ONNX graph produces different numeric output on a different execution
provider, so an accelerated run is a different measurement rather than a faster one — see
Backends for how to select one and what it costs you.
sceptre’s test suite compares its output against two goldens for every image in the parity corpus:
- EasyOCR reference (fuzzy) — the authoritative Python EasyOCR output, compared with bag-of-words F1 (word/char) plus per-line box-IoU (≥ 0.5). This is the actual parity claim: what EasyOCR reads, sceptre reads.
- sceptre self-snapshot (exact) — a previously committed sceptre run, compared for byte-exact text equality, catching regressions in sceptre’s own output between changes.
Real-model parity tests are opt-in, gated on SCEPTRE_REQUIRE_MODELS: unset (or the models absent)
skips the test; set and the models are missing, the test panics so CI surfaces a misconfigured
cache rather than silently passing. See
ADR 0016.
The same corpus and parity signal (char/word-F1, box-IoU) back the head-to-head performance benchmark — see Benchmarks.
Cross-provider coverage
Section titled “Cross-provider coverage”CI is designed with a per-execution-provider matrix: the golden parity suite runs on the CPU
provider across Linux, macOS, and Windows, plus a CoreML leg on macOS. The golden suite itself is
gated to the CPU provider only — the CoreML leg instead runs one end-to-end OCR invocation and
checks the JSON output contains a recognized "text" field, proving the provider registers and
the pipeline runs at all, not that its numerics match the CPU golden fixtures. GitHub’s macOS
runners are virtual machines with no Neural Engine in any case, so even a passing golden
comparison there would not be evidence about on-device ANE numerics. CUDA is unvalidated: there
is no GPU runner in CI, and none of the measured accuracy or performance figures should be assumed
to transfer to it.
DirectML is deliberately not a CI lane. The Windows runner has no GPU, so DirectML falls back to the WARP software adapter, and numbers from it would describe neither CPU nor a real GPU.