ultralytics 8.3.101__py3-none-any.whl → 8.3.103__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. tests/test_exports.py +14 -5
  2. tests/test_solutions.py +140 -76
  3. ultralytics/__init__.py +1 -1
  4. ultralytics/cfg/__init__.py +1 -1
  5. ultralytics/engine/exporter.py +23 -8
  6. ultralytics/engine/tuner.py +8 -2
  7. ultralytics/hub/__init__.py +29 -2
  8. ultralytics/hub/google/__init__.py +18 -1
  9. ultralytics/models/fastsam/predict.py +12 -1
  10. ultralytics/models/nas/predict.py +21 -3
  11. ultralytics/models/rtdetr/val.py +26 -2
  12. ultralytics/models/sam/amg.py +22 -1
  13. ultralytics/models/sam/modules/encoders.py +85 -4
  14. ultralytics/models/sam/modules/memory_attention.py +61 -3
  15. ultralytics/models/sam/modules/utils.py +108 -5
  16. ultralytics/models/utils/loss.py +38 -2
  17. ultralytics/models/utils/ops.py +15 -1
  18. ultralytics/models/yolo/classify/predict.py +11 -1
  19. ultralytics/models/yolo/classify/train.py +17 -1
  20. ultralytics/models/yolo/classify/val.py +82 -6
  21. ultralytics/models/yolo/detect/predict.py +20 -1
  22. ultralytics/models/yolo/model.py +55 -4
  23. ultralytics/models/yolo/obb/predict.py +16 -1
  24. ultralytics/models/yolo/obb/train.py +35 -2
  25. ultralytics/models/yolo/obb/val.py +87 -6
  26. ultralytics/models/yolo/pose/predict.py +18 -1
  27. ultralytics/models/yolo/pose/train.py +48 -3
  28. ultralytics/models/yolo/pose/val.py +113 -8
  29. ultralytics/models/yolo/segment/predict.py +27 -2
  30. ultralytics/models/yolo/segment/train.py +61 -3
  31. ultralytics/models/yolo/segment/val.py +10 -1
  32. ultralytics/models/yolo/world/train_world.py +29 -1
  33. ultralytics/models/yolo/yoloe/train.py +47 -3
  34. ultralytics/nn/autobackend.py +9 -8
  35. ultralytics/nn/modules/activation.py +26 -3
  36. ultralytics/nn/modules/block.py +89 -0
  37. ultralytics/nn/modules/head.py +3 -92
  38. ultralytics/nn/modules/utils.py +70 -4
  39. ultralytics/nn/tasks.py +3 -0
  40. ultralytics/nn/text_model.py +93 -17
  41. ultralytics/solutions/instance_segmentation.py +15 -7
  42. ultralytics/solutions/solutions.py +2 -47
  43. ultralytics/utils/benchmarks.py +1 -1
  44. ultralytics/utils/callbacks/base.py +22 -5
  45. ultralytics/utils/callbacks/comet.py +93 -5
  46. ultralytics/utils/callbacks/dvc.py +64 -5
  47. ultralytics/utils/callbacks/neptune.py +25 -2
  48. ultralytics/utils/callbacks/tensorboard.py +30 -2
  49. ultralytics/utils/callbacks/wb.py +16 -1
  50. ultralytics/utils/dist.py +35 -2
  51. ultralytics/utils/errors.py +27 -6
  52. ultralytics/utils/metrics.py +1 -1
  53. ultralytics/utils/patches.py +33 -5
  54. ultralytics/utils/torch_utils.py +14 -6
  55. ultralytics/utils/triton.py +16 -3
  56. ultralytics/utils/tuner.py +17 -9
  57. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/METADATA +3 -4
  58. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/RECORD +62 -62
  59. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/WHEEL +0 -0
  60. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/entry_points.txt +0 -0
  61. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/licenses/LICENSE +0 -0
  62. {ultralytics-8.3.101.dist-info → ultralytics-8.3.103.dist-info}/top_level.txt +0 -0
tests/test_exports.py CHANGED
@@ -1,7 +1,9 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ import io
3
4
  import shutil
4
5
  import uuid
6
+ from contextlib import redirect_stderr, redirect_stdout
5
7
  from itertools import product
6
8
  from pathlib import Path
7
9
 
@@ -167,12 +169,19 @@ def test_export_tflite_matrix(task, dynamic, int8, half, batch, nms):
167
169
  @pytest.mark.skipif(LINUX and ARM64, reason="CoreML not supported on aarch64 Linux")
