dgenerate-ultralytics-headless 8.3.214__py3-none-any.whl → 8.3.248__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 (236) hide show
  1. {dgenerate_ultralytics_headless-8.3.214.dist-info → dgenerate_ultralytics_headless-8.3.248.dist-info}/METADATA +13 -14
  2. dgenerate_ultralytics_headless-8.3.248.dist-info/RECORD +298 -0
  3. tests/__init__.py +5 -7
  4. tests/conftest.py +8 -15
  5. tests/test_cli.py +1 -1
  6. tests/test_cuda.py +5 -8
  7. tests/test_engine.py +1 -1
  8. tests/test_exports.py +57 -12
  9. tests/test_integrations.py +4 -4
  10. tests/test_python.py +84 -53
  11. tests/test_solutions.py +160 -151
  12. ultralytics/__init__.py +1 -1
  13. ultralytics/cfg/__init__.py +56 -62
  14. ultralytics/cfg/datasets/Argoverse.yaml +7 -6
  15. ultralytics/cfg/datasets/DOTAv1.5.yaml +1 -1
  16. ultralytics/cfg/datasets/DOTAv1.yaml +1 -1
  17. ultralytics/cfg/datasets/ImageNet.yaml +1 -1
  18. ultralytics/cfg/datasets/VOC.yaml +15 -16
  19. ultralytics/cfg/datasets/african-wildlife.yaml +1 -1
  20. ultralytics/cfg/datasets/coco-pose.yaml +21 -0
  21. ultralytics/cfg/datasets/coco128-seg.yaml +1 -1
  22. ultralytics/cfg/datasets/coco8-pose.yaml +21 -0
  23. ultralytics/cfg/datasets/dog-pose.yaml +28 -0
  24. ultralytics/cfg/datasets/dota8-multispectral.yaml +1 -1
  25. ultralytics/cfg/datasets/dota8.yaml +2 -2
  26. ultralytics/cfg/datasets/hand-keypoints.yaml +26 -2
  27. ultralytics/cfg/datasets/kitti.yaml +27 -0
  28. ultralytics/cfg/datasets/lvis.yaml +5 -5
  29. ultralytics/cfg/datasets/open-images-v7.yaml +1 -1
  30. ultralytics/cfg/datasets/tiger-pose.yaml +16 -0
  31. ultralytics/cfg/datasets/xView.yaml +16 -16
  32. ultralytics/cfg/default.yaml +1 -1
  33. ultralytics/cfg/models/11/yolo11-pose.yaml +1 -1
  34. ultralytics/cfg/models/11/yoloe-11-seg.yaml +2 -2
  35. ultralytics/cfg/models/11/yoloe-11.yaml +2 -2
  36. ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +1 -1
  37. ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +1 -1
  38. ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +1 -1
  39. ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +1 -1
  40. ultralytics/cfg/models/v10/yolov10b.yaml +2 -2
  41. ultralytics/cfg/models/v10/yolov10l.yaml +2 -2
  42. ultralytics/cfg/models/v10/yolov10m.yaml +2 -2
  43. ultralytics/cfg/models/v10/yolov10n.yaml +2 -2
  44. ultralytics/cfg/models/v10/yolov10s.yaml +2 -2
  45. ultralytics/cfg/models/v10/yolov10x.yaml +2 -2
  46. ultralytics/cfg/models/v3/yolov3-tiny.yaml +1 -1
  47. ultralytics/cfg/models/v6/yolov6.yaml +1 -1
  48. ultralytics/cfg/models/v8/yoloe-v8-seg.yaml +9 -6
  49. ultralytics/cfg/models/v8/yoloe-v8.yaml +9 -6
  50. ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +1 -1
  51. ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +1 -1
  52. ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +2 -2
  53. ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +2 -2
  54. ultralytics/cfg/models/v8/yolov8-ghost.yaml +2 -2
  55. ultralytics/cfg/models/v8/yolov8-obb.yaml +1 -1
  56. ultralytics/cfg/models/v8/yolov8-p2.yaml +1 -1
  57. ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +1 -1
  58. ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +1 -1
  59. ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +1 -1
  60. ultralytics/cfg/models/v8/yolov8-world.yaml +1 -1
  61. ultralytics/cfg/models/v8/yolov8-worldv2.yaml +6 -6
  62. ultralytics/cfg/models/v9/yolov9s.yaml +1 -1
  63. ultralytics/data/__init__.py +4 -4
  64. ultralytics/data/annotator.py +3 -4
  65. ultralytics/data/augment.py +285 -475
  66. ultralytics/data/base.py +18 -26
  67. ultralytics/data/build.py +147 -25
  68. ultralytics/data/converter.py +36 -46
  69. ultralytics/data/dataset.py +46 -74
  70. ultralytics/data/loaders.py +42 -49
  71. ultralytics/data/split.py +5 -6
  72. ultralytics/data/split_dota.py +8 -15
  73. ultralytics/data/utils.py +34 -43
  74. ultralytics/engine/exporter.py +319 -237
  75. ultralytics/engine/model.py +148 -188
  76. ultralytics/engine/predictor.py +29 -38
  77. ultralytics/engine/results.py +177 -311
  78. ultralytics/engine/trainer.py +83 -59
  79. ultralytics/engine/tuner.py +23 -34
  80. ultralytics/engine/validator.py +39 -22
  81. ultralytics/hub/__init__.py +16 -19
  82. ultralytics/hub/auth.py +6 -12
  83. ultralytics/hub/google/__init__.py +7 -10
  84. ultralytics/hub/session.py +15 -25
  85. ultralytics/hub/utils.py +5 -8
  86. ultralytics/models/__init__.py +1 -1
  87. ultralytics/models/fastsam/__init__.py +1 -1
  88. ultralytics/models/fastsam/model.py +8 -10
  89. ultralytics/models/fastsam/predict.py +17 -29
  90. ultralytics/models/fastsam/utils.py +1 -2
  91. ultralytics/models/fastsam/val.py +5 -7
  92. ultralytics/models/nas/__init__.py +1 -1
  93. ultralytics/models/nas/model.py +5 -8
  94. ultralytics/models/nas/predict.py +7 -9
  95. ultralytics/models/nas/val.py +1 -2
  96. ultralytics/models/rtdetr/__init__.py +1 -1
  97. ultralytics/models/rtdetr/model.py +5 -8
  98. ultralytics/models/rtdetr/predict.py +15 -19
  99. ultralytics/models/rtdetr/train.py +10 -13
  100. ultralytics/models/rtdetr/val.py +21 -23
  101. ultralytics/models/sam/__init__.py +15 -2
  102. ultralytics/models/sam/amg.py +14 -20
  103. ultralytics/models/sam/build.py +26 -19
  104. ultralytics/models/sam/build_sam3.py +377 -0
  105. ultralytics/models/sam/model.py +29 -32
  106. ultralytics/models/sam/modules/blocks.py +83 -144
  107. ultralytics/models/sam/modules/decoders.py +19 -37
  108. ultralytics/models/sam/modules/encoders.py +44 -101
  109. ultralytics/models/sam/modules/memory_attention.py +16 -30
  110. ultralytics/models/sam/modules/sam.py +200 -73
  111. ultralytics/models/sam/modules/tiny_encoder.py +64 -83
  112. ultralytics/models/sam/modules/transformer.py +18 -28
  113. ultralytics/models/sam/modules/utils.py +174 -50
  114. ultralytics/models/sam/predict.py +2248 -350
  115. ultralytics/models/sam/sam3/__init__.py +3 -0
  116. ultralytics/models/sam/sam3/decoder.py +546 -0
  117. ultralytics/models/sam/sam3/encoder.py +529 -0
  118. ultralytics/models/sam/sam3/geometry_encoders.py +415 -0
  119. ultralytics/models/sam/sam3/maskformer_segmentation.py +286 -0
  120. ultralytics/models/sam/sam3/model_misc.py +199 -0
  121. ultralytics/models/sam/sam3/necks.py +129 -0
  122. ultralytics/models/sam/sam3/sam3_image.py +339 -0
  123. ultralytics/models/sam/sam3/text_encoder_ve.py +307 -0
  124. ultralytics/models/sam/sam3/vitdet.py +547 -0
  125. ultralytics/models/sam/sam3/vl_combiner.py +160 -0
  126. ultralytics/models/utils/loss.py +14 -26
  127. ultralytics/models/utils/ops.py +13 -17
  128. ultralytics/models/yolo/__init__.py +1 -1
  129. ultralytics/models/yolo/classify/predict.py +9 -12
  130. ultralytics/models/yolo/classify/train.py +11 -32
  131. ultralytics/models/yolo/classify/val.py +29 -28
  132. ultralytics/models/yolo/detect/predict.py +7 -10
  133. ultralytics/models/yolo/detect/train.py +11 -20
  134. ultralytics/models/yolo/detect/val.py +70 -58
  135. ultralytics/models/yolo/model.py +36 -53
  136. ultralytics/models/yolo/obb/predict.py +5 -14
  137. ultralytics/models/yolo/obb/train.py +11 -14
  138. ultralytics/models/yolo/obb/val.py +39 -36
  139. ultralytics/models/yolo/pose/__init__.py +1 -1
  140. ultralytics/models/yolo/pose/predict.py +6 -21
  141. ultralytics/models/yolo/pose/train.py +10 -15
  142. ultralytics/models/yolo/pose/val.py +38 -57
  143. ultralytics/models/yolo/segment/predict.py +14 -18
  144. ultralytics/models/yolo/segment/train.py +3 -6
  145. ultralytics/models/yolo/segment/val.py +93 -45
  146. ultralytics/models/yolo/world/train.py +8 -14
  147. ultralytics/models/yolo/world/train_world.py +11 -34
  148. ultralytics/models/yolo/yoloe/__init__.py +7 -7
  149. ultralytics/models/yolo/yoloe/predict.py +16 -23
  150. ultralytics/models/yolo/yoloe/train.py +30 -43
  151. ultralytics/models/yolo/yoloe/train_seg.py +5 -10
  152. ultralytics/models/yolo/yoloe/val.py +15 -20
  153. ultralytics/nn/__init__.py +7 -7
  154. ultralytics/nn/autobackend.py +145 -77
  155. ultralytics/nn/modules/__init__.py +60 -60
  156. ultralytics/nn/modules/activation.py +4 -6
  157. ultralytics/nn/modules/block.py +132 -216
  158. ultralytics/nn/modules/conv.py +52 -97
  159. ultralytics/nn/modules/head.py +50 -103
  160. ultralytics/nn/modules/transformer.py +76 -88
  161. ultralytics/nn/modules/utils.py +16 -21
  162. ultralytics/nn/tasks.py +94 -154
  163. ultralytics/nn/text_model.py +40 -67
  164. ultralytics/solutions/__init__.py +12 -12
  165. ultralytics/solutions/ai_gym.py +11 -17
  166. ultralytics/solutions/analytics.py +15 -16
  167. ultralytics/solutions/config.py +5 -6
  168. ultralytics/solutions/distance_calculation.py +10 -13
  169. ultralytics/solutions/heatmap.py +7 -13
  170. ultralytics/solutions/instance_segmentation.py +5 -8
  171. ultralytics/solutions/object_blurrer.py +7 -10
  172. ultralytics/solutions/object_counter.py +12 -19
  173. ultralytics/solutions/object_cropper.py +8 -14
  174. ultralytics/solutions/parking_management.py +33 -31
  175. ultralytics/solutions/queue_management.py +10 -12
  176. ultralytics/solutions/region_counter.py +9 -12
  177. ultralytics/solutions/security_alarm.py +15 -20
  178. ultralytics/solutions/similarity_search.py +10 -15
  179. ultralytics/solutions/solutions.py +75 -74
  180. ultralytics/solutions/speed_estimation.py +7 -10
  181. ultralytics/solutions/streamlit_inference.py +2 -4
  182. ultralytics/solutions/templates/similarity-search.html +7 -18
  183. ultralytics/solutions/trackzone.py +7 -10
  184. ultralytics/solutions/vision_eye.py +5 -8
  185. ultralytics/trackers/__init__.py +1 -1
  186. ultralytics/trackers/basetrack.py +3 -5
  187. ultralytics/trackers/bot_sort.py +10 -27
  188. ultralytics/trackers/byte_tracker.py +14 -30
  189. ultralytics/trackers/track.py +3 -6
  190. ultralytics/trackers/utils/gmc.py +11 -22
  191. ultralytics/trackers/utils/kalman_filter.py +37 -48
  192. ultralytics/trackers/utils/matching.py +12 -15
  193. ultralytics/utils/__init__.py +116 -116
  194. ultralytics/utils/autobatch.py +2 -4
  195. ultralytics/utils/autodevice.py +17 -18
  196. ultralytics/utils/benchmarks.py +32 -46
  197. ultralytics/utils/callbacks/base.py +8 -10
  198. ultralytics/utils/callbacks/clearml.py +5 -13
  199. ultralytics/utils/callbacks/comet.py +32 -46
  200. ultralytics/utils/callbacks/dvc.py +13 -18
  201. ultralytics/utils/callbacks/mlflow.py +4 -5
  202. ultralytics/utils/callbacks/neptune.py +7 -15
  203. ultralytics/utils/callbacks/platform.py +314 -38
  204. ultralytics/utils/callbacks/raytune.py +3 -4
  205. ultralytics/utils/callbacks/tensorboard.py +23 -31
  206. ultralytics/utils/callbacks/wb.py +10 -13
  207. ultralytics/utils/checks.py +99 -76
  208. ultralytics/utils/cpu.py +3 -8
  209. ultralytics/utils/dist.py +8 -12
  210. ultralytics/utils/downloads.py +20 -30
  211. ultralytics/utils/errors.py +6 -14
  212. ultralytics/utils/events.py +2 -4
  213. ultralytics/utils/export/__init__.py +4 -236
  214. ultralytics/utils/export/engine.py +237 -0
  215. ultralytics/utils/export/imx.py +91 -55
  216. ultralytics/utils/export/tensorflow.py +231 -0
  217. ultralytics/utils/files.py +24 -28
  218. ultralytics/utils/git.py +9 -11
  219. ultralytics/utils/instance.py +30 -51
  220. ultralytics/utils/logger.py +212 -114
  221. ultralytics/utils/loss.py +14 -22
  222. ultralytics/utils/metrics.py +126 -155
  223. ultralytics/utils/nms.py +13 -16
  224. ultralytics/utils/ops.py +107 -165
  225. ultralytics/utils/patches.py +33 -21
  226. ultralytics/utils/plotting.py +72 -80
  227. ultralytics/utils/tal.py +25 -39
  228. ultralytics/utils/torch_utils.py +52 -78
  229. ultralytics/utils/tqdm.py +20 -20
  230. ultralytics/utils/triton.py +13 -19
  231. ultralytics/utils/tuner.py +17 -5
  232. dgenerate_ultralytics_headless-8.3.214.dist-info/RECORD +0 -283
  233. {dgenerate_ultralytics_headless-8.3.214.dist-info → dgenerate_ultralytics_headless-8.3.248.dist-info}/WHEEL +0 -0
  234. {dgenerate_ultralytics_headless-8.3.214.dist-info → dgenerate_ultralytics_headless-8.3.248.dist-info}/entry_points.txt +0 -0
  235. {dgenerate_ultralytics_headless-8.3.214.dist-info → dgenerate_ultralytics_headless-8.3.248.dist-info}/licenses/LICENSE +0 -0
  236. {dgenerate_ultralytics_headless-8.3.214.dist-info → dgenerate_ultralytics_headless-8.3.248.dist-info}/top_level.txt +0 -0
