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,42 +1,67 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import math
4
- from collections import defaultdict
6
+ from collections import Counter, defaultdict
7
+ from functools import lru_cache
8
+ from typing import Any
5
9
 
6
10
  import cv2
7
11
  import numpy as np
8
12
 
9
13
  from ultralytics import YOLO
10
14
  from ultralytics.solutions.config import SolutionConfig
11
- from ultralytics.utils import ASSETS_URL, LOGGER
15
+ from ultralytics.utils import ASSETS_URL, LOGGER, ops
12
16
  from ultralytics.utils.checks import check_imshow, check_requirements
13
17
  from ultralytics.utils.plotting import Annotator
14
18
 
15
19
 
16
20
  class BaseSolution:
17
- """
18
- A base class for managing Ultralytics Solutions.
21
+ """A base class for managing Ultralytics Solutions.
19
22
 
20
23
  This class provides core functionality for various Ultralytics Solutions, including model loading, object tracking,
21
- and region initialization.
24
+ and region initialization. It serves as the foundation for implementing specific computer vision solutions such as
25
+ object counting, pose estimation, and analytics.
22
26
 
23
27
  Attributes:
24
- LineString (shapely.geometry.LineString): Class for creating line string geometries.
25
- Polygon (shapely.geometry.Polygon): Class for creating polygon geometries.
26
- Point (shapely.geometry.Point): Class for creating point geometries.
27
- CFG (dict): Configuration dictionary loaded from a YAML file and updated with kwargs.
28
- region (List[Tuple[int, int]]): List of coordinate tuples defining a region of interest.
28
+ LineString: Class for creating line string geometries from shapely.
29
+ Polygon: Class for creating polygon geometries from shapely.
30
+ Point: Class for creating point geometries from shapely.
31
+ prep: Prepared geometry function from shapely for optimized spatial operations.
32
+ CFG (dict[str, Any]): Configuration dictionary loaded from YAML file and updated with kwargs.
33
+ LOGGER: Logger instance for solution-specific logging.
34
+ annotator: Annotator instance for drawing on images.
35
+ tracks: YOLO tracking results from the latest inference.
36
+ track_data: Extracted tracking data (boxes or OBB) from tracks.
37
+ boxes (list): Bounding box coordinates from tracking results.
38
+ clss (list[int]): Class indices from tracking results.
39
+ track_ids (list[int]): Track IDs from tracking results.
40
+ confs (list[float]): Confidence scores from tracking results.
41
+ track_line: Current track line for storing tracking history.
42
+ masks: Segmentation masks from tracking results.
43
+ r_s: Region or line geometry object for spatial operations.
44
+ frame_no (int): Current frame number for logging purposes.
45
+ region (list[tuple[int, int]]): List of coordinate tuples defining region of interest.
29
46
  line_width (int): Width of lines used in visualizations.
30
- model (ultralytics.YOLO): Loaded YOLO model instance.
31
- names (Dict[int, str]): Dictionary mapping class indices to class names.
32
- env_check (bool): Flag indicating whether the environment supports image display.
33
- track_history (collections.defaultdict): Dictionary to store tracking history for each object.
47
+ model (YOLO): Loaded YOLO model instance.
48
+ names (dict[int, str]): Dictionary mapping class indices to class names.
49
+ classes (list[int]): List of class indices to track.
50
+ show_conf (bool): Flag to show confidence scores in annotations.
51
+ show_labels (bool): Flag to show class labels in annotations.
52
+ device (str): Device for model inference.
53
+ track_add_args (dict[str, Any]): Additional arguments for tracking configuration.
54
+ env_check (bool): Flag indicating whether environment supports image display.
55
+ track_history (defaultdict): Dictionary storing tracking history for each object.
56
+ profilers (tuple): Profiler instances for performance monitoring.
34
57
 
35
58
  Methods:
36
- extract_tracks: Apply object tracking and extract tracks from an input image.
37
- store_tracking_history: Store object tracking history for a given track ID and bounding box.
38
- initialize_region: Initialize the counting region and line segment based on configuration.
39
- display_output: Display the results of processing, including showing frames or saving results.
59
+ adjust_box_label: Generate formatted label for bounding box.
60
+ extract_tracks: Apply object tracking and extract tracks from input image.
61
+ store_tracking_history: Store object tracking history for given track ID and bounding box.
62
+ initialize_region: Initialize counting region and line segment based on configuration.
63
+ display_output: Display processing results including frames or saved results.
64
+ process: Process method to be implemented by each Solution subclass.
40
65
 
41
66
  Examples:
42
67
  >>> solution = BaseSolution(model="yolo11n.pt", region=[(0, 0), (100, 0), (100, 100), (0, 100)])
@@ -46,87 +71,90 @@ class BaseSolution:
46
71
  >>> solution.display_output(image)
47
72
  """
48
73
 
49
- def __init__(self, is_cli=False, **kwargs):
50
- """
51
- Initializes the BaseSolution class with configuration settings and the YOLO model.
74
+ def __init__(self, is_cli: bool = False, **kwargs: Any) -> None:
75
+ """Initialize the BaseSolution class with configuration settings and YOLO model.
52
76
 
53
77
  Args:
54
- is_cli (bool): Enables CLI mode if set to True.
78
+ is_cli (bool): Enable CLI mode if set to True.
55
79
  **kwargs (Any): Additional configuration parameters that override defaults.
56
80
  """
57
81
  self.CFG = vars(SolutionConfig().update(**kwargs))
58
82
  self.LOGGER = LOGGER # Store logger object to be used in multiple solution classes
59
83
 
