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

# pspnet

> pspnet - AIBSECO 在 ModelScope 开源的模型。1. 模型背景 PSPNet（Pyramid Scene Parsing Network） 是一款经典的语义分割模型，由 香港中文大学和商汤科技（SenseTime）研究团队 于 2017 年在论文《Pyramid Scene Parsing Network》中提出，并发表在 CVPR 2017…

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

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

---

# PSPNet图像分类模型

## 2. 方案简介

### 2.1 算法流程介绍

#### 1. 模型背景
**PSPNet（Pyramid Scene Parsing Network）** 是一款经典的语义分割模型，由 **香港中文大学和商汤科技（SenseTime）研究团队** 于 **2017 年**在论文《**Pyramid Scene Parsing Network**》中提出，并发表在 **CVPR 2017** 上。该网络专为提升图像中多尺度场景理解能力而设计，针对语义分割中区域上下文缺失与物体尺度变化问题，引入了创新性的结构：**金字塔池化模块（Pyramid Pooling Module, PPM）**。

在传统的语义分割网络中，往往存在**感受野不足**与**上下文信息缺失**的问题，导致模型对大物体结构掌握不全、对小物体容易忽略。为解决这一痛点，PSPNet 在骨干网络（如 ResNet）提取的最后一层特征图上构建一个**多尺度上下文聚合结构**，通过不同尺度的全局池化操作提取特征，从而增强模型对不同尺寸目标的适应能力。

PSPNet 的核心设计是 **金字塔池化模块（PPM）**，其主要流程包括：

- 使用多种不同尺度（如 1×1、2×2、3×3、6×6）的池化核对输入特征进行下采样；
- 将不同尺度池化后的特征图通过卷积统一通道数后上采样至原始尺寸；
- 将所有上采样后的特征图与原始输入进行**通道拼接（concatenation）**，融合多尺度上下文信息；
- 最终用于分割预测的特征图同时具备局部精细结构和全局语义感知。

相较于仅使用深层卷积或 FCN 的方法，PSPNet 在保持分割精度的同时显著提升了对复杂场景和结构的解析能力，特别适用于城市街景、卫星图像、遥感识别等具有**多尺度目标分布**的场景。

此外，PSPNet 还具有如下特点与优势：

- **全局上下文建模强**：PPM 能整合不同感受野下的信息，提升对大目标、遮挡区域的判别能力；
- **适配主干网络灵活**：可与 ResNet-50、ResNet-101 等多种骨干网络组合使用；
- **无需额外标注**：在仅使用标准语义分割数据集的前提下即可获得优异性能；
- **结构简洁有效**：相比后续 DeepLabV3 等复杂结构，PPM 更加高效轻量。

PSPNet 在 Cityscapes、ADE20K 等主流语义分割数据集上均取得了 SOTA 级别的性能表现，其模块化设计也被广泛借鉴于如 HRNet、OCRNet、PIDNet 等后续高性能语义分割模型中。

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

#### 2. 算法流程图
```mermaid
graph TD
    A[输入图像] --> B[前处理]
    B --> C[PSPNet模块]
    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图像缩放至473px]
        C1 --> C2[rPad填充]
        D --> D1["数据类型转换 uint8→float32"]
        D1 --> D2["归一化 /255.0"]
        D2 --> D3["通道转换 BGR→RGB"]
        D3 --> D4["添加Batch维度 [1,3,473,473]"]
        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传输初始化
```

