ultralytics 8.3.86__py3-none-any.whl → 8.3.88__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.
- tests/test_solutions.py +47 -39
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +58 -55
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +1 -1
- ultralytics/cfg/models/11/yolo11-cls.yaml +6 -6
- ultralytics/data/augment.py +2 -2
- ultralytics/data/loaders.py +1 -1
- ultralytics/engine/exporter.py +1 -1
- ultralytics/engine/results.py +76 -41
- ultralytics/engine/trainer.py +11 -5
- ultralytics/engine/tuner.py +3 -2
- ultralytics/nn/autobackend.py +1 -1
- ultralytics/nn/tasks.py +1 -1
- ultralytics/solutions/__init__.py +14 -6
- ultralytics/solutions/ai_gym.py +39 -28
- ultralytics/solutions/analytics.py +22 -18
- ultralytics/solutions/distance_calculation.py +25 -25
- ultralytics/solutions/heatmap.py +40 -38
- ultralytics/solutions/instance_segmentation.py +69 -0
- ultralytics/solutions/object_blurrer.py +89 -0
- ultralytics/solutions/object_counter.py +35 -33
- ultralytics/solutions/object_cropper.py +84 -0
- ultralytics/solutions/parking_management.py +40 -13
- ultralytics/solutions/queue_management.py +20 -39
- ultralytics/solutions/region_counter.py +54 -51
- ultralytics/solutions/security_alarm.py +40 -30
- ultralytics/solutions/solutions.py +594 -16
- ultralytics/solutions/speed_estimation.py +34 -31
- ultralytics/solutions/streamlit_inference.py +34 -28
- ultralytics/solutions/trackzone.py +29 -18
- ultralytics/solutions/vision_eye.py +69 -0
- ultralytics/trackers/utils/kalman_filter.py +23 -23
- ultralytics/utils/__init__.py +2 -3
- ultralytics/utils/callbacks/comet.py +37 -5
- ultralytics/utils/instance.py +3 -3
- ultralytics/utils/plotting.py +0 -414
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/METADATA +8 -8
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/RECORD +42 -38
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/WHEEL +1 -1
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/LICENSE +0 -0
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.3.86.dist-info → ultralytics-8.3.88.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
2
|
+
|
3
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
|
4
|
+
from ultralytics.utils.plotting import colors
|
5
|
+
|
6
|
+
|
7
|
+
class InstanceSegmentation(BaseSolution):
|
8
|
+
"""
|
9
|
+
A class to manage instance segmentation in images or video streams.
|
10
|
+
|
11
|
+
This class extends the BaseSolution class and provides functionality for performing instance segmentation, including
|
12
|
+
drawing segmented masks with bounding boxes and labels.
|
13
|
+
|
14
|
+
Attributes:
|
15
|
+
model (str): The segmentation model to use for inference.
|
16
|
+
|
17
|
+
Methods:
|
18
|
+
process: Processes the input image to perform instance segmentation and annotate results.
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
>>> segmenter = InstanceSegmentation()
|
22
|
+
>>> frame = cv2.imread("frame.jpg")
|
23
|
+
>>> results = segmenter.segment(frame)
|
24
|
+
>>> print(f"Total segmented instances: {results['total_tracks']}")
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, **kwargs):
|
28
|
+
"""
|
29
|
+
Initializes the InstanceSegmentation class for detecting and annotating segmented instances.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
**kwargs (Any): Keyword arguments passed to the BaseSolution parent class.
|
33
|
+
model (str): Model name or path, defaults to "yolo11n-seg.pt".
|
34
|
+
"""
|
35
|
+
kwargs["model"] = kwargs.get("model", "yolo11n-seg.pt")
|
36
|
+
super().__init__(**kwargs)
|
37
|
+
|
38
|
+
def process(self, im0):
|
39
|
+
"""
|
40
|
+
Performs instance segmentation on the input image and annotates the results.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
im0 (numpy.ndarray): The input image for segmentation.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
(SolutionResults): Object containing the annotated image and total number of tracked instances.
|
47
|
+
|
48
|
+
Examples:
|
49
|
+
>>> segmenter = InstanceSegmentation()
|
50
|
+
>>> frame = cv2.imread("image.jpg")
|
51
|
+
>>> summary = segmenter.segment(frame)
|
52
|
+
>>> print(summary)
|
53
|
+
"""
|
54
|
+
self.extract_tracks(im0) # Extract tracks (bounding boxes, classes, and masks)
|
55
|
+
annotator = SolutionAnnotator(im0, self.line_width)
|
56
|
+
|
57
|
+
# Iterate over detected classes, track IDs, and segmentation masks
|
58
|
+
if self.masks is None:
|
59
|
+
self.LOGGER.warning("⚠️ No masks detected! Ensure you're using a supported Ultralytics segmentation model.")
|
60
|
+
else:
|
61
|
+
for cls, t_id, mask in zip(self.clss, self.track_ids, self.masks):
|
62
|
+
# Annotate the image with segmentation mask, mask color, and label
|
63
|
+
annotator.segmentation_mask(mask=mask, mask_color=colors(t_id, True), label=self.names[cls])
|
64
|
+
|
65
|
+
plot_im = annotator.result()
|
66
|
+
self.display_output(plot_im) # Display the annotated output using the base class function
|
67
|
+
|
68
|
+
# Return SolutionResults
|
69
|
+
return SolutionResults(plot_im=plot_im, total_tracks=len(self.track_ids))
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
|
+
|
3
|
+
|
4
|
+
import cv2
|
5
|
+
|
6
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
|
7
|
+
from ultralytics.utils import LOGGER
|
8
|
+
from ultralytics.utils.plotting import colors
|
9
|
+
|
10
|
+
|
11
|
+
class ObjectBlurrer(BaseSolution):
|
12
|
+
"""
|
13
|
+
A class to manage the blurring of detected objects in a real-time video stream.
|
14
|
+
|
15
|
+
This class extends the BaseSolution class and provides functionality for blurring objects based on detected bounding
|
16
|
+
boxes. The blurred areas are updated directly in the input image, allowing for privacy preservation or other effects.
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
blur_ratio (int): The intensity of the blur effect applied to detected objects (higher values create more blur).
|
20
|
+
iou (float): Intersection over Union threshold for object detection.
|
21
|
+
conf (float): Confidence threshold for object detection.
|
22
|
+
|
23
|
+
Methods:
|
24
|
+
process: Applies a blurring effect to detected objects in the input image.
|
25
|
+
extract_tracks: Extracts tracking information from detected objects.
|
26
|
+
display_output: Displays the processed output image.
|
27
|
+
|
28
|
+
Examples:
|
29
|
+
>>> blurrer = ObjectBlurrer()
|
30
|
+
>>> frame = cv2.imread("frame.jpg")
|
31
|
+
>>> processed_results = blurrer.process(frame)
|
32
|
+
>>> print(f"Total blurred objects: {processed_results.total_tracks}")
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, **kwargs):
|
36
|
+
"""
|
37
|
+
Initializes the ObjectBlurrer class for applying a blur effect to objects detected in video streams or images.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
**kwargs (Any): Keyword arguments passed to the parent class and for configuration.
|
41
|
+
blur_ratio (float): Intensity of the blur effect (0.1-1.0, default=0.5).
|
42
|
+
"""
|
43
|
+
super().__init__(**kwargs)
|
44
|
+
blur_ratio = kwargs.get("blur_ratio", 0.5)
|
45
|
+
if blur_ratio < 0.1:
|
46
|
+
LOGGER.warning("⚠️ blur ratio cannot be less than 0.1, updating it to default value 0.5")
|
47
|
+
blur_ratio = 0.5
|
48
|
+
self.blur_ratio = int(blur_ratio * 100)
|
49
|
+
|
50
|
+
def process(self, im0):
|
51
|
+
"""
|
52
|
+
Applies a blurring effect to detected objects in the input image.
|
53
|
+
|
54
|
+
This method extracts tracking information, applies blur to regions corresponding to detected objects,
|
55
|
+
and annotates the image with bounding boxes.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
im0 (numpy.ndarray): The input image containing detected objects.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
(SolutionResults): Object containing the processed image and number of tracked objects.
|
62
|
+
- plot_im (numpy.ndarray): The annotated output image with blurred objects.
|
63
|
+
- total_tracks (int): The total number of tracked objects in the frame.
|
64
|
+
|
65
|
+
Examples:
|
66
|
+
>>> blurrer = ObjectBlurrer()
|
67
|
+
>>> frame = cv2.imread("image.jpg")
|
68
|
+
>>> results = blurrer.process(frame)
|
69
|
+
>>> print(f"Blurred {results.total_tracks} objects")
|
70
|
+
"""
|
71
|
+
self.extract_tracks(im0) # Extract tracks
|
72
|
+
annotator = SolutionAnnotator(im0, self.line_width)
|
73
|
+
|
74
|
+
# Iterate over bounding boxes and classes
|
75
|
+
for box, cls in zip(self.boxes, self.clss):
|
76
|
+
# Crop and blur the detected object
|
77
|
+
blur_obj = cv2.blur(
|
78
|
+
im0[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])],
|
79
|
+
(self.blur_ratio, self.blur_ratio),
|
80
|
+
)
|
81
|
+
# Update the blurred area in the original image
|
82
|
+
im0[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] = blur_obj
|
83
|
+
annotator.box_label(box, label=self.names[cls], color=colors(cls, True)) # Annotate bounding box
|
84
|
+
|
85
|
+
plot_im = annotator.result()
|
86
|
+
self.display_output(plot_im) # Display the output using the base class function
|
87
|
+
|
88
|
+
# Return a SolutionResults
|
89
|
+
return SolutionResults(plot_im=plot_im, total_tracks=len(self.track_ids))
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
-
from ultralytics.solutions.solutions import BaseSolution
|
4
|
-
from ultralytics.utils.plotting import
|
3
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
|
4
|
+
from ultralytics.utils.plotting import colors
|
5
5
|
|
6
6
|
|
7
7
|
class ObjectCounter(BaseSolution):
|
@@ -24,12 +24,12 @@ class ObjectCounter(BaseSolution):
|
|
24
24
|
count_objects: Counts objects within a polygonal or linear region.
|
25
25
|
store_classwise_counts: Initializes class-wise counts if not already present.
|
26
26
|
display_counts: Displays object counts on the frame.
|
27
|
-
|
27
|
+
process: Processes input data (frames or object tracks) and updates counts.
|
28
28
|
|
29
29
|
Examples:
|
30
30
|
>>> counter = ObjectCounter()
|
31
31
|
>>> frame = cv2.imread("frame.jpg")
|
32
|
-
>>>
|
32
|
+
>>> results = counter.process(frame)
|
33
33
|
>>> print(f"Inward count: {counter.in_count}, Outward count: {counter.out_count}")
|
34
34
|
"""
|
35
35
|
|
@@ -41,7 +41,7 @@ class ObjectCounter(BaseSolution):
|
|
41
41
|
self.out_count = 0 # Counter for objects moving outward
|
42
42
|
self.counted_ids = [] # List of IDs of objects that have been counted
|
43
43
|
self.classwise_counts = {} # Dictionary for counts, categorized by object class
|
44
|
-
self.region_initialized = False #
|
44
|
+
self.region_initialized = False # Flag indicating whether the region has been initialized
|
45
45
|
|
46
46
|
self.show_in = self.CFG["show_in"]
|
47
47
|
self.show_out = self.CFG["show_out"]
|
@@ -51,7 +51,7 @@ class ObjectCounter(BaseSolution):
|
|
51
51
|
Counts objects within a polygonal or linear region based on their tracks.
|
52
52
|
|
53
53
|
Args:
|
54
|
-
current_centroid (Tuple[float, float]): Current centroid
|
54
|
+
current_centroid (Tuple[float, float]): Current centroid coordinates (x, y) in the current frame.
|
55
55
|
track_id (int): Unique identifier for the tracked object.
|
56
56
|
prev_position (Tuple[float, float]): Last frame position coordinates (x, y) of the track.
|
57
57
|
cls (int): Class index for classwise count updates.
|
@@ -60,10 +60,10 @@ class ObjectCounter(BaseSolution):
|
|
60
60
|
>>> counter = ObjectCounter()
|
61
61
|
>>> track_line = {1: [100, 200], 2: [110, 210], 3: [120, 220]}
|
62
62
|
>>> box = [130, 230, 150, 250]
|
63
|
-
>>>
|
64
|
-
>>>
|
65
|
-
>>>
|
66
|
-
>>> counter.count_objects(
|
63
|
+
>>> track_id_num = 1
|
64
|
+
>>> previous_position = (120, 220)
|
65
|
+
>>> class_to_count = 0 # In COCO model, class 0 = person
|
66
|
+
>>> counter.count_objects((140, 240), track_id_num, previous_position, class_to_count)
|
67
67
|
"""
|
68
68
|
if prev_position is None or track_id in self.counted_ids:
|
69
69
|
return
|
@@ -101,10 +101,10 @@ class ObjectCounter(BaseSolution):
|
|
101
101
|
and current_centroid[0] > prev_position[0]
|
102
102
|
or region_width >= region_height
|
103
103
|
and current_centroid[1] > prev_position[1]
|
104
|
-
): # Moving right
|
104
|
+
): # Moving right or downward
|
105
105
|
self.in_count += 1
|
106
106
|
self.classwise_counts[self.names[cls]]["IN"] += 1
|
107
|
-
else: # Moving left
|
107
|
+
else: # Moving left or upward
|
108
108
|
self.out_count += 1
|
109
109
|
self.classwise_counts[self.names[cls]]["OUT"] += 1
|
110
110
|
self.counted_ids.append(track_id)
|
@@ -116,9 +116,6 @@ class ObjectCounter(BaseSolution):
|
|
116
116
|
Args:
|
117
117
|
cls (int): Class index for classwise count updates.
|
118
118
|
|
119
|
-
This method ensures that the 'classwise_counts' dictionary contains an entry for the specified class,
|
120
|
-
initializing 'IN' and 'OUT' counts to zero if the class is not already present.
|
121
|
-
|
122
119
|
Examples:
|
123
120
|
>>> counter = ObjectCounter()
|
124
121
|
>>> counter.store_classwise_counts(0) # Initialize counts for class index 0
|
@@ -128,12 +125,12 @@ class ObjectCounter(BaseSolution):
|
|
128
125
|
if self.names[cls] not in self.classwise_counts:
|
129
126
|
self.classwise_counts[self.names[cls]] = {"IN": 0, "OUT": 0}
|
130
127
|
|
131
|
-
def display_counts(self,
|
128
|
+
def display_counts(self, plot_im):
|
132
129
|
"""
|
133
130
|
Displays object counts on the input image or frame.
|
134
131
|
|
135
132
|
Args:
|
136
|
-
|
133
|
+
plot_im (numpy.ndarray): The image or frame to display counts on.
|
137
134
|
|
138
135
|
Examples:
|
139
136
|
>>> counter = ObjectCounter()
|
@@ -146,11 +143,10 @@ class ObjectCounter(BaseSolution):
|
|
146
143
|
for key, value in self.classwise_counts.items()
|
147
144
|
if value["IN"] != 0 or value["OUT"] != 0
|
148
145
|
}
|
149
|
-
|
150
146
|
if labels_dict:
|
151
|
-
self.annotator.display_analytics(
|
147
|
+
self.annotator.display_analytics(plot_im, labels_dict, (104, 31, 17), (255, 255, 255), 10)
|
152
148
|
|
153
|
-
def
|
149
|
+
def process(self, im0):
|
154
150
|
"""
|
155
151
|
Processes input data (frames or object tracks) and updates object counts.
|
156
152
|
|
@@ -161,19 +157,21 @@ class ObjectCounter(BaseSolution):
|
|
161
157
|
im0 (numpy.ndarray): The input image or frame to be processed.
|
162
158
|
|
163
159
|
Returns:
|
164
|
-
(
|
160
|
+
(SolutionResults): Contains processed image `im0`, 'in_count' (int, count of objects entering the region),
|
161
|
+
'out_count' (int, count of objects exiting the region), 'classwise_count' (Dict, per-class object count),
|
162
|
+
and 'total_tracks' (int, total number of tracked objects).
|
165
163
|
|
166
164
|
Examples:
|
167
165
|
>>> counter = ObjectCounter()
|
168
166
|
>>> frame = cv2.imread("path/to/image.jpg")
|
169
|
-
>>>
|
167
|
+
>>> results = counter.process(frame)
|
170
168
|
"""
|
171
169
|
if not self.region_initialized:
|
172
170
|
self.initialize_region()
|
173
171
|
self.region_initialized = True
|
174
172
|
|
175
|
-
self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
|
176
173
|
self.extract_tracks(im0) # Extract tracks
|
174
|
+
self.annotator = SolutionAnnotator(im0, line_width=self.line_width) # Initialize annotator
|
177
175
|
|
178
176
|
self.annotator.draw_region(
|
179
177
|
reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2
|
@@ -184,20 +182,24 @@ class ObjectCounter(BaseSolution):
|
|
184
182
|
# Draw bounding box and counting region
|
185
183
|
self.annotator.box_label(box, label=self.names[cls], color=colors(cls, True))
|
186
184
|
self.store_tracking_history(track_id, box) # Store track history
|
187
|
-
self.store_classwise_counts(cls) #
|
185
|
+
self.store_classwise_counts(cls) # Store classwise counts in dict
|
188
186
|
|
189
|
-
# Draw tracks of objects
|
190
|
-
self.annotator.draw_centroid_and_tracks(
|
191
|
-
self.track_line, color=colors(int(cls), True), track_thickness=self.line_width
|
192
|
-
)
|
193
187
|
current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
|
194
|
-
#
|
188
|
+
# Store previous position of track for object counting
|
195
189
|
prev_position = None
|
196
190
|
if len(self.track_history[track_id]) > 1:
|
197
191
|
prev_position = self.track_history[track_id][-2]
|
198
192
|
self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting
|
199
193
|
|
200
|
-
self.
|
201
|
-
self.
|
202
|
-
|
203
|
-
|
194
|
+
plot_im = self.annotator.result()
|
195
|
+
self.display_counts(plot_im) # Display the counts on the frame
|
196
|
+
self.display_output(plot_im) # Display output with base class function
|
197
|
+
|
198
|
+
# Return SolutionResults
|
199
|
+
return SolutionResults(
|
200
|
+
plot_im=plot_im,
|
201
|
+
in_count=self.in_count,
|
202
|
+
out_count=self.out_count,
|
203
|
+
classwise_count=self.classwise_counts,
|
204
|
+
total_tracks=len(self.track_ids),
|
205
|
+
)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
|
+
|
3
|
+
import os
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionResults
|
7
|
+
from ultralytics.utils.plotting import save_one_box
|
8
|
+
|
9
|
+
|
10
|
+
class ObjectCropper(BaseSolution):
|
11
|
+
"""
|
12
|
+
A class to manage the cropping of detected objects in a real-time video stream or images.
|
13
|
+
|
14
|
+
This class extends the BaseSolution class and provides functionality for cropping objects based on detected bounding
|
15
|
+
boxes. The cropped images are saved to a specified directory for further analysis or usage.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
crop_dir (str): Directory where cropped object images are stored.
|
19
|
+
crop_idx (int): Counter for the total number of cropped objects.
|
20
|
+
iou (float): IoU (Intersection over Union) threshold for non-maximum suppression.
|
21
|
+
conf (float): Confidence threshold for filtering detections.
|
22
|
+
|
23
|
+
Methods:
|
24
|
+
process: Crops detected objects from the input image and saves them to the output directory.
|
25
|
+
|
26
|
+
Examples:
|
27
|
+
>>> cropper = ObjectCropper()
|
28
|
+
>>> frame = cv2.imread("frame.jpg")
|
29
|
+
>>> processed_results = cropper.process(frame)
|
30
|
+
>>> print(f"Total cropped objects: {cropper.crop_idx}")
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self, **kwargs):
|
34
|
+
"""
|
35
|
+
Initializes the ObjectCropper class for cropping objects from detected bounding boxes.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
**kwargs (Any): Keyword arguments passed to the parent class and used for configuration.
|
39
|
+
crop_dir (str): Path to the directory for saving cropped object images.
|
40
|
+
"""
|
41
|
+
super().__init__(**kwargs)
|
42
|
+
|
43
|
+
self.crop_dir = kwargs.get("crop_dir", "cropped-detections") # Directory for storing cropped detections
|
44
|
+
if not os.path.exists(self.crop_dir):
|
45
|
+
os.mkdir(self.crop_dir) # Create directory if it does not exist
|
46
|
+
if self.CFG["show"]:
|
47
|
+
self.LOGGER.info(
|
48
|
+
f"⚠️ show=True disabled for crop solution, results will be saved in the directory named: {self.crop_dir}"
|
49
|
+
)
|
50
|
+
self.crop_idx = 0 # Initialize counter for total cropped objects
|
51
|
+
self.iou = self.CFG["iou"]
|
52
|
+
self.conf = self.CFG["conf"] if self.CFG["conf"] is not None else 0.25
|
53
|
+
|
54
|
+
def process(self, im0):
|
55
|
+
"""
|
56
|
+
Crops detected objects from the input image and saves them as separate images.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
im0 (numpy.ndarray): The input image containing detected objects.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
(SolutionResults): A SolutionResults object containing the total number of cropped objects and processed image.
|
63
|
+
|
64
|
+
Examples:
|
65
|
+
>>> cropper = ObjectCropper()
|
66
|
+
>>> frame = cv2.imread("image.jpg")
|
67
|
+
>>> results = cropper.process(frame)
|
68
|
+
>>> print(f"Total cropped objects: {results.total_crop_objects}")
|
69
|
+
"""
|
70
|
+
results = self.model.predict(
|
71
|
+
im0, classes=self.classes, conf=self.conf, iou=self.iou, device=self.CFG["device"]
|
72
|
+
)[0]
|
73
|
+
|
74
|
+
for box in results.boxes:
|
75
|
+
self.crop_idx += 1
|
76
|
+
save_one_box(
|
77
|
+
box.xyxy,
|
78
|
+
im0,
|
79
|
+
file=Path(self.crop_dir) / f"crop_{self.crop_idx}.jpg",
|
80
|
+
BGR=True,
|
81
|
+
)
|
82
|
+
|
83
|
+
# Return SolutionResults
|
84
|
+
return SolutionResults(plot_im=im0, total_crop_objects=self.crop_idx)
|
@@ -5,10 +5,9 @@ import json
|
|
5
5
|
import cv2
|
6
6
|
import numpy as np
|
7
7
|
|
8
|
-
from ultralytics.solutions.solutions import BaseSolution
|
8
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
|
9
9
|
from ultralytics.utils import LOGGER
|
10
|
-
from ultralytics.utils.checks import
|
11
|
-
from ultralytics.utils.plotting import Annotator
|
10
|
+
from ultralytics.utils.checks import check_imshow
|
12
11
|
|
13
12
|
|
14
13
|
class ParkingPtsSelection:
|
@@ -49,9 +48,24 @@ class ParkingPtsSelection:
|
|
49
48
|
|
50
49
|
def __init__(self):
|
51
50
|
"""Initializes the ParkingPtsSelection class, setting up UI and properties for parking zone point selection."""
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
try: # check if tkinter installed
|
52
|
+
import tkinter as tk
|
53
|
+
from tkinter import filedialog, messagebox
|
54
|
+
except ImportError: # Display error with recommendations
|
55
|
+
import platform
|
56
|
+
|
57
|
+
install_cmd = {
|
58
|
+
"Linux": "sudo apt install python3-tk (Debian/Ubuntu) | sudo dnf install python3-tkinter (Fedora) | "
|
59
|
+
"sudo pacman -S tk (Arch)",
|
60
|
+
"Windows": "reinstall Python and enable the checkbox `tcl/tk and IDLE` on **Optional Features** during installation",
|
61
|
+
"Darwin": "reinstall Python from https://www.python.org/downloads/mac-osx/ or `brew install python-tk`",
|
62
|
+
}.get(platform.system(), "Unknown OS. Check your Python installation.")
|
63
|
+
|
64
|
+
LOGGER.warning(f"WARNING ⚠️ Tkinter is not configured or supported. Potential fix: {install_cmd}")
|
65
|
+
return
|
66
|
+
|
67
|
+
if not check_imshow(warn=True):
|
68
|
+
return
|
55
69
|
|
56
70
|
self.tk, self.filedialog, self.messagebox = tk, filedialog, messagebox
|
57
71
|
self.master = self.tk.Tk() # Reference to the main application window or parent widget
|
@@ -174,7 +188,7 @@ class ParkingManagement(BaseSolution):
|
|
174
188
|
dc (Tuple[int, int, int]): RGB color tuple for centroid visualization of detected objects.
|
175
189
|
|
176
190
|
Methods:
|
177
|
-
|
191
|
+
process: Processes the input image for parking lot management and visualization.
|
178
192
|
|
179
193
|
Examples:
|
180
194
|
>>> from ultralytics.solutions import ParkingManagement
|
@@ -201,9 +215,9 @@ class ParkingManagement(BaseSolution):
|
|
201
215
|
self.occ = (0, 255, 0) # occupied region color
|
202
216
|
self.dc = (255, 0, 189) # centroid color for each box
|
203
217
|
|
204
|
-
def
|
218
|
+
def process(self, im0):
|
205
219
|
"""
|
206
|
-
Processes the
|
220
|
+
Processes the input image for parking lot management and visualization.
|
207
221
|
|
208
222
|
This function analyzes the input image, extracts tracks, and determines the occupancy status of parking
|
209
223
|
regions defined in the JSON file. It annotates the image with occupied and available parking spots,
|
@@ -212,14 +226,18 @@ class ParkingManagement(BaseSolution):
|
|
212
226
|
Args:
|
213
227
|
im0 (np.ndarray): The input inference image.
|
214
228
|
|
229
|
+
Returns:
|
230
|
+
(SolutionResults): Contains processed image `plot_im`, 'filled_slots' (number of occupied parking slots),
|
231
|
+
'available_slots' (number of available parking slots), and 'total_tracks' (total number of tracked objects).
|
232
|
+
|
215
233
|
Examples:
|
216
234
|
>>> parking_manager = ParkingManagement(json_file="parking_regions.json")
|
217
235
|
>>> image = cv2.imread("parking_lot.jpg")
|
218
|
-
>>> parking_manager.
|
236
|
+
>>> results = parking_manager.process(image)
|
219
237
|
"""
|
220
238
|
self.extract_tracks(im0) # extract tracks from im0
|
221
239
|
es, fs = len(self.json), 0 # empty slots, filled slots
|
222
|
-
annotator =
|
240
|
+
annotator = SolutionAnnotator(im0, self.line_width) # init annotator
|
223
241
|
|
224
242
|
for region in self.json:
|
225
243
|
# Convert points to a NumPy array with the correct dtype and reshape properly
|
@@ -242,5 +260,14 @@ class ParkingManagement(BaseSolution):
|
|
242
260
|
self.pr_info["Occupancy"], self.pr_info["Available"] = fs, es
|
243
261
|
|
244
262
|
annotator.display_analytics(im0, self.pr_info, (104, 31, 17), (255, 255, 255), 10)
|
245
|
-
|
246
|
-
|
263
|
+
|
264
|
+
plot_im = annotator.result()
|
265
|
+
self.display_output(plot_im) # display output with base class function
|
266
|
+
|
267
|
+
# Return SolutionResults
|
268
|
+
return SolutionResults(
|
269
|
+
plot_im=plot_im,
|
270
|
+
filled_slots=self.pr_info["Occupancy"],
|
271
|
+
available_slots=self.pr_info["Available"],
|
272
|
+
total_tracks=len(self.track_ids),
|
273
|
+
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
-
from ultralytics.solutions.solutions import BaseSolution
|
4
|
-
from ultralytics.utils.plotting import
|
3
|
+
from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
|
4
|
+
from ultralytics.utils.plotting import colors
|
5
5
|
|
6
6
|
|
7
7
|
class QueueManager(BaseSolution):
|
@@ -15,13 +15,12 @@ class QueueManager(BaseSolution):
|
|
15
15
|
counts (int): The current count of objects in the queue.
|
16
16
|
rect_color (Tuple[int, int, int]): RGB color tuple for drawing the queue region rectangle.
|
17
17
|
region_length (int): The number of points defining the queue region.
|
18
|
-
annotator (Annotator): An instance of the Annotator class for drawing on frames.
|
19
18
|
track_line (List[Tuple[int, int]]): List of track line coordinates.
|
20
19
|
track_history (Dict[int, List[Tuple[int, int]]]): Dictionary storing tracking history for each object.
|
21
20
|
|
22
21
|
Methods:
|
23
22
|
initialize_region: Initializes the queue region.
|
24
|
-
|
23
|
+
process: Processes a single frame for queue management.
|
25
24
|
extract_tracks: Extracts object tracks from the current frame.
|
26
25
|
store_tracking_history: Stores the tracking history for an object.
|
27
26
|
display_output: Displays the processed output.
|
@@ -33,18 +32,18 @@ class QueueManager(BaseSolution):
|
|
33
32
|
>>> success, im0 = cap.read()
|
34
33
|
>>> if not success:
|
35
34
|
>>> break
|
36
|
-
>>>
|
35
|
+
>>> results = queue_manager.process(im0)
|
37
36
|
"""
|
38
37
|
|
39
38
|
def __init__(self, **kwargs):
|
40
39
|
"""Initializes the QueueManager with parameters for tracking and counting objects in a video stream."""
|
41
40
|
super().__init__(**kwargs)
|
42
41
|
self.initialize_region()
|
43
|
-
self.counts = 0 # Queue counts
|
44
|
-
self.rect_color = (255, 255, 255) # Rectangle color
|
42
|
+
self.counts = 0 # Queue counts information
|
43
|
+
self.rect_color = (255, 255, 255) # Rectangle color for visualization
|
45
44
|
self.region_length = len(self.region) # Store region length for further usage
|
46
45
|
|
47
|
-
def
|
46
|
+
def process(self, im0):
|
48
47
|
"""
|
49
48
|
Processes the queue management for a single frame of video.
|
50
49
|
|
@@ -52,48 +51,28 @@ class QueueManager(BaseSolution):
|
|
52
51
|
im0 (numpy.ndarray): Input image for processing, typically a frame from a video stream.
|
53
52
|
|
54
53
|
Returns:
|
55
|
-
(
|
56
|
-
|
57
|
-
This method performs the following steps:
|
58
|
-
1. Resets the queue count for the current frame.
|
59
|
-
2. Initializes an Annotator object for drawing on the image.
|
60
|
-
3. Extracts tracks from the image.
|
61
|
-
4. Draws the counting region on the image.
|
62
|
-
5. For each detected object:
|
63
|
-
- Draws bounding boxes and labels.
|
64
|
-
- Stores tracking history.
|
65
|
-
- Draws centroids and tracks.
|
66
|
-
- Checks if the object is inside the counting region and updates the count.
|
67
|
-
6. Displays the queue count on the image.
|
68
|
-
7. Displays the processed output.
|
54
|
+
(SolutionResults): Contains processed image `im0`, 'queue_count' (int, number of objects in the queue) and
|
55
|
+
'total_tracks' (int, total number of tracked objects).
|
69
56
|
|
70
57
|
Examples:
|
71
58
|
>>> queue_manager = QueueManager()
|
72
59
|
>>> frame = cv2.imread("frame.jpg")
|
73
|
-
>>>
|
60
|
+
>>> results = queue_manager.process(frame)
|
74
61
|
"""
|
75
62
|
self.counts = 0 # Reset counts every frame
|
76
|
-
self.
|
77
|
-
self.
|
78
|
-
|
79
|
-
self.annotator.draw_region(
|
80
|
-
reg_pts=self.region, color=self.rect_color, thickness=self.line_width * 2
|
81
|
-
) # Draw region
|
63
|
+
self.extract_tracks(im0) # Extract tracks from the current frame
|
64
|
+
annotator = SolutionAnnotator(im0, line_width=self.line_width) # Initialize annotator
|
65
|
+
annotator.draw_region(reg_pts=self.region, color=self.rect_color, thickness=self.line_width * 2) # Draw region
|
82
66
|
|
83
67
|
for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
|
84
68
|
# Draw bounding box and counting region
|
85
|
-
|
69
|
+
annotator.box_label(box, label=self.names[cls], color=colors(track_id, True))
|
86
70
|
self.store_tracking_history(track_id, box) # Store track history
|
87
71
|
|
88
|
-
# Draw tracks of objects
|
89
|
-
self.annotator.draw_centroid_and_tracks(
|
90
|
-
self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width
|
91
|
-
)
|
92
|
-
|
93
72
|
# Cache frequently accessed attributes
|
94
73
|
track_history = self.track_history.get(track_id, [])
|
95
74
|
|
96
|
-
#
|
75
|
+
# Store previous position of track and check if the object is inside the counting region
|
97
76
|
prev_position = None
|
98
77
|
if len(track_history) > 1:
|
99
78
|
prev_position = track_history[-2]
|
@@ -101,12 +80,14 @@ class QueueManager(BaseSolution):
|
|
101
80
|
self.counts += 1
|
102
81
|
|
103
82
|
# Display queue counts
|
104
|
-
|
83
|
+
annotator.queue_counts_display(
|
105
84
|
f"Queue Counts : {str(self.counts)}",
|
106
85
|
points=self.region,
|
107
86
|
region_color=self.rect_color,
|
108
87
|
txt_color=(104, 31, 17),
|
109
88
|
)
|
110
|
-
|
89
|
+
plot_im = annotator.result()
|
90
|
+
self.display_output(plot_im) # Display output with base class function
|
111
91
|
|
112
|
-
|
92
|
+
# Return a SolutionResults object with processed data
|
93
|
+
return SolutionResults(plot_im=plot_im, queue_count=self.counts, total_tracks=len(self.track_ids))
|