60
- if self.__class__.__name__ != "VisualAISearch":
61
- check_requirements("shapely>=2.0.0")
62
- from shapely.geometry import LineString, Point, Polygon
63
- from shapely.prepared import prep
64
-
65
- self.LineString = LineString
66
- self.Polygon = Polygon
67
- self.Point = Point
68
- self.prep = prep
69
- self.annotator = None # Initialize annotator
70
- self.tracks = None
71
- self.track_data = None
72
- self.boxes = []
73
- self.clss = []
74
- self.track_ids = []
75
- self.track_line = None
76
- self.masks = None
77
- self.r_s = None
78
-
79
- self.LOGGER.info(f"Ultralytics Solutions: ✅ {self.CFG}")
80
- self.region = self.CFG["region"] # Store region data for other classes usage
81
- self.line_width = self.CFG["line_width"]
82
-
83
- # Load Model and store additional information (classes, show_conf, show_label)
84
- if self.CFG["model"] is None:
85
- self.CFG["model"] = "yolo11n.pt"
86
- self.model = YOLO(self.CFG["model"])
87
- self.names = self.model.names
88
- self.classes = self.CFG["classes"]
89
- self.show_conf = self.CFG["show_conf"]
90
- self.show_labels = self.CFG["show_labels"]
91
-
92
- self.track_add_args = { # Tracker additional arguments for advance configuration
93
- k: self.CFG[k] for k in ["iou", "conf", "device", "max_det", "half", "tracker", "device", "verbose"]
94
- } # verbose must be passed to track method; setting it False in YOLO still logs the track information.
95
-
96
- if is_cli and self.CFG["source"] is None:
97
- d_s = "solutions_ci_demo.mp4" if "-pose" not in self.CFG["model"] else "solution_ci_pose_demo.mp4"
98
- self.LOGGER.warning(f"source not provided. using default source {ASSETS_URL}/{d_s}")
99
- from ultralytics.utils.downloads import safe_download
100
-
101
- safe_download(f"{ASSETS_URL}/{d_s}") # download source from ultralytics assets
102
- self.CFG["source"] = d_s # set default source
103
-
104
- # Initialize environment and region setup
105
- self.env_check = check_imshow(warn=True)
106
- self.track_history = defaultdict(list)
107
-
108
- def adjust_box_label(self, cls, conf, track_id=None):
109
- """
110
- Generates a formatted label for a bounding box.
84
+ check_requirements("shapely>=2.0.0")
85
+ from shapely.geometry import LineString, Point, Polygon
86
+ from shapely.prepared import prep
87
+
88
+ self.LineString = LineString
89
+ self.Polygon = Polygon
90
+ self.Point = Point
91
+ self.prep = prep
92
+ self.annotator = None # Initialize annotator
93
+ self.tracks = None
94
+ self.track_data = None
95
+ self.boxes = []
96
+ self.clss = []
97
+ self.track_ids = []
98
+ self.track_line = None
99
+ self.masks = None
100
+ self.r_s = None
101
+ self.frame_no = -1 # Only for logging
102
+
103
+ self.LOGGER.info(f"Ultralytics Solutions: ✅ {self.CFG}")
104
+ self.region = self.CFG["region"] # Store region data for other classes usage
105
+ self.line_width = self.CFG["line_width"]
106
+
107
+ # Load Model and store additional information (classes, show_conf, show_label)
108
+ if self.CFG["model"] is None:
109
+ self.CFG["model"] = "yolo11n.pt"
110
+ self.model = YOLO(self.CFG["model"])
111
+ self.names = self.model.names
112
+ self.classes = self.CFG["classes"]
113
+ self.show_conf = self.CFG["show_conf"]
114
+ self.show_labels = self.CFG["show_labels"]
115
+ self.device = self.CFG["device"]
116
+
117
+ self.track_add_args = { # Tracker additional arguments for advance configuration
118
+ k: self.CFG[k] for k in {"iou", "conf", "device", "max_det", "half", "tracker"}
119
+ } # verbose must be passed to track method; setting it False in YOLO still logs the track information.
120
+
121
+ if is_cli and self.CFG["source"] is None:
122
+ d_s = "solutions_ci_demo.mp4" if "-pose" not in self.CFG["model"] else "solution_ci_pose_demo.mp4"
123
+ self.LOGGER.warning(f"source not provided. using default source {ASSETS_URL}/{d_s}")
124
+ from ultralytics.utils.downloads import safe_download
125
+
126
+ safe_download(f"{ASSETS_URL}/{d_s}") # download source from ultralytics assets
127
+ self.CFG["source"] = d_s # set default source
128
+
129
+ # Initialize environment and region setup
130
+ self.env_check = check_imshow(warn=True)
131
+ self.track_history = defaultdict(list)
132
+
133
+ self.profilers = (
134
+ ops.Profile(device=self.device), # track
135
+ ops.Profile(device=self.device), # solution
136
+ )
111
137
 
112
- This method constructs a label string for a bounding box using the class index and confidence score.
113
- Optionally includes the track ID if provided. The label format adapts based on the display settings
114
- defined in `self.show_conf` and `self.show_labels`.
138
+ def adjust_box_label(self, cls: int, conf: float, track_id: int | None = None) -> str | None:
139
+ """Generate a formatted label for a bounding box.
140
+
141
+ This method constructs a label string for a bounding box using the class index and confidence score. Optionally
142
+ includes the track ID if provided. The label format adapts based on the display settings defined in
143
+ `self.show_conf` and `self.show_labels`.
115
144
 
116
145
  Args:
117
146
  cls (int): The class index of the detected object.
118
147
  conf (float): The confidence score of the detection.
119
- track_id (int, optional): The unique identifier for the tracked object. Defaults to None.
148
+ track_id (int, optional): The unique identifier for the tracked object.
120
149
 
121
150
  Returns:
122
- (str or None): The formatted label string if `self.show_labels` is True; otherwise, None.
151
+ (str | None): The formatted label string if `self.show_labels` is True; otherwise, None.
123
152
  """
124
153
  name = ("" if track_id is None else f"{track_id} ") + self.names[cls]
125
154
  return (f"{name} {conf:.2f}" if self.show_conf else name) if self.show_labels else None
126
155
 
127
- def extract_tracks(self, im0):
128
- """
129
- Applies object tracking and extracts tracks from an input image or frame.
156
+ def extract_tracks(self, im0: np.ndarray) -> None:
157
+ """Apply object tracking and extract tracks from an input image or frame.
130
158
 
131
159
  Args:
132
160
  im0 (np.ndarray): The input image or frame.
@@ -136,11 +164,15 @@ class BaseSolution:
136
164
  >>> frame = cv2.imread("path/to/image.jpg")
137
165
  >>> solution.extract_tracks(frame)
138
166
  """
139
- self.tracks = self.model.track(source=im0, persist=True, classes=self.classes, **self.track_add_args)
140
- self.track_data = self.tracks[0].obb or self.tracks[0].boxes # Extract tracks for OBB or object detection
167
+ with self.profilers[0]:
168
+ self.tracks = self.model.track(
169
+ source=im0, persist=True, classes=self.classes, verbose=False, **self.track_add_args
170
+ )[0]
171
+ is_obb = self.tracks.obb is not None
172
+ self.track_data = self.tracks.obb if is_obb else self.tracks.boxes # Extract tracks for OBB or object detection
141
173
 
142
- if self.track_data and self.track_data.id is not None:
143
- self.boxes = self.track_data.xyxy.cpu()
174
+ if self.track_data and self.track_data.is_track:
175
+ self.boxes = (self.track_data.xyxyxyxy if is_obb else self.track_data.xyxy).cpu()
144
176
  self.clss = self.track_data.cls.cpu().tolist()
145
177
  self.track_ids = self.track_data.id.int().cpu().tolist()
146
178
  self.confs = self.track_data.conf.cpu().tolist()
@@ -148,17 +180,15 @@ class BaseSolution:
148
180
  self.LOGGER.warning("no tracks found!")
149
181
  self.boxes, self.clss, self.track_ids, self.confs = [], [], [], []
150
182
 
151
- def store_tracking_history(self, track_id, box, is_obb=False):
152
- """
153
- Stores the tracking history of an object.
183
+ def store_tracking_history(self, track_id: int, box) -> None:
184
+ """Store the tracking history of an object.
154
185
 
155
- This method updates the tracking history for a given object by appending the center point of its
156
- bounding box to the track line. It maintains a maximum of 30 points in the tracking history.
186
+ This method updates the tracking history for a given object by appending the center point of its bounding box to
187
+ the track line. It maintains a maximum of 30 points in the tracking history.
157
188
 
158
189
  Args:
159
190
  track_id (int): The unique identifier for the tracked object.
160
- box (List[float]): The bounding box coordinates of the object in the format [x1, y1, x2, y2].
161
- is_obb (bool): True if OBB model is used (applies to object counting only).
191
+ box (list[float]): The bounding box coordinates of the object in the format [x1, y1, x2, y2].
162
192
 
163
193
  Examples:
164
194
  >>> solution = BaseSolution()
