k20hcmus/FishEye8K
3
1import argparse2import contextlib3import json4import os5import platform6import re7import subprocess8import sys9import time10import warnings11from pathlib import Path12 13import pandas as pd14import torch15from torch.utils.mobile_optimizer import optimize_for_mobile16 17FILE = Path(__file__).resolve()18ROOT = FILE.parents[0] # YOLO root directory19if str(ROOT) not in sys.path:20 sys.path.append(str(ROOT)) # add ROOT to PATH21if platform.system() != 'Windows':22 ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative23 24from models.experimental import attempt_load, End2End25from models.yolo import ClassificationModel, Detect, DDetect, DualDetect, DualDDetect, IDualDDetect, DetectionModel, SegmentationModel26from utils.dataloaders import LoadImages27from utils.general import (LOGGER, Profile, check_dataset, check_img_size, check_requirements, check_version,28 check_yaml, colorstr, file_size, get_default_args, print_args, url2file, yaml_save)29from utils.torch_utils import select_device, smart_inference_mode30 31MACOS = platform.system() == 'Darwin' # macOS environment32 33 34def export_formats():35 # YOLO export formats36 x = [37 ['PyTorch', '-', '.pt', True, True],38 ['TorchScript', 'torchscript', '.torchscript', True, True],39 ['ONNX', 'onnx', '.onnx', True, True],40 ['ONNX END2END', 'onnx_end2end', '_end2end.onnx', True, True],41 ['OpenVINO', 'openvino', '_openvino_model', True, False],42 ['TensorRT', 'engine', '.engine', False, True],43 ['CoreML', 'coreml', '.mlmodel', True, False],44 ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True],45 ['TensorFlow GraphDef', 'pb', '.pb', True, True],46 ['TensorFlow Lite', 'tflite', '.tflite', True, False],47 ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False],48 ['TensorFlow.js', 'tfjs', '_web_model', False, False],49 ['PaddlePaddle', 'paddle', '_paddle_model', True, True],]50 return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU'])51 52 53def try_export(inner_func):54 # YOLO export decorator, i..e @try_export55 inner_args = get_default_args(inner_func)56 57 def outer_func(*args, **kwargs):58 prefix = inner_args['prefix']59 try:60 with Profile() as dt:61 f, model = inner_func(*args, **kwargs)62 LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)')63 return f, model64 except Exception as e:65 LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}')66 return None, None67 68 return outer_func69 70 71@try_export72def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):73 # YOLO TorchScript model export74 LOGGER.info(f'\n{prefix} starting export with torch {torch.__version__}...')75 f = file.with_suffix('.torchscript')76 77 ts = torch.jit.trace(model, im, strict=False)78 d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names}79 extra_files = {'config.txt': json.dumps(d)} # torch._C.ExtraFilesMap()80 if optimize: # https://pytorch.org/tutorials/recipes/mobile_interpreter.html81 optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files)82 else:83 ts.save(str(f), _extra_files=extra_files)84 return f, None85 86 87@try_export88def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')):89 # YOLO ONNX export90 check_requirements('onnx')91 import onnx92 93 LOGGER.info(f'\n{prefix} starting export with onnx {onnx.__version__}...')94 f = file.with_suffix('.onnx')95 96 output_names = ['output0', 'output1'] if isinstance(model, SegmentationModel) else ['output0']97 if dynamic:98 dynamic = {'images': {0: 'batch', 2: 'height', 3: 'width'}} # shape(1,3,640,640)99 if isinstance(model, SegmentationModel):100 dynamic['output0'] = {0: 'batch', 1: 'anchors'} # shape(1,25200,85)101 dynamic['output1'] = {0: 'batch', 2: 'mask_height', 3: 'mask_width'} # shape(1,32,160,160)102 elif isinstance(model, DetectionModel):103 dynamic['output0'] = {0: 'batch', 1: 'anchors'} # shape(1,25200,85)104 105 torch.onnx.export(106 model.cpu() if dynamic else model, # --dynamic only compatible with cpu107 im.cpu() if dynamic else im,108 f,109 verbose=False,110 opset_version=opset,111 do_constant_folding=True,112 input_names=['images'],113 output_names=output_names,114 dynamic_axes=dynamic or None)115 116 # Checks117 model_onnx = onnx.load(f) # load onnx model118 onnx.checker.check_model(model_onnx) # check onnx model119 120 # Metadata121 d = {'stride': int(max(model.stride)), 'names': model.names}122 for k, v in d.items():123 meta = model_onnx.metadata_props.add()124 meta.key, meta.value = k, str(v)125 onnx.save(model_onnx, f)126 127 # Simplify128 if simplify:129 try:130 cuda = torch.cuda.is_available()131 check_requirements(('onnxruntime-gpu' if cuda else 'onnxruntime', 'onnx-simplifier>=0.4.1'))132 import onnxsim133 134 LOGGER.info(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...')135 model_onnx, check = onnxsim.simplify(model_onnx)136 assert check, 'assert check failed'137 onnx.save(model_onnx, f)138 except Exception as e:139 LOGGER.info(f'{prefix} simplifier failure: {e}')140 return f, model_onnx141 142 143@try_export144def export_onnx_end2end(model, im, file, simplify, topk_all, iou_thres, conf_thres, device, labels, prefix=colorstr('ONNX END2END:')):145 # YOLO ONNX export146 check_requirements('onnx')147 import onnx148 LOGGER.info(f'\n{prefix} starting export with onnx {onnx.__version__}...')149 f = os.path.splitext(file)[0] + "-end2end.onnx"150 batch_size = 'batch'151 152 dynamic_axes = {'images': {0 : 'batch', 2: 'height', 3:'width'}, } # variable length axes153 154 output_axes = {155 'num_dets': {0: 'batch'},156 'det_boxes': {0: 'batch'},157 'det_scores': {0: 'batch'},158 'det_classes': {0: 'batch'},159 }160 dynamic_axes.update(output_axes)161 model = End2End(model, topk_all, iou_thres, conf_thres, None ,device, labels)162 163 output_names = ['num_dets', 'det_boxes', 'det_scores', 'det_classes']164 shapes = [ batch_size, 1, batch_size, topk_all, 4,165 batch_size, topk_all, batch_size, topk_all]166 167 torch.onnx.export(model, 168 im, 169 f, 170 verbose=False, 171 export_params=True, # store the trained parameter weights inside the model file172 opset_version=12, 173 do_constant_folding=True, # whether to execute constant folding for optimization174 input_names=['images'],175 output_names=output_names,176 dynamic_axes=dynamic_axes)177 178 # Checks179 model_onnx = onnx.load(f) # load onnx model180 onnx.checker.check_model(model_onnx) # check onnx model181 for i in model_onnx.graph.output:182 for j in i.type.tensor_type.shape.dim:183 j.dim_param = str(shapes.pop(0))184 185 if simplify:186 try:187 import onnxsim188 189 print('\nStarting to simplify ONNX...')190 model_onnx, check = onnxsim.simplify(model_onnx)191 assert check, 'assert check failed'192 except Exception as e:193 print(f'Simplifier failure: {e}')194 195 # print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model196 onnx.save(model_onnx,f)197 print('ONNX export success, saved as %s' % f)198 return f, model_onnx199 200 201@try_export202def export_openvino(file, metadata, half, prefix=colorstr('OpenVINO:')):203 # YOLO OpenVINO export204 check_requirements('openvino-dev') # requires openvino-dev: https://pypi.org/project/openvino-dev/205 import openvino.inference_engine as ie206 207 LOGGER.info(f'\n{prefix} starting export with openvino {ie.__version__}...')208 f = str(file).replace('.pt', f'_openvino_model{os.sep}')209 210 #cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} --data_type {'FP16' if half else 'FP32'}"211 #cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} {"--compress_to_fp16" if half else ""}"212 half_arg = "--compress_to_fp16" if half else ""213 cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} {half_arg}"214 subprocess.run(cmd.split(), check=True, env=os.environ) # export215 yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata) # add metadata.yaml216 return f, None217 218 219@try_export220def export_paddle(model, im, file, metadata, prefix=colorstr('PaddlePaddle:')):221 # YOLO Paddle export222 check_requirements(('paddlepaddle', 'x2paddle'))223 import x2paddle224 from x2paddle.convert import pytorch2paddle225 226 LOGGER.info(f'\n{prefix} starting export with X2Paddle {x2paddle.__version__}...')227 f = str(file).replace('.pt', f'_paddle_model{os.sep}')228 229 pytorch2paddle(module=model, save_dir=f, jit_type='trace', input_examples=[im]) # export230 yaml_save(Path(f) / file.with_suffix('.yaml').name, metadata) # add metadata.yaml231 return f, None232 233 234@try_export235def export_coreml(model, im, file, int8, half, prefix=colorstr('CoreML:')):236 # YOLO CoreML export237 check_requirements('coremltools')238 import coremltools as ct239 240 LOGGER.info(f'\n{prefix} starting export with coremltools {ct.__version__}...')241 f = file.with_suffix('.mlmodel')242 243 ts = torch.jit.trace(model, im, strict=False) # TorchScript model244 ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=im.shape, scale=1 / 255, bias=[0, 0, 0])])245 bits, mode = (8, 'kmeans_lut') if int8 else (16, 'linear') if half else (32, None)246 if bits < 32:247 if MACOS: # quantization only supported on macOS248 with warnings.catch_warnings():249 warnings.filterwarnings("ignore", category=DeprecationWarning) # suppress numpy==1.20 float warning250 ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode)251 else:252 print(f'{prefix} quantization only supported on macOS, skipping...')253 ct_model.save(f)254 return f, ct_model255 256 257@try_export258def export_engine(model, im, file, half, dynamic, simplify, workspace=4, verbose=False, prefix=colorstr('TensorRT:')):259 # YOLO TensorRT export https://developer.nvidia.com/tensorrt260 assert im.device.type != 'cpu', 'export running on CPU but must be on GPU, i.e. `python export.py --device 0`'261 try:262 import tensorrt as trt263 except Exception:264 if platform.system() == 'Linux':265 check_requirements('nvidia-tensorrt', cmds='-U --index-url https://pypi.ngc.nvidia.com')266 import tensorrt as trt267 268 if trt.__version__[0] == '7': # TensorRT 7 handling https://github.com/ultralytics/yolov5/issues/6012269 grid = model.model[-1].anchor_grid270 model.model[-1].anchor_grid = [a[..., :1, :1, :] for a in grid]271 export_onnx(model, im, file, 12, dynamic, simplify) # opset 12272 model.model[-1].anchor_grid = grid273 else: # TensorRT >= 8274 check_version(trt.__version__, '8.0.0', hard=True) # require tensorrt>=8.0.0275 export_onnx(model, im, file, 12, dynamic, simplify) # opset 12276 onnx = file.with_suffix('.onnx')277 278 LOGGER.info(f'\n{prefix} starting export with TensorRT {trt.__version__}...')279 assert onnx.exists(), f'failed to export ONNX file: {onnx}'280 f = file.with_suffix('.engine') # TensorRT engine file281 logger = trt.Logger(trt.Logger.INFO)282 if verbose:283 logger.min_severity = trt.Logger.Severity.VERBOSE284 285 builder = trt.Builder(logger)286 config = builder.create_builder_config()287 config.max_workspace_size = workspace * 1 << 30288 # config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace << 30) # fix TRT 8.4 deprecation notice289 290 flag = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))291 network = builder.create_network(flag)292 parser = trt.OnnxParser(network, logger)293 if not parser.parse_from_file(str(onnx)):294 raise RuntimeError(f'failed to load ONNX file: {onnx}')295 296 inputs = [network.get_input(i) for i in range(network.num_inputs)]297 outputs = [network.get_output(i) for i in range(network.num_outputs)]298 for inp in inputs:299 LOGGER.info(f'{prefix} input "{inp.name}" with shape{inp.shape} {inp.dtype}')300 for out in outputs:301 LOGGER.info(f'{prefix} output "{out.name}" with shape{out.shape} {out.dtype}')302 303 if dynamic:304 if im.shape[0] <= 1:305 LOGGER.warning(f"{prefix} WARNING ⚠️ --dynamic model requires maximum --batch-size argument")306 profile = builder.create_optimization_profile()307 for inp in inputs:308 profile.set_shape(inp.name, (1, *im.shape[1:]), (max(1, im.shape[0] // 2), *im.shape[1:]), im.shape)309 config.add_optimization_profile(profile)310 311 LOGGER.info(f'{prefix} building FP{16 if builder.platform_has_fast_fp16 and half else 32} engine as {f}')312 if builder.platform_has_fast_fp16 and half:313 config.set_flag(trt.BuilderFlag.FP16)314 with builder.build_engine(network, config) as engine, open(f, 'wb') as t:315 t.write(engine.serialize())316 return f, None317 318 319@try_export320def export_saved_model(model,321 im,322 file,323 dynamic,324 tf_nms=False,325 agnostic_nms=False,326 topk_per_class=100,327 topk_all=100,328 iou_thres=0.45,329 conf_thres=0.25,330 keras=False,331 prefix=colorstr('TensorFlow SavedModel:')):332 # YOLO TensorFlow SavedModel export333 try:334 import tensorflow as tf335 except Exception:336 check_requirements(f"tensorflow{'' if torch.cuda.is_available() else '-macos' if MACOS else '-cpu'}")337 import tensorflow as tf338 from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2339 340 from models.tf import TFModel341 342 LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')343 f = str(file).replace('.pt', '_saved_model')344 batch_size, ch, *imgsz = list(im.shape) # BCHW345 346 tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)347 im = tf.zeros((batch_size, *imgsz, ch)) # BHWC order for TensorFlow348 _ = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)349 inputs = tf.keras.Input(shape=(*imgsz, ch), batch_size=None if dynamic else batch_size)350 outputs = tf_model.predict(inputs, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)351 keras_model = tf.keras.Model(inputs=inputs, outputs=outputs)352 keras_model.trainable = False353 keras_model.summary()354 if keras:355 keras_model.save(f, save_format='tf')356 else:357 spec = tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype)358 m = tf.function(lambda x: keras_model(x)) # full model359 m = m.get_concrete_function(spec)360 frozen_func = convert_variables_to_constants_v2(m)361 tfm = tf.Module()362 tfm.__call__ = tf.function(lambda x: frozen_func(x)[:4] if tf_nms else frozen_func(x), [spec])363 tfm.__call__(im)364 tf.saved_model.save(tfm,365 f,366 options=tf.saved_model.SaveOptions(experimental_custom_gradients=False) if check_version(367 tf.__version__, '2.6') else tf.saved_model.SaveOptions())368 return f, keras_model369 370 371@try_export372def export_pb(keras_model, file, prefix=colorstr('TensorFlow GraphDef:')):373 # YOLO TensorFlow GraphDef *.pb export https://github.com/leimao/Frozen_Graph_TensorFlow374 import tensorflow as tf375 from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2376 377 LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')378 f = file.with_suffix('.pb')379 380 m = tf.function(lambda x: keras_model(x)) # full model381 m = m.get_concrete_function(tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype))382 frozen_func = convert_variables_to_constants_v2(m)383 frozen_func.graph.as_graph_def()384 tf.io.write_graph(graph_or_graph_def=frozen_func.graph, logdir=str(f.parent), name=f.name, as_text=False)385 return f, None386 387 388@try_export389def export_tflite(keras_model, im, file, int8, data, nms, agnostic_nms, prefix=colorstr('TensorFlow Lite:')):390 # YOLOv5 TensorFlow Lite export391 import tensorflow as tf392 393 LOGGER.info(f'\n{prefix} starting export with tensorflow {tf.__version__}...')394 batch_size, ch, *imgsz = list(im.shape) # BCHW395 f = str(file).replace('.pt', '-fp16.tflite')396 397 converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)398 converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]399 converter.target_spec.supported_types = [tf.float16]400 converter.optimizations = [tf.lite.Optimize.DEFAULT]401 if int8:402 from models.tf import representative_dataset_gen403 dataset = LoadImages(check_dataset(check_yaml(data))['train'], img_size=imgsz, auto=False)404 converter.representative_dataset = lambda: representative_dataset_gen(dataset, ncalib=100)405 converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]406 converter.target_spec.supported_types = []407 converter.inference_input_type = tf.uint8 # or tf.int8408 converter.inference_output_type = tf.uint8 # or tf.int8409 converter.experimental_new_quantizer = True410 f = str(file).replace('.pt', '-int8.tflite')411 if nms or agnostic_nms:412 converter.target_spec.supported_ops.append(tf.lite.OpsSet.SELECT_TF_OPS)413 414 tflite_model = converter.convert()415 open(f, "wb").write(tflite_model)416 return f, None417 418 419@try_export420def export_edgetpu(file, prefix=colorstr('Edge TPU:')):421 # YOLO Edge TPU export https://coral.ai/docs/edgetpu/models-intro/422 cmd = 'edgetpu_compiler --version'423 help_url = 'https://coral.ai/docs/edgetpu/compiler/'424 assert platform.system() == 'Linux', f'export only supported on Linux. See {help_url}'425 if subprocess.run(f'{cmd} >/dev/null', shell=True).returncode != 0:426 LOGGER.info(f'\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}')427 sudo = subprocess.run('sudo --version >/dev/null', shell=True).returncode == 0 # sudo installed on system428 for c in (429 'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -',430 'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',431 'sudo apt-get update', 'sudo apt-get install edgetpu-compiler'):432 subprocess.run(c if sudo else c.replace('sudo ', ''), shell=True, check=True)433 ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]434 435 LOGGER.info(f'\n{prefix} starting export with Edge TPU compiler {ver}...')436 f = str(file).replace('.pt', '-int8_edgetpu.tflite') # Edge TPU model437 f_tfl = str(file).replace('.pt', '-int8.tflite') # TFLite model438 439 cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {file.parent} {f_tfl}"440 subprocess.run(cmd.split(), check=True)441 return f, None442 443 444@try_export445def export_tfjs(file, prefix=colorstr('TensorFlow.js:')):446 # YOLO TensorFlow.js export447 check_requirements('tensorflowjs')448 import tensorflowjs as tfjs449 450 LOGGER.info(f'\n{prefix} starting export with tensorflowjs {tfjs.__version__}...')451 f = str(file).replace('.pt', '_web_model') # js dir452 f_pb = file.with_suffix('.pb') # *.pb path453 f_json = f'{f}/model.json' # *.json path454 455 cmd = f'tensorflowjs_converter --input_format=tf_frozen_model ' \456 f'--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}'457 subprocess.run(cmd.split())458 459 json = Path(f_json).read_text()460 with open(f_json, 'w') as j: # sort JSON Identity_* in ascending order461 subst = re.sub(462 r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '463 r'"Identity.?.?": {"name": "Identity.?.?"}, '464 r'"Identity.?.?": {"name": "Identity.?.?"}, '465 r'"Identity.?.?": {"name": "Identity.?.?"}}}', r'{"outputs": {"Identity": {"name": "Identity"}, '466 r'"Identity_1": {"name": "Identity_1"}, '467 r'"Identity_2": {"name": "Identity_2"}, '468 r'"Identity_3": {"name": "Identity_3"}}}', json)469 j.write(subst)470 return f, None471 472 473def add_tflite_metadata(file, metadata, num_outputs):474 # Add metadata to *.tflite models per https://www.tensorflow.org/lite/models/convert/metadata475 with contextlib.suppress(ImportError):476 # check_requirements('tflite_support')477 from tflite_support import flatbuffers478 from tflite_support import metadata as _metadata479 from tflite_support import metadata_schema_py_generated as _metadata_fb480 481 tmp_file = Path('/tmp/meta.txt')482 with open(tmp_file, 'w') as meta_f:483 meta_f.write(str(metadata))484 485 model_meta = _metadata_fb.ModelMetadataT()486 label_file = _metadata_fb.AssociatedFileT()487 label_file.name = tmp_file.name488 model_meta.associatedFiles = [label_file]489 490 subgraph = _metadata_fb.SubGraphMetadataT()491 subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()]492 subgraph.outputTensorMetadata = [_metadata_fb.TensorMetadataT()] * num_outputs493 model_meta.subgraphMetadata = [subgraph]494 495 b = flatbuffers.Builder(0)496 b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)497 metadata_buf = b.Output()498 499 populator = _metadata.MetadataPopulator.with_model_file(file)500 populator.load_metadata_buffer(metadata_buf)501 populator.load_associated_files([str(tmp_file)])502 populator.populate()503 tmp_file.unlink()504 505 506@smart_inference_mode()507def run(508 data=ROOT / 'data/coco.yaml', # 'dataset.yaml path'509 weights=ROOT / 'yolo.pt', # weights path510 imgsz=(640, 640), # image (height, width)511 batch_size=1, # batch size512 device='cpu', # cuda device, i.e. 0 or 0,1,2,3 or cpu513 include=('torchscript', 'onnx'), # include formats514 half=False, # FP16 half-precision export515 inplace=False, # set YOLO Detect() inplace=True516 keras=False, # use Keras517 optimize=False, # TorchScript: optimize for mobile518 int8=False, # CoreML/TF INT8 quantization519 dynamic=False, # ONNX/TF/TensorRT: dynamic axes520 simplify=False, # ONNX: simplify model521 opset=12, # ONNX: opset version522 verbose=False, # TensorRT: verbose log523 workspace=4, # TensorRT: workspace size (GB)524 nms=False, # TF: add NMS to model525 agnostic_nms=False, # TF: add agnostic NMS to model526 topk_per_class=100, # TF.js NMS: topk per class to keep527 topk_all=100, # TF.js NMS: topk for all classes to keep528 iou_thres=0.45, # TF.js NMS: IoU threshold529 conf_thres=0.25, # TF.js NMS: confidence threshold530):531 t = time.time()532 include = [x.lower() for x in include] # to lowercase533 fmts = tuple(export_formats()['Argument'][1:]) # --include arguments534 flags = [x in include for x in fmts]535 assert sum(flags) == len(include), f'ERROR: Invalid --include {include}, valid --include arguments are {fmts}'536 jit, onnx, onnx_end2end, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle = flags # export booleans537 file = Path(url2file(weights) if str(weights).startswith(('http:/', 'https:/')) else weights) # PyTorch weights538 539 # Load PyTorch model540 device = select_device(device)541 if half:542 assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0'543 assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both'544 model = attempt_load(weights, device=device, inplace=True, fuse=True) # load FP32 model545 546 # Checks547 imgsz *= 2 if len(imgsz) == 1 else 1 # expand548 if optimize:549 assert device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu'550 551 # Input552 gs = int(max(model.stride)) # grid size (max stride)553 imgsz = [check_img_size(x, gs) for x in imgsz] # verify img_size are gs-multiples554 im = torch.zeros(batch_size, 3, *imgsz).to(device) # image size(1,3,320,192) BCHW iDetection555 556 # Update model557 model.eval()558 for k, m in model.named_modules():559 if isinstance(m, (Detect, DDetect, DualDetect, DualDDetect, IDualDDetect)):560 m.inplace = inplace561 m.dynamic = dynamic562 m.export = True563 564 for _ in range(2):565 y = model(im) # dry runs566 if half and not coreml:567 im, model = im.half(), model.half() # to FP16568 shape = tuple((y[0] if isinstance(y, (tuple, list)) else y).shape) # model output shape569 metadata = {'stride': int(max(model.stride)), 'names': model.names} # model metadata570 LOGGER.info(f"\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)")571 572 # Exports573 f = [''] * len(fmts) # exported filenames574 warnings.filterwarnings(action='ignore', category=torch.jit.TracerWarning) # suppress TracerWarning575 if jit: # TorchScript576 f[0], _ = export_torchscript(model, im, file, optimize)577 if engine: # TensorRT required before ONNX578 f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)579 if onnx or xml: # OpenVINO requires ONNX580 f[2], _ = export_onnx(model, im, file, opset, dynamic, simplify)581 if onnx_end2end:582 if isinstance(model, DetectionModel):583 labels = model.names584 f[2], _ = export_onnx_end2end(model, im, file, simplify, topk_all, iou_thres, conf_thres, device, len(labels))585 else:586 raise RuntimeError("The model is not a DetectionModel.")587 if xml: # OpenVINO588 f[3], _ = export_openvino(file, metadata, half)589 if coreml: # CoreML590 f[4], _ = export_coreml(model, im, file, int8, half)591 if any((saved_model, pb, tflite, edgetpu, tfjs)): # TensorFlow formats592 assert not tflite or not tfjs, 'TFLite and TF.js models must be exported separately, please pass only one type.'593 assert not isinstance(model, ClassificationModel), 'ClassificationModel export to TF formats not yet supported.'594 f[5], s_model = export_saved_model(model.cpu(),595 im,596 file,597 dynamic,598 tf_nms=nms or agnostic_nms or tfjs,599 agnostic_nms=agnostic_nms or tfjs,600 topk_per_class=topk_per_class,601 topk_all=topk_all,602 iou_thres=iou_thres,603 conf_thres=conf_thres,604 keras=keras)605 if pb or tfjs: # pb prerequisite to tfjs606 f[6], _ = export_pb(s_model, file)607 if tflite or edgetpu:608 f[7], _ = export_tflite(s_model, im, file, int8 or edgetpu, data=data, nms=nms, agnostic_nms=agnostic_nms)609 if edgetpu:610 f[8], _ = export_edgetpu(file)611 add_tflite_metadata(f[8] or f[7], metadata, num_outputs=len(s_model.outputs))612 if tfjs:613 f[9], _ = export_tfjs(file)614 if paddle: # PaddlePaddle615 f[10], _ = export_paddle(model, im, file, metadata)616 617 # Finish618 f = [str(x) for x in f if x] # filter out '' and None619 if any(f):620 cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel)) # type621 dir = Path('segment' if seg else 'classify' if cls else '')622 h = '--half' if half else '' # --half FP16 inference arg623 s = "# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference" if cls else \624 "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference" if seg else ''625 if onnx_end2end:626 LOGGER.info(f'\nExport complete ({time.time() - t:.1f}s)'627 f"\nResults saved to {colorstr('bold', file.parent.resolve())}"628 f"\nVisualize: https://netron.app")629 else:630 LOGGER.info(f'\nExport complete ({time.time() - t:.1f}s)'631 f"\nResults saved to {colorstr('bold', file.parent.resolve())}"632 f"\nDetect: python {dir / ('detect.py' if det else 'predict.py')} --weights {f[-1]} {h}"633 f"\nValidate: python {dir / 'val.py'} --weights {f[-1]} {h}"634 f"\nPyTorch Hub: model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}') {s}"635 f"\nVisualize: https://netron.app")636 return f # return list of exported files/dirs637 638 639def parse_opt():640 parser = argparse.ArgumentParser()641 parser.add_argument('--data', type=str, default=ROOT / 'data/coco.yaml', help='dataset.yaml path')642 parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolo.pt', help='model.pt path(s)')643 parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640, 640], help='image (h, w)')644 parser.add_argument('--batch-size', type=int, default=1, help='batch size')645 parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')646 parser.add_argument('--half', action='store_true', help='FP16 half-precision export')647 parser.add_argument('--inplace', action='store_true', help='set YOLO Detect() inplace=True')648 parser.add_argument('--keras', action='store_true', help='TF: use Keras')649 parser.add_argument('--optimize', action='store_true', help='TorchScript: optimize for mobile')650 parser.add_argument('--int8', action='store_true', help='CoreML/TF INT8 quantization')651 parser.add_argument('--dynamic', action='store_true', help='ONNX/TF/TensorRT: dynamic axes')652 parser.add_argument('--simplify', action='store_true', help='ONNX: simplify model')653 parser.add_argument('--opset', type=int, default=12, help='ONNX: opset version')654 parser.add_argument('--verbose', action='store_true', help='TensorRT: verbose log')655 parser.add_argument('--workspace', type=int, default=4, help='TensorRT: workspace size (GB)')656 parser.add_argument('--nms', action='store_true', help='TF: add NMS to model')657 parser.add_argument('--agnostic-nms', action='store_true', help='TF: add agnostic NMS to model')658 parser.add_argument('--topk-per-class', type=int, default=100, help='TF.js NMS: topk per class to keep')659 parser.add_argument('--topk-all', type=int, default=100, help='ONNX END2END/TF.js NMS: topk for all classes to keep')660 parser.add_argument('--iou-thres', type=float, default=0.45, help='ONNX END2END/TF.js NMS: IoU threshold')661 parser.add_argument('--conf-thres', type=float, default=0.25, help='ONNX END2END/TF.js NMS: confidence threshold')662 parser.add_argument(663 '--include',664 nargs='+',665 default=['torchscript'],666 help='torchscript, onnx, onnx_end2end, openvino, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle')667 opt = parser.parse_args()668 669 if 'onnx_end2end' in opt.include: 670 opt.simplify = True671 opt.dynamic = True672 opt.inplace = True673 opt.half = False674 675 print_args(vars(opt))676 return opt677 678 679def main(opt):680 for opt.weights in (opt.weights if isinstance(opt.weights, list) else [opt.weights]):681 run(**vars(opt))682 683 684if __name__ == "__main__":685 opt = parse_opt()686 main(opt)687 