Metadata-Version: 2.4
Name: legere
Version: 0.6.0
Summary: Compact CRNN OCR for printed text (Portuguese charset) — pure PyTorch, CPU-friendly, ~360k parameters
Author-email: Rafael Cezar <imrafaelcezar@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/imrafaelcezar/legere
Project-URL: Repository, https://github.com/imrafaelcezar/legere
Project-URL: Issues, https://github.com/imrafaelcezar/legere/issues
Keywords: ocr,crnn,ctc,pytorch,computer-vision,portuguese,text-recognition
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
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: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch>=2.0
Requires-Dist: numpy>=1.24
Requires-Dist: opencv-python-headless>=4.8
Requires-Dist: pillow>=10.0
Provides-Extra: pdf
Requires-Dist: pypdfium2>=4.28; extra == "pdf"
Provides-Extra: train
Requires-Dist: rich>=13.0; extra == "train"
Requires-Dist: psutil>=5.9; extra == "train"
Dynamic: license-file

# Legere

Compact CRNN OCR for printed text (Portuguese charset with accents, digits,
punctuation), built from scratch with PyTorch and trained entirely on
synthetic pages generated with PIL. No pretrained models, no OCR libraries.

- **~360k parameters** (hard limit enforced: 1M) — bundled weights are 1.4 MB
- CRNN: MobileNet-style depthwise separable CNN + 1 BiGRU(128) + linear head, CTC loss
- Classic (non-neural) deskew + projection-based line segmentation
- CPU-friendly: full-HD page in well under a second; INT8/TorchScript exports
- Trained weights ship inside the package — install and read pages immediately

## Install

```bash
pip install legere              # inference only
pip install legere[pdf]        # + PDF input support (pypdfium2)
pip install legere[train]       # + training extras (rich, psutil)
```

From a checkout:

```bash
pip install -e .[train]
```

## Library usage

```python
from legere import Legere

ocr = Legere()                      # bundled weights, CPU
result = ocr.read("page.png")       # path, or a numpy array (gray or BGR)

print(result.text)                  # full text in reading order
print(result.skew_angle)            # estimated page skew (degrees)
for line in result.lines:
    print(line.text, line.bbox)     # per-line text + (x0, y0, x1, y1)
```

Options: `Legere(model="checkpoints/best.pt")` for your own checkpoint,
`Legere(model="exports/model_int8.ts.pt", torchscript=True)` for the INT8
export, `beam_width=8` for CTC beam search, `threads=N` to cap CPU threads.

PDFs (with `legere[pdf]` installed):

```python
results = ocr.read_pdf("document.pdf", dpi=200)   # one PageResult per page

from legere.pdf import pdf_to_pngs
pdf_to_pngs("document.pdf", out_dir="pages/")     # just convert to PNGs
```

## CLI

```bash
legere read page.png                    # text on stdout
legere page.png                         # same (shortcut)
legere read page.png --json out.json    # + per-line text and bboxes
legere read page.png --beam 8           # CTC beam search
legere read page.png --model exports/model_int8.ts.pt --torchscript

legere read document.pdf                # OCR every PDF page (legere[pdf])
legere pdf document.pdf --out-dir pages # convert PDF pages to PNGs (no OCR)

legere train                            # train on synthetic pages
legere export                           # TorchScript fp32 + INT8 exports
legere benchmark                        # CPU latency + page CER report
```

Pipeline of `read`: fit to 1920x1080 → adaptive binarization + deskew → line
segmentation by horizontal projection (rules removed via morphology) →
height-32 line batch through the CRNN → CTC greedy decode → text in reading
order.

## Project layout

```
src/legere/
├── charset.py        # character set + encode/decode helpers
├── model.py          # CompactCRNN, parameter budget, CTC greedy/beam decode
├── segment.py        # classic deskew + projection line segmentation
├── lines.py          # line-crop normalization and batching
├── inference.py      # Legere engine + full-page pipeline
├── pdf.py            # PDF page rendering / PNG conversion (legere[pdf])
├── metrics.py        # edit distance, CER, WER
├── cli.py            # `legere` command
├── data/model.pt     # bundled trained weights (state dict + charset)
└── training/         # synthetic pagegen, dataset stream, train/export/benchmark
```

## Training

Training data is generated on the fly: synthetic pages with headers,
paragraphs, key/value lines, tables, Portuguese-like words, dates, monetary
values and CPF/CNPJ, degraded with noise/blur/JPEG artifacts. Fonts are read
from `C:\Windows\Fonts` (training currently expects Windows).

```bash
legere train                          # defaults, live dashboard
legere train --setup                  # interactive setup first
legere train --finetune bundled       # start from the packaged weights
legere train --finetune ckpt.pt       # start from any checkpoint's weights
legere train --no-dashboard           # plain log lines
legere train --resume checkpoints/last.pt   # continue an interrupted run
```

`legere train --setup` opens an interactive setup where every option
(mode, style, steps, threads, workers, batch size, ...) is prompted with a
sensible default — press Enter to accept, then confirm the summary.

`--finetune` loads weights only (fresh optimizer, LR schedule, step counter
and best-CER — peak LR defaults to 5e-4 instead of 2e-3), so a new-style run
converges much faster than from scratch. `--resume` restores the full
training state and is meant for continuing the same interrupted run.

The live dashboard shows the run mode (scratch/fine-tune/resume), loss and
CER trend sparklines, per-style CER (`document` vs `report`), punctuation
CER, throughput, eval history, sample predictions and RAM/CPU gauges.

Resource usage is configurable for bigger machines:

```bash
legere train --workers 2              # generate pages in background processes
legere train --threads 12             # default: all physical cores
legere train --width-budget 18432     # batch size knob (default: 9216)
legere train --ram-limit 8192         # MB shown in the dashboard RAM gauge
```

With `--workers 0` (default) pages are generated in-process — lowest RAM.
`--workers 1-2` overlaps data generation with the model's forward/backward
pass, which speeds up long runs when generation is the bottleneck (each
worker adds a few hundred MB of RAM).

Two synthetic page styles are available via `--style` (default `mixed`):
`document` (free-form headers, paragraphs, key/value lines, tables) and
`report` (bordered technical report forms: gray section bars, label/value
grid cells, measurements, serials, gray footer). `mixed` alternates both.

Training content is deliberately dense in easily-confused punctuation
(`.` `,` `;` `:` in times, decimals and lists); validation reports a
dedicated punctuation CER (`val punct CER` on the dashboard, `punct_cer`
in `runs/metrics.jsonl`) alongside overall CER/WER.

Checkpoints go to `checkpoints/` (`best.pt` by validation CER, `last.pt`
every eval); metrics append to `runs/metrics.jsonl`. Training stops early
once CER < 1% holds with no further improvement, or on a clear plateau.
Useful flags: `--threads N`, `--width-budget N` (batch size via padded-width
budget; lower it to reduce RAM).

After training, refresh the packaged weights:

```bash
legere export --checkpoint checkpoints/best.pt --bundle src/legere/data/model.pt
```

## Export & benchmark

```bash
legere export        # writes exports/model_fp32.ts.pt and exports/model_int8.ts.pt
legere benchmark     # line/page CPU latency + full-page CER per variant
```

## Results

Bundled weights: validation CER 0.044% (line level, 2000-line fixed set) at
step 26k, trained on the `document` style only — retraining with
`--style mixed` is recommended for report-style pages. `legere benchmark`
reproduces line/page latency and full-page CER on synthetic pages.

## License

MIT
