---
title: MSPN50
canonical_url: "https://www.modelscope.cn/models/AIBSECO/MSPN50"
md_url: "https://www.modelscope.cn/models/AIBSECO/MSPN50.md"
repository: AIBSECO/MSPN50
last_updated: 2025-11-19
pipeline_tag: human-wholebody-keypoint
tasks:
  - human-wholebody-keypoint
library_name:
  - onnx
downloads: 0
stars: 0
---

# MSPN50

> MSPN50 - AIBSECO 在 ModelScope 开源的模型。| item | status | detail | | --------------------------------------- | ------ | ------ | | 方案选择与硬件资源划分 | 1 | | 模型导出 | 1 | | | 导出模型精度一致，提供python验证脚本 | 1 | | cpu处理部分c++工程移植 | 1 | | c++工程精度一致，提供验证脚本 | 1 | | c++工程性能优化 | 1 | |…

- **Repository**: AIBSECO/MSPN50
- **Tasks**: human-wholebody-keypoint
- **Downloads**: 0
- **Stars**: 0
- **Last updated**: 2025-11-19

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

---

# MSPN50

## 1. 摘要

1. 身份编号：fwb0042


2. 类型与序号：MSPN50 方案1


3. 进展：

    | item                                    | status | detail |
    | --------------------------------------- | ------ | ------ |
    | 方案选择与硬件资源划分                  | 1      |
    | 模型导出                                | 1      |        |
    | ~导出模型精度一致，提供python验证脚本   | 1      |
    | cpu处理部分c++工程移植                  | 1      |
    | ~c++工程精度一致，提供验证脚本          | 1      |
    | ~c++工程性能优化                        | 1      |
    | icraft编译通过                          | 1      |        |
    | ~存在量化精度调优工作                   | 1      |
    | ~完成基本pyrtcrt搭建与结果验证          | 1      |
    | ~完成pyrtcrt精度测试与对齐              | 1      |
    | 完成上板耗时测试                        | 1      |
    | ~使用运行时提供的api进行基本性能优化    | 1      |
    | ~存在整体方案各环节优化或优化方案的设计 | 1      |
    | 提出bug数                               | 0      |


## 2. 方案简介

### 2.1  算法流程介绍

#### 模型或算法背景：
    
- MSPN（**Multi-Stage Pose Network**）是一种用于高精度多人人体姿态估计的框架，它通过多阶段、由粗到精的级联方式来逐步提升关键点定位的准确性。
- 源仓库链接：https://github.com/open-mmlab/mmpose (作为MMPOSE项目的一部分)
- 开发团队：微软亚洲研究院 (MSRA) (文方、李程、方慧芝等人主导开发)
- 发布时间：2019年
- 性能水平：MSPN在当时的COCO姿态估计挑战赛上取得了顶尖成绩。其核心思想——**多阶段 refinement** 和 **跨阶段特征聚合**——被证明是提升姿态估计精度的极其有效的方法，对后续的高精度姿态估计算法产生了深远影响。


在COCO val2017数据集上，使用ResNet-50作为主干的MSPN50与其他方法的性能对比：

| Model                |   Backbone    | Input Size  |  GFLOPs  | Params(M) |    AP    |   AP50   |   AP75   |    AR    |   AR50   |
| :------------------- | :-----------: | :---------: | :------: | :-------: | :------: | :------: | :------: | :------: | :------: |
| Hourglass            |   Hourglass   |   256x192   |   19.7   |   25.1    |   66.9   |    -     |    -     |    -     |    -     |
| CPN                  |   ResNet-50   |   384x288   |   13.5   |   32.8    |   72.1   |   91.4   |   79.5   |   78.5   |   95.0   |
| Simple Baseline      |   ResNet-50   |   256x192   |   5.3    |   25.6    |   70.4   |   88.6   |   78.3   |   76.3   |   92.5   |
| **MSPN50 (2-stage)** | **ResNet-50** | **256x192** | **12.2** | **29.1**  | **74.1** | **90.0** | **81.6** | **79.5** | **93.8** |
| **MSPN50 (4-stage)** | **ResNet-50** | **256x192** | **24.1** | **32.0**  | **75.1** | **90.4** | **82.3** | **80.4** | **94.2** |


#### 算法流程梳理：

<img src="./assets/2.png" alt="运行时工程整体介绍" style="zoom: 80%;" />


