---
title: erfnet
canonical_url: "https://www.modelscope.cn/models/AIBSECO/erfnet"
md_url: "https://www.modelscope.cn/models/AIBSECO/erfnet.md"
repository: AIBSECO/erfnet
last_updated: 2025-11-19
pipeline_tag: image-segmentation
tasks:
  - image-segmentation
library_name:
  - pytorch
frameworks:
  - Pytorch
downloads: 2
stars: 0
---

# erfnet

> erfnet - AIBSECO 在 ModelScope 开源的模型。1. 模型背景 ErfNet（Efficient Residual Factorized Network） 是一款专为高效语义分割任务设计的神经网络结构，由 西班牙巴利亚多利德大学（University of Valladolid） 的研究团队于 2017 年在论文《ErfNet: Efficient Residual Factorized ConvNet for Real-time Semantic…

- **Repository**: AIBSECO/erfnet
- **Tasks**: image-segmentation
- **Downloads**: 2
- **Stars**: 0
- **Last updated**: 2025-11-19

Source: https://www.modelscope.cn/models/AIBSECO/erfnet

---

# ErfNet图像分割模型

## 2. 方案简介

### 2.1 算法流程介绍

#### 1. 模型背景
**ErfNet（Efficient Residual Factorized Network）** 是一款专为**高效语义分割**任务设计的神经网络结构，由 **西班牙巴利亚多利德大学（University of Valladolid）** 的研究团队于 **2017 年**在论文《**ErfNet: Efficient Residual Factorized ConvNet for Real-time Semantic Segmentation**》中首次提出，并发表在 **arXiv** 上。该模型主要面向**自动驾驶场景中的城市街景语义分割任务**，强调在保持较高准确率的同时实现**低延迟与低计算成本**。

ErfNet 的核心创新点体现在其提出的两项关键设计：

- **残差结构（Residual Connections）**：提高深层网络的可训练性，缓解梯度消失；
- **卷积因式分解（Factorized Convolutions）**：将常规的 3×3 卷积分解为连续的 1×3 和 3×1 卷积，大幅减少参数与计算量。

此外，ErfNet 引入了高效的 **非对称残差模块（Non-bottleneck-1D block）**，结合上述设计在语义表达能力与推理速度之间取得了良好平衡。

其结构包含：

- **初始下采样层（Downsampling Blocks）**：快速压缩空间分辨率；
- **编码器（Encoder）**：堆叠多个非对称残差模块提取深层语义信息；
- **解码器（Decoder）**：通过反卷积逐步恢复空间分辨率；
- **跳跃连接（Optional Skip Connections）**：增强低层细节。

该网络具备以下特点：

- 所有卷积层均使用残差连接以确保信息流动顺畅；
- 不采用传统的大型预训练主干网络（如 ResNet），而是从零设计轻量结构，**适用于实时推理和嵌入式部署**；
- 不使用显式注意力机制，而是依靠结构本身完成特征融合与压缩。

<img src="./fig/flops_latency.png" style="zoom:100%;" />

#### 2. 算法流程图
```mermaid
graph TD
    A[输入图像] --> B[前处理]
    B --> C[ErfNet模块]
    C --> D[后处理]
    
```



#### 3. 前处理（Host端CPU处理）
**前处理流程**：

```mermaid
graph LR
    A[输入图像路径] --> B[图像加载]
    B --> C[尺寸变换 + Padding]
    C --> D[张量转换]
    D --> E[DMA准备]

    subgraph 前处理流程
        B -->|cv::IMREAD_COLOR| B1["示例：/data/street.png"]
        C --> C1[Resize长边1024px] --> C2["示例：1024x2048→512x1024"]
        C2 --> C3[rPad填充] --> C4["示例：补高后→1024x1024"]
        D --> D1["数据类型转换 uint8→float32"]
        D1 --> D2["归一化 /255.0"]
        D2 --> D3["通道转换 BGR→RGB"]
        D3 --> D4["添加Batch维度 [1,3,1024,1024]"]
        E --> E1["配置DMA参数"] --> E2["Host→NPU数据传输"]
    end

    style B fill:#F8CECC,stroke:#B85450
    style C fill:#D5E8D4,stroke:#82B366
    style D fill:#DAE8FC,stroke:#6C8EBF
    style E fill:#FFF2CC,stroke:#D6B656

```

