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 |
|
Local / Remote |
Cost |
Install |
Blocks |
Notable |
|---|---|---|---|---|---|---|
Tesseract |
|
local |
free |
|
word |
100+ language baseline |
EasyOCR |
|
local |
free |
|
line |
80+ languages, scene text |
RapidOCR |
|
local |
free |
|
line |
same PP-OCR models as Paddle, light CPU/ONNX — recommended default for local plain text |
PaddleOCR |
|
local |
free |
|
line |
the platform: server models, GPU, on-ramp to tables/layout (PP-Structure) & VL — heavier install |
ocrmac (Apple Vision) |
|
local (macOS) |
free |
|
line |
on-device, handwriting |
pix2tex (LaTeX-OCR) |
|
local |
free |
|
text only |
one printed equation → LaTeX, offline |
TrOCR (handwritten) |
|
local |
free |
|
text only |
one pre-segmented handwritten line, MIT weights |
OCR.space |
|
remote |
free tier |
|
word |
zero-install REST |
Google Cloud Vision |
|
remote |
paid (+free tier) |
|
word |
high accuracy, handwriting, structure |
AWS Textract |
|
remote |
paid (+free tier) |
|
word |
business docs, handwriting (forms/tables in analyze mode) |
Azure Document Intelligence |
|
remote |
paid (+free tier) |
|
word |
layout/tables/handwriting, on-prem container option |
Mistral OCR |
|
remote |
paid (pay-as-you-go) |
|
text only |
cheap VLM → clean Markdown + math/tables |
Mathpix |
|
remote |
paid (+free tier) |
|
text only |
math/handwriting → LaTeX & Markdown |
Claude Vision |
|
remote |
paid |
|
text only |
prompt-driven “read + reason” over messy/mixed documents |
GPT-4o Vision |
|
remote |
paid |
|
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 (
.wordspopulated,.linesempty):tesseract,google-vision,aws-textract,azure-document-intelligence,ocr-space.line-level (
.linespopulated,.wordsempty):easyocr,rapidocr,paddleocr,ocrmac.text only (
blocks == [], so both are empty andmean_confidenceisNone):claude-vision,gpt-4o-vision,mathpix,mistral-ocr,pix2tex-latex-ocr,trocr-handwritten. Useresult.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 |
|---|---|---|
|
|
https://ocr.space/ocrapi/freekey (free, 25k/month) |
|
|
|
|
|
https://docs.aws.amazon.com/textract/latest/dg/getting-started.html |
|
|
|
|
|
|
|
|
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 |
|---|---|---|
|
users |
OCR an image/PDF, choose & install a backend, read the result |
|
users |
filter & compare the 64-backend ledger to pick the right engine |
|
users / agents |
get a backend running — heavy installs, system deps, GPU, weights, keys |
|
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— theocracyCLI (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