> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# qtimlpostprocess

> AI 后处理插件

# 概述

推理模型生成的**输出张量**通常需要**后处理**，才能使结果可供下游组件使用或供应用程序解释。例如：

* **图像分类**的输出是需要解释的置信度分数数组，例如选择超过指定阈值的前几个类别。
* **目标检测**的输出应转换为一组带有关联标签的边界框。
* **姿态估计**的输出应转换为一组关键点及其之间的连接。
* **图像分割**的输出应转换为可叠加在原始帧上的 RGBA 图像掩码。
* **原始张量**数据可能需要转换为后续插件或处理阶段所期望的格式。

在 IM SDK 中，**qtimlpostprocess** 元素负责管理后处理任务。该插件将原始模型输出转换为 **GStreamer ML 元数据**。它是一个可定制的插件，为推理插件的张量输出后处理提供库接口。**后处理库**仅负责张量解析并输出预测结果列表。我们称之为后处理（PP）模块。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_arch.png" alt="Postprocess diagram" />

**qtimlpostprocess** 元素接收张量列表作为输入，每个张量封装在一个 GST Buffer 中。描述张量的元数据（例如张量数量、形状、时间戳、批量索引等）以 GStreamer 元数据的形式附加。

后处理插件配置（GStreamer 属性）：

***

## 示例流水线

