ocracy#

One façade over many OCR engines — plus a ledger to help you choose between them.

OCR (“read the text in this image”) is solved a dozen ways: local engines (Tesseract, EasyOCR, PaddleOCR), cloud APIs (Google Vision, AWS Textract, Azure), and VLM readers — each with its own install, API, pricing, and language coverage. ocracy gives you a uniform call, a browsable catalog of every option, and the tools to wrap any of them.

import ocracy

text = ocracy.read_text("scan.png")  # just the text, default backend
result = ocracy.ocr("scan.png")  # full result: text + word boxes + confidence
print(result)  # -> the recognized text
for word in result.words:
    print(word.text, word.bbox.as_tuple, word.confidence)

Install#

import ocracy is dependency-free. Install only the backends you use, via extras:

pip install "ocracy[tesseract]"     # local, free (also needs the system `tesseract` binary)
pip install "ocracy[easyocr]"       # local, pip-only
pip install "ocracy[google-vision]" # cloud API
pip install "ocracy[table]"         # pandas, for catalog.to_dataframe()

Backends that ship today#

The Blocks column is the one people get caught by: each backend reports exactly one granularity, so result.words is empty on a line-level backend and result.lines is empty on a word-level one — an empty list, not an error. See Granularity just below.

Backend

backend= id

Local / Remote

Cost

Install

Blocks

Notable

Tesseract

tesseract

local

free

ocracy[tesseract] (+ system tesseract)

word

100+ language baseline

EasyOCR

easyocr

local

free

ocracy[easyocr]

line

80+ languages, scene text

RapidOCR

rapidocr

local

free

ocracy[rapidocr]

line

same PP-OCR models as Paddle, light CPU/ONNX — recommended default for local plain text

PaddleOCR

paddleocr

local

free

ocracy[paddleocr]

line

the platform: server models, GPU, on-ramp to tables/layout (PP-Structure) & VL — heavier install

ocrmac (Apple Vision)

ocrmac

local (macOS)

free

ocracy[ocrmac]

line

on-device, handwriting

pix2tex (LaTeX-OCR)

pix2tex-latex-ocr

local

free

ocracy[pix2tex]

text only

one printed equation → LaTeX, offline

TrOCR (handwritten)

trocr-handwritten

local

free

ocracy[trocr]

text only

one pre-segmented handwritten line, MIT weights

OCR.space

ocr-space

remote

free tier

ocracy[ocr-space]

word

zero-install REST

Google Cloud Vision

google-vision

remote

paid (+free tier)

ocracy[google-vision]

word

high accuracy, handwriting, structure

AWS Textract

aws-textract

remote

paid (+free tier)

ocracy[aws-textract]

word

business docs, handwriting (forms/tables in analyze mode)

Azure Document Intelligence

azure-document-intelligence

remote

paid (+free tier)

ocracy[azure]

word

layout/tables/handwriting, on-prem container option

Mistral OCR

mistral-ocr

remote

paid (pay-as-you-go)

ocracy[mistral]

text only

cheap VLM → clean Markdown + math/tables

Mathpix

mathpix

remote

paid (+free tier)

ocracy[mathpix]

text only

math/handwriting → LaTeX & Markdown

Claude Vision

claude-vision

remote

paid

ocracy[anthropic]

text only

prompt-driven “read + reason” over messy/mixed documents

GPT-4o Vision

gpt-4o-vision

remote

paid

ocracy[openai]

text only

same, via OpenAI; schema-constrained JSON output

…plus 49 more engines/services catalogued in the ledger that you can turn into a working façade with one command (see Add a backend below).

Granularity: which backends fill .words, which fill .lines#

Every backend emits exactly one granularity level. Nothing promotes words into lines or splits lines into words, and result.words / result.lines are just filters over the one flat blocks list — so asking for the level a backend does not produce returns [], silently:

r = ocracy.ocr("scan.png", backend="rapidocr")
r.lines  # populated
r.words  # [] — rapidocr reports lines, and nothing raises

r = ocracy.ocr("scan.png", backend="tesseract")
r.words  # populated
r.lines  # [] — tesseract reports words
  • word-level (.words populated, .lines empty): tesseract, google-vision, aws-textract, azure-document-intelligence, ocr-space.

  • line-level (.lines populated, .words empty): easyocr, rapidocr, paddleocr, ocrmac.

  • text only (blocks == [], so both are empty and mean_confidence is None): claude-vision, gpt-4o-vision, mathpix, mistral-ocr, pix2tex-latex-ocr, trocr-handwritten. Use result.text / result.markdown.

Iterating a result (for block in result:) yields whatever units that engine gave you, at its own level — it is not “lines by default”.

