Metadata-Version: 2.4
Name: cnrocr
Version: 0.3.3
Summary: Container number detection and recognition (ISO 6346)
Author: Vislab
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://github.com/theodore-labs/cnrocr
Project-URL: Issues, https://github.com/theodore-labs/cnrocr/issues
Keywords: ocr,container,iso6346,onnx,d-fine,crnn,logistics
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: pillow>=9.0
Requires-Dist: onnxruntime>=1.16
Requires-Dist: cryptography>=42
Provides-Extra: gpu
Requires-Dist: onnxruntime-gpu>=1.16; extra == "gpu"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Dynamic: license-file

# cnrocr

[![PyPI](https://img.shields.io/pypi/v/cnrocr.svg)](https://pypi.org/project/cnrocr/)
[![Python](https://img.shields.io/pypi/pyversions/cnrocr.svg)](https://pypi.org/project/cnrocr/)
[![Downloads](https://img.shields.io/pypi/dm/cnrocr.svg)](https://pypi.org/project/cnrocr/)
[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macOS%20%7C%20Windows-lightgrey.svg)](https://pypi.org/project/cnrocr/#files)
[![License](https://img.shields.io/badge/license-Proprietary-red.svg)](#license)

Container number detection and recognition — **ISO 6346** end-to-end, ONNX only.

A region detector locates the number, ISO type code, owner code and serial on
the container; an OCR recognizer reads each crop with a spec-constrained beam
search. Fragments split across panels are merged back into a single number and
validated against the ISO 6346 check digit.

**No PyTorch required.** The runtime is `onnxruntime` + `numpy` + `pillow`.

---

## Install

```bash
pip install cnrocr           # CPU
pip install cnrocr[gpu]      # NVIDIA CUDA via onnxruntime-gpu
```

Model weights (~113 MB) are **not** bundled in the wheel. They are downloaded on
first use and cached locally:

```bash
cnrocr models download       # optional — happens automatically otherwise
```

---

## Python API

```python
from cnrocr import ContainerOCR

ocr = ContainerOCR()                      # weights resolved from cache
result = ocr.read("gate_cam.jpg")

for c in result.containers:
    print(c.number, c.iso_type, c.confidence, c.needs_review)
# TGHU8913884 22G1 0.9997 False
```

### Multiple images

```python
results = ocr.read_many(["a.jpg", "b.jpg", "c.jpg"], batch_size=8)
```

`read_many` treats the images as **unrelated** — N images in, N results out.

### Multi-view fusion

When several cameras photograph the **same** container, fuse them into one
answer instead of voting on strings:

```python
mv = ocr.read_multiview(["cam1.jpg", "cam2.jpg", "cam3.jpg"])
print(mv.number, mv.agreement, mv.mode)
```

Beam-search candidates from every view are summed in log space, so a character
that one view is unsure about can be settled by the others. If the views appear
to be looking at *different* containers, they are not fused — `mv.consensus`
becomes `False` rather than producing a confident wrong answer.

### Review triage

A check digit alone is not enough. The constrained decoder only emits
spec-conforming candidates, so when the true string is absent from the beam it
will confidently output a *plausible* wrong number that still passes the check
digit. `needs_review` combines the check digit, the spec flag and a confidence
floor:

```python
if c.needs_review:
    print(c.review_reason)     # "low confidence (0.612 < 0.7)"
```

### Owner code registry (optional)

Real-world owner codes are registered with the BIC. Supplying the list filters
out invented codes that would otherwise pass both the format check and the check
digit:

```python
from cnrocr import OwnerCodeRegistry
ocr = ContainerOCR(registry=OwnerCodeRegistry.from_file("bic_codes.txt"))
```

The list is not shipped with this package.

---

## Command line

```bash
cnrocr read gate_cam.jpg
cnrocr read *.jpg --json --device cuda
cnrocr multiview cam1.jpg cam2.jpg cam3.jpg
cnrocr models status
cnrocr check                    # diagnose install, providers, cache
```

`--fail-on-review` makes the process exit with code 2 when any result needs
human review, which is convenient in batch pipelines.

---

## Evaluation limit

Without a licence, cnrocr processes **10 images per day**. The counter is per
image, not per call, and resets at 00:00 UTC.

```bash
cnrocr license      # licence status and today's usage
```

When the quota runs out the process exits with code 3 and prints how to ask for
a licence. Nothing is processed on a call that would exceed the quota — it is
all or nothing, so a refused call costs no quota.

To request an unrestricted licence, email **vislab2026@gmail.com** with your
name, organisation and intended use. You will receive a key:

```bash
# Windows
setx CNROCR_LICENSE "eyJlbWFpbCI6..."

# macOS / Linux
export CNROCR_LICENSE='eyJlbWFpbCI6...'
```

The key may also be saved to a file named `license` in the cache directory
(`cnrocr models path` shows where). Verify with `cnrocr check`.

---

## Weights and caching

| Platform | Location |
|---|---|
| Cache (Windows) | `%LOCALAPPDATA%\cnrocr\Cache\models\<set>` |
| Cache (macOS) | `~/Library/Caches/cnrocr/models/<set>` |
| Cache (Linux) | `~/.cache/cnrocr/models/<set>` |

Every file is verified against a SHA-256 recorded in the wheel. Released assets
are immutable: a new model set ships under a new tag and a new library version,
so upgrading never invalidates an existing install.

The weights are encrypted and are decrypted into memory when a session is
built. The cache holds ciphertext only; no plaintext model is written to disk.

Environment overrides:

| Variable | Effect |
|---|---|
| `CNROCR_MODEL_DIR` | Use this directory as-is; never download |
| `CNROCR_CACHE_DIR` | Relocate the cache root |
| `CNROCR_WEIGHTS_BASE_URL` | Fetch weights from somewhere else (`file://` works) |

---

## License

Proprietary. Evaluation and non-commercial research use only — see `LICENSE`.
Contact the copyright holder for commercial licensing.