<Steps>
  <Step title="下载所需文件">
    | 文件            | 下载                                                                                                                           | 保存为                         |
    | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
    | YOLOX W8A8 模型 | [Qualcomm AI Hub — YOLOX](https://aihub.qualcomm.com/iot/models/yolox)                                                       | `yolo_x_w8a8.tflite`        |
    | 检测标签          | <a href="/SDKs/IMSDK/labels/yolov8.json" download="yolov8.json">yolov8.json</a>                                              | `yolov8.json`               |
    | 示例视频          | <a href="https://github.com/qualcomm/sample-apps-for-qualcomm-linux/raw/refs/heads/main/artifacts/videos/video.mp4">输入视频</a> | `Draw_1080p_180s_30FPS.mp4` |
  </Step>

  <Step title="将文件复制到设备">
    <CodeGroup>
      ```bash SCP (SSH) theme={null}
      # Replace $HOME to the appropriate device path before running the commands.
      # For QLI:    /root
      # For Ubuntu: /home/ubuntu
      # Modify this based on your platform and ensure files are copied to the correct location on the device.
      # Run from your host machine — replace <user> and <device-ip>

      ssh <user>@<device-ip> "mkdir -p $HOME/{models,labels,media,media/output}"
      scp yolo_x_w8a8.tflite          <user>@<device-ip>:$HOME/models/
      scp yolov8.json                  <user>@<device-ip>:$HOME/labels/
      scp Draw_1080p_180s_30FPS.mp4   <user>@<device-ip>:$HOME/media/
      ```
    </CodeGroup>
  </Step>

  <Step title="连接到设备">
    <CodeGroup>
      ```bash SCP (SSH) theme={null}
      # Run from your host machine — replace <user> and <device-ip>
      ssh <user>@<device-ip>
      ```
    </CodeGroup>
  </Step>

  <Step title="设置环境变量">
    在设备上运行以下命令

    ```bash theme={null}
    mkdir -p $HOME/{models,labels,media,media/output}
    export MODEL_NAME=yolo_x_w8a8.tflite
    export LABELS_NAME=yolov8.json
    export SRC_VIDEO_NAME=Draw_1080p_180s_30FPS.mp4
    ```
  </Step>

  <Step title="运行流水线">
    ```bash theme={null}
    gst-launch-1.0 -e --gst-debug=2 \
    filesrc location=$HOME/media/$SRC_VIDEO_NAME ! qtdemux ! h264parse ! \
    v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! queue ! \
    tee name=t ! qtimetamux name=obj_mux ! qtivoverlay ! waylandsink fullscreen=true sync=false \
    t. ! queue ! qtimlvconverter ! queue ! \
    qtimltflite model=$HOME/models/$MODEL_NAME delegate=external external-delegate-path=libQnnTFLiteDelegate.so \
      external-delegate-options="QNNExternalDelegate,backend_type=htp,log_level=(string)1;" ! queue ! \
    qtimlpostprocess module=yolov8 labels=$HOME/labels/$LABELS_NAME \
      settings="{\"confidence\": 51.0}" ! text/x-raw ! queue ! obj_mux.
    ```
  </Step>
</Steps>

# 元素属性

| 属性                   | 描述                                                                                                                                      |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `module`             | 后处理模块名称。此必填属性指定张量的解析方式。它不定义插件输出类型，输出类型在流水线 caps 协商期间确定。<br /><br />`Type: String`<br />`Default: NULL`<br />`Flags: readable/writable`  |
| `labels`             | 标签文件的路径。文件直接传递给模块，插件不做解释。支持 JSON 和换行分隔格式。<br /><br />`Type: String`<br />`Default: NULL`<br />`Flags: readable/writable`                |
| `results`            | 限制输出结果的数量。如果检测结果超过此值，插件会自动丢弃置信度较低的结果。<br /><br />`Type: Integer`<br />`Default: 5`<br />`Range: 0 - 50`<br />`Flags: readable/writable` |
| `settings`           | JSON 字符串或指向 JSON 文件的路径，其中包含特定于模块的配置，例如置信度阈值、关键点或其他参数。<br /><br />`Type: String`<br />`Default: NULL`<br />`Flags: readable/writable`    |
| `bbox-stabilization` | 启用边界框（bbox）稳定功能，以减少帧间抖动。<br /><br />`Type: Boolean`<br />`Default: false`<br />`Flags: readable/writable`                               |

### 标签文件示例

#### 换行分隔格式

```
tench
goldfish
great white shark
tiger shark
hammerhead
electric ray
stingray
cock
hen
ostrich
```

#### JSON 格式

```json theme={null}
[
  {"id": 3, "color": "0x00FF00FF", "label": "tiger shark"},
  {"id": 4, "color": "0x00FF00FF", "label": "hammerhead"},
  {"id": 5, "color": "0x00FF00FF", "label": "electric ray"},
  {"id": 6, "color": "0x00FF00FF", "label": "stingray"},
  {"id": 17, "color": "0x00FF00FF", "label": "jay"}
]
```

### 设置示例（姿态估计）

```json theme={null}
{
  "confidence": 51.0,
  "connections": [
    {"id": 5, "connection": 6},
    {"id": 6, "connection": 12},
    {"id": 7, "connection": 5},
    {"id": 8, "connection": 6}
  ]
}
```

**qtimlpostprocess** 元素的输出可以是以下格式之一：

* **文本（text）** - 后处理插件将 ML 元数据序列化为文本。该元数据既可以被其他插件直接使用，也可以通过 qtimetamuxer 附加到源流。
* **图像掩码（image mask）** - 后处理插件可以生成带有叠加文本、边界框、点、线和其他视觉元素的图像掩码。这是一个仅包含 ML 结果的透明帧。例如，如果后处理类型是目标检测，插件将绘制带标签的边界框。随后可使用 qtivcomposer 插件将图像掩码贴合（blit）到源视频流上。
* **张量（tensor）** - 后处理插件可以生成张量。当一个推理阶段的输出张量需要传递给下一阶段但张量形状不完全匹配时，这非常有用。例如，第一阶段可能生成四个输出张量，而下一阶段可能只需要其中三个。

输出格式在 GStreamer 流水线 caps 协商期间选择。大多数可设置的格式通常会自动协商，但开发者可以通过 GStreamer caps filter 手动指定。该插件仅支持一个 source pad。如果流水线同时需要两种或更多受支持的格式，则应在流水线中添加并执行两次后处理插件。

# 后处理模块

**后处理模块**仅负责张量解析并输出预测结果列表。每个后处理模块实现针对特定模型类别定制的解析逻辑。例如，一个模块负责所有变体的 YoloV8 检测模型。插件负责管理模块的执行、输出（ML 元数据或图像掩码）的生成、批处理、级联 AI 模型以及其他相关任务。

后处理模块当前支持以下输出数据类型：

* object-detection
* image-classification
* image-segmentation
* super-resolution
* pose-estimation
* audio-classification
* tensor

后处理模块是可加载的实体。IM SDK 提供了一整套后处理模块，但应用开发者也可以编写自己的自定义模块并将其部署到设备上。每个模块构建为共享库。

所有后处理模块共享库必须部署在目标设备上的专用文件夹中，通常为：

```
/usr/lib/gstreamer-1.0/ml/modules
```

当后处理模块部署在此位置时，**qtimlpostprocess** 元素会自动检测受支持的后处理模块。

下面列出了当前支持的 AI 模块。

<AccordionGroup>
  <Accordion title="图像分类">
    * `mobilenet-softmax`
    * `mobilenet`
    * `ocr-recognizer`
    * `ocr`
    * `qfr-softmax`
    * `qfr`
  </Accordion>

  <Accordion title="目标检测">
    * `easy-textdt`
    * `easy-ocr-detector`
    * `mediapipe-pose`
    * `qfd`
    * `qpd`
    * `ssd-mobilenet`
    * `yolo-nas`
    * `yolov5`
    * `yolov8`
    * `palmd`
  </Accordion>

  <Accordion title="语义分割">
    * `deeplab-argmax`
    * `yolov8-seg`
  </Accordion>

  <Accordion title="深度估计">
    * `midas-v2`
  </Accordion>

  <Accordion title="姿态估计">
    * `hrnet`
    * `lite-3dmm`
    * `posenet`
    * `hlandmark`
    * `mediapipe-pose-landmark`
  </Accordion>

  <Accordion title="超分辨率">
    * `srnet`
  </Accordion>

  <Accordion title="音频分类">
    * `wave2vec`
    * `yamnet`
  </Accordion>

  <Accordion title="张量生成">
    * `tensor`
  </Accordion>
</AccordionGroup>

上述所有类别支持的模型可在[支持的模型](/zh/supportedmodels/)部分中查看。

\*\*重要提示：\*\*一个后处理模块支持多个 ML 模型是非常常见的。例如：

* yolov8 模块可同时用于 YoloV8 和 YoloX 模型，因为两个 Yolo 模型具有相同的输出，需要相同的后处理实现。YoloV3 和 YoloV5 也是如此。
* mobilenet 模块可用于 MobileNet、ResNet 和其他图像分类 ML 模型，因为分类后处理在各种 ML 模型中非常通用。

受支持的后处理模块列表及其支持的输入张量形状和数据类型，可以直接在设备上查看。
要查看受支持模块的完整列表，请使用以下命令：

```
gst-inspect-1.0 qtimlpostprocess
```

当部署或移除新的后处理模块时，此信息会立即更新。

示例输出：

```
module              : Module name that is going to be used for processing the tensors
                      flags: readable, writable
                      Enum "GstMLPostProcessModules" Default: 0, "none"
                         (0): none             - No module, default invalid mode
                         (1): ssd-mobilenet    -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 10, 4
                                Tensor 1: 1, 10
                                Tensor 2: 1, 10
                                Tensor 3: 1
 
                         (2): hrnet            -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 1-256, 1-256, 1-17
 
                         (3): srnet            -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 32-4096, 32-4096
                                Type: FLOAT32
                                Tensor 0: 1, 32-4096, 32-4096, 1-3
 
                         (4): yolov8-seg       -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 4
                                Tensor 1: 1, 21-42840
                                Tensor 2: 1, 21-42840, 1-32
                                Tensor 3: 1, 21-42840
                                Tensor 4: 1, 1-32, 32-2048, 32-2048
 
                         (5): posenet          -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 5-251, 5-251, 1-17
                                Tensor 1: 1, 5-251, 5-251, 2-34
                                Tensor 2: 1, 5-251, 5-251, 4-64
 
                         (6): east-textdt      -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 8-480, 8-480, 1-5
                                Tensor 1: 1, 8-480, 8-480, 1-5
 
                         (7): qfr              -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 512
                                Tensor 1: 1, 32
                                Tensor 2: 1, 2
                                Tensor 3: 1, 2
                                Tensor 4: 1, 2
                                Tensor 5: 1, 2
 
                         (8): deeplab-argmax   -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 32-2048, 32-2048
                                Type: FLOAT32
                                Tensor 0: 1, 32-2048, 32-2048, 1-21
 
                         (9): yolov8           -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 4
                                Tensor 1: 1, 21-42840
                                Tensor 2: 1, 21-42840
                                Type: FLOAT32
                                Tensor 0: 1, 4, 21-42840
                                Tensor 1: 1, 1-1001, 21-42840
                                Type: FLOAT32
                                Tensor 0: 1, 5-1005, 21-42840
 
                         (10): mobilenet        -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 1000-1001
 
                         (11): lite-3dmm        -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 512
                                Tensor 1: 1, 265
                                Type: FLOAT32
                                Tensor 0: 1, 265
 
                         (12): ocr              -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 26, 1, 37
                                Type: FLOAT32
                                Tensor 0: 1, 26-48, 37
 
                         (13): yolov5           -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 1-136, 1-136, 18-3018
                                Tensor 1: 1, 1-136, 1-136, 18-3018
                                Tensor 2: 1, 1-136, 1-136, 18-3018
                                Type: FLOAT32
                                Tensor 0: 1, 3, 1-136, 1-136, 6-85
                                Tensor 1: 1, 3, 1-136, 1-136, 6-85
                                Tensor 2: 1, 3, 1-136, 1-136, 6-85
                                Type: FLOAT32
                                Tensor 0: 1, 21-72828, 6-85
 
                         (14): mobilenet-softmax -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 1000-1001
 
                         (15): yolo-nas         -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 4
                                Tensor 1: 1, 21-42840
                                Tensor 2: 1, 21-42840
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 2
                                Tensor 1: 1, 21-42840, 2
                                Tensor 2: 1, 21-42840, 81
                                Type: FLOAT32
                                Tensor 0: 1, 5-1005, 21-42840
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 1-1001
                                Tensor 1: 1, 21-42840, 4
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 4
                                Tensor 1: 1, 21-42840, 1-1001
 
                         (16): qfd              -
                              Supported tensors:
                                Type: UINT8, FLOAT32
                                Tensor 0: 1, 60, 80, 1
                                Tensor 1: 1, 60, 80, 1
                                Tensor 2: 1, 60, 80, 10
                                Tensor 3: 1, 60, 80, 4
                                Type: UINT8, FLOAT32
                                Tensor 0: 1, 120, 160, 1
                                Tensor 1: 1, 120, 160, 10
                                Tensor 2: 1, 120, 160, 4
                                Type: UINT8, FLOAT32
                                Tensor 0: 1, 60, 80, 4
                                Tensor 1: 1, 60, 80, 10
                                Tensor 2: 1, 60, 80, 1
                                Type: UINT8, FLOAT32
                                Tensor 0: 1, 60, 80, 1
                                Tensor 1: 1, 60, 80, 4
                                Tensor 2: 1, 60, 80, 10
 
                         (17): yamnet           -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 521
 
                         (18): midas-v2         -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 256, 256, 1
                                Type: FLOAT32
                                Tensor 0: 1, 256, 256
 
                         (19): qfr-softmax      -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 512
                                Tensor 1: 1, 32
                                Tensor 2: 1, 2
                                Tensor 3: 1, 2
                                Tensor 4: 1, 2
                                Tensor 5: 1, 2
 
                         (20): qpd              -
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 120, 160, 3
                                Tensor 1: 1, 120, 160, 12
                                Tensor 2: 1, 120, 160, 34
                                Tensor 3: 1, 120, 160, 17
```

以 yolov8 后处理模块为例：

```
(9): yolov8           -
    Supported tensors:
      Type: FLOAT32
      Tensor 0: 1, 21-42840, 4
      Tensor 1: 1, 21-42840
      Tensor 2: 1, 21-42840
      Type: FLOAT32
      Tensor 0: 1, 4, 21-42840
      Tensor 1: 1, 1-1001, 21-42840
      Type: FLOAT32
      Tensor 0: 1, 5-1005, 21-42840
```

**1. 具有三个输出张量的模型**

这些模型生成三个独立的张量。第二个维度是动态的，取决于训练时使用的类别数量。这种灵活性使单个后处理模块能够支持具有不同类别配置的多个 YOLOv8 模型。

示例形状：

* 1, (21–42840), 4
* 1, (21–42840)
* 1, (21–42840)

**2. 具有两个输出张量的模型**

这些模型将边界框和分类数据组合在两个张量中。

示例形状：

* 1, 4, (21–42840)
* 1, (1–1001), (21–42840)

**3. 具有一个输出张量的模型**

这些模型将所有相关数据输出在单个张量中。

示例形状：

* 1, (5–1005), (21–42840)

*所有情况下数据格式均为 Float32。*

## 批处理与级联（Daisy Chaining）

IM SDK 支持**批量模型**和**级联（daisy chaining）**（顺序执行模型）等高级功能。这些复杂任务由 SDK 自动处理，使后处理模块代码保持通用，仅专注于核心后处理逻辑。

**示例：**

* 如果模型的批量大小为 4，**qtimlpostprocess** 元素将执行**后处理模块**四次 — 批量中的每个项目各一次。这意味着同一个模块既可用于简单场景（一次处理一帧），也可用于更复杂的场景（并行处理多个源）。

* 在顺序模型设置中，第一个模型检测目标，第二个模型执行姿态估计，此时第二个模型会针对第一个模型检测到的每个目标执行一次。在这种情况下，**IM SDK** 负责处理为每个推理结果调用**后处理模块**并将输出映射回原始帧的复杂性。

## AI 后处理用例概述

IM SDK（Qualcomm 智能多媒体 SDK）是一个框架，为终端应用构建 AI、多媒体和 CV 流水线提供必要的构建模块。构建 AI 工作流需要三个组件 / GStreamer 插件。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_aipostprocess.png" alt="AI Post Process overview" />

1. 预处理元素将数据流转换为张量。
2. 推理元素在 AI 模型上执行推理，并最终对输出张量应用反量化。除反量化之外，不涉及其他额外的预处理或后处理。
3. 后处理是一个解析张量并创建包含 ML 元数据或图像掩码的缓冲区的插件。IM SDK 可以以两种不同方式处理 ML 元数据：既可以通过 qtimetamuxer 将其附加到源流，也可以直接使用并流式传输到 RTSP、RTMP、Redis 等。图像掩码可以通过 qtivcomposer 叠加到源视频帧上。

示例图例：

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_aipostprocess_example_legend.png" alt="AI Post Process Legend" />

示例 1：直接使用 ML 元数据（推理插件之后不再传播源流）：

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_aipostprocess_example1.png" alt="AI Post Process Example 1" />

示例 2：将 ML 元数据附加到源视频。叠加层使用附加的 ML 元数据绘制边界框、文本和其他视觉元素。结果可以显示在屏幕上，也可以通过网络流式传输。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_aipostprocess_example2.png" alt="AI Post Process Example 1" />

示例 3：将 ML 元数据转换为图像掩码，然后将其贴合（blit）到源流之上。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/SDKs/IMSDK/plugin-reference/images/qtimlpostprocess_aipostprocess_example3.png" alt="AI Post Process Example 1" />

## 编写自定义后处理模块的指南

如果找不到适合你的 AI 模型的后处理模块，可以自行实现。你可以完全独立于 IM SDK 构建后处理模块 — 只需要接口头文件和工具链。模块构建完成后，应部署到设备上的以下位置：/usr/lib/gstreamer-1.0/ml/modules/。后处理插件会自动检测到它，用户即可在 GStreamer 流水线中选择使用。

[后处理模块头文件](https://git.codelinaro.org/clo/le/platform/vendor/qcom-opensource/gst-plugins-qti-oss/-/blob/imsdk.lnx.2.0.0/gst-plugin-mlpostprocess/modules/qti-ml-post-process.h?ref_type=heads)

### 模块/库命名

后处理模块共享库必须遵循以下命名约定：libml-postprocess-\<module-name>.so。这是为了避免后处理模块名称重复。例如，YoloV8 模块的共享库应命名为 libml-postprocess-yolov8.so。在配置后处理插件时使用相同的 \<module-name>，例如：**module=yolov8**。

```bash theme={null}
gst-launch-1.0 -e --gst-debug=2 filesrc location=$HOME/models/Draw_1080p_180s_30FPS_1_ref.mp4 ! qtdemux ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! qtimlvconverter ! qtimltflite name=inference delegate=external external-delegate-path=libQnnTFLiteDelegate.so external-delegate-options="QNNExternalDelegate,backend_type=htp;" model=$HOME/models/yolov8_det_quantized.tflite ! qtimlpostprocess **module=yolov8** labels=$HOME/labels/yolov8_json.labels ! filesink location=$HOME/data/ml-results.txt
```

### AI 后处理模块接口

```
class IModule {
 public:
  virtual ~IModule() {};

  virtual std::string Caps() = 0;

  virtual bool Configure(const std::string& labels_file, const std::string& json_settings) = 0;

  virtual bool Process(const Tensors& tensors, Dictionary& mlparams, std::any& output) = 0;
};
```

**后处理模块**暴露 C++ API。由于 C++ 类无法直接从共享库实例化，类的创建被封装在一个 C 风格的函数中。模块开发者必须在源文件中包含以下代码：

```
IModule* NewModule(LogCallback logger) {
  return new Module(logger);
}
```

开发者只需在派生自 IModule 接口的 Module 类中实现以下 API：

* **构造函数 / 析构函数** - 用于初始化和清理。
* **Caps** - 此函数必须返回模块类型（例如图像分类、目标检测）、支持的张量维度以及支持的数据类型（例如 uint8、float32）。
* **Configuration** - 在初始化期间调用一次。处理标签文件和配置文件。
* **Process** - 每次推理后调用。在这里将张量转换为受支持格式之一的预测结果。

**std::string Caps()**

此 API 以 JSON 字符串形式返回模块类型和支持的张量形状。张量形状不是固定的，而是在一个范围内定义，使用方括号表示。例如，\[1, \[21, 42840], 4] 表示第二个维度可以在 21 到 42840 之间变化。

后处理模块能力定义示例。此示例中模块实现"object-detection"后处理。支持三种不同的张量输出：一个张量、两个张量、三个张量。此示例中支持的张量格式仅为 FLOAT32。

```
static const char* kModuleCaps = R"(
{
  "type": "object-detection",
  "tensors": [
    {
      "format": ["FLOAT32"],
      "dimensions": [
        [1, [21, 42840], 4],
        [1, [21, 42840]],
        [1, [21, 42840]]
      ]
    },
    {
      "format": ["FLOAT32"],
      "dimensions": [
        [1, 4, [21, 42840]],
        [1, [1, 1001], [21, 42840]]
      ]
    },
    {
      "format": ["FLOAT32"],
      "dimensions": [
        [1, [5, 1005], [21, 42840]]
      ]
    }
  ]
}
)";
```

支持的后处理模块类型：

* object-detection
* image-classification
* image-segmentation
* super-resolution
* pose-estimation
* audio-classification
* tensor

支持的张量类型：

* FLOAT32
* FLOAT16
* INT8
* UINT8
* INT16
* UINT16
* INT32
* UINT32
* INT64
* UINT64

可以同时指定多种格式。示例：

```
...
     {
      "format": ["FLOAT32", "INT8"],
      "dimensions": [
        [1, 4, [21, 42840]],
        [1, [1, 1001], [21, 42840]]
      ]
    },
...
```

**bool Configure(const std::string& labels\_file, const std::string& json\_settings)**

* **labels** -（可选）保存包含标签的文件路径的字符串。如果用户未提供标签文件，该字符串保持为空。标签文件可以是任何格式。IM SDK 包含针对换行分隔标签和 JSON 格式标签的解析器。
* **settings** -（可选）包含特定于模块设置的 JSON 字符串。这些设置由用户通过后处理 GStreamer 插件的 settings 属性提供。如果用户未提供任何设置，则为空。

**bool Process(const Tensors& tensors, Dictionary& mlparams, std::any& output)**
模块的输入包括：张量、张量形状、输入张量的填充方式信息。输出应为受支持格式之一的预测结果列表

* object-detection
* image-classification
* image-segmentation
* super-resolution
* pose-estimation
* audio-classification
* tensors

<Note>
  张量输出是一种特殊情况，此时后处理插件和模块生成的是张量而不是预测结果。当两个 ML 模型级联在一起、第一个模型的输出张量在传递给下一个模型之前需要修改时使用此方式。如果输出张量不需要修改，则两个推理插件可以直接前后相连，此时不需要后处理插件。
</Note>

#### 理解后处理模块输入

输入分为两个字段：

1. **tensor** – 此字段保存推理输出张量并描述其结构。每个输出张量表示为向量中的一个条目。例如，对于生成三个输出张量（框、分数、类别索引）的 YOLOv8，向量将包含三个条目。
   * type – float、uint8 等
   * name - 张量名称。当两个或多个输出张量具有相同形状时，此字段非常有用。张量名称是唯一的，可保证选择到确切的张量。
   * dimensions – 此字段描述张量的形状。例如，具有三个输出张量的 YoloV8：\[1,8400,4]、\[1,8400]、\[1,8400]
   * data – 指向张量的指针
2. **mlparams** – 张量处理可能需要的附加参数。这些参数并不适用于所有子模块。此字段还提供有关输入流处理方式的信息，这一点尤其重要，因为流的分辨率和宽高比通常与输入张量的形状不匹配。此字段是使用 std::any 实现的字典。模块开发者必须知道预期的键及其对应的返回类型。使用 std::any 可确保返回值与给定键关联的类型匹配。使用示例：

```
video::Region& region =
    std::any_cast<video::Region&>(mlparams["input-tensor-region"]);
```

支持的键：

* 键："input-tensor-region"<br />
  类型：video::Region<br />
  描述：此参数指示输入张量的哪一部分填充了来自流的实际数据。其余区域被视为填充（padding）<br />

* 键："input-tensor-dimensions"<br />
  类型：video::Resolution<br />
  描述：指定输入张量的大小。当后处理算法以绝对坐标生成输出时非常有用。由于后处理模块必须输出相对坐标，因此需要输入张量大小来将绝对值转换为相对值。<br />

#### 生成后处理模块输出

输出是结果数组的数组。数组嵌套是为了应对批处理情况。如果没有批处理，则只填充内层数组。内层数组的大小与找到的结果数量一致。结果始终使用相对尺寸。结果类型取决于模块类型：

1. **图像/音频分类**
   * Name – 类别标签。图像/音频所属的预测类别或类。
   * Confidence – 类别概率 / 置信度分数
   * Color – 用于在叠加插件中可视化的 RGBA8888 颜色
   * Xtraparams –（可选）#Dictionary（键/值对）形式的附加参数，用户可以从模块导出任意额外结果并传递给下游。
2. 目标检测：
   * Left、top、right、bottom – 边界框坐标
   * Name – 类别标签。图像/音频所属的预测类别或类。
   * Landmarks –（可选）关键点列表。例如，人脸检测模型可以在输出边界框的同时输出人脸关键点。
   * Confidence – 类别概率 / 置信度分数
   * Color – 用于在叠加插件中可视化的 RGBA8888 颜色
   * Xtraparams –（可选）#Dictionary（键/值对）形式的附加参数，用户可以从模块导出任意额外结果并传递给下游。
3. 姿态估计：
   * Name – 类别标签。图像/音频所属的预测类别或类。
   * Confidence – 类别概率 / 置信度分数
   * Keypoints – 关键点向量
   * Links –（可选）关键点之间的连接向量。
   * Color – 用于在叠加插件中可视化的 RGBA8888 颜色
   * Xtraparams –（可选）#Dictionary（键/值对）形式的附加参数，用户可以从模块导出任意额外结果并传递给下游。
4. 图像分割 / 超分辨率：
   * 输出为图像帧/掩码
5. 张量
   * 张量列表

### 模块辅助工具

作为接口头文件的一部分，我们还提供了标签解析器和 JSON 解析器。用户没有义务使用它们，仅为方便而提供。开发者可以使用任何标签和/或 JSON 解析器，但模块必须与其静态链接。

**标签解析器** – 该解析器支持两种格式。标签解析器接收标签文件的路径并自动检测格式：

* 换行分隔格式。行号即类别 id。
* JSON 格式。此格式应设置类别索引、标签和可视化颜色。此格式更灵活，因为用户可以只传递部分类别，其余类别将被自动过滤掉。

**JSON 解析器** – 设置以 JSON 字符串传递，因此该工具可用于解析设置。该实现也在我们的标签解析器中用于处理 JSON 格式。

### 日志记录

后处理模块可以在不直接依赖 GStreamer 的情况下将日志输出到 GStreamer 日志系统。日志对象通过构造函数传递给模块。该对象与 LOG 宏一起，可用于将日志直接输出到 GStreamer 日志。支持的日志级别包括：Error、Warning、Info、Debug、Trace 和 Log。

LOG 宏：

```
#define LOG(logger, level, fmt, ...)
```

日志使用示例：

```
LOG(logger_, kError, "ML frame with unsupported post-processing procedure!");
LOG(logger_, kLog, "Threshold: %f", threshold_);
```

## 如何独立编译后处理模块

前提条件：Ubuntu22.04 或 Ubuntu24.04 PC

1. 安装工具

```
sudo apt-get install g++-aarch64-linux-gnu
sudo apt-get install cmake
```

2. 将 IMSDK 头文件和模块源代码放在同一个文件夹中。

```
ml-postprocess-yolov8.cc
ml-postprocess-yolov8.h
qti-json-parser.h
qti-labels-parser.h
qti-ml-post-process.h
```

3. 创建 CMakeLists.txt 文件。示例：

```
cmake_minimum_required(VERSION 3.8.2)
project(QTI_OSS_ML_MODULES LANGUAGES C CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Common compiler flags.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter")

include_directories(
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(TARGET_NAME ml-postprocess-yolov8)

add_library(${TARGET_NAME} SHARED
  ml-postprocess-yolov8.cc
)
```

<Note>
  后处理模块共享库必须遵循命名约定：libml-postprocess-\<module-name>.so
  例如，YoloV8 模块的共享库应命名为 libml-postprocess-yolov8.so
</Note>

4. 创建工具链文件，例如 aarch64-toolchain.cmake。示例：

```
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_CXX_FLAGS "-march=armv8-a")
```

5. 配置并构建项目

```
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake ..
cmake --build .
```

## 如何部署和测试后处理模块

1. 将模块部署到设备

```
scp libml-postprocess-yolov8.so  <user>@<device-ip>:/usr/lib/gstreamer-1.0/ml/modules/
```

2. 运行 GST inspect 并检查你的模块是否出现在受支持模块列表中。你应能在受支持模块列表中看到你的后处理模块及其支持的张量形状。

```
gst-inspect-1.0 qtimlpostprocess
```

3. 拥有后处理模块后，你需要构建 GStreamer 流水线。必须通过 qtimlpostprocess 插件的 module 属性选择你的后处理模块。如果你的模块需要标签文件或配置，必须相应地通过 label 和 settings 属性传递。<br />
   下面是运行 YOLOv8 模型的示例流水线。使用离线视频作为视频源。视频通过 v4l2h264dec 解码器解码为 YUV 格式。YUV 帧由 qtimlvconverter 插件进行预处理。使用 qtimltflite 插件运行 TensorFlow Lite YOLOv8 模型的推理。后处理插件加载 YOLOv8 模块并传入 JSON 格式的标签文件。ML 结果保存到文件中。

```bash theme={null}
gst-launch-1.0 -e --gst-debug=2 filesrc location=&lt;Path to mp4 file&gt; ! qtdemux ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! qtimlvconverter  ! qtimltflite name=inference delegate=external external-delegate-path=libQnnTFLiteDelegate.so external-delegate-options="QNNExternalDelegate,backend_type=htp;" model=$HOME/models/yolov8_det_quantized.tflite ! qtimlpostprocess module=yolov8 labels=$HOME/labels/yolov8_json.labels ! filesink location=$HOME/data/ml-results.txt
```