**关键操作**：

```cpp
PicPre img(img_path, cv::IMREAD_COLOR); // 图像加载
img.Resize({ netinfo.i_cubic[0].h, netinfo.i_cubic[0].w }, PicPre::LONG_SIDE).rPad(); // 调整尺寸 + Padding填充
Tensor img_tensor = CvMat2Tensor(img.dst_img, network); // OpenCV Mat转Tensor
dmaInit(run_sim, netinfo.ImageMake_on, img_tensor, device); // DMA传输初始化
```

**具体处理流程**：

1. **图像加载**：通过 OpenCV 读取原始图像（`cv::IMREAD_COLOR` 保持 RGB 格式）
   - 示例：输入 `img_path` 为 `"/data/street.png"`，图像尺寸为 **1024×2048（高×宽）**
2. **尺寸变换与补边**：
   - `Resize`：以 **长边为基准**（此处为宽 = 2048），将图像缩放，使其长边匹配目标尺寸（如输入为 512×1024，则长边为1024）
     - 缩放比例 = 1024 / 2048 = 0.5
     - 缩放后图像尺寸为 **512×1024**
   - `rPad`：在 **短边方向（此处为高 = 512）** 进行对称补零，补成最终输入尺寸（如目标为 512×1024，实际已满足则无需额外 padding）
   - 若目标为正方形输入（如 1024×1024），则：
     - 当前图像已为 512×1024，需要在高方向补齐至1024，高度补 512 像素（上下各补 256）
     - 最终尺寸为 **1024×1024**
3. **张量转换**：
   - `CvMat2Tensor` 完成以下处理：
     - 数据类型转换：`uint8 → float32`
     - 像素归一化（例如除以 255.0）
     - 通道顺序调整：如 BGR → RGB（若模型要求）
     - 添加 Batch 维度：转换后张量尺寸为 `[1, 3, H, W]`，如 `[1, 3, 1024, 1024]`
4. **DMA准备**：
   - `dmaInit` 函数配置 DMA 传输，将图像张量从主机内存拷贝至 NPU 设备端，用于硬件加速推理。

#### 4. 后处理（Host端CPU处理）

**后处理机制**：

```mermaid
graph LR
    A[NPU输出张量] --> B[数据回传]
    B --> C[结果解析]
    C --> D[尺寸调整]
    D --> E[类别映射]
    E --> F[可视化处理]

    subgraph 后处理流程
        B --> B1["to(HostDevice::MemRegion())"]
        B1 --> B2["示例：shape=[1,512,512,19]"]
        C --> C1["(float*/int8_t*)host_tensor.data().cptr()"]
        D --> D1["resize(原图尺寸)"] --> D2["示例：512x512→1024x2048"]
        E --> E1["map_plot颜色映射"] --> E2["使用Cityscapes调色板"]
        F --> F1["cv::imwrite保存"] --> F2["示例：road→灰色，car→蓝色"]
    end

    style B fill:#F8CECC,stroke:#B85450
    style C fill:#D5E8D4,stroke:#82B366
    style D fill:#DAE8FC,stroke:#6C8EBF
    style E fill:#FFF2CC,stroke:#D6B656
    style F fill:#E1D5E7,stroke:#9673A6
```

**关键操作**：

```cpp
// 数据回传
auto host_tensor = result_tensor[0].to(HostDevice::MemRegion());

// 结果解析（多通道float/单通道int8）
if (output_channel == 1) {
    auto tensor_data = (int8_t*)host_tensor.data().cptr();
    net_result_mat = cv::Mat(h,w,CV_8SC1,tensor_data); 
} else {
    auto tensor_data = (float*)host_tensor.data().cptr();
    net_result_mat = cv::Mat(h,w,CV_32FC19,tensor_data);
}

// 尺寸调整
cv::resize(net_result_mat, resultMat, cv::Size(orig_w,orig_h), cv::INTER_LINEAR);

// 类别映射与可视化
map_plot(resultMat, img_id_mat, img_color_mat, img.dst_img);
```

**具体处理流程**：

1. **数据回传**

   - `to(HostDevice::MemRegion())` 将NPU输出的分割张量复制到Host内存
   - 示例输出shape为`[1,512,512,19]`（Cityscapes的19类分割任务）