#### 模型结构上的创新与意义：
MSPN的核心是一个级联式的多阶段架构，每个阶段都包含一个编码器-解码器结构，并将前一阶段的预测结果作为后一阶段的先验知识，进行逐步精炼。

人体检测框输入 ——> **Stage 1** ——> 粗略热力图 ——> **Stage 2** ——> 精炼热力图 ——> ... ——> **Final Stage** ——> 最终高精度热力图

<img src="./assets/MSPN.jpg" alt="MSPN模型结构" style="zoom: 80%;" />

MSPN的架构可以分解为以下几个关键部分：

**1. 多阶段级联 (Multi-Stage Cascade):**
- 整个网络由多个（通常是2或4个）结构相似的子网络（即“阶段”）串联而成。
- **第一阶段**：接收裁剪后的人像图片，进行一次完整的姿态估计，生成一个初步的、较为粗糙的关键点热力图。
- **后续阶段**：每个后续阶段的输入不仅包含原始图像下采样后的特征，还**融合了前一阶段输出的热力图**。这相当于告诉当前阶段：“上一阶段认为关键点大概在这个位置，请你在这个基础上进行更精细的定位。”

**2. 单阶段内的结构 (Intra-Stage Architecture):**
- 每个阶段内部都是一个经典的编码器-解码器（Encoder-Decoder）结构。
- **编码器 (Downsample)**：通常采用标准的卷积神经网络（如ResNet-50）作为主干，负责从图像中提取多层次的特征。
- **解码器 (Upsample)**：采用一系列反卷积（Deconvolution）或上采样+卷积层，逐步将高层语义特征恢复到较高的分辨率，并最终生成与输入尺寸1/4大小的热力图。

**3. 跨阶段连接 (Cross-Stage Connection):**
- 这是MSPN设计的精髓。它并非简单地将前一阶段的热力图直接送入下一阶段，而是设计了精巧的**跨阶段特征聚合单元**。
- 在每个尺度上（编码和解码过程中），当前阶段的特征图都会与前一阶段**对应尺度**的特征图进行融合。这通常通过一个1x1卷积进行压缩，然后进行元素相加（Element-wise Sum）来实现。
- 这种设计确保了后一阶段不仅能看到前一阶段的最终预测，还能利用其所有中间层的丰富特征，从而实现更有效的信息传递和精炼。

**4. 监督信号 (Supervision):**
- **多阶段监督**：与只监督最终输出不同，MSPN在**每个阶段的末尾**都计算损失并进行监督。这相当于为每个子网络都设置了明确的学习目标，避免了梯度消失问题，使得整个深层网络能够被有效训练。

基本组件：

**ResNet-50**：作为每个阶段编码器的强大主干网络，用于提取高质量的视觉特征。
**反卷积层 (Deconvolution Layer)**：在解码器中用于上采样，恢复特征图的分辨率。
**跨阶段特征融合单元**：通过1x1卷积和元素相加，将前一阶段的中间特征高效地传递给当前阶段。





### 前后处理

**前处理：**
1.  **人体检测与裁剪**：
    - 首先，需要一个目标检测器（如YOLO, SSD）来检测出图像中的每个人。
    - 根据检测框对每个人进行裁剪，并将裁剪出的人像区域作为MSPN50的输入。
    - （为测试单模型部署效果，此处采用直接用裁剪后的图片作为输入）
2.  **尺寸调整与归一化**：
    - 将裁剪出的人像区域缩放到模型所需的固定输入尺寸（如 256x192），并保持固定的宽高比。
    - 进行像素值的归一化（如减均值、除以标准差）。
      ```python
        # 读取原始图像
        ori_img = cv2.imread(image_path)
        self.ori_height, self.ori_width = ori_img.shape[:2]

        # 调整尺寸 (保持宽高比填充到模型输入尺寸)
        input_height, input_width = self.input_shape[2], self.input_shape[3]
        scale = min(input_width / self.ori_width, input_height / self.ori_height)
        new_width = int(self.ori_width * scale)
        new_height = int(self.ori_height * scale)

        # 填充黑边使图像符合模型输入尺寸
        resized_img = cv2.resize(ori_img, (new_width, new_height))
        padded_img = np.zeros((input_height, input_width, 3), dtype=np.uint8)
        dx = (input_width - new_width) // 2
        dy = (input_height - new_height) // 2
        padded_img[dy:dy + new_height, dx:dx + new_width] = resized_img

      ```