168
170
  @pytest.mark.skipif(checks.IS_PYTHON_3_13, reason="CoreML not supported in Python 3.13")
169
171
  def test_export_coreml():
170
- """Test YOLO exports to CoreML format, optimized for macOS only."""
171
- if MACOS:
172
- file = YOLO(MODEL).export(format="coreml", imgsz=32)
173
- YOLO(file)(SOURCE, imgsz=32) # model prediction only supported on macOS for nms=False models
174
- else:
172
+ """Test YOLO exports to CoreML format and check for errors."""
173
+ # Capture stdout and stderr
174
+ stdout, stderr = io.StringIO(), io.StringIO()
175
+ with redirect_stdout(stdout), redirect_stderr(stderr):
175
176
  YOLO(MODEL).export(format="coreml", nms=True, imgsz=32)
177
+ if MACOS:
178
+ file = YOLO(MODEL).export(format="coreml", imgsz=32)
179
+ YOLO(file)(SOURCE, imgsz=32) # model prediction only supported on macOS for nms=False models
180
+
181
+ # Check captured output for errors
182
+ output = stdout.getvalue() + stderr.getvalue()
183
+ assert "Error" not in output, f"CoreML export produced errors: {output}"
184
+ assert "You will not be able to run predict()" not in output, "CoreML export has predict() error"
176
185
 
177
186
 
178
187
  @pytest.mark.skipif(not checks.IS_PYTHON_MINIMUM_3_10, reason="TFLite export requires Python>=3.10")
tests/test_solutions.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
- # This is file for Ultralytics Solutions tests: https://docs.ultralytics.com/solutions/,
4
- # It includes every solution excluding DistanceCalculation and Security Alarm System.
3
+ # Tests Ultralytics Solutions: https://docs.ultralytics.com/solutions/,
4
+ # including every solution excluding DistanceCalculation and Security Alarm System.
5
5
 
6
6
  import cv2
7
7
  import pytest
@@ -11,93 +11,157 @@ from ultralytics import solutions
11
11
  from ultralytics.utils import ASSETS_URL
12
12
  from ultralytics.utils.downloads import safe_download
13
13
 
14
- DEMO_VIDEO = "solutions_ci_demo.mp4" # for all the solutions, except workout and parking
14
+ # Pre-defined arguments values
15
+ SHOW = False
16
+ DEMO_VIDEO = "solutions_ci_demo.mp4" # for all the solutions, except workout, object cropping and parking management
17
+ CROP_VIDEO = "decelera_landscape_min.mov" # for object cropping solution
15
18
  POSE_VIDEO = "solution_ci_pose_demo.mp4" # only for workouts monitoring solution
16
19
  PARKING_VIDEO = "solution_ci_parking_demo.mp4" # only for parking management solution
17
20
  PARKING_AREAS_JSON = "solution_ci_parking_areas.json" # only for parking management solution
18
21
  PARKING_MODEL = "solutions_ci_parking_model.pt" # only for parking management solution
22
+ MODEL_FILE = "yolo11n.pt" # model file used for solutions, except parking management and instance segmentation
23
+ REGION = [(20, 400), (1080, 400), (1080, 360), (20, 360)] # for object counting, speed estimation and queue management
19
24
 
