ultralytics 8.1.29__py3-none-any.whl → 8.3.62__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/__init__.py +22 -0
- tests/conftest.py +83 -0
- tests/test_cli.py +122 -0
- tests/test_cuda.py +155 -0
- tests/test_engine.py +131 -0
- tests/test_exports.py +216 -0
- tests/test_integrations.py +150 -0
- tests/test_python.py +615 -0
- tests/test_solutions.py +94 -0
- ultralytics/__init__.py +11 -8
- ultralytics/cfg/__init__.py +569 -131
- ultralytics/cfg/datasets/Argoverse.yaml +2 -1
- ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
- ultralytics/cfg/datasets/ImageNet.yaml +2 -1
- ultralytics/cfg/datasets/Objects365.yaml +5 -4
- ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
- ultralytics/cfg/datasets/VOC.yaml +3 -2
- ultralytics/cfg/datasets/VisDrone.yaml +6 -5
- ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
- ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
- ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco-pose.yaml +7 -6
- ultralytics/cfg/datasets/coco.yaml +3 -2
- ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
- ultralytics/cfg/datasets/coco128.yaml +4 -3
- ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco8.yaml +3 -2
- ultralytics/cfg/datasets/crack-seg.yaml +3 -2
- ultralytics/cfg/datasets/dog-pose.yaml +24 -0
- ultralytics/cfg/datasets/dota8.yaml +3 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
- ultralytics/cfg/datasets/lvis.yaml +1236 -0
- ultralytics/cfg/datasets/medical-pills.yaml +22 -0
- ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
- ultralytics/cfg/datasets/package-seg.yaml +5 -4
- ultralytics/cfg/datasets/signature.yaml +21 -0
- ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
- ultralytics/cfg/datasets/xView.yaml +2 -1
- ultralytics/cfg/default.yaml +14 -11
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
- ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
- ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
- ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
- ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
- ultralytics/cfg/models/11/yolo11.yaml +50 -0
- ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
- ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
- ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5.yaml +5 -2
- ultralytics/cfg/models/v6/yolov6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
- ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8.yaml +5 -2
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
- ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
- ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
- ultralytics/cfg/solutions/default.yaml +24 -0
- ultralytics/cfg/trackers/botsort.yaml +8 -5
- ultralytics/cfg/trackers/bytetrack.yaml +8 -5
- ultralytics/data/__init__.py +14 -3
- ultralytics/data/annotator.py +37 -15
- ultralytics/data/augment.py +1783 -289
- ultralytics/data/base.py +62 -27
- ultralytics/data/build.py +36 -8
- ultralytics/data/converter.py +196 -36
- ultralytics/data/dataset.py +233 -94
- ultralytics/data/loaders.py +199 -96
- ultralytics/data/split_dota.py +39 -29
- ultralytics/data/utils.py +110 -40
- ultralytics/engine/__init__.py +1 -1
- ultralytics/engine/exporter.py +569 -242
- ultralytics/engine/model.py +604 -252
- ultralytics/engine/predictor.py +22 -11
- ultralytics/engine/results.py +1228 -218
- ultralytics/engine/trainer.py +190 -129
- ultralytics/engine/tuner.py +18 -18
- ultralytics/engine/validator.py +18 -15
- ultralytics/hub/__init__.py +31 -13
- ultralytics/hub/auth.py +11 -7
- ultralytics/hub/google/__init__.py +159 -0
- ultralytics/hub/session.py +128 -94
- ultralytics/hub/utils.py +20 -21
- ultralytics/models/__init__.py +4 -2
- ultralytics/models/fastsam/__init__.py +2 -3
- ultralytics/models/fastsam/model.py +26 -4
- ultralytics/models/fastsam/predict.py +127 -63
- ultralytics/models/fastsam/utils.py +1 -44
- ultralytics/models/fastsam/val.py +1 -1
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +21 -10
- ultralytics/models/nas/predict.py +3 -6
- ultralytics/models/nas/val.py +4 -4
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +1 -1
- ultralytics/models/rtdetr/predict.py +6 -8
- ultralytics/models/rtdetr/train.py +6 -2
- ultralytics/models/rtdetr/val.py +3 -3
- ultralytics/models/sam/__init__.py +3 -3
- ultralytics/models/sam/amg.py +29 -23
- ultralytics/models/sam/build.py +211 -13
- ultralytics/models/sam/model.py +91 -30
- ultralytics/models/sam/modules/__init__.py +1 -1
- ultralytics/models/sam/modules/blocks.py +1129 -0
- ultralytics/models/sam/modules/decoders.py +381 -53
- ultralytics/models/sam/modules/encoders.py +515 -324
- ultralytics/models/sam/modules/memory_attention.py +237 -0
- ultralytics/models/sam/modules/sam.py +969 -21
- ultralytics/models/sam/modules/tiny_encoder.py +425 -154
- ultralytics/models/sam/modules/transformer.py +159 -60
- ultralytics/models/sam/modules/utils.py +293 -0
- ultralytics/models/sam/predict.py +1263 -132
- ultralytics/models/utils/__init__.py +1 -1
- ultralytics/models/utils/loss.py +36 -24
- ultralytics/models/utils/ops.py +3 -7
- ultralytics/models/yolo/__init__.py +3 -3
- ultralytics/models/yolo/classify/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +7 -8
- ultralytics/models/yolo/classify/train.py +17 -22
- ultralytics/models/yolo/classify/val.py +8 -4
- ultralytics/models/yolo/detect/__init__.py +1 -1
- ultralytics/models/yolo/detect/predict.py +3 -5
- ultralytics/models/yolo/detect/train.py +11 -4
- ultralytics/models/yolo/detect/val.py +90 -52
- ultralytics/models/yolo/model.py +14 -9
- ultralytics/models/yolo/obb/__init__.py +1 -1
- ultralytics/models/yolo/obb/predict.py +2 -2
- ultralytics/models/yolo/obb/train.py +5 -3
- ultralytics/models/yolo/obb/val.py +41 -23
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +3 -5
- ultralytics/models/yolo/pose/train.py +2 -2
- ultralytics/models/yolo/pose/val.py +51 -17
- ultralytics/models/yolo/segment/__init__.py +1 -1
- ultralytics/models/yolo/segment/predict.py +3 -5
- ultralytics/models/yolo/segment/train.py +2 -2
- ultralytics/models/yolo/segment/val.py +60 -19
- ultralytics/models/yolo/world/__init__.py +5 -0
- ultralytics/models/yolo/world/train.py +92 -0
- ultralytics/models/yolo/world/train_world.py +109 -0
- ultralytics/nn/__init__.py +1 -1
- ultralytics/nn/autobackend.py +228 -93
- ultralytics/nn/modules/__init__.py +39 -14
- ultralytics/nn/modules/activation.py +21 -0
- ultralytics/nn/modules/block.py +526 -66
- ultralytics/nn/modules/conv.py +24 -7
- ultralytics/nn/modules/head.py +177 -34
- ultralytics/nn/modules/transformer.py +6 -5
- ultralytics/nn/modules/utils.py +1 -2
- ultralytics/nn/tasks.py +225 -77
- ultralytics/solutions/__init__.py +30 -1
- ultralytics/solutions/ai_gym.py +96 -143
- ultralytics/solutions/analytics.py +247 -0
- ultralytics/solutions/distance_calculation.py +78 -135
- ultralytics/solutions/heatmap.py +93 -247
- ultralytics/solutions/object_counter.py +184 -259
- ultralytics/solutions/parking_management.py +246 -0
- ultralytics/solutions/queue_management.py +112 -0
- ultralytics/solutions/region_counter.py +116 -0
- ultralytics/solutions/security_alarm.py +144 -0
- ultralytics/solutions/solutions.py +178 -0
- ultralytics/solutions/speed_estimation.py +86 -174
- ultralytics/solutions/streamlit_inference.py +190 -0
- ultralytics/solutions/trackzone.py +68 -0
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +32 -13
- ultralytics/trackers/bot_sort.py +61 -28
- ultralytics/trackers/byte_tracker.py +83 -51
- ultralytics/trackers/track.py +21 -6
- ultralytics/trackers/utils/__init__.py +1 -1
- ultralytics/trackers/utils/gmc.py +62 -48
- ultralytics/trackers/utils/kalman_filter.py +166 -35
- ultralytics/trackers/utils/matching.py +40 -21
- ultralytics/utils/__init__.py +511 -239
- ultralytics/utils/autobatch.py +40 -22
- ultralytics/utils/benchmarks.py +266 -85
- ultralytics/utils/callbacks/__init__.py +1 -1
- ultralytics/utils/callbacks/base.py +1 -3
- ultralytics/utils/callbacks/clearml.py +7 -6
- ultralytics/utils/callbacks/comet.py +39 -17
- ultralytics/utils/callbacks/dvc.py +1 -1
- ultralytics/utils/callbacks/hub.py +16 -16
- ultralytics/utils/callbacks/mlflow.py +28 -24
- ultralytics/utils/callbacks/neptune.py +6 -2
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +18 -18
- ultralytics/utils/callbacks/wb.py +27 -20
- ultralytics/utils/checks.py +160 -100
- ultralytics/utils/dist.py +2 -1
- ultralytics/utils/downloads.py +40 -34
- ultralytics/utils/errors.py +1 -1
- ultralytics/utils/files.py +72 -38
- ultralytics/utils/instance.py +41 -19
- ultralytics/utils/loss.py +83 -55
- ultralytics/utils/metrics.py +61 -56
- ultralytics/utils/ops.py +94 -89
- ultralytics/utils/patches.py +30 -14
- ultralytics/utils/plotting.py +600 -269
- ultralytics/utils/tal.py +67 -26
- ultralytics/utils/torch_utils.py +302 -102
- ultralytics/utils/triton.py +2 -1
- ultralytics/utils/tuner.py +21 -12
- ultralytics-8.3.62.dist-info/METADATA +370 -0
- ultralytics-8.3.62.dist-info/RECORD +241 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/WHEEL +1 -1
- ultralytics/data/explorer/__init__.py +0 -5
- ultralytics/data/explorer/explorer.py +0 -472
- ultralytics/data/explorer/gui/__init__.py +0 -1
- ultralytics/data/explorer/gui/dash.py +0 -268
- ultralytics/data/explorer/utils.py +0 -166
- ultralytics/models/fastsam/prompt.py +0 -357
- ultralytics-8.1.29.dist-info/METADATA +0 -373
- ultralytics-8.1.29.dist-info/RECORD +0 -197
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/top_level.txt +0 -0
@@ -1,181 +1,124 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
3
|
import math
|
4
4
|
|
5
5
|
import cv2
|
6
6
|
|
7
|
-
from ultralytics.
|
7
|
+
from ultralytics.solutions.solutions import BaseSolution
|
8
8
|
from ultralytics.utils.plotting import Annotator, colors
|
9
9
|
|
10
10
|
|
11
|
-
class DistanceCalculation:
|
12
|
-
"""
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
11
|
+
class DistanceCalculation(BaseSolution):
|
12
|
+
"""
|
13
|
+
A class to calculate distance between two objects in a real-time video stream based on their tracks.
|
14
|
+
|
15
|
+
This class extends BaseSolution to provide functionality for selecting objects and calculating the distance
|
16
|
+
between them in a video stream using YOLO object detection and tracking.
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
left_mouse_count (int): Counter for left mouse button clicks.
|
20
|
+
selected_boxes (Dict[int, List[float]]): Dictionary to store selected bounding boxes and their track IDs.
|
21
|
+
annotator (Annotator): An instance of the Annotator class for drawing on the image.
|
22
|
+
boxes (List[List[float]]): List of bounding boxes for detected objects.
|
23
|
+
track_ids (List[int]): List of track IDs for detected objects.
|
24
|
+
clss (List[int]): List of class indices for detected objects.
|
25
|
+
names (List[str]): List of class names that the model can detect.
|
26
|
+
centroids (List[List[int]]): List to store centroids of selected bounding boxes.
|
27
|
+
|
28
|
+
Methods:
|
29
|
+
mouse_event_for_distance: Handles mouse events for selecting objects in the video stream.
|
30
|
+
calculate: Processes video frames and calculates the distance between selected objects.
|
31
|
+
|
32
|
+
Examples:
|
33
|
+
>>> distance_calc = DistanceCalculation()
|
34
|
+
>>> frame = cv2.imread("frame.jpg")
|
35
|
+
>>> processed_frame = distance_calc.calculate(frame)
|
36
|
+
>>> cv2.imshow("Distance Calculation", processed_frame)
|
37
|
+
>>> cv2.waitKey(0)
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(self, **kwargs):
|
41
|
+
"""Initializes the DistanceCalculation class for measuring object distances in video streams."""
|
42
|
+
super().__init__(**kwargs)
|
43
|
+
|
44
|
+
# Mouse event information
|
38
45
|
self.left_mouse_count = 0
|
39
46
|
self.selected_boxes = {}
|
40
47
|
|
41
|
-
#
|
42
|
-
self.env_check = check_imshow(warn=True)
|
43
|
-
|
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
|
48
|
+
self.centroids = [] # Initialize empty list to store centroids
|
70
49
|
|
71
50
|
def mouse_event_for_distance(self, event, x, y, flags, param):
|
72
51
|
"""
|
73
|
-
|
52
|
+
Handles mouse events to select regions in a real-time video stream for distance calculation.
|
74
53
|
|
75
54
|
Args:
|
76
|
-
event (int):
|
77
|
-
x (int):
|
78
|
-
y (int):
|
79
|
-
flags (int):
|
80
|
-
|
81
|
-
|
55
|
+
event (int): Type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN).
|
56
|
+
x (int): X-coordinate of the mouse pointer.
|
57
|
+
y (int): Y-coordinate of the mouse pointer.
|
58
|
+
flags (int): Flags associated with the event (e.g., cv2.EVENT_FLAG_CTRLKEY, cv2.EVENT_FLAG_SHIFTKEY).
|
59
|
+
param (Dict): Additional parameters passed to the function.
|
60
|
+
|
61
|
+
Examples:
|
62
|
+
>>> # Assuming 'dc' is an instance of DistanceCalculation
|
63
|
+
>>> cv2.setMouseCallback("window_name", dc.mouse_event_for_distance)
|
82
64
|
"""
|
83
|
-
global selected_boxes
|
84
|
-
global left_mouse_count
|
85
65
|
if event == cv2.EVENT_LBUTTONDOWN:
|
86
66
|
self.left_mouse_count += 1
|
87
67
|
if self.left_mouse_count <= 2:
|
88
|
-
for box, track_id in zip(self.boxes, self.
|
68
|
+
for box, track_id in zip(self.boxes, self.track_ids):
|
89
69
|
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
70
|
self.selected_boxes[track_id] = box
|
92
71
|
|
93
|
-
|
72
|
+
elif event == cv2.EVENT_RBUTTONDOWN:
|
94
73
|
self.selected_boxes = {}
|
95
74
|
self.left_mouse_count = 0
|
96
75
|
|
97
|
-
def
|
98
|
-
"""
|
99
|
-
Extracts results from the provided data.
|
100
|
-
|
101
|
-
Args:
|
102
|
-
tracks (list): List of tracks obtained from the object tracking process.
|
103
|
-
"""
|
104
|
-
self.boxes = tracks[0].boxes.xyxy.cpu()
|
105
|
-
self.clss = tracks[0].boxes.cls.cpu().tolist()
|
106
|
-
self.trk_ids = tracks[0].boxes.id.int().cpu().tolist()
|
107
|
-
|
108
|
-
def calculate_centroid(self, box):
|
76
|
+
def calculate(self, im0):
|
109
77
|
"""
|
110
|
-
|
78
|
+
Processes a video frame and calculates the distance between two selected bounding boxes.
|
111
79
|
|
112
|
-
|
113
|
-
|
114
|
-
"""
|
115
|
-
return int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2)
|
116
|
-
|
117
|
-
def calculate_distance(self, centroid1, centroid2):
|
118
|
-
"""
|
119
|
-
Calculate distance between two centroids.
|
80
|
+
This method extracts tracks from the input frame, annotates bounding boxes, and calculates the distance
|
81
|
+
between two user-selected objects if they have been chosen.
|
120
82
|
|
121
83
|
Args:
|
122
|
-
|
123
|
-
centroid2 (point): Second bounding box data
|
124
|
-
"""
|
125
|
-
pixel_distance = math.sqrt((centroid1[0] - centroid2[0]) ** 2 + (centroid1[1] - centroid2[1]) ** 2)
|
126
|
-
return pixel_distance / self.pixel_per_meter, (pixel_distance / self.pixel_per_meter) * 1000
|
84
|
+
im0 (numpy.ndarray): The input image frame to process.
|
127
85
|
|
128
|
-
|
129
|
-
|
130
|
-
Calculate distance between two bounding boxes based on tracking data.
|
86
|
+
Returns:
|
87
|
+
(numpy.ndarray): The processed image frame with annotations and distance calculations.
|
131
88
|
|
132
|
-
|
133
|
-
|
134
|
-
|
89
|
+
Examples:
|
90
|
+
>>> import numpy as np
|
91
|
+
>>> from ultralytics.solutions import DistanceCalculation
|
92
|
+
>>> dc = DistanceCalculation()
|
93
|
+
>>> frame = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
|
94
|
+
>>> processed_frame = dc.calculate(frame)
|
135
95
|
"""
|
136
|
-
self.im0 =
|
137
|
-
|
138
|
-
if self.view_img:
|
139
|
-
self.display_frames()
|
140
|
-
return
|
141
|
-
self.extract_tracks(tracks)
|
96
|
+
self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
|
97
|
+
self.extract_tracks(im0) # Extract tracks
|
142
98
|
|
143
|
-
|
144
|
-
|
145
|
-
for box, cls, track_id in zip(self.boxes, self.clss, self.trk_ids):
|
99
|
+
# Iterate over bounding boxes, track ids and classes index
|
100
|
+
for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
|
146
101
|
self.annotator.box_label(box, color=colors(int(cls), True), label=self.names[int(cls)])
|
147
102
|
|
148
103
|
if len(self.selected_boxes) == 2:
|
149
|
-
for trk_id
|
104
|
+
for trk_id in self.selected_boxes.keys():
|
150
105
|
if trk_id == track_id:
|
151
106
|
self.selected_boxes[track_id] = box
|
152
107
|
|
153
108
|
if len(self.selected_boxes) == 2:
|
154
|
-
|
155
|
-
|
156
|
-
self.
|
157
|
-
|
158
|
-
distance_m, distance_mm = self.calculate_distance(self.centroids[0], self.centroids[1])
|
159
|
-
self.annotator.plot_distance_and_line(
|
160
|
-
distance_m, distance_mm, self.centroids, self.line_color, self.centroid_color
|
109
|
+
# Store user selected boxes in centroids list
|
110
|
+
self.centroids.extend(
|
111
|
+
[[int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2)] for box in self.selected_boxes.values()]
|
161
112
|
)
|
113
|
+
# Calculate pixels distance
|
114
|
+
pixels_distance = math.sqrt(
|
115
|
+
(self.centroids[0][0] - self.centroids[1][0]) ** 2 + (self.centroids[0][1] - self.centroids[1][1]) ** 2
|
116
|
+
)
|
117
|
+
self.annotator.plot_distance_and_line(pixels_distance, self.centroids)
|
162
118
|
|
163
119
|
self.centroids = []
|
164
120
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
return im0
|
169
|
-
|
170
|
-
def display_frames(self):
|
171
|
-
"""Display frame."""
|
172
|
-
cv2.namedWindow("Ultralytics Distance Estimation")
|
173
|
-
cv2.setMouseCallback("Ultralytics Distance Estimation", self.mouse_event_for_distance)
|
174
|
-
cv2.imshow("Ultralytics Distance Estimation", self.im0)
|
175
|
-
|
176
|
-
if cv2.waitKey(1) & 0xFF == ord("q"):
|
177
|
-
return
|
178
|
-
|
121
|
+
self.display_output(im0) # display output with base class function
|
122
|
+
cv2.setMouseCallback("Ultralytics Solutions", self.mouse_event_for_distance)
|
179
123
|
|
180
|
-
|
181
|
-
DistanceCalculation()
|
124
|
+
return im0 # return output image for more usage
|
ultralytics/solutions/heatmap.py
CHANGED
@@ -1,281 +1,127 @@
|
|
1
|
-
# Ultralytics
|
2
|
-
|
3
|
-
from collections import defaultdict
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
4
2
|
|
5
3
|
import cv2
|
6
4
|
import numpy as np
|
7
5
|
|
8
|
-
from ultralytics.
|
6
|
+
from ultralytics.solutions.object_counter import ObjectCounter
|
9
7
|
from ultralytics.utils.plotting import Annotator
|
10
8
|
|
11
|
-
check_requirements("shapely>=2.0.0")
|
12
|
-
|
13
|
-
from shapely.geometry import LineString, Point, Polygon
|
14
|
-
|
15
9
|
|
16
|
-
class Heatmap:
|
17
|
-
"""
|
10
|
+
class Heatmap(ObjectCounter):
|
11
|
+
"""
|
12
|
+
A class to draw heatmaps in real-time video streams based on object tracks.
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
This class extends the ObjectCounter class to generate and visualize heatmaps of object movements in video
|
15
|
+
streams. It uses tracked object positions to create a cumulative heatmap effect over time.
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
Attributes:
|
18
|
+
initialized (bool): Flag indicating whether the heatmap has been initialized.
|
19
|
+
colormap (int): OpenCV colormap used for heatmap visualization.
|
20
|
+
heatmap (np.ndarray): Array storing the cumulative heatmap data.
|
21
|
+
annotator (Annotator): Object for drawing annotations on the image.
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
self.im0 = None
|
31
|
-
self.view_in_counts = True
|
32
|
-
self.view_out_counts = True
|
33
|
-
|
34
|
-
# Heatmap colormap and heatmap np array
|
35
|
-
self.colormap = None
|
36
|
-
self.heatmap = None
|
37
|
-
self.heatmap_alpha = 0.5
|
23
|
+
Methods:
|
24
|
+
heatmap_effect: Calculates and updates the heatmap effect for a given bounding box.
|
25
|
+
generate_heatmap: Generates and applies the heatmap effect to each frame.
|
38
26
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
27
|
+
Examples:
|
28
|
+
>>> from ultralytics.solutions import Heatmap
|
29
|
+
>>> heatmap = Heatmap(model="yolov8n.pt", colormap=cv2.COLORMAP_JET)
|
30
|
+
>>> frame = cv2.imread("frame.jpg")
|
31
|
+
>>> processed_frame = heatmap.generate_heatmap(frame)
|
32
|
+
"""
|
44
33
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
self.line_dist_thresh = 15
|
49
|
-
self.region_thickness = 5
|
50
|
-
self.region_color = (255, 0, 255)
|
34
|
+
def __init__(self, **kwargs):
|
35
|
+
"""Initializes the Heatmap class for real-time video stream heatmap generation based on object tracks."""
|
36
|
+
super().__init__(**kwargs)
|
51
37
|
|
52
|
-
#
|
53
|
-
self.
|
54
|
-
|
55
|
-
self.counting_list = []
|
56
|
-
self.count_txt_thickness = 0
|
57
|
-
self.count_txt_color = (0, 0, 0)
|
58
|
-
self.count_color = (255, 255, 255)
|
38
|
+
self.initialized = False # bool variable for heatmap initialization
|
39
|
+
if self.region is not None: # check if user provided the region coordinates
|
40
|
+
self.initialize_region()
|
59
41
|
|
60
|
-
#
|
61
|
-
self.
|
62
|
-
|
63
|
-
# Check if environment support imshow
|
64
|
-
self.env_check = check_imshow(warn=True)
|
42
|
+
# store colormap
|
43
|
+
self.colormap = cv2.COLORMAP_PARULA if self.CFG["colormap"] is None else self.CFG["colormap"]
|
44
|
+
self.heatmap = None
|
65
45
|
|
66
|
-
def
|
67
|
-
self,
|
68
|
-
imw,
|
69
|
-
imh,
|
70
|
-
colormap=cv2.COLORMAP_JET,
|
71
|
-
heatmap_alpha=0.5,
|
72
|
-
view_img=False,
|
73
|
-
view_in_counts=True,
|
74
|
-
view_out_counts=True,
|
75
|
-
count_reg_pts=None,
|
76
|
-
count_txt_thickness=2,
|
77
|
-
count_txt_color=(0, 0, 0),
|
78
|
-
count_color=(255, 255, 255),
|
79
|
-
count_reg_color=(255, 0, 255),
|
80
|
-
region_thickness=5,
|
81
|
-
line_dist_thresh=15,
|
82
|
-
decay_factor=0.99,
|
83
|
-
shape="circle",
|
84
|
-
):
|
46
|
+
def heatmap_effect(self, box):
|
85
47
|
"""
|
86
|
-
|
48
|
+
Efficiently calculates heatmap area and effect location for applying colormap.
|
87
49
|
|
88
50
|
Args:
|
89
|
-
|
90
|
-
imw (int): The width of the frame.
|
91
|
-
imh (int): The height of the frame.
|
92
|
-
heatmap_alpha (float): alpha value for heatmap display
|
93
|
-
view_img (bool): Flag indicating frame display
|
94
|
-
view_in_counts (bool): Flag to control whether to display the incounts on video stream.
|
95
|
-
view_out_counts (bool): Flag to control whether to display the outcounts on video stream.
|
96
|
-
count_reg_pts (list): Object counting region points
|
97
|
-
count_txt_thickness (int): Text thickness for object counting display
|
98
|
-
count_txt_color (RGB color): count text color value
|
99
|
-
count_color (RGB color): count text background color value
|
100
|
-
count_reg_color (RGB color): Color of object counting region
|
101
|
-
region_thickness (int): Object counting Region thickness
|
102
|
-
line_dist_thresh (int): Euclidean Distance threshold for line counter
|
103
|
-
decay_factor (float): value for removing heatmap area after object passed
|
104
|
-
shape (str): Heatmap shape, rect or circle shape supported
|
105
|
-
"""
|
106
|
-
self.imw = imw
|
107
|
-
self.imh = imh
|
108
|
-
self.heatmap_alpha = heatmap_alpha
|
109
|
-
self.view_img = view_img
|
110
|
-
self.view_in_counts = view_in_counts
|
111
|
-
self.view_out_counts = view_out_counts
|
112
|
-
self.colormap = colormap
|
113
|
-
|
114
|
-
# Region and line selection
|
115
|
-
if count_reg_pts is not None:
|
116
|
-
if len(count_reg_pts) == 2:
|
117
|
-
print("Line Counter Initiated.")
|
118
|
-
self.count_reg_pts = count_reg_pts
|
119
|
-
self.counting_region = LineString(count_reg_pts)
|
51
|
+
box (List[float]): Bounding box coordinates [x0, y0, x1, y1].
|
120
52
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
53
|
+
Examples:
|
54
|
+
>>> heatmap = Heatmap()
|
55
|
+
>>> box = [100, 100, 200, 200]
|
56
|
+
>>> heatmap.heatmap_effect(box)
|
57
|
+
"""
|
58
|
+
x0, y0, x1, y1 = map(int, box)
|
59
|
+
radius_squared = (min(x1 - x0, y1 - y0) // 2) ** 2
|
125
60
|
|
126
|
-
|
127
|
-
|
128
|
-
print("Using Line Counter Now")
|
129
|
-
self.counting_region = Polygon([(20, 400), (1260, 400)]) # dummy points
|
61
|
+
# Create a meshgrid with region of interest (ROI) for vectorized distance calculations
|
62
|
+
xv, yv = np.meshgrid(np.arange(x0, x1), np.arange(y0, y1))
|
130
63
|
|
131
|
-
#
|
132
|
-
|
64
|
+
# Calculate squared distances from the center
|
65
|
+
dist_squared = (xv - ((x0 + x1) // 2)) ** 2 + (yv - ((y0 + y1) // 2)) ** 2
|
133
66
|
|
134
|
-
|
135
|
-
|
136
|
-
self.count_color = count_color
|
137
|
-
self.region_color = count_reg_color
|
138
|
-
self.region_thickness = region_thickness
|
139
|
-
self.decay_factor = decay_factor
|
140
|
-
self.line_dist_thresh = line_dist_thresh
|
141
|
-
self.shape = shape
|
67
|
+
# Create a mask of points within the radius
|
68
|
+
within_radius = dist_squared <= radius_squared
|
142
69
|
|
143
|
-
#
|
144
|
-
|
145
|
-
print("Unknown shape value provided, 'circle' & 'rect' supported")
|
146
|
-
print("Using Circular shape now")
|
147
|
-
self.shape = "circle"
|
70
|
+
# Update only the values within the bounding box in a single vectorized operation
|
71
|
+
self.heatmap[y0:y1, x0:x1][within_radius] += 2
|
148
72
|
|
149
|
-
def
|
73
|
+
def generate_heatmap(self, im0):
|
150
74
|
"""
|
151
|
-
|
75
|
+
Generate heatmap for each frame using Ultralytics.
|
152
76
|
|
153
77
|
Args:
|
154
|
-
|
155
|
-
"""
|
156
|
-
self.boxes = tracks[0].boxes.xyxy.cpu()
|
157
|
-
self.clss = tracks[0].boxes.cls.cpu().tolist()
|
158
|
-
self.track_ids = tracks[0].boxes.id.int().cpu().tolist()
|
78
|
+
im0 (np.ndarray): Input image array for processing.
|
159
79
|
|
160
|
-
|
161
|
-
|
162
|
-
Generate heatmap based on tracking data.
|
80
|
+
Returns:
|
81
|
+
(np.ndarray): Processed image with heatmap overlay and object counts (if region is specified).
|
163
82
|
|
164
|
-
|
165
|
-
|
166
|
-
|
83
|
+
Examples:
|
84
|
+
>>> heatmap = Heatmap()
|
85
|
+
>>> im0 = cv2.imread("image.jpg")
|
86
|
+
>>> result = heatmap.generate_heatmap(im0)
|
167
87
|
"""
|
168
|
-
self.
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
if self.
|
181
|
-
self.annotator.draw_region(
|
182
|
-
|
183
|
-
)
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
self.heatmap[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] += (
|
194
|
-
2 * mask[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])]
|
195
|
-
)
|
196
|
-
|
197
|
-
else:
|
198
|
-
self.heatmap[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] += 2
|
199
|
-
|
200
|
-
# Store tracking hist
|
201
|
-
track_line = self.track_history[track_id]
|
202
|
-
track_line.append((float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2)))
|
203
|
-
if len(track_line) > 30:
|
204
|
-
track_line.pop(0)
|
205
|
-
|
206
|
-
# Count objects
|
207
|
-
if len(self.count_reg_pts) == 4:
|
208
|
-
if self.counting_region.contains(Point(track_line[-1])) and track_id not in self.counting_list:
|
209
|
-
self.counting_list.append(track_id)
|
210
|
-
if box[0] < self.counting_region.centroid.x:
|
211
|
-
self.out_counts += 1
|
212
|
-
else:
|
213
|
-
self.in_counts += 1
|
214
|
-
|
215
|
-
elif len(self.count_reg_pts) == 2:
|
216
|
-
distance = Point(track_line[-1]).distance(self.counting_region)
|
217
|
-
if distance < self.line_dist_thresh and track_id not in self.counting_list:
|
218
|
-
self.counting_list.append(track_id)
|
219
|
-
if box[0] < self.counting_region.centroid.x:
|
220
|
-
self.out_counts += 1
|
221
|
-
else:
|
222
|
-
self.in_counts += 1
|
223
|
-
else:
|
224
|
-
for box, cls in zip(self.boxes, self.clss):
|
225
|
-
if self.shape == "circle":
|
226
|
-
center = (int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2))
|
227
|
-
radius = min(int(box[2]) - int(box[0]), int(box[3]) - int(box[1])) // 2
|
228
|
-
|
229
|
-
y, x = np.ogrid[0 : self.heatmap.shape[0], 0 : self.heatmap.shape[1]]
|
230
|
-
mask = (x - center[0]) ** 2 + (y - center[1]) ** 2 <= radius**2
|
231
|
-
|
232
|
-
self.heatmap[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] += (
|
233
|
-
2 * mask[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])]
|
234
|
-
)
|
235
|
-
|
236
|
-
else:
|
237
|
-
self.heatmap[int(box[1]) : int(box[3]), int(box[0]) : int(box[2])] += 2
|
88
|
+
if not self.initialized:
|
89
|
+
self.heatmap = np.zeros_like(im0, dtype=np.float32) * 0.99
|
90
|
+
self.initialized = True # Initialize heatmap only once
|
91
|
+
|
92
|
+
self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
|
93
|
+
self.extract_tracks(im0) # Extract tracks
|
94
|
+
|
95
|
+
# Iterate over bounding boxes, track ids and classes index
|
96
|
+
for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
|
97
|
+
# Draw bounding box and counting region
|
98
|
+
self.heatmap_effect(box)
|
99
|
+
|
100
|
+
if self.region is not None:
|
101
|
+
self.annotator.draw_region(reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2)
|
102
|
+
self.store_tracking_history(track_id, box) # Store track history
|
103
|
+
self.store_classwise_counts(cls) # store classwise counts in dict
|
104
|
+
current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
|
105
|
+
# Store tracking previous position and perform object counting
|
106
|
+
prev_position = None
|
107
|
+
if len(self.track_history[track_id]) > 1:
|
108
|
+
prev_position = self.track_history[track_id][-2]
|
109
|
+
self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting
|
110
|
+
|
111
|
+
if self.region is not None:
|
112
|
+
self.display_counts(im0) # Display the counts on the frame
|
238
113
|
|
239
114
|
# Normalize, apply colormap to heatmap and combine with original image
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
counts_label = None
|
250
|
-
elif not self.view_in_counts:
|
251
|
-
counts_label = outcount_label
|
252
|
-
elif not self.view_out_counts:
|
253
|
-
counts_label = incount_label
|
254
|
-
else:
|
255
|
-
counts_label = f"{incount_label} {outcount_label}"
|
256
|
-
|
257
|
-
if self.count_reg_pts is not None and counts_label is not None:
|
258
|
-
self.annotator.count_labels(
|
259
|
-
counts=counts_label,
|
260
|
-
count_txt_size=self.count_txt_thickness,
|
261
|
-
txt_color=self.count_txt_color,
|
262
|
-
color=self.count_color,
|
115
|
+
if self.track_data.id is not None:
|
116
|
+
im0 = cv2.addWeighted(
|
117
|
+
im0,
|
118
|
+
0.5,
|
119
|
+
cv2.applyColorMap(
|
120
|
+
cv2.normalize(self.heatmap, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8), self.colormap
|
121
|
+
),
|
122
|
+
0.5,
|
123
|
+
0,
|
263
124
|
)
|
264
125
|
|
265
|
-
self.
|
266
|
-
|
267
|
-
if self.env_check and self.view_img:
|
268
|
-
self.display_frames()
|
269
|
-
|
270
|
-
return self.im0
|
271
|
-
|
272
|
-
def display_frames(self):
|
273
|
-
"""Display frame."""
|
274
|
-
cv2.imshow("Ultralytics Heatmap", self.im0)
|
275
|
-
|
276
|
-
if cv2.waitKey(1) & 0xFF == ord("q"):
|
277
|
-
return
|
278
|
-
|
279
|
-
|
280
|
-
if __name__ == "__main__":
|
281
|
-
Heatmap()
|
126
|
+
self.display_output(im0) # display output with base class function
|
127
|
+
return im0 # return output image for more usage
|