jo-lnpr / app.py
digitalbigbrain's picture
Update Default Confidence Threshold
38f5ab8
Raw
History Blame Contribute Delete
8.55 kB
"""Jordanian LPR - Vehicle Plate Detection and Recognition. (نظام التعرف على لوحات المركبات الأردنية).
An automated system for detecting and recognizing Jordanian vehicle license plates using
computer vision and deep learning. The model handles multiple plate types under varied
real-world conditions including daylight, night, and motion blur.
Jordanian vehicle license plates are divided into two primary sections, each serving a
distinct identification purpose. The left side of the plate contains the vehicle
classification and registration category information, which identifies the type of vehicle
and its registration class within the Kingdom. Depending on the plate type, this section
may include indicators for private vehicles, public transportation, government vehicles,
diplomatic vehicles, or other designated categories. The right side of the plate contains
the unique vehicle registration number, which serves as the primary identifier for the
individual vehicle. This number is unique within its respective classification and is used
by authorities and automated recognition systems to distinguish one vehicle from another.
Together, these two sections provide both the classification context and the unique
identification necessary for vehicle registration, law enforcement, and automated license
plate recognition (ALPR) systems.
"""
import os
import numpy as np
import gradio as gr
from ultralytics import YOLO
# Weights ship in the repo; override with a HF Hub path via env if you prefer.
VEHICLE_MODEL_PATH = os.environ.get("MODEL_PATH", "yolo26x.pt")
PLATE_DETECTION_MODEL_PATH = os.environ.get("MODEL_PATH", "license-plate-finetune-v1l.pt")
PLATE_RECOGNITION_MODEL_PATH = os.environ.get("MODEL_PATH", "jordanian-plate-recognition-finetune-v5s.pt")
DEFAULT_CONF = 0.70 # matches the production deterrent's localYoloConfidenceThreshold
vehicle_model = YOLO(VEHICLE_MODEL_PATH)
plate_detection_model = YOLO(PLATE_DETECTION_MODEL_PATH)
plate_recognition_model = YOLO(PLATE_RECOGNITION_MODEL_PATH)
def detect(image, conf):
boxes, rows = [], []
"""Run detection and return (annotated image, table rows, deterrent verdict)."""
if image is None:
return None, [], "Upload a frame to begin."
# 1- Detect Vehicles
detected_vehicles = vehicle_model.predict(image, conf=0.8, verbose=False, iou=0.45, classes=[2, 3, 5, 7])[0]
results = detected_vehicles
for vehicle_index, box in enumerate(detected_vehicles.boxes.xyxy.cpu().tolist()):
vehicle_x1, vehicle_y1, vehicle_x2, vehicle_y2 = map(int, box)
# 2- Crop vehicle ROI
vehicle_crop = image.crop((vehicle_x1, vehicle_y1, vehicle_x2, vehicle_y2))
# 3- Detect plate pos
detected_plate = plate_detection_model.predict(vehicle_crop, verbose=False, conf=0.5, iou=0.05) # Adjust confidence and IoU thresholds as needed
plate_id = 0
# 4- Detect Plates
for plate_index, box in enumerate(detected_plate[0].boxes.xyxy.cpu().tolist()):
plate_id += 1
plate_x1, plate_y1, plate_x2, plate_y2 = map(int, box)
# 5- Detect Vehicles
plate_crop = vehicle_crop.crop((plate_x1, plate_y1, plate_x2, plate_y2))
plate_width = plate_x2 - plate_x1
plate_height = plate_y2 - plate_y1
# 6- Recognize Plates
detected_plate_artifacts = plate_recognition_model.predict(plate_crop, verbose=False, conf=conf, iou=0.05)
# 7- Initial Validations
if len(detected_plate_artifacts[0].boxes) == 0:
continue # No Detections
detections = detected_plate_artifacts[0]
ordered_idx = np.argsort(detections.boxes.xyxy.cpu().numpy()[:,0])
# 8- Construct Plate Artifacts
cls_str = ""
if (plate_width/plate_height >2.2): # tall plate
for idx in ordered_idx:
box = detections.boxes.xyxy[idx]
cls_id = detections.boxes.cls[idx]
cls_str += plate_recognition_model.names[int(cls_id)]
cls_str = cls_str.replace("jo_pvt", "JO-PVT ")
elif plate_width/plate_height > 1.5: # wide plate
from_y_highest = detections.boxes.xyxy[:,1].max()
from_y_lowest = detections.boxes.xyxy[:,1].min()
middle_y = (from_y_highest + from_y_lowest) / 2
for idx in ordered_idx:
box = detections.boxes.xyxy[idx]
from_y = box[1]
if from_y < middle_y:
cls_id = detections.boxes.cls[idx]
cls_str += plate_recognition_model.names[int(cls_id)]
cls_str = cls_str.replace("jo_pvt", "JO-PVT ")
for idx in ordered_idx:
box = detections.boxes.xyxy[idx]
from_y = box[1]
if from_y > middle_y:
cls_id = detections.boxes.cls[int(idx)]
cls_str += plate_recognition_model.names[int(cls_id)]
cls_str = cls_str.replace("jo_pvt", "JO-PVT ")
else:
cls_str = ""
# 9- Annotate Dedection
if cls_str != "" and len(cls_str) >= 5:
plate_number = cls_str
top_left = (vehicle_x1+plate_x1, vehicle_y1+plate_y1)
bottom_right = (vehicle_x1+plate_x1+(plate_x2-plate_x1), vehicle_y1+plate_y1+(plate_y2-plate_y1))
rectangle_color = (0, 0, 255) # Blue in BGR
plate_detections = cls_str
avgconf = f"{detected_plate_artifacts[0].boxes.conf.mean():0.2}"
rows.append([plate_detections, avgconf])
boxes.append(((int(vehicle_x1+plate_x1), int(vehicle_y1+plate_y1), int(vehicle_x1+plate_x1+(plate_x2-plate_x1)), int(vehicle_y1+plate_y1+(plate_y2-plate_y1))), f"[{plate_detections}]"))
verdict = f"✅ ({len(detected_vehicles.boxes)}) Vechiles Detected, and ({len(rows)}) Plates Recognized"
return (image, boxes), rows, verdict
EXAMPLES = [
["sample_1.jpg", DEFAULT_CONF],
["sample_2.jpg", DEFAULT_CONF],
["sample_3.jpg", DEFAULT_CONF],
["sample_4.jpg", DEFAULT_CONF]
]
# Drop the examples that don't exist yet so the Space still launches.
EXAMPLES = [e for e in EXAMPLES if os.path.exists(e[0])]
demo = gr.Interface(
fn=detect,
inputs=[
gr.Image(type="pil", label="Street Captures"),
gr.Slider(0.05, 0.90, value=DEFAULT_CONF, step=0.01, label="Confidence threshold"),
],
outputs=[
gr.AnnotatedImage(label="Detections"),
gr.Dataframe(headers=["Detected Plates", "Avg Confidence"], label="What the model identified"),
gr.Textbox(label="Deterrent verdict"),
],
examples=EXAMPLES or None,
title="🚘 Jordanian LPR - Vehicle Plate Detection and Recognition",
description=(
"Jordanian vehicle license plates are divided into two primary sections, each serving "
"a distinct identification purpose. The left side of the plate contains the vehicle "
"classification and registration category information, which identifies the type of "
"vehicle and its registration class within the Kingdom. Depending on the plate type, "
"this section may include indicators for private vehicles, public transportation, "
"government vehicles, diplomatic vehicles, or other designated categories. The right "
"side of the plate contains the unique vehicle registration number, which serves as "
"the primary identifier for the individual vehicle. This number is unique within its "
"respective classification and is used by authorities and automated recognition systems "
"to distinguish one vehicle from another. Together, these two sections provide both the "
"classification context and the unique identification necessary for vehicle registration, "
"law enforcement, and automated license plate recognition (ALPR) systems."
),
article=(
"Built for the Gradio(Hugging Face) **Build Small** (DigitalBigBrain Mobility AI). "
"For more information, visit [https://www.DigitalBigBrain.com](https://www.digitalbigbrain.com)."
),
)
if __name__ == "__main__":
demo.launch()