dgenerate-ultralytics-headless 8.3.137__py3-none-any.whl → 8.3.224__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 (215) hide show
  1. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/METADATA +41 -34
  2. dgenerate_ultralytics_headless-8.3.224.dist-info/RECORD +285 -0
  3. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/WHEEL +1 -1
  4. tests/__init__.py +7 -6
  5. tests/conftest.py +15 -39
  6. tests/test_cli.py +17 -17
  7. tests/test_cuda.py +17 -8
  8. tests/test_engine.py +36 -10
  9. tests/test_exports.py +98 -37
  10. tests/test_integrations.py +12 -15
  11. tests/test_python.py +126 -82
  12. tests/test_solutions.py +319 -135
  13. ultralytics/__init__.py +27 -9
  14. ultralytics/cfg/__init__.py +83 -87
  15. ultralytics/cfg/datasets/Argoverse.yaml +4 -4
  16. ultralytics/cfg/datasets/DOTAv1.5.yaml +2 -2
  17. ultralytics/cfg/datasets/DOTAv1.yaml +2 -2
  18. ultralytics/cfg/datasets/GlobalWheat2020.yaml +2 -2
  19. ultralytics/cfg/datasets/HomeObjects-3K.yaml +4 -5
  20. ultralytics/cfg/datasets/ImageNet.yaml +3 -3
  21. ultralytics/cfg/datasets/Objects365.yaml +24 -20
  22. ultralytics/cfg/datasets/SKU-110K.yaml +9 -9
  23. ultralytics/cfg/datasets/VOC.yaml +10 -13
  24. ultralytics/cfg/datasets/VisDrone.yaml +43 -33
  25. ultralytics/cfg/datasets/african-wildlife.yaml +5 -5
  26. ultralytics/cfg/datasets/brain-tumor.yaml +4 -5
  27. ultralytics/cfg/datasets/carparts-seg.yaml +5 -5
  28. ultralytics/cfg/datasets/coco-pose.yaml +26 -4
  29. ultralytics/cfg/datasets/coco.yaml +4 -4
  30. ultralytics/cfg/datasets/coco128-seg.yaml +2 -2
  31. ultralytics/cfg/datasets/coco128.yaml +2 -2
  32. ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
  33. ultralytics/cfg/datasets/coco8-multispectral.yaml +2 -2
  34. ultralytics/cfg/datasets/coco8-pose.yaml +23 -2
  35. ultralytics/cfg/datasets/coco8-seg.yaml +2 -2
  36. ultralytics/cfg/datasets/coco8.yaml +2 -2
  37. ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
  38. ultralytics/cfg/datasets/crack-seg.yaml +5 -5
  39. ultralytics/cfg/datasets/dog-pose.yaml +32 -4
  40. ultralytics/cfg/datasets/dota8-multispectral.yaml +2 -2
  41. ultralytics/cfg/datasets/dota8.yaml +2 -2
  42. ultralytics/cfg/datasets/hand-keypoints.yaml +29 -4
  43. ultralytics/cfg/datasets/lvis.yaml +9 -9
  44. ultralytics/cfg/datasets/medical-pills.yaml +4 -5
  45. ultralytics/cfg/datasets/open-images-v7.yaml +7 -10
  46. ultralytics/cfg/datasets/package-seg.yaml +5 -5
  47. ultralytics/cfg/datasets/signature.yaml +4 -4
  48. ultralytics/cfg/datasets/tiger-pose.yaml +20 -4
  49. ultralytics/cfg/datasets/xView.yaml +5 -5
  50. ultralytics/cfg/default.yaml +96 -93
  51. ultralytics/cfg/trackers/botsort.yaml +16 -17
  52. ultralytics/cfg/trackers/bytetrack.yaml +9 -11
  53. ultralytics/data/__init__.py +4 -4
  54. ultralytics/data/annotator.py +12 -12
  55. ultralytics/data/augment.py +531 -564
  56. ultralytics/data/base.py +76 -81
  57. ultralytics/data/build.py +206 -42
  58. ultralytics/data/converter.py +179 -78
  59. ultralytics/data/dataset.py +121 -121
  60. ultralytics/data/loaders.py +114 -91
  61. ultralytics/data/split.py +28 -15
  62. ultralytics/data/split_dota.py +67 -48
  63. ultralytics/data/utils.py +110 -89
  64. ultralytics/engine/exporter.py +422 -460
  65. ultralytics/engine/model.py +224 -252
  66. ultralytics/engine/predictor.py +94 -89
  67. ultralytics/engine/results.py +345 -595
  68. ultralytics/engine/trainer.py +231 -134
  69. ultralytics/engine/tuner.py +279 -73
  70. ultralytics/engine/validator.py +53 -46
  71. ultralytics/hub/__init__.py +26 -28
  72. ultralytics/hub/auth.py +30 -16
  73. ultralytics/hub/google/__init__.py +34 -36
  74. ultralytics/hub/session.py +53 -77
  75. ultralytics/hub/utils.py +23 -109
  76. ultralytics/models/__init__.py +1 -1
  77. ultralytics/models/fastsam/__init__.py +1 -1
  78. ultralytics/models/fastsam/model.py +36 -18
  79. ultralytics/models/fastsam/predict.py +33 -44
  80. ultralytics/models/fastsam/utils.py +4 -5
  81. ultralytics/models/fastsam/val.py +12 -14
  82. ultralytics/models/nas/__init__.py +1 -1
  83. ultralytics/models/nas/model.py +16 -20
  84. ultralytics/models/nas/predict.py +12 -14
  85. ultralytics/models/nas/val.py +4 -5
  86. ultralytics/models/rtdetr/__init__.py +1 -1
  87. ultralytics/models/rtdetr/model.py +9 -9
  88. ultralytics/models/rtdetr/predict.py +22 -17
  89. ultralytics/models/rtdetr/train.py +20 -16
  90. ultralytics/models/rtdetr/val.py +79 -59
  91. ultralytics/models/sam/__init__.py +8 -2
  92. ultralytics/models/sam/amg.py +53 -38
  93. ultralytics/models/sam/build.py +29 -31
  94. ultralytics/models/sam/model.py +33 -38
  95. ultralytics/models/sam/modules/blocks.py +159 -182
  96. ultralytics/models/sam/modules/decoders.py +38 -47
  97. ultralytics/models/sam/modules/encoders.py +114 -133
  98. ultralytics/models/sam/modules/memory_attention.py +38 -31
  99. ultralytics/models/sam/modules/sam.py +114 -93
  100. ultralytics/models/sam/modules/tiny_encoder.py +268 -291
  101. ultralytics/models/sam/modules/transformer.py +59 -66
  102. ultralytics/models/sam/modules/utils.py +55 -72
  103. ultralytics/models/sam/predict.py +745 -341
  104. ultralytics/models/utils/loss.py +118 -107
  105. ultralytics/models/utils/ops.py +118 -71
  106. ultralytics/models/yolo/__init__.py +1 -1
  107. ultralytics/models/yolo/classify/predict.py +28 -26
  108. ultralytics/models/yolo/classify/train.py +50 -81
  109. ultralytics/models/yolo/classify/val.py +68 -61
  110. ultralytics/models/yolo/detect/predict.py +12 -15
  111. ultralytics/models/yolo/detect/train.py +56 -46
  112. ultralytics/models/yolo/detect/val.py +279 -223
  113. ultralytics/models/yolo/model.py +167 -86
  114. ultralytics/models/yolo/obb/predict.py +7 -11
  115. ultralytics/models/yolo/obb/train.py +23 -25
  116. ultralytics/models/yolo/obb/val.py +107 -99
  117. ultralytics/models/yolo/pose/__init__.py +1 -1
  118. ultralytics/models/yolo/pose/predict.py +12 -14
  119. ultralytics/models/yolo/pose/train.py +31 -69
  120. ultralytics/models/yolo/pose/val.py +119 -254
  121. ultralytics/models/yolo/segment/predict.py +21 -25
  122. ultralytics/models/yolo/segment/train.py +12 -66
  123. ultralytics/models/yolo/segment/val.py +126 -305
  124. ultralytics/models/yolo/world/train.py +53 -45
  125. ultralytics/models/yolo/world/train_world.py +51 -32
  126. ultralytics/models/yolo/yoloe/__init__.py +7 -7
  127. ultralytics/models/yolo/yoloe/predict.py +30 -37
  128. ultralytics/models/yolo/yoloe/train.py +89 -71
  129. ultralytics/models/yolo/yoloe/train_seg.py +15 -17
  130. ultralytics/models/yolo/yoloe/val.py +56 -41
  131. ultralytics/nn/__init__.py +9 -11
  132. ultralytics/nn/autobackend.py +179 -107
  133. ultralytics/nn/modules/__init__.py +67 -67
  134. ultralytics/nn/modules/activation.py +8 -7
  135. ultralytics/nn/modules/block.py +302 -323
  136. ultralytics/nn/modules/conv.py +61 -104
  137. ultralytics/nn/modules/head.py +488 -186
  138. ultralytics/nn/modules/transformer.py +183 -123
  139. ultralytics/nn/modules/utils.py +15 -20
  140. ultralytics/nn/tasks.py +327 -203
  141. ultralytics/nn/text_model.py +81 -65
  142. ultralytics/py.typed +1 -0
  143. ultralytics/solutions/__init__.py +12 -12
  144. ultralytics/solutions/ai_gym.py +19 -27
  145. ultralytics/solutions/analytics.py +36 -26
  146. ultralytics/solutions/config.py +29 -28
  147. ultralytics/solutions/distance_calculation.py +23 -24
  148. ultralytics/solutions/heatmap.py +17 -19
  149. ultralytics/solutions/instance_segmentation.py +21 -19
  150. ultralytics/solutions/object_blurrer.py +16 -17
  151. ultralytics/solutions/object_counter.py +48 -53
  152. ultralytics/solutions/object_cropper.py +22 -16
  153. ultralytics/solutions/parking_management.py +61 -58
  154. ultralytics/solutions/queue_management.py +19 -19
  155. ultralytics/solutions/region_counter.py +63 -50
  156. ultralytics/solutions/security_alarm.py +22 -25
  157. ultralytics/solutions/similarity_search.py +107 -60
  158. ultralytics/solutions/solutions.py +343 -262
  159. ultralytics/solutions/speed_estimation.py +35 -31
  160. ultralytics/solutions/streamlit_inference.py +104 -40
  161. ultralytics/solutions/templates/similarity-search.html +31 -24
  162. ultralytics/solutions/trackzone.py +24 -24
  163. ultralytics/solutions/vision_eye.py +11 -12
  164. ultralytics/trackers/__init__.py +1 -1
  165. ultralytics/trackers/basetrack.py +18 -27
  166. ultralytics/trackers/bot_sort.py +48 -39
  167. ultralytics/trackers/byte_tracker.py +94 -94
  168. ultralytics/trackers/track.py +7 -16
  169. ultralytics/trackers/utils/gmc.py +37 -69
  170. ultralytics/trackers/utils/kalman_filter.py +68 -76
  171. ultralytics/trackers/utils/matching.py +13 -17
  172. ultralytics/utils/__init__.py +251 -275
  173. ultralytics/utils/autobatch.py +19 -7
  174. ultralytics/utils/autodevice.py +68 -38
  175. ultralytics/utils/benchmarks.py +169 -130
  176. ultralytics/utils/callbacks/base.py +12 -13
  177. ultralytics/utils/callbacks/clearml.py +14 -15
  178. ultralytics/utils/callbacks/comet.py +139 -66
  179. ultralytics/utils/callbacks/dvc.py +19 -27
  180. ultralytics/utils/callbacks/hub.py +8 -6
  181. ultralytics/utils/callbacks/mlflow.py +6 -10
  182. ultralytics/utils/callbacks/neptune.py +11 -19
  183. ultralytics/utils/callbacks/platform.py +73 -0
  184. ultralytics/utils/callbacks/raytune.py +3 -4
  185. ultralytics/utils/callbacks/tensorboard.py +9 -12
  186. ultralytics/utils/callbacks/wb.py +33 -30
  187. ultralytics/utils/checks.py +163 -114
  188. ultralytics/utils/cpu.py +89 -0
  189. ultralytics/utils/dist.py +24 -20
  190. ultralytics/utils/downloads.py +176 -146
  191. ultralytics/utils/errors.py +11 -13
  192. ultralytics/utils/events.py +113 -0
  193. ultralytics/utils/export/__init__.py +7 -0
  194. ultralytics/utils/{export.py → export/engine.py} +81 -63
  195. ultralytics/utils/export/imx.py +294 -0
  196. ultralytics/utils/export/tensorflow.py +217 -0
  197. ultralytics/utils/files.py +33 -36
  198. ultralytics/utils/git.py +137 -0
  199. ultralytics/utils/instance.py +105 -120
  200. ultralytics/utils/logger.py +404 -0
  201. ultralytics/utils/loss.py +99 -61
  202. ultralytics/utils/metrics.py +649 -478
  203. ultralytics/utils/nms.py +337 -0
  204. ultralytics/utils/ops.py +263 -451
  205. ultralytics/utils/patches.py +70 -31
  206. ultralytics/utils/plotting.py +253 -223
  207. ultralytics/utils/tal.py +48 -61
  208. ultralytics/utils/torch_utils.py +244 -251
  209. ultralytics/utils/tqdm.py +438 -0
  210. ultralytics/utils/triton.py +22 -23
  211. ultralytics/utils/tuner.py +11 -10
  212. dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD +0 -272
  213. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/entry_points.txt +0 -0
  214. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/licenses/LICENSE +0 -0
  215. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,16 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections import defaultdict
