`ultralytics 8.0.47` Docker and reformat updates (#1153)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
pull/1174/head^2 v8.0.47
Glenn Jocher 2 years ago committed by GitHub
parent d4be4cb24b
commit a58f766f94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/workflows/ci.yaml
  2. 3
      README.zh-CN.md
  3. 3
      docker/Dockerfile
  4. 3
      docker/Dockerfile-arm64
  5. 3
      docker/Dockerfile-cpu
  6. 4
      docs/predict.md
  7. 17
      docs/tasks/tracking.md
  8. 1
      requirements.txt
  9. 12
      setup.cfg
  10. 4
      tests/test_cli.py
  11. 5
      tests/test_python.py
  12. 4
      ultralytics/__init__.py
  13. 3
      ultralytics/hub/utils.py
  14. 26
      ultralytics/nn/autobackend.py
  15. 39
      ultralytics/nn/tasks.py
  16. 2
      ultralytics/tracker/__init__.py
  17. 2
      ultralytics/tracker/trackers/__init__.py
  18. 2
      ultralytics/tracker/utils/gmc.py
  19. 2
      ultralytics/yolo/__init__.py
  20. 17
      ultralytics/yolo/cfg/__init__.py
  21. 11
      ultralytics/yolo/data/__init__.py
  22. 10
      ultralytics/yolo/data/augment.py
  23. 6
      ultralytics/yolo/data/base.py
  24. 4
      ultralytics/yolo/data/dataloaders/v5augmentations.py
  25. 7
      ultralytics/yolo/data/dataset.py
  26. 113
      ultralytics/yolo/engine/exporter.py
  27. 17
      ultralytics/yolo/engine/model.py
  28. 1
      ultralytics/yolo/engine/predictor.py
  29. 2
      ultralytics/yolo/engine/trainer.py
  30. 4
      ultralytics/yolo/utils/__init__.py
  31. 25
      ultralytics/yolo/utils/benchmarks.py
  32. 4
      ultralytics/yolo/utils/callbacks/__init__.py
  33. 3
      ultralytics/yolo/utils/checks.py
  34. 5
      ultralytics/yolo/utils/instance.py
  35. 3
      ultralytics/yolo/utils/plotting.py
  36. 4
      ultralytics/yolo/utils/torch_utils.py
  37. 2
      ultralytics/yolo/v8/__init__.py
  38. 2
      ultralytics/yolo/v8/classify/__init__.py
  39. 2
      ultralytics/yolo/v8/detect/__init__.py
  40. 2
      ultralytics/yolo/v8/segment/__init__.py

@ -38,7 +38,7 @@ jobs:
if [ "${{ matrix.os }}" == "macos-latest" ]; then if [ "${{ matrix.os }}" == "macos-latest" ]; then
pip install -e . coremltools openvino-dev tensorflow-macos --extra-index-url https://download.pytorch.org/whl/cpu pip install -e . coremltools openvino-dev tensorflow-macos --extra-index-url https://download.pytorch.org/whl/cpu
else 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 fi
yolo export format=tflite yolo export format=tflite
- name: Check environment - name: Check environment
@ -66,7 +66,7 @@ jobs:
shell: python shell: python
run: | run: |
from ultralytics.yolo.utils.benchmarks import benchmark 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 - name: Benchmark Summary
run: cat benchmarks.log run: cat benchmarks.log

@ -223,7 +223,8 @@ Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自
## <div align="center">License</div> ## <div align="center">License</div>
- YOLOv8 在两种不同的 License 下可用: YOLOv8 在两种不同的 License 下可用:
- **GPL-3.0 License**: 查看 [License](https://github.com/ultralytics/ultralytics/blob/main/LICENSE) 文件的详细信息。 - **GPL-3.0 License**: 查看 [License](https://github.com/ultralytics/ultralytics/blob/main/LICENSE) 文件的详细信息。
- **企业License**:在没有 GPL-3.0 开源要求的情况下为商业产品开发提供更大的灵活性。典型用例是将 Ultralytics 软件和 AI - **企业License**:在没有 GPL-3.0 开源要求的情况下为商业产品开发提供更大的灵活性。典型用例是将 Ultralytics 软件和 AI
模型嵌入到商业产品和应用程序中。在以下位置申请企业许可证 [Ultralytics 许可](https://ultralytics.com/license) 。 模型嵌入到商业产品和应用程序中。在以下位置申请企业许可证 [Ultralytics 许可](https://ultralytics.com/license) 。

@ -29,9 +29,8 @@ WORKDIR /usr/src/ultralytics
RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics
# Install pip packages # Install pip packages
COPY requirements.txt .
RUN python3 -m pip install --upgrade pip wheel 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 # Set environment variables
ENV OMP_NUM_THREADS=1 ENV OMP_NUM_THREADS=1

@ -24,9 +24,8 @@ WORKDIR /usr/src/ultralytics
RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics
# Install pip packages # Install pip packages
COPY requirements.txt .
RUN python3 -m pip install --upgrade pip wheel 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 # Cleanup
ENV DEBIAN_FRONTEND teletype ENV DEBIAN_FRONTEND teletype

@ -24,9 +24,8 @@ WORKDIR /usr/src/ultralytics
RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics RUN git clone https://github.com/ultralytics/ultralytics /usr/src/ultralytics
# Install pip packages # Install pip packages
COPY requirements.txt .
RUN python3 -m pip install --upgrade pip wheel 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 --extra-index-url https://download.pytorch.org/whl/cpu
# Cleanup # Cleanup

@ -97,8 +97,8 @@ Class reference documentation for `Results` module and its components can be fou
## Plotting results ## Plotting results
You can use `plot()` function of `Result` object to plot results on in image object. It plots all components(boxes, masks, You can use `plot()` function of `Result` object to plot results on in image object. It plots all components(boxes,
classification logits, etc) found in the results object masks, classification logits, etc) found in the results object
```python ```python
res = model(img) res = model(img)

@ -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 ## Configuration
### Tracking ### 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 "" !!! example ""
=== "Python" === "Python"
@ -65,7 +69,10 @@ Tracking shares the configuration with predict, i.e `conf`, `iou`, `show`. More
``` ```
### Tracker ### 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 "" !!! example ""
=== "Python" === "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' 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.

@ -37,7 +37,6 @@ seaborn>=0.11.0
# Extras -------------------------------------- # Extras --------------------------------------
psutil # system utilization psutil # system utilization
thop>=0.1.1 # FLOPs computation thop>=0.1.1 # FLOPs computation
wheel>=0.38.0 # Snyk vulnerability fix
# ipython # interactive notebook # ipython # interactive notebook
# albumentations>=1.0.3 # albumentations>=1.0.3
# pycocotools>=2.0.6 # COCO mAP # pycocotools>=2.0.6 # COCO mAP

@ -25,17 +25,19 @@ verbose = 2
# https://pep8.readthedocs.io/en/latest/intro.html#error-codes # https://pep8.readthedocs.io/en/latest/intro.html#error-codes
format = pylint format = pylint
# see: https://www.flake8rules.com/ # 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 # E731: Do not assign a lambda expression, use a def
# F405: name may be undefined, or defined from star imports: module # F405: name may be undefined, or defined from star imports: module
# E402: module level import not at top of file # E402: module level import not at top of file
# F401: module imported but unused
# W504: line break after binary operator # W504: line break after binary operator
# E127: continuation line over-indented for visual indent
# E231: missing whitespace after ‘,’, ‘;’, or ‘:’
# E501: line too long # 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 # F403: ‘from module import *’ used; unable to detect undefined names
[isort] [isort]
# https://pycqa.github.io/isort/docs/configuration/options.html # https://pycqa.github.io/isort/docs/configuration/options.html
line_length = 120 line_length = 120
@ -48,7 +50,7 @@ spaces_before_comment = 2
COLUMN_LIMIT = 120 COLUMN_LIMIT = 120
COALESCE_BRACKETS = True COALESCE_BRACKETS = True
SPACES_AROUND_POWER_OPERATOR = 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_CLOSING_BRACKET = False
SPLIT_BEFORE_FIRST_ARGUMENT = False SPLIT_BEFORE_FIRST_ARGUMENT = False
# EACH_DICT_ENTRY_ON_SEPARATE_LINE = False # EACH_DICT_ENTRY_ON_SEPARATE_LINE = False

@ -22,7 +22,7 @@ def test_special_modes():
# Train checks --------------------------------------------------------------------------------------------------------- # Train checks ---------------------------------------------------------------------------------------------------------
def test_train_det(): 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(): def test_train_seg():
@ -48,7 +48,7 @@ def test_val_classify():
# Predict checks ------------------------------------------------------------------------------------------------------- # Predict checks -------------------------------------------------------------------------------------------------------
def test_predict_detect(): 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(): 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/images/bus.jpg imgsz=32')
run(f'yolo predict model={MODEL}.pt source=https://ultralytics.com/assets/decelera_landscape_min.mov imgsz=32') run(f'yolo predict model={MODEL}.pt source=https://ultralytics.com/assets/decelera_landscape_min.mov imgsz=32')

@ -162,9 +162,8 @@ def test_workflow():
def test_predict_callback_and_setup(): def test_predict_callback_and_setup():
# test callback addition for prediction
def on_predict_batch_end(predictor): def on_predict_batch_end(predictor): # results -> List[batch_size]
# results -> List[batch_size]
path, _, im0s, _, _ = predictor.batch path, _, im0s, _, _ = predictor.batch
# print('on_predict_batch_end', im0s[0].shape) # print('on_predict_batch_end', im0s[0].shape)
im0s = im0s if isinstance(im0s, list) else [im0s] im0s = im0s if isinstance(im0s, list) else [im0s]

@ -1,8 +1,8 @@
# Ultralytics YOLO 🚀, GPL-3.0 license # Ultralytics YOLO 🚀, GPL-3.0 license
__version__ = '8.0.46' __version__ = '8.0.47'
from ultralytics.yolo.engine.model import YOLO from ultralytics.yolo.engine.model import YOLO
from ultralytics.yolo.utils.checks import check_yolo as checks from ultralytics.yolo.utils.checks import check_yolo as checks
__all__ = ['__version__', 'YOLO', 'checks'] # allow simpler import __all__ = '__version__', 'YOLO', 'checks' # allow simpler import

@ -154,7 +154,8 @@ class Traces:
'python': platform.python_version(), 'python': platform.python_version(),
'release': __version__, 'release': __version__,
'environment': ENVIRONMENT} 'environment': ENVIRONMENT}
self.enabled = SETTINGS['sync'] and \ self.enabled = \
SETTINGS['sync'] and \
RANK in {-1, 0} and \ RANK in {-1, 0} and \
check_online() and \ check_online() and \
not TESTS_RUNNING and \ not TESTS_RUNNING and \

@ -136,7 +136,7 @@ class AutoBackend(nn.Module):
batch_dim = get_batch(network) batch_dim = get_batch(network)
if batch_dim.is_static: if batch_dim.is_static:
batch_size = batch_dim.get_length() 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 elif engine: # TensorRT
LOGGER.info(f'Loading {w} for TensorRT inference...') LOGGER.info(f'Loading {w} for TensorRT inference...')
import tensorrt as trt # https://developer.nvidia.com/nvidia-tensorrt-download 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...') LOGGER.info(f'Loading {w} for CoreML inference...')
import coremltools as ct import coremltools as ct
model = ct.models.MLModel(w) 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 elif saved_model: # TF SavedModel
LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...') LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...')
import tensorflow as tf import tensorflow as tf
@ -185,18 +187,13 @@ class AutoBackend(nn.Module):
LOGGER.info(f'Loading {w} for TensorFlow GraphDef inference...') LOGGER.info(f'Loading {w} for TensorFlow GraphDef inference...')
import tensorflow as tf import tensorflow as tf
from ultralytics.yolo.engine.exporter import gd_outputs
def wrap_frozen_graph(gd, inputs, outputs): def wrap_frozen_graph(gd, inputs, outputs):
x = tf.compat.v1.wrap_function(lambda: tf.compat.v1.import_graph_def(gd, name=''), []) # wrapped x = tf.compat.v1.wrap_function(lambda: tf.compat.v1.import_graph_def(gd, name=''), []) # wrapped
ge = x.graph.as_graph_element ge = x.graph.as_graph_element
return x.prune(tf.nest.map_structure(ge, inputs), tf.nest.map_structure(ge, outputs)) 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 gd = tf.Graph().as_graph_def() # TF GraphDef
with open(w, 'rb') as f: with open(w, 'rb') as f:
gd.ParseFromString(f.read()) gd.ParseFromString(f.read())
@ -319,10 +316,17 @@ class AutoBackend(nn.Module):
self.context.execute_v2(list(self.binding_addrs.values())) self.context.execute_v2(list(self.binding_addrs.values()))
y = [self.bindings[x].data for x in sorted(self.output_names)] y = [self.bindings[x].data for x in sorted(self.output_names)]
elif self.coreml: # CoreML elif self.coreml: # CoreML
im = im.cpu().numpy() im = im[0].cpu().numpy()
im = Image.fromarray((im[0] * 255).astype('uint8')) 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) # 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: if 'confidence' in y:
box = xywh2xyxy(y['coordinates'] * [[w, h, w, h]]) # xyxy pixels box = xywh2xyxy(y['coordinates'] * [[w, h, w, h]]) # xyxy pixels
conf, cls = y['confidence'].max(1), y['confidence'].argmax(1).astype(np.float) conf, cls = y['confidence'].max(1), y['confidence'].argmax(1).astype(np.float)