@@ -166,11 +196,11 @@ class BaseSolution:
166
196
  """
167
197
  # Store tracking history
168
198
  self.track_line = self.track_history[track_id]
169
- self.track_line.append(tuple(box.mean(dim=0)) if is_obb else (box[:4:2].mean(), box[1:4:2].mean()))
199
+ self.track_line.append(tuple(box.mean(dim=0)) if box.numel() > 4 else (box[:4:2].mean(), box[1:4:2].mean()))
170
200
  if len(self.track_line) > 30:
171
201
  self.track_line.pop(0)
172
202
 
173
- def initialize_region(self):
203
+ def initialize_region(self) -> None:
174
204
  """Initialize the counting region and line segment based on configuration settings."""
175
205
  if self.region is None:
176
206
  self.region = [(10, 200), (540, 200), (540, 180), (10, 180)]
@@ -178,15 +208,15 @@ class BaseSolution:
178
208
  self.Polygon(self.region) if len(self.region) >= 3 else self.LineString(self.region)
179
209
  ) # region or line
180
210
 
181
- def display_output(self, plot_im):
182
- """
183
- Display the results of the processing, which could involve showing frames, printing counts, or saving results.
211
+ def display_output(self, plot_im: np.ndarray) -> None:
212
+ """Display the results of the processing, which could involve showing frames, printing counts, or saving
213
+ results.
184
214
 
185
215
  This method is responsible for visualizing the output of the object detection and tracking process. It displays
186
216
  the processed frame with annotations, and allows for user interaction to close the display.
187
217
 
188
218
  Args:
189
- plot_im (numpy.ndarray): The image or frame that has been processed and annotated.
219
+ plot_im (np.ndarray): The image or frame that has been processed and annotated.
190
220
 
191
221
  Examples:
192
222
  >>> solution = BaseSolution()
@@ -204,24 +234,36 @@ class BaseSolution:
204
234
  cv2.destroyAllWindows() # Closes current frame window
205
235
  return
206
236
 
207
- def process(self, *args, **kwargs):
237
+ def process(self, *args: Any, **kwargs: Any):
208
238
  """Process method should be implemented by each Solution subclass."""
209
239
 
210
- def __call__(self, *args, **kwargs):
240
+ def __call__(self, *args: Any, **kwargs: Any):
211
241
  """Allow instances to be called like a function with flexible arguments."""
212
- result = self.process(*args, **kwargs) # Call the subclass-specific process method
213
- if self.CFG["verbose"]: # extract verbose value to display the output logs if True
214
- LOGGER.info(f"🚀 Results: {result}")
242
+ with self.profilers[1]:
243
+ result = self.process(*args, **kwargs) # Call the subclass-specific process method
244
+ track_or_predict = "predict" if type(self).__name__ == "ObjectCropper" else "track"
245
+ track_or_predict_speed = self.profilers[0].dt * 1e3
246
+ solution_speed = (self.profilers[1].dt - self.profilers[0].dt) * 1e3 # solution time = process - track
247
+ result.speed = {track_or_predict: track_or_predict_speed, "solution": solution_speed}
248
+ if self.CFG["verbose"]:
249
+ self.frame_no += 1
250
+ counts = Counter(self.clss) # Only for logging.
251
+ LOGGER.info(
252
+ f"{self.frame_no}: {result.plot_im.shape[0]}x{result.plot_im.shape[1]} {solution_speed:.1f}ms,"
253
+ f" {', '.join([f'{v} {self.names[k]}' for k, v in counts.items()])}\n"
254
+ f"Speed: {track_or_predict_speed:.1f}ms {track_or_predict}, "
255
+ f"{solution_speed:.1f}ms solution per image at shape "
256
+ f"(1, {getattr(self.model, 'ch', 3)}, {result.plot_im.shape[0]}, {result.plot_im.shape[1]})\n"
257
+ )
215
258
  return result
216
259
 
217
260
 
218
261
  class SolutionAnnotator(Annotator):
219
- """
220
- A specialized annotator class for visualizing and analyzing computer vision tasks.
262
+ """A specialized annotator class for visualizing and analyzing computer vision tasks.
221
263
 
222
264
  This class extends the base Annotator class, providing additional methods for drawing regions, centroids, tracking
223
- trails, and visual annotations for Ultralytics Solutions: https://docs.ultralytics.com/solutions/.
224
- and parking management.
265
+ trails, and visual annotations for Ultralytics Solutions. It offers comprehensive visualization capabilities for
266
+ various computer vision applications including object detection, tracking, pose estimation, and analytics.
225
267
 
226
268
  Attributes:
227
269
  im (np.ndarray): The image being annotated.
@@ -232,19 +274,18 @@ class SolutionAnnotator(Annotator):
232
274
  example (str): An example attribute for demonstration purposes.
233
275
 
234
276
  Methods:
235
- draw_region: Draws a region using specified points, colors, and thickness.
236
- queue_counts_display: Displays queue counts in the specified region.
237
- display_analytics: Displays overall statistics for parking lot management.
238
- estimate_pose_angle: Calculates the angle between three points in an object pose.
239
- draw_specific_points: Draws specific keypoints on the image.
240
- plot_workout_information: Draws a labeled text box on the image.
241
- plot_angle_and_count_and_stage: Visualizes angle, step count, and stage for workout monitoring.
242
- plot_distance_and_line: Displays the distance between centroids and connects them with a line.
243
- display_objects_labels: Annotates bounding boxes with object class labels.
244
- sweep_annotator: Visualizes a vertical sweep line and optional label.
245
- visioneye: Maps and connects object centroids to a visual "eye" point.
246
- circle_label: Draws a circular label within a bounding box.
247
- text_label: Draws a rectangular label within a bounding box.
277
+ draw_region: Draw a region using specified points, colors, and thickness.
278
+ queue_counts_display: Display queue counts in the specified region.
279
+ display_analytics: Display overall statistics for parking lot management.
280
+ estimate_pose_angle: Calculate the angle between three points in an object pose.
281
+ draw_specific_kpts: Draw specific keypoints on the image.
282
+ plot_workout_information: Draw a labeled text box on the image.
283
+ plot_angle_and_count_and_stage: Visualize angle, step count, and stage for workout monitoring.
284
+ plot_distance_and_line: Display the distance between centroids and connect them with a line.
285
+ display_objects_labels: Annotate bounding boxes with object class labels.
286
+ sweep_annotator: Visualize a vertical sweep line and optional label.
287
+ visioneye: Map and connect object centroids to a visual "eye" point.
288
+ adaptive_label: Draw a circular or rectangle background shape label in center of a bounding box.
248
289
 
249
290
  Examples:
250
291
  >>> annotator = SolutionAnnotator(image)
@@ -254,27 +295,38 @@ class SolutionAnnotator(Annotator):
254
295
  ... )
255
296
  """
256
297
 
257
- def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):
258
- """
259
- Initializes the SolutionAnnotator class with an image for annotation.
298
+ def __init__(
299
+ self,
300
+ im: np.ndarray,
301
+ line_width: int | None = None,
302
+ font_size: int | None = None,
303
+ font: str = "Arial.ttf",
304
+ pil: bool = False,
305
+ example: str = "abc",
306
+ ):
307
+ """Initialize the SolutionAnnotator class with an image for annotation.
260
308
 
261
309
  Args:
262
310
  im (np.ndarray): The image to be annotated.
263
311
  line_width (int, optional): Line thickness for drawing on the image.
264
312
  font_size (int, optional): Font size for text annotations.
265
- font (str, optional): Path to the font file.
266
- pil (bool, optional): Indicates whether to use PIL for rendering text.
267
- example (str, optional): An example parameter for demonstration purposes.
313
+ font (str): Path to the font file.
314
+ pil (bool): Indicates whether to use PIL for rendering text.
315
+ example (str): An example parameter for demonstration purposes.
268
316
  """
269
317
  super().__init__(im, line_width, font_size, font, pil, example)
270
318
 
271
- def draw_region(self, reg_pts=None, color=(0, 255, 0), thickness=5):
272
- """
273
- Draw a region or line on the image.
319
+ def draw_region(
320
+ self,
321
+ reg_pts: list[tuple[int, int]] | None = None,
322
+ color: tuple[int, int, int] = (0, 255, 0),
323
+ thickness: int = 5,
324
+ ):
325
+ """Draw a region or line on the image.
274
326
 
275
327
  Args:
276
- reg_pts (List[Tuple[int, int]]): Region points (for line 2 points, for region 4+ points).
277
- color (Tuple[int, int, int]): RGB color value for the region.
328
+ reg_pts (list[tuple[int, int]], optional): Region points (for line 2 points, for region 4+ points).
329
+ color (tuple[int, int, int]): RGB color value for the region.
278
330
  thickness (int): Line thickness for drawing the region.
279
331
  """