6
+ from typing import Any
4
7
 
5
8
  from ultralytics.solutions.solutions import BaseSolution, SolutionAnnotator, SolutionResults
6
9
  from ultralytics.utils.plotting import colors
7
10
 
8
11
 
9
12
  class ObjectCounter(BaseSolution):
10
- """
11
- A class to manage the counting of objects in a real-time video stream based on their tracks.
13
+ """A class to manage the counting of objects in a real-time video stream based on their tracks.
12
14
 
13
15
  This class extends the BaseSolution class and provides functionality for counting objects moving in and out of a
14
16
  specified region in a video stream. It supports both polygonal and linear regions for counting.
@@ -16,16 +18,17 @@ class ObjectCounter(BaseSolution):
16
18
  Attributes:
17
19
  in_count (int): Counter for objects moving inward.
18
20
  out_count (int): Counter for objects moving outward.
19
- counted_ids (List[int]): List of IDs of objects that have been counted.
20
- classwise_counts (Dict[str, Dict[str, int]]): Dictionary for counts, categorized by object class.
21
+ counted_ids (list[int]): List of IDs of objects that have been counted.
22
+ classwise_counts (dict[str, dict[str, int]]): Dictionary for counts, categorized by object class.
21
23
  region_initialized (bool): Flag indicating whether the counting region has been initialized.
