File size: 7,006 Bytes
4f5a43e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ec2b63
 
 
 
50bdc0c
4f5a43e
 
 
 
 
50bdc0c
 
 
4f5a43e
7684c9c
 
 
 
4f5a43e
 
 
 
 
 
7684c9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f5a43e
 
 
7684c9c
 
4f5a43e
 
7684c9c
4f5a43e
 
 
 
 
 
 
7684c9c
4f5a43e
7684c9c
4f5a43e
7684c9c
 
4f5a43e
7684c9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f5a43e
7684c9c
4f5a43e
 
 
50bdc0c
7684c9c
 
4f5a43e
 
 
 
 
 
 
7684c9c
4f5a43e
 
 
 
7684c9c
 
4f5a43e
7684c9c
4f5a43e
 
 
 
 
 
7684c9c
 
65362c9
7684c9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f5a43e
 
 
 
 
 
 
 
7684c9c
4f5a43e
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
---
license: mit
language:
  - fa
library_name: onnxruntime
pipeline_tag: image-to-text
tags:
  - alpr
  - license-plate-recognition
  - ocr
  - persian
  - farsi
  - iranian-license-plate
  - crnn
  - ctc
  - yolo
  - onnx
  - object-detection
---

<p align="center">
  <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/banner.png" alt="Platrix" width="100%" />
</p>

# Platrix — Iranian License-Plate Recognition Models

