# export_utils

Python API: `luxonis_train.core.utils.export_utils`

Helpers of the ONNX export and of the conversions that follow it.

[LuxonisModel.export](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md),
[LuxonisModel.archive](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md),
[LuxonisModel.convert](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md),
and
[LuxonisModel.quantize](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md)
call these helpers. The helpers do these steps:

 * simplify the ONNX graph and duplicate its shared initializers;
 * rename the graph outputs;
 * read the normalization of the config;
 * convert the model with `blobconverter` or the HubAI SDK.

[replace_weights](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/utils/export_utils.md)
also loads the weights for
[LuxonisModel.test](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md),
[LuxonisModel.infer](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md),
and
[LuxonisModel.annotate](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md).

## Functions

### blobconverter_export

```python
def blobconverter_export(cfg: ExportConfig, scale_values: list[float] | None, mean_values: list[float] | None, reverse_channels: bool, export_path: PathType, onnx_path: PathType) -> Path:
```

Convert an ONNX model to a `.blob` file with `blobconverter`.

The function calls `blobconverter.from_onnx` with `cfg.blobconverter.shaves` and `cfg.blobconverter.version`, and with the cache
off. It passes `scale_values`, `mean_values`, and `reverse_channels` to the model optimizer as `--scale_values=[...]`,
`--mean_values=[...]`, and `--reverse_input_channels`. It leaves out a value that is `None`, empty, or `False`. `blobconverter`
sends the model to its online service, so the conversion needs network access.

`cfg.quantization_mode` selects the data type. The mode `"FP16_STANDARD"` gives `FP16`, and `"FP32_STANDARD"` gives `FP32`. Any
other mode, such as the default `"INT8_STANDARD"`, gives `FP16` and logs a warning.

Parameters

 * `cfg` (`ExportConfig`): The `exporter` section of the config.
 * `scale_values` (`list[float] | None`): The scale of the input normalization, per channel, in `uint8` pixel units. The standard
   deviation from
   [get_preprocessing](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/utils/export_utils.md)
   has this format.
 * `mean_values` (`list[float] | None`): The mean of the input normalization, per channel, in `uint8` pixel units. The mean from
   [get_preprocessing](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/utils/export_utils.md)
   has this format.
 * `reverse_channels` (`bool`): When `True`, pass `--reverse_input_channels`, which swaps the order of the input channels.
 * `export_path` (`PathType`): The directory that receives the `.blob` file.
 * `onnx_path` (`PathType`): The ONNX file to convert.

Returns

 * `Path`: The path of the `.blob` file.

### get_preprocessing

```python
def get_preprocessing(cfg: PreprocessingConfig, log_label: str | None = None) -> tuple[list[float] | None, list[float] | None, Literal['RGB', 'BGR', 'GRAY']]:
```

Read the normalization values and the color space of a config.

The mean and the standard deviation come from `cfg.normalize.params`, multiplied by `255` and rounded to five decimals, so they
apply to `uint8` pixel values.
[LuxonisModel](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/core/core.md)
puts these values into the `modelconverter` config and the NN Archive, and passes them to `blobconverter`. `exporter.mean_values`
and `exporter.scale_values` replace them there when they are set.

A value is `None` in these cases:

 * `cfg.normalize.active` is `False`. Both values are then `None`.
 * `cfg.normalize.params` has no `"mean"` or `"std"` key for the value.
 * The value under the key is not a list of numbers.

In the last two cases, the function logs a warning when `log_label` is not `None`. The warning names the caller and the value that
stays unset.

> **Examples**
> ```pycon
>>> from luxonis_train.config.config import PreprocessingConfig
>>> get_preprocessing(PreprocessingConfig())
([123.675, 116.28, 103.53], [58.395, 57.12, 57.375], 'RGB')
```

```pycon
>>> get_preprocessing(PreprocessingConfig(normalize={"active": False}))
(None, None, 'RGB')
```

```pycon
>>> cfg = PreprocessingConfig(normalize={"params": {"mean": [0.5]}})
>>> get_preprocessing(cfg)
([127.5], None, 'RGB')
```

Parameters

 * `cfg` (`PreprocessingConfig`): The `trainer.preprocessing` section of the config.
 * `log_label` (`str | None`): The name of the caller in the warning, such as `"Model export"`. `None` disables the warning.

Returns

 * `tuple[list[float] | None, list[float] | None, Literal['RGB', 'BGR', 'GRAY']]`: The mean values, the standard deviation values, and `cfg.color_space`.

### hubai_export

```python
def hubai_export(cfg: HubAIExportConfig, quantization_mode: str, archive_path: PathType, export_path: PathType, model_name: str,
dataset_name: str | None = None) -> Path:
```

Convert an ONNX NN Archive for a device through the HubAI SDK.

The function uploads the archive to HubAI as a variant of the model named `model_name`. It reuses the first model with this name, and creates the model when none exists. When the lookup of the models fails, the function logs a warning and creates a new model. The variant is named `<model_name>:<dataset_name>`, or `model_name` when `dataset_name` is `None` or empty.

