C++ 完整示例

本章节包含2部分内容:(1) C++ 示例程序; (2) C++ 应用开发说明

本章节展示的所有C++ 示例代码位于 demo/c++ ,内部有详细使用说明。

这些C++ 示例是通过shell端在安卓手机上执行,可以快速验证模型的正确性。验证模型正确性后,可以在安卓APP中使用PaddleLite部署模型。

下面以轻量级api的demo为例,进行说明。

一、C++ 示例程序

1. 环境准备

要编译和运行Android C++ 示例程序,你需要准备:

  • 一台可以编译PaddleLite的电脑,具体环境配置,请参考文档,推荐使用docker。

  • 一台armv7或armv8架构的安卓手机,安装adb,确保电脑和手机可以通过adb连接。

2. 下载或者编译预测库

预测库下载界面位于Lite预编译库下载,可根据您的手机型号选择合适版本。

Android-ARMv8架构为例,可以下载以下版本:

Archwith_extraarm_stlwith_cv下载
armv8OFFc++_staticOFF2.8-rc

解压后内容结构如下:

  1. inference_lite_lib.android.armv8 Paddle-Lite 预测库
  2. ├── cxx C++ 预测库
  3. ├── include C++ 预测库头文件
  4. └── lib C++ 预测库文件
  5. ├── libpaddle_api_light_bundled.a 静态预测库
  6. └── libpaddle_light_api_shared.so 动态预测库
  7. ├── demo 示例 Demo
  8. ├── cxx C++ 示例 Demo
  9. └── java Java 示例 Demo
  10. └── java Java 预测库

如果要使用最新的预测库执行C++示例,可以参考编译文档编译PaddleLite预测库和C++示例。

3. 准备预测部署模型

(1) 模型下载:下载mobilenet_v1模型后解压,得到Paddle非combined形式的模型,位于文件夹 mobilenet_v1 下。可通过模型可视化工具Netron打开文件夹下的__model__文件,查看模型结构。

  1. wget http://paddle-inference-dist.bj.bcebos.com/mobilenet_v1.tar.gz
  2. tar zxf mobilenet_v1.tar.gz

(2) 模型转换:Paddle的原生模型需要经过opt工具转化为Paddle-Lite可以支持的naive_buffer格式。

方式一: 下载opt工具,放入与mobilenet_v1文件夹同级目录,终端输入以下命令转化模型

  1. # Linux
  2. wget https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.6.1/opt
  3. chmod +x opt
  4. ./opt --model_dir=./mobilenet_v1 \
  5. --optimize_out_type=naive_buffer \
  6. --optimize_out=./mobilenet_v1_opt
  7. # Mac
  8. wget https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.6.1/opt_mac
  9. chmod +x opt_mac
  10. ./opt_mac --model_dir=./mobilenet_v1 \
  11. --optimize_out_type=naive_buffer \
  12. --optimize_out=./mobilenet_v1_opt

方式二: 通过pip安装paddlelite,终端输入命令转化模型

  1. python -m pip install paddlelite
  2. paddle_lite_opt --model_dir=./mobilenet_v1 \
  3. --optimize_out_type=naive_buffer \
  4. --optimize_out=./mobilenet_v1_opt

以上命令执行成功之后将在同级目录生成名为mobilenet_v1_opt.nb的优化后模型文件。

4. 编译预测示例程序

准备好预测库和模型,就可以直接编译随着预测库一起发布的 C++ Demo,位于在第二步中下载的预测库文件目录下inference_lite_lib.android.armv8/demo/cxx。以mobilenet_v1为例,目录下的mobile_light为mobilenet_v1预测示例,预测程序需要编译为Android可执行文件。

  1. cd inference_lite_lib.android.armv8/demo/mobile_light
  2. make

会在同级目录下生成名为mobilenetv1_light_api的可执行文件。

5. 预测部署和执行

(1) 设置手机:手机USB连接电脑,打开设置 -> 开发者模式 -> USB调试 -> 允许(授权)当前电脑调试手机。保证当前电脑已经安装adb工具,运行以下命令,确认当前手机设备已被识别:

  1. adb devices
  2. # 如果手机设备已经被正确识别,将输出如下信息
  3. List of devices attached
  4. 017QXM19C1000664 device

