segment-everything 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. segment_everything/__init__.py +5 -0
  2. segment_everything/augmentation/albumentations_helper.py +0 -0
  3. segment_everything/detect_and_segment.py +131 -0
  4. segment_everything/napari_helper.py +15 -0
  5. segment_everything/prompt_generator.py +188 -0
  6. segment_everything/py.typed +5 -0
  7. segment_everything/stacked_label_dataset.py +113 -0
  8. segment_everything/stacked_labels.py +428 -0
  9. segment_everything/vendored/PromptGuidedDecoder/Prompt_guided_Mask_Decoder.pt +0 -0
  10. segment_everything/vendored/__init__.py +5 -0
  11. segment_everything/vendored/dice.py +158 -0
  12. segment_everything/vendored/efficientvit/__init__.py +0 -0
  13. segment_everything/vendored/efficientvit/apps/__init__.py +0 -0
  14. segment_everything/vendored/efficientvit/apps/data_provider/__init__.py +7 -0
  15. segment_everything/vendored/efficientvit/apps/data_provider/augment/__init__.py +6 -0
  16. segment_everything/vendored/efficientvit/apps/data_provider/augment/bbox.py +30 -0
  17. segment_everything/vendored/efficientvit/apps/data_provider/augment/color_aug.py +78 -0
  18. segment_everything/vendored/efficientvit/apps/data_provider/base.py +254 -0
  19. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/__init__.py +6 -0
  20. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_loader.py +1538 -0
  21. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_worker.py +357 -0
  22. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/controller.py +100 -0
  23. segment_everything/vendored/efficientvit/apps/setup.py +150 -0
  24. segment_everything/vendored/efficientvit/apps/trainer/__init__.py +6 -0
  25. segment_everything/vendored/efficientvit/apps/trainer/base.py +318 -0
  26. segment_everything/vendored/efficientvit/apps/trainer/run_config.py +129 -0
  27. segment_everything/vendored/efficientvit/apps/utils/__init__.py +12 -0
  28. segment_everything/vendored/efficientvit/apps/utils/dist.py +32 -0
  29. segment_everything/vendored/efficientvit/apps/utils/ema.py +52 -0
  30. segment_everything/vendored/efficientvit/apps/utils/export.py +45 -0
  31. segment_everything/vendored/efficientvit/apps/utils/init.py +66 -0
  32. segment_everything/vendored/efficientvit/apps/utils/lr.py +52 -0
  33. segment_everything/vendored/efficientvit/apps/utils/metric.py +43 -0
  34. segment_everything/vendored/efficientvit/apps/utils/misc.py +101 -0
  35. segment_everything/vendored/efficientvit/apps/utils/opt.py +28 -0
  36. segment_everything/vendored/efficientvit/cls_model_zoo.py +79 -0
  37. segment_everything/vendored/efficientvit/clscore/__init__.py +0 -0
  38. segment_everything/vendored/efficientvit/clscore/data_provider/__init__.py +5 -0
  39. segment_everything/vendored/efficientvit/clscore/data_provider/imagenet.py +142 -0
  40. segment_everything/vendored/efficientvit/clscore/trainer/__init__.py +6 -0
  41. segment_everything/vendored/efficientvit/clscore/trainer/cls_run_config.py +18 -0
  42. segment_everything/vendored/efficientvit/clscore/trainer/cls_trainer.py +265 -0
  43. segment_everything/vendored/efficientvit/clscore/trainer/utils/__init__.py +7 -0
  44. segment_everything/vendored/efficientvit/clscore/trainer/utils/label_smooth.py +18 -0
  45. segment_everything/vendored/efficientvit/clscore/trainer/utils/metric.py +23 -0
  46. segment_everything/vendored/efficientvit/clscore/trainer/utils/mixup.py +67 -0
  47. segment_everything/vendored/efficientvit/models/__init__.py +0 -0
  48. segment_everything/vendored/efficientvit/models/efficientvit/__init__.py +8 -0
  49. segment_everything/vendored/efficientvit/models/efficientvit/backbone.py +380 -0
  50. segment_everything/vendored/efficientvit/models/efficientvit/cls.py +188 -0
  51. segment_everything/vendored/efficientvit/models/efficientvit/sam.py +181 -0
  52. segment_everything/vendored/efficientvit/models/efficientvit/seg.py +373 -0
  53. segment_everything/vendored/efficientvit/models/nn/__init__.py +8 -0
  54. segment_everything/vendored/efficientvit/models/nn/act.py +30 -0
  55. segment_everything/vendored/efficientvit/models/nn/drop.py +104 -0
  56. segment_everything/vendored/efficientvit/models/nn/norm.py +164 -0
  57. segment_everything/vendored/efficientvit/models/nn/ops.py +597 -0
  58. segment_everything/vendored/efficientvit/models/utils/__init__.py +7 -0
  59. segment_everything/vendored/efficientvit/models/utils/list.py +53 -0
  60. segment_everything/vendored/efficientvit/models/utils/network.py +73 -0
  61. segment_everything/vendored/efficientvit/models/utils/random.py +65 -0
  62. segment_everything/vendored/efficientvit/sam_model_zoo.py +45 -0
  63. segment_everything/vendored/efficientvit/seg_model_zoo.py +70 -0
  64. segment_everything/vendored/get_object_aware.py +26 -0
  65. segment_everything/vendored/mobilesamv2/__init__.py +16 -0
  66. segment_everything/vendored/mobilesamv2/automatic_mask_generator.py +415 -0
  67. segment_everything/vendored/mobilesamv2/build_sam.py +246 -0
  68. segment_everything/vendored/mobilesamv2/modeling/__init__.py +11 -0
  69. segment_everything/vendored/mobilesamv2/modeling/common.py +43 -0
  70. segment_everything/vendored/mobilesamv2/modeling/image_encoder.py +394 -0
  71. segment_everything/vendored/mobilesamv2/modeling/mask_decoder.py +213 -0
  72. segment_everything/vendored/mobilesamv2/modeling/prompt_encoder.py +217 -0
  73. segment_everything/vendored/mobilesamv2/modeling/sam.py +203 -0
  74. segment_everything/vendored/mobilesamv2/modeling/transformer.py +240 -0
  75. segment_everything/vendored/mobilesamv2/predictor.py +384 -0
  76. segment_everything/vendored/mobilesamv2/utils/__init__.py +5 -0
  77. segment_everything/vendored/mobilesamv2/utils/amg.py +347 -0
  78. segment_everything/vendored/mobilesamv2/utils/onnx.py +144 -0
  79. segment_everything/vendored/mobilesamv2/utils/transforms.py +103 -0
  80. segment_everything/vendored/object_detection/__init__.py +0 -0
  81. segment_everything/vendored/object_detection/ultralytics/__init__.py +5 -0
  82. segment_everything/vendored/object_detection/ultralytics/nn/__init__.py +9 -0
  83. segment_everything/vendored/object_detection/ultralytics/nn/autobackend.py +658 -0
  84. segment_everything/vendored/object_detection/ultralytics/nn/autoshape.py +397 -0
  85. segment_everything/vendored/object_detection/ultralytics/nn/modules/__init__.py +110 -0
  86. segment_everything/vendored/object_detection/ultralytics/nn/modules/block.py +304 -0
  87. segment_everything/vendored/object_detection/ultralytics/nn/modules/conv.py +297 -0
  88. segment_everything/vendored/object_detection/ultralytics/nn/modules/head.py +468 -0
  89. segment_everything/vendored/object_detection/ultralytics/nn/modules/transformer.py +378 -0
  90. segment_everything/vendored/object_detection/ultralytics/nn/modules/utils.py +78 -0
  91. segment_everything/vendored/object_detection/ultralytics/nn/tasks.py +1049 -0
  92. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/__init__.py +6 -0
  93. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/model.py +104 -0
  94. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/predict.py +95 -0
  95. segment_everything/vendored/object_detection/ultralytics/yolo/__init__.py +5 -0
  96. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/__init__.py +588 -0
  97. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/default.yaml +117 -0
  98. segment_everything/vendored/object_detection/ultralytics/yolo/data/__init__.py +9 -0
  99. segment_everything/vendored/object_detection/ultralytics/yolo/data/annotator.py +53 -0
  100. segment_everything/vendored/object_detection/ultralytics/yolo/data/augment.py +899 -0
  101. segment_everything/vendored/object_detection/ultralytics/yolo/data/base.py +286 -0
  102. segment_everything/vendored/object_detection/ultralytics/yolo/data/build.py +213 -0
  103. segment_everything/vendored/object_detection/ultralytics/yolo/data/converter.py +358 -0
  104. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/__init__.py +0 -0
  105. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/stream_loaders.py +459 -0
  106. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset.py +274 -0
  107. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset_wrappers.py +53 -0
  108. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/download_weights.sh +18 -0
  109. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco.sh +60 -0
  110. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco128.sh +17 -0
  111. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_imagenet.sh +51 -0
  112. segment_everything/vendored/object_detection/ultralytics/yolo/data/utils.py +716 -0
  113. segment_everything/vendored/object_detection/ultralytics/yolo/engine/__init__.py +0 -0
  114. segment_everything/vendored/object_detection/ultralytics/yolo/engine/exporter.py +1214 -0
  115. segment_everything/vendored/object_detection/ultralytics/yolo/engine/model.py +641 -0
  116. segment_everything/vendored/object_detection/ultralytics/yolo/engine/predictor.py +461 -0
  117. segment_everything/vendored/object_detection/ultralytics/yolo/engine/results.py +741 -0
  118. segment_everything/vendored/object_detection/ultralytics/yolo/utils/__init__.py +893 -0
  119. segment_everything/vendored/object_detection/ultralytics/yolo/utils/autobatch.py +108 -0
  120. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/__init__.py +5 -0
  121. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/base.py +212 -0
  122. segment_everything/vendored/object_detection/ultralytics/yolo/utils/checks.py +547 -0
  123. segment_everything/vendored/object_detection/ultralytics/yolo/utils/dist.py +67 -0
  124. segment_everything/vendored/object_detection/ultralytics/yolo/utils/downloads.py +353 -0
  125. segment_everything/vendored/object_detection/ultralytics/yolo/utils/errors.py +12 -0
  126. segment_everything/vendored/object_detection/ultralytics/yolo/utils/files.py +100 -0
  127. segment_everything/vendored/object_detection/ultralytics/yolo/utils/instance.py +391 -0
  128. segment_everything/vendored/object_detection/ultralytics/yolo/utils/loss.py +579 -0
  129. segment_everything/vendored/object_detection/ultralytics/yolo/utils/metrics.py +1189 -0
  130. segment_everything/vendored/object_detection/ultralytics/yolo/utils/ops.py +870 -0
  131. segment_everything/vendored/object_detection/ultralytics/yolo/utils/patches.py +45 -0
  132. segment_everything/vendored/object_detection/ultralytics/yolo/utils/plotting.py +767 -0
  133. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tal.py +276 -0
  134. segment_everything/vendored/object_detection/ultralytics/yolo/utils/torch_utils.py +684 -0
  135. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tuner.py +54 -0
  136. segment_everything/vendored/object_detection/ultralytics/yolo/v8/__init__.py +5 -0
  137. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/__init__.py +5 -0
  138. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/predict.py +69 -0
  139. segment_everything/vendored/tinyvit/__init__.py +2 -0
  140. segment_everything/vendored/tinyvit/tiny_vit.py +867 -0
  141. segment_everything/weights_helper.py +124 -0
  142. segment_everything-0.1.0.dist-info/METADATA +53 -0
  143. segment_everything-0.1.0.dist-info/RECORD +145 -0
  144. segment_everything-0.1.0.dist-info/WHEEL +4 -0
  145. segment_everything-0.1.0.dist-info/licenses/LICENSE +28 -0