@ -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, 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, Concat, Conv, ConvTranspose, Detect, DWConv, DWConvTranspose2d, Ensemble, Focus,
GhostBottleneck, GhostConv, Segment) 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.checks import check_requirements, check_yaml
from ultralytics.yolo.utils.torch_utils import (fuse_conv_and_bn, fuse_deconv_and_bn, initialize_weights, 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) intersect_dicts, make_divisible, model_info, scale_img, time_sync)
@ -76,7 +76,7 @@ class BaseModel(nn.Module):
None None
""" """
c = m == self.model[-1] # is final layer, copy input as inplace fix 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() t = time_sync()
for _ in range(10): for _ in range(10):
m(x.clone() if c else x) 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 file = attempt_download_asset(weight) # search online if missing locally
try: try:
return torch.load(file, map_location='cpu'), file # load return torch.load(file, map_location='cpu'), file # load
except ModuleNotFoundError as e: except ModuleNotFoundError as e: # e.name is missing module name
if e.name == 'omegaconf': # e.name is missing module name if e.name == 'models':
LOGGER.warning(f'WARNING ⚠ {weight} requires {e.name}, which is not in ultralytics requirements.' raise TypeError(
f'\nAutoInstall will run now for {e.name} but this feature will be removed in the future.' emojis(f'ERROR ❌ {weight} appears to be an Ultralytics YOLOv5 model originally trained '
f'\nRecommend fixes are to train a new model using updated ultralytics package or to ' f'with https://github.com/ultralytics/yolov5.\nThis model is NOT forwards compatible with '
f'download updated models from https://github.com/ultralytics/assets/releases/tag/v0.0.0') f'YOLOv8 at https://github.com/ultralytics/ultralytics.'
if e.name != 'models': 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 check_requirements(e.name) # install missing module
return torch.load(file, map_location='cpu'), file # load 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 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 n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
if m in { if m in (Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus,
Classify, Conv, ConvTranspose, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, Focus, BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x):
BottleneckCSP, C1, C2, C2f, C3, C3TR, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x}:
c1, c2 = ch[f], args[0] c1, c2 = ch[f], args[0]
if c2 != nc: # if c2 not equal to number of classes (i.e. for Classify() output) if c2 != nc: # if c2 not equal to number of classes (i.e. for Classify() output)
c2 = make_divisible(c2 * gw, 8) c2 = make_divisible(c2 * gw, 8)
args = [c1, c2, *args[1:]] 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 args.insert(2, n) # number of repeats
n = 1 n = 1
elif m is nn.BatchNorm2d: elif m is nn.BatchNorm2d:
args = [ch[f]] args = [ch[f]]
elif m is Concat: elif m is Concat:
c2 = sum(ch[x] for x in f) 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]) args.append([ch[x] for x in f])
if m is Segment: if m is Segment:
args[2] = make_divisible(args[2] * gw, 8) args[2] = make_divisible(args[2] * gw, 8)
@ -490,11 +495,11 @@ def guess_model_task(model):
def cfg2task(cfg): def cfg2task(cfg):
# Guess from YAML dictionary # Guess from YAML dictionary
m = cfg['head'][-1][-2].lower() # output module name 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' return 'classify'
if m in ['detect']: if m == 'detect':
return 'detect' return 'detect'
if m in ['segment']: if m == 'segment':
return 'segment' return 'segment'
# Guess from model cfg # Guess from model cfg

