ultralytics 8.1.29__py3-none-any.whl → 8.3.63__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. tests/__init__.py +22 -0
  2. tests/conftest.py +83 -0
  3. tests/test_cli.py +122 -0
  4. tests/test_cuda.py +155 -0
  5. tests/test_engine.py +131 -0
  6. tests/test_exports.py +216 -0
  7. tests/test_integrations.py +150 -0
  8. tests/test_python.py +615 -0
  9. tests/test_solutions.py +94 -0
  10. ultralytics/__init__.py +11 -8
  11. ultralytics/cfg/__init__.py +569 -131
  12. ultralytics/cfg/datasets/Argoverse.yaml +2 -1
  13. ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
  14. ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
  15. ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
  16. ultralytics/cfg/datasets/ImageNet.yaml +2 -1
  17. ultralytics/cfg/datasets/Objects365.yaml +5 -4
  18. ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
  19. ultralytics/cfg/datasets/VOC.yaml +3 -2
  20. ultralytics/cfg/datasets/VisDrone.yaml +6 -5
  21. ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
  22. ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
  23. ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
  24. ultralytics/cfg/datasets/coco-pose.yaml +7 -6
  25. ultralytics/cfg/datasets/coco.yaml +3 -2
  26. ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
  27. ultralytics/cfg/datasets/coco128.yaml +4 -3
  28. ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
  29. ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
  30. ultralytics/cfg/datasets/coco8.yaml +3 -2
  31. ultralytics/cfg/datasets/crack-seg.yaml +3 -2
  32. ultralytics/cfg/datasets/dog-pose.yaml +24 -0
  33. ultralytics/cfg/datasets/dota8.yaml +3 -2
  34. ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
  35. ultralytics/cfg/datasets/lvis.yaml +1236 -0
  36. ultralytics/cfg/datasets/medical-pills.yaml +22 -0
  37. ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
  38. ultralytics/cfg/datasets/package-seg.yaml +5 -4
  39. ultralytics/cfg/datasets/signature.yaml +21 -0
  40. ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
  41. ultralytics/cfg/datasets/xView.yaml +2 -1
  42. ultralytics/cfg/default.yaml +14 -11
  43. ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
  44. ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
  45. ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
  46. ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
  47. ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
  48. ultralytics/cfg/models/11/yolo11.yaml +50 -0
  49. ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
  50. ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
  51. ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
  52. ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
  53. ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
  54. ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
  55. ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
  56. ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
  57. ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
  58. ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
  59. ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
  60. ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
  61. ultralytics/cfg/models/v3/yolov3.yaml +5 -2
  62. ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
  63. ultralytics/cfg/models/v5/yolov5.yaml +5 -2
  64. ultralytics/cfg/models/v6/yolov6.yaml +5 -2
  65. ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
  66. ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
  67. ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
  68. ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
  69. ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
  70. ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
  71. ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
  72. ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
  73. ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
  74. ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
  75. ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
  76. ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
  77. ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
  78. ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
  79. ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
  80. ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
  81. ultralytics/cfg/models/v8/yolov8.yaml +5 -2
  82. ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
  83. ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
  84. ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
  85. ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
  86. ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
  87. ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
  88. ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
  89. ultralytics/cfg/solutions/default.yaml +24 -0
  90. ultralytics/cfg/trackers/botsort.yaml +8 -5
  91. ultralytics/cfg/trackers/bytetrack.yaml +8 -5
  92. ultralytics/data/__init__.py +14 -3
  93. ultralytics/data/annotator.py +37 -15
  94. ultralytics/data/augment.py +1783 -289
  95. ultralytics/data/base.py +62 -27
  96. ultralytics/data/build.py +37 -8
  97. ultralytics/data/converter.py +196 -36
  98. ultralytics/data/dataset.py +233 -94
  99. ultralytics/data/loaders.py +199 -96
  100. ultralytics/data/split_dota.py +39 -29
  101. ultralytics/data/utils.py +111 -41
  102. ultralytics/engine/__init__.py +1 -1
  103. ultralytics/engine/exporter.py +579 -244
  104. ultralytics/engine/model.py +604 -252
  105. ultralytics/engine/predictor.py +22 -11
  106. ultralytics/engine/results.py +1228 -218
  107. ultralytics/engine/trainer.py +191 -129
  108. ultralytics/engine/tuner.py +18 -18
  109. ultralytics/engine/validator.py +18 -15
  110. ultralytics/hub/__init__.py +31 -13
  111. ultralytics/hub/auth.py +11 -7
  112. ultralytics/hub/google/__init__.py +159 -0
  113. ultralytics/hub/session.py +128 -94
  114. ultralytics/hub/utils.py +20 -21
  115. ultralytics/models/__init__.py +4 -2
  116. ultralytics/models/fastsam/__init__.py +2 -3
  117. ultralytics/models/fastsam/model.py +26 -4
  118. ultralytics/models/fastsam/predict.py +127 -63
  119. ultralytics/models/fastsam/utils.py +1 -44
  120. ultralytics/models/fastsam/val.py +1 -1
  121. ultralytics/models/nas/__init__.py +1 -1
  122. ultralytics/models/nas/model.py +21 -10
  123. ultralytics/models/nas/predict.py +3 -6
  124. ultralytics/models/nas/val.py +4 -4
  125. ultralytics/models/rtdetr/__init__.py +1 -1
  126. ultralytics/models/rtdetr/model.py +1 -1
  127. ultralytics/models/rtdetr/predict.py +6 -8
  128. ultralytics/models/rtdetr/train.py +6 -2
  129. ultralytics/models/rtdetr/val.py +3 -3
  130. ultralytics/models/sam/__init__.py +3 -3
  131. ultralytics/models/sam/amg.py +29 -23
  132. ultralytics/models/sam/build.py +211 -13
  133. ultralytics/models/sam/model.py +91 -30
  134. ultralytics/models/sam/modules/__init__.py +1 -1
  135. ultralytics/models/sam/modules/blocks.py +1129 -0
  136. ultralytics/models/sam/modules/decoders.py +381 -53
  137. ultralytics/models/sam/modules/encoders.py +515 -324
  138. ultralytics/models/sam/modules/memory_attention.py +237 -0
  139. ultralytics/models/sam/modules/sam.py +969 -21
  140. ultralytics/models/sam/modules/tiny_encoder.py +425 -154
  141. ultralytics/models/sam/modules/transformer.py +159 -60
  142. ultralytics/models/sam/modules/utils.py +293 -0
  143. ultralytics/models/sam/predict.py +1263 -132
  144. ultralytics/models/utils/__init__.py +1 -1
  145. ultralytics/models/utils/loss.py +36 -24
  146. ultralytics/models/utils/ops.py +3 -7
  147. ultralytics/models/yolo/__init__.py +3 -3
  148. ultralytics/models/yolo/classify/__init__.py +1 -1
  149. ultralytics/models/yolo/classify/predict.py +7 -8
  150. ultralytics/models/yolo/classify/train.py +17 -22
  151. ultralytics/models/yolo/classify/val.py +8 -4
  152. ultralytics/models/yolo/detect/__init__.py +1 -1
  153. ultralytics/models/yolo/detect/predict.py +3 -5
  154. ultralytics/models/yolo/detect/train.py +11 -4
  155. ultralytics/models/yolo/detect/val.py +90 -52
  156. ultralytics/models/yolo/model.py +14 -9
  157. ultralytics/models/yolo/obb/__init__.py +1 -1
  158. ultralytics/models/yolo/obb/predict.py +2 -2
  159. ultralytics/models/yolo/obb/train.py +5 -3
  160. ultralytics/models/yolo/obb/val.py +41 -23
  161. ultralytics/models/yolo/pose/__init__.py +1 -1
  162. ultralytics/models/yolo/pose/predict.py +3 -5
  163. ultralytics/models/yolo/pose/train.py +2 -2
  164. ultralytics/models/yolo/pose/val.py +51 -17
  165. ultralytics/models/yolo/segment/__init__.py +1 -1
  166. ultralytics/models/yolo/segment/predict.py +3 -5
  167. ultralytics/models/yolo/segment/train.py +2 -2
  168. ultralytics/models/yolo/segment/val.py +60 -19
  169. ultralytics/models/yolo/world/__init__.py +5 -0
  170. ultralytics/models/yolo/world/train.py +92 -0
  171. ultralytics/models/yolo/world/train_world.py +109 -0
  172. ultralytics/nn/__init__.py +1 -1
  173. ultralytics/nn/autobackend.py +228 -93
  174. ultralytics/nn/modules/__init__.py +39 -14
  175. ultralytics/nn/modules/activation.py +21 -0
  176. ultralytics/nn/modules/block.py +526 -66
  177. ultralytics/nn/modules/conv.py +24 -7
  178. ultralytics/nn/modules/head.py +177 -34
  179. ultralytics/nn/modules/transformer.py +6 -5
  180. ultralytics/nn/modules/utils.py +1 -2
  181. ultralytics/nn/tasks.py +226 -82
  182. ultralytics/solutions/__init__.py +30 -1
  183. ultralytics/solutions/ai_gym.py +96 -143
  184. ultralytics/solutions/analytics.py +247 -0
  185. ultralytics/solutions/distance_calculation.py +78 -135
  186. ultralytics/solutions/heatmap.py +93 -247
  187. ultralytics/solutions/object_counter.py +184 -259
  188. ultralytics/solutions/parking_management.py +246 -0
  189. ultralytics/solutions/queue_management.py +112 -0
  190. ultralytics/solutions/region_counter.py +116 -0
  191. ultralytics/solutions/security_alarm.py +144 -0
  192. ultralytics/solutions/solutions.py +178 -0
  193. ultralytics/solutions/speed_estimation.py +86 -174
  194. ultralytics/solutions/streamlit_inference.py +190 -0
  195. ultralytics/solutions/trackzone.py +68 -0
  196. ultralytics/trackers/__init__.py +1 -1
  197. ultralytics/trackers/basetrack.py +32 -13
  198. ultralytics/trackers/bot_sort.py +61 -28
  199. ultralytics/trackers/byte_tracker.py +83 -51
  200. ultralytics/trackers/track.py +21 -6
  201. ultralytics/trackers/utils/__init__.py +1 -1
  202. ultralytics/trackers/utils/gmc.py +62 -48
  203. ultralytics/trackers/utils/kalman_filter.py +166 -35
  204. ultralytics/trackers/utils/matching.py +40 -21
  205. ultralytics/utils/__init__.py +511 -239
  206. ultralytics/utils/autobatch.py +40 -22
  207. ultralytics/utils/benchmarks.py +266 -85
  208. ultralytics/utils/callbacks/__init__.py +1 -1
  209. ultralytics/utils/callbacks/base.py +1 -3
  210. ultralytics/utils/callbacks/clearml.py +7 -6
  211. ultralytics/utils/callbacks/comet.py +39 -17
  212. ultralytics/utils/callbacks/dvc.py +1 -1
  213. ultralytics/utils/callbacks/hub.py +16 -16
  214. ultralytics/utils/callbacks/mlflow.py +28 -24
  215. ultralytics/utils/callbacks/neptune.py +6 -2
  216. ultralytics/utils/callbacks/raytune.py +3 -4
  217. ultralytics/utils/callbacks/tensorboard.py +18 -18
  218. ultralytics/utils/callbacks/wb.py +27 -20
  219. ultralytics/utils/checks.py +172 -100
  220. ultralytics/utils/dist.py +2 -1
  221. ultralytics/utils/downloads.py +40 -34
  222. ultralytics/utils/errors.py +1 -1
  223. ultralytics/utils/files.py +72 -38
  224. ultralytics/utils/instance.py +41 -19
  225. ultralytics/utils/loss.py +83 -55
  226. ultralytics/utils/metrics.py +61 -56
  227. ultralytics/utils/ops.py +94 -89
  228. ultralytics/utils/patches.py +30 -14
  229. ultralytics/utils/plotting.py +600 -269
  230. ultralytics/utils/tal.py +67 -26
  231. ultralytics/utils/torch_utils.py +305 -112
  232. ultralytics/utils/triton.py +2 -1
  233. ultralytics/utils/tuner.py +21 -12
  234. ultralytics-8.3.63.dist-info/METADATA +370 -0
  235. ultralytics-8.3.63.dist-info/RECORD +241 -0
  236. {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/WHEEL +1 -1
  237. ultralytics/data/explorer/__init__.py +0 -5
  238. ultralytics/data/explorer/explorer.py +0 -472
  239. ultralytics/data/explorer/gui/__init__.py +0 -1
  240. ultralytics/data/explorer/gui/dash.py +0 -268
  241. ultralytics/data/explorer/utils.py +0 -166
  242. ultralytics/models/fastsam/prompt.py +0 -357
  243. ultralytics-8.1.29.dist-info/METADATA +0 -373
  244. ultralytics-8.1.29.dist-info/RECORD +0 -197
  245. {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/LICENSE +0 -0
  246. {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/entry_points.txt +0 -0
  247. {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/top_level.txt +0 -0
@@ -1,278 +1,203 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
1
+ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
- from collections import defaultdict
4
-
5
- import cv2
6
-
7
- from ultralytics.utils.checks import check_imshow, check_requirements
3
+ from ultralytics.solutions.solutions import BaseSolution
8
4
  from ultralytics.utils.plotting import Annotator, colors
9
5
 
10
- check_requirements("shapely>=2.0.0")
11
-
12
- from shapely.geometry import LineString, Point, Polygon
13
-
14
-
15
- class ObjectCounter:
16
- """A class to manage the counting of objects in a real-time video stream based on their tracks."""
17
-
18
- def __init__(self):
19
- """Initializes the Counter with default values for various tracking and counting parameters."""
20
-
21
- # Mouse events
22
- self.is_drawing = False
23
- self.selected_point = None
24
-
25
- # Region & Line Information
26
- self.reg_pts = [(20, 400), (1260, 400)]
27
- self.line_dist_thresh = 15
28
- self.counting_region = None
29
- self.region_color = (255, 0, 255)
30
- self.region_thickness = 5
31
-
32
- # Image and annotation Information
33
- self.im0 = None
34
- self.tf = None
35
- self.view_img = False
36
- self.view_in_counts = True
37
- self.view_out_counts = True
38
-
39
- self.names = None # Classes names
40
- self.annotator = None # Annotator
41
- self.window_name = "Ultralytics YOLOv8 Object Counter"
42
-
43
- # Object counting Information
44
- self.in_counts = 0
45
- self.out_counts = 0
46
- self.counting_dict = {}
47
- self.count_txt_thickness = 0
48
- self.count_txt_color = (0, 0, 0)
49
- self.count_color = (255, 255, 255)
50
-
51
- # Tracks info
52
- self.track_history = defaultdict(list)
53
- self.track_thickness = 2
54
- self.draw_tracks = False
55
- self.track_color = (0, 255, 0)
56
-
57
- # Check if environment support imshow
58
- self.env_check = check_imshow(warn=True)
59
-
60
- def set_args(
61
- self,
62
- classes_names,
63
- reg_pts,
64
- count_reg_color=(255, 0, 255),
65
- line_thickness=2,
66
- track_thickness=2,
67
- view_img=False,
68
- view_in_counts=True,
69
- view_out_counts=True,
70
- draw_tracks=False,
71
- count_txt_thickness=2,
72
- count_txt_color=(0, 0, 0),
73
- count_color=(255, 255, 255),
74
- track_color=(0, 255, 0),
75
- region_thickness=5,
76
- line_dist_thresh=15,
77
- ):
6
+
7
+ class ObjectCounter(BaseSolution):
8
+ """
9
+ A class to manage the counting of objects in a real-time video stream based on their tracks.
10
+
11
+ This class extends the BaseSolution class and provides functionality for counting objects moving in and out of a
12
+ specified region in a video stream. It supports both polygonal and linear regions for counting.
13
+
14
+ Attributes:
15
+ in_count (int): Counter for objects moving inward.
16
+ out_count (int): Counter for objects moving outward.
17
+ counted_ids (List[int]): List of IDs of objects that have been counted.
18
+ classwise_counts (Dict[str, Dict[str, int]]): Dictionary for counts, categorized by object class.
19
+ region_initialized (bool): Flag indicating whether the counting region has been initialized.
20
+ show_in (bool): Flag to control display of inward count.
21
+ show_out (bool): Flag to control display of outward count.
22
+
23
+ Methods:
24
+ count_objects: Counts objects within a polygonal or linear region.
25
+ store_classwise_counts: Initializes class-wise counts if not already present.
26
+ display_counts: Displays object counts on the frame.
27
+ count: Processes input data (frames or object tracks) and updates counts.
28
+
29
+ Examples:
30
+ >>> counter = ObjectCounter()
31
+ >>> frame = cv2.imread("frame.jpg")
32
+ >>> processed_frame = counter.count(frame)
33
+ >>> print(f"Inward count: {counter.in_count}, Outward count: {counter.out_count}")
34
+ """
35
+
36
+ def __init__(self, **kwargs):
37
+ """Initializes the ObjectCounter class for real-time object counting in video streams."""
38
+ super().__init__(**kwargs)
39
+
40
+ self.in_count = 0 # Counter for objects moving inward
41
+ self.out_count = 0 # Counter for objects moving outward
42
+ self.counted_ids = [] # List of IDs of objects that have been counted
43
+ self.classwise_counts = {} # Dictionary for counts, categorized by object class
44
+ self.region_initialized = False # Bool variable for region initialization
45
+
46
+ self.show_in = self.CFG["show_in"]
47
+ self.show_out = self.CFG["show_out"]
48
+
49
+ def count_objects(self, current_centroid, track_id, prev_position, cls):
78
50
  """
79
- Configures the Counter's image, bounding box line thickness, and counting region points.
51
+ Counts objects within a polygonal or linear region based on their tracks.
80
52
 
81
53
  Args:
82
- line_thickness (int): Line thickness for bounding boxes.
83
- view_img (bool): Flag to control whether to display the video stream.
84
- view_in_counts (bool): Flag to control whether to display the incounts on video stream.
85
- view_out_counts (bool): Flag to control whether to display the outcounts on video stream.
86
- reg_pts (list): Initial list of points defining the counting region.
87
- classes_names (dict): Classes names
88
- track_thickness (int): Track thickness
89
- draw_tracks (Bool): draw tracks
90
- count_txt_thickness (int): Text thickness for object counting display
91
- count_txt_color (RGB color): count text color value
92
- count_color (RGB color): count text background color value
93
- count_reg_color (RGB color): Color of object counting region
94
- track_color (RGB color): color for tracks
95
- region_thickness (int): Object counting Region thickness
96
- line_dist_thresh (int): Euclidean Distance threshold for line counter
54
+ current_centroid (Tuple[float, float]): Current centroid values in the current frame.
55
+ track_id (int): Unique identifier for the tracked object.
56
+ prev_position (Tuple[float, float]): Last frame position coordinates (x, y) of the track.
57
+ cls (int): Class index for classwise count updates.
58
+
59
+ Examples:
60
+ >>> counter = ObjectCounter()
61
+ >>> track_line = {1: [100, 200], 2: [110, 210], 3: [120, 220]}
62
+ >>> box = [130, 230, 150, 250]
63
+ >>> track_id = 1
64
+ >>> prev_position = (120, 220)
65
+ >>> cls = 0
66
+ >>> counter.count_objects(current_centroid, track_id, prev_position, cls)
97
67
  """
98
- self.tf = line_thickness
99
- self.view_img = view_img
100
- self.view_in_counts = view_in_counts
101
- self.view_out_counts = view_out_counts
102
- self.track_thickness = track_thickness
103
- self.draw_tracks = draw_tracks
104
-
105
- # Region and line selection
106
- if len(reg_pts) == 2:
107
- print("Line Counter Initiated.")
108
- self.reg_pts = reg_pts
109
- self.counting_region = LineString(self.reg_pts)
110
- elif len(reg_pts) >= 3:
111
- print("Region Counter Initiated.")
112
- self.reg_pts = reg_pts
113
- self.counting_region = Polygon(self.reg_pts)
114
- else:
115
- print("Invalid Region points provided, region_points must be 2 for lines or >= 3 for polygons.")
116
- print("Using Line Counter Now")
117
- self.counting_region = LineString(self.reg_pts)
118
-
119
- self.names = classes_names
120
- self.track_color = track_color
121
- self.count_txt_thickness = count_txt_thickness
122
- self.count_txt_color = count_txt_color
123
- self.count_color = count_color
124
- self.region_color = count_reg_color
125
- self.region_thickness = region_thickness
126
- self.line_dist_thresh = line_dist_thresh
127
-
128
- def mouse_event_for_region(self, event, x, y, flags, params):
68
+ if prev_position is None or track_id in self.counted_ids:
69
+ return
70
+
71
+ if len(self.region) == 2: # Linear region (defined as a line segment)
72
+ line = self.LineString(self.region) # Check if the line intersects the trajectory of the object
73
+ if line.intersects(self.LineString([prev_position, current_centroid])):
74
+ # Determine orientation of the region (vertical or horizontal)
75
+ if abs(self.region[0][0] - self.region[1][0]) < abs(self.region[0][1] - self.region[1][1]):
76
+ # Vertical region: Compare x-coordinates to determine direction
77
+ if current_centroid[0] > prev_position[0]: # Moving right
78
+ self.in_count += 1
79
+ self.classwise_counts[self.names[cls]]["IN"] += 1
80
+ else: # Moving left
81
+ self.out_count += 1
82
+ self.classwise_counts[self.names[cls]]["OUT"] += 1
83
+ # Horizontal region: Compare y-coordinates to determine direction
84
+ elif current_centroid[1] > prev_position[1]: # Moving downward
85
+ self.in_count += 1
86
+ self.classwise_counts[self.names[cls]]["IN"] += 1
87
+ else: # Moving upward
88
+ self.out_count += 1
89
+ self.classwise_counts[self.names[cls]]["OUT"] += 1
90
+ self.counted_ids.append(track_id)
91
+
92
+ elif len(self.region) > 2: # Polygonal region
93
+ polygon = self.Polygon(self.region)
94
+ if polygon.contains(self.Point(current_centroid)):
95
+ # Determine motion direction for vertical or horizontal polygons
96
+ region_width = max(p[0] for p in self.region) - min(p[0] for p in self.region)
97
+ region_height = max(p[1] for p in self.region) - min(p[1] for p in self.region)
98
+
99
+ if (
100
+ region_width < region_height
101
+ and current_centroid[0] > prev_position[0]
102
+ or region_width >= region_height
103
+ and current_centroid[1] > prev_position[1]
104
+ ): # Moving right
105
+ self.in_count += 1
106
+ self.classwise_counts[self.names[cls]]["IN"] += 1
107
+ else: # Moving left
108
+ self.out_count += 1
109
+ self.classwise_counts[self.names[cls]]["OUT"] += 1
110
+ self.counted_ids.append(track_id)
111
+
112
+ def store_classwise_counts(self, cls):
129
113
  """
130
- This function is designed to move region with mouse events in a real-time video stream.
114
+ Initialize class-wise counts for a specific object class if not already present.
131
115
 
132
116
  Args:
133
- event (int): The type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN, etc.).
134
- x (int): The x-coordinate of the mouse pointer.
135
- y (int): The y-coordinate of the mouse pointer.
136
- flags (int): Any flags associated with the event (e.g., cv2.EVENT_FLAG_CTRLKEY,
137
- cv2.EVENT_FLAG_SHIFTKEY, etc.).
138
- params (dict): Additional parameters you may want to pass to the function.
117
+ cls (int): Class index for classwise count updates.
118
+
119
+ This method ensures that the 'classwise_counts' dictionary contains an entry for the specified class,
120
+ initializing 'IN' and 'OUT' counts to zero if the class is not already present.
121
+
122
+ Examples:
123
+ >>> counter = ObjectCounter()
124
+ >>> counter.store_classwise_counts(0) # Initialize counts for class index 0
125
+ >>> print(counter.classwise_counts)
126
+ {'person': {'IN': 0, 'OUT': 0}}
139
127
  """
140
- if event == cv2.EVENT_LBUTTONDOWN:
141
- for i, point in enumerate(self.reg_pts):
142
- if (
143
- isinstance(point, (tuple, list))
144
- and len(point) >= 2
145
- and (abs(x - point[0]) < 10 and abs(y - point[1]) < 10)
146
- ):
147
- self.selected_point = i
148
- self.is_drawing = True
149
- break
150
-
151
- elif event == cv2.EVENT_MOUSEMOVE:
152
- if self.is_drawing and self.selected_point is not None:
153
- self.reg_pts[self.selected_point] = (x, y)
154
- self.counting_region = Polygon(self.reg_pts)
155
-
156
- elif event == cv2.EVENT_LBUTTONUP:
157
- self.is_drawing = False
158
- self.selected_point = None
159
-
160
- def extract_and_process_tracks(self, tracks):
161
- """Extracts and processes tracks for object counting in a video stream."""
162
-
163
- # Annotator Init and region drawing
164
- self.annotator = Annotator(self.im0, self.tf, self.names)
165
-
166
- if tracks[0].boxes.id is not None:
167
- boxes = tracks[0].boxes.xyxy.cpu()
168
- clss = tracks[0].boxes.cls.cpu().tolist()
169
- track_ids = tracks[0].boxes.id.int().cpu().tolist()
170
-
171
- # Extract tracks
172
- for box, track_id, cls in zip(boxes, track_ids, clss):
173
- # Draw bounding box
174
- self.annotator.box_label(box, label=f"{track_id}:{self.names[cls]}", color=colors(int(cls), True))
175
-
176
- # Draw Tracks
177
- track_line = self.track_history[track_id]
178
- track_line.append((float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2)))
179
- if len(track_line) > 30:
180
- track_line.pop(0)
181
-
182
- # Draw track trails
183
- if self.draw_tracks:
184
- self.annotator.draw_centroid_and_tracks(
185
- track_line, color=self.track_color, track_thickness=self.track_thickness
186
- )
187
-
188
- prev_position = self.track_history[track_id][-2] if len(self.track_history[track_id]) > 1 else None
189
- centroid = Point((box[:2] + box[2:]) / 2)
190
-
191
- # Count objects
192
- if len(self.reg_pts) >= 3: # any polygon
193
- is_inside = self.counting_region.contains(centroid)
194
- current_position = "in" if is_inside else "out"
195
-
196
- if prev_position is not None:
197
- if self.counting_dict[track_id] != current_position and is_inside:
198
- self.in_counts += 1
199
- self.counting_dict[track_id] = "in"
200
- elif self.counting_dict[track_id] != current_position and not is_inside:
201
- self.out_counts += 1
202
- self.counting_dict[track_id] = "out"
203
- else:
204
- self.counting_dict[track_id] = current_position
205
-
206
- else:
207
- self.counting_dict[track_id] = current_position
208
-
209
- elif len(self.reg_pts) == 2:
210
- if prev_position is not None:
211
- is_inside = (box[0] - prev_position[0]) * (
212
- self.counting_region.centroid.x - prev_position[0]
213
- ) > 0
214
- current_position = "in" if is_inside else "out"
215
-
216
- if self.counting_dict[track_id] != current_position and is_inside:
217
- self.in_counts += 1
218
- self.counting_dict[track_id] = "in"
219
- elif self.counting_dict[track_id] != current_position and not is_inside:
220
- self.out_counts += 1
221
- self.counting_dict[track_id] = "out"
222
- else:
223
- self.counting_dict[track_id] = current_position
224
- else:
225
- self.counting_dict[track_id] = None
226
-
227
- incount_label = f"In Count : {self.in_counts}"
228
- outcount_label = f"OutCount : {self.out_counts}"
229
-
230
- # Display counts based on user choice
231
- counts_label = None
232
- if not self.view_in_counts and not self.view_out_counts:
233
- counts_label = None
234
- elif not self.view_in_counts:
235
- counts_label = outcount_label
236
- elif not self.view_out_counts:
237
- counts_label = incount_label
238
- else:
239
- counts_label = f"{incount_label} {outcount_label}"
240
-
241
- if counts_label is not None:
242
- self.annotator.count_labels(
243
- counts=counts_label,
244
- count_txt_size=self.count_txt_thickness,
245
- txt_color=self.count_txt_color,
246
- color=self.count_color,
247
- )
128
+ if self.names[cls] not in self.classwise_counts:
129
+ self.classwise_counts[self.names[cls]] = {"IN": 0, "OUT": 0}
248
130
 
249
- def display_frames(self):
250
- """Display frame."""
251
- if self.env_check:
252
- self.annotator.draw_region(reg_pts=self.reg_pts, color=self.region_color, thickness=self.region_thickness)
253
- cv2.namedWindow(self.window_name)
254
- if len(self.reg_pts) == 4: # only add mouse event If user drawn region
255
- cv2.setMouseCallback(self.window_name, self.mouse_event_for_region, {"region_points": self.reg_pts})
256
- cv2.imshow(self.window_name, self.im0)
257
- # Break Window
258
- if cv2.waitKey(1) & 0xFF == ord("q"):
259
- return
260
-
261
- def start_counting(self, im0, tracks):
131
+ def display_counts(self, im0):
262
132
  """
263
- Main function to start the object counting process.
133
+ Displays object counts on the input image or frame.
264
134
 
265
135
  Args:
266
- im0 (ndarray): Current frame from the video stream.
267
- tracks (list): List of tracks obtained from the object tracking process.
136
+ im0 (numpy.ndarray): The input image or frame to display counts on.
137
+
138
+ Examples:
139
+ >>> counter = ObjectCounter()
140
+ >>> frame = cv2.imread("image.jpg")
141
+ >>> counter.display_counts(frame)
268
142
  """
269
- self.im0 = im0 # store image
270
- self.extract_and_process_tracks(tracks) # draw region even if no objects
143
+ labels_dict = {
144
+ str.capitalize(key): f"{'IN ' + str(value['IN']) if self.show_in else ''} "
145
+ f"{'OUT ' + str(value['OUT']) if self.show_out else ''}".strip()
146
+ for key, value in self.classwise_counts.items()
147
+ if value["IN"] != 0 or value["OUT"] != 0
148
+ }
271
149
 
272
- if self.view_img:
273
- self.display_frames()
274
- return self.im0
150
+ if labels_dict:
151
+ self.annotator.display_analytics(im0, labels_dict, (104, 31, 17), (255, 255, 255), 10)
152
+
153
+ def count(self, im0):
154
+ """
155
+ Processes input data (frames or object tracks) and updates object counts.
156
+
157
+ This method initializes the counting region, extracts tracks, draws bounding boxes and regions, updates
158
+ object counts, and displays the results on the input image.
159
+
160
+ Args:
161
+ im0 (numpy.ndarray): The input image or frame to be processed.
162
+
163
+ Returns:
164
+ (numpy.ndarray): The processed image with annotations and count information.
165
+
166
+ Examples:
167
+ >>> counter = ObjectCounter()
168
+ >>> frame = cv2.imread("path/to/image.jpg")
169
+ >>> processed_frame = counter.count(frame)
170
+ """
171
+ if not self.region_initialized:
172
+ self.initialize_region()
173
+ self.region_initialized = True
174
+
175
+ self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
176
+ self.extract_tracks(im0) # Extract tracks
177
+
178
+ self.annotator.draw_region(
179
+ reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2
180
+ ) # Draw region
181
+
182
+ # Iterate over bounding boxes, track ids and classes index
183
+ for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
184
+ # Draw bounding box and counting region
185
+ self.annotator.box_label(box, label=self.names[cls], color=colors(cls, True))
186
+ self.store_tracking_history(track_id, box) # Store track history
187
+ self.store_classwise_counts(cls) # store classwise counts in dict
188
+
189
+ # Draw tracks of objects
190
+ self.annotator.draw_centroid_and_tracks(
191
+ self.track_line, color=colors(int(cls), True), track_thickness=self.line_width
192
+ )
193
+ current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
194
+ # store previous position of track for object counting
195
+ prev_position = None
196
+ if len(self.track_history[track_id]) > 1:
197
+ prev_position = self.track_history[track_id][-2]
198
+ self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting
275
199
 
200
+ self.display_counts(im0) # Display the counts on the frame
201
+ self.display_output(im0) # display output with base class function
276
202
 
277
- if __name__ == "__main__":
278
- ObjectCounter()
203
+ return im0 # return output image for more usage