Source code for 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 :func:`ocr` and get the same :class:`~ocracy.base.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: :func:`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
"""

from ocracy.base import BBox, ImageInput, LEVELS, OcrResult, TextBlock
from ocracy.catalog import BackendInfo, Catalog, catalog
from ocracy.registry import (
    get_config,
    get_default_backend,
    list_backends,
    register_backend,
)
from ocracy.services import ServiceCollection
from ocracy.make_backend import (
    BaseOcrAdapter,
    make_block,
    scaffold_backend,
    validate_adapter,
)
from ocracy.install import (
    Requirements,
    available_backends,
    check,
    doctor,
    install,
    requirements,
)
from ocracy.status import (
    LEVELS,
    backend_ids,
    backend_info,
    is_set_up,
    is_tested,
    names_with_sites,
    status_table,
)

__all__ = [
    "ocr",
    "read_text",
    "services",
    "catalog",
    "find",
    "list_backends",
    "register_backend",
    "get_default_backend",
    "get_config",
    "OcrResult",
    "TextBlock",
    "BBox",
    "ImageInput",
    "LEVELS",
    "BackendInfo",
    "Catalog",
    "ServiceCollection",
    "BaseOcrAdapter",
    "make_block",
    "scaffold_backend",
    "validate_adapter",
    "requirements",
    "check",
    "doctor",
    "install",
    "available_backends",
    "Requirements",
    "backend_ids",
    "backend_info",
    "status_table",
    "names_with_sites",
    "is_set_up",
    "is_tested",
    "LEVELS",
    "__version__",
]

# Derive the version from installed package metadata (the pyproject SSOT, which CI
# auto-bumps) so __version__ never drifts from a hardcoded literal.
from importlib.metadata import PackageNotFoundError as _PNFE, version as _version

try:
    __version__ = _version("ocracy")
except _PNFE:  # running from a source tree without install metadata
    __version__ = "0.0.0+source"
del _version, _PNFE

#: Singleton service collection for per-backend access (``services.tesseract``).
services = ServiceCollection()


[docs] def ocr(image: ImageInput, *, backend: str = None, **kwargs) -> OcrResult: """Read text from an image with any backend, returning a normalized result. Args: image: A path, ``http(s)`` URL, ``bytes``, PIL image, or numpy array. backend: Backend id (see :func:`list_backends`). Defaults to the first *installed* implemented backend (see :func:`get_default_backend`). **kwargs: Normalized, backend-translated options (e.g. ``languages``). Unknown options for the chosen backend are warned about and dropped. Returns: An :class:`~ocracy.base.OcrResult` (``str(result)`` is the text). """ backend = backend or get_default_backend() return services[backend].read(image, **kwargs)
[docs] def read_text(image: ImageInput, *, backend: str = None, **kwargs) -> str: """Like :func:`ocr` but returns just the recognized text string.""" return ocr(image, backend=backend, **kwargs).text
[docs] def find(**criteria) -> Catalog: """Filter the ledger; shorthand for :meth:`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 catalog.filter(**criteria)