Metadata-Version: 2.5
Name: ocracy
Version: 0.1.9
Summary: Facade to many implementations of (OCR) image-to-text-characters engines
Project-URL: Homepage, https://github.com/thorwhalen/ocracy
Project-URL: Repository, https://github.com/thorwhalen/ocracy
Project-URL: Documentation, https://thorwhalen.github.io/ocracy
Author: Thor Whalen
License: mit
License-File: LICENSE
Keywords: document-ai,facade,image-to-text,ocr,tesseract
Requires-Python: >=3.10
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.40; extra == 'anthropic'
Requires-Dist: pillow>=9; extra == 'anthropic'
Provides-Extra: aws-textract
Requires-Dist: boto3>=1.28; extra == 'aws-textract'
Requires-Dist: pillow>=9; extra == 'aws-textract'
Provides-Extra: azure
Requires-Dist: azure-ai-documentintelligence>=1; extra == 'azure'
Requires-Dist: pillow>=9; extra == 'azure'
Provides-Extra: cli
Requires-Dist: argcomplete>=3; extra == 'cli'
Requires-Dist: cw<0.2,>=0.1.1; extra == 'cli'
Provides-Extra: dev
Requires-Dist: pillow>=9; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: sphinx-rtd-theme>=1.0; extra == 'docs'
Requires-Dist: sphinx>=6.0; extra == 'docs'
Provides-Extra: doctr
Requires-Dist: python-doctr>=0.8; extra == 'doctr'
Provides-Extra: easyocr
Requires-Dist: easyocr>=1.7; extra == 'easyocr'
Provides-Extra: google-vision
Requires-Dist: google-cloud-vision>=3; extra == 'google-vision'
Requires-Dist: pillow>=9; extra == 'google-vision'
Provides-Extra: image
Requires-Dist: numpy>=1.23; extra == 'image'
Requires-Dist: pillow>=9; extra == 'image'
Provides-Extra: mathpix
Requires-Dist: requests>=2.28; extra == 'mathpix'
Provides-Extra: mistral
Requires-Dist: mistralai>=1; extra == 'mistral'
Provides-Extra: ocr-space
Requires-Dist: requests>=2.28; extra == 'ocr-space'
Provides-Extra: ocrmac
Requires-Dist: ocrmac>=0.2; extra == 'ocrmac'
Provides-Extra: openai
Requires-Dist: openai>=1; extra == 'openai'
Requires-Dist: pillow>=9; extra == 'openai'
Provides-Extra: paddleocr
Requires-Dist: paddleocr>=2.7; extra == 'paddleocr'
Requires-Dist: paddlepaddle>=2.5; extra == 'paddleocr'
Provides-Extra: pix2tex
Requires-Dist: pillow>=9; extra == 'pix2tex'
Requires-Dist: pix2tex>=0.1; extra == 'pix2tex'
Provides-Extra: rapidocr
Requires-Dist: pillow>=9; extra == 'rapidocr'
Requires-Dist: rapidocr-onnxruntime>=1.3; extra == 'rapidocr'
Provides-Extra: table
Requires-Dist: pandas>=1.5; extra == 'table'
Provides-Extra: tesseract
Requires-Dist: pillow>=9; extra == 'tesseract'
Requires-Dist: pytesseract>=0.3.10; extra == 'tesseract'
Provides-Extra: trocr
Requires-Dist: pillow>=9; extra == 'trocr'
Requires-Dist: torch>=2; extra == 'trocr'
Requires-Dist: transformers>=4.40; extra == 'trocr'
Description-Content-Type: text/markdown

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

```python
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:

```bash
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**:

```python
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:

```python
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:

```python
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)
```
```bash
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:

```python
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](#granularity-which-backends-fill-words-which-fill-lines)
before writing code that depends on one.

## Command line

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

```bash
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`](https://github.com/thorwhalen/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.

```python
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`](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:

```python
>>> 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:

```python
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/tools.py` + `ocracy/__main__.py` — the `ocracy` CLI (cw).

The architecture mirrors the sibling façade packages
[`denote`](https://github.com/thorwhalen/denote) (audio→symbol) and
[`aix`](https://github.com/thorwhalen/aix) (LLM providers).