2. **结果解析**

   - 根据输出类型（float多通道/int8单通道）转换数据指针
   - 构建OpenCV矩阵：多通道用`CV_32FC19`，单通道用`CV_8SC1`

3. **尺寸调整**

   - 将网络输出resize到原始图像尺寸（如1024x2048）
   - 使用双线性插值保持边缘平滑

4. **类别映射**

   - `map_plot`函数核心操作：

     ```
     解释// 像素级类别索引→调色板颜色
     for(auto i : img_id){
         if(i >= 19) color = [0,0,0]; // 未知类别用黑色
         else color = PALETTE_CITYSCAPES[i]; // 按预设调色板着色
     }
     ```

5. **结果保存**

   - Windows/Linux路径差异化处理
   - 保存两种结果：
     - `img_id_mat`：单通道类别ID图（可用于量化分析）
     - `img_color_mat`：三通道伪彩色图（直观可视化）

### 2.2 硬件资源分配

#### 1. 硬件映射策略
```mermaid
graph TD
    subgraph CPU前处理
        B1[图像加载: OpenCV读取] --> B2[尺寸变换: Resize]
        B2 --> B3[补边处理: rPad]
        B3 --> B4[张量转换: uint8转float32等]
    end
    
    subgraph ImageMake硬件加速
        C1[DMA传输初始化: 主机到NPU]
    end
    
    subgraph NPU推理
        D1[NPU推理计算]
    end
    
    subgraph CPU后处理
        E1[数据回传: NPU到主机] --> E2[结果解析: float/int8转OpenCV矩阵]
        E2 --> E3[尺寸调整: 调整到原始尺寸]
        E3 --> E4[类别映射: 像素索引到颜色]
    end
 
    A[输入图像] --> B[CPU前处理]
    B --> C[ImageMake硬件加速]
    C --> D[NPU推理]
    D --> E[detpost硬件加速]
    E --> F[CPU后处理]
```

####  2. **硬件资源分配方案总表**

| ** 硬件分配**        | **代码/模块对应位置**     | **关键变更说明** |                                      |                              |
| :------------------- | :------------------------ | :--------------- | ------------------------------------ | ---------------------------- |
| **1. CPU前处理**     |                           |                  |                                      |                              |
| - 图像加载           | OpenCV读取图像文件        | CPU              | `cv::imread(img_path)`               | 新增补边处理环节             |
| - 尺寸变换           | 动态Resize（保持长宽比）  | CPU              | `cv::resize(..., INTER_LINEAR)`      | 支持非方形输入               |
| - 补边处理           | 四周Padding填充（常量值） | CPU              | `cv::copyMakeBorder()`               | 新增预处理步骤               |
| - 张量转换           | uint8转float32/归一化     | CPU              | `convertTo(CV_32F, 1/255.0)`         | 归一化合并到转换             |
| **2. ImageMake加速** |                           |                  |                                      |                              |
| - DMA传输初始化      | 主机内存→NPU内存          | FPGA             | `dmaInit(host_ptr, npu_ptr, size)`   | 硬件自动完成数据对齐         |
| **3. NPU推理**       |                           |                  |                                      |                              |
| - 双分支卷积计算     | 细节分支+语义分支并行     | NPU              | `session.forward()`                  | 新增双分支支持               |
| - BGALayer特征融合   | 动态加权融合双分支特征    | NPU              | 编译器自动优化                       | 新增硬件融合模块             |
| **4. CPU后处理**     |                           |                  |                                      |                              |
| - 数据回传           | NPU内存→主机内存          | FPGA/CPU         | `tensor.to(HostDevice::MemRegion())` | 支持int8/float多格式         |
| - 结果解析           | 原始数据转OpenCV矩阵      | CPU              | `cv::Mat(..., data_ptr)`             | 自动识别单通道/多通道        |
| - 尺寸调整           | 还原至原始图像尺寸        | CPU              | `cv::resize(..., orig_h, orig_w)`    | 双线性插值保持边缘           |
| - 类别映射           | 像素索引→Cityscapes调色板 | CPU              | `map_plot()`中的PALETTE_CITYSCAPES   | 新增未知类别处理（填充黑色） |
| - 掩膜渲染           | 伪彩色结果叠加原始图      | CPU              | `cv::addWeighted()`                  | 可调节透明度                 |

#### 3. **硬件-软件交互示意图**

