|
|
|
|
| import os
|
| import sys
|
| import logging
|
| import platform
|
| import ctypes
|
| from pathlib import Path
|
|
|
| try:
|
| import tensorrt as trt
|
| except ModuleNotFoundError:
|
| pass
|
|
|
| logging.basicConfig(level=logging.INFO)
|
| logging.getLogger("EngineBuilder").setLevel(logging.INFO)
|
| log = logging.getLogger("EngineBuilder")
|
|
|
| if 'trt' in globals():
|
|
|
| TRT_LOGGER = trt.Logger(trt.Logger.INFO)
|
| else:
|
| TRT_LOGGER = {}
|
|
|
|
|
|
|
| class EngineBuilder:
|
| """
|
| Parses an ONNX graph and builds a TensorRT engine from it.
|
| """
|
|
|
| def __init__(self, verbose=False, custom_plugin_path=None, builder_optimization_level=3):
|
| """
|
| :param verbose: If enabled, a higher verbosity level will be set on the TensorRT logger.
|
| :param custom_plugin_path: Path to the custom plugin library (DLL or SO).
|
| """
|
| if verbose:
|
| TRT_LOGGER.min_severity = trt.Logger.Severity.VERBOSE
|
|
|
|
|
| trt.init_libnvinfer_plugins(TRT_LOGGER, namespace="")
|
|
|
|
|
| self.builder = trt.Builder(TRT_LOGGER)
|
| self.config = self.builder.create_builder_config()
|
|
|
| self.config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 3 * (2 ** 30))
|
|
|
|
|
| self.config.builder_optimization_level = builder_optimization_level
|
|
|
|
|
| profile = self.builder.create_optimization_profile()
|
| self.config.add_optimization_profile(profile)
|
|
|
| self.batch_size = None
|
| self.network = None
|
| self.parser = None
|
|
|
|
|
| if custom_plugin_path is not None:
|
| if platform.system().lower() == 'linux':
|
| ctypes.CDLL(custom_plugin_path, mode=ctypes.RTLD_GLOBAL)
|
| else:
|
| ctypes.CDLL(custom_plugin_path, mode=ctypes.RTLD_GLOBAL, winmode=0)
|
|
|
| def create_network(self, onnx_path):
|
| """
|
| Parse the ONNX graph and create the corresponding TensorRT network definition.
|
| :param onnx_path: The path to the ONNX graph to load.
|
| """
|
| network_flags = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
|
|
|
| self.network = self.builder.create_network(network_flags)
|
| self.parser = trt.OnnxParser(self.network, TRT_LOGGER)
|
|
|
| onnx_path = os.path.realpath(onnx_path)
|
| with open(onnx_path, "rb") as f:
|
| if not self.parser.parse(f.read()):
|
| log.error("Failed to load ONNX file: %s", onnx_path)
|
| for error in range(self.parser.num_errors):
|
| log.error(self.parser.get_error(error))
|
| sys.exit(1)
|
|
|
| inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)]
|
| outputs = [self.network.get_output(i) for i in range(self.network.num_outputs)]
|
|
|
| log.info("Network Description")
|
| for net_input in inputs:
|
| self.batch_size = net_input.shape[0]
|
| log.info("Input '%s' with shape %s and dtype %s", net_input.name, net_input.shape, net_input.dtype)
|
| for net_output in outputs:
|
| log.info("Output %s' with shape %s and dtype %s", net_output.name, net_output.shape, net_output.dtype)
|
|
|
| def create_engine(self, engine_path, precision):
|
| """
|
| Build the TensorRT engine and serialize it to disk.
|
| :param engine_path: The path where to serialize the engine to.
|
| :param precision: The datatype to use for the engine, either 'fp32', 'fp16' or 'int8'.
|
| """
|
| engine_path = os.path.realpath(engine_path)
|
| engine_dir = os.path.dirname(engine_path)
|
| os.makedirs(engine_dir, exist_ok=True)
|
| log.info("Building %s Engine in %s", precision, engine_path)
|
|
|
|
|
| self.config.set_flag(trt.BuilderFlag.PREFER_PRECISION_CONSTRAINTS)
|
|
|
| if precision == "fp16":
|
| if not self.builder.platform_has_fast_fp16:
|
| log.warning("FP16 is not supported natively on this platform/device")
|
| else:
|
| self.config.set_flag(trt.BuilderFlag.FP16)
|
|
|
|
|
| serialized_engine = self.builder.build_serialized_network(self.network, self.config)
|
|
|
|
|
| if serialized_engine is None:
|
| raise RuntimeError("Errore nella costruzione del motore TensorRT!")
|
|
|
|
|
| with open(engine_path, "wb") as f:
|
| log.info("Serializing engine to file: %s", engine_path)
|
| f.write(serialized_engine)
|
|
|
| def change_extension(file_path, new_extension, version=None):
|
| """
|
| Change the extension of the file path and optionally prepend a version.
|
| """
|
|
|
| new_extension = new_extension.lstrip('.')
|
|
|
|
|
| if version:
|
| new_file_path = Path(file_path).with_suffix(f'.{version}.{new_extension}')
|
| else:
|
| new_file_path = Path(file_path).with_suffix(f'.{new_extension}')
|
|
|
| return str(new_file_path)
|
|
|
| def onnx_to_trt(onnx_model_path, trt_model_path=None, precision="fp16", custom_plugin_path=None, verbose=False):
|
|
|
|
|
| if trt_model_path is None:
|
| trt_version = trt.__version__
|
| trt_model_path = change_extension(onnx_model_path, "trt", version=trt_version)
|
| builder = EngineBuilder(verbose=verbose, custom_plugin_path=custom_plugin_path)
|
|
|
| builder.create_network(onnx_model_path)
|
| builder.create_engine(trt_model_path, precision)
|
|
|