**后处理：**
1.  **热力图解码**：
    - 模型输出一组热力图，每张图对应一个关键点。
    - 在每张热力图中找到响应值（概率）最高的像素点位置 `(x, y)`。
      ```python
        heatmap=np.array(output_tensors[0]).transpose(0,3,1,2)
        batch_size, num_kpts, H, W = heatmap.shape
        heatmap = heatmap[0]  # 取batch中的第一个

        keypoints = []
        for i in range(num_kpts):
            hm = heatmap[i]
            max_loc = np.unravel_index(np.argmax(hm), hm.shape)
      ```
2.  **坐标映射**：
    - 将从热力图中解码出的坐标，从模型的输入尺寸（256x192）反向映射回原始图像的坐标系中。
      ```python
  
        score = hm[max_loc]
        x=max_loc[1]*(256/64)
        y=max_loc[0]*(192/48)
        # 从热图坐标转换到输入图像坐标
        x = (x - pad_info['padding'][0]) / pad_info['scale']
        y = (y - pad_info['padding'][1]) / pad_info['scale']

        # 限制在图像范围内
        x = np.clip(x, 0, ori_shape[0] - 1)
        y = np.clip(y, 0, ori_shape[1] - 1)

        keypoints.append([x, y, score])

      ```



### 2.2  硬件资源分配

  psin模式：由电脑端传入测试图片

 前处理：图片的resize和pad等由CPU完成，从PS->PL由FPGA完成

 网络主体：npu

 后处理热力图解码等：fpga

 后处理坐标映射等：cpu

 可视化：cpu


## 4. 模型导出说明

  关于模型源码：

  模型源码为OPENMMLAB的MMPOSE库，具体安装参考如下链接：
  https://mmpose.readthedocs.io/zh-cn/latest/installation.html
  
  MMPOSE源码仓库链接为：https://github.com/open-mmlab/mmpose.git
  
  从MMPOSE导出模型为ONNX需要安装OPENMMLAB的MMDEPLOY库，具体安装参考如下链接：
  https://mmdeploy.readthedocs.io/zh-cn/stable/get_started.html#mmdeploy

  MMDEPLOY源码仓库链接为：https://github.com/open-mmlab/mmdeploy.git


  关于模型导出：
  确保文件夹结构如下所示：

  ├── mmdeploy         >openmmlab用于部署导出文件的代码

  ├── mmpose	        >openmmlab关于关键点检测的代码

  路径切到mmdeploy中，然后并下载好对应预训练模型，放在对应位置，执行下面代码，模型输出在rtmpose-ort文件夹下

  ~~~
  python tools/deploy.py configs/mmpose/pose-detection_onnxruntime_static.py ../mmpose/configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_mspn50_8xb32-210e_coco-256x192.py ../mmpose/ckpt/mspn50_coco_256x192.pth demo/resources/human-pose.jpg --work-dir rtmpose-ort/rtmpose-m --device cpu --show --dump-info
  ~~~
  不额外做处理，直接将pt模型导出为onnx


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

**1. CPU环节主要实现的内容**
- 1.读取并设置输入yaml文件中的相关参数，以及载入测试图片等功能
- 2.图片的resize和pad,以符合模型输入尺寸
- 3.在模型后处理部分，负责将热力图解码得到的坐标映射到原图中，并实现可视化
- 4.在MSPN50_task_queue.cpp中，还负责控制多线程实现并行计算
- 5.在16bit精度的工程中，由于无法使用硬算子实现热力图解码，这一部分也由CPU实现


**2. 做了哪些测试验证移植后准确性**
- 分别做了仿真阶段以及实际Socket上板阶段的精度测试，经测试精度一致，符合准确性
- 分别做了在pyrt和crt下的上板精度测试，经过测试精度一致，符合准确性

**3.各环节耗时**

在8bit条件下，耗时如下图所示
<img src="./assets/8bit_time.png" alt="8bit各阶段耗时" style="zoom: 80%;" />

在16bit条件下，耗时如下图所示
<img src="./assets/16bit_time.png" alt="16bit各阶段耗时" style="zoom: 80%;" />

在混合精度条件下，耗时如下图所示
<img src="./assets/mixbit_time.png" alt="mixbit各阶段耗时" style="zoom: 80%;" />

## 6. ICRAFT编译

---

**编译通过**