(2) 预测部署:将第二步中的C++动态预测库文件libpaddle_light_api_shared.so、第三步中生成的优化后模型文件mobilenet_v1_opt.nb和第四步中编译得到的预测示例程序mobilenetv1_light_api放入同一文件夹,并将这三个文件推送到手机:

  1. chmod +x mobilenetv1_light_api
  2. adb push mobilenet_v1_opt.nb /data/local/tmp
  3. adb push libpaddle_light_api_shared.so /data/local/tmp
  4. adb push mobilenetv1_light_api /data/local/tmp
  5. # 如果推送成功,将显示如下信息
  6. adb shell 'ls -l /data/local/tmp'
  7. total 24168
  8. -rwxrwxrwx 1 root root 1624280 2020-09-01 13:47 libpaddle_light_api_shared.so
  9. -rw-rw-rw- 1 root root 17018243 2020-09-01 12:28 mobilenet_v1_opt.nb
  10. -rwxrwxrwx 1 root root 6076144 2020-09-01 13:47 mobilenetv1_light_api

(3) 执行预测,以下输出为mobilenet_v1模型在全1输入时,得到的预测结果。

  1. adb shell 'cd /data/local/tmp && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp && ./mobilenetv1_light_api mobilenet_v1_opt.nb'
  2. # 如果正确运行,将输出如下信息
  3. run_idx:1 / 10: 33.821 ms
  4. run_idx:2 / 10: 33.8 ms
  5. run_idx:3 / 10: 33.867 ms
  6. run_idx:4 / 10: 34.009 ms
  7. run_idx:5 / 10: 33.699 ms
  8. run_idx:6 / 10: 33.644 ms
  9. run_idx:7 / 10: 33.611 ms
  10. run_idx:8 / 10: 33.783 ms
  11. run_idx:9 / 10: 33.731 ms
  12. run_idx:10 / 10: 33.423 ms
  13. ======= benchmark summary =======
  14. input_shape(NCHW):1 3 224 224
  15. model_dir:mobilenet_v1_opt.nb
  16. warmup:10
  17. repeats:10
  18. max_duration:34.009
  19. min_duration:33.423
  20. avg_duration:33.7388
  21. ====== output summary ======
  22. output tensor num:1
  23. --- output tensor 0 ---
  24. output shape(NCHW):1 1000
  25. output tensor 0 elem num:1000
  26. output tensor 0 standard deviation:0.00219646
  27. output tensor 0 mean value:0.001

二、更多C++示例

更多C++ 示例,请参考 demo/c++ 的详细说明。

图像分类示例

使用OpenCV读取处理输入图片,使用Paddle-Lite执行预测。

  1. cd inference_lite_lib.android.armv8/demo/cxx/mobile_classify
  2. # 下载模型
  3. wget http://paddle-inference-dist.bj.bcebos.com/mobilenet_v1.tar.gz
  4. tar zxvf mobilenet_v1.tar.gz
  5. # 转化模型
  6. paddle_lite_opt --model_dir=./mobilenet_v1 \
  7. --optimize_out_type=naive_buffer \
  8. --optimize_out=./mobilenet_v1_opt
  9. # 编译预测程序
  10. make
  11. # 预测部署
  12. adb push mobile_classify /data/local/tmp/
  13. adb push mobilenet_v1_opt.nb /data/local/tmp/
  14. adb push mobilenet_v1/test.jpg /data/local/tmp/
  15. adb push mobilenet_v1/labels.txt /data/local/tmp/
  16. adb push ../../../cxx/lib/libpaddle_light_api_shared.so /data/local/tmp/
  17. adb shell 'chmod +x /data/local/tmp/mobile_classify'
  18. # 执行预测
  19. adb shell 'cd /data/local/tmp && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp && ./mobile_classify mobilenet_v1_opt.nb test.jpg labels.txt'
  20. # 运行成功后 ,将在控制台输出预测结果的前5个类别的类型索引、名字和预测概率
  21. parameter: model_file, image_path and label_file are necessary
  22. parameter: topk, input_width, input_height, are optional
  23. i: 0, index: 287, name: lynx, catamount, score: 0.317595
  24. i: 1, index: 285, name: Egyptian cat, score: 0.308135
  25. i: 2, index: 281, name: tabby, tabby cat, score: 0.161924
  26. i: 3, index: 282, name: tiger cat, score: 0.093659
  27. i: 4, index: 283, name: Persian cat, score: 0.060198

