ultralytics 8.0.66__py3-none-any.whl → 8.0.68__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.
Potentially problematic release.
This version of ultralytics might be problematic. Click here for more details.
- ultralytics/__init__.py +1 -1
- ultralytics/hub/__init__.py +6 -4
- ultralytics/hub/auth.py +6 -4
- ultralytics/hub/session.py +1 -1
- ultralytics/yolo/data/utils.py +2 -2
- ultralytics/yolo/engine/exporter.py +8 -3
- ultralytics/yolo/engine/model.py +9 -11
- ultralytics/yolo/engine/predictor.py +8 -3
- ultralytics/yolo/engine/trainer.py +4 -5
- ultralytics/yolo/engine/validator.py +8 -3
- ultralytics/yolo/utils/__init__.py +16 -5
- ultralytics/yolo/utils/callbacks/__init__.py +2 -2
- ultralytics/yolo/utils/callbacks/base.py +6 -0
- ultralytics/yolo/utils/callbacks/clearml.py +85 -18
- ultralytics/yolo/utils/callbacks/comet.py +290 -20
- ultralytics/yolo/utils/checks.py +5 -5
- ultralytics/yolo/utils/downloads.py +17 -9
- ultralytics/yolo/utils/files.py +0 -7
- ultralytics/yolo/v8/classify/train.py +2 -2
- ultralytics/yolo/v8/classify/val.py +2 -2
- ultralytics/yolo/v8/detect/val.py +2 -2
- ultralytics/yolo/v8/pose/train.py +2 -2
- ultralytics/yolo/v8/pose/val.py +2 -2
- ultralytics/yolo/v8/segment/train.py +2 -2
- ultralytics/yolo/v8/segment/val.py +2 -2
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/METADATA +11 -11
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/RECORD +31 -31
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/LICENSE +0 -0
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/WHEEL +0 -0
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.0.66.dist-info → ultralytics-8.0.68.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Ultralytics YOLO 🚀, GPL-3.0 license
|
|
2
|
-
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ultralytics.yolo.utils import LOGGER, RANK, TESTS_RUNNING, ops
|
|
3
6
|
from ultralytics.yolo.utils.torch_utils import get_flops, get_num_params
|
|
4
7
|
|
|
5
8
|
try:
|
|
@@ -10,41 +13,308 @@ try:
|
|
|
10
13
|
except (ImportError, AssertionError):
|
|
11
14
|
comet_ml = None
|
|
12
15
|
|
|
16
|
+
COMET_MODE = os.getenv('COMET_MODE', 'online')
|
|
17
|
+
COMET_MODEL_NAME = os.getenv('COMET_MODEL_NAME', 'YOLOv8')
|
|
18
|
+
# determines how many batches of image predictions to log from the validation set
|
|
19
|
+
COMET_EVAL_BATCH_LOGGING_INTERVAL = int(os.getenv('COMET_EVAL_BATCH_LOGGING_INTERVAL', 1))
|
|
20
|
+
# determines whether to log confusion matrix every evaluation epoch
|
|
21
|
+
COMET_EVAL_LOG_CONFUSION_MATRIX = (os.getenv('COMET_EVAL_LOG_CONFUSION_MATRIX', 'true').lower() == 'true')
|
|
22
|
+
# determines whether to log image predictions every evaluation epoch
|
|
23
|
+
COMET_EVAL_LOG_IMAGE_PREDICTIONS = (os.getenv('COMET_EVAL_LOG_IMAGE_PREDICTIONS', 'true').lower() == 'true')
|
|
24
|
+
COMET_MAX_IMAGE_PREDICTIONS = int(os.getenv('COMET_MAX_IMAGE_PREDICTIONS', 100))
|
|
13
25
|
|
|
14
|
-
|
|
26
|
+
# ensures certain logging functions only run for supported tasks
|
|
27
|
+
COMET_SUPPORTED_TASKS = ['detect']
|
|
28
|
+
# scales reported confidence scores (0.0-1.0) by this value
|
|
29
|
+
COMET_MAX_CONFIDENCE_SCORE = int(os.getenv('COMET_MAX_CONFIDENCE_SCORE', 100))
|
|
30
|
+
|
|
31
|
+
# names of plots created by YOLOv8 that are logged to Comet
|
|
32
|
+
EVALUATION_PLOT_NAMES = 'F1_curve', 'P_curve', 'R_curve', 'PR_curve', 'confusion_matrix'
|
|
33
|
+
LABEL_PLOT_NAMES = 'labels', 'labels_correlogram'
|
|
34
|
+
|
|
35
|
+
_comet_image_prediction_count = 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_experiment_type(mode, project_name):
|
|
39
|
+
if mode == 'offline':
|
|
40
|
+
return comet_ml.OfflineExperiment(project_name=project_name)
|
|
41
|
+
|
|
42
|
+
return comet_ml.Experiment(project_name=project_name)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _create_experiment(args):
|
|
46
|
+
# Ensures that the experiment object is only created in a single process during distributed training.
|
|
47
|
+
if RANK not in (-1, 0):
|
|
48
|
+
return
|
|
15
49
|
try:
|
|
16
|
-
experiment =
|
|
17
|
-
experiment.
|
|
18
|
-
experiment.
|
|
50
|
+
experiment = _get_experiment_type(COMET_MODE, args.project)
|
|
51
|
+
experiment.log_parameters(vars(args))
|
|
52
|
+
experiment.log_others({
|
|
53
|
+
'eval_batch_logging_interval': COMET_EVAL_BATCH_LOGGING_INTERVAL,
|
|
54
|
+
'log_confusion_matrix': COMET_EVAL_LOG_CONFUSION_MATRIX,
|
|
55
|
+
'log_image_predictions': COMET_EVAL_LOG_IMAGE_PREDICTIONS,
|
|
56
|
+
'max_image_predictions': COMET_MAX_IMAGE_PREDICTIONS, })
|
|
57
|
+
experiment.log_other('Created from', 'yolov8')
|
|
58
|
+
|
|
19
59
|
except Exception as e:
|
|
20
60
|
LOGGER.warning(f'WARNING ⚠️ Comet installed but not initialized correctly, not logging this run. {e}')
|
|
21
61
|
|
|
22
62
|
|
|
63
|
+
def _fetch_trainer_metadata(trainer):
|
|
64
|
+
curr_epoch = trainer.epoch + 1
|
|
65
|
+
|
|
66
|
+
train_num_steps_per_epoch = len(trainer.train_loader.dataset) // trainer.batch_size
|
|
67
|
+
curr_step = curr_epoch * train_num_steps_per_epoch
|
|
68
|
+
final_epoch = curr_epoch == trainer.epochs
|
|
69
|
+
|
|
70
|
+
save = trainer.args.save
|
|
71
|
+
save_period = trainer.args.save_period
|
|
72
|
+
save_interval = curr_epoch % save_period == 0
|
|
73
|
+
save_assets = save and save_period > 0 and save_interval and not final_epoch
|
|
74
|
+
|
|
75
|
+
return dict(curr_epoch=curr_epoch, curr_step=curr_step, save_assets=save_assets, final_epoch=final_epoch)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _scale_bounding_box_to_original_image_shape(box, resized_image_shape, original_image_shape, ratio_pad):
|
|
79
|
+
"""YOLOv8 resizes images during training and the label values
|
|
80
|
+
are normalized based on this resized shape. This function rescales the
|
|
81
|
+
bounding box labels to the original image shape.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
resized_image_height, resized_image_width = resized_image_shape
|
|
85
|
+
|
|
86
|
+
# convert normalized xywh format predictions to xyxy in resized scale format
|
|
87
|
+
box = ops.xywhn2xyxy(box, h=resized_image_height, w=resized_image_width)
|
|
88
|
+
# scale box predictions from resized image scale back to original image scale
|
|
89
|
+
box = ops.scale_boxes(resized_image_shape, box, original_image_shape, ratio_pad)
|
|
90
|
+
# Convert bounding box format from xyxy to xywh for Comet logging
|
|
91
|
+
box = ops.xyxy2xywh(box)
|
|
92
|
+
# adjust xy center to correspond top-left corner
|
|
93
|
+
box[:2] -= box[2:] / 2
|
|
94
|
+
box = box.tolist()
|
|
95
|
+
|
|
96
|
+
return box
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _format_ground_truth_annotations_for_detection(img_idx, image_path, batch, class_name_map=None):
|
|
100
|
+
indices = batch['batch_idx'] == img_idx
|
|
101
|
+
bboxes = batch['bboxes'][indices]
|
|
102
|
+
if len(bboxes) == 0:
|
|
103
|
+
LOGGER.debug(f'COMET WARNING: Image: {image_path} has no bounding boxes labels')
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
cls_labels = batch['cls'][indices].squeeze(1).tolist()
|
|
107
|
+
if class_name_map:
|
|
108
|
+
cls_labels = [str(class_name_map[label]) for label in cls_labels]
|
|
109
|
+
|
|
110
|
+
original_image_shape = batch['ori_shape'][img_idx]
|
|
111
|
+
resized_image_shape = batch['resized_shape'][img_idx]
|
|
112
|
+
ratio_pad = batch['ratio_pad'][img_idx]
|
|
113
|
+
|
|
114
|
+
data = []
|
|
115
|
+
for box, label in zip(bboxes, cls_labels):
|
|
116
|
+
box = _scale_bounding_box_to_original_image_shape(box, resized_image_shape, original_image_shape, ratio_pad)
|
|
117
|
+
data.append({'boxes': [box], 'label': f'gt_{label}', 'score': COMET_MAX_CONFIDENCE_SCORE})
|
|
118
|
+
|
|
119
|
+
return {'name': 'ground_truth', 'data': data}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _format_prediction_annotations_for_detection(image_path, metadata, class_label_map=None):
|
|
123
|
+
stem = image_path.stem
|
|
124
|
+
image_id = int(stem) if stem.isnumeric() else stem
|
|
125
|
+
|
|
126
|
+
predictions = metadata.get(image_id)
|
|
127
|
+
if not predictions:
|
|
128
|
+
LOGGER.debug(f'COMET WARNING: Image: {image_path} has no bounding boxes predictions')
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
data = []
|
|
132
|
+
for prediction in predictions:
|
|
133
|
+
boxes = prediction['bbox']
|
|
134
|
+
score = prediction['score'] * COMET_MAX_CONFIDENCE_SCORE
|
|
135
|
+
cls_label = prediction['category_id']
|
|
136
|
+
if class_label_map:
|
|
137
|
+
cls_label = str(class_label_map[cls_label])
|
|
138
|
+
|
|
139
|
+
data.append({'boxes': [boxes], 'label': cls_label, 'score': score})
|
|
140
|
+
|
|
141
|
+
return {'name': 'prediction', 'data': data}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _fetch_annotations(img_idx, image_path, batch, prediction_metadata_map, class_label_map):
|
|
145
|
+
ground_truth_annotations = _format_ground_truth_annotations_for_detection(img_idx, image_path, batch,
|
|
146
|
+
class_label_map)
|
|
147
|
+
prediction_annotations = _format_prediction_annotations_for_detection(image_path, prediction_metadata_map,
|
|
148
|
+
class_label_map)
|
|
149
|
+
|
|
150
|
+
annotations = [
|
|
151
|
+
annotation for annotation in [ground_truth_annotations, prediction_annotations] if annotation is not None]
|
|
152
|
+
return [annotations] if annotations else None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _create_prediction_metadata_map(model_predictions):
|
|
156
|
+
pred_metadata_map = {}
|
|
157
|
+
for prediction in model_predictions:
|
|
158
|
+
pred_metadata_map.setdefault(prediction['image_id'], [])
|
|
159
|
+
pred_metadata_map[prediction['image_id']].append(prediction)
|
|
160
|
+
|
|
161
|
+
return pred_metadata_map
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _log_confusion_matrix(experiment, trainer, curr_step, curr_epoch):
|
|
165
|
+
conf_mat = trainer.validator.confusion_matrix.matrix
|
|
166
|
+
names = list(trainer.data['names'].values()) + ['background']
|
|
167
|
+
experiment.log_confusion_matrix(
|
|
168
|
+
matrix=conf_mat,
|
|
169
|
+
labels=names,
|
|
170
|
+
max_categories=len(names),
|
|
171
|
+
epoch=curr_epoch,
|
|
172
|
+
step=curr_step,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _log_images(experiment, image_paths, curr_step, annotations=None):
|
|
177
|
+
if annotations:
|
|
178
|
+
for image_path, annotation in zip(image_paths, annotations):
|
|
179
|
+
experiment.log_image(image_path, name=image_path.stem, step=curr_step, annotations=annotation)
|
|
180
|
+
|
|
181
|
+
else:
|
|
182
|
+
for image_path in image_paths:
|
|
183
|
+
experiment.log_image(image_path, name=image_path.stem, step=curr_step)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _log_image_predictions(experiment, validator, curr_step):
|
|
187
|
+
global _comet_image_prediction_count
|
|
188
|
+
|
|
189
|
+
task = validator.args.task
|
|
190
|
+
if task not in COMET_SUPPORTED_TASKS:
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
jdict = validator.jdict
|
|
194
|
+
if not jdict:
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
predictions_metadata_map = _create_prediction_metadata_map(jdict)
|
|
198
|
+
dataloader = validator.dataloader
|
|
199
|
+
class_label_map = validator.names
|
|
200
|
+
|
|
201
|
+
for batch_idx, batch in enumerate(dataloader):
|
|
202
|
+
if (batch_idx + 1) % COMET_EVAL_BATCH_LOGGING_INTERVAL != 0:
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
image_paths = batch['im_file']
|
|
206
|
+
for img_idx, image_path in enumerate(image_paths):
|
|
207
|
+
if _comet_image_prediction_count >= COMET_MAX_IMAGE_PREDICTIONS:
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
image_path = Path(image_path)
|
|
211
|
+
annotations = _fetch_annotations(
|
|
212
|
+
img_idx,
|
|
213
|
+
image_path,
|
|
214
|
+
batch,
|
|
215
|
+
predictions_metadata_map,
|
|
216
|
+
class_label_map,
|
|
217
|
+
)
|
|
218
|
+
_log_images(
|
|
219
|
+
experiment,
|
|
220
|
+
[image_path],
|
|
221
|
+
curr_step,
|
|
222
|
+
annotations=annotations,
|
|
223
|
+
)
|
|
224
|
+
_comet_image_prediction_count += 1
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _log_plots(experiment, trainer):
|
|
228
|
+
plot_filenames = [trainer.save_dir / f'{plots}.png' for plots in EVALUATION_PLOT_NAMES]
|
|
229
|
+
_log_images(experiment, plot_filenames, None)
|
|
230
|
+
|
|
231
|
+
label_plot_filenames = [trainer.save_dir / f'{labels}.jpg' for labels in LABEL_PLOT_NAMES]
|
|
232
|
+
_log_images(experiment, label_plot_filenames, None)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _log_model(experiment, trainer):
|
|
236
|
+
experiment.log_model(
|
|
237
|
+
COMET_MODEL_NAME,
|
|
238
|
+
file_or_folder=str(trainer.best),
|
|
239
|
+
file_name='best.pt',
|
|
240
|
+
overwrite=True,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def on_pretrain_routine_start(trainer):
|
|
245
|
+
experiment = comet_ml.get_global_experiment()
|
|
246
|
+
if not experiment:
|
|
247
|
+
_create_experiment(trainer.args)
|
|
248
|
+
|
|
249
|
+
|
|
23
250
|
def on_train_epoch_end(trainer):
|
|
24
251
|
experiment = comet_ml.get_global_experiment()
|
|
25
|
-
if experiment:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
252
|
+
if not experiment:
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
metadata = _fetch_trainer_metadata(trainer)
|
|
256
|
+
curr_epoch = metadata['curr_epoch']
|
|
257
|
+
curr_step = metadata['curr_step']
|
|
258
|
+
|
|
259
|
+
experiment.log_metrics(
|
|
260
|
+
trainer.label_loss_items(trainer.tloss, prefix='train'),
|
|
261
|
+
step=curr_step,
|
|
262
|
+
epoch=curr_epoch,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if curr_epoch == 1:
|
|
266
|
+
_log_images(experiment, trainer.save_dir.glob('train_batch*.jpg'), curr_step)
|
|
30
267
|
|
|
31
268
|
|
|
32
269
|
def on_fit_epoch_end(trainer):
|
|
33
270
|
experiment = comet_ml.get_global_experiment()
|
|
34
|
-
if experiment:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
271
|
+
if not experiment:
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
metadata = _fetch_trainer_metadata(trainer)
|
|
275
|
+
curr_epoch = metadata['curr_epoch']
|
|
276
|
+
curr_step = metadata['curr_step']
|
|
277
|
+
save_assets = metadata['save_assets']
|
|
278
|
+
|
|
279
|
+
experiment.log_metrics(trainer.metrics, step=curr_step, epoch=curr_epoch)
|
|
280
|
+
experiment.log_metrics(trainer.lr, step=curr_step, epoch=curr_epoch)
|
|
281
|
+
if curr_epoch == 1:
|
|
282
|
+
model_info = {
|
|
283
|
+
'model/parameters': get_num_params(trainer.model),
|
|
284
|
+
'model/GFLOPs': round(get_flops(trainer.model), 3),
|
|
285
|
+
'model/speed(ms)': round(trainer.validator.speed['inference'], 3)}
|
|
286
|
+
experiment.log_metrics(model_info, step=curr_step, epoch=curr_epoch)
|
|
287
|
+
|
|
288
|
+
if not save_assets:
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
_log_model(experiment, trainer)
|
|
292
|
+
if COMET_EVAL_LOG_CONFUSION_MATRIX:
|
|
293
|
+
_log_confusion_matrix(experiment, trainer, curr_step, curr_epoch)
|
|
294
|
+
if COMET_EVAL_LOG_IMAGE_PREDICTIONS:
|
|
295
|
+
_log_image_predictions(experiment, trainer.validator, curr_step)
|
|
42
296
|
|
|
43
297
|
|
|
44
298
|
def on_train_end(trainer):
|
|
45
299
|
experiment = comet_ml.get_global_experiment()
|
|
46
|
-
if experiment:
|
|
47
|
-
|
|
300
|
+
if not experiment:
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
metadata = _fetch_trainer_metadata(trainer)
|
|
304
|
+
curr_epoch = metadata['curr_epoch']
|
|
305
|
+
curr_step = metadata['curr_step']
|
|
306
|
+
plots = trainer.args.plots
|
|
307
|
+
|
|
308
|
+
_log_model(experiment, trainer)
|
|
309
|
+
if plots:
|
|
310
|
+
_log_plots(experiment, trainer)
|
|
311
|
+
|
|
312
|
+
_log_confusion_matrix(experiment, trainer, curr_step, curr_epoch)
|
|
313
|
+
_log_image_predictions(experiment, trainer.validator, curr_step)
|
|
314
|
+
experiment.end()
|
|
315
|
+
|
|
316
|
+
global _comet_image_prediction_count
|
|
317
|
+
_comet_image_prediction_count = 0
|
|
48
318
|
|
|
49
319
|
|
|
50
320
|
callbacks = {
|
ultralytics/yolo/utils/checks.py
CHANGED
|
@@ -8,7 +8,6 @@ import platform
|
|
|
8
8
|
import re
|
|
9
9
|
import shutil
|
|
10
10
|
import subprocess
|
|
11
|
-
import urllib
|
|
12
11
|
from pathlib import Path
|
|
13
12
|
from typing import Optional
|
|
14
13
|
|
|
@@ -20,8 +19,9 @@ import requests
|
|
|
20
19
|
import torch
|
|
21
20
|
from matplotlib import font_manager
|
|
22
21
|
|
|
23
|
-
from ultralytics.yolo.utils import (AUTOINSTALL, LOGGER, ONLINE, ROOT, USER_CONFIG_DIR, TryExcept,
|
|
24
|
-
emojis, is_colab, is_docker, is_kaggle, is_online, is_pip_package
|
|
22
|
+
from ultralytics.yolo.utils import (AUTOINSTALL, LOGGER, ONLINE, ROOT, USER_CONFIG_DIR, TryExcept, clean_url, colorstr,
|
|
23
|
+
downloads, emojis, is_colab, is_docker, is_kaggle, is_online, is_pip_package,
|
|
24
|
+
url2file)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def is_ascii(s) -> bool:
|
|
@@ -267,9 +267,9 @@ def check_file(file, suffix='', download=True, hard=True):
|
|
|
267
267
|
return file
|
|
268
268
|
elif download and file.lower().startswith(('https://', 'http://', 'rtsp://', 'rtmp://')): # download
|
|
269
269
|
url = file # warning: Pathlib turns :// -> :/
|
|
270
|
-
file =
|
|
270
|
+
file = url2file(file) # '%2F' to '/', split https://url.com/file.txt?auth
|
|
271
271
|
if Path(file).exists():
|
|
272
|
-
LOGGER.info(f'Found {url} locally at {file}') # file already exists
|
|
272
|
+
LOGGER.info(f'Found {clean_url(url)} locally at {file}') # file already exists
|
|
273
273
|
else:
|
|
274
274
|
downloads.safe_download(url=url, file=file, unzip=False)
|
|
275
275
|
return file
|
|
@@ -12,7 +12,7 @@ import requests
|
|
|
12
12
|
import torch
|
|
13
13
|
from tqdm import tqdm
|
|
14
14
|
|
|
15
|
-
from ultralytics.yolo.utils import LOGGER, checks, emojis, is_online
|
|
15
|
+
from ultralytics.yolo.utils import LOGGER, checks, clean_url, emojis, is_online, url2file
|
|
16
16
|
|
|
17
17
|
GITHUB_ASSET_NAMES = [f'yolov8{k}{suffix}.pt' for k in 'nsmlx' for suffix in ('', '6', '-cls', '-seg', '-pose')] + \
|
|
18
18
|
[f'yolov5{k}u.pt' for k in 'nsmlx'] + \
|
|
@@ -43,10 +43,18 @@ def unzip_file(file, path=None, exclude=('.DS_Store', '__MACOSX')):
|
|
|
43
43
|
if path is None:
|
|
44
44
|
path = Path(file).parent # default path
|
|
45
45
|
with ZipFile(file) as zipObj:
|
|
46
|
-
for f in zipObj.namelist(): # list all archived filenames in the zip
|
|
46
|
+
for i, f in enumerate(zipObj.namelist()): # list all archived filenames in the zip
|
|
47
|
+
# If zip does not expand into a directory create a new directory to expand into
|
|
48
|
+
if i == 0:
|
|
49
|
+
info = zipObj.getinfo(f)
|
|
50
|
+
if info.file_size > 0 or not info.filename.endswith('/'): # element is a file and not a directory
|
|
51
|
+
path = Path(path) / Path(file).stem # define new unzip directory
|
|
52
|
+
unzip_dir = path
|
|
53
|
+
else:
|
|
54
|
+
unzip_dir = f
|
|
47
55
|
if all(x not in f for x in exclude):
|
|
48
56
|
zipObj.extract(f, path=path)
|
|
49
|
-
return
|
|
57
|
+
return unzip_dir # return unzip dir
|
|
50
58
|
|
|
51
59
|
|
|
52
60
|
def safe_download(url,
|
|
@@ -79,8 +87,8 @@ def safe_download(url,
|
|
|
79
87
|
f = Path(url) # filename
|
|
80
88
|
else: # does not exist
|
|
81
89
|
assert dir or file, 'dir or file required for download'
|
|
82
|
-
f = dir /
|
|
83
|
-
desc = f'Downloading {url} to {f}'
|
|
90
|
+
f = dir / url2file(url) if dir else Path(file)
|
|
91
|
+
desc = f'Downloading {clean_url(url)} to {f}'
|
|
84
92
|
LOGGER.info(f'{desc}...')
|
|
85
93
|
f.parent.mkdir(parents=True, exist_ok=True) # make directory if missing
|
|
86
94
|
for i in range(retry + 1):
|
|
@@ -118,10 +126,10 @@ def safe_download(url,
|
|
|
118
126
|
raise ConnectionError(emojis(f'❌ Download failure for {url}. Retry limit reached.')) from e
|
|
119
127
|
LOGGER.warning(f'⚠️ Download failure, retrying {i + 1}/{retry} {url}...')
|
|
120
128
|
|
|
121
|
-
if unzip and f.exists() and f.suffix in ('.zip', '.tar', '.gz'):
|
|
129
|
+
if unzip and f.exists() and f.suffix in ('', '.zip', '.tar', '.gz'):
|
|
122
130
|
unzip_dir = dir or f.parent # unzip to dir if provided else unzip in place
|
|
123
131
|
LOGGER.info(f'Unzipping {f} to {unzip_dir}...')
|
|
124
|
-
if f
|
|
132
|
+
if is_zipfile(f):
|
|
125
133
|
unzip_dir = unzip_file(file=f, path=unzip_dir) # unzip
|
|
126
134
|
elif f.suffix == '.tar':
|
|
127
135
|
subprocess.run(['tar', 'xf', f, '--directory', unzip_dir], check=True) # unzip
|
|
@@ -156,9 +164,9 @@ def attempt_download_asset(file, repo='ultralytics/assets', release='v0.0.0'):
|
|
|
156
164
|
name = Path(parse.unquote(str(file))).name # decode '%2F' to '/' etc.
|
|
157
165
|
if str(file).startswith(('http:/', 'https:/')): # download
|
|
158
166
|
url = str(file).replace(':/', '://') # Pathlib turns :// -> :/
|
|
159
|
-
file = name
|
|
167
|
+
file = url2file(name) # parse authentication https://url.com/file.txt?auth...
|
|
160
168
|
if Path(file).is_file():
|
|
161
|
-
LOGGER.info(f'Found {url} locally at {file}') # file already exists
|
|
169
|
+
LOGGER.info(f'Found {clean_url(url)} locally at {file}') # file already exists
|
|
162
170
|
else:
|
|
163
171
|
safe_download(url=url, file=file, min_bytes=1E5)
|
|
164
172
|
return file
|
ultralytics/yolo/utils/files.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import glob
|
|
5
5
|
import os
|
|
6
|
-
import urllib
|
|
7
6
|
from datetime import datetime
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
|
|
@@ -80,12 +79,6 @@ def file_size(path):
|
|
|
80
79
|
return 0.0
|
|
81
80
|
|
|
82
81
|
|
|
83
|
-
def url2file(url):
|
|
84
|
-
# Convert URL to filename, i.e. https://url.com/file.txt?auth -> file.txt
|
|
85
|
-
url = str(Path(url)).replace(':/', '://') # Pathlib turns :// -> :/
|
|
86
|
-
return Path(urllib.parse.unquote(url)).name.split('?')[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
|
87
|
-
|
|
88
|
-
|
|
89
82
|
def get_latest_run(search_dir='.'):
|
|
90
83
|
# Return path to most recent 'last.pt' in /runs (i.e. to --resume from)
|
|
91
84
|
last_list = glob.glob(f'{search_dir}/**/last*.pt', recursive=True)
|
|
@@ -13,11 +13,11 @@ from ultralytics.yolo.utils.torch_utils import is_parallel, strip_optimizer
|
|
|
13
13
|
|
|
14
14
|
class ClassificationTrainer(BaseTrainer):
|
|
15
15
|
|
|
16
|
-
def __init__(self, cfg=DEFAULT_CFG, overrides=None):
|
|
16
|
+
def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
|
|
17
17
|
if overrides is None:
|
|
18
18
|
overrides = {}
|
|
19
19
|
overrides['task'] = 'classify'
|
|
20
|
-
super().__init__(cfg, overrides)
|
|
20
|
+
super().__init__(cfg, overrides, _callbacks)
|
|
21
21
|
|
|
22
22
|
def set_model_attributes(self):
|
|
23
23
|
self.model.names = self.data['names']
|
|
@@ -8,8 +8,8 @@ from ultralytics.yolo.utils.metrics import ClassifyMetrics
|
|
|
8
8
|
|
|
9
9
|
class ClassificationValidator(BaseValidator):
|
|
10
10
|
|
|
11
|
-
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None):
|
|
12
|
-
super().__init__(dataloader, save_dir, pbar, args)
|
|
11
|
+
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
|
|
12
|
+
super().__init__(dataloader, save_dir, pbar, args, _callbacks)
|
|
13
13
|
self.args.task = 'classify'
|
|
14
14
|
self.metrics = ClassifyMetrics()
|
|
15
15
|
|
|
@@ -18,8 +18,8 @@ from ultralytics.yolo.utils.torch_utils import de_parallel
|
|
|
18
18
|
|
|
19
19
|
class DetectionValidator(BaseValidator):
|
|
20
20
|
|
|
21
|
-
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None):
|
|
22
|
-
super().__init__(dataloader, save_dir, pbar, args)
|
|
21
|
+
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
|
|
22
|
+
super().__init__(dataloader, save_dir, pbar, args, _callbacks)
|
|
23
23
|
self.args.task = 'detect'
|
|
24
24
|
self.is_coco = False
|
|
25
25
|
self.class_map = None
|
|
@@ -20,11 +20,11 @@ from ultralytics.yolo.v8.detect.train import Loss
|
|
|
20
20
|
# BaseTrainer python usage
|
|
21
21
|
class PoseTrainer(v8.detect.DetectionTrainer):
|
|
22
22
|
|
|
23
|
-
def __init__(self, cfg=DEFAULT_CFG, overrides=None):
|
|
23
|
+
def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
|
|
24
24
|
if overrides is None:
|
|
25
25
|
overrides = {}
|
|
26
26
|
overrides['task'] = 'pose'
|
|
27
|
-
super().__init__(cfg, overrides)
|
|
27
|
+
super().__init__(cfg, overrides, _callbacks)
|
|
28
28
|
|
|
29
29
|
def get_model(self, cfg=None, weights=None, verbose=True):
|
|
30
30
|
model = PoseModel(cfg, ch=3, nc=self.data['nc'], data_kpt_shape=self.data['kpt_shape'], verbose=verbose)
|
ultralytics/yolo/v8/pose/val.py
CHANGED
|
@@ -14,8 +14,8 @@ from ultralytics.yolo.v8.detect import DetectionValidator
|
|
|
14
14
|
|
|
15
15
|
class PoseValidator(DetectionValidator):
|
|
16
16
|
|
|
17
|
-
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None):
|
|
18
|
-
super().__init__(dataloader, save_dir, pbar, args)
|
|
17
|
+
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
|
|
18
|
+
super().__init__(dataloader, save_dir, pbar, args, _callbacks)
|
|
19
19
|
self.args.task = 'pose'
|
|
20
20
|
self.metrics = PoseMetrics(save_dir=self.save_dir)
|
|
21
21
|
|
|
@@ -17,11 +17,11 @@ from ultralytics.yolo.v8.detect.train import Loss
|
|
|
17
17
|
# BaseTrainer python usage
|
|
18
18
|
class SegmentationTrainer(v8.detect.DetectionTrainer):
|
|
19
19
|
|
|
20
|
-
def __init__(self, cfg=DEFAULT_CFG, overrides=None):
|
|
20
|
+
def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
|
|
21
21
|
if overrides is None:
|
|
22
22
|
overrides = {}
|
|
23
23
|
overrides['task'] = 'segment'
|
|
24
|
-
super().__init__(cfg, overrides)
|
|
24
|
+
super().__init__(cfg, overrides, _callbacks)
|
|
25
25
|
|
|
26
26
|
def get_model(self, cfg=None, weights=None, verbose=True):
|
|
27
27
|
model = SegmentationModel(cfg, ch=3, nc=self.data['nc'], verbose=verbose and RANK == -1)
|
|
@@ -16,8 +16,8 @@ from ultralytics.yolo.v8.detect import DetectionValidator
|
|
|
16
16
|
|
|
17
17
|
class SegmentationValidator(DetectionValidator):
|
|
18
18
|
|
|
19
|
-
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None):
|
|
20
|
-
super().__init__(dataloader, save_dir, pbar, args)
|
|
19
|
+
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
|
|
20
|
+
super().__init__(dataloader, save_dir, pbar, args, _callbacks)
|
|
21
21
|
self.args.task = 'segment'
|
|
22
22
|
self.metrics = SegmentMetrics(save_dir=self.save_dir)
|
|
23
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ultralytics
|
|
3
|
-
Version: 8.0.
|
|
3
|
+
Version: 8.0.68
|
|
4
4
|
Summary: Ultralytics YOLOv8
|
|
5
5
|
Home-page: https://github.com/ultralytics/ultralytics
|
|
6
6
|
Author: Ultralytics
|
|
@@ -250,12 +250,12 @@ See [Pose Docs](https://docs.ultralytics.com/tasks/) for usage examples with the
|
|
|
250
250
|
|
|
251
251
|
| Model | size<br><sup>(pixels) | mAP<sup>box<br>50-95 | mAP<sup>pose<br>50-95 | Speed<br><sup>CPU ONNX<br>(ms) | Speed<br><sup>A100 TensorRT<br>(ms) | params<br><sup>(M) | FLOPs<br><sup>(B) |
|
|
252
252
|
| ---------------------------------------------------------------------------------------------------- | --------------------- | -------------------- | --------------------- | ------------------------------ | ----------------------------------- | ------------------ | ----------------- |
|
|
253
|
-
| [YOLOv8n-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n-pose.pt) | 640 | - | 49.7 |
|
|
254
|
-
| [YOLOv8s-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s-pose.pt) | 640 | - | 59.2 |
|
|
255
|
-
| [YOLOv8m-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8m-pose.pt) | 640 | - | 63.6 |
|
|
256
|
-
| [YOLOv8l-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8l-pose.pt) | 640 | - | 67.0 |
|
|
257
|
-
| [YOLOv8x-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-pose.pt) | 640 | - | 68.9 |
|
|
258
|
-
| [YOLOv8x-pose-p6](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-pose-p6.pt) | 1280 | - | 71.5 |
|
|
253
|
+
| [YOLOv8n-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n-pose.pt) | 640 | - | 49.7 | 131.8 | 1.18 | 3.3 | 9.2 |
|
|
254
|
+
| [YOLOv8s-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s-pose.pt) | 640 | - | 59.2 | 233.2 | 1.42 | 11.6 | 30.2 |
|
|
255
|
+
| [YOLOv8m-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8m-pose.pt) | 640 | - | 63.6 | 456.3 | 2.00 | 26.4 | 81.0 |
|
|
256
|
+
| [YOLOv8l-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8l-pose.pt) | 640 | - | 67.0 | 784.5 | 2.59 | 44.4 | 168.6 |
|
|
257
|
+
| [YOLOv8x-pose](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-pose.pt) | 640 | - | 68.9 | 1607.1 | 3.73 | 69.4 | 263.2 |
|
|
258
|
+
| [YOLOv8x-pose-p6](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-pose-p6.pt) | 1280 | - | 71.5 | 4088.7 | 10.04 | 99.1 | 1066.4 |
|
|
259
259
|
|
|
260
260
|
- **mAP<sup>val</sup>** values are for single-model single-scale on [COCO Keypoints val2017](http://cocodataset.org)
|
|
261
261
|
dataset.
|
|
@@ -281,16 +281,16 @@ See [Pose Docs](https://docs.ultralytics.com/tasks/) for usage examples with the
|
|
|
281
281
|
<a href="https://cutt.ly/yolov5-readme-clearml">
|
|
282
282
|
<img src="https://github.com/ultralytics/assets/raw/main/partners/logo-clearml.png" width="10%" /></a>
|
|
283
283
|
<img src="https://github.com/ultralytics/assets/raw/main/social/logo-transparent.png" width="15%" height="0" alt="" />
|
|
284
|
-
<a href="https://bit.ly/
|
|
284
|
+
<a href="https://bit.ly/yolov8-readme-comet">
|
|
285
285
|
<img src="https://github.com/ultralytics/assets/raw/main/partners/logo-comet.png" width="10%" /></a>
|
|
286
286
|
<img src="https://github.com/ultralytics/assets/raw/main/social/logo-transparent.png" width="15%" height="0" alt="" />
|
|
287
287
|
<a href="https://bit.ly/yolov5-neuralmagic">
|
|
288
288
|
<img src="https://github.com/ultralytics/assets/raw/main/partners/logo-neuralmagic.png" width="10%" /></a>
|
|
289
289
|
</div>
|
|
290
290
|
|
|
291
|
-
| Roboflow | ClearML ⭐ NEW | Comet ⭐ NEW
|
|
292
|
-
| :--------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: |
|
|
293
|
-
| Label and export your custom datasets directly to YOLOv8 for training with [Roboflow](https://roboflow.com/?ref=ultralytics) | Automatically track, visualize and even remotely train YOLOv8 using [ClearML](https://cutt.ly/yolov5-readme-clearml) (open-source!) | Free forever, [Comet](https://bit.ly/
|
|
291
|
+
| Roboflow | ClearML ⭐ NEW | Comet ⭐ NEW | Neural Magic ⭐ NEW |
|
|
292
|
+
| :--------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------: |
|
|
293
|
+
| Label and export your custom datasets directly to YOLOv8 for training with [Roboflow](https://roboflow.com/?ref=ultralytics) | Automatically track, visualize and even remotely train YOLOv8 using [ClearML](https://cutt.ly/yolov5-readme-clearml) (open-source!) | Free forever, [Comet](https://bit.ly/yolov8-readme-comet) lets you save YOLOv8 models, resume training, and interactively visualize and debug predictions | Run YOLOv8 inference up to 6x faster with [Neural Magic DeepSparse](https://bit.ly/yolov5-neuralmagic) |
|
|
294
294
|
|
|
295
295
|
## <div align="center">Ultralytics HUB</div>
|
|
296
296
|
|