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.
Files changed (247) hide show
  1. tests/__init__.py +22 -0
  2. tests/conftest.py +83 -0
  3. tests/test_cli.py +122 -0
  4. tests/test_cuda.py +155 -0
  5. tests/test_engine.py +131 -0
  6. tests/test_exports.py +216 -0
  7. tests/test_integrations.py +150 -0
  8. tests/test_python.py +615 -0
  9. tests/test_solutions.py +94 -0
  10. ultralytics/__init__.py +11 -8
  11. ultralytics/cfg/__init__.py +569 -131
  12. ultralytics/cfg/datasets/Argoverse.yaml +2 -1
  13. ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
  14. ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
  15. ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
  16. ultralytics/cfg/datasets/ImageNet.yaml +2 -1
  17. ultralytics/cfg/datasets/Objects365.yaml +5 -4
  18. ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
  19. ultralytics/cfg/datasets/VOC.yaml +3 -2
  20. ultralytics/cfg/datasets/VisDrone.yaml +6 -5
  21. ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
  22. ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
  23. ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
  24. ultralytics/cfg/datasets/coco-pose.yaml +7 -6
  25. ultralytics/cfg/datasets/coco.yaml +3 -2
  26. ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
  27. ultralytics/cfg/datasets/coco128.yaml +4 -3
  28. ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
  29. ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
  30. ultralytics/cfg/datasets/coco8.yaml +3 -2
  31. ultralytics/cfg/datasets/crack-seg.yaml +3 -2
  32. ultralytics/cfg/datasets/dog-pose.yaml +24 -0
  33. ultralytics/cfg/datasets/dota8.yaml +3 -2
  34. ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
  35. ultralytics/cfg/datasets/lvis.yaml +1236 -0
  36. ultralytics/cfg/datasets/medical-pills.yaml +22 -0
  37. ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
  38. ultralytics/cfg/datasets/package-seg.yaml +5 -4
  39. ultralytics/cfg/datasets/signature.yaml +21 -0
  40. ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
  41. ultralytics/cfg/datasets/xView.yaml +2 -1
  42. ultralytics/cfg/default.yaml +14 -11
  43. ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
  44. ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
  45. ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
  46. ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
  47. ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
  48. ultralytics/cfg/models/11/yolo11.yaml +50 -0
  49. ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
  50. ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
  51. ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
  52. ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
  53. ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
  54. ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
  55. ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
  56. ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
  57. ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
  58. ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
  59. ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
  60. ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
  61. ultralytics/cfg/models/v3/yolov3.yaml +5 -2
  62. ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
  63. ultralytics/cfg/models/v5/yolov5.yaml +5 -2
  64. ultralytics/cfg/models/v6/yolov6.yaml +5 -2
  65. ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
  66. ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
  67. ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
  68. ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
  69. ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
  70. ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
  71. ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
  72. ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
  73. ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
  74. ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
  75. ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
  76. ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
  77. ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
  78. ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
  79. ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
  80. ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
  81. ultralytics/cfg/models/v8/yolov8.yaml +5 -2
  82. ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
  83. ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
  84. ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
  85. ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
  86. ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
  87. ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
  88. ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
  89. ultralytics/cfg/solutions/default.yaml +24 -0
  90. ultralytics/cfg/trackers/botsort.yaml +8 -5
  91. ultralytics/cfg/trackers/bytetrack.yaml +8 -5
  92. ultralytics/data/__init__.py +14 -3
  93. ultralytics/data/annotator.py +37 -15
  94. ultralytics/data/augment.py +1783 -289
  95. ultralytics/data/base.py +62 -27
  96. ultralytics/data/build.py +36 -8
  97. ultralytics/data/converter.py +196 -36
  98. ultralytics/data/dataset.py +233 -94
  99. ultralytics/data/loaders.py +199 -96
  100. ultralytics/data/split_dota.py +39 -29
  101. ultralytics/data/utils.py +110 -40
  102. ultralytics/engine/__init__.py +1 -1
  103. ultralytics/engine/exporter.py +569 -242
  104. ultralytics/engine/model.py +604 -252
  105. ultralytics/engine/predictor.py +22 -11
  106. ultralytics/engine/results.py +1228 -218
  107. ultralytics/engine/trainer.py +190 -129
  108. ultralytics/engine/tuner.py +18 -18
  109. ultralytics/engine/validator.py +18 -15
  110. ultralytics/hub/__init__.py +31 -13
  111. ultralytics/hub/auth.py +11 -7
  112. ultralytics/hub/google/__init__.py +159 -0
  113. ultralytics/hub/session.py +128 -94
  114. ultralytics/hub/utils.py +20 -21
  115. ultralytics/models/__init__.py +4 -2
  116. ultralytics/models/fastsam/__init__.py +2 -3
  117. ultralytics/models/fastsam/model.py +26 -4
  118. ultralytics/models/fastsam/predict.py +127 -63
  119. ultralytics/models/fastsam/utils.py +1 -44
  120. ultralytics/models/fastsam/val.py +1 -1
  121. ultralytics/models/nas/__init__.py +1 -1
  122. ultralytics/models/nas/model.py +21 -10
  123. ultralytics/models/nas/predict.py +3 -6
  124. ultralytics/models/nas/val.py +4 -4
  125. ultralytics/models/rtdetr/__init__.py +1 -1
  126. ultralytics/models/rtdetr/model.py +1 -1
  127. ultralytics/models/rtdetr/predict.py +6 -8
  128. ultralytics/models/rtdetr/train.py +6 -2
  129. ultralytics/models/rtdetr/val.py +3 -3
  130. ultralytics/models/sam/__init__.py +3 -3
  131. ultralytics/models/sam/amg.py +29 -23
  132. ultralytics/models/sam/build.py +211 -13
  133. ultralytics/models/sam/model.py +91 -30
  134. ultralytics/models/sam/modules/__init__.py +1 -1
  135. ultralytics/models/sam/modules/blocks.py +1129 -0
  136. ultralytics/models/sam/modules/decoders.py +381 -53
  137. ultralytics/models/sam/modules/encoders.py +515 -324
  138. ultralytics/models/sam/modules/memory_attention.py +237 -0
  139. ultralytics/models/sam/modules/sam.py +969 -21
  140. ultralytics/models/sam/modules/tiny_encoder.py +425 -154
  141. ultralytics/models/sam/modules/transformer.py +159 -60
  142. ultralytics/models/sam/modules/utils.py +293 -0
  143. ultralytics/models/sam/predict.py +1263 -132
  144. ultralytics/models/utils/__init__.py +1 -1
  145. ultralytics/models/utils/loss.py +36 -24
  146. ultralytics/models/utils/ops.py +3 -7
  147. ultralytics/models/yolo/__init__.py +3 -3
  148. ultralytics/models/yolo/classify/__init__.py +1 -1
  149. ultralytics/models/yolo/classify/predict.py +7 -8
  150. ultralytics/models/yolo/classify/train.py +17 -22
  151. ultralytics/models/yolo/classify/val.py +8 -4
  152. ultralytics/models/yolo/detect/__init__.py +1 -1
  153. ultralytics/models/yolo/detect/predict.py +3 -5
  154. ultralytics/models/yolo/detect/train.py +11 -4
  155. ultralytics/models/yolo/detect/val.py +90 -52
  156. ultralytics/models/yolo/model.py +14 -9
  157. ultralytics/models/yolo/obb/__init__.py +1 -1
  158. ultralytics/models/yolo/obb/predict.py +2 -2
  159. ultralytics/models/yolo/obb/train.py +5 -3
  160. ultralytics/models/yolo/obb/val.py +41 -23
  161. ultralytics/models/yolo/pose/__init__.py +1 -1
  162. ultralytics/models/yolo/pose/predict.py +3 -5
  163. ultralytics/models/yolo/pose/train.py +2 -2
  164. ultralytics/models/yolo/pose/val.py +51 -17
  165. ultralytics/models/yolo/segment/__init__.py +1 -1
  166. ultralytics/models/yolo/segment/predict.py +3 -5
  167. ultralytics/models/yolo/segment/train.py +2 -2
  168. ultralytics/models/yolo/segment/val.py +60 -19
  169. ultralytics/models/yolo/world/__init__.py +5 -0
  170. ultralytics/models/yolo/world/train.py +92 -0
  171. ultralytics/models/yolo/world/train_world.py +109 -0
  172. ultralytics/nn/__init__.py +1 -1
  173. ultralytics/nn/autobackend.py +228 -93
  174. ultralytics/nn/modules/__init__.py +39 -14
  175. ultralytics/nn/modules/activation.py +21 -0
  176. ultralytics/nn/modules/block.py +526 -66
  177. ultralytics/nn/modules/conv.py +24 -7
  178. ultralytics/nn/modules/head.py +177 -34
  179. ultralytics/nn/modules/transformer.py +6 -5
  180. ultralytics/nn/modules/utils.py +1 -2
  181. ultralytics/nn/tasks.py +225 -77
  182. ultralytics/solutions/__init__.py +30 -1
  183. ultralytics/solutions/ai_gym.py +96 -143
  184. ultralytics/solutions/analytics.py +247 -0
  185. ultralytics/solutions/distance_calculation.py +78 -135
  186. ultralytics/solutions/heatmap.py +93 -247
  187. ultralytics/solutions/object_counter.py +184 -259
  188. ultralytics/solutions/parking_management.py +246 -0
  189. ultralytics/solutions/queue_management.py +112 -0
  190. ultralytics/solutions/region_counter.py +116 -0
  191. ultralytics/solutions/security_alarm.py +144 -0
  192. ultralytics/solutions/solutions.py +178 -0
  193. ultralytics/solutions/speed_estimation.py +86 -174
  194. ultralytics/solutions/streamlit_inference.py +190 -0
  195. ultralytics/solutions/trackzone.py +68 -0
  196. ultralytics/trackers/__init__.py +1 -1
  197. ultralytics/trackers/basetrack.py +32 -13
  198. ultralytics/trackers/bot_sort.py +61 -28
  199. ultralytics/trackers/byte_tracker.py +83 -51
  200. ultralytics/trackers/track.py +21 -6
  201. ultralytics/trackers/utils/__init__.py +1 -1
  202. ultralytics/trackers/utils/gmc.py +62 -48
  203. ultralytics/trackers/utils/kalman_filter.py +166 -35
  204. ultralytics/trackers/utils/matching.py +40 -21
  205. ultralytics/utils/__init__.py +511 -239
  206. ultralytics/utils/autobatch.py +40 -22
  207. ultralytics/utils/benchmarks.py +266 -85
  208. ultralytics/utils/callbacks/__init__.py +1 -1
  209. ultralytics/utils/callbacks/base.py +1 -3
  210. ultralytics/utils/callbacks/clearml.py +7 -6
  211. ultralytics/utils/callbacks/comet.py +39 -17
  212. ultralytics/utils/callbacks/dvc.py +1 -1
  213. ultralytics/utils/callbacks/hub.py +16 -16
  214. ultralytics/utils/callbacks/mlflow.py +28 -24
  215. ultralytics/utils/callbacks/neptune.py +6 -2
  216. ultralytics/utils/callbacks/raytune.py +3 -4
  217. ultralytics/utils/callbacks/tensorboard.py +18 -18
  218. ultralytics/utils/callbacks/wb.py +27 -20
  219. ultralytics/utils/checks.py +160 -100
  220. ultralytics/utils/dist.py +2 -1
  221. ultralytics/utils/downloads.py +40 -34
  222. ultralytics/utils/errors.py +1 -1
  223. ultralytics/utils/files.py +72 -38
  224. ultralytics/utils/instance.py +41 -19
  225. ultralytics/utils/loss.py +83 -55
  226. ultralytics/utils/metrics.py +61 -56
  227. ultralytics/utils/ops.py +94 -89
  228. ultralytics/utils/patches.py +30 -14
  229. ultralytics/utils/plotting.py +600 -269
  230. ultralytics/utils/tal.py +67 -26
  231. ultralytics/utils/torch_utils.py +302 -102
  232. ultralytics/utils/triton.py +2 -1
  233. ultralytics/utils/tuner.py +21 -12
  234. ultralytics-8.3.62.dist-info/METADATA +370 -0
  235. ultralytics-8.3.62.dist-info/RECORD +241 -0
  236. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/WHEEL +1 -1
  237. ultralytics/data/explorer/__init__.py +0 -5
  238. ultralytics/data/explorer/explorer.py +0 -472
  239. ultralytics/data/explorer/gui/__init__.py +0 -1
  240. ultralytics/data/explorer/gui/dash.py +0 -268
  241. ultralytics/data/explorer/utils.py +0 -166
  242. ultralytics/models/fastsam/prompt.py +0 -357
  243. ultralytics-8.1.29.dist-info/METADATA +0 -373
  244. ultralytics-8.1.29.dist-info/RECORD +0 -197
  245. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
  246. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
  247. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/top_level.txt +0 -0