目标检测示例

  1. cd inference_lite_lib.android.armv8/demo/cxx/ssd_detection
  2. # 下载模型
  3. wget https://paddlelite-data.bj.bcebos.com/doc_models/ssd_mobilenet_v1.tar.gz
  4. tar zxvf ssd_mobilenet_v1.tar.gz
  5. # 转化模型
  6. paddle_lite_opt --model_dir=./ssd_mobilenet_v1 \
  7. --optimize_out_type=naive_buffer \
  8. --optimize_out=./ssd_mobilenet_v1_opt
  9. # 编译预测程序
  10. make
  11. # 预测部署
  12. adb push ssd_detection /data/local/tmp/
  13. adb push ssd_mobilenet_v1_opt.nb /data/local/tmp/
  14. adb push test.jpg /data/local/tmp/
  15. adb push ../../../cxx/lib/libpaddle_light_api_shared.so /data/local/tmp/
  16. adb shell 'chmod +x /data/local/tmp/ssd_detection'
  17. # 执行预测
  18. adb shell 'cd /data/local/tmp && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp && ./ssd_detection ssd_mobilenet_v1_opt.nb test.jpg'
  19. # 运行成功后 ,将在控制台输出检测目标的类型、预测概率和坐标
  20. detection, image size: 935, 1241, detect object: person, score: 0.995543, location: x=187, y=43, width=540, height=591
  21. detection, image size: 935, 1241, detect object: person, score: 0.929626, location: x=125, y=639, width=577, height=597
  22. # 获得目标检测结果图片,并查看
  23. adb pull /data/local/tmp/test_ssd_detection_result.jpg ./

口罩检测示例

  1. cd inference_lite_lib.android.armv8/demo/cxx/mask_detection
  2. # 准备预测部署文件
  3. bash prepare.sh
  4. # 执行预测
  5. cd mask_demo && bash run.sh
  6. # 运行成功后,将在控制台输出如下内容,可以打开test_img_result.jpg图片查看预测结果
  7. ../mask_demo/: 9 files pushed, 0 skipped. 141.6 MB/s (28652282 bytes in 0.193s)
  8. Load detecion model succeed.
  9. Detecting face succeed.
  10. Load classification model succeed.
  11. detect face, location: x=237, y=107, width=194, height=255, wear mask: 1, prob: 0.987625
  12. detect face, location: x=61, y=238, width=166, height=213, wear mask: 1, prob: 0.925679
  13. detect face, location: x=566, y=176, width=245, height=294, wear mask: 1, prob: 0.550348
  14. write result to file: test_img_result.jpg, success.
  15. /data/local/tmp/mask_demo/test_img_result.jpg: 1 file pulled, 0 skipped. 13.7 MB/s (87742 bytes in 0.006s)

C++ 应用开发说明

C++代码调用Paddle-Lite执行预测库仅需以下五步:

(1) 引用头文件和命名空间

  1. #include "paddle_api.h"
  2. using namespace paddle::lite_api;

(2) 指定模型文件,创建Predictor

  1. // 1. Set MobileConfig
  2. MobileConfig config;
  3. // 2. Set the path to the model generated by opt tools
  4. config.set_model_from_file(model_file_path);
  5. // 3. Create PaddlePredictor by MobileConfig
  6. std::shared_ptr<PaddlePredictor> predictor =
  7. CreatePaddlePredictor<MobileConfig>(config);

(3) 设置模型输入 (下面以全一输入为例)

  1. std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
  2. input_tensor->Resize({1, 3, 224, 224});
  3. auto* data = input_tensor->mutable_data<float>();
  4. for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
  5. data[i] = 1;
  6. }

如果模型有多个输入,每一个模型输入都需要准确设置shape和data。

(4) 执行预测

  1. predictor->Run();

(5) 获得预测结果

  1. std::unique_ptr<const Tensor> output_tensor(
  2. std::move(predictor->GetOutput(0)));
  3. // 转化为数据
  4. auto output_data=output_tensor->data<float>();

详细的C++ API说明文档位于C++ API。更多C++应用预测开发可以参考位于位于Paddle-Lite-Demo的工程示例代码。