Spaces:
Sleeping
Sleeping
import gradio as gr | |
from ultralytics import YOLO | |
import cv2 | |
import numpy as np | |
from paddleocr import PaddleOCR | |
import re | |
# Load models once | |
model_path = 'yolo11x.pt' | |
model2_path = r"yolo11_anpr_ghd.pt" | |
model = YOLO(model_path) | |
model2 = YOLO(model2_path) | |
# Initialize PaddleOCR globally to avoid re-initializing on each call | |
# For Hugging Face Spaces, ensure necessary PaddleOCR models are downloaded (e.g., within Dockerfile or setup script) | |
# Using 'en' for English license plates and disabling angle classification for simplicity | |
ocr = PaddleOCR(use_doc_orientation_classify=False, | |
use_doc_unwarping=False, | |
use_textline_orientation=False, | |
lang='en') # Added lang='en' for better English OCR | |
# Example images paths - these might need adjustment for Hugging Face Spaces | |
# If these are locally stored, they need to be part of the uploaded files in your Space. | |
example_images = [ | |
r"examples/car1.png", | |
r"examples/car2.png", | |
r"examples/car3.png", | |
r"examples/car4.png", | |
r"examples/car5.png", | |
] | |
def detect_and_annotate(image): | |
# Convert PIL image to OpenCV format | |
img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) | |
# Initialize message | |
output_message = "" | |
# Start with a copy of the original image for annotations | |
annotated_frame = img.copy() | |
# Vehicle detection | |
results = model(img, classes=[2, 5, 7, 3]) # vehicle classes | |
# IMPORTANT FIX: The .plot() method on results[0] already returns a new image with annotations. | |
# We should use this directly as our base annotated_frame. | |
if results and results[0].boxes and len(results[0].boxes) > 0: # Check if vehicle results exist and have boxes | |
# Plot vehicle detections. This returns an annotated NumPy array (BGR format). | |
annotated_frame = results[0].plot(line_width=3) | |
# License plate detection only if vehicle is detected | |
license_plate_results = model2(img) | |
if license_plate_results and len(license_plate_results[0].boxes) > 0: | |
# Iterate through detected license plates if there are multiple | |
for license_box_data in license_plate_results[0].boxes: | |
license_box = license_box_data.xyxy[0].cpu().numpy() | |
x1, y1, x2, y2 = map(int, license_box) | |
# Draw rectangle for license plate on the annotated frame | |
cv2.rectangle(annotated_frame, (x1, y1), (x2, y2), (255, 0, 0), 3) | |
# OCR on license plate region | |
license_plate_region = img[y1:y2, x1:x2] | |
try: | |
# IMPORTANT FIX: Pass the NumPy array directly to PaddleOCR's ocr method | |
# This avoids the file read/write error. | |
ocr_result = ocr.ocr(img=license_plate_region, cls=True) # Changed from .predict to .ocr | |
license_plate_text = "" | |
if ocr_result and ocr_result[0] and ocr_result[0][0]: # Check for valid OCR result structure | |
# The OCR result structure for ocr.ocr is different: it's a list of blocks, each containing [bbox, (text, score)] | |
# We are interested in the text from the first block's first item | |
text = ocr_result[0][0][1][0] | |
text = re.sub(r'[^a-zA-Z0-9]', '', text) | |
license_plate_text = text | |
output_message += f"Detected License Plate Text: {license_plate_text}\n" | |
else: | |
output_message += "No text found on license plate.\n" | |
# Put text on the annotated frame (still show on image for visual feedback) | |
display_text_on_image = license_plate_text if license_plate_text else "No text found" | |
cv2.putText(annotated_frame, display_text_on_image, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) | |
except Exception as e: | |
output_message += f"OCR Error: {e}\n" | |
print(f"OCR error on region: {e}") | |
else: | |
output_message = "No number plate detected in vehicle.\n" | |
else: | |
output_message = "No vehicles detected." | |
# If no vehicles are detected, simply return the original image (or a copy) | |
# as there's no need to annotate it with a "No vehicles detected" message on the image itself. | |
annotated_frame = img.copy() # Ensure we always return an image | |
# Convert back to RGB for display in Gradio | |
annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB) | |
return annotated_frame, output_message | |
iface = gr.Interface( | |
fn=detect_and_annotate, | |
inputs=gr.Image(type="pil"), | |
outputs=[gr.Image(type="numpy", label="Annotated Image"), | |
gr.Textbox(label="Detection Messages")], # Added Textbox for messages | |
title="Vehicle & License Plate Detection with OCR", | |
description="Upload an image. The app will detect vehicles and license plates, perform OCR, and display the result with annotations and messages.", | |
examples=example_images, | |
cache_examples=False | |
) | |
if __name__ == "__main__": | |
iface.launch(share=True) |