ultralytics 8.2.17__py3-none-any.whl → 8.2.18__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.
- ultralytics/__init__.py +1 -1
- ultralytics/solutions/__init__.py +18 -0
- ultralytics/solutions/ai_gym.py +53 -79
- ultralytics/solutions/distance_calculation.py +66 -71
- ultralytics/solutions/heatmap.py +95 -133
- ultralytics/solutions/object_counter.py +62 -82
- ultralytics/solutions/parking_management.py +22 -9
- ultralytics/solutions/queue_management.py +63 -82
- ultralytics/solutions/speed_estimation.py +50 -68
- ultralytics/utils/plotting.py +40 -42
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/METADATA +1 -1
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/RECORD +16 -16
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/LICENSE +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/WHEEL +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.18.dist-info}/top_level.txt +0 -0
ultralytics/solutions/heatmap.py
CHANGED
|
@@ -16,28 +16,48 @@ from shapely.geometry import LineString, Point, Polygon
|
|
|
16
16
|
class Heatmap:
|
|
17
17
|
"""A class to draw heatmaps in real-time video stream based on their tracks."""
|
|
18
18
|
|
|
19
|
-
def __init__(
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
classes_names,
|
|
22
|
+
imw=0,
|
|
23
|
+
imh=0,
|
|
24
|
+
colormap=cv2.COLORMAP_JET,
|
|
25
|
+
heatmap_alpha=0.5,
|
|
26
|
+
view_img=False,
|
|
27
|
+
view_in_counts=True,
|
|
28
|
+
view_out_counts=True,
|
|
29
|
+
count_reg_pts=None,
|
|
30
|
+
count_txt_color=(0, 0, 0),
|
|
31
|
+
count_bg_color=(255, 255, 255),
|
|
32
|
+
count_reg_color=(255, 0, 255),
|
|
33
|
+
region_thickness=5,
|
|
34
|
+
line_dist_thresh=15,
|
|
35
|
+
line_thickness=2,
|
|
36
|
+
decay_factor=0.99,
|
|
37
|
+
shape="circle",
|
|
38
|
+
):
|
|
20
39
|
"""Initializes the heatmap class with default values for Visual, Image, track, count and heatmap parameters."""
|
|
21
40
|
|
|
22
41
|
# Visual information
|
|
23
42
|
self.annotator = None
|
|
24
|
-
self.view_img =
|
|
25
|
-
self.shape =
|
|
43
|
+
self.view_img = view_img
|
|
44
|
+
self.shape = shape
|
|
26
45
|
|
|
27
|
-
self.
|
|
46
|
+
self.initialized = False
|
|
47
|
+
self.names = classes_names # Classes names
|
|
28
48
|
|
|
29
49
|
# Image information
|
|
30
|
-
self.imw =
|
|
31
|
-
self.imh =
|
|
50
|
+
self.imw = imw
|
|
51
|
+
self.imh = imh
|
|
32
52
|
self.im0 = None
|
|
33
|
-
self.tf =
|
|
34
|
-
self.view_in_counts =
|
|
35
|
-
self.view_out_counts =
|
|
53
|
+
self.tf = line_thickness
|
|
54
|
+
self.view_in_counts = view_in_counts
|
|
55
|
+
self.view_out_counts = view_out_counts
|
|
36
56
|
|
|
37
57
|
# Heatmap colormap and heatmap np array
|
|
38
|
-
self.colormap =
|
|
58
|
+
self.colormap = colormap
|
|
39
59
|
self.heatmap = None
|
|
40
|
-
self.heatmap_alpha =
|
|
60
|
+
self.heatmap_alpha = heatmap_alpha
|
|
41
61
|
|
|
42
62
|
# Predict/track information
|
|
43
63
|
self.boxes = None
|
|
@@ -46,112 +66,48 @@ class Heatmap:
|
|
|
46
66
|
self.track_history = defaultdict(list)
|
|
47
67
|
|
|
48
68
|
# Region & Line Information
|
|
49
|
-
self.count_reg_pts = None
|
|
50
69
|
self.counting_region = None
|
|
51
|
-
self.line_dist_thresh =
|
|
52
|
-
self.region_thickness =
|
|
53
|
-
self.region_color =
|
|
70
|
+
self.line_dist_thresh = line_dist_thresh
|
|
71
|
+
self.region_thickness = region_thickness
|
|
72
|
+
self.region_color = count_reg_color
|
|
54
73
|
|
|
55
74
|
# Object Counting Information
|
|
56
75
|
self.in_counts = 0
|
|
57
76
|
self.out_counts = 0
|
|
58
77
|
self.count_ids = []
|
|
59
78
|
self.class_wise_count = {}
|
|
60
|
-
self.count_txt_color =
|
|
61
|
-
self.count_bg_color =
|
|
79
|
+
self.count_txt_color = count_txt_color
|
|
80
|
+
self.count_bg_color = count_bg_color
|
|
62
81
|
self.cls_txtdisplay_gap = 50
|
|
63
82
|
|
|
64
83
|
# Decay factor
|
|
65
|
-
self.decay_factor =
|
|
84
|
+
self.decay_factor = decay_factor
|
|
66
85
|
|
|
67
|
-
# Check if environment
|
|
86
|
+
# Check if environment supports imshow
|
|
68
87
|
self.env_check = check_imshow(warn=True)
|
|
69
88
|
|
|
70
|
-
def set_args(
|
|
71
|
-
self,
|
|
72
|
-
imw,
|
|
73
|
-
imh,
|
|
74
|
-
classes_names=None,
|
|
75
|
-
colormap=cv2.COLORMAP_JET,
|
|
76
|
-
heatmap_alpha=0.5,
|
|
77
|
-
view_img=False,
|
|
78
|
-
view_in_counts=True,
|
|
79
|
-
view_out_counts=True,
|
|
80
|
-
count_reg_pts=None,
|
|
81
|
-
count_txt_color=(0, 0, 0),
|
|
82
|
-
count_bg_color=(255, 255, 255),
|
|
83
|
-
count_reg_color=(255, 0, 255),
|
|
84
|
-
region_thickness=5,
|
|
85
|
-
line_dist_thresh=15,
|
|
86
|
-
line_thickness=2,
|
|
87
|
-
decay_factor=0.99,
|
|
88
|
-
shape="circle",
|
|
89
|
-
):
|
|
90
|
-
"""
|
|
91
|
-
Configures the heatmap colormap, width, height and display parameters.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
colormap (cv2.COLORMAP): The colormap to be set.
|
|
95
|
-
imw (int): The width of the frame.
|
|
96
|
-
imh (int): The height of the frame.
|
|
97
|
-
classes_names (dict): Classes names
|
|
98
|
-
line_thickness (int): Line thickness for bounding boxes.
|
|
99
|
-
heatmap_alpha (float): alpha value for heatmap display
|
|
100
|
-
view_img (bool): Flag indicating frame display
|
|
101
|
-
view_in_counts (bool): Flag to control whether to display the incounts on video stream.
|
|
102
|
-
view_out_counts (bool): Flag to control whether to display the outcounts on video stream.
|
|
103
|
-
count_reg_pts (list): Object counting region points
|
|
104
|
-
count_txt_color (RGB color): count text color value
|
|
105
|
-
count_bg_color (RGB color): count highlighter line color
|
|
106
|
-
count_reg_color (RGB color): Color of object counting region
|
|
107
|
-
region_thickness (int): Object counting Region thickness
|
|
108
|
-
line_dist_thresh (int): Euclidean Distance threshold for line counter
|
|
109
|
-
decay_factor (float): value for removing heatmap area after object passed
|
|
110
|
-
shape (str): Heatmap shape, rect or circle shape supported
|
|
111
|
-
"""
|
|
112
|
-
self.tf = line_thickness
|
|
113
|
-
self.names = classes_names
|
|
114
|
-
self.imw = imw
|
|
115
|
-
self.imh = imh
|
|
116
|
-
self.heatmap_alpha = heatmap_alpha
|
|
117
|
-
self.view_img = view_img
|
|
118
|
-
self.view_in_counts = view_in_counts
|
|
119
|
-
self.view_out_counts = view_out_counts
|
|
120
|
-
self.colormap = colormap
|
|
121
|
-
|
|
122
89
|
# Region and line selection
|
|
123
|
-
|
|
124
|
-
|
|
90
|
+
self.count_reg_pts = count_reg_pts
|
|
91
|
+
print(self.count_reg_pts)
|
|
92
|
+
if self.count_reg_pts is not None:
|
|
93
|
+
if len(self.count_reg_pts) == 2:
|
|
125
94
|
print("Line Counter Initiated.")
|
|
126
|
-
self.count_reg_pts = count_reg_pts
|
|
127
95
|
self.counting_region = LineString(self.count_reg_pts)
|
|
128
|
-
elif len(count_reg_pts) >= 3:
|
|
96
|
+
elif len(self.count_reg_pts) >= 3:
|
|
129
97
|
print("Polygon Counter Initiated.")
|
|
130
|
-
self.count_reg_pts = count_reg_pts
|
|
131
98
|
self.counting_region = Polygon(self.count_reg_pts)
|
|
132
99
|
else:
|
|
133
100
|
print("Invalid Region points provided, region_points must be 2 for lines or >= 3 for polygons.")
|
|
134
101
|
print("Using Line Counter Now")
|
|
135
102
|
self.counting_region = LineString(self.count_reg_pts)
|
|
136
103
|
|
|
137
|
-
#
|
|
138
|
-
self.heatmap = np.zeros((int(self.imh), int(self.imw)), dtype=np.float32)
|
|
139
|
-
|
|
140
|
-
self.count_txt_color = count_txt_color
|
|
141
|
-
self.count_bg_color = count_bg_color
|
|
142
|
-
self.region_color = count_reg_color
|
|
143
|
-
self.region_thickness = region_thickness
|
|
144
|
-
self.decay_factor = decay_factor
|
|
145
|
-
self.line_dist_thresh = line_dist_thresh
|
|
146
|
-
self.shape = shape
|
|
147
|
-
|
|
148
|
-
# shape of heatmap, if not selected
|
|
104
|
+
# Shape of heatmap, if not selected
|
|
149
105
|
if self.shape not in {"circle", "rect"}:
|
|
150
106
|
print("Unknown shape value provided, 'circle' & 'rect' supported")
|
|
151
107
|
print("Using Circular shape now")
|
|
152
108
|
self.shape = "circle"
|
|
153
109
|
|
|
154
|
-
def extract_results(self, tracks):
|
|
110
|
+
def extract_results(self, tracks, _intialized=False):
|
|
155
111
|
"""
|
|
156
112
|
Extracts results from the provided data.
|
|
157
113
|
|
|
@@ -171,18 +127,20 @@ class Heatmap:
|
|
|
171
127
|
tracks (list): List of tracks obtained from the object tracking process.
|
|
172
128
|
"""
|
|
173
129
|
self.im0 = im0
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
130
|
+
|
|
131
|
+
# Initialize heatmap only once
|
|
132
|
+
if not self.initialized:
|
|
133
|
+
self.heatmap = np.zeros((int(self.im0.shape[0]), int(self.im0.shape[1])), dtype=np.float32)
|
|
134
|
+
self.initialized = True
|
|
135
|
+
|
|
179
136
|
self.heatmap *= self.decay_factor # decay factor
|
|
137
|
+
|
|
180
138
|
self.extract_results(tracks)
|
|
181
139
|
self.annotator = Annotator(self.im0, self.tf, None)
|
|
182
140
|
|
|
183
|
-
if self.
|
|
141
|
+
if self.track_ids is not None:
|
|
184
142
|
# Draw counting region
|
|
185
|
-
if self.
|
|
143
|
+
if self.count_reg_pts is not None:
|
|
186
144
|
self.annotator.draw_region(
|
|
187
145
|
reg_pts=self.count_reg_pts, color=self.region_color, thickness=self.region_thickness
|
|
188
146
|
)
|
|
@@ -214,25 +172,12 @@ class Heatmap:
|
|
|
214
172
|
|
|
215
173
|
prev_position = self.track_history[track_id][-2] if len(self.track_history[track_id]) > 1 else None
|
|
216
174
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
self.count_ids
|
|
223
|
-
|
|
224
|
-
if (box[0] - prev_position[0]) * (self.counting_region.centroid.x - prev_position[0]) > 0:
|
|
225
|
-
self.in_counts += 1
|
|
226
|
-
self.class_wise_count[self.names[cls]]["IN"] += 1
|
|
227
|
-
else:
|
|
228
|
-
self.out_counts += 1
|
|
229
|
-
self.class_wise_count[self.names[cls]]["OUT"] += 1
|
|
230
|
-
|
|
231
|
-
# Count objects using line
|
|
232
|
-
elif len(self.count_reg_pts) == 2:
|
|
233
|
-
if prev_position is not None and track_id not in self.count_ids:
|
|
234
|
-
distance = Point(track_line[-1]).distance(self.counting_region)
|
|
235
|
-
if distance < self.line_dist_thresh and track_id not in self.count_ids:
|
|
175
|
+
if self.count_reg_pts is not None:
|
|
176
|
+
# Count objects in any polygon
|
|
177
|
+
if len(self.count_reg_pts) >= 3:
|
|
178
|
+
is_inside = self.counting_region.contains(Point(track_line[-1]))
|
|
179
|
+
|
|
180
|
+
if prev_position is not None and is_inside and track_id not in self.count_ids:
|
|
236
181
|
self.count_ids.append(track_id)
|
|
237
182
|
|
|
238
183
|
if (box[0] - prev_position[0]) * (self.counting_region.centroid.x - prev_position[0]) > 0:
|
|
@@ -242,6 +187,22 @@ class Heatmap:
|
|
|
242
187
|
self.out_counts += 1
|
|
243
188
|
self.class_wise_count[self.names[cls]]["OUT"] += 1
|
|
244
189
|
|
|
190
|
+
# Count objects using line
|
|
191
|
+
elif len(self.count_reg_pts) == 2:
|
|
192
|
+
if prev_position is not None and track_id not in self.count_ids:
|
|
193
|
+
distance = Point(track_line[-1]).distance(self.counting_region)
|
|
194
|
+
if distance < self.line_dist_thresh and track_id not in self.count_ids:
|
|
195
|
+
self.count_ids.append(track_id)
|
|
196
|
+
|
|
197
|
+
if (box[0] - prev_position[0]) * (
|
|
198
|
+
self.counting_region.centroid.x - prev_position[0]
|
|
199
|
+
) > 0:
|
|
200
|
+
self.in_counts += 1
|
|
201
|
+
self.class_wise_count[self.names[cls]]["IN"] += 1
|
|
202
|
+
else:
|
|
203
|
+
self.out_counts += 1
|
|
204
|
+
self.class_wise_count[self.names[cls]]["OUT"] += 1
|
|
205
|
+
|
|
245
206
|
else:
|
|
246
207
|
for box, cls in zip(self.boxes, self.clss):
|
|
247
208
|
if self.shape == "circle":
|
|
@@ -258,26 +219,26 @@ class Heatmap:
|
|
|
258
219
|
else:
|
|
259
220
|
self.heatmap[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] += 2
|
|
260
221
|
|
|
222
|
+
if self.count_reg_pts is not None:
|
|
223
|
+
labels_dict = {}
|
|
224
|
+
|
|
225
|
+
for key, value in self.class_wise_count.items():
|
|
226
|
+
if value["IN"] != 0 or value["OUT"] != 0:
|
|
227
|
+
if not self.view_in_counts and not self.view_out_counts:
|
|
228
|
+
continue
|
|
229
|
+
elif not self.view_in_counts:
|
|
230
|
+
labels_dict[str.capitalize(key)] = f"OUT {value['OUT']}"
|
|
231
|
+
elif not self.view_out_counts:
|
|
232
|
+
labels_dict[str.capitalize(key)] = f"IN {value['IN']}"
|
|
233
|
+
else:
|
|
234
|
+
labels_dict[str.capitalize(key)] = f"IN {value['IN']} OUT {value['OUT']}"
|
|
235
|
+
|
|
236
|
+
if labels_dict is not None:
|
|
237
|
+
self.annotator.display_analytics(self.im0, labels_dict, self.count_txt_color, self.count_bg_color, 10)
|
|
238
|
+
|
|
261
239
|
# Normalize, apply colormap to heatmap and combine with original image
|
|
262
240
|
heatmap_normalized = cv2.normalize(self.heatmap, None, 0, 255, cv2.NORM_MINMAX)
|
|
263
241
|
heatmap_colored = cv2.applyColorMap(heatmap_normalized.astype(np.uint8), self.colormap)
|
|
264
|
-
|
|
265
|
-
labels_dict = {}
|
|
266
|
-
|
|
267
|
-
for key, value in self.class_wise_count.items():
|
|
268
|
-
if value["IN"] != 0 or value["OUT"] != 0:
|
|
269
|
-
if not self.view_in_counts and not self.view_out_counts:
|
|
270
|
-
continue
|
|
271
|
-
elif not self.view_in_counts:
|
|
272
|
-
labels_dict[str.capitalize(key)] = f"OUT {value['OUT']}"
|
|
273
|
-
elif not self.view_out_counts:
|
|
274
|
-
labels_dict[str.capitalize(key)] = f"IN {value['IN']}"
|
|
275
|
-
else:
|
|
276
|
-
labels_dict[str.capitalize(key)] = f"IN {value['IN']} OUT {value['OUT']}"
|
|
277
|
-
|
|
278
|
-
if labels_dict is not None:
|
|
279
|
-
self.annotator.display_analytics(self.im0, labels_dict, self.count_txt_color, self.count_bg_color, 10)
|
|
280
|
-
|
|
281
242
|
self.im0 = cv2.addWeighted(self.im0, 1 - self.heatmap_alpha, heatmap_colored, self.heatmap_alpha, 0)
|
|
282
243
|
|
|
283
244
|
if self.env_check and self.view_img:
|
|
@@ -294,4 +255,5 @@ class Heatmap:
|
|
|
294
255
|
|
|
295
256
|
|
|
296
257
|
if __name__ == "__main__":
|
|
297
|
-
|
|
258
|
+
classes_names = {0: "person", 1: "car"} # example class names
|
|
259
|
+
heatmap = Heatmap(classes_names)
|
|
@@ -15,55 +15,10 @@ from shapely.geometry import LineString, Point, Polygon
|
|
|
15
15
|
class ObjectCounter:
|
|
16
16
|
"""A class to manage the counting of objects in a real-time video stream based on their tracks."""
|
|
17
17
|
|
|
18
|
-
def __init__(
|
|
19
|
-
"""Initializes the Counter with default values for various tracking and counting parameters."""
|
|
20
|
-
|
|
21
|
-
# Mouse events
|
|
22
|
-
self.is_drawing = False
|
|
23
|
-
self.selected_point = None
|
|
24
|
-
|
|
25
|
-
# Region & Line Information
|
|
26
|
-
self.reg_pts = [(20, 400), (1260, 400)]
|
|
27
|
-
self.line_dist_thresh = 15
|
|
28
|
-
self.counting_region = None
|
|
29
|
-
self.region_color = (255, 0, 255)
|
|
30
|
-
self.region_thickness = 5
|
|
31
|
-
|
|
32
|
-
# Image and annotation Information
|
|
33
|
-
self.im0 = None
|
|
34
|
-
self.tf = None
|
|
35
|
-
self.view_img = False
|
|
36
|
-
self.view_in_counts = True
|
|
37
|
-
self.view_out_counts = True
|
|
38
|
-
|
|
39
|
-
self.names = None # Classes names
|
|
40
|
-
self.annotator = None # Annotator
|
|
41
|
-
self.window_name = "Ultralytics YOLOv8 Object Counter"
|
|
42
|
-
|
|
43
|
-
# Object counting Information
|
|
44
|
-
self.in_counts = 0
|
|
45
|
-
self.out_counts = 0
|
|
46
|
-
self.count_ids = []
|
|
47
|
-
self.class_wise_count = {}
|
|
48
|
-
self.count_txt_thickness = 0
|
|
49
|
-
self.count_txt_color = (255, 255, 255)
|
|
50
|
-
self.count_bg_color = (255, 255, 255)
|
|
51
|
-
self.cls_txtdisplay_gap = 50
|
|
52
|
-
self.fontsize = 0.6
|
|
53
|
-
|
|
54
|
-
# Tracks info
|
|
55
|
-
self.track_history = defaultdict(list)
|
|
56
|
-
self.track_thickness = 2
|
|
57
|
-
self.draw_tracks = False
|
|
58
|
-
self.track_color = None
|
|
59
|
-
|
|
60
|
-
# Check if environment support imshow
|
|
61
|
-
self.env_check = check_imshow(warn=True)
|
|
62
|
-
|
|
63
|
-
def set_args(
|
|
18
|
+
def __init__(
|
|
64
19
|
self,
|
|
65
20
|
classes_names,
|
|
66
|
-
reg_pts,
|
|
21
|
+
reg_pts=None,
|
|
67
22
|
count_reg_color=(255, 0, 255),
|
|
68
23
|
count_txt_color=(0, 0, 0),
|
|
69
24
|
count_bg_color=(255, 255, 255),
|
|
@@ -79,66 +34,90 @@ class ObjectCounter:
|
|
|
79
34
|
cls_txtdisplay_gap=50,
|
|
80
35
|
):
|
|
81
36
|
"""
|
|
82
|
-
|
|
37
|
+
Initializes the ObjectCounter with various tracking and counting parameters.
|
|
83
38
|
|
|
84
39
|
Args:
|
|
40
|
+
classes_names (dict): Dictionary of class names.
|
|
41
|
+
reg_pts (list): List of points defining the counting region.
|
|
42
|
+
count_reg_color (tuple): RGB color of the counting region.
|
|
43
|
+
count_txt_color (tuple): RGB color of the count text.
|
|
44
|
+
count_bg_color (tuple): RGB color of the count text background.
|
|
85
45
|
line_thickness (int): Line thickness for bounding boxes.
|
|
46
|
+
track_thickness (int): Thickness of the track lines.
|
|
86
47
|
view_img (bool): Flag to control whether to display the video stream.
|
|
87
|
-
view_in_counts (bool): Flag to control whether to display the
|
|
88
|
-
view_out_counts (bool): Flag to control whether to display the
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
count_bg_color (RGB color): count highlighter line color
|
|
95
|
-
count_reg_color (RGB color): Color of object counting region
|
|
96
|
-
track_color (RGB color): color for tracks
|
|
97
|
-
region_thickness (int): Object counting Region thickness
|
|
98
|
-
line_dist_thresh (int): Euclidean Distance threshold for line counter
|
|
99
|
-
cls_txtdisplay_gap (int): Display gap between each class count
|
|
48
|
+
view_in_counts (bool): Flag to control whether to display the in counts on the video stream.
|
|
49
|
+
view_out_counts (bool): Flag to control whether to display the out counts on the video stream.
|
|
50
|
+
draw_tracks (bool): Flag to control whether to draw the object tracks.
|
|
51
|
+
track_color (tuple): RGB color of the tracks.
|
|
52
|
+
region_thickness (int): Thickness of the object counting region.
|
|
53
|
+
line_dist_thresh (int): Euclidean distance threshold for line counter.
|
|
54
|
+
cls_txtdisplay_gap (int): Display gap between each class count.
|
|
100
55
|
"""
|
|
56
|
+
|
|
57
|
+
# Mouse events
|
|
58
|
+
self.is_drawing = False
|
|
59
|
+
self.selected_point = None
|
|
60
|
+
|
|
61
|
+
# Region & Line Information
|
|
62
|
+
self.reg_pts = [(20, 400), (1260, 400)] if reg_pts is None else reg_pts
|
|
63
|
+
self.line_dist_thresh = line_dist_thresh
|
|
64
|
+
self.counting_region = None
|
|
65
|
+
self.region_color = count_reg_color
|
|
66
|
+
self.region_thickness = region_thickness
|
|
67
|
+
|
|
68
|
+
# Image and annotation Information
|
|
69
|
+
self.im0 = None
|
|
101
70
|
self.tf = line_thickness
|
|
102
71
|
self.view_img = view_img
|
|
103
72
|
self.view_in_counts = view_in_counts
|
|
104
73
|
self.view_out_counts = view_out_counts
|
|
74
|
+
|
|
75
|
+
self.names = classes_names # Classes names
|
|
76
|
+
self.annotator = None # Annotator
|
|
77
|
+
self.window_name = "Ultralytics YOLOv8 Object Counter"
|
|
78
|
+
|
|
79
|
+
# Object counting Information
|
|
80
|
+
self.in_counts = 0
|
|
81
|
+
self.out_counts = 0
|
|
82
|
+
self.count_ids = []
|
|
83
|
+
self.class_wise_count = {}
|
|
84
|
+
self.count_txt_thickness = 0
|
|
85
|
+
self.count_txt_color = count_txt_color
|
|
86
|
+
self.count_bg_color = count_bg_color
|
|
87
|
+
self.cls_txtdisplay_gap = cls_txtdisplay_gap
|
|
88
|
+
self.fontsize = 0.6
|
|
89
|
+
|
|
90
|
+
# Tracks info
|
|
91
|
+
self.track_history = defaultdict(list)
|
|
105
92
|
self.track_thickness = track_thickness
|
|
106
93
|
self.draw_tracks = draw_tracks
|
|
94
|
+
self.track_color = track_color
|
|
95
|
+
|
|
96
|
+
# Check if environment supports imshow
|
|
97
|
+
self.env_check = check_imshow(warn=True)
|
|
107
98
|
|
|
108
|
-
#
|
|
109
|
-
if len(reg_pts) == 2:
|
|
99
|
+
# Initialize counting region
|
|
100
|
+
if len(self.reg_pts) == 2:
|
|
110
101
|
print("Line Counter Initiated.")
|
|
111
|
-
self.reg_pts = reg_pts
|
|
112
102
|
self.counting_region = LineString(self.reg_pts)
|
|
113
|
-
elif len(reg_pts) >= 3:
|
|
103
|
+
elif len(self.reg_pts) >= 3:
|
|
114
104
|
print("Polygon Counter Initiated.")
|
|
115
|
-
self.reg_pts = reg_pts
|
|
116
105
|
self.counting_region = Polygon(self.reg_pts)
|
|
117
106
|
else:
|
|
118
107
|
print("Invalid Region points provided, region_points must be 2 for lines or >= 3 for polygons.")
|
|
119
108
|
print("Using Line Counter Now")
|
|
120
109
|
self.counting_region = LineString(self.reg_pts)
|
|
121
110
|
|
|
122
|
-
self.names = classes_names
|
|
123
|
-
self.track_color = track_color
|
|
124
|
-
self.count_txt_color = count_txt_color
|
|
125
|
-
self.count_bg_color = count_bg_color
|
|
126
|
-
self.region_color = count_reg_color
|
|
127
|
-
self.region_thickness = region_thickness
|
|
128
|
-
self.line_dist_thresh = line_dist_thresh
|
|
129
|
-
self.cls_txtdisplay_gap = cls_txtdisplay_gap
|
|
130
|
-
|
|
131
111
|
def mouse_event_for_region(self, event, x, y, flags, params):
|
|
132
112
|
"""
|
|
133
|
-
|
|
113
|
+
Handles mouse events for defining and moving the counting region in a real-time video stream.
|
|
134
114
|
|
|
135
115
|
Args:
|
|
136
116
|
event (int): The type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN, etc.).
|
|
137
117
|
x (int): The x-coordinate of the mouse pointer.
|
|
138
118
|
y (int): The y-coordinate of the mouse pointer.
|
|
139
|
-
flags (int): Any
|
|
140
|
-
|
|
141
|
-
params (dict): Additional parameters you may want to pass to the function.
|
|
119
|
+
flags (int): Any associated event flags (e.g., cv2.EVENT_FLAG_CTRLKEY, cv2.EVENT_FLAG_SHIFTKEY, etc.).
|
|
120
|
+
params (dict): Additional parameters for the function.
|
|
142
121
|
"""
|
|
143
122
|
if event == cv2.EVENT_LBUTTONDOWN:
|
|
144
123
|
for i, point in enumerate(self.reg_pts):
|
|
@@ -240,11 +219,11 @@ class ObjectCounter:
|
|
|
240
219
|
else:
|
|
241
220
|
labels_dict[str.capitalize(key)] = f"IN {value['IN']} OUT {value['OUT']}"
|
|
242
221
|
|
|
243
|
-
if labels_dict
|
|
222
|
+
if labels_dict:
|
|
244
223
|
self.annotator.display_analytics(self.im0, labels_dict, self.count_txt_color, self.count_bg_color, 10)
|
|
245
224
|
|
|
246
225
|
def display_frames(self):
|
|
247
|
-
"""
|
|
226
|
+
"""Displays the current frame with annotations and regions in a window."""
|
|
248
227
|
if self.env_check:
|
|
249
228
|
cv2.namedWindow(self.window_name)
|
|
250
229
|
if len(self.reg_pts) == 4: # only add mouse event If user drawn region
|
|
@@ -271,4 +250,5 @@ class ObjectCounter:
|
|
|
271
250
|
|
|
272
251
|
|
|
273
252
|
if __name__ == "__main__":
|
|
274
|
-
|
|
253
|
+
classes_names = {0: "person", 1: "car"} # example class names
|
|
254
|
+
ObjectCounter(classes_names)
|
|
@@ -8,17 +8,22 @@ from PIL import Image, ImageTk
|
|
|
8
8
|
from ultralytics.utils.checks import check_imshow, check_requirements
|
|
9
9
|
from ultralytics.utils.plotting import Annotator
|
|
10
10
|
|
|
11
|
-
check_requirements("tkinter")
|
|
12
|
-
import tkinter as tk
|
|
13
|
-
|
|
14
11
|
|
|
15
12
|
class ParkingPtsSelection:
|
|
16
13
|
def __init__(self, master):
|
|
17
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Initializes the UI for selecting parking zone points in a tkinter window.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
master (tk.Tk): The main tkinter window object.
|
|
19
|
+
"""
|
|
20
|
+
check_requirements("tkinter")
|
|
21
|
+
import tkinter as tk
|
|
22
|
+
|
|
18
23
|
self.master = master
|
|
19
24
|
master.title("Ultralytics Parking Zones Points Selector")
|
|
20
25
|
|
|
21
|
-
#
|
|
26
|
+
# Disable window resizing
|
|
22
27
|
master.resizable(False, False)
|
|
23
28
|
|
|
24
29
|
# Setup canvas for image display
|
|
@@ -36,7 +41,6 @@ class ParkingPtsSelection:
|
|
|
36
41
|
self.image_path = None
|
|
37
42
|
self.image = None
|
|
38
43
|
self.canvas_image = None
|
|
39
|
-
self.canvas = None
|
|
40
44
|
self.bounding_boxes = []
|
|
41
45
|
self.current_box = []
|
|
42
46
|
self.img_width = 0
|
|
@@ -101,7 +105,6 @@ class ParkingPtsSelection:
|
|
|
101
105
|
Args:
|
|
102
106
|
box (list): Bounding box data
|
|
103
107
|
"""
|
|
104
|
-
|
|
105
108
|
for i in range(4):
|
|
106
109
|
x1, y1 = box[i]
|
|
107
110
|
x2, y2 = box[(i + 1) % 4]
|
|
@@ -151,6 +154,17 @@ class ParkingManagement:
|
|
|
151
154
|
available_region_color=(0, 0, 255),
|
|
152
155
|
margin=10,
|
|
153
156
|
):
|
|
157
|
+
"""
|
|
158
|
+
Initializes the parking management system with a YOLOv8 model and visualization settings.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
model_path (str): Path to the YOLOv8 model.
|
|
162
|
+
txt_color (tuple): RGB color tuple for text.
|
|
163
|
+
bg_color (tuple): RGB color tuple for background.
|
|
164
|
+
occupied_region_color (tuple): RGB color tuple for occupied regions.
|
|
165
|
+
available_region_color (tuple): RGB color tuple for available regions.
|
|
166
|
+
margin (int): Margin for text display.
|
|
167
|
+
"""
|
|
154
168
|
# Model path and initialization
|
|
155
169
|
self.model_path = model_path
|
|
156
170
|
self.model = self.load_model()
|
|
@@ -166,7 +180,7 @@ class ParkingManagement:
|
|
|
166
180
|
self.available_region_color = available_region_color
|
|
167
181
|
|
|
168
182
|
self.window_name = "Ultralytics YOLOv8 Parking Management System"
|
|
169
|
-
# Check if environment
|
|
183
|
+
# Check if environment supports imshow
|
|
170
184
|
self.env_check = check_imshow(warn=True)
|
|
171
185
|
|
|
172
186
|
def load_model(self):
|
|
@@ -184,7 +198,6 @@ class ParkingManagement:
|
|
|
184
198
|
Args:
|
|
185
199
|
json_file (str): file that have all parking slot points
|
|
186
200
|
"""
|
|
187
|
-
|
|
188
201
|
with open(json_file, "r") as json_file:
|
|
189
202
|
return json.load(json_file)
|
|
190
203
|
|