segment-everything 0.1.0__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 (145) hide show
  1. segment_everything/__init__.py +5 -0
  2. segment_everything/augmentation/albumentations_helper.py +0 -0
  3. segment_everything/detect_and_segment.py +131 -0
  4. segment_everything/napari_helper.py +15 -0
  5. segment_everything/prompt_generator.py +188 -0
  6. segment_everything/py.typed +5 -0
  7. segment_everything/stacked_label_dataset.py +113 -0
  8. segment_everything/stacked_labels.py +428 -0
  9. segment_everything/vendored/PromptGuidedDecoder/Prompt_guided_Mask_Decoder.pt +0 -0
  10. segment_everything/vendored/__init__.py +5 -0
  11. segment_everything/vendored/dice.py +158 -0
  12. segment_everything/vendored/efficientvit/__init__.py +0 -0
  13. segment_everything/vendored/efficientvit/apps/__init__.py +0 -0
  14. segment_everything/vendored/efficientvit/apps/data_provider/__init__.py +7 -0
  15. segment_everything/vendored/efficientvit/apps/data_provider/augment/__init__.py +6 -0
  16. segment_everything/vendored/efficientvit/apps/data_provider/augment/bbox.py +30 -0
  17. segment_everything/vendored/efficientvit/apps/data_provider/augment/color_aug.py +78 -0
  18. segment_everything/vendored/efficientvit/apps/data_provider/base.py +254 -0
  19. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/__init__.py +6 -0
  20. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_loader.py +1538 -0
  21. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_worker.py +357 -0
  22. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/controller.py +100 -0
  23. segment_everything/vendored/efficientvit/apps/setup.py +150 -0
  24. segment_everything/vendored/efficientvit/apps/trainer/__init__.py +6 -0
  25. segment_everything/vendored/efficientvit/apps/trainer/base.py +318 -0
  26. segment_everything/vendored/efficientvit/apps/trainer/run_config.py +129 -0
  27. segment_everything/vendored/efficientvit/apps/utils/__init__.py +12 -0
  28. segment_everything/vendored/efficientvit/apps/utils/dist.py +32 -0
  29. segment_everything/vendored/efficientvit/apps/utils/ema.py +52 -0
  30. segment_everything/vendored/efficientvit/apps/utils/export.py +45 -0
  31. segment_everything/vendored/efficientvit/apps/utils/init.py +66 -0
  32. segment_everything/vendored/efficientvit/apps/utils/lr.py +52 -0
  33. segment_everything/vendored/efficientvit/apps/utils/metric.py +43 -0
  34. segment_everything/vendored/efficientvit/apps/utils/misc.py +101 -0
  35. segment_everything/vendored/efficientvit/apps/utils/opt.py +28 -0
  36. segment_everything/vendored/efficientvit/cls_model_zoo.py +79 -0
  37. segment_everything/vendored/efficientvit/clscore/__init__.py +0 -0
  38. segment_everything/vendored/efficientvit/clscore/data_provider/__init__.py +5 -0
  39. segment_everything/vendored/efficientvit/clscore/data_provider/imagenet.py +142 -0
  40. segment_everything/vendored/efficientvit/clscore/trainer/__init__.py +6 -0
  41. segment_everything/vendored/efficientvit/clscore/trainer/cls_run_config.py +18 -0
  42. segment_everything/vendored/efficientvit/clscore/trainer/cls_trainer.py +265 -0
  43. segment_everything/vendored/efficientvit/clscore/trainer/utils/__init__.py +7 -0
  44. segment_everything/vendored/efficientvit/clscore/trainer/utils/label_smooth.py +18 -0
  45. segment_everything/vendored/efficientvit/clscore/trainer/utils/metric.py +23 -0
  46. segment_everything/vendored/efficientvit/clscore/trainer/utils/mixup.py +67 -0
  47. segment_everything/vendored/efficientvit/models/__init__.py +0 -0
  48. segment_everything/vendored/efficientvit/models/efficientvit/__init__.py +8 -0
  49. segment_everything/vendored/efficientvit/models/efficientvit/backbone.py +380 -0
  50. segment_everything/vendored/efficientvit/models/efficientvit/cls.py +188 -0
  51. segment_everything/vendored/efficientvit/models/efficientvit/sam.py +181 -0
  52. segment_everything/vendored/efficientvit/models/efficientvit/seg.py +373 -0
  53. segment_everything/vendored/efficientvit/models/nn/__init__.py +8 -0
  54. segment_everything/vendored/efficientvit/models/nn/act.py +30 -0
  55. segment_everything/vendored/efficientvit/models/nn/drop.py +104 -0
  56. segment_everything/vendored/efficientvit/models/nn/norm.py +164 -0
  57. segment_everything/vendored/efficientvit/models/nn/ops.py +597 -0
  58. segment_everything/vendored/efficientvit/models/utils/__init__.py +7 -0
  59. segment_everything/vendored/efficientvit/models/utils/list.py +53 -0
  60. segment_everything/vendored/efficientvit/models/utils/network.py +73 -0
  61. segment_everything/vendored/efficientvit/models/utils/random.py +65 -0
  62. segment_everything/vendored/efficientvit/sam_model_zoo.py +45 -0
  63. segment_everything/vendored/efficientvit/seg_model_zoo.py +70 -0
  64. segment_everything/vendored/get_object_aware.py +26 -0
  65. segment_everything/vendored/mobilesamv2/__init__.py +16 -0
  66. segment_everything/vendored/mobilesamv2/automatic_mask_generator.py +415 -0
  67. segment_everything/vendored/mobilesamv2/build_sam.py +246 -0
  68. segment_everything/vendored/mobilesamv2/modeling/__init__.py +11 -0
  69. segment_everything/vendored/mobilesamv2/modeling/common.py +43 -0
  70. segment_everything/vendored/mobilesamv2/modeling/image_encoder.py +394 -0
  71. segment_everything/vendored/mobilesamv2/modeling/mask_decoder.py +213 -0
  72. segment_everything/vendored/mobilesamv2/modeling/prompt_encoder.py +217 -0
  73. segment_everything/vendored/mobilesamv2/modeling/sam.py +203 -0
  74. segment_everything/vendored/mobilesamv2/modeling/transformer.py +240 -0
  75. segment_everything/vendored/mobilesamv2/predictor.py +384 -0
  76. segment_everything/vendored/mobilesamv2/utils/__init__.py +5 -0
  77. segment_everything/vendored/mobilesamv2/utils/amg.py +347 -0
  78. segment_everything/vendored/mobilesamv2/utils/onnx.py +144 -0
  79. segment_everything/vendored/mobilesamv2/utils/transforms.py +103 -0
  80. segment_everything/vendored/object_detection/__init__.py +0 -0
  81. segment_everything/vendored/object_detection/ultralytics/__init__.py +5 -0
  82. segment_everything/vendored/object_detection/ultralytics/nn/__init__.py +9 -0
  83. segment_everything/vendored/object_detection/ultralytics/nn/autobackend.py +658 -0
  84. segment_everything/vendored/object_detection/ultralytics/nn/autoshape.py +397 -0
  85. segment_everything/vendored/object_detection/ultralytics/nn/modules/__init__.py +110 -0
  86. segment_everything/vendored/object_detection/ultralytics/nn/modules/block.py +304 -0
  87. segment_everything/vendored/object_detection/ultralytics/nn/modules/conv.py +297 -0
  88. segment_everything/vendored/object_detection/ultralytics/nn/modules/head.py +468 -0
  89. segment_everything/vendored/object_detection/ultralytics/nn/modules/transformer.py +378 -0
  90. segment_everything/vendored/object_detection/ultralytics/nn/modules/utils.py +78 -0
  91. segment_everything/vendored/object_detection/ultralytics/nn/tasks.py +1049 -0
  92. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/__init__.py +6 -0
  93. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/model.py +104 -0
  94. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/predict.py +95 -0
  95. segment_everything/vendored/object_detection/ultralytics/yolo/__init__.py +5 -0
  96. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/__init__.py +588 -0
  97. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/default.yaml +117 -0
  98. segment_everything/vendored/object_detection/ultralytics/yolo/data/__init__.py +9 -0
  99. segment_everything/vendored/object_detection/ultralytics/yolo/data/annotator.py +53 -0
  100. segment_everything/vendored/object_detection/ultralytics/yolo/data/augment.py +899 -0
  101. segment_everything/vendored/object_detection/ultralytics/yolo/data/base.py +286 -0
  102. segment_everything/vendored/object_detection/ultralytics/yolo/data/build.py +213 -0
  103. segment_everything/vendored/object_detection/ultralytics/yolo/data/converter.py +358 -0
  104. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/__init__.py +0 -0
  105. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/stream_loaders.py +459 -0
  106. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset.py +274 -0
  107. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset_wrappers.py +53 -0
  108. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/download_weights.sh +18 -0
  109. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco.sh +60 -0
  110. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco128.sh +17 -0
  111. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_imagenet.sh +51 -0
  112. segment_everything/vendored/object_detection/ultralytics/yolo/data/utils.py +716 -0
  113. segment_everything/vendored/object_detection/ultralytics/yolo/engine/__init__.py +0 -0
  114. segment_everything/vendored/object_detection/ultralytics/yolo/engine/exporter.py +1214 -0
  115. segment_everything/vendored/object_detection/ultralytics/yolo/engine/model.py +641 -0
  116. segment_everything/vendored/object_detection/ultralytics/yolo/engine/predictor.py +461 -0
  117. segment_everything/vendored/object_detection/ultralytics/yolo/engine/results.py +741 -0
  118. segment_everything/vendored/object_detection/ultralytics/yolo/utils/__init__.py +893 -0
  119. segment_everything/vendored/object_detection/ultralytics/yolo/utils/autobatch.py +108 -0
  120. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/__init__.py +5 -0
  121. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/base.py +212 -0
  122. segment_everything/vendored/object_detection/ultralytics/yolo/utils/checks.py +547 -0
  123. segment_everything/vendored/object_detection/ultralytics/yolo/utils/dist.py +67 -0
  124. segment_everything/vendored/object_detection/ultralytics/yolo/utils/downloads.py +353 -0
  125. segment_everything/vendored/object_detection/ultralytics/yolo/utils/errors.py +12 -0
  126. segment_everything/vendored/object_detection/ultralytics/yolo/utils/files.py +100 -0
  127. segment_everything/vendored/object_detection/ultralytics/yolo/utils/instance.py +391 -0
  128. segment_everything/vendored/object_detection/ultralytics/yolo/utils/loss.py +579 -0
  129. segment_everything/vendored/object_detection/ultralytics/yolo/utils/metrics.py +1189 -0
  130. segment_everything/vendored/object_detection/ultralytics/yolo/utils/ops.py +870 -0
  131. segment_everything/vendored/object_detection/ultralytics/yolo/utils/patches.py +45 -0
  132. segment_everything/vendored/object_detection/ultralytics/yolo/utils/plotting.py +767 -0
  133. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tal.py +276 -0
  134. segment_everything/vendored/object_detection/ultralytics/yolo/utils/torch_utils.py +684 -0
  135. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tuner.py +54 -0
  136. segment_everything/vendored/object_detection/ultralytics/yolo/v8/__init__.py +5 -0
  137. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/__init__.py +5 -0
  138. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/predict.py +69 -0
  139. segment_everything/vendored/tinyvit/__init__.py +2 -0
  140. segment_everything/vendored/tinyvit/tiny_vit.py +867 -0
  141. segment_everything/weights_helper.py +124 -0
  142. segment_everything-0.1.0.dist-info/METADATA +53 -0
  143. segment_everything-0.1.0.dist-info/RECORD +145 -0
  144. segment_everything-0.1.0.dist-info/WHEEL +4 -0
  145. segment_everything-0.1.0.dist-info/licenses/LICENSE +28 -0
