dgenerate-ultralytics-headless 8.3.137__py3-none-any.whl → 8.3.224__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/METADATA +41 -34
  2. dgenerate_ultralytics_headless-8.3.224.dist-info/RECORD +285 -0
  3. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/WHEEL +1 -1
  4. tests/__init__.py +7 -6
  5. tests/conftest.py +15 -39
  6. tests/test_cli.py +17 -17
  7. tests/test_cuda.py +17 -8
  8. tests/test_engine.py +36 -10
  9. tests/test_exports.py +98 -37
  10. tests/test_integrations.py +12 -15
  11. tests/test_python.py +126 -82
  12. tests/test_solutions.py +319 -135
  13. ultralytics/__init__.py +27 -9
  14. ultralytics/cfg/__init__.py +83 -87
  15. ultralytics/cfg/datasets/Argoverse.yaml +4 -4
  16. ultralytics/cfg/datasets/DOTAv1.5.yaml +2 -2
  17. ultralytics/cfg/datasets/DOTAv1.yaml +2 -2
  18. ultralytics/cfg/datasets/GlobalWheat2020.yaml +2 -2
  19. ultralytics/cfg/datasets/HomeObjects-3K.yaml +4 -5
  20. ultralytics/cfg/datasets/ImageNet.yaml +3 -3
  21. ultralytics/cfg/datasets/Objects365.yaml +24 -20
  22. ultralytics/cfg/datasets/SKU-110K.yaml +9 -9
  23. ultralytics/cfg/datasets/VOC.yaml +10 -13
  24. ultralytics/cfg/datasets/VisDrone.yaml +43 -33
  25. ultralytics/cfg/datasets/african-wildlife.yaml +5 -5
  26. ultralytics/cfg/datasets/brain-tumor.yaml +4 -5
  27. ultralytics/cfg/datasets/carparts-seg.yaml +5 -5
  28. ultralytics/cfg/datasets/coco-pose.yaml +26 -4
  29. ultralytics/cfg/datasets/coco.yaml +4 -4
  30. ultralytics/cfg/datasets/coco128-seg.yaml +2 -2
  31. ultralytics/cfg/datasets/coco128.yaml +2 -2
  32. ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
  33. ultralytics/cfg/datasets/coco8-multispectral.yaml +2 -2
  34. ultralytics/cfg/datasets/coco8-pose.yaml +23 -2
  35. ultralytics/cfg/datasets/coco8-seg.yaml +2 -2
  36. ultralytics/cfg/datasets/coco8.yaml +2 -2
  37. ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
  38. ultralytics/cfg/datasets/crack-seg.yaml +5 -5
  39. ultralytics/cfg/datasets/dog-pose.yaml +32 -4
  40. ultralytics/cfg/datasets/dota8-multispectral.yaml +2 -2
  41. ultralytics/cfg/datasets/dota8.yaml +2 -2
  42. ultralytics/cfg/datasets/hand-keypoints.yaml +29 -4
  43. ultralytics/cfg/datasets/lvis.yaml +9 -9
  44. ultralytics/cfg/datasets/medical-pills.yaml +4 -5
  45. ultralytics/cfg/datasets/open-images-v7.yaml +7 -10
  46. ultralytics/cfg/datasets/package-seg.yaml +5 -5
  47. ultralytics/cfg/datasets/signature.yaml +4 -4
  48. ultralytics/cfg/datasets/tiger-pose.yaml +20 -4
  49. ultralytics/cfg/datasets/xView.yaml +5 -5
  50. ultralytics/cfg/default.yaml +96 -93
  51. ultralytics/cfg/trackers/botsort.yaml +16 -17
  52. ultralytics/cfg/trackers/bytetrack.yaml +9 -11
  53. ultralytics/data/__init__.py +4 -4
  54. ultralytics/data/annotator.py +12 -12
  55. ultralytics/data/augment.py +531 -564
  56. ultralytics/data/base.py +76 -81
  57. ultralytics/data/build.py +206 -42
  58. ultralytics/data/converter.py +179 -78
  59. ultralytics/data/dataset.py +121 -121
  60. ultralytics/data/loaders.py +114 -91
  61. ultralytics/data/split.py +28 -15
  62. ultralytics/data/split_dota.py +67 -48
  63. ultralytics/data/utils.py +110 -89
  64. ultralytics/engine/exporter.py +422 -460
  65. ultralytics/engine/model.py +224 -252
  66. ultralytics/engine/predictor.py +94 -89
  67. ultralytics/engine/results.py +345 -595
  68. ultralytics/engine/trainer.py +231 -134
  69. ultralytics/engine/tuner.py +279 -73
  70. ultralytics/engine/validator.py +53 -46
  71. ultralytics/hub/__init__.py +26 -28
  72. ultralytics/hub/auth.py +30 -16
  73. ultralytics/hub/google/__init__.py +34 -36
  74. ultralytics/hub/session.py +53 -77
  75. ultralytics/hub/utils.py +23 -109
  76. ultralytics/models/__init__.py +1 -1
  77. ultralytics/models/fastsam/__init__.py +1 -1
  78. ultralytics/models/fastsam/model.py +36 -18
  79. ultralytics/models/fastsam/predict.py +33 -44
  80. ultralytics/models/fastsam/utils.py +4 -5
  81. ultralytics/models/fastsam/val.py +12 -14
  82. ultralytics/models/nas/__init__.py +1 -1
  83. ultralytics/models/nas/model.py +16 -20
  84. ultralytics/models/nas/predict.py +12 -14
  85. ultralytics/models/nas/val.py +4 -5
  86. ultralytics/models/rtdetr/__init__.py +1 -1
  87. ultralytics/models/rtdetr/model.py +9 -9
  88. ultralytics/models/rtdetr/predict.py +22 -17
  89. ultralytics/models/rtdetr/train.py +20 -16
  90. ultralytics/models/rtdetr/val.py +79 -59
  91. ultralytics/models/sam/__init__.py +8 -2
  92. ultralytics/models/sam/amg.py +53 -38
  93. ultralytics/models/sam/build.py +29 -31
  94. ultralytics/models/sam/model.py +33 -38
  95. ultralytics/models/sam/modules/blocks.py +159 -182
  96. ultralytics/models/sam/modules/decoders.py +38 -47
  97. ultralytics/models/sam/modules/encoders.py +114 -133
  98. ultralytics/models/sam/modules/memory_attention.py +38 -31
  99. ultralytics/models/sam/modules/sam.py +114 -93
  100. ultralytics/models/sam/modules/tiny_encoder.py +268 -291
  101. ultralytics/models/sam/modules/transformer.py +59 -66
  102. ultralytics/models/sam/modules/utils.py +55 -72
  103. ultralytics/models/sam/predict.py +745 -341
  104. ultralytics/models/utils/loss.py +118 -107
  105. ultralytics/models/utils/ops.py +118 -71
  106. ultralytics/models/yolo/__init__.py +1 -1
  107. ultralytics/models/yolo/classify/predict.py +28 -26
  108. ultralytics/models/yolo/classify/train.py +50 -81
  109. ultralytics/models/yolo/classify/val.py +68 -61
  110. ultralytics/models/yolo/detect/predict.py +12 -15
  111. ultralytics/models/yolo/detect/train.py +56 -46
  112. ultralytics/models/yolo/detect/val.py +279 -223
  113. ultralytics/models/yolo/model.py +167 -86
  114. ultralytics/models/yolo/obb/predict.py +7 -11
  115. ultralytics/models/yolo/obb/train.py +23 -25
  116. ultralytics/models/yolo/obb/val.py +107 -99
  117. ultralytics/models/yolo/pose/__init__.py +1 -1
  118. ultralytics/models/yolo/pose/predict.py +12 -14
  119. ultralytics/models/yolo/pose/train.py +31 -69
  120. ultralytics/models/yolo/pose/val.py +119 -254
  121. ultralytics/models/yolo/segment/predict.py +21 -25
  122. ultralytics/models/yolo/segment/train.py +12 -66
  123. ultralytics/models/yolo/segment/val.py +126 -305
  124. ultralytics/models/yolo/world/train.py +53 -45
  125. ultralytics/models/yolo/world/train_world.py +51 -32
  126. ultralytics/models/yolo/yoloe/__init__.py +7 -7
  127. ultralytics/models/yolo/yoloe/predict.py +30 -37
  128. ultralytics/models/yolo/yoloe/train.py +89 -71
  129. ultralytics/models/yolo/yoloe/train_seg.py +15 -17
  130. ultralytics/models/yolo/yoloe/val.py +56 -41
  131. ultralytics/nn/__init__.py +9 -11
  132. ultralytics/nn/autobackend.py +179 -107
  133. ultralytics/nn/modules/__init__.py +67 -67
  134. ultralytics/nn/modules/activation.py +8 -7
  135. ultralytics/nn/modules/block.py +302 -323
  136. ultralytics/nn/modules/conv.py +61 -104
  137. ultralytics/nn/modules/head.py +488 -186
  138. ultralytics/nn/modules/transformer.py +183 -123
  139. ultralytics/nn/modules/utils.py +15 -20
  140. ultralytics/nn/tasks.py +327 -203
  141. ultralytics/nn/text_model.py +81 -65
  142. ultralytics/py.typed +1 -0
  143. ultralytics/solutions/__init__.py +12 -12
  144. ultralytics/solutions/ai_gym.py +19 -27
  145. ultralytics/solutions/analytics.py +36 -26
  146. ultralytics/solutions/config.py +29 -28
  147. ultralytics/solutions/distance_calculation.py +23 -24
  148. ultralytics/solutions/heatmap.py +17 -19
  149. ultralytics/solutions/instance_segmentation.py +21 -19
  150. ultralytics/solutions/object_blurrer.py +16 -17
  151. ultralytics/solutions/object_counter.py +48 -53
  152. ultralytics/solutions/object_cropper.py +22 -16
  153. ultralytics/solutions/parking_management.py +61 -58
  154. ultralytics/solutions/queue_management.py +19 -19
  155. ultralytics/solutions/region_counter.py +63 -50
  156. ultralytics/solutions/security_alarm.py +22 -25
  157. ultralytics/solutions/similarity_search.py +107 -60
  158. ultralytics/solutions/solutions.py +343 -262
  159. ultralytics/solutions/speed_estimation.py +35 -31
  160. ultralytics/solutions/streamlit_inference.py +104 -40
  161. ultralytics/solutions/templates/similarity-search.html +31 -24
  162. ultralytics/solutions/trackzone.py +24 -24
  163. ultralytics/solutions/vision_eye.py +11 -12
  164. ultralytics/trackers/__init__.py +1 -1
  165. ultralytics/trackers/basetrack.py +18 -27
  166. ultralytics/trackers/bot_sort.py +48 -39
  167. ultralytics/trackers/byte_tracker.py +94 -94
  168. ultralytics/trackers/track.py +7 -16
  169. ultralytics/trackers/utils/gmc.py +37 -69
  170. ultralytics/trackers/utils/kalman_filter.py +68 -76
  171. ultralytics/trackers/utils/matching.py +13 -17
  172. ultralytics/utils/__init__.py +251 -275
  173. ultralytics/utils/autobatch.py +19 -7
  174. ultralytics/utils/autodevice.py +68 -38
  175. ultralytics/utils/benchmarks.py +169 -130
  176. ultralytics/utils/callbacks/base.py +12 -13
  177. ultralytics/utils/callbacks/clearml.py +14 -15
  178. ultralytics/utils/callbacks/comet.py +139 -66
  179. ultralytics/utils/callbacks/dvc.py +19 -27
  180. ultralytics/utils/callbacks/hub.py +8 -6
  181. ultralytics/utils/callbacks/mlflow.py +6 -10
  182. ultralytics/utils/callbacks/neptune.py +11 -19
  183. ultralytics/utils/callbacks/platform.py +73 -0
  184. ultralytics/utils/callbacks/raytune.py +3 -4
  185. ultralytics/utils/callbacks/tensorboard.py +9 -12
  186. ultralytics/utils/callbacks/wb.py +33 -30
  187. ultralytics/utils/checks.py +163 -114
  188. ultralytics/utils/cpu.py +89 -0
  189. ultralytics/utils/dist.py +24 -20
  190. ultralytics/utils/downloads.py +176 -146
  191. ultralytics/utils/errors.py +11 -13
  192. ultralytics/utils/events.py +113 -0
  193. ultralytics/utils/export/__init__.py +7 -0
  194. ultralytics/utils/{export.py → export/engine.py} +81 -63
  195. ultralytics/utils/export/imx.py +294 -0
  196. ultralytics/utils/export/tensorflow.py +217 -0
  197. ultralytics/utils/files.py +33 -36
  198. ultralytics/utils/git.py +137 -0
  199. ultralytics/utils/instance.py +105 -120
  200. ultralytics/utils/logger.py +404 -0
  201. ultralytics/utils/loss.py +99 -61
  202. ultralytics/utils/metrics.py +649 -478
  203. ultralytics/utils/nms.py +337 -0
  204. ultralytics/utils/ops.py +263 -451
  205. ultralytics/utils/patches.py +70 -31
  206. ultralytics/utils/plotting.py +253 -223
  207. ultralytics/utils/tal.py +48 -61
  208. ultralytics/utils/torch_utils.py +244 -251
  209. ultralytics/utils/tqdm.py +438 -0
  210. ultralytics/utils/triton.py +22 -23
  211. ultralytics/utils/tuner.py +11 -10
  212. dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD +0 -272
  213. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/entry_points.txt +0 -0
  214. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/licenses/LICENSE +0 -0
  215. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,8 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
