Spaces:
Running
Running
| """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() | |