mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2025-10-06 00:57:33 +08:00
first commit
This commit is contained in:
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
199
README.md
Normal file
199
README.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# FastDeploy
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache%202-dfd.svg"></a>
|
||||||
|
<a href="https://github.com/PaddlePaddle/FastDeploy/releases"><img src="https://img.shields.io/github/v/release/PaddlePaddle/FastDeploy?color=ffa"></a>
|
||||||
|
<a href=""><img src="https://img.shields.io/badge/python-3.7+-aff.svg"></a>
|
||||||
|
<a href=""><img src="https://img.shields.io/badge/os-linux%2C%20win%2C%20mac-pink.svg"></a>
|
||||||
|
<a href="https://github.com/PaddlePaddle/FastDeploy/graphs/contributors"><img src="https://img.shields.io/github/contributors/PaddlePaddle/FastDeploy?color=9ea"></a>
|
||||||
|
<a href="https://github.com/PaddlePaddle/FastDeploy/commits"><img src="https://img.shields.io/github/commit-activity/m/PaddlePaddle/FastDeploy?color=3af"></a>
|
||||||
|
<a href="https://pypi.org/project/FastDeploy-python/"><img src="https://img.shields.io/pypi/dm/FastDeploy-python?color=9cf"></a>
|
||||||
|
<a href="https://github.com/PaddlePaddle/FastDeploy/issues"><img src="https://img.shields.io/github/issues/PaddlePaddle/FastDeploy?color=9cc"></a>
|
||||||
|
<a href="https://github.com/PaddlePaddle/FastDeploy/stargazers"><img src="https://img.shields.io/github/stars/PaddlePaddle/FastDeploy?color=ccf"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h4 align="center">
|
||||||
|
<a href=#特性> 特性 </a> |
|
||||||
|
<a href=#SDK安装> 安装 </a> |
|
||||||
|
<a href=#SDK使用> 快速开始 </a> |
|
||||||
|
<a href=#社区交流> 社区交流 </a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
**FastDeploy**是一款**简单易用**的推理部署工具箱。覆盖业界主流**优质预训练模型**并提供**开箱即用**的开发体验,包括图像分类、目标检测、图像分割、人脸检测、人体关键点识别、文字识别等多任务,满足开发者**多场景**,**多硬件**、**多平台**的快速部署需求。
|
||||||
|
|
||||||
|
## News 📢
|
||||||
|
|
||||||
|
* 🔥 2022.6.30 B站[飞桨直播课](https://space.bilibili.com/476867757),FastDeploy天使用户邀测沟通会,与开发者共同讨论推理部署痛点问题。
|
||||||
|
|
||||||
|
* 🔥 2022.6.27 [**FastDeploy v0.1**](https://github.com/PaddlePaddle/FastDeploy/releases/tag/v0.1)邀测版发布!🎉
|
||||||
|
* 💎 第一批发布对于40个重点模型在8种重点软硬件环境的支持的SDK
|
||||||
|
* 😊 支持网页端、pip包两种下载使用方式
|
||||||
|
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
|
||||||
|
### 📦开箱即用的推理部署工具链,支持云边端、多硬件、多平台部署
|
||||||
|
- 网页端点选下载、PIP 安装一行命令,快速下载多种类型SDK安装包
|
||||||
|
- 云端(含服务器、数据中心):
|
||||||
|
- 支持一行命令启动 Serving 服务(含网页图形化展示)
|
||||||
|
- 支持一行命令启动图像、本地视频流、本地摄像头、网络视频流预测
|
||||||
|
- 支持 Window、Linux 操作系统
|
||||||
|
- 支持 Python、C++ 编程语言
|
||||||
|
- 边缘端:
|
||||||
|
- 支持 NVIDIA Jetson 等边缘设备,支持视频流预测服务
|
||||||
|
- 端侧(含移动端)
|
||||||
|
- 支持 iOS、Android 移动端
|
||||||
|
- 支持 ARM CPU 端侧设备
|
||||||
|
- 支持主流硬件
|
||||||
|
- 支持 Intel CPU 系列(含酷睿、至强等)
|
||||||
|
- 支持 ARM CPU 全系(含高通、MTK、RK等)
|
||||||
|
- 支持 NVIDIA GPU 全系(含 V100、T4、Jetson 等)
|
||||||
|
|
||||||
|
### 🤗丰富的预训练模型,轻松下载SDK搞定推理部署
|
||||||
|
|
||||||
|
|
||||||
|
<font size=0.5>
|
||||||
|
|
||||||
|
|<font size=2> 模型| <font size=2> 任务 |<font size=2> 大小(MB) | <font size=2>端侧 | <font size=2>移动端 |<font size=2> 移动端 |<font size=2>边缘端 |<font size=2>服务器+云端 | <font size=2>服务器+云端 |<font size=2> 服务器+云端 |<font size=2> 服务器+云端 |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
|----- | ---- |----- |<font size=2> Linux | <font size=2> Android |<font size=2> iOS | <font size=2> Linux |<font size=2> Linux |<font size=2> Linux |<font size=2> Windows |<font size=2> Windows |
|
||||||
|
|----- | ---- |--- | <font size=2> ARM CPU |<font size=2> ARM CPU | <font size=2> ARM CPU |<font size=2> Jetson |<font size=2> X86 CPU |<font size=2> GPU |<font size=2> X86 CPU |<font size=2> GPU |
|
||||||
|
| <font size=2> [PP-LCNet](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 11.9 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
| <font size=2> [PP-LCNetv2](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 26.6 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [EfficientNet](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication |31.4 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [GhostNet](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 20.8 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [MobileNetV1](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 17 |✅|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [MobileNetV2](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 14.2 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [MobileNetV3](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 22 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [ShuffleNetV2](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md)|Classfication | 9.2 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [SqueezeNetV1.1](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication |5 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [Inceptionv3](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication |95.5 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-HGNet](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 59 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [ResNet50_vd](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 102.5 |❌|❌|❌|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [SwinTransformer_224_win7](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/models_training/classification.md) |Classfication | 352.7 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-PicoDet_s_320_coco](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 4.1 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-PicoDet_s_320_lcnet](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 4.9 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [CenterNet](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection |4.8 |✅|✅|✅|✅ |✅ |✅|✅|✅|
|
||||||
|
|<font size=2> [YOLOv3_MobileNetV3](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 94.6 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-YOLO_tiny_650e_coco](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection |4.4 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [SSD_MobileNetV1_300_120e_voc](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 23.3 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [YOLOX_Nano_300e_coco](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 3.7 |❌|❌|❌|✅|✅ |✅|✅|✅|
|
||||||
|
|<font size=2> [PP-YOLO_ResNet50vd](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 188.5|✅ |✅ |✅ |✅ |✅ |✅|✅|✅|
|
||||||
|
|<font size=2> [PP-YOLOv2_ResNet50vd](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 218.7 |✅|✅|✅|✅|✅ |✅|✅|✅|
|
||||||
|
|<font size=2> [PP-YOLO_crn_l_300e_coco](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 209.1 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [YOLOv5s](https://github.com/Sharpiless/PaddleDetection-Yolov5) |Detection | 29.3|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [Faster R-CNN_r50_fpn_1x_coco](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Detection | 167.2 |❌|❌|❌|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [BlazeFace](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Face Detection |1.5|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [RetinaFace](https://github.com/GuoQuanhao/RetinaFace-Paddle) |Face Localisation |1.7| ✅|❌|❌|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-TinyPose](https://github.com/PaddlePaddle/PaddleDetection/blob/develop/docs/tutorials/GETTING_STARTED_cn.md) |Keypoint Detection| 5.5 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-LiteSeg(STDC1)](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/configs/pp_liteseg/README.md)|Segmentation | 32.2|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-HumanSeg-Lite](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/contrib/PP-HumanSeg/README_cn.md) |Segmentation | 0.556|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [HRNet-w18](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/docs/train/train_cn.md) |Segmentation | 38.7|✅|✅|✅|❌|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [Mask R-CNN_r50_fpn_1x_coco](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/contrib/PP-HumanSeg/README_cn.md)|Segmentation| 107.2|❌|❌|❌|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-HumanSeg-Server](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/contrib/PP-HumanSeg/README_cn.md)|Segmentation | 107.2|✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [Unet](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/docs/train/train_cn.md) |Segmentation | 53.7|❌|✅|❌|❌|✅|✅|✅|❌|
|
||||||
|
|<font size=2> [Deeplabv3-ResNet50](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/docs/train/train_cn.md)|Segmentation |156.5|❌|❌|❌|❌|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-OCRv1](https://github.com/PaddlePaddle/PaddleOCR/blob/release%2F2.5/doc/doc_ch/ppocr_introduction.md) |OCR | 2.3+4.4 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
|<font size=2> [PP-OCRv2](https://github.com/PaddlePaddle/PaddleOCR/blob/release%2F2.5/doc/doc_ch/ppocr_introduction.md) |OCR | 2.3+4.4 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
| <font size=2> [PP-OCRv3](https://github.com/PaddlePaddle/PaddleOCR/blob/release%2F2.5/doc/doc_ch/PP-OCRv3_introduction.md) |OCR | 2.4+10.6 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
| <font size=2> [PP-OCRv3-tiny](https://github.com/PaddlePaddle/PaddleOCR/blob/release%2F2.5/doc/doc_ch/models_list.md) |OCR |2.4+10.7 |✅|✅|✅|✅|✅|✅|✅|✅|
|
||||||
|
</font>
|
||||||
|
|
||||||
|
|
||||||
|
## SDK安装
|
||||||
|
|
||||||
|
### 方式1:网页版下载安装
|
||||||
|
|
||||||
|
- 可以登录[EasyEdge网页端](https://ai.baidu.com/easyedge/app/openSource)下载SDK
|
||||||
|
|
||||||
|
### 方式2:pip安装
|
||||||
|
|
||||||
|
开发者可以通过pip安装`fastdeploy-python`来获取最新的下载链接
|
||||||
|
|
||||||
|
- 环境依赖
|
||||||
|
|
||||||
|
python >= 3.6
|
||||||
|
|
||||||
|
- 安装方式
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install fastdeploy-python --upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
- 使用方式
|
||||||
|
|
||||||
|
- 列出FastDeploy当前支持的所有模型
|
||||||
|
```
|
||||||
|
fastdeploy --list_models
|
||||||
|
```
|
||||||
|
- 下载模型在具体平台和对应硬件上的部署SDK以及示例
|
||||||
|
```
|
||||||
|
fastdeploy --download_sdk \
|
||||||
|
--model PP-PicoDet-s_320 \
|
||||||
|
--platform Linux \
|
||||||
|
--soc x86-NVIDIA-GPU \
|
||||||
|
--save_dir .
|
||||||
|
```
|
||||||
|
|
||||||
|
- 参数说明
|
||||||
|
- `list_models`: 列出FastDeploy当前最新支持的所有模型
|
||||||
|
- `download_sdk`: 下载模型在具体平台和对应硬件上的部署SDK以及示例
|
||||||
|
- `model`: 模型名,如"PP-PicoDet-s_320",可通过`list_models`查看所有的可选项
|
||||||
|
- `platform`: 部署平台,支持 Windows/Linux/Android/iOS
|
||||||
|
- `soc`: 部署硬件,支持Intel-x86_64/x86-NVIDIA-GPU/ARM/Jetson
|
||||||
|
- `save_dir`: SDK下载保存目录
|
||||||
|
|
||||||
|
## SDK使用
|
||||||
|
### 1 云+服务器部署
|
||||||
|
- Linux 系统(X86 CPU、NVIDIA GPU)
|
||||||
|
- [C++ Inference部署(含视频流)](./docs/Linux-CPP-SDK-Inference.md)
|
||||||
|
- [C++ 服务化部署](./docs/Linux-CPP-SDK-Serving.md)
|
||||||
|
- [Python Inference部署](./docs/Linux-Python-SDK-Inference.md)
|
||||||
|
- [Python 服务化部署](./docs/Linux-Python-SDK-Serving.md)
|
||||||
|
- Window系统(X86 CPU、NVIDIA GPU)
|
||||||
|
- [C++ Inference部署(含视频流)](./docs/Windows-CPP-SDK-Inference.md)
|
||||||
|
- [C++ 服务化部署](./docs/Windows-CPP-SDK-Serving.md)
|
||||||
|
- [Python Inference部署](./docs/Windows-Python-SDK-Inference.md)
|
||||||
|
- [Python 服务化部署](./docs/Windows-Python-SDK-Serving.md)
|
||||||
|
|
||||||
|
### 2 边缘侧部署
|
||||||
|
- ArmLinux 系统(NVIDIA Jetson Nano/TX2/Xavier)
|
||||||
|
- [C++ Inference部署(含视频流)](./docs/Jetson-Linux-CPP-SDK-Inference.md)
|
||||||
|
- [C++ 服务化部署](./docs/Jetson-Linux-CPP-SDK-Serving.md)
|
||||||
|
|
||||||
|
### 3 端侧部署
|
||||||
|
- ArmLinux 系统(ARM CPU)
|
||||||
|
- [C++ Inference部署(含视频流)](./docs/ARM-Linux-CPP-SDK-Inference.md)
|
||||||
|
- [C++ 服务化部署](./docs/ARM-Linux-CPP-SDK-Serving.md)
|
||||||
|
- [Python Inference部署](./docs/ARM-Linux-Python-SDK-Inference.md)
|
||||||
|
- [Python 服务化部署](./docs/ARM-Linux-Python-SDK-Serving.md)
|
||||||
|
|
||||||
|
### 4 移动端部署
|
||||||
|
- [iOS 系统部署](./docs/iOS-SDK.md)
|
||||||
|
- [Android 系统部署](./docs/Android-SDK.md)
|
||||||
|
|
||||||
|
### 5 自定义模型部署
|
||||||
|
- [快速实现个性化模型替换](./docs/Replace-Model-With-Anther-One.md)
|
||||||
|
|
||||||
|
## 社区交流
|
||||||
|
- **加入社区👬:** 微信扫描二维码后,填写问卷加入交流群,与开发者共同讨论推理部署痛点问题
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854075-2c0f9997-ed18-4b17-9aaf-1b43266d3996.jpeg" width = "200" height = "200" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Acknowledge
|
||||||
|
|
||||||
|
本项目中SDK生成和下载使用了[EasyEdge](https://ai.baidu.com/easyedge/app/openSource)中的免费开放能力,再次表示感谢。
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
FastDeploy遵循[Apache-2.0开源协议](./LICENSE)。
|
6
commit-prepare.sh
Normal file
6
commit-prepare.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
path=$(cd `dirname $0`; pwd)
|
||||||
|
cd $path
|
||||||
|
|
||||||
|
pip install pre-commit
|
||||||
|
pip install yapf
|
||||||
|
pre-commit install
|
404
docs/ARM-Linux-CPP-SDK-Inference.md
Normal file
404
docs/ARM-Linux-CPP-SDK-Inference.md
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在ARM Linux C++环境下 : (1)推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中ARM Linux Python请参考[ARM Linux Python环境下的推理部署](./ARM-Linux-Python-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
**注意**:部分模型(如Tinypose、OCR等)仅支持图像推理,不支持视频推理。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. 硬件支持](#1-硬件支持)
|
||||||
|
* [2. 软件环境](#2-软件环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1 预测图像](#21-预测图像)
|
||||||
|
* [2.2 预测视频流](#22-预测视频流)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. SDK参数运行配置](#1-sdk参数运行配置)
|
||||||
|
* [2. 初始化Predictor](#2-初始化predictor)
|
||||||
|
* [3. 预测推理](#3-预测推理)
|
||||||
|
* [3.1 预测图像](#31-预测图像)
|
||||||
|
* [3.2 预测视频](#32-预测视频)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. 硬件支持
|
||||||
|
|
||||||
|
目前支持的ARM架构:aarch64 、armv7hf
|
||||||
|
|
||||||
|
## 2. 软件环境
|
||||||
|
|
||||||
|
1.运行二进制文件-环境要求
|
||||||
|
|
||||||
|
* gcc: 5.4 以上 (GLIBCXX_3.4.22)
|
||||||
|
* Linux下查看gcc版本命名(可能因系统差异命令会不同):`gcc --version`
|
||||||
|
* Linux下C++基础库GLIBCXX的命令(因系统差异,库路径会有不同):`strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX`
|
||||||
|
* glibc:2.23以上
|
||||||
|
* Linux查看命令:`ldd --version`
|
||||||
|
|
||||||
|
2.二次开发编译-环境要求
|
||||||
|
|
||||||
|
编译源代码时,除gcc、GLIBCXX、glibc满足`1.运行二进制文件-环境要求`外,cmake需满足:
|
||||||
|
|
||||||
|
* cmake: 3.0 以上
|
||||||
|
|
||||||
|
* Linux查看命令:`cmake --version`
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-m43157-b97741-x86
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_ocr_linux_cpp_aarch64_ARM_gcc5.4_v1.5.1_20220530.tar.gz #armv8架构硬件的C++包,根据自己硬件,选择对应的压缩包解压即可
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
└── baidu_easyedge_ocr_linux_cpp_armv7l_armv7hf_ARM_gcc5.4_v1.5.1_20220530.tar.gz #armv7架构硬件的C++包,根据自己硬件,选择对应的压缩包解压即可
|
||||||
|
└── python # Python SDK 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:
|
||||||
|
|
||||||
|
1. 【OCR需要编译】因为OCR任务的特殊性,本次SDK没有提供bin文件夹可执行文件。开发者根据需要,满足文档中gcc和cmake要求后,在`src/demo*`路径编译获取可执行文件,具体可参考。
|
||||||
|
2. 【OCR仅支持图像推理,不支持视频流推理】
|
||||||
|
3. ARM-Linux-Python的环境要求和使用,请参考[ARM Linux Python环境下的推理部署](./ARM-Linux-Python-SDK.md)文档。
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1 预测图像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./easyedge_image_inference {模型RES文件夹路径} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855351-68d1a4f0-6226-4484-b190-65f1ac2c7128.png" width="400"></div>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> ./easyedge_image_inference ../../../../RES 2.jpeg
|
||||||
|
2019-02-13 16:46:12,659 INFO [EasyEdge] [easyedge.cpp:34] 140606189016192 Baidu EasyEdge Linux Development Kit 0.2.1(20190213)
|
||||||
|
2019-02-13 16:46:14,083 INFO [EasyEdge] [paddlev2_edge_predictor.cpp:60] 140606189016192 Allocate graph success.
|
||||||
|
2019-02-13 16:46:14,326 DEBUG [EasyEdge] [paddlev2_edge_predictor.cpp:143] 140606189016192 Inference costs 168 ms
|
||||||
|
1, 1:txt_frame, p:0.994905 loc: 0.168161, 0.153654, 0.920856, 0.779621
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 预测视频流
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_video_inference {模型RES文件夹路径} {video_type} {video_src_path}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 video_type 支持三种:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_type : 1 // 本地视频文件
|
||||||
|
video_type : 2 // 摄像头的index
|
||||||
|
video_type : 3 // 网络视频流
|
||||||
|
```
|
||||||
|
|
||||||
|
video_src_path: 为 video_type 数值所对应的本地视频路径 、本地摄像头id、网络视频流地址,如:
|
||||||
|
|
||||||
|
```
|
||||||
|
本地视频文件: ./easyedge_video_inference {模型RES文件夹路径} 1 ~/my_video_file.mp4
|
||||||
|
本地摄像头: ./easyedge_video_inference {模型RES文件夹路径} 2 1 #/dev/video1
|
||||||
|
网络视频流: ./easyedge_video_inference {模型RES文件夹路径} 3 rtmp://192.168.x.x:8733/live/src
|
||||||
|
```
|
||||||
|
|
||||||
|
注:以上路径是假模拟路径,开发者需要根据自己实际图像/视频,准备测试图像,并填写正确的测试路径。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.测试Demo](#4)的Demo示例介绍推理API,方便开发者学习后二次开发。更详细的API请参考`include/easyedge/easyedge*.h`文件。图像、视频的推理包含以下3个API,如下代码片段`step`注释所示。
|
||||||
|
|
||||||
|
> ❗注意:<br>
|
||||||
|
> (1)`src`文件夹中包含完整可编译的cmake工程实例,建议开发者先行了解[cmake工程基本知识](https://cmake.org/cmake/help/latest/guide/tutorial/index.html)。 <br>
|
||||||
|
> (2)请优先参考SDK中自带的Demo工程的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// step 1: SDK配置运行参数
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
|
||||||
|
// step 2: 创建并初始化Predictor;这这里选择合适的引擎
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
|
||||||
|
// step 3-1: 预测图像
|
||||||
|
auto img = cv::imread({图片路径});
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
predictor->infer(img, results);
|
||||||
|
|
||||||
|
// step 3-2: 预测视频
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
FrameTensor frame_tensor;
|
||||||
|
VideoConfig video_config;
|
||||||
|
video_config.source_type = static_cast<SourceType>(video_type); // source_type 定义参考头文件 easyedge_video.h
|
||||||
|
video_config.source_value = video_src;
|
||||||
|
/*
|
||||||
|
... more video_configs, 根据需要配置video_config的各选项
|
||||||
|
*/
|
||||||
|
auto video_decoding = CreateVideoDecoding(video_config);
|
||||||
|
while (video_decoding->next(frame_tensor) == EDGE_OK) {
|
||||||
|
results.clear();
|
||||||
|
if (frame_tensor.is_needed) {
|
||||||
|
predictor->infer(frame_tensor.frame, results);
|
||||||
|
render(frame_tensor.frame, results, predictor->model_info().kind);
|
||||||
|
}
|
||||||
|
//video_decoding->display(frame_tensor); // 显示当前frame,需在video_config中开启配置
|
||||||
|
//video_decoding->save(frame_tensor); // 存储当前frame到视频,需在video_config中开启配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若需自定义library search path或者gcc路径,修改对应Demo工程下的CMakeList.txt即可。
|
||||||
|
|
||||||
|
## 1. SDK参数运行配置
|
||||||
|
|
||||||
|
SDK的参数通过`EdgePredictorConfig::set_config`和`global_controller()->set_config`配置。本Demo 中设置了模型路径,其他参数保留默认参数。更详细的支持运行参数等,可以参考开发工具包中的头文件(`include/easyedge/easyedge_xxxx_config.h`)的详细说明。
|
||||||
|
|
||||||
|
配置参数使用方法如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化Predictor
|
||||||
|
|
||||||
|
* 接口
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
predictor->init();
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回非0,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. 预测推理
|
||||||
|
|
||||||
|
### 3.1 预测图像
|
||||||
|
|
||||||
|
> 在Demo中展示了预测接口infer()传入cv::Mat& image图像内容,并将推理结果赋值给std::vector<EdgeResultData>& result。更多关于infer()的使用,可以根据参考`easyedge.h`头文件中的实际情况、参数说明自行传入需要的内容做推理
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* 通用接口
|
||||||
|
* @param image: must be BGR , HWC format (opencv default)
|
||||||
|
* @param result
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int infer(cv::Mat& image, std::vector<EdgeResultData>& result) = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
图片的格式务必为opencv默认的BGR, HWC格式。
|
||||||
|
|
||||||
|
* 接口返回
|
||||||
|
|
||||||
|
`EdgeResultData`中可以获取对应的分类信息、位置信息。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct EdgeResultData {
|
||||||
|
int index; // 分类结果的index
|
||||||
|
std::string label; // 分类结果的label
|
||||||
|
float prob; // 置信度
|
||||||
|
|
||||||
|
// 物体检测 或 图像分割时使用:
|
||||||
|
float x1, y1, x2, y2; // (x1, y1): 左上角, (x2, y2): 右下角; 均为0~1的长宽比例值。
|
||||||
|
|
||||||
|
// 图像分割时使用:
|
||||||
|
cv::Mat mask; // 0, 1 的mask
|
||||||
|
std::string mask_rle; // Run Length Encoding,游程编码的mask
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于图像分割mask_rle ***
|
||||||
|
|
||||||
|
该字段返回了mask的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
以上字段可以参考demo文件中使用opencv绘制的逻辑进行解析
|
||||||
|
|
||||||
|
### 3.2 预测视频
|
||||||
|
|
||||||
|
SDK 提供了支持摄像头读取、视频文件和网络视频流的解析工具类`VideoDecoding`,此类提供了获取视频帧数据的便利函数。通过`VideoConfig`结构体可以控制视频/摄像头的解析策略、抽帧策略、分辨率调整、结果视频存储等功能。对于抽取到的视频帧可以直接作为SDK infer 接口的参数进行预测。
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
class`VideoDecoding`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 获取输入源的下一帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int next(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示当前frame_tensor中的视频帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int display(const FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将当前frame_tensor中的视频帧写为本地视频文件
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int save(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的fps属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_fps() = 0;
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的width属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_width() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的height属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_height() = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
struct `VideoConfig`
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 视频源、抽帧策略、存储策略的设置选项
|
||||||
|
*/
|
||||||
|
struct VideoConfig {
|
||||||
|
SourceType source_type; // 输入源类型
|
||||||
|
std::string source_value; // 输入源地址,如视频文件路径、摄像头index、网络流地址
|
||||||
|
int skip_frames{0}; // 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true
|
||||||
|
int retrieve_all{false}; // 是否抽取所有frame以便于作为显示和存储,对于不满足skip_frames策略的frame,把所抽取帧的is_needed置为false
|
||||||
|
int input_fps{0}; // 在采取抽帧之前设置视频的fps
|
||||||
|
Resolution resolution{Resolution::kAuto}; // 采样分辨率,只对camera有效
|
||||||
|
|
||||||
|
bool enable_display{false}; // 默认不支持。
|
||||||
|
std::string window_name{"EasyEdge"};
|
||||||
|
bool display_all{false}; // 是否显示所有frame,若为false,仅显示根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
bool enable_save{false};
|
||||||
|
std::string save_path; // frame存储为视频文件的路径
|
||||||
|
bool save_all{false}; // 是否存储所有frame,若为false,仅存储根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
std::map<std::string, std::string> conf;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
| 序号 | 字段 | 含义 |
|
||||||
|
| --- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 1 | `source_type` | 输入源类型,支持视频文件、摄像头、网络视频流三种,值分别为1、2、3 |
|
||||||
|
| 2 | `source_value` | 若`source_type`为视频文件,该值为指向视频文件的完整路径;若`source_type`为摄像头,该值为摄像头的index,如对于`/dev/video0`的摄像头,则index为0;若`source_type`为网络视频流,则为该视频流的完整地址。 |
|
||||||
|
| 3 | `skip_frames` | 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true,标记为is_needed的帧是用来做预测的帧。反之,直接跳过该帧,不经过预测。 |
|
||||||
|
| 4 | `retrieve_all` | 若置该项为true,则无论是否设置跳帧,所有的帧都会被抽取返回,以作为显示或存储用。 |
|
||||||
|
| 5 | `input_fps` | 用于抽帧前设置fps |
|
||||||
|
| 6 | `resolution` | 设置摄像头采样的分辨率,其值请参考`easyedge_video.h`中的定义,注意该分辨率调整仅对输入源为摄像头时有效 |
|
||||||
|
| 7 | `conf` | 高级选项。部分配置会通过该map来设置 |
|
||||||
|
|
||||||
|
*** 注意:***
|
||||||
|
|
||||||
|
1. `VideoConfig`不支持`display`功能。如果需要使用`VideoConfig`的`display`功能,需要自行编译带有GTK选项的OpenCV。
|
||||||
|
|
||||||
|
2. 使用摄像头抽帧时,如果通过`resolution`设置了分辨率调整,但是不起作用,请添加如下选项:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_config.conf["backend"] = "2";
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 部分设备上的CSI摄像头尚未兼容,如遇到问题,可以通过工单、QQ交流群或微信交流群反馈。
|
||||||
|
|
||||||
|
具体接口调用流程,可以参考SDK中的`demo_video_inference`。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
318
docs/ARM-Linux-CPP-SDK-Serving.md
Normal file
318
docs/ARM-Linux-CPP-SDK-Serving.md
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在ARM Linux C++环境下:(1)服务化推理部署步骤;(2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中ARM Linux Python请参考[ARM Linux Python环境下的HTTP推理部署](./ARM-Linux-Python-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
**注意**:部分模型(如OCR等)不支持服务化推理。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [安装准备](#安装准备)
|
||||||
|
|
||||||
|
* [1. 硬件支持](#1-硬件支持)
|
||||||
|
* [2. 软件环境](#2-软件环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试 HTTP Demo](#2-测试-http-demo)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式一:不使用图片base64格式](#21-http-请求方式一不使用图片base64格式)
|
||||||
|
* [2.2 http 请求方法二:使用图片base64格式](#22-http-请求方法二使用图片base64格式)
|
||||||
|
* [3. http返回数据](#3-http返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 安装准备
|
||||||
|
|
||||||
|
## 1. 硬件支持
|
||||||
|
|
||||||
|
目前支持的ARM架构:aarch64 、armv7hf
|
||||||
|
|
||||||
|
## 2. 软件环境
|
||||||
|
|
||||||
|
1.运行二进制文件-环境要求
|
||||||
|
|
||||||
|
* gcc: 5.4 以上 (GLIBCXX_3.4.22)
|
||||||
|
* Linux下查看gcc版本命名(可能因系统差异命令会不同):`gcc --version`;
|
||||||
|
* Linux下C++基础库GLIBCXX的命令(可能因系统差异路径会有不同,可检测自己环境下的情况):`strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX`
|
||||||
|
* glibc:2.23以上
|
||||||
|
* Linux查看命令:`ldd --version`
|
||||||
|
|
||||||
|
2.二次开发编译-环境要求
|
||||||
|
|
||||||
|
编译源代码时,除了gcc、GLIBCXX、glibc满足`1.运行二进制文件-环境要求`外,还需要cmake满足要求。
|
||||||
|
|
||||||
|
* cmake: 3.0 以上
|
||||||
|
|
||||||
|
* Linux查看命令:`cmake --version`
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压后SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-m43157-b97741-x86
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_linux_cpp_x86_64_CPU.Generic_gcc5.4_v1.4.0_20220325.tar.gz
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
└── python # Python SDK 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试 HTTP Demo
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1 启动HTTP预测服务
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_serving {模型RES文件夹路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后,日志中会显示如下设备IP和24401端口号信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP is now serving at 0.0.0.0:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
此时,开发者可以打开浏览器,输入链接地址`http://0.0.0.0:24401`(这里的`设备IP和24401端口号`根据开发者电脑显示修改),选择图片来进行测试。
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855495-cd8d46ec-2492-4297-b3e4-2bda4cd6727c.png" width="600"></div>
|
||||||
|
|
||||||
|
同时,可以调用HTTP接口来访问服务,具体参考下文的[二次开发](#10)接口说明。
|
||||||
|
|
||||||
|
# HTTP API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.1 HTTP Demo]()的API介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。http服务包含服务端和客户端,目前支持的能力包括以下几种方式,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动可直接使用`bin/easyedge_serving`,或参考`src/demo_serving.cpp`文件修改相关逻辑
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief 开启一个简单的demo http服务。
|
||||||
|
* 该方法会block直到收到sigint/sigterm。
|
||||||
|
* http服务里,图片的解码运行在cpu之上,可能会降低推理速度。
|
||||||
|
* @tparam ConfigT
|
||||||
|
* @param config
|
||||||
|
* @param host
|
||||||
|
* @param port
|
||||||
|
* @param service_id service_id user parameter, uri '/get/service_id' will respond this value with 'text/plain'
|
||||||
|
* @param instance_num 实例数量,根据内存/显存/时延要求调整
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template<typename ConfigT>
|
||||||
|
int start_http_server(
|
||||||
|
const ConfigT &config,
|
||||||
|
const std::string &host,
|
||||||
|
int port,
|
||||||
|
const std::string &service_id,
|
||||||
|
int instance_num = 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式一:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容(无需base64, 无需json)
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 http 请求方法二:使用图片base64格式
|
||||||
|
|
||||||
|
HTTP方法:POST
|
||||||
|
Header如下:
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
| ------------ | ---------------- |
|
||||||
|
| Content-Type | application/json |
|
||||||
|
|
||||||
|
**Body请求填写**:
|
||||||
|
|
||||||
|
* 分类网络:
|
||||||
|
body 中请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
"top_num": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| ------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| top_num | 否 | number | - | 返回分类数量,不填该参数,则默认返回全部分类结果 |
|
||||||
|
|
||||||
|
* 检测和分割网络:
|
||||||
|
Body请求示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情:
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| --------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| threshold | 否 | number | - | 默认为推荐阈值,也可自行根据需要进行设置 |
|
||||||
|
|
||||||
|
Python请求示例:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
def main():
|
||||||
|
with open("图像路径", 'rb') as f:
|
||||||
|
result = requests.post("http://{服务ip地址}:24401/", json={
|
||||||
|
"image": base64.b64encode(f.read()).decode("utf8")
|
||||||
|
})
|
||||||
|
# print(result.request.body)
|
||||||
|
# print(result.request.headers)
|
||||||
|
print(result.content)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. http返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 使用libcurl请求http服务时,速度明显变慢
|
||||||
|
|
||||||
|
这是因为libcurl请求continue导致server等待数据的问题,添加空的header即可
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headers = curl_slist_append(headers, "Expect:");
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
371
docs/ARM-Linux-Python-SDK-Inference.md
Normal file
371
docs/ARM-Linux-Python-SDK-Inference.md
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍FastDeploy中的模型SDK, 在**ARM Linux Python** 环境下:(1)图像推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。其中ARM Linux C++请参考[ARM Linux C++环境下的推理部署](./ARM-Linux-CPP-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
**注意**:部分模型(如Tinypose、OCR等)仅支持图像推理,不支持视频推理。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1.SDK下载](#1sdk下载)
|
||||||
|
* [2.硬件支持](#2硬件支持)
|
||||||
|
* [3.python环境](#3python环境)
|
||||||
|
* [4.安装依赖](#4安装依赖)
|
||||||
|
* [4.1.安装paddlepaddle](#41安装paddlepaddle)
|
||||||
|
* [4.2.安装EasyEdge Python Wheel 包](#42安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1.文件结构说明](#1文件结构说明)
|
||||||
|
* [2.测试Demo](#2测试demo)
|
||||||
|
* [2.1预测图像](#21预测图像)
|
||||||
|
|
||||||
|
* [Demo API介绍](#demo-api介绍)
|
||||||
|
|
||||||
|
* [1.基础流程](#1基础流程)
|
||||||
|
* [2.初始化](#2初始化)
|
||||||
|
* [3.SDK参数配置](#3sdk参数配置)
|
||||||
|
* [4.预测图像](#4预测图像)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1.SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86--[部署芯片]
|
||||||
|
├──...
|
||||||
|
├──python # Linux Python SDK
|
||||||
|
├── # 特定Python版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.3.1-cp36-cp36m-linux_aarch64.whl
|
||||||
|
├── infer_demo # demo体验完整文件
|
||||||
|
│ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
├── tensor_demo # 学习自定义算法前后处理时使用
|
||||||
|
│ └── demo_xxx.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.硬件支持
|
||||||
|
|
||||||
|
目前支持的ARM架构:aarch64 、armv7hf
|
||||||
|
|
||||||
|
## 3.python环境
|
||||||
|
|
||||||
|
> ARM Linux SDK仅支持Python 3.6
|
||||||
|
|
||||||
|
使用如下命令获取已安装的Python版本号。如果本机的版本不匹配,建议使用[pyenv](https://github.com/pyenv/pyenv)、[anaconda](https://www.anaconda.com/)等Python版本管理工具对SDK所在目录进行配置。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.安装依赖
|
||||||
|
|
||||||
|
### 4.1.安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。
|
||||||
|
|
||||||
|
`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install http://aipe-easyedge-public.bj.bcebos.com/easydeploy/paddlelite-2.11-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2.安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装特定Python版本的EasyEdge Wheel包。`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-1.3.1-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1.文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
.EasyEdge-Linux-x86--[部署芯片]
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ └── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── python # Python SDK 文件
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.3.1-cp36-cp36m-linux_aarch64.whl #EasyEdge Python Wheel 包
|
||||||
|
├── infer_demo
|
||||||
|
├── demo_armv8_cpu.py # 图像推理
|
||||||
|
├── demo_serving.py # HTTP服务化推理
|
||||||
|
└── tensor_demo # 学习自定义算法前后处理时使用
|
||||||
|
├── demo_armv8_cpu.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.测试Demo
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中, 默认为`RES`目录。
|
||||||
|
|
||||||
|
### 2.1预测图像
|
||||||
|
|
||||||
|
使用infer_demo文件夹下的demo文件。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 demo_x86_cpu.py {模型RES文件夹} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854068-28d27c0a-ef83-43ee-9e89-b65eed99b476.jpg" width="300"></div>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
2022-06-14 14:40:16 INFO [EasyEdge] [demo_nvidia_gpu.py:38] 140518522509120: Init paddlefluid engine...
|
||||||
|
2022-06-14 14:40:20 INFO [EasyEdge] [demo_nvidia_gpu.py:38] 140518522509120: Paddle version: 2.2.2
|
||||||
|
{'confidence': 0.9012349843978882, 'index': 8, 'label': 'n01514859 hen'}
|
||||||
|
```
|
||||||
|
|
||||||
|
可以看到,运行结果为`index:8,label:hen`,通过imagenet [类别映射表](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a),可以找到对应的类别,即 'hen',由此说明我们的预测结果正确。
|
||||||
|
|
||||||
|
# Demo API介绍
|
||||||
|
|
||||||
|
本章节主要结合[测试Demo](#2测试Demo)的Demo示例介绍推理API,方便开发者学习后二次开发。
|
||||||
|
|
||||||
|
## 1.基础流程
|
||||||
|
|
||||||
|
> ❗注意,请优先参考SDK中自带demo的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
`infer_demo/demo_xx_xx.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 引入EasyEdge运行库
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
|
||||||
|
# 创建并初始化一个预测Progam;选择合适的引擎
|
||||||
|
pred = edge.Program()
|
||||||
|
pred.init(model_dir={RES文件夹路径}, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
|
||||||
|
# 预测图像
|
||||||
|
res = pred.infer_image({numpy.ndarray的图片})
|
||||||
|
|
||||||
|
# 关闭结束预测Progam
|
||||||
|
pred.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
`infer_demo/demo_serving.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
from BaiduAI.EasyEdge.serving import Serving
|
||||||
|
|
||||||
|
# 创建并初始化Http服务
|
||||||
|
server = Serving(model_dir={RES文件夹路径}, license=serial_key)
|
||||||
|
|
||||||
|
# 运行Http服务
|
||||||
|
# 请参考同级目录下demo_xx_xx.py里:
|
||||||
|
# pred.init(model_dir=xx, device=xx, engine=xx, device_id=xx)
|
||||||
|
# 对以下参数device\device_id和engine进行修改
|
||||||
|
server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.初始化
|
||||||
|
|
||||||
|
* 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def init(self,
|
||||||
|
model_dir,
|
||||||
|
device=Device.CPU,
|
||||||
|
engine=Engine.PADDLE_FLUID,
|
||||||
|
config_file='conf.json',
|
||||||
|
preprocess_file='preprocess_args.json',
|
||||||
|
model_file='model',
|
||||||
|
params_file='params',
|
||||||
|
label_file='label_list.txt',
|
||||||
|
infer_cfg_file='infer_cfg.json',
|
||||||
|
device_id=0,
|
||||||
|
thread_num=1
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
model_dir: str
|
||||||
|
device: BaiduAI.EasyEdge.Device,比如:Device.CPU
|
||||||
|
engine: BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID
|
||||||
|
config_file: str
|
||||||
|
preprocess_file: str
|
||||||
|
model_file: str
|
||||||
|
params_file: str
|
||||||
|
label_file: str 标签文件
|
||||||
|
infer_cfg_file: 包含预处理、后处理信息的文件
|
||||||
|
device_id: int 设备ID
|
||||||
|
thread_num: int CPU的线程数
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError, IOError
|
||||||
|
Returns:
|
||||||
|
bool: True if success
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回不是True,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3.SDK参数配置
|
||||||
|
|
||||||
|
使用 CPU 预测时,可以通过在 init 中设置 thread_num 使用多线程预测。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID, thread_num=4)
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 GPU 预测时,可以通过在 init 中设置 device_id 指定需要的GPU device id。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID, device_id=0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.预测图像
|
||||||
|
|
||||||
|
* 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def infer_image(self, img,
|
||||||
|
threshold=0.3,
|
||||||
|
channel_order='HWC',
|
||||||
|
color_format='BGR',
|
||||||
|
data_type='numpy'):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
img: np.ndarray or bytes
|
||||||
|
threshold: float
|
||||||
|
only return result with confidence larger than threshold
|
||||||
|
channel_order: string
|
||||||
|
channel order HWC or CHW
|
||||||
|
color_format: string
|
||||||
|
color format order RGB or BGR
|
||||||
|
data_type: string
|
||||||
|
仅在图像分割时有意义。 'numpy' or 'string'
|
||||||
|
'numpy': 返回已解析的mask
|
||||||
|
'string': 返回未解析的mask游程编码
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list
|
||||||
|
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
* 返回格式: `[dict1, dict2, ...]`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 取值 | 说明 |
|
||||||
|
| ---------- | -------------------- | --------- | ------------------------ |
|
||||||
|
| confidence | float | 0~1 | 分类或检测的置信度 |
|
||||||
|
| label | string | | 分类或检测的类别 |
|
||||||
|
| index | number | | 分类或检测的类别 |
|
||||||
|
| x1, y1 | float | 0~1 | 物体检测,矩形的左上角坐标 (相对长宽的比例值) |
|
||||||
|
| x2, y2 | float | 0~1 | 物体检测,矩形的右下角坐标(相对长宽的比例值) |
|
||||||
|
| mask | string/numpy.ndarray | 图像分割的mask | |
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
可以参考 demo 文件中使用 opencv 绘制矩形的逻辑。
|
||||||
|
|
||||||
|
***结果示例***
|
||||||
|
|
||||||
|
i) 图像分类
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 736,
|
||||||
|
"label": "table",
|
||||||
|
"confidence": 0.9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
ii) 物体检测
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 8,
|
||||||
|
"label": "cat",
|
||||||
|
"confidence": 1.0,
|
||||||
|
"x1": 0.21289,
|
||||||
|
"y1": 0.12671,
|
||||||
|
"x2": 0.91504,
|
||||||
|
"y2": 0.91211,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
iii) 图像分割
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "cat",
|
||||||
|
"score": 1.0,
|
||||||
|
"location": {
|
||||||
|
"left": ...,
|
||||||
|
"top": ...,
|
||||||
|
"width": ...,
|
||||||
|
"height": ...,
|
||||||
|
},
|
||||||
|
"mask": ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
mask字段中,data_type为`numpy`时,返回图像掩码的二维数组
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
data_type为`string`时,mask的游程编码,解析方式可参考 [demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1.执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
266
docs/ARM-Linux-Python-SDK-Serving.md
Normal file
266
docs/ARM-Linux-Python-SDK-Serving.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍FastDeploy中的模型SDK, 在**ARM Linux Python** 环境下: (1)**服务化**推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。其中ARM Linux Python请参考[ARM Linux C++环境下的HTTP推理部署](./ARM-Linux-CPP-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
**注意**:部分模型(如OCR等)不支持服务化推理。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1.SDK下载](#1sdk下载)
|
||||||
|
* [2.硬件支持](#2硬件支持)
|
||||||
|
* [3.Python环境](#3python环境)
|
||||||
|
* [4.安装依赖](#4安装依赖)
|
||||||
|
* [4.1.安装paddlepaddle](#41安装paddlepaddle)
|
||||||
|
* [4.2.安装EasyEdge Python Wheel 包](#42安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1.文件结构说明](#1文件结构说明)
|
||||||
|
* [2.测试Serving服务](#2测试serving服务)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式:不使用图片base64格式](#21-http-请求方式不使用图片base64格式)
|
||||||
|
* [3. http返回数据](#3-http返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1.SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压缩后的文件结构如下。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86-[部署芯片]
|
||||||
|
├── RES # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
├── README.md
|
||||||
|
├── cpp # C++ SDK
|
||||||
|
└── python # Python SDK
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.硬件支持
|
||||||
|
|
||||||
|
目前支持的ARM架构:aarch64 、armv7hf
|
||||||
|
|
||||||
|
## 3.Python环境
|
||||||
|
|
||||||
|
> ARM Linux SDK仅支持Python 3.6
|
||||||
|
|
||||||
|
使用如下命令获取已安装的Python版本号。如果本机的版本不匹配,需要根据ARM Linux下Python安装方式进行安装。(不建议在ARM Linux下使用conda,因为ARM Linux场景通常资源很有限)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.安装依赖
|
||||||
|
|
||||||
|
### 4.1.安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。
|
||||||
|
|
||||||
|
`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install http://aipe-easyedge-public.bj.bcebos.com/easydeploy/paddlelite-2.11-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2.安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装特定Python版本的EasyEdge Wheel包。`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-1.3.1-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
# 二.快速开始
|
||||||
|
|
||||||
|
## 1.文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86--[部署芯片]
|
||||||
|
├──...
|
||||||
|
├──python # Linux Python SDK
|
||||||
|
├── # 特定Python版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
├── BBaiduAI_EasyEdge_SDK-1.3.1-cp36-cp36m-linux_aarch64.whl
|
||||||
|
├── infer_demo # demo体验完整文件
|
||||||
|
│ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
├── tensor_demo # 学习自定义算法前后处理时使用
|
||||||
|
│ └── demo_xxx.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.测试Serving服务
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中, 默认为`RES`目录。
|
||||||
|
|
||||||
|
### 2.1 启动HTTP预测服务
|
||||||
|
|
||||||
|
指定对应的模型文件夹(默认为`RES`)、设备ip和指定端口号,运行如下命令。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 demo_serving.py {模型RES文件夹} {host, default 0.0.0.0} {port, default 24401}
|
||||||
|
```
|
||||||
|
|
||||||
|
成功启动后,终端中会显示如下字样。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
...
|
||||||
|
* Running on {host ip}:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
如果是在局域网内的机器上部署,开发者此时可以打开浏览器,输入`http://{host ip}:24401`,选择图片来进行测试,运行效果如下。
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854073-fb8189e5-0ffb-472c-a17d-0f35aa6a8418.png" style="zoom:50%;" />
|
||||||
|
|
||||||
|
如果是在远程机器上部署,那么可以参考`demo_serving.py`中的 `http_client_test()函数`请求http服务来执行推理。
|
||||||
|
|
||||||
|
# 三. HTTP API流程详解
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动使用`demo_serving.py`文件
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Serving(object):
|
||||||
|
"""
|
||||||
|
SDK local serving
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, model_dir, license='', model_filename='model', params_filename='params'):
|
||||||
|
|
||||||
|
self.program = None
|
||||||
|
self.model_dir = model_dir
|
||||||
|
self.model_filename = model_filename
|
||||||
|
self.params_filename = params_filename
|
||||||
|
self.program_lock = threading.Lock()
|
||||||
|
self.license_key = license
|
||||||
|
# 只有ObjectTracking会初始化video_processor
|
||||||
|
self.video_processor = None
|
||||||
|
|
||||||
|
def run(self, host, port, device, engine=Engine.PADDLE_FLUID, service_id=0, device_id=0, **kwargs):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
host : str
|
||||||
|
port : str
|
||||||
|
device : BaiduAI.EasyEdge.Device,比如:Device.CPU
|
||||||
|
engine : BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID
|
||||||
|
"""
|
||||||
|
self.run_serving_with_flask(host, port, device, engine, service_id, device_id, **kwargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. http返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1.执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
404
docs/Android-SDK.md
Normal file
404
docs/Android-SDK.md
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在Android环境下:(1)推理操作步骤;(2)介绍模型SDK使用说明,方便开发者了解项目后二次开发。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [系统支持说明](#系统支持说明)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. APP 标准版测试](#2-app-标准版测试)
|
||||||
|
* [2.1 扫码体验](#21-扫码体验)
|
||||||
|
* [2.2 源码运行](#22-源码运行)
|
||||||
|
* [3. 精简版测试](#3-精简版测试)
|
||||||
|
|
||||||
|
* [SDK使用说明](#sdk使用说明)
|
||||||
|
|
||||||
|
* [1. 集成指南](#1-集成指南)
|
||||||
|
* [1.1 依赖库集成](#11-依赖库集成)
|
||||||
|
* [1.2 添加权限](#12-添加权限)
|
||||||
|
* [1.3 混淆规则(可选)](#13-混淆规则可选)
|
||||||
|
* [2. API调用流程示例](#2-api调用流程示例)
|
||||||
|
* [2.1 初始化](#21-初始化)
|
||||||
|
* [2.2 预测图像](#22-预测图像)
|
||||||
|
|
||||||
|
* [错误码](#错误码)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 系统支持说明
|
||||||
|
|
||||||
|
1. Android 版本支持范围:Android 5.0(API21)<= Android < Android 10(API 29)。
|
||||||
|
|
||||||
|
2. 硬件支持情况:支持 arm64-v8a 和 armeabi-v7a,暂不支持模拟器。
|
||||||
|
* 官网测试机型:红米k30,Vivo v1981a,华为oxp-an00,华为cdy-an90,华为pct-al10,荣耀yal-al00,OPPO Reno5 Pro 5G
|
||||||
|
3. 其他说明
|
||||||
|
* 【图像分割类算法】(1)图像分割类算法,暂未提供实时摄像头推理功能,开发者可根据自己需要,进行安卓开发;(2)PP-Humanseg-Lite模型设计初衷为横屏视频会议等场景,本次安卓SDK仅支持竖屏场景,开发者可根据自己需要,开发横屏功能。
|
||||||
|
* 【OCR模型】OCR任务第一次启动任务,第一张推理时间久,属于正常情况(因为涉及到模型加载、预处理等工作)。
|
||||||
|
|
||||||
|
> 预测图像时运行内存不能过小,一般大于模型资源文件夹大小的3倍。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Android-SDK
|
||||||
|
├── app
|
||||||
|
│ ├── src/main
|
||||||
|
│ │ ├── assets
|
||||||
|
│ │ │ ├── demo
|
||||||
|
│ │ │ │ └── conf.json # APP名字
|
||||||
|
│ │ │ ├── infer # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ │ │ │ ├── model # 模型结构文件
|
||||||
|
│ │ │ │ ├── params # 模型参数文件
|
||||||
|
│ │ │ │ ├── label_list.txt # 模型标签文件
|
||||||
|
│ │ │ │ └── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
│ │ ├── java/com.baidu.ai.edge/demo
|
||||||
|
│ │ │ ├── infertest # 通用ARM精简版测试
|
||||||
|
│ │ │ │ ├── TestInferClassifyTask.java # 图像分类
|
||||||
|
│ │ │ │ ├── TestInferDetectionTask.java # 物体检测
|
||||||
|
│ │ │ │ ├── TestInferSegmentTask.java # 实例分割
|
||||||
|
│ │ │ │ ├── TestInferPoseTask.java # 姿态估计
|
||||||
|
│ │ │ │ ├── TestInferOcrTask.java # OCR
|
||||||
|
│ │ │ │ └── MainActivity.java # 精简版启动 Activity
|
||||||
|
│ │ │ ├── MainActivity.java # Demo APP 启动 Activity
|
||||||
|
│ │ │ ├── CameraActivity.java # 摄像头UI逻辑
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── libs
|
||||||
|
│ │ ├── armeabi-v7a # v7a的依赖库
|
||||||
|
│ │ ├── arm64-v8a # v8a的依赖库
|
||||||
|
│ │ └── easyedge-sdk.jar # jar文件
|
||||||
|
│ └── ...
|
||||||
|
├── camera_ui # UI模块,包含相机逻辑
|
||||||
|
├── README.md
|
||||||
|
└── ... # 其他 gradle 等工程文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. APP 标准版测试
|
||||||
|
|
||||||
|
考虑部分Android开发板没有摄像头,因此本项目开发了标准版和精简版两种。标准版会调用Android系统的摄像头,采集摄像头来进行AI模型推理;精简版在没有摄像头的开发板上运行,需要开发者准备图像。开发者根据硬件情况,选择对应的版本。
|
||||||
|
|
||||||
|
### 2.1 扫码体验
|
||||||
|
|
||||||
|
扫描二维码(二维码见下载网页`体验Demo`),无需任何依赖,手机上下载即可直接体验。
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854064-a31755d1-52b9-416d-b35d-885b7338a6cc.png" width="600"></div>
|
||||||
|
|
||||||
|
### 2.2 源码运行
|
||||||
|
|
||||||
|
(1)下载对应的SDK,解压工程。</br>
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854071-f4c17de8-83c2-434e-882d-c175f4202a2d.png" width="600"></div>
|
||||||
|
(2)打开Android Studio, 点击 "Import Project...",即:File->New-> "Import Project...", 选择解压后的目录。</br>
|
||||||
|
(3)手机链接Android Studio,并打开开发者模式。(不了解开发者模式的开发者,可浏览器搜索)</br>
|
||||||
|
(4)此时点击运行按钮,手机上会有新app安装完毕,运行效果和二维码扫描的一样。</br>
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854049-988414c7-116a-4261-a0c7-2705cc199538.png" width="400"></div>
|
||||||
|
|
||||||
|
## 3. 精简版测试
|
||||||
|
|
||||||
|
* 考虑部分Android开发板没有摄像头,本项目提供了精简版本,精简版忽略摄像头等UI逻辑,可兼容如无摄像头的开发板测试。
|
||||||
|
|
||||||
|
* 精简版对应的测试图像路径,在代码`src/main/java/com.baidu.ai.edge/demo/TestInfer*.java`中进行了设置,开发者可以准备图像到对应路径测试,也可以修改java代码测试。
|
||||||
|
|
||||||
|
* 支持以下硬件环境的精简版测试:通用ARM:图像分类、物体检测、实例分割、姿态估计、文字识别。
|
||||||
|
|
||||||
|
示例代码位于 app 模块下 infertest 目录,修改 app/src/main/AndroidManifest.xml 中的启动 Activity 开启测试。
|
||||||
|
修改前:
|
||||||
|
|
||||||
|
```
|
||||||
|
<activity android:name=".MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
infertest.MainActivity
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".CameraActivity"
|
||||||
|
android:screenOrientation="portrait" >
|
||||||
|
</activity>
|
||||||
|
```
|
||||||
|
|
||||||
|
修改后:
|
||||||
|
|
||||||
|
```
|
||||||
|
<!-- 以通用ARM为例 -->
|
||||||
|
<activity android:name=".infertest.MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:修改后,因为没有测试数据,需要开发者准备一张测试图像,放到 `app/src/main/asserts/` 路径下,并按照`app/src/main/java/com/baidu/ai/edge/demo/infertest/TestInfer*.java`中的图像命名要求对图像进行命名。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
| Demo APP 检测模型运行示例 | 精简版检测模型运行示例 |
|
||||||
|
| --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
|  |  |
|
||||||
|
</div>
|
||||||
|
|
||||||
|
# SDK使用说明
|
||||||
|
|
||||||
|
本节介绍如何将 SDK 接入开发者的项目中使用。
|
||||||
|
|
||||||
|
## 1. 集成指南
|
||||||
|
|
||||||
|
步骤一:依赖库集成
|
||||||
|
步骤二:添加必要权限
|
||||||
|
步骤三:混淆配置(可选)
|
||||||
|
|
||||||
|
### 1.1 依赖库集成
|
||||||
|
|
||||||
|
A. 项目中未集成其他 jar 包和 so 文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
// 1. 复制 app/libs 至项目的 app/libs 目录
|
||||||
|
// 2. 参考 app/build.gradle 配置 NDK 可用架构和 so 依赖库目录
|
||||||
|
|
||||||
|
android {
|
||||||
|
...
|
||||||
|
defaultConfig {
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
jniLibs.srcDirs = ['libs']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
B. 项目中已集成其他 jar 包,未集成 so 文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
// 1. 复制 app/libs/easyedge-sdk.jar 与其他 jar 包同目录
|
||||||
|
// 2. 复制 app/libs 下 armeabi-v7a 和 arm64-v8a 目录至 app/src/main/jniLibs 目录下
|
||||||
|
// 3. 参考 app/build.gradle 配置 NDK 可用架构
|
||||||
|
|
||||||
|
android {
|
||||||
|
...
|
||||||
|
defaultConfig {
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
C. 项目中已集成其他 jar 包和 so 文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
// 1. 复制 app/libs/easyedge-sdk.jar 与其他 jar 包同目录
|
||||||
|
// 2. 融合 app/libs 下 armeabi-v7a 和 arm64-v8a 下的 so 文件与其他同架构 so 文件同目录
|
||||||
|
// 3. 参考 app/build.gradle 配置 NDK 可用架构
|
||||||
|
|
||||||
|
android {
|
||||||
|
...
|
||||||
|
defaultConfig {
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a' // 只支持 v7a 和 v8a 两种架构,有其他架构需删除
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 添加权限
|
||||||
|
|
||||||
|
参考 app/src/main/AndroidManifest.xml 中配置的权限。
|
||||||
|
|
||||||
|
```
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 混淆规则(可选)
|
||||||
|
|
||||||
|
请不要混淆 jar 包文件,参考 app/proguard-rules.pro 配置。
|
||||||
|
|
||||||
|
```
|
||||||
|
-keep class com.baidu.ai.edge.core.*.*{ *; }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. API调用流程示例
|
||||||
|
|
||||||
|
以通用ARM的图像分类预测流程为例,详细说明请参考后续章节:
|
||||||
|
|
||||||
|
```
|
||||||
|
try {
|
||||||
|
// step 1-1: 准备配置类
|
||||||
|
InferConfig config = new InferConfig(context.getAssets(), "infer");
|
||||||
|
|
||||||
|
// step 1-2: 准备预测 Manager
|
||||||
|
InferManager manager = new InferManager(context, config, "");
|
||||||
|
|
||||||
|
// step 2-1: 准备待预测的图像,必须为 Bitmap.Config.ARGB_8888 格式,一般为默认格式
|
||||||
|
Bitmap image = getFromSomeWhere();
|
||||||
|
|
||||||
|
// step 2-2: 预测图像
|
||||||
|
List<ClassificationResultModel> results = manager.classify(image, 0.3f);
|
||||||
|
|
||||||
|
// step 3: 解析结果
|
||||||
|
for (ClassificationResultModel resultModel : results) {
|
||||||
|
Log.i(TAG, "labelIndex=" + resultModel.getLabelIndex()
|
||||||
|
+ ", labelName=" + resultModel.getLabel()
|
||||||
|
+ ", confidence=" + resultModel.getConfidence());
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 4: 释放资源。预测完毕请及时释放资源
|
||||||
|
manager.destroy();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, e.getMessage());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 初始化
|
||||||
|
|
||||||
|
**准备配置类**
|
||||||
|
芯片与配置类对应关系:
|
||||||
|
|
||||||
|
- 通用ARM:InferConfig
|
||||||
|
|
||||||
|
```
|
||||||
|
// 示例
|
||||||
|
// 参数二为芯片对应的模型资源文件夹名称
|
||||||
|
InferConfig config = new InferConfig(context.getAssets(), "infer");
|
||||||
|
```
|
||||||
|
|
||||||
|
**准备预测 Manager**
|
||||||
|
芯片与 Manager 对应关系:
|
||||||
|
|
||||||
|
- 通用ARM:InferManager
|
||||||
|
|
||||||
|
```
|
||||||
|
// 示例
|
||||||
|
// 参数二为配置类对象
|
||||||
|
// 参数三保持空字符串即可
|
||||||
|
InferManager manager = new InferManager(context, config, "");
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意**
|
||||||
|
>
|
||||||
|
> 1. 同一时刻只能有且唯一有效的 Manager,若要新建一个 Manager,之前创建的 Manager 需先调用 destroy() 销毁;
|
||||||
|
> 2. Manager 的任何方法都不能在 UI 线程调用;
|
||||||
|
> 3. Manager 的任何成员变量及方法由于线程同步问题,都必须在同一个线程中执行;
|
||||||
|
|
||||||
|
### 2.2 预测图像
|
||||||
|
|
||||||
|
本节介绍各种模型类型的预测函数及结果解析。
|
||||||
|
|
||||||
|
> **注意**
|
||||||
|
> 预测函数可以多次调用,但必须在同一个线程中,不支持并发
|
||||||
|
> 预测函数中的 confidence 非必需,默认使用模型推荐值。填 0 可返回所有结果
|
||||||
|
> 待预测的图像必须为 Bitmap.Config.ARGB_8888 格式的 Bitmap
|
||||||
|
|
||||||
|
**图像分类**
|
||||||
|
|
||||||
|
```
|
||||||
|
// 预测函数
|
||||||
|
List<ClassificationResultModel> classify(Bitmap bitmap) throws BaseException;
|
||||||
|
List<ClassificationResultModel> classify(Bitmap bitmap, float confidence) throws BaseException;
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
ClassificationResultModel
|
||||||
|
- label: 分类标签,定义在label_list.txt中
|
||||||
|
- labelIndex: 分类标签对应的序号
|
||||||
|
- confidence: 置信度,0-1
|
||||||
|
```
|
||||||
|
|
||||||
|
**物体检测**
|
||||||
|
|
||||||
|
```
|
||||||
|
// 预测函数
|
||||||
|
List<DetectionResultModel> detect(Bitmap bitmap) throws BaseException;
|
||||||
|
List<DetectionResultModel> detect(Bitmap bitmap, float confidence) throws BaseException;
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
DetectionResultModel
|
||||||
|
- label: 标签,定义在label_list.txt中
|
||||||
|
- confidence: 置信度,0-1
|
||||||
|
- bounds: Rect,包含左上角和右下角坐标,指示物体在图像中的位置
|
||||||
|
```
|
||||||
|
|
||||||
|
**实例分割**
|
||||||
|
|
||||||
|
```
|
||||||
|
// 预测函数
|
||||||
|
List<SegmentationResultModel> segment(Bitmap bitmap) throws BaseException;
|
||||||
|
List<SegmentationResultModel> segment(Bitmap bitmap, float confidence) throws BaseException;
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
SegmentationResultModel
|
||||||
|
- label: 标签,定义在label_list.txt中
|
||||||
|
- confidence: 置信度,0-1
|
||||||
|
- lableIndex: 标签对应的序号
|
||||||
|
- box: Rect,指示物体在图像中的位置
|
||||||
|
- mask: byte[],表示原图大小的0,1掩码,绘制1的像素即可得到当前对象区域
|
||||||
|
- maskLEcode: mask的游程编码
|
||||||
|
```
|
||||||
|
|
||||||
|
> 关于 maskLEcode 的解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
**姿态估计**
|
||||||
|
|
||||||
|
```
|
||||||
|
// 预测函数
|
||||||
|
List<PoseResultModel> pose(Bitmap bitmap) throws BaseException;
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
PoseResultModel
|
||||||
|
- label: 标签,定义在label_list.txt中
|
||||||
|
- confidence: 置信度,0-1
|
||||||
|
- points: Pair<Point, Point>, 2个点构成一条线
|
||||||
|
```
|
||||||
|
|
||||||
|
**文字识别**
|
||||||
|
|
||||||
|
```
|
||||||
|
// 预测函数
|
||||||
|
List<OcrResultModel> ocr(Bitmap bitmap) throws BaseException;
|
||||||
|
List<OcrResultModel> ocr(Bitmap bitmap, float confidence) throws BaseException;
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
OcrResultModel
|
||||||
|
- label: 识别出的文字
|
||||||
|
- confidence: 置信度,0-1
|
||||||
|
- points: List<Point>, 文字所在区域的点位
|
||||||
|
```
|
||||||
|
|
||||||
|
# 错误码
|
||||||
|
|
||||||
|
| 错误码 | 错误描述 | 详细描述及解决方法 |
|
||||||
|
| ---- | ------------------------------ | ------------------------------------------------------------------------------------ |
|
||||||
|
| 1001 | assets 目录下用户指定的配置文件不存在 | SDK可以使用assets目录下config.json作为配置文件。如果传入的config.json不在assets目录下,则有此报错 |
|
||||||
|
| 1002 | 用户传入的配置文件作为json解析格式不准确,如缺少某些字段 | 正常情况下,demo中的config.json不要修改 |
|
||||||
|
| 19xx | Sdk内部错误 | 请与百度人员联系 |
|
||||||
|
| 2001 | XxxxMANAGER 只允许一个实例 | 如已有XxxxMANAGER对象,请调用destory方法 |
|
||||||
|
| 2002 | XxxxMANAGER 已经调用过destory方法 | 在一个已经调用destory方法的DETECT_MANAGER对象上,不允许再调用任何方法 |
|
||||||
|
| 2003 | 传入的assets下模型文件路径为null | XxxxConfig.getModelFileAssetPath() 返回为null。由setModelFileAssetPath(null)导致 |
|
||||||
|
| 2011 | libedge-xxxx.so 加载失败 | System.loadLibrary("edge-xxxx"); libedge-xxxx.so 没有在apk中。CPU架构仅支持armeabi-v7a arm-v8a |
|
||||||
|
| 2012 | JNI内存错误 | heap的内存不够 |
|
||||||
|
| 2103 | license过期 | license失效或者系统时间有异常 |
|
||||||
|
| 2601 | assets 目录下模型文件打开失败 | 请根据报错信息检查模型文件是否存在 |
|
||||||
|
| 2611 | 检测图片时,传递至引擎的图片二进制与长宽不符合 | 具体见报错信息 |
|
||||||
|
| 27xx | Sdk内部错误 | 请与百度人员联系 |
|
||||||
|
| 28xx | 引擎内部错误 | 请与百度人员联系 |
|
||||||
|
| 29xx | Sdk内部错误 | 请与百度人员联系 |
|
||||||
|
| 3000 | so加载错误 | 请确认所有so文件存在于apk中 |
|
||||||
|
| 3001 | 模型加载错误 | 请确认模型放置于能被加载到的合法路径中,并确保config.json配置正确 |
|
||||||
|
| 3002 | 模型卸载错误 | 请与百度人员联系 |
|
||||||
|
| 3003 | 调用模型错误 | 在模型未加载正确或者so库未加载正确的情况下调用了分类接口 |
|
||||||
|
| 50xx | 在线模式调用异常 | 请与百度人员联系 |
|
382
docs/Jetson-Linux-CPP-SDK-Inference.md
Normal file
382
docs/Jetson-Linux-CPP-SDK-Inference.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK, 在**Jetson Linux C++** 环境下:(1) 图像和视频 推理部署步骤, (2)介绍推理全流程API,方便开发者了解项目后二次开发。如果开发者对Jetson的服务化部署感兴趣,可以参考[Jetson CPP Serving](./Jetson-Linux-CPP-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
**注意**:OCR目前只支持**图像**推理部署。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境要求](#环境要求)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1 预测图像](#21-预测图像)
|
||||||
|
* [2.2 预测视频流](#22-预测视频流)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. SDK参数运行配置](#1-sdk参数运行配置)
|
||||||
|
* [2. 初始化Predictor](#2-初始化predictor)
|
||||||
|
* [3. 预测推理](#3-预测推理)
|
||||||
|
* [3.1 预测图像](#31-预测图像)
|
||||||
|
* [3.2 预测视频](#32-预测视频)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境要求
|
||||||
|
|
||||||
|
* Jetpack: 4.6,安装Jetpack,参考[NVIDIA 官网-Jetpack4.6安装指南](https://developer.nvidia.com/jetpack-sdk-46),或者参考采购的硬件厂商提供的安装方式进行安装。![]()
|
||||||
|
|
||||||
|
| 序号 | 硬件 | Jetpack安装方式 | 下载链接 | ---- |
|
||||||
|
| --- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||||
|
| 1 | Jetson Xavier NX | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jetson_xavier_nx/jetson-nx-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 2 | Jetson Nano | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jeston_nano/jetson-nano-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 3 | Jetson Nano 2GB | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jeston_nano_2gb/jetson-nano-2gb-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 4 | agx xavier等 | NVIDIA SDK Manager | [Download NVIDIA SDK](https://developer.nvidia.com/nvsdk-manager) | ---- |
|
||||||
|
| 5 | 非官方版本,如emmc版 | 参考采购的硬件公司提供的安装指南 | ---- | ---- |
|
||||||
|
|
||||||
|
注意:本项目SDK要求 `CUDA=10.2`、`cuDNN=8.2`、`TensorRT=8.0`、`gcc>=7.5` 、`cmake 在 3.0以上` ,安装 Jetpack4.6系统包后,CUDA、cuDNN、TensorRT、gcc和cmake版本就已经满足要求,无需在进行安装。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压后SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-硬件芯片
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_linux_cpp_x86_64_CPU.Generic_gcc5.4_v1.4.0_20220325.tar.gz
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1 预测图像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./easyedge_image_inference {模型RES文件夹路径} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855351-68d1a4f0-6226-4484-b190-65f1ac2c7128.png" width="400"></div>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> ./easyedge_image_inference ../../../../RES 2.jpeg
|
||||||
|
2019-02-13 16:46:12,659 INFO [EasyEdge] [easyedge.cpp:34] 140606189016192 Baidu EasyEdge Linux Development Kit 0.2.1(20190213)
|
||||||
|
2019-02-13 16:46:14,083 INFO [EasyEdge] [paddlev2_edge_predictor.cpp:60] 140606189016192 Allocate graph success.
|
||||||
|
2019-02-13 16:46:14,326 DEBUG [EasyEdge] [paddlev2_edge_predictor.cpp:143] 140606189016192 Inference costs 168 ms
|
||||||
|
1, 1:txt_frame, p:0.994905 loc: 0.168161, 0.153654, 0.920856, 0.779621
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 预测视频流
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_video_inference {模型RES文件夹路径} {video_type} {video_src_path}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 video_type 支持三种:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_type : 1 // 本地视频文件
|
||||||
|
video_type : 2 // 摄像头的index
|
||||||
|
video_type : 3 // 网络视频流
|
||||||
|
```
|
||||||
|
|
||||||
|
video_src_path: 为 video_type 数值所对应的本地视频路径 、本地摄像头id、网络视频流地址,如:
|
||||||
|
|
||||||
|
```
|
||||||
|
本地视频文件: ./easyedge_video_inference {模型RES文件夹路径} 1 ~/my_video_file.mp4
|
||||||
|
本地摄像头: ./easyedge_video_inference {模型RES文件夹路径} 2 1 #/dev/video1
|
||||||
|
网络视频流: ./easyedge_video_inference {模型RES文件夹路径} 3 rtmp://192.168.x.x:8733/live/src
|
||||||
|
```
|
||||||
|
|
||||||
|
注:以上路径是假模拟路径,开发者需要根据自己实际图像/视频,准备测试图像,并填写正确的测试路径。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.测试Demo](#4)的Demo示例介绍推理API,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。图像、视频的推理包含以下3个API,如代码step注释所示:
|
||||||
|
|
||||||
|
> ❗注意:<br>
|
||||||
|
> (1)`src`文件夹中包含完整可编译的cmake工程实例,建议开发者先行了解[cmake工程基本知识](https://cmake.org/cmake/help/latest/guide/tutorial/index.html)。 <br>
|
||||||
|
> (2)请优先参考SDK中自带的Demo工程的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// step 1: SDK配置运行参数
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
|
||||||
|
// step 2: 创建并初始化Predictor;这这里选择合适的引擎
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
|
||||||
|
// step 3-1: 预测图像
|
||||||
|
auto img = cv::imread({图片路径});
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
predictor->infer(img, results);
|
||||||
|
|
||||||
|
// step 3-2: 预测视频
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
FrameTensor frame_tensor;
|
||||||
|
VideoConfig video_config;
|
||||||
|
video_config.source_type = static_cast<SourceType>(video_type); // source_type 定义参考头文件 easyedge_video.h
|
||||||
|
video_config.source_value = video_src;
|
||||||
|
/*
|
||||||
|
... more video_configs, 根据需要配置video_config的各选项
|
||||||
|
*/
|
||||||
|
auto video_decoding = CreateVideoDecoding(video_config);
|
||||||
|
while (video_decoding->next(frame_tensor) == EDGE_OK) {
|
||||||
|
results.clear();
|
||||||
|
if (frame_tensor.is_needed) {
|
||||||
|
predictor->infer(frame_tensor.frame, results);
|
||||||
|
render(frame_tensor.frame, results, predictor->model_info().kind);
|
||||||
|
}
|
||||||
|
//video_decoding->display(frame_tensor); // 显示当前frame,需在video_config中开启配置
|
||||||
|
//video_decoding->save(frame_tensor); // 存储当前frame到视频,需在video_config中开启配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若需自定义library search path或者gcc路径,修改对应Demo工程下的CMakeList.txt即可。
|
||||||
|
|
||||||
|
## 1. SDK参数运行配置
|
||||||
|
|
||||||
|
SDK的参数通过`EdgePredictorConfig::set_config`和`global_controller()->set_config`配置。本Demo 中设置了模型路径,其他参数保留默认参数。更详细的支持运行参数等,可以参考开发工具包中的头文件(`include/easyedge/easyedge_xxxx_config.h`)的详细说明。
|
||||||
|
|
||||||
|
配置参数使用方法如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化Predictor
|
||||||
|
|
||||||
|
* 接口
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
predictor->init();
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回非0,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. 预测推理
|
||||||
|
|
||||||
|
### 3.1 预测图像
|
||||||
|
|
||||||
|
> 在Demo中展示了预测接口infer()传入cv::Mat& image图像内容,并将推理结果赋值给std::vector<EdgeResultData>& result。更多关于infer()的使用,可以根据参考`easyedge.h`头文件中的实际情况、参数说明自行传入需要的内容做推理
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* 通用接口
|
||||||
|
* @param image: must be BGR , HWC format (opencv default)
|
||||||
|
* @param result
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int infer(cv::Mat& image, std::vector<EdgeResultData>& result) = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
图片的格式务必为opencv默认的BGR, HWC格式。
|
||||||
|
|
||||||
|
* 接口返回
|
||||||
|
|
||||||
|
`EdgeResultData`中可以获取对应的分类信息、位置信息。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct EdgeResultData {
|
||||||
|
int index; // 分类结果的index
|
||||||
|
std::string label; // 分类结果的label
|
||||||
|
float prob; // 置信度
|
||||||
|
|
||||||
|
// 物体检测 或 图像分割时使用:
|
||||||
|
float x1, y1, x2, y2; // (x1, y1): 左上角, (x2, y2): 右下角; 均为0~1的长宽比例值。
|
||||||
|
|
||||||
|
// 图像分割时使用:
|
||||||
|
cv::Mat mask; // 0, 1 的mask
|
||||||
|
std::string mask_rle; // Run Length Encoding,游程编码的mask
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于图像分割mask_rle ***
|
||||||
|
|
||||||
|
该字段返回了mask的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
以上字段可以参考demo文件中使用opencv绘制的逻辑进行解析
|
||||||
|
|
||||||
|
### 3.2 预测视频
|
||||||
|
|
||||||
|
SDK 提供了支持摄像头读取、视频文件和网络视频流的解析工具类`VideoDecoding`,此类提供了获取视频帧数据的便利函数。通过`VideoConfig`结构体可以控制视频/摄像头的解析策略、抽帧策略、分辨率调整、结果视频存储等功能。对于抽取到的视频帧可以直接作为SDK infer 接口的参数进行预测。
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
class`VideoDecoding`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 获取输入源的下一帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int next(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示当前frame_tensor中的视频帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int display(const FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将当前frame_tensor中的视频帧写为本地视频文件
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int save(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的fps属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_fps() = 0;
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的width属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_width() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的height属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_height() = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
struct `VideoConfig`
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 视频源、抽帧策略、存储策略的设置选项
|
||||||
|
*/
|
||||||
|
struct VideoConfig {
|
||||||
|
SourceType source_type; // 输入源类型
|
||||||
|
std::string source_value; // 输入源地址,如视频文件路径、摄像头index、网络流地址
|
||||||
|
int skip_frames{0}; // 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true
|
||||||
|
int retrieve_all{false}; // 是否抽取所有frame以便于作为显示和存储,对于不满足skip_frames策略的frame,把所抽取帧的is_needed置为false
|
||||||
|
int input_fps{0}; // 在采取抽帧之前设置视频的fps
|
||||||
|
Resolution resolution{Resolution::kAuto}; // 采样分辨率,只对camera有效
|
||||||
|
|
||||||
|
bool enable_display{false}; // 默认不支持。
|
||||||
|
std::string window_name{"EasyEdge"};
|
||||||
|
bool display_all{false}; // 是否显示所有frame,若为false,仅显示根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
bool enable_save{false};
|
||||||
|
std::string save_path; // frame存储为视频文件的路径
|
||||||
|
bool save_all{false}; // 是否存储所有frame,若为false,仅存储根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
std::map<std::string, std::string> conf;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
| 序号 | 字段 | 含义 |
|
||||||
|
| --- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 1 | `source_type` | 输入源类型,支持视频文件、摄像头、网络视频流三种,值分别为1、2、3 |
|
||||||
|
| 2 | `source_value` | 若`source_type`为视频文件,该值为指向视频文件的完整路径;若`source_type`为摄像头,该值为摄像头的index,如对于`/dev/video0`的摄像头,则index为0;若`source_type`为网络视频流,则为该视频流的完整地址。 |
|
||||||
|
| 3 | `skip_frames` | 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true,标记为is_needed的帧是用来做预测的帧。反之,直接跳过该帧,不经过预测。 |
|
||||||
|
| 4 | `retrieve_all` | 若置该项为true,则无论是否设置跳帧,所有的帧都会被抽取返回,以作为显示或存储用。 |
|
||||||
|
| 5 | `input_fps` | 用于抽帧前设置fps |
|
||||||
|
| 6 | `resolution` | 设置摄像头采样的分辨率,其值请参考`easyedge_video.h`中的定义,注意该分辨率调整仅对输入源为摄像头时有效 |
|
||||||
|
| 7 | `conf` | 高级选项。部分配置会通过该map来设置 |
|
||||||
|
|
||||||
|
*** 注意:***
|
||||||
|
|
||||||
|
1. `VideoConfig`不支持`display`功能。如果需要使用`VideoConfig`的`display`功能,需要自行编译带有GTK选项的OpenCV。
|
||||||
|
|
||||||
|
2. 使用摄像头抽帧时,如果通过`resolution`设置了分辨率调整,但是不起作用,请添加如下选项:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_config.conf["backend"] = "2";
|
||||||
|
```
|
||||||
|
|
||||||
|
3.部分设备上的CSI摄像头尚未兼容,如遇到问题,可以通过工单、QQ交流群或微信交流群反馈。
|
||||||
|
|
||||||
|
具体接口调用流程,可以参考SDK中的`demo_video_inference`。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
293
docs/Jetson-Linux-CPP-SDK-Serving.md
Normal file
293
docs/Jetson-Linux-CPP-SDK-Serving.md
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在**Jetson Linux C++** 环境下:(1) **服务化**推理部署步骤,(2)介绍推理全流程API,方便开发者了解项目后二次开发。如果开发者对Jetson图像/视频部署感兴趣,可以参考[Jetson CPP Inference](./Jetson-Linux-CPP-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
**注意**:OCR目前不支持服务化推理部署。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试 HTTP Demo](#2-测试-http-demo)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
|
||||||
|
* [HTTP API介绍](#http-api介绍)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式一:不使用图片base64格式](#21-http-请求方式一不使用图片base64格式)
|
||||||
|
* [2.2 http 请求方法二:使用图片base64格式](#22-http-请求方法二使用图片base64格式)
|
||||||
|
* [3. http 返回数据](#3-http-返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
* Jetpack: 4.6 。安装Jetpack 4.6,参考[NVIDIA 官网-Jetpack4.6安装指南](https://developer.nvidia.com/jetpack-sdk-46),或者参考采购的硬件厂商提供的安装方式进行安装。![]()
|
||||||
|
|
||||||
|
| 序号 | 硬件 | Jetpack安装方式 | 下载链接 | ---- |
|
||||||
|
| --- | ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||||
|
| 1 | Jetson Xavier NX | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jetson_xavier_nx/jetson-nx-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 2 | Jetson Nano | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jeston_nano/jetson-nano-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 3 | Jetson Nano 2GB | SD Card Image | [Download SD Card Image](https://developer.nvidia.com/embedded/l4t/r32_release_v6.1/jeston_nano_2gb/jetson-nano-2gb-jp46-sd-card-image.zip) | ---- |
|
||||||
|
| 4 | agx xavier等 | NVIDIA SDK Manager | [Download NVIDIA SDK](https://developer.nvidia.com/nvsdk-manager) | ---- |
|
||||||
|
| 5 | 非官方版本,如emmc版 | 参考采购的硬件公司提供的安装指南 | ---- | ---- |
|
||||||
|
|
||||||
|
注意:本项目SDK要求 `CUDA=10.2`、`cuDNN=8.2`、`TensorRT=8.0`、`gcc>=7.5` 、`cmake 在 3.0以上` ,安装 Jetpack4.6系统包后,CUDA、cuDNN、TensorRT、gcc和cmake版本就已经满足要求,无需在进行安装。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压后SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-硬件芯片
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_linux_cpp_x86_64_CPU.Generic_gcc5.4_v1.4.0_20220325.tar.gz
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试 HTTP Demo
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1 启动HTTP预测服务
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_serving {模型RES文件夹路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后,日志中会显示如下设备IP和24401端口号信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP is now serving at 0.0.0.0:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
此时,开发者可以打开浏览器,输入链接地址`http://0.0.0.0:24401`(这里的`设备IP和24401端口号`根据开发者电脑显示修改),选择图片来进行测试。
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855495-cd8d46ec-2492-4297-b3e4-2bda4cd6727c.png" width="600"></div>
|
||||||
|
|
||||||
|
同时,可以调用HTTP接口来访问服务,具体参考下文的[二次开发](#10)接口说明。
|
||||||
|
|
||||||
|
# HTTP API介绍
|
||||||
|
|
||||||
|
本章节主要结合[2.1 HTTP Demo]()的API介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。http服务包含服务端和客户端,目前支持的能力包括以下几种方式,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动可直接使用`bin/easyedge_serving`,或参考`src/demo_serving.cpp`文件修改相关逻辑
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief 开启一个简单的demo http服务。
|
||||||
|
* 该方法会block直到收到sigint/sigterm。
|
||||||
|
* http服务里,图片的解码运行在cpu之上,可能会降低推理速度。
|
||||||
|
* @tparam ConfigT
|
||||||
|
* @param config
|
||||||
|
* @param host
|
||||||
|
* @param port
|
||||||
|
* @param service_id service_id user parameter, uri '/get/service_id' will respond this value with 'text/plain'
|
||||||
|
* @param instance_num 实例数量,根据内存/显存/时延要求调整
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template<typename ConfigT>
|
||||||
|
int start_http_server(
|
||||||
|
const ConfigT &config,
|
||||||
|
const std::string &host,
|
||||||
|
int port,
|
||||||
|
const std::string &service_id,
|
||||||
|
int instance_num = 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式一:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容(无需base64, 无需json)
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 http 请求方法二:使用图片base64格式
|
||||||
|
|
||||||
|
HTTP方法:POST
|
||||||
|
Header如下:
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
| ------------ | ---------------- |
|
||||||
|
| Content-Type | application/json |
|
||||||
|
|
||||||
|
**Body请求填写**:
|
||||||
|
|
||||||
|
* 分类网络:
|
||||||
|
body 中请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
"top_num": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| ------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| top_num | 否 | number | - | 返回分类数量,不填该参数,则默认返回全部分类结果 |
|
||||||
|
|
||||||
|
* 检测和分割网络:
|
||||||
|
Body请求示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情:
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| --------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| threshold | 否 | number | - | 默认为推荐阈值,也可自行根据需要进行设置 |
|
||||||
|
|
||||||
|
Python请求示例:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
def main():
|
||||||
|
with open("图像路径", 'rb') as f:
|
||||||
|
result = requests.post("http://{服务ip地址}:24401/", json={
|
||||||
|
"image": base64.b64encode(f.read()).decode("utf8")
|
||||||
|
})
|
||||||
|
# print(result.request.body)
|
||||||
|
# print(result.request.headers)
|
||||||
|
print(result.content)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. http 返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于分割模型 ***
|
||||||
|
|
||||||
|
其中,mask为分割模型的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 使用libcurl请求http服务时,速度明显变慢
|
||||||
|
|
||||||
|
这是因为libcurl请求continue导致server等待数据的问题,添加空的header即可
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headers = curl_slist_append(headers, "Expect:");
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
412
docs/Linux-CPP-SDK-Inference.md
Normal file
412
docs/Linux-CPP-SDK-Inference.md
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在 **Intel X86-CPU/ NVIDIA GPU、Linux** 操作系统下的C++ :(1)图像和视频的推理部署步骤,(2)介绍推理全流程API,方便了解项目后二次开发。如果对Linux操作系统下的 Python部署感兴趣,请参考[Linux Python环境下的推理部署](./Linux-Python-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. 硬件支持](#1-硬件支持)
|
||||||
|
* [2. 软件环境](#2-软件环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1. 预测图像](#21-预测图像)
|
||||||
|
* [2.2. 预测视频流](#22-预测视频流)
|
||||||
|
* [3. 编译Demo](#3-编译demo)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. SDK参数运行配置](#1-sdk参数运行配置)
|
||||||
|
* [2. 初始化Predictor](#2-初始化predictor)
|
||||||
|
* [3. 预测推理](#3-预测推理)
|
||||||
|
* [3.1 预测图像](#31-预测图像)
|
||||||
|
* [3.2 预测视频](#32-预测视频)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1.硬件支持
|
||||||
|
|
||||||
|
* NVIDIA GPU: x86_64
|
||||||
|
* cuda支持版本:CUDA10.0/10.1/10.2 + cuDNN 7 (cuDNN版本>=7.6.5)
|
||||||
|
* cuda支持版本:CUDA11.0 + cuDNN v8.0.4
|
||||||
|
* CPU:Intel x86_64
|
||||||
|
|
||||||
|
## 2. 软件环境
|
||||||
|
|
||||||
|
1.运行二进制文件-环境要求
|
||||||
|
|
||||||
|
* gcc: 5.4 以上 (GLIBCXX_3.4.22)
|
||||||
|
* Linux下查看gcc版本命名(可能因系统差异命令会不同):`gcc --version`;
|
||||||
|
* Linux下C++基础库GLIBCXX的命令(可能因系统差异路径会有不同,可检测自己环境下的情况):`strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX`
|
||||||
|
* glibc:2.23以上
|
||||||
|
* Linux查看命令:`ldd --version`
|
||||||
|
|
||||||
|
2.二次开发编译-环境要求
|
||||||
|
|
||||||
|
编译源代码时,除了gcc、GLIBCXX、glibc满足`1.运行二进制文件-环境要求`外,还需要cmake满足要求。
|
||||||
|
|
||||||
|
* cmake: 3.0 以上
|
||||||
|
|
||||||
|
* Linux查看命令:`cmake --version`
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-硬件芯片
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_linux_cpp_x86_64_CPU.Generic_gcc5.4_v1.4.0_20220325.tar.gz
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
└── python # Python SDK 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
**注意**: OCR算法目前没有提供
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1. 预测图像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./easyedge_image_inference {模型RES文件夹路径} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855351-68d1a4f0-6226-4484-b190-65f1ac2c7128.png" width="400"></div>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> ./easyedge_image_inference ../../../../RES 2.jpeg
|
||||||
|
2019-02-13 16:46:12,659 INFO [EasyEdge] [easyedge.cpp:34] 140606189016192 Baidu EasyEdge Linux Development Kit 0.2.1(20190213)
|
||||||
|
2019-02-13 16:46:14,083 INFO [EasyEdge] [paddlev2_edge_predictor.cpp:60] 140606189016192 Allocate graph success.
|
||||||
|
2019-02-13 16:46:14,326 DEBUG [EasyEdge] [paddlev2_edge_predictor.cpp:143] 140606189016192 Inference costs 168 ms
|
||||||
|
1, 1:txt_frame, p:0.994905 loc: 0.168161, 0.153654, 0.920856, 0.779621
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2. 预测视频流
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_video_inference {模型RES文件夹路径} {video_type} {video_src_path}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 video_type 支持三种:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_type : 1 // 本地视频文件
|
||||||
|
video_type : 2 // 摄像头的index
|
||||||
|
video_type : 3 // 网络视频流
|
||||||
|
```
|
||||||
|
|
||||||
|
video_src_path: 为 video_type 数值所对应的本地视频路径 、本地摄像头id、网络视频流地址,如:
|
||||||
|
|
||||||
|
```
|
||||||
|
本地视频文件: ./easyedge_video_inference {模型RES文件夹路径} 1 ~/my_video_file.mp4
|
||||||
|
本地摄像头: ./easyedge_video_inference {模型RES文件夹路径} 2 1 #/dev/video1
|
||||||
|
网络视频流: ./easyedge_video_inference {模型RES文件夹路径} 3 rtmp://192.168.x.x:8733/live/src
|
||||||
|
```
|
||||||
|
|
||||||
|
注:以上路径是假模拟路径,开发者需要根据自己实际图像/视频,准备测试图像,并填写正确的测试路径。
|
||||||
|
|
||||||
|
## 3. 编译Demo
|
||||||
|
|
||||||
|
通过[项目结构说明](#3)了解到,`bin`路径下的可执行文件 由`src`下的对应文件编译得到。 通过以下命令,即可完成`src`下的源码编译。
|
||||||
|
|
||||||
|
```
|
||||||
|
cd src
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && make
|
||||||
|
```
|
||||||
|
|
||||||
|
至此,会在build文件夹下生成编译好的可执行文件,如图像推理的二进制文件:`build/demo_image_inference/easyedge_image_inference`。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.测试Demo](#4)的Demo示例介绍推理API,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。图像、视频的推理包含以下3个API,查看下面的cpp代码中的step注释说明。
|
||||||
|
|
||||||
|
> ❗注意:<br>
|
||||||
|
> (1)`src`文件夹中包含完整可编译的cmake工程实例,建议开发者先行了解[cmake工程基本知识](https://cmake.org/cmake/help/latest/guide/tutorial/index.html)。 <br>
|
||||||
|
> (2)请优先参考SDK中自带的Demo工程的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// step 1: SDK配置运行参数
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
|
||||||
|
// step 2: 创建并初始化Predictor;这这里选择合适的引擎
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
|
||||||
|
// step 3-1: 预测图像
|
||||||
|
auto img = cv::imread({图片路径});
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
predictor->infer(img, results);
|
||||||
|
|
||||||
|
// step 3-2: 预测视频
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
FrameTensor frame_tensor;
|
||||||
|
VideoConfig video_config;
|
||||||
|
video_config.source_type = static_cast<SourceType>(video_type); // source_type 定义参考头文件 easyedge_video.h
|
||||||
|
video_config.source_value = video_src;
|
||||||
|
/*
|
||||||
|
... more video_configs, 根据需要配置video_config的各选项
|
||||||
|
*/
|
||||||
|
auto video_decoding = CreateVideoDecoding(video_config);
|
||||||
|
while (video_decoding->next(frame_tensor) == EDGE_OK) {
|
||||||
|
results.clear();
|
||||||
|
if (frame_tensor.is_needed) {
|
||||||
|
predictor->infer(frame_tensor.frame, results);
|
||||||
|
render(frame_tensor.frame, results, predictor->model_info().kind);
|
||||||
|
}
|
||||||
|
//video_decoding->display(frame_tensor); // 显示当前frame,需在video_config中开启配置
|
||||||
|
//video_decoding->save(frame_tensor); // 存储当前frame到视频,需在video_config中开启配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若需自定义library search path或者gcc路径,修改对应Demo工程下的CMakeList.txt即可。
|
||||||
|
|
||||||
|
## 1. SDK参数运行配置
|
||||||
|
|
||||||
|
SDK的参数通过`EdgePredictorConfig::set_config`和`global_controller()->set_config`配置。本Demo 中设置了模型路径,其他参数保留默认参数。更详细的支持运行参数等,可以参考开发工具包中的头文件(`include/easyedge/easyedge_xxxx_config.h`)的详细说明。
|
||||||
|
|
||||||
|
配置参数使用方法如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化Predictor
|
||||||
|
|
||||||
|
* 接口
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
predictor->init();
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回非0,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. 预测推理
|
||||||
|
|
||||||
|
### 3.1 预测图像
|
||||||
|
|
||||||
|
> 在Demo中展示了预测接口infer()传入cv::Mat& image图像内容,并将推理结果赋值给std::vector<EdgeResultData>& result。更多关于infer()的使用,可以根据参考`easyedge.h`头文件中的实际情况、参数说明自行传入需要的内容做推理
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* 通用接口
|
||||||
|
* @param image: must be BGR , HWC format (opencv default)
|
||||||
|
* @param result
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int infer(cv::Mat& image, std::vector<EdgeResultData>& result) = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
图片的格式务必为opencv默认的BGR, HWC格式。
|
||||||
|
|
||||||
|
* 接口返回
|
||||||
|
|
||||||
|
`EdgeResultData`中可以获取对应的分类信息、位置信息。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct EdgeResultData {
|
||||||
|
int index; // 分类结果的index
|
||||||
|
std::string label; // 分类结果的label
|
||||||
|
float prob; // 置信度
|
||||||
|
|
||||||
|
// 物体检测 或 图像分割时使用:
|
||||||
|
float x1, y1, x2, y2; // (x1, y1): 左上角, (x2, y2): 右下角; 均为0~1的长宽比例值。
|
||||||
|
|
||||||
|
// 图像分割时使用:
|
||||||
|
cv::Mat mask; // 0, 1 的mask
|
||||||
|
std::string mask_rle; // Run Length Encoding,游程编码的mask
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于图像分割mask_rle ***
|
||||||
|
|
||||||
|
该字段返回了mask的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
以上字段可以参考demo文件中使用opencv绘制的逻辑进行解析
|
||||||
|
|
||||||
|
### 3.2 预测视频
|
||||||
|
|
||||||
|
SDK 提供了支持摄像头读取、视频文件和网络视频流的解析工具类`VideoDecoding`,此类提供了获取视频帧数据的便利函数。通过`VideoConfig`结构体可以控制视频/摄像头的解析策略、抽帧策略、分辨率调整、结果视频存储等功能。对于抽取到的视频帧可以直接作为SDK infer 接口的参数进行预测。
|
||||||
|
|
||||||
|
* 接口输入
|
||||||
|
|
||||||
|
class`VideoDecoding`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 获取输入源的下一帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int next(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示当前frame_tensor中的视频帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int display(const FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将当前frame_tensor中的视频帧写为本地视频文件
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int save(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的fps属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_fps() = 0;
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的width属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_width() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的height属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_height() = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
struct `VideoConfig`
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 视频源、抽帧策略、存储策略的设置选项
|
||||||
|
*/
|
||||||
|
struct VideoConfig {
|
||||||
|
SourceType source_type; // 输入源类型
|
||||||
|
std::string source_value; // 输入源地址,如视频文件路径、摄像头index、网络流地址
|
||||||
|
int skip_frames{0}; // 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true
|
||||||
|
int retrieve_all{false}; // 是否抽取所有frame以便于作为显示和存储,对于不满足skip_frames策略的frame,把所抽取帧的is_needed置为false
|
||||||
|
int input_fps{0}; // 在采取抽帧之前设置视频的fps
|
||||||
|
Resolution resolution{Resolution::kAuto}; // 采样分辨率,只对camera有效
|
||||||
|
|
||||||
|
bool enable_display{false}; // 默认不支持。
|
||||||
|
std::string window_name{"EasyEdge"};
|
||||||
|
bool display_all{false}; // 是否显示所有frame,若为false,仅显示根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
bool enable_save{false};
|
||||||
|
std::string save_path; // frame存储为视频文件的路径
|
||||||
|
bool save_all{false}; // 是否存储所有frame,若为false,仅存储根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
std::map<std::string, std::string> conf;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
| 序号 | 字段 | 含义 |
|
||||||
|
| --- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 1 | `source_type` | 输入源类型,支持视频文件、摄像头、网络视频流三种,值分别为1、2、3 |
|
||||||
|
| 2 | `source_value` | 若`source_type`为视频文件,该值为指向视频文件的完整路径;若`source_type`为摄像头,该值为摄像头的index,如对于`/dev/video0`的摄像头,则index为0;若`source_type`为网络视频流,则为该视频流的完整地址。 |
|
||||||
|
| 3 | `skip_frames` | 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true,标记为is_needed的帧是用来做预测的帧。反之,直接跳过该帧,不经过预测。 |
|
||||||
|
| 4 | `retrieve_all` | 若置该项为true,则无论是否设置跳帧,所有的帧都会被抽取返回,以作为显示或存储用。 |
|
||||||
|
| 5 | `input_fps` | 用于抽帧前设置fps |
|
||||||
|
| 6 | `resolution` | 设置摄像头采样的分辨率,其值请参考`easyedge_video.h`中的定义,注意该分辨率调整仅对输入源为摄像头时有效 |
|
||||||
|
| 7 | `conf` | 高级选项。部分配置会通过该map来设置 |
|
||||||
|
|
||||||
|
*** 注意:***
|
||||||
|
|
||||||
|
1. `VideoConfig`不支持`display`功能。如果需要使用`VideoConfig`的`display`功能,需要自行编译带有GTK选项的OpenCV。
|
||||||
|
|
||||||
|
2. 使用摄像头抽帧时,如果通过`resolution`设置了分辨率调整,但是不起作用,请添加如下选项:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_config.conf["backend"] = "2";
|
||||||
|
```
|
||||||
|
|
||||||
|
3.部分设备上的CSI摄像头尚未兼容,如遇到问题,可以通过工单、QQ交流群或微信交流群反馈。
|
||||||
|
|
||||||
|
具体接口调用流程,可以参考SDK中的`demo_video_inference`。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
329
docs/Linux-CPP-SDK-Serving.md
Normal file
329
docs/Linux-CPP-SDK-Serving.md
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在**X86 CPU/ NVIDIA GPU、Linux操作系统** 的C++环境:(1)HTTP服务化推理部署步骤,(2)介绍推理全流程API,方便开发者了解项目后二次开发。
|
||||||
|
如果开发者对Python语言的相关能力感兴趣,可以参考Linux Python请参考[Linux Python环境下的推理部署](./Linux-Python-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
**【注意】**:OCR Demo 暂不支持服务化部署。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [安装准备](#安装准备)
|
||||||
|
|
||||||
|
* [1. 硬件支持](#1-硬件支持)
|
||||||
|
* [2. 软件环境](#2-软件环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试 HTTP Demo](#2-测试-http-demo)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
* [3. 编译Demo](#3-编译demo)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式一:不使用图片base64格式](#21-http-请求方式一不使用图片base64格式)
|
||||||
|
* [2.2 http 请求方法二:使用图片base64格式](#22-http-请求方法二使用图片base64格式)
|
||||||
|
* [3. http返回数据](#3-http返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 安装准备
|
||||||
|
|
||||||
|
## 1.硬件支持
|
||||||
|
|
||||||
|
- NVIDIA GPU: x86_64
|
||||||
|
- cuda支持版本:CUDA10.0/10.1/10.2 + cuDNN 7 (cuDNN版本>=7.6.5)
|
||||||
|
- cuda支持版本:CUDA11.0 + cuDNN v8.0.4
|
||||||
|
- CPU:Intel x86_64
|
||||||
|
|
||||||
|
## 2. 软件环境
|
||||||
|
|
||||||
|
1.运行二进制文件-环境要求
|
||||||
|
|
||||||
|
- gcc: 5.4 以上 (GLIBCXX_3.4.22)
|
||||||
|
- Linux下查看gcc版本命名(可能因系统差异命令会不同):`gcc --version`;
|
||||||
|
- Linux下C++基础库GLIBCXX的命令(可能因系统差异路径会有不同,可检测自己环境下的情况):`strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX`
|
||||||
|
- glibc:2.23以上
|
||||||
|
- Linux查看命令:`ldd --version`
|
||||||
|
|
||||||
|
2.二次开发编译-环境要求
|
||||||
|
|
||||||
|
编译源代码时,除了gcc、GLIBCXX、glibc满足`1.运行二进制文件-环境要求`外,还需要cmake满足要求。
|
||||||
|
|
||||||
|
- cmake: 3.0 以上
|
||||||
|
|
||||||
|
- Linux查看命令:`cmake --version`
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-Linux-硬件芯片
|
||||||
|
├── RES # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── cpp # C++ SDK 文件结构
|
||||||
|
└── baidu_easyedge_linux_cpp_x86_64_CPU.Generic_gcc5.4_v1.4.0_20220325.tar.gz
|
||||||
|
├── ReadMe.txt
|
||||||
|
├── bin # 可直接运行的二进制文件
|
||||||
|
├── include # 二次开发用的头文件
|
||||||
|
├── lib # 二次开发用的所依赖的库
|
||||||
|
├── src # 二次开发用的示例工程
|
||||||
|
└── thirdparty # 第三方依赖
|
||||||
|
└── python # Python SDK 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试 HTTP Demo
|
||||||
|
|
||||||
|
> 模型资源文件(即压缩包中的RES文件夹)默认已经打包在开发者下载的SDK包中,请先将tar包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK中已经包含预先编译的二进制,可直接运行。以下运行示例均是`cd cpp/bin`路径下执行的结果。
|
||||||
|
|
||||||
|
### 2.1. 启动HTTP预测服务
|
||||||
|
|
||||||
|
```
|
||||||
|
./easyedge_serving {模型RES文件夹路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后,日志中会显示如下设备IP和24401端口号信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP is now serving at 0.0.0.0:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
此时,开发者可以打开浏览器,输入链接地址`http://0.0.0.0:24401`(这里的`设备IP和24401端口号`根据开发者电脑显示修改),选择图片来进行测试。
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175855495-cd8d46ec-2492-4297-b3e4-2bda4cd6727c.png" width="600"></div>
|
||||||
|
|
||||||
|
同时,可以调用HTTP接口来访问服务,具体参考下文的[二次开发](#10)接口说明。
|
||||||
|
|
||||||
|
## 3. 编译Demo
|
||||||
|
|
||||||
|
通过[项目结构说明](#3)了解到,`bin`路径下的可执行文件是由`src`下的对应文件编译得到的。 该部分说明C++编译命令。
|
||||||
|
|
||||||
|
```
|
||||||
|
cd src
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && make
|
||||||
|
```
|
||||||
|
|
||||||
|
至此,会在build文件夹下生成编译好的可执行文件,如图像推理的二进制文件:`build/demo_serving/easyedge_serving`。
|
||||||
|
|
||||||
|
# HTTP API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.1 HTTP Demo](#4)的API介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。http服务包含服务端和客户端,目前支持的能力包括以下几种方式,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动可直接使用`bin/easyedge_serving`,或参考`src/demo_serving.cpp`文件修改相关逻辑
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief 开启一个简单的demo http服务。
|
||||||
|
* 该方法会block直到收到sigint/sigterm。
|
||||||
|
* http服务里,图片的解码运行在cpu之上,可能会降低推理速度。
|
||||||
|
* @tparam ConfigT
|
||||||
|
* @param config
|
||||||
|
* @param host
|
||||||
|
* @param port
|
||||||
|
* @param service_id service_id user parameter, uri '/get/service_id' will respond this value with 'text/plain'
|
||||||
|
* @param instance_num 实例数量,根据内存/显存/时延要求调整
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template<typename ConfigT>
|
||||||
|
int start_http_server(
|
||||||
|
const ConfigT &config,
|
||||||
|
const std::string &host,
|
||||||
|
int port,
|
||||||
|
const std::string &service_id,
|
||||||
|
int instance_num = 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式一:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容(无需base64, 无需json)
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 http 请求方法二:使用图片base64格式
|
||||||
|
|
||||||
|
HTTP方法:POST
|
||||||
|
Header如下:
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
| ------------ | ---------------- |
|
||||||
|
| Content-Type | application/json |
|
||||||
|
|
||||||
|
**Body请求填写**:
|
||||||
|
|
||||||
|
* 分类网络:
|
||||||
|
body 中请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
"top_num": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| ------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| top_num | 否 | number | - | 返回分类数量,不填该参数,则默认返回全部分类结果 |
|
||||||
|
|
||||||
|
* 检测和分割网络:
|
||||||
|
Body请求示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情:
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| --------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| threshold | 否 | number | - | 默认为推荐阈值,也可自行根据需要进行设置 |
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with open("图像路径", 'rb') as f:
|
||||||
|
result = requests.post("http://{服务ip地址}:24401/", json={
|
||||||
|
"image": base64.b64encode(f.read()).decode("utf8")
|
||||||
|
})
|
||||||
|
# print(result.request.body)
|
||||||
|
# print(result.request.headers)
|
||||||
|
print(result.content)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. http返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于分割模型 ***
|
||||||
|
|
||||||
|
其中,mask为分割模型的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
2. 使用libcurl请求http服务时,速度明显变慢
|
||||||
|
|
||||||
|
这是因为libcurl请求continue导致server等待数据的问题,添加空的header即可
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headers = curl_slist_append(headers, "Expect:");
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译。
|
369
docs/Linux-Python-SDK-Inference.md
Normal file
369
docs/Linux-Python-SDK-Inference.md
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在**Intel x86_64 / NVIDIA GPU Linux Python** 环境下: (1)图像推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中Linux C++请参考[Linux CPP环境下的推理部署](./Linux-CPP-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. Python环境](#2-python环境)
|
||||||
|
* [3. 安装依赖](#3-安装依赖)
|
||||||
|
* [3.1 安装paddlepaddle](#31-安装paddlepaddle)
|
||||||
|
* [3.2 安装EasyEdge Python Wheel 包](#32-安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 文件结构说明](#1-文件结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1 预测图像](#21-预测图像)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. 基础流程](#1-基础流程)
|
||||||
|
* [2. 初始化](#2-初始化)
|
||||||
|
* [3. SDK参数配置](#3-sdk参数配置)
|
||||||
|
* [4. 预测图像](#4-预测图像)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压后SDK目录结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86-[部署芯片]
|
||||||
|
├── RES # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
├── README.md
|
||||||
|
├── cpp # C++ SDK
|
||||||
|
└── python # Python SDK
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Python环境
|
||||||
|
|
||||||
|
> 当前SDK仅支持Python 3.5, 3.6, 3.7
|
||||||
|
|
||||||
|
使用如下命令获取已安装的Python版本号。如果本机的版本不匹配,建议使用[pyenv](https://github.com/pyenv/pyenv)、[anaconda](https://www.anaconda.com/)等Python版本管理工具对SDK所在目录进行配置。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 安装依赖
|
||||||
|
|
||||||
|
### 3.1 安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。
|
||||||
|
|
||||||
|
`x86_64 CPU` 平台可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install paddlepaddle==2.2.2 -i https://mirror.baidu.com/pypi/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
NVIDIA GPU平台的详细安装教程可以参考[官网Paddle安装教程](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)。
|
||||||
|
|
||||||
|
> 使用 NVIDIA GPU 预测时,必须满足:
|
||||||
|
>
|
||||||
|
> 1. 机器已安装 cuda, cudnn
|
||||||
|
> 2. 已正确安装对应 cuda 版本的paddle 版本
|
||||||
|
> 3. 通过设置环境变量`FLAGS_fraction_of_gpu_memory_to_use`设置合理的初始内存使用比例
|
||||||
|
|
||||||
|
### 3.2 安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装特定Python版本的EasyEdge Wheel包。对`x86_64 CPU` 或 `x86_64 Nvidia GPU平台 `可以使用如下命令进行安装,具体名称以 Python SDK 包中的 whl 为准。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-{SDK版本号}-cp{Python版本号}-cp{Python版本号}m-linux_x86_64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-{版本号}-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86--[部署芯片]
|
||||||
|
├──...
|
||||||
|
├──python # Linux Python SDK
|
||||||
|
├── # 特定Python版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp35-cp35m-linux_x86_64.whl
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp36-cp36m-linux_x86_64.whl
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp37-cp37m-linux_x86_64.whl
|
||||||
|
├── infer_demo # demo体验完整文件
|
||||||
|
│ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
├── tensor_demo # tensor in/out demo文件
|
||||||
|
│ └── demo_xxx.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中, 默认为`RES`目录。
|
||||||
|
|
||||||
|
### 2.1 预测图像
|
||||||
|
|
||||||
|
使用infer_demo文件夹下的demo文件。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 demo_x86_cpu.py {模型RES文件夹} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854068-28d27c0a-ef83-43ee-9e89-b65eed99b476.jpg" width="400"></div>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
2022-06-14 14:40:16 INFO [EasyEdge] [demo_nvidia_gpu.py:38] 140518522509120: Init paddlefluid engine...
|
||||||
|
2022-06-14 14:40:20 INFO [EasyEdge] [demo_nvidia_gpu.py:38] 140518522509120: Paddle version: 2.2.2
|
||||||
|
{'confidence': 0.9012349843978882, 'index': 8, 'label': 'n01514859 hen'}
|
||||||
|
```
|
||||||
|
|
||||||
|
可以看到,运行结果为`index:8,label:hen`,通过imagenet [类别映射表](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a),可以找到对应的类别,即 'hen',由此说明我们的预测结果正确。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合前文的Demo示例来介绍推理API,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`infer_demo/demo_xx_xx.py`文件,查看下面的Python代码中的step注释说明。
|
||||||
|
|
||||||
|
## 1. 基础流程
|
||||||
|
|
||||||
|
> ❗注意,请优先参考SDK中自带demo的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
`infer_demo/demo_xx_xx.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 引入EasyEdge运行库
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
|
||||||
|
# 创建并初始化一个预测Progam;选择合适的引擎
|
||||||
|
pred = edge.Program()
|
||||||
|
pred.init(model_dir={RES文件夹路径}, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
|
||||||
|
# 预测图像
|
||||||
|
res = pred.infer_image({numpy.ndarray的图片})
|
||||||
|
|
||||||
|
# 关闭结束预测Progam
|
||||||
|
pred.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
`infer_demo/demo_serving.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
from BaiduAI.EasyEdge.serving import Serving
|
||||||
|
|
||||||
|
# 创建并初始化Http服务
|
||||||
|
server = Serving(model_dir={RES文件夹路径}, license=serial_key)
|
||||||
|
|
||||||
|
# 运行Http服务
|
||||||
|
# 请参考同级目录下demo_xx_xx.py里:
|
||||||
|
# pred.init(model_dir=xx, device=xx, engine=xx, device_id=xx)
|
||||||
|
# 对以下参数device\device_id和engine进行修改
|
||||||
|
server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化
|
||||||
|
|
||||||
|
- 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def init(self,
|
||||||
|
model_dir,
|
||||||
|
device=Device.CPU,
|
||||||
|
engine=Engine.PADDLE_FLUID,
|
||||||
|
config_file='conf.json',
|
||||||
|
preprocess_file='preprocess_args.json',
|
||||||
|
model_file='model',
|
||||||
|
params_file='params',
|
||||||
|
label_file='label_list.txt',
|
||||||
|
infer_cfg_file='infer_cfg.json',
|
||||||
|
device_id=0,
|
||||||
|
thread_num=1
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
model_dir: str
|
||||||
|
device: BaiduAI.EasyEdge.Device,比如:Device.CPU
|
||||||
|
engine: BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID
|
||||||
|
config_file: str
|
||||||
|
preprocess_file: str
|
||||||
|
model_file: str
|
||||||
|
params_file: str
|
||||||
|
label_file: str 标签文件
|
||||||
|
infer_cfg_file: 包含预处理、后处理信息的文件
|
||||||
|
device_id: int 设备ID
|
||||||
|
thread_num: int CPU的线程数
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError, IOError
|
||||||
|
Returns:
|
||||||
|
bool: True if success
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回不是True,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. SDK参数配置
|
||||||
|
|
||||||
|
使用 CPU 预测时,可以通过在 init 中设置 thread_num 使用多线程预测。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID, thread_num=4)
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 GPU 预测时,可以通过在 init 中设置 device_id 指定需要的GPU device id。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID, device_id=0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 预测图像
|
||||||
|
|
||||||
|
- 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def infer_image(self, img,
|
||||||
|
threshold=0.3,
|
||||||
|
channel_order='HWC',
|
||||||
|
color_format='BGR',
|
||||||
|
data_type='numpy')
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
img: np.ndarray or bytes
|
||||||
|
threshold: float
|
||||||
|
only return result with confidence larger than threshold
|
||||||
|
channel_order: string
|
||||||
|
channel order HWC or CHW
|
||||||
|
color_format: string
|
||||||
|
color format order RGB or BGR
|
||||||
|
data_type: string
|
||||||
|
仅在图像分割时有意义。 'numpy' or 'string'
|
||||||
|
'numpy': 返回已解析的mask
|
||||||
|
'string': 返回未解析的mask游程编码
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list
|
||||||
|
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
| 字段 | 类型 | 取值 | 说明 |
|
||||||
|
| ---------- | -------------------- | --------- | ------------------------ |
|
||||||
|
| confidence | float | 0~1 | 分类或检测的置信度 |
|
||||||
|
| label | string | | 分类或检测的类别 |
|
||||||
|
| index | number | | 分类或检测的类别 |
|
||||||
|
| x1, y1 | float | 0~1 | 物体检测,矩形的左上角坐标 (相对长宽的比例值) |
|
||||||
|
| x2, y2 | float | 0~1 | 物体检测,矩形的右下角坐标(相对长宽的比例值) |
|
||||||
|
| mask | string/numpy.ndarray | 图像分割的mask | |
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
可以参考 demo 文件中使用 opencv 绘制矩形的逻辑。
|
||||||
|
|
||||||
|
***结果示例***
|
||||||
|
|
||||||
|
i) 图像分类
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 736,
|
||||||
|
"label": "table",
|
||||||
|
"confidence": 0.9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
ii) 物体检测
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 8,
|
||||||
|
"label": "cat",
|
||||||
|
"confidence": 1.0,
|
||||||
|
"x1": 0.21289,
|
||||||
|
"y1": 0.12671,
|
||||||
|
"x2": 0.91504,
|
||||||
|
"y2": 0.91211,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
iii) 图像分割
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "cat",
|
||||||
|
"score": 1.0,
|
||||||
|
"location": {
|
||||||
|
"left": ...,
|
||||||
|
"top": ...,
|
||||||
|
"width": ...,
|
||||||
|
"height": ...,
|
||||||
|
},
|
||||||
|
"mask": ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
mask字段中,data_type为`numpy`时,返回图像掩码的二维数组
|
||||||
|
|
||||||
|
```text
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
data_type为`string`时,mask的游程编码,解析方式可参考 [demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
268
docs/Linux-Python-SDK-Serving.md
Normal file
268
docs/Linux-Python-SDK-Serving.md
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍 FastDeploy中的模型SDK ,在**Intel x86_64 / NVIDIA GPU Linux Python** 环境下: (1)SDK **服务化**推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中Linux C++请参考[Linux C++环境下的服务化推理部署](./Linux-CPP-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
**【注意】**:OCR Demo 暂不支持服务化部署。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. Python环境](#2-python环境)
|
||||||
|
* [3. 安装依赖](#3-安装依赖)
|
||||||
|
* [3.1 安装paddlepaddle](#31-安装paddlepaddle)
|
||||||
|
* [3.2 安装EasyEdge Python Wheel 包](#32-安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 文件结构说明](#1-文件结构说明)
|
||||||
|
* [2. 测试Serving服务](#2-测试serving服务)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式:不使用图片base64格式](#21-http-请求方式不使用图片base64格式)
|
||||||
|
* [3. http 返回数据](#3-http-返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压后SDK目录结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86-[部署芯片]
|
||||||
|
├── RES # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
├── README.md
|
||||||
|
├── cpp # C++ SDK
|
||||||
|
└── python # Python SDK
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Python环境
|
||||||
|
|
||||||
|
> 当前SDK仅支持Python 3.5, 3.6, 3.7
|
||||||
|
|
||||||
|
使用如下命令获取已安装的Python版本号。如果本机的版本不匹配,建议使用[pyenv](https://github.com/pyenv/pyenv)、[anaconda](https://www.anaconda.com/)等Python版本管理工具对SDK所在目录进行配置。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$python3 -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 安装依赖
|
||||||
|
|
||||||
|
### 3.1 安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。
|
||||||
|
|
||||||
|
1.`x86_64 CPU` 平台可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install paddlepaddle==2.2.2 -i https://mirror.baidu.com/pypi/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
2.`x86_64 NVIDIA GPU` 支持的CUDA和CUDNN版本与PaddlePaddle框架保持一致,如下:
|
||||||
|
|
||||||
|
* CUDA 工具包10.1/10.2配合cuDNN 7 (cuDNN版本>=7.6.5, 如需多卡支持,需配合NCCL2.7及更高)
|
||||||
|
* CUDA 工具包11.0配合cuDNN v8.0.4(如需多卡支持,需配合NCCL2.7及更高)
|
||||||
|
* CUDA 工具包11.1配合cuDNN v8.1.1(如需多卡支持,需配合NCCL2.7及更高)
|
||||||
|
* CUDA 工具包11.2配合cuDNN v8.1.1(如需多卡支持,需配合NCCL2.7及更高)
|
||||||
|
|
||||||
|
具体安装命令,参考[官网Paddle安装教程](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)。
|
||||||
|
|
||||||
|
> 使用 NVIDIA GPU 预测时,必须满足:
|
||||||
|
>
|
||||||
|
> 1. 机器已安装 cuda, cudnn
|
||||||
|
> 2. 已正确安装对应 cuda 版本的paddle 版本
|
||||||
|
> 3. 通过设置环境变量`FLAGS_fraction_of_gpu_memory_to_use`设置合理的初始内存使用比例
|
||||||
|
|
||||||
|
### 3.2 安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装特定Python版本的EasyEdge Wheel包。对`x86_64 CPU` 或 `x86_64 Nvidia GPU平台 `可以使用如下命令进行安装,具体名称以 Python SDK 包中的 whl 为准。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-{SDK版本号}-cp{Python版本号}-cp{Python版本号}m-linux_x86_64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
`armv8 CPU平台`可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install -U BaiduAI_EasyEdge_SDK-{SDK版本号}-cp36-cp36m-linux_aarch64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-Linux-x86--[部署芯片]
|
||||||
|
├──...
|
||||||
|
├──python # Linux Python SDK
|
||||||
|
├── # 特定Python版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp35-cp35m-linux_x86_64.whl
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp36-cp36m-linux_x86_64.whl
|
||||||
|
├── BaiduAI_EasyEdge_SDK-1.2.8-cp37-cp37m-linux_x86_64.whl
|
||||||
|
├── infer_demo # demo体验完整文件
|
||||||
|
│ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
├── tensor_demo # 学习自定义算法前后处理时使用
|
||||||
|
│ └── demo_xxx.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Serving服务
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中, 默认为`RES`目录。
|
||||||
|
|
||||||
|
### 2.1 启动HTTP预测服务
|
||||||
|
|
||||||
|
指定对应的模型文件夹(默认为`RES`)、设备ip和指定端口号,运行如下命令。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 demo_serving.py {模型RES文件夹} {host, default 0.0.0.0} {port, default 24401}
|
||||||
|
```
|
||||||
|
|
||||||
|
成功启动后,终端中会显示如下字样。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
...
|
||||||
|
* Running on {host ip}:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
如果是在局域网内的机器上部署,开发者此时可以打开浏览器,输入`http://{host ip}:24401`,选择图片来进行测试,运行效果如下。
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854073-fb8189e5-0ffb-472c-a17d-0f35aa6a8418.png" style="zoom:50%;" />
|
||||||
|
|
||||||
|
如果是在远程机器上部署,那么可以参考`demo_serving.py`中的 `http_client_test()函数`请求http服务来执行推理。
|
||||||
|
|
||||||
|
# HTTP API流程详解
|
||||||
|
|
||||||
|
本章节主要结合前文的Demo示例来对API进行介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考对应的Python文件。http服务包含服务端和客户端,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动使用`demo_serving.py`文件
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Serving(object):
|
||||||
|
""" SDK local serving """
|
||||||
|
|
||||||
|
def __init__(self, model_dir, license='', model_filename='model', params_filename='params'):
|
||||||
|
|
||||||
|
self.program = None
|
||||||
|
self.model_dir = model_dir
|
||||||
|
self.model_filename = model_filename
|
||||||
|
self.params_filename = params_filename
|
||||||
|
self.program_lock = threading.Lock()
|
||||||
|
self.license_key = license
|
||||||
|
# 只有ObjectTracking会初始化video_processor
|
||||||
|
self.video_processor = None
|
||||||
|
|
||||||
|
def run(self, host, port, device, engine=Engine.PADDLE_FLUID, service_id=0, device_id=0, **kwargs):
|
||||||
|
""" Args: host : str port : str device : BaiduAI.EasyEdge.Device,比如:Device.CPU engine : BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID """
|
||||||
|
self.run_serving_with_flask(host, port, device, engine, service_id, device_id, **kwargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容。
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. http 返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
***关于分割模型***
|
||||||
|
|
||||||
|
其中,mask为分割模型的游程编码,解析方式可参考 [demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
266
docs/Replace-Model-With-Anther-One.md
Normal file
266
docs/Replace-Model-With-Anther-One.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
<a name="0"></a>
|
||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍如何将FastDeploy的Demo模型,替换成开发者自己训练的AI模型。(**注意**:FastDeploy下载的SDK和Demo仅支持相同算法模型的替换)。本文档要求开发者已经将Demo和SDK运行跑通,如果要了解运行跑通Demo和SDK指导文档,可以参考[SDK使用文档](https://github.com/PaddlePaddle/FastDeploy/blob/develop/README.md#sdk使用)
|
||||||
|
|
||||||
|
* [简介](#0)<br>
|
||||||
|
* [模型替换](#1)<br>
|
||||||
|
* [1.模型准备](#2)<br>
|
||||||
|
* [1.1 Paddle模型](#3)<br>
|
||||||
|
* [1.2 Paddle OCR模型增加一步特殊转换](#4)<br>
|
||||||
|
* [1.2.1 下载模型转换工具](#5)<br>
|
||||||
|
* [1.2.2 下载模型转换工具](#6)<br>
|
||||||
|
* [1.3 其他框架模型](#7)<br>
|
||||||
|
* [2.模型名修改和label文件准备](#8)<br>
|
||||||
|
* [2.1 非OCR模型名修改](#9)<br>
|
||||||
|
* [2.2 OCR模型名修改](#10)<br>
|
||||||
|
* [2.3 模型label文件](#11)<br>
|
||||||
|
* [3.修改配置文件](#12)<br>
|
||||||
|
* [测试效果](#13)<br>
|
||||||
|
* [完整配置文件说明](#14)<br>
|
||||||
|
* [1.配置文件字段含义](#15)<br>
|
||||||
|
* [2.预处理顺序](#16)<br>
|
||||||
|
* [FAQ](#17)<br>
|
||||||
|
|
||||||
|
**注意事项:**
|
||||||
|
|
||||||
|
1. PP-PicoDet模型: 在FastDeploy中,支持PP-Picodet模型,是将后处理写到网络里面的方式(即后处理+NMS都在网络结构里面)。Paddle Detection导出静态模型时,有3种方法,选择将后处理和NMS导入到网络里面即可(参考[导出部分](https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.4/configs/picodet#%E5%AF%BC%E5%87%BA%E5%8F%8A%E8%BD%AC%E6%8D%A2%E6%A8%A1%E5%9E%8B))。详细网络区别,可以通过netron工具对比。
|
||||||
|
|
||||||
|
2. PP-Picodet模型:在FastDeploy中,支持PP-Picodet模型,是将前处理写在网络外面的方式。Paddle Detection中的TinyPose算法中,会将PP-PicoDet模型的前处理写入网络中。如果要使用FastDeploy的SDK进行模型替换,需要将前处理写到网络外面。(参考[Detection中的导出命令](https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.4/configs/keypoint/tiny_pose#%E5%B0%86%E8%AE%AD%E7%BB%83%E7%9A%84%E6%A8%A1%E5%9E%8B%E5%AE%9E%E7%8E%B0%E7%AB%AF%E4%BE%A7%E9%83%A8%E7%BD%B2),将TestReader.fuse_normalize=False即可)。
|
||||||
|
|
||||||
|
<a name="1"></a>
|
||||||
|
|
||||||
|
# 模型替换
|
||||||
|
|
||||||
|
开发者从PaddleDetection、PaddleClas、PaddleOCR、PaddleSeg等飞桨开发套件导出来的对应模型,完成 [1.模型准备](#)、[1.模型名修改和模型label](#)、[3.修改配置文件](#) 3步操作(需要相同算法才可替换),可完成自定义模型的模型文件,运行时指定新的模型文件,即可在自己训练的模型上实现相应的预测推理任务。
|
||||||
|
|
||||||
|
* Linux下模型资源文件夹路径:`EasyEdge-Linux-**/RES/` 。
|
||||||
|
* Windows下模型资源文件夹路径:`EasyEdge-Windows-**/data/model/`。
|
||||||
|
* Android下模型资源文件夹路径:`EasyEdge-Android-**/app/src/assets/infer/` 和 ` app/src/assets/demo/conf.json`
|
||||||
|
* iOS下模型资源文件夹路径:`EasyEdge-iOS-**/RES/easyedge/`
|
||||||
|
|
||||||
|
主要涉及到下面4个模型相关的文件(mode、params、label_list.txt、infer_cfg.json)和一个APP名相关的配置文件(仅Android、iOS、HTTP需要,APP名字,非必需。)
|
||||||
|
|
||||||
|
* ```
|
||||||
|
├── RES、model、infer # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
```
|
||||||
|
|
||||||
|
> ❗注意:OCR模型在ARM CPU硬件上(包括Android、Linux、iOS 三款操作系统),因为任务的特殊性,替换在 [1.模型准备](#)、[1.模型名修改和模型label](#) 不同于其他任务模型,详细参考下面步骤。
|
||||||
|
|
||||||
|
<a name="2"></a>
|
||||||
|
|
||||||
|
## 1.模型准备
|
||||||
|
|
||||||
|
<a name="3"></a>
|
||||||
|
|
||||||
|
### 1.1 Paddle模型
|
||||||
|
|
||||||
|
* 通过PaddleDetection、PaddleClas、PaddleOCR、PaddleSeg等导出来飞桨模型文件,包括如下文件(可能存在导出时修改了名字的情况,后缀`.pdmodel`为模型网络结构文件,后缀`.pdiparams`为模型权重文件):
|
||||||
|
|
||||||
|
```
|
||||||
|
model.pdmodel # 模型网络结构
|
||||||
|
model.pdiparams # 模型权重
|
||||||
|
model.yml # 模型的配置文件(包括预处理参数、模型定义等)
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="4"></a>
|
||||||
|
|
||||||
|
### 1.2 OCR模型特殊转换(仅在ARM CPU上需要)
|
||||||
|
|
||||||
|
因为推理引擎版本的问题,OCR模型需要在[1.1 Paddle模型](#3)导出`.pdmodel`和`.pdiparams`模型后,多增加一步模型转换的特殊处理,主要执行下面2步:
|
||||||
|
|
||||||
|
<a name="5"></a>
|
||||||
|
|
||||||
|
#### 1.2.1 下载模型转换工具
|
||||||
|
|
||||||
|
Linux 模型转换工具下载链接:[opt_linux](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.11/opt_linux)</br>
|
||||||
|
M1 模型转换工具下载链接:[opt_m1](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.11/opt_m1)</br>
|
||||||
|
mac 模型转换工具下载链接:[opt_mac](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.11/opt_mac)</br>
|
||||||
|
|
||||||
|
<a name="6"></a>
|
||||||
|
|
||||||
|
#### 1.2.2 模型转换
|
||||||
|
|
||||||
|
以下命令,以mac为例,完成模型转换。
|
||||||
|
|
||||||
|
```
|
||||||
|
* 转换 OCR 检测模型命名:
|
||||||
|
./opt_mac --model_dir=./ch_PP-OCRv3_det_infer/ --valid_targets=arm --optimize_out_type=naive_buffer --optimize_out=./ocr_det
|
||||||
|
|
||||||
|
* 转换 OCR 识别模型命名:
|
||||||
|
./opt_mac --model_dir=./ch_PP-OCRv3_rec_infer/ --valid_targets=arm --optimize_out_type=naive_buffer --optimize_out=./ocr_rec
|
||||||
|
```
|
||||||
|
|
||||||
|
产出:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175856746-501b05ad-6fba-482e-8e72-fdd68fe52101.png" width="400"></div>
|
||||||
|
|
||||||
|
<a name="7"></a>
|
||||||
|
|
||||||
|
### 1.3 其他框架模型
|
||||||
|
|
||||||
|
* 如果开发着是PyTorch、TensorFLow、Caffe、ONNX等其他框架模型,可以参考[X2Paddle](https://github.com/PaddlePaddle/X2Paddle)官网完成模型转换,即可得到对应的`model.pdmodel`和`model.pdiparams`模型文件。
|
||||||
|
|
||||||
|
<a name="8"></a>
|
||||||
|
|
||||||
|
## 2.模型名修改和label文件准备
|
||||||
|
|
||||||
|
<a name="9"></a>
|
||||||
|
|
||||||
|
### 2.1 非OCR模型名修改
|
||||||
|
|
||||||
|
按照下面的规则,修改套件导出来的模型名和标签文件,并替换到模型资源文件中。
|
||||||
|
|
||||||
|
```
|
||||||
|
1. model.pdmodel 修改成 model
|
||||||
|
2. model.pdiparams 修改成 params
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="10"></a>
|
||||||
|
|
||||||
|
### 2.2 OCR模型名修改
|
||||||
|
|
||||||
|
```
|
||||||
|
1. ocr_det.nb 修改成 model # 将 检测模型 修改名称成 model
|
||||||
|
2. ocr_rec.nb 修改成 params # 将 识别模型 修改名称成 model
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="11"></a>
|
||||||
|
|
||||||
|
### 2.3 模型label文件
|
||||||
|
|
||||||
|
同时需要准备模型文件对应的label文件`label_list.txt`。label文件可以参考原Demo中`label_list.txt`的格式准备。
|
||||||
|
|
||||||
|
<a name="12"></a>
|
||||||
|
|
||||||
|
## 3. 修改模型相关配置文件
|
||||||
|
|
||||||
|
(1)infer_cfg.json 文件修改
|
||||||
|
|
||||||
|
所有程序开发者都需要关注该配置文件。开发者在自己数据/任务中训练模型,可能会修改输入图像尺寸、修改阈值等操作,因此需要根据训练情况修改`Res文件夹下的infer_cfg.json`文件中的对应。CV任务涉及到的配置文件修改包括如下字段:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. "best_threshold": 0.3, #网络输出的阈值,根据开发者模型实际情况修改
|
||||||
|
2. "resize": [512, 512], #[w, h]网络输入图像尺寸,用户根据实际情况修改。
|
||||||
|
```
|
||||||
|
|
||||||
|
(2)conf.json 文件修改
|
||||||
|
仅Android、iOS、HTTP服务应用开发者,需要关注该配置文件。开发者根据自己应用程序命名需要,参考已有`conf.json`即可。
|
||||||
|
|
||||||
|
通常,开发者修改FastDeploy项目中的模型,涉及到主要是这几个配置信息的修改。FastDeploy详细的配置文件介绍参考[完整配置文件说明](#8)。
|
||||||
|
|
||||||
|
<a name="13"></a>
|
||||||
|
|
||||||
|
# 测试效果
|
||||||
|
|
||||||
|
将自定义准备的`RES`文件,按照第2、3步完成修改后,参考可以参考[SDK使用文档](https://github.com/PaddlePaddle/FastDeploy/blob/develop/README.md#sdk%E4%BD%BF%E7%94%A8)完成自己模型上的不同预测体验。
|
||||||
|
|
||||||
|
<a name="14"></a>
|
||||||
|
|
||||||
|
# 完整配置文件说明
|
||||||
|
|
||||||
|
<a name="15"></a>
|
||||||
|
|
||||||
|
## 1. 配置文件字段含义
|
||||||
|
|
||||||
|
模型资源文件`infer_cfg.json`涉及到大量不同算法的前后处理等信息,下表是相关的字段介绍,通常开发者如果没有修改算法前出处理,不需要关心这些字段。非标记【必须】的可不填。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"model_info": {
|
||||||
|
"best_threshold": 0.3, // 默认0.3
|
||||||
|
"model_kind": 1, // 【必须】 1-分类,2-检测,6-实例分割,12-追踪,14-语义分割,401-人脸,402-姿态,10001-决策
|
||||||
|
},
|
||||||
|
"pre_process": { // 【必须】
|
||||||
|
// 归一化, 预处理会把图像 (origin_img - mean) * scale
|
||||||
|
"skip_norm": false, // 默认为false, 如果设置为true,不做mean scale处理
|
||||||
|
"mean": [123, 123, 123], // 【必须,一般不需要动】图像均值,已经根据Paddle套件均值做了转换处理,开发者如果没有修改套件参数,可以不用关注。(X-mean)/ scale
|
||||||
|
"scale": [0.017, 0.017, 0.017], // 【必须,一般不需要动】
|
||||||
|
"color_format": "RGB", // BGR 【必须,一般不需要动】
|
||||||
|
"channel_order": "CHW", // HWC
|
||||||
|
// 大小相关
|
||||||
|
"resize": [300, 300], // w, h 【必须】
|
||||||
|
"rescale_mode": "keep_size", // 默认keep_size, keep_ratio, keep_ratio2, keep_raw_size, warp_affine
|
||||||
|
"max_size": 1366, // keep_ratio 用。如果没有提供,则用 resize[0]
|
||||||
|
"target_size": 800, // keep_ratio 用。如果没有提供,则用 resize[1]
|
||||||
|
"raw_size_range": [100, 10000], // keep_raw_size 用
|
||||||
|
"warp_affine_keep_res": // warp_affine模式使用,默认为false
|
||||||
|
"center_crop_size": [224, 224], // w, h, 如果需要做center_crop,则提供,否则,无需提供该字段
|
||||||
|
"padding": false,
|
||||||
|
"padding_mode": "padding_align32", // 【非必须】默认padding_align32, 其他可指定:padding_fill_size
|
||||||
|
"padding_fill_size": [416, 416], // 【非必须】仅padding_fill_size模式下需要提供, [fill_size_w, fill_size_h], 这里padding fill对齐paddle detection实现,在bottom和right方向实现补齐
|
||||||
|
"padding_fill_value": [114, 114, 114] // 【非必须】仅padding_fill_size模式下需要提供
|
||||||
|
// 其他
|
||||||
|
"letterbox": true,
|
||||||
|
},
|
||||||
|
"post_process": {
|
||||||
|
"box_normed": true, // 默认为true, 如果为false 则表示该模型的box坐标输出不是归一化的
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="16"></a>
|
||||||
|
|
||||||
|
## 2. 预处理顺序(没有的流程自动略过)
|
||||||
|
|
||||||
|
1. 灰度图 -> rgb图变换
|
||||||
|
2. resize 尺寸变换
|
||||||
|
3. center_crop
|
||||||
|
4. rgb/bgr变换
|
||||||
|
5. padding_fill_size
|
||||||
|
6. letterbox(画个厚边框,填上黑色)
|
||||||
|
7. chw/hwc变换
|
||||||
|
8. 归一化:mean, scale
|
||||||
|
9. padding_align32
|
||||||
|
|
||||||
|
rescale_mode说明:
|
||||||
|
|
||||||
|
* keep_size: 将图片缩放到resize指定的大小
|
||||||
|
* keep_ratio:将图片按比例缩放,长边不超过max_size,短边不超过target_size
|
||||||
|
* keep_raw_size:保持原图尺寸,但必须在raw_size_range之间
|
||||||
|
* warp_affine: 仿射变换,可以设置warp_affine_keep_res指定是否keep_res,在keep_res为false场景下,宽高通过resize字段指定
|
||||||
|
|
||||||
|
<a name="17"></a>
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
### 1. 如何处理一些 undefined reference / error while loading shared libraries?
|
||||||
|
|
||||||
|
> 如:./easyedge_demo: error while loading shared libraries: libeasyedge.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
|
遇到该问题时,请找到具体的库的位置,设置LD_LIBRARY_PATH;或者安装缺少的库。
|
||||||
|
|
||||||
|
> 示例一:libverify.so.1: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libveirfy.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../lib 解决(实际冒号后面添加的路径以libverify.so文件所在的路径为准)
|
||||||
|
|
||||||
|
> 示例二:libopencv_videoio.so.4.5: cannot open shared object file: No such file or directory
|
||||||
|
> 链接找不到libopencv_videoio.so文件,一般可通过 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../../thirdparty/opencv/lib 解决(实际冒号后面添加的路径以libopencv_videoio.so所在路径为准)
|
||||||
|
|
||||||
|
> 示例三:GLIBCXX_X.X.X not found
|
||||||
|
> 链接无法找到glibc版本,请确保系统gcc版本>=SDK的gcc版本。升级gcc/glibc可以百度搜索相关文献。
|
||||||
|
|
||||||
|
### 2. 使用libcurl请求http服务时,速度明显变慢
|
||||||
|
|
||||||
|
这是因为libcurl请求continue导致server等待数据的问题,添加空的header即可
|
||||||
|
|
||||||
|
```bash
|
||||||
|
headers = curl_slist_append(headers, "Expect:");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 运行二进制时,提示 libverify.so cannot open shared object file
|
||||||
|
|
||||||
|
可能cmake没有正确设置rpath, 可以设置LD_LIBRARY_PATH为sdk的lib文件夹后,再运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../lib ./easyedge_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 编译时报错:file format not recognized
|
||||||
|
|
||||||
|
可能是因为在复制SDK时文件信息丢失。请将整个压缩包复制到目标设备中,再解压缩、编译
|
389
docs/Windows-CPP-SDK-Inference.md
Normal file
389
docs/Windows-CPP-SDK-Inference.md
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍FastDeploy中的模型SDK ,在**Intel x86_64 / NVIDIA GPU Windows C++** 环境下:(1)SDK 图像和视频推理部署步骤;(2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中Windows Python请参考[Windows Python环境下的推理部署](./Windows-Python-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. CPP环境](#2-cpp环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试EasyEdge服务](#2-测试easyedge服务)
|
||||||
|
* [3. 预测图像](#3-预测图像)
|
||||||
|
* [4. 预测视频流](#4-预测视频流)
|
||||||
|
* [5. 编译Demo](#5-编译demo)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. SDK参数运行配置](#1-sdk参数运行配置)
|
||||||
|
* [2. 初始化Predictor](#2-初始化predictor)
|
||||||
|
* [3. 预测推理](#3-预测推理)
|
||||||
|
* [3.1 预测图像](#31-预测图像)
|
||||||
|
* [3.2 预测视频](#32-预测视频)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压缩后的文件结构如`快速开始`中[1项目介绍说明](#1-项目结构说明)介绍。
|
||||||
|
|
||||||
|
## 2. CPP环境
|
||||||
|
|
||||||
|
> 建议使用Microsoft Visual Studio 2015及以上版本,获取核心 C 和 C++ 支持,安装时请选择“使用 C++ 的桌面开发”工作负载。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-xxx
|
||||||
|
├── data
|
||||||
|
│ ├── model # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ └── config # 配置文件
|
||||||
|
├── bin # demo二进制程序
|
||||||
|
│ ├── xxx_image # 预测图像demo
|
||||||
|
│ ├── xxx_video # 预测视频流demo
|
||||||
|
│ └── xxx_serving # 启动http预测服务demo
|
||||||
|
├── dll # demo二进制程序依赖的动态库
|
||||||
|
├── ... # 二次开发依赖的文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
├── EasyEdge.exe # EasyEdge服务
|
||||||
|
└── README.md # 环境说明
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试EasyEdge服务
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中,请先将zip包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK下载完成后,双击打开EasyEdge.exe启动推理服务,输入要绑定的Host ip及端口号Port,点击启动服务。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854086-d507c288-56c8-4fa9-a00c-9d3cfeaac1c8.png" alt="图片" style="zoom: 67%;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
服务启动后,打开浏览器输入`http://{Host ip}:{Port}`,添加图片或者视频来进行测试。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854073-fb8189e5-0ffb-472c-a17d-0f35aa6a8418.png" style="zoom:67%;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 3. 预测图像
|
||||||
|
|
||||||
|
除了通过上述方式外,您还可以使用bin目录下的可执行文件来体验单一的功能。在dll目录下,点击右键,选择"在终端打开",执行如下命令。
|
||||||
|
|
||||||
|
> 需要将bin目录下的可执行文件移动到dll目录下执行,或者将dll目录添加到系统环境变量中。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.\easyedge_image_inference {模型model文件夹} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854068-28d27c0a-ef83-43ee-9e89-b65eed99b476.jpg" width="400"></div>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
2022-06-20 10:36:57,602 INFO [EasyEdge] 9788 EasyEdge Windows Development Kit 1.5.2(Build CPU.Generic 20220607) Release
|
||||||
|
e[37m--- Fused 0 subgraphs into layer_norm op.e[0m
|
||||||
|
2022-06-20 10:36:58,008 INFO [EasyEdge] 9788 Allocate graph success.
|
||||||
|
Results of image ..\demo.jpg:
|
||||||
|
8, n01514859 hen, p:0.953429
|
||||||
|
save result image to ..\demo.jpg.result-cpp.jpg
|
||||||
|
Done
|
||||||
|
```
|
||||||
|
|
||||||
|
可以看到,运行结果为`index:8,label:hen`,通过imagenet [类别映射表](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a),可以找到对应的类别,即 'hen',由此说明我们的预测结果正确。
|
||||||
|
|
||||||
|
## 4. 预测视频流
|
||||||
|
|
||||||
|
```
|
||||||
|
.\easyedge_video_inference {模型model文件夹} {video_type} {video_src}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中video_type支持三种视频流类型,它们分别是:(1)本地视频文件 (2)本地摄像头id(3)网络视频流地址。
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 输入源类型
|
||||||
|
*/
|
||||||
|
enum class SourceType {
|
||||||
|
kVideoFile = 1, // 本地视频文件
|
||||||
|
kCameraId = 2, // 摄像头的index
|
||||||
|
kNetworkStream = 3, // 网络视频流
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
video_src 即为文件路径。
|
||||||
|
|
||||||
|
## 5. 编译Demo
|
||||||
|
|
||||||
|
在[项目结构说明](#1-项目结构说明)中,`bin`路径下的可执行文件是由`src`下的对应文件编译得到的,具体的编译命令如下。
|
||||||
|
|
||||||
|
```
|
||||||
|
cd src
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && make
|
||||||
|
```
|
||||||
|
|
||||||
|
编译完成后,在build文件夹下会生成编译好的可执行文件,如图像推理的二进制文件:`build/demo_serving/easyedge_serving`。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合前文的Demo示例来介绍推理API,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。图像、视频的推理包含以下3个API,查看下面的cpp代码中的step注释说明。
|
||||||
|
|
||||||
|
> ❗注意:
|
||||||
|
> (1)`src`文件夹中包含完整可编译的cmake工程实例,建议开发者先行了解[cmake工程基本知识](https://cmake.org/cmake/help/latest/guide/tutorial/index.html)。
|
||||||
|
> (2)请优先参考SDK中自带的Demo工程的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// step 1: SDK配置运行参数
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
|
||||||
|
// step 2: 创建并初始化Predictor;这这里选择合适的引擎
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
|
||||||
|
// step 3-1: 预测图像
|
||||||
|
auto img = cv::imread({图片路径});
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
predictor->infer(img, results);
|
||||||
|
|
||||||
|
// step 3-2: 预测视频
|
||||||
|
std::vector<EdgeResultData> results;
|
||||||
|
FrameTensor frame_tensor;
|
||||||
|
VideoConfig video_config;
|
||||||
|
video_config.source_type = static_cast<SourceType>(video_type); // source_type 定义参考头文件 easyedge_video.h
|
||||||
|
video_config.source_value = video_src;
|
||||||
|
/*
|
||||||
|
... more video_configs, 根据需要配置video_config的各选项
|
||||||
|
*/
|
||||||
|
auto video_decoding = CreateVideoDecoding(video_config);
|
||||||
|
while (video_decoding->next(frame_tensor) == EDGE_OK) {
|
||||||
|
results.clear();
|
||||||
|
if (frame_tensor.is_needed) {
|
||||||
|
predictor->infer(frame_tensor.frame, results);
|
||||||
|
render(frame_tensor.frame, results, predictor->model_info().kind);
|
||||||
|
}
|
||||||
|
//video_decoding->display(frame_tensor); // 显示当前frame,需在video_config中开启配置
|
||||||
|
//video_decoding->save(frame_tensor); // 存储当前frame到视频,需在video_config中开启配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若需自定义library search path或者gcc路径,修改对应Demo工程下的CMakeList.txt即可。
|
||||||
|
|
||||||
|
## 1. SDK参数运行配置
|
||||||
|
|
||||||
|
SDK的参数通过`EdgePredictorConfig::set_config`和`global_controller()->set_config`配置。本Demo 中设置了模型路径,其他参数保留默认参数。更详细的支持运行参数等,可以参考开发工具包中的头文件(`include/easyedge/easyedge_xxxx_config.h`)的详细说明。
|
||||||
|
|
||||||
|
配置参数使用方法如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
EdgePredictorConfig config;
|
||||||
|
config.model_dir = {模型文件目录};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化Predictor
|
||||||
|
|
||||||
|
- 接口
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto predictor = global_controller()->CreateEdgePredictor(config);
|
||||||
|
predictor->init();
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回非0,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. 预测推理
|
||||||
|
|
||||||
|
### 3.1 预测图像
|
||||||
|
|
||||||
|
> 在Demo中展示了预测接口infer()传入cv::Mat& image图像内容,并将推理结果赋值给std::vector& result。更多关于infer()的使用,可以根据参考`easyedge.h`头文件中的实际情况、参数说明自行传入需要的内容做推理
|
||||||
|
|
||||||
|
- 接口输入
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* 通用接口
|
||||||
|
* @param image: must be BGR , HWC format (opencv default)
|
||||||
|
* @param result
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int infer(cv::Mat& image, std::vector<EdgeResultData>& result) = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
图片的格式务必为opencv默认的BGR, HWC格式。
|
||||||
|
|
||||||
|
- 接口返回
|
||||||
|
|
||||||
|
`EdgeResultData`中可以获取对应的分类信息、位置信息。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct EdgeResultData {
|
||||||
|
int index; // 分类结果的index
|
||||||
|
std::string label; // 分类结果的label
|
||||||
|
float prob; // 置信度
|
||||||
|
|
||||||
|
// 物体检测 或 图像分割时使用:
|
||||||
|
float x1, y1, x2, y2; // (x1, y1): 左上角, (x2, y2): 右下角; 均为0~1的长宽比例值。
|
||||||
|
|
||||||
|
// 图像分割时使用:
|
||||||
|
cv::Mat mask; // 0, 1 的mask
|
||||||
|
std::string mask_rle; // Run Length Encoding,游程编码的mask
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于图像分割mask ***
|
||||||
|
|
||||||
|
```
|
||||||
|
cv::Mat mask为图像掩码的二维数组
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于图像分割mask_rle ***
|
||||||
|
|
||||||
|
该字段返回了mask的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
以上字段可以参考demo文件中使用opencv绘制的逻辑进行解析。
|
||||||
|
|
||||||
|
### 3.2 预测视频
|
||||||
|
|
||||||
|
SDK 提供了支持摄像头读取、视频文件和网络视频流的解析工具类`VideoDecoding`,此类提供了获取视频帧数据的便利函数。通过`VideoConfig`结构体可以控制视频/摄像头的解析策略、抽帧策略、分辨率调整、结果视频存储等功能。对于抽取到的视频帧可以直接作为SDK infer 接口的参数进行预测。
|
||||||
|
|
||||||
|
- 接口输入
|
||||||
|
|
||||||
|
class`VideoDecoding`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 获取输入源的下一帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int next(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示当前frame_tensor中的视频帧
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int display(const FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将当前frame_tensor中的视频帧写为本地视频文件
|
||||||
|
* @param frame_tensor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int save(FrameTensor &frame_tensor) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的fps属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_fps() = 0;
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的width属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_width() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取视频的height属性
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual int get_height() = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
struct `VideoConfig`
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* @brief 视频源、抽帧策略、存储策略的设置选项
|
||||||
|
*/
|
||||||
|
struct VideoConfig {
|
||||||
|
SourceType source_type; // 输入源类型
|
||||||
|
std::string source_value; // 输入源地址,如视频文件路径、摄像头index、网络流地址
|
||||||
|
int skip_frames{0}; // 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true
|
||||||
|
int retrieve_all{false}; // 是否抽取所有frame以便于作为显示和存储,对于不满足skip_frames策略的frame,把所抽取帧的is_needed置为false
|
||||||
|
int input_fps{0}; // 在采取抽帧之前设置视频的fps
|
||||||
|
Resolution resolution{Resolution::kAuto}; // 采样分辨率,只对camera有效
|
||||||
|
|
||||||
|
bool enable_display{false}; // 默认不支持。
|
||||||
|
std::string window_name{"EasyEdge"};
|
||||||
|
bool display_all{false}; // 是否显示所有frame,若为false,仅显示根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
bool enable_save{false};
|
||||||
|
std::string save_path; // frame存储为视频文件的路径
|
||||||
|
bool save_all{false}; // 是否存储所有frame,若为false,仅存储根据skip_frames抽取的frame
|
||||||
|
|
||||||
|
std::map<std::string, std::string> conf;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
| 序号 | 字段 | 含义 |
|
||||||
|
| --- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 1 | `source_type` | 输入源类型,支持视频文件、摄像头、网络视频流三种,值分别为1、2、3 |
|
||||||
|
| 2 | `source_value` | 若`source_type`为视频文件,该值为指向视频文件的完整路径;若`source_type`为摄像头,该值为摄像头的index,如对于`/dev/video0`的摄像头,则index为0;若`source_type`为网络视频流,则为该视频流的完整地址。 |
|
||||||
|
| 3 | `skip_frames` | 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true,标记为is_needed的帧是用来做预测的帧。反之,直接跳过该帧,不经过预测。 |
|
||||||
|
| 4 | `retrieve_all` | 若置该项为true,则无论是否设置跳帧,所有的帧都会被抽取返回,以作为显示或存储用。 |
|
||||||
|
| 5 | `input_fps` | 用于抽帧前设置fps |
|
||||||
|
| 6 | `resolution` | 设置摄像头采样的分辨率,其值请参考`easyedge_video.h`中的定义,注意该分辨率调整仅对输入源为摄像头时有效 |
|
||||||
|
| 7 | `conf` | 高级选项。部分配置会通过该map来设置 |
|
||||||
|
|
||||||
|
*** 注意:***
|
||||||
|
|
||||||
|
1. `VideoConfig`不支持`display`功能。如果需要使用`VideoConfig`的`display`功能,需要自行编译带有GTK选项的OpenCV。
|
||||||
|
|
||||||
|
2. 使用摄像头抽帧时,如果通过`resolution`设置了分辨率调整,但是不起作用,请添加如下选项:
|
||||||
|
|
||||||
|
```
|
||||||
|
video_config.conf["backend"] = "2";
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 部分设备上的CSI摄像头尚未兼容,如遇到问题,可以通过工单、QQ交流群或微信交流群反馈。
|
||||||
|
|
||||||
|
具体接口调用流程,可以参考SDK中的`demo_video_inference`。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
275
docs/Windows-CPP-SDK-Serving.md
Normal file
275
docs/Windows-CPP-SDK-Serving.md
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,本文档介绍FastDeploy中的模型SDK,在**Intel x86_64 / NVIDIA GPU、Windows操作系统** 的C++环境:(1)HTTP服务化推理部署步骤,(2)介绍推理全流程API,方便开发者了解项目后二次开发。
|
||||||
|
如果开发者对Python语言的相关能力感兴趣,可以参考Windows Python请参考[Windows Python环境下的推理部署](./Windows-Python-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. CPP环境](#2-cpp环境)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试EasyEdge服务](#2-测试easyedge服务)
|
||||||
|
* [3. 启动HTTP预测服务](#3-启动http预测服务)
|
||||||
|
* [4. 编译Demo](#4-编译demo)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
* [2.1 http 请求方式一:不使用图片base64格式](#21-http-请求方式一不使用图片base64格式)
|
||||||
|
* [2.2 http 请求方法二:使用图片base64格式](#22-http-请求方法二使用图片base64格式)
|
||||||
|
* [3. http 返回数据](#3-http-返回数据)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压缩后的文件结构如`快速开始`中[1项目介绍说明](#1-%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E)介绍。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. CPP环境
|
||||||
|
|
||||||
|
> 建议使用Microsoft Visual Studio 2015及以上版本,获取核心 C 和 C++ 支持,安装时请选择“使用 C++ 的桌面开发”工作负载。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-xxx
|
||||||
|
├── data
|
||||||
|
│ ├── model # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ └── config # 配置文件
|
||||||
|
├── bin # demo二进制程序
|
||||||
|
│ ├── xxx_image # 预测图像demo
|
||||||
|
│ ├── xxx_video # 预测视频流demo
|
||||||
|
│ └── xxx_serving # 启动http预测服务demo
|
||||||
|
├── dll # demo二进制程序依赖的动态库
|
||||||
|
├── ... # 二次开发依赖的文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
├── EasyEdge.exe # EasyEdge服务
|
||||||
|
└── README.md # 环境说明
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试EasyEdge服务
|
||||||
|
|
||||||
|
> 模型资源文件默认已经打包在开发者下载的SDK包中,请先将zip包整体拷贝到具体运行的设备中,再解压缩使用。
|
||||||
|
|
||||||
|
SDK下载完成后,双击打开EasyEdge.exe启动推理服务,输入要绑定的Host ip及端口号Port,点击启动服务。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854086-d507c288-56c8-4fa9-a00c-9d3cfeaac1c8.png" alt="图片" style="zoom: 67%;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
服务启动后,打开浏览器输入`http://{Host ip}:{Port}`,添加图片或者视频来进行测试。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854073-fb8189e5-0ffb-472c-a17d-0f35aa6a8418.png" style="zoom:67%;" />
|
||||||
|
</div>
|
||||||
|
## 3. 启动HTTP预测服务
|
||||||
|
|
||||||
|
除了通过上述方式外,您还可以使用bin目录下的可执行文件来体验单一的功能。在dll目录下,点击右键,选择"在终端打开",执行如下命令。
|
||||||
|
|
||||||
|
> 需要将bin目录下的可执行文件移动到dll目录下执行,或者将dll目录添加到系统环境变量中。
|
||||||
|
|
||||||
|
```
|
||||||
|
.\easyedge_serving {模型model文件夹路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后,日志中会显示如下字样。
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP is now serving at 0.0.0.0:24401
|
||||||
|
```
|
||||||
|
|
||||||
|
此时,开发者可以打开浏览器,`http://127.0.0.1:24401`,执行和之前一样的操作即可。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 4. 编译Demo
|
||||||
|
|
||||||
|
在[项目结构说明](#1项目结构说明)中,`bin`路径下的可执行文件是由`src`下的对应文件编译得到的,具体的编译命令如下。
|
||||||
|
|
||||||
|
```
|
||||||
|
cd src
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && make
|
||||||
|
```
|
||||||
|
|
||||||
|
编译完成后,在build文件夹下会生成编译好的可执行文件,如图像推理的二进制文件:`build/demo_serving/easyedge_serving`。
|
||||||
|
|
||||||
|
# HTTP API流程详解
|
||||||
|
|
||||||
|
本章节主要结合[2.1 HTTP Demo](#4)的API介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`include/easyedge/easyedge*.h`文件。http服务包含服务端和客户端,目前支持的能力包括以下几种方式,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动可直接使用`bin/easyedge_serving`,或参考`src/demo_serving.cpp`文件修改相关逻辑
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief 开启一个简单的demo http服务。
|
||||||
|
* 该方法会block直到收到sigint/sigterm。
|
||||||
|
* http服务里,图片的解码运行在cpu之上,可能会降低推理速度。
|
||||||
|
* @tparam ConfigT
|
||||||
|
* @param config
|
||||||
|
* @param host
|
||||||
|
* @param port
|
||||||
|
* @param service_id service_id user parameter, uri '/get/service_id' will respond this value with 'text/plain'
|
||||||
|
* @param instance_num 实例数量,根据内存/显存/时延要求调整
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template<typename ConfigT>
|
||||||
|
int start_http_server(
|
||||||
|
const ConfigT &config,
|
||||||
|
const std::string &host,
|
||||||
|
int port,
|
||||||
|
const std::string &service_id,
|
||||||
|
int instance_num = 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式一:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容(无需base64, 无需json)
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```Python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 http 请求方法二:使用图片base64格式
|
||||||
|
|
||||||
|
HTTP方法:POST
|
||||||
|
Header如下:
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
| ------------ | ---------------- |
|
||||||
|
| Content-Type | application/json |
|
||||||
|
|
||||||
|
**Body请求填写**:
|
||||||
|
|
||||||
|
- 分类网络:
|
||||||
|
body 中请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
"top_num": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| ------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| top_num | 否 | number | - | 返回分类数量,不填该参数,则默认返回全部分类结果 |
|
||||||
|
|
||||||
|
- 检测和分割网络:
|
||||||
|
Body请求示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"image": "<base64数据>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
body中参数详情:
|
||||||
|
|
||||||
|
| 参数 | 是否必选 | 类型 | 可选值范围 | 说明 |
|
||||||
|
| --------- | ---- | ------ | ----- | ----------------------------------------------------------------------------------- |
|
||||||
|
| image | 是 | string | - | 图像数据,base64编码,要求base64图片编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/png/bmp格式 **注意去掉头部** |
|
||||||
|
| threshold | 否 | number | - | 默认为推荐阈值,也可自行根据需要进行设置 |
|
||||||
|
|
||||||
|
## 3. http 返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*** 关于矩形坐标 ***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
*** 关于分割模型 ***
|
||||||
|
|
||||||
|
其中,mask为分割模型的游程编码,解析方式可参考 [http demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
381
docs/Windows-Python-SDK-Inference.md
Normal file
381
docs/Windows-Python-SDK-Inference.md
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍 FastDeploy中的模型SDK ,在**Intel x86_64 / NVIDIA GPU Windows Python** 环境下: (1)图像推理部署步骤; (2)介绍模型推流全流程API,方便开发者了解项目后二次开发。
|
||||||
|
其中Windows Python请参考[Windows C++环境下的推理部署](./Windows-CPP-SDK-Inference.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. Python环境](#2-python环境)
|
||||||
|
* [3. 安装依赖](#3-安装依赖)
|
||||||
|
* [3.1 安装paddlepaddle](#31-安装paddlepaddle)
|
||||||
|
* [3.2 安装EasyEdge Python Wheel 包](#32-安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 文件结构说明](#1-文件结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1 预测图像](#21-预测图像)
|
||||||
|
|
||||||
|
* [预测API流程详解](#预测api流程详解)
|
||||||
|
|
||||||
|
* [1. 基础流程](#1-基础流程)
|
||||||
|
* [2. 初始化](#2-初始化)
|
||||||
|
* [3. SDK参数配置](#3-sdk参数配置)
|
||||||
|
* [4. 预测图像](#4-预测图像)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压缩后的文件结构如下所示:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-[部署芯片]
|
||||||
|
├── data # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
├── ... # C++/C# 相关文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
├── EasyEdge.exe # 主程序
|
||||||
|
└── README.md # 环境说明
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="3"></a>
|
||||||
|
|
||||||
|
## 2. Python环境
|
||||||
|
|
||||||
|
> 当前SDK仅支持Python 3.7
|
||||||
|
|
||||||
|
打开命令行工具,使用如下命令获取已安装的Python版本号。如果还没有安装Python环境,可以前往[官网](https://www.python.org/)下载Python 3.7对应的安装程序,特别要注意勾上`Add Python 3.7 to PATH`,然后点“Install Now”即可完成安装。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python --version
|
||||||
|
```
|
||||||
|
|
||||||
|
如果本机的版本不匹配,建议使用[pyenv](https://github.com/pyenv/pyenv)、[anaconda](https://www.anaconda.com/)等Python版本管理工具对Python SDK所在目录进行配置。
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 安装依赖
|
||||||
|
|
||||||
|
### 3.1 安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。`x86_64 CPU` 平台可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip install paddlepaddle==2.2.2 -i https://mirror.baidu.com/pypi/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
`NVIDIA GPU平台`的详细安装教程可以参考[官网Paddle安装教程](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)。
|
||||||
|
|
||||||
|
> 使用 NVIDIA GPU 预测时,必须满足:
|
||||||
|
>
|
||||||
|
> 1. 机器已安装 cuda, cudnn
|
||||||
|
>
|
||||||
|
> 2. 已正确安装对应 cuda 版本的paddle 版本
|
||||||
|
> 3. 通过设置环境变量`FLAGS_fraction_of_gpu_memory_to_use`设置合理的初始内存使用比例
|
||||||
|
|
||||||
|
<a name="6"></a>
|
||||||
|
|
||||||
|
### 3.2 安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装Python3.7版本对应的EasyEdge Wheel包。对`x86_64 CPU` 或 `x86_64 Nvidia GPU平台 `可以使用如下命令进行安装,具体名称以 Python SDK 包中的 whl 为准。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip install -U BaiduAI_EasyEdge_SDK-{SDK版本号}-cp37-cp37m-win_amd64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="7"></a>
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
<a name="8"></a>
|
||||||
|
|
||||||
|
## 1. 文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-[部署芯片]
|
||||||
|
├── data # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ ├── model # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ └── config # 配置文件
|
||||||
|
├── ... # C++/C# 相关文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
│ ├── # 特定Python 3.7版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
│ ├── BaiduAI_EasyEdge_SDK-${SDK版本号}-cp37-cp37m-win_amd64.whl
|
||||||
|
│ ├── requirements.txt #
|
||||||
|
│ ├── infer_demo # demo体验完整文件
|
||||||
|
│ │ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ │ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
│ └── tensor_demo # tensor in/out demo文件
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="9"></a>
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
<a name="10"></a>
|
||||||
|
|
||||||
|
### 2.1 预测图像
|
||||||
|
|
||||||
|
根据部署平台,使用infer_demo文件夹下的demo文件,执行如下命令。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python demo_x86_cpu.py {模型model文件夹} {测试图片路径}
|
||||||
|
```
|
||||||
|
|
||||||
|
运行效果示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854068-28d27c0a-ef83-43ee-9e89-b65eed99b476.jpg" width="400"></div>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
2022-06-14 18:35:44 DEBUG [EasyEdge] [demo_x86_cpu.py:41] 19424: Config:: w: 256, h: 256; mean: [123.675, 116.28, 103.53]; scale: [0.01712475 0.017507 0.01742919]
|
||||||
|
2022-06-14 18:35:44 INFO [EasyEdge] [demo_x86_cpu.py:41] 19424: Init paddlefluid engine...
|
||||||
|
2022-06-14 18:35:45 INFO [EasyEdge] [demo_x86_cpu.py:41] 19424: Paddle version: 2.2.2
|
||||||
|
2022-06-14 18:35:45 DEBUG [EasyEdge] [demo_x86_cpu.py:41] 19424: CPU thread num set to 1
|
||||||
|
2022-06-14 18:35:45 DEBUG [EasyEdge] [demo_x86_cpu.py:55] 19424: resize to w257, h256
|
||||||
|
2022-06-14 18:35:45 DEBUG [EasyEdge] [demo_x86_cpu.py:55] 19424: Switch to CHW
|
||||||
|
2022-06-14 18:35:45 DEBUG [EasyEdge] [demo_x86_cpu.py:55] 19424: Infer cost: 70.1(66.1) ms
|
||||||
|
{'confidence': 0.9012351036071777, 'index': 8, 'label': 'n01514859 hen'}
|
||||||
|
```
|
||||||
|
|
||||||
|
可以看到,运行结果为`index:8,label:hen`,通过imagenet [类别映射表](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a),可以找到对应的类别,即 'hen',由此说明我们的预测结果正确。
|
||||||
|
|
||||||
|
# 预测API流程详解
|
||||||
|
|
||||||
|
本章节主要结合前文的Demo示例来介绍推理API,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考`infer_demo/demo_xx_xx.py`文件,查看下面的Python代码中的step注释说明。
|
||||||
|
|
||||||
|
## 1. 基础流程
|
||||||
|
|
||||||
|
> ❗注意,请优先参考SDK中自带demo的使用流程和说明。遇到错误,请优先参考文件中的注释、解释、日志说明。
|
||||||
|
|
||||||
|
`infer_demo/demo_xx_xx.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 引入EasyEdge运行库
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
|
||||||
|
# 创建并初始化一个预测Progam;选择合适的引擎
|
||||||
|
pred = edge.Program()
|
||||||
|
pred.init(model_dir={RES文件夹路径}, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
|
||||||
|
# 预测图像
|
||||||
|
res = pred.infer_image({numpy.ndarray的图片})
|
||||||
|
|
||||||
|
# 关闭结束预测Progam
|
||||||
|
pred.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
`infer_demo/demo_serving.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import BaiduAI.EasyEdge as edge
|
||||||
|
from BaiduAI.EasyEdge.serving import Serving
|
||||||
|
|
||||||
|
# 创建并初始化Http服务
|
||||||
|
server = Serving(model_dir={RES文件夹路径}, license=serial_key)
|
||||||
|
|
||||||
|
# 运行Http服务
|
||||||
|
# 请参考同级目录下demo_xx_xx.py里:
|
||||||
|
# pred.init(model_dir=xx, device=xx, engine=xx, device_id=xx)
|
||||||
|
# 对以下参数device\device_id和engine进行修改
|
||||||
|
server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 CPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID) # x86_64 Nvidia GPU
|
||||||
|
# server.run(host=host, port=port, device=edge.Device.CPU, engine=edge.Engine.PADDLE_LITE) # armv8 CPU
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 初始化
|
||||||
|
|
||||||
|
- 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def init(self,
|
||||||
|
model_dir,
|
||||||
|
device=Device.CPU,
|
||||||
|
engine=Engine.PADDLE_FLUID,
|
||||||
|
config_file='conf.json',
|
||||||
|
preprocess_file='preprocess_args.json',
|
||||||
|
model_file='model',
|
||||||
|
params_file='params',
|
||||||
|
label_file='label_list.txt',
|
||||||
|
infer_cfg_file='infer_cfg.json',
|
||||||
|
device_id=0,
|
||||||
|
thread_num=1
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
model_dir: str
|
||||||
|
device: BaiduAI.EasyEdge.Device,比如:Device.CPU
|
||||||
|
engine: BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID
|
||||||
|
config_file: str
|
||||||
|
preprocess_file: str
|
||||||
|
model_file: str
|
||||||
|
params_file: str
|
||||||
|
label_file: str 标签文件
|
||||||
|
infer_cfg_file: 包含预处理、后处理信息的文件
|
||||||
|
device_id: int 设备ID
|
||||||
|
thread_num: int CPU的线程数
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError, IOError
|
||||||
|
Returns:
|
||||||
|
bool: True if success
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
若返回不是True,请查看输出日志排查错误原因。
|
||||||
|
|
||||||
|
## 3. SDK参数配置
|
||||||
|
|
||||||
|
使用 CPU 预测时,可以通过在 init 中设置 thread_num 使用多线程预测。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.CPU, engine=edge.Engine.PADDLE_FLUID, thread_num=4)
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 GPU 预测时,可以通过在 init 中设置 device_id 指定需要的GPU device id。如:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pred.init(model_dir=_model_dir, device=edge.Device.GPU, engine=edge.Engine.PADDLE_FLUID, device_id=0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 预测图像
|
||||||
|
|
||||||
|
- 接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
def infer_image(self, img,
|
||||||
|
threshold=0.3,
|
||||||
|
channel_order='HWC',
|
||||||
|
color_format='BGR',
|
||||||
|
data_type='numpy')
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
img: np.ndarray or bytes
|
||||||
|
threshold: float
|
||||||
|
only return result with confidence larger than threshold
|
||||||
|
channel_order: string
|
||||||
|
channel order HWC or CHW
|
||||||
|
color_format: string
|
||||||
|
color format order RGB or BGR
|
||||||
|
data_type: string
|
||||||
|
仅在图像分割时有意义。 'numpy' or 'string'
|
||||||
|
'numpy': 返回已解析的mask
|
||||||
|
'string': 返回未解析的mask游程编码
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list
|
||||||
|
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
| 字段 | 类型 | 取值 | 说明 |
|
||||||
|
| ---------- | -------------------- | --------- | ------------------------ |
|
||||||
|
| confidence | float | 0~1 | 分类或检测的置信度 |
|
||||||
|
| label | string | | 分类或检测的类别 |
|
||||||
|
| index | number | | 分类或检测的类别 |
|
||||||
|
| x1, y1 | float | 0~1 | 物体检测,矩形的左上角坐标 (相对长宽的比例值) |
|
||||||
|
| x2, y2 | float | 0~1 | 物体检测,矩形的右下角坐标(相对长宽的比例值) |
|
||||||
|
| mask | string/numpy.ndarray | 图像分割的mask | |
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
可以参考 demo 文件中使用 opencv 绘制矩形的逻辑。
|
||||||
|
|
||||||
|
***结果示例***
|
||||||
|
|
||||||
|
i) 图像分类
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 736,
|
||||||
|
"label": "table",
|
||||||
|
"confidence": 0.9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
ii) 物体检测
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"index": 8,
|
||||||
|
"label": "cat",
|
||||||
|
"confidence": 1.0,
|
||||||
|
"x1": 0.21289,
|
||||||
|
"y1": 0.12671,
|
||||||
|
"x2": 0.91504,
|
||||||
|
"y2": 0.91211,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
iii) 图像分割
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "cat",
|
||||||
|
"score": 1.0,
|
||||||
|
"location": {
|
||||||
|
"left": ...,
|
||||||
|
"top": ...,
|
||||||
|
"width": ...,
|
||||||
|
"height": ...,
|
||||||
|
},
|
||||||
|
"mask": ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
mask字段中,data_type为`numpy`时,返回图像掩码的二维数组
|
||||||
|
|
||||||
|
```text
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
}
|
||||||
|
其中1代表为目标区域,0代表非目标区域
|
||||||
|
```
|
||||||
|
|
||||||
|
data_type为`string`时,mask的游程编码,解析方式可参考 [demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
262
docs/Windows-Python-SDK-Serving.md
Normal file
262
docs/Windows-Python-SDK-Serving.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档以[千分类模型_MobileNetV3](https://ai.baidu.com/easyedge/app/openSource)为例,介绍FastDeploy中的模型SDK,在**Intel x86_64 /NVIDIA GPU、Windows操作系统** 的Python环境:(1)HTTP服务化推理部署步骤,(2)介绍推理全流程API,方便开发者了解项目后二次开发。
|
||||||
|
如果开发者对C++语言的相关能力感兴趣,可以参考Windows C++请参考[Windows C++环境下的推理部署](./Windows-CPP-SDK-Serving.md)文档。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [环境准备](#环境准备)
|
||||||
|
|
||||||
|
* [1. SDK下载](#1-sdk下载)
|
||||||
|
* [2. Python环境](#2-python环境)
|
||||||
|
* [3. 安装依赖](#3-安装依赖)
|
||||||
|
* [3.1 安装paddlepaddle](#31-安装paddlepaddle)
|
||||||
|
* [3.2 安装EasyEdge Python Wheel 包](#32-安装easyedge-python-wheel-包)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 文件结构说明](#1-文件结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
* [2.1 启动HTTP预测服务](#21-启动http预测服务)
|
||||||
|
|
||||||
|
* [HTTP API流程详解](#http-api流程详解)
|
||||||
|
|
||||||
|
* [1. 开启http服务](#1-开启http服务)
|
||||||
|
|
||||||
|
* [2. 请求http服务](#2-请求http服务)
|
||||||
|
|
||||||
|
* [2.1 http 请求方式:不使用图片base64格式](#21-http-请求方式不使用图片base64格式)
|
||||||
|
|
||||||
|
* [3. http返回数据](#3-http返回数据)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 环境准备
|
||||||
|
|
||||||
|
## 1. SDK下载
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。解压缩后的文件结构如下所示:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-[部署芯片]
|
||||||
|
├── data # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
├── ... # C++/C# 相关文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
├── EasyEdge.exe # 主程序
|
||||||
|
└── README.md # 环境说明
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Python环境
|
||||||
|
|
||||||
|
> 当前SDK仅支持Python 3.7
|
||||||
|
|
||||||
|
打开命令行工具,使用如下命令获取已安装的Python版本号。如果还没有安装Python环境,可以前往[官网](https://www.python.org/)下载Python 3.7对应的安装程序,特别要注意勾上`Add Python 3.7 to PATH`,然后点“Install Now”即可完成安装。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python --version
|
||||||
|
```
|
||||||
|
|
||||||
|
如果本机的版本不匹配,建议使用[pyenv](https://github.com/pyenv/pyenv)、[anaconda](https://www.anaconda.com/)等Python版本管理工具对Python SDK所在目录进行配置。
|
||||||
|
|
||||||
|
接着使用如下命令确认pip的版本是否满足要求,要求pip版本为20.2.2或更高版本。详细的pip安装过程可以参考[官网教程](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 安装依赖
|
||||||
|
|
||||||
|
### 3.1 安装paddlepaddle
|
||||||
|
|
||||||
|
根据具体的部署芯片(CPU/GPU)安装对应的PaddlePaddle的whl包。`x86_64 CPU` 平台可以使用如下命令进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip install paddlepaddle==2.2.2 -i https://mirror.baidu.com/pypi/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
`NVIDIA GPU平台`的详细安装教程可以参考[官网Paddle安装教程](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)。
|
||||||
|
|
||||||
|
> 使用 NVIDIA GPU 预测时,必须满足:
|
||||||
|
>
|
||||||
|
> 1. 机器已安装 cuda, cudnn
|
||||||
|
>
|
||||||
|
> 2. 已正确安装对应 cuda 版本的paddle 版本
|
||||||
|
> 3. 通过设置环境变量`FLAGS_fraction_of_gpu_memory_to_use`设置合理的初始内存使用比例
|
||||||
|
|
||||||
|
### 3.2 安装EasyEdge Python Wheel 包
|
||||||
|
|
||||||
|
在`python`目录下,安装Python3.7版本对应的EasyEdge Wheel包。对`x86_64 CPU` 或 `x86_64 Nvidia GPU平台 `可以使用如下命令进行安装,具体名称以 Python SDK 包中的 whl 为准。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m pip install -U BaiduAI_EasyEdge_SDK-{SDK版本号}-cp37-cp37m-win_amd64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 文件结构说明
|
||||||
|
|
||||||
|
Python SDK文件结构如下:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
EasyEdge-win-[部署芯片]
|
||||||
|
├── data # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ ├── model # 模型文件资源文件夹,可替换为其他模型
|
||||||
|
│ └── config # 配置文件
|
||||||
|
├── ... # C++/C# 相关文件
|
||||||
|
├── python # Python SDK文件
|
||||||
|
│ ├── # 特定Python 3.7版本的EasyEdge Wheel包, 二次开发可使用
|
||||||
|
│ ├── BaiduAI_EasyEdge_SDK-${SDK版本号}-cp37-cp37m-win_amd64.whl
|
||||||
|
│ ├── requirements.txt #
|
||||||
|
│ ├── infer_demo # demo体验完整文件
|
||||||
|
│ │ ├── demo_xxx.py # 包含前后处理的端到端推理demo文件
|
||||||
|
│ │ └── demo_serving.py # 提供http服务的demo文件
|
||||||
|
│ └── tensor_demo # tensor in/out demo文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
### 2.1 启动HTTP预测服务
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python demo_serving.py {模型model文件夹} {host, default 0.0.0.0} {port, default 24401}
|
||||||
|
```
|
||||||
|
|
||||||
|
成功启动后,终端中会显示如下字样。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
2022-06-14 18:45:15 INFO [EasyEdge] [demo_serving.py:50] 21212: Init paddlefluid engine...
|
||||||
|
2022-06-14 18:45:16 INFO [EasyEdge] [demo_serving.py:50] 21212: Paddle version: 2.2.2
|
||||||
|
* Serving Flask app 'Serving' (lazy loading)
|
||||||
|
* Environment: production
|
||||||
|
WARNING: This is a development server. Do not use it in a production deployment.
|
||||||
|
Use a production WSGI server instead.
|
||||||
|
* Debug mode: off
|
||||||
|
* Running on all addresses (0.0.0.0)
|
||||||
|
WARNING: This is a development server. Do not use it in a production deployment.
|
||||||
|
* Running on http://127.0.0.1:24401
|
||||||
|
* Running on http://192.168.3.17:24401 (Press CTRL+C to quit)
|
||||||
|
```
|
||||||
|
|
||||||
|
开发者此时可以打开浏览器,输入`http://{host ip}:24401`,选择图片或者视频来进行测试,运行效果如下。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://user-images.githubusercontent.com/54695910/175854073-fb8189e5-0ffb-472c-a17d-0f35aa6a8418.png" style="zoom:50%;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
# HTTP API流程详解
|
||||||
|
|
||||||
|
本章节主要结合前文的Demo示例来对API进行介绍,方便开发者学习并将运行库嵌入到开发者的程序当中,更详细的API请参考对应的Python文件。http服务包含服务端和客户端,Demo中提供了不使用图片base格式的`方式一:浏览器请求的方式`,其他几种方式开发者根据个人需要,选择开发。
|
||||||
|
|
||||||
|
## 1. 开启http服务
|
||||||
|
|
||||||
|
http服务的启动使用`demo_serving.py`文件
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Serving(object):
|
||||||
|
""" SDK local serving """
|
||||||
|
|
||||||
|
def __init__(self, model_dir, license='', model_filename='model', params_filename='params'):
|
||||||
|
|
||||||
|
self.program = None
|
||||||
|
self.model_dir = model_dir
|
||||||
|
self.model_filename = model_filename
|
||||||
|
self.params_filename = params_filename
|
||||||
|
self.program_lock = threading.Lock()
|
||||||
|
self.license_key = license
|
||||||
|
# 只有ObjectTracking会初始化video_processor
|
||||||
|
self.video_processor = None
|
||||||
|
|
||||||
|
def run(self, host, port, device, engine=Engine.PADDLE_FLUID, service_id=0, device_id=0, **kwargs):
|
||||||
|
""" Args: host : str port : str device : BaiduAI.EasyEdge.Device,比如:Device.CPU engine : BaiduAI.EasyEdge.Engine, 比如: Engine.PADDLE_FLUID """
|
||||||
|
self.run_serving_with_flask(host, port, device, engine, service_id, device_id, **kwargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 请求http服务
|
||||||
|
|
||||||
|
> 开发者可以打开浏览器,`http://{设备ip}:24401`,选择图片来进行测试。
|
||||||
|
|
||||||
|
### 2.1 http 请求方式:不使用图片base64格式
|
||||||
|
|
||||||
|
URL中的get参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 默认值 |
|
||||||
|
| --------- | --------- | ---------------- |
|
||||||
|
| threshold | 阈值过滤, 0~1 | 如不提供,则会使用模型的推荐阈值 |
|
||||||
|
|
||||||
|
HTTP POST Body即为图片的二进制内容。
|
||||||
|
|
||||||
|
Python请求示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
with open('./1.jpg', 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
result = requests.post(
|
||||||
|
'http://127.0.0.1:24401/',
|
||||||
|
params={'threshold': 0.1},
|
||||||
|
data=img).json()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. http返回数据
|
||||||
|
|
||||||
|
| 字段 | 类型说明 | 其他 |
|
||||||
|
| ---------- | ------ | ------------------------------------ |
|
||||||
|
| error_code | Number | 0为成功,非0参考message获得具体错误信息 |
|
||||||
|
| results | Array | 内容为具体的识别结果。其中字段的具体含义请参考`预测图像-返回格式`一节 |
|
||||||
|
| cost_ms | Number | 预测耗时ms,不含网络交互时间 |
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cost_ms": 52,
|
||||||
|
"error_code": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"confidence": 0.94482421875,
|
||||||
|
"index": 1,
|
||||||
|
"label": "IronMan",
|
||||||
|
"x1": 0.059185408055782318,
|
||||||
|
"x2": 0.18795496225357056,
|
||||||
|
"y1": 0.14762254059314728,
|
||||||
|
"y2": 0.52510076761245728,
|
||||||
|
"mask": "...", // 图像分割模型字段
|
||||||
|
"trackId": 0, // 目标追踪模型字段
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
***关于矩形坐标***
|
||||||
|
|
||||||
|
x1 * 图片宽度 = 检测框的左上角的横坐标
|
||||||
|
|
||||||
|
y1 * 图片高度 = 检测框的左上角的纵坐标
|
||||||
|
|
||||||
|
x2 * 图片宽度 = 检测框的右下角的横坐标
|
||||||
|
|
||||||
|
y2 * 图片高度 = 检测框的右下角的纵坐标
|
||||||
|
|
||||||
|
***关于分割模型***
|
||||||
|
|
||||||
|
其中,mask为分割模型的游程编码,解析方式可参考 [demo](https://github.com/Baidu-AIP/EasyDL-Segmentation-Demo)。
|
||||||
|
|
||||||
|
**FAQ**
|
||||||
|
|
||||||
|
1. 执行infer_demo文件时,提示your generated code is out of date and must be regenerated with protoc >= 3.19.0
|
||||||
|
|
||||||
|
进入当前项目,首先卸载protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip uninstall protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
安装低版本protobuf
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python3 -m pip install protobuf==3.19.0
|
||||||
|
```
|
212
docs/iOS-SDK.md
Normal file
212
docs/iOS-SDK.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# 简介
|
||||||
|
|
||||||
|
本文档介绍FastDeploy中的模型SDK,在iOS环境下:(1)推理部署步骤;(2)介绍SDK使用说明,方便开发者了解项目后二次开发。
|
||||||
|
|
||||||
|
<!--ts-->
|
||||||
|
|
||||||
|
* [简介](#简介)
|
||||||
|
|
||||||
|
* [系统支持说明](#系统支持说明)
|
||||||
|
|
||||||
|
* [1. 系统支持说明](#1-系统支持说明)
|
||||||
|
* [2. SDK大小说明](#2-sdk大小说明)
|
||||||
|
|
||||||
|
* [快速开始](#快速开始)
|
||||||
|
|
||||||
|
* [1. 项目结构说明](#1-项目结构说明)
|
||||||
|
* [2. 测试Demo](#2-测试demo)
|
||||||
|
|
||||||
|
* [SDK使用说明](#sdk使用说明)
|
||||||
|
|
||||||
|
* [1. 集成指南](#1-集成指南)
|
||||||
|
* [1.1 依赖库集成](#11-依赖库集成)
|
||||||
|
* [2. 调用流程示例](#2-调用流程示例)
|
||||||
|
* [2.1 初始化](#21-初始化)
|
||||||
|
* [2.2 预测图像](#22-预测图像)
|
||||||
|
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
|
<!--te-->
|
||||||
|
|
||||||
|
# 系统支持说明
|
||||||
|
|
||||||
|
## 1. 系统支持说明
|
||||||
|
|
||||||
|
1. 系统支持:iOS 9.0及以上。
|
||||||
|
|
||||||
|
2. 硬件支持:支持 arm64 (Starndard architectures),暂不支持模拟器。
|
||||||
|
|
||||||
|
* 官方验证过的手机机型:大部分ARM 架构的手机、平板及开发板。
|
||||||
|
|
||||||
|
3.其他说明
|
||||||
|
|
||||||
|
* 3.1 【图像分割类模型】(1)图像分割类Demo暂未提供实时摄像头录制拍摄的能力,开发者可根据自己需要,进行安卓开发完成;(2)PP-Humanseg-Lite模型设计初衷为横屏视频会议等场景,本次安卓开发仅支持述评场景,开发者可根据自己需要,开发横屏的Android功能。<br>
|
||||||
|
|
||||||
|
* 3.2 【OCR模型】OCR任务第一次启动任务,第一张推理时间久,属于正常情况(因为涉及到模型加载、预处理等工作)。<br>
|
||||||
|
|
||||||
|
## 2. SDK大小说明
|
||||||
|
|
||||||
|
1. 模型资源文件大小影响 SDK 大小
|
||||||
|
2. SDK 包及 IPA 安装包虽然比较大,但最终安装到设备后所占大小会缩小很多。这与 multi architechtures、bitcode 和 AppStore 的优化有关。
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 1. 项目结构说明
|
||||||
|
|
||||||
|
根据开发者模型、部署芯片、操作系统需要,在图像界面[飞桨开源模型](https://ai.baidu.com/easyedge/app/openSource)或[GIthub](https://github.com/PaddlePaddle/FastDeploy)中选择对应的SDK进行下载。SDK目录结构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
.EasyEdge-iOS-SDK
|
||||||
|
├── EasyDLDemo # Demo工程文件
|
||||||
|
├── LIB # 依赖库
|
||||||
|
├── RES
|
||||||
|
│ ├── easyedge # 模型资源文件夹,一套模型适配不同硬件、OS和部署方式
|
||||||
|
│ ├── conf.json # Android、iOS系统APP名字需要
|
||||||
|
│ ├── model # 模型结构文件
|
||||||
|
│ ├── params # 模型参数文件
|
||||||
|
│ ├── label_list.txt # 模型标签文件
|
||||||
|
│ ├── infer_cfg.json # 模型前后处理等配置文件
|
||||||
|
└── DOC # 文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 测试Demo
|
||||||
|
|
||||||
|
按如下步骤可直接运行 SDK 体验 Demo:
|
||||||
|
步骤一:用 Xcode 打开 `EasyDLDemo/EasyDLDemo.xcodeproj`
|
||||||
|
步骤二:配置开发者自己的签名(不了解签名机制的,可以看FAQ [iOS签名介绍](#100))</br>
|
||||||
|
步骤三:连接手机运行,不支持模拟器
|
||||||
|
|
||||||
|
检测模型运行示例:
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854078-4f1f761d-0629-411a-92cc-6f4180164ca5.png" width="400"></div>
|
||||||
|
|
||||||
|
# SDK使用说明
|
||||||
|
|
||||||
|
本节介绍如何将 SDK 接入开发者的项目中使用。
|
||||||
|
|
||||||
|
## 1. 集成指南
|
||||||
|
|
||||||
|
步骤一:依赖库集成
|
||||||
|
步骤二:`import <EasyDL/EasyDL.h>`
|
||||||
|
|
||||||
|
### 1.1 依赖库集成
|
||||||
|
|
||||||
|
1. 复制 LIB 目录至项目合适的位置
|
||||||
|
2. 配置 Build Settings 中 Search paths: 以 SDK 中 LIB 目录路径为例
|
||||||
|
- Framework Search Paths:`${PROJECT_DIR}/../LIB/lib`
|
||||||
|
- Header Search Paths:`${PROJECT_DIR}/../LIB/include`
|
||||||
|
- Library Search Paths:`${PROJECT_DIR}/../LIB/lib`
|
||||||
|
|
||||||
|
> 集成过程如出现错误,请参考 Demo 工程对依赖库的引用
|
||||||
|
|
||||||
|
## 2. 调用流程示例
|
||||||
|
|
||||||
|
以通用ARM的图像分类预测流程为例,详细说明请参考后续章节:
|
||||||
|
|
||||||
|
```
|
||||||
|
NSError *err;
|
||||||
|
|
||||||
|
// step 1: 初始化模型
|
||||||
|
EasyDLModel *model = [[EasyDLModel alloc] initModelFromResourceDirectory:@"easyedge" withError:&err];
|
||||||
|
|
||||||
|
// step 2: 准备待预测的图像
|
||||||
|
UIImage *image = ...;
|
||||||
|
|
||||||
|
// step 3: 预测图像
|
||||||
|
NSArray *results = [model detectUIImage:image withFilterScore:0 andError:&err];
|
||||||
|
|
||||||
|
// step 4: 解析结果
|
||||||
|
for (id res in results) {
|
||||||
|
EasyDLClassfiData *clsData = (EasyDLClassfiData *) res;
|
||||||
|
NSLog(@"labelIndex=%d, labelName=%@, confidence=%f", clsData.category, clsData.label, clsData.accuracy);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 初始化
|
||||||
|
|
||||||
|
```
|
||||||
|
// 示例
|
||||||
|
// 参数一为模型资源文件夹名称
|
||||||
|
EasyDLModel *model = [[EasyDLModel alloc] initModelFromResourceDirectory:@"easyedge" withError:&err];
|
||||||
|
```
|
||||||
|
|
||||||
|
> 模型资源文件夹需以 folder reference 方式加入 Xcode 工程,如 `RES/easyedge` 文件夹在 Demo 工程中表现为蓝色
|
||||||
|
|
||||||
|
### 2.2 预测图像
|
||||||
|
|
||||||
|
所有模型类型通过以下接口获取预测结果:
|
||||||
|
|
||||||
|
```
|
||||||
|
// 返回的数组类型不定
|
||||||
|
NSArray *results = [model detectUIImage:image withFilterScore:0 andError:&err];
|
||||||
|
```
|
||||||
|
|
||||||
|
返回的数组类型如下,具体可参考 `EasyDLResultData.h` 中的定义:
|
||||||
|
| 模型类型 | 类型 |
|
||||||
|
| --- | ---- |
|
||||||
|
| 图像分类 | EasyDLClassfiData |
|
||||||
|
| 物体检测/人脸检测 | EasyDLObjectDetectionData |
|
||||||
|
| 实例分割 | EasyDLObjSegmentationData |
|
||||||
|
| 姿态估计 | EasyDLPoseData |
|
||||||
|
| 文字识别 | EasyDLOcrData |
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
1. 如何多线程并发预测?
|
||||||
|
|
||||||
|
SDK内部已经能充分利用多核的计算能力。不建议使用并发来预测。
|
||||||
|
|
||||||
|
如果开发者想并发使用,请务必注意`EasyDLModel`所有的方法都不是线程安全的。请初始化多个实例进行并发使用,如
|
||||||
|
|
||||||
|
```c
|
||||||
|
- (void)testMultiThread {
|
||||||
|
UIImage *img = [UIImage imageNamed:@"1.jpeg"];
|
||||||
|
NSError *err;
|
||||||
|
EasyDLModel * model1 = [[EasyDLModel alloc] initModelFromResourceDirectory:@"easyedge" withError:&err];
|
||||||
|
EasyDLModel * model2 = [[EasyDLModel alloc] initModelFromResourceDirectory:@"easyedge" withError:&err];
|
||||||
|
|
||||||
|
dispatch_queue_t queue1 = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||||
|
dispatch_queue_t queue2 = dispatch_queue_create("testQueue2", DISPATCH_QUEUE_CONCURRENT);
|
||||||
|
|
||||||
|
dispatch_async(queue1, ^{
|
||||||
|
NSError *detectErr;
|
||||||
|
for(int i = 0; i < 1000; ++i) {
|
||||||
|
NSArray * res = [model1 detectUIImage:img withFilterScore:0 andError:&detectErr];
|
||||||
|
NSLog(@"1: %@", res[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch_async(queue2, ^{
|
||||||
|
NSError *detectErr;
|
||||||
|
for(int i = 0; i < 1000; ++i) {
|
||||||
|
NSArray * res = [model2 detectUIImage:img withFilterScore:0 andError:&detectErr];
|
||||||
|
NSLog(@"2: %@", res[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 编译时出现 Undefined symbols for architecture arm64: ...
|
||||||
|
* 出现 `cxx11, vtable` 字样:请引入 `libc++.tbd`
|
||||||
|
* 出现 `cv::Mat` 字样:请引入 `opencv2.framework`
|
||||||
|
* 出现 `CoreML`, `VNRequest` 字样:请引入`CoreML.framework` 并务必`#import <CoreML/CoreML.h> `
|
||||||
|
3. 运行时报错 Image not found: xxx ...
|
||||||
|
|
||||||
|
请Embed具体报错的库。
|
||||||
|
|
||||||
|
4. 编译时报错:Invalid bitcode version
|
||||||
|
|
||||||
|
这个可能是开发者使用的 Xcode 低于12导致,可以升级至12版本。
|
||||||
|
|
||||||
|
5. 错误说明
|
||||||
|
|
||||||
|
SDK 的方法会返回 NSError,直接返回的 NSError 的错误码定义在 `EasyDLDefine.h - EEasyDLErrorCode` 中。NSError 附带 message (有时候会附带 NSUnderlyingError),开发者可根据 code 和 message 进行错误判断和处理。
|
||||||
|
|
||||||
|
6. iOS签名说明
|
||||||
|
|
||||||
|
iOS 签名是苹果生态对 APP 开发者做的限定,对于个人开发者是免费的,对于企业开发者(譬如APP要上架应用市场),是收费的。此处,仅简单说明作为普通开发者,第一次尝试使用 Xcode编译代码,需要进行的签名操作。<br>
|
||||||
|
(1)在Xcode/Preferences/Accounts 中添加个人Apple ID;<br>
|
||||||
|
(2)在对应的EasyDLDemo中做如下图设置:<br>
|
||||||
|
|
||||||
|
<div align=center><img src="https://user-images.githubusercontent.com/54695910/175854089-aa1d1af8-7daa-43ae-868d-32041c27ad86.jpg" width="600"></div>
|
||||||
|
(3)(2)后会在手机上安装好对应APP,还需要在手机上`设置/通用/设备管理/开发者应用/信任appleID`,才能运行该 APP。
|
199
fastdeploy/__init__.py
Normal file
199
fastdeploy/__init__.py
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from six import text_type as _text_type
|
||||||
|
from .download import download, download_and_decompress
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Since the source code is not fully open sourced,
|
||||||
|
# currently we will provide the prebuilt library
|
||||||
|
# and demo codes
|
||||||
|
import os
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
'--model',
|
||||||
|
type=_text_type,
|
||||||
|
default=None,
|
||||||
|
help='Name of model, which can be listed by --list_models')
|
||||||
|
parser.add_argument(
|
||||||
|
'--platform',
|
||||||
|
type=_text_type,
|
||||||
|
default=None,
|
||||||
|
help='Define platform, supports Windows/Linux/Android/iOS.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--soc',
|
||||||
|
type=_text_type,
|
||||||
|
default=None,
|
||||||
|
help='Define soc for the platform, supports x86/x86-NVIDIA_GPU/ARM/jetson.'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--save_dir',
|
||||||
|
type=_text_type,
|
||||||
|
default=".",
|
||||||
|
help='Path to download and extract deployment SDK.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--list_models',
|
||||||
|
required=False,
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help='List all the supported models.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--download_sdk',
|
||||||
|
required=False,
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help='List all the supported models.')
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def read_sources():
|
||||||
|
user_dir = os.path.expanduser('~')
|
||||||
|
print("Updating the newest sdk information...")
|
||||||
|
source_cfgs = "https://bj.bcebos.com/paddlehub/fastdeploy/fastdeploy_newest_sources.cfg.1"
|
||||||
|
if os.path.exists(os.path.join(user_dir, "fastdeploy_newest_sources.cfg.1")):
|
||||||
|
os.remove(os.path.join(user_dir, "fastdeploy_newest_sources.cfg.1"))
|
||||||
|
download(source_cfgs, user_dir)
|
||||||
|
categories = dict()
|
||||||
|
res = dict()
|
||||||
|
with open(os.path.join(user_dir, "fastdeploy_newest_sources.cfg.1")) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.strip().startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.strip() == "":
|
||||||
|
continue
|
||||||
|
category, model, plat, soc, url = line.strip().split('\t')
|
||||||
|
if category not in categories:
|
||||||
|
categories[category] = set()
|
||||||
|
categories[category].add(model)
|
||||||
|
if model not in res:
|
||||||
|
res[model] = dict()
|
||||||
|
if plat not in res[model]:
|
||||||
|
res[model][plat] = dict()
|
||||||
|
if soc not in res[model][plat]:
|
||||||
|
res[model][plat][soc] = dict()
|
||||||
|
res[model][plat][soc] = url
|
||||||
|
return categories, res
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_arguments()
|
||||||
|
|
||||||
|
if not args.list_models and not args.download_sdk:
|
||||||
|
print(
|
||||||
|
"Please use flag --list_models to show all the supported models, or use flag --download_sdk to download the specify SDK to deploy you model."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
categories, all_sources = read_sources()
|
||||||
|
all_models = list(all_sources.keys())
|
||||||
|
all_models.sort()
|
||||||
|
|
||||||
|
if args.list_models:
|
||||||
|
print("Currently, FastDeploy supports {} models, list as below,\n".format(
|
||||||
|
len(all_models)))
|
||||||
|
|
||||||
|
for k, v in categories.items():
|
||||||
|
print("\nModel Category: {}".format(k))
|
||||||
|
print("_"*100)
|
||||||
|
models = list(categories[k])
|
||||||
|
models.sort()
|
||||||
|
i = 0
|
||||||
|
while i < len(models):
|
||||||
|
if i == len(models) - 1:
|
||||||
|
print(models[i].center(30))
|
||||||
|
i += 1
|
||||||
|
elif i == len(models) - 2:
|
||||||
|
print(models[i].center(30), models[i+1].center(30))
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
print(models[i].center(30), models[i+1].center(30), models[i+2].center(30))
|
||||||
|
i += 3
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists(args.save_dir):
|
||||||
|
print("The specified save_dir: {} is not exist.".format(args.save_dir))
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.model is None or args.model == "":
|
||||||
|
print(
|
||||||
|
"Please define --model to choose which kind of model to deploy, use --list_models to show all the supported models."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.model not in all_sources:
|
||||||
|
print(
|
||||||
|
"{} is not supported, use --list_models to list all the models FastDeploy supported.".
|
||||||
|
format(args.model))
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.platform is None or args.platform == "":
|
||||||
|
print(
|
||||||
|
"Please define --platform to choose which platform to deploy, supports windows/linux/android/ios."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.platform not in all_sources[args.model]:
|
||||||
|
print(
|
||||||
|
"The model:{} only supports platform of {}, {} is not supported now.".
|
||||||
|
format(args.model,
|
||||||
|
list(all_sources[args.model].keys()), args.platform))
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.soc is None or args.soc == "":
|
||||||
|
print(
|
||||||
|
"Please define --soc to choose which hardware to deploy, for model:{} and platform:{}, the available socs are {}.".
|
||||||
|
format(args.model, args.platform,
|
||||||
|
list(all_sources[args.model][args.platform].keys())))
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.soc not in all_sources[args.model][args.platform]:
|
||||||
|
print(
|
||||||
|
"The model:{} in platform:{} only supports soc of {}, {} is not supported now.".
|
||||||
|
format(args.model, args.platform,
|
||||||
|
list(all_sources[args.model][args.platform].keys()),
|
||||||
|
args.soc))
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\nDownloading SDK:",
|
||||||
|
all_sources[args.model][args.platform][args.soc])
|
||||||
|
|
||||||
|
save_dir = args.save_dir
|
||||||
|
sdk_name = os.path.split(all_sources[args.model][args.platform][args.soc])[
|
||||||
|
-1].strip()
|
||||||
|
if all_sources[args.model][args.platform][args.soc].count(".zip") > 0:
|
||||||
|
sdk_name = os.path.split(all_sources[args.model][args.platform][
|
||||||
|
args.soc])[-1].strip().split(".zip")[0]
|
||||||
|
new_save_dir = os.path.join(args.save_dir, sdk_name)
|
||||||
|
if not os.path.exists(new_save_dir):
|
||||||
|
os.mkdir(new_save_dir)
|
||||||
|
save_dir = new_save_dir
|
||||||
|
download_and_decompress(
|
||||||
|
all_sources[args.model][args.platform][args.soc],
|
||||||
|
new_save_dir,
|
||||||
|
rename=sdk_name + ".zip")
|
||||||
|
os.remove(os.path.join(new_save_dir, sdk_name + ".zip"))
|
||||||
|
print("Done. All the files of SDK have been extracted in {}.".format(
|
||||||
|
new_save_dir))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
18
fastdeploy/__main__.py
Normal file
18
fastdeploy/__main__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import fastdeploy
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
fastdeploy.main()
|
186
fastdeploy/download.py
Normal file
186
fastdeploy/download.py
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
import shutil
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
import hashlib
|
||||||
|
import tqdm
|
||||||
|
import logging
|
||||||
|
|
||||||
|
DOWNLOAD_RETRY_LIMIT = 3
|
||||||
|
|
||||||
|
|
||||||
|
def md5check(fullname, md5sum=None):
|
||||||
|
if md5sum is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
logging.info("File {} md5 checking...".format(fullname))
|
||||||
|
md5 = hashlib.md5()
|
||||||
|
with open(fullname, 'rb') as f:
|
||||||
|
for chunk in iter(lambda: f.read(4096), b""):
|
||||||
|
md5.update(chunk)
|
||||||
|
calc_md5sum = md5.hexdigest()
|
||||||
|
|
||||||
|
if calc_md5sum != md5sum:
|
||||||
|
logging.info("File {} md5 check failed, {}(calc) != "
|
||||||
|
"{}(base)".format(fullname, calc_md5sum, md5sum))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def move_and_merge_tree(src, dst):
|
||||||
|
"""
|
||||||
|
Move src directory to dst, if dst is already exists,
|
||||||
|
merge src to dst
|
||||||
|
"""
|
||||||
|
if not osp.exists(dst):
|
||||||
|
shutil.move(src, dst)
|
||||||
|
else:
|
||||||
|
if not osp.isdir(src):
|
||||||
|
shutil.move(src, dst)
|
||||||
|
return
|
||||||
|
for fp in os.listdir(src):
|
||||||
|
src_fp = osp.join(src, fp)
|
||||||
|
dst_fp = osp.join(dst, fp)
|
||||||
|
if osp.isdir(src_fp):
|
||||||
|
if osp.isdir(dst_fp):
|
||||||
|
move_and_merge_tree(src_fp, dst_fp)
|
||||||
|
else:
|
||||||
|
shutil.move(src_fp, dst_fp)
|
||||||
|
elif osp.isfile(src_fp) and \
|
||||||
|
not osp.isfile(dst_fp):
|
||||||
|
shutil.move(src_fp, dst_fp)
|
||||||
|
|
||||||
|
|
||||||
|
def download(url, path, rename=None, md5sum=None, show_progress=False):
|
||||||
|
"""
|
||||||
|
Download from url, save to path.
|
||||||
|
url (str): download url
|
||||||
|
path (str): download to given path
|
||||||
|
"""
|
||||||
|
if not osp.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
fname = osp.split(url)[-1]
|
||||||
|
fullname = osp.join(path, fname)
|
||||||
|
if rename is not None:
|
||||||
|
fullname = osp.join(path, rename)
|
||||||
|
retry_cnt = 0
|
||||||
|
while not (osp.exists(fullname) and md5check(fullname, md5sum)):
|
||||||
|
if retry_cnt < DOWNLOAD_RETRY_LIMIT:
|
||||||
|
retry_cnt += 1
|
||||||
|
else:
|
||||||
|
logging.debug("{} download failed.".format(fname))
|
||||||
|
raise RuntimeError("Download from {} failed. "
|
||||||
|
"Retry limit reached".format(url))
|
||||||
|
|
||||||
|
logging.info("Downloading {} from {}".format(fname, url))
|
||||||
|
|
||||||
|
req = requests.get(url, stream=True)
|
||||||
|
if req.status_code != 200:
|
||||||
|
raise RuntimeError("Downloading from {} failed with code "
|
||||||
|
"{}!".format(url, req.status_code))
|
||||||
|
|
||||||
|
# For protecting download interupted, download to
|
||||||
|
# tmp_fullname firstly, move tmp_fullname to fullname
|
||||||
|
# after download finished
|
||||||
|
tmp_fullname = fullname + "_tmp"
|
||||||
|
total_size = req.headers.get('content-length')
|
||||||
|
with open(tmp_fullname, 'wb') as f:
|
||||||
|
if total_size and show_progress:
|
||||||
|
for chunk in tqdm.tqdm(
|
||||||
|
req.iter_content(chunk_size=1024),
|
||||||
|
total=(int(total_size) + 1023) // 1024,
|
||||||
|
unit='KB'):
|
||||||
|
f.write(chunk)
|
||||||
|
else:
|
||||||
|
for chunk in req.iter_content(chunk_size=1024):
|
||||||
|
if chunk:
|
||||||
|
f.write(chunk)
|
||||||
|
shutil.move(tmp_fullname, fullname)
|
||||||
|
logging.debug("{} download completed.".format(fname))
|
||||||
|
|
||||||
|
return fullname
|
||||||
|
|
||||||
|
|
||||||
|
def decompress(fname):
|
||||||
|
"""
|
||||||
|
Decompress for zip and tar file
|
||||||
|
"""
|
||||||
|
logging.info("Decompressing {}...".format(fname))
|
||||||
|
|
||||||
|
# For protecting decompressing interupted,
|
||||||
|
# decompress to fpath_tmp directory firstly, if decompress
|
||||||
|
# successed, move decompress files to fpath and delete
|
||||||
|
# fpath_tmp and remove download compress file.
|
||||||
|
fpath = osp.split(fname)[0]
|
||||||
|
fpath_tmp = osp.join(fpath, 'tmp')
|
||||||
|
if osp.isdir(fpath_tmp):
|
||||||
|
shutil.rmtree(fpath_tmp)
|
||||||
|
os.makedirs(fpath_tmp)
|
||||||
|
|
||||||
|
if fname.find('.tar') >= 0 or fname.find('.tgz') >= 0:
|
||||||
|
with tarfile.open(fname) as tf:
|
||||||
|
tf.extractall(path=fpath_tmp)
|
||||||
|
elif fname.find('.zip') >= 0:
|
||||||
|
with zipfile.ZipFile(fname) as zf:
|
||||||
|
zf.extractall(path=fpath_tmp)
|
||||||
|
else:
|
||||||
|
raise TypeError("Unsupport compress file type {}".format(fname))
|
||||||
|
|
||||||
|
for f in os.listdir(fpath_tmp):
|
||||||
|
src_dir = osp.join(fpath_tmp, f)
|
||||||
|
dst_dir = osp.join(fpath, f)
|
||||||
|
move_and_merge_tree(src_dir, dst_dir)
|
||||||
|
|
||||||
|
shutil.rmtree(fpath_tmp)
|
||||||
|
logging.debug("{} decompressed.".format(fname))
|
||||||
|
return dst_dir
|
||||||
|
|
||||||
|
|
||||||
|
def url2dir(url, path, rename=None):
|
||||||
|
full_name = download(url, path, rename, show_progress=True)
|
||||||
|
print("SDK is donwloaded, now extracting...")
|
||||||
|
if url.count(".tgz") > 0 or url.count(".tar") > 0 or url.count("zip") > 0:
|
||||||
|
return decompress(full_name)
|
||||||
|
|
||||||
|
|
||||||
|
def download_and_decompress(url, path='.', rename=None):
|
||||||
|
fname = osp.split(url)[-1]
|
||||||
|
fullname = osp.join(path, fname)
|
||||||
|
# if url.endswith(('tgz', 'tar.gz', 'tar', 'zip')):
|
||||||
|
# fullname = osp.join(path, fname.split('.')[0])
|
||||||
|
nranks = 0
|
||||||
|
if nranks <= 1:
|
||||||
|
dst_dir = url2dir(url, path, rename)
|
||||||
|
if dst_dir is not None:
|
||||||
|
fullname = dst_dir
|
||||||
|
else:
|
||||||
|
lock_path = fullname + '.lock'
|
||||||
|
if not os.path.exists(fullname):
|
||||||
|
with open(lock_path, 'w'):
|
||||||
|
os.utime(lock_path, None)
|
||||||
|
if local_rank == 0:
|
||||||
|
dst_dir = url2dir(url, path, rename)
|
||||||
|
if dst_dir is not None:
|
||||||
|
fullname = dst_dir
|
||||||
|
os.remove(lock_path)
|
||||||
|
else:
|
||||||
|
while os.path.exists(lock_path):
|
||||||
|
time.sleep(1)
|
||||||
|
return
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
tqdm
|
||||||
|
six
|
34
setup.py
Normal file
34
setup.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import setuptools
|
||||||
|
import fastdeploy
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
|
||||||
|
with open("requirements.txt") as fin:
|
||||||
|
REQUIRED_PACKAGES = fin.read()
|
||||||
|
|
||||||
|
def read(*names, **kwargs):
|
||||||
|
with io.open(
|
||||||
|
os.path.join(os.path.dirname(__file__), *names),
|
||||||
|
encoding=kwargs.get("encoding", "utf8")) as fp:
|
||||||
|
return fp.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="fastdeploy-python",
|
||||||
|
version=fastdeploy.__version__,
|
||||||
|
author="FastDeploy",
|
||||||
|
author_email="fastdeploy@baidu.com",
|
||||||
|
description="FastDeploy is a toolkit to deploy deeplearning models.",
|
||||||
|
long_description=read("README.md"),
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/PaddlePaddle/FastDeploy",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
install_requires=REQUIRED_PACKAGES,
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: Apache Software License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
license='Apache 2.0',
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': ['fastdeploy=fastdeploy.__init__:main', ]
|
||||||
|
})
|
Reference in New Issue
Block a user