```mermaid
sequenceDiagram
    participant Host as Host CPU
    participant NPU as NPU/PL
    participant DDR as 设备内存

    Host->>Host: 图像加载/Resize/Crop
    Host->>DDR: DMA传输预处理数据
    NPU->>NPU: 执行模型推理
    DDR->>Host: DMA回传输出张量
    Host->>Host: Softmax/TopK处理
    Host->>Host: 结果可视化与保存
```

## 3. 模型导出说明

#### ErfNet 主干

1）**基础卷积算子**：

- 常规卷积：`nn.Conv2d`直接实现
- 转置卷积（上采样）：`nn.ConvTranspose2d`实现
- 批归一化：`nn.BatchNorm2d`直接实现
- 池化操作：`nn.MaxPool2d`实现下采样
- 正则化：`nn.Dropout2d`实现通道随机失活

2）**特殊卷积结构**：

- 非对称卷积分解：
  - 3x1卷积+1x3卷积：`nn.Conv2d`组合实现
  - 带膨胀率的非对称卷积：通过`dilation`参数实现
- 残差连接：通过张量相加实现

3）**下采样模块**：

- DownsamplerBlock：
  - 并行路径：
    - 卷积路径：`nn.Conv2d`+`nn.BatchNorm2d`
    - 池化路径：`nn.MaxPool2d`
  - 特征合并：通道维度拼接
  - 批归一化：`nn.BatchNorm2d`统一处理合并特征

4）**上采样模块**：

- UpsamplerBlock：
  - 转置卷积：`nn.ConvTranspose2d`实现2倍上采样
  - 批归一化：`nn.BatchNorm2d`处理上采样结果

5）**核心残差模块**：

- non_bottleneck_1d：
  - 双分支非对称卷积：
    - 第一组：3x1→1x3卷积+BN
    - 第二组：带膨胀率的3x1→1x3卷积+BN
  - 残差连接：输入输出直接相加
  - 正则化：`nn.Dropout2d`

6）**编解码结构**：

- 编码器(Encoder)：
  - 初始下采样块→多级下采样→系列non_bottleneck_1d
  - 输出卷积：1x1卷积降维
- 解码器(Decoder)：
  - 上采样块→non_bottleneck_1d→上采样→输出转置卷积

7）**特殊参数配置**：

- 膨胀率设置：采用[1,2,4,8,16]的指数增长模式
- 批归一化参数：固定eps=0.001，momentum=0.1
- Dropout配置：编码器p=0.1，解码器p=0

8）**维度变化处理**：

- 下采样时通道扩展：通过concat合并卷积和池化路径
- 上采样时通道压缩：通过转置卷积同步调整空间和通道维度

5）**ErfNet 与 Icraft 硬件支持算子对应表**

| **ERFNet 功能组件**    | **PyTorch 实现方式**                 | **iCraft 对应算子组合**            |
| :--------------------- | :----------------------------------- | :--------------------------------- |
| **基础卷积算子**       |                                      |                                    |
| 常规卷积               | `nn.Conv2d`                          | `Conv2d`                           |
| 转置卷积（上采样）     | `nn.ConvTranspose2d`                 | `ConvTranspose2d`                  |
| 批归一化               | `nn.BatchNorm2d`                     | `BatchNorm2d`                      |
| 最大池化               | `nn.MaxPool2d`                       | `MaxPool2d`                        |
| Dropout正则化          | `nn.Dropout2d`                       | **不支持二维`Dropout`**            |
| **下采样模块**         |                                      |                                    |
| DownsamplerBlock双路径 | `Conv2d+MaxPool2d`并行 → 通道拼接    | `Conv2d + MaxPool2d + Concat`      |
| 合并后批归一化         | `nn.BatchNorm2d`                     | `BatchNorm2d`                      |
| **上采样模块**         |                                      |                                    |
| UpsamplerBlock         | `ConvTranspose2d` + BN               | `ConvTranspose2d + BatchNorm2d`    |
| **核心残差模块**       |                                      |                                    |
| non_bottleneck_1d      | 3x1→1x3非对称卷积组合                | `Conv2d(k=3x1) + Conv2d(k=1x3)` ×2 |
| 带膨胀率的卷积         | `dilation=[1,2,4,8,16]`              | `Conv2d(dilation=*)`               |
| 残差连接               | 张量相加                             | `Add`                              |
| **编解码结构**         |                                      |                                    |
| 编码器初始块           | `DownsamplerBlock`                   | 见下采样模块                       |
| 编码器层级结构         | 交替使用下采样和non_bottleneck_1d    | 组合对应算子                       |
| 解码器上采样块         | `UpsamplerBlock` + non_bottleneck_1d | 见上采样模块和核心残差模块         |
| **特殊操作**           |                                      |                                    |
| 输出卷积               | 1x1卷积降维                          | `Conv2d(k=1x1)`                    |
| 多尺度膨胀卷积         | 膨胀率指数增长                       | `Conv2d(dilation=2^n)`             |
| 特征维度处理           | 通道拼接/维度调整                    | `Concat`/`Reshape`                 |
| **激活函数**           |                                      |                                    |
| ReLU（隐含）           | 默认激活                             | `ReLU`                             |

