ocracy#

ocracy — one facade over many OCR engines, plus a ledger to choose between them.

OCR (“read the text in this image”) is solved a dozen different ways: local engines (Tesseract, EasyOCR, PaddleOCR), cloud APIs (Google Vision, AWS Textract, Azure), and VLM-based readers — each with its own install, API, pricing, language coverage, and quirks. ocracy gives you:

  1. A uniform facade. Call ocr() and get the same OcrResult back no matter which backend ran:

    import ocracy
    result = ocracy.ocr("scan.png")          # default (first installed) backend
    print(result)                            # -> the recognized text
    result = ocracy.ocr("scan.png", backend="easyocr", languages=["en", "fr"])
    

    Convenience: read_text() returns just the string.

  2. A ledger / gallery of every engine we researched — not only the ones with a working facade — so you can choose with eyes open:

    ocracy.catalog                                   # the whole ledger
    ocracy.find(is_local=True, open_source=True)     # filter it
    ocracy.find(handwriting="yes", is_remote=True)
    ocracy.catalog.to_dataframe()                    # browse as a table
    

    The ledger lives in data (ocracy/data/backends.json), not code.

  3. Tools to build new facades. The catalog is large; ocracy ships a facade for a curated subset and gives you the machinery (and a SKILL) to add any other one in minutes:

    from ocracy.make_backend import scaffold_backend, validate_adapter
    scaffold_backend("mathpix")    # generate a backend package from the ledger
    validate_adapter("tesseract")  # smoke-test an adapter end to end
    

Three tiers of access, from simplest to most powerful:

ocracy.ocr(img)                                # facade, default backend
ocracy.services.tesseract.read(img, lang="fra")  # pick a backend
ocracy.services.tesseract.adapter              # raw engine adapter

Functions

ocr(image, *[, backend])

Read text from an image with any backend, returning a normalized result.

read_text(image, *[, backend])

Like ocr() but returns just the recognized text string.

find(**criteria)

Filter the ledger; shorthand for ocracy.catalog.Catalog.filter().

list_backends([capability])

Sorted ids of implemented backends, optionally filtered by capability.

register_backend(backend_id, config[, adapter])

Register a backend at runtime (for third-party plugins).

get_default_backend([capability, ...])

Pick a sensible default backend id for a capability.

get_config(backend_id)

A backend's BACKEND_CONFIG without loading its adapter.

make_block(text, *[, bbox, confidence, ...])

Build a normalized TextBlock.

scaffold_backend(backend_id, *[, dest, ...])

Create a new ocracy/backends/<id>/ package from the template.

validate_adapter(backend_id, *[, image, ...])

Smoke-test a backend adapter end to end, returning a report dict.

requirements(backend_id, *[, gpu])

Return structured install Requirements for backend_id.

check(backend_id)

Is backend_id importable / usable right now? (no install, no network).

doctor()

Report which implemented backends are usable now and what the rest need.

install(backend_id, *[, yes, gpu, verify, ...])

Plan (and optionally run) the pip install for a backend.

available_backends()

Implemented backends whose dependency is importable right now.

backend_ids([level, info, test_image])

Sorted backend ids at a readiness level (one of LEVELS).

backend_info([ids, run_tests, test_image])

Per-backend status: every ledger field plus implemented/set_up/tested.

status_table([ids, info, columns, ...])

Render an aligned Markdown table of backend status (also plain-text readable).

names_with_sites(ids, *[, info])

"Name (website), Name2 (website2), ..." for ids (name, not id).

is_set_up(backend_id)

Ready to run here & now.

is_tested(backend_id, *[, image])

Actually run OCR on image (a generated one if None); True iff it worked.

Classes

OcrResult(text[, blocks, backend, raw, meta])

The normalized result of reading an image with any backend.

TextBlock(text[, bbox, confidence, level, ...])

One recognized unit of text.

BBox(x0, y0, x1, y1[, polygon])

An axis-aligned bounding box in pixel coordinates (origin = top-left).

BackendInfo(record, *[, implemented])

One backend's ledger entry — a read-only, attribute-and-dict accessible record.

Catalog([path, _records])

A filterable, dict-like collection of BackendInfo, keyed by id.

ServiceCollection()

Lazy mapping of backend ids -> ServiceHandle.

BaseOcrAdapter(config)

Optional base class for backend adapters.

Requirements(backend_id, implemented, ...[, ...])

What a backend needs to run, structured for an agent to act on.

class ocracy.BBox(x0, y0, x1, y1, polygon=None)[source]#

Bases: object

An axis-aligned bounding box in pixel coordinates (origin = top-left).

polygon optionally carries the original (possibly rotated) vertices as a sequence of (x, y) points; the axis-aligned x0/y0/x1/y1 are always populated (derived from the polygon if a backend only gives one).

