DibaAi commited on
Commit
7684c9c
Β·
1 Parent(s): 65362c9

Add professional performance charts and a complete setup guide to the model card

Browse files
README.md CHANGED
@@ -32,24 +32,55 @@ real-time, self-hosted plate-surveillance system.
32
  > πŸ€— **This model repo:** https://huggingface.co/Dibachain/Platrix
33
 
34
  The pipeline is **two-stage**: a YOLO **detector** locates the plate in the
35
- frame, then a segmentation-free **CRNN + CTC reader** reads the whole plate at
36
- once and returns the standard Iranian layout `DD L DDD DD`
37
- (two digits · letter · three digits · two-digit region), e.g. `۸۱ و ۢ۳۸ ۱۳`.
 
38
 
39
  All models run with **ONNX Runtime** β€” no PyTorch or TensorFlow needed at
40
  inference time.
41
 
42
  ---
43
 
44
- ## Files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  | File | Role | Input | Output |
47
  |------|------|-------|--------|
48
- | `plate_yolo.onnx` | **Plate detector** (YOLOv8) | `1Γ—3Γ—HΓ—W` RGB, letterboxed, `/255` | `1Γ—5Γ—N` β†’ `cx,cy,w,h,conf` |
49
- | `plate_yolo_fallback.onnx` | **Secondary detector** β€” runs only when the primary finds nothing; recovers hard surveillance frames (grayscale / dim / small / truck plates) | `1Γ—3Γ—640Γ—640` | `1Γ—5Γ—N` |
50
  | `ocr_crnn.onnx` | **Whole-plate reader** (CRNN+CTC) β€” *recommended* | `1Γ—1Γ—32Γ—128` grayscale, `/255` | `1Γ—TΓ—(C+1)` logits (CTC, blank = last) |
51
  | `ocr_crnn.labels.json` | Class list for the CRNN (index β†’ character) | β€” | 32 classes |
52
- | `ocr_cnn.onnx` | Per-character classifier (lightweight fallback) | `1Γ—1Γ—32Γ—32` grayscale | class logits |
53
  | `ocr_cnn.labels.json` | Class list for the per-char classifier | β€” | β€” |
54
 
55
  **Character set (32 classes):** digits `0–9` and the Persian plate letters
@@ -57,69 +88,82 @@ inference time.
57
 
58
  ---
59
 
60
- ## Why a segmentation-free reader?
61
 
62
- Splitting a plate into individual characters is fragile on real photos
63
- (shadows, motion blur, tilt, dirt). The CRNN reads the **entire plate in one
64
- pass** with a CTC head, which is far more robust. It is trained on **real
65
- Iranian plate-character shapes**, so look-alike glyphs (e.g. the digit `Ϋ΄` vs
66
- `ΫΆ`) are read correctly.
67
 
68
- A key detail: the **same image-enhancement** (upscale β†’ denoise β†’ CLAHE
69
- contrast β†’ unsharp) is applied both during training and at serving time, so
70
- there is no train/serve mismatch β€” the enhancement genuinely helps accuracy
71
- instead of shifting the input distribution.
72
 
73
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- ## Quick usage
76
 
77
  ```bash
78
  pip install onnxruntime opencv-python-headless numpy
79
  huggingface-cli download Dibachain/Platrix \
80
- plate_yolo.onnx ocr_crnn.onnx ocr_crnn.labels.json --local-dir models/
 
81
  ```
82
 
83
- A few **real test photos** ship in this repo under [`img-test/`](./tree/main/img-test)
84
- so you can try recognition right away:
85
-
86
- <p align="center">
87
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-01.jpg" width="32%" />
88
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-02.jpg" width="32%" />
89
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-03.jpg" width="32%" />
90
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-05.jpg" width="32%" />
91
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-06.jpg" width="32%" />
92
- <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/img-test/sample-08.jpg" width="32%" />
93
- </p>
94
-
95
  ```python
96
  import json, cv2, numpy as np, onnxruntime as ort
97
 
98
  # --- Reader (CRNN + CTC) ---
99
  labels = json.load(open("models/ocr_crnn.labels.json", encoding="utf-8"))
100
- blank = len(labels) # CTC blank is the last index
101
  crnn = ort.InferenceSession("models/ocr_crnn.onnx", providers=["CPUExecutionProvider"])
102
 
103
  def read_plate(plate_bgr):
104
  g = cv2.cvtColor(plate_bgr, cv2.COLOR_BGR2GRAY)
105
- g = cv2.resize(g, (128, 32)).astype(np.float32) / 255.0 # 1x1x32x128
106
- logits = crnn.run(None, {"input": g[None, None]})[0][0] # T x (C+1)
107
  ids, out, prev = logits.argmax(1), [], -1
108
- for i in ids: # greedy CTC decode
109
  if i != blank and i != prev:
110
  out.append(labels[i])
111
  prev = i
112
  return "".join(out)
113
  ```
114
 
115
- Run `plate_yolo.onnx` first to crop the plate from a full frame (standard
116
- YOLOv8 letterbox pre-process + confidence/NMS post-process), then pass the crop
117
  to `read_plate`. If the primary detector returns nothing, run