@ -2,3 +2,5 @@
from .track import register_tracker from .track import register_tracker
from .trackers import BOTSORT, BYTETracker from .trackers import BOTSORT, BYTETracker
__all__ = 'register_tracker', 'BOTSORT', 'BYTETracker' # allow simpler import

@ -2,3 +2,5 @@
from .bot_sort import BOTSORT from .bot_sort import BOTSORT
from .byte_tracker import BYTETracker from .byte_tracker import BYTETracker
__all__ = 'BOTSORT', 'BYTETracker' # allow simpler import

@ -213,7 +213,7 @@ class GMC:
prev_pt = np.array(self.prevKeyPoints[m.queryIdx].pt, dtype=np.int_) 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 = np.array(keypoints[m.trainIdx].pt, dtype=np.int_)
curr_pt[0] += W 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])) 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) matches_img = cv2.line(matches_img, prev_pt, curr_pt, tuple(color), 1, cv2.LINE_AA)

@ -2,4 +2,4 @@
from . import v8 from . import v8
__all__ = ['v8'] __all__ = 'v8', # tuple or list

@ -269,6 +269,11 @@ def entrypoint(debug=''):
checks.check_yolo() checks.check_yolo()
return 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
model = overrides.pop('model', DEFAULT_CFG.model) model = overrides.pop('model', DEFAULT_CFG.model)
if model is None: if model is None:
@ -276,15 +281,11 @@ def entrypoint(debug=''):
LOGGER.warning(f"WARNING ⚠ 'model' is missing. Using default 'model={model}'.") LOGGER.warning(f"WARNING ⚠ 'model' is missing. Using default 'model={model}'.")
from ultralytics.yolo.engine.model import YOLO from ultralytics.yolo.engine.model import YOLO
overrides['model'] = model overrides['model'] = model
model = YOLO(model) model = YOLO(model, task=task)
# Task # Task Update
task = overrides.get('task', model.task) task = task or model.task
if task is not None: overrides['task'] = task
if task not in TASKS:
raise ValueError(f"Invalid 'task={task}'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}")
else:
model.task = task
# Mode # Mode
if mode in {'predict', 'track'} and 'source' not in overrides: if mode in {'predict', 'track'} and 'source' not in overrides:

@ -5,12 +5,5 @@ from .build import build_classification_dataloader, build_dataloader, load_infer
from .dataset import ClassificationDataset, SemanticDataset, YOLODataset from .dataset import ClassificationDataset, SemanticDataset, YOLODataset
from .dataset_wrappers import MixAndRectDataset from .dataset_wrappers import MixAndRectDataset
__all__ = [ __all__ = ('BaseDataset', 'ClassificationDataset', 'MixAndRectDataset', 'SemanticDataset', 'YOLODataset',
'BaseDataset', 'build_classification_dataloader', 'build_dataloader', 'load_inference_source')
'ClassificationDataset',
'MixAndRectDataset',
'SemanticDataset',
'YOLODataset',
'build_classification_dataloader',
'build_dataloader',
'load_inference_source',]

@ -564,7 +564,7 @@ class Albumentations:
A.CLAHE(p=0.01), A.CLAHE(p=0.01),
A.RandomBrightnessContrast(p=0.0), A.RandomBrightnessContrast(p=0.0),
A.RandomGamma(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'])) 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)) 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, shear=hyp.shear,
perspective=hyp.perspective, perspective=hyp.perspective,
pre_transform=LetterBox(new_shape=(imgsz, imgsz)), pre_transform=LetterBox(new_shape=(imgsz, imgsz)),
),]) )])
return Compose([ return Compose([
pre_transform, pre_transform,
MixUp(dataset, pre_transform=pre_transform, p=hyp.mixup), MixUp(dataset, pre_transform=pre_transform, p=hyp.mixup),
Albumentations(p=1.0), Albumentations(p=1.0),
RandomHSV(hgain=hyp.hsv_h, sgain=hyp.hsv_s, vgain=hyp.hsv_v), RandomHSV(hgain=hyp.hsv_h, sgain=hyp.hsv_s, vgain=hyp.hsv_v),
RandomFlip(direction='vertical', p=hyp.flipud), RandomFlip(direction='vertical', p=hyp.flipud),
RandomFlip(direction='horizontal', p=hyp.fliplr),]) # transforms RandomFlip(direction='horizontal', p=hyp.fliplr)]) # transforms
# Classification augmentations ----------------------------------------------------------------------------------------- # Classification augmentations -----------------------------------------------------------------------------------------
@ -719,8 +719,8 @@ def classify_albumentations(
if vflip > 0: if vflip > 0:
T += [A.VerticalFlip(p=vflip)] T += [A.VerticalFlip(p=vflip)]
if jitter > 0: if jitter > 0:
color_jitter = (float(jitter),) * 3 # repeat value for brightness, contrast, saturation, 0 hue jitter = float(jitter)
T += [A.ColorJitter(*color_jitter, 0)] T += [A.ColorJitter(jitter, jitter, jitter, 0)] # brightness, contrast, saturation, 0 hue
else: # Use fixed crop for eval set (reproducibility) else: # Use fixed crop for eval set (reproducibility)
T = [A.SmallestMaxSize(max_size=size), A.CenterCrop(height=size, width=size)] 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 T += [A.Normalize(mean=mean, std=std), ToTensorV2()] # Normalize and convert to Tensor

@ -24,8 +24,7 @@ class BaseDataset(Dataset):
label_path (str): label path, this can also be an ann_file or other custom label path. label_path (str): label path, this can also be an ann_file or other custom label path.
""" """
def __init__( def __init__(self,
self,
img_path, img_path,
imgsz=640, imgsz=640,
cache=False, cache=False,
@ -36,8 +35,7 @@ class BaseDataset(Dataset):
batch_size=None, batch_size=None,
stride=32, stride=32,
pad=0.5, pad=0.5,
single_cls=False, single_cls=False):
):
super().__init__() super().__init__()
self.img_path = img_path self.img_path = img_path
self.imgsz = imgsz self.imgsz = imgsz

@ -335,8 +335,8 @@ def classify_albumentations(
if vflip > 0: if vflip > 0:
T += [A.VerticalFlip(p=vflip)] T += [A.VerticalFlip(p=vflip)]
if jitter > 0: if jitter > 0:
color_jitter = (float(jitter),) * 3 # repeat value for brightness, contrast, satuaration, 0 hue jitter = float(jitter)
T += [A.ColorJitter(*color_jitter, 0)] T += [A.ColorJitter(jitter, jitter, jitter, 0)] # brightness, contrast, satuaration, 0 hue
else: # Use fixed crop for eval set (reproducibility) else: # Use fixed crop for eval set (reproducibility)
T = [A.SmallestMaxSize(max_size=size), A.CenterCrop(height=size, width=size)] 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 T += [A.Normalize(mean=mean, std=std), ToTensorV2()] # Normalize and convert to Tensor

@ -4,13 +4,16 @@ from itertools import repeat
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
from pathlib import Path from pathlib import Path
import cv2
import numpy as np
import torch
import torchvision import torchvision
from tqdm import tqdm from tqdm import tqdm
from ..utils import NUM_THREADS, TQDM_BAR_FORMAT, is_dir_writeable 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 .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): class YOLODataset(BaseDataset):

@ -50,7 +50,6 @@ TensorFlow.js:
import json import json
import os import os
import platform import platform
import re
import subprocess import subprocess
import time import time
import warnings import warnings
@ -90,8 +89,8 @@ def export_formats():
['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True], ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True],
['TensorFlow GraphDef', 'pb', '.pb', True, True], ['TensorFlow GraphDef', 'pb', '.pb', True, True],
['TensorFlow Lite', 'tflite', '.tflite', True, False], ['TensorFlow Lite', 'tflite', '.tflite', True, False],
['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False], ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', True, False],
['TensorFlow.js', 'tfjs', '_web_model', False, False], ['TensorFlow.js', 'tfjs', '_web_model', True, False],
['PaddlePaddle', 'paddle', '_paddle_model', True, True], ] ['PaddlePaddle', 'paddle', '_paddle_model', True, True], ]
return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU']) 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()) 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): def try_export(inner_func):
# YOLOv8 export decorator, i..e @try_export # YOLOv8 export decorator, i..e @try_export
inner_args = get_default_args(inner_func) inner_args = get_default_args(inner_func)
@ -164,10 +172,10 @@ class Exporter:
# Checks # Checks
model.names = check_class_names(model.names) model.names = check_class_names(model.names)
self.imgsz = check_imgsz(self.args.imgsz, stride=model.stride, min_dim=2) # check image size 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: if self.args.optimize:
assert self.device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu' 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 # Input
im = torch.zeros(self.args.batch, 3, *self.imgsz).to(self.device) im = torch.zeros(self.args.batch, 3, *self.imgsz).to(self.device)
@ -239,8 +247,7 @@ class Exporter:
'Please consider contributing to the effort if you have TF expertise. Thank you!') 'Please consider contributing to the effort if you have TF expertise. Thank you!')
nms = False nms = False
self.args.int8 |= edgetpu self.args.int8 |= edgetpu
f[5], s_model = self._export_saved_model(nms=nms or self.args.agnostic_nms or tfjs, f[5], s_model = self._export_saved_model()
agnostic_nms=self.args.agnostic_nms or tfjs)
if pb or tfjs: # pb prerequisite to tfjs if pb or tfjs: # pb prerequisite to tfjs
f[6], _ = self._export_pb(s_model) f[6], _ = self._export_pb(s_model)
if tflite: if tflite:
@ -386,7 +393,7 @@ class Exporter:
check_requirements('coremltools>=6.0') check_requirements('coremltools>=6.0')
import coremltools as ct # noqa import coremltools as ct # noqa
class iOSModel(torch.nn.Module): class iOSDetectModel(torch.nn.Module):
# Wrap an Ultralytics YOLO model for iOS export # Wrap an Ultralytics YOLO model for iOS export
def __init__(self, model, im): def __init__(self, model, im):
super().__init__() super().__init__()
@ -405,29 +412,36 @@ class Exporter:
LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...') LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...')
f = self.file.with_suffix('.mlmodel') f = self.file.with_suffix('.mlmodel')
bias = [0.0, 0.0, 0.0]
scale = 1 / 255
classifier_config = None
if self.model.task == 'classify': if self.model.task == 'classify':
bias = [-x for x in IMAGENET_MEAN] bias = [-x for x in IMAGENET_MEAN]
scale = 1 / 255 / (sum(IMAGENET_STD) / 3) scale = 1 / 255 / (sum(IMAGENET_STD) / 3)
classifier_config = ct.ClassifierConfig(list(self.model.names.values())) if self.args.nms else None classifier_config = ct.ClassifierConfig(list(self.model.names.values())) if self.args.nms else None
else: model = self.model
bias = [0.0, 0.0, 0.0] elif self.model.task == 'detect':
scale = 1 / 255 model = iOSDetectModel(self.model, self.im) if self.args.nms else self.model
classifier_config = None elif self.model.task == 'segment':
model = iOSModel(self.model, self.im).eval() if self.args.nms else self.model # TODO CoreML Segmentation model pipelining
ts = torch.jit.trace(model, self.im, strict=False) # TorchScript model model = self.model
ts = torch.jit.trace(model.eval(), self.im, strict=False) # TorchScript model
ct_model = ct.convert(ts, ct_model = ct.convert(ts,
inputs=[ct.ImageType('image', shape=self.im.shape, scale=scale, bias=bias)], inputs=[ct.ImageType('image', shape=self.im.shape, scale=scale, bias=bias)],
classifier_config=classifier_config) classifier_config=classifier_config)
bits, mode = (8, 'kmeans_lut') if self.args.int8 else (16, 'linear') if self.args.half else (32, None) bits, mode = (8, 'kmeans_lut') if self.args.int8 else (16, 'linear') if self.args.half else (32, None)
if bits < 32: if bits < 32:
ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode) 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 = self._pipeline_coreml(ct_model)
ct_model.short_description = self.metadata['description'] m = self.metadata # metadata dict
ct_model.author = self.metadata['author'] ct_model.short_description = m['description']
ct_model.license = self.metadata['license'] ct_model.author = m['author']
ct_model.version = self.metadata['version'] 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)) ct_model.save(str(f))
return f, ct_model return f, ct_model
@ -497,14 +511,7 @@ class Exporter:
return f, None return f, None
@try_export @try_export
def _export_saved_model(self, def _export_saved_model(self, prefix=colorstr('TensorFlow SavedModel:')):
nms=False,
agnostic_nms=False,
topk_per_class=100,
topk_all=100,
iou_thres=0.45,
conf_thres=0.25,
prefix=colorstr('TensorFlow SavedModel:')):
# YOLOv8 TensorFlow SavedModel export # YOLOv8 TensorFlow SavedModel export
try: try:
@ -562,6 +569,9 @@ class Exporter:
@try_export @try_export
def _export_tflite(self, keras_model, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')): def _export_tflite(self, keras_model, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')):
# YOLOv8 TensorFlow Lite export # 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')) saved_model = Path(str(self.file).replace(self.file.suffix, '_saved_model'))
if self.args.int8: if self.args.int8:
f = saved_model / (self.file.stem + 'yolov8n_integer_quant.tflite') # fp32 in/out f = saved_model / (self.file.stem + 'yolov8n_integer_quant.tflite') # fp32 in/out
@ -572,9 +582,6 @@ class Exporter:
return str(f), None # noqa return str(f), None # noqa
# OLD VERSION BELOW --------------------------------------------------------------- # 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 batch_size, ch, *imgsz = list(self.im.shape) # BCHW
f = str(self.file).replace(self.file.suffix, '-fp16.tflite') 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}') 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 sudo = subprocess.run('sudo --version >/dev/null', shell=True).returncode == 0 # sudo installed on system
for c in ( 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 '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 tee /etc/apt/sources.list.d/coral-edgetpu.list',
'sudo apt-get update', 'sudo apt-get update',
@ -639,30 +648,36 @@ class Exporter:
def _export_tfjs(self, prefix=colorstr('TensorFlow.js:')): def _export_tfjs(self, prefix=colorstr('TensorFlow.js:')):
# YOLOv8 TensorFlow.js export # YOLOv8 TensorFlow.js export
check_requirements('tensorflowjs') check_requirements('tensorflowjs')
import tensorflow as tf
import tensorflowjs as tfjs # noqa import tensorflowjs as tfjs # noqa
LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...') LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...')
f = str(self.file).replace(self.file.suffix, '_web_model') # js dir f = str(self.file).replace(self.file.suffix, '_web_model') # js dir
f_pb = self.file.with_suffix('.pb') # *.pb path 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 ' \ gd = tf.Graph().as_graph_def() # TF GraphDef
f'--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}' 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) subprocess.run(cmd.split(), check=True)
with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order # f_json = Path(f) / 'model.json' # *.json path
subst = re.sub( # with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order
r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, ' # subst = re.sub(
r'"Identity.?.?": {"name": "Identity.?.?"}, ' # r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
r'"Identity.?.?": {"name": "Identity.?.?"}, ' # r'"Identity.?.?": {"name": "Identity.?.?"}, '
r'"Identity.?.?": {"name": "Identity.?.?"}}}', # r'"Identity.?.?": {"name": "Identity.?.?"}, '
r'{"outputs": {"Identity": {"name": "Identity"}, ' # r'"Identity.?.?": {"name": "Identity.?.?"}}}',
r'"Identity_1": {"name": "Identity_1"}, ' # r'{"outputs": {"Identity": {"name": "Identity"}, '
r'"Identity_2": {"name": "Identity_2"}, ' # r'"Identity_1": {"name": "Identity_1"}, '
r'"Identity_3": {"name": "Identity_3"}}}', # r'"Identity_2": {"name": "Identity_2"}, '
f_json.read_text(), # r'"Identity_3": {"name": "Identity_3"}}}',
) # f_json.read_text(),
j.write(subst) # )
# j.write(subst)
yaml_save(Path(f) / 'metadata.yaml', self.metadata) # add metadata.yaml yaml_save(Path(f) / 'metadata.yaml', self.metadata) # add metadata.yaml
return f, None return f, None
@ -680,7 +695,7 @@ class Exporter:
model_meta.license = self.metadata['license'] model_meta.license = self.metadata['license']
# Label file # Label file
tmp_file = file.parent / 'temp_meta.txt' tmp_file = Path(file).parent / 'temp_meta.txt'
with open(tmp_file, 'w') as f: with open(tmp_file, 'w') as f:
f.write(str(self.metadata)) f.write(str(self.metadata))
@ -718,7 +733,7 @@ class Exporter:
b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER) b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
metadata_buf = b.Output() 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_metadata_buffer(metadata_buf)
populator.load_associated_files([str(tmp_file)]) populator.load_associated_files([str(tmp_file)])
populator.populate() populator.populate()