####  导出过程和1_scripts中的脚本运行说明

**直接加载模型，只导出ErfNet 主干，前后处理不保存在在导出模型中。**

~~~
├── 0_infer.py       >框架下推断脚本
├── 1_save.py        >trace模型保存onnx/pt脚本
├── 2_save_infer.py  >onnx/pt推断脚本
~~~

  - **0_infer.py** 
    - 可以推理一张图（或其他格式数据）并得到最终结果，目的是为了快速跑通推理代码，为后续剥离/移植前后处理做准备。为了实现快速不能用整个数据集作为输入。
    - 用法： python 0_infer.py
    
  - **1_save.py**
    - 保存模型，导出的模型默认存放至`2_compile/fmodel`路径下，如`../2_compile/fmodel/erfnet_512x1048.pt`
    
    - 用法： python 1_save.py
    
    - **导出过程修改点**
    
      - （1）因为icraft不支持`nn.Dropout2d`，原本在`non_bottleneck_1d.forward`中会使用`nn.Dropout2d`，直接去掉这个算子，生成一个新的`forward`。
    
        ```py
        # 1_save.py文件中
        
        # -------- 修改 non_bottleneck_1d 的 forward --------
        def new_forward(self, input):
            output = self.conv3x1_1(input)
            output = F.relu(output)
            output = self.conv1x3_1(output)
            output = self.bn1(output)
            output = F.relu(output)
        
            output = self.conv3x1_2(output)
            output = F.relu(output)
            output = self.conv1x3_2(output)
            output = self.bn2(output)
        
            return F.relu(output + input)
        
        non_bottleneck_1d.forward = new_forward
        ```
      
        
      
      - （2）分析parsed后的模型最后一层，可以看到最后一层是Conv2dTranspose算子，因为要使用segpost FPGA硬算子加速，要求最后一层是普通卷积。所以在Conv2dTranspose后再加一个恒等卷积。
      
        - ```py
          # -------- ERFNet + 非分组恒等卷积 --------
          class ERFNetWithIdentityConv(nn.Module):
              def __init__(self, base_model, n_classes):
                  super().__init__()
                  self.base = base_model
                  self.final_conv = nn.Conv2d(n_classes, n_classes, kernel_size=1, bias=False)  # 默认 groups=1
          
                  # 初始化为恒等变换 (单位矩阵形式)
                  with torch.no_grad():
                      self.final_conv.weight.zero_()  # 所有权重先清零
                      for i in range(n_classes):
                          self.final_conv.weight[i, i, 0, 0] = 1.0  # 对角线设置为1
          
              def forward(self, x):
                  out = self.base(x)
                  out = self.final_conv(out)
                  return out
          ```
      
        - <img src="./fig/parsed.png" style="zoom:50%;" />
        - 最后一层是一个恒等卷积（不需训练）。
    
  - **2_save_infer.py** 
    
    - 用修改后保存的模型做前向推理，目的是为了与**0_infer**的结果对比，确保导出静态模型的正确性。
    - 用法： python 2_save_infer.py

## 4. cpu处理部分c++工程移植说明

#### 4.1 cpu环节实现的功能

**CPU（Host 端）**承担了**模型部署**与推理流程中大量关键的**前处理**、**后处理**和**控制逻辑**。

1）**配置与初始化（控制逻辑）**