5
+ import asyncio
3
6
  import json
4
7
  import random
5
8
  import shutil
@@ -11,17 +14,17 @@ import cv2
11
14
  import numpy as np
12
15
  from PIL import Image
13
16
 
14
- from ultralytics.utils import DATASETS_DIR, LOGGER, NUM_THREADS, TQDM
17
+ from ultralytics.utils import ASSETS_URL, DATASETS_DIR, LOGGER, NUM_THREADS, TQDM, YAML
18
+ from ultralytics.utils.checks import check_file, check_requirements
15
19
  from ultralytics.utils.downloads import download, zip_directory
16
20
  from ultralytics.utils.files import increment_path
17
21
 
18
22
 
19
- def coco91_to_coco80_class():
20
- """
21
- Converts 91-index COCO class IDs to 80-index COCO class IDs.
23
+ def coco91_to_coco80_class() -> list[int]:
24
+ """Convert 91-index COCO class IDs to 80-index COCO class IDs.
22
25
 
23
26
  Returns:
24
- (list): A list of 91 class IDs where the index represents the 80-index class ID and the value is the
27
+ (list[int]): A list of 91 class IDs where the index represents the 80-index class ID and the value is the
25
28
  corresponding 91-index class ID.
26
29
  """
27
30
  return [
@@ -119,10 +122,11 @@ def coco91_to_coco80_class():
119
122
  ]
120
123
 
121
124
 
122
- def coco80_to_coco91_class():
123
- r"""
124
- Converts 80-index (val2014) to 91-index (paper).
125
- For details see https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/.
125
+ def coco80_to_coco91_class() -> list[int]:
126
+ r"""Convert 80-index (val2014) to 91-index (paper).
127
+
128
+ Returns:
129
+ (list[int]): A list of 80 class IDs where each value is the corresponding 91-index class ID.
126
130
 
127
131
  Examples:
128
132
  >>> import numpy as np
@@ -134,6 +138,9 @@ def coco80_to_coco91_class():
134
138
 
135
139
  Convert the COCO to darknet format
136
140
  >>> x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)]
141
+
142
+ References:
143
+ https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
137
144
  """