The ledger’s bounding_boxes flag describes the engine, not ocracy’s adapter. claude-vision, mathpix and mistral-ocr are all bounding_boxes: true (their APIs can return geometry) while ocracy’s façades for them return text only. Treat ocracy.find(bounding_boxes=True) as “engines worth wrapping for geometry”, and the list above as what you get today. If your code needs a level, assert it:

result = ocracy.ocr(img, backend=chosen)
if not result.words:
    raise ValueError(f"{chosen} does not report word-level boxes")

Getting a backend running#

Some backends need more than pip install (Tesseract’s system binary, Paddle’s framework, GPU wheels, first-run model weights, or an API key). ocracy turns that into structured, OS-aware guidance — handy for humans and AI agents alike:

ocracy.doctor()  # what's usable now vs what each missing one needs
ocracy.check("paddleocr")  # -> True/False (usable right now?)
print(
    ocracy.requirements("paddleocr").instructions()
)  # exact plan: pip + system deps + GPU + weights
ocracy.install(
    "rapidocr", yes=True
)  # run the pip install + verify (yes=False = dry run)
ocracy doctor · ocracy requirements paddleocr --gpu · ocracy install rapidocr --yes

requirements() even suggests a lighter alternative when one exists (e.g. PaddleOCR → RapidOCR). The ocracy-install-backend skill drives all of this.

Three tiers of access#

From simplest to most powerful — reach for the next tier only when you need it:

ocracy.ocr(img)  # 1. facade, default (installed) backend
ocracy.ocr(img, backend="easyocr", languages=["en", "fr"])
ocracy.services.tesseract.read(img, psm=6)  # 2. pick a backend explicitly
ocracy.services.tesseract.adapter  # 3. the raw engine adapter, full control

Every backend returns the same OcrResult, so your code doesn’t change when you switch engines: result.text, result.words / result.lines, each block’s .bbox and .confidence (normalized to 0..1), result.mean_confidence, result.markdown (when a backend produces it), and result.raw (the untouched engine output).

The shape is uniform; the granularity is not. Each engine fills either .words or .lines (or neither), and the other is an empty list rather than an error — see Granularity before writing code that depends on one.

Command line#

Install the CLI extra (pip install "ocracy[cli]") for an ocracy command:

ocracy read scan.png                          # print recognized text
ocracy read scan.png --backend easyocr --languages en,fr
ocracy read scan.png --output json            # text + blocks (boxes, confidence)
ocracy backends                               # backends you can run now
ocracy backends --capability math
ocracy status                                 # readiness table: all / implemented / set-up / tested
ocracy status --level set_up --names          # one level + the "Name (website)" lists
ocracy status --run-tests                     # also OCR-test set-up backends (real calls for remotes)
ocracy find --local --free --handwriting      # browse/filter the ledger
ocracy info google-vision                     # a backend's full record
ocracy scaffold surya                          # start a new façade from the ledger
ocracy validate tesseract                      # smoke-test a backend

Everything is also reachable as python -m ocracy <command>. cw builds the parser from the function signatures in ocracy/tools.py; argcomplete gives tab completion when activated.

The ledger — choose a backend with eyes open#

ocracy ships a curated, data-driven catalog of every engine we researched — not only the ones with a working façade — so you can compare on the axes that matter (local vs remote, price, accuracy, languages, handwriting, math, tables, privacy). The data lives in ocracy/data/backends.json, separate from code.

ocracy.catalog  # <Catalog … backends … implemented …>
ocracy.catalog["google-vision"]  # one backend's full record
ocracy.find(is_local=True, open_source=True)  # filter the ledger
ocracy.find(handwriting="yes", is_remote=True)
ocracy.catalog.can("math")  # engines that read formulas
ocracy.catalog.supports_language("Arabic")
ocracy.find(implemented=True)  # only what ocracy can run today
ocracy.catalog.to_dataframe()  # browse as a pandas table
ocracy.catalog.compare(["tesseract", "google-vision", "mathpix"])

implemented is computed live from the code, so the ledger never lies about what actually runs. See ocracy/data/SCHEMA.md for every field. The full cited research report behind the ledger is in misc/docs/ocr_landscape_research.md.

Getting API keys (remote backends)#

Remote backends need a credential in an environment variable. ocracy tells you exactly which one — with a link to get it — the moment it’s missing:

>>> ocracy.ocr("receipt.png", backend="ocr-space")
MissingCredentialError: No credential found for ocr-space (set one of: OCR_SPACE_API_KEY).
How to get a credential for ocr-space: Register a free API key by email; free tier
allows 25,000 requests/month. Get a key: https://ocr.space/ocrapi/freekey