280
332
  cv2.polylines(self.im, [np.array(reg_pts, dtype=np.int32)], isClosed=True, color=color, thickness=thickness)
@@ -283,15 +335,20 @@ class SolutionAnnotator(Annotator):
283
335
  for point in reg_pts:
284
336
  cv2.circle(self.im, (point[0], point[1]), thickness * 2, color, -1) # -1 fills the circle
285
337
 
286
- def queue_counts_display(self, label, points=None, region_color=(255, 255, 255), txt_color=(0, 0, 0)):
287
- """
288
- Displays queue counts on an image centered at the points with customizable font size and colors.
338
+ def queue_counts_display(
339
+ self,
340
+ label: str,
341
+ points: list[tuple[int, int]] | None = None,
342
+ region_color: tuple[int, int, int] = (255, 255, 255),
343
+ txt_color: tuple[int, int, int] = (0, 0, 0),
344
+ ):
345
+ """Display queue counts on an image centered at the points with customizable font size and colors.
289
346
 
290
347
  Args:
291
348
  label (str): Queue counts label.
292
- points (List[Tuple[int, int]]): Region points for center point calculation to display text.
293
- region_color (Tuple[int, int, int]): RGB queue region color.
294
- txt_color (Tuple[int, int, int]): RGB text display color.
349
+ points (list[tuple[int, int]], optional): Region points for center point calculation to display text.
350
+ region_color (tuple[int, int, int]): RGB queue region color.
351
+ txt_color (tuple[int, int, int]): RGB text display color.
295
352
  """
296
353
  x_values = [point[0] for point in points]
297
354
  y_values = [point[1] for point in points]
@@ -323,15 +380,21 @@ class SolutionAnnotator(Annotator):
323
380
  lineType=cv2.LINE_AA,
324
381
  )
325
382
 
326
- def display_analytics(self, im0, text, txt_color, bg_color, margin):
327
- """
328
- Display the overall statistics for parking lots, object counter etc.
383
+ def display_analytics(
384
+ self,
385
+ im0: np.ndarray,
386
+ text: dict[str, Any],
387
+ txt_color: tuple[int, int, int],
388
+ bg_color: tuple[int, int, int],
389
+ margin: int,
390
+ ):
391
+ """Display the overall statistics for parking lots, object counter etc.
329
392
 
330
393
  Args:
331
394
  im0 (np.ndarray): Inference image.
332
- text (Dict[str, Any]): Labels dictionary.
333
- txt_color (Tuple[int, int, int]): Display color for text foreground.
334
- bg_color (Tuple[int, int, int]): Display color for text background.
395
+ text (dict[str, Any]): Labels dictionary.
396
+ txt_color (tuple[int, int, int]): Display color for text foreground.
397
+ bg_color (tuple[int, int, int]): Display color for text background.
335
398
  margin (int): Gap between text and rectangle for better display.
336
399
  """
337
400
  horizontal_gap = int(im0.shape[1] * 0.02)
@@ -353,14 +416,14 @@ class SolutionAnnotator(Annotator):
353
416
  text_y_offset = rect_y2
354
417
 
355
418
  @staticmethod
356
- def estimate_pose_angle(a, b, c):
357
- """
358
- Calculate the angle between three points for workout monitoring.
419
+ @lru_cache(maxsize=256)
420
+ def estimate_pose_angle(a: list[float], b: list[float], c: list[float]) -> float:
421
+ """Calculate the angle between three points for workout monitoring.
359
422
 
360
423
  Args:
361
- a (List[float]): The coordinates of the first point.
362
- b (List[float]): The coordinates of the second point (vertex).
363
- c (List[float]): The coordinates of the third point.
424
+ a (list[float]): The coordinates of the first point.
425
+ b (list[float]): The coordinates of the second point (vertex).
426
+ c (list[float]): The coordinates of the third point.
364
427
 
365
428
  Returns:
366
429
  (float): The angle in degrees between the three points.
@@ -369,20 +432,25 @@ class SolutionAnnotator(Annotator):
369
432
  angle = abs(radians * 180.0 / math.pi)
370
433
  return angle if angle <= 180.0 else (360 - angle)
371
434
 
372
- def draw_specific_kpts(self, keypoints, indices=None, radius=2, conf_thresh=0.25):
373
- """
374
- Draw specific keypoints for gym steps counting.
435
+ def draw_specific_kpts(
436
+ self,
437
+ keypoints: list[list[float]],
438
+ indices: list[int] | None = None,
439
+ radius: int = 2,
440
+ conf_thresh: float = 0.25,
441
+ ) -> np.ndarray:
442
+ """Draw specific keypoints for gym steps counting.
375
443
 
376
444
  Args:
377
- keypoints (List[List[float]]): Keypoints data to be plotted, each in format [x, y, confidence].
378
- indices (List[int], optional): Keypoint indices to be plotted.
379
- radius (int, optional): Keypoint radius.
380
- conf_thresh (float, optional): Confidence threshold for keypoints.
445
+ keypoints (list[list[float]]): Keypoints data to be plotted, each in format [x, y, confidence].
446
+ indices (list[int], optional): Keypoint indices to be plotted.
447
+ radius (int): Keypoint radius.
448
+ conf_thresh (float): Confidence threshold for keypoints.
381
449
 
382
450
  Returns:
383
451
  (np.ndarray): Image with drawn keypoints.
384
452
 
385
- Note:
453
+ Notes:
386
454
  Keypoint format: [x, y] or [x, y, confidence].
387
455
  Modifies self.im in-place.