@@ -1,181 +1,124 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
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.utils.checks import check_imshow
7
+ from ultralytics.solutions.solutions import BaseSolution
8
8
  from ultralytics.utils.plotting import Annotator, colors
9
9
 
10
10
 
11
- class DistanceCalculation:
12
- """A class to calculate distance between two objects in real-time video stream based on their tracks."""
13
-
14
- def __init__(self):
15
- """Initializes the distance calculation class with default values for Visual, Image, track and distance
16
- parameters.
17
- """
18
-
19
- # Visual & im0 information
20
- self.im0 = None
21
- self.annotator = None
22
- self.view_img = False
23
- self.line_color = (255, 255, 0)
24
- self.centroid_color = (255, 0, 255)
25
-
26
- # Predict/track information
27
- self.clss = None
28
- self.names = None
29
- self.boxes = None
30
- self.line_thickness = 2
31
- self.trk_ids = None
32
-
33
- # Distance calculation information
34
- self.centroids = []
35
- self.pixel_per_meter = 10
36
-
37
- # Mouse event
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
- # Check if environment support imshow
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
- This function is designed to move region with mouse events in a real-time video stream.
52
+ Handles mouse events to select regions in a real-time video stream for distance calculation.
74
53
 
75
54
  Args:
76
- event (int): The type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN, etc.).
77
- x (int): The x-coordinate of the mouse pointer.
78
- y (int): The y-coordinate of the mouse pointer.
79
- flags (int): Any flags associated with the event (e.g., cv2.EVENT_FLAG_CTRLKEY,
80
- cv2.EVENT_FLAG_SHIFTKEY, etc.).
81
- param (dict): Additional parameters you may want to pass to the function.
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.trk_ids):
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
- if event == cv2.EVENT_RBUTTONDOWN:
72
+ elif event == cv2.EVENT_RBUTTONDOWN:
94
73
  self.selected_boxes = {}
