ultralytics 8.2.17__py3-none-any.whl → 8.2.19__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/cfg/datasets/signature.yaml +20 -0
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +22 -22
- ultralytics/cfg/models/v9/yolov9c.yaml +22 -22
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +34 -34
- ultralytics/cfg/models/v9/yolov9e.yaml +34 -34
- ultralytics/data/build.py +1 -1
- ultralytics/engine/exporter.py +3 -0
- 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.19.dist-info}/METADATA +1 -1
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.19.dist-info}/RECORD +23 -22
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.19.dist-info}/LICENSE +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.19.dist-info}/WHEEL +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.19.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.2.17.dist-info → ultralytics-8.2.19.dist-info}/top_level.txt +0 -0
|
@@ -9,94 +9,78 @@ from ultralytics.utils.plotting import Annotator, colors
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DistanceCalculation:
|
|
12
|
-
"""A class to calculate distance between two objects in real-time video stream based on their tracks."""
|
|
12
|
+
"""A class to calculate distance between two objects in a real-time video stream based on their tracks."""
|
|
13
13
|
|
|
14
|
-
def __init__(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
names,
|
|
17
|
+
pixels_per_meter=10,
|
|
18
|
+
view_img=False,
|
|
19
|
+
line_thickness=2,
|
|
20
|
+
line_color=(255, 255, 0),
|
|
21
|
+
centroid_color=(255, 0, 255),
|
|
22
|
+
):
|
|
17
23
|
"""
|
|
24
|
+
Initializes the DistanceCalculation class with the given parameters.
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
Args:
|
|
27
|
+
names (dict): Dictionary mapping class indices to class names.
|
|
28
|
+
pixels_per_meter (int, optional): Conversion factor from pixels to meters. Defaults to 10.
|
|
29
|
+
view_img (bool, optional): Flag to indicate if the video stream should be displayed. Defaults to False.
|
|
30
|
+
line_thickness (int, optional): Thickness of the lines drawn on the image. Defaults to 2.
|
|
31
|
+
line_color (tuple, optional): Color of the lines drawn on the image (BGR format). Defaults to (255, 255, 0).
|
|
32
|
+
centroid_color (tuple, optional): Color of the centroids drawn (BGR format). Defaults to (255, 0, 255).
|
|
33
|
+
"""
|
|
34
|
+
# Visual & image information
|
|
20
35
|
self.im0 = None
|
|
21
36
|
self.annotator = None
|
|
22
|
-
self.view_img =
|
|
23
|
-
self.line_color =
|
|
24
|
-
self.centroid_color =
|
|
37
|
+
self.view_img = view_img
|
|
38
|
+
self.line_color = line_color
|
|
39
|
+
self.centroid_color = centroid_color
|
|
25
40
|
|
|
26
|
-
#
|
|
41
|
+
# Prediction & tracking information
|
|
27
42
|
self.clss = None
|
|
28
|
-
self.names =
|
|
43
|
+
self.names = names
|
|
29
44
|
self.boxes = None
|
|
30
|
-
self.line_thickness =
|
|
45
|
+
self.line_thickness = line_thickness
|
|
31
46
|
self.trk_ids = None
|
|
32
47
|
|
|
33
48
|
# Distance calculation information
|
|
34
49
|
self.centroids = []
|
|
35
|
-
self.pixel_per_meter =
|
|
50
|
+
self.pixel_per_meter = pixels_per_meter
|
|
36
51
|
|
|
37
|
-
# Mouse event
|
|
52
|
+
# Mouse event information
|
|
38
53
|
self.left_mouse_count = 0
|
|
39
54
|
self.selected_boxes = {}
|
|
40
55
|
|
|
41
|
-
# Check if environment
|
|
56
|
+
# Check if environment supports imshow
|
|
42
57
|
self.env_check = check_imshow(warn=True)
|
|
43
58
|
|
|
44
|
-
def set_args(
|
|
45
|
-
self,
|
|
46
|
-
names,
|
|
47
|
-
pixels_per_meter=10,
|
|
48
|
-
view_img=False,
|
|
49
|
-
line_thickness=2,
|
|
50
|
-
line_color=(255, 255, 0),
|
|
51
|
-
centroid_color=(255, 0, 255),
|
|
52
|
-
):
|
|
53
|
-
"""
|
|
54
|
-
Configures the distance calculation and display parameters.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
names (dict): object detection classes names
|
|
58
|
-
pixels_per_meter (int): Number of pixels in meter
|
|
59
|
-
view_img (bool): Flag indicating frame display
|
|
60
|
-
line_thickness (int): Line thickness for bounding boxes.
|
|
61
|
-
line_color (RGB): color of centroids line
|
|
62
|
-
centroid_color (RGB): colors of bbox centroids
|
|
63
|
-
"""
|
|
64
|
-
self.names = names
|
|
65
|
-
self.pixel_per_meter = pixels_per_meter
|
|
66
|
-
self.view_img = view_img
|
|
67
|
-
self.line_thickness = line_thickness
|
|
68
|
-
self.line_color = line_color
|
|
69
|
-
self.centroid_color = centroid_color
|
|
70
|
-
|
|
71
59
|
def mouse_event_for_distance(self, event, x, y, flags, param):
|
|
72
60
|
"""
|
|
73
|
-
|
|
61
|
+
Handles mouse events to select regions in a real-time video stream.
|
|
74
62
|
|
|
75
63
|
Args:
|
|
76
|
-
event (int):
|
|
77
|
-
x (int):
|
|
78
|
-
y (int):
|
|
79
|
-
flags (int):
|
|
80
|
-
|
|
81
|
-
param (dict): Additional parameters you may want to pass to the function.
|
|
64
|
+
event (int): Type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN, etc.).
|
|
65
|
+
x (int): X-coordinate of the mouse pointer.
|
|
66
|
+
y (int): Y-coordinate of the mouse pointer.
|
|
67
|
+
flags (int): Flags associated with the event (e.g., cv2.EVENT_FLAG_CTRLKEY, cv2.EVENT_FLAG_SHIFTKEY, etc.).
|
|
68
|
+
param (dict): Additional parameters passed to the function.
|
|
82
69
|
"""
|
|
83
|
-
global selected_boxes
|
|
84
|
-
global left_mouse_count
|
|
85
70
|
if event == cv2.EVENT_LBUTTONDOWN:
|
|
86
71
|
self.left_mouse_count += 1
|
|
87
72
|
if self.left_mouse_count <= 2:
|
|
88
73
|
for box, track_id in zip(self.boxes, self.trk_ids):
|
|
89
74
|
if box[0] < x < box[2] and box[1] < y < box[3] and track_id not in self.selected_boxes:
|
|
90
|
-
self.selected_boxes[track_id] = []
|
|
91
75
|
self.selected_boxes[track_id] = box
|
|
92
76
|
|
|
93
|
-
|
|
77
|
+
elif event == cv2.EVENT_RBUTTONDOWN:
|
|
94
78
|
self.selected_boxes = {}
|
|
95
79
|
self.left_mouse_count = 0
|
|
96
80
|
|
|
97
81
|
def extract_tracks(self, tracks):
|
|
98
82
|
"""
|
|
99
|
-
Extracts results from the provided data.
|
|
83
|
+
Extracts tracking results from the provided data.
|
|
100
84
|
|
|
101
85
|
Args:
|
|
102
86
|
tracks (list): List of tracks obtained from the object tracking process.
|
|
@@ -105,55 +89,65 @@ class DistanceCalculation:
|
|
|
105
89
|
self.clss = tracks[0].boxes.cls.cpu().tolist()
|
|
106
90
|
self.trk_ids = tracks[0].boxes.id.int().cpu().tolist()
|
|
107
91
|
|
|
108
|
-
|
|
92
|
+
@staticmethod
|
|
93
|
+
def calculate_centroid(box):
|
|
109
94
|
"""
|
|
110
|
-
|
|
95
|
+
Calculates the centroid of a bounding box.
|
|
111
96
|
|
|
112
97
|
Args:
|
|
113
|
-
box (list): Bounding box
|
|
98
|
+
box (list): Bounding box coordinates [x1, y1, x2, y2].
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
(tuple): Centroid coordinates (x, y).
|
|
114
102
|
"""
|
|
115
103
|
return int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2)
|
|
116
104
|
|
|
117
105
|
def calculate_distance(self, centroid1, centroid2):
|
|
118
106
|
"""
|
|
119
|
-
|
|
107
|
+
Calculates the distance between two centroids.
|
|
120
108
|
|
|
121
109
|
Args:
|
|
122
|
-
centroid1 (
|
|
123
|
-
centroid2 (
|
|
110
|
+
centroid1 (tuple): Coordinates of the first centroid (x, y).
|
|
111
|
+
centroid2 (tuple): Coordinates of the second centroid (x, y).
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
(tuple): Distance in meters and millimeters.
|
|
124
115
|
"""
|
|
125
116
|
pixel_distance = math.sqrt((centroid1[0] - centroid2[0]) ** 2 + (centroid1[1] - centroid2[1]) ** 2)
|
|
126
|
-
|
|
117
|
+
distance_m = pixel_distance / self.pixel_per_meter
|
|
118
|
+
distance_mm = distance_m * 1000
|
|
119
|
+
return distance_m, distance_mm
|
|
127
120
|
|
|
128
121
|
def start_process(self, im0, tracks):
|
|
129
122
|
"""
|
|
130
|
-
|
|
123
|
+
Processes the video frame and calculates the distance between two bounding boxes.
|
|
131
124
|
|
|
132
125
|
Args:
|
|
133
|
-
im0 (
|
|
126
|
+
im0 (ndarray): The image frame.
|
|
134
127
|
tracks (list): List of tracks obtained from the object tracking process.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
(ndarray): The processed image frame.
|
|
135
131
|
"""
|
|
136
132
|
self.im0 = im0
|
|
137
133
|
if tracks[0].boxes.id is None:
|
|
138
134
|
if self.view_img:
|
|
139
135
|
self.display_frames()
|
|
140
|
-
return
|
|
141
|
-
self.extract_tracks(tracks)
|
|
136
|
+
return im0
|
|
142
137
|
|
|
143
|
-
self.
|
|
138
|
+
self.extract_tracks(tracks)
|
|
139
|
+
self.annotator = Annotator(self.im0, line_width=self.line_thickness)
|
|
144
140
|
|
|
145
141
|
for box, cls, track_id in zip(self.boxes, self.clss, self.trk_ids):
|
|
146
142
|
self.annotator.box_label(box, color=colors(int(cls), True), label=self.names[int(cls)])
|
|
147
143
|
|
|
148
144
|
if len(self.selected_boxes) == 2:
|
|
149
|
-
for trk_id
|
|
145
|
+
for trk_id in self.selected_boxes.keys():
|
|
150
146
|
if trk_id == track_id:
|
|
151
147
|
self.selected_boxes[track_id] = box
|
|
152
148
|
|
|
153
149
|
if len(self.selected_boxes) == 2:
|
|
154
|
-
for trk_id
|
|
155
|
-
centroid = self.calculate_centroid(self.selected_boxes[trk_id])
|
|
156
|
-
self.centroids.append(centroid)
|
|
150
|
+
self.centroids = [self.calculate_centroid(self.selected_boxes[trk_id]) for trk_id in self.selected_boxes]
|
|
157
151
|
|
|
158
152
|
distance_m, distance_mm = self.calculate_distance(self.centroids[0], self.centroids[1])
|
|
159
153
|
self.annotator.plot_distance_and_line(
|
|
@@ -168,7 +162,7 @@ class DistanceCalculation:
|
|
|
168
162
|
return im0
|
|
169
163
|
|
|
170
164
|
def display_frames(self):
|
|
171
|
-
"""
|
|
165
|
+
"""Displays the current frame with annotations."""
|
|
172
166
|
cv2.namedWindow("Ultralytics Distance Estimation")
|
|
173
167
|
cv2.setMouseCallback("Ultralytics Distance Estimation", self.mouse_event_for_distance)
|
|
174
168
|
cv2.imshow("Ultralytics Distance Estimation", self.im0)
|
|
@@ -178,4 +172,5 @@ class DistanceCalculation:
|
|
|
178
172
|
|
|
179
173
|
|
|
180
174
|
if __name__ == "__main__":
|
|
181
|
-
|
|
175
|
+
names = {0: "person", 1: "car"} # example class names
|
|
176
|
+
distance_calculation = DistanceCalculation(names)
|
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)
|