25
+ # Test configs for each solution : (name, class, needs_frame_count, video, kwargs)
26
+ SOLUTIONS = [
27
+ (
28
+ "ObjectCounter",
29
+ solutions.ObjectCounter,
30
+ False,
31
+ DEMO_VIDEO,
32
+ {"region": REGION, "model": MODEL_FILE, "show": SHOW},
33
+ ),
34
+ (
35
+ "Heatmap",
36
+ solutions.Heatmap,
37
+ False,
38
+ DEMO_VIDEO,
39
+ {"colormap": cv2.COLORMAP_PARULA, "model": MODEL_FILE, "show": SHOW, "region": None},
40
+ ),
41
+ (
42
+ "HeatmapWithRegion",
43
+ solutions.Heatmap,
44
+ False,
45
+ DEMO_VIDEO,
46
+ {"colormap": cv2.COLORMAP_PARULA, "region": REGION, "model": MODEL_FILE, "show": SHOW},
47
+ ),
48
+ (
49
+ "SpeedEstimator",
50
+ solutions.SpeedEstimator,
51
+ False,
52
+ DEMO_VIDEO,
53
+ {"region": REGION, "model": MODEL_FILE, "show": SHOW},
54
+ ),
55
+ (
56
+ "QueueManager",
57
+ solutions.QueueManager,
58
+ False,
59
+ DEMO_VIDEO,
60
+ {"region": REGION, "model": MODEL_FILE, "show": SHOW},
61
+ ),
62
+ (
63
+ "LineAnalytics",
64
+ solutions.Analytics,
65
+ True,
66
+ DEMO_VIDEO,
67
+ {"analytics_type": "line", "model": MODEL_FILE, "show": SHOW},
68
+ ),
69
+ (
70
+ "PieAnalytics",
71
+ solutions.Analytics,
72
+ True,
73
+ DEMO_VIDEO,
74
+ {"analytics_type": "pie", "model": MODEL_FILE, "show": SHOW},
75
+ ),
76
+ (
77
+ "BarAnalytics",
78
+ solutions.Analytics,
79
+ True,
80
+ DEMO_VIDEO,
81
+ {"analytics_type": "bar", "model": MODEL_FILE, "show": SHOW},
82
+ ),
83
+ (
84
+ "AreaAnalytics",
85
+ solutions.Analytics,
86
+ True,
87
+ DEMO_VIDEO,
88
+ {"analytics_type": "area", "model": MODEL_FILE, "show": SHOW},
89
+ ),
90
+ ("TrackZone", solutions.TrackZone, False, DEMO_VIDEO, {"region": REGION, "model": MODEL_FILE, "show": SHOW}),
91
+ (
92
+ "ObjectCropper",
93
+ solutions.ObjectCropper,
94
+ False,
95
+ CROP_VIDEO,
96
+ {"crop_dir": str(TMP / "cropped-detections"), "model": MODEL_FILE, "show": SHOW},
97
+ ),
98
+ (
99
+ "ObjectBlurrer",
100
+ solutions.ObjectBlurrer,
101
+ False,
102
+ DEMO_VIDEO,
103
+ {"blur_ratio": 0.5, "model": MODEL_FILE, "show": SHOW},
104
+ ),
105
+ (
106
+ "InstanceSegmentation",
107
+ solutions.InstanceSegmentation,
108
+ False,
109
+ DEMO_VIDEO,
110
+ {"model": "yolo11n-seg.pt", "show": SHOW},
111
+ ),
112
+ ("VisionEye", solutions.VisionEye, False, DEMO_VIDEO, {"model": MODEL_FILE, "show": SHOW}),
113
+ (
114
+ "RegionCounter",
115
+ solutions.RegionCounter,
116
+ False,
117
+ DEMO_VIDEO,
118
+ {"region": REGION, "model": MODEL_FILE, "show": SHOW},
119
+ ),
120
+ ("AIGym", solutions.AIGym, False, POSE_VIDEO, {"kpts": [6, 8, 10], "show": SHOW}),
121
+ (
122
+ "ParkingManager",
123
+ solutions.ParkingManagement,
124
+ False,
125
+ PARKING_VIDEO,
126
+ {"model": str(TMP / PARKING_MODEL), "show": SHOW, "json_file": str(TMP / PARKING_AREAS_JSON)},
127
+ ),
128
+ (
129
+ "StreamlitInference",
130
+ solutions.Inference,
131
+ False,
132
+ None, # streamlit application don't require video file
133
+ {}, # streamlit application don't accept arguments
134
+ ),
135
+ ]
20
136
 