`cfg.platform` selects the conversion call of the SDK: `RVC3` for `"rvc3"`, `RVC4` for `"rvc4"`, and `RVC2` for `"rvc2"` or `None`. The call receives the keyword arguments `path`, `quantization_mode`, `name`, and `model_id`. The entries of `cfg.params` go to the call too, and replace an argument of the same name.

The SDK downloads the converted archive. The function moves it into `export_path` under its own file name. It then removes the download directory when that directory is empty and is not the working directory.

When `cfg.delete_remote_model` is set, the function cleans up HubAI at the end:

 * It deletes the model that it created, also when the conversion raises an error.
 * It deletes only the new variant when the model existed before. This happens only when the conversion call returns.

A failed deletion logs a warning and does not raise an error.

Parameters

 * `cfg` (`HubAIExportConfig`): The `exporter.hubai` section of the config.
 * `quantization_mode` (`str`): The precision to convert to, such as `"INT8_STANDARD"` or `"FP16_STANDARD"`.
 * `archive_path` (`PathType`): The ONNX NN Archive to convert.
 * `export_path` (`PathType`): The directory that receives the converted archive.
 * `model_name` (`str`): The name of the model on HubAI.
 * `dataset_name` (`str | None`): The name of the train dataset. It is the second part of the variant name.

Returns

 * `Path`: The path of the converted archive inside `export_path`.

Raises

 * `ValueError`: When the `HUBAI_API_KEY` environment variable is not set or empty.
 * `NotImplementedError`: When `cfg.platform` is `"hailo"`.

### make_initializers_unique

```python
def make_initializers_unique(onnx_path: PathType):
```

Give every node input its own copy of a shared ONNX initializer.

The function counts how many node inputs of the main graph read each initializer. It replaces an initializer read by two or more inputs with one copy per input, named `<name>_unique_<i>`. The `i`-th such input in node order, from `0`, reads the copy with index `i`. An initializer read once, or not at all, keeps its name. The function saves the model over `onnx_path` and checks it with `onnx.checker.check_model`. A failed check logs a warning. At the end, the function logs how many initializers it duplicated.

When the graph has no initializers, the function logs a warning and leaves the file unchanged.

Parameters

 * `onnx_path` (`PathType`): The ONNX file to modify.

### rename_onnx_outputs

```python
def rename_onnx_outputs(onnx_path: PathType, output_names: list[str]):
```

Rename the graph outputs of an ONNX model in place.

The function pairs the outputs of the graph with `output_names` in order. It renames each graph output, and the output of every node that produces it. It does not rename the inputs of the nodes that read such an output.

When the file `<onnx_path.name>.data` exists next to the model, the function deletes it after the load. It then saves the model over `onnx_path`, with the initializers in a new external data file of that name. The tensors of the node attributes stay in the model file. Otherwise the function saves the whole model over `onnx_path`. It then checks the saved file with `onnx.checker.check_model`. The check raises an error when the model is not valid.

Parameters

 * `onnx_path` (`PathType`): The ONNX file to modify.
 * `output_names` (`list[str]`): The new output names, one for each graph output, in graph order.

Raises

 * `ValueError`: When the length of `output_names` differs from the number of graph outputs.

### replace_weights

```python
def replace_weights(module: _WeightLoadable[_WeightsT_contra], weights: _WeightsT_contra | None = None) -> Generator[None, None,
None]:
```

Load `weights` into `module` inside a `with` block.

On entry, when `weights` is not `None`, the manager keeps a deep copy of `module.state_dict()`. It then loads `weights` with [LuxonisLightningModule.load_checkpoint](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/lightning/luxonis_lightning.md), which also puts the module in evaluation mode. It sets the private flag `_weights_explicitly_loaded` on the module. While that flag is set, [EMACallback](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/callbacks/ema.md) does not swap the module to the EMA weights and does not swap it back. On exit, also after an error in the block, the manager clears the flag and loads the kept state dict into the module. It does not restore the training mode that the module had before the block.

With `weights` of `None`, the block runs with the module unchanged.

Parameters

 * `module` (`_WeightLoadable[_WeightsT_contra]`): The module that receives the weights.
 * `weights` (`_WeightsT_contra | None`): A path to a checkpoint file, or a loaded checkpoint with a `state_dict` key. `None` leaves the module unchanged.

Returns

 * `Generator[None, None, None]`

Yields

 * The manager yields no value. The block runs with `weights` loaded.

### try_onnx_simplify

```python
def try_onnx_simplify(onnx_path: PathType):
```

Simplify an ONNX model in place with `onnxsim`, when available.

The function loads the model, runs `onnxsim.simplify`, and saves the result over `onnx_path`. It logs an error and leaves the file unchanged in these cases:

 * `onnxsim` is not installed. The function also logs a warning.
 * The check of `onnxsim` reports that the simplified model is not valid.

Parameters

 * `onnx_path` (`PathType`): The ONNX file to simplify.
