> ## 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 插件为 Qualcomm IM SDK 管线添加自定义模型后处理支持。

本指南描述了如何在 Qualcomm IM SDK 管线中添加模型后处理支持。
当 Qualcomm IM SDK 插件不支持某个模型的后处理时,
这是必要的。

<Note>
  有关后处理在管线中的作用背景,请参阅 [IM SDK 概述](../topic/develop-your-own-application-im-sdk)。有关完整的 `qtimlpostprocess` 参考和自定义插件构建详细信息,请参阅 [Discover SDKs → IM SDKs](https://imsdkdocs.qualcomm.com/plugin-reference/introduction)。
</Note>

本节涵盖以下主题。

1. [AI IM SDK 管线概述](#ai-im-sdk-pipeline)。
2. [qtimlpostprocess 插件简介](#postprocessing-plugin-introduction)。
3. [如何编写后处理模块](#write-postprocessing-module)。
4. [如何编译后处理模块](#compile-postprocessing-module)。
5. [如何部署和测试后处理模块](#deploy-test-postprocessing-module)。

本示例说明了向 `qtimlpostprocess` 插件添加自定义 YOLOv8 模型后处理的步骤。

下图显示了添加自己的后处理模型的流程,
从开发和集成模型到运行参考应用程序。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/add-postprocessing-custom-model.png" alt="向 Qualcomm IM SDK 添加自定义模型后处理的流程" />

<h2 id="ai-im-sdk-pipeline">
  AI IM SDK 管线概述
</h2>

Qualcomm Intelligent Multimedia SDK (IM SDK) 包含构建 AI、多媒体和
计算机视觉管线所需的构建模块,
用于构建应用程序。

使用 IM SDK 构建 AI 工作流涉及三个关键的 GStreamer 插件。

1. 预处理元素:将传入的数据流转换为适合 AI 推理的
   张量格式。
2. 推理元素:使用 AI 模型执行推理,并对输出张量应用
   反量化。除反量化之外,此元素不执行任何
   预处理或后处理。
3. 后处理元素:解析输出张量并生成包含机器学习元数据的
   缓冲区。此元素以以下方式之一输出
   元数据。

* 使用 `qtimetamuxer` 将其附加到源流
* 将其直接流式传输到 RTSP、RTMP 或 Redis 等端点。
* 作为图像掩码,使用 `qtivcomposer` 叠加到源视频帧上。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/ai-im-sdk-pipelines.png" alt="包含预处理、推理和后处理元素的 Qualcomm IM SDK AI 管线" />

### 示例:直接使用 ML 元数据

在以下示例中,源流在推理插件之后不再传播。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/ai-im-sdk-example-direct-metadata.png" alt="IM SDK 管线示例:直接使用 ML 元数据,不传播源流" />

### 示例:将 ML 元数据附加到源视频

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

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/ai-im-sdk-example-attached-metadata.png" alt="IM SDK 管线示例:ML 元数据附加到源视频流" />

### 示例:将 ML 元数据转换为图像掩码

在以下示例中,ML 元数据被转换为图像掩码,然后
叠加 (blit) 到源流之上。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/ai-im-sdk-example-mask-metadata.png" alt="IM SDK 管线示例:ML 元数据转换为叠加在源流上的图像掩码" />

<h2 id="postprocessing-plugin-introduction">
  IM SDK 中的 AI 后处理插件简介
</h2>

`qtimlpostprocess` 是一个可自定义的插件,为推理插件的张量输出后处理
提供库接口。后处理库
负责张量解析,并输出预测结果列表。

后处理 (PP) 模块处理一种类型的机器学习 (ML) 模型。
每个 PP 模块处理特定类型的模型及其变体,例如
所有 YOLOv8 检测模型变体。该插件负责管理模块的执行、
输出生成 (ML 元数据或图像掩码)、批处理、ML 暂存以及其他相关任务。

下图显示了输入、输出、后处理
模块和后处理插件之间的关系。

<img src="https://mintlify.s3.us-west-1.amazonaws.com/qualcomm-prod/zh/AI-Developer-Workflow-Ubuntu/_images/add-postprocessing-support.png" alt="显示后处理模块输入和输出的图片。" />

后处理插件支持以下模型类型:

* 目标检测
* 图像分类
* 图像分割
* 超分辨率
* 姿态估计
* 音频分类

后处理插件接收张量列表作为输入。
这些张量封装在 GST Buffer 中。机器学习元数据附加到
每个缓冲区,指定诸如张量数量、张量形状、模型输入
张量形状、每个输入张量中有多少被流数据填充、
时间戳和批处理索引等详细信息。

后处理插件可以生成以下格式之一:

* 文本:后处理插件将机器学习元数据序列化为文本。此
  元数据可被其他插件直接使用,或使用
  `qtimetamuxer` 附加到源流。

* 图像掩码:后处理插件可以生成带有叠加文本、
  边界框、点、线和其他可视元素的图像掩码。这是一个仅包含
  机器学习结果的透明帧。

  例如,如果后处理类型是目标检测,插件会绘制带标签的
  边界框。然后,`qtivcomposer` 插件可以将图像掩码叠加到
  源视频流上。

* 张量:后处理插件可以生成张量。当下一个推理
  阶段需要当前推理阶段的输出张量,但张量形状
  不完全匹配时使用此格式。

  例如,第一阶段产生四个输出张量,而下一阶段
  需要其中三个。

GStreamer 管线的 caps 协商决定输出格式。系统会自动协商
最合适的格式,但您可以使用 GStreamer caps 过滤器手动指定。

该插件仅支持一个源 pad。如果管线需要同时使用两种或更多
支持的格式,请在管线中添加并运行后处理插件
两次。

后处理插件配置由以下内容组成 (GStreamer 属性):

* Module:(必需)后处理模块名称。此 GStreamer 属性指定如何
  解析张量。它不定义插件输出类型。输出类型在
  管线 caps 协商期间确定。

* Settings:(可选)JSON 字符串或 JSON 文件的路径。此配置仅适用
  于模块而非插件。它向后处理模块传递任意配置,
  因为每个模块都有特定需求。

  例如,使用它传递置信度阈值 (confidence-threshold)、关键点、NMS 阈值和 token。

* Labels:(可选)标签文件的路径。您可以将标签文件的路径直接传递
  给模块,使用换行分隔的标签列表、JSON 格式的标签
  或自定义格式。前两种格式的解析器已在头文件中提供,
  对于自定义格式,您可以在后处理模块中实现自己的解析器。

* Results:(可选)例如,如果模型检测到 7 个结果但最多允许 4 个,则
  会丢弃置信度得分最低的 3 个结果。此功能由插件实现,因此
  模块开发者无需自行处理。

<h2 id="write-postprocessing-module">
  为自定义模型编写后处理模块
</h2>

后处理模块是一个共享库,用于解析推理插件的张量输出。
后处理 GST 插件 (`qtimlpostprocess`) 加载并运行该模块。IM SDK
提供了种类丰富的开箱即用后处理模块:

* image-detection (yolov5、yolov8、yolonas、ssd-mobilenet、qfd、qpd、east-textdt)
* classification (mobilnet、mobilenet-softmax)
* pose-estimation (hrnet、lite-3dmm、posenet)
* segmentation (deeplab-argmax、midas-v2)
* super-resolution (srnet)

使用 `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): mediapipe-pose   - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 896, 12
                                Tensor 1: 1, 896, 1

                         (2): ocr              - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 26, 1, 37
                                Type: FLOAT32
                                Tensor 0: 1, 26-48, 37

                         (3): midas-v2         - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 256-518, 256-518, 1
                                Type: FLOAT32
                                Tensor 0: 1, 256-518, 256-518

                         (4): 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

                         (5): 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

                         (6): srnet            - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 32-4096, 32-4096
                                Type: FLOAT32
                                Tensor 0: 1, 32-4096, 32-4096, 1-3

                         (7): wave2vec         - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 124, 32

                         (8): mobilenet-softmax - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 2-2000

                         (9): ssd-mobilenet    - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 10, 4
                                Tensor 1: 1, 10
                                Tensor 2: 1, 10
                                Tensor 3: 1
                                Type: FLOAT32
                                Tensor 0: 1, 10
                                Tensor 1: 1, 10, 4
                                Tensor 2: 1, 10
                                Tensor 3: 1
                                Tensor 4: 1, 10
                                Type: FLOAT32
                                Tensor 0: 1, 100
                                Tensor 1: 1
                                Tensor 2: 1, 100, 4
                                Tensor 3: 1, 100
                                Type: FLOAT32
                                Tensor 0: 1, 25, 4
                                Tensor 1: 1, 25
                                Tensor 2: 1, 25
                                Tensor 3: 1

                         (10): easy-ocr-detector - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 8-480, 8-480, 1-5

                         (11): tensor           - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 63
                                Tensor 1: 1, 1
                                Tensor 2: 1, 1
                                Tensor 3: 1, 63

                         (12): deeplab-argmax   - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 32-2048, 32-2048
                                Type: FLOAT32
                                Tensor 0: 1, 32-2048, 32-2048, 1-150

                         (13): mediapipe-pose-landmark - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1
                                Tensor 1: 1, 25, 4

                         (14): hlandmark        - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 63
                                Tensor 1: 1, 1
                                Tensor 2: 1, 1
                                Tensor 3: 1, 63

                         (15): 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
                                Type: FLOAT32
                                Tensor 0: 1, 17, 33, 17
                                Tensor 1: 1, 34, 33, 17
                                Tensor 2: 1, 32, 33, 17
                                Tensor 3: 1, 32, 33, 17
                                Tensor 4: 1, 17, 33, 17

                         (16): hrnet            - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 1-256, 1-256, 1-17

                         (17): palmd            - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 2016, 18
                                Tensor 1: 1, 2016, 1

                         (18): ocr-recognizer   - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 250, 1, 97
                                Type: FLOAT32
                                Tensor 0: 1, 26-250, 97

                         (19): mobilenet        - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 2-6440

                         (20): 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

                         (21): 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

                         (22): 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

                         (23): 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

                         (24): yamnet           - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 521

                         (25): 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

                         (26): lite-3dmm        - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 512
                                Tensor 1: 1, 265
                                Type: FLOAT32
                                Tensor 0: 1, 265

                         (27): east-textdt      - 
                              Supported tensors:
                                Type: FLOAT32
                                Tensor 0: 1, 8-480, 8-480, 1-5
                                Tensor 1: 1, 8-480, 8-480, 1-5

                         (28): 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, 32-2048, 32-2048, 1-32
                                Type: FLOAT32
                                Tensor 0: 1, 21-42840, 4
                                Tensor 1: 1, 21-42840
                                Tensor 2: 1, 21-42840, 1-32
                                Tensor 3: 1, 32-2048, 32-2048, 1-32

```

如果找不到适合您模型的后处理模块,您可以实现自己的模块。
您可以独立于 IM SDK 构建后处理模块。
要在不使用 IM SDK 的情况下构建后处理模块,您需要接口
头文件和一个工具链。构建模块后,将其部署到设备上的
`/usr/lib/aarch64-linux-gnu/imsdk/qtimlpostprocess/modules/`。

后处理插件会自动检测到它,用户可以在 GStreamer
管线中选择它。

### 模块与库命名

为避免后处理模块名称重复,后处理模块共享库
必须遵循 `libml-postprocess-<module-name>.so` 命名约定。

例如,YoloV8 模块的共享库必须命名为 `libml-postprocess-yolov8.so`。
在配置后处理插件时使用相同的 `<module-name>`。例如,`module=yolov8`。

### 运行 gstreamer 管线

```shell theme={null}
gst-launch-1.0 -e \
filesrc location=/etc/media/video1.mp4 ! qtdemux ! queue ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! queue ! tee name=split split. ! \
queue ! qtivcomposer name=mixer sink_1::dimensions="<1920,1080>" ! queue ! waylandsink fullscreen=true split. ! queue ! qtimlvconverter ! queue ! \
qtimltflite delegate=external external-delegate-path=libQnnTFLiteDelegate.so external-delegate-options="QNNExternalDelegate,backend_type=htp;" \
model=/etc/models/yolox_quantized.tflite ! queue ! qtimlpostprocess settings="{\"confidence\": 75.0}" results=10 module=yolov8 labels=/etc/labels/yolox.json \
! video/x-raw,format=BGRA,width=640,height=360 ! queue ! mixer.
```

### AI 后处理模块推理

AI 后处理模块暴露一个 C++ API。由于 C++ API 无法直接从共享库加载,
因此类的实例化被封装在一个 C 函数中。此机制已在头文件中实现,
所以您无需手动处理 C++ 类的实例化。您只需在模块类
(派生自 IModule 接口)中实现以下 API。

* 构造函数/析构函数:构造函数不接受任何参数,是开发者的
  通用入口点。

* `Caps()`:以 JSON 格式返回模块类型和支持的张量维度。

* `Configure()`:接受一个标签文件路径和一个包含模块特定设置的 JSON 字符串。
  用户通过后处理 GStreamer 插件的 settings 属性提供这些设置。

* `Process()`:解析输入张量并基于模型输出生成预测。

#### std::string Caps()

以 JSON 字符串形式返回模块类型和支持的张量形状。张量形状并非固定,
而是在一个范围内定义,用方括号表示。

例如,`[1, [21, 42840], 4]` 表示第二个维度可以在 `21` 和 `42840` 之间变化。

以下代码片段是后处理模块能力定义的示例。该示例实现
目标检测后处理,以 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\_file   | (可选)包含标签的文件的字符串路径。如果未提供,该字符串保持为空。                                           |
| -------------- | --------------------------------------------------------------------------- |
| json\_settings | (可选)包含模块特定设置的 JSON 字符串。用户通过后处理 GStreamer 插件的 settings 属性提供这些设置。如果未提供,则保持为空。 |

#### bool Process(const Tensors& tensors, Dictionary& mlparams, std::any& output)

**参数**

| tensors  | 张量形状以及输入张量的填充方式。          |                  |                      |                    |                  |                 |                      |         |
| -------- | ------------------------- | ---------------- | -------------------- | ------------------ | ---------------- | --------------- | -------------------- | ------- |
| mlparams | 用于张量处理的附加参数,可能并不适用于所有子模块。 |                  |                      |                    |                  |                 |                      |         |
| output   | 以支持格式之一表示的预测列表。           | object-detection | image-classification | image-segmentation | super-resolution | pose-estimation | audio-classification | tensors |

<Note>
  张量输出是一种特殊情况,后处理插件和模块生成的是张量
  而不是预测。当两个机器学习模型链接在一起,并且第一个模型的输出
  张量需要在传递给下一个模型之前进行修改时使用。

  如果输出张量不需要修改,两个推理插件可以直接
  一个接一个地链接,不需要后处理插件。
</Note>

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

后处理模块的输入分为两个字段:

* tensor:此字段保存推理输出张量并描述其结构。向量
  将每个输出张量表示为一个条目。例如,对于 YOLOv8,它产生
  三个输出张量(boxes、scores、class indices),向量包含四个条目。

  * Type:float、uint8 等。

  * Name:张量名称,当两个或多个输出张量具有相同形状时用于识别。
    张量名称是唯一的,可保证选择准确的张量。

  * Dimensions:描述张量形状。

    例如,具有三个输出张量的 YoloV8:

    `[1,8400,4], [1,8400], [1,8400]`

  * Data:指向张量的指针。

* mlparams:用于张量处理的附加参数,可能并不适用于所有子模块。
  此字段提供有关管线如何处理输入流的信息,在流的分辨率和
  纵横比与输入张量的形状不匹配时可提供帮助。

  此字段是使用 `std::any` 实现的字典。您必须知道预期的键
  及其对应的返回类型。使用 `std::any` 可确保返回值与
  给定键关联的类型相匹配。用法示例:

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

  **支持的键**

  * Key:"input-tensor-region"

    Type:video::Region

    描述:此参数指示输入张量中哪一部分被来自流的实际数据填充。
    其余区域被视为填充。

  * Key:"input-tensor-dimensions"

    Type:video::Resolution

    描述:指定输入张量的大小。当后处理算法以绝对坐标产生输出时,
    需要将绝对坐标转换为相对坐标,因为后处理模块必须
    输出相对坐标。

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

输出是一个由结果数组组成的数组。
数组嵌套是为了支持批处理用例。
在没有批处理的情况下,只填充内部数组。内部数组大小与找到的结果数量匹配。
结果始终以相对维度表示,结果类型取决于模块类型。

* 图像/音频分类

  * Name:类别标签;预测的图像/音频所属的类别或分类。
  * Confidence:类别概率或置信度得分。
  * Color:用于叠加插件中可视化的 RGBA8888 颜色。
  * Xtraparams:(可选)字典中的额外参数 (键/值对),用于从模块导出任意额外结果以传递到下游。

* 目标检测

  * Left、top、right、bottom:边界框坐标。
  * Name:类别标签;预测的图像/音频所属的类别或分类。
  * Landmarks:(可选)关键点列表;例如,人脸检测模型可以输出带边界框的人脸关键点。
  * Confidence:类别概率或置信度得分。
  * Color:用于叠加插件中可视化的 RGBA8888 颜色。
  * Xtraparams:(可选)字典中的额外参数 (键/值对),用于从模块导出任意额外结果以传递到下游。

* 姿态估计

  * Name:类别标签;预测的图像/音频所属的类别或分类。
  * Confidence:类别概率或置信度得分。
  * Keypoints:关键点的向量。
  * Links:(可选)关键点之间的连接向量。
  * Color:用于叠加插件中可视化的 RGBA8888 颜色。
  * Xtraparams:(可选)字典中的额外参数 (键/值对),用于从模块导出任意额外结果以传递到下游。

* 图像分割和超分辨率

  * 输出为图像帧/掩码。

* 张量

  * 张量列表。

### 批处理

后处理插件会自动将张量批次拆分为单个张量。
批处理由插件层处理,您无需处理批处理用例。

例如,如果批大小为 4,则会为每个批次自动调用模块 4 次。

### 模块辅助工具

标签和 JSON 解析器包含在接口头文件中。
您不必使用它们,但为方便起见提供了它们。
您可以使用任何标签或 JSON 解析器,但模块必须与其静态链接。

* 标签解析器:此解析器支持两种格式,接受标签文件的路径,并自动检测格式。

  * 换行分隔格式:行号即为类别 ID。
  * JSON 格式:您应在此格式中设置类别索引、标签和可视化颜色。

    此格式更为灵活,因为您可以只传递部分类别,其余类别将被自动过滤掉。

* JSON 解析器:设置以 JSON 字符串传递。此工具用于解析设置,在 JSON 格式的情况下,此实现
  也用于 Qualcomm 提供的标签解析器。

### 日志记录

后处理模块可以将日志输出到 GStreamer 日志系统,而无需直接依赖 GStreamer。
构造函数将一个日志对象传递给模块。
此对象连同 LOG 宏,可用于将日志直接输出到 GStreamer 日志。

支持的日志级别包括:Error、Warning、Info、Debug、Trace 和 Log。

LOG 宏:

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

日志记录用法示例:

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

<h2 id="compile-postprocessing-module">
  在主机上编译后处理模块
</h2>

**前提条件**

* Ubuntu 22.04 或 Ubuntu 24.04 主机。

<Steps>
  <Step title="安装所需工具">
    ```
    sudo apt-get install g++-aarch64-linux-gnu
    ```

    ```
    sudo apt-get install cmake
    ```
  </Step>

  <Step title="从 CodeLinaro 下载所需的 .h 和 .cc 文件">
    * [qti-json-parser.h](https://github.com/qualcomm/gst-plugins-imsdk/blob/main/gst-plugin-mlpostprocess/modules/qti-json-parser.h)
    * [qti-labels-parser.h](https://github.com/qualcomm/gst-plugins-imsdk/blob/main/gst-plugin-mlpostprocess/modules/qti-labels-parser.h)
    * [qti-ml-post-process.h](https://github.com/qualcomm/gst-plugins-imsdk/blob/main/gst-plugin-mlpostprocess/modules/qti-ml-post-process.h)
    * [ml-postprocess-yolov8.h](https://github.com/qualcomm/gst-plugins-imsdk/blob/main/gst-plugin-mlpostprocess/modules/object-detection/ml-postprocess-yolov8.h)
    * [ml-postprocess-yolov8.cc](https://github.com/qualcomm/gst-plugins-imsdk/blob/main/gst-plugin-mlpostprocess/modules/object-detection/ml-postprocess-yolov8.cc)
  </Step>

  <Step title="将 IM SDK 头文件和模块源文件放在一个文件夹中">
    ```
    <root>/
      ml-postprocess-yolov8.cc
      ml-postprocess-yolov8.h
      qti-json-parser.h
      qti-labels-parser.h
      qti-ml-post-process.h
    ```
  </Step>

  <Step title="创建 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
    )
    ```

    <Warning>
      后处理模块共享库必须遵循 `libml-postprocess-<module-name>.so` 命名约定。

      例如,YoloV8 模块的共享库应命名为 `libml-postprocess-yolov8.so`。
    </Warning>
  </Step>

  <Step title="创建工具链文件">
    例如,`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")
    ```
  </Step>

  <Step title="配置并构建模块">
    ```shell theme={null}
    mkdir build
    ```

    ```shell theme={null}
    cd build
    ```

    ```
    cmake -DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake ..
    ```

    ```
    cmake --build .
    ```
  </Step>
</Steps>

<h2 id="deploy-test-postprocessing-module">
  部署和测试后处理模块
</h2>

<Steps>
  <Step title="在主机上设置用户环境变量">
    ```shell theme={null}
    export USER=ubuntu
    ```
  </Step>

  <Step title="下载所需的脚本和工件">
    [下载所需的脚本和工件](../topic/classify-objects-with-default-model#download-model-and-label-files)。
  </Step>

  <Step title="将模块部署到目标设备">
    <Steps>
      <Step title="将模块传输到目标设备">
        从主机上的终端:

        ```shell theme={null}
        scp libml-postprocess-yolov8.so $USER@<IP address of the target device>:/tmp
        ```
      </Step>

      <Step title="通过 SSH 登录到目标设备">
        从主机上的终端:

        ```shell theme={null}
        ssh $USER@<IP address of the target device>
        ```
      </Step>

      <Step title="出现提示时输入密码">
        出现提示时,输入密码。
      </Step>

      <Step title="以读写权限重新挂载 /">
        在 Ubuntu 目标设备上 (SSH 登录后):

        ```shell theme={null}
        sudo mount -o remount,rw /
        ```
      </Step>

      <Step title="将模块复制到 GStreamer 插件目录">
        在目标设备上 (SSH 登录后):

        ```shell theme={null}
        sudo cp /tmp/libml-postprocess-yolov8.so /usr/lib/aarch64-linux-gnu/imsdk/qtimlpostprocess/modules/.
        ```
      </Step>
    </Steps>
  </Step>

  <Step title="在目标设备上运行 GST inspect">
    确认您的模块出现在支持的模块列表中。

    您必须看到您的后处理模块出现在支持的模块列表中,并显示支持的张量形状。

    ```shell theme={null}
    gst-inspect-1.0 qtimlpostprocess
    ```
  </Step>

  <Step title="下载模型、标签和媒体以运行 GStreamer 管线">
    <Steps>
      <Step title="下载 yolox.json">
        下载 [yolox.json](https://github.com/qualcomm/sample-apps-for-qualcomm-linux/blob/main/qualcomm-linux/artifacts/json_labels/yolox.json)。
      </Step>

      <Step title="将 yolox.json 复制到目标设备">
        ```shell theme={null}
        scp yolox.json $USER@<IP address of the target device>:/etc/labels/
        ```
      </Step>

      <Step title="下载 video1.mp4">
        下载 [video1.mp4](https://github.com/qualcomm/sample-apps-for-qualcomm-linux/tree/main/qualcomm-linux/artifacts/videos)。
      </Step>

      <Step title="将 video1.mp4 复制到目标设备">
        ```shell theme={null}
        scp video1.mp4 $USER@<IP address of the target device>:/etc/media/
        ```
      </Step>

      <Step title="下载 yolox_quantized.tflite">
        下载 [yolox\_quantized.tflite](https://huggingface.co/qualcomm/Yolo-X/resolve/v0.30.5/Yolo-X_w8a8.tflite)。
      </Step>

      <Step title="将 yolox_quantized.tflite 复制到目标设备">
        ```shell theme={null}
        scp yolox_quantized.tflite $USER@<IP address of the target device>:/etc/models/
        ```
      </Step>
    </Steps>
  </Step>

  <Step title="构建带有后处理模块的 GStreamer 管线">
    使用 `qtimlpostprocess` 插件的 module 属性选择您的后处理模块。

    如果您的模块需要标签文件或配置,请使用 label 和 settings 属性传递它们。

    在以下运行 YOLO-X 模型的示例管线中:

    * 该管线使用离线视频作为源。
    * 该管线使用 v4l2h264dec 解码器将视频解码为 YUV 格式。
    * `qtimlvconverter` 插件对 YUV 帧进行预处理。
    * `qtimltflite` 插件使用 LiteRT YOLO-X 模型运行推理。
    * 后处理插件加载 YOLO-X 模块并传递 JSON 格式的标签文件。
    * 该管线在 Wayland 上显示结果。

    ```shell theme={null}
    gst-launch-1.0 -e \
    filesrc location=/etc/media/video1.mp4 ! qtdemux ! queue ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! queue ! tee name=split split. ! \
    queue ! qtivcomposer name=mixer sink_1::dimensions="<1920,1080>" ! queue ! waylandsink fullscreen=true split. ! queue ! qtimlvconverter ! queue ! \
    qtimltflite delegate=external external-delegate-path=libQnnTFLiteDelegate.so external-delegate-options="QNNExternalDelegate,backend_type=htp;" \
    model=/etc/models/yolox_quantized.tflite ! queue ! qtimlpostprocess settings="{\"confidence\": 75.0}" results=10 module=yolov8 labels=/etc/labels/yolox.json \
    ! video/x-raw,format=BGRA,width=640,height=360 ! queue ! mixer.
    ```
  </Step>
</Steps>
