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.
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/METADATA +41 -34
- dgenerate_ultralytics_headless-8.3.224.dist-info/RECORD +285 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/WHEEL +1 -1
- tests/__init__.py +7 -6
- tests/conftest.py +15 -39
- tests/test_cli.py +17 -17
- tests/test_cuda.py +17 -8
- tests/test_engine.py +36 -10
- tests/test_exports.py +98 -37
- tests/test_integrations.py +12 -15
- tests/test_python.py +126 -82
- tests/test_solutions.py +319 -135
- ultralytics/__init__.py +27 -9
- ultralytics/cfg/__init__.py +83 -87
- ultralytics/cfg/datasets/Argoverse.yaml +4 -4
- ultralytics/cfg/datasets/DOTAv1.5.yaml +2 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +2 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +2 -2
- ultralytics/cfg/datasets/HomeObjects-3K.yaml +4 -5
- ultralytics/cfg/datasets/ImageNet.yaml +3 -3
- ultralytics/cfg/datasets/Objects365.yaml +24 -20
- ultralytics/cfg/datasets/SKU-110K.yaml +9 -9
- ultralytics/cfg/datasets/VOC.yaml +10 -13
- ultralytics/cfg/datasets/VisDrone.yaml +43 -33
- ultralytics/cfg/datasets/african-wildlife.yaml +5 -5
- ultralytics/cfg/datasets/brain-tumor.yaml +4 -5
- ultralytics/cfg/datasets/carparts-seg.yaml +5 -5
- ultralytics/cfg/datasets/coco-pose.yaml +26 -4
- ultralytics/cfg/datasets/coco.yaml +4 -4
- ultralytics/cfg/datasets/coco128-seg.yaml +2 -2
- ultralytics/cfg/datasets/coco128.yaml +2 -2
- ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
- ultralytics/cfg/datasets/coco8-multispectral.yaml +2 -2
- ultralytics/cfg/datasets/coco8-pose.yaml +23 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +2 -2
- ultralytics/cfg/datasets/coco8.yaml +2 -2
- ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
- ultralytics/cfg/datasets/crack-seg.yaml +5 -5
- ultralytics/cfg/datasets/dog-pose.yaml +32 -4
- ultralytics/cfg/datasets/dota8-multispectral.yaml +2 -2
- ultralytics/cfg/datasets/dota8.yaml +2 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +29 -4
- ultralytics/cfg/datasets/lvis.yaml +9 -9
- ultralytics/cfg/datasets/medical-pills.yaml +4 -5
- ultralytics/cfg/datasets/open-images-v7.yaml +7 -10
- ultralytics/cfg/datasets/package-seg.yaml +5 -5
- ultralytics/cfg/datasets/signature.yaml +4 -4
- ultralytics/cfg/datasets/tiger-pose.yaml +20 -4
- ultralytics/cfg/datasets/xView.yaml +5 -5
- ultralytics/cfg/default.yaml +96 -93
- ultralytics/cfg/trackers/botsort.yaml +16 -17
- ultralytics/cfg/trackers/bytetrack.yaml +9 -11
- ultralytics/data/__init__.py +4 -4
- ultralytics/data/annotator.py +12 -12
- ultralytics/data/augment.py +531 -564
- ultralytics/data/base.py +76 -81
- ultralytics/data/build.py +206 -42
- ultralytics/data/converter.py +179 -78
- ultralytics/data/dataset.py +121 -121
- ultralytics/data/loaders.py +114 -91
- ultralytics/data/split.py +28 -15
- ultralytics/data/split_dota.py +67 -48
- ultralytics/data/utils.py +110 -89
- ultralytics/engine/exporter.py +422 -460
- ultralytics/engine/model.py +224 -252
- ultralytics/engine/predictor.py +94 -89
- ultralytics/engine/results.py +345 -595
- ultralytics/engine/trainer.py +231 -134
- ultralytics/engine/tuner.py +279 -73
- ultralytics/engine/validator.py +53 -46
- ultralytics/hub/__init__.py +26 -28
- ultralytics/hub/auth.py +30 -16
- ultralytics/hub/google/__init__.py +34 -36
- ultralytics/hub/session.py +53 -77
- ultralytics/hub/utils.py +23 -109
- ultralytics/models/__init__.py +1 -1
- ultralytics/models/fastsam/__init__.py +1 -1
- ultralytics/models/fastsam/model.py +36 -18
- ultralytics/models/fastsam/predict.py +33 -44
- ultralytics/models/fastsam/utils.py +4 -5
- ultralytics/models/fastsam/val.py +12 -14
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +16 -20
- ultralytics/models/nas/predict.py +12 -14
- ultralytics/models/nas/val.py +4 -5
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +9 -9
- ultralytics/models/rtdetr/predict.py +22 -17
- ultralytics/models/rtdetr/train.py +20 -16
- ultralytics/models/rtdetr/val.py +79 -59
- ultralytics/models/sam/__init__.py +8 -2
- ultralytics/models/sam/amg.py +53 -38
- ultralytics/models/sam/build.py +29 -31
- ultralytics/models/sam/model.py +33 -38
- ultralytics/models/sam/modules/blocks.py +159 -182
- ultralytics/models/sam/modules/decoders.py +38 -47
- ultralytics/models/sam/modules/encoders.py +114 -133
- ultralytics/models/sam/modules/memory_attention.py +38 -31
- ultralytics/models/sam/modules/sam.py +114 -93
- ultralytics/models/sam/modules/tiny_encoder.py +268 -291
- ultralytics/models/sam/modules/transformer.py +59 -66
- ultralytics/models/sam/modules/utils.py +55 -72
- ultralytics/models/sam/predict.py +745 -341
- ultralytics/models/utils/loss.py +118 -107
- ultralytics/models/utils/ops.py +118 -71
- ultralytics/models/yolo/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +28 -26
- ultralytics/models/yolo/classify/train.py +50 -81
- ultralytics/models/yolo/classify/val.py +68 -61
- ultralytics/models/yolo/detect/predict.py +12 -15
- ultralytics/models/yolo/detect/train.py +56 -46
- ultralytics/models/yolo/detect/val.py +279 -223
- ultralytics/models/yolo/model.py +167 -86
- ultralytics/models/yolo/obb/predict.py +7 -11
- ultralytics/models/yolo/obb/train.py +23 -25
- ultralytics/models/yolo/obb/val.py +107 -99
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +12 -14
- ultralytics/models/yolo/pose/train.py +31 -69
- ultralytics/models/yolo/pose/val.py +119 -254
- ultralytics/models/yolo/segment/predict.py +21 -25
- ultralytics/models/yolo/segment/train.py +12 -66
- ultralytics/models/yolo/segment/val.py +126 -305
- ultralytics/models/yolo/world/train.py +53 -45
- ultralytics/models/yolo/world/train_world.py +51 -32
- ultralytics/models/yolo/yoloe/__init__.py +7 -7
- ultralytics/models/yolo/yoloe/predict.py +30 -37
- ultralytics/models/yolo/yoloe/train.py +89 -71
- ultralytics/models/yolo/yoloe/train_seg.py +15 -17
- ultralytics/models/yolo/yoloe/val.py +56 -41
- ultralytics/nn/__init__.py +9 -11
- ultralytics/nn/autobackend.py +179 -107
- ultralytics/nn/modules/__init__.py +67 -67
- ultralytics/nn/modules/activation.py +8 -7
- ultralytics/nn/modules/block.py +302 -323
- ultralytics/nn/modules/conv.py +61 -104
- ultralytics/nn/modules/head.py +488 -186
- ultralytics/nn/modules/transformer.py +183 -123
- ultralytics/nn/modules/utils.py +15 -20
- ultralytics/nn/tasks.py +327 -203
- ultralytics/nn/text_model.py +81 -65
- ultralytics/py.typed +1 -0
- ultralytics/solutions/__init__.py +12 -12
- ultralytics/solutions/ai_gym.py +19 -27
- ultralytics/solutions/analytics.py +36 -26
- ultralytics/solutions/config.py +29 -28
- ultralytics/solutions/distance_calculation.py +23 -24
- ultralytics/solutions/heatmap.py +17 -19
- ultralytics/solutions/instance_segmentation.py +21 -19
- ultralytics/solutions/object_blurrer.py +16 -17
- ultralytics/solutions/object_counter.py +48 -53
- ultralytics/solutions/object_cropper.py +22 -16
- ultralytics/solutions/parking_management.py +61 -58
- ultralytics/solutions/queue_management.py +19 -19
- ultralytics/solutions/region_counter.py +63 -50
- ultralytics/solutions/security_alarm.py +22 -25
- ultralytics/solutions/similarity_search.py +107 -60
- ultralytics/solutions/solutions.py +343 -262
- ultralytics/solutions/speed_estimation.py +35 -31
- ultralytics/solutions/streamlit_inference.py +104 -40
- ultralytics/solutions/templates/similarity-search.html +31 -24
- ultralytics/solutions/trackzone.py +24 -24
- ultralytics/solutions/vision_eye.py +11 -12
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +18 -27
- ultralytics/trackers/bot_sort.py +48 -39
- ultralytics/trackers/byte_tracker.py +94 -94
- ultralytics/trackers/track.py +7 -16
- ultralytics/trackers/utils/gmc.py +37 -69
- ultralytics/trackers/utils/kalman_filter.py +68 -76
- ultralytics/trackers/utils/matching.py +13 -17
- ultralytics/utils/__init__.py +251 -275
- ultralytics/utils/autobatch.py +19 -7
- ultralytics/utils/autodevice.py +68 -38
- ultralytics/utils/benchmarks.py +169 -130
- ultralytics/utils/callbacks/base.py +12 -13
- ultralytics/utils/callbacks/clearml.py +14 -15
- ultralytics/utils/callbacks/comet.py +139 -66
- ultralytics/utils/callbacks/dvc.py +19 -27
- ultralytics/utils/callbacks/hub.py +8 -6
- ultralytics/utils/callbacks/mlflow.py +6 -10
- ultralytics/utils/callbacks/neptune.py +11 -19
- ultralytics/utils/callbacks/platform.py +73 -0
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +9 -12
- ultralytics/utils/callbacks/wb.py +33 -30
- ultralytics/utils/checks.py +163 -114
- ultralytics/utils/cpu.py +89 -0
- ultralytics/utils/dist.py +24 -20
- ultralytics/utils/downloads.py +176 -146
- ultralytics/utils/errors.py +11 -13
- ultralytics/utils/events.py +113 -0
- ultralytics/utils/export/__init__.py +7 -0
- ultralytics/utils/{export.py → export/engine.py} +81 -63
- ultralytics/utils/export/imx.py +294 -0
- ultralytics/utils/export/tensorflow.py +217 -0
- ultralytics/utils/files.py +33 -36
- ultralytics/utils/git.py +137 -0
- ultralytics/utils/instance.py +105 -120
- ultralytics/utils/logger.py +404 -0
- ultralytics/utils/loss.py +99 -61
- ultralytics/utils/metrics.py +649 -478
- ultralytics/utils/nms.py +337 -0
- ultralytics/utils/ops.py +263 -451
- ultralytics/utils/patches.py +70 -31
- ultralytics/utils/plotting.py +253 -223
- ultralytics/utils/tal.py +48 -61
- ultralytics/utils/torch_utils.py +244 -251
- ultralytics/utils/tqdm.py +438 -0
- ultralytics/utils/triton.py +22 -23
- ultralytics/utils/tuner.py +11 -10
- dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD +0 -272
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
25
|
-
Polygon
|
|
26
|
-
Point
|
|
27
|
-
|
|
28
|
-
|
|
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 (
|
|
31
|
-
names (
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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):
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
148
|
+
track_id (int, optional): The unique identifier for the tracked object.
|
|
120
149
|
|
|
121
150
|
Returns:
|
|
122
|
-
(str
|
|
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
|
-
|
|
140
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
224
|
-
and
|
|
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:
|
|
236
|
-
queue_counts_display:
|
|
237
|
-
display_analytics:
|
|
238
|
-
estimate_pose_angle:
|
|
239
|
-
|
|
240
|
-
plot_workout_information:
|
|
241
|
-
plot_angle_and_count_and_stage:
|
|
242
|
-
plot_distance_and_line:
|
|
243
|
-
display_objects_labels:
|
|
244
|
-
sweep_annotator:
|
|
245
|
-
visioneye:
|
|
246
|
-
|
|
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__(
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
266
|
-
pil (bool
|
|
267
|
-
example (str
|
|
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(
|
|
272
|
-
|
|
273
|
-
|
|
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 (
|
|
277
|
-
color (
|
|
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(
|
|
287
|
-
|
|
288
|
-
|
|
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 (
|
|
293
|
-
region_color (
|
|
294
|
-
txt_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(
|
|
327
|
-
|
|
328
|
-
|
|
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 (
|
|
333
|
-
txt_color (
|
|
334
|
-
bg_color (
|
|
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
|
-
|
|
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 (
|
|
362
|
-
b (
|
|
363
|
-
c (
|
|
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(
|
|
373
|
-
|
|
374
|
-
|
|
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 (
|
|
378
|
-
indices (
|
|
379
|
-
radius (int
|
|
380
|
-
conf_thresh (float
|
|
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
|
-
|
|
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(
|
|
403
|
-
|
|
404
|
-
|
|
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 (
|
|
409
|
-
color (
|
|
410
|
-
txt_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,
|
|
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 (
|
|
441
|
-
color (
|
|
442
|
-
txt_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,
|
|
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 (
|
|
467
|
-
line_color (
|
|
468
|
-
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(
|
|
495
|
-
|
|
496
|
-
|
|
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 (
|
|
502
|
-
bg_color (
|
|
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(
|
|
535
|
-
|
|
536
|
-
|
|
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 (
|
|
543
|
-
txt_color (
|
|
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(
|
|
569
|
-
|
|
570
|
-
|
|
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 (
|
|
574
|
-
center_point (
|
|
575
|
-
color (
|
|
576
|
-
pin_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
|
|
584
|
-
|
|
585
|
-
|
|
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 (
|
|
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 (
|
|
591
|
-
txt_color (
|
|
592
|
-
|
|
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
|
|
599
|
-
|
|
600
|
-
#
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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.
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
805
|
+
return ", ".join(f"{k}={v}" for k, v in attrs.items())
|