ultralytics 8.3.88__py3-none-any.whl → 8.3.90__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/conftest.py +2 -2
- tests/test_cli.py +13 -11
- tests/test_cuda.py +10 -1
- tests/test_integrations.py +1 -5
- tests/test_python.py +16 -16
- tests/test_solutions.py +9 -9
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +3 -1
- ultralytics/cfg/models/11/yolo11-cls.yaml +5 -5
- ultralytics/cfg/models/11/yolo11-obb.yaml +5 -5
- ultralytics/cfg/models/11/yolo11-pose.yaml +5 -5
- ultralytics/cfg/models/11/yolo11-seg.yaml +5 -5
- ultralytics/cfg/models/11/yolo11.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-p6.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-world.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -5
- ultralytics/cfg/models/v8/yolov8.yaml +5 -5
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9c.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9e.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9m.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9s.yaml +1 -1
- ultralytics/cfg/models/v9/yolov9t.yaml +1 -1
- ultralytics/data/annotator.py +9 -14
- ultralytics/data/base.py +125 -39
- ultralytics/data/build.py +63 -24
- ultralytics/data/converter.py +34 -33
- ultralytics/data/dataset.py +207 -53
- ultralytics/data/loaders.py +1 -0
- ultralytics/data/split_dota.py +39 -12
- ultralytics/data/utils.py +33 -47
- ultralytics/engine/exporter.py +19 -17
- ultralytics/engine/model.py +69 -90
- ultralytics/engine/predictor.py +106 -21
- ultralytics/engine/trainer.py +32 -23
- ultralytics/engine/tuner.py +31 -38
- ultralytics/engine/validator.py +75 -41
- ultralytics/hub/__init__.py +21 -26
- ultralytics/hub/auth.py +9 -12
- ultralytics/hub/session.py +76 -21
- ultralytics/hub/utils.py +19 -17
- ultralytics/models/fastsam/model.py +23 -17
- ultralytics/models/fastsam/predict.py +36 -16
- ultralytics/models/fastsam/utils.py +5 -5
- ultralytics/models/fastsam/val.py +6 -6
- ultralytics/models/nas/model.py +29 -24
- ultralytics/models/nas/predict.py +14 -11
- ultralytics/models/nas/val.py +11 -13
- ultralytics/models/rtdetr/model.py +20 -11
- ultralytics/models/rtdetr/predict.py +21 -21
- ultralytics/models/rtdetr/train.py +25 -24
- ultralytics/models/rtdetr/val.py +47 -14
- ultralytics/models/sam/__init__.py +1 -1
- ultralytics/models/sam/amg.py +50 -4
- ultralytics/models/sam/model.py +8 -14
- ultralytics/models/sam/modules/decoders.py +18 -21
- ultralytics/models/sam/modules/encoders.py +25 -46
- ultralytics/models/sam/modules/memory_attention.py +19 -15
- ultralytics/models/sam/modules/sam.py +18 -25
- ultralytics/models/sam/modules/tiny_encoder.py +19 -29
- ultralytics/models/sam/modules/transformer.py +35 -57
- ultralytics/models/sam/modules/utils.py +15 -15
- ultralytics/models/sam/predict.py +0 -3
- ultralytics/models/utils/loss.py +87 -36
- ultralytics/models/utils/ops.py +26 -31
- ultralytics/models/yolo/classify/predict.py +30 -12
- ultralytics/models/yolo/classify/train.py +83 -19
- ultralytics/models/yolo/classify/val.py +45 -23
- ultralytics/models/yolo/detect/predict.py +29 -19
- ultralytics/models/yolo/detect/train.py +90 -23
- ultralytics/models/yolo/detect/val.py +150 -29
- ultralytics/models/yolo/model.py +1 -2
- ultralytics/models/yolo/obb/predict.py +18 -13
- ultralytics/models/yolo/obb/train.py +12 -8
- ultralytics/models/yolo/obb/val.py +35 -22
- ultralytics/models/yolo/pose/predict.py +28 -15
- ultralytics/models/yolo/pose/train.py +21 -8
- ultralytics/models/yolo/pose/val.py +51 -31
- ultralytics/models/yolo/segment/predict.py +27 -16
- ultralytics/models/yolo/segment/train.py +11 -8
- ultralytics/models/yolo/segment/val.py +110 -29
- ultralytics/models/yolo/world/train.py +43 -16
- ultralytics/models/yolo/world/train_world.py +61 -36
- ultralytics/nn/autobackend.py +28 -14
- ultralytics/nn/modules/__init__.py +12 -12
- ultralytics/nn/modules/activation.py +12 -3
- ultralytics/nn/modules/block.py +587 -84
- ultralytics/nn/modules/conv.py +418 -54
- ultralytics/nn/modules/head.py +3 -4
- ultralytics/nn/modules/transformer.py +320 -34
- ultralytics/nn/modules/utils.py +17 -3
- ultralytics/nn/tasks.py +226 -79
- ultralytics/solutions/ai_gym.py +2 -2
- ultralytics/solutions/analytics.py +4 -4
- ultralytics/solutions/heatmap.py +4 -4
- ultralytics/solutions/instance_segmentation.py +10 -4
- ultralytics/solutions/object_blurrer.py +2 -2
- ultralytics/solutions/object_counter.py +2 -2
- ultralytics/solutions/object_cropper.py +2 -2
- ultralytics/solutions/parking_management.py +9 -9
- ultralytics/solutions/queue_management.py +1 -1
- ultralytics/solutions/region_counter.py +2 -2
- ultralytics/solutions/security_alarm.py +7 -7
- ultralytics/solutions/solutions.py +7 -4
- ultralytics/solutions/speed_estimation.py +2 -2
- ultralytics/solutions/streamlit_inference.py +6 -6
- ultralytics/solutions/trackzone.py +9 -2
- ultralytics/solutions/vision_eye.py +4 -4
- ultralytics/trackers/basetrack.py +1 -1
- ultralytics/trackers/bot_sort.py +23 -22
- ultralytics/trackers/byte_tracker.py +4 -4
- ultralytics/trackers/track.py +2 -1
- ultralytics/trackers/utils/gmc.py +26 -27
- ultralytics/trackers/utils/kalman_filter.py +31 -29
- ultralytics/trackers/utils/matching.py +7 -7
- ultralytics/utils/__init__.py +37 -35
- ultralytics/utils/autobatch.py +5 -5
- ultralytics/utils/benchmarks.py +111 -18
- ultralytics/utils/callbacks/base.py +3 -3
- ultralytics/utils/callbacks/clearml.py +11 -11
- ultralytics/utils/callbacks/comet.py +35 -22
- ultralytics/utils/callbacks/dvc.py +11 -10
- ultralytics/utils/callbacks/hub.py +8 -8
- ultralytics/utils/callbacks/mlflow.py +1 -1
- ultralytics/utils/callbacks/neptune.py +12 -10
- ultralytics/utils/callbacks/raytune.py +1 -1
- ultralytics/utils/callbacks/tensorboard.py +6 -6
- ultralytics/utils/callbacks/wb.py +16 -16
- ultralytics/utils/checks.py +139 -68
- ultralytics/utils/dist.py +15 -2
- ultralytics/utils/downloads.py +37 -56
- ultralytics/utils/files.py +12 -13
- ultralytics/utils/instance.py +117 -52
- ultralytics/utils/loss.py +28 -33
- ultralytics/utils/metrics.py +246 -181
- ultralytics/utils/ops.py +65 -61
- ultralytics/utils/patches.py +8 -6
- ultralytics/utils/plotting.py +72 -59
- ultralytics/utils/tal.py +88 -57
- ultralytics/utils/torch_utils.py +202 -64
- ultralytics/utils/triton.py +13 -3
- ultralytics/utils/tuner.py +13 -25
- {ultralytics-8.3.88.dist-info → ultralytics-8.3.90.dist-info}/METADATA +2 -2
- ultralytics-8.3.90.dist-info/RECORD +250 -0
- ultralytics-8.3.88.dist-info/RECORD +0 -250
- {ultralytics-8.3.88.dist-info → ultralytics-8.3.90.dist-info}/LICENSE +0 -0
- {ultralytics-8.3.88.dist-info → ultralytics-8.3.90.dist-info}/WHEEL +0 -0
- {ultralytics-8.3.88.dist-info → ultralytics-8.3.90.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.3.88.dist-info → ultralytics-8.3.90.dist-info}/top_level.txt +0 -0
ultralytics/utils/downloads.py
CHANGED
@@ -48,10 +48,8 @@ def is_url(url, check=False):
|
|
48
48
|
(bool): Returns True for a valid URL. If 'check' is True, also returns True if the URL exists online.
|
49
49
|
Returns False otherwise.
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
valid = is_url("https://www.example.com")
|
54
|
-
```
|
51
|
+
Examples:
|
52
|
+
>>> valid = is_url("https://www.example.com")
|
55
53
|
"""
|
56
54
|
try:
|
57
55
|
url = str(url)
|
@@ -67,20 +65,17 @@ def is_url(url, check=False):
|
|
67
65
|
|
68
66
|
def delete_dsstore(path, files_to_delete=(".DS_Store", "__MACOSX")):
|
69
67
|
"""
|
70
|
-
|
68
|
+
Delete all ".DS_store" files in a specified directory.
|
71
69
|
|
72
70
|
Args:
|
73
71
|
path (str, optional): The directory path where the ".DS_store" files should be deleted.
|
74
72
|
files_to_delete (tuple): The files to be deleted.
|
75
73
|
|
76
|
-
|
77
|
-
|
78
|
-
|
74
|
+
Examples:
|
75
|
+
>>> from ultralytics.utils.downloads import delete_dsstore
|
76
|
+
>>> delete_dsstore("path/to/dir")
|
79
77
|
|
80
|
-
|
81
|
-
```
|
82
|
-
|
83
|
-
Note:
|
78
|
+
Notes:
|
84
79
|
".DS_store" files are created by the Apple operating system and contain metadata about folders and files. They
|
85
80
|
are hidden system files and can cause issues when transferring files between different operating systems.
|
86
81
|
"""
|
@@ -105,12 +100,9 @@ def zip_directory(directory, compress=True, exclude=(".DS_Store", "__MACOSX"), p
|
|
105
100
|
Returns:
|
106
101
|
(Path): The path to the resulting zip file.
|
107
102
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
file = zip_directory("path/to/dir")
|
113
|
-
```
|
103
|
+
Examples:
|
104
|
+
>>> from ultralytics.utils.downloads import zip_directory
|
105
|
+
>>> file = zip_directory("path/to/dir")
|
114
106
|
"""
|
115
107
|
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
116
108
|
|
@@ -140,7 +132,7 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
140
132
|
|
141
133
|
Args:
|
142
134
|
file (str | Path): The path to the zipfile to be extracted.
|
143
|
-
path (str, optional): The path to extract the zipfile to. Defaults to None.
|
135
|
+
path (str | Path, optional): The path to extract the zipfile to. Defaults to None.
|
144
136
|
exclude (tuple, optional): A tuple of filename strings to be excluded. Defaults to ('.DS_Store', '__MACOSX').
|
145
137
|
exist_ok (bool, optional): Whether to overwrite existing contents if they exist. Defaults to False.
|
146
138
|
progress (bool, optional): Whether to display a progress bar. Defaults to True.
|
@@ -151,12 +143,9 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
151
143
|
Returns:
|
152
144
|
(Path): The path to the directory where the zipfile was extracted.
|
153
145
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
dir = unzip_file("path/to/file.zip")
|
159
|
-
```
|
146
|
+
Examples:
|
147
|
+
>>> from ultralytics.utils.downloads import unzip_file
|
148
|
+
>>> directory = unzip_file("path/to/file.zip")
|
160
149
|
"""
|
161
150
|
from zipfile import BadZipFile, ZipFile, is_zipfile
|
162
151
|
|
@@ -245,13 +234,10 @@ def get_google_drive_file_info(link):
|
|
245
234
|
(str): Direct download URL for the Google Drive file.
|
246
235
|
(str): Original filename of the Google Drive file. If filename extraction fails, returns None.
|
247
236
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
link = "https://drive.google.com/file/d/1cqT-cJgANNrhIHCrEufUYhQ4RqiWG_lJ/view?usp=drive_link"
|
253
|
-
url, filename = get_google_drive_file_info(link)
|
254
|
-
```
|
237
|
+
Examples:
|
238
|
+
>>> from ultralytics.utils.downloads import get_google_drive_file_info
|
239
|
+
>>> link = "https://drive.google.com/file/d/1cqT-cJgANNrhIHCrEufUYhQ4RqiWG_lJ/view?usp=drive_link"
|
240
|
+
>>> url, filename = get_google_drive_file_info(link)
|
255
241
|
"""
|
256
242
|
file_id = link.split("/d/")[1].split("/view")[0]
|
257
243
|
drive_url = f"https://drive.google.com/uc?export=download&id={file_id}"
|
@@ -294,7 +280,7 @@ def safe_download(
|
|
294
280
|
url (str): The URL of the file to be downloaded.
|
295
281
|
file (str, optional): The filename of the downloaded file.
|
296
282
|
If not provided, the file will be saved with the same name as the URL.
|
297
|
-
dir (str, optional): The directory to save the downloaded file.
|
283
|
+
dir (str | Path, optional): The directory to save the downloaded file.
|
298
284
|
If not provided, the file will be saved in the current working directory.
|
299
285
|
unzip (bool, optional): Whether to unzip the downloaded file. Default: True.
|
300
286
|
delete (bool, optional): Whether to delete the downloaded file after unzipping. Default: False.
|
@@ -305,13 +291,13 @@ def safe_download(
|
|
305
291
|
exist_ok (bool, optional): Whether to overwrite existing contents during unzipping. Defaults to False.
|
306
292
|
progress (bool, optional): Whether to display a progress bar during the download. Default: True.
|
307
293
|
|
308
|
-
|
309
|
-
|
310
|
-
from ultralytics.utils.downloads import safe_download
|
294
|
+
Returns:
|
295
|
+
(Path | str): The path to the downloaded file or extracted directory.
|
311
296
|
|
312
|
-
|
313
|
-
|
314
|
-
|
297
|
+
Examples:
|
298
|
+
>>> from ultralytics.utils.downloads import safe_download
|
299
|
+
>>> link = "https://ultralytics.com/assets/bus.jpg"
|
300
|
+
>>> path = safe_download(link)
|
315
301
|
"""
|
316
302
|
gdrive = url.startswith("https://drive.google.com/") # check if the URL is a Google Drive link
|
317
303
|
if gdrive:
|
@@ -376,6 +362,7 @@ def safe_download(
|
|
376
362
|
if delete:
|
377
363
|
f.unlink() # remove zip
|
378
364
|
return unzip_dir
|
365
|
+
return f
|
379
366
|
|
380
367
|
|
381
368
|
def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
|
@@ -389,12 +376,11 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
|
|
389
376
|
retry (bool, optional): Flag to retry the request in case of a failure. Defaults to False.
|
390
377
|
|
391
378
|
Returns:
|
392
|
-
(
|
379
|
+
(str): The release tag.
|
380
|
+
(List[str]): A list of asset names.
|
393
381
|
|
394
|
-
|
395
|
-
|
396
|
-
tag, assets = get_github_assets(repo="ultralytics/assets", version="latest")
|
397
|
-
```
|
382
|
+
Examples:
|
383
|
+
>>> tag, assets = get_github_assets(repo="ultralytics/assets", version="latest")
|
398
384
|
"""
|
399
385
|
if version != "latest":
|
400
386
|
version = f"tags/{version}" # i.e. tags/v6.2
|
@@ -411,22 +397,19 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
|
|
411
397
|
|
412
398
|
def attempt_download_asset(file, repo="ultralytics/assets", release="v8.3.0", **kwargs):
|
413
399
|
"""
|
414
|
-
Attempt to download a file from GitHub release assets if it is not found locally.
|
415
|
-
locally first, then tries to download it from the specified GitHub repository release.
|
400
|
+
Attempt to download a file from GitHub release assets if it is not found locally.
|
416
401
|
|
417
402
|
Args:
|
418
403
|
file (str | Path): The filename or file path to be downloaded.
|
419
404
|
repo (str, optional): The GitHub repository in the format 'owner/repo'. Defaults to 'ultralytics/assets'.
|
420
405
|
release (str, optional): The specific release version to be downloaded. Defaults to 'v8.3.0'.
|
421
|
-
**kwargs (
|
406
|
+
**kwargs (Any): Additional keyword arguments for the download process.
|
422
407
|
|
423
408
|
Returns:
|
424
409
|
(str): The path to the downloaded file.
|
425
410
|
|
426
|
-
|
427
|
-
|
428
|
-
file_path = attempt_download_asset("yolo11n.pt", repo="ultralytics/assets", release="latest")
|
429
|
-
```
|
411
|
+
Examples:
|
412
|
+
>>> file_path = attempt_download_asset("yolo11n.pt", repo="ultralytics/assets", release="latest")
|
430
413
|
"""
|
431
414
|
from ultralytics.utils import SETTINGS # scoped for circular import
|
432
415
|
|
@@ -469,7 +452,7 @@ def download(url, dir=Path.cwd(), unzip=True, delete=False, curl=False, threads=
|
|
469
452
|
specified.
|
470
453
|
|
471
454
|
Args:
|
472
|
-
url (str |
|
455
|
+
url (str | List[str]): The URL or list of URLs of the files to be downloaded.
|
473
456
|
dir (Path, optional): The directory where the files will be saved. Defaults to the current working directory.
|
474
457
|
unzip (bool, optional): Flag to unzip the files after downloading. Defaults to True.
|
475
458
|
delete (bool, optional): Flag to delete the zip files after extraction. Defaults to False.
|
@@ -478,10 +461,8 @@ def download(url, dir=Path.cwd(), unzip=True, delete=False, curl=False, threads=
|
|
478
461
|
retry (int, optional): Number of retries in case of download failure. Defaults to 3.
|
479
462
|
exist_ok (bool, optional): Whether to overwrite existing contents during unzipping. Defaults to False.
|
480
463
|
|
481
|
-
|
482
|
-
|
483
|
-
download("https://ultralytics.com/assets/example.zip", dir="path/to/dir", unzip=True)
|
484
|
-
```
|
464
|
+
Examples:
|
465
|
+
>>> download("https://ultralytics.com/assets/example.zip", dir="path/to/dir", unzip=True)
|
485
466
|
"""
|
486
467
|
dir = Path(dir)
|
487
468
|
dir.mkdir(parents=True, exist_ok=True) # make directory
|
ultralytics/utils/files.py
CHANGED
@@ -18,7 +18,7 @@ class WorkingDirectory(contextlib.ContextDecorator):
|
|
18
18
|
It ensures that the original working directory is restored after the context or decorated function completes.
|
19
19
|
|
20
20
|
Attributes:
|
21
|
-
dir (Path): The new directory to switch to.
|
21
|
+
dir (Path | str): The new directory to switch to.
|
22
22
|
cwd (Path): The original current working directory before the switch.
|
23
23
|
|
24
24
|
Methods:
|
@@ -55,21 +55,21 @@ class WorkingDirectory(contextlib.ContextDecorator):
|
|
55
55
|
@contextmanager
|
56
56
|
def spaces_in_path(path):
|
57
57
|
"""
|
58
|
-
Context manager to handle paths with spaces in their names.
|
59
|
-
|
60
|
-
file/directory
|
58
|
+
Context manager to handle paths with spaces in their names.
|
59
|
+
|
60
|
+
If a path contains spaces, it replaces them with underscores, copies the file/directory to the new path, executes
|
61
|
+
the context code block, then copies the file/directory back to its original location.
|
61
62
|
|
62
63
|
Args:
|
63
64
|
path (str | Path): The original path that may contain spaces.
|
64
65
|
|
65
66
|
Yields:
|
66
|
-
(Path): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.
|
67
|
+
(Path | str): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.
|
67
68
|
|
68
69
|
Examples:
|
69
|
-
Use the context manager to handle paths with spaces:
|
70
|
-
>>> from ultralytics.utils.files import spaces_in_path
|
71
70
|
>>> with spaces_in_path('/path/with spaces') as new_path:
|
72
71
|
>>> # Your code here
|
72
|
+
>>> pass
|
73
73
|
"""
|
74
74
|
# If path has spaces, replace them with underscores
|
75
75
|
if " " in str(path):
|
@@ -106,21 +106,20 @@ def spaces_in_path(path):
|
|
106
106
|
|
107
107
|
def increment_path(path, exist_ok=False, sep="", mkdir=False):
|
108
108
|
"""
|
109
|
-
|
109
|
+
Increment a file or directory path, i.e., runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.
|
110
110
|
|
111
111
|
If the path exists and `exist_ok` is not True, the path will be incremented by appending a number and `sep` to
|
112
112
|
the end of the path. If the path is a file, the file extension will be preserved. If the path is a directory, the
|
113
|
-
number will be appended directly to the end of the path.
|
114
|
-
directory if it does not already exist.
|
113
|
+
number will be appended directly to the end of the path.
|
115
114
|
|
116
115
|
Args:
|
117
|
-
path (str |
|
116
|
+
path (str | Path): Path to increment.
|
118
117
|
exist_ok (bool): If True, the path will not be incremented and returned as-is.
|
119
118
|
sep (str): Separator to use between the path and the incrementation number.
|
120
119
|
mkdir (bool): Create a directory if it does not exist.
|
121
120
|
|
122
121
|
Returns:
|
123
|
-
(
|
122
|
+
(Path): Incremented path.
|
124
123
|
|
125
124
|
Examples:
|
126
125
|
Increment a directory path:
|
@@ -185,7 +184,7 @@ def get_latest_run(search_dir="."):
|
|
185
184
|
|
186
185
|
def update_models(model_names=("yolo11n.pt",), source_dir=Path("."), update_names=False):
|
187
186
|
"""
|
188
|
-
|
187
|
+
Update and re-save specified YOLO models in an 'updated_models' subdirectory.
|
189
188
|
|
190
189
|
Args:
|
191
190
|
model_names (Tuple[str, ...]): Model filenames to update.
|
ultralytics/utils/instance.py
CHANGED
@@ -14,7 +14,7 @@ def _ntuple(n):
|
|
14
14
|
"""From PyTorch internals."""
|
15
15
|
|
16
16
|
def parse(x):
|
17
|
-
"""Parse
|
17
|
+
"""Parse input to return n-tuple by repeating singleton values n times."""
|
18
18
|
return x if isinstance(x, abc.Iterable) else tuple(repeat(x, n))
|
19
19
|
|
20
20
|
return parse
|
@@ -39,7 +39,7 @@ class Bboxes:
|
|
39
39
|
Bounding box data should be provided in numpy arrays.
|
40
40
|
|
41
41
|
Attributes:
|
42
|
-
bboxes (
|
42
|
+
bboxes (np.ndarray): The bounding boxes stored in a 2D numpy array with shape (N, 4).
|
43
43
|
format (str): The format of the bounding boxes ('xyxy', 'xywh', or 'ltwh').
|
44
44
|
|
45
45
|
Note:
|
@@ -47,7 +47,13 @@ class Bboxes:
|
|
47
47
|
"""
|
48
48
|
|
49
49
|
def __init__(self, bboxes, format="xyxy") -> None:
|
50
|
-
"""
|
50
|
+
"""
|
51
|
+
Initialize the Bboxes class with bounding box data in a specified format.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
bboxes (np.ndarray): Array of bounding boxes with shape (N, 4) or (4,).
|
55
|
+
format (str): Format of the bounding boxes, one of 'xyxy', 'xywh', or 'ltwh'.
|
56
|
+
"""
|
51
57
|
assert format in _formats, f"Invalid bounding box format: {format}, format must be one of {_formats}"
|
52
58
|
bboxes = bboxes[None, :] if bboxes.ndim == 1 else bboxes
|
53
59
|
assert bboxes.ndim == 2
|
@@ -57,7 +63,12 @@ class Bboxes:
|
|
57
63
|
# self.normalized = normalized
|
58
64
|
|
59
65
|
def convert(self, format):
|
60
|
-
"""
|
66
|
+
"""
|
67
|
+
Convert bounding box format from one type to another.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
format (str): Target format for conversion, one of 'xyxy', 'xywh', or 'ltwh'.
|
71
|
+
"""
|
61
72
|
assert format in _formats, f"Invalid bounding box format: {format}, format must be one of {_formats}"
|
62
73
|
if self.format == format:
|
63
74
|
return
|
@@ -140,10 +151,9 @@ class Bboxes:
|
|
140
151
|
Args:
|
141
152
|
boxes_list (List[Bboxes]): A list of Bboxes objects to concatenate.
|
142
153
|
axis (int, optional): The axis along which to concatenate the bounding boxes.
|
143
|
-
Defaults to 0.
|
144
154
|
|
145
155
|
Returns:
|
146
|
-
Bboxes: A new Bboxes object containing the concatenated bounding boxes.
|
156
|
+
(Bboxes): A new Bboxes object containing the concatenated bounding boxes.
|
147
157
|
|
148
158
|
Note:
|
149
159
|
The input should be a list or tuple of Bboxes objects.
|
@@ -162,11 +172,11 @@ class Bboxes:
|
|
162
172
|
Retrieve a specific bounding box or a set of bounding boxes using indexing.
|
163
173
|
|
164
174
|
Args:
|
165
|
-
index (int
|
166
|
-
|
175
|
+
index (int | slice | np.ndarray): The index, slice, or boolean array to select
|
176
|
+
the desired bounding boxes.
|
167
177
|
|
168
178
|
Returns:
|
169
|
-
Bboxes: A new Bboxes object containing the selected bounding boxes.
|
179
|
+
(Bboxes): A new Bboxes object containing the selected bounding boxes.
|
170
180
|
|
171
181
|
Raises:
|
172
182
|
AssertionError: If the indexed bounding boxes do not form a 2-dimensional matrix.
|
@@ -188,30 +198,29 @@ class Instances:
|
|
188
198
|
|
189
199
|
Attributes:
|
190
200
|
_bboxes (Bboxes): Internal object for handling bounding box operations.
|
191
|
-
keypoints (np.ndarray):
|
201
|
+
keypoints (np.ndarray): Keypoints with shape (N, 17, 3) in format (x, y, visible).
|
192
202
|
normalized (bool): Flag indicating whether the bounding box coordinates are normalized.
|
193
|
-
segments (np.ndarray): Segments array with shape
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
203
|
+
segments (np.ndarray): Segments array with shape (N, M, 2) after resampling.
|
204
|
+
|
205
|
+
Methods:
|
206
|
+
convert_bbox: Convert bounding box format.
|
207
|
+
scale: Scale coordinates by given factors.
|
208
|
+
denormalize: Convert normalized coordinates to absolute coordinates.
|
209
|
+
normalize: Convert absolute coordinates to normalized coordinates.
|
210
|
+
add_padding: Add padding to coordinates.
|
211
|
+
flipud: Flip coordinates vertically.
|
212
|
+
fliplr: Flip coordinates horizontally.
|
213
|
+
clip: Clip coordinates to stay within image boundaries.
|
214
|
+
remove_zero_area_boxes: Remove boxes with zero area.
|
215
|
+
update: Update instance variables.
|
216
|
+
concatenate: Concatenate multiple Instances objects.
|
201
217
|
|
202
218
|
Examples:
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
keypoints=np.array([[[5, 5, 1], [10, 10, 1]], [[15, 15, 1], [20, 20, 1]]]),
|
209
|
-
)
|
210
|
-
```
|
211
|
-
|
212
|
-
Note:
|
213
|
-
The bounding box format is either 'xywh' or 'xyxy', and is determined by the `bbox_format` argument.
|
214
|
-
This class does not perform input validation, and it assumes the inputs are well-formed.
|
219
|
+
>>> instances = Instances(
|
220
|
+
... bboxes=np.array([[10, 10, 30, 30], [20, 20, 40, 40]]),
|
221
|
+
... segments=[np.array([[5, 5], [10, 10]]), np.array([[15, 15], [20, 20]])],
|
222
|
+
... keypoints=np.array([[[5, 5, 1], [10, 10, 1]], [[15, 15, 1], [20, 20, 1]]]),
|
223
|
+
... )
|
215
224
|
"""
|
216
225
|
|
217
226
|
def __init__(self, bboxes, segments=None, keypoints=None, bbox_format="xywh", normalized=True) -> None:
|
@@ -219,11 +228,11 @@ class Instances:
|
|
219
228
|
Initialize the object with bounding boxes, segments, and keypoints.
|
220
229
|
|
221
230
|
Args:
|
222
|
-
bboxes (np.ndarray): Bounding boxes, shape
|
223
|
-
segments (
|
224
|
-
keypoints (np.ndarray, optional): Keypoints, shape
|
225
|
-
bbox_format (str, optional): Format of bboxes.
|
226
|
-
normalized (bool, optional): Whether the coordinates are normalized.
|
231
|
+
bboxes (np.ndarray): Bounding boxes, shape (N, 4).
|
232
|
+
segments (List | np.ndarray, optional): Segmentation masks.
|
233
|
+
keypoints (np.ndarray, optional): Keypoints, shape (N, 17, 3) in format (x, y, visible).
|
234
|
+
bbox_format (str, optional): Format of bboxes.
|
235
|
+
normalized (bool, optional): Whether the coordinates are normalized.
|
227
236
|
"""
|
228
237
|
self._bboxes = Bboxes(bboxes=bboxes, format=bbox_format)
|
229
238
|
self.keypoints = keypoints
|
@@ -231,7 +240,12 @@ class Instances:
|
|
231
240
|
self.segments = segments
|
232
241
|
|
233
242
|
def convert_bbox(self, format):
|
234
|
-
"""
|
243
|
+
"""
|
244
|
+
Convert bounding box format.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
format (str): Target format for conversion, one of 'xyxy', 'xywh', or 'ltwh'.
|
248
|
+
"""
|
235
249
|
self._bboxes.convert(format=format)
|
236
250
|
|
237
251
|
@property
|
@@ -240,7 +254,14 @@ class Instances:
|
|
240
254
|
return self._bboxes.areas()
|
241
255
|
|
242
256
|
def scale(self, scale_w, scale_h, bbox_only=False):
|
243
|
-
"""
|
257
|
+
"""
|
258
|
+
Scale coordinates by given factors.
|
259
|
+
|
260
|
+
Args:
|
261
|
+
scale_w (float): Scale factor for width.
|
262
|
+
scale_h (float): Scale factor for height.
|
263
|
+
bbox_only (bool, optional): Whether to scale only bounding boxes.
|
264
|
+
"""
|
244
265
|
self._bboxes.mul(scale=(scale_w, scale_h, scale_w, scale_h))
|
245
266
|
if bbox_only:
|
246
267
|
return
|
@@ -251,7 +272,13 @@ class Instances:
|
|
251
272
|
self.keypoints[..., 1] *= scale_h
|
252
273
|
|
253
274
|
def denormalize(self, w, h):
|
254
|
-
"""
|
275
|
+
"""
|
276
|
+
Convert normalized coordinates to absolute coordinates.
|
277
|
+
|
278
|
+
Args:
|
279
|
+
w (int): Image width.
|
280
|
+
h (int): Image height.
|
281
|
+
"""
|
255
282
|
if not self.normalized:
|
256
283
|
return
|
257
284
|
self._bboxes.mul(scale=(w, h, w, h))
|
@@ -263,7 +290,13 @@ class Instances:
|
|
263
290
|
self.normalized = False
|
264
291
|
|
265
292
|
def normalize(self, w, h):
|
266
|
-
"""
|
293
|
+
"""
|
294
|
+
Convert absolute coordinates to normalized coordinates.
|
295
|
+
|
296
|
+
Args:
|
297
|
+
w (int): Image width.
|
298
|
+
h (int): Image height.
|
299
|
+
"""
|
267
300
|
if self.normalized:
|
268
301
|
return
|
269
302
|
self._bboxes.mul(scale=(1 / w, 1 / h, 1 / w, 1 / h))
|
@@ -275,7 +308,13 @@ class Instances:
|
|
275
308
|
self.normalized = True
|
276
309
|
|
277
310
|
def add_padding(self, padw, padh):
|
278
|
-
"""
|
311
|
+
"""
|
312
|
+
Add padding to coordinates.
|
313
|
+
|
314
|
+
Args:
|
315
|
+
padw (int): Padding width.
|
316
|
+
padh (int): Padding height.
|
317
|
+
"""
|
279
318
|
assert not self.normalized, "you should add padding with absolute coordinates."
|
280
319
|
self._bboxes.add(offset=(padw, padh, padw, padh))
|
281
320
|
self.segments[..., 0] += padw
|
@@ -289,12 +328,10 @@ class Instances:
|
|
289
328
|
Retrieve a specific instance or a set of instances using indexing.
|
290
329
|
|
291
330
|
Args:
|
292
|
-
index (int
|
293
|
-
the desired instances.
|
331
|
+
index (int | slice | np.ndarray): The index, slice, or boolean array to select the desired instances.
|
294
332
|
|
295
333
|
Returns:
|
296
|
-
Instances: A new Instances object containing the selected
|
297
|
-
segments, and keypoints if present.
|
334
|
+
(Instances): A new Instances object containing the selected boxes, segments, and keypoints if present.
|
298
335
|
|
299
336
|
Note:
|
300
337
|
When using boolean indexing, make sure to provide a boolean array with the same
|
@@ -313,7 +350,12 @@ class Instances:
|
|
313
350
|
)
|
314
351
|
|
315
352
|
def flipud(self, h):
|
316
|
-
"""
|
353
|
+
"""
|
354
|
+
Flip coordinates vertically.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
h (int): Image height.
|
358
|
+
"""
|
317
359
|
if self._bboxes.format == "xyxy":
|
318
360
|
y1 = self.bboxes[:, 1].copy()
|
319
361
|
y2 = self.bboxes[:, 3].copy()
|
@@ -326,7 +368,12 @@ class Instances:
|
|
326
368
|
self.keypoints[..., 1] = h - self.keypoints[..., 1]
|
327
369
|
|
328
370
|
def fliplr(self, w):
|
329
|
-
"""
|
371
|
+
"""
|
372
|
+
Flip coordinates horizontally.
|
373
|
+
|
374
|
+
Args:
|
375
|
+
w (int): Image width.
|
376
|
+
"""
|
330
377
|
if self._bboxes.format == "xyxy":
|
331
378
|
x1 = self.bboxes[:, 0].copy()
|
332
379
|
x2 = self.bboxes[:, 2].copy()
|
@@ -339,7 +386,13 @@ class Instances:
|
|
339
386
|
self.keypoints[..., 0] = w - self.keypoints[..., 0]
|
340
387
|
|
341
388
|
def clip(self, w, h):
|
342
|
-
"""
|
389
|
+
"""
|
390
|
+
Clip coordinates to stay within image boundaries.
|
391
|
+
|
392
|
+
Args:
|
393
|
+
w (int): Image width.
|
394
|
+
h (int): Image height.
|
395
|
+
"""
|
343
396
|
ori_format = self._bboxes.format
|
344
397
|
self.convert_bbox(format="xyxy")
|
345
398
|
self.bboxes[:, [0, 2]] = self.bboxes[:, [0, 2]].clip(0, w)
|
@@ -353,7 +406,12 @@ class Instances:
|
|
353
406
|
self.keypoints[..., 1] = self.keypoints[..., 1].clip(0, h)
|
354
407
|
|
355
408
|
def remove_zero_area_boxes(self):
|
356
|
-
"""
|
409
|
+
"""
|
410
|
+
Remove zero-area boxes, i.e. after clipping some boxes may have zero width or height.
|
411
|
+
|
412
|
+
Returns:
|
413
|
+
(np.ndarray): Boolean array indicating which boxes were kept.
|
414
|
+
"""
|
357
415
|
good = self.bbox_areas > 0
|
358
416
|
if not all(good):
|
359
417
|
self._bboxes = self._bboxes[good]
|
@@ -364,7 +422,14 @@ class Instances:
|
|
364
422
|
return good
|
365
423
|
|
366
424
|
def update(self, bboxes, segments=None, keypoints=None):
|
367
|
-
"""
|
425
|
+
"""
|
426
|
+
Update instance variables.
|
427
|
+
|
428
|
+
Args:
|
429
|
+
bboxes (np.ndarray): New bounding boxes.
|
430
|
+
segments (np.ndarray, optional): New segments.
|
431
|
+
keypoints (np.ndarray, optional): New keypoints.
|
432
|
+
"""
|
368
433
|
self._bboxes = Bboxes(bboxes, format=self._bboxes.format)
|
369
434
|
if segments is not None:
|
370
435
|
self.segments = segments
|
@@ -378,14 +443,14 @@ class Instances:
|
|
378
443
|
@classmethod
|
379
444
|
def concatenate(cls, instances_list: List["Instances"], axis=0) -> "Instances":
|
380
445
|
"""
|
381
|
-
|
446
|
+
Concatenate a list of Instances objects into a single Instances object.
|
382
447
|
|
383
448
|
Args:
|
384
449
|
instances_list (List[Instances]): A list of Instances objects to concatenate.
|
385
|
-
axis (int, optional): The axis along which the arrays will be concatenated.
|
450
|
+
axis (int, optional): The axis along which the arrays will be concatenated.
|
386
451
|
|
387
452
|
Returns:
|
388
|
-
Instances: A new Instances object containing the concatenated bounding boxes,
|
453
|
+
(Instances): A new Instances object containing the concatenated bounding boxes,
|
389
454
|
segments, and keypoints if present.
|
390
455
|
|
391
456
|
Note:
|