1. **读取 YAML 配置文件：**
   - 包括模型路径、仿真与实卡运行开关、是否可视化输出、是否保存结果等。
   - 加载图像路径、类别标签文件、网络输入尺寸等信息。
2. **构建模型路径、打开设备并加载模型：**
   - 根据配置动态拼接 JSON 模型路径和 RAW 参数路径。
   - 判断是否在仿真（sim）还是实际设备（实卡）上运行。
   - 加载网络结构并初始化 `Session` 和 `Device`。

2）**前处理（Preprocess）**

这是典型的 **在 CPU 上运行的图像前处理操作**，目标是将原始图片转为模型输入格式：

1. **图像读取：**
   - 使用 OpenCV 加载图像为 BGR 格式。
2. **尺寸变换与补边**：
   - `Resize`：以 **长边为基准**（此处为宽 = 2048），将图像缩放，使其长边匹配目标尺寸（如输入为 512×1024，则长边为1024）
     - 缩放比例 = 1024 / 2048 = 0.5
     - 缩放后图像尺寸为 **512×1024**
   - `rPad`：在 **短边方向（此处为高 = 512）** 进行对称补零，补成最终输入尺寸（如目标为 512×1024，实际已满足则无需额外 padding）
   - 若目标为正方形输入（如 1024×1024），则：
     - 当前图像已为 512×1024，需要在高方向补齐至1024，高度补 512 像素（上下各补 256）
     - 最终尺寸为 **1024×1024**
3. **图像转换为张量：**
   - 将裁剪后的图像转为 NCHW 格式、float 类型张量，用于输入模型（`CvMat2Tensor()`）。
4. **输入 DMA 传输：**
   - 将 CPU 中的图像张量传输给设备端执行推理（`dmaInit()`）。

3）**模型执行控制**

1. **执行模型前向推理：**
   - 由 CPU 调用 `session.forward()` 启动一次推理流程，控制输入和获取输出。
   - 返回的是在设备端计算完成的结果张量。
2. **设备端输出转回 Host 内存：**
   - 将设备端输出张量（float*）搬运回 CPU 内存空间供后处理使用（`to(HostDevice::MemRegion())`）。

4）**后处理（Postprocess）**

这些操作也是由 CPU 进行的，主要包括：

1. **数据回传**
   - `to(HostDevice::MemRegion())` 将NPU输出的分割张量复制到Host内存。
   - 这里使用segpost FPGA硬算子加速。
   - 示例输出shape为`[1,512,1024,19]`（Cityscapes的19类分割任务）
2. **结果解析**
   - 根据输出类型（float多通道/int8单通道）转换数据指针
   - 构建OpenCV矩阵：多通道用`CV_32FC19`，单通道用`CV_8SC1`
3. **尺寸调整**
   - 将网络输出resize到原始图像尺寸（如1024x2048）
   - 使用双线性插值保持边缘平滑
4. **类别映射**
   - 将每个类别用不同颜色表示。

#### 4.2 各环节耗时

**命令行输入`icraft run ./config/ErfNet_8.toml`，以仿真int8量化后的模型为例，单一输入图像对理时间输如下：**

```
# ErfNet_8.toml文件中
[run]
log_time = true
log_io = true
dump_format = "SFB"
json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.json"
raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.raw"
input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
log_path = "./logs/"
```