property as_tuple: Tuple[float, float, float, float]#

(x0, y0, x1, y1) — the convention PIL’s crop expects.

classmethod from_polygon(points)[source]#

Build a box from polygon vertices [(x, y), ...].

The axis-aligned extent is computed from the vertices and the original polygon is preserved for callers that care about rotation.

Return type:

BBox

property xywh: Tuple[float, float, float, float]#

(x, y, width, height) — the convention many drawing libs expect.

class ocracy.BackendInfo(record, *, implemented=False)[source]#

Bases: Mapping

One backend’s ledger entry — a read-only, attribute-and-dict accessible record.

Wraps the raw record dict so that new fields added to the JSON are available immediately (via attribute or key access) without code changes, while a few commonly used fields get typed properties for convenience and discoverability.

class ocracy.BaseOcrAdapter(config)[source]#

Bases: object

Optional base class for backend adapters.

Stores the config, builds a kwarg translator from config['param_map'], and implements read as: translate normalized kwargs -> native kwargs -> _read(). Subclasses implement _read() and return an OcrResult.

Adapters are not required to subclass this — the registry only needs an Adapter class with a read(image, **kwargs) method — but doing so removes the boilerplate.

ADAPTER_KWARGS = ('api_key', 'app_key', 'app_id')#

Keyword arguments a remote adapter reads itself (credentials), passed through untranslated: no param_map declares them, and they must never be dropped. On a local backend they are dropped with a (redacted) note.

read(image, **kwargs)[source]#

Translate kwargs, run _read(), and put any drop notes on the result.

A parameter the backend cannot honour is warned about and dropped, and the drop is recorded in result.meta["notes"] so it is visible after the warning is gone (or filtered).

Return type:

OcrResult

class ocracy.Catalog(path=None, *, _records=None)[source]#

Bases: Mapping

A filterable, dict-like collection of BackendInfo, keyed by id.

Loaded lazily from DEFAULT_LEDGER_PATH (override via the path argument or the OCRACY_LEDGER environment variable). filter() returns a new Catalog over the matching subset, so filters compose:

catalog.filter(is_remote=True).filter(pricing_model="free_tier_then_paid")
can(capability)[source]#

Backends with a non-plain-text capability (e.g. "math", "tables").

Matches either the beyond_text list or a <capability> field set to "yes" (e.g. handwriting, tables, math_formula).

Return type:

Catalog

compare(ids=None, *, fields=('name', 'is_local', 'is_remote', 'open_source', 'pricing_model', 'price_note', 'accuracy_tier', 'languages_count', 'handwriting', 'math_formula', 'tables', 'best_for'))[source]#

A trimmed, side-by-side view of selected backends and fields.

Return type:

List[dict]

filter(*, predicate=None, implemented=None, **criteria)[source]#

Return a new Catalog of backends matching every criterion.

Parameters:
  • predicate (Optional[Callable[[BackendInfo], bool]]) – An arbitrary BackendInfo -> bool callable.

  • implemented (Optional[bool]) – If set, keep only (un)implemented backends.

  • **criteria (Any) – field=value constraints. value may be a set/list/tuple meaning “one of”. Free-text fields (languages_note, beyond_text, output_formats) use a case-insensitive substring match.

Return type:

Catalog

Example:

catalog.filter(is_local=True, open_source=True, handwriting="yes")
supports_language(language)[source]#

Backends whose languages_note mentions language (name or code).

Return type:

Catalog

to_dataframe(*, columns=None)[source]#

Return a pandas DataFrame of the catalog (pandas imported lazily).

class ocracy.OcrResult(text, blocks=<factory>, backend='', raw=None, meta=<factory>)[source]#

Bases: object

The normalized result of reading an image with any backend.

text is the headline payload: the full recognized text in reading order. blocks carries the structured units (with boxes/confidences) when the backend provides them. raw is the untouched backend output. meta holds cross-cutting extras (languages, page count, a Markdown rendering, timing, …).

Progressive disclosure:

result = ocracy.ocr("scan.png")
print(result)              # -> the text
result.text                # -> the same string
for line in result:        # -> iterate TextBlocks (lines by default)
    print(line.text, line.confidence)
result.words               # -> only word-level blocks
result.mean_confidence     # -> average confidence, if available
result.raw                 # -> engine-specific structure
at_level(level)[source]#

Blocks at a given granularity ("word", "line", …).

Return type:

List[TextBlock]

filter_confidence(min_confidence)[source]#

Return a copy keeping only blocks at or above min_confidence.

Blocks without a confidence are dropped. text is rebuilt from the surviving blocks (joined by newline).

Return type:

OcrResult

classmethod from_blocks(blocks, *, backend='', raw=None, text=None, joiner='\\n', **meta)[source]#