95
74
  self.left_mouse_count = 0
96
75
 
97
- def extract_tracks(self, tracks):
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
- Calculate the centroid of bounding box.
78
+ Processes a video frame and calculates the distance between two selected bounding boxes.
111
79
 
112
- Args:
113
- box (list): Bounding box data
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
- centroid1 (point): First bounding box data
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
- def start_process(self, im0, tracks):
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
- Args:
133
- im0 (nd array): Image
134
- tracks (list): List of tracks obtained from the object tracking process.
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 = im0
137
- if tracks[0].boxes.id is None:
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
- self.annotator = Annotator(self.im0, line_width=2)
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, _ in self.selected_boxes.items():
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
- for trk_id, box in self.selected_boxes.items():
155
- centroid = self.calculate_centroid(self.selected_boxes[trk_id])
156
- self.centroids.append(centroid)
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
- if self.view_img and self.env_check:
166
- self.display_frames()
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
- if __name__ == "__main__":
181
- DistanceCalculation()
124
+ return im0 # return output image for more usage
@@ -1,281 +1,127 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
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.utils.checks import check_imshow, check_requirements
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
- """A class to draw heatmaps in real-time video stream based on their tracks."""
10
+ class Heatmap(ObjectCounter):
11
+ """
12
+ A class to draw heatmaps in real-time video streams based on object tracks.
18
13
 
19
- def __init__(self):
20
- """Initializes the heatmap class with default values for Visual, Image, track, count and heatmap parameters."""
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
- # Visual information
23
- self.annotator = None
24
- self.view_img = False
25
- self.shape = "circle"
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
- # Image information
28
- self.imw = None
29
- self.imh = None
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
- # Predict/track information
40
- self.boxes = None
41
- self.track_ids = None
42
- self.clss = None
43
- self.track_history = defaultdict(list)
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
- # Region & Line Information
46
- self.count_reg_pts = None
47
- self.counting_region = None
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
- # Object Counting Information
53
- self.in_counts = 0
54
- self.out_counts = 0
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
- # Decay factor
61
- self.decay_factor = 0.99
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 set_args(
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
- Configures the heatmap colormap, width, height and display parameters.
48
+ Efficiently calculates heatmap area and effect location for applying colormap.
87
49
 
88
50
  Args:
89
- colormap (cv2.COLORMAP): The colormap to be set.
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
- elif len(count_reg_pts) == 4:
122
- print("Region Counter Initiated.")
123
- self.count_reg_pts = count_reg_pts
124
- self.counting_region = Polygon(self.count_reg_pts)
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
- else:
127
- print("Region or line points Invalid, 2 or 4 points supported")
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
- # Heatmap new frame
132
- self.heatmap = np.zeros((int(self.imh), int(self.imw)), dtype=np.float32)
64
+ # Calculate squared distances from the center
65
+ dist_squared = (xv - ((x0 + x1) // 2)) ** 2 + (yv - ((y0 + y1) // 2)) ** 2
133
66
 
134
- self.count_txt_thickness = count_txt_thickness
135
- self.count_txt_color = count_txt_color
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
- # shape of heatmap, if not selected
144
- if self.shape not in ["circle", "rect"]:
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 extract_results(self, tracks):
73
+ def generate_heatmap(self, im0):
150
74
  """