22
24
  show_in (bool): Flag to control display of inward count.
23
25
  show_out (bool): Flag to control display of outward count.
26
+ margin (int): Margin for background rectangle size to display counts properly.
24
27
 
25
28
  Methods:
26
- count_objects: Counts objects within a polygonal or linear region.
27
- display_counts: Displays object counts on the frame.
28
- process: Processes input data (frames or object tracks) and updates counts.
29
+ count_objects: Count objects within a polygonal or linear region based on their tracks.
30
+ display_counts: Display object counts on the frame.
31
+ process: Process input data and update counts.
29
32
 
30
33
  Examples:
31
34
  >>> counter = ObjectCounter()
@@ -34,28 +37,33 @@ class ObjectCounter(BaseSolution):
34
37
  >>> print(f"Inward count: {counter.in_count}, Outward count: {counter.out_count}")
35
38
  """
36
39
 
37
- def __init__(self, **kwargs):
38
- """Initializes the ObjectCounter class for real-time object counting in video streams."""
40
+ def __init__(self, **kwargs: Any) -> None:
41
+ """Initialize the ObjectCounter class for real-time object counting in video streams."""
39
42
  super().__init__(**kwargs)
40
43
 
41
44
  self.in_count = 0 # Counter for objects moving inward
42
45
  self.out_count = 0 # Counter for objects moving outward
43
46
  self.counted_ids = [] # List of IDs of objects that have been counted
44
- self.classwise_counts = defaultdict(lambda: {"IN": 0, "OUT": 0}) # Dictionary for counts, categorized by class
47
+ self.classwise_count = defaultdict(lambda: {"IN": 0, "OUT": 0}) # Dictionary for counts, categorized by class
45
48
  self.region_initialized = False # Flag indicating whether the region has been initialized
46
49
 
47
50
  self.show_in = self.CFG["show_in"]
48
51
  self.show_out = self.CFG["show_out"]
49
52
  self.margin = self.line_width * 2 # Scales the background rectangle size to display counts properly
50
53
 
51
- def count_objects(self, current_centroid, track_id, prev_position, cls):
52
- """
53
- Counts objects within a polygonal or linear region based on their tracks.
54
+ def count_objects(
55
+ self,
56
+ current_centroid: tuple[float, float],
57
+ track_id: int,
58
+ prev_position: tuple[float, float] | None,
59
+ cls: int,
60
+ ) -> None:
61
+ """Count objects within a polygonal or linear region based on their tracks.
54
62
 
55
63
  Args:
56
- current_centroid (Tuple[float, float]): Current centroid coordinates (x, y) in the current frame.
64
+ current_centroid (tuple[float, float]): Current centroid coordinates (x, y) in the current frame.
57
65
  track_id (int): Unique identifier for the tracked object.
58
- prev_position (Tuple[float, float]): Last frame position coordinates (x, y) of the track.
66
+ prev_position (tuple[float, float], optional): Last frame position coordinates (x, y) of the track.
59
67
  cls (int): Class index for classwise count updates.
60
68
 
61
69
  Examples:
@@ -71,52 +79,46 @@ class ObjectCounter(BaseSolution):
71
79
  return
72
80
 
73
81
  if len(self.region) == 2: # Linear region (defined as a line segment)
74
- line = self.LineString(self.region) # Check if the line intersects the trajectory of the object
75
- if line.intersects(self.LineString([prev_position, current_centroid])):
82
+ if self.r_s.intersects(self.LineString([prev_position, current_centroid])):
76
83
  # Determine orientation of the region (vertical or horizontal)
77
84
  if abs(self.region[0][0] - self.region[1][0]) < abs(self.region[0][1] - self.region[1][1]):
78
85
  # Vertical region: Compare x-coordinates to determine direction
79
86
  if current_centroid[0] > prev_position[0]: # Moving right
80
87
  self.in_count += 1
81
- self.classwise_counts[self.names[cls]]["IN"] += 1
88
+ self.classwise_count[self.names[cls]]["IN"] += 1
82
89
  else: # Moving left
83
90
  self.out_count += 1
84
- self.classwise_counts[self.names[cls]]["OUT"] += 1
91
+ self.classwise_count[self.names[cls]]["OUT"] += 1
85
92
  # Horizontal region: Compare y-coordinates to determine direction
86
93
  elif current_centroid[1] > prev_position[1]: # Moving downward
87
94
  self.in_count += 1
88
- self.classwise_counts[self.names[cls]]["IN"] += 1
95
+ self.classwise_count[self.names[cls]]["IN"] += 1
89
96
  else: # Moving upward
90
97
  self.out_count += 1
91
- self.classwise_counts[self.names[cls]]["OUT"] += 1
98
+ self.classwise_count[self.names[cls]]["OUT"] += 1
92
99
  self.counted_ids.append(track_id)
93
100
 
94
101
  elif len(self.region) > 2: # Polygonal region
95
- polygon = self.Polygon(self.region)
96
- if polygon.contains(self.Point(current_centroid)):
102
+ if self.r_s.contains(self.Point(current_centroid)):
97
103
  # Determine motion direction for vertical or horizontal polygons
98
104
  region_width = max(p[0] for p in self.region) - min(p[0] for p in self.region)
99
105
  region_height = max(p[1] for p in self.region) - min(p[1] for p in self.region)
100
106
 
101
- if (
102
- region_width < region_height
103
- and current_centroid[0] > prev_position[0]
104
- or region_width >= region_height
105
- and current_centroid[1] > prev_position[1]
107
+ if (region_width < region_height and current_centroid[0] > prev_position[0]) or (
108
+ region_width >= region_height and current_centroid[1] > prev_position[1]
106
109
  ): # Moving right or downward
107
110
  self.in_count += 1
108
- self.classwise_counts[self.names[cls]]["IN"] += 1
111
+ self.classwise_count[self.names[cls]]["IN"] += 1
109
112
  else: # Moving left or upward
110
113
  self.out_count += 1
111
- self.classwise_counts[self.names[cls]]["OUT"] += 1
114
+ self.classwise_count[self.names[cls]]["OUT"] += 1
112
115
  self.counted_ids.append(track_id)
113
116
 
114
- def display_counts(self, plot_im):
115
- """
116
- Display object counts on the input image or frame.
117
+ def display_counts(self, plot_im) -> None:
118
+ """Display object counts on the input image or frame.
117
119
 
118
120
  Args:
119
- plot_im (numpy.ndarray): The image or frame to display counts on.
121
+ plot_im (np.ndarray): The image or frame to display counts on.
120
122
 
121
123
  Examples:
122
124
  >>> counter = ObjectCounter()
@@ -126,26 +128,25 @@ class ObjectCounter(BaseSolution):
126
128
  labels_dict = {
127
129
  str.capitalize(key): f"{'IN ' + str(value['IN']) if self.show_in else ''} "
128
130
  f"{'OUT ' + str(value['OUT']) if self.show_out else ''}".strip()
129
- for key, value in self.classwise_counts.items()
130
- if value["IN"] != 0 or value["OUT"] != 0
131
+ for key, value in self.classwise_count.items()
132
+ if value["IN"] != 0 or (value["OUT"] != 0 and (self.show_in or self.show_out))
131
133
  }
132
134
  if labels_dict:
133
135
  self.annotator.display_analytics(plot_im, labels_dict, (104, 31, 17), (255, 255, 255), self.margin)
134
136
 
135
- def process(self, im0):
136
- """
137
- Process input data (frames or object tracks) and update object counts.
137
+ def process(self, im0) -> SolutionResults:
138
+ """Process input data (frames or object tracks) and update object counts.
138
139
 
139
- This method initializes the counting region, extracts tracks, draws bounding boxes and regions, updates
140
- object counts, and displays the results on the input image.
140
+ This method initializes the counting region, extracts tracks, draws bounding boxes and regions, updates object
141
+ counts, and displays the results on the input image.
141
142
 
142
143
  Args:
143
- im0 (numpy.ndarray): The input image or frame to be processed.
144
+ im0 (np.ndarray): The input image or frame to be processed.
144
145
 
145
146
  Returns:
146
147
  (SolutionResults): Contains processed image `im0`, 'in_count' (int, count of objects entering the region),
147
- 'out_count' (int, count of objects exiting the region), 'classwise_count' (dict, per-class object count),
148
- and 'total_tracks' (int, total number of tracked objects).
148
+ 'out_count' (int, count of objects exiting the region), 'classwise_count' (dict, per-class object
149
+ count), and 'total_tracks' (int, total number of tracked objects).
149
150
 
150
151
  Examples:
151
152
  >>> counter = ObjectCounter()
@@ -159,10 +160,6 @@ class ObjectCounter(BaseSolution):
159
160
  self.extract_tracks(im0) # Extract tracks
160
161
  self.annotator = SolutionAnnotator(im0, line_width=self.line_width) # Initialize annotator
161
162
 
162
- is_obb = getattr(self.tracks[0], "obb", None) is not None # True if OBB results exist
163
- if is_obb:
164
- self.boxes = self.track_data.xyxyxyxy.reshape(-1, 4, 2).cpu()
165
-
166
163
  self.annotator.draw_region(
167
164
  reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2
168
165
  ) # Draw region
@@ -170,10 +167,8 @@ class ObjectCounter(BaseSolution):
170
167
  # Iterate over bounding boxes, track ids and classes index
171
168
  for box, track_id, cls, conf in zip(self.boxes, self.track_ids, self.clss, self.confs):
172
169
  # Draw bounding box and counting region
173
- self.annotator.box_label(
174
- box, label=self.adjust_box_label(cls, conf, track_id), color=colors(cls, True), rotated=is_obb
175
- )
176
- self.store_tracking_history(track_id, box, is_obb=is_obb) # Store track history
170
+ self.annotator.box_label(box, label=self.adjust_box_label(cls, conf, track_id), color=colors(cls, True))
171
+ self.store_tracking_history(track_id, box) # Store track history
177
172
 
178
173
  # Store previous position of track for object counting
179
174
  prev_position = None
@@ -190,6 +185,6 @@ class ObjectCounter(BaseSolution):
190
185
  plot_im=plot_im,
191
186
  in_count=self.in_count,
192
187
  out_count=self.out_count,
193
- classwise_count=dict(self.classwise_counts),
188
+ classwise_count=dict(self.classwise_count),
194
189
  total_tracks=len(self.track_ids),
195
190
  )
@@ -2,14 +2,14 @@
2
2
 
3
3
  import os
4
4
  from pathlib import Path
5
+ from typing import Any
5
6
 
6
7
  from ultralytics.solutions.solutions import BaseSolution, SolutionResults
7
8
  from ultralytics.utils.plotting import save_one_box
8
9
 
9
10
 
10
11
  class ObjectCropper(BaseSolution):
11
- """
12
- A class to manage the cropping of detected objects in a real-time video stream or images.
12
+ """A class to manage the cropping of detected objects in a real-time video stream or images.
13
13
 
14
14
  This class extends the BaseSolution class and provides functionality for cropping objects based on detected bounding
15
15
  boxes. The cropped images are saved to a specified directory for further analysis or usage.
@@ -21,7 +21,7 @@ class ObjectCropper(BaseSolution):
21
21
  conf (float): Confidence threshold for filtering detections.
22
22
 
23
23
  Methods:
24
- process: Crops detected objects from the input image and saves them to the output directory.
24
+ process: Crop detected objects from the input image and save them to the output directory.
25
25
 
26
26
  Examples:
27
27
  >>> cropper = ObjectCropper()
@@ -30,13 +30,12 @@ class ObjectCropper(BaseSolution):
30
30
  >>> print(f"Total cropped objects: {cropper.crop_idx}")
31
31
  """