388
456
  """
@@ -399,20 +467,25 @@ class SolutionAnnotator(Annotator):
399
467
 
400
468
  return self.im
401
469
 
402
- def plot_workout_information(self, display_text, position, color=(104, 31, 17), txt_color=(255, 255, 255)):
403
- """
404
- Draw workout text with a background on the image.
470
+ def plot_workout_information(
471
+ self,
472
+ display_text: str,
473
+ position: tuple[int, int],
474
+ color: tuple[int, int, int] = (104, 31, 17),
475
+ txt_color: tuple[int, int, int] = (255, 255, 255),
476
+ ) -> int:
477
+ """Draw workout text with a background on the image.
405
478
 
406
479
  Args:
407
480
  display_text (str): The text to be displayed.
408
- position (Tuple[int, int]): Coordinates (x, y) on the image where the text will be placed.
409
- color (Tuple[int, int, int], optional): Text background color.
410
- txt_color (Tuple[int, int, int], optional): Text foreground color.
481
+ position (tuple[int, int]): Coordinates (x, y) on the image where the text will be placed.
482
+ color (tuple[int, int, int]): Text background color.
483
+ txt_color (tuple[int, int, int]): Text foreground color.
411
484
 
412
485
  Returns:
413
486
  (int): The height of the text.
414
487
  """
415
- (text_width, text_height), _ = cv2.getTextSize(display_text, 0, self.sf, self.tf)
488
+ (text_width, text_height), _ = cv2.getTextSize(display_text, 0, fontScale=self.sf, thickness=self.tf)
416
489
 
417
490
  # Draw background rectangle
418
491
  cv2.rectangle(
@@ -428,18 +501,23 @@ class SolutionAnnotator(Annotator):
428
501
  return text_height
429
502
 
430
503
  def plot_angle_and_count_and_stage(
431
- self, angle_text, count_text, stage_text, center_kpt, color=(104, 31, 17), txt_color=(255, 255, 255)
504
+ self,
505
+ angle_text: str,
506
+ count_text: str,
507
+ stage_text: str,
508
+ center_kpt: list[int],
509
+ color: tuple[int, int, int] = (104, 31, 17),
510
+ txt_color: tuple[int, int, int] = (255, 255, 255),
432
511
  ):
433
- """
434
- Plot the pose angle, count value, and step stage for workout monitoring.
512
+ """Plot the pose angle, count value, and step stage for workout monitoring.
435
513
 
436
514
  Args:
437
515
  angle_text (str): Angle value for workout monitoring.
438
516
  count_text (str): Counts value for workout monitoring.
439
517
  stage_text (str): Stage decision for workout monitoring.
440
- center_kpt (List[int]): Centroid pose index for workout monitoring.
441
- color (Tuple[int, int, int], optional): Text background color.
442
- txt_color (Tuple[int, int, int], optional): Text foreground color.
518
+ center_kpt (list[int]): Centroid pose index for workout monitoring.
519
+ color (tuple[int, int, int]): Text background color.
520
+ txt_color (tuple[int, int, int]): Text foreground color.
443
521
  """
444
522
  # Format text
445
523
  angle_text, count_text, stage_text = f" {angle_text:.2f}", f"Steps : {count_text}", f" {stage_text}"
@@ -456,16 +534,19 @@ class SolutionAnnotator(Annotator):
456
534
  )
457
535
 
458
536
  def plot_distance_and_line(
459
- self, pixels_distance, centroids, line_color=(104, 31, 17), centroid_color=(255, 0, 255)
537
+ self,
538
+ pixels_distance: float,
539
+ centroids: list[tuple[int, int]],
540
+ line_color: tuple[int, int, int] = (104, 31, 17),
541
+ centroid_color: tuple[int, int, int] = (255, 0, 255),
460
542
  ):
461
- """
462
- Plot the distance and line between two centroids on the frame.
543
+ """Plot the distance and line between two centroids on the frame.
463
544
 
464
545
  Args:
465
546
  pixels_distance (float): Pixels distance between two bbox centroids.
466
- centroids (List[Tuple[int, int]]): Bounding box centroids data.
467
- line_color (Tuple[int, int, int], optional): Distance line color.
468
- centroid_color (Tuple[int, int, int], optional): Bounding box centroid color.
547
+ centroids (list[tuple[int, int]]): Bounding box centroids data.
548
+ line_color (tuple[int, int, int]): Distance line color.
549
+ centroid_color (tuple[int, int, int]): Bounding box centroid color.
469
550
  """
470
551
  # Get the text size
471
552
  text = f"Pixels Distance: {pixels_distance:.2f}"
@@ -491,15 +572,23 @@ class SolutionAnnotator(Annotator):
491
572
  cv2.circle(self.im, centroids[0], 6, centroid_color, -1)
492
573
  cv2.circle(self.im, centroids[1], 6, centroid_color, -1)
493
574
 
494
- def display_objects_labels(self, im0, text, txt_color, bg_color, x_center, y_center, margin):
495
- """
496
- Display the bounding boxes labels in parking management app.
575
+ def display_objects_labels(
576
+ self,
577
+ im0: np.ndarray,
578
+ text: str,
579
+ txt_color: tuple[int, int, int],
580
+ bg_color: tuple[int, int, int],
581
+ x_center: float,
582
+ y_center: float,
583
+ margin: int,
584
+ ):
585
+ """Display the bounding boxes labels in parking management app.
497
586
 
498
587
  Args:
499
588
  im0 (np.ndarray): Inference image.
500
589
  text (str): Object/class name.
501
- txt_color (Tuple[int, int, int]): Display color for text foreground.
502
- bg_color (Tuple[int, int, int]): Display color for text background.
590
+ txt_color (tuple[int, int, int]): Display color for text foreground.
591
+ bg_color (tuple[int, int, int]): Display color for text background.
503
592
  x_center (float): The x position center point for bounding box.
504
593
  y_center (float): The y position center point for bounding box.
505
594
  margin (int): The gap between text and rectangle for better display.
@@ -531,16 +620,22 @@ class SolutionAnnotator(Annotator):
531
620
  lineType=cv2.LINE_AA,
532
621
  )
533
622
 
