Platrix

Platrix β€” Iranian License-Plate Recognition Models

Production ONNX models for detecting and reading Iranian vehicle license plates, powering the 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).

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).

Training curves

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.

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)

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/
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/ 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

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support