@@ -0,0 +1,1214 @@
1
+ # Ultralytics YOLO 🚀, AGPL-3.0 license
2
+ """
3
+ Export a YOLOv8 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit
4
+
5
+ Format | `format=argument` | Model
6
+ --- | --- | ---
7
+ PyTorch | - | yolov8n.pt
8
+ TorchScript | `torchscript` | yolov8n.torchscript
9
+ ONNX | `onnx` | yolov8n.onnx
10
+ OpenVINO | `openvino` | yolov8n_openvino_model/
11
+ TensorRT | `engine` | yolov8n.engine
12
+ CoreML | `coreml` | yolov8n.mlmodel
13
+ TensorFlow SavedModel | `saved_model` | yolov8n_saved_model/
14
+ TensorFlow GraphDef | `pb` | yolov8n.pb
15
+ TensorFlow Lite | `tflite` | yolov8n.tflite
16
+ TensorFlow Edge TPU | `edgetpu` | yolov8n_edgetpu.tflite
17
+ TensorFlow.js | `tfjs` | yolov8n_web_model/
18
+ PaddlePaddle | `paddle` | yolov8n_paddle_model/
19
+
20
+ Requirements:
21
+ $ pip install ultralytics[export]
22
+
23
+ Python:
24
+ from ultralytics import YOLO
25
+ model = YOLO('yolov8n.pt')
26
+ results = model.export(format='onnx')
27
+
28
+ CLI:
29
+ $ yolo mode=export model=yolov8n.pt format=onnx
30
+
31
+ Inference:
32
+ $ yolo predict model=yolov8n.pt # PyTorch
33
+ yolov8n.torchscript # TorchScript
34
+ yolov8n.onnx # ONNX Runtime or OpenCV DNN with --dnn
35
+ yolov8n_openvino_model # OpenVINO
36
+ yolov8n.engine # TensorRT
37
+ yolov8n.mlmodel # CoreML (macOS-only)
38
+ yolov8n_saved_model # TensorFlow SavedModel
39
+ yolov8n.pb # TensorFlow GraphDef
40
+ yolov8n.tflite # TensorFlow Lite
41
+ yolov8n_edgetpu.tflite # TensorFlow Edge TPU
42
+ yolov8n_paddle_model # PaddlePaddle
43
+
44
+ TensorFlow.js:
45
+ $ cd .. && git clone https://github.com/zldrobit/tfjs-yolov5-example.git && cd tfjs-yolov5-example
46
+ $ npm install
47
+ $ ln -s ../../yolov5/yolov8n_web_model public/yolov8n_web_model
48
+ $ npm start
49
+ """
50
+ import json
51
+ import os
52
+ import platform
53
+ import subprocess
54
+ import time
55
+ import warnings
56
+ from copy import deepcopy
57
+ from pathlib import Path
58
+
59
+ import torch
60
+
61
+ from ...nn.autobackend import check_class_names
62
+ from ...nn.modules import C2f, Detect, Segment
63
+ from ...nn.tasks import DetectionModel, SegmentationModel
64
+ from ..cfg import get_cfg
65
+ from ..utils import (
66
+ DEFAULT_CFG,
67
+ LINUX,
68
+ LOGGER,
69
+ MACOS,
70
+ __version__,
71
+ callbacks,
72
+ colorstr,
73
+ get_default_args,
74
+ yaml_save,
75
+ )
76
+ from ..utils.checks import (
77
+ check_imgsz,
78
+ check_requirements,
79
+ check_version,
80
+ )
81
+ from ..utils.files import file_size
82
+ from ..utils.ops import Profile
83
+ from ..utils.torch_utils import (
84
+ get_latest_opset,
85
+ select_device,
86
+ smart_inference_mode,
87
+ )
88
+
89
+ ARM64 = platform.machine() in ("arm64", "aarch64")
90
+
91
+
92
+ def export_formats():
93
+ """YOLOv8 export formats."""
94
+ import pandas
95
+
96
+ x = [
97
+ ["PyTorch", "-", ".pt", True, True],
98
+ ["TorchScript", "torchscript", ".torchscript", True, True],
99
+ ["ONNX", "onnx", ".onnx", True, True],
100
+ ["OpenVINO", "openvino", "_openvino_model", True, False],
101
+ ["TensorRT", "engine", ".engine", False, True],
102
+ ["CoreML", "coreml", ".mlmodel", True, False],
103
+ ["TensorFlow SavedModel", "saved_model", "_saved_model", True, True],
104
+ ["TensorFlow GraphDef", "pb", ".pb", True, True],
105
+ ["TensorFlow Lite", "tflite", ".tflite", True, False],
106
+ ["TensorFlow Edge TPU", "edgetpu", "_edgetpu.tflite", True, False],
107
+ ["TensorFlow.js", "tfjs", "_web_model", True, False],
108
+ ["PaddlePaddle", "paddle", "_paddle_model", True, True],
109
+ ]
110
+ return pandas.DataFrame(
111
+ x, columns=["Format", "Argument", "Suffix", "CPU", "GPU"]
112
+ )
113
+
114
+
115
+ def gd_outputs(gd):
116
+ """TensorFlow GraphDef model output node names."""
117
+ name_list, input_list = [], []
118
+ for node in gd.node: # tensorflow.core.framework.node_def_pb2.NodeDef
119
+ name_list.append(node.name)
120
+ input_list.extend(node.input)
121
+ return sorted(
122
+ f"{x}:0"
123
+ for x in list(set(name_list) - set(input_list))
124
+ if not x.startswith("NoOp")
125
+ )
126
+
127
+
128
+ def try_export(inner_func):
129
+ """YOLOv8 export decorator, i..e @try_export."""
130
+ inner_args = get_default_args(inner_func)
131
+
132
+ def outer_func(*args, **kwargs):
133
+ """Export a model."""
134
+ prefix = inner_args["prefix"]
135
+ try:
136
+ with Profile() as dt:
137
+ f, model = inner_func(*args, **kwargs)
138
+ LOGGER.info(
139
+ f"{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)"
140
+ )
141
+ return f, model
142
+ except Exception as e:
143
+ LOGGER.info(f"{prefix} export failure ❌ {dt.t:.1f}s: {e}")
144
+ return None, None
145
+
146
+ return outer_func
147
+
148
+
149
+ class Exporter:
150
+ """
151
+ A class for exporting a model.
152
+
153
+ Attributes:
154
+ args (SimpleNamespace): Configuration for the exporter.
155
+ save_dir (Path): Directory to save results.
156
+ """
157
+
158
+ def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
159
+ """
160
+ Initializes the Exporter class.
161
+
162
+ Args:
163
+ cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CFG.
164
+ overrides (dict, optional): Configuration overrides. Defaults to None.
165
+ _callbacks (list, optional): List of callback functions. Defaults to None.
166
+ """
167
+ self.args = get_cfg(cfg, overrides)
168
+ self.callbacks = _callbacks or callbacks.get_default_callbacks()
169
+
170
+ @smart_inference_mode()
171
+ def __call__(self, model=None):
172
+ """Returns list of exported files/dirs after running callbacks."""
173
+ self.run_callbacks("on_export_start")
174
+ t = time.time()
175
+ format = self.args.format.lower() # to lowercase
176
+ if format in ("tensorrt", "trt"): # engine aliases
177
+ format = "engine"
178
+ fmts = tuple(
179
+ export_formats()["Argument"][1:]
180
+ ) # available export formats
181
+ flags = [x == format for x in fmts]
182
+ if sum(flags) != 1:
183
+ raise ValueError(
184
+ f"Invalid export format='{format}'. Valid formats are {fmts}"
185
+ )
186
+ (
187
+ jit,
188
+ onnx,
189
+ xml,
190
+ engine,
191
+ coreml,
192
+ saved_model,
193
+ pb,
194
+ tflite,
195
+ edgetpu,
196
+ tfjs,
197
+ paddle,
198
+ ) = flags # export booleans
199
+
200
+ # Load PyTorch model
201
+ self.device = select_device(
202
+ "cpu" if self.args.device is None else self.args.device
203
+ )
204
+ if self.args.half and onnx and self.device.type == "cpu":
205
+ LOGGER.warning(
206
+ "WARNING ⚠️ half=True only compatible with GPU export, i.e. use device=0"
207
+ )
208
+ self.args.half = False
209
+ assert (
210
+ not self.args.dynamic
211
+ ), "half=True not compatible with dynamic=True, i.e. use only one."
212
+
213
+ # Checks
214
+ model.names = check_class_names(model.names)
215
+ self.imgsz = check_imgsz(
216
+ self.args.imgsz, stride=model.stride, min_dim=2
217
+ ) # check image size
218
+ if self.args.optimize:
219
+ assert (
220
+ self.device.type == "cpu"
221
+ ), "--optimize not compatible with cuda devices, i.e. use --device cpu"
222
+ if edgetpu and not LINUX:
223
+ raise SystemError(
224
+ "Edge TPU export only supported on Linux. See https://coral.ai/docs/edgetpu/compiler/"
225
+ )
226
+
227
+ # Input
228
+ im = torch.zeros(self.args.batch, 3, *self.imgsz).to(self.device)
229
+ file = Path(
230
+ getattr(model, "pt_path", None)
231
+ or getattr(model, "yaml_file", None)
232
+ or model.yaml.get("yaml_file", "")
233
+ )
234
+ if file.suffix == ".yaml":
235
+ file = Path(file.name)
236
+
237
+ # Update model
238
+ model = deepcopy(model).to(self.device)
239
+ for p in model.parameters():
240
+ p.requires_grad = False
241
+ model.eval()
242
+ model.float()
243
+ model = model.fuse()
244
+ for k, m in model.named_modules():
245
+ if isinstance(m, (Detect, Segment)):
246
+ m.dynamic = self.args.dynamic
247
+ m.export = True
248
+ m.format = self.args.format
249
+ elif isinstance(m, C2f) and not any(
250
+ (saved_model, pb, tflite, edgetpu, tfjs)
251
+ ):
252
+ # EdgeTPU does not support FlexSplitV while split provides cleaner ONNX graph
253
+ m.forward = m.forward_split
254
+
255
+ y = None
256
+ for _ in range(2):
257
+ y = model(im) # dry runs
258
+ if self.args.half and (engine or onnx) and self.device.type != "cpu":
259
+ im, model = im.half(), model.half() # to FP16
260
+
261
+ # Warnings
262
+ warnings.filterwarnings(
263
+ "ignore", category=torch.jit.TracerWarning
264
+ ) # suppress TracerWarning
265
+ warnings.filterwarnings(
266
+ "ignore", category=UserWarning
267
+ ) # suppress shape prim::Constant missing ONNX warning
268
+ warnings.filterwarnings(
269
+ "ignore", category=DeprecationWarning
270
+ ) # suppress CoreML np.bool deprecation warning
271
+
272
+ # Assign
273
+ self.im = im
274
+ self.model = model
275
+ self.file = file
276
+ self.output_shape = (
277
+ tuple(y.shape)
278
+ if isinstance(y, torch.Tensor)
279
+ else tuple(
280
+ tuple(x.shape if isinstance(x, torch.Tensor) else [])
281
+ for x in y
282
+ )
283
+ )
284
+ self.pretty_name = Path(
285
+ self.model.yaml.get("yaml_file", self.file)
286
+ ).stem.replace("yolo", "YOLO")
287
+ trained_on = (
288
+ f"trained on {Path(self.args.data).name}"
289
+ if self.args.data
290
+ else "(untrained)"
291
+ )
292
+ description = f"Ultralytics {self.pretty_name} model {trained_on}"
293
+ self.metadata = {
294
+ "description": description,
295
+ "author": "Ultralytics",
296
+ "license": "AGPL-3.0 https://ultralytics.com/license",
297
+ "version": __version__,
298
+ "stride": int(max(model.stride)),
299
+ "task": model.task,
300
+ "batch": self.args.batch,
301
+ "imgsz": self.imgsz,
302
+ "names": model.names,
303
+ } # model metadata
304
+ if model.task == "pose":
305
+ self.metadata["kpt_shape"] = model.kpt_shape
306
+
307
+ LOGGER.info(
308
+ f"\n{colorstr('PyTorch:')} starting from {file} with input shape {tuple(im.shape)} BCHW and "
309
+ f"output shape(s) {self.output_shape} ({file_size(file):.1f} MB)"
310
+ )
311
+
312
+ # Exports
313
+ f = [""] * len(fmts) # exported filenames
314
+ if jit: # TorchScript
315
+ f[0], _ = self.export_torchscript()
316
+ if engine: # TensorRT required before ONNX
317
+ f[1], _ = self.export_engine()
318
+ if onnx or xml: # OpenVINO requires ONNX
319
+ f[2], _ = self.export_onnx()
320
+ if xml: # OpenVINO
321
+ f[3], _ = self.export_openvino()
322
+ if coreml: # CoreML
323
+ f[4], _ = self.export_coreml()
324
+ if any((saved_model, pb, tflite, edgetpu, tfjs)): # TensorFlow formats
325
+ self.args.int8 |= edgetpu
326
+ f[5], s_model = self.export_saved_model()
327
+ if pb or tfjs: # pb prerequisite to tfjs
328
+ f[6], _ = self.export_pb(s_model)
329
+ if tflite:
330
+ f[7], _ = self.export_tflite(
331
+ s_model, nms=False, agnostic_nms=self.args.agnostic_nms
332
+ )
333
+ if edgetpu:
334
+ f[8], _ = self.export_edgetpu(
335
+ tflite_model=Path(f[5])
336
+ / f"{self.file.stem}_full_integer_quant.tflite"
337
+ )
338
+ if tfjs:
339
+ f[9], _ = self.export_tfjs()
340
+ if paddle: # PaddlePaddle
341
+ f[10], _ = self.export_paddle()
342
+
343
+ # Finish
344
+ f = [str(x) for x in f if x] # filter out '' and None
345
+ if any(f):
346
+ f = str(Path(f[-1]))
347
+ square = self.imgsz[0] == self.imgsz[1]
348
+ s = (
349
+ ""
350
+ if square
351
+ else f"WARNING ⚠️ non-PyTorch val requires square images, 'imgsz={self.imgsz}' will not "
352
+ f"work. Use export 'imgsz={max(self.imgsz)}' if val is required."
353
+ )
354
+ imgsz = (
355
+ self.imgsz[0]
356
+ if square
357
+ else str(self.imgsz)[1:-1].replace(" ", "")
358
+ )
359
+ data = (
360
+ f"data={self.args.data}"
361
+ if model.task == "segment" and format == "pb"
362
+ else ""
363
+ )
364
+ LOGGER.info(
365
+ f"\nExport complete ({time.time() - t:.1f}s)"
366
+ f"\nResults saved to {colorstr('bold', file.parent.resolve())}"
367
+ f"\nPredict: yolo predict task={model.task} model={f} imgsz={imgsz} {data}"
368
+ f"\nValidate: yolo val task={model.task} model={f} imgsz={imgsz} data={self.args.data} {s}"
369
+ f"\nVisualize: https://netron.app"
370
+ )
371
+
372
+ self.run_callbacks("on_export_end")
373
+ return f # return list of exported files/dirs
374
+
375
+ @try_export
376
+ def export_torchscript(self, prefix=colorstr("TorchScript:")):
377
+ """YOLOv8 TorchScript model export."""
378
+ LOGGER.info(
379
+ f"\n{prefix} starting export with torch {torch.__version__}..."
380
+ )
381
+ f = self.file.with_suffix(".torchscript")
382
+
383
+ ts = torch.jit.trace(self.model, self.im, strict=False)
384
+ extra_files = {
385
+ "config.txt": json.dumps(self.metadata)
386
+ } # torch._C.ExtraFilesMap()
387
+ if (
388
+ self.args.optimize
389
+ ): # https://pytorch.org/tutorials/recipes/mobile_interpreter.html
390
+ LOGGER.info(f"{prefix} optimizing for mobile...")
391
+ from torch.utils.mobile_optimizer import optimize_for_mobile
392
+
393
+ optimize_for_mobile(ts)._save_for_lite_interpreter(
394
+ str(f), _extra_files=extra_files
395
+ )
396
+ else:
397
+ ts.save(str(f), _extra_files=extra_files)
398
+ return f, None
399
+
400
+ @try_export
401
+ def export_onnx(self, prefix=colorstr("ONNX:")):
402
+ """YOLOv8 ONNX export."""
403
+ requirements = ["onnx>=1.12.0"]
404
+ if self.args.simplify:
405
+ requirements += [
406
+ "onnxsim>=0.4.17",
407
+ (
408
+ "onnxruntime-gpu"
409
+ if torch.cuda.is_available()
410
+ else "onnxruntime"
411
+ ),
412
+ ]
413
+ check_requirements(requirements)
414
+ import onnx # noqa
415
+
416
+ opset_version = self.args.opset or get_latest_opset()
417
+ LOGGER.info(
418
+ f"\n{prefix} starting export with onnx {onnx.__version__} opset {opset_version}..."
419
+ )
420
+ f = str(self.file.with_suffix(".onnx"))
421
+
422
+ output_names = (
423
+ ["output0", "output1"]
424
+ if isinstance(self.model, SegmentationModel)
425
+ else ["output0"]
426
+ )
427
+ dynamic = self.args.dynamic
428
+ if dynamic:
429
+ dynamic = {
430
+ "images": {0: "batch", 2: "height", 3: "width"}
431
+ } # shape(1,3,640,640)
432
+ if isinstance(self.model, SegmentationModel):
433
+ dynamic["output0"] = {
434
+ 0: "batch",
435
+ 1: "anchors",
436
+ } # shape(1,25200,85)
437
+ dynamic["output1"] = {
438
+ 0: "batch",
439
+ 2: "mask_height",
440
+ 3: "mask_width",
441
+ } # shape(1,32,160,160)
442
+ elif isinstance(self.model, DetectionModel):
443
+ dynamic["output0"] = {
444
+ 0: "batch",
445
+ 1: "anchors",
446
+ } # shape(1,25200,85)
447
+
448
+ torch.onnx.export(
449
+ (
450
+ self.model.cpu() if dynamic else self.model
451
+ ), # --dynamic only compatible with cpu
452
+ self.im.cpu() if dynamic else self.im,
453
+ f,
454
+ verbose=False,
455
+ opset_version=opset_version,
456
+ do_constant_folding=True, # WARNING: DNN inference with torch>=1.12 may require do_constant_folding=False
457
+ input_names=["images"],
458
+ output_names=output_names,
459
+ dynamic_axes=dynamic or None,
460
+ )
461
+
462
+ # Checks
463
+ model_onnx = onnx.load(f) # load onnx model
464
+ # onnx.checker.check_model(model_onnx) # check onnx model
465
+
466
+ # Simplify
467
+ if self.args.simplify:
468
+ try:
469
+ import onnxsim
470
+
471
+ LOGGER.info(
472
+ f"{prefix} simplifying with onnxsim {onnxsim.__version__}..."
473
+ )
474
+ # subprocess.run(f'onnxsim {f} {f}', shell=True)
475
+ model_onnx, check = onnxsim.simplify(model_onnx)
476
+ assert check, "Simplified ONNX model could not be validated"
477
+ except Exception as e:
478
+ LOGGER.info(f"{prefix} simplifier failure: {e}")
479
+
480
+ # Metadata
481
+ for k, v in self.metadata.items():
482
+ meta = model_onnx.metadata_props.add()
483
+ meta.key, meta.value = k, str(v)
484
+
485
+ onnx.save(model_onnx, f)
486
+ return f, model_onnx
487
+
488
+ @try_export
489
+ def export_openvino(self, prefix=colorstr("OpenVINO:")):
490
+ """YOLOv8 OpenVINO export."""
491
+ check_requirements(
492
+ "openvino-dev>=2022.3"
493
+ ) # requires openvino-dev: https://pypi.org/project/openvino-dev/
494
+ import openvino.runtime as ov # noqa
495
+ from openvino.tools import mo # noqa
496
+
497
+ LOGGER.info(
498
+ f"\n{prefix} starting export with openvino {ov.__version__}..."
499
+ )
500
+ f = str(self.file).replace(
501
+ self.file.suffix, f"_openvino_model{os.sep}"
502
+ )
503
+ f_onnx = self.file.with_suffix(".onnx")
504
+ f_ov = str(Path(f) / self.file.with_suffix(".xml").name)
505
+
506
+ ov_model = mo.convert_model(
507
+ f_onnx,
508
+ model_name=self.pretty_name,
509
+ framework="onnx",
510
+ compress_to_fp16=self.args.half,
511
+ ) # export
512
+
513
+ # Set RT info
514
+ ov_model.set_rt_info("YOLOv8", ["model_info", "model_type"])
515
+ ov_model.set_rt_info(True, ["model_info", "reverse_input_channels"])
516
+ ov_model.set_rt_info(114, ["model_info", "pad_value"])
517
+ ov_model.set_rt_info([255.0], ["model_info", "scale_values"])
518
+ ov_model.set_rt_info(self.args.iou, ["model_info", "iou_threshold"])
519
+ ov_model.set_rt_info(
520
+ [v.replace(" ", "_") for k, v in sorted(self.model.names.items())],
521
+ ["model_info", "labels"],
522
+ )
523
+ if self.model.task != "classify":
524
+ ov_model.set_rt_info(
525
+ "fit_to_window_letterbox", ["model_info", "resize_type"]
526
+ )
527
+
528
+ ov.serialize(ov_model, f_ov) # save
529
+ yaml_save(
530
+ Path(f) / "metadata.yaml", self.metadata
531
+ ) # add metadata.yaml
532
+ return f, None
533
+
534
+ @try_export
535
+ def export_paddle(self, prefix=colorstr("PaddlePaddle:")):
536
+ """YOLOv8 Paddle export."""
537
+ check_requirements(("paddlepaddle", "x2paddle"))
538
+ import x2paddle # noqa
539
+ from x2paddle.convert import pytorch2paddle # noqa
540
+
541
+ LOGGER.info(
542
+ f"\n{prefix} starting export with X2Paddle {x2paddle.__version__}..."
543
+ )
544
+ f = str(self.file).replace(self.file.suffix, f"_paddle_model{os.sep}")
545
+
546
+ pytorch2paddle(
547
+ module=self.model,
548
+ save_dir=f,
549
+ jit_type="trace",
550
+ input_examples=[self.im],
551
+ ) # export
552
+ yaml_save(
553
+ Path(f) / "metadata.yaml", self.metadata
554
+ ) # add metadata.yaml
555
+ return f, None
556
+
557
+ @try_export
558
+ def export_coreml(self, prefix=colorstr("CoreML:")):
559
+ """YOLOv8 CoreML export."""
560
+ check_requirements("coremltools>=6.0")
561
+ import coremltools as ct # noqa
562
+
563
+ LOGGER.info(
564
+ f"\n{prefix} starting export with coremltools {ct.__version__}..."
565
+ )
566
+ f = self.file.with_suffix(".mlmodel")
567
+
568
+ bias = [0.0, 0.0, 0.0]
569
+ scale = 1 / 255
570
+ classifier_config = None
571
+ if self.model.task == "classify":
572
+ classifier_config = (
573
+ ct.ClassifierConfig(list(self.model.names.values()))
574
+ if self.args.nms
575
+ else None
576
+ )
577
+ model = self.model
578
+ elif self.model.task == "detect":
579
+ model = (
580
+ iOSDetectModel(self.model, self.im)
581
+ if self.args.nms
582
+ else self.model
583
+ )
584
+ else:
585
+ # TODO CoreML Segment and Pose model pipelining
586
+ model = self.model
587
+
588
+ ts = torch.jit.trace(
589
+ model.eval(), self.im, strict=False
590
+ ) # TorchScript model
591
+ ct_model = ct.convert(
592
+ ts,
593
+ inputs=[
594
+ ct.ImageType(
595
+ "image", shape=self.im.shape, scale=scale, bias=bias
596
+ )
597
+ ],
598
+ classifier_config=classifier_config,
599
+ )
600
+ bits, mode = (
601
+ (8, "kmeans_lut")
602
+ if self.args.int8
603
+ else (16, "linear") if self.args.half else (32, None)
604
+ )
605
+ if bits < 32:
606
+ if "kmeans" in mode:
607
+ check_requirements(
608
+ "scikit-learn"
609
+ ) # scikit-learn package required for k-means quantization
610
+ ct_model = (
611
+ ct.models.neural_network.quantization_utils.quantize_weights(
612
+ ct_model, bits, mode
613
+ )
614
+ )
615
+ if self.args.nms and self.model.task == "detect":
616
+ ct_model = self._pipeline_coreml(ct_model)
617
+
618
+ m = self.metadata # metadata dict
619
+ ct_model.short_description = m.pop("description")
620
+ ct_model.author = m.pop("author")
621
+ ct_model.license = m.pop("license")
622
+ ct_model.version = m.pop("version")
623
+ ct_model.user_defined_metadata.update(
624
+ {k: str(v) for k, v in m.items()}
625
+ )
626
+ ct_model.save(str(f))
627
+ return f, ct_model
628
+
629
+ @try_export
630
+ def export_engine(self, prefix=colorstr("TensorRT:")):
631
+ """YOLOv8 TensorRT export https://developer.nvidia.com/tensorrt."""
632
+ assert (
633
+ self.im.device.type != "cpu"
634
+ ), "export running on CPU but must be on GPU, i.e. use 'device=0'"
635
+ try:
636
+ import tensorrt as trt # noqa
637
+ except ImportError:
638
+ if LINUX:
639
+ check_requirements(
640
+ "nvidia-tensorrt",
641
+ cmds="-U --index-url https://pypi.ngc.nvidia.com",
642
+ )
643
+ import tensorrt as trt # noqa
644
+
645
+ check_version(
646
+ trt.__version__, "7.0.0", hard=True
647
+ ) # require tensorrt>=8.0.0
648
+ self.args.simplify = True
649
+ f_onnx, _ = self.export_onnx()
650
+
651
+ LOGGER.info(
652
+ f"\n{prefix} starting export with TensorRT {trt.__version__}..."
653
+ )
654
+ assert Path(f_onnx).exists(), f"failed to export ONNX file: {f_onnx}"
655
+ f = self.file.with_suffix(".engine") # TensorRT engine file
656
+ logger = trt.Logger(trt.Logger.INFO)
657
+ if self.args.verbose:
658
+ logger.min_severity = trt.Logger.Severity.VERBOSE
659
+
660
+ builder = trt.Builder(logger)
661
+ config = builder.create_builder_config()
662
+ config.max_workspace_size = self.args.workspace * 1 << 30
663
+ # config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace << 30) # fix TRT 8.4 deprecation notice
664
+
665
+ flag = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
666
+ network = builder.create_network(flag)
667
+ parser = trt.OnnxParser(network, logger)
668
+ if not parser.parse_from_file(f_onnx):
669
+ raise RuntimeError(f"failed to load ONNX file: {f_onnx}")
670
+
671
+ inputs = [network.get_input(i) for i in range(network.num_inputs)]
672
+ outputs = [network.get_output(i) for i in range(network.num_outputs)]
673
+ for inp in inputs:
674
+ LOGGER.info(
675
+ f'{prefix} input "{inp.name}" with shape{inp.shape} {inp.dtype}'
676
+ )
677
+ for out in outputs:
678
+ LOGGER.info(
679
+ f'{prefix} output "{out.name}" with shape{out.shape} {out.dtype}'
680
+ )
681
+
682
+ if self.args.dynamic:
683
+ shape = self.im.shape
684
+ if shape[0] <= 1:
685
+ LOGGER.warning(
686
+ f"{prefix} WARNING ⚠️ --dynamic model requires maximum --batch-size argument"
687
+ )
688
+ profile = builder.create_optimization_profile()
689
+ for inp in inputs:
690
+ profile.set_shape(
691
+ inp.name,
692
+ (1, *shape[1:]),
693
+ (max(1, shape[0] // 2), *shape[1:]),
694
+ shape,
695
+ )
696
+ config.add_optimization_profile(profile)
697
+
698
+ LOGGER.info(
699
+ f"{prefix} building FP{16 if builder.platform_has_fast_fp16 and self.args.half else 32} engine as {f}"
700
+ )
701
+ if builder.platform_has_fast_fp16 and self.args.half:
702
+ config.set_flag(trt.BuilderFlag.FP16)
703
+
704
+ # Write file
705
+ with builder.build_engine(network, config) as engine, open(
706
+ f, "wb"
707
+ ) as t:
708
+ # Metadata
709
+ meta = json.dumps(self.metadata)
710
+ t.write(len(meta).to_bytes(4, byteorder="little", signed=True))
711
+ t.write(meta.encode())
712
+ # Model
713
+ t.write(engine.serialize())
714
+
715
+ return f, None
716
+
717
+ @try_export
718
+ def export_saved_model(self, prefix=colorstr("TensorFlow SavedModel:")):
719
+ """YOLOv8 TensorFlow SavedModel export."""
720
+ try:
721
+ import tensorflow as tf # noqa
722
+ except ImportError:
723
+ cuda = torch.cuda.is_available()
724
+ check_requirements(
725
+ f"tensorflow{'-macos' if MACOS else '-aarch64' if ARM64 else '' if cuda else '-cpu'}"
726
+ )
727
+ import tensorflow as tf # noqa
728
+ check_requirements(
729
+ (
730
+ "onnx",
731
+ "onnx2tf>=1.7.7",
732
+ "sng4onnx>=1.0.1",
733
+ "onnxsim>=0.4.17",
734
+ "onnx_graphsurgeon>=0.3.26",
735
+ "tflite_support",
736
+ (
737
+ "onnxruntime-gpu"
738
+ if torch.cuda.is_available()
739
+ else "onnxruntime"
740
+ ),
741
+ ),
742
+ cmds="--extra-index-url https://pypi.ngc.nvidia.com",
743
+ )
744
+
745
+ LOGGER.info(
746
+ f"\n{prefix} starting export with tensorflow {tf.__version__}..."
747
+ )
748
+ f = Path(str(self.file).replace(self.file.suffix, "_saved_model"))
749
+ if f.is_dir():
750
+ import shutil
751
+
752
+ shutil.rmtree(f) # delete output folder
753
+
754
+ # Export to ONNX
755
+ self.args.simplify = True
756
+ f_onnx, _ = self.export_onnx()
757
+
758
+ # Export to TF
759
+ int8 = "-oiqt -qt per-tensor" if self.args.int8 else ""
760
+ cmd = f"onnx2tf -i {f_onnx} -o {f} -nuo --non_verbose {int8}"
761
+ LOGGER.info(f"\n{prefix} running '{cmd.strip()}'")
762
+ subprocess.run(cmd, shell=True)
763
+ yaml_save(f / "metadata.yaml", self.metadata) # add metadata.yaml
764
+
765
+ # Remove/rename TFLite models
766
+ if self.args.int8:
767
+ for file in f.rglob("*_dynamic_range_quant.tflite"):
768
+ file.rename(
769
+ file.with_stem(
770
+ file.stem.replace("_dynamic_range_quant", "_int8")
771
+ )
772
+ )
773
+ for file in f.rglob("*_integer_quant_with_int16_act.tflite"):
774
+ file.unlink() # delete extra fp16 activation TFLite files
775
+
776
+ # Add TFLite metadata
777
+ for file in f.rglob("*.tflite"):
778
+ (
779
+ f.unlink()
780
+ if "quant_with_int16_act.tflite" in str(f)
781
+ else self._add_tflite_metadata(file)
782
+ )
783
+
784
+ # Load saved_model
785
+ keras_model = tf.saved_model.load(f, tags=None, options=None)
786
+
787
+ return str(f), keras_model
788
+
789
+ @try_export
790
+ def export_pb(self, keras_model, prefix=colorstr("TensorFlow GraphDef:")):
791
+ """YOLOv8 TensorFlow GraphDef *.pb export https://github.com/leimao/Frozen_Graph_TensorFlow."""
792
+ import tensorflow as tf # noqa
793
+ from tensorflow.python.framework.convert_to_constants import (
794
+ convert_variables_to_constants_v2,
795
+ ) # noqa
796
+
797
+ LOGGER.info(
798
+ f"\n{prefix} starting export with tensorflow {tf.__version__}..."
799
+ )
800
+ f = self.file.with_suffix(".pb")
801
+
802
+ m = tf.function(lambda x: keras_model(x)) # full model
803
+ m = m.get_concrete_function(
804
+ tf.TensorSpec(
805
+ keras_model.inputs[0].shape, keras_model.inputs[0].dtype
806
+ )
807
+ )
808
+ frozen_func = convert_variables_to_constants_v2(m)
809
+ frozen_func.graph.as_graph_def()
810
+ tf.io.write_graph(
811
+ graph_or_graph_def=frozen_func.graph,
812
+ logdir=str(f.parent),
813
+ name=f.name,
814
+ as_text=False,
815
+ )
816
+ return f, None
817
+
818
+ @try_export
819
+ def export_tflite(
820
+ self,
821
+ keras_model,
822
+ nms,
823
+ agnostic_nms,
824
+ prefix=colorstr("TensorFlow Lite:"),
825
+ ):
826
+ """YOLOv8 TensorFlow Lite export."""
827
+ import tensorflow as tf # noqa
828
+
829
+ LOGGER.info(
830
+ f"\n{prefix} starting export with tensorflow {tf.__version__}..."
831
+ )
832
+ saved_model = Path(
833
+ str(self.file).replace(self.file.suffix, "_saved_model")
834
+ )
835
+ if self.args.int8:
836
+ f = saved_model / f"{self.file.stem}_int8.tflite" # fp32 in/out
837
+ elif self.args.half:
838
+ f = saved_model / f"{self.file.stem}_float16.tflite" # fp32 in/out
839
+ else:
840
+ f = saved_model / f"{self.file.stem}_float32.tflite"
841
+ return str(f), None
842
+
843
+ @try_export
844
+ def export_edgetpu(self, tflite_model="", prefix=colorstr("Edge TPU:")):
845
+ """YOLOv8 Edge TPU export https://coral.ai/docs/edgetpu/models-intro/."""
846
+ LOGGER.warning(
847
+ f"{prefix} WARNING ⚠️ Edge TPU known bug https://github.com/ultralytics/ultralytics/issues/1185"
848
+ )
849
+
850
+ cmd = "edgetpu_compiler --version"
851
+ help_url = "https://coral.ai/docs/edgetpu/compiler/"
852
+ assert LINUX, f"export only supported on Linux. See {help_url}"
853
+ if (
854
+ subprocess.run(
855
+ cmd,
856
+ stdout=subprocess.DEVNULL,
857
+ stderr=subprocess.DEVNULL,
858
+ shell=True,
859
+ ).returncode
860
+ != 0
861
+ ):
862
+ LOGGER.info(
863
+ f"\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}"
864
+ )
865
+ sudo = (
866
+ subprocess.run(
867
+ "sudo --version >/dev/null", shell=True
868
+ ).returncode
869
+ == 0
870
+ ) # sudo installed on system
871
+ for c in (
872
+ "curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -",
873
+ 'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',
874
+ "sudo apt-get update",
875
+ "sudo apt-get install edgetpu-compiler",
876
+ ):
877
+ subprocess.run(
878
+ c if sudo else c.replace("sudo ", ""),
879
+ shell=True,
880
+ check=True,
881
+ )
882
+ ver = (
883
+ subprocess.run(cmd, shell=True, capture_output=True, check=True)
884
+ .stdout.decode()
885
+ .split()[-1]
886
+ )
887
+
888
+ LOGGER.info(
889
+ f"\n{prefix} starting export with Edge TPU compiler {ver}..."
890
+ )
891
+ f = str(tflite_model).replace(
892
+ ".tflite", "_edgetpu.tflite"
893
+ ) # Edge TPU model
894
+
895
+ cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {Path(f).parent} {tflite_model}"
896
+ LOGGER.info(f"{prefix} running '{cmd}'")
897
+ subprocess.run(cmd.split(), check=True)
898
+ self._add_tflite_metadata(f)
899
+ return f, None
900
+
901
+ @try_export
902
+ def export_tfjs(self, prefix=colorstr("TensorFlow.js:")):
903
+ """YOLOv8 TensorFlow.js export."""
904
+ check_requirements("tensorflowjs")
905
+ import tensorflow as tf
906
+ import tensorflowjs as tfjs # noqa
907
+
908
+ LOGGER.info(
909
+ f"\n{prefix} starting export with tensorflowjs {tfjs.__version__}..."
910
+ )
911
+ f = str(self.file).replace(self.file.suffix, "_web_model") # js dir
912
+ f_pb = self.file.with_suffix(".pb") # *.pb path
913
+
914
+ gd = tf.Graph().as_graph_def() # TF GraphDef
915
+ with open(f_pb, "rb") as file:
916
+ gd.ParseFromString(file.read())
917
+ outputs = ",".join(gd_outputs(gd))
918
+ LOGGER.info(f"\n{prefix} output node names: {outputs}")
919
+
920
+ cmd = f"tensorflowjs_converter --input_format=tf_frozen_model --output_node_names={outputs} {f_pb} {f}"
921
+ subprocess.run(cmd.split(), check=True)
922
+
923
+ # f_json = Path(f) / 'model.json' # *.json path
924
+ # with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order
925
+ # subst = re.sub(
926
+ # r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
927
+ # r'"Identity.?.?": {"name": "Identity.?.?"}, '
928
+ # r'"Identity.?.?": {"name": "Identity.?.?"}, '
929
+ # r'"Identity.?.?": {"name": "Identity.?.?"}}}',
930
+ # r'{"outputs": {"Identity": {"name": "Identity"}, '
931
+ # r'"Identity_1": {"name": "Identity_1"}, '
932
+ # r'"Identity_2": {"name": "Identity_2"}, '
933
+ # r'"Identity_3": {"name": "Identity_3"}}}',
934
+ # f_json.read_text(),
935
+ # )
936
+ # j.write(subst)
937
+ yaml_save(
938
+ Path(f) / "metadata.yaml", self.metadata
939
+ ) # add metadata.yaml
940
+ return f, None
941
+
942
+ def _add_tflite_metadata(self, file):
943
+ """Add metadata to *.tflite models per https://www.tensorflow.org/lite/models/convert/metadata."""
944
+ from tflite_support import flatbuffers # noqa
945
+ from tflite_support import metadata as _metadata # noqa
946
+ from tflite_support import (
947
+ metadata_schema_py_generated as _metadata_fb,
948
+ ) # noqa
949
+
950
+ # Create model info
951
+ model_meta = _metadata_fb.ModelMetadataT()
952
+ model_meta.name = self.metadata["description"]
953
+ model_meta.version = self.metadata["version"]
954
+ model_meta.author = self.metadata["author"]
955
+ model_meta.license = self.metadata["license"]
956
+
957
+ # Label file
958
+ tmp_file = Path(file).parent / "temp_meta.txt"
959
+ with open(tmp_file, "w") as f:
960
+ f.write(str(self.metadata))
961
+
962
+ label_file = _metadata_fb.AssociatedFileT()
963
+ label_file.name = tmp_file.name
964
+ label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS
965
+
966
+ # Create input info
967
+ input_meta = _metadata_fb.TensorMetadataT()
968
+ input_meta.name = "image"
969
+ input_meta.description = "Input image to be detected."
970
+ input_meta.content = _metadata_fb.ContentT()
971
+ input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()
972
+ input_meta.content.contentProperties.colorSpace = (
973
+ _metadata_fb.ColorSpaceType.RGB
974
+ )
975
+ input_meta.content.contentPropertiesType = (
976
+ _metadata_fb.ContentProperties.ImageProperties
977
+ )
978
+
979
+ # Create output info
980
+ output1 = _metadata_fb.TensorMetadataT()
981
+ output1.name = "output"
982
+ output1.description = "Coordinates of detected objects, class labels, and confidence score"
983
+ output1.associatedFiles = [label_file]
984
+ if self.model.task == "segment":
985
+ output2 = _metadata_fb.TensorMetadataT()
986
+ output2.name = "output"
987
+ output2.description = "Mask protos"
988
+ output2.associatedFiles = [label_file]
989
+
990
+ # Create subgraph info
991
+ subgraph = _metadata_fb.SubGraphMetadataT()
992
+ subgraph.inputTensorMetadata = [input_meta]
993
+ subgraph.outputTensorMetadata = (
994
+ [output1, output2] if self.model.task == "segment" else [output1]
995
+ )
996
+ model_meta.subgraphMetadata = [subgraph]
997
+
998
+ b = flatbuffers.Builder(0)
999
+ b.Finish(
1000
+ model_meta.Pack(b),
1001
+ _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER,
1002
+ )
1003
+ metadata_buf = b.Output()
1004
+
1005
+ populator = _metadata.MetadataPopulator.with_model_file(str(file))
1006
+ populator.load_metadata_buffer(metadata_buf)
1007
+ populator.load_associated_files([str(tmp_file)])
1008
+ populator.populate()
1009
+ tmp_file.unlink()
1010
+
1011
+ def _pipeline_coreml(self, model, prefix=colorstr("CoreML Pipeline:")):
1012
+ """YOLOv8 CoreML pipeline."""
1013
+ import coremltools as ct # noqa
1014
+
1015
+ LOGGER.info(
1016
+ f"{prefix} starting pipeline with coremltools {ct.__version__}..."
1017
+ )
1018
+ batch_size, ch, h, w = list(self.im.shape) # BCHW
1019
+
1020
+ # Output shapes
1021
+ spec = model.get_spec()
1022
+ out0, out1 = iter(spec.description.output)
1023
+ if MACOS:
1024
+ from PIL import Image
1025
+
1026
+ img = Image.new("RGB", (w, h)) # img(192 width, 320 height)
1027
+ # img = torch.zeros((*opt.img_size, 3)).numpy() # img size(320,192,3) iDetection
1028
+ out = model.predict({"image": img})
1029
+ out0_shape = out[out0.name].shape
1030
+ out1_shape = out[out1.name].shape
1031
+ else: # linux and windows can not run model.predict(), get sizes from pytorch output y
1032
+ out0_shape = (
1033
+ self.output_shape[2],
1034
+ self.output_shape[1] - 4,
1035
+ ) # (3780, 80)
1036
+ out1_shape = self.output_shape[2], 4 # (3780, 4)
1037
+
1038
+ # Checks
1039
+ names = self.metadata["names"]
1040
+ nx, ny = (
1041
+ spec.description.input[0].type.imageType.width,
1042
+ spec.description.input[0].type.imageType.height,
1043
+ )
1044
+ na, nc = out0_shape
1045
+ # na, nc = out0.type.multiArrayType.shape # number anchors, classes
1046
+ assert (
1047
+ len(names) == nc
1048
+ ), f"{len(names)} names found for nc={nc}" # check
1049
+
1050
+ # Define output shapes (missing)
1051
+ out0.type.multiArrayType.shape[:] = out0_shape # (3780, 80)
1052
+ out1.type.multiArrayType.shape[:] = out1_shape # (3780, 4)
1053
+ # spec.neuralNetwork.preprocessing[0].featureName = '0'
1054
+
1055
+ # Flexible input shapes
1056
+ # from coremltools.models.neural_network import flexible_shape_utils
1057
+ # s = [] # shapes
1058
+ # s.append(flexible_shape_utils.NeuralNetworkImageSize(320, 192))
1059
+ # s.append(flexible_shape_utils.NeuralNetworkImageSize(640, 384)) # (height, width)
1060
+ # flexible_shape_utils.add_enumerated_image_sizes(spec, feature_name='image', sizes=s)
1061
+ # r = flexible_shape_utils.NeuralNetworkImageSizeRange() # shape ranges
1062
+ # r.add_height_range((192, 640))
1063
+ # r.add_width_range((192, 640))
1064
+ # flexible_shape_utils.update_image_size_range(spec, feature_name='image', size_range=r)
1065
+
1066
+ # Print
1067
+ # print(spec.description)
1068
+
1069
+ # Model from spec
1070
+ model = ct.models.MLModel(spec)
1071
+
1072
+ # 3. Create NMS protobuf
1073
+ nms_spec = ct.proto.Model_pb2.Model()
1074
+ nms_spec.specificationVersion = 5
1075
+ for i in range(2):
1076
+ decoder_output = model._spec.description.output[
1077
+ i
1078
+ ].SerializeToString()
1079
+ nms_spec.description.input.add()
1080
+ nms_spec.description.input[i].ParseFromString(decoder_output)
1081
+ nms_spec.description.output.add()
1082
+ nms_spec.description.output[i].ParseFromString(decoder_output)
1083
+
1084
+ nms_spec.description.output[0].name = "confidence"
1085
+ nms_spec.description.output[1].name = "coordinates"
1086
+
1087
+ output_sizes = [nc, 4]
1088
+ for i in range(2):
1089
+ ma_type = nms_spec.description.output[i].type.multiArrayType
1090
+ ma_type.shapeRange.sizeRanges.add()
1091
+ ma_type.shapeRange.sizeRanges[0].lowerBound = 0
1092
+ ma_type.shapeRange.sizeRanges[0].upperBound = -1
1093
+ ma_type.shapeRange.sizeRanges.add()
1094
+ ma_type.shapeRange.sizeRanges[1].lowerBound = output_sizes[i]
1095
+ ma_type.shapeRange.sizeRanges[1].upperBound = output_sizes[i]
1096
+ del ma_type.shape[:]
1097
+
1098
+ nms = nms_spec.nonMaximumSuppression
1099
+ nms.confidenceInputFeatureName = out0.name # 1x507x80
1100
+ nms.coordinatesInputFeatureName = out1.name # 1x507x4
1101
+ nms.confidenceOutputFeatureName = "confidence"
1102
+ nms.coordinatesOutputFeatureName = "coordinates"
1103
+ nms.iouThresholdInputFeatureName = "iouThreshold"
1104
+ nms.confidenceThresholdInputFeatureName = "confidenceThreshold"
1105
+ nms.iouThreshold = 0.45
1106
+ nms.confidenceThreshold = 0.25
1107
+ nms.pickTop.perClass = True
1108
+ nms.stringClassLabels.vector.extend(names.values())
1109
+ nms_model = ct.models.MLModel(nms_spec)
1110
+
1111
+ # 4. Pipeline models together
1112
+ pipeline = ct.models.pipeline.Pipeline(
1113
+ input_features=[
1114
+ ("image", ct.models.datatypes.Array(3, ny, nx)),
1115
+ ("iouThreshold", ct.models.datatypes.Double()),
1116
+ ("confidenceThreshold", ct.models.datatypes.Double()),
1117
+ ],
1118
+ output_features=["confidence", "coordinates"],
1119
+ )
1120
+ pipeline.add_model(model)
1121
+ pipeline.add_model(nms_model)
1122
+
1123
+ # Correct datatypes
1124
+ pipeline.spec.description.input[0].ParseFromString(
1125
+ model._spec.description.input[0].SerializeToString()
1126
+ )
1127
+ pipeline.spec.description.output[0].ParseFromString(
1128
+ nms_model._spec.description.output[0].SerializeToString()
1129
+ )
1130
+ pipeline.spec.description.output[1].ParseFromString(
1131
+ nms_model._spec.description.output[1].SerializeToString()
1132
+ )
1133
+
1134
+ # Update metadata
1135
+ pipeline.spec.specificationVersion = 5
1136
+ pipeline.spec.description.metadata.userDefined.update(
1137
+ {
1138
+ "IoU threshold": str(nms.iouThreshold),
1139
+ "Confidence threshold": str(nms.confidenceThreshold),
1140
+ }
1141
+ )
1142
+
1143
+ # Save the model
1144
+ model = ct.models.MLModel(pipeline.spec)
1145
+ model.input_description["image"] = "Input image"
1146
+ model.input_description["iouThreshold"] = (
1147
+ f"(optional) IOU threshold override (default: {nms.iouThreshold})"
1148
+ )
1149
+ model.input_description["confidenceThreshold"] = (
1150
+ f"(optional) Confidence threshold override (default: {nms.confidenceThreshold})"
1151
+ )
1152
+ model.output_description["confidence"] = (
1153
+ 'Boxes × Class confidence (see user-defined metadata "classes")'
1154
+ )
1155
+ model.output_description["coordinates"] = (
1156
+ "Boxes × [x, y, width, height] (relative to image size)"
1157
+ )
1158
+ LOGGER.info(f"{prefix} pipeline success")
1159
+ return model
1160
+
1161
+ def add_callback(self, event: str, callback):
1162
+ """
1163
+ Appends the given callback.
1164
+ """
1165
+ self.callbacks[event].append(callback)
1166
+
1167
+ def run_callbacks(self, event: str):
1168
+ """Execute all callbacks for a given event."""
1169
+ for callback in self.callbacks.get(event, []):
1170
+ callback(self)
1171
+
1172
+
1173
+ class iOSDetectModel(torch.nn.Module):
1174
+ """Wrap an Ultralytics YOLO model for iOS export."""
1175
+
1176
+ def __init__(self, model, im):
1177
+ """Initialize the iOSDetectModel class with a YOLO model and example image."""
1178
+ super().__init__()
1179
+ b, c, h, w = im.shape # batch, channel, height, width
1180
+ self.model = model
1181
+ self.nc = len(model.names) # number of classes
1182
+ if w == h:
1183
+ self.normalize = 1.0 / w # scalar
1184
+ else:
1185
+ self.normalize = torch.tensor(
1186
+ [1.0 / w, 1.0 / h, 1.0 / w, 1.0 / h]
1187
+ ) # broadcast (slower, smaller)
1188
+
1189
+ def forward(self, x):
1190
+ """Normalize predictions of object detection model with input size-dependent factors."""
1191
+ xywh, cls = self.model(x)[0].transpose(0, 1).split((4, self.nc), 1)
1192
+ return (
1193
+ cls,
1194
+ xywh * self.normalize,
1195
+ ) # confidence (3780, 80), coordinates (3780, 4)
1196
+
1197
+
1198
+ def export(cfg=DEFAULT_CFG):
1199
+ """Export a YOLOv model to a specific format."""
1200
+ cfg.model = cfg.model or "yolov8n.yaml"
1201
+ cfg.format = cfg.format or "torchscript"
1202
+
1203
+ from ultralytics import YOLO
1204
+
1205
+ model = YOLO(cfg.model)
1206
+ model.export(**vars(cfg))
1207
+
1208
+
1209
+ if __name__ == "__main__":
1210
+ """
1211
+ CLI:
1212
+ yolo mode=export model=yolov8n.yaml format=onnx
1213
+ """
1214
+ export()