534
- def sweep_annotator(self, line_x=0, line_y=0, label=None, color=(221, 0, 186), txt_color=(255, 255, 255)):
535
- """
536
- Draw a sweep annotation line and an optional label.
623
+ def sweep_annotator(
624
+ self,
625
+ line_x: int = 0,
626
+ line_y: int = 0,
627
+ label: str | None = None,
628
+ color: tuple[int, int, int] = (221, 0, 186),
629
+ txt_color: tuple[int, int, int] = (255, 255, 255),
630
+ ):
631
+ """Draw a sweep annotation line and an optional label.
537
632
 
538
633
  Args:
539
634
  line_x (int): The x-coordinate of the sweep line.
540
635
  line_y (int): The y-coordinate limit of the sweep line.
541
636
  label (str, optional): Text label to be drawn in center of sweep line. If None, no label is drawn.
542
- color (Tuple[int, int, int]): RGB color for the line and label background.
543
- txt_color (Tuple[int, int, int]): RGB color for the label text.
637
+ color (tuple[int, int, int]): RGB color for the line and label background.
638
+ txt_color (tuple[int, int, int]): RGB color for the label text.
544
639
  """
545
640
  # Draw the sweep line
546
641
  cv2.line(self.im, (line_x, 0), (line_x, line_y), color, self.tf * 2)
@@ -565,91 +660,77 @@ class SolutionAnnotator(Annotator):
565
660
  self.tf,
566
661
  )
567
662
 
568
- def visioneye(self, box, center_point, color=(235, 219, 11), pin_color=(255, 0, 255)):
569
- """
570
- Perform pinpoint human-vision eye mapping and plotting.
663
+ def visioneye(
664
+ self,
665
+ box: list[float],
666
+ center_point: tuple[int, int],
667
+ color: tuple[int, int, int] = (235, 219, 11),
668
+ pin_color: tuple[int, int, int] = (255, 0, 255),
669
+ ):
670
+ """Perform pinpoint human-vision eye mapping and plotting.
571
671
 
572
672
  Args:
573
- box (List[float]): Bounding box coordinates in format [x1, y1, x2, y2].
574
- center_point (Tuple[int, int]): Center point for vision eye view.
575
- color (Tuple[int, int, int]): Object centroid and line color.
576
- pin_color (Tuple[int, int, int]): Visioneye point color.
673
+ box (list[float]): Bounding box coordinates in format [x1, y1, x2, y2].
674
+ center_point (tuple[int, int]): Center point for vision eye view.
675
+ color (tuple[int, int, int]): Object centroid and line color.
676
+ pin_color (tuple[int, int, int]): Visioneye point color.
577
677
  """
578
678
  center_bbox = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
579
679
  cv2.circle(self.im, center_point, self.tf * 2, pin_color, -1)
580
680
  cv2.circle(self.im, center_bbox, self.tf * 2, color, -1)
581
681
  cv2.line(self.im, center_point, center_bbox, color, self.tf)
582
682
 
583
- def circle_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), margin=2):
584
- """
585
- Draw a label with a background circle centered within a given bounding box.
683
+ def adaptive_label(
684
+ self,
685
+ box: tuple[float, float, float, float],
686
+ label: str = "",
687
+ color: tuple[int, int, int] = (128, 128, 128),
688
+ txt_color: tuple[int, int, int] = (255, 255, 255),
689
+ shape: str = "rect",
690
+ margin: int = 5,
691
+ ):
692
+ """Draw a label with a background rectangle or circle centered within a given bounding box.
586
693
 
587
694
  Args:
588
- box (Tuple[float, float, float, float]): The bounding box coordinates (x1, y1, x2, y2).
695
+ box (tuple[float, float, float, float]): The bounding box coordinates (x1, y1, x2, y2).
589
696
  label (str): The text label to be displayed.
590
- color (Tuple[int, int, int]): The background color of the circle (B, G, R).
591
- txt_color (Tuple[int, int, int]): The color of the text (R, G, B).
592
- margin (int): The margin between the text and the circle border.
697
+ color (tuple[int, int, int]): The background color of the rectangle (B, G, R).
698
+ txt_color (tuple[int, int, int]): The color of the text (R, G, B).
699
+ shape (str): The shape of the label i.e "circle" or "rect"
700
+ margin (int): The margin between the text and the rectangle border.
593
701
  """
594
- if len(label) > 3:
702
+ if shape == "circle" and len(label) > 3:
595
703
  LOGGER.warning(f"Length of label is {len(label)}, only first 3 letters will be used for circle annotation.")
596
704
  label = label[:3]
597
705
 
598
- # Calculate the center of the box
599
- x_center, y_center = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
600
- # Get the text size
601
- text_size = cv2.getTextSize(str(label), cv2.FONT_HERSHEY_SIMPLEX, self.sf - 0.15, self.tf)[0]
602
- # Calculate the required radius to fit the text with the margin
603
- required_radius = int(((text_size[0] ** 2 + text_size[1] ** 2) ** 0.5) / 2) + margin
604
- # Draw the circle with the required radius
605
- cv2.circle(self.im, (x_center, y_center), required_radius, color, -1)
606
- # Calculate the position for the text
607
- text_x = x_center - text_size[0] // 2
608
- text_y = y_center + text_size[1] // 2
609
- # Draw the text
610
- cv2.putText(
611
- self.im,
612
- str(label),
613
- (text_x, text_y),
614
- cv2.FONT_HERSHEY_SIMPLEX,
615
- self.sf - 0.15,
616
- self.get_txt_color(color, txt_color),
617
- self.tf,
618
- lineType=cv2.LINE_AA,
619
- )
706
+ x_center, y_center = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2) # Calculate center of the bbox
707
+ text_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, self.sf - 0.15, self.tf)[0] # Get size of the text
708
+ text_x, text_y = x_center - text_size[0] // 2, y_center + text_size[1] // 2 # Calculate top-left corner of text
620
709
 
621
- def text_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), margin=5):
622
- """
623
- Draw a label with a background rectangle centered within a given bounding box.
710
+ if shape == "circle":
711
+ cv2.circle(
712
+ self.im,
713
+ (x_center, y_center),
714
+ int(((text_size[0] ** 2 + text_size[1] ** 2) ** 0.5) / 2) + margin, # Calculate the radius
715
+ color,
716
+ -1,
717
+ )
718
+ else:
719
+ cv2.rectangle(
720
+ self.im,
721
+ (text_x - margin, text_y - text_size[1] - margin), # Calculate coordinates of the rectangle
722
+ (text_x + text_size[0] + margin, text_y + margin), # Calculate coordinates of the rectangle
723
+ color,
724
+ -1,
725
+ )
624
726
 
