# 转换

要将自定义模型部署到OAK设备上，必须将其从原始框架（如PyTorch、TFLite等）转换为DepthAI兼容的MyriadX blob格式。在本指南中，我们将介绍如何使用BlobConverter工具完成转换，并演示如何进行本地转换。

转换过程如下图所示：

 1. 模型来源： 从使用ONNX、Caffe或TensorFlow格式等框架开发的模型开始。
 2. 模型优化器： 使用模型优化器将模型转换为OpenVINO的中间表示（IR），生成.xml（配置文件）和.bin（权重文件）。
 3. 模型编译器： 使用模型编译器将.xml和.bin文件编译成.blob文件。
 4. 部署： 将.blob文件部署到OAK设备中的MYRIAD-X处理器上进行推理。

## 模型来源准备

第一步，将模型从其原始框架转换为适合进一步转换的格式，可能包括ONNX或其他格式，具体取决于模型的来源。

### PyTorch 转 ONNX

你可以使用 [PyTorch ONNX API](https://pytorch.org/docs/stable/onnx.html) 来转换并导出模型：

```python
import torch
# 加载你的PyTorch模型
your_model = Model()
# 创建一个与模型输入形状匹配的虚拟输入张量
dummy_input = torch.randn(1, 3, 224, 224)
# 转换并保存为ONNX
torch.onnx.export(your_model, dummy_input, 'output.onnx')
```

### TFLite 转 ONNX

对于TensorFlow Lite（.tflite）格式的模型，推荐的转换工具是 tflite2onnx。 该工具可将TFLite模型转换为ONNX格式：

 * 首先，安装 tflite2onnx 包：

```bash
pip install tflite2onnx
```

 * 然后可以通过命令行进行转换：

```bash
tflite2onnx your_model.tflite output.onnx
```

或通过Python：

```python
import tflite2onnx
tflite2onnx.convert('your_model.tflite', 'output.onnx')
```

### 其他TensorFlow表示形式

对于非TFLite的TensorFlow模型（如SavedModel或冻结图），转换直接涉及OpenVINO的模型优化器。详细说明请参见
[OpenVINO文档](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_prepare_model_convert_model_Convert_Model_From_TensorFlow.html)。完成OpenVINO转换后，你可以使用
[BlobConverter](#Using%20BlobConverter) 或 [OpenVINO的编译工具](#Local%20Conversion) 来获取 .blob 文件。

## 获取Blob

有两种获取 .blob 文件的方法：第一种也是最简单的方法是使用BlobConverter，这也是推荐的方法。另一种是本地方法，即直接使用OpenVINO工具。

### 使用BlobConverter

BlobConverter 提供了一种直接获取 .blob 文件的方法。该工具可通过Web界面、API和命令行界面（CLI）使用。以下各节概述了使用这些工具将模型转换为 .blob 的步骤。

#### 通过BlobConverter Web界面进行转换

 * 访问 [BlobConverter网站](https://blobconverter.luxonis.com/)。
 * 选择你要使用的OpenVINO版本。我们将使用BlobConverter支持的最新版本，目前是 2022.1。 对于RAE和带有RVC3的其他设备，可以直接选择RVC3。选择版本后，
   指定模型来源。在我们的例子中是ONNX模型，也可以上传IR格式的模型。然后点击 继续。

 * 点击 选择文件 上传ONNX文件。

 * 此外，在进行模型转换之前，你可以通过点击 高级 自定义 [转换参数](#Advanced%20Settings)。

 * 最后，点击 转换 并等待过程完成。

或者，你可以使用BlobConverter API，这对于自动化工作流特别有用。你可以通过向BlobConverter服务发送带有必要模型和参数的HTTP请求来实现。更多信息请点击网站右上角的 使用API 按钮。

> 注意：BlobConverter 工具也可以自行托管。有关此过程的指导，请参阅我们的
> [BlobConverter 仓库](https://github.com/luxonis/blobconverter/tree/master)
> 中的说明。

#### 通过 BlobConverter CLI 进行转换

 * 首先，安装 [BlobConverter CLI](https://github.com/luxonis/blobconverter/tree/master/cli)：

```bash
python3 -m pip install blobconverter
```

 * 使用该软件包，您可以通过命令行或 Python 脚本直接转换模型：

```bash
python3 -m blobconverter --onnx-model /path/to/model.onnx --shaves 6
```

或

```python
import blobconverter

blob_path = blobconverter.from_onnx(
    model="/path/to/model.onnx",
    data_type="FP16",
    shaves=6,
)
```

### 本地转换

本地转换非常适合离线使用，允许您使用自己的系统获取 .blob 文件。在互联网访问受限的环境中，或当您需要将转换集成到工作流程中时，此方法尤为有用。接下来的步骤将指导您使用 OpenVINO 的 Model Optimizer 和 Compile Tool
等工具完成此过程。

#### 模型优化器

OpenVINO 的 Model Optimizer 将模型从其原始框架格式转换为 OpenVINO 的中间表示（IR）标准格式（.bin 和 .xml）。这种标准化模型格式可以部署在各种 Intel 设备上，包括
VPU。此外，您可以通过指定各种标志来自定义转换过程，我们将在 [后续章节](#Model%20Optimizer%20Flags) 中对此进行说明。

要执行转换，请确保已安装 OpenVINO-dev。请注意，此方法支持 OpenVINO 版本 2022.1，但不支持之后的版本：

```bash
pip install openvino-dev==2022.1
```

然后按如下方式运行命令：

```bash
mo --input_model path/to/model.onnx --data_type=FP16 --mean_values=[0,0,0] --scale_values=[255,255,255]
```

#### 编译工具

模型转换为 OpenVINO 的 IR 格式后，下一步是使用 [OpenVINO 的 Compile
Tool](https://docs.openvino.ai/2022.3/openvino_inference_engine_tools_compile_tool_README.html)。该工具用于将 IR 格式的模型编译为 .blob
文件，以便在设备上部署。

 * Compile Tool 是 OpenVINO 工具包的一部分。其位置取决于您的安装路径。通常位于 OpenVINO 安装的 .../tools/compile_tool 目录中：

```bash
cd .../tools/compile_tool
```

 * 使用以下命令格式将 IR 模型编译为 .blob 文件：

```bash
./compile_tool -m path_to_model/model_name.xml -d MYRIAD
```

> 值得一提的是，虽然我们的平台支持广泛的模型，但由于某些算子限制，一些自定义或独特的模型可能需要额外步骤才能完美运行。为了获得流畅的体验，请查看
> [OpenVINO 支持的算子列表](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_prepare_model_Supported_Frameworks_Layers.html)

## 高级设置

### 模型优化器标志

#### 数据类型

由于我们是为 VPU（支持 FP16）进行转换，因此需要使用参数 --data_type=FP16。对于 OpenVINO 2022.3 及更高版本，应改用参数
--compress_to_fp16。更多详细信息请参见[此处](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_FP16_Compression.html)。

#### 均值和缩放值

通过 --mean_values 和 --scale_values 实现对模型输入图像的归一化。默认情况下，ColorCamera/MonoCamera 的帧为 U8 数据类型，范围是 [0,255]。

然而，模型通常在归一化后的帧上训练，范围在 [-1,1] 或 [0,1] 内。为了确保推理结果准确，需要事先对帧进行归一化。

虽然可以创建自定义模型在推理前对帧进行归一化（[示例在此](https://github.com/luxonis/oak-examples/blob/master/gen2-custom-models/generate_model/pytorch_normalize.py)），但更高效的方法是在模型优化步骤中直接使用标志，将归一化嵌入模型本身。

以下是一些常见的归一化选项（假设初始输入范围为 [0,255]）：

 * 若要求输入值在 0 到 1 之间，使用 mean=0 和 scale=255，计算公式为 ([0,255] - 0) / 255 = [0,1]。
 * 若要求输入值在 -1 到 1 之间，使用 mean=127.5 和 scale=127.5，计算公式为 ([0,255] - 127.5) / 127.5 = [-1,1]。
 * 若要求输入值在 -0.5 到 0.5 之间，使用 mean=127.5 和 scale=255，计算公式为 ([0,255] - 127.5) / 255 = [-0.5,0.5]。

更多信息请参阅 [OpenVINO
文档](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_Additional_Optimization_Use_Cases.html#specifying-mean-and-scale-values)。

#### 模型布局

模型布局可以使用 --layout 参数定义。例如：

```bash
--layout NCHW
```

配置如下：

 * N - 批大小
 * C - 通道数
 * H - 高度
 * W - 宽度

如果图像布局与模型布局不匹配，DepthAI 将显示相应的错误消息： [NeuralNetwork(0)] [warning] Input image (416x416) does not match NN (3x416)

值得注意的是，ColorCamera 节点默认以交错 / HWC 布局输出 preview 帧，这是 OpenCV 的原生格式。 但您可以通过 API 将其切换为平面 / CHW 布局：

```python
import depthai as dai
pipeline = dai.Pipeline()
colorCam = pipeline.createColorCamera()
colorCam.setInterleaved(False) # False = 平面布局
```

更多详情请参见 [OpenVINO
文档](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_Additional_Optimization_Use_Cases.html#specifying-layout)。

#### 颜色顺序

神经网络模型通常使用 RGB 颜色顺序的图像进行训练。ColorCamera 节点默认输出 BGR 格式的帧。输入帧与训练模型之间的颜色顺序不匹配可能导致预测不准确。为此，可以使用 --reverse_input_channels 标志。

此外，还可以通过 API 将相机输出切换为 RGB，从而无需使用该标志：

```python
import depthai as dai
pipeline = dai.Pipeline()
colorCam = pipeline.createColorCamera()
colorCam.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB) # RGB 颜色顺序，默认 BGR
```

更多详情请参见 [OpenVINO
文档](https://docs.openvino.ai/2022.3/openvino_docs_MO_DG_Additional_Optimization_Use_Cases.html#reversing-input-channels)。

### 模型编译器标志

#### 输入层精度

使用 -ip U8 将在模型的所有输入层上添加一个 U8->FP16 的转换层，这通常是期望的配置。然而，在某些特定场景下，例如处理非帧数据时，直接使用 FP16 精度是必要的。这种情况下，您可以选择 -ip FP16，如
[余弦距离模型示例](https://github.com/luxonis/oak-examples/blob/master/gen2-custom-models/generate_model/pytorch_cos_dist.py#L56-L65) 所示。

#### SHAVE 核心

在编译过程中增加 SHAVE 核心数量可以提升模型速度，但 SHAVE 核心数与性能之间并非线性关系。固件会给出一个建议最优 SHAVE 核心数的警告，通常为可用核心数的一半。

## 导出示例

本指南将引导您将广泛用于图像分类的深度神经网络 ResNet18 导出为 .blob 文件，以便在 OAK 设备上部署。我们将使用 torchvision 获取预训练版本的模型。

> 对于 YOLO 模型的转换，请考虑使用我们为此任务设计的
> [专用工具](https://tools.luxonis.com/)
> 。有关 YOLO 转换过程的详细指导，请访问
> [此文档页面](https://docs.luxonis.com/software/ai-inference/integrations/yolo.md)
> 。

### 将模型导出为 ONNX

首先，我们将 ResNet18 模型从 PyTorch 导出为 ONNX 格式。

```python
import torch
import torchvision.models as models

# 从 torchvision 加载预训练的 ResNet18 模型
resnet18 = models.resnet18(pretrained=True)

# 将模型设置为评估模式
resnet18.eval()

# 创建一个与模型输入形状匹配的虚拟输入张量
dummy_input = torch.randn(1, 3, 224, 224)

# 将模型导出为 ONNX 文件
torch.onnx.export(
    resnet18,
    dummy_input,
    'resnet18.onnx',
    export_params=True,
    opset_version=11,
    input_names=['input'],
    output_names=['output']
)
```

参数说明：

 * export_params：此标志确保训练好的参数随模型结构一起导出。
 * opset_version：指定要使用的 ONNX 版本。我们通常使用版本 11 以确保与 ResNet18 要求兼容，但更高版本也可用。
 * input_names 和 output_names：我们使用这些标志为模型的输入和输出节点命名，以增加清晰度。在本示例中，输入节点命名为 "input"，输出节点命名为 "output"。
 * 导出后，您将获得一个名为 "resnet18.onnx" 的文件，如第三个参数所定义。

### 使用 BlobConverter 将 ONNX 转换为 .blob

无需手动将 ONNX 文件转换为 OpenVINO IR 再编译，我们将使用 BlobConverter 一次性处理这两个步骤。

 * 访问 [BlobConverter 网站](https://blobconverter.luxonis.com/)。
 * 选择合适的 OpenVINO 版本，本例中为 2022.1。
 * 上传 .onnx 文件，并在“高级”设置中输入必要的 Model Optimizer 参数。
 * --data_type：设置为 'FP16'，以便与 VPU 处理器兼容。
 * --mean_values：设置为 [123.675, 116.28, 103.53]。这些值对应于 ImageNet 数据集（ResNet18 训练所用）中所有图像的红色、绿色和蓝色通道的平均值。
 * --scale_values：设置为 [58.395, 57.12, 57.375]，即每个通道的标准差。此缩放确保输入图像的像素值范围与训练数据中的范围匹配，这对模型正确运行至关重要。
 * --reverse_input_channels：使用此标志将 BGR 转换为 RGB，因为 ColorCamera 节点输出 BGR 格式的帧，而模型需要 RGB 图像。
 * 最终，标志应如下所示：

```bash
--data_type=FP16 --mean_values=[123.675,116.28,103.53] --scale_values=[58.395,57.12,57.375] --reverse_input_channels
```

 * 点击 Convert 开始转换，转换完成后下载 .blob 文件。

按照上述说明操作后，您将获得一个 resnet18.blob 文件，该文件已准备好用于 OAK 设备的推理。转换后的模型将期望输入为 BGR 格式、像素值范围 0 到 255 的图像。然后这些图像将被缩放到 0 到 1 的范围，并使用我们设置的标志进行归一化。
