Skip to content

Models & parity

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.

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_CACHEHUGGINGFACE_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.

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.

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.