21
- @pytest.mark.slow
22
- def test_major_solutions():
23
- """Test the object counting, heatmap, speed estimation, trackzone and queue management solution."""
24
- safe_download(url=f"{ASSETS_URL}/{DEMO_VIDEO}", dir=TMP)
25
- cap = cv2.VideoCapture(str(TMP / DEMO_VIDEO))
26
- assert cap.isOpened(), "Error reading video file"
27
- region_points = [(20, 400), (1080, 400), (1080, 360), (20, 360)]
28
- counter = solutions.ObjectCounter(region=region_points, model="yolo11n.pt", show=False) # Test object counter
29
- heatmap = solutions.Heatmap(colormap=cv2.COLORMAP_PARULA, model="yolo11n.pt", show=False) # Test heatmaps
30
- heatmapcounter = solutions.Heatmap(
31
- colormap=cv2.COLORMAP_PARULA, model="yolo11n.pt", show=False, region=region_points
32
- ) # Test heatmaps with object counting
33
- speed = solutions.SpeedEstimator(region=region_points, model="yolo11n.pt", show=False) # Test speed estimation
34
- queue = solutions.QueueManager(region=region_points, model="yolo11n.pt", show=False) # Test queue management
35
- lineanalytics = solutions.Analytics(analytics_type="line", model="yolo11n.pt", show=False) # Line analytics
36
- pieanalytics = solutions.Analytics(analytics_type="pie", model="yolo11n.pt", show=False) # Pie analytics
37
- baranalytics = solutions.Analytics(analytics_type="bar", model="yolo11n.pt", show=False) # Bar analytics
38
- areaanalytics = solutions.Analytics(analytics_type="area", model="yolo11n.pt", show=False) # Area analytics
39
- trackzone = solutions.TrackZone(region=region_points, model="yolo11n.pt", show=False) # Track zone monitoring
40
- objectcropper = solutions.ObjectCropper(
41
- model="yolo11n.pt", show=False, crop_dir=str(TMP / "cropped-detections")
42
- ) # Object cropping
43
- objectblurrer = solutions.ObjectBlurrer(blur_ratio=0.5, model="yolo11n.pt", show=False) # Object blurring
44
- isegment = solutions.InstanceSegmentation(model="yolo11n-seg.pt", show=False) # Instance segmentation
45
- visioneye = solutions.VisionEye(model="yolo11n.pt", show=False) # Vision Eye solution
46
- regioncounter = solutions.RegionCounter(region=region_points, model="yolo11n.pt", show=False) # Region counter
47
- frame_count = 0 # Required for analytics
48
- while cap.isOpened():
49
- success, im0 = cap.read()
50
- if not success:
51
- break
52
- frame_count += 1
53
- original_im0 = im0.copy()
54
- _ = counter(original_im0.copy())
55
- _ = heatmap(original_im0.copy())
56
- _ = heatmapcounter(original_im0.copy())
57
- _ = speed(original_im0.copy())
58
- _ = queue(original_im0.copy())
59
- _ = lineanalytics(original_im0.copy(), frame_count)
60
- _ = pieanalytics(original_im0.copy(), frame_count)
61
- _ = baranalytics(original_im0.copy(), frame_count)
62
- _ = areaanalytics(original_im0.copy(), frame_count)
63
- _ = trackzone(original_im0.copy())
64
- _ = objectcropper(original_im0.copy())
65
- _ = isegment(original_im0.copy())
66
- _ = objectblurrer(original_im0.copy())
67
- _ = visioneye(original_im0.copy())
68
- _ = regioncounter(original_im0.copy())
69
- cap.release()
70
137
 
71
- # Test workouts monitoring
72
- safe_download(url=f"{ASSETS_URL}/{POSE_VIDEO}", dir=TMP)
73
- cap = cv2.VideoCapture(str(TMP / POSE_VIDEO))
74
- assert cap.isOpened(), "Error reading video file"
75
- gym = solutions.AIGym(kpts=[5, 11, 13], show=False)
76
- while cap.isOpened():
77
- success, im0 = cap.read()
78
- if not success:
79
- break
80
- _ = gym(im0)
81
- cap.release()
138
+ def process_video(solution, video_path, needs_frame_count=False):
139
+ """Process video with solution, feeding frames and optional frame count."""
140
+ cap = cv2.VideoCapture(video_path)
141
+ assert cap.isOpened(), f"Error reading video file {video_path}"
82
142
 
83
- # Test parking management
84
- safe_download(url=f"{ASSETS_URL}/{PARKING_VIDEO}", dir=TMP)
85
- safe_download(url=f"{ASSETS_URL}/{PARKING_AREAS_JSON}", dir=TMP)
86
- safe_download(url=f"{ASSETS_URL}/{PARKING_MODEL}", dir=TMP)
87
- cap = cv2.VideoCapture(str(TMP / PARKING_VIDEO))
88
- assert cap.isOpened(), "Error reading video file"
89
- parkingmanager = solutions.ParkingManagement(
90
- json_file=str(TMP / PARKING_AREAS_JSON), model=str(TMP / PARKING_MODEL), show=False
91
- )
143
+ frame_count = 0
92
144
  while cap.isOpened():
93
145
  success, im0 = cap.read()
94
146
  if not success:
95
147
  break
96
- _ = parkingmanager(im0)
148
+ frame_count += 1
149
+ im_copy = im0.copy()
150
+ args = [im_copy, frame_count] if needs_frame_count else [im_copy]
151
+ _ = solution(*args)
152
+
97
153
  cap.release()
98
154
 
99
155
 
100
156
  @pytest.mark.slow