@ -2,7 +2,6 @@
import sys import sys
from pathlib import Path from pathlib import Path
from typing import List
from ultralytics import yolo # noqa from ultralytics import yolo # noqa
from ultralytics.nn.tasks import (ClassificationModel, DetectionModel, SegmentationModel, attempt_load_one_weight, 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. 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. Initializes the YOLO model.
@ -91,9 +90,9 @@ class YOLO:
if not suffix and Path(model).stem in GITHUB_ASSET_STEMS: 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 model, suffix = Path(model).with_suffix('.pt'), '.pt' # add suffix, i.e. yolov8n -> yolov8n.pt
if suffix == '.yaml': if suffix == '.yaml':
self._new(model) self._new(model, task)
else: else:
self._load(model) self._load(model, task)
def __call__(self, source=None, stream=False, **kwargs): def __call__(self, source=None, stream=False, **kwargs):
return self.predict(source, stream, **kwargs) return self.predict(source, stream, **kwargs)
@ -102,17 +101,18 @@ class YOLO:
name = self.__class__.__name__ name = self.__class__.__name__
raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") 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. Initializes a new model and infers the task type from the model definitions.
Args: Args:
cfg (str): model configuration file cfg (str): model configuration file
task (str) or (None): model task
verbose (bool): display model info on load verbose (bool): display model info on load
""" """
self.cfg = check_yaml(cfg) # check YAML self.cfg = check_yaml(cfg) # check YAML
cfg_dict = yaml_load(self.cfg, append_filename=True) # model dict 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.model = TASK_MAP[self.task][0](cfg_dict, verbose=verbose and RANK == -1) # build model
self.overrides['model'] = self.cfg 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.args = {k: v for k, v in args.items() if k in DEFAULT_CFG_KEYS} # attach args to model
self.model.task = self.task 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. Initializes a new model and infers the task type from the model head.
Args: Args:
weights (str): model checkpoint to be loaded weights (str): model checkpoint to be loaded
task (str) or (None): model task
""" """
suffix = Path(weights).suffix suffix = Path(weights).suffix
if suffix == '.pt': if suffix == '.pt':
@ -137,7 +138,7 @@ class YOLO:
else: else:
weights = check_file(weights) weights = check_file(weights)
self.model, self.ckpt = weights, None 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.ckpt_path = weights
self.overrides['model'] = weights self.overrides['model'] = weights

@ -32,7 +32,6 @@ from collections import defaultdict
from pathlib import Path from pathlib import Path
import cv2 import cv2
import torch
from ultralytics.nn.autobackend import AutoBackend from ultralytics.nn.autobackend import AutoBackend
from ultralytics.yolo.cfg import get_cfg from ultralytics.yolo.cfg import get_cfg

@ -242,7 +242,7 @@ class BaseTrainer:
metric_keys = self.validator.metrics.keys + self.label_loss_items(prefix='val') 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.metrics = dict(zip(metric_keys, [0] * len(metric_keys))) # TODO: init metrics for plot_results()?
self.ema = ModelEMA(self.model) self.ema = ModelEMA(self.model)
if self.args.plots: if self.args.plots and not self.args.v5loader:
self.plot_training_labels() self.plot_training_labels()
self.resume_training(ckpt) self.resume_training(ckpt)
self.scheduler.last_epoch = self.start_epoch - 1 # do not move self.scheduler.last_epoch = self.start_epoch - 1 # do not move

@ -18,7 +18,6 @@ from typing import Union
import cv2 import cv2
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import requests
import torch import torch
import yaml import yaml
@ -517,10 +516,7 @@ def set_sentry():
((is_pip_package() and not is_git_dir()) or ((is_pip_package() and not is_git_dir()) or
(get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git' and get_git_branch() == 'main')): (get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git' and get_git_branch() == 'main')):
import hashlib
import sentry_sdk # noqa import sentry_sdk # noqa
sentry_sdk.init( sentry_sdk.init(
dsn='https://f805855f03bb4363bc1e16cb7d87b654@o4504521589325824.ingest.sentry.io/4504521592406016', dsn='https://f805855f03bb4363bc1e16cb7d87b654@o4504521589325824.ingest.sentry.io/4504521592406016',
debug=False, debug=False,

@ -30,14 +30,14 @@ import pandas as pd
from ultralytics import YOLO from ultralytics import YOLO
from ultralytics.yolo.engine.exporter import export_formats 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.checks import check_yolo
from ultralytics.yolo.utils.downloads import download from ultralytics.yolo.utils.downloads import download
from ultralytics.yolo.utils.files import file_size from ultralytics.yolo.utils.files import file_size
from ultralytics.yolo.utils.torch_utils import select_device 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) device = select_device(device, verbose=False)
if isinstance(model, (str, Path)): if isinstance(model, (str, Path)):
model = YOLO(model) model = YOLO(model)
@ -45,11 +45,10 @@ def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, hal
y = [] y = []
t0 = time.time() t0 = time.time()
for i, (name, format, suffix, cpu, gpu) in export_formats().iterrows(): # index, (name, format, suffix, CPU, GPU) for i, (name, format, suffix, cpu, gpu) in export_formats().iterrows(): # index, (name, format, suffix, CPU, GPU)
emoji = '' # indicates export failure
try: try:
assert i not in (9, 10), 'inference not supported' # Edge TPU and TF.js are unsupported assert i != 11, 'paddle exports coming soon'
assert i != 5 or platform.system() == 'Darwin', 'inference only supported on macOS>=10.13' # CoreML assert i != 9 or LINUX, 'Edge TPU export only supported on Linux'
assert i != 11 or model.task != 'classify', 'paddle-classify bug'
if 'cpu' in device.type: if 'cpu' in device.type:
assert cpu, 'inference not supported on CPU' assert cpu, 'inference not supported on CPU'
if 'cuda' in device.type: 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 export = model # PyTorch format
else: else:
filename = model.export(imgsz=imgsz, format=format, half=half, device=device) # all others 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' assert suffix in str(filename), 'export failed'
emoji = '' # indicates export succeeded
# Predict # 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(): if not (ROOT / 'assets/bus.jpg').exists():
download(url='https://ultralytics.com/images/bus.jpg', dir=ROOT / 'assets') 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 # Validate
if model.task == 'detect': if model.task == 'detect':
@ -84,17 +86,16 @@ def benchmark(model=Path(SETTINGS['weights_dir']) / 'yolov8n.pt', imgsz=160, hal
if hard_fail: if hard_fail:
assert type(e) is AssertionError, f'Benchmark hard_fail for {name}: {e}' assert type(e) is AssertionError, f'Benchmark hard_fail for {name}: {e}'
LOGGER.warning(f'ERROR ❌ Benchmark failure 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 # Print results
check_yolo(device=device) # print system info check_yolo(device=device) # print system info
c = ['Format', 'Status❔', 'Size (MB)', key, 'Inference time (ms/im)'] df = pd.DataFrame(y, columns=['Format', 'Status❔', 'Size (MB)', key, 'Inference time (ms/im)'])
df = pd.DataFrame(y, columns=c)
name = Path(model.ckpt_path).name 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' s = f'\nBenchmarks complete for {name} on {data} at imgsz={imgsz} ({time.time() - t0:.2f}s)\n{df}\n'
LOGGER.info(s) 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) f.write(s)
if hard_fail and isinstance(hard_fail, float): if hard_fail and isinstance(hard_fail, float):

@ -1,5 +1,3 @@
from .base import add_integration_callbacks, default_callbacks from .base import add_integration_callbacks, default_callbacks
__all__ = [ __all__ = 'add_integration_callbacks', 'default_callbacks'
'add_integration_callbacks',
'default_callbacks',]

@ -137,7 +137,6 @@ def check_latest_pypi_version(package_name='ultralytics'):
def check_pip_update(): def check_pip_update():
from ultralytics import __version__ from ultralytics import __version__
latest = check_latest_pypi_version() latest = check_latest_pypi_version()
latest = '9.0.0'
if pkg.parse_version(__version__) < pkg.parse_version(latest): if pkg.parse_version(__version__) < pkg.parse_version(latest):
LOGGER.info(f'New https://pypi.org/project/ultralytics/{latest} available 😃 ' LOGGER.info(f'New https://pypi.org/project/ultralytics/{latest} available 😃 '
f"Update with 'pip install -U ultralytics'") 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}') 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 # Check file(s) for acceptable suffix
if file and suffix: if file and suffix:
if isinstance(suffix, str): if isinstance(suffix, str):

@ -10,9 +10,8 @@ import numpy as np
from .ops import ltwh2xywh, ltwh2xyxy, resample_segments, xywh2ltwh, xywh2xyxy, xyxy2ltwh, xyxy2xywh from .ops import ltwh2xywh, ltwh2xyxy, resample_segments, xywh2ltwh, xywh2xyxy, xyxy2ltwh, xyxy2xywh
# From PyTorch internals
def _ntuple(n): def _ntuple(n):
# From PyTorch internals
def parse(x): def parse(x):
return x if isinstance(x, abc.Iterable) else tuple(repeat(x, n)) 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) # `ltwh` means left top and width, height(coco format)
_formats = ['xyxy', 'xywh', 'ltwh'] _formats = ['xyxy', 'xywh', 'ltwh']
__all__ = ['Bboxes'] __all__ = 'Bboxes', # tuple or list
class Bboxes: class Bboxes:

@ -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): 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 # 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.view(-1, 4)) # boxes
b = xyxy2xywh(xyxy) # boxes
if square: if square:
b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # attempt rectangle to square b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # attempt rectangle to square
b[:, 2:] = b[:, 2:] * gain + pad # box wh * gain + pad b[:, 2:] = b[:, 2:] * gain + pad # box wh * gain + pad

@ -195,7 +195,7 @@ def get_flops(model, imgsz=640):
p = next(model.parameters()) p = next(model.parameters())
stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32 # max stride 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 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 imgsz = imgsz if isinstance(imgsz, list) else [imgsz, imgsz] # expand if int/float
flops = flops * imgsz[0] / stride * imgsz[1] / stride # 640x640 GFLOPs flops = flops * imgsz[0] / stride * imgsz[1] / stride # 640x640 GFLOPs
return flops 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 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 tf, tb, t = 0, 0, [0, 0, 0] # dt forward, backward
try: 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: except Exception:
flops = 0 flops = 0

@ -2,4 +2,4 @@
from ultralytics.yolo.v8 import classify, detect, segment from ultralytics.yolo.v8 import classify, detect, segment
__all__ = ['classify', 'segment', 'detect'] __all__ = 'classify', 'segment', 'detect'

@ -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.train import ClassificationTrainer, train
from ultralytics.yolo.v8.classify.val import ClassificationValidator, val from ultralytics.yolo.v8.classify.val import ClassificationValidator, val
__all__ = ['ClassificationPredictor', 'predict', 'ClassificationTrainer', 'train', 'ClassificationValidator', 'val'] __all__ = 'ClassificationPredictor', 'predict', 'ClassificationTrainer', 'train', 'ClassificationValidator', 'val'

@ -4,4 +4,4 @@ from .predict import DetectionPredictor, predict
from .train import DetectionTrainer, train from .train import DetectionTrainer, train
from .val import DetectionValidator, val from .val import DetectionValidator, val
__all__ = ['DetectionPredictor', 'predict', 'DetectionTrainer', 'train', 'DetectionValidator', 'val'] __all__ = 'DetectionPredictor', 'predict', 'DetectionTrainer', 'train', 'DetectionValidator', 'val'

@ -4,4 +4,4 @@ from .predict import SegmentationPredictor, predict
from .train import SegmentationTrainer, train from .train import SegmentationTrainer, train
from .val import SegmentationValidator, val from .val import SegmentationValidator, val
__all__ = ['SegmentationPredictor', 'predict', 'SegmentationTrainer', 'train', 'SegmentationValidator', 'val'] __all__ = 'SegmentationPredictor', 'predict', 'SegmentationTrainer', 'train', 'SegmentationValidator', 'val'

Loading…
Cancel
Save