`ultralytics 8.2.5` New 🌟 Parking Management Solution (#10385)
Co-authored-by: UltralyticsAssistant <web@ultralytics.com> Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>pull/10404/head v8.2.5
parent
156b6be8d3
commit
bc9fd45cdf
10 changed files with 453 additions and 83 deletions
@ -0,0 +1,116 @@ |
||||
--- |
||||
comments: true |
||||
description: Parking Management System Using Ultralytics YOLOv8 |
||||
keywords: Ultralytics, YOLOv8, Object Detection, Object Counting, Parking lots, Object Tracking, Notebook, IPython Kernel, CLI, Python SDK |
||||
--- |
||||
|
||||
# Parking Management using Ultralytics YOLOv8 🚀 |
||||
|
||||
## What is Parking Management System? |
||||
|
||||
Parking management with [Ultralytics YOLOv8](https://github.com/ultralytics/ultralytics/) ensures efficient and safe parking by organizing spaces and monitoring availability. YOLOv8 can improve parking lot management through real-time vehicle detection, and insights into parking occupancy. |
||||
|
||||
## Advantages of Parking Management System? |
||||
|
||||
- **Efficiency**: Parking lot management optimizes the use of parking spaces and reduces congestion. |
||||
- **Safety and Security**: Parking management using YOLOv8 improves the safety of both people and vehicles through surveillance and security measures. |
||||
- **Reduced Emissions**: Parking management using YOLOv8 manages traffic flow to minimize idle time and emissions in parking lots. |
||||
|
||||
## Real World Applications |
||||
|
||||
| Parking Management System | Parking Management System | |
||||
|:-------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------:| |
||||
| ![Parking lots Analytics Using Ultralytics YOLOv8](https://github.com/RizwanMunawar/RizwanMunawar/assets/62513924/e3d4bc3e-cf4a-4da9-b42e-0da55cc74ad6) | ![Parking management top view using Ultralytics YOLOv8](https://github.com/RizwanMunawar/RizwanMunawar/assets/62513924/fe186719-1aca-43c9-b388-1ded91280eb5) | |
||||
| Parking management Aeriel View using Ultralytics YOLOv8 | Parking management Top View using Ultralytics YOLOv8 | |
||||
|
||||
|
||||
## Parking Management System Code Workflow |
||||
|
||||
### Selection of Points |
||||
|
||||
!!! Tip "Point Selection is now Easy" |
||||
|
||||
Choosing parking points is a critical and complex task in parking management systems. Ultralytics streamlines this process by providing a tool that lets you define parking lot areas, which can be utilized later for additional processing. |
||||
|
||||
- Capture a frame from the video or camera stream where you want to manage the parking lot. |
||||
- Use the provided code to launch a graphical interface, where you can select an image and start outlining parking regions by mouse click to create polygons. |
||||
|
||||
!!! Warning "Image Size" |
||||
|
||||
Max Image Size of 1920 * 1080 supported |
||||
|
||||
```python |
||||
from ultralytics.solutions.parking_management import ParkingPtsSelection, tk |
||||
root = tk.Tk() |
||||
ParkingPtsSelection(root) |
||||
root.mainloop() |
||||
``` |
||||
|
||||
- After defining the parking areas with polygons, click `save` to store a JSON file with the data in your working directory. |
||||
|
||||
![Ultralytics YOLOv8 Points Selection Demo](https://github.com/RizwanMunawar/RizwanMunawar/assets/62513924/72737b8a-0f0f-4efb-98ad-b917a0039535) |
||||
|
||||
|
||||
### Python Code for Parking Management |
||||
|
||||
!!! Example "Parking management using YOLOv8 Example" |
||||
|
||||
=== "Parking Management" |
||||
|
||||
```python |
||||
import cv2 |
||||
from ultralytics.solutions.parking_management import ParkingManagement |
||||
|
||||
# Path to json file, that created with above point selection app |
||||
polygon_json_path = "bounding_boxes.json" |
||||
|
||||
# Video Capture |
||||
cap = cv2.VideoCapture("Path/to/video/file.mp4") |
||||
assert cap.isOpened(), "Error reading video file" |
||||
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) |
||||
video_writer = cv2.VideoWriter("parking management.avi", cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) |
||||
|
||||
# Initialize parking management object |
||||
management = ParkingManagement(model_path="yolov8n.pt") |
||||
|
||||
while cap.isOpened(): |
||||
ret, im0 = cap.read() |
||||
if not ret: |
||||
break |
||||
json_data = management.parking_regions_extraction(polygon_json_path) |
||||
results = management.model.track(im0, persist=True, show=False) |
||||
|
||||
if results[0].boxes.id is not None: |
||||
boxes = results[0].boxes.xyxy.cpu().tolist() |
||||
clss = results[0].boxes.cls.cpu().tolist() |
||||
management.process_data(json_data, im0, boxes, clss) |
||||
|
||||
management.display_frames(im0) |
||||
video_writer.write(im0) |
||||
|
||||
cap.release() |
||||
video_writer.release() |
||||
cv2.destroyAllWindows() |
||||
``` |
||||
|
||||
### Optional Arguments `ParkingManagement()` |
||||
|
||||
| Name | Type | Default | Description | |
||||
|--------------------------|-------------|-------------------|-----------------------------------------------------| |
||||
| `occupied_region_color` | `RGB Color` | `(0, 255, 0)` | Parking space occupied region color | |
||||
| `available_region_color` | `RGB Color` | `(0, 0, 255)` | Parking space available region color | |
||||
| `margin` | `int` | `10` | Gap between text display for multiple classes count | |
||||
| `txt_color` | `RGB Color` | `(255, 255, 255)` | Foreground color for object counts text | |
||||
| `bg_color` | `RGB Color` | `(255, 255, 255)` | Rectangle behind text background color | |
||||
|
||||
### Arguments `model.track` |
||||
|
||||
| Name | Type | Default | Description | |
||||
|-----------|---------|----------------|-------------------------------------------------------------| |
||||
| `source` | `im0` | `None` | source directory for images or videos | |
||||
| `persist` | `bool` | `False` | persisting tracks between frames | |
||||
| `tracker` | `str` | `botsort.yaml` | Tracking method 'bytetrack' or 'botsort' | |
||||
| `conf` | `float` | `0.3` | Confidence Threshold | |
||||
| `iou` | `float` | `0.5` | IOU Threshold | |
||||
| `classes` | `list` | `None` | filter results by class, i.e. classes=0, or classes=[0,2,3] | |
||||
| `verbose` | `bool` | `True` | Display the object tracking results | |
@ -0,0 +1,235 @@ |
||||
import json |
||||
from tkinter import filedialog, messagebox |
||||
|
||||
import cv2 |
||||
import numpy as np |
||||
from PIL import Image, ImageTk |
||||
|
||||
from ultralytics.utils.checks import check_imshow, check_requirements |
||||
from ultralytics.utils.plotting import Annotator |
||||
|
||||
check_requirements("tkinter") |
||||
import tkinter as tk |
||||
|
||||
|
||||
class ParkingPtsSelection: |
||||
def __init__(self, master): |
||||
# Initialize window and widgets. |
||||
self.master = master |
||||
master.title("Ultralytics Parking Zones Points Selector") |
||||
self.initialize_ui() |
||||
|
||||
# Initialize properties |
||||
self.image_path = None |
||||
self.image = None |
||||
self.canvas_image = None |
||||
self.canvas = None |
||||
self.bounding_boxes = [] |
||||
self.current_box = [] |
||||
self.img_width = 0 |
||||
self.img_height = 0 |
||||
|
||||
# Constants |
||||
self.canvas_max_width = 1280 |
||||
self.canvas_max_height = 720 |
||||
|
||||
def initialize_ui(self): |
||||
"""Setup UI components.""" |
||||
# Setup buttons |
||||
button_frame = tk.Frame(self.master) |
||||
button_frame.pack(side=tk.TOP) |
||||
|
||||
tk.Button(button_frame, text="Upload Image", command=self.upload_image).grid(row=0, column=0) |
||||
tk.Button(button_frame, text="Remove Last BBox", command=self.remove_last_bounding_box).grid(row=0, column=1) |
||||
tk.Button(button_frame, text="Save", command=self.save_to_json).grid(row=0, column=2) |
||||
|
||||
# Setup canvas for image display |
||||
self.canvas = tk.Canvas(self.master, bg="white") |
||||
self.canvas.pack(side=tk.BOTTOM) |
||||
self.canvas.bind("<Button-1>", self.on_canvas_click) |
||||
|
||||
def upload_image(self): |
||||
"""Upload an image and resize it to fit canvas.""" |
||||
self.image_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]) |
||||
if not self.image_path: |
||||
return |
||||
|
||||
self.image = Image.open(self.image_path) |
||||
self.img_width, self.img_height = self.image.size |
||||
|
||||
# Calculate the aspect ratio and resize image |
||||
aspect_ratio = self.img_width / self.img_height |
||||
if aspect_ratio > 1: |
||||
# Landscape orientation |
||||
canvas_width = min(self.canvas_max_width, self.img_width) |
||||
canvas_height = int(canvas_width / aspect_ratio) |
||||
else: |
||||
# Portrait orientation |
||||
canvas_height = min(self.canvas_max_height, self.img_height) |
||||
canvas_width = int(canvas_height * aspect_ratio) |
||||
|
||||
self.canvas.config(width=canvas_width, height=canvas_height) |
||||
resized_image = self.image.resize((canvas_width, canvas_height), Image.LANCZOS) |
||||
self.canvas_image = ImageTk.PhotoImage(resized_image) |
||||
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.canvas_image) |
||||
|
||||
# Reset bounding boxes and current box |
||||
self.bounding_boxes = [] |
||||
self.current_box = [] |
||||
|
||||
def on_canvas_click(self, event): |
||||
"""Handle mouse clicks on canvas to create points for bounding boxes.""" |
||||
self.current_box.append((event.x, event.y)) |
||||
|
||||
if len(self.current_box) == 4: |
||||
self.bounding_boxes.append(self.current_box) |
||||
self.draw_bounding_box(self.current_box) |
||||
self.current_box = [] |
||||
|
||||
def draw_bounding_box(self, box): |
||||
"""Draw bounding box on canvas.""" |
||||
for i in range(4): |
||||
x1, y1 = box[i] |
||||
x2, y2 = box[(i + 1) % 4] |
||||
self.canvas.create_line(x1, y1, x2, y2, fill="blue", width=2) |
||||
|
||||
def remove_last_bounding_box(self): |
||||
"""Remove the last drawn bounding box from canvas.""" |
||||
if self.bounding_boxes: |
||||
self.bounding_boxes.pop() # Remove the last bounding box |
||||
self.canvas.delete("all") # Clear the canvas |
||||
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.canvas_image) # Redraw the image |
||||
|
||||
# Redraw all bounding boxes |
||||
for box in self.bounding_boxes: |
||||
self.draw_bounding_box(box) |
||||
|
||||
messagebox.showinfo("Success", "Last bounding box removed.") |
||||
else: |
||||
messagebox.showwarning("Warning", "No bounding boxes to remove.") |
||||
|
||||
def save_to_json(self): |
||||
canvas_width, canvas_height = self.canvas.winfo_width(), self.canvas.winfo_height() |
||||
width_scaling_factor = self.img_width / canvas_width |
||||
height_scaling_factor = self.img_height / canvas_height |
||||
bounding_boxes_data = [] |
||||
for box in self.bounding_boxes: |
||||
print("Bounding Box ", bounding_boxes_data) |
||||
rescaled_box = [] |
||||
for x, y in box: |
||||
rescaled_x = int(x * width_scaling_factor) |
||||
rescaled_y = int(y * height_scaling_factor) |
||||
rescaled_box.append((rescaled_x, rescaled_y)) |
||||
bounding_boxes_data.append({"points": rescaled_box}) |
||||
with open("bounding_boxes.json", "w") as json_file: |
||||
json.dump(bounding_boxes_data, json_file, indent=4) |
||||
|
||||
messagebox.showinfo("Success", "Bounding boxes saved to bounding_boxes.json") |
||||
|
||||
|
||||
class ParkingManagement: |
||||
def __init__( |
||||
self, |
||||
model_path, |
||||
txt_color=(0, 0, 0), |
||||
bg_color=(255, 255, 255), |
||||
occupied_region_color=(0, 255, 0), |
||||
available_region_color=(0, 0, 255), |
||||
margin=10, |
||||
): |
||||
# Model path and initialization |
||||
self.model_path = model_path |
||||
self.model = self.load_model() |
||||
|
||||
# Labels dictionary |
||||
self.labels_dict = {"Occupancy": 0, "Available": 0} |
||||
|
||||
# Visualization details |
||||
self.margin = margin |
||||
self.bg_color = bg_color |
||||
self.txt_color = txt_color |
||||
self.occupied_region_color = occupied_region_color |
||||
self.available_region_color = available_region_color |
||||
|
||||
self.window_name = "Ultralytics YOLOv8 Parking Management System" |
||||
# Check if environment support imshow |
||||
self.env_check = check_imshow(warn=True) |
||||
|
||||
def load_model(self): |
||||
"""Load the Ultralytics YOLOv8 model for inference and analytics.""" |
||||
from ultralytics import YOLO |
||||
|
||||
self.model = YOLO(self.model_path) |
||||
return self.model |
||||
|
||||
def parking_regions_extraction(self, json_file): |
||||
""" |
||||
Extract parking regions from json file. |
||||
|
||||
Args: |
||||
json_file (str): file that have all parking slot points |
||||
""" |
||||
|
||||
with open(json_file, "r") as json_file: |
||||
json_data = json.load(json_file) |
||||
return json_data |
||||
|
||||
def process_data(self, json_data, im0, boxes, clss): |
||||
""" |
||||
Process the model data for parking lot management. |
||||
|
||||
Args: |
||||
json_data (str): json data for parking lot management |
||||
im0 (ndarray): inference image |
||||
boxes (list): bounding boxes data |
||||
clss (list): bounding boxes classes list |
||||
Returns: |
||||
filled_slots (int): total slots that are filled in parking lot |
||||
empty_slots (int): total slots that are available in parking lot |
||||
""" |
||||
annotator = Annotator(im0) |
||||
total_slots, filled_slots = len(json_data), 0 |
||||
empty_slots = total_slots |
||||
|
||||
for region in json_data: |
||||
points = region["points"] |
||||
points_array = np.array(points, dtype=np.int32).reshape((-1, 1, 2)) |
||||
region_occupied = False |
||||
|
||||
for box, cls in zip(boxes, clss): |
||||
x_center = int((box[0] + box[2]) / 2) |
||||
y_center = int((box[1] + box[3]) / 2) |
||||
text = f"{self.model.names[int(cls)]}" |
||||
|
||||
annotator.display_objects_labels( |
||||
im0, text, self.txt_color, self.bg_color, x_center, y_center, self.margin |
||||
) |
||||
dist = cv2.pointPolygonTest(points_array, (x_center, y_center), False) |
||||
if dist >= 0: |
||||
region_occupied = True |
||||
break |
||||
|
||||
color = self.occupied_region_color if region_occupied else self.available_region_color |
||||
cv2.polylines(im0, [points_array], isClosed=True, color=color, thickness=2) |
||||
if region_occupied: |
||||
filled_slots += 1 |
||||
empty_slots -= 1 |
||||
|
||||
self.labels_dict["Occupancy"] = filled_slots |
||||
self.labels_dict["Available"] = empty_slots |
||||
|
||||
annotator.display_analytics(im0, self.labels_dict, self.txt_color, self.bg_color, self.margin) |
||||
|
||||
def display_frames(self, im0): |
||||
""" |
||||
Display frame. |
||||
|
||||
Args: |
||||
im0 (ndarray): inference image |
||||
""" |
||||
if self.env_check: |
||||
cv2.namedWindow(self.window_name) |
||||
cv2.imshow(self.window_name, im0) |
||||
# Break Window |
||||
if cv2.waitKey(1) & 0xFF == ord("q"): |
||||
return |
Loading…
Reference in new issue