Production ONNX models for detecting and reading **Iranian vehicle license
plates**, powering the [**Platrix**](https://github.com/AliAkrami1375/Platrix)
real-time, self-hosted plate-surveillance system.

> 📦 **Code & full pipeline:** https://github.com/AliAkrami1375/Platrix
> 🤗 **This model repo:** https://huggingface.co/Dibachain/Platrix

The pipeline is **two-stage**: a YOLO **detector** locates the plate in the
frame, an image-quality **enhancement** step cleans the crop, then a
segmentation-free **CRNN + CTC reader** reads the whole plate at once and returns
the standard Iranian layout `DD L DDD DD` (two digits · letter · three digits ·
two-digit region), e.g. `۸۱ و ۶۳۸ ۱۳`.

All models run with **ONNX Runtime** — no PyTorch or TensorFlow needed at
inference time.

---

## 📊 Performance

Measured on **220 real Iranian surveillance photos** (grayscale gate/road
cameras — the hard, real-world domain, not staged shots).

<p align="center">
  <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/benchmark.png" width="88%" />
</p>

A lightweight **secondary detector** runs only when the primary finds nothing.
It recovers plates the primary is blind to — trucks, night shots, small/far and
dim plates — lifting the end-to-end read rate from **95.9% → 97.7%** with **no
regression** on the easy majority (the fallback fires on only ~2% of frames).

<p align="center">
  <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/recovery.png" width="70%" />
</p>

### Training curves

<p align="center">
  <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/reader_training.png" width="49%" />
  <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/detector_training.png" width="49%" />
</p>

The reader reaches ~93% whole-plate accuracy in training and **~98% on real
photos**; the detector reaches **mAP@0.5 ≈ 0.99** on held-out real frames.

---

## 📁 Files

| File | Role | Input | Output |
|------|------|-------|--------|
| `plate_yolo.onnx` | **Plate detector** (YOLOv8, primary) | `1×3×H×W` RGB, letterboxed, `/255` | `1×5×N``cx,cy,w,h,conf` |
| `plate_yolo_fallback.onnx` | **Secondary detector** — runs only when the primary finds nothing; recovers hard surveillance frames | `1×3×640×640` | `1×5×N` |
| `ocr_crnn.onnx` | **Whole-plate reader** (CRNN+CTC) — *recommended* | `1×1×32×128` grayscale, `/255` | `1×T×(C+1)` logits (CTC, blank = last) |
| `ocr_crnn.labels.json` | Class list for the CRNN (index → character) | — | 32 classes |
| `ocr_cnn.onnx` | Per-character classifier (lightweight fallback reader) | `1×1×32×32` grayscale | class logits |
| `ocr_cnn.labels.json` | Class list for the per-char classifier | — | — |

**Character set (32 classes):** digits `0–9` and the Persian plate letters
`ا ب ت ث ج ح د ز س ش ص ط ع ق ل م ن ه و پ ژ ی`.

---

## 🚀 Getting started

### Option A — Run the full Platrix system (recommended)

The complete app (web dashboard, multi-camera streaming, watchlists, API) lives
in the GitHub repo. It downloads these models for you.

```bash
git clone https://github.com/AliAkrami1375/Platrix.git
cd Platrix

# fetch the models from this repo into ./models
pip install -U "huggingface_hub[cli]"
huggingface-cli download Dibachain/Platrix \
    plate_yolo.onnx plate_yolo_fallback.onnx \
    ocr_crnn.onnx ocr_crnn.labels.json ocr_cnn.onnx ocr_cnn.labels.json \
    --local-dir models/

# then either:
docker compose up --build             # Docker
# — or —
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt && pip install -e .
platrix serve                         # dashboard at http://localhost:8080
```

### Option B — Use the models directly (ONNX Runtime)

```bash
pip install onnxruntime opencv-python-headless numpy
huggingface-cli download Dibachain/Platrix \
    plate_yolo.onnx plate_yolo_fallback.onnx ocr_crnn.onnx ocr_crnn.labels.json \
    --local-dir models/
```

```python
import json, cv2, numpy as np, onnxruntime as ort

# --- Reader (CRNN + CTC) ---
labels = json.load(open("models/ocr_crnn.labels.json", encoding="utf-8"))
blank  = len(labels)                        # CTC blank is the last index
crnn   = ort.InferenceSession("models/ocr_crnn.onnx", providers=["CPUExecutionProvider"])

def read_plate(plate_bgr):
    g = cv2.cvtColor(plate_bgr, cv2.COLOR_BGR2GRAY)
    g = cv2.resize(g, (128, 32)).astype(np.float32) / 255.0      # 1x1x32x128
    logits = crnn.run(None, {"input": g[None, None]})[0][0]       # T x (C+1)
    ids, out, prev = logits.argmax(1), [], -1
    for i in ids:                                                # greedy CTC decode
        if i != blank and i != prev:
            out.append(labels[i])
        prev = i
    return "".join(out)
```

**Two-stage flow:** run `plate_yolo.onnx` first (standard YOLOv8 letterbox
pre-process + confidence/NMS post-process) to crop the plate, then pass the crop
to `read_plate`. If the primary detector returns nothing, run
`plate_yolo_fallback.onnx` at **640×640** as a second pass — it recovers the hard
surveillance frames the primary is blind to. A few **real test photos** ship
under [`img-test/`](./tree/main/img-test) so you can try it immediately.

---

## 🧠 How it works

1. **Detect** — YOLOv8 locates the plate; weak/non-plate boxes are ignored.
2. **Enhance** — the crop is upscaled, denoised, contrast-corrected and sharpened.
   The *same* enhancement is applied during training, so there is no train/serve
   mismatch — the enhancement genuinely helps instead of shifting the input.
3. **Read** — the segmentation-free CRNN reads the whole plate in one pass with a
   CTC head. It is trained on **real Iranian plate-character shapes**, so
   look-alike glyphs (e.g. the digit `۴` vs `۶`) are read correctly.

Splitting a plate into individual characters is fragile on real photos (shadows,
motion blur, tilt, dirt); reading the entire plate at once is far more robust.

---

## Intended use & limitations

- **Intended for** lawful applications such as parking management, access
  control, gate automation and traffic analytics.
- **Optimised for** standard private Iranian plates. Very low-resolution,
  heavily occluded or non-standard plates may reduce accuracy.
- You are responsible for complying with the privacy and surveillance laws that
  apply to your deployment.

---

## Links

- **Project & full pipeline:** https://github.com/AliAkrami1375/Platrix
- **License:** MIT