```c
D:\dtpw\Desktop\icraft_workplace\erfnet\2_compile>icraft run ./config/ErfNet_8.toml
[06/30/25 17:55:49.661] [I] ImageMake Version is 3.8.1.0
SegPost Version is 3.8.1.0
[06/30/25 17:55:49.741] [W] Turn on both dump and calctime will get wrong timing results.
Running     : [██████████████████████████████████████████████████ ] 100% [00m:22s]
Input     0    In:                                                              Out: (0|-1,-1,-1,-1)
Resize    1    In: (0|-1,-1,-1,-1)                                              Out: (1|1,512,1024,3)
ImageMake 609  In: (1|1,512,1024,3)                                             Out: (1295|1,1,512,1024,4)
HardOp    902  In: (1295|1,1,512,1024,4)                                        Out: (443|1,1,512,1024,32)
SegPost   610  In: (443|1,1,512,1024,32)                                        Out: (1296|1,512,1024,1)
Output    215  In: (1296|1,512,1024,1)                                          Out:
[06/30/25 17:56:12.117] [I] network wall:   0.0162 ms
Load Json Time:   43.718 ms
Load Raw Time:   32.475 ms
opId: 0    opType: Input           wall:   32.332 ms  mem:    0.000 ms  hard:    0.000 ms  other:   32.332 ms
opId: 1    opType: Resize          wall:   50.862 ms  mem:    0.000 ms  hard:    0.000 ms  other:   50.862 ms
opId: 609  opType: ImageMake       wall:  130.088 ms  mem:    0.000 ms  hard:    0.000 ms  other:  130.088 ms
opId: 902  opType: HardOp          wall: 19747.800 ms  mem:    0.000 ms  hard:    0.000 ms  other: 19747.800 ms
opId: 610  opType: SegPost         wall: 2015.161 ms  mem:    0.000 ms  hard:    0.000 ms  other: 2015.161 ms
opId: 215  opType: Output          wall:    0.013 ms  mem:    0.000 ms  hard:    0.000 ms  other:    0.013 ms


################# SUMMARY #################

OpNum 1   OpType HardOp           total_wall: 19747.800 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other: 19747.800 ms
OpNum 1   OpType ImageMake        total_wall:  130.088 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:  130.088 ms
OpNum 1   OpType Input            total_wall:   32.332 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   32.332 ms
OpNum 1   OpType Output           total_wall:    0.013 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:    0.013 ms
OpNum 1   OpType Resize           total_wall:   50.862 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   50.862 ms
OpNum 1   OpType SegPost          total_wall: 2015.161 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other: 2015.161 ms
total_time: 0.016 ms, total_hard_time: 0.000 ms
ImageMake: 0.000 ms
SegPost: 0.000 ms
```

**当前仿真系统的cpu配置情况**（不同配置运行速度不同，只作参考）

```
D:\dtpw\Desktop\icraft_workplace\ErfNet\2_compile>wmic cpu get name,numberofcores,numberoflogicalprocessors,maxclockspeed
MaxClockSpeed  Name                                    NumberOfCores  NumberOfLogicalProcessors
1800           AMD Ryzen 7 4800U with Radeon Graphics  8              16
```

**分析**

* **核心推理耗时在 `HardOp`，即模型主干部分（19747.800 ms）**，这是真正的前向计算阶段；
* **前处理部分（Resize + ImageMake）耗时约  130.088ms**；
* **类型转换和输出阶段使用SegPost耗时2015.161 ms**；

## 6. ICRAFT编译

- **不支持算子（但通过前面所述手段绕过）**

  在icraft logs中找到不支持的算子序列、在源码中定位对应的源码、实现的功能

  | 算子序列              | 对应源码       | 实现的功能   |
  | --------------------- | -------------- | ------------ |
  | aten::feature_dropout | `nn.Dropout2d` | 整个通道置零 |

### **编译通过**