4. - **具体处理流程**：
     1. **图像加载**：通过 OpenCV 读取原始图像（`cv::IMREAD_COLOR` 保持 RGB 格式）
        - 示例：输入 `img_path` 为 `"/data/voc2012/2007_000032.jpg"`，图像尺寸为 **375×500（高×宽）**
     2. **尺寸变换与补边**：
        - `Resize`：以 **长边为基准**（此处为宽 = 500），将图像缩放至473px
          - 缩放比例 = 473 / 500 ≈ 0.946
          - 缩放后图像尺寸为 **354×473**
        - `rPad`：在 **短边方向（此处为高 = 354）** 进行对称补零
          - 需要补高度：473 - 354 = 119px（上下各补59.5→实际补60和59）
          - 最终尺寸为 **473×473**
     3. **张量转换**：
        - `CvMat2Tensor` 完成以下处理：
          - 数据类型转换：`uint8 → float32`
          - 像素归一化（除以 255.0）
          - 通道顺序调整：BGR → RGB
          - 添加 Batch 维度：转换后张量尺寸为 `[1, 3, 473, 473]`
     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,473,473,21]"]
        C --> C1["(float*/int8_t*)host_tensor.data().cptr()"]
        D --> D1["resize(原图尺寸)"]
        E --> E1["map_plot颜色映射"] --> E2["使用VOC2012调色板"]
        F --> F1["cv::imwrite保存"]
    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_32FC21,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,473,473,21]`（VOC的21类分割任务）

2. **结果解析**

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

3. **尺寸调整**

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

4. **类别映射**

   - `map_plot`函数核心操作：

     ```
     解释// 像素级类别索引→调色板颜色
     for(auto i : img_id){
         if(i >= 21) color = [0,0,0]; // 未知类别用黑色
         else color = PALETTE_VOC[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. 模型导出说明

#### PSPNet 主干

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

  - 常规卷积：`nn.Conv2d`实现所有基础卷积
  - 批归一化：`nn.BatchNorm2d`配合每个卷积层
  - 池化操作：`nn.MaxPool2d`实现初始下采样
  - 自适应池化：`nn.AdaptiveAvgPool2d`实现多尺度池化

  2）**残差模块结构**：

  - Bottleneck设计：
    - 1x1降维→3x3卷积→1x1升维
    - 残差连接：通过`downsample`模块匹配维度
  - 膨胀卷积：layer4中使用dilation=2的3x3卷积

  3）**金字塔池化模块**：

  - _PSPModule结构：
    - 多尺度池化路径：
      - 1x1池化+卷积
      - 2x2池化+卷积
      - 3x3池化+卷积
      - 6x6池化+卷积
    - 特征合并：通道维度拼接所有路径输出
    - 瓶颈层：3x3卷积降维+BN+ReLU+Dropout

  4）**主干网络**：

  - ResNet骨架：
    - 初始卷积：7x7卷积→BN→ReLU→3x3最大池化
    - 四级残差块：
      - layer1：3个bottleneck（256维）
      - layer2：4个bottleneck（512维）
      - layer3：6个bottleneck（1024维）
      - layer4：3个bottleneck（2048维，含膨胀卷积）

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

  - 膨胀率设置：layer4中使用固定dilation=2
  - 批归一化参数：固定eps=1e-05，momentum=0.1
  - Dropout配置：PSP模块中p=0.1
  - 输出卷积：1x1卷积将512维降至21类

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

  - 下采样：通过stride=2的卷积实现
  - 特征融合：PSP模块中4096→512维降维
  - 通道扩展：bottleneck中1x1卷积调整通道数

  7）**多尺度特征融合**：

  - 金字塔池化：同时捕获全局和局部上下文
    - 1x1：全局上下文
    - 2x2：粗粒度区域
    - 3x3：中等粒度
    - 6x6：细粒度特征

  8）**特殊结构设计**：

  - 深度监督：最终直接输出预测图
  - 上下文聚合：通过多级池化实现不同感受野融合
  - 膨胀卷积：高层网络保持特征图分辨率

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

| **PSPNet 功能组件** | **PyTorch 实现方式**         | **iCraft 对应算子组合**                     |
| :------------------ | :--------------------------- | :------------------------------------------ |
| **基础卷积算子**    |                              |                                             |
| 常规卷积            | `nn.Conv2d`                  | `Conv2d`                                    |
| 批归一化            | `nn.BatchNorm2d`             | `BatchNorm2d`                               |
| 最大池化            | `nn.MaxPool2d`               | `MaxPool2d`                                 |
| Dropout正则化       | `nn.Dropout2d(p=0.1)`        | **不支持二维`Dropout`**                     |
| **残差模块**        |                              |                                             |
| Bottleneck结构      | 1x1→3x3→1x1卷积组合          | `Conv2d(k=1x1)→Conv2d(k=3x3)→Conv2d(k=1x1)` |
| 膨胀卷积            | `dilation=2`                 | `Conv2d(dilation=2)`                        |
| 残差连接            | `downsample`路径匹配维度     | `Add`                                       |
| **金字塔池化模块**  |                              |                                             |
| 多尺度池化路径      | 4级`AdaptiveAvgPool2d`       | **需手动实现多尺度池化**                    |
| 特征合并            | 通道维度拼接                 | `Concat`                                    |
| 瓶颈层              | 3x3卷积降维                  | `Conv2d(k=3x3)+BN+ReLU`                     |
| **主干网络**        |                              |                                             |
| ResNet骨架          | 4级残差块（3-4-6-3结构）     | 组合对应算子                                |
| 初始卷积块          | 7x7卷积→BN→ReLU→池化         | `Conv2d(k=7x7)+BN+MaxPool2d`                |
| **特殊操作**        |                              |                                             |
| 输出卷积            | 1x1卷积降维至类别数          | `Conv2d(k=1x1)`                             |
| 多尺度特征融合      | 金字塔各层级输出拼接         | `Concat`                                    |
| 膨胀率配置          | 固定`dilation=2`（仅layer4） | `Conv2d(dilation=2)`                        |
| **激活函数**        |                              |                                             |
| ReLU（隐含）        | 默认激活                     | `ReLU`                                      |
| **维度处理**        |                              |                                             |
| 下采样              | stride=2的卷积               | `Conv2d(stride=2)`                          |
| 通道扩展            | 1x1卷积调整维度              | `Conv2d(k=1x1)`                             |

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

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

~~~
├── 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/pspnet_512x1048.pt`
    
    - 用法： python 1_save.py
    
    - **导出过程修改点**
    
      - （1）因为icraft不支持`nn.Dropout2d`，在原本模型出现`nn.Dropout2d`的位置使用1x1卷积代替，在Icraft Adapter组件内会被优化掉，能提升速度。
    
        ```py
        # 1_save.py文件中
        
        # -------- 替换 Dropout2d 为 AvgPool2d(1) ----------
        def replace_dropout2d(module):
            for name, child in module.named_children():
                if isinstance(child, nn.Dropout2d):
                    setattr(module, name, nn.AvgPool2d(kernel_size=1))
                else:
                    replace_dropout2d(child)
        ```
        
        
        
      - （2）分析parsed后的模型最后一层，可以看到最后一层是Upsample算子，因为要使用segpost FPGA硬算子加速，要求最后一层是普通卷积。所以在Upsample后再加一个恒等卷积。
      
        - ```py
          # -------- 包裹模型并添加恒等卷积 ----------
          class PSPNetWithIdentityConv(nn.Module):
              def __init__(self, base_model, n_classes):
                  super().__init__()
                  self.base = base_model
                  self.identity_conv = nn.Conv2d(n_classes, n_classes, kernel_size=1, bias=False)
                  
                  # 初始化为单位卷积（恒等）
                  with torch.no_grad():
                      self.identity_conv.weight.zero_()
                      for i in range(n_classes):
                          self.identity_conv.weight[i, i, 0, 0] = 1.0
          
              def forward(self, x):
                  out = self.base(x) #[0]  # 只取主分支输出
                  out = self.identity_conv(out)
                  return out
          ```
      
        - <img src="./fig/parsed.png" style="zoom:70%;" />
        - 最后一层是一个恒等卷积（不需训练）。
    
  - **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`：以 **长边为基准**（此处为宽 = 500），将图像缩放至473px
     - 缩放比例 = 473 / 500 ≈ 0.946
     - 缩放后图像尺寸为 **354×473**
   - `rPad`：在 **短边方向（此处为高 = 354）** 进行对称补零
     - 需要补高度：473 - 354 = 119px（上下各补59.5→实际补60和59）
     - 最终尺寸为 **473×473**
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硬算子加速。
2. **结果解析**
   - 根据输出类型（float多通道/int8单通道）转换数据指针
3. **尺寸调整**
   - 将网络输出resize到原始图像尺寸
4. **类别映射**
   - 将每个类别用不同颜色表示。

#### 4.2 各环节耗时

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

```
# PSPNet_8.toml文件中
[run]
log_time = true
log_io = true
dump_format = "SFB"
json = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_BY.json"
raw = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_BY.raw"
input = "./qtset/street.jpg"
log_path = "./logs/"
```

```c
D:\dtpw\Desktop\icraft_workplace\pspnet\2_compile>icraft run ./config/PSPNet_8.toml
[06/30/25 19:56:04.168] [I] ImageMake Version is 3.8.1.0
SegPost Version is 3.8.1.0
[06/30/25 19:56:04.205] [W] Turn on both dump and calctime will get wrong timing results.
Running     : [██████████████████████████████████████████████████ ] 100% [00m:26s]
Input     0    In:                                                              Out: (0|-1,-1,-1,-1)
Resize    1    In: (0|-1,-1,-1,-1)                                              Out: (1|1,473,473,3)
ImageMake 362  In: (1|1,473,473,3)                                              Out: (981|1,1,475,475,4)
HardOp    600  In: (981|1,1,475,475,4)                                          Out: (395|1,1,473,473,32)
SegPost   363  In: (395|1,1,473,473,32)                                         Out: (982|1,473,473,1)
Output    211  In: (982|1,473,473,1)                                            Out:
[06/30/25 19:56:31.147] [I] network wall:   0.0147 ms
Load Json Time:   87.608 ms
Load Raw Time:  482.962 ms
opId: 0    opType: Input           wall:   35.679 ms  mem:    0.000 ms  hard:    0.000 ms  other:   35.679 ms
opId: 1    opType: Resize          wall:   52.669 ms  mem:    0.000 ms  hard:    0.000 ms  other:   52.669 ms
opId: 362  opType: ImageMake       wall:   58.052 ms  mem:    0.000 ms  hard:    0.000 ms  other:   58.052 ms
opId: 600  opType: HardOp          wall: 25650.661 ms  mem:    0.000 ms  hard:    0.000 ms  other: 25650.661 ms
opId: 363  opType: SegPost         wall:  928.881 ms  mem:    0.000 ms  hard:    0.000 ms  other:  928.881 ms
opId: 211  opType: Output          wall:    0.012 ms  mem:    0.000 ms  hard:    0.000 ms  other:    0.012 ms


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

OpNum 1   OpType HardOp           total_wall: 25650.661 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other: 25650.661 ms
OpNum 1   OpType ImageMake        total_wall:   58.052 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   58.052 ms
OpNum 1   OpType Input            total_wall:   35.679 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   35.679 ms
OpNum 1   OpType Output           total_wall:    0.012 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:    0.012 ms
OpNum 1   OpType Resize           total_wall:   52.669 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   52.669 ms
OpNum 1   OpType SegPost          total_wall:  928.881 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:  928.881 ms
total_time: 0.015 ms, total_hard_time: 0.000 ms
ImageMake: 0.000 ms
SegPost: 0.000 ms
```

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

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

**分析**

* **核心推理耗时在 `HardOp`，即模型主干部分（25650.661 ms）**，这是真正的前向计算阶段；
* **前处理部分（Resize + ImageMake）耗时约  58.052ms**；
* **类型转换和输出阶段使用SegPost耗时928.881 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:100%;" />

* 框架推理

  * `python 0_infer.py`

  * 输出结果：

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

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

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

  * `python 2_save_infer.py`

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

* 命令：

  * ```
    # PSPNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_parsed.json"  					
    raw = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_parsed.raw"                      
    input = "./qtset/street.jpg"
    backends = "Host"
    ```

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

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

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

* 命令：

  * ```
    # PSPNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_quantized.json"  					
    raw = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_quantized.raw"                      
    input = "./qtset/street.jpg"
    backends = "Host"
    ```

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

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

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

* 命令：

  * ```
    # PSPNet_8.toml文件中
    [run]
    log_time = true
    log_io = true
    dump_format = "SFB"
    json = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_BY.json"  					
    raw = "../3_deploy/modelzoo/PSPNet/imodel/8/PSPNet_BY.raw"                      
    input = "./qtset/street.jpg"
    backends = "Host"
    ```

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

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

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

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

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

* 输出截图

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

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

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

  ~~~shell
  Release\PSPNet_psin.exe ..\cfg\PSPNet_test_8.yaml  # 注意，windows中一般用\,而/可以用但有些时假会报错
  
  Release\PSPNet_psin.exe ..\cfg\PSPNet_test_16.yaml
  
  Release\PSPNet_psin.exe ..\cfg\PSPNet_test_mix.yaml
  ~~~

  结果分别保存在

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

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

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

  - 精度测试脚本存放于：[https://gitee.com/mxh-spiger/benchmark](https://gitee.com/mxh-spiger/benchmark)
  
* 精度结果

  * | PSPNet        | input shape      | 精度                                                      |
    | ------------- | ---------------- | --------------------------------------------------------- |
    | int8          | [1, 3, 512,1024] | Acc: 0.8707,  Acc_class:0.7949,  mIoU:0.6914, fwIoU:0.852 |
    | int16         | [1, 3, 512,1024] | Acc: 0.8907,  Acc_class:0.8049,  mIoU:0.7174, fwIoU:0.882 |
    | mix(混合精度) | [1, 3, 512,1024] | Acc: 0.8717,  Acc_class:0.7729,  mIoU:0.6714, fwIoU:0.852 |

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

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

* 编译命令注意事项

  * ```
    cd 3_deploy\modelzoo\PSPNet
    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编译器编译。