Backend

Environment variable(s)

Where to get a key

ocr-space

OCR_SPACE_API_KEY

https://ocr.space/ocrapi/freekey (free, 25k/month)

google-vision

GOOGLE_APPLICATION_CREDENTIALS (service-account JSON path)

https://cloud.google.com/vision/docs/setup

aws-textract

AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_DEFAULT_REGION

https://docs.aws.amazon.com/textract/latest/dg/getting-started.html

azure-document-intelligence

AZURE_DOCUMENT_INTELLIGENCE_KEY + AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT

https://learn.microsoft.com/azure/ai-services/document-intelligence/create-document-intelligence-resource

mistral-ocr

MISTRAL_API_KEY

https://console.mistral.ai/api-keys

mathpix

MATHPIX_APP_ID + MATHPIX_APP_KEY

https://mathpix.com/ocr-api

The same guidance (and links for AWS, Azure, Mistral, OpenAI, Anthropic, Gemini) lives in ocracy.credentials.CREDENTIAL_GUIDANCE, and powers the dynamic errors. Tip: drop the variables in a .env file — ocracy soft-loads it via python-dotenv if that package is installed.

Add a backend (wrap any engine in minutes)#

The catalog is large; ocracy ships façades for a curated subset and gives you the machinery to add any other one. Scaffold a backend straight from its ledger entry, fill in the adapter, and validate it:

from ocracy.make_backend import scaffold_backend, validate_adapter

scaffold_backend(
    "mathpix"
)  # creates ocracy/backends/mathpix/ prefilled from the ledger
# ... implement adapter.py's _read (call the engine, return an OcrResult) ...
validate_adapter("mathpix")  # smoke-test it end to end

The full, step-by-step process — including the adapter contract, input/output normalization helpers, credential handling, and packaging — is captured as an agent skill at ocracy/data/skills/ocracy-add-backend/SKILL.md. The ocracy/backends/tesseract/ package is a complete worked example.

Agent skills (Claude Code & other AI agents)#

ocracy ships four Anthropic-style skills (under ocracy/data/skills/, installed with the package) so AI coding agents can both use and extend it:

Skill

Audience

What it helps with

ocracy

users

OCR an image/PDF, choose & install a backend, read the result

ocracy-choose-backend

users

filter & compare the 64-backend ledger to pick the right engine

ocracy-install-backend

users / agents

get a backend running — heavy installs, system deps, GPU, weights, keys

ocracy-add-backend

developers

wrap a new engine behind ocracy’s interface (scaffold → adapter → validate)

In a repo using Claude Code they’re discoverable via the .claude/skills/ symlink bridge; they also travel with pip install ocracy under ocracy/data/skills/.

How it works#

  • ocracy/base.py — the normalized types (OcrResult, TextBlock, BBox).

  • ocracy/catalog.py — the ledger reader/filter (ocracy/data/backends.json).

  • ocracy/registry.py — lazy backend discovery + default selection.

  • ocracy/services.py — the three-tier service layer.

  • ocracy/make_backend.py — the façade-building toolkit (BaseOcrAdapter, make_block, scaffold_backend, validate_adapter).

  • ocracy/backends/<id>/ — one subpackage per engine (config.py + adapter.py).

  • ocracy/credentials.py — credential resolution for remote backends.

  • ocracy/kit/ — the facade kit: the param_map translator, the credential chain and install plans, stdlib-only and shared with other facades (see below).

  • ocracy/tools.py + ocracy/__main__.py — the ocracy CLI (cw).

The facade kit, for building other facades#

The machinery under ocracy is not OCR-specific, and other facades (foley, falaw, voxy, scribed) use it instead of copying it:

from ocracy.kit import make_translator, resolve_credential, using_credentials

translate = make_translator(
    {"languages": {"native_name": "lang", "coerce": "+".join}, "dpi": None},
    backend="tess",
    on_unsupported="note",
    always_raise=("seed",),
)
t = translate({"languages": ["eng", "fra"], "dpi": 300})
# t.kwargs == {"lang": "eng+fra"}; t.notes == ["dpi=300 is not supported by tess; dropped"]

key = resolve_credential("acme", env_var="ACME_API_KEY")  # explicit -> bound -> env
with using_credentials(acme=request_key):  # per-request BYO key
    ...

ocracy.kit imports only the standard library; the design is in docs/adr/0001-facade-kit.md.

The architecture mirrors the sibling façade packages denote (audio→symbol) and aix (LLM providers).

This documentation as a single file: ocracy.md (Markdown, for agents).

built 2026-10-14 02:29 UTC from 4de5796 (main) · ocracy 0.1.13 · about this build