- 使用运行时仿真得到的仿真效果截图或数据，需提供表格

  > 比赛报告中要求有：框架动态图推理、导出静态模型推理、解析组件仿真、量化组件仿真、指令组件仿真的仿真效果截图或数据。对于解析组件仿真、量化组件仿真、指令组件仿真等中间过程仿真该怎么做？文档中并没写明，只能参考[docs/Part 2_2 icraft tools.md · spiger/TUTORIAL - 码云 - 开源中国](https://gitee.com/mxh-spiger/tutorial-runtime/blob/tt3.7.1/docs/Part 2_2 icraft tools.md#34-结果分析)中的`icraft run`工作简单输出每个组件的仿真。


#### 1. 框架动态图推理

* 输入图片

  * <img src="./fig/input.png" style="zoom:37%;" />

* 框架推理

  * `python 0_infer.py`

  * 输出结果：

    <img src="./fig/output.png" style="zoom:70%;" />

#### 2. 导出静态模型推理

* 输入图片：同上
* 导出静态模型推理

  * `python 2_save_infer.py`

#### 3. 解析组件仿真(icraft run命令)

* 命令：

  * ```
    # ErfNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_parsed.json"  					
    raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_parsed.raw"                      
    input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
    backends = "Host"
    ```

  * 命令行运行`\ErfNet\2_compile>icraft run ./config/ErfNet_8.toml`

 * 输出截图（使用仿真命令icraft run）
   * <img src="./fig/解析组件仿真.png" style="zoom:100%;" />

#### 4. 量化组件仿真(icraft run命令)

* 命令：

  * ```
    # ErfNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_quantized.json"  					
    raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_quantized.raw"                      
    input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
    backends = "Host"
    ```

  * 命令行运行`\ErfNet\2_compile>icraft run ./config/ErfNet_8.toml`

 * 输出截图
   * <img src="./fig/量化组件仿真.png" style="zoom:100%;" />

#### 5. 指令组件仿真(icraft run命令)

* 命令：

  * ```
    # ErfNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.json"  					
    raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.raw"                      
    input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
    backends = "Host"
    ```

  * 命令行运行`\Bisenet\2_compile>icraft run ./config/ErfNet_8.toml`

* 输出截图
  * <img src="./fig/指令组件仿真.png" style="zoom:100%;" />

#### 6. 完整的c++工程仿真输出（c++  runtime）

* 在`3_deploy/modelzoo/ErfNet/build_win`目录下运行：

  ```shell
  Release\ErfNet_psin.exe ..\cfg\ErfNet_8.yaml  #注意，windows中一般用\,而/可以用但有些时假会报错,仿真时分割任务时间长，请等待。
  
  Release\ErfNet_psin.exe ..\cfg\ErfNet_16.yaml
  
  Release\ErfNet_psin.exe ..\cfg\ErfNet_mix.yaml
  ```

* 输出截图

  * <img src="./fig/output2.png" style="zoom:70%;" />

#### 7.   8bit，16bit，混合精度的精度测试结果

*  分别运行以下命令，得到8bit，16bit，混合精度模型在**CITYSCAPES验证集**图像上的分类结果。

  ~~~shell
  Release\ErfNet_psin.exe ..\cfg\ErfNet_test_8.yaml  # 注意，windows中一般用\,而/可以用但有些时假会报错
  
  Release\ErfNet_psin.exe ..\cfg\ErfNet_test_16.yaml
  
  Release\ErfNet_psin.exe ..\cfg\ErfNet_test_mix.yaml
  ~~~

  结果分别保存在

  ```
  3_deploy\modelzoo\ErfNet\io\output\8\val_5000
  3_deploy\modelzoo\ErfNet\io\output\16\val_5000
  3_deploy\modelzoo\ErfNet\io\output\mix\val_5000
  ```

* 运行精度测试命令`\Release\ErfNet_psin.exe ..\cfg\ErfNet_test_8.yaml `后，**使用metrics测试脚本**，根据保存的测试结果计算出精度指标。

  metrics测试脚本使用与配置说明：

  - 精度测试脚本存放于：[https://gitee.com/mxh-spiger/benchmark](https://gitee.com/mxh-spiger/benchmark)
  - 因为该模型是在512x1024的输入和输出下训练的，CITYSCAPES验证集的GT大小都为1024x2028，所以可使用我提供的erfnet\3_deploy\modelzoo\ErfNet\io\output\8\leftImg8bit\val\myresizeGT.py的这个脚本resize到1024x2028大小，再进行精度测试。
  
* 精度结果

  * | ErfNet        | input shape      | 精度                                  |
    | ------------- | ---------------- | ------------------------------------- |
    | int8          | [1, 3, 512,1024] | allAcc/mAcc/mIoU 93.11%/71.78%/61.67% |
    | int16         | [1, 3, 512,1024] | allAcc/mAcc/mIoU 94.94%/81.27%/71.23% |
    | mix(混合精度) | [1, 3, 512,1024] | allAcc/mAcc/mIoU 93.43%/70.61%/63.32% |

#### 8. 编译过程的一些特殊说明

* 注意，windows中的命令行中，一般用左\,右/可以用但有些时假会报错

* 编译命令注意事项

  * ```
    cd 3_deploy\modelzoo\ErfNet
    mkdir -p build_win
    cd build_win
    
    cmake -G "Visual Studio 17 2022" -A x64 ..    # 不要直接cmake ..
    cmake --build . --config Release
    ```

  * 不能直接使用`cmake ..`命令，因为如果系统上多种编译器，且默认cmake使用的编译器不是[visualstudio2022](https://visualstudio.microsoft.com/zh-hans/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&passive=false&cid=2030)会出错，所以使用`-G "Visual Studio 17 2022" -A x64`命令，让cmake使用visualstudio2022编译器编译。
