ultralytics 8.1.29__py3-none-any.whl → 8.3.62__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/__init__.py +22 -0
- tests/conftest.py +83 -0
- tests/test_cli.py +122 -0
- tests/test_cuda.py +155 -0
- tests/test_engine.py +131 -0
- tests/test_exports.py +216 -0
- tests/test_integrations.py +150 -0
- tests/test_python.py +615 -0
- tests/test_solutions.py +94 -0
- ultralytics/__init__.py +11 -8
- ultralytics/cfg/__init__.py +569 -131
- ultralytics/cfg/datasets/Argoverse.yaml +2 -1
- ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
- ultralytics/cfg/datasets/ImageNet.yaml +2 -1
- ultralytics/cfg/datasets/Objects365.yaml +5 -4
- ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
- ultralytics/cfg/datasets/VOC.yaml +3 -2
- ultralytics/cfg/datasets/VisDrone.yaml +6 -5
- ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
- ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
- ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco-pose.yaml +7 -6
- ultralytics/cfg/datasets/coco.yaml +3 -2
- ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
- ultralytics/cfg/datasets/coco128.yaml +4 -3
- ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco8.yaml +3 -2
- ultralytics/cfg/datasets/crack-seg.yaml +3 -2
- ultralytics/cfg/datasets/dog-pose.yaml +24 -0
- ultralytics/cfg/datasets/dota8.yaml +3 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
- ultralytics/cfg/datasets/lvis.yaml +1236 -0
- ultralytics/cfg/datasets/medical-pills.yaml +22 -0
- ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
- ultralytics/cfg/datasets/package-seg.yaml +5 -4
- ultralytics/cfg/datasets/signature.yaml +21 -0
- ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
- ultralytics/cfg/datasets/xView.yaml +2 -1
- ultralytics/cfg/default.yaml +14 -11
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
- ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
- ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
- ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
- ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
- ultralytics/cfg/models/11/yolo11.yaml +50 -0
- ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
- ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
- ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5.yaml +5 -2
- ultralytics/cfg/models/v6/yolov6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
- ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8.yaml +5 -2
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
- ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
- ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
- ultralytics/cfg/solutions/default.yaml +24 -0
- ultralytics/cfg/trackers/botsort.yaml +8 -5
- ultralytics/cfg/trackers/bytetrack.yaml +8 -5
- ultralytics/data/__init__.py +14 -3
- ultralytics/data/annotator.py +37 -15
- ultralytics/data/augment.py +1783 -289
- ultralytics/data/base.py +62 -27
- ultralytics/data/build.py +36 -8
- ultralytics/data/converter.py +196 -36
- ultralytics/data/dataset.py +233 -94
- ultralytics/data/loaders.py +199 -96
- ultralytics/data/split_dota.py +39 -29
- ultralytics/data/utils.py +110 -40
- ultralytics/engine/__init__.py +1 -1
- ultralytics/engine/exporter.py +569 -242
- ultralytics/engine/model.py +604 -252
- ultralytics/engine/predictor.py +22 -11
- ultralytics/engine/results.py +1228 -218
- ultralytics/engine/trainer.py +190 -129
- ultralytics/engine/tuner.py +18 -18
- ultralytics/engine/validator.py +18 -15
- ultralytics/hub/__init__.py +31 -13
- ultralytics/hub/auth.py +11 -7
- ultralytics/hub/google/__init__.py +159 -0
- ultralytics/hub/session.py +128 -94
- ultralytics/hub/utils.py +20 -21
- ultralytics/models/__init__.py +4 -2
- ultralytics/models/fastsam/__init__.py +2 -3
- ultralytics/models/fastsam/model.py +26 -4
- ultralytics/models/fastsam/predict.py +127 -63
- ultralytics/models/fastsam/utils.py +1 -44
- ultralytics/models/fastsam/val.py +1 -1
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +21 -10
- ultralytics/models/nas/predict.py +3 -6
- ultralytics/models/nas/val.py +4 -4
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +1 -1
- ultralytics/models/rtdetr/predict.py +6 -8
- ultralytics/models/rtdetr/train.py +6 -2
- ultralytics/models/rtdetr/val.py +3 -3
- ultralytics/models/sam/__init__.py +3 -3
- ultralytics/models/sam/amg.py +29 -23
- ultralytics/models/sam/build.py +211 -13
- ultralytics/models/sam/model.py +91 -30
- ultralytics/models/sam/modules/__init__.py +1 -1
- ultralytics/models/sam/modules/blocks.py +1129 -0
- ultralytics/models/sam/modules/decoders.py +381 -53
- ultralytics/models/sam/modules/encoders.py +515 -324
- ultralytics/models/sam/modules/memory_attention.py +237 -0
- ultralytics/models/sam/modules/sam.py +969 -21
- ultralytics/models/sam/modules/tiny_encoder.py +425 -154
- ultralytics/models/sam/modules/transformer.py +159 -60
- ultralytics/models/sam/modules/utils.py +293 -0
- ultralytics/models/sam/predict.py +1263 -132
- ultralytics/models/utils/__init__.py +1 -1
- ultralytics/models/utils/loss.py +36 -24
- ultralytics/models/utils/ops.py +3 -7
- ultralytics/models/yolo/__init__.py +3 -3
- ultralytics/models/yolo/classify/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +7 -8
- ultralytics/models/yolo/classify/train.py +17 -22
- ultralytics/models/yolo/classify/val.py +8 -4
- ultralytics/models/yolo/detect/__init__.py +1 -1
- ultralytics/models/yolo/detect/predict.py +3 -5
- ultralytics/models/yolo/detect/train.py +11 -4
- ultralytics/models/yolo/detect/val.py +90 -52
- ultralytics/models/yolo/model.py +14 -9
- ultralytics/models/yolo/obb/__init__.py +1 -1
- ultralytics/models/yolo/obb/predict.py +2 -2
- ultralytics/models/yolo/obb/train.py +5 -3
- ultralytics/models/yolo/obb/val.py +41 -23
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +3 -5
- ultralytics/models/yolo/pose/train.py +2 -2
- ultralytics/models/yolo/pose/val.py +51 -17
- ultralytics/models/yolo/segment/__init__.py +1 -1
- ultralytics/models/yolo/segment/predict.py +3 -5
- ultralytics/models/yolo/segment/train.py +2 -2
- ultralytics/models/yolo/segment/val.py +60 -19
- ultralytics/models/yolo/world/__init__.py +5 -0
- ultralytics/models/yolo/world/train.py +92 -0
- ultralytics/models/yolo/world/train_world.py +109 -0
- ultralytics/nn/__init__.py +1 -1
- ultralytics/nn/autobackend.py +228 -93
- ultralytics/nn/modules/__init__.py +39 -14
- ultralytics/nn/modules/activation.py +21 -0
- ultralytics/nn/modules/block.py +526 -66
- ultralytics/nn/modules/conv.py +24 -7
- ultralytics/nn/modules/head.py +177 -34
- ultralytics/nn/modules/transformer.py +6 -5
- ultralytics/nn/modules/utils.py +1 -2
- ultralytics/nn/tasks.py +225 -77
- ultralytics/solutions/__init__.py +30 -1
- ultralytics/solutions/ai_gym.py +96 -143
- ultralytics/solutions/analytics.py +247 -0
- ultralytics/solutions/distance_calculation.py +78 -135
- ultralytics/solutions/heatmap.py +93 -247
- ultralytics/solutions/object_counter.py +184 -259
- ultralytics/solutions/parking_management.py +246 -0
- ultralytics/solutions/queue_management.py +112 -0
- ultralytics/solutions/region_counter.py +116 -0
- ultralytics/solutions/security_alarm.py +144 -0
- ultralytics/solutions/solutions.py +178 -0
- ultralytics/solutions/speed_estimation.py +86 -174
- ultralytics/solutions/streamlit_inference.py +190 -0
- ultralytics/solutions/trackzone.py +68 -0
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +32 -13
- ultralytics/trackers/bot_sort.py +61 -28
- ultralytics/trackers/byte_tracker.py +83 -51
- ultralytics/trackers/track.py +21 -6
- ultralytics/trackers/utils/__init__.py +1 -1
- ultralytics/trackers/utils/gmc.py +62 -48
- ultralytics/trackers/utils/kalman_filter.py +166 -35
- ultralytics/trackers/utils/matching.py +40 -21
- ultralytics/utils/__init__.py +511 -239
- ultralytics/utils/autobatch.py +40 -22
- ultralytics/utils/benchmarks.py +266 -85
- ultralytics/utils/callbacks/__init__.py +1 -1
- ultralytics/utils/callbacks/base.py +1 -3
- ultralytics/utils/callbacks/clearml.py +7 -6
- ultralytics/utils/callbacks/comet.py +39 -17
- ultralytics/utils/callbacks/dvc.py +1 -1
- ultralytics/utils/callbacks/hub.py +16 -16
- ultralytics/utils/callbacks/mlflow.py +28 -24
- ultralytics/utils/callbacks/neptune.py +6 -2
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +18 -18
- ultralytics/utils/callbacks/wb.py +27 -20
- ultralytics/utils/checks.py +160 -100
- ultralytics/utils/dist.py +2 -1
- ultralytics/utils/downloads.py +40 -34
- ultralytics/utils/errors.py +1 -1
- ultralytics/utils/files.py +72 -38
- ultralytics/utils/instance.py +41 -19
- ultralytics/utils/loss.py +83 -55
- ultralytics/utils/metrics.py +61 -56
- ultralytics/utils/ops.py +94 -89
- ultralytics/utils/patches.py +30 -14
- ultralytics/utils/plotting.py +600 -269
- ultralytics/utils/tal.py +67 -26
- ultralytics/utils/torch_utils.py +302 -102
- ultralytics/utils/triton.py +2 -1
- ultralytics/utils/tuner.py +21 -12
- ultralytics-8.3.62.dist-info/METADATA +370 -0
- ultralytics-8.3.62.dist-info/RECORD +241 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/WHEEL +1 -1
- ultralytics/data/explorer/__init__.py +0 -5
- ultralytics/data/explorer/explorer.py +0 -472
- ultralytics/data/explorer/gui/__init__.py +0 -1
- ultralytics/data/explorer/gui/dash.py +0 -268
- ultralytics/data/explorer/utils.py +0 -166
- ultralytics/models/fastsam/prompt.py +0 -357
- ultralytics-8.1.29.dist-info/METADATA +0 -373
- ultralytics-8.1.29.dist-info/RECORD +0 -197
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/top_level.txt +0 -0
@@ -1,278 +1,203 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
-
from
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
class
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
self.
|
45
|
-
self.
|
46
|
-
self.
|
47
|
-
self.
|
48
|
-
self.
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
51
|
+
Counts objects within a polygonal or linear region based on their tracks.
|
80
52
|
|
81
53
|
Args:
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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.
|
99
|
-
|
100
|
-
|
101
|
-
self.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
self.
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
114
|
+
Initialize class-wise counts for a specific object class if not already present.
|
131
115
|
|
132
116
|
Args:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
141
|
-
|
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
|
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
|
-
|
133
|
+
Displays object counts on the input image or frame.
|
264
134
|
|
265
135
|
Args:
|
266
|
-
im0 (ndarray):
|
267
|
-
|
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
|
-
|
270
|
-
|
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
|
273
|
-
self.
|
274
|
-
|
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
|
-
|
278
|
-
ObjectCounter()
|
203
|
+
return im0 # return output image for more usage
|