32
32
 
33
- def __init__(self, **kwargs):
34
- """
35
- Initialize the ObjectCropper class for cropping objects from detected bounding boxes.
33
+ def __init__(self, **kwargs: Any) -> None:
34
+ """Initialize the ObjectCropper class for cropping objects from detected bounding boxes.
36
35
 
37
36
  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.
37
+ **kwargs (Any): Keyword arguments passed to the parent class and used for configuration including:
38
+ - crop_dir (str): Path to the directory for saving cropped object images.
40
39
  """
41
40
  super().__init__(**kwargs)
42
41
 
@@ -51,15 +50,15 @@ class ObjectCropper(BaseSolution):
51
50
  self.iou = self.CFG["iou"]
52
51
  self.conf = self.CFG["conf"]
53
52
 
54
- def process(self, im0):
55
- """
56
- Crop detected objects from the input image and save them as separate images.
53
+ def process(self, im0) -> SolutionResults:
54
+ """Crop detected objects from the input image and save them as separate images.
57
55
 
58
56
  Args:
59
- im0 (numpy.ndarray): The input image containing detected objects.
57
+ im0 (np.ndarray): The input image containing detected objects.
60
58
 
61
59
  Returns:
62
- (SolutionResults): A SolutionResults object containing the total number of cropped objects and processed image.
60
+ (SolutionResults): A SolutionResults object containing the total number of cropped objects and processed
61
+ image.
63
62
 
64
63
  Examples:
65
64
  >>> cropper = ObjectCropper()
@@ -67,9 +66,16 @@ class ObjectCropper(BaseSolution):
67
66
  >>> results = cropper.process(frame)
68
67
  >>> print(f"Total cropped objects: {results.total_crop_objects}")
69
68
  """
