Skip to content

Commit 488d258

Browse files
authored
Create Object-Detection-System-Include-Webcam.py
1 parent 785b439 commit 488d258

1 file changed

Lines changed: 205 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import tkinter as tk
2+
from tkinter import filedialog, messagebox
3+
from PIL import Image, ImageTk
4+
import cv2
5+
from ultralytics import YOLO
6+
import ttkbootstrap as tb
7+
from ttkbootstrap.constants import *
8+
from collections import Counter
9+
10+
# Initialize YOLO model
11+
model = YOLO("yolov8n.pt") # YOLOv8 Nano model
12+
13+
class ObjectDetectionApp:
14+
def __init__(self, root):
15+
self.root = root
16+
self.root.title("Advanced Object Detection System")
17+
self.root.geometry("1000x750")
18+
self.root.resizable(False, False)
19+
20+
# ttkbootstrap style
21+
self.style = tb.Style("superhero")
22+
23+
# ---------------- Top Frame ----------------
24+
self.top_frame = tb.Frame(root)
25+
self.top_frame.pack(side=TOP, pady=10)
26+
27+
# Buttons
28+
self.load_btn = tb.Button(self.top_frame, text="Load Image", bootstyle=PRIMARY, command=self.load_image)
29+
self.load_btn.grid(row=0, column=0, padx=5)
30+
31+
self.detect_btn = tb.Button(self.top_frame, text="Detect Image", bootstyle=SUCCESS, command=self.detect_objects)
32+
self.detect_btn.grid(row=0, column=1, padx=5)
33+
34+
self.start_cam_btn = tb.Button(self.top_frame, text="Start Webcam", bootstyle=INFO, command=self.start_webcam)
35+
self.start_cam_btn.grid(row=0, column=2, padx=5)
36+
37+
self.stop_cam_btn = tb.Button(self.top_frame, text="Stop Webcam", bootstyle=DANGER, command=self.stop_webcam)
38+
self.stop_cam_btn.grid(row=0, column=3, padx=5)
39+
40+
self.save_snapshot_btn = tb.Button(self.top_frame, text="Save Snapshot", bootstyle=WARNING, command=self.save_snapshot)
41+
self.save_snapshot_btn.grid(row=0, column=4, padx=5)
42+
43+
# ---------------- Confidence Slider ----------------
44+
self.conf_label = tb.Label(self.top_frame, text="Confidence Threshold: 0.25")
45+
self.conf_label.grid(row=1, column=0, columnspan=2, pady=5)
46+
47+
# Fixed ttkbootstrap scale (remove increment)
48+
self.conf_slider = tb.Scale(
49+
self.top_frame,
50+
from_=0.0,
51+
to=1.0,
52+
bootstyle=INFO,
53+
orient=tk.HORIZONTAL,
54+
command=self.update_conf_label
55+
)
56+
self.conf_slider.set(0.25)
57+
self.conf_slider.grid(row=1, column=2, columnspan=3, sticky="we", padx=5)
58+
59+
# ---------------- Image Frame ----------------
60+
self.image_label = tb.Label(root)
61+
self.image_label.pack(pady=10)
62+
63+
# ---------------- Class Counts Frame ----------------
64+
self.counts_label = tb.Label(root, text="Detected Objects: None", font=("Arial", 12))
65+
self.counts_label.pack(pady=5)
66+
67+
# ---------------- Variables ----------------
68+
self.img_path = None
69+
self.display_img = None
70+
self.cap = None
71+
self.webcam_running = False
72+
self.current_frame = None
73+
self.conf_threshold = 0.25
74+
75+
# ---------------- Update Confidence Label ----------------
76+
def update_conf_label(self, val):
77+
self.conf_threshold = round(float(val), 2)
78+
self.conf_label.config(text=f"Confidence Threshold: {self.conf_threshold:.2f}")
79+
80+
# ---------------- Load Image ----------------
81+
def load_image(self):
82+
self.img_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")])
83+
if self.img_path:
84+
img = Image.open(self.img_path)
85+
img = img.resize((800, 500), Image.Resampling.LANCZOS)
86+
self.display_img = ImageTk.PhotoImage(img)
87+
self.image_label.config(image=self.display_img)
88+
89+
# ---------------- Detect Image ----------------
90+
def detect_objects(self):
91+
if not self.img_path:
92+
messagebox.showwarning("Warning", "Please load an image first!")
93+
return
94+
95+
results = model(self.img_path)[0]
96+
97+
img_cv = cv2.imread(self.img_path)
98+
detected_classes = []
99+
100+
for box, cls, conf in zip(results.boxes.xyxy, results.boxes.cls, results.boxes.conf):
101+
if conf < self.conf_threshold:
102+
continue
103+
x1, y1, x2, y2 = map(int, box)
104+
label = f"{model.names[int(cls)]} {conf:.2f}"
105+
detected_classes.append(model.names[int(cls)])
106+
cv2.rectangle(img_cv, (x1, y1), (x2, y2), (0, 255, 0), 2)
107+
cv2.putText(img_cv, label, (x1, y1 - 10),
108+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
109+
110+
# Update class counts
111+
self.update_class_counts(detected_classes)
112+
113+
img_rgb = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
114+
img_pil = Image.fromarray(img_rgb)
115+
img_pil = img_pil.resize((800, 500), Image.Resampling.LANCZOS)
116+
self.display_img = ImageTk.PhotoImage(img_pil)
117+
self.image_label.config(image=self.display_img)
118+
119+
# ---------------- Start Webcam ----------------
120+
def start_webcam(self):
121+
if self.webcam_running:
122+
return
123+
self.cap = cv2.VideoCapture(0)
124+
if not self.cap.isOpened():
125+
messagebox.showerror("Error", "Cannot open webcam")
126+
return
127+
self.webcam_running = True
128+
self.update_webcam_frame()
129+
130+
# ---------------- Stop Webcam ----------------
131+
def stop_webcam(self):
132+
self.webcam_running = False
133+
if self.cap:
134+
self.cap.release()
135+
self.cap = None
136+
self.counts_label.config(text="Detected Objects: None")
137+
138+
# ---------------- Update Webcam Frame ----------------
139+
def update_webcam_frame(self):
140+
if not self.webcam_running:
141+
return
142+
ret, frame = self.cap.read()
143+
if ret:
144+
self.current_frame = frame.copy()
145+
results = model(frame)[0]
146+
detected_classes = []
147+
148+
for box, cls, conf in zip(results.boxes.xyxy, results.boxes.cls, results.boxes.conf):
149+
if conf < self.conf_threshold:
150+
continue
151+
x1, y1, x2, y2 = map(int, box)
152+
label = f"{model.names[int(cls)]} {conf:.2f}"
153+
detected_classes.append(model.names[int(cls)])
154+
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
155+
cv2.putText(frame, label, (x1, y1 - 10),
156+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
157+
158+
# Update class counts
159+
self.update_class_counts(detected_classes)
160+
161+
img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
162+
img_pil = Image.fromarray(img_rgb)
163+
img_pil = img_pil.resize((800, 500), Image.Resampling.LANCZOS)
164+
self.display_img = ImageTk.PhotoImage(img_pil)
165+
self.image_label.config(image=self.display_img)
166+
167+
self.root.after(30, self.update_webcam_frame) # ~33 FPS
168+
169+
# ---------------- Update Class Counts ----------------
170+
def update_class_counts(self, detected_classes):
171+
if detected_classes:
172+
counts = Counter(detected_classes)
173+
counts_str = ", ".join([f"{cls}: {cnt}" for cls, cnt in counts.items()])
174+
else:
175+
counts_str = "None"
176+
self.counts_label.config(text=f"Detected Objects: {counts_str}")
177+
178+
# ---------------- Save Snapshot ----------------
179+
def save_snapshot(self):
180+
if self.current_frame is None:
181+
messagebox.showwarning("Warning", "No frame available to save!")
182+
return
183+
save_path = filedialog.asksaveasfilename(defaultextension=".jpg",
184+
filetypes=[("JPEG files", "*.jpg"),
185+
("PNG files", "*.png")])
186+
if save_path:
187+
frame = self.current_frame.copy()
188+
results = model(frame)[0]
189+
for box, cls, conf in zip(results.boxes.xyxy, results.boxes.cls, results.boxes.conf):
190+
if conf < self.conf_threshold:
191+
continue
192+
x1, y1, x2, y2 = map(int, box)
193+
label = f"{model.names[int(cls)]} {conf:.2f}"
194+
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
195+
cv2.putText(frame, label, (x1, y1 - 10),
196+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
197+
cv2.imwrite(save_path, frame)
198+
messagebox.showinfo("Saved", f"Snapshot saved to {save_path}")
199+
200+
201+
# ---------------- Main ----------------
202+
if __name__ == "__main__":
203+
root = tb.Window(themename="superhero")
204+
app = ObjectDetectionApp(root)
205+
root.mainloop()

0 commit comments

Comments
 (0)