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:
A uniform facade. Call
ocr()and get the sameOcrResultback 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.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.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
|
Read text from an image with any backend, returning a normalized result. |
|
Like |
|
Filter the ledger; shorthand for |
|
Sorted ids of implemented backends, optionally filtered by capability. |
|
Register a backend at runtime (for third-party plugins). |
|
Pick a sensible default backend id for a capability. |
|
A backend's |
|
Build a normalized |
|
Create a new |
|
Smoke-test a backend adapter end to end, returning a report dict. |
|
Return structured install |
|
Is |
|
Report which implemented backends are usable now and what the rest need. |
|
Plan (and optionally run) the pip install for a backend. |
Implemented backends whose dependency is importable right now. |
|
|
Sorted backend ids at a readiness |
|
Per-backend status: every ledger field plus |
|
Render an aligned Markdown table of backend status (also plain-text readable). |
|
|
|
Ready to run here & now. |
|
Actually run OCR on |
Classes
|
The normalized result of reading an image with any backend. |
|
One recognized unit of text. |
|
An axis-aligned bounding box in pixel coordinates (origin = top-left). |
|
One backend's ledger entry — a read-only, attribute-and-dict accessible record. |
|
A filterable, dict-like collection of |
Lazy mapping of backend ids -> |
|
|
Optional base class for backend adapters. |
|
What a backend needs to run, structured for an agent to act on. |
- class ocracy.BBox(x0, y0, x1, y1, polygon=None)[source]#
Bases:
objectAn axis-aligned bounding box in pixel coordinates (origin = top-left).
polygonoptionally carries the original (possibly rotated) vertices as a sequence of(x, y)points; the axis-alignedx0/y0/x1/y1are 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’scropexpects.
- class ocracy.BackendInfo(record, *, implemented=False)[source]#
Bases:
MappingOne 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:
objectOptional base class for backend adapters.
Stores the config, builds a kwarg translator from
config['param_map'], and implementsreadas: translate normalized kwargs -> native kwargs ->_read(). Subclasses implement_read()and return anOcrResult.Adapters are not required to subclass this — the registry only needs an
Adapterclass with aread(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_mapdeclares them, and they must never be dropped. On a local backend they are dropped with a (redacted) note.
- class ocracy.Catalog(path=None, *, _records=None)[source]#
Bases:
MappingA filterable, dict-like collection of
BackendInfo, keyed by id.Loaded lazily from
DEFAULT_LEDGER_PATH(override via thepathargument or theOCRACY_LEDGERenvironment variable).filter()returns a newCatalogover 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_textlist or a<capability>field set to"yes"(e.g.handwriting,tables,math_formula).- Return type:
- 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.
- filter(*, predicate=None, implemented=None, **criteria)[source]#
Return a new
Catalogof backends matching every criterion.- Parameters:
predicate (
Optional[Callable[[BackendInfo],bool]]) – An arbitraryBackendInfo -> boolcallable.implemented (
Optional[bool]) – If set, keep only (un)implemented backends.**criteria (
Any) –field=valueconstraints.valuemay 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:
Example:
catalog.filter(is_local=True, open_source=True, handwriting="yes")
- class ocracy.OcrResult(text, blocks=<factory>, backend='', raw=None, meta=<factory>)[source]#
Bases:
objectThe normalized result of reading an image with any backend.
textis the headline payload: the full recognized text in reading order.blockscarries the structured units (with boxes/confidences) when the backend provides them.rawis the untouched backend output.metaholds 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
- filter_confidence(min_confidence)[source]#
Return a copy keeping only blocks at or above
min_confidence.Blocks without a confidence are dropped.
textis rebuilt from the surviving blocks (joined by newline).- Return type:
- classmethod from_blocks(blocks, *, backend='', raw=None, text=None, joiner='\\n', **meta)[source]#
Build a result from structured blocks.
If
textis not given it is synthesized by joining the blocks’ text in their given order withjoiner(callers should pass blocks already in reading order, or pre-join and passtextexplicitly).- Return type:
- 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:
objectWhat a backend needs to run, structured for an agent to act on.
- class ocracy.ServiceCollection[source]#
Bases:
MappingLazy 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:
objectOne 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.backend_ids(level='all', *, info=None, test_image=None)[source]#
Sorted backend ids at a readiness
level(one ofLEVELS).Pass a precomputed
info(frombackend_info()) to avoid recomputation — important for"tested", which otherwise re-runs OCR.
- 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 (testedisNone); 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:
- Returns:
{id: {..., "name", "website", "implemented", "set_up", "tested"}}wheretestedisTrue/False/None(None = not attempted).
- ocracy.check(backend_id)[source]#
Is
backend_idimportable / usable right now? (no install, no network).- Return type:
- 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:
- 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:
- ocracy.get_config(backend_id)[source]#
A backend’s
BACKEND_CONFIGwithout loading its adapter.- Return type:
- 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_forthe capability and whose dependency is importable; then any importable backend for the capability; then — ifrequire_availableis False or nothing is installed — the first registered candidate (using it will raise a helpful install error).- Return type:
- 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 — callresult['requirements'].instructions()to show it. Withyes=Trueit runspip installfor the backend’s extra in the current interpreter, then (ifverify) 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:
- 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:
- 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:
- ocracy.list_backends(capability=None)[source]#
Sorted ids of implemented backends, optionally filtered by capability.
A backend matches
capabilityif it appears in the backend’scapabilitieslist (the primary"read"is implied for all).
- ocracy.make_block(text, *, bbox=None, confidence=None, conf_scale=1.0, level='word', language=None, **meta)[source]#
Build a normalized
TextBlock.bboxmay be any shape accepted byas_bbox().confidenceis normalized to[0, 1]viaconf_scale(e.g.conf_scale=100for percent-scale engines).- Return type:
- ocracy.names_with_sites(ids, *, info=None)[source]#
"Name (website), Name2 (website2), ..."forids(name, not id).- Return type:
- 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 (seelist_backends()). Defaults to the first installed implemented backend (seeget_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:
- ocracy.register_backend(backend_id, config, adapter=None)[source]#
Register a backend at runtime (for third-party plugins).
- ocracy.requirements(backend_id, *, gpu=False)[source]#
Return structured install
Requirementsforbackend_id.Works for both implemented backends (uses the
ocracy[extra]install and the recipe) and ledger-only backends (falls back to the ledger’spython_installstring). A recipe’s GPU guidance is always included;gpuis accepted for compatibility and changes nothing.- Return type:
- 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.pyfrom the backend’s ledger entry (if any) so you only have to flesh outparam_mapand implementadapter.py’s_read.- Parameters:
backend_id (
str) – The ledger id (e.g."easyocr","google-vision"). The on-disk module name uses underscores; the configidkeeps the id verbatim.dest (
Union[str,Path,None]) – Target directory (defaults toocracy/backends/<id_underscored>).overwrite (
bool) – Allow writing into an existing non-empty directory.ledger (
Any) – ACatalogto 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:
- 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
infoto avoid re-running tests; otherwiserun_testscontrols which set-up backends get OCR-tested.- Return type:
- 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
readon a generated (or supplied) image, and checks the contract: anOcrResultcame back with text and/or blocks. Never raises on a recognition mismatch — it returns what happened so callers can decide.- Return type:
Modules
Implemented OCR backends. |
|
Core types and normalized result objects for ocracy. |
|
A filterable, dict-like collection of |
|
Credential resolution for remote OCR backends. |
|
The facade kit: the three helpers every fleet facade used to copy by hand. |
|
Abstraction tools for building OCR facades. |
|
Backend discovery, registration, and lazy loading. |
|
Lazy mapping of backend ids -> |
|
Backend readiness status — four nested levels, info dicts, and a Markdown table. |
|
Command-line tools for ocracy (dispatched via |
|
Parameter translation between ocracy's normalized kwargs and native engines. |
|
Image-input normalization and small shared helpers. |