118
- `plate_yolo_fallback.onnx` (at 640Γ—640) as a second pass β€” it recovers hard
119
- surveillance frames (grayscale, dim, small or truck-mounted plates) the primary
120
- is blind to, raising end-to-end read rate on real photos from ~96% to ~98%. The full, batteries-included pipeline β€” detection, enhancement,
121
- grammar-constrained decoding, multi-camera streaming, watchlists and a web
122
- dashboard β€” lives in the [Platrix repository](https://github.com/AliAkrami1375/Platrix).
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  ---
125
 
@@ -128,7 +172,7 @@ dashboard β€” lives in the [Platrix repository](https://github.com/AliAkrami1375
128
  - **Intended for** lawful applications such as parking management, access
129
  control, gate automation and traffic analytics.
130
  - **Optimised for** standard private Iranian plates. Very low-resolution,
131
- heavily occluded, or non-standard plates may reduce accuracy.
132
  - You are responsible for complying with the privacy and surveillance laws that
133
  apply to your deployment.
134
 
 
32
  > πŸ€— **This model repo:** https://huggingface.co/Dibachain/Platrix
33
 
34
  The pipeline is **two-stage**: a YOLO **detector** locates the plate in the
35
+ frame, an image-quality **enhancement** step cleans the crop, then a
36
+ segmentation-free **CRNN + CTC reader** reads the whole plate at once and returns
37
+ the standard Iranian layout `DD L DDD DD` (two digits Β· letter Β· three digits Β·
38
+ two-digit region), e.g. `۸۱ و ۢ۳۸ ۱۳`.
39
 
40
  All models run with **ONNX Runtime** β€” no PyTorch or TensorFlow needed at
41
  inference time.
42
 
43
  ---
44
 
45
+ ## πŸ“Š Performance
46
+
47
+ Measured on **220 real Iranian surveillance photos** (grayscale gate/road
48
+ cameras β€” the hard, real-world domain, not staged shots).
49
+
50
+ <p align="center">
51
+ <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/benchmark.png" width="88%" />
52
+ </p>
53
+
54
+ A lightweight **secondary detector** runs only when the primary finds nothing.
55
+ It recovers plates the primary is blind to β€” trucks, night shots, small/far and
56
+ dim plates β€” lifting the end-to-end read rate from **95.9% β†’ 97.7%** with **no
57
+ regression** on the easy majority (the fallback fires on only ~2% of frames).
58
+
59
+ <p align="center">
60
+ <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/recovery.png" width="70%" />
61
+ </p>
62
+
63
+ ### Training curves
64
+
65
+ <p align="center">
66
+ <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/reader_training.png" width="49%" />
67
+ <img src="https://huggingface.co/Dibachain/Platrix/resolve/main/assets/detector_training.png" width="49%" />
68
+ </p>
69
+
70
+ The reader reaches ~93% whole-plate accuracy in training and **~98% on real
71
+ photos**; the detector reaches **mAP@0.5 β‰ˆ 0.99** on held-out real frames.
72
+
73
+ ---
74
+
75
+ ## πŸ“ Files
76
 
77
  | File | Role | Input | Output |
78
  |------|------|-------|--------|
79
+ | `plate_yolo.onnx` | **Plate detector** (YOLOv8, primary) | `1Γ—3Γ—HΓ—W` RGB, letterboxed, `/255` | `1Γ—5Γ—N` β†’ `cx,cy,w,h,conf` |
80
+ | `plate_yolo_fallback.onnx` | **Secondary detector** β€” runs only when the primary finds nothing; recovers hard surveillance frames | `1Γ—3Γ—640Γ—640` | `1Γ—5Γ—N` |
81
  | `ocr_crnn.onnx` | **Whole-plate reader** (CRNN+CTC) β€” *recommended* | `1Γ—1Γ—32Γ—128` grayscale, `/255` | `1Γ—TΓ—(C+1)` logits (CTC, blank = last) |
82
  | `ocr_crnn.labels.json` | Class list for the CRNN (index β†’ character) | β€” | 32 classes |
83
+ | `ocr_cnn.onnx` | Per-character classifier (lightweight fallback reader) | `1Γ—1Γ—32Γ—32` grayscale | class logits |
84
  | `ocr_cnn.labels.json` | Class list for the per-char classifier | β€” | β€” |
85
 
86
  **Character set (32 classes):** digits `0–9` and the Persian plate letters
 
88
 
89
  ---
90
 
91
+ ## πŸš€ Getting started
92
 
93
+ ### Option A β€” Run the full Platrix system (recommended)
 
 
 
 
94
 
95
+ The complete app (web dashboard, multi-camera streaming, watchlists, API) lives
96
+ in the GitHub repo. It downloads these models for you.
 
 
97
 
98
+ ```bash
99
+ git clone https://github.com/AliAkrami1375/Platrix.git
100
+ cd Platrix
101
+
102
+ # fetch the models from this repo into ./models
103
+ pip install -U "huggingface_hub[cli]"
104
+ huggingface-cli download Dibachain/Platrix \
105
+ plate_yolo.onnx plate_yolo_fallback.onnx \
106
+ ocr_crnn.onnx ocr_crnn.labels.json ocr_cnn.onnx ocr_cnn.labels.json \
107
+ --local-dir models/
108
+
109
+ # then either:
110
+ docker compose up --build # Docker
111
+ # β€” or β€”
112
+ python -m venv .venv && source .venv/bin/activate
113
+ pip install -r requirements.txt && pip install -e .
114
+ platrix serve # dashboard at http://localhost:8080
115
+ ```
116
 
117
+ ### Option B β€” Use the models directly (ONNX Runtime)
118
 
119
  ```bash
120
  pip install onnxruntime opencv-python-headless numpy
121
  huggingface-cli download Dibachain/Platrix \
122
+ plate_yolo.onnx plate_yolo_fallback.onnx ocr_crnn.onnx ocr_crnn.labels.json \
123
+ --local-dir models/
124
  ```
125
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  ```python
127
  import json, cv2, numpy as np, onnxruntime as ort
128
 
129
  # --- Reader (CRNN + CTC) ---
130
  labels = json.load(open("models/ocr_crnn.labels.json", encoding="utf-8"))
131
+ blank = len(labels) # CTC blank is the last index
132
  crnn = ort.InferenceSession("models/ocr_crnn.onnx", providers=["CPUExecutionProvider"])
133
 
134
  def read_plate(plate_bgr):
135
  g = cv2.cvtColor(plate_bgr, cv2.COLOR_BGR2GRAY)
136
+ g = cv2.resize(g, (128, 32)).astype(np.float32) / 255.0 # 1x1x32x128
137
+ logits = crnn.run(None, {"input": g[None, None]})[0][0] # T x (C+1)
138
  ids, out, prev = logits.argmax(1), [], -1
139
+ for i in ids: # greedy CTC decode
140
  if i != blank and i != prev:
141
  out.append(labels[i])
142
  prev = i
143
  return "".join(out)
144
  ```
145
 
146
+ **Two-stage flow:** run `plate_yolo.onnx` first (standard YOLOv8 letterbox
147
+ pre-process + confidence/NMS post-process) to crop the plate, then pass the crop
148
  to `read_plate`. If the primary detector returns nothing, run
149
+ `plate_yolo_fallback.onnx` at **640Γ—640** as a second pass β€” it recovers the hard
150
+ surveillance frames the primary is blind to. A few **real test photos** ship
151
+ under [`img-test/`](./tree/main/img-test) so you can try it immediately.
152
+
153
+ ---
154
+
155
+ ## 🧠 How it works
156
+
157
+ 1. **Detect** β€” YOLOv8 locates the plate; weak/non-plate boxes are ignored.
158
+ 2. **Enhance** β€” the crop is upscaled, denoised, contrast-corrected and sharpened.
159
+ The *same* enhancement is applied during training, so there is no train/serve
160
+ mismatch β€” the enhancement genuinely helps instead of shifting the input.
161
+ 3. **Read** β€” the segmentation-free CRNN reads the whole plate in one pass with a
162
+ CTC head. It is trained on **real Iranian plate-character shapes**, so
163
+ look-alike glyphs (e.g. the digit `Ϋ΄` vs `ΫΆ`) are read correctly.
164
+
165
+ Splitting a plate into individual characters is fragile on real photos (shadows,
166
+ motion blur, tilt, dirt); reading the entire plate at once is far more robust.
167
 
168
  ---
169
 
 
172
  - **Intended for** lawful applications such as parking management, access
173
  control, gate automation and traffic analytics.
174
  - **Optimised for** standard private Iranian plates. Very low-resolution,
175
+ heavily occluded or non-standard plates may reduce accuracy.
176
  - You are responsible for complying with the privacy and surveillance laws that
177
  apply to your deployment.
178
 
assets/benchmark.png ADDED

Git LFS Details

  • SHA256: 0bf30f6b38efb135433cdecc2cc090ed927bfa06878e5181f52750db6ffc7bec
  • Pointer size: 130 Bytes
  • Size of remote file: 78.8 kB
assets/detector_loss.png ADDED

Git LFS Details

  • SHA256: e17c867d9148d040d826634a96977acca97fd653394cc2c275422417248d13b2
  • Pointer size: 130 Bytes
  • Size of remote file: 87.2 kB
assets/detector_training.png ADDED

Git LFS Details

  • SHA256: dcdeda772796d49c9faa2927042f3e65b87bf47d8a03bdc67f896364628f60be
  • Pointer size: 130 Bytes
  • Size of remote file: 88.6 kB
assets/reader_training.png ADDED

Git LFS Details

  • SHA256: 774c7c727ca5e7d54b78ba4985a2beeb439fb4c5b89b0ce3c5eeb8c94fb146d6
  • Pointer size: 131 Bytes
  • Size of remote file: 102 kB
assets/recovery.png ADDED

Git LFS Details

  • SHA256: 6b9b17c19fb1be6541a9e44e03aa225542b1e6c3735cf306d2e56cbda6a849fa
  • Pointer size: 130 Bytes
  • Size of remote file: 52.7 kB