70
- results = self.model.predict(
71
- im0, classes=self.classes, conf=self.conf, iou=self.iou, device=self.CFG["device"]
72
- )[0]
69
+ with self.profilers[0]:
70
+ results = self.model.predict(
71
+ im0,
72
+ classes=self.classes,
73
+ conf=self.conf,
74
+ iou=self.iou,
75
+ device=self.CFG["device"],
76
+ verbose=False,
77
+ )[0]
78
+ self.clss = results.boxes.cls.tolist() # required for logging only.
73
79
 
74
80
  for box in results.boxes:
75
81
  self.crop_idx += 1
@@ -1,6 +1,9 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import json
6
+ from typing import Any
4
7
 
5
8
  import cv2
6
9
  import numpy as np
@@ -11,11 +14,10 @@ from ultralytics.utils.checks import check_imshow
11
14
 
12
15
 
13
16
  class ParkingPtsSelection:
14
- """
15
- A class for selecting and managing parking zone points on images using a Tkinter-based UI.
17
+ """A class for selecting and managing parking zone points on images using a Tkinter-based UI.
16
18
 
17
- This class provides functionality to upload an image, select points to define parking zones, and save the
18
- selected points to a JSON file. It uses Tkinter for the graphical user interface.
19
+ This class provides functionality to upload an image, select points to define parking zones, and save the selected
20
+ points to a JSON file. It uses Tkinter for the graphical user interface.
19
21
 
