如何将 MediaPipe 模型部署到嵌入式平台边缘
扫描二维码
随时随地手机看文章
极速模型
在本项目中,我首先回顾了在部署 MediaPipe 模型时可能出现的挑战,特别是针对高通龙翼 QCS6490 芯片而言。
然后,我将逐一解决这些挑战,之后再将模型部署到 QAI Hub 工作平台中。
最后,我将进行分析以确定我们的加速目标是否已经达成。
高通人工智能工作台
高通公司提供了一套在线工具,使用户能够将模型部署到他们的芯片设备上。
QAI 中心工作台允许用户自行导入模型(BYOM)和数据集(BYOD),以便对其进行编译、量化,并优化以适配在高通设备上的部署。
量化步骤是可选的,具体取决于所针对的设备。
在该项目中,我们将专门针对 QCS6490 设备的神经处理单元进行研究,因此将需要进行量化处理。
QAI 中心工作台支持以下模型格式作为输入:
•PyTorch
•ONNX(开放神经网络转换格式)
其他框架则是通过转换为 ONNX 格式来间接获得支持的。在我们的案例中,我们将把 MediaPipe 模型从 TFLite 格式转换为 ONNX 格式,使用 tf2onnx 工具。
此次部署包含以下任务,这些任务在 QAI Hub 工作台中被称为“作业”:
•量化
•编译
•验证;确认
•简介
量化任务在内部使用 AIMET 框架来对模型进行量化处理。要完成这一量化操作,需要使用训练数据集的一部分。所需校准数据的大小通常在数千个样本的量级。
编译任务可以针对多个运行时目标进行部署:
•TFLite 运行时(量子神经网络代理)
•ONNX 运行时
•高通人工智能运行时(QAIRT)
在这个项目中,我对使用 QNN 代理的 TFLite 运行时与 QAIRT 进行了对比研究并进行了探索。
我的理解是,使用 ONNX 运行时进行部署仅在 Windows 系统上可行,而在嵌入式 Linux 系统上则无法实现。
“验证”和“配置”任务可用于在实际的目标设备(在我们的案例中为 QCS6490)上对模型进行推理和/或进行性能分析。这些任务是在云端执行的,并且是在实际设备上进行的。我曾遇到过一些情况,这些任务因代理设备不可用而超时。
在这个项目中,我使用了 QAI Hub 工作台的 2026.01.05.0 版本,其包含以下内容:
•AI 资源中心工作台:aihub-2026.01.05.0
•QAIRT:2.40.0.251030114326_189385
高通公司的流程和相关文档一直在不断变化,每次改进都会使用户体验得到提升。
从 Vision AI-KIT 6490 上的 QIRP 1.6 版本图像开始
该项目是在 Vision AI-KIT 6490 上进行测试的,所使用的图像为 QIRP 1.6 版本。如果您所使用的板子不同,或者使用的 QIRP 版本不同,那么其他部分中的说明可能需要进行修改。
请使用以下启动指南将 QIRP 1.6 镜像程序化到 QCS6490 视觉人工智能套件中:
•Vision AI-KIT 6490 创业指南 v1.4
这将提供有关如何对最新版本的 QIRP 1.6 图像进行编程的说明(文件名为 visionai_6490_qirp_1.6_v4.zip):
•visionai_6490_qirp_1.6_v4.zip
在使用 QIRP 1.6 镜像启动 Vision AI-KIT 6490 后,您可以使用出厂即用演示来进行一次完整性检查:
请注意底部的系统温度图和系统使用率图。在我们的探索过程中,我们会利用这些数据。
在“视觉 AI-KIT 6490”上安装 QAI Hub
首先,要确保在 Vision AI-KIT 6490 上所做的更改能够持久生效:
QAI Hub 客户端可以通过 pip 进行安装。我们还将安装对 PyTorch 和 TFLite 的支持:
我尝试安装“qai-hub[onnx]”这个软件包,但安装过程失败了。这似乎证实了我的看法,即使用 ONNX 运行时进行部署仅在 Windows 系统上可行,而在嵌入式 Linux 系统上则无法实现。
我们还可以安装 QAI 中心模型库,具体操作如下:
然后按照以下命令输入您的登录信息一次:
作为一项验证手段,您可以列出 QAI 中心工作台所支持的设备:
QAI 平台上的 MediaPipe 模型
如果您已经阅读过高通公司的相关文档,您就会发现他们已经在其 QAI 平台上部署了 MediaPipe 模型。
那么……我们为什么要重新发明轮子呢?
这不仅是一个非常好的问题,而且也是一个非常重要的需要强调的观点。
首先,在我撰写这篇文章之时,以下这些 mediapipe 模型中仅有 1 个在高通 QCS6490 芯片上得到了支持:
•mediapipe_hand => 在 QCS6490 上不支持
•mediapipe_face => 在 QCS6490 上已支持
•mediapipe_pose => 在 QCS6490 上不支持
其次,高通公司选择支持较旧版本的 mediapipe 模型(v0.07),而非最新的版本(v0.10)。
这一点非常重要,需要特别强调一下,因为在 v0.7 版本之后,我们对手掌检测和手部特征点模型进行了重大更新,这些更新专门用于手势和手语识别:
•SignAll SDK:使用 MediaPipe 构建的手语交互界面现已面向开发者开放 - 谷歌开发者博客
事实上,高通公司选择支持的是由开源社区将这些模型转换为 PyTorch 版本后的版本:
•[维杜尔·萨蒂贾] 布莱兹帕尔姆:vidursatija/布莱兹帕尔姆
•[马蒂希·霍勒曼斯] 布莱泽脸 - PyTorch : hollance/BlazeFace-PyTorch
•[扎克·穆雷兹] MediaPipePyTorch :zmurez/MediaPipePytorch
尽管 zmurez 没有透露用于生成 PyTorch 版本模型的转换脚本,但 vidrsatija 和 holland(zmurez 的研究工作基于他们的成果)确实以 Jupyter 笔记本的形式提供了这些转换脚本。
不幸的是,这些转换脚本/笔记本仅适用于 v0.7 版本,而不适用于后续版本(相信我,我试过了……).
当我们在 QCS6490 板上运行支持的 mediapipe_face 模型时,可以看到对 zmurez/MediaPipePyTorch 仓库的引用:
选择这个过时的模型对我来说毫无意义,除了可能是在整合的时候,只有 PyTorch 被 Qualcomm AI 堆栈所支持之外?
当我将这些模型部署到 AMD/Xilinx 的 Vitis-AI 平台时,我也遇到了同样的情况。
不管原因如何,我认为都有机会进一步加强对 MediaPipe 的支持。由于我们可以将 TFLite 模型转换为 ONNX 格式,所以我为 QCS6490 上的 MediaPipe 模型提出了以下更新的流程:
在高通平台上部署 MediaPipe 所面临的挑战
在第一部分中,我遇到的第一个难题是:在嵌入式平台上运行 MediaPipe 模型时,其性能会明显低于在现代计算机上的表现。这就是我试图通过 QAI Hub Workbench 来加速这些模型的原因。
第二个挑战在于,谷歌并未提供用于训练 MediaPipe 模型的数据集。由于量化需要使用其中的一部分训练数据,这就要求我们自己去收集这些数据。
为了应对这些挑战,我们将克隆以下存储库(blaze_tutorial),该存储库将用于在 QAI Hub Workbench 中对云端的模型进行量化、编译和性能分析:
创建量化校准数据集
正如“QAI Hub 工作台概述”部分所描述的那样,量化阶段需要数百到数千个数据样本,理想情况下应是从训练数据中选取的一部分样本。由于我们无法获取训练数据集,所以我们需要自己生成这些数据。
我们可以使用修改后的 blaze_app_python.py 脚本生成校准数据集,具体步骤如下:
对于每一张包含至少一只手的输入图像,我们想要生成:
•手掌检测输入图像:经过调整大小并填充至模型输入尺寸的图像
•手部特征点输入图像:每个手部的裁剪图像,经过调整大小处理以适应模型的输入尺寸。
输入图像的可能来源如下:
•Kaggle:存在众多数据集,并且这些数据集可以被重复使用。
•Pixabay:包含多段有趣视频,从中可以提取出图片。
对于 Kaggle 这个案例,如果我们采用以下这样一个现有的数据集:
•[Kaggle] 手势数据集(由瑞蒂卡·吉里达尔创作)
我们可以对 blaze_app_python 仓库中的 blaze_detect_live.py 脚本进行修改,从而创建一个版本,该版本能够扫描所有图像,并生成一个特定于 NumPy 的二进制格式文件(*.npy),其中包含我们在量化步骤中所需的校准数据:
•blaze_app_python/calib_dataset_kaggle/gen_calib_hand_dataset.py
要运行此脚本,请前往“blaze_app_python/calib_dataset_kaggle”目录,将 Kaggle 数据集下载到该子目录中,然后按照以下方式启动脚本:
这将为 0.10 版本的手掌检测和手部特征点模型生成以下校准数据:
•calib_palm_detection_192_dataset.npy 文件:包含 1871 个 192x192 像素的 RGB 图像样本。
•calib_hand_landmark_224_dataset.npy 文件:包含 1880 个 224x224 像素的 RGB 图像样本。
最终我决定不使用这个数据集,但还是记录下了整个过程以便日后参考,这个过程可以适用于任何其他 Kaggle 数据集。
再次说明,我们可以对 blaze_app_python 仓库中的 blaze_detect_live.py 脚本进行修改,从而生成一个新的版本。该版本将对视频进行扫描,并生成一个包含量化步骤校准数据的 NumPy 特定的二进制格式文件(*.npy):
•blaze_app_python/calib_dataset_pixabay/gen_calib_hand_dataset.py
要运行此脚本,请前往“blaze_app_python/calib_dataset_pixabay”目录,将 Pixabay 视频下载到“videos”子目录中,然后按照以下方式启动脚本:
这将为 0.10 版本的手掌检测和手部特征点模型生成以下校准数据:
•calib_palm_detection_192_dataset.npy 文件:包含 1577 个 192x192 像素的 RGB 图像样本。
calib_hand_landmark_224_dataset.npy 文件:包含 2595 个 224x224 像素的 RGB 图像样本。
您可以自由选择上述所描述的任一数据源,或者使用您自己的数据源作为量化阶段的数据。
我已经将关于这个子主题(为各种版本的模型创建手部/面部/姿势数据集)的探索成果存入了以下两个档案中:
•Kaggle:calib_dataset_kaggle.zip
•Pixabay:calib_dataset_pixabay.zip
为了此次探索,我准备了校准数据(来自 Pixabay),您可以按照以下方式下载并提取这些数据:
模型转换
使用 QAI Hub Workbench 进行部署的第二步是下载 TFLite 模型,并使用 tf2onnx 工具将其转换为 ONNX 格式:
•get_tflite_models.sh:从谷歌下载 TFLite 模型
•convert_models.sh:使用 tf2onnx 工具将模型转换为 ONNX 格式
使用以下命令来下载并转换 mediapipe 模型为 ONNX 格式:
模型部署
既然我们有了校准数据,并且我们的模型已转换为 ONNX 格式,那么就可以使用 AI Hub 工作台来执行模型量化、性能分析和编译操作了。
为此我准备了一份脚本:
•qai_hub_workbench_flow.py
此脚本在被调用时会接受三个(3)参数:
•名称:型号名称(例如:palm_detection_lite)
•模型:模型文件(例如:models/palm_detection_lite.onnx)
•说明:输入尺寸(例如:256)
“名称参数”用于指明我们所部署的模型类型,例如对于手掌检测器而言,可以是“palm_detection_lite”或“palm_detection_full”;而对于手部特征点模型,则可以是“hand_landmark_lite”或“hand_landmark_full”。而“分辨率”则表示模型的输入尺寸。
这两项参数将决定用于量化操作的校准数据集是哪一个。例如:
•名称:palm_detection_lite,大小:192 => 文件名:calib_palm_detection_192_dataset.npy
•名称=hand_landmark_lite,大小=224 => calib_hand_landmark_224_dataset.npy
该脚本将为以下目标运行环境生成输出文件:
•TFLite(*.tflite)
•onnx(*.onnx.zip)
•qnn_dlc (*.dlc)
•qnn_context_binary (*.bin)
•预编译的 QNN ONNX 文件(*.onnx.zip)
我将仅使用以下两个目标运行时环境来进行推理测试:
•TFLite (*.tflite) => 使用 TFLite(搭配 QNN 代理)
•qnn_context_binary (*.bin) => 使用 QAIRT
我已提供了另一份脚本,该脚本将调用 qai_hub_workbench_flow.py 脚本来对模型进行量化、编译和性能分析:
•deploy_models_qai_hub_workbench.sh
在执行之前,您需要对以下列表进行修改:
•模型列表:请指定您想要部署的模型(或多个模型)
以下是修改后的脚本版本,它将部署 0.10 版本的掌部检测和手部特征点模型。
此脚本的执行方式如下:
完成之后,以下编译好的模型将位于当前目录中:
•palm_detection_full.tflite、palm_detection_full.bin……
•hand_landmarks_full.tflite、hand_landmarks_full.bin……
•palm_detection_lite.tflite、palm_detection_lite.bin……
•hand_landmarks_lite.tflite、hand_landmarks_lite.bin 等……
为了方便起见,我已将 QCS6490 的编译模型存入以下档案中:
•TFLite 模型(*.tflite):blaze_tflite_qnn_models_qcs6490.zip
•QAIRT 模型(*.bin):blaze_qairt_models_qcs6490.zip
在 QAI 资源中心工作台中分析结果
在“职位”页面中,如果我们点击“个人资料”选项卡,就能看到针对每个模型的个人资料生成结果:
如果我们将运行在 CPU 上的未量化 ONNX 模型与运行在 NPU 上的量化模型进行比较:
•“手掌检测(基于 QAIRT 版本)”:66.4 毫秒 => 1.3 毫秒
•hand_landmarks_full(QAIRT 版本):47.4 毫秒 => 1 毫秒
如果我们将运行在 CPU 上的未量化 ONNX 模型与运行在 NPU 上的量化模型进行比较:
•“palm_detection_lite(基于QAIRT的版本):54.7 毫秒 => 1.2 毫秒”
•hand_landmarks_lite(基于 QAIRT 的版本):29.9 毫秒 => 0.7 毫秒
这是显著的加速(幅度在 30 倍到 60 倍之间)!
在量化模型的性能分析结果中存在一个异常情况:
•palm_detection_full(TFlite 版本):66.4 毫秒 => 31.0 毫秒
如果我们点击该任务以了解其运行情况,就会发现该模型在 NPU 上的加速程度并不完全,仍有 150 层运算是在 CPU 上进行的。
所有其他的工作都采用了干净的 NPU 实现方式,包括 palm_detection_full 模型的 QAIRT 版本:
如果我们向下滚动并选择“运行时层分析”部分,然后点击“查看操作跟踪”按钮,就能获得逐层的详细分析报告:
如果我们查看每个模型的层数(中央处理器、神经处理单元、图形处理器等),则在性能测试报告中可得到以下所报告的层数:
“ONNX 层”指的是我们作为 QAI Hub 工作台输入所使用的浮点型 ONNX 模型。
“TFLite 层”指的是经过量化处理的模型,专为 TFLite 运行时环境设计。
“QAIRT 层”对应的是经过量化处理的模型,其目标是“qnn_compiled_binary”,并且可以与高通 AI 运行时配合使用。
模型准确率
如果我们查看模型的准确率,那么在量化任务中我们得到的结果如下:
我们可以看到,模型的准确度仍有待提高。这可能与校准数据有关,但我尚未进一步进行调查。
使用 0.07 版本的手掌检测模型时,能达到最佳的准确度。
在“手部特征点模型”0.07 版本中,达到了最差的准确度。这一点可以从 0.07 版本的流程中看出,即手部能够被正确检测出来,但特征点的准确性却不高。
不幸的是,手部特征点模型 v0.10 版本的 PSNR 计算出现了错误,所以我不清楚具体的指标数值是什么,但它们看起来肯定是准确的,所以我推测其数值肯定高于 30 分贝。
除了方向判断有误(总是错误地报告为“左手”)之外。
模型执行
为了支持 QCS6490 型号,对“blaze_app_python”应用程序进行了以下增强,添加了相应的推理目标:
如图所示,我已经为原始的 TFLite 模型以及这些模型的 PyTorch 版本(v0.07)提供了支持。
我们也可以运行未量化化的 ONNX 模型,但更重要的是,通过以下两个运行时目标实现了在 NPU 上的执行支持:
•TFLite(采用 QNN 代理)
•QAIRT
我针对 TFLite(并使用了 QNN 代理)编写的最终推理代码,实际上采用了 EdgeAI Lite-RT(即谷歌最新版的 TFLite),该代码可在“blaze_app_python”存储库中的“blaze_tflite_qnn”子目录中找到:
•blaze_app_python/blaze_tflite_qnn/blazedetector.py
•blaze_app_python/blaze_tflite_qnn/blazelandmark.py
我们需要确保我们的电路板上具备所需的库文件,而实际上它确实具备:
我为 QAIRT 编写的最终推理代码可以在“blaze_app_python”存储库中的“blaze_qairt”子目录下找到:
•blaze_app_python/blaze_qairt/blazedetector.py
•blaze_app_python/blaze_qairt/blazelandmark.py
我们需要在我们的板子上安装 QAIRT SDK,这可以通过以下步骤来完成。
首先,我们下载并安装了 QAIRT SDK 的 2.40 版本:
然后,我们(可选地)会克隆、构建并安装 QAI 应用程序构建器:
在“视觉 AI-KIT 6490”设备上安装 Python 应用程序
首先,我们要确保我们的更改能够持久生效:
该 Python 演示应用程序需要一些特定的包,这些包可以通过以下方式安装:
该 Python 应用程序可通过以下 GitHub 仓库进行访问:
若要成功使用带有原始 TFLite 模型的 Python 示例程序,需从谷歌网站下载这些模型:
若要成功使用与 QCS6490 型号配套的 Python 示例程序,需按照以下步骤进行下载:
你们都准备好了!
在 Vision AI-KIT 6490 上启动 Python 应用程序
正如我们在第 1 部分中所看到的,Python 应用程序能够启动多种双推理流程的变体,这些变体可以通过以下参数进行筛选:
——布莱兹:手部动作 | 面部表情 | 姿态动作
--目标:blaze_tflite | ... | blaze_tflite_qnn | blaze_qairt
--pipeline:管道的特定名称(可通过 --list 参数进行查询)
为了显示所有支持的管道列表,请按照以下方式运行 Python 脚本:
为了启动用于手部检测和关键点识别的 v0.10 便携版管道,以及带有 QNN 代理的 TFLite 运行时,请按照以下方式使用 Python 脚本:
这将启动针对 QCS6490 系统编译的 0.10(精简版)版本的模型,如图所示:
之前的视频并未进行加速处理。该视频显示,在未检测到手部时(运行一种模型:手掌检测),帧率为约 30 帧每秒;在检测到一只手时(运行两种模型:手掌检测和手部特征点检测),帧率为约 20 帧每秒;在检测到两只手时(运行三种模型:手掌检测和两只手特征点检测),帧率为约 15 帧每秒。
这比在 CPU 上运行的原始 TFLite 模型要差一些,所以对于这种情况,我不得不考虑放弃使用 TFLite 并采用 QNN 代理的做法。
为了启动用于手部检测和关键点识别的 v0.10 便携版管道以及高通 AI 运行时,请按照以下方式使用 Python 脚本:
这将启动针对 QCS6490 系统编译的 0.10(精简版)版本的模型,如图所示:
之前的视频并未进行加速处理。该视频显示,在未检测到手部时(运行一种模型:手掌检测),帧率为 30 帧每秒;当检测到一只手时(运行两种模型:手掌检测和手部特征点检测),帧率为 30 帧每秒;当检测到两只手时(运行三种模型:手掌检测和两个手的特征点检测),帧率为 30 帧每秒。
与使用 QNN 代理的 TFLite 运行时相比,高通 AI 运行时实现了显著的加速!
为了了解使用 QAIRT 运行的模型的实际表现,我们需要暂时断开 USB 摄像头(该摄像头决定了 30 帧每秒的帧率)。我们将在下一节中进行此项操作。
在“视觉 AI-KIT 6490”平台上对模型进行基准测试
为了获得稳定的轮廓结果,我们使用了一张测试图片(包含双手),该图片可以从谷歌上下载,具体操作如下:
我们可以使用以下命令来直观地比较原始的 TFLite 模型与 QAIRT 加速模型的性能分析结果:
以下图表将会出现:
您可能已经注意到图表中的柱状图存在一些抖动现象,因此我们将把一系列数据保存到 CSV 文件中,然后对结果进行平均处理,以便更清晰地了解其性能情况。
我没有这种自动化的流程,所以这是一个需要人工操作的流程,包括采集、处理和呈现等环节……
以下命令可用于使用 QCS6490 模型和测试图像为 qairt_hand_v0_10_lite 流水线生成性能报告结果:
以下命令可用于使用 TFLite 模型及测试图像为 tfl_hand_v0_10_lite 流水线生成性能报告结果:
对于 qairt_hand_v0_10_full 和 tfl_hand_v0_10_full 这两个模型,也采用了同样的操作。
所有.csv 文件中的数据均进行了平均处理,然后使用 Excel 进行了绘图。
以下是使用 QAIRT 部署的模型的性能分析结果,与参考的 TFLite 模型进行了对比:
同样,需要指出的是,这些基准测试是通过单线程的 Python 脚本完成的。如果采用多线程实现方式,还有进一步加速的空间。在图形运行器等待从一个模型的子图中获取数据的同时,可以同时启动另一个(或多个其他)模型……
此外,还有机会通过使用 C++ 代码来加快整个开发流程的进度……
已知问题
尽管我已经对 palm_detection 和 hand_landmarks 这两个模型的 v0.07 版本进行了量化和部署,但 hand_landmarks 模型的准确性已经下降了,所以在您的应用中请不要再使用这个模型。
对于 0.10 版本的手部特征点数据,手的朝向似乎没有得到正确处理。这些模型总是返回约 1.0 的数值,这对应于“左手”。
本文编译自hackster.io