138
145
  return [
139
146
  1,
@@ -220,15 +227,14 @@ def coco80_to_coco91_class():
220
227
 
221
228
 
222
229
  def convert_coco(
223
- labels_dir="../coco/annotations/",
224
- save_dir="coco_converted/",
225
- use_segments=False,
226
- use_keypoints=False,
227
- cls91to80=True,
228
- lvis=False,
230
+ labels_dir: str = "../coco/annotations/",
231
+ save_dir: str = "coco_converted/",
232
+ use_segments: bool = False,
233
+ use_keypoints: bool = False,
234
+ cls91to80: bool = True,
235
+ lvis: bool = False,
229
236
  ):
230
- """
231
- Converts COCO dataset annotations to a YOLO annotation format suitable for training YOLO models.
237
+ """Convert COCO dataset annotations to a YOLO annotation format suitable for training YOLO models.
232
238
 
233
239
  Args:
234
240
  labels_dir (str, optional): Path to directory containing COCO dataset annotation files.
@@ -242,19 +248,10 @@ def convert_coco(
242
248
  >>> from ultralytics.data.converter import convert_coco
243
249
 
244
250
  Convert COCO annotations to YOLO format
245
- >>> convert_coco("../datasets/coco/annotations/", use_segments=True, use_keypoints=False, cls91to80=False)
251
+ >>> convert_coco("coco/annotations/", use_segments=True, use_keypoints=False, cls91to80=False)
246
252
 
247
253
  Convert LVIS annotations to YOLO format
248
- >>> convert_coco(
249
- >>> "../datasets/lvis/annotations/",
250
- ... use_segments=True,
251
- ... use_keypoints=False,
252
- ... cls91to80=False,
253
- ... lvis=True
254
- ... )
255
-
256
- Output:
257
- Generates output files in the specified output directory.
254
+ >>> convert_coco("lvis/annotations/", use_segments=True, use_keypoints=False, cls91to80=False, lvis=True)
258
255
  """
259
256
  # Create dataset directory
260
257
  save_dir = increment_path(save_dir) # increment if save directory already exists
@@ -308,7 +305,7 @@ def convert_coco(
308
305
  continue
309
306
 
310
307
  cls = coco80[ann["category_id"] - 1] if cls91to80 else ann["category_id"] - 1 # class
311
- box = [cls] + box.tolist()
308
+ box = [cls, *box.tolist()]
312
309
  if box not in bboxes:
313
310
  bboxes.append(box)
314
311
  if use_segments and ann.get("segmentation") is not None:
@@ -321,7 +318,7 @@ def convert_coco(
321
318
  else:
322
319
  s = [j for i in ann["segmentation"] for j in i] # all segments concatenated
323
320
  s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist()
324
- s = [cls] + s
321
+ s = [cls, *s]
325
322
  segments.append(s)
326
323
  if use_keypoints and ann.get("keypoints") is not None:
327
324
  keypoints.append(
@@ -347,12 +344,11 @@ def convert_coco(
347
344
  LOGGER.info(f"{'LVIS' if lvis else 'COCO'} data converted successfully.\nResults saved to {save_dir.resolve()}")
348
345
 
349
346
 
350
- def convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes):
351
- """
352
- Converts a dataset of segmentation mask images to the YOLO segmentation format.
347
+ def convert_segment_masks_to_yolo_seg(masks_dir: str, output_dir: str, classes: int):
348
+ """Convert a dataset of segmentation mask images to the YOLO segmentation format.
353
349
 
354
- This function takes the directory containing the binary format mask images and converts them into YOLO segmentation format.
355
- The converted masks are saved in the specified output directory.
350
+ This function takes the directory containing the binary format mask images and converts them into YOLO segmentation
351
+ format. The converted masks are saved in the specified output directory.
356
352
 
357
353
  Args:
358
354
  masks_dir (str): The path to the directory where all mask images (png, jpg) are stored.
@@ -424,8 +420,7 @@ def convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes):
424
420
 
425
421
 
426
422
  def convert_dota_to_yolo_obb(dota_root_path: str):
427
- """
428
- Converts DOTA dataset annotations to YOLO OBB (Oriented Bounding Box) format.
423
+ """Convert DOTA dataset annotations to YOLO OBB (Oriented Bounding Box) format.
429
424
 
430
425
  The function processes images in the 'train' and 'val' folders of the DOTA dataset. For each image, it reads the
431
426
  associated label from the original labels directory and writes new labels in YOLO OBB format to a new directory.
@@ -479,8 +474,8 @@ def convert_dota_to_yolo_obb(dota_root_path: str):
479
474
  "helipad": 17,
480
475
  }
481
476
 
482
- def convert_label(image_name, image_width, image_height, orig_label_dir, save_dir):
483
- """Converts a single image's DOTA annotation to YOLO OBB format and saves it to a specified directory."""
477
+ def convert_label(image_name: str, image_width: int, image_height: int, orig_label_dir: Path, save_dir: Path):
478
+ """Convert a single image's DOTA annotation to YOLO OBB format and save it to a specified directory."""
484
479
  orig_label_path = orig_label_dir / f"{image_name}.txt"
485
480
  save_path = save_dir / f"{image_name}.txt"
486
481
 
@@ -499,7 +494,7 @@ def convert_dota_to_yolo_obb(dota_root_path: str):
499
494
  formatted_coords = [f"{coord:.6g}" for coord in normalized_coords]
500
495
  g.write(f"{class_idx} {' '.join(formatted_coords)}\n")
501
496
 
502
- for phase in ["train", "val"]:
497
+ for phase in {"train", "val"}:
503
498
  image_dir = dota_root_path / "images" / phase
504
499
  orig_label_dir = dota_root_path / "labels" / f"{phase}_original"
505
500
  save_dir = dota_root_path / "labels" / phase
@@ -516,32 +511,33 @@ def convert_dota_to_yolo_obb(dota_root_path: str):
516
511
  convert_label(image_name_without_ext, w, h, orig_label_dir, save_dir)
517
512
 
518
513
 
519
- def min_index(arr1, arr2):
520
- """
521
- Find a pair of indexes with the shortest distance between two arrays of 2D points.
514
+ def min_index(arr1: np.ndarray, arr2: np.ndarray):
515
+ """Find a pair of indexes with the shortest distance between two arrays of 2D points.
522
516
 
523
517
  Args:
524
518
  arr1 (np.ndarray): A NumPy array of shape (N, 2) representing N 2D points.
525
519
  arr2 (np.ndarray): A NumPy array of shape (M, 2) representing M 2D points.
526
520
 
527
521
  Returns:
528
- (tuple): A tuple containing the indexes of the points with the shortest distance in arr1 and arr2 respectively.
522
+ idx1 (int): Index of the point in arr1 with the shortest distance.
523
+ idx2 (int): Index of the point in arr2 with the shortest distance.
529
524
  """
530
525
  dis = ((arr1[:, None, :] - arr2[None, :, :]) ** 2).sum(-1)
531
526
  return np.unravel_index(np.argmin(dis, axis=None), dis.shape)
532
527
 
533
528
 
534
- def merge_multi_segment(segments):
535
- """
536
- Merge multiple segments into one list by connecting the coordinates with the minimum distance between each segment.
529
+ def merge_multi_segment(segments: list[list]):
530
+ """Merge multiple segments into one list by connecting the coordinates with the minimum distance between each
531
+ segment.
532
+
537
533
  This function connects these coordinates with a thin line to merge all segments into one.
538
534
 
539
535
  Args:
540
- segments (List[List]): Original segmentations in COCO's JSON file.
541
- Each element is a list of coordinates, like [segmentation1, segmentation2,...].
536
+ segments (list[list]): Original segmentations in COCO's JSON file. Each element is a list of coordinates, like
537
+ [segmentation1, segmentation2,...].
542
538
 
543
539
  Returns:
544
- s (List[np.ndarray]): A list of connected segments represented as NumPy arrays.
540
+ s (list[np.ndarray]): A list of connected segments represented as NumPy arrays.
545
541
  """
546
542
  s = []
547
543
  segments = [np.array(i).reshape(-1, 2) for i in segments]
@@ -581,17 +577,16 @@ def merge_multi_segment(segments):
581
577
  return s
582
578
 
583
579
 
584
- def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt", device=None):
585
- """
586
- Converts existing object detection dataset (bounding boxes) to segmentation dataset or oriented bounding box (OBB)
587
- in YOLO format. Generates segmentation data using SAM auto-annotator as needed.
580
+ def yolo_bbox2segment(im_dir: str | Path, save_dir: str | Path | None = None, sam_model: str = "sam_b.pt", device=None):
581
+ """Convert existing object detection dataset (bounding boxes) to segmentation dataset or oriented bounding box (OBB)
582
+ in YOLO format. Generate segmentation data using SAM auto-annotator as needed.
588
583
 
589
584
  Args:
590
585
  im_dir (str | Path): Path to image directory to convert.
591
- save_dir (str | Path): Path to save the generated labels, labels will be saved
592
- into `labels-segment` in the same directory level of `im_dir` if save_dir is None.
586
+ save_dir (str | Path, optional): Path to save the generated labels, labels will be saved into `labels-segment`
587
+ in the same directory level of `im_dir` if save_dir is None.
593
588
  sam_model (str): Segmentation model to use for intermediate segmentation data.
594
- device (int | str): The specific device to run SAM models.
589
+ device (int | str, optional): The specific device to run SAM models.
595
590
 
596
591
  Notes:
597
592
  The input directory structure assumed for dataset:
@@ -610,7 +605,7 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt", device=None):
610
605
  from ultralytics.utils.ops import xywh2xyxy
611
606
 
612
607
  # NOTE: add placeholder to pass class index check
613
- dataset = YOLODataset(im_dir, data=dict(names=list(range(1000))))
608
+ dataset = YOLODataset(im_dir, data=dict(names=list(range(1000)), channels=3))
614
609
  if len(dataset.labels[0]["segments"]) > 0: # if it's segment data
615
610
  LOGGER.info("Segmentation labels detected, no need to generate new ones!")
616
611
  return
@@ -646,12 +641,11 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt", device=None):
646
641
 
647
642
 
648
643
  def create_synthetic_coco_dataset():
649
- """
650
- Creates a synthetic COCO dataset with random images based on filenames from label lists.
644
+ """Create a synthetic COCO dataset with random images based on filenames from label lists.
651
645
 
652
- This function downloads COCO labels, reads image filenames from label list files,
653
- creates synthetic images for train2017 and val2017 subsets, and organizes
654
- them in the COCO dataset structure. It uses multithreading to generate images efficiently.
646
+ This function downloads COCO labels, reads image filenames from label list files, creates synthetic images for
647
+ train2017 and val2017 subsets, and organizes them in the COCO dataset structure. It uses multithreading to generate
648
+ images efficiently.
655
649
 
656
650
  Examples:
657
651
  >>> from ultralytics.data.converter import create_synthetic_coco_dataset
@@ -664,8 +658,8 @@ def create_synthetic_coco_dataset():
664
658
  - Reads image filenames from train2017.txt and val2017.txt files.
665
659
  """
666
660
 
667
- def create_synthetic_image(image_file):
668
- """Generates synthetic images with random sizes and colors for dataset augmentation or testing purposes."""
661
+ def create_synthetic_image(image_file: Path):
662
+ """Generate synthetic images with random sizes and colors for dataset augmentation or testing purposes."""
669
663
  if not image_file.exists():
670
664
  size = (random.randint(480, 640), random.randint(480, 640))
671
665
  Image.new(
@@ -676,14 +670,12 @@ def create_synthetic_coco_dataset():
676
670
 
677
671
  # Download labels
678
672
  dir = DATASETS_DIR / "coco"
679
- url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
680
- label_zip = "coco2017labels-segments.zip"
681
- download([url + label_zip], dir=dir.parent)
673
+ download([f"{ASSETS_URL}/coco2017labels-segments.zip"], dir=dir.parent)
682
674
 
683
675
  # Create synthetic images
684
676
  shutil.rmtree(dir / "labels" / "test2017", ignore_errors=True) # Remove test2017 directory as not needed
685
677
  with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
686
- for subset in ["train2017", "val2017"]:
678
+ for subset in {"train2017", "val2017"}:
687
679
  subset_dir = dir / "images" / subset
688
680
  subset_dir.mkdir(parents=True, exist_ok=True)
689
681
 
@@ -703,12 +695,11 @@ def create_synthetic_coco_dataset():
703
695
  LOGGER.info("Synthetic COCO dataset created successfully.")
704
696
 
705
697
 
706
- def convert_to_multispectral(path, n_channels=10, replace=False, zip=False):
707
- """
708
- Convert RGB images to multispectral images by interpolating across wavelength bands.
698
+ def convert_to_multispectral(path: str | Path, n_channels: int = 10, replace: bool = False, zip: bool = False):
699
+ """Convert RGB images to multispectral images by interpolating across wavelength bands.
709
700
 
710
- This function takes RGB images and interpolates them to create multispectral images with a specified number
711
- of channels. It can process either a single image or a directory of images.
701
+ This function takes RGB images and interpolates them to create multispectral images with a specified number of
702
+ channels. It can process either a single image or a directory of images.
712
703
 
713
704
  Args:
714
705
  path (str | Path): Path to an image file or directory containing images to convert.
@@ -717,10 +708,11 @@ def convert_to_multispectral(path, n_channels=10, replace=False, zip=False):
717
708
  zip (bool): Whether to zip the converted images into a zip file.
718
709
 
719
710
  Examples:
720
- >>> # Convert a single image
711
+ Convert a single image
721
712
  >>> convert_to_multispectral("path/to/image.jpg", n_channels=10)
722
- >>> # Convert a dataset
723
- >>> convert_to_multispectral("../datasets/coco8", n_channels=10)
713
+
714
+ Convert a dataset
715
+ >>> convert_to_multispectral("coco8", n_channels=10)
724
716
  """
725
717
  from scipy.interpolate import interp1d
726
718
 
@@ -729,7 +721,7 @@ def convert_to_multispectral(path, n_channels=10, replace=False, zip=False):
729
721
  path = Path(path)
730
722
  if path.is_dir():
731
723
  # Process directory
732
- im_files = sum([list(path.rglob(f"*.{ext}")) for ext in (IMG_FORMATS - {"tif", "tiff"})], [])
724
+ im_files = [f for ext in (IMG_FORMATS - {"tif", "tiff"}) for f in path.rglob(f"*.{ext}")]
733
725
  for im_path in im_files:
734
726
  try:
735
727
  convert_to_multispectral(im_path, n_channels)
@@ -752,3 +744,112 @@ def convert_to_multispectral(path, n_channels=10, replace=False, zip=False):
752
744
  multispectral = f(target_wavelengths)
753
745
  cv2.imwritemulti(str(output_path), np.clip(multispectral, 0, 255).astype(np.uint8).transpose(2, 0, 1))
754
746
  LOGGER.info(f"Converted {output_path}")
747
+
748
+
749
+ async def convert_ndjson_to_yolo(ndjson_path: str | Path, output_path: str | Path | None = None) -> Path:
750
+ """Convert NDJSON dataset format to Ultralytics YOLO11 dataset structure.
751
+
752
+ This function converts datasets stored in NDJSON (Newline Delimited JSON) format to the standard YOLO format with
753
+ separate directories for images and labels. It supports parallel processing for efficient conversion of large
754
+ datasets and can download images from URLs if they don't exist locally.
755
+
756
+ The NDJSON format consists of:
757
+ - First line: Dataset metadata with class names and configuration
758
+ - Subsequent lines: Individual image records with annotations and optional URLs
759
+
760
+ Args:
761
+ ndjson_path (Union[str, Path]): Path to the input NDJSON file containing dataset information.
762
+ output_path (Optional[Union[str, Path]], optional): Directory where the converted YOLO dataset will be saved. If
763
+ None, uses the parent directory of the NDJSON file. Defaults to None.
764
+
765
+ Returns:
766
+ (Path): Path to the generated data.yaml file that can be used for YOLO training.
767
+
768
+ Examples:
769
+ Convert a local NDJSON file:
770
+ >>> yaml_path = convert_ndjson_to_yolo("dataset.ndjson")
771
+ >>> print(f"Dataset converted to: {yaml_path}")
772
+
773
+ Convert with custom output directory:
774
+ >>> yaml_path = convert_ndjson_to_yolo("dataset.ndjson", output_path="./converted_datasets")
775
+
776
+ Use with YOLO training
777
+ >>> from ultralytics import YOLO
778
+ >>> model = YOLO("yolo11n.pt")
779
+ >>> model.train(data="https://github.com/ultralytics/assets/releases/download/v0.0.0/coco8-ndjson.ndjson")
780
+ """
781
+ check_requirements("aiohttp")
782
+ import aiohttp
783
+
784
+ ndjson_path = Path(check_file(ndjson_path))
785
+ output_path = Path(output_path or DATASETS_DIR)
786
+ with open(ndjson_path) as f:
787
+ lines = [json.loads(line.strip()) for line in f if line.strip()]
788
+
789
+ dataset_record, image_records = lines[0], lines[1:]
790
+ dataset_dir = output_path / ndjson_path.stem
791
+ splits = {record["split"] for record in image_records}
792
+
793
+ # Create directories and prepare YAML structure
794
+ dataset_dir.mkdir(parents=True, exist_ok=True)
795
+ data_yaml = dict(dataset_record)
796
+ data_yaml["names"] = {int(k): v for k, v in dataset_record.get("class_names", {}).items()}
797
+ data_yaml.pop("class_names")
798
+
799
+ for split in sorted(splits):
800
+ (dataset_dir / "images" / split).mkdir(parents=True, exist_ok=True)
801
+ (dataset_dir / "labels" / split).mkdir(parents=True, exist_ok=True)
802
+ data_yaml[split] = f"images/{split}"
803
+
804
+ async def process_record(session, semaphore, record):
805
+ """Process single image record with async session."""
806
+ async with semaphore:
807
+ split, original_name = record["split"], record["file"]
808
+ label_path = dataset_dir / "labels" / split / f"{Path(original_name).stem}.txt"
809
+ image_path = dataset_dir / "images" / split / original_name
810
+
811
+ annotations = record.get("annotations", {})
812
+ lines_to_write = []
813
+ for key in annotations.keys():
814
+ lines_to_write = [" ".join(map(str, item)) for item in annotations[key]]
815
+ break
816
+ if "classification" in annotations:
817
+ lines_to_write = [str(cls) for cls in annotations["classification"]]
818
+
819
+ label_path.write_text("\n".join(lines_to_write) + "\n" if lines_to_write else "")
820
+
821
+ if http_url := record.get("url"):
822
+ if not image_path.exists():
823
+ try:
824
+ async with session.get(http_url, timeout=aiohttp.ClientTimeout(total=30)) as response:
825
+ response.raise_for_status()
826
+ with open(image_path, "wb") as f:
827
+ async for chunk in response.content.iter_chunked(8192):
828
+ f.write(chunk)
829
+ return True
830
+ except Exception as e:
831
+ LOGGER.warning(f"Failed to download {http_url}: {e}")
832
+ return False
833
+ return True
834
+
835
+ # Process all images with async downloads
836
+ semaphore = asyncio.Semaphore(64)
837
+ async with aiohttp.ClientSession() as session:
838
+ pbar = TQDM(
839
+ total=len(image_records),
840
+ desc=f"Converting {ndjson_path.name} → {dataset_dir} ({len(image_records)} images)",
841
+ )
842
+
843
+ async def tracked_process(record):
844
+ result = await process_record(session, semaphore, record)
845
+ pbar.update(1)
846
+ return result
847
+
848
+ await asyncio.gather(*[tracked_process(record) for record in image_records])
849
+ pbar.close()
850
+
851
+ # Write data.yaml
852
+ yaml_path = dataset_dir / "data.yaml"
853
+ YAML.save(yaml_path, data_yaml)
854
+
855
+ return yaml_path