-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdetection_engine.py
More file actions
219 lines (175 loc) · 6.92 KB
/
Copy pathdetection_engine.py
File metadata and controls
219 lines (175 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
from ultralytics import YOLO
import cv2
import numpy as np
# -------- OBJECT GROUPS --------
PEOPLE = ["person"]
FURNITURE = ["chair", "couch", "bed", "dining table"]
VEHICLES = ["car", "motorbike", "bus", "truck", "bicycle"]
COMMON_OBSTACLES = [
"bottle", "backpack", "handbag", "suitcase",
"tv", "laptop", "keyboard", "cell phone",
"book", "cup"
]
IMPORTANT_OBJECTS = PEOPLE + FURNITURE + VEHICLES + COMMON_OBSTACLES
class DetectionEngine:
"""YOLOv8-based object detection engine with guidance generation."""
def __init__(self, model_path="yolov8n.pt"):
"""Initialize the detection model.
Args:
model_path: Path to the YOLOv8 model file
"""
self.model = YOLO(model_path)
def detect_frame(self, frame, confidence_threshold=0.5, min_width=40):
"""Run detection on a single frame.
Args:
frame: Input frame from OpenCV (numpy array)
confidence_threshold: Minimum confidence score (0-1)
min_width: Minimum bounding box width to consider
Returns:
dict: Contains 'closest_obj' (tuple or None), 'all_detections' (list), 'frame_shape'
"""
results = self.model(frame, verbose=False)
h, w, _ = frame.shape
closest_obj = None
max_width = 0
all_detections = []
# Find closest important object
for r in results:
for box in r.boxes:
conf = float(box.conf[0])
if conf < confidence_threshold:
continue
x1, y1, x2, y2 = map(int, box.xyxy[0])
cls = int(box.cls[0])
label = self.model.names[cls]
width = x2 - x1
if width < min_width:
continue
if label not in IMPORTANT_OBJECTS:
continue
# Store detection
all_detections.append({
'label': label,
'box': (x1, y1, x2, y2),
'confidence': conf,
'width': width
})
# Track closest
if width > max_width:
max_width = width
closest_obj = (label, x1, y1, x2, y2)
return {
'closest_obj': closest_obj,
'all_detections': all_detections,
'frame_shape': (h, w)
}
@staticmethod
def get_distance(width, thresholds=(220, 120)):
"""Classify distance based on bounding box width.
Args:
width: Bounding box width
thresholds: (very_close_threshold, near_threshold)
Returns:
str: Distance classification ('very close', 'near', 'far')
"""
if width > thresholds[0]:
return "very close"
elif width > thresholds[1]:
return "near"
else:
return "far"
@staticmethod
def get_direction(center_x, frame_width):
"""Classify direction based on horizontal position.
Args:
center_x: Center X coordinate of bounding box
frame_width: Width of frame
Returns:
str: Direction classification ('left', 'center', 'right')
"""
if center_x < frame_width / 3:
return "left"
elif center_x > 2 * frame_width / 3:
return "right"
else:
return "center"
@staticmethod
def get_object_type(label):
"""Classify object type.
Args:
label: Object label from YOLO
Returns:
str: Object type ('vehicle', 'person', 'furniture', 'object')
"""
if label in VEHICLES:
return "vehicle"
elif label in PEOPLE:
return "person"
elif label in FURNITURE:
return "furniture"
else:
return "object"
@staticmethod
def generate_guidance(label, obj_type, distance, direction):
"""Generate guidance text based on detected object.
Args:
label: Object label
obj_type: Object type classification
distance: Distance classification
direction: Direction classification
Returns:
str: Guidance text for the user
"""
if obj_type == "vehicle":
if distance == "very close":
return f"Stop immediately, {label} very close ahead"
elif distance == "near":
return f"Warning, {label} approaching on {direction}"
else:
return f"{label} detected ahead"
elif obj_type == "person":
if distance == "very close":
return f"Person very close on {direction}, slow down"
else:
return f"Person on {direction}"
else: # furniture, obstacles, other objects
if distance == "very close":
if direction == "left":
return "Obstacle very close on left, move right"
elif direction == "right":
return "Obstacle very close on right, move left"
else:
return "Obstacle ahead, stop or move sideways"
elif distance == "near":
return f"Obstacle nearby on {direction}"
else:
return "Path clear, move forward"
return "Path clear, move forward"
@staticmethod
def draw_annotations(frame, closest_obj, frame_width):
"""Draw bounding box and text annotations on frame.
Args:
frame: Input frame (will be modified in place)
closest_obj: Tuple of (label, x1, y1, x2, y2) or None
frame_width: Width of frame for centering text
Returns:
frame: Annotated frame
"""
if closest_obj:
label, x1, y1, x2, y2 = closest_obj
# Determine color based on distance
width = x2 - x1
if width > 220:
color = (0, 0, 255) # Red for very close
else:
color = (0, 255, 255) # Yellow
# Draw bounding box
cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
# Draw label with distance and direction
distance = DetectionEngine.get_distance(width)
center_x = (x1 + x2) // 2
direction = DetectionEngine.get_direction(center_x, frame_width)
text = f"{label} | {distance} | {direction}"
cv2.putText(frame, text, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
return frame