diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d739fed..53bdf9d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,7 +38,7 @@ jobs: if [ "${{ matrix.os }}" == "macos-latest" ]; then pip install -e . coremltools openvino-dev tensorflow-macos --extra-index-url https://download.pytorch.org/whl/cpu else - pip install -e . coremltools openvino-dev tensorflow-cpu paddlepaddle x2paddle --extra-index-url https://download.pytorch.org/whl/cpu + pip install -e . coremltools openvino-dev tensorflow-cpu --extra-index-url https://download.pytorch.org/whl/cpu fi yolo export format=tflite - name: Check environment @@ -66,7 +66,7 @@ jobs: shell: python run: | from ultralytics.yolo.utils.benchmarks import benchmark - benchmark(model='${{ matrix.model }}-cls.pt', imgsz=160, half=False, hard_fail=0.70) + benchmark(model='${{ matrix.model }}-cls.pt', imgsz=160, half=False, hard_fail=0.60) - name: Benchmark Summary run: cat benchmarks.log diff --git a/README.zh-CN.md b/README.zh-CN.md index 13aede8..355c281 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -223,10 +223,11 @@ Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自 ##
License
-- YOLOv8 在两种不同的 License 下可用: - - **GPL-3.0 License**: 查看 [License](https://github.com/ultralytics/ultralytics/blob/main/LICENSE) 文件的详细信息。 - - **企业License**:在没有 GPL-3.0 开源要求的情况下为商业产品开发提供更大的灵活性。典型用例是将 Ultralytics 软件和 AI - 模型嵌入到商业产品和应用程序中。在以下位置申请企业许可证 [Ultralytics 许可](https://ultralytics.com/license) 。 +YOLOv8 在两种不同的 License 下可用: + +- **GPL-3.0 License**: 查看 [License](https://github.com/ultralytics/ultralytics/blob/main/LICENSE) 文件的详细信息。 +- **企业License**:在没有 GPL-3.0 开源要求的情况下为商业产品开发提供更大的灵活性。典型用例是将 Ultralytics 软件和 AI + 模型嵌入到商业产品和应用程序中。在以下位置申请企业许可证 [Ultralytics 许可](https://ultralytics.com/license) 。 ##
联系我们
diff --git a/docker/Dockerfile b/docker/Dockerfile index 89964c1..345c882 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,9 +29,8 @@ WORKDIR /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics # Install pip packages -COPY requirements.txt . RUN python3 -m pip install --upgrade pip wheel -RUN pip install --no-cache ultralytics[export] albumentations comet gsutil notebook +RUN pip install --no-cache '.[export]' albumentations comet gsutil notebook # Set environment variables ENV OMP_NUM_THREADS=1 diff --git a/docker/Dockerfile-arm64 b/docker/Dockerfile-arm64 index 6499f38..82ff4a9 100644 --- a/docker/Dockerfile-arm64 +++ b/docker/Dockerfile-arm64 @@ -24,9 +24,8 @@ WORKDIR /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics # Install pip packages -COPY requirements.txt . RUN python3 -m pip install --upgrade pip wheel -RUN pip install --no-cache ultralytics albumentations gsutil notebook +RUN pip install --no-cache . albumentations gsutil notebook # Cleanup ENV DEBIAN_FRONTEND teletype diff --git a/docker/Dockerfile-cpu b/docker/Dockerfile-cpu index 2a93c55..364aaf4 100644 --- a/docker/Dockerfile-cpu +++ b/docker/Dockerfile-cpu @@ -24,9 +24,8 @@ WORKDIR /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics # Install pip packages -COPY requirements.txt . RUN python3 -m pip install --upgrade pip wheel -RUN pip install --no-cache ultralytics[export] albumentations gsutil notebook \ +RUN pip install --no-cache '.[export]' albumentations gsutil notebook \ --extra-index-url https://download.pytorch.org/whl/cpu # Cleanup diff --git a/docs/predict.md b/docs/predict.md index 966af3f..ef8dd0a 100644 --- a/docs/predict.md +++ b/docs/predict.md @@ -97,8 +97,8 @@ Class reference documentation for `Results` module and its components can be fou ## Plotting results -You can use `plot()` function of `Result` object to plot results on in image object. It plots all components(boxes, masks, -classification logits, etc) found in the results object +You can use `plot()` function of `Result` object to plot results on in image object. It plots all components(boxes, +masks, classification logits, etc) found in the results object ```python res = model(img) diff --git a/docs/tasks/tracking.md b/docs/tasks/tracking.md index 259487b..81734fe 100644 --- a/docs/tasks/tracking.md +++ b/docs/tasks/tracking.md @@ -42,11 +42,15 @@ Use a trained YOLOv8n/YOLOv8n-seg model to run tracker on video streams. ``` -As in the above usage, we support both the detection and segmentation models for tracking and the only thing you need to do is loading the corresponding(detection or segmentation) model. +As in the above usage, we support both the detection and segmentation models for tracking and the only thing you need to +do is loading the corresponding (detection or segmentation) model. ## Configuration + ### Tracking -Tracking shares the configuration with predict, i.e `conf`, `iou`, `show`. More configurations please refer to [predict page](https://docs.ultralytics.com/cfg/#prediction). + +Tracking shares the configuration with predict, i.e `conf`, `iou`, `show`. More configurations please refer +to [predict page](https://docs.ultralytics.com/cfg/#prediction). !!! example "" === "Python" @@ -65,7 +69,10 @@ Tracking shares the configuration with predict, i.e `conf`, `iou`, `show`. More ``` ### Tracker -We also support using a modified tracker config file, just copy a config file i.e `custom_tracker.yaml` from [ultralytics/tracker/cfg](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/tracker/cfg) and modify any configurations(expect the `tracker_type`) you need to. + +We also support using a modified tracker config file, just copy a config file i.e `custom_tracker.yaml` +from [ultralytics/tracker/cfg](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/tracker/cfg) and modify +any configurations(expect the `tracker_type`) you need to. !!! example "" === "Python" @@ -82,5 +89,7 @@ We also support using a modified tracker config file, just copy a config file i. yolo track model=yolov8n.pt source="https://youtu.be/Zgi9g1ksQHc" tracker='custom_tracker.yaml' ``` -Please refer to [ultralytics/tracker/cfg](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/tracker/cfg) page. + +Please refer to [ultralytics/tracker/cfg](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/tracker/cfg) +page. diff --git a/requirements.txt b/requirements.txt index 2a3010f..eb4b7fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,7 +37,6 @@ seaborn>=0.11.0 # Extras -------------------------------------- psutil # system utilization thop>=0.1.1 # FLOPs computation -wheel>=0.38.0 # Snyk vulnerability fix # ipython # interactive notebook # albumentations>=1.0.3 # pycocotools>=2.0.6 # COCO mAP diff --git a/setup.cfg b/setup.cfg index fcc17e1..2cde6a4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,17 +25,19 @@ verbose = 2 # https://pep8.readthedocs.io/en/latest/intro.html#error-codes format = pylint # see: https://www.flake8rules.com/ -ignore = E731,F405,E402,F401,W504,E127,E231,E501,F403 +ignore = E731,F405,E402,W504,E501 # E731: Do not assign a lambda expression, use a def # F405: name may be undefined, or defined from star imports: module # E402: module level import not at top of file - # F401: module imported but unused # W504: line break after binary operator - # E127: continuation line over-indented for visual indent - # E231: missing whitespace after ‘,’, ‘;’, or ‘:’ # E501: line too long + # removed: + # F401: module imported but unused + # E231: missing whitespace after ‘,’, ‘;’, or ‘:’ + # E127: continuation line over-indented for visual indent # F403: ‘from module import *’ used; unable to detect undefined names + [isort] # https://pycqa.github.io/isort/docs/configuration/options.html line_length = 120 @@ -48,7 +50,7 @@ spaces_before_comment = 2 COLUMN_LIMIT = 120 COALESCE_BRACKETS = True SPACES_AROUND_POWER_OPERATOR = True -SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET = False +SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET = True SPLIT_BEFORE_CLOSING_BRACKET = False SPLIT_BEFORE_FIRST_ARGUMENT = False # EACH_DICT_ENTRY_ON_SEPARATE_LINE = False diff --git a/setup.py b/setup.py index 6ba9fc0..ba0296e 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ setup( 'Topic :: Scientific/Engineering :: Image Recognition', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows',], + 'Operating System :: Microsoft :: Windows', ], keywords='machine-learning, deep-learning, vision, ML, DL, AI, YOLO, YOLOv3, YOLOv5, YOLOv8, HUB, Ultralytics', entry_points={ 'console_scripts': ['yolo = ultralytics.yolo.cfg:entrypoint', 'ultralytics = ultralytics.yolo.cfg:entrypoint']}) diff --git a/tests/test_cli.py b/tests/test_cli.py index 2879b2a..9b01ce2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -22,7 +22,7 @@ def test_special_modes(): # Train checks --------------------------------------------------------------------------------------------------------- def test_train_det(): - run(f'yolo train detect model={CFG}.yaml data=coco8.yaml imgsz=32 epochs=1') + run(f'yolo train detect model={CFG}.yaml data=coco8.yaml imgsz=32 epochs=1 v5loader') def test_train_seg(): @@ -48,7 +48,7 @@ def test_val_classify(): # Predict checks ------------------------------------------------------------------------------------------------------- def test_predict_detect(): - run(f"yolo predict model={MODEL}.pt source={ROOT / 'assets'} imgsz=32 save") + run(f"yolo predict model={MODEL}.pt source={ROOT / 'assets'} imgsz=32 save save_crop save_txt") if checks.check_online(): run(f'yolo predict model={MODEL}.pt source=https://ultralytics.com/images/bus.jpg imgsz=32') run(f'yolo predict model={MODEL}.pt source=https://ultralytics.com/assets/decelera_landscape_min.mov imgsz=32') diff --git a/tests/test_python.py b/tests/test_python.py index 3ae8077..ad8c314 100644 --- a/tests/test_python.py +++ b/tests/test_python.py @@ -162,9 +162,8 @@ def test_workflow(): def test_predict_callback_and_setup(): - - def on_predict_batch_end(predictor): - # results -> List[batch_size] + # test callback addition for prediction + def on_predict_batch_end(predictor): # results -> List[batch_size] path, _, im0s, _, _ = predictor.batch # print('on_predict_batch_end', im0s[0].shape) im0s = im0s if isinstance(im0s, list) else [im0s] diff --git a/ultralytics/__init__.py b/ultralytics/__init__.py index 1f37bbc..f6b125a 100644 --- a/ultralytics/__init__.py +++ b/ultralytics/__init__.py @@ -1,8 +1,8 @@ # Ultralytics YOLO 🚀, GPL-3.0 license -__version__ = '8.0.46' +__version__ = '8.0.47' from ultralytics.yolo.engine.model import YOLO from ultralytics.yolo.utils.checks import check_yolo as checks -__all__ = ['__version__', 'YOLO', 'checks'] # allow simpler import +__all__ = '__version__', 'YOLO', 'checks' # allow simpler import diff --git a/ultralytics/hub/utils.py b/ultralytics/hub/utils.py index 8a2bc49..e0a7daa 100644 --- a/ultralytics/hub/utils.py +++ b/ultralytics/hub/utils.py @@ -154,11 +154,12 @@ class Traces: 'python': platform.python_version(), 'release': __version__, 'environment': ENVIRONMENT} - self.enabled = SETTINGS['sync'] and \ - RANK in {-1, 0} and \ - check_online() and \ - not TESTS_RUNNING and \ - (is_pip_package() or get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git') + self.enabled = \ + SETTINGS['sync'] and \ + RANK in {-1, 0} and \ + check_online() and \ + not TESTS_RUNNING and \ + (is_pip_package() or get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git') def __call__(self, cfg, all_keys=False, traces_sample_rate=1.0): """ diff --git a/ultralytics/nn/autobackend.py b/ultralytics/nn/autobackend.py index 7656859..3f05258 100644 --- a/ultralytics/nn/autobackend.py +++ b/ultralytics/nn/autobackend.py @@ -136,7 +136,7 @@ class AutoBackend(nn.Module): batch_dim = get_batch(network) if batch_dim.is_static: batch_size = batch_dim.get_length() - executable_network = ie.compile_model(network, device_name='CPU') # device_name="MYRIAD" for Intel NCS2 + executable_network = ie.compile_model(network, device_name='CPU') # device_name="MYRIAD" for NCS2 elif engine: # TensorRT LOGGER.info(f'Loading {w} for TensorRT inference...') import tensorrt as trt # https://developer.nvidia.com/nvidia-tensorrt-download @@ -176,6 +176,8 @@ class AutoBackend(nn.Module): LOGGER.info(f'Loading {w} for CoreML inference...') import coremltools as ct model = ct.models.MLModel(w) + names, stride, task = (model.user_defined_metadata.get(k) for k in ('names', 'stride', 'task')) + names, stride = eval(names), int(stride) elif saved_model: # TF SavedModel LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...') import tensorflow as tf @@ -185,18 +187,13 @@ class AutoBackend(nn.Module): LOGGER.info(f'Loading {w} for TensorFlow GraphDef inference...') import tensorflow as tf + from ultralytics.yolo.engine.exporter import gd_outputs + def wrap_frozen_graph(gd, inputs, outputs): x = tf.compat.v1.wrap_function(lambda: tf.compat.v1.import_graph_def(gd, name=''), []) # wrapped ge = x.graph.as_graph_element return x.prune(tf.nest.map_structure(ge, inputs), tf.nest.map_structure(ge, outputs)) - def gd_outputs(gd): - name_list, input_list = [], [] - for node in gd.node: # tensorflow.core.framework.node_def_pb2.NodeDef - name_list.append(node.name) - input_list.extend(node.input) - return sorted(f'{x}:0' for x in list(set(name_list) - set(input_list)) if not x.startswith('NoOp')) - gd = tf.Graph().as_graph_def() # TF GraphDef with open(w, 'rb') as f: gd.ParseFromString(f.read()) @@ -319,10 +316,17 @@ class AutoBackend(nn.Module): self.context.execute_v2(list(self.binding_addrs.values())) y = [self.bindings[x].data for x in sorted(self.output_names)] elif self.coreml: # CoreML - im = im.cpu().numpy() - im = Image.fromarray((im[0] * 255).astype('uint8')) + im = im[0].cpu().numpy() + if self.task == 'classify': + from ultralytics.yolo.data.utils import IMAGENET_MEAN, IMAGENET_STD + + # im_pil = Image.fromarray(((im / 6 + 0.5) * 255).astype('uint8')) + for i in range(3): + im[..., i] *= IMAGENET_STD[i] + im[..., i] += IMAGENET_MEAN[i] + im_pil = Image.fromarray((im * 255).astype('uint8')) # im = im.resize((192, 320), Image.ANTIALIAS) - y = self.model.predict({'image': im}) # coordinates are xywh normalized + y = self.model.predict({'image': im_pil}) # coordinates are xywh normalized if 'confidence' in y: box = xywh2xyxy(y['coordinates'] * [[w, h, w, h]]) # xyxy pixels conf, cls = y['confidence'].max(1), y['confidence'].argmax(1).astype(np.float) diff --git a/ultralytics/nn/tasks.py b/ultralytics/nn/tasks.py index 9c09d61..7104f9d 100644 --- a/ultralytics/nn/tasks.py +++ b/ultralytics/nn/tasks.py @@ -11,7 +11,7 @@ import torch.nn as nn from ultralytics.nn.modules import (C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x, Classify, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Ensemble, Focus, GhostBottleneck, GhostConv, Segment) -from ultralytics.yolo.utils import DEFAULT_CFG_DICT, DEFAULT_CFG_KEYS, LOGGER, RANK, colorstr, yaml_load +from ultralytics.yolo.utils import DEFAULT_CFG_DICT, DEFAULT_CFG_KEYS, LOGGER, RANK, colorstr, emojis, yaml_load from ultralytics.yolo.utils.checks import check_requirements, check_yaml from ultralytics.yolo.utils.torch_utils import (fuse_conv_and_bn, fuse_deconv_and_bn, initialize_weights, intersect_dicts, make_divisible, model_info, scale_img, time_sync) @@ -76,7 +76,7 @@ class BaseModel(nn.Module): None """ c = m == self.model[-1] # is final layer, copy input as inplace fix - o = thop.profile(m, inputs=(x.clone() if c else x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPs + o = thop.profile(m, inputs=[x.clone() if c else x], verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPs t = time_sync() for _ in range(10): m(x.clone() if c else x) @@ -339,14 +339,20 @@ def torch_safe_load(weight): file = attempt_download_asset(weight) # search online if missing locally try: return torch.load(file, map_location='cpu'), file # load - except ModuleNotFoundError as e: - if e.name == 'omegaconf': # e.name is missing module name - LOGGER.warning(f'WARNING ⚠️ {weight} requires {e.name}, which is not in ultralytics requirements.' - f'\nAutoInstall will run now for {e.name} but this feature will be removed in the future.' - f'\nRecommend fixes are to train a new model using updated ultralytics package or to ' - f'download updated models from https://github.com/ultralytics/assets/releases/tag/v0.0.0') - if e.name != 'models': - check_requirements(e.name) # install missing module + except ModuleNotFoundError as e: # e.name is missing module name + if e.name == 'models': + raise TypeError( + emojis(f'ERROR ❌️ {weight} appears to be an Ultralytics YOLOv5 model originally trained ' + f'with https://github.com/ultralytics/yolov5.\nThis model is NOT forwards compatible with ' + f'YOLOv8 at https://github.com/ultralytics/ultralytics.' + f"\nRecommend fixes are to train a new model using the latest 'ultralytics' package or to " + f"run a command with an official YOLOv8 model, i.e. 'yolo predict model=yolov8n.pt'")) from e + LOGGER.warning(f"WARNING ⚠️ {weight} appears to require '{e.name}', which is not in ultralytics requirements." + f"\nAutoInstall will run now for '{e.name}' but this feature will be removed in the future." + f"\nRecommend fixes are to train a new model using the latest 'ultralytics' package or to " + f"run a command with an official YOLOv8 model, i.e. 'yolo predict model=yolov8n.pt'") + check_requirements(e.name) # install missing module + return torch.load(file, map_location='cpu'), file # load @@ -437,22 +443,21 @@ def parse_model(d, ch, verbose=True): # model_dict, input_channels(3) args[j] = eval(a) if isinstance(a, str) else a # eval strings n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain - if m in { - Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus, - BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x}: + if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus, + BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x): c1, c2 = ch[f], args[0] if c2 != nc: # if c2 not equal to number of classes (i.e. for Classify() output) c2 = make_divisible(c2 * gw, 8) args = [c1, c2, *args[1:]] - if m in {BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, C3x}: + if m in (BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, C3x): args.insert(2, n) # number of repeats n = 1 elif m is nn.BatchNorm2d: args = [ch[f]] elif m is Concat: c2 = sum(ch[x] for x in f) - elif m in {Detect, Segment}: + elif m in (Detect, Segment): args.append([ch[x] for x in f]) if m is Segment: args[2] = make_divisible(args[2] * gw, 8) @@ -490,11 +495,11 @@ def guess_model_task(model): def cfg2task(cfg): # Guess from YAML dictionary m = cfg['head'][-1][-2].lower() # output module name - if m in ['classify', 'classifier', 'cls', 'fc']: + if m in ('classify', 'classifier', 'cls', 'fc'): return 'classify' - if m in ['detect']: + if m == 'detect': return 'detect' - if m in ['segment']: + if m == 'segment': return 'segment' # Guess from model cfg diff --git a/ultralytics/tracker/__init__.py b/ultralytics/tracker/__init__.py index 7f4e335..85bf24e 100644 --- a/ultralytics/tracker/__init__.py +++ b/ultralytics/tracker/__init__.py @@ -2,3 +2,5 @@ from .track import register_tracker from .trackers import BOTSORT, BYTETracker + +__all__ = 'register_tracker', 'BOTSORT', 'BYTETracker' # allow simpler import diff --git a/ultralytics/tracker/trackers/__init__.py b/ultralytics/tracker/trackers/__init__.py index ae99ab6..10f1cf5 100644 --- a/ultralytics/tracker/trackers/__init__.py +++ b/ultralytics/tracker/trackers/__init__.py @@ -2,3 +2,5 @@ from .bot_sort import BOTSORT from .byte_tracker import BYTETracker + +__all__ = 'BOTSORT', 'BYTETracker' # allow simpler import diff --git a/ultralytics/tracker/utils/gmc.py b/ultralytics/tracker/utils/gmc.py index 653b5e8..09c4835 100644 --- a/ultralytics/tracker/utils/gmc.py +++ b/ultralytics/tracker/utils/gmc.py @@ -213,7 +213,7 @@ class GMC: prev_pt = np.array(self.prevKeyPoints[m.queryIdx].pt, dtype=np.int_) curr_pt = np.array(keypoints[m.trainIdx].pt, dtype=np.int_) curr_pt[0] += W - color = np.random.randint(0, 255, (3,)) + color = np.random.randint(0, 255, 3) color = (int(color[0]), int(color[1]), int(color[2])) matches_img = cv2.line(matches_img, prev_pt, curr_pt, tuple(color), 1, cv2.LINE_AA) diff --git a/ultralytics/yolo/__init__.py b/ultralytics/yolo/__init__.py index 1eea113..13e457d 100644 --- a/ultralytics/yolo/__init__.py +++ b/ultralytics/yolo/__init__.py @@ -2,4 +2,4 @@ from . import v8 -__all__ = ['v8'] +__all__ = 'v8', # tuple or list diff --git a/ultralytics/yolo/cfg/__init__.py b/ultralytics/yolo/cfg/__init__.py index 2fb9895..9e8116d 100644 --- a/ultralytics/yolo/cfg/__init__.py +++ b/ultralytics/yolo/cfg/__init__.py @@ -269,6 +269,11 @@ def entrypoint(debug=''): checks.check_yolo() return + # Task + task = overrides.get('task') + if task and task not in TASKS: + raise ValueError(f"Invalid 'task={task}'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}") + # Model model = overrides.pop('model', DEFAULT_CFG.model) if model is None: @@ -276,15 +281,11 @@ def entrypoint(debug=''): LOGGER.warning(f"WARNING ⚠️ 'model' is missing. Using default 'model={model}'.") from ultralytics.yolo.engine.model import YOLO overrides['model'] = model - model = YOLO(model) + model = YOLO(model, task=task) - # Task - task = overrides.get('task', model.task) - if task is not None: - if task not in TASKS: - raise ValueError(f"Invalid 'task={task}'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}") - else: - model.task = task + # Task Update + task = task or model.task + overrides['task'] = task # Mode if mode in {'predict', 'track'} and 'source' not in overrides: diff --git a/ultralytics/yolo/data/__init__.py b/ultralytics/yolo/data/__init__.py index 08dbf74..a7bf7fa 100644 --- a/ultralytics/yolo/data/__init__.py +++ b/ultralytics/yolo/data/__init__.py @@ -5,12 +5,5 @@ from .build import build_classification_dataloader, build_dataloader, load_infer from .dataset import ClassificationDataset, SemanticDataset, YOLODataset from .dataset_wrappers import MixAndRectDataset -__all__ = [ - 'BaseDataset', - 'ClassificationDataset', - 'MixAndRectDataset', - 'SemanticDataset', - 'YOLODataset', - 'build_classification_dataloader', - 'build_dataloader', - 'load_inference_source',] +__all__ = ('BaseDataset', 'ClassificationDataset', 'MixAndRectDataset', 'SemanticDataset', 'YOLODataset', + 'build_classification_dataloader', 'build_dataloader', 'load_inference_source') diff --git a/ultralytics/yolo/data/augment.py b/ultralytics/yolo/data/augment.py index 6f31799..136e015 100644 --- a/ultralytics/yolo/data/augment.py +++ b/ultralytics/yolo/data/augment.py @@ -564,7 +564,7 @@ class Albumentations: A.CLAHE(p=0.01), A.RandomBrightnessContrast(p=0.0), A.RandomGamma(p=0.0), - A.ImageCompression(quality_lower=75, p=0.0),] # transforms + A.ImageCompression(quality_lower=75, p=0.0)] # transforms self.transform = A.Compose(T, bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels'])) LOGGER.info(prefix + ', '.join(f'{x}'.replace('always_apply=False, ', '') for x in T if x.p)) @@ -671,14 +671,14 @@ def v8_transforms(dataset, imgsz, hyp): shear=hyp.shear, perspective=hyp.perspective, pre_transform=LetterBox(new_shape=(imgsz, imgsz)), - ),]) + )]) return Compose([ pre_transform, MixUp(dataset, pre_transform=pre_transform, p=hyp.mixup), Albumentations(p=1.0), RandomHSV(hgain=hyp.hsv_h, sgain=hyp.hsv_s, vgain=hyp.hsv_v), RandomFlip(direction='vertical', p=hyp.flipud), - RandomFlip(direction='horizontal', p=hyp.fliplr),]) # transforms + RandomFlip(direction='horizontal', p=hyp.fliplr)]) # transforms # Classification augmentations ----------------------------------------------------------------------------------------- @@ -719,8 +719,8 @@ def classify_albumentations( if vflip > 0: T += [A.VerticalFlip(p=vflip)] if jitter > 0: - color_jitter = (float(jitter),) * 3 # repeat value for brightness, contrast, saturation, 0 hue - T += [A.ColorJitter(*color_jitter, 0)] + jitter = float(jitter) + T += [A.ColorJitter(jitter, jitter, jitter, 0)] # brightness, contrast, saturation, 0 hue else: # Use fixed crop for eval set (reproducibility) T = [A.SmallestMaxSize(max_size=size), A.CenterCrop(height=size, width=size)] T += [A.Normalize(mean=mean, std=std), ToTensorV2()] # Normalize and convert to Tensor diff --git a/ultralytics/yolo/data/base.py b/ultralytics/yolo/data/base.py index 7860b6d..7557751 100644 --- a/ultralytics/yolo/data/base.py +++ b/ultralytics/yolo/data/base.py @@ -24,20 +24,18 @@ class BaseDataset(Dataset): label_path (str): label path, this can also be an ann_file or other custom label path. """ - def __init__( - self, - img_path, - imgsz=640, - cache=False, - augment=True, - hyp=None, - prefix='', - rect=False, - batch_size=None, - stride=32, - pad=0.5, - single_cls=False, - ): + def __init__(self, + img_path, + imgsz=640, + cache=False, + augment=True, + hyp=None, + prefix='', + rect=False, + batch_size=None, + stride=32, + pad=0.5, + single_cls=False): super().__init__() self.img_path = img_path self.imgsz = imgsz diff --git a/ultralytics/yolo/data/dataloaders/v5augmentations.py b/ultralytics/yolo/data/dataloaders/v5augmentations.py index 0595d7a..acd6d83 100644 --- a/ultralytics/yolo/data/dataloaders/v5augmentations.py +++ b/ultralytics/yolo/data/dataloaders/v5augmentations.py @@ -335,8 +335,8 @@ def classify_albumentations( if vflip > 0: T += [A.VerticalFlip(p=vflip)] if jitter > 0: - color_jitter = (float(jitter),) * 3 # repeat value for brightness, contrast, satuaration, 0 hue - T += [A.ColorJitter(*color_jitter, 0)] + jitter = float(jitter) + T += [A.ColorJitter(jitter, jitter, jitter, 0)] # brightness, contrast, satuaration, 0 hue else: # Use fixed crop for eval set (reproducibility) T = [A.SmallestMaxSize(max_size=size), A.CenterCrop(height=size, width=size)] T += [A.Normalize(mean=mean, std=std), ToTensorV2()] # Normalize and convert to Tensor diff --git a/ultralytics/yolo/data/dataset.py b/ultralytics/yolo/data/dataset.py index 76a3a32..abb743c 100644 --- a/ultralytics/yolo/data/dataset.py +++ b/ultralytics/yolo/data/dataset.py @@ -4,13 +4,16 @@ from itertools import repeat from multiprocessing.pool import ThreadPool from pathlib import Path +import cv2 +import numpy as np +import torch import torchvision from tqdm import tqdm from ..utils import NUM_THREADS, TQDM_BAR_FORMAT, is_dir_writeable -from .augment import * +from .augment import Compose, Format, Instances, LetterBox, classify_albumentations, classify_transforms, v8_transforms from .base import BaseDataset -from .utils import HELP_URL, LOCAL_RANK, get_hash, img2label_paths, verify_image_label +from .utils import HELP_URL, LOCAL_RANK, LOGGER, get_hash, img2label_paths, verify_image_label class YOLODataset(BaseDataset): diff --git a/ultralytics/yolo/engine/exporter.py b/ultralytics/yolo/engine/exporter.py index 886a0b1..dfdfa68 100644 --- a/ultralytics/yolo/engine/exporter.py +++ b/ultralytics/yolo/engine/exporter.py @@ -50,7 +50,6 @@ TensorFlow.js: import json import os import platform -import re import subprocess import time import warnings @@ -90,9 +89,9 @@ def export_formats(): ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True], ['TensorFlow GraphDef', 'pb', '.pb', True, True], ['TensorFlow Lite', 'tflite', '.tflite', True, False], - ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False], - ['TensorFlow.js', 'tfjs', '_web_model', False, False], - ['PaddlePaddle', 'paddle', '_paddle_model', True, True],] + ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', True, False], + ['TensorFlow.js', 'tfjs', '_web_model', True, False], + ['PaddlePaddle', 'paddle', '_paddle_model', True, True], ] return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU']) @@ -100,6 +99,15 @@ EXPORT_FORMATS_LIST = list(export_formats()['Argument'][1:]) EXPORT_FORMATS_TABLE = str(export_formats()) +def gd_outputs(gd): + # TensorFlow GraphDef model output node names + name_list, input_list = [], [] + for node in gd.node: # tensorflow.core.framework.node_def_pb2.NodeDef + name_list.append(node.name) + input_list.extend(node.input) + return sorted(f'{x}:0' for x in list(set(name_list) - set(input_list)) if not x.startswith('NoOp')) + + def try_export(inner_func): # YOLOv8 export decorator, i..e @try_export inner_args = get_default_args(inner_func) @@ -164,10 +172,10 @@ class Exporter: # Checks model.names = check_class_names(model.names) self.imgsz = check_imgsz(self.args.imgsz, stride=model.stride, min_dim=2) # check image size - if model.task == 'classify': - self.args.nms = self.args.agnostic_nms = False if self.args.optimize: assert self.device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu' + if edgetpu and not LINUX: + raise SystemError('Edge TPU export only supported on Linux. See https://coral.ai/docs/edgetpu/compiler/') # Input im = torch.zeros(self.args.batch, 3, *self.imgsz).to(self.device) @@ -208,7 +216,7 @@ class Exporter: self.file = file self.output_shape = tuple(y.shape) if isinstance(y, torch.Tensor) else tuple(tuple(x.shape) for x in y) self.pretty_name = self.file.stem.replace('yolo', 'YOLO') - description = f'Ultralytics {self.pretty_name} model' + f'trained on {Path(self.args.data).name}' \ + description = f'Ultralytics {self.pretty_name} model ' + f'trained on {Path(self.args.data).name}' \ if self.args.data else '(untrained)' self.metadata = { 'description': description, @@ -239,8 +247,7 @@ class Exporter: 'Please consider contributing to the effort if you have TF expertise. Thank you!') nms = False self.args.int8 |= edgetpu - f[5], s_model = self._export_saved_model(nms=nms or self.args.agnostic_nms or tfjs, - agnostic_nms=self.args.agnostic_nms or tfjs) + f[5], s_model = self._export_saved_model() if pb or tfjs: # pb prerequisite to tfjs f[6], _ = self._export_pb(s_model) if tflite: @@ -386,7 +393,7 @@ class Exporter: check_requirements('coremltools>=6.0') import coremltools as ct # noqa - class iOSModel(torch.nn.Module): + class iOSDetectModel(torch.nn.Module): # Wrap an Ultralytics YOLO model for iOS export def __init__(self, model, im): super().__init__() @@ -405,29 +412,36 @@ class Exporter: LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...') f = self.file.with_suffix('.mlmodel') + bias = [0.0, 0.0, 0.0] + scale = 1 / 255 + classifier_config = None if self.model.task == 'classify': bias = [-x for x in IMAGENET_MEAN] scale = 1 / 255 / (sum(IMAGENET_STD) / 3) classifier_config = ct.ClassifierConfig(list(self.model.names.values())) if self.args.nms else None - else: - bias = [0.0, 0.0, 0.0] - scale = 1 / 255 - classifier_config = None - model = iOSModel(self.model, self.im).eval() if self.args.nms else self.model - ts = torch.jit.trace(model, self.im, strict=False) # TorchScript model + model = self.model + elif self.model.task == 'detect': + model = iOSDetectModel(self.model, self.im) if self.args.nms else self.model + elif self.model.task == 'segment': + # TODO CoreML Segmentation model pipelining + model = self.model + + ts = torch.jit.trace(model.eval(), self.im, strict=False) # TorchScript model ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=self.im.shape, scale=scale, bias=bias)], classifier_config=classifier_config) bits, mode = (8, 'kmeans_lut') if self.args.int8 else (16, 'linear') if self.args.half else (32, None) if bits < 32: ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode) - if self.args.nms: + if self.args.nms and self.model.task == 'detect': ct_model = self._pipeline_coreml(ct_model) - ct_model.short_description = self.metadata['description'] - ct_model.author = self.metadata['author'] - ct_model.license = self.metadata['license'] - ct_model.version = self.metadata['version'] + m = self.metadata # metadata dict + ct_model.short_description = m['description'] + ct_model.author = m['author'] + ct_model.license = m['license'] + ct_model.version = m['version'] + ct_model.user_defined_metadata.update({k: str(v) for k, v in m.items() if k in ('stride', 'task', 'names')}) ct_model.save(str(f)) return f, ct_model @@ -497,14 +511,7 @@ class Exporter: return f, None @try_export - def _export_saved_model(self, - nms=False, - agnostic_nms=False, - topk_per_class=100, - topk_all=100, - iou_thres=0.45, - conf_thres=0.25, - prefix=colorstr('TensorFlow SavedModel:')): + def _export_saved_model(self, prefix=colorstr('TensorFlow SavedModel:')): # YOLOv8 TensorFlow SavedModel export try: @@ -562,6 +569,9 @@ class Exporter: @try_export def _export_tflite(self, keras_model, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')): # YOLOv8 TensorFlow Lite export + import tensorflow as tf # noqa + + LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...') saved_model = Path(str(self.file).replace(self.file.suffix, '_saved_model')) if self.args.int8: f = saved_model / (self.file.stem + 'yolov8n_integer_quant.tflite') # fp32 in/out @@ -572,9 +582,6 @@ class Exporter: return str(f), None # noqa # OLD VERSION BELOW --------------------------------------------------------------- - import tensorflow as tf # noqa - - LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...') batch_size, ch, *imgsz = list(self.im.shape) # BCHW f = str(self.file).replace(self.file.suffix, '-fp16.tflite') @@ -619,7 +626,9 @@ class Exporter: LOGGER.info(f'\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}') sudo = subprocess.run('sudo --version >/dev/null', shell=True).returncode == 0 # sudo installed on system for c in ( - 'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -', + # 'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -', # errors + 'wget --no-check-certificate -q -O - https://packages.cloud.google.com/apt/doc/apt-key.gpg | ' + 'sudo apt-key add -', 'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | ' # no comma 'sudo tee /etc/apt/sources.list.d/coral-edgetpu.list', 'sudo apt-get update', @@ -639,30 +648,36 @@ class Exporter: def _export_tfjs(self, prefix=colorstr('TensorFlow.js:')): # YOLOv8 TensorFlow.js export check_requirements('tensorflowjs') + import tensorflow as tf import tensorflowjs as tfjs # noqa LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...') f = str(self.file).replace(self.file.suffix, '_web_model') # js dir f_pb = self.file.with_suffix('.pb') # *.pb path - f_json = Path(f) / 'model.json' # *.json path - cmd = f'tensorflowjs_converter --input_format=tf_frozen_model ' \ - f'--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}' + gd = tf.Graph().as_graph_def() # TF GraphDef + with open(f_pb, 'rb') as file: + gd.ParseFromString(file.read()) + outputs = ','.join(gd_outputs(gd)) + LOGGER.info(f'\n{prefix} output node names: {outputs}') + + cmd = f'tensorflowjs_converter --input_format=tf_frozen_model --output_node_names={outputs} {f_pb} {f}' subprocess.run(cmd.split(), check=True) - with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order - subst = re.sub( - r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, ' - r'"Identity.?.?": {"name": "Identity.?.?"}, ' - r'"Identity.?.?": {"name": "Identity.?.?"}, ' - r'"Identity.?.?": {"name": "Identity.?.?"}}}', - r'{"outputs": {"Identity": {"name": "Identity"}, ' - r'"Identity_1": {"name": "Identity_1"}, ' - r'"Identity_2": {"name": "Identity_2"}, ' - r'"Identity_3": {"name": "Identity_3"}}}', - f_json.read_text(), - ) - j.write(subst) + # f_json = Path(f) / 'model.json' # *.json path + # with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order + # subst = re.sub( + # r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, ' + # r'"Identity.?.?": {"name": "Identity.?.?"}, ' + # r'"Identity.?.?": {"name": "Identity.?.?"}, ' + # r'"Identity.?.?": {"name": "Identity.?.?"}}}', + # r'{"outputs": {"Identity": {"name": "Identity"}, ' + # r'"Identity_1": {"name": "Identity_1"}, ' + # r'"Identity_2": {"name": "Identity_2"}, ' + # r'"Identity_3": {"name": "Identity_3"}}}', + # f_json.read_text(), + # ) + # j.write(subst) yaml_save(Path(f) / 'metadata.yaml', self.metadata) # add metadata.yaml return f, None @@ -680,7 +695,7 @@ class Exporter: model_meta.license = self.metadata['license'] # Label file - tmp_file = file.parent / 'temp_meta.txt' + tmp_file = Path(file).parent / 'temp_meta.txt' with open(tmp_file, 'w') as f: f.write(str(self.metadata)) @@ -718,7 +733,7 @@ class Exporter: b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER) metadata_buf = b.Output() - populator = _metadata.MetadataPopulator.with_model_file(file) + populator = _metadata.MetadataPopulator.with_model_file(str(file)) populator.load_metadata_buffer(metadata_buf) populator.load_associated_files([str(tmp_file)]) populator.populate() diff --git a/ultralytics/yolo/engine/model.py b/ultralytics/yolo/engine/model.py index 2e65836..104dcaf 100644 --- a/ultralytics/yolo/engine/model.py +++ b/ultralytics/yolo/engine/model.py @@ -2,7 +2,6 @@ import sys from pathlib import Path -from typing import List from ultralytics import yolo # noqa from ultralytics.nn.tasks import (ClassificationModel, DetectionModel, SegmentationModel, attempt_load_one_weight, @@ -68,7 +67,7 @@ class YOLO: list(ultralytics.yolo.engine.results.Results): The prediction results. """ - def __init__(self, model='yolov8n.pt') -> None: + def __init__(self, model='yolov8n.pt', task=None) -> None: """ Initializes the YOLO model. @@ -91,9 +90,9 @@ class YOLO: if not suffix and Path(model).stem in GITHUB_ASSET_STEMS: model, suffix = Path(model).with_suffix('.pt'), '.pt' # add suffix, i.e. yolov8n -> yolov8n.pt if suffix == '.yaml': - self._new(model) + self._new(model, task) else: - self._load(model) + self._load(model, task) def __call__(self, source=None, stream=False, **kwargs): return self.predict(source, stream, **kwargs) @@ -102,17 +101,18 @@ class YOLO: name = self.__class__.__name__ raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - def _new(self, cfg: str, verbose=True): + def _new(self, cfg: str, task=None, verbose=True): """ Initializes a new model and infers the task type from the model definitions. Args: cfg (str): model configuration file + task (str) or (None): model task verbose (bool): display model info on load """ self.cfg = check_yaml(cfg) # check YAML cfg_dict = yaml_load(self.cfg, append_filename=True) # model dict - self.task = guess_model_task(cfg_dict) + self.task = task or guess_model_task(cfg_dict) self.model = TASK_MAP[self.task][0](cfg_dict, verbose=verbose and RANK == -1) # build model self.overrides['model'] = self.cfg @@ -121,12 +121,13 @@ class YOLO: self.model.args = {k: v for k, v in args.items() if k in DEFAULT_CFG_KEYS} # attach args to model self.model.task = self.task - def _load(self, weights: str, task=''): + def _load(self, weights: str, task=None): """ Initializes a new model and infers the task type from the model head. Args: weights (str): model checkpoint to be loaded + task (str) or (None): model task """ suffix = Path(weights).suffix if suffix == '.pt': @@ -137,7 +138,7 @@ class YOLO: else: weights = check_file(weights) self.model, self.ckpt = weights, None - self.task = guess_model_task(weights) + self.task = task or guess_model_task(weights) self.ckpt_path = weights self.overrides['model'] = weights diff --git a/ultralytics/yolo/engine/predictor.py b/ultralytics/yolo/engine/predictor.py index 17eee83..de15d51 100644 --- a/ultralytics/yolo/engine/predictor.py +++ b/ultralytics/yolo/engine/predictor.py @@ -32,7 +32,6 @@ from collections import defaultdict from pathlib import Path import cv2 -import torch from ultralytics.nn.autobackend import AutoBackend from ultralytics.yolo.cfg import get_cfg diff --git a/ultralytics/yolo/engine/trainer.py b/ultralytics/yolo/engine/trainer.py index 263750b..a0ce215 100644 --- a/ultralytics/yolo/engine/trainer.py +++ b/ultralytics/yolo/engine/trainer.py @@ -242,7 +242,7 @@ class BaseTrainer: metric_keys = self.validator.metrics.keys + self.label_loss_items(prefix='val') self.metrics = dict(zip(metric_keys, [0] * len(metric_keys))) # TODO: init metrics for plot_results()? self.ema = ModelEMA(self.model) - if self.args.plots: + if self.args.plots and not self.args.v5loader: self.plot_training_labels() self.resume_training(ckpt) self.scheduler.last_epoch = self.start_epoch - 1 # do not move diff --git a/ultralytics/yolo/utils/__init__.py b/ultralytics/yolo/utils/__init__.py index e0ad000..da289d3 100644 --- a/ultralytics/yolo/utils/__init__.py +++ b/ultralytics/yolo/utils/__init__.py @@ -18,7 +18,6 @@ from typing import Union import cv2 import numpy as np import pandas as pd -import requests import torch import yaml @@ -517,10 +516,7 @@ def set_sentry(): ((is_pip_package() and not is_git_dir()) or (get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git' and get_git_branch() == 'main')): - import hashlib - import sentry_sdk # noqa - sentry_sdk.init( dsn='https://f805855f03bb4363bc1e16cb7d87b654@o4504521589325824.ingest.sentry.io/4504521592406016', debug=False, diff --git a/ultralytics/yolo/utils/benchmarks.py b/ultralytics/yolo/utils/benchmarks.py index 993e6d1..3b3d1be 100644 --- a/ultralytics/yolo/utils/benchmarks.py +++ b/ultralytics/yolo/utils/benchmarks.py @@ -30,14 +30,14 @@ import pandas as pd from ultralytics import YOLO from ultralytics.yolo.engine.exporter import export_formats -from ultralytics.yolo.utils import LOGGER, ROOT, SETTINGS +from ultralytics.yolo.utils import LINUX, LOGGER, ROOT, SETTINGS from ultralytics.yolo.utils.checks import check_yolo from ultralytics.yolo.utils.downloads import download from ultralytics.yolo.utils.files import file_size from ultralytics.yolo.utils.torch_utils import select_device -def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, half=False, device='cpu', hard_fail=0.30): +def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, half=False, device='cpu', hard_fail=False): device = select_device(device, verbose=False) if isinstance(model, (str, Path)): model = YOLO(model) @@ -45,11 +45,10 @@ def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, hal y = [] t0 = time.time() for i, (name, format, suffix, cpu, gpu) in export_formats().iterrows(): # index, (name, format, suffix, CPU, GPU) + emoji = '❌' # indicates export failure try: - assert i not in (9, 10), 'inference not supported' # Edge TPU and TF.js are unsupported - assert i != 5 or platform.system() == 'Darwin', 'inference only supported on macOS>=10.13' # CoreML - assert i != 11 or model.task != 'classify', 'paddle-classify bug' - + assert i != 11, 'paddle exports coming soon' + assert i != 9 or LINUX, 'Edge TPU export only supported on Linux' if 'cpu' in device.type: assert cpu, 'inference not supported on CPU' if 'cuda' in device.type: @@ -61,13 +60,16 @@ def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, hal export = model # PyTorch format else: filename = model.export(imgsz=imgsz, format=format, half=half, device=device) # all others - export = YOLO(filename) + export = YOLO(filename, task=model.task) assert suffix in str(filename), 'export failed' + emoji = '❎' # indicates export succeeded # Predict + assert i not in (9, 10), 'inference not supported' # Edge TPU and TF.js are unsupported + assert i != 5 or platform.system() == 'Darwin', 'inference only supported on macOS>=10.13' # CoreML if not (ROOT / 'assets/bus.jpg').exists(): download(url='https://ultralytics.com/images/bus.jpg', dir=ROOT / 'assets') - export.predict(ROOT / 'assets/bus.jpg', imgsz=imgsz, device=device, half=half) # test + export.predict(ROOT / 'assets/bus.jpg', imgsz=imgsz, device=device, half=half) # Validate if model.task == 'detect': @@ -84,17 +86,16 @@ def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, hal if hard_fail: assert type(e) is AssertionError, f'Benchmark hard_fail for {name}: {e}' LOGGER.warning(f'ERROR ❌️ Benchmark failure for {name}: {e}') - y.append([name, '❌', None, None, None]) # mAP, t_inference + y.append([name, emoji, None, None, None]) # mAP, t_inference # Print results check_yolo(device=device) # print system info - c = ['Format', 'Status❔', 'Size (MB)', key, 'Inference time (ms/im)'] - df = pd.DataFrame(y, columns=c) + df = pd.DataFrame(y, columns=['Format', 'Status❔', 'Size (MB)', key, 'Inference time (ms/im)']) name = Path(model.ckpt_path).name s = f'\nBenchmarks complete for {name} on {data} at imgsz={imgsz} ({time.time() - t0:.2f}s)\n{df}\n' LOGGER.info(s) - with open('benchmarks.log', 'a') as f: + with open('benchmarks.log', 'a', errors='ignore', encoding='utf-8') as f: f.write(s) if hard_fail and isinstance(hard_fail, float): diff --git a/ultralytics/yolo/utils/callbacks/__init__.py b/ultralytics/yolo/utils/callbacks/__init__.py index a885dec..fb5dfe2 100644 --- a/ultralytics/yolo/utils/callbacks/__init__.py +++ b/ultralytics/yolo/utils/callbacks/__init__.py @@ -1,5 +1,3 @@ from .base import add_integration_callbacks, default_callbacks -__all__ = [ - 'add_integration_callbacks', - 'default_callbacks',] +__all__ = 'add_integration_callbacks', 'default_callbacks' diff --git a/ultralytics/yolo/utils/checks.py b/ultralytics/yolo/utils/checks.py index e40f787..013f5ad 100644 --- a/ultralytics/yolo/utils/checks.py +++ b/ultralytics/yolo/utils/checks.py @@ -137,7 +137,6 @@ def check_latest_pypi_version(package_name='ultralytics'): def check_pip_update(): from ultralytics import __version__ latest = check_latest_pypi_version() - latest = '9.0.0' if pkg.parse_version(__version__) < pkg.parse_version(latest): LOGGER.info(f'New https://pypi.org/project/ultralytics/{latest} available 😃 ' f"Update with 'pip install -U ultralytics'") @@ -239,7 +238,7 @@ def check_requirements(requirements=ROOT.parent / 'requirements.txt', exclude=() LOGGER.warning(f'{prefix} ❌ {e}') -def check_suffix(file='yolov8n.pt', suffix=('.pt',), msg=''): +def check_suffix(file='yolov8n.pt', suffix='.pt', msg=''): # Check file(s) for acceptable suffix if file and suffix: if isinstance(suffix, str): diff --git a/ultralytics/yolo/utils/instance.py b/ultralytics/yolo/utils/instance.py index cee4a43..95a62ca 100644 --- a/ultralytics/yolo/utils/instance.py +++ b/ultralytics/yolo/utils/instance.py @@ -10,9 +10,8 @@ import numpy as np from .ops import ltwh2xywh, ltwh2xyxy, resample_segments, xywh2ltwh, xywh2xyxy, xyxy2ltwh, xyxy2xywh -# From PyTorch internals def _ntuple(n): - + # From PyTorch internals def parse(x): return x if isinstance(x, abc.Iterable) else tuple(repeat(x, n)) @@ -26,7 +25,7 @@ to_4tuple = _ntuple(4) # `ltwh` means left top and width, height(coco format) _formats = ['xyxy', 'xywh', 'ltwh'] -__all__ = ['Bboxes'] +__all__ = 'Bboxes', # tuple or list class Bboxes: diff --git a/ultralytics/yolo/utils/plotting.py b/ultralytics/yolo/utils/plotting.py index 76c042c..fd773b4 100644 --- a/ultralytics/yolo/utils/plotting.py +++ b/ultralytics/yolo/utils/plotting.py @@ -207,8 +207,7 @@ def plot_labels(boxes, cls, names=(), save_dir=Path('')): def save_one_box(xyxy, im, file=Path('im.jpg'), gain=1.02, pad=10, square=False, BGR=False, save=True): # Save image crop as {file} with crop size multiple {gain} and {pad} pixels. Save and/or return crop - xyxy = torch.Tensor(xyxy).view(-1, 4) - b = xyxy2xywh(xyxy) # boxes + b = xyxy2xywh(xyxy.view(-1, 4)) # boxes if square: b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # attempt rectangle to square b[:, 2:] = b[:, 2:] * gain + pad # box wh * gain + pad diff --git a/ultralytics/yolo/utils/torch_utils.py b/ultralytics/yolo/utils/torch_utils.py index 84cb5ec..e918808 100644 --- a/ultralytics/yolo/utils/torch_utils.py +++ b/ultralytics/yolo/utils/torch_utils.py @@ -195,7 +195,7 @@ def get_flops(model, imgsz=640): p = next(model.parameters()) stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32 # max stride im = torch.empty((1, p.shape[1], stride, stride), device=p.device) # input image in BCHW format - flops = thop.profile(deepcopy(model), inputs=(im,), verbose=False)[0] / 1E9 * 2 # stride GFLOPs + flops = thop.profile(deepcopy(model), inputs=[im], verbose=False)[0] / 1E9 * 2 # stride GFLOPs imgsz = imgsz if isinstance(imgsz, list) else [imgsz, imgsz] # expand if int/float flops = flops * imgsz[0] / stride * imgsz[1] / stride # 640x640 GFLOPs return flops @@ -374,7 +374,7 @@ def profile(input, ops, n=10, device=None): m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m tf, tb, t = 0, 0, [0, 0, 0] # dt forward, backward try: - flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPs + flops = thop.profile(m, inputs=[x], verbose=False)[0] / 1E9 * 2 # GFLOPs except Exception: flops = 0 diff --git a/ultralytics/yolo/v8/__init__.py b/ultralytics/yolo/v8/__init__.py index 50c710d..1f03762 100644 --- a/ultralytics/yolo/v8/__init__.py +++ b/ultralytics/yolo/v8/__init__.py @@ -2,4 +2,4 @@ from ultralytics.yolo.v8 import classify, detect, segment -__all__ = ['classify', 'segment', 'detect'] +__all__ = 'classify', 'segment', 'detect' diff --git a/ultralytics/yolo/v8/classify/__init__.py b/ultralytics/yolo/v8/classify/__init__.py index 6b2d641..7fff3a8 100644 --- a/ultralytics/yolo/v8/classify/__init__.py +++ b/ultralytics/yolo/v8/classify/__init__.py @@ -4,4 +4,4 @@ from ultralytics.yolo.v8.classify.predict import ClassificationPredictor, predic from ultralytics.yolo.v8.classify.train import ClassificationTrainer, train from ultralytics.yolo.v8.classify.val import ClassificationValidator, val -__all__ = ['ClassificationPredictor', 'predict', 'ClassificationTrainer', 'train', 'ClassificationValidator', 'val'] +__all__ = 'ClassificationPredictor', 'predict', 'ClassificationTrainer', 'train', 'ClassificationValidator', 'val' diff --git a/ultralytics/yolo/v8/detect/__init__.py b/ultralytics/yolo/v8/detect/__init__.py index 2145179..972b5e8 100644 --- a/ultralytics/yolo/v8/detect/__init__.py +++ b/ultralytics/yolo/v8/detect/__init__.py @@ -4,4 +4,4 @@ from .predict import DetectionPredictor, predict from .train import DetectionTrainer, train from .val import DetectionValidator, val -__all__ = ['DetectionPredictor', 'predict', 'DetectionTrainer', 'train', 'DetectionValidator', 'val'] +__all__ = 'DetectionPredictor', 'predict', 'DetectionTrainer', 'train', 'DetectionValidator', 'val' diff --git a/ultralytics/yolo/v8/segment/__init__.py b/ultralytics/yolo/v8/segment/__init__.py index 099c8a5..a9831ac 100644 --- a/ultralytics/yolo/v8/segment/__init__.py +++ b/ultralytics/yolo/v8/segment/__init__.py @@ -4,4 +4,4 @@ from .predict import SegmentationPredictor, predict from .train import SegmentationTrainer, train from .val import SegmentationValidator, val -__all__ = ['SegmentationPredictor', 'predict', 'SegmentationTrainer', 'train', 'SegmentationValidator', 'val'] +__all__ = 'SegmentationPredictor', 'predict', 'SegmentationTrainer', 'train', 'SegmentationValidator', 'val'