151
- Extracts results from the provided data.
75
+ Generate heatmap for each frame using Ultralytics.
152
76
 
153
77
  Args:
154
- tracks (list): List of tracks obtained from the object tracking process.
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
- def generate_heatmap(self, im0, tracks):
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
- Args:
165
- im0 (nd array): Image
166
- tracks (list): List of tracks obtained from the object tracking process.
83
+ Examples:
84
+ >>> heatmap = Heatmap()
85
+ >>> im0 = cv2.imread("image.jpg")
86
+ >>> result = heatmap.generate_heatmap(im0)
167
87
  """
168
- self.im0 = im0
169
- if tracks[0].boxes.id is None:
170
- self.heatmap = np.zeros((int(self.imh), int(self.imw)), dtype=np.float32)
171
- if self.view_img and self.env_check:
172
- self.display_frames()
173
- return im0
174
- self.heatmap *= self.decay_factor # decay factor
175
- self.extract_results(tracks)
176
- self.annotator = Annotator(self.im0, self.count_txt_thickness, None)
177
-
178
- if self.count_reg_pts is not None:
179
- # Draw counting region
180
- if self.view_in_counts or self.view_out_counts:
181
- self.annotator.draw_region(
182
- reg_pts=self.count_reg_pts, color=self.region_color, thickness=self.region_thickness
183
- )
184
-
185
- for box, cls, track_id in zip(self.boxes, self.clss, self.track_ids):
186
- if self.shape == "circle":
187
- center = (int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2))
188
- radius = min(int(box[2]) - int(box[0]), int(box[3]) - int(box[1])) // 2
189
-
190
- y, x = np.ogrid[0 : self.heatmap.shape[0], 0 : self.heatmap.shape[1]]
191
- mask = (x - center[0]) ** 2 + (y - center[1]) ** 2 <= radius**2
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
- heatmap_normalized = cv2.normalize(self.heatmap, None, 0, 255, cv2.NORM_MINMAX)
241
- heatmap_colored = cv2.applyColorMap(heatmap_normalized.astype(np.uint8), self.colormap)
242
-
243
- incount_label = f"In Count : {self.in_counts}"
244
- outcount_label = f"OutCount : {self.out_counts}"
245
-
246
- # Display counts based on user choice
247
- counts_label = None
248
- if not self.view_in_counts and not self.view_out_counts:
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.im0 = cv2.addWeighted(self.im0, 1 - self.heatmap_alpha, heatmap_colored, self.heatmap_alpha, 0)
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