20
22
  Attributes:
21
23
  tk (module): The Tkinter module for GUI operations.
@@ -25,30 +27,30 @@ class ParkingPtsSelection:
25
27
  canvas (tk.Canvas): The canvas widget for displaying the image and drawing bounding boxes.
26
28
  image (PIL.Image.Image): The uploaded image.
27
29
  canvas_image (ImageTk.PhotoImage): The image displayed on the canvas.
28
- rg_data (List[List[Tuple[int, int]]]): List of bounding boxes, each defined by 4 points.
29
- current_box (List[Tuple[int, int]]): Temporary storage for the points of the current bounding box.
30
+ rg_data (list[list[tuple[int, int]]]): List of bounding boxes, each defined by 4 points.
31
+ current_box (list[tuple[int, int]]): Temporary storage for the points of the current bounding box.
30
32
  imgw (int): Original width of the uploaded image.
31
33
  imgh (int): Original height of the uploaded image.
32
34
  canvas_max_width (int): Maximum width of the canvas.
33
35
  canvas_max_height (int): Maximum height of the canvas.
34
36
 
35
37
  Methods:
36
- initialize_properties: Initializes the necessary properties.
37
- upload_image: Uploads an image, resizes it to fit the canvas, and displays it.
38
- on_canvas_click: Handles mouse clicks to add points for bounding boxes.
39
- draw_box: Draws a bounding box on the canvas.
40
- remove_last_bounding_box: Removes the last bounding box and redraws the canvas.
41
- redraw_canvas: Redraws the canvas with the image and all bounding boxes.
42
- save_to_json: Saves the bounding boxes to a JSON file.
38
+ initialize_properties: Initialize properties for image, canvas, bounding boxes, and dimensions.
39
+ upload_image: Upload and display an image on the canvas, resizing it to fit within specified dimensions.
40
+ on_canvas_click: Handle mouse clicks to add points for bounding boxes on the canvas.
41
+ draw_box: Draw a bounding box on the canvas using the provided coordinates.
42
+ remove_last_bounding_box: Remove the last bounding box from the list and redraw the canvas.
43
+ redraw_canvas: Redraw the canvas with the image and all bounding boxes.
44
+ save_to_json: Save the selected parking zone points to a JSON file with scaled coordinates.
43
45
 
44
46
  Examples:
45
47
  >>> parking_selector = ParkingPtsSelection()
46
48
  >>> # Use the GUI to upload an image, select parking zones, and save the data