101
- def test_streamlit_predict():
102
- """Test streamlit predict live inference solution."""
103
- solutions.Inference().inference()
157
+ @pytest.mark.parametrize("name, solution_class, needs_frame_count, video, kwargs", SOLUTIONS)
158
+ def test_solution(name, solution_class, needs_frame_count, video, kwargs):
159
+ """Test individual Ultralytics solution."""
160
+ safe_download(url=f"{ASSETS_URL}/{video}", dir=TMP)
161
+ if name == "ParkingManager":
162
+ safe_download(url=f"{ASSETS_URL}/{PARKING_AREAS_JSON}", dir=TMP)
163
+ safe_download(url=f"{ASSETS_URL}/{PARKING_MODEL}", dir=TMP)
164
+ solution = solution_class(**kwargs)
165
+ solution.inference() if name == "StreamlitInference" else process_video(
166
+ solution, str(TMP / video), needs_frame_count
167
+ )
ultralytics/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
- __version__ = "8.3.101"
3
+ __version__ = "8.3.103"
4
4
 
5
5
  import os
6
6
 
@@ -700,7 +700,7 @@ def handle_yolo_solutions(args: List[str]) -> None:
700
700
  solution_name = "count" # Default for invalid solution
701
701
 
702
702
  if solution_name == "inference":
703
- checks.check_requirements("streamlit>=1.29.0")
703
+ checks.check_requirements("streamlit>=1.29.0,<1.44.0")
704
704
  LOGGER.info("💡 Loading Ultralytics live inference app...")
