Platrix / README.md
DibaAi's picture
Add professional performance charts and a complete setup guide to the model card
7684c9c
|
Raw
History Blame Contribute Delete
7.01 kB
metadata
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

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×Ncx,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