@@ -0,0 +1,716 @@
1
+ # Ultralytics YOLO 🚀, AGPL-3.0 license
2
+
3
+ import contextlib
4
+ import hashlib
5
+ import json
6
+ import os
7
+ import subprocess
8
+ import time
9
+ import zipfile
10
+ from multiprocessing.pool import ThreadPool
11
+ from pathlib import Path
12
+ from tarfile import is_tarfile
13
+
14
+ import cv2
15
+ import numpy as np
16
+ from PIL import ExifTags, Image, ImageOps
17
+ from tqdm import tqdm
18
+
19
+ from ...nn.autobackend import check_class_names
20
+ from ..utils import (
21
+ DATASETS_DIR,
22
+ LOGGER,
23
+ NUM_THREADS,
24
+ ROOT,
25
+ SETTINGS_YAML,
26
+ clean_url,
27
+ colorstr,
28
+ emojis,
29
+ yaml_load,
30
+ )
31
+ from ..utils.checks import check_file, check_font, is_ascii
32
+ from ..utils.downloads import download, safe_download, unzip_file
33
+ from ..utils.ops import segments2boxes
34
+
35
+ HELP_URL = (
36
+ "See https://docs.ultralytics.com/yolov5/tutorials/train_custom_data"
37
+ )
38
+ IMG_FORMATS = (
39
+ "bmp",
40
+ "dng",
41
+ "jpeg",
42
+ "jpg",
43
+ "mpo",
44
+ "png",
45
+ "tif",
46
+ "tiff",
47
+ "webp",
48
+ "pfm",
49
+ ) # image suffixes
50
+ VID_FORMATS = (
51
+ "asf",
52
+ "avi",
53
+ "gif",
54
+ "m4v",
55
+ "mkv",
56
+ "mov",
57
+ "mp4",
58
+ "mpeg",
59
+ "mpg",
60
+ "ts",
61
+ "wmv",
62
+ "webm",
63
+ ) # video suffixes
64
+ PIN_MEMORY = (
65
+ str(os.getenv("PIN_MEMORY", True)).lower() == "true"
66
+ ) # global pin_memory for dataloaders
67
+ IMAGENET_MEAN = 0.485, 0.456, 0.406 # RGB mean
68
+ IMAGENET_STD = 0.229, 0.224, 0.225 # RGB standard deviation
69
+
70
+ # Get orientation exif tag
71
+ for orientation in ExifTags.TAGS.keys():
72
+ if ExifTags.TAGS[orientation] == "Orientation":
73
+ break
74
+
75
+
76
+ def img2label_paths(img_paths):
77
+ """Define label paths as a function of image paths."""
78
+ sa, sb = (
79
+ f"{os.sep}images{os.sep}",
80
+ f"{os.sep}labels{os.sep}",
81
+ ) # /images/, /labels/ substrings
82
+ return [
83
+ sb.join(x.rsplit(sa, 1)).rsplit(".", 1)[0] + ".txt" for x in img_paths
84
+ ]
85
+
86
+
87
+ def get_hash(paths):
88
+ """Returns a single hash value of a list of paths (files or dirs)."""
89
+ size = sum(os.path.getsize(p) for p in paths if os.path.exists(p)) # sizes
90
+ h = hashlib.sha256(str(size).encode()) # hash sizes
91
+ h.update("".join(paths).encode()) # hash paths
92
+ return h.hexdigest() # return hash
93
+
94
+
95
+ def exif_size(img):
96
+ """Returns exif-corrected PIL size."""
97
+ s = img.size # (width, height)
98
+ with contextlib.suppress(Exception):
99
+ rotation = dict(img._getexif().items())[orientation]
100
+ if rotation in [6, 8]: # rotation 270 or 90
101
+ s = (s[1], s[0])
102
+ return s
103
+
104
+
105
+ def verify_image_label(args):
106
+ """Verify one image-label pair."""
107
+ im_file, lb_file, prefix, keypoint, num_cls, nkpt, ndim = args
108
+ # Number (missing, found, empty, corrupt), message, segments, keypoints
109
+ nm, nf, ne, nc, msg, segments, keypoints = 0, 0, 0, 0, "", [], None
110
+ try:
111
+ # Verify images
112
+ im = Image.open(im_file)
113
+ im.verify() # PIL verify
114
+ shape = exif_size(im) # image size
115
+ shape = (shape[1], shape[0]) # hw
116
+ assert (shape[0] > 9) & (
117
+ shape[1] > 9
118
+ ), f"image size {shape} <10 pixels"
119
+ assert (
120
+ im.format.lower() in IMG_FORMATS
121
+ ), f"invalid image format {im.format}"
122
+ if im.format.lower() in ("jpg", "jpeg"):
123
+ with open(im_file, "rb") as f:
124
+ f.seek(-2, 2)
125
+ if f.read() != b"\xff\xd9": # corrupt JPEG
126
+ ImageOps.exif_transpose(Image.open(im_file)).save(
127
+ im_file, "JPEG", subsampling=0, quality=100
128
+ )
129
+ msg = f"{prefix}WARNING ⚠️ {im_file}: corrupt JPEG restored and saved"
130
+
131
+ # Verify labels
132
+ if os.path.isfile(lb_file):
133
+ nf = 1 # label found
134
+ with open(lb_file) as f:
135
+ lb = [
136
+ x.split() for x in f.read().strip().splitlines() if len(x)
137
+ ]
138
+ if any(len(x) > 6 for x in lb) and (
139
+ not keypoint
140
+ ): # is segment
141
+ classes = np.array([x[0] for x in lb], dtype=np.float32)
142
+ segments = [
143
+ np.array(x[1:], dtype=np.float32).reshape(-1, 2)
144
+ for x in lb
145
+ ] # (cls, xy1...)
146
+ lb = np.concatenate(
147
+ (classes.reshape(-1, 1), segments2boxes(segments)), 1
148
+ ) # (cls, xywh)
149
+ lb = np.array(lb, dtype=np.float32)
150
+ nl = len(lb)
151
+ if nl:
152
+ if keypoint:
153
+ assert lb.shape[1] == (
154
+ 5 + nkpt * ndim
155
+ ), f"labels require {(5 + nkpt * ndim)} columns each"
156
+ assert (
157
+ lb[:, 5::ndim] <= 1
158
+ ).all(), (
159
+ "non-normalized or out of bounds coordinate labels"
160
+ )
161
+ assert (
162
+ lb[:, 6::ndim] <= 1
163
+ ).all(), (
164
+ "non-normalized or out of bounds coordinate labels"
165
+ )
166
+ else:
167
+ assert (
168
+ lb.shape[1] == 5
169
+ ), f"labels require 5 columns, {lb.shape[1]} columns detected"
170
+ assert (
171
+ lb[:, 1:] <= 1
172
+ ).all(), f"non-normalized or out of bounds coordinates {lb[:, 1:][lb[:, 1:] > 1]}"
173
+ assert (
174
+ lb >= 0
175
+ ).all(), f"negative label values {lb[lb < 0]}"
176
+ # All labels
177
+ max_cls = int(lb[:, 0].max()) # max label count
178
+ assert max_cls <= num_cls, (
179
+ f"Label class {max_cls} exceeds dataset class count {num_cls}. "
180
+ f"Possible class labels are 0-{num_cls - 1}"
181
+ )
182
+ _, i = np.unique(lb, axis=0, return_index=True)
183
+ if len(i) < nl: # duplicate row check
184
+ lb = lb[i] # remove duplicates
185
+ if segments:
186
+ segments = [segments[x] for x in i]
187
+ msg = f"{prefix}WARNING ⚠️ {im_file}: {nl - len(i)} duplicate labels removed"
188
+ else:
189
+ ne = 1 # label empty
190
+ lb = (
191
+ np.zeros((0, (5 + nkpt * ndim)), dtype=np.float32)
192
+ if keypoint
193
+ else np.zeros((0, 5), dtype=np.float32)
194
+ )
195
+ else:
196
+ nm = 1 # label missing
197
+ lb = (
198
+ np.zeros((0, (5 + nkpt * ndim)), dtype=np.float32)
199
+ if keypoint
200
+ else np.zeros((0, 5), dtype=np.float32)
201
+ )
202
+ if keypoint:
203
+ keypoints = lb[:, 5:].reshape(-1, nkpt, ndim)
204
+ if ndim == 2:
205
+ kpt_mask = np.ones(keypoints.shape[:2], dtype=np.float32)
206
+ kpt_mask = np.where(keypoints[..., 0] < 0, 0.0, kpt_mask)
207
+ kpt_mask = np.where(keypoints[..., 1] < 0, 0.0, kpt_mask)
208
+ keypoints = np.concatenate(
209
+ [keypoints, kpt_mask[..., None]], axis=-1
210
+ ) # (nl, nkpt, 3)
211
+ lb = lb[:, :5]
212
+ return im_file, lb, shape, segments, keypoints, nm, nf, ne, nc, msg
213
+ except Exception as e:
214
+ nc = 1
215
+ msg = f"{prefix}WARNING ⚠️ {im_file}: ignoring corrupt image/label: {e}"
216
+ return [None, None, None, None, None, nm, nf, ne, nc, msg]
217
+
218
+
219
+ def polygon2mask(imgsz, polygons, color=1, downsample_ratio=1):
220
+ """
221
+ Args:
222
+ imgsz (tuple): The image size.
223
+ polygons (list[np.ndarray]): [N, M], N is the number of polygons, M is the number of points(Be divided by 2).
224
+ color (int): color
225
+ downsample_ratio (int): downsample ratio
226
+ """
227
+ mask = np.zeros(imgsz, dtype=np.uint8)
228
+ polygons = np.asarray(polygons)
229
+ polygons = polygons.astype(np.int32)
230
+ shape = polygons.shape
231
+ polygons = polygons.reshape(shape[0], -1, 2)
232
+ cv2.fillPoly(mask, polygons, color=color)
233
+ nh, nw = (imgsz[0] // downsample_ratio, imgsz[1] // downsample_ratio)
234
+ # NOTE: fillPoly firstly then resize is trying the keep the same way
235
+ # of loss calculation when mask-ratio=1.
236
+ mask = cv2.resize(mask, (nw, nh))
237
+ return mask
238
+
239
+
240
+ def polygons2masks(imgsz, polygons, color, downsample_ratio=1):
241
+ """
242
+ Args:
243
+ imgsz (tuple): The image size.
244
+ polygons (list[np.ndarray]): each polygon is [N, M], N is number of polygons, M is number of points (M % 2 = 0)
245
+ color (int): color
246
+ downsample_ratio (int): downsample ratio
247
+ """
248
+ masks = []
249
+ for si in range(len(polygons)):
250
+ mask = polygon2mask(
251
+ imgsz, [polygons[si].reshape(-1)], color, downsample_ratio
252
+ )
253
+ masks.append(mask)
254
+ return np.array(masks)
255
+
256
+
257
+ def polygons2masks_overlap(imgsz, segments, downsample_ratio=1):
258
+ """Return a (640, 640) overlap mask."""
259
+ masks = np.zeros(
260
+ (imgsz[0] // downsample_ratio, imgsz[1] // downsample_ratio),
261
+ dtype=np.int32 if len(segments) > 255 else np.uint8,
262
+ )
263
+ areas = []
264
+ ms = []
265
+ for si in range(len(segments)):
266
+ mask = polygon2mask(
267
+ imgsz,
268
+ [segments[si].reshape(-1)],
269
+ downsample_ratio=downsample_ratio,
270
+ color=1,
271
+ )
272
+ ms.append(mask)
273
+ areas.append(mask.sum())
274
+ areas = np.asarray(areas)
275
+ index = np.argsort(-areas)
276
+ ms = np.array(ms)[index]
277
+ for i in range(len(segments)):
278
+ mask = ms[i] * (i + 1)
279
+ masks = masks + mask
280
+ masks = np.clip(masks, a_min=0, a_max=i + 1)
281
+ return masks, index
282
+
283
+
284
+ def check_det_dataset(dataset, autodownload=True):
285
+ """Download, check and/or unzip dataset if not found locally."""
286
+ data = check_file(dataset)
287
+
288
+ # Download (optional)
289
+ extract_dir = ""
290
+ if isinstance(data, (str, Path)) and (
291
+ zipfile.is_zipfile(data) or is_tarfile(data)
292
+ ):
293
+ new_dir = safe_download(
294
+ data, dir=DATASETS_DIR, unzip=True, delete=False, curl=False
295
+ )
296
+ data = next((DATASETS_DIR / new_dir).rglob("*.yaml"))
297
+ extract_dir, autodownload = data.parent, False
298
+
299
+ # Read yaml (optional)
300
+ if isinstance(data, (str, Path)):
301
+ data = yaml_load(data, append_filename=True) # dictionary
302
+
303
+ # Checks
304
+ for k in "train", "val":
305
+ if k not in data:
306
+ raise SyntaxError(
307
+ emojis(
308
+ f"{dataset} '{k}:' key missing ❌.\n'train' and 'val' are required in all data YAMLs."
309
+ )
310
+ )
311
+ if "names" not in data and "nc" not in data:
312
+ raise SyntaxError(
313
+ emojis(
314
+ f"{dataset} key missing ❌.\n either 'names' or 'nc' are required in all data YAMLs."
315
+ )
316
+ )
317
+ if "names" in data and "nc" in data and len(data["names"]) != data["nc"]:
318
+ raise SyntaxError(
319
+ emojis(
320
+ f"{dataset} 'names' length {len(data['names'])} and 'nc: {data['nc']}' must match."
321
+ )
322
+ )
323
+ if "names" not in data:
324
+ data["names"] = [f"class_{i}" for i in range(data["nc"])]
325
+ else:
326
+ data["nc"] = len(data["names"])
327
+
328
+ data["names"] = check_class_names(data["names"])
329
+
330
+ # Resolve paths
331
+ path = Path(
332
+ extract_dir
333
+ or data.get("path")
334
+ or Path(data.get("yaml_file", "")).parent
335
+ ) # dataset root
336
+
337
+ if not path.is_absolute():
338
+ path = (DATASETS_DIR / path).resolve()
339
+ data["path"] = path # download scripts
340
+ for k in "train", "val", "test":
341
+ if data.get(k): # prepend path
342
+ if isinstance(data[k], str):
343
+ x = (path / data[k]).resolve()
344
+ if not x.exists() and data[k].startswith("../"):
345
+ x = (path / data[k][3:]).resolve()
346
+ data[k] = str(x)
347
+ else:
348
+ data[k] = [str((path / x).resolve()) for x in data[k]]
349
+
350
+ # Parse yaml
351
+ train, val, test, s = (
352
+ data.get(x) for x in ("train", "val", "test", "download")
353
+ )
354
+ if val:
355
+ val = [
356
+ Path(x).resolve()
357
+ for x in (val if isinstance(val, list) else [val])
358
+ ] # val path
359
+ if not all(x.exists() for x in val):
360
+ name = clean_url(dataset) # dataset name with URL auth stripped
361
+ m = f"\nDataset '{name}' images not found ⚠️, missing paths %s" % [
362
+ str(x) for x in val if not x.exists()
363
+ ]
364
+ if s and autodownload:
365
+ LOGGER.warning(m)
366
+ else:
367
+ m += f"\nNote dataset download directory is '{DATASETS_DIR}'. You can update this in '{SETTINGS_YAML}'"
368
+ raise FileNotFoundError(m)
369
+ t = time.time()
370
+ if s.startswith("http") and s.endswith(".zip"): # URL
371
+ safe_download(url=s, dir=DATASETS_DIR, delete=True)
372
+ r = None # success
373
+ elif s.startswith("bash "): # bash script
374
+ LOGGER.info(f"Running {s} ...")
375
+ r = os.system(s)
376
+ else: # python script
377
+ r = exec(s, {"yaml": data}) # return None
378
+ dt = f"({round(time.time() - t, 1)}s)"
379
+ s = (
380
+ f"success ✅ {dt}, saved to {colorstr('bold', DATASETS_DIR)}"
381
+ if r in (0, None)
382
+ else f"failure {dt} ❌"
383
+ )
384
+ LOGGER.info(f"Dataset download {s}\n")
385
+ check_font(
386
+ "Arial.ttf" if is_ascii(data["names"]) else "Arial.Unicode.ttf"
387
+ ) # download fonts
388
+
389
+ return data # dictionary
390
+
391
+
392
+ def check_cls_dataset(dataset: str, split=""):
393
+ """
394
+ Check a classification dataset such as Imagenet.
395
+
396
+ This function takes a `dataset` name as input and returns a dictionary containing information about the dataset.
397
+ If the dataset is not found, it attempts to download the dataset from the internet and save it locally.
398
+
399
+ Args:
400
+ dataset (str): Name of the dataset.
401
+ split (str, optional): Dataset split, either 'val', 'test', or ''. Defaults to ''.
402
+
403
+ Returns:
404
+ data (dict): A dictionary containing the following keys and values:
405
+ 'train': Path object for the directory containing the training set of the dataset
406
+ 'val': Path object for the directory containing the validation set of the dataset
407
+ 'test': Path object for the directory containing the test set of the dataset
408
+ 'nc': Number of classes in the dataset
409
+ 'names': List of class names in the dataset
410
+ """
411
+ data_dir = (DATASETS_DIR / dataset).resolve()
412
+ if not data_dir.is_dir():
413
+ LOGGER.info(
414
+ f"\nDataset not found ⚠️, missing path {data_dir}, attempting download..."
415
+ )
416
+ t = time.time()
417
+ if dataset == "imagenet":
418
+ subprocess.run(
419
+ f"bash {ROOT / 'yolo/data/scripts/get_imagenet.sh'}",
420
+ shell=True,
421
+ check=True,
422
+ )
423
+ else:
424
+ url = f"https://github.com/ultralytics/yolov5/releases/download/v1.0/{dataset}.zip"
425
+ download(url, dir=data_dir.parent)
426
+ s = f"Dataset download success ✅ ({time.time() - t:.1f}s), saved to {colorstr('bold', data_dir)}\n"
427
+ LOGGER.info(s)
428
+ train_set = data_dir / "train"
429
+ val_set = (
430
+ data_dir / "val" if (data_dir / "val").exists() else None
431
+ ) # data/test or data/val
432
+ test_set = (
433
+ data_dir / "test" if (data_dir / "test").exists() else None
434
+ ) # data/val or data/test
435
+ if split == "val" and not val_set:
436
+ LOGGER.info(
437
+ "WARNING ⚠️ Dataset 'split=val' not found, using 'split=test' instead."
438
+ )
439
+ elif split == "test" and not test_set:
440
+ LOGGER.info(
441
+ "WARNING ⚠️ Dataset 'split=test' not found, using 'split=val' instead."
442
+ )
443
+
444
+ nc = len(
445
+ [x for x in (data_dir / "train").glob("*") if x.is_dir()]
446
+ ) # number of classes
447
+ names = [
448
+ x.name for x in (data_dir / "train").iterdir() if x.is_dir()
449
+ ] # class names list
450
+ names = dict(enumerate(sorted(names)))
451
+ return {
452
+ "train": train_set,
453
+ "val": val_set or test_set,
454
+ "test": test_set or val_set,
455
+ "nc": nc,
456
+ "names": names,
457
+ }
458
+
459
+
460
+ class HUBDatasetStats:
461
+ """
462
+ Class for generating HUB dataset JSON and `-hub` dataset directory
463
+
464
+ Arguments
465
+ path: Path to data.yaml or data.zip (with data.yaml inside data.zip)
466
+ task: Dataset task. Options are 'detect', 'segment', 'pose', 'classify'.
467
+ autodownload: Attempt to download dataset if not found locally
468
+
469
+ Usage
470
+ from ultralytics.yolo.data.utils import HUBDatasetStats
471
+ stats = HUBDatasetStats('/Users/glennjocher/Downloads/coco8.zip', task='detect') # detect dataset
472
+ stats = HUBDatasetStats('/Users/glennjocher/Downloads/coco8-seg.zip', task='segment') # segment dataset
473
+ stats = HUBDatasetStats('/Users/glennjocher/Downloads/coco8-pose.zip', task='pose') # pose dataset
474
+ stats.get_json(save=False)
475
+ stats.process_images()
476
+ """
477
+
478
+ def __init__(self, path="coco128.yaml", task="detect", autodownload=False):
479
+ """Initialize class."""
480
+ LOGGER.info(f"Starting HUB dataset checks for {path}....")
481
+ zipped, data_dir, yaml_path = self._unzip(Path(path))
482
+ try:
483
+ # data = yaml_load(check_yaml(yaml_path)) # data dict
484
+ data = check_det_dataset(yaml_path, autodownload) # data dict
485
+ if zipped:
486
+ data["path"] = data_dir
487
+ except Exception as e:
488
+ raise Exception("error/HUB/dataset_stats/yaml_load") from e
489
+
490
+ self.hub_dir = Path(str(data["path"]) + "-hub")
491
+ self.im_dir = self.hub_dir / "images"
492
+ self.im_dir.mkdir(parents=True, exist_ok=True) # makes /images
493
+ self.stats = {
494
+ "nc": len(data["names"]),
495
+ "names": list(data["names"].values()),
496
+ } # statistics dictionary
497
+ self.data = data
498
+ self.task = task # detect, segment, pose, classify
499
+
500
+ @staticmethod
501
+ def _find_yaml(dir):
502
+ """Return data.yaml file."""
503
+ files = list(dir.glob("*.yaml")) or list(
504
+ dir.rglob("*.yaml")
505
+ ) # try root level first and then recursive
506
+ assert files, f"No *.yaml file found in {dir}"
507
+ if len(files) > 1:
508
+ files = [
509
+ f for f in files if f.stem == dir.stem
510
+ ] # prefer *.yaml files that match dir name
511
+ assert (
512
+ files
513
+ ), f"Multiple *.yaml files found in {dir}, only 1 *.yaml file allowed"
514
+ assert (
515
+ len(files) == 1
516
+ ), f"Multiple *.yaml files found: {files}, only 1 *.yaml file allowed in {dir}"
517
+ return files[0]
518
+
519
+ def _unzip(self, path):
520
+ """Unzip data.zip."""
521
+ if not str(path).endswith(".zip"): # path is data.yaml
522
+ return False, None, path
523
+ unzip_dir = unzip_file(path, path=path.parent)
524
+ assert unzip_dir.is_dir(), (
525
+ f"Error unzipping {path}, {unzip_dir} not found. "
526
+ f"path/to/abc.zip MUST unzip to path/to/abc/"
527
+ )
528
+ return (
529
+ True,
530
+ str(unzip_dir),
531
+ self._find_yaml(unzip_dir),
532
+ ) # zipped, data_dir, yaml_path
533
+
534
+ def _hub_ops(self, f):
535
+ """Saves a compressed image for HUB previews."""
536
+ compress_one_image(
537
+ f, self.im_dir / Path(f).name
538
+ ) # save to dataset-hub
539
+
540
+ def get_json(self, save=False, verbose=False):
541
+ """Return dataset JSON for Ultralytics HUB."""
542
+ from ultralytics.yolo.data import YOLODataset # ClassificationDataset
543
+
544
+ def _round(labels):
545
+ """Update labels to integer class and 4 decimal place floats."""
546
+ if self.task == "detect":
547
+ coordinates = labels["bboxes"]
548
+ elif self.task == "segment":
549
+ coordinates = [x.flatten() for x in labels["segments"]]
550
+ elif self.task == "pose":
551
+ n = labels["keypoints"].shape[0]
552
+ coordinates = np.concatenate(
553
+ (labels["bboxes"], labels["keypoints"].reshape(n, -1)), 1
554
+ )
555
+ else:
556
+ raise ValueError("Undefined dataset task.")
557
+ zipped = zip(labels["cls"], coordinates)
558
+ return [
559
+ [int(c), *(round(float(x), 4) for x in points)]
560
+ for c, points in zipped
561
+ ]
562
+
563
+ for split in "train", "val", "test":
564
+ if self.data.get(split) is None:
565
+ self.stats[split] = None # i.e. no test set
566
+ continue
567
+
568
+ dataset = YOLODataset(
569
+ img_path=self.data[split],
570
+ data=self.data,
571
+ use_segments=self.task == "segment",
572
+ use_keypoints=self.task == "pose",
573
+ )
574
+ x = np.array(
575
+ [
576
+ np.bincount(
577
+ label["cls"].astype(int).flatten(),
578
+ minlength=self.data["nc"],
579
+ )
580
+ for label in tqdm(
581
+ dataset.labels, total=len(dataset), desc="Statistics"
582
+ )
583
+ ]
584
+ ) # shape(128x80)
585
+ self.stats[split] = {
586
+ "instance_stats": {
587
+ "total": int(x.sum()),
588
+ "per_class": x.sum(0).tolist(),
589
+ },
590
+ "image_stats": {
591
+ "total": len(dataset),
592
+ "unlabelled": int(np.all(x == 0, 1).sum()),
593
+ "per_class": (x > 0).sum(0).tolist(),
594
+ },
595
+ "labels": [
596
+ {Path(k).name: _round(v)}
597
+ for k, v in zip(dataset.im_files, dataset.labels)
598
+ ],
599
+ }
600
+
601
+ # Save, print and return
602
+ if save:
603
+ stats_path = self.hub_dir / "stats.json"
604
+ LOGGER.info(f"Saving {stats_path.resolve()}...")
605
+ with open(stats_path, "w") as f:
606
+ json.dump(self.stats, f) # save stats.json
607
+ if verbose:
608
+ LOGGER.info(json.dumps(self.stats, indent=2, sort_keys=False))
609
+ return self.stats
610
+
611
+ def process_images(self):
612
+ """Compress images for Ultralytics HUB."""
613
+ from ultralytics.yolo.data import YOLODataset # ClassificationDataset
614
+
615
+ for split in "train", "val", "test":
616
+ if self.data.get(split) is None:
617
+ continue
618
+ dataset = YOLODataset(img_path=self.data[split], data=self.data)
619
+ with ThreadPool(NUM_THREADS) as pool:
620
+ for _ in tqdm(
621
+ pool.imap(self._hub_ops, dataset.im_files),
622
+ total=len(dataset),
623
+ desc=f"{split} images",
624
+ ):
625
+ pass
626
+ LOGGER.info(f"Done. All images saved to {self.im_dir}")
627
+ return self.im_dir
628
+
629
+
630
+ def compress_one_image(f, f_new=None, max_dim=1920, quality=50):
631
+ """
632
+ Compresses a single image file to reduced size while preserving its aspect ratio and quality using either the
633
+ Python Imaging Library (PIL) or OpenCV library. If the input image is smaller than the maximum dimension, it will
634
+ not be resized.
635
+
636
+ Args:
637
+ f (str): The path to the input image file.
638
+ f_new (str, optional): The path to the output image file. If not specified, the input file will be overwritten.
639
+ max_dim (int, optional): The maximum dimension (width or height) of the output image. Default is 1920 pixels.
640
+ quality (int, optional): The image compression quality as a percentage. Default is 50%.
641
+
642
+ Usage:
643
+ from pathlib import Path
644
+ from ultralytics.yolo.data.utils import compress_one_image
645
+ for f in Path('/Users/glennjocher/Downloads/dataset').rglob('*.jpg'):
646
+ compress_one_image(f)
647
+ """
648
+ try: # use PIL
649
+ im = Image.open(f)
650
+ r = max_dim / max(im.height, im.width) # ratio
651
+ if r < 1.0: # image too large
652
+ im = im.resize((int(im.width * r), int(im.height * r)))
653
+ im.save(f_new or f, "JPEG", quality=quality, optimize=True) # save
654
+ except Exception as e: # use OpenCV
655
+ LOGGER.info(f"WARNING ⚠️ HUB ops PIL failure {f}: {e}")
656
+ im = cv2.imread(f)
657
+ im_height, im_width = im.shape[:2]
658
+ r = max_dim / max(im_height, im_width) # ratio
659
+ if r < 1.0: # image too large
660
+ im = cv2.resize(
661
+ im,
662
+ (int(im_width * r), int(im_height * r)),
663
+ interpolation=cv2.INTER_AREA,
664
+ )
665
+ cv2.imwrite(str(f_new or f), im)
666
+
667
+
668
+ def delete_dsstore(path):
669
+ """
670
+ Deletes all ".DS_store" files under a specified directory.
671
+
672
+ Args:
673
+ path (str, optional): The directory path where the ".DS_store" files should be deleted.
674
+
675
+ Usage:
676
+ from ultralytics.yolo.data.utils import delete_dsstore
677
+ delete_dsstore('/Users/glennjocher/Downloads/dataset')
678
+
679
+ Note:
680
+ ".DS_store" files are created by the Apple operating system and contain metadata about folders and files. They
681
+ are hidden system files and can cause issues when transferring files between different operating systems.
682
+ """
683
+ # Delete Apple .DS_store files
684
+ files = list(Path(path).rglob(".DS_store"))
685
+ LOGGER.info(f"Deleting *.DS_store files: {files}")
686
+ for f in files:
687
+ f.unlink()
688
+
689
+
690
+ def zip_directory(dir, use_zipfile_library=True):
691
+ """
692
+ Zips a directory and saves the archive to the specified output path.
693
+
694
+ Args:
695
+ dir (str): The path to the directory to be zipped.
696
+ use_zipfile_library (bool): Whether to use zipfile library or shutil for zipping.
697
+
698
+ Usage:
699
+ from ultralytics.yolo.data.utils import zip_directory
700
+ zip_directory('/Users/glennjocher/Downloads/playground')
701
+
702
+ zip -r coco8-pose.zip coco8-pose
703
+ """
704
+ delete_dsstore(dir)
705
+ if use_zipfile_library:
706
+ dir = Path(dir)
707
+ with zipfile.ZipFile(
708
+ dir.with_suffix(".zip"), "w", zipfile.ZIP_DEFLATED
709
+ ) as zip_file:
710
+ for file_path in dir.glob("**/*"):
711
+ if file_path.is_file():
712
+ zip_file.write(file_path, file_path.relative_to(dir))
713
+ else:
714
+ import shutil
715
+
716
+ shutil.make_archive(dir, "zip", dir)