625
- Args:
626
- box (Tuple[float, float, float, float]): The bounding box coordinates (x1, y1, x2, y2).
627
- label (str): The text label to be displayed.
628
- color (Tuple[int, int, int]): The background color of the rectangle (B, G, R).
629
- txt_color (Tuple[int, int, int]): The color of the text (R, G, B).
630
- margin (int): The margin between the text and the rectangle border.
631
- """
632
- # Calculate the center of the bounding box
633
- x_center, y_center = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
634
- # Get the size of the text
635
- text_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, self.sf - 0.1, self.tf)[0]
636
- # Calculate the top-left corner of the text (to center it)
637
- text_x = x_center - text_size[0] // 2
638
- text_y = y_center + text_size[1] // 2
639
- # Calculate the coordinates of the background rectangle
640
- rect_x1 = text_x - margin
641
- rect_y1 = text_y - text_size[1] - margin
642
- rect_x2 = text_x + text_size[0] + margin
643
- rect_y2 = text_y + margin
644
- # Draw the background rectangle
645
- cv2.rectangle(self.im, (rect_x1, rect_y1), (rect_x2, rect_y2), color, -1)
646
727
  # Draw the text on top of the rectangle
647
728
  cv2.putText(
648
729
  self.im,
649
730
  label,
650
- (text_x, text_y),
731
+ (text_x, text_y), # Calculate top-left corner of the text
651
732
  cv2.FONT_HERSHEY_SIMPLEX,
652
- self.sf - 0.1,
733
+ self.sf - 0.15,
653
734
  self.get_txt_color(color, txt_color),
654
735
  self.tf,
655
736
  lineType=cv2.LINE_AA,
@@ -657,17 +738,17 @@ class SolutionAnnotator(Annotator):
657
738
 
658
739
 
659
740
  class SolutionResults:
660
- """
661
- A class to encapsulate the results of Ultralytics Solutions.
741
+ """A class to encapsulate the results of Ultralytics Solutions.
662
742
 
663
743
  This class is designed to store and manage various outputs generated by the solution pipeline, including counts,
664
- angles, and workout stages.
744
+ angles, workout stages, and other analytics data. It provides a structured way to access and manipulate results from
745
+ different computer vision solutions such as object counting, pose estimation, and tracking analytics.
665
746
 
666
747
  Attributes:
667
748
  plot_im (np.ndarray): Processed image with counts, blurred, or other effects from solutions.
668
749
  in_count (int): The total number of "in" counts in a video stream.
669
750
  out_count (int): The total number of "out" counts in a video stream.
670
- classwise_count (Dict[str, int]): A dictionary containing counts of objects categorized by class.
751
+ classwise_count (dict[str, int]): A dictionary containing counts of objects categorized by class.
671
752
  queue_count (int): The count of objects in a queue or waiting area.
672
753
  workout_count (int): The count of workout repetitions.
673
754
  workout_angle (float): The angle calculated during a workout exercise.
@@ -677,14 +758,14 @@ class SolutionResults:
677
758
  filled_slots (int): The number of filled slots in a monitored area.
678
759
  email_sent (bool): A flag indicating whether an email notification was sent.
679
760
  total_tracks (int): The total number of tracked objects.
680
- region_counts (dict): The count of objects within a specific region.
681
- speed_dict (Dict[str, float]): A dictionary containing speed information for tracked objects.
761
+ region_counts (dict[str, int]): The count of objects within a specific region.
762
+ speed_dict (dict[str, float]): A dictionary containing speed information for tracked objects.
682
763
  total_crop_objects (int): Total number of cropped objects using ObjectCropper class.
764
+ speed (dict[str, float]): Performance timing information for tracking and solution processing.
683
765
  """
684
766
 
685
767
  def __init__(self, **kwargs):
686
- """
687
- Initialize a SolutionResults object with default or user-specified values.
768
+ """Initialize a SolutionResults object with default or user-specified values.
688
769
 
689
770
  Args:
690
771
  **kwargs (Any): Optional arguments to override default attribute values.
@@ -703,15 +784,15 @@ class SolutionResults:
703
784
  self.email_sent = False
704
785
  self.total_tracks = 0
705
786
  self.region_counts = {}
706
- self.speed_dict = {}
787
+ self.speed_dict = {} # for speed estimation
707
788
  self.total_crop_objects = 0
789
+ self.speed = {}
708
790
 
709
791
  # Override with user-defined values
710
792
  self.__dict__.update(kwargs)
711
793
 
712
- def __str__(self):
713
- """
714
- Return a formatted string representation of the SolutionResults object.
794
+ def __str__(self) -> str:
795
+ """Return a formatted string representation of the SolutionResults object.
715
796
 
716
797
  Returns:
717
798
  (str): A string representation listing non-null attributes.
@@ -721,4 +802,4 @@ class SolutionResults:
721
802
  for k, v in self.__dict__.items()
722
803
  if k != "plot_im" and v not in [None, {}, 0, 0.0, False] # Exclude `plot_im` explicitly
723
804
  }
724
- return f"SolutionResults({', '.join(f'{k}={v}' for k, v in attrs.items())})"
805
+ return ", ".join(f"{k}={v}" for k, v in attrs.items())