Build a result from structured blocks.

If text is not given it is synthesized by joining the blocks’ text in their given order with joiner (callers should pass blocks already in reading order, or pre-join and pass text explicitly).

Return type:

OcrResult

classmethod from_text(text, *, backend='', raw=None, **meta)[source]#

Build a minimal result from just a text string (no geometry).

Return type:

OcrResult

property markdown: str | None#

Markdown rendering if the backend produced one (else None).

property mean_confidence: float | None#

Mean confidence over blocks that report one, or None.

class ocracy.Requirements(backend_id, implemented, available, is_local, is_remote, pip_command, extra=None, system=<factory>, system_note=None, gpu=None, weights=None, heavy=False, alternative=None, credentials=<factory>, notes=<factory>, verify_command=None, alternative_label='Lighter alternative')[source]#

Bases: object

What a backend needs to run, structured for an agent to act on.

instructions()[source]#

An agent- and human-readable, copy-pasteable install plan.

Return type:

str

class ocracy.ServiceCollection[source]#

Bases: Mapping

Lazy mapping of backend ids -> ServiceHandle.

Supports dict-style (services['tesseract']) and attribute-style (services.tesseract) access.

class ocracy.TextBlock(text, bbox=None, confidence=None, level='line', language=None, meta=<factory>)[source]#

Bases: object

One recognized unit of text.

text#

The recognized string for this unit.

bbox#

Where it was found (pixel coordinates), if the backend reports it.

confidence#

Recognition confidence in [0, 1] (normalized by ocracy from whatever scale the backend used), if available.

level#

Granularity — one of ocracy.base.LEVELS (“word”, “line”, …).

language#

Detected/declared language code for this unit, if any.

meta#

Backend-specific extras (font size, style, page index, …).

ocracy.available_backends()[source]#

Implemented backends whose dependency is importable right now.

Return type:

List[str]

ocracy.backend_ids(level='all', *, info=None, test_image=None)[source]#

Sorted backend ids at a readiness level (one of LEVELS).

Pass a precomputed info (from backend_info()) to avoid recomputation — important for "tested", which otherwise re-runs OCR.

Return type:

List[str]

ocracy.backend_info(ids=None, *, run_tests=False, test_image=None)[source]#

Per-backend status: every ledger field plus implemented/set_up/tested.

Parameters:
  • ids (Optional[Iterable[str]]) – Which backends (default: every ledger id).

  • run_tests – Which set-up backends to actually OCR-test. True = all set-up (real API calls for remotes!); False = none (tested is None); or an iterable of ids to limit testing to those.

  • test_image (Any) – Image to test with (default: a generated one, shared across all).

Return type:

Dict[str, dict]

Returns:

{id: {..., "name", "website", "implemented", "set_up", "tested"}} where tested is True/False/None (None = not attempted).

ocracy.check(backend_id)[source]#

Is backend_id importable / usable right now? (no install, no network).

Return type:

bool

ocracy.doctor()[source]#

Report which implemented backends are usable now and what the rest need.

Returns {"available": [...], "missing": {id: one-line install hint}}.

Return type:

dict

ocracy.find(**criteria)[source]#

Filter the ledger; shorthand for ocracy.catalog.Catalog.filter().

Example:

ocracy.find(is_local=True, open_source=True, handwriting="yes")
ocracy.find(implemented=True)        # only backends ocracy can run now
Return type:

Catalog

ocracy.get_config(backend_id)[source]#

A backend’s BACKEND_CONFIG without loading its adapter.

Return type:

dict

ocracy.get_default_backend(capability='read', *, require_available=True)[source]#

Pick a sensible default backend id for a capability.

Strategy (OCR-tuned): prefer a backend explicitly flagged default_for the capability and whose dependency is importable; then any importable backend for the capability; then — if require_available is False or nothing is installed — the first registered candidate (using it will raise a helpful install error).

Return type:

str

ocracy.install(backend_id, *, yes=False, gpu=False, verify=True, upgrade=False)[source]#

Plan (and optionally run) the pip install for a backend.

With yes=False (default) this is a dry run: it returns the plan without changing anything — call result['requirements'].instructions() to show it. With yes=True it runs pip install for the backend’s extra in the current interpreter, then (if verify) checks importability.

System dependencies and GPU wheels are surfaced, never run automatically (they need sudo/brew or an environment-specific CUDA choice) — run those yourself from result['requirements'].system / .gpu.

Return type:

dict

ocracy.is_set_up(backend_id)[source]#

Ready to run here & now.

Requires the engine/client to be importable (local and remote), and — for a remote backend — its (primary) credential env var to resolve. So a remote whose key is set but whose client library isn’t installed is not set up (it couldn’t actually run). Returns False for ledger-only backends.

Return type:

bool