tests/test_exports.py CHANGED
@@ -12,15 +12,8 @@ import pytest
12
12
  from tests import MODEL, SOURCE
13
13
  from ultralytics import YOLO
14
14
  from ultralytics.cfg import TASK2DATA, TASK2MODEL, TASKS
15
- from ultralytics.utils import (
16
- ARM64,
17
- IS_RASPBERRYPI,
18
- LINUX,
19
- MACOS,
20
- WINDOWS,
21
- checks,
22
- )
23
- from ultralytics.utils.torch_utils import TORCH_1_11, TORCH_1_13, TORCH_2_1
15
+ from ultralytics.utils import ARM64, IS_RASPBERRYPI, LINUX, MACOS, WINDOWS, checks
16
+ from ultralytics.utils.torch_utils import TORCH_1_10, TORCH_1_11, TORCH_1_13, TORCH_2_1, TORCH_2_8, TORCH_2_9
24
17
 
25
18
 
26
19
  def test_export_torchscript():
@@ -217,6 +210,7 @@ def test_export_paddle():
217
210
 
218
211
 
219
212
  @pytest.mark.slow
213
+ @pytest.mark.skipif(not TORCH_1_10, reason="MNN export requires torch>=1.10")
220
214
  def test_export_mnn():
221
215
  """Test YOLO export to MNN format (WARNING: MNN test must precede NCNN test or CI error on Windows)."""
