ultralytics 8.3.4__py3-none-any.whl → 8.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ultralytics might be problematic. Click here for more details.
- tests/test_solutions.py +6 -8
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/default.yaml +1 -1
- ultralytics/cfg/solutions/default.yaml +16 -0
- ultralytics/data/base.py +37 -5
- ultralytics/data/utils.py +3 -3
- ultralytics/engine/exporter.py +3 -4
- ultralytics/engine/trainer.py +4 -4
- ultralytics/engine/validator.py +2 -0
- ultralytics/solutions/ai_gym.py +62 -110
- ultralytics/solutions/heatmap.py +62 -228
- ultralytics/solutions/object_counter.py +105 -217
- ultralytics/solutions/solutions.py +93 -0
- ultralytics/utils/__init__.py +55 -54
- ultralytics/utils/checks.py +36 -20
- ultralytics/utils/plotting.py +50 -70
- ultralytics/utils/torch_utils.py +7 -2
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/METADATA +8 -9
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/RECORD +23 -21
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/LICENSE +0 -0
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/WHEEL +0 -0
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.3.4.dist-info → ultralytics-8.3.6.dist-info}/top_level.txt +0 -0
|
@@ -1,243 +1,131 @@
|
|
|
1
1
|
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from shapely.geometry import LineString, Point
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
from ultralytics.utils.checks import check_imshow, check_requirements
|
|
5
|
+
from ultralytics.solutions.solutions import BaseSolution # Import a parent class
|
|
8
6
|
from ultralytics.utils.plotting import Annotator, colors
|
|
9
7
|
|
|
10
|
-
check_requirements("shapely>=2.0.0")
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
class ObjectCounter(BaseSolution):
|
|
10
|
+
"""A class to manage the counting of objects in a real-time video stream based on their tracks."""
|
|
13
11
|
|
|
12
|
+
def __init__(self, **kwargs):
|
|
13
|
+
"""Initialization function for Count class, a child class of BaseSolution class, can be used for counting the
|
|
14
|
+
objects.
|
|
15
|
+
"""
|
|
16
|
+
super().__init__(**kwargs)
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
self.in_count = 0 # Counter for objects moving inward
|
|
19
|
+
self.out_count = 0 # Counter for objects moving outward
|
|
20
|
+
self.counted_ids = [] # List of IDs of objects that have been counted
|
|
21
|
+
self.classwise_counts = {} # Dictionary for counts, categorized by object class
|
|
22
|
+
self.region_initialized = False # Bool variable for region initialization
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
self
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
line_thickness=2,
|
|
23
|
-
view_img=False,
|
|
24
|
-
view_in_counts=True,
|
|
25
|
-
view_out_counts=True,
|
|
26
|
-
draw_tracks=False,
|
|
27
|
-
):
|
|
24
|
+
self.show_in = self.CFG["show_in"]
|
|
25
|
+
self.show_out = self.CFG["show_out"]
|
|
26
|
+
|
|
27
|
+
def count_objects(self, track_line, box, track_id, prev_position, cls):
|
|
28
28
|
"""
|
|
29
|
-
|
|
29
|
+
Helper function to count objects within a polygonal region.
|
|
30
30
|
|
|
31
31
|
Args:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
view_out_counts (bool): Flag to control whether to display the out counts on the video stream.
|
|
38
|
-
draw_tracks (bool): Flag to control whether to draw the object tracks.
|
|
32
|
+
track_line (dict): last 30 frame track record
|
|
33
|
+
box (list): Bounding box data for specific track in current frame
|
|
34
|
+
track_id (int): track ID of the object
|
|
35
|
+
prev_position (tuple): last frame position coordinates of the track
|
|
36
|
+
cls (int): Class index for classwise count updates
|
|
39
37
|
"""
|
|
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
|
-
# Check if environment supports imshow
|
|
69
|
-
self.env_check = check_imshow(warn=True)
|
|
70
|
-
|
|
71
|
-
# Initialize counting region
|
|
72
|
-
if len(self.reg_pts) == 2:
|
|
73
|
-
print("Line Counter Initiated.")
|
|
74
|
-
self.counting_region = LineString(self.reg_pts)
|
|
75
|
-
elif len(self.reg_pts) >= 3:
|
|
76
|
-
print("Polygon Counter Initiated.")
|
|
77
|
-
self.counting_region = Polygon(self.reg_pts)
|
|
78
|
-
else:
|
|
79
|
-
print("Invalid Region points provided, region_points must be 2 for lines or >= 3 for polygons.")
|
|
80
|
-
print("Using Line Counter Now")
|
|
81
|
-
self.counting_region = LineString(self.reg_pts)
|
|
82
|
-
|
|
83
|
-
# Define the counting line segment
|
|
84
|
-
self.counting_line_segment = LineString(
|
|
85
|
-
[
|
|
86
|
-
(self.reg_pts[0][0], self.reg_pts[0][1]),
|
|
87
|
-
(self.reg_pts[1][0], self.reg_pts[1][1]),
|
|
88
|
-
]
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
def mouse_event_for_region(self, event, x, y, flags, params):
|
|
38
|
+
if prev_position is None or track_id in self.counted_ids:
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
centroid = self.r_s.centroid
|
|
42
|
+
dx = (box[0] - prev_position[0]) * (centroid.x - prev_position[0])
|
|
43
|
+
dy = (box[1] - prev_position[1]) * (centroid.y - prev_position[1])
|
|
44
|
+
|
|
45
|
+
if len(self.region) >= 3 and self.r_s.contains(Point(track_line[-1])):
|
|
46
|
+
self.counted_ids.append(track_id)
|
|
47
|
+
# For polygon region
|
|
48
|
+
if dx > 0:
|
|
49
|
+
self.in_count += 1
|
|
50
|
+
self.classwise_counts[self.names[cls]]["IN"] += 1
|
|
51
|
+
else:
|
|
52
|
+
self.out_count += 1
|
|
53
|
+
self.classwise_counts[self.names[cls]]["OUT"] += 1
|
|
54
|
+
|
|
55
|
+
elif len(self.region) < 3 and LineString([prev_position, box[:2]]).intersects(self.l_s):
|
|
56
|
+
self.counted_ids.append(track_id)
|
|
57
|
+
# For linear region
|
|
58
|
+
if dx > 0 and dy > 0:
|
|
59
|
+
self.in_count += 1
|
|
60
|
+
self.classwise_counts[self.names[cls]]["IN"] += 1
|
|
61
|
+
else:
|
|
62
|
+
self.out_count += 1
|
|
63
|
+
self.classwise_counts[self.names[cls]]["OUT"] += 1
|
|
64
|
+
|
|
65
|
+
def store_classwise_counts(self, cls):
|
|
92
66
|
"""
|
|
93
|
-
|
|
67
|
+
Initialize class-wise counts if not already present.
|
|
94
68
|
|
|
95
69
|
Args:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
70
|
+
cls (int): Class index for classwise count updates
|
|
71
|
+
"""
|
|
72
|
+
if self.names[cls] not in self.classwise_counts:
|
|
73
|
+
self.classwise_counts[self.names[cls]] = {"IN": 0, "OUT": 0}
|
|
74
|
+
|
|
75
|
+
def display_counts(self, im0):
|
|
101
76
|
"""
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
elif event == cv2.EVENT_MOUSEMOVE:
|
|
114
|
-
if self.is_drawing and self.selected_point is not None:
|
|
115
|
-
self.reg_pts[self.selected_point] = (x, y)
|
|
116
|
-
self.counting_region = Polygon(self.reg_pts)
|
|
117
|
-
|
|
118
|
-
elif event == cv2.EVENT_LBUTTONUP:
|
|
119
|
-
self.is_drawing = False
|
|
120
|
-
self.selected_point = None
|
|
121
|
-
|
|
122
|
-
def extract_and_process_tracks(self, tracks):
|
|
123
|
-
"""Extracts and processes tracks for object counting in a video stream."""
|
|
124
|
-
# Annotator Init and region drawing
|
|
125
|
-
annotator = Annotator(self.im0, self.tf, self.names)
|
|
126
|
-
|
|
127
|
-
# Draw region or line
|
|
128
|
-
annotator.draw_region(reg_pts=self.reg_pts, color=(104, 0, 123), thickness=self.tf * 2)
|
|
129
|
-
|
|
130
|
-
# Extract tracks for OBB or object detection
|
|
131
|
-
track_data = tracks[0].obb or tracks[0].boxes
|
|
132
|
-
|
|
133
|
-
if track_data and track_data.id is not None:
|
|
134
|
-
boxes = track_data.xyxy.cpu()
|
|
135
|
-
clss = track_data.cls.cpu().tolist()
|
|
136
|
-
track_ids = track_data.id.int().cpu().tolist()
|
|
137
|
-
|
|
138
|
-
# Extract tracks
|
|
139
|
-
for box, track_id, cls in zip(boxes, track_ids, clss):
|
|
140
|
-
# Draw bounding box
|
|
141
|
-
annotator.box_label(box, label=self.names[cls], color=colors(int(track_id), True))
|
|
142
|
-
|
|
143
|
-
# Store class info
|
|
144
|
-
if self.names[cls] not in self.class_wise_count:
|
|
145
|
-
self.class_wise_count[self.names[cls]] = {"IN": 0, "OUT": 0}
|
|
146
|
-
|
|
147
|
-
# Draw Tracks
|
|
148
|
-
track_line = self.track_history[track_id]
|
|
149
|
-
track_line.append((float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2)))
|
|
150
|
-
if len(track_line) > 30:
|
|
151
|
-
track_line.pop(0)
|
|
152
|
-
|
|
153
|
-
# Draw track trails
|
|
154
|
-
if self.draw_tracks:
|
|
155
|
-
annotator.draw_centroid_and_tracks(
|
|
156
|
-
track_line,
|
|
157
|
-
color=colors(int(track_id), True),
|
|
158
|
-
track_thickness=self.tf,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
prev_position = self.track_history[track_id][-2] if len(self.track_history[track_id]) > 1 else None
|
|
162
|
-
|
|
163
|
-
# Count objects in any polygon
|
|
164
|
-
if len(self.reg_pts) >= 3:
|
|
165
|
-
is_inside = self.counting_region.contains(Point(track_line[-1]))
|
|
166
|
-
|
|
167
|
-
if prev_position is not None and is_inside and track_id not in self.count_ids:
|
|
168
|
-
self.count_ids.append(track_id)
|
|
169
|
-
|
|
170
|
-
if (box[0] - prev_position[0]) * (self.counting_region.centroid.x - prev_position[0]) > 0:
|
|
171
|
-
self.in_counts += 1
|
|
172
|
-
self.class_wise_count[self.names[cls]]["IN"] += 1
|
|
173
|
-
else:
|
|
174
|
-
self.out_counts += 1
|
|
175
|
-
self.class_wise_count[self.names[cls]]["OUT"] += 1
|
|
176
|
-
|
|
177
|
-
# Count objects using line
|
|
178
|
-
elif len(self.reg_pts) == 2:
|
|
179
|
-
if (
|
|
180
|
-
prev_position is not None
|
|
181
|
-
and track_id not in self.count_ids
|
|
182
|
-
and LineString([(prev_position[0], prev_position[1]), (box[0], box[1])]).intersects(
|
|
183
|
-
self.counting_line_segment
|
|
184
|
-
)
|
|
185
|
-
):
|
|
186
|
-
self.count_ids.append(track_id)
|
|
187
|
-
|
|
188
|
-
# Determine the direction of movement (IN or OUT)
|
|
189
|
-
dx = (box[0] - prev_position[0]) * (self.counting_region.centroid.x - prev_position[0])
|
|
190
|
-
dy = (box[1] - prev_position[1]) * (self.counting_region.centroid.y - prev_position[1])
|
|
191
|
-
if dx > 0 and dy > 0:
|
|
192
|
-
self.in_counts += 1
|
|
193
|
-
self.class_wise_count[self.names[cls]]["IN"] += 1
|
|
194
|
-
else:
|
|
195
|
-
self.out_counts += 1
|
|
196
|
-
self.class_wise_count[self.names[cls]]["OUT"] += 1
|
|
197
|
-
|
|
198
|
-
labels_dict = {}
|
|
199
|
-
|
|
200
|
-
for key, value in self.class_wise_count.items():
|
|
201
|
-
if value["IN"] != 0 or value["OUT"] != 0:
|
|
202
|
-
if not self.view_in_counts and not self.view_out_counts:
|
|
203
|
-
continue
|
|
204
|
-
elif not self.view_in_counts:
|
|
205
|
-
labels_dict[str.capitalize(key)] = f"OUT {value['OUT']}"
|
|
206
|
-
elif not self.view_out_counts:
|
|
207
|
-
labels_dict[str.capitalize(key)] = f"IN {value['IN']}"
|
|
208
|
-
else:
|
|
209
|
-
labels_dict[str.capitalize(key)] = f"IN {value['IN']} OUT {value['OUT']}"
|
|
77
|
+
Helper function to display object counts on the frame.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
im0 (ndarray): The input image or frame
|
|
81
|
+
"""
|
|
82
|
+
labels_dict = {
|
|
83
|
+
str.capitalize(key): f"{'IN ' + str(value['IN']) if self.show_in else ''} "
|
|
84
|
+
f"{'OUT ' + str(value['OUT']) if self.show_out else ''}".strip()
|
|
85
|
+
for key, value in self.classwise_counts.items()
|
|
86
|
+
if value["IN"] != 0 or value["OUT"] != 0
|
|
87
|
+
}
|
|
210
88
|
|
|
211
89
|
if labels_dict:
|
|
212
|
-
annotator.display_analytics(
|
|
213
|
-
|
|
214
|
-
def
|
|
215
|
-
"""Displays the current frame with annotations and regions in a window."""
|
|
216
|
-
if self.env_check:
|
|
217
|
-
cv2.namedWindow(self.window_name)
|
|
218
|
-
if len(self.reg_pts) == 4: # only add mouse event If user drawn region
|
|
219
|
-
cv2.setMouseCallback(self.window_name, self.mouse_event_for_region, {"region_points": self.reg_pts})
|
|
220
|
-
cv2.imshow(self.window_name, self.im0)
|
|
221
|
-
# Break Window
|
|
222
|
-
if cv2.waitKey(1) & 0xFF == ord("q"):
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
def start_counting(self, im0, tracks):
|
|
90
|
+
self.annotator.display_analytics(im0, labels_dict, (104, 31, 17), (255, 255, 255), 10)
|
|
91
|
+
|
|
92
|
+
def count(self, im0):
|
|
226
93
|
"""
|
|
227
|
-
|
|
94
|
+
Processes input data (frames or object tracks) and updates counts.
|
|
228
95
|
|
|
229
96
|
Args:
|
|
230
|
-
im0 (ndarray):
|
|
231
|
-
|
|
97
|
+
im0 (ndarray): The input image that will be used for processing
|
|
98
|
+
Returns
|
|
99
|
+
im0 (ndarray): The processed image for more usage
|
|
232
100
|
"""
|
|
233
|
-
self.
|
|
234
|
-
|
|
101
|
+
if not self.region_initialized:
|
|
102
|
+
self.initialize_region()
|
|
103
|
+
self.region_initialized = True
|
|
104
|
+
|
|
105
|
+
self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
|
|
106
|
+
self.extract_tracks(im0) # Extract tracks
|
|
107
|
+
|
|
108
|
+
self.annotator.draw_region(
|
|
109
|
+
reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2
|
|
110
|
+
) # Draw region
|
|
111
|
+
|
|
112
|
+
# Iterate over bounding boxes, track ids and classes index
|
|
113
|
+
for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
|
|
114
|
+
# Draw bounding box and counting region
|
|
115
|
+
self.annotator.box_label(box, label=self.names[cls], color=colors(track_id, True))
|
|
116
|
+
self.store_tracking_history(track_id, box) # Store track history
|
|
117
|
+
self.store_classwise_counts(cls) # store classwise counts in dict
|
|
118
|
+
|
|
119
|
+
# Draw centroid of objects
|
|
120
|
+
self.annotator.draw_centroid_and_tracks(
|
|
121
|
+
self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width
|
|
122
|
+
)
|
|
235
123
|
|
|
236
|
-
|
|
237
|
-
self.
|
|
238
|
-
|
|
124
|
+
# store previous position of track for object counting
|
|
125
|
+
prev_position = self.track_history[track_id][-2] if len(self.track_history[track_id]) > 1 else None
|
|
126
|
+
self.count_objects(self.track_line, box, track_id, prev_position, cls) # Perform object counting
|
|
239
127
|
|
|
128
|
+
self.display_counts(im0) # Display the counts on the frame
|
|
129
|
+
self.display_output(im0) # display output with base class function
|
|
240
130
|
|
|
241
|
-
|
|
242
|
-
classes_names = {0: "person", 1: "car"} # example class names
|
|
243
|
-
ObjectCounter(classes_names)
|
|
131
|
+
return im0 # return output image for more usage
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import cv2
|
|
7
|
+
|
|
8
|
+
from ultralytics import YOLO
|
|
9
|
+
from ultralytics.utils import LOGGER, yaml_load
|
|
10
|
+
from ultralytics.utils.checks import check_imshow, check_requirements
|
|
11
|
+
|
|
12
|
+
check_requirements("shapely>=2.0.0")
|
|
13
|
+
from shapely.geometry import LineString, Polygon
|
|
14
|
+
|
|
15
|
+
DEFAULT_SOL_CFG_PATH = Path(__file__).resolve().parents[1] / "cfg/solutions/default.yaml"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BaseSolution:
|
|
19
|
+
"""A class to manage all the Ultralytics Solutions: https://docs.ultralytics.com/solutions/."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
"""
|
|
23
|
+
Base initializer for all solutions.
|
|
24
|
+
|
|
25
|
+
Child classes should call this with necessary parameters.
|
|
26
|
+
"""
|
|
27
|
+
# Load config and update with args
|
|
28
|
+
self.CFG = yaml_load(DEFAULT_SOL_CFG_PATH)
|
|
29
|
+
self.CFG.update(kwargs)
|
|
30
|
+
LOGGER.info(f"Ultralytics Solutions: ✅ {self.CFG}")
|
|
31
|
+
|
|
32
|
+
self.region = self.CFG["region"] # Store region data for other classes usage
|
|
33
|
+
self.line_width = self.CFG["line_width"] # Store line_width for usage
|
|
34
|
+
|
|
35
|
+
# Load Model and store classes names
|
|
36
|
+
self.model = YOLO(self.CFG["model"])
|
|
37
|
+
self.names = self.model.names
|
|
38
|
+
|
|
39
|
+
# Initialize environment and region setup
|
|
40
|
+
self.env_check = check_imshow(warn=True)
|
|
41
|
+
self.track_history = defaultdict(list)
|
|
42
|
+
|
|
43
|
+
def extract_tracks(self, im0):
|
|
44
|
+
"""
|
|
45
|
+
Apply object tracking and extract tracks.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
im0 (ndarray): The input image or frame
|
|
49
|
+
"""
|
|
50
|
+
self.tracks = self.model.track(source=im0, persist=True, classes=self.CFG["classes"])
|
|
51
|
+
|
|
52
|
+
# Extract tracks for OBB or object detection
|
|
53
|
+
self.track_data = self.tracks[0].obb or self.tracks[0].boxes
|
|
54
|
+
|
|
55
|
+
if self.track_data and self.track_data.id is not None:
|
|
56
|
+
self.boxes = self.track_data.xyxy.cpu()
|
|
57
|
+
self.clss = self.track_data.cls.cpu().tolist()
|
|
58
|
+
self.track_ids = self.track_data.id.int().cpu().tolist()
|
|
59
|
+
else:
|
|
60
|
+
LOGGER.warning("WARNING ⚠️ no tracks found!")
|
|
61
|
+
self.boxes, self.clss, self.track_ids = [], [], []
|
|
62
|
+
|
|
63
|
+
def store_tracking_history(self, track_id, box):
|
|
64
|
+
"""
|
|
65
|
+
Store object tracking history.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
track_id (int): The track ID of the object
|
|
69
|
+
box (list): Bounding box coordinates of the object
|
|
70
|
+
"""
|
|
71
|
+
# Store tracking history
|
|
72
|
+
self.track_line = self.track_history[track_id]
|
|
73
|
+
self.track_line.append(((box[0] + box[2]) / 2, (box[1] + box[3]) / 2))
|
|
74
|
+
if len(self.track_line) > 30:
|
|
75
|
+
self.track_line.pop(0)
|
|
76
|
+
|
|
77
|
+
def initialize_region(self):
|
|
78
|
+
"""Initialize the counting region and line segment based on config."""
|
|
79
|
+
self.region = [(20, 400), (1260, 400)] if self.region is None else self.region
|
|
80
|
+
self.r_s = Polygon(self.region) if len(self.region) >= 3 else LineString(self.region)
|
|
81
|
+
self.l_s = LineString([(self.region[0][0], self.region[0][1]), (self.region[1][0], self.region[1][1])])
|
|
82
|
+
|
|
83
|
+
def display_output(self, im0):
|
|
84
|
+
"""
|
|
85
|
+
Display the results of the processing, which could involve showing frames, printing counts, or saving results.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
im0 (ndarray): The input image or frame
|
|
89
|
+
"""
|
|
90
|
+
if self.CFG.get("show") and self.env_check:
|
|
91
|
+
cv2.imshow("Ultralytics Solutions", im0)
|
|
92
|
+
if cv2.waitKey(1) & 0xFF == ord("q"):
|
|
93
|
+
return
|
ultralytics/utils/__init__.py
CHANGED
|
@@ -61,8 +61,8 @@ HELP_MSG = """
|
|
|
61
61
|
from ultralytics import YOLO
|
|
62
62
|
|
|
63
63
|
# Load a model
|
|
64
|
-
model = YOLO("
|
|
65
|
-
model = YOLO("
|
|
64
|
+
model = YOLO("yolo11n.yaml") # build a new model from scratch
|
|
65
|
+
model = YOLO("yolo11n.pt") # load a pretrained model (recommended for training)
|
|
66
66
|
|
|
67
67
|
# Use the model
|
|
68
68
|
results = model.train(data="coco8.yaml", epochs=3) # train the model
|
|
@@ -77,21 +77,21 @@ HELP_MSG = """
|
|
|
77
77
|
yolo TASK MODE ARGS
|
|
78
78
|
|
|
79
79
|
Where TASK (optional) is one of [detect, segment, classify, pose, obb]
|
|
80
|
-
MODE (required) is one of [train, val, predict, export, benchmark]
|
|
80
|
+
MODE (required) is one of [train, val, predict, export, track, benchmark]
|
|
81
81
|
ARGS (optional) are any number of custom "arg=value" pairs like "imgsz=320" that override defaults.
|
|
82
82
|
See all ARGS at https://docs.ultralytics.com/usage/cfg or with "yolo cfg"
|
|
83
83
|
|
|
84
84
|
- Train a detection model for 10 epochs with an initial learning_rate of 0.01
|
|
85
|
-
yolo detect train data=coco8.yaml model=
|
|
85
|
+
yolo detect train data=coco8.yaml model=yolo11n.pt epochs=10 lr0=0.01
|
|
86
86
|
|
|
87
87
|
- Predict a YouTube video using a pretrained segmentation model at image size 320:
|
|
88
|
-
yolo segment predict model=
|
|
88
|
+
yolo segment predict model=yolo11n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320
|
|
89
89
|
|
|
90
90
|
- Val a pretrained detection model at batch-size 1 and image size 640:
|
|
91
|
-
yolo detect val model=
|
|
91
|
+
yolo detect val model=yolo11n.pt data=coco8.yaml batch=1 imgsz=640
|
|
92
92
|
|
|
93
|
-
- Export a
|
|
94
|
-
yolo export model=
|
|
93
|
+
- Export a YOLO11n classification model to ONNX format at image size 224 by 128 (no TASK required)
|
|
94
|
+
yolo export model=yolo11n-cls.pt format=onnx imgsz=224,128
|
|
95
95
|
|
|
96
96
|
- Run special commands:
|
|
97
97
|
yolo help
|
|
@@ -989,55 +989,56 @@ def set_sentry():
|
|
|
989
989
|
Additionally, the function sets custom tags and user information for Sentry events.
|
|
990
990
|
"""
|
|
991
991
|
if (
|
|
992
|
-
SETTINGS["sync"]
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
992
|
+
not SETTINGS["sync"]
|
|
993
|
+
or RANK not in {-1, 0}
|
|
994
|
+
or Path(ARGV[0]).name != "yolo"
|
|
995
|
+
or TESTS_RUNNING
|
|
996
|
+
or not ONLINE
|
|
997
|
+
or not IS_PIP_PACKAGE
|
|
998
|
+
or IS_GIT_DIR
|
|
999
999
|
):
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1000
|
+
return
|
|
1001
|
+
# If sentry_sdk package is not installed then return and do not use Sentry
|
|
1002
|
+
try:
|
|
1003
|
+
import sentry_sdk # noqa
|
|
1004
|
+
except ImportError:
|
|
1005
|
+
return
|
|
1006
|
+
|
|
1007
|
+
def before_send(event, hint):
|
|
1008
|
+
"""
|
|
1009
|
+
Modify the event before sending it to Sentry based on specific exception types and messages.
|
|
1009
1010
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1011
|
+
Args:
|
|
1012
|
+
event (dict): The event dictionary containing information about the error.
|
|
1013
|
+
hint (dict): A dictionary containing additional information about the error.
|
|
1013
1014
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1015
|
+
Returns:
|
|
1016
|
+
dict: The modified event or None if the event should not be sent to Sentry.
|
|
1017
|
+
"""
|
|
1018
|
+
if "exc_info" in hint:
|
|
1019
|
+
exc_type, exc_value, _ = hint["exc_info"]
|
|
1020
|
+
if exc_type in {KeyboardInterrupt, FileNotFoundError} or "out of memory" in str(exc_value):
|
|
1021
|
+
return None # do not send event
|
|
1022
|
+
|
|
1023
|
+
event["tags"] = {
|
|
1024
|
+
"sys_argv": ARGV[0],
|
|
1025
|
+
"sys_argv_name": Path(ARGV[0]).name,
|
|
1026
|
+
"install": "git" if IS_GIT_DIR else "pip" if IS_PIP_PACKAGE else "other",
|
|
1027
|
+
"os": ENVIRONMENT,
|
|
1028
|
+
}
|
|
1029
|
+
return event
|
|
1030
|
+
|
|
1031
|
+
sentry_sdk.init(
|
|
1032
|
+
dsn="https://888e5a0778212e1d0314c37d4b9aae5d@o4504521589325824.ingest.us.sentry.io/4504521592406016",
|
|
1033
|
+
debug=False,
|
|
1034
|
+
auto_enabling_integrations=False,
|
|
1035
|
+
traces_sample_rate=1.0,
|
|
1036
|
+
release=__version__,
|
|
1037
|
+
environment="production", # 'dev' or 'production'
|
|
1038
|
+
before_send=before_send,
|
|
1039
|
+
ignore_errors=[KeyboardInterrupt, FileNotFoundError],
|
|
1040
|
+
)
|
|
1041
|
+
sentry_sdk.set_user({"id": SETTINGS["uuid"]}) # SHA-256 anonymized UUID hash
|
|
1041
1042
|
|
|
1042
1043
|
|
|
1043
1044
|
class JSONDict(dict):
|