ocracy.is_tested(backend_id, *, image=None)[source]#

Actually run OCR on image (a generated one if None); True iff it worked.

For remote backends this performs a real API call. Returns False if the backend isn’t set up or the run fails.

Return type:

bool

ocracy.list_backends(capability=None)[source]#

Sorted ids of implemented backends, optionally filtered by capability.

A backend matches capability if it appears in the backend’s capabilities list (the primary "read" is implied for all).

Return type:

List[str]

ocracy.make_block(text, *, bbox=None, confidence=None, conf_scale=1.0, level='word', language=None, **meta)[source]#

Build a normalized TextBlock.

bbox may be any shape accepted by as_bbox(). confidence is normalized to [0, 1] via conf_scale (e.g. conf_scale=100 for percent-scale engines).

Return type:

TextBlock

ocracy.names_with_sites(ids, *, info=None)[source]#

"Name (website), Name2 (website2), ..." for ids (name, not id).

Return type:

str

ocracy.ocr(image, *, backend=None, **kwargs)[source]#

Read text from an image with any backend, returning a normalized result.

Parameters:
  • image (ImageInput) – A path, http(s) URL, bytes, PIL image, or numpy array.

  • backend (str) – Backend id (see list_backends()). Defaults to the first installed implemented backend (see get_default_backend()).

  • **kwargs – Normalized, backend-translated options (e.g. languages). Unknown options for the chosen backend are warned about and dropped.

Return type:

OcrResult

Returns:

An OcrResult (str(result) is the text).

ocracy.read_text(image, *, backend=None, **kwargs)[source]#

Like ocr() but returns just the recognized text string.

Return type:

str

ocracy.register_backend(backend_id, config, adapter=None)[source]#

Register a backend at runtime (for third-party plugins).

Parameters:
  • backend_id (str) – Unique identifier.

  • config (dict) – BACKEND_CONFIG-shaped dict (needs at least name).

  • adapter (Any) – Optional pre-instantiated adapter; else loaded lazily from config['module_path'] when first used.

Return type:

None

ocracy.requirements(backend_id, *, gpu=False)[source]#

Return structured install Requirements for backend_id.

Works for both implemented backends (uses the ocracy[extra] install and the recipe) and ledger-only backends (falls back to the ledger’s python_install string). A recipe’s GPU guidance is always included; gpu is accepted for compatibility and changes nothing.

Return type:

Requirements

ocracy.scaffold_backend(backend_id, *, dest=None, overwrite=False, ledger=None, extra_overrides=None)[source]#

Create a new ocracy/backends/<id>/ package from the template.

Pre-fills config.py from the backend’s ledger entry (if any) so you only have to flesh out param_map and implement adapter.py’s _read.

Parameters:
  • backend_id (str) – The ledger id (e.g. "easyocr", "google-vision"). The on-disk module name uses underscores; the config id keeps the id verbatim.

  • dest (Union[str, Path, None]) – Target directory (defaults to ocracy/backends/<id_underscored>).

  • overwrite (bool) – Allow writing into an existing non-empty directory.

  • ledger (Any) – A Catalog to read the record from (defaults to the shipped catalog).

  • extra_overrides (Optional[dict]) – Extra config-key overrides applied on top of the record.

Return type:

Path

Returns:

The path to the created backend package.

ocracy.status_table(ids=None, *, info=None, columns=None, run_tests=False, test_image=None)[source]#

Render an aligned Markdown table of backend status (also plain-text readable).

Rows are ordered tested → set-up → implemented → listed, then by name, so the “live” backends float to the top. Pass a precomputed info to avoid re-running tests; otherwise run_tests controls which set-up backends get OCR-tested.

Return type:

str

ocracy.validate_adapter(backend_id, *, image=None, expect_text=None)[source]#

Smoke-test a backend adapter end to end, returning a report dict.

Loads the adapter (reporting unavailability instead of raising), runs read on a generated (or supplied) image, and checks the contract: an OcrResult came back with text and/or blocks. Never raises on a recognition mismatch — it returns what happened so callers can decide.

Return type:

dict

Modules

backends

Implemented OCR backends.

base

Core types and normalized result objects for ocracy.

catalog

A filterable, dict-like collection of BackendInfo, keyed by id.

credentials

Credential resolution for remote OCR backends.

kit

The facade kit: the three helpers every fleet facade used to copy by hand.

make_backend

Abstraction tools for building OCR facades.

registry

Backend discovery, registration, and lazy loading.

services

Lazy mapping of backend ids -> ServiceHandle.

status

Backend readiness status — four nested levels, info dicts, and a Markdown table.

tools

Command-line tools for ocracy (dispatched via cw in __main__).

translation

Parameter translation between ocracy's normalized kwargs and native engines.

util

Image-input normalization and small shared helpers.