222
216
  file = YOLO(MODEL).export(format="mnn", imgsz=32)
@@ -224,6 +218,7 @@ def test_export_mnn():
224
218
 
225
219
 
226
220
  @pytest.mark.slow
221
+ @pytest.mark.skipif(not TORCH_1_10, reason="MNN export requires torch>=1.10")
227
222
  @pytest.mark.parametrize(
228
223
  "task, int8, half, batch",
229
224
  [ # generate all combinations except for exclusion cases
@@ -255,10 +250,60 @@ def test_export_ncnn_matrix(task, half, batch):
255
250
  shutil.rmtree(file, ignore_errors=True) # retry in case of potential lingering multi-threaded file usage errors
256
251
 
257
252
 
258
- @pytest.mark.skipif(True, reason="Test disabled as keras and tensorflow version conflicts with TFlite export.")
259
- @pytest.mark.skipif(not LINUX or MACOS, reason="Skipping test on Windows and Macos")
253
+ @pytest.mark.skipif(not TORCH_2_9, reason="IMX export requires torch>=2.9.0")
254
+ @pytest.mark.skipif(not checks.IS_PYTHON_MINIMUM_3_9, reason="Requires Python>=3.9")
255
+ @pytest.mark.skipif(WINDOWS or MACOS, reason="Skipping test on Windows and Macos")
256
+ @pytest.mark.skipif(ARM64, reason="IMX export is not supported on ARM64 architectures.")
260
257
  def test_export_imx():
261
258
  """Test YOLO export to IMX format."""
262
- model = YOLO("yolov8n.pt")
259
+ model = YOLO(MODEL)
263
260
  file = model.export(format="imx", imgsz=32)
264
261
  YOLO(file)(SOURCE, imgsz=32)
262
+
263
+
264
+ @pytest.mark.slow
265
+ @pytest.mark.skipif(not TORCH_2_8, reason="Axelera export requires torch>=2.8.0")
266
+ @pytest.mark.skipif(not LINUX, reason="Axelera export only supported on Linux")
267
+ @pytest.mark.skipif(not checks.IS_PYTHON_3_10, reason="Axelera export requires Python 3.10")
268
+ def test_export_axelera():
269
+ """Test YOLO export to Axelera format."""
270
+ # For faster testing, use a smaller calibration dataset (32 image size crashes axelera export, so 64 is used)
271
+ file = YOLO(MODEL).export(format="axelera", imgsz=64, data="coco8.yaml")
272
+ assert Path(file).exists(), f"Axelera export failed, directory not found: {file}"
273
+ shutil.rmtree(file, ignore_errors=True) # cleanup
274
+
275
+
276
+ # @pytest.mark.skipif(True, reason="Disabled for debugging ruamel.yaml installation required by executorch")
277
+ @pytest.mark.skipif(not checks.IS_PYTHON_MINIMUM_3_10 or not TORCH_2_9, reason="Requires Python>=3.10 and Torch>=2.9.0")
278
+ @pytest.mark.skipif(WINDOWS, reason="Skipping test on Windows")
279
+ def test_export_executorch():
280
+ """Test YOLO model export to ExecuTorch format."""
281
+ file = YOLO(MODEL).export(format="executorch", imgsz=32)
282
+ assert Path(file).exists(), f"ExecuTorch export failed, directory not found: {file}"
283
+ # Check that .pte file exists in the exported directory
284
+ pte_file = Path(file) / Path(MODEL).with_suffix(".pte").name
285
+ assert pte_file.exists(), f"ExecuTorch .pte file not found: {pte_file}"
286
+ # Check that metadata.yaml exists
287
+ metadata_file = Path(file) / "metadata.yaml"
288
+ assert metadata_file.exists(), f"ExecuTorch metadata.yaml not found: {metadata_file}"
289
+ # Note: Inference testing skipped as ExecuTorch requires special runtime setup
290
+ shutil.rmtree(file, ignore_errors=True) # cleanup
291
+
292
+
293
+ @pytest.mark.slow
294
+ @pytest.mark.skipif(not checks.IS_PYTHON_MINIMUM_3_10 or not TORCH_2_9, reason="Requires Python>=3.10 and Torch>=2.9.0")
295
+ @pytest.mark.skipif(WINDOWS, reason="Skipping test on Windows")
296
+ @pytest.mark.parametrize("task", TASKS)
297
+ def test_export_executorch_matrix(task):
298
+ """Test YOLO export to ExecuTorch format for various task types."""
299
+ file = YOLO(TASK2MODEL[task]).export(format="executorch", imgsz=32)
300
+ assert Path(file).exists(), f"ExecuTorch export failed for task '{task}', directory not found: {file}"
301
+ # Check that .pte file exists in the exported directory
302
+ model_name = Path(TASK2MODEL[task]).with_suffix(".pte").name
303
+ pte_file = Path(file) / model_name
304
+ assert pte_file.exists(), f"ExecuTorch .pte file not found for task '{task}': {pte_file}"
305
+ # Check that metadata.yaml exists
306
+ metadata_file = Path(file) / "metadata.yaml"
307
+ assert metadata_file.exists(), f"ExecuTorch metadata.yaml not found for task '{task}': {metadata_file}"
308
+ # Note: Inference testing skipped as ExecuTorch requires special runtime setup
309
+ shutil.rmtree(file, ignore_errors=True) # cleanup
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import pytest
10
10
 
11
- from tests import MODEL, SOURCE, TMP
11
+ from tests import MODEL, SOURCE
12
12
  from ultralytics import YOLO, download
13
13
  from ultralytics.utils import ASSETS_URL, DATASETS_DIR, SETTINGS
14
14
  from ultralytics.utils.checks import check_requirements
@@ -71,14 +71,14 @@ def test_mlflow_keep_run_active():
71
71
 
72
72
 
73
73
  @pytest.mark.skipif(not check_requirements("tritonclient", install=False), reason="tritonclient[all] not installed")
74
- def test_triton():
74
+ def test_triton(tmp_path):
75
75
  """Test NVIDIA Triton Server functionalities with YOLO model."""
76
76
  check_requirements("tritonclient[all]")
77
- from tritonclient.http import InferenceServerClient # noqa
77
+ from tritonclient.http import InferenceServerClient
78
78
 
79
79
  # Create variables
80
80
  model_name = "yolo"
81
- triton_repo = TMP / "triton_repo" # Triton repo path
81
+ triton_repo = tmp_path / "triton_repo" # Triton repo path
82
82
  triton_model = triton_repo / model_name # Triton model path
83
83
 
84
84
  # Export model to ONNX
tests/test_python.py CHANGED
@@ -12,7 +12,7 @@ import pytest
12
12
  import torch
13
13
  from PIL import Image
14
14
 
15
- from tests import CFG, MODEL, MODELS, SOURCE, SOURCES_LIST, TASK_MODEL_DATA, TMP
15
+ from tests import CFG, MODEL, MODELS, SOURCE, SOURCES_LIST, TASK_MODEL_DATA
16
16
  from ultralytics import RTDETR, YOLO
17
17
  from ultralytics.cfg import TASK2DATA, TASKS
18
18
  from ultralytics.data.build import load_inference_source
@@ -33,14 +33,11 @@ from ultralytics.utils import (
33
33
  WINDOWS,
34
34
  YAML,
35
35
  checks,
36
- is_dir_writeable,
37
36
  is_github_action_running,
38
37
  )
39
38
  from ultralytics.utils.downloads import download
40
39
  from ultralytics.utils.torch_utils import TORCH_1_11, TORCH_1_13
41
40
 
42
- IS_TMP_WRITEABLE = is_dir_writeable(TMP) # WARNING: must be run once tests start as TMP does not exist on tests/init
43
-
44
41
 
45
42
  def test_model_forward():
46
43
  """Test the forward pass of the YOLO model."""
@@ -77,10 +74,9 @@ def test_model_profile():
77
74
  _ = model.predict(im, profile=True)
78
75
 
79
76
 
80
- @pytest.mark.skipif(not IS_TMP_WRITEABLE, reason="directory is not writeable")
81
- def test_predict_txt():
77
+ def test_predict_txt(tmp_path):
82
78
  """Test YOLO predictions with file, directory, and pattern sources listed in a text file."""
83
- file = TMP / "sources_multi_row.txt"
79
+ file = tmp_path / "sources_multi_row.txt"
84
80
  with open(file, "w") as f:
85
81
  for src in SOURCES_LIST:
86
82
  f.write(f"{src}\n")
@@ -89,10 +85,9 @@ def test_predict_txt():
89
85
 
90
86
 
91
87
  @pytest.mark.skipif(True, reason="disabled for testing")
92
- @pytest.mark.skipif(not IS_TMP_WRITEABLE, reason="directory is not writeable")
93
- def test_predict_csv_multi_row():
88
+ def test_predict_csv_multi_row(tmp_path):
94
89
  """Test YOLO predictions with sources listed in multiple rows of a CSV file."""
95
- file = TMP / "sources_multi_row.csv"
90
+ file = tmp_path / "sources_multi_row.csv"
96
91
  with open(file, "w", newline="") as f:
97
92
  writer = csv.writer(f)
98
93
  writer.writerow(["source"])
@@ -102,10 +97,9 @@ def test_predict_csv_multi_row():
102
97
 
103
98
 
104
99
  @pytest.mark.skipif(True, reason="disabled for testing")
105
- @pytest.mark.skipif(not IS_TMP_WRITEABLE, reason="directory is not writeable")
106
- def test_predict_csv_single_row():
100
+ def test_predict_csv_single_row(tmp_path):
107
101
  """Test YOLO predictions with sources listed in a single row of a CSV file."""
108
- file = TMP / "sources_single_row.csv"
102
+ file = tmp_path / "sources_single_row.csv"
109
103
  with open(file, "w", newline="") as f:
110
104
  writer = csv.writer(f)
111
105
  writer.writerow(SOURCES_LIST)
@@ -118,7 +112,7 @@ def test_predict_img(model_name):
118
112
  """Test YOLO model predictions on various image input types and sources, including online images."""
119
113
  channels = 1 if model_name == "yolo11n-grayscale.pt" else 3
120
114
  model = YOLO(WEIGHTS_DIR / model_name)
121
- im = cv2.imread(str(SOURCE), flags=cv2.IMREAD_GRAYSCALE if channels == 1 else cv2.IMREAD_COLOR) # uint8 numpy array
115
+ im = cv2.imread(str(SOURCE), flags=cv2.IMREAD_GRAYSCALE if channels == 1 else cv2.IMREAD_COLOR) # uint8 NumPy array
122
116
  assert len(model(source=Image.open(SOURCE), save=True, verbose=True, imgsz=32)) == 1 # PIL
123
117
  assert len(model(source=im, save=True, save_txt=True, imgsz=32)) == 1 # ndarray
124
118
  assert len(model(torch.rand((2, channels, 32, 32)), imgsz=32)) == 2 # batch-size 2 Tensor, FP32 0.0-1.0 RGB order
@@ -142,25 +136,23 @@ def test_predict_visualize(model):
142
136
  YOLO(WEIGHTS_DIR / model)(SOURCE, imgsz=32, visualize=True)
143
137
 
144
138
 
145
- def test_predict_grey_and_4ch():
146
- """Test YOLO prediction on SOURCE converted to greyscale and 4-channel images with various filenames."""
139
+ def test_predict_gray_and_4ch(tmp_path):
140
+ """Test YOLO prediction on SOURCE converted to grayscale and 4-channel images with various filenames."""
147
141
  im = Image.open(SOURCE)
148
- directory = TMP / "im4"
149
- directory.mkdir(parents=True, exist_ok=True)
150
142
 
151
- source_greyscale = directory / "greyscale.jpg"
152
- source_rgba = directory / "4ch.png"
153
- source_non_utf = directory / "non_UTF_测试文件_tést_image.jpg"
154
- source_spaces = directory / "image with spaces.jpg"
143
+ source_grayscale = tmp_path / "grayscale.jpg"
144
+ source_rgba = tmp_path / "4ch.png"
145
+ source_non_utf = tmp_path / "non_UTF_测试文件_tést_image.jpg"
146
+ source_spaces = tmp_path / "image with spaces.jpg"
155
147
 
156
- im.convert("L").save(source_greyscale) # greyscale
148
+ im.convert("L").save(source_grayscale) # grayscale
157
149
  im.convert("RGBA").save(source_rgba) # 4-ch PNG with alpha
158
150
  im.save(source_non_utf) # non-UTF characters in filename
159
151
  im.save(source_spaces) # spaces in filename
160
152
 
161
153
  # Inference
162
154
  model = YOLO(MODEL)
163
- for f in source_rgba, source_greyscale, source_non_utf, source_spaces:
155
+ for f in source_rgba, source_grayscale, source_non_utf, source_spaces:
164
156
  for source in Image.open(f), cv2.imread(str(f)), f:
165
157
  results = model(source, save=True, verbose=True, imgsz=32)
166
158
  assert len(results) == 1 # verify that an image was run
@@ -181,11 +173,9 @@ def test_youtube():
181
173
 
182
174
 
183
175
  @pytest.mark.skipif(not ONLINE, reason="environment is offline")
184
- @pytest.mark.skipif(not IS_TMP_WRITEABLE, reason="directory is not writeable")
185
176
  @pytest.mark.parametrize("model", MODELS)
186
- def test_track_stream(model):
187
- """
188
- Test streaming tracking on a short 10 frame video using ByteTrack tracker and different GMC methods.
177
+ def test_track_stream(model, tmp_path):
178
+ """Test streaming tracking on a short 10 frame video using ByteTrack tracker and different GMC methods.
189
179
 
190
180
  Note imgsz=160 required for tracking for higher confidence and better matches.
191
181
  """
@@ -199,7 +189,7 @@ def test_track_stream(model):
199
189
  # Test Global Motion Compensation (GMC) methods and ReID
200
190
  for gmc, reidm in zip(["orb", "sift", "ecc"], ["auto", "auto", "yolo11n-cls.pt"]):
201
191
  default_args = YAML.load(ROOT / "cfg/trackers/botsort.yaml")
202
- custom_yaml = TMP / f"botsort-{gmc}.yaml"
192
+ custom_yaml = tmp_path / f"botsort-{gmc}.yaml"
203
193
  YAML.save(custom_yaml, {**default_args, "gmc_method": gmc, "with_reid": True, "model": reidm})
204
194
  model.track(video_url, imgsz=160, tracker=custom_yaml)
205
195
 
@@ -278,7 +268,7 @@ def test_predict_callback_and_setup():
278
268
  model.add_callback("on_predict_batch_end", on_predict_batch_end)
279
269
 
280
270
  dataset = load_inference_source(source=SOURCE)
281
- bs = dataset.bs # noqa access predictor properties
271
+ bs = dataset.bs # access predictor properties
282
272
  results = model.predict(dataset, stream=True, imgsz=160) # source already setup
283
273
  for r, im0, bs in results:
284
274
  print("test_callback", im0.shape)
@@ -288,7 +278,7 @@ def test_predict_callback_and_setup():
288
278
 
289
279
 
290
280
  @pytest.mark.parametrize("model", MODELS)
291
- def test_results(model: str):
281
+ def test_results(model: str, tmp_path):
292
282
  """Test YOLO model results processing and output in various formats."""
293
283
  im = f"{ASSETS_URL}/boats.jpg" if model == "yolo11n-obb.pt" else SOURCE
294
284
  results = YOLO(WEIGHTS_DIR / model)([im, im], imgsz=160)
@@ -297,12 +287,12 @@ def test_results(model: str):
297
287
  r = r.cpu().numpy()
298
288
  print(r, len(r), r.path) # print numpy attributes
299
289
  r = r.to(device="cpu", dtype=torch.float32)
300
- r.save_txt(txt_file=TMP / "runs/tests/label.txt", save_conf=True)
301
- r.save_crop(save_dir=TMP / "runs/tests/crops/")
290
+ r.save_txt(txt_file=tmp_path / "runs/tests/label.txt", save_conf=True)
291
+ r.save_crop(save_dir=tmp_path / "runs/tests/crops/")
302
292
  r.to_df(decimals=3) # Align to_ methods: https://docs.ultralytics.com/modes/predict/#working-with-results
303
293
  r.to_csv()
304
294
  r.to_json(normalize=True)
305
- r.plot(pil=True, save=True, filename=TMP / "results_plot_save.jpg")
295
+ r.plot(pil=True, save=True, filename=tmp_path / "results_plot_save.jpg")
306
296
  r.plot(conf=True, boxes=True)
307
297
  print(r, len(r), r.path) # print after methods
308
298
 
@@ -332,7 +322,7 @@ def test_labels_and_crops():
332
322
 
333
323
 
334
324
  @pytest.mark.skipif(not ONLINE, reason="environment is offline")
335
- def test_data_utils():
325
+ def test_data_utils(tmp_path):
336
326
  """Test utility functions in ultralytics/data/utils.py, including dataset stats and auto-splitting."""
337
327
  from ultralytics.data.split import autosplit
338
328
  from ultralytics.data.utils import HUBDatasetStats
@@ -343,26 +333,28 @@ def test_data_utils():
343
333
 
344
334
  for task in TASKS:
345
335
  file = Path(TASK2DATA[task]).with_suffix(".zip") # i.e. coco8.zip
346
- download(f"https://github.com/ultralytics/hub/raw/main/example_datasets/{file}", unzip=False, dir=TMP)
347
- stats = HUBDatasetStats(TMP / file, task=task)
336
+ download(f"https://github.com/ultralytics/hub/raw/main/example_datasets/{file}", unzip=False, dir=tmp_path)
337
+ stats = HUBDatasetStats(tmp_path / file, task=task)
348
338
  stats.get_json(save=True)
349
339
  stats.process_images()
350
340
 
351
- autosplit(TMP / "coco8")
352
- zip_directory(TMP / "coco8/images/val") # zip
341
+ autosplit(tmp_path / "coco8")
342
+ zip_directory(tmp_path / "coco8/images/val") # zip
353
343
 
354
344
 
355
345
  @pytest.mark.skipif(not ONLINE, reason="environment is offline")
356
- def test_data_converter():
346
+ def test_data_converter(tmp_path):
357
347
  """Test dataset conversion functions from COCO to YOLO format and class mappings."""
358
348
  from ultralytics.data.converter import coco80_to_coco91_class, convert_coco
359
349
 
360
- download(f"{ASSETS_URL}/instances_val2017.json", dir=TMP)
361
- convert_coco(labels_dir=TMP, save_dir=TMP / "yolo_labels", use_segments=True, use_keypoints=False, cls91to80=True)
350
+ download(f"{ASSETS_URL}/instances_val2017.json", dir=tmp_path)
351
+ convert_coco(
352
+ labels_dir=tmp_path, save_dir=tmp_path / "yolo_labels", use_segments=True, use_keypoints=False, cls91to80=True
353
+ )
362
354
  coco80_to_coco91_class()
363
355
 
364
356
 
365
- def test_data_annotator():
357
+ def test_data_annotator(tmp_path):
366
358
  """Test automatic annotation of data using detection and segmentation models."""
367
359
  from ultralytics.data.annotator import auto_annotate
368
360
 
@@ -370,7 +362,7 @@ def test_data_annotator():
370
362
  ASSETS,
371
363
  det_model=WEIGHTS_DIR / "yolo11n.pt",
372
364
  sam_model=WEIGHTS_DIR / "mobile_sam.pt",
373
- output_dir=TMP / "auto_annotate_labels",
365
+ output_dir=tmp_path / "auto_annotate_labels",
374
366
  )
375
367
 
376
368
 
@@ -393,7 +385,46 @@ def test_cfg_init():
393
385
  check_dict_alignment({"a": 1}, {"b": 2})
394
386
  copy_default_cfg()
395
387
  (Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")).unlink(missing_ok=False)
396
- [smart_value(x) for x in {"none", "true", "false"}]
388
+
389
+ # Test smart_value() with comprehensive cases
390
+ # Test None conversion
391
+ assert smart_value("none") is None
392
+ assert smart_value("None") is None
393
+ assert smart_value("NONE") is None
394
+
395
+ # Test boolean conversion
396
+ assert smart_value("true") is True
397
+ assert smart_value("True") is True
398
+ assert smart_value("TRUE") is True
399
+ assert smart_value("false") is False
400
+ assert smart_value("False") is False
401
+ assert smart_value("FALSE") is False
402
+
403
+ # Test numeric conversion (ast.literal_eval)
404
+ assert smart_value("42") == 42
405
+ assert smart_value("-42") == -42
406
+ assert smart_value("3.14") == 3.14
407
+ assert smart_value("-3.14") == -3.14
408
+ assert smart_value("1e-3") == 0.001
409
+
410
+ # Test list/tuple conversion (ast.literal_eval)
411
+ assert smart_value("[1, 2, 3]") == [1, 2, 3]
412
+ assert smart_value("(1, 2, 3)") == (1, 2, 3)
413
+ assert smart_value("[640, 640]") == [640, 640]
414
+
415
+ # Test dict conversion (ast.literal_eval)
416
+ assert smart_value("{'a': 1, 'b': 2}") == {"a": 1, "b": 2}
417
+
418
+ # Test string fallback (when ast.literal_eval fails)
419
+ assert smart_value("some_string") == "some_string"
420
+ assert smart_value("path/to/file") == "path/to/file"
421
+ assert smart_value("hello world") == "hello world"
422
+
423
+ # Test that code injection is prevented (ast.literal_eval safety)
424
+ # These should return strings, not execute code
425
+ assert smart_value("__import__('os').system('ls')") == "__import__('os').system('ls')"
426
+ assert smart_value("eval('1+1')") == "eval('1+1')"
427
+ assert smart_value("exec('x=1')") == "exec('x=1')"
397
428
 
398
429
 
399
430
  def test_utils_init():
@@ -464,7 +495,7 @@ def test_utils_ops():
464
495
  torch.allclose(boxes, xyxyxyxy2xywhr(xywhr2xyxyxyxy(boxes)), rtol=1e-3)
465
496
 
466
497
 
467
- def test_utils_files():
498
+ def test_utils_files(tmp_path):
468
499
  """Test file handling utilities including file age, date, and paths with spaces."""
469
500
  from ultralytics.utils.files import file_age, file_date, get_latest_run, spaces_in_path
470
501
 
@@ -472,14 +503,14 @@ def test_utils_files():
472
503
  file_date(SOURCE)
473
504
  get_latest_run(ROOT / "runs")
474
505
 
475
- path = TMP / "path/with spaces"
506
+ path = tmp_path / "path/with spaces"
476
507
  path.mkdir(parents=True, exist_ok=True)
477
508
  with spaces_in_path(path) as new_path:
478
509
  print(new_path)
479
510
 
480
511
 
481
512
  @pytest.mark.slow
482
- def test_utils_patches_torch_save():
513
+ def test_utils_patches_torch_save(tmp_path):
483
514
  """Test torch_save backoff when _torch_save raises RuntimeError."""
484
515
  from unittest.mock import MagicMock, patch
485
516
 
@@ -489,7 +520,7 @@ def test_utils_patches_torch_save():
489
520
 
490
521
  with patch("ultralytics.utils.patches._torch_save", new=mock):
491
522
  with pytest.raises(RuntimeError):
492
- torch_save(torch.zeros(1), TMP / "test.pt")
523
+ torch_save(torch.zeros(1), tmp_path / "test.pt")
493
524
 
494
525
  assert mock.call_count == 4, "torch_save was not attempted the expected number of times"
495
526
 
@@ -541,7 +572,7 @@ def test_hub():
541
572
 
542
573
  @pytest.fixture
543
574
  def image():
544
- """Load and return an image from a predefined source."""
575
+ """Load and return an image from a predefined source (OpenCV BGR)."""
545
576
  return cv2.imread(str(SOURCE))
546
577
 
547
578
 
@@ -722,14 +753,14 @@ def test_multichannel():
722
753
 
723
754
 
724
755
  @pytest.mark.parametrize("task,model,data", TASK_MODEL_DATA)
725
- def test_grayscale(task: str, model: str, data: str) -> None:
756
+ def test_grayscale(task: str, model: str, data: str, tmp_path) -> None:
726
757
  """Test YOLO model grayscale training, validation, and prediction functionality."""
727
758
  if task == "classify": # not support grayscale classification yet
728
759
  return
729
- grayscale_data = Path(TMP) / f"{Path(data).stem}-grayscale.yaml"
760
+ grayscale_data = tmp_path / f"{Path(data).stem}-grayscale.yaml"
730
761
  data = check_det_dataset(data)
731
762
  data["channels"] = 1 # add additional channels key for grayscale
732
- YAML.save(grayscale_data, data)
763
+ YAML.save(data=data, file=grayscale_data)
733
764
  # remove npy files in train/val splits if exists, might be created by previous tests
734
765
  for split in {"train", "val"}:
735
766
  for npy_file in (Path(data["path"]) / data[split]).glob("*.npy"):