705
705
  subprocess.run(
706
706
  [ # Run subprocess with Streamlit custom argument
@@ -115,18 +115,32 @@ def export_formats():
115
115
  ["PyTorch", "-", ".pt", True, True, []],
116
116
  ["TorchScript", "torchscript", ".torchscript", True, True, ["batch", "optimize", "nms"]],
117
117
  ["ONNX", "onnx", ".onnx", True, True, ["batch", "dynamic", "half", "opset", "simplify", "nms"]],
118
- ["OpenVINO", "openvino", "_openvino_model", True, False, ["batch", "dynamic", "half", "int8", "nms"]],
119
- ["TensorRT", "engine", ".engine", False, True, ["batch", "dynamic", "half", "int8", "simplify", "nms"]],
118
+ [
119
+ "OpenVINO",
120
+ "openvino",
121
+ "_openvino_model",
122
+ True,
123
+ False,
124
+ ["batch", "dynamic", "half", "int8", "nms", "fraction"],
125
+ ],
126
+ [
127
+ "TensorRT",
128
+ "engine",
129
+ ".engine",
130
+ False,
131
+ True,
132
+ ["batch", "dynamic", "half", "int8", "simplify", "nms", "fraction"],
133
+ ],
120
134
  ["CoreML", "coreml", ".mlpackage", True, False, ["batch", "half", "int8", "nms"]],
121
135
  ["TensorFlow SavedModel", "saved_model", "_saved_model", True, True, ["batch", "int8", "keras", "nms"]],
122
136
  ["TensorFlow GraphDef", "pb", ".pb", True, True, ["batch"]],
123
- ["TensorFlow Lite", "tflite", ".tflite", True, False, ["batch", "half", "int8", "nms"]],
137
+ ["TensorFlow Lite", "tflite", ".tflite", True, False, ["batch", "half", "int8", "nms", "fraction"]],
124
138
  ["TensorFlow Edge TPU", "edgetpu", "_edgetpu.tflite", True, False, []],
125
139
  ["TensorFlow.js", "tfjs", "_web_model", True, False, ["batch", "half", "int8", "nms"]],
126
140
  ["PaddlePaddle", "paddle", "_paddle_model", True, True, ["batch"]],
127
141
  ["MNN", "mnn", ".mnn", True, True, ["batch", "half", "int8"]],
128
142
  ["NCNN", "ncnn", "_ncnn_model", True, True, ["batch", "half"]],
129
- ["IMX", "imx", "_imx_model", True, True, ["int8"]],
143
+ ["IMX", "imx", "_imx_model", True, True, ["int8", "fraction"]],
130
144
  ["RKNN", "rknn", "_rknn_model", False, False, ["batch", "name"]],
131
145
  ]
132
146
  return dict(zip(["Format", "Argument", "Suffix", "CPU", "GPU", "Arguments"], zip(*x)))
@@ -144,7 +158,7 @@ def validate_args(format, passed_args, valid_args):
144
158
  Raises:
145
159
  AssertionError: If an unsupported argument is used, or if the format lacks supported argument listings.
146
160
  """
147
- export_args = ["half", "int8", "dynamic", "keras", "nms", "batch"]
161
+ export_args = ["half", "int8", "dynamic", "keras", "nms", "batch", "fraction"]
148
162
 
149
163
  assert valid_args is not None, f"ERROR ❌️ valid arguments for '{format}' not listed."
150
164
  custom = {"batch": 1, "data": None, "device": None} # exporter defaults
@@ -493,6 +507,7 @@ class Exporter:
493
507
  dataset = YOLODataset(
494
508
  data[self.args.split or "val"],
495
509
  data=data,
510
+ fraction=self.args.fraction,
496
511
  task=self.model.task,
497
512
  imgsz=self.imgsz[0],
498
513
  augment=False,
@@ -803,7 +818,7 @@ class Exporter:
803
818
  ts,
804
819
  inputs=[ct.ImageType("image", shape=self.im.shape, scale=scale, bias=bias)], # expects ct.TensorType
805
820
  classifier_config=classifier_config,
806
- minimum_deployment_target=ct.target.iOS16,
821
+ minimum_deployment_target=ct.target.iOS15, # warning: >=16 causes pipeline errors
807
822
  convert_to="neuralnetwork" if mlmodel else "mlprogram",
808
823
  )
809
824
  bits, mode = (8, "kmeans") if self.args.int8 else (16, "linear") if self.args.half else (32, None)
@@ -1449,7 +1464,7 @@ class Exporter:
1449
1464
 
1450
1465
  # 3. Create NMS protobuf
1451
1466
  nms_spec = ct.proto.Model_pb2.Model()
1452
- nms_spec.specificationVersion = 5
1467
+ nms_spec.specificationVersion = 9
1453
1468
  for i in range(2):
1454
1469
  decoder_output = model._spec.description.output[i].SerializeToString()
1455
1470
  nms_spec.description.input.add()
@@ -1502,7 +1517,7 @@ class Exporter:
1502
1517
  pipeline.spec.description.output[1].ParseFromString(nms_model._spec.description.output[1].SerializeToString())
1503
1518
 
1504
1519
  # Update metadata
1505
- pipeline.spec.specificationVersion = 5
1520
+ pipeline.spec.specificationVersion = 9
1506
1521
  pipeline.spec.description.metadata.userDefined.update(
1507
1522
  {"IoU threshold": str(nms.iouThreshold), "Confidence threshold": str(nms.confidenceThreshold)}
1508
1523
  )
@@ -93,8 +93,9 @@ class Tuner:
93
93
  "copy_paste": (0.0, 1.0), # segment copy-paste (probability)
94
94
  }
95
95
  self.args = get_cfg(overrides=args)
96
+ self.args.exist_ok = self.args.resume # resume w/ same tune_dir
96
97
  self.tune_dir = get_save_dir(self.args, name=self.args.name or "tune")
97
- self.args.name = None # reset to not affect training directory
98
+ self.args.name, self.args.exist_ok, self.args.resume = (None, False, False) # reset to not affect training
98
99
  self.tune_csv = self.tune_dir / "tune_results.csv"
99
100
  self.callbacks = _callbacks or callbacks.get_default_callbacks()
100
101
  self.prefix = colorstr("Tuner: ")
@@ -173,7 +174,12 @@ class Tuner:
173
174
  t0 = time.time()
174
175
  best_save_dir, best_metrics = None, None
175
176
  (self.tune_dir / "weights").mkdir(parents=True, exist_ok=True)
176
- for i in range(iterations):
177
+ start = 0
178
+ if self.tune_csv.exists():
179
+ x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
180
+ start = x.shape[0]
181
+ LOGGER.info(f"{self.prefix}Resuming tuning run {self.tune_dir} from iteration {start + 1}...")
182
+ for i in range(start, iterations):
177
183
  # Mutate hyperparameters
178
184
  mutated_hyp = self._mutate()
179
185
  LOGGER.info(f"{self.prefix}Starting iteration {i + 1}/{iterations} with hyperparameters: {mutated_hyp}")
@@ -96,7 +96,21 @@ def export_fmts_hub():
96
96
 
97
97
 
98
98
  def export_model(model_id: str = "", format: str = "torchscript"):
99
- """Export a model to the specified format."""
99
+ """
100
+ Export a model to a specified format for deployment via the Ultralytics HUB API.
101
+
102
+ Args:
103
+ model_id (str): The ID of the model to export. An empty string will use the default model.
104
+ format (str): The format to export the model to. Must be one of the supported formats returned by
105
+ export_fmts_hub().
106
+
107
+ Raises:
108
+ AssertionError: If the specified format is not supported or if the export request fails.
109
+
110
+ Examples:
111
+ >>> from ultralytics import hub
112
+ >>> hub.export_model(model_id="your_model_id", format="torchscript")
113
+ """
100
114
  assert format in export_fmts_hub(), f"Unsupported export format '{format}', valid formats are {export_fmts_hub()}"
101
115
  r = requests.post(
102
116
  f"{HUB_API_ROOT}/v1/models/{model_id}/export", json={"format": format}, headers={"x-api-key": Auth().api_key}
@@ -106,7 +120,20 @@ def export_model(model_id: str = "", format: str = "torchscript"):
106
120
 
107
121
 
108
122
  def get_export(model_id: str = "", format: str = "torchscript"):
109
- """Get an exported model dictionary with download URL."""
123
+ """
124
+ Retrieve an exported model in the specified format from Ultralytics HUB using the model ID.
125
+
126
+ Args:
127
+ model_id (str): The ID of the model to retrieve from Ultralytics HUB.
128
+ format (str): The export format to retrieve. Must be one of the supported formats returned by export_fmts_hub().
129
+
130
+ Raises:
131
+ AssertionError: If the specified format is not supported or if the API request fails.
132
+
133
+ Examples:
134
+ >>> from ultralytics import hub
135
+ >>> hub.get_export(model_id="your_model_id", format="torchscript")
136
+ """
110
137
  assert format in export_fmts_hub(), f"Unsupported export format '{format}', valid formats are {export_fmts_hub()}"
111
138
  r = requests.post(
112
139
  f"{HUB_API_ROOT}/get-export",
@@ -83,7 +83,24 @@ class GCPRegions:
83
83
 
84
84
  @staticmethod
85
85
  def _ping_region(region: str, attempts: int = 1) -> Tuple[str, float, float, float, float]:
86
- """Pings a specified GCP region and returns latency statistics: mean, min, max, and standard deviation."""
86
+ """
87
+ Ping a specified GCP region and measure network latency statistics.
88
+
89
+ Args:
90
+ region (str): The GCP region identifier to ping (e.g., 'us-central1').
91
+ attempts (int): Number of ping attempts to make for calculating statistics.
92
+
93
+ Returns:
94
+ region (str): The GCP region identifier that was pinged.
95
+ mean_latency (float): Mean latency in milliseconds, or infinity if all pings failed.
96
+ min_latency (float): Minimum latency in milliseconds, or infinity if all pings failed.
97
+ max_latency (float): Maximum latency in milliseconds, or infinity if all pings failed.
98
+ std_dev (float): Standard deviation of latencies in milliseconds, or infinity if all pings failed.
99
+
100
+ Examples:
101
+ >>> region, mean, min_lat, max_lat, std = GCPRegions._ping_region("us-central1", attempts=3)
102
+ >>> print(f"Region {region} has mean latency: {mean:.2f}ms")
103
+ """
87
104
  url = f"https://{region}-docker.pkg.dev"
88
105
  latencies = []
89
106
  for _ in range(attempts):
@@ -33,7 +33,18 @@ class FastSAMPredictor(SegmentationPredictor):
33
33
  """
34
34
 
35
35
  def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
36
- """Initialize the FastSAMPredictor with configuration and callbacks."""
36
+ """
37
+ Initialize the FastSAMPredictor with configuration and callbacks.
38
+
39
+ This initializes a predictor specialized for Fast SAM (Segment Anything Model) segmentation tasks. The predictor
40
+ extends SegmentationPredictor with custom post-processing for mask prediction and non-maximum suppression
41
+ optimized for single-class segmentation.
42
+
43
+ Args:
44
+ cfg (dict): Configuration for the predictor. Defaults to Ultralytics DEFAULT_CFG.
45
+ overrides (dict, optional): Configuration overrides.
46
+ _callbacks (list, optional): List of callback functions.
47
+ """
37
48
  super().__init__(cfg, overrides, _callbacks)
38
49
  self.prompts = {}
39
50
 
@@ -33,8 +33,26 @@ class NASPredictor(DetectionPredictor):
33
33
  """
34
34
 
35
35
  def postprocess(self, preds_in, img, orig_imgs):
36
- """Postprocess predictions and returns a list of Results objects."""
37
- # Convert boxes from xyxy to xywh format and concatenate with class scores
36
+ """
37
+ Postprocess NAS model predictions to generate final detection results.
38
+
39
+ This method takes raw predictions from a YOLO NAS model, converts bounding box formats, and applies
40
+ post-processing operations to generate the final detection results compatible with Ultralytics
41
+ result visualization and analysis tools.
42
+
43
+ Args:
44
+ preds_in (list): Raw predictions from the NAS model, typically containing bounding boxes and class scores.
45
+ img (torch.Tensor): Input image tensor that was fed to the model, with shape (B, C, H, W).
46
+ orig_imgs (list | torch.Tensor | np.ndarray): Original images before preprocessing, used for scaling
47
+ coordinates back to original dimensions.
48
+
49
+ Returns:
50
+ (list): List of Results objects containing the processed predictions for each image in the batch.
51
+
52
+ Examples:
53
+ >>> predictor = NAS("yolo_nas_s").predictor
54
+ >>> results = predictor.postprocess(raw_preds, img, orig_imgs)
55
+ """
38
56
  boxes = ops.xyxy2xywh(preds_in[0][0])
39
- preds = torch.cat((boxes, preds_in[0][1]), -1).permute(0, 2, 1)
57
+ preds = torch.cat((boxes, preds_in[0][1]), -1).permute(0, 2, 1) # concatenate with class scores
40
58
  return super().postprocess(preds, img, orig_imgs)
@@ -19,11 +19,35 @@ class RTDETRDataset(YOLODataset):
19
19
  """
20
20
 
21
21
  def __init__(self, *args, data=None, **kwargs):
22
- """Initialize the RTDETRDataset class by inheriting from the YOLODataset class."""
22
+ """
23
+ Initialize the RTDETRDataset class by inheriting from the YOLODataset class.
24
+
25
+ This constructor sets up a dataset specifically optimized for the RT-DETR (Real-Time DEtection and TRacking)
26
+ model, building upon the base YOLODataset functionality.
27
+
28
+ Args:
29
+ *args (Any): Variable length argument list passed to the parent YOLODataset class.
30
+ data (Dict | None): Dictionary containing dataset information. If None, default values will be used.
31
+ **kwargs (Any): Additional keyword arguments passed to the parent YOLODataset class.
32
+ """
23
33
  super().__init__(*args, data=data, **kwargs)
24
34
 
25
35
  def load_image(self, i, rect_mode=False):
26
- """Loads 1 image from dataset index 'i', returns (im, resized hw)."""
36
+ """
37
+ Load one image from dataset index 'i'.
38
+
39
+ Args:
40
+ i (int): Index of the image to load.
41
+ rect_mode (bool, optional): Whether to use rectangular mode for batch inference.
42
+
43
+ Returns:
44
+ im (numpy.ndarray): The loaded image.
45
+ resized_hw (tuple): Height and width of the resized image with shape (2,).
46
+
47
+ Examples:
48
+ >>> dataset = RTDETRDataset(...)
49
+ >>> image, hw = dataset.load_image(0)
50
+ """
27
51
  return super().load_image(i=i, rect_mode=rect_mode)
28
52
 
29
53
  def build_transforms(self, hyp=None):
@@ -22,7 +22,28 @@ def is_box_near_crop_edge(
22
22
 
23
23
 
24
24
  def batch_iterator(batch_size: int, *args) -> Generator[List[Any], None, None]:
25
- """Yields batches of data from input arguments with specified batch size for efficient processing."""
25
+ """
26
+ Yield batches of data from input arguments with specified batch size for efficient processing.
27
+
28
+ This function takes a batch size and any number of iterables, then yields batches of elements from those
29
+ iterables. All input iterables must have the same length.
30
+
31
+ Args:
32
+ batch_size (int): Size of each batch to yield.
33
+ *args (Any): Variable length input iterables to batch. All iterables must have the same length.
34
+
35
+ Yields:
36
+ (List[Any]): A list of batched elements from each input iterable.
37
+
38
+ Examples:
39
+ >>> data = [1, 2, 3, 4, 5]
40
+ >>> labels = ["a", "b", "c", "d", "e"]
41
+ >>> for batch in batch_iterator(2, data, labels):
42
+ ... print(batch)
43
+ [[1, 2], ['a', 'b']]
44
+ [[3, 4], ['c', 'd']]
45
+ [[5], ['e']]
46
+ """
26
47
  assert args and all(len(a) == len(args[0]) for a in args), "Batched iteration must have same-size inputs."
27
48
  n_batches = len(args[0]) // batch_size + int(len(args[0]) % batch_size != 0)
28
49
  for b in range(n_batches):