- 16bit下推理测试结果：
  | 框架动态图推理                           | 导出静态模型推理                             | 解析组件仿真                       | 量化组件仿真                       | 指令组件仿真                       |
  | ---------------------------------------- | -------------------------------------------- | ---------------------------------- | ---------------------------------- | ---------------------------------- |
  | ![alt text](./assets/output_pytorch.jpg) | ![alt text](./assets/output_onnxruntime.jpg) | ![alt text](./assets/output_p.jpg) | ![alt text](./assets/output_q.jpg) | ![alt text](./assets/output_g.jpg) |
  

- 8bit，16bit，混合精度的精度测试结果



- 8bit测试结果:
  
        --- Evaluation Summary ---
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.570
        Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.851
        Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.631
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.558
        Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.590
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.699
        Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.910
        Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.771
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.665
        Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.748
        ------------------------



- 16bit测试结果:
  
        --- Evaluation Summary ---
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.582
        Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.856
        Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.646
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.561
        Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.620
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.709
        Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.917
        Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.781
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.676
        Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.758
        ------------------------



- 混合精度测试结果:

        --- Evaluation Summary ---
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.574
        Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.854
        Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.635
        Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.553
        Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.617
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.704
        Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.916
        Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.777
        Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.670
        Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.753
        ------------------------


- 编译过程的一些特殊说明

  > - 量化校准集尽量选择经过目标识别后的裁剪图片，或者数据集中人物占图片比例较大的图片
                          


## 7. 上板测试与优化

1. 优化前各环节耗时统计(表格)


**带imk，speedmode=false，compressftmp=false，fpga_argmax=false, socket上板**
<img src="./assets/1.png" alt="优化前各阶段耗时" style="zoom: 80%;" />


2. 优化后耗时统计(表格)


**带imk，speedmode=true，compressftmp=true，fpga_argmax=true, socket上板**
<img src="./assets/8bit_time.png" alt="8bit各阶段耗时" style="zoom: 80%;" />

3. 优化项与效果

**优化项1：合并算子**

设置speedmode=true,compress=true实现算子合并，加速计算
<img src="./assets/3.png" alt="运行时工程整体介绍" style="zoom: 80%;" />

可见总耗时由原来的200ms左右变为10ms左右


**优化项2：后处理上，去除cast算子，并用FPGA算子替代CPU实现热力图解码**



<img src="./assets/4.png" alt="运行时工程整体介绍" style="zoom: 80%;" />

可见总耗时由10ms左右变为7ms左右

**优化项3：采用多线程并行计算**

采用多线程并行计算的方法，将整体runtime分为三个线程：

1.input_thread: 负责读取裁剪图片并输入imk算子完成搬数。

2.icore_thread[n]: 负责完成模型的前向推理工作。

3.post_thread:负责完成模型的后处理工作。

<img src="./assets/5.png" alt="运行时工程整体介绍" style="zoom: 80%;" />

在多线程条件下，运行工程吞吐率约在60-70FPS，单线程条件下，运行工程吞吐率约在40FPS，提升约150％


**优化项4：前处理部分采用imagemake硬算子，缩短数据输入时间**





1. 运行时工程的整体介绍

<img src="./assets/2.png" alt="运行时工程整体介绍" style="zoom: 80%;" />

如上图流程图所示，整个流程展示了从模型部署、数据处理到硬件加速推理和结果生成的完整解决方案。程序首先通过解析 YAML 配置文件来初始化各项参数，包括模型路径、运行模式（硬件仿真或上板运行）、数据集位置以及后处理选项。随后，它加载预编译的神经网络模型（.json 和 .raw 文件），并根据配置创建与目标设备的会话（Session），完成模型的部署和资源分配。

在主处理循环中，程序逐一读取输入图像。对每张图像，它会执行一个保持纵横比的预处理操作：将图像缩放并填充（padding）至模型所需的输入尺寸，然后转换为框架所需的 Tensor 格式。该 Tensor 被送入 session.forward() 接口进行硬件加速推理。

推理完成后，程序进入后处理阶段。它从输出的热图（heatmap）中提取关键点坐标。此过程支持两种模式：一种是纯软件实现的 argmax 算法，在 CPU 上计算；另一种是高性能的硬件加速模式，调用 FPGA 上的 argmax 逻辑直接计算坐标。最后，程序将提取出的坐标经过反向变换，映射回原始图像尺寸，并将结果可视化（绘制关键点）或保存到指定目录。