47
49
  """
48
50
 
49
- def __init__(self):
51
+ def __init__(self) -> None:
50
52
  """Initialize the ParkingPtsSelection class, setting up UI and properties for parking zone point selection."""
51
- try: # check if tkinter installed
53
+ try: # Check if tkinter is installed
52
54
  import tkinter as tk
53
55
  from tkinter import filedialog, messagebox
54
56
  except ImportError: # Display error with recommendations
@@ -68,19 +70,19 @@ class ParkingPtsSelection:
68
70
  return
69
71
 
70
72
  self.tk, self.filedialog, self.messagebox = tk, filedialog, messagebox
71
- self.master = self.tk.Tk() # Reference to the main application window or parent widget
73
+ self.master = self.tk.Tk() # Reference to the main application window
72
74
  self.master.title("Ultralytics Parking Zones Points Selector")
73
75
  self.master.resizable(False, False)
74
76
 
75
- self.canvas = self.tk.Canvas(self.master, bg="white") # Canvas widget for displaying images or graphics
77
+ self.canvas = self.tk.Canvas(self.master, bg="white") # Canvas widget for displaying images
76
78
  self.canvas.pack(side=self.tk.BOTTOM)
77
79
 
78
80
  self.image = None # Variable to store the loaded image
79
81
  self.canvas_image = None # Reference to the image displayed on the canvas
80
82
  self.canvas_max_width = None # Maximum allowed width for the canvas
81
83
  self.canvas_max_height = None # Maximum allowed height for the canvas
82
- self.rg_data = None # Data related to region or annotation management
83
- self.current_box = None # Stores the currently selected or active bounding box
84
+ self.rg_data = None # Data for region annotation management
85
+ self.current_box = None # Stores the currently selected bounding box
84
86
  self.imgh = None # Height of the current image
85
87
  self.imgw = None # Width of the current image
86
88
 
@@ -98,21 +100,23 @@ class ParkingPtsSelection:
98
100
  self.initialize_properties()
99
101
  self.master.mainloop()
100
102
 
101
- def initialize_properties(self):
103
+ def initialize_properties(self) -> None:
102
104
  """Initialize properties for image, canvas, bounding boxes, and dimensions."""
103
105
  self.image = self.canvas_image = None
104
106
  self.rg_data, self.current_box = [], []
105
107
  self.imgw = self.imgh = 0
106
108
  self.canvas_max_width, self.canvas_max_height = 1280, 720
107
109
 
108
- def upload_image(self):
110
+ def upload_image(self) -> None:
109
111
  """Upload and display an image on the canvas, resizing it to fit within specified dimensions."""
110
- from PIL import Image, ImageTk # scope because ImageTk requires tkinter package
112
+ from PIL import Image, ImageTk # Scoped import because ImageTk requires tkinter package
111
113
 
112
- self.image = Image.open(self.filedialog.askopenfilename(filetypes=[("Image Files", "*.png *.jpg *.jpeg")]))
113
- if not self.image:
114
+ file = self.filedialog.askopenfilename(filetypes=[("Image Files", "*.png *.jpg *.jpeg")])
115
+ if not file:
116
+ LOGGER.info("No image selected.")
114
117
  return
115
118
 
119
+ self.image = Image.open(file)
116
120
  self.imgw, self.imgh = self.image.size
117
121
  aspect_ratio = self.imgw / self.imgh
118
122
  canvas_width = (
@@ -129,7 +133,7 @@ class ParkingPtsSelection:
129
133
 
130
134
  self.rg_data.clear(), self.current_box.clear()
131
135
 
132
- def on_canvas_click(self, event):
136
+ def on_canvas_click(self, event) -> None:
133
137
  """Handle mouse clicks to add points for bounding boxes on the canvas."""
134
138
  self.current_box.append((event.x, event.y))
135
139
  self.canvas.create_oval(event.x - 3, event.y - 3, event.x + 3, event.y + 3, fill="red")
@@ -138,12 +142,12 @@ class ParkingPtsSelection:
138
142
  self.draw_box(self.current_box)
139
143
  self.current_box.clear()
140
144
 
141
- def draw_box(self, box):
145
+ def draw_box(self, box: list[tuple[int, int]]) -> None:
142
146
  """Draw a bounding box on the canvas using the provided coordinates."""
143
147
  for i in range(4):
144
148
  self.canvas.create_line(box[i], box[(i + 1) % 4], fill="blue", width=2)
145
149
 
146
- def remove_last_bounding_box(self):
150
+ def remove_last_bounding_box(self) -> None:
147
151
  """Remove the last bounding box from the list and redraw the canvas."""
148
152
  if not self.rg_data:
149
153
  self.messagebox.showwarning("Warning", "No bounding boxes to remove.")
@@ -151,19 +155,19 @@ class ParkingPtsSelection:
151
155
  self.rg_data.pop()
152
156
  self.redraw_canvas()
153
157
 
154
- def redraw_canvas(self):
158
+ def redraw_canvas(self) -> None:
155
159
  """Redraw the canvas with the image and all bounding boxes."""
156
160
  self.canvas.delete("all")
157
161
  self.canvas.create_image(0, 0, anchor=self.tk.NW, image=self.canvas_image)
158
162
  for box in self.rg_data:
159
163
  self.draw_box(box)
160
164
 
161
- def save_to_json(self):
165
+ def save_to_json(self) -> None:
162
166
  """Save the selected parking zone points to a JSON file with scaled coordinates."""
163
167
  scale_w, scale_h = self.imgw / self.canvas.winfo_width(), self.imgh / self.canvas.winfo_height()
164
168
  data = [{"points": [(int(x * scale_w), int(y * scale_h)) for x, y in box]} for box in self.rg_data]
165
169
 
166
- from io import StringIO # Function level import, as it's only required to store coordinates, not every frame
170
+ from io import StringIO # Function level import, as it's only required to store coordinates
167
171
 
168
172
  write_buffer = StringIO()
169
173
  json.dump(data, write_buffer, indent=4)
@@ -173,22 +177,21 @@ class ParkingPtsSelection:
173
177
 
174
178
 
175
179
  class ParkingManagement(BaseSolution):
176
- """
177
- Manages parking occupancy and availability using YOLO model for real-time monitoring and visualization.
180
+ """Manages parking occupancy and availability using YOLO model for real-time monitoring and visualization.
178
181
 
179
- This class extends BaseSolution to provide functionality for parking lot management, including detection of
180
- occupied spaces, visualization of parking regions, and display of occupancy statistics.
182
+ This class extends BaseSolution to provide functionality for parking lot management, including detection of occupied
183
+ spaces, visualization of parking regions, and display of occupancy statistics.
181
184
 
182
185
  Attributes:
183
186
  json_file (str): Path to the JSON file containing parking region details.
184
- json (List[Dict]): Loaded JSON data containing parking region information.
185
- pr_info (Dict[str, int]): Dictionary storing parking information (Occupancy and Available spaces).
186
- arc (Tuple[int, int, int]): RGB color tuple for available region visualization.
187
- occ (Tuple[int, int, int]): RGB color tuple for occupied region visualization.
188
- dc (Tuple[int, int, int]): RGB color tuple for centroid visualization of detected objects.
187
+ json (list[dict]): Loaded JSON data containing parking region information.
188
+ pr_info (dict[str, int]): Dictionary storing parking information (Occupancy and Available spaces).
189
+ arc (tuple[int, int, int]): RGB color tuple for available region visualization.
190
+ occ (tuple[int, int, int]): RGB color tuple for occupied region visualization.
191
+ dc (tuple[int, int, int]): RGB color tuple for centroid visualization of detected objects.
189
192
 
190
193
  Methods:
191
- process: Processes the input image for parking lot management and visualization.
194
+ process: Process the input image for parking lot management and visualization.
192
195
 
193
196
  Examples:
194
197
  >>> from ultralytics.solutions import ParkingManagement
@@ -197,7 +200,7 @@ class ParkingManagement(BaseSolution):
197
200
  >>> print(f"Available spaces: {parking_manager.pr_info['Available']}")
198
201
  """
199
202
 
200
- def __init__(self, **kwargs):
203
+ def __init__(self, **kwargs: Any) -> None:
201
204
  """Initialize the parking management system with a YOLO model and visualization settings."""
202
205
  super().__init__(**kwargs)
203
206
 
@@ -209,40 +212,40 @@ class ParkingManagement(BaseSolution):
209
212
  with open(self.json_file) as f:
210
213
  self.json = json.load(f)
211
214
 
212
- self.pr_info = {"Occupancy": 0, "Available": 0} # dictionary for parking information
215
+ self.pr_info = {"Occupancy": 0, "Available": 0} # Dictionary for parking information
213
216
 
214
- self.arc = (0, 0, 255) # available region color
215
- self.occ = (0, 255, 0) # occupied region color
216
- self.dc = (255, 0, 189) # centroid color for each box
217
+ self.arc = (0, 0, 255) # Available region color
218
+ self.occ = (0, 255, 0) # Occupied region color
219
+ self.dc = (255, 0, 189) # Centroid color for each box
217
220
 
218
- def process(self, im0):
219
- """
220
- Process the input image for parking lot management and visualization.
221
+ def process(self, im0: np.ndarray) -> SolutionResults:
222
+ """Process the input image for parking lot management and visualization.
221
223
 
222
- This function analyzes the input image, extracts tracks, and determines the occupancy status of parking
223
- regions defined in the JSON file. It annotates the image with occupied and available parking spots,
224
- and updates the parking information.
224
+ This function analyzes the input image, extracts tracks, and determines the occupancy status of parking regions
225
+ defined in the JSON file. It annotates the image with occupied and available parking spots, and updates the
226
+ parking information.
225
227
 
226
228
  Args:
227
229
  im0 (np.ndarray): The input inference image.
228
230
 
229
231
  Returns:
230
232
  (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).
233
+ 'available_slots' (number of available parking slots), and 'total_tracks' (total number of
234
+ tracked objects).
232
235
 
233
236
  Examples:
234
237
  >>> parking_manager = ParkingManagement(json_file="parking_regions.json")
235
238
  >>> image = cv2.imread("parking_lot.jpg")
236
239
  >>> results = parking_manager.process(image)
237
240
  """
238
- self.extract_tracks(im0) # extract tracks from im0
239
- es, fs = len(self.json), 0 # empty slots, filled slots
240
- annotator = SolutionAnnotator(im0, self.line_width) # init annotator
241
+ self.extract_tracks(im0) # Extract tracks from im0
242
+ es, fs = len(self.json), 0 # Empty slots, filled slots
243
+ annotator = SolutionAnnotator(im0, self.line_width) # Initialize annotator
241
244
 
242
245
  for region in self.json:
243
246
  # Convert points to a NumPy array with the correct dtype and reshape properly
244
247
  pts_array = np.array(region["points"], dtype=np.int32).reshape((-1, 1, 2))
245
- rg_occupied = False # occupied region initialization
248
+ rg_occupied = False # Occupied region initialization
246
249
  for box, cls in zip(self.boxes, self.clss):
247
250
  xc, yc = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
248
251
  dist = cv2.pointPolygonTest(pts_array, (xc, yc), False)
@@ -254,7 +257,7 @@ class ParkingManagement(BaseSolution):
254
257
  rg_occupied = True
255
258
  break
256
259
  fs, es = (fs + 1, es - 1) if rg_occupied else (fs, es)
257
- # Plotting regions
260
+ # Plot regions
258
261
  cv2.polylines(im0, [pts_array], isClosed=True, color=self.occ if rg_occupied else self.arc, thickness=2)
259
262
 
260
263
  self.pr_info["Occupancy"], self.pr_info["Available"] = fs, es
@@ -262,7 +265,7 @@ class ParkingManagement(BaseSolution):
262
265
  annotator.display_analytics(im0, self.pr_info, (104, 31, 17), (255, 255, 255), 10)
263
266
 
264
267
  plot_im = annotator.result()
265
- self.display_output(plot_im) # display output with base class function
268
+ self.display_output(plot_im) # Display output with base class function
266
269
 
267
270
  # Return SolutionResults
268
271
  return SolutionResults(