提交 36cd1193 编写于 作者: L LielinJiang

pull develop

...@@ -4,7 +4,6 @@ __pycache__/ ...@@ -4,7 +4,6 @@ __pycache__/
*$py.class *$py.class
# C extensions # C extensions
*.so
# Distribution / packaging # Distribution / packaging
.Python .Python
......
...@@ -64,11 +64,15 @@ $ pip install -r requirements.txt ...@@ -64,11 +64,15 @@ $ pip install -r requirements.txt
* [如何训练U-Net](./turtorial/finetune_unet.md) * [如何训练U-Net](./turtorial/finetune_unet.md)
* [如何训练ICNet](./turtorial/finetune_icnet.md) * [如何训练ICNet](./turtorial/finetune_icnet.md)
* [如何训练PSPNet](./turtorial/finetune_pspnet.md) * [如何训练PSPNet](./turtorial/finetune_pspnet.md)
* [如何训练HRNet](./turtorial/finetune_hrnet.md)
### 预测部署 ### 预测部署
* [模型导出](./docs/model_export.md) * [模型导出](./docs/model_export.md)
* [C++预测库使用](./inference) * [使用Python预测](./deploy/python/)
* [使用C++预测](./deploy/cpp/)
* [移动端预测部署](./deploy/lite/)
### 高级功能 ### 高级功能
......
TRAIN_CROP_SIZE: (512, 512) # (width, height), for unpadding rangescaling and stepscaling
EVAL_CROP_SIZE: (512, 512) # (width, height), for unpadding rangescaling and stepscaling
AUG:
AUG_METHOD: "unpadding" # choice unpadding rangescaling and stepscaling
FIX_RESIZE_SIZE: (512, 512) # (width, height), for unpadding
INF_RESIZE_VALUE: 500 # for rangescaling
MAX_RESIZE_VALUE: 600 # for rangescaling
MIN_RESIZE_VALUE: 400 # for rangescaling
MAX_SCALE_FACTOR: 1.25 # for stepscaling
MIN_SCALE_FACTOR: 0.75 # for stepscaling
SCALE_STEP_SIZE: 0.25 # for stepscaling
MIRROR: True
BATCH_SIZE: 4
DATASET:
DATA_DIR: "./dataset/mini_pet/"
IMAGE_TYPE: "rgb" # choice rgb or rgba
NUM_CLASSES: 3
TEST_FILE_LIST: "./dataset/mini_pet/file_list/test_list.txt"
TRAIN_FILE_LIST: "./dataset/mini_pet/file_list/train_list.txt"
VAL_FILE_LIST: "./dataset/mini_pet/file_list/val_list.txt"
VIS_FILE_LIST: "./dataset/mini_pet/file_list/test_list.txt"
IGNORE_INDEX: 255
SEPARATOR: " "
FREEZE:
MODEL_FILENAME: "__model__"
PARAMS_FILENAME: "__params__"
MODEL:
MODEL_NAME: "hrnet"
DEFAULT_NORM_TYPE: "bn"
HRNET:
STAGE2:
NUM_CHANNELS: [18, 36]
STAGE3:
NUM_CHANNELS: [18, 36, 72]
STAGE4:
NUM_CHANNELS: [18, 36, 72, 144]
TRAIN:
PRETRAINED_MODEL_DIR: "./pretrained_model/hrnet_w18_bn_cityscapes/"
MODEL_SAVE_DIR: "./saved_model/hrnet_w18_bn_pet/"
SNAPSHOT_EPOCH: 10
TEST:
TEST_MODEL: "./saved_model/hrnet_w18_bn_pet/final"
SOLVER:
NUM_EPOCHS: 100
LR: 0.005
LR_POLICY: "poly"
OPTIMIZER: "sgd"
# PaddleSeg 预测部署
`PaddleSeg`目前支持使用`Python``C++`部署在`Windows``Linux` 上, 也可以集成`PaddleServing`服务化部署在 `Linux` 上。
[1. Python预测(支持 Linux 和 Windows)](./python/)
[2. C++预测(支持 Linux 和 Windows)](./cpp/)
[3. 服务化部署(仅支持 Linux)](./serving)
[4. 移动端部署(仅支持Android)](./lite)
...@@ -195,8 +195,8 @@ endif(NOT WIN32) ...@@ -195,8 +195,8 @@ endif(NOT WIN32)
if(WITH_GPU) if(WITH_GPU)
if(NOT WIN32) if(NOT WIN32)
if (USE_TENSORRT) if (USE_TENSORRT)
set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer${CMAKE_STATIC_LIBRARY_SUFFIX}) set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX}) set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX})
endif() endif()
set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX}) set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX}) set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
......
# PaddleSeg C++预测部署方案 # PaddleSeg 预测部署方案
[1.说明](#1说明) [1.说明](#1说明)
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
## 1.说明 ## 1.说明
本目录提供一个跨平台的图像分割模型的C++预测部署方案,用户通过一定的配置,加上少量的代码,即可把模型集成到自己的服务中,完成图像分割的任务。 本目录提供一个跨平台`PaddlePaddle`图像分割模型的`C++`预测部署方案,用户通过一定的配置,加上少量的代码,即可把模型集成到自己的服务中,完成图像分割的任务。
主要设计的目标包括以下四点: 主要设计的目标包括以下四点:
- 跨平台,支持在 windows 和 Linux 完成编译、开发和部署 - 跨平台,支持在 windows 和 Linux 完成编译、开发和部署
...@@ -19,11 +19,13 @@ ...@@ -19,11 +19,13 @@
- 可扩展性,支持用户针对新模型开发自己特殊的数据预处理、后处理等逻辑 - 可扩展性,支持用户针对新模型开发自己特殊的数据预处理、后处理等逻辑
- 高性能,除了`PaddlePaddle`自身带来的性能优势,我们还针对图像分割的特点对关键步骤进行了性能优化 - 高性能,除了`PaddlePaddle`自身带来的性能优势,我们还针对图像分割的特点对关键步骤进行了性能优化
**注意** 如需要使用`Python`的预测部署方法,请参考:[Python预测部署](../python/)
## 2.主要目录和文件 ## 2.主要目录和文件
``` ```
inference cpp
├── demo.cpp # 演示加载模型、读入数据、完成预测任务C++代码 ├── demo.cpp # 演示加载模型、读入数据、完成预测任务C++代码
| |
├── conf ├── conf
...@@ -88,6 +90,8 @@ deeplabv3p_xception65_humanseg ...@@ -88,6 +90,8 @@ deeplabv3p_xception65_humanseg
DEPLOY: DEPLOY:
# 是否使用GPU预测 # 是否使用GPU预测
USE_GPU: 1 USE_GPU: 1
# 是否是PaddleSeg 0.3.0新版本模型
USE_PR : 1
# 模型和参数文件所在目录路径 # 模型和参数文件所在目录路径
MODEL_PATH: "/root/projects/models/deeplabv3p_xception65_humanseg" MODEL_PATH: "/root/projects/models/deeplabv3p_xception65_humanseg"
# 模型文件名 # 模型文件名
...@@ -123,11 +127,11 @@ DEPLOY: ...@@ -123,11 +127,11 @@ DEPLOY:
`Linux` 系统中执行以下命令: `Linux` 系统中执行以下命令:
```shell ```shell
./demo --conf=/root/projects/PaddleSeg/inference/conf/humanseg.yaml --input_dir=/root/projects/PaddleSeg/inference/images/humanseg/ ./demo --conf=/root/projects/PaddleSeg/deploy/cpp/conf/humanseg.yaml --input_dir=/root/projects/PaddleSeg/deploy/cpp/images/humanseg/
``` ```
`Windows` 中执行以下命令: `Windows` 中执行以下命令:
```shell ```shell
D:\projects\PaddleSeg\inference\build\Release>demo.exe --conf=D:\\projects\\PaddleSeg\\inference\\conf\\humanseg.yaml --input_dir=D:\\projects\\PaddleSeg\\inference\\images\humanseg\\ D:\projects\PaddleSeg\deploy\cpp\build\Release>demo.exe --conf=D:\\projects\\PaddleSeg\\deploy\\cpp\\conf\\humanseg.yaml --input_dir=D:\\projects\\PaddleSeg\\deploy\\cpp\\images\humanseg\\
``` ```
...@@ -139,7 +143,7 @@ D:\projects\PaddleSeg\inference\build\Release>demo.exe --conf=D:\\projects\\Padd ...@@ -139,7 +143,7 @@ D:\projects\PaddleSeg\inference\build\Release>demo.exe --conf=D:\\projects\\Padd
| input_dir | 需要预测的图片目录 | | input_dir | 需要预测的图片目录 |
配置文件说明请参考上一步,样例程序会扫描input_dir目录下的所有以**jpg或jpeg**为后缀的图片,并生成对应的预测结果(若input_dir目录下没有以**jpg或jpeg**为后缀的图片,程序会报错)。图像分割会对`demo.jpg`的每个像素进行分类,其预测的结果保存在`demo_jpg.png`中。分割预测结果的图不能直接看到效果,必须经过可视化处理。对于二分类的图像分割模型,样例程序自动将预测结果转换成可视化结果,保存在`demo_jpg_scoremap.png`中, 原始尺寸的预测结果在`demo_jpg_recover.png`中,如下图。对于**多分类**的图像分割模型,请参考[可视化脚本使用方法](./docs/vis.md) 配置文件说明请参考上一步,样例程序会扫描input_dir目录下的所有以**jpg或jpeg**为后缀的图片,并生成对应的预测结果(若input_dir目录下没有以**jpg或jpeg**为后缀的图片,程序会报错)。图像分割会对`demo.jpg`的每个像素进行分类,其预测的结果保存在`demo_jpg_mask.png`中。分割预测结果的图不能直接看到效果,必须经过可视化处理。对于二分类的图像分割模型。如果需要对预测结果进行**可视化**,请参考[可视化脚本使用方法](./docs/vis.md)
输入原图 输入原图
![avatar](images/humanseg/demo2.jpeg) ![avatar](images/humanseg/demo2.jpeg)
......
...@@ -24,7 +24,7 @@ int main(int argc, char** argv) { ...@@ -24,7 +24,7 @@ int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true); google::ParseCommandLineFlags(&argc, &argv, true);
if (FLAGS_conf.empty() || FLAGS_input_dir.empty()) { if (FLAGS_conf.empty() || FLAGS_input_dir.empty()) {
std::cout << "Usage: ./predictor --conf=/config/path/to/your/model " std::cout << "Usage: ./predictor --conf=/config/path/to/your/model "
<< "--input_dir=/directory/of/your/input/images"; << "--input_dir=/directory/of/your/input/images" << std::endl;
return -1; return -1;
} }
// 1. create a predictor and init it with conf // 1. create a predictor and init it with conf
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
1. `mkdir -p /root/projects/ && cd /root/projects` 1. `mkdir -p /root/projects/ && cd /root/projects`
2. `git clone https://github.com/PaddlePaddle/PaddleSeg.git` 2. `git clone https://github.com/PaddlePaddle/PaddleSeg.git`
`C++`预测代码在`/root/projects/PaddleSeg/inference` 目录,该目录不依赖任何`PaddleSeg`下其他目录。 `C++`预测代码在`/root/projects/PaddleSeg/deploy/cpp` 目录,该目录不依赖任何`PaddleSeg`下其他目录。
### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
...@@ -25,9 +25,9 @@ PaddlePaddle C++ 预测库主要分为CPU版本和GPU版本。其中,针对不 ...@@ -25,9 +25,9 @@ PaddlePaddle C++ 预测库主要分为CPU版本和GPU版本。其中,针对不
| 版本 | 链接 | | 版本 | 链接 |
| ---- | ---- | | ---- | ---- |
| CPU版本 | [fluid_inference.tgz](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_linux_cpu_1.6.1.tgz) | | CPU版本 | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.6.1-cpu-avx-mkl/fluid_inference.tgz) |
| CUDA 9.0版本 | [fluid_inference.tgz](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_linux_cuda97_1.6.1.tgz) | | CUDA 9.0版本 | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.6.1-gpu-cuda9-cudnn7-avx-mkl/fluid_inference.tgz) |
| CUDA 10.0版本 | [fluid_inference.tgz](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_linux_cuda10_1.6.1.tgz) | | CUDA 10.0版本 | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.6.1-gpu-cuda10-cudnn7-avx-mkl/fluid_inference.tgz) |
针对不同的CPU类型、不同的指令集,官方提供更多可用的预测库版本,目前已经推出1.6版本的预测库。其余版本具体请参考以下链接:[C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_usage/deploy/inference/build_and_install_lib_cn.html) 针对不同的CPU类型、不同的指令集,官方提供更多可用的预测库版本,目前已经推出1.6版本的预测库。其余版本具体请参考以下链接:[C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_usage/deploy/inference/build_and_install_lib_cn.html)
...@@ -75,7 +75,7 @@ make install ...@@ -75,7 +75,7 @@ make install
在使用**GPU版本**预测库进行编译时,可执行下列操作。**注意**把对应的参数改为你的上述依赖库实际路径: 在使用**GPU版本**预测库进行编译时,可执行下列操作。**注意**把对应的参数改为你的上述依赖库实际路径:
```shell ```shell
cd /root/projects/PaddleSeg/inference cd /root/projects/PaddleSeg/deploy/cpp
mkdir build && cd build mkdir build && cd build
cmake .. -DWITH_GPU=ON -DPADDLE_DIR=/root/projects/fluid_inference -DCUDA_LIB=/usr/local/cuda/lib64/ -DOPENCV_DIR=/root/projects/opencv3/ -DCUDNN_LIB=/usr/local/cuda/lib64/ -DWITH_STATIC_LIB=OFF cmake .. -DWITH_GPU=ON -DPADDLE_DIR=/root/projects/fluid_inference -DCUDA_LIB=/usr/local/cuda/lib64/ -DOPENCV_DIR=/root/projects/opencv3/ -DCUDNN_LIB=/usr/local/cuda/lib64/ -DWITH_STATIC_LIB=OFF
make make
...@@ -83,7 +83,7 @@ make ...@@ -83,7 +83,7 @@ make
在使用**CPU版本**预测库进行编译时,可执行下列操作。 在使用**CPU版本**预测库进行编译时,可执行下列操作。
```shell ```shell
cd /root/projects/PaddleSeg/inference cd /root/projects/PaddleSeg/cpp
mkdir build && cd build mkdir build && cd build
cmake .. -DWITH_GPU=OFF -DPADDLE_DIR=/root/projects/fluid_inference -DOPENCV_DIR=/root/projects/opencv3/ -DWITH_STATIC_LIB=OFF cmake .. -DWITH_GPU=OFF -DPADDLE_DIR=/root/projects/fluid_inference -DOPENCV_DIR=/root/projects/opencv3/ -DWITH_STATIC_LIB=OFF
......
...@@ -12,7 +12,7 @@ cd inference/tools/ ...@@ -12,7 +12,7 @@ cd inference/tools/
# 拷贝保存分割预测结果的图片到本目录 # 拷贝保存分割预测结果的图片到本目录
cp XXX/demo_jpg.png . cp XXX/demo_jpg.png .
# 运行可视化脚本 # 运行可视化脚本
python visualize.py demo.jpg demo_jpg.png vis_result.png python visualize.py demo.jpg demo_jpg_mask.png vis_result.png
``` ```
以下为上述运行可视化脚本例子中每个参数的含义,请根据测试机器中图片的**实际路径**修改对应参数。 以下为上述运行可视化脚本例子中每个参数的含义,请根据测试机器中图片的**实际路径**修改对应参数。
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
1. 打开`cmd`, 执行 `cd /d D:\projects` 1. 打开`cmd`, 执行 `cd /d D:\projects`
2. `git clone http://gitlab.baidu.com/Paddle/PaddleSeg.git` 2. `git clone http://gitlab.baidu.com/Paddle/PaddleSeg.git`
`C++`预测库代码在`D:\projects\PaddleSeg\inference` 目录,该目录不依赖任何`PaddleSeg`下其他目录。 `C++`预测库代码在`D:\projects\PaddleSeg\deploy\cpp` 目录,该目录不依赖任何`PaddleSeg`下其他目录。
### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
...@@ -24,9 +24,9 @@ PaddlePaddle C++ 预测库主要分为两大版本:CPU版本和GPU版本。其 ...@@ -24,9 +24,9 @@ PaddlePaddle C++ 预测库主要分为两大版本:CPU版本和GPU版本。其
| 版本 | 链接 | | 版本 | 链接 |
| ---- | ---- | | ---- | ---- |
| CPU版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_install_dir_win_cpu_1.6.zip) | | CPU版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.2/win-infer/mkl/cpu/fluid_inference_install_dir.zip) |
| CUDA 9.0版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_install_dir_win_cuda9_1.6.1.zip) | | CUDA 9.0版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.2/win-infer/mkl/post97/fluid_inference_install_dir.zip) |
| CUDA 10.0版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_install_dir_win_cuda10_1.6.1.zip) | | CUDA 10.0版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.2/win-infer/mkl/post107/fluid_inference_install_dir.zip) |
解压后`D:\projects\fluid_inference`目录包含内容为: 解压后`D:\projects\fluid_inference`目录包含内容为:
``` ```
...@@ -70,19 +70,19 @@ call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd6 ...@@ -70,19 +70,19 @@ call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd6
```bash ```bash
# 切换到预测库所在目录 # 切换到预测库所在目录
cd /d D:\projects\PaddleSeg\inference\ cd /d D:\projects\PaddleSeg\deply\cpp\
# 创建构建目录, 重新构建只需要删除该目录即可 # 创建构建目录, 重新构建只需要删除该目录即可
mkdir build mkdir build
cd build cd build
# cmake构建VS项目 # cmake构建VS项目
D:\projects\PaddleSeg\inference\build> cmake .. -G "Visual Studio 14 2015 Win64" -DWITH_GPU=ON -DPADDLE_DIR=D:\projects\fluid_inference -DCUDA_LIB=D:\projects\cudalib\v9.0\lib\x64 -DOPENCV_DIR=D:\projects\opencv -T host=x64 D:\projects\PaddleSeg\deploy\cpp\build> cmake .. -G "Visual Studio 14 2015 Win64" -DWITH_GPU=ON -DPADDLE_DIR=D:\projects\fluid_inference -DCUDA_LIB=D:\projects\cudalib\v9.0\lib\x64 -DOPENCV_DIR=D:\projects\opencv -T host=x64
``` ```
在使用**CPU版本**预测库进行编译时,可执行下列操作。 在使用**CPU版本**预测库进行编译时,可执行下列操作。
```bash ```bash
# 切换到预测库所在目录 # 切换到预测库所在目录
cd /d D:\projects\PaddleSeg\inference\ cd /d D:\projects\PaddleSeg\deploy\cpp\
# 创建构建目录, 重新构建只需要删除该目录即可 # 创建构建目录, 重新构建只需要删除该目录即可
mkdir build mkdir build
cd build cd build
...@@ -102,7 +102,7 @@ D:\projects\PaddleSeg\inference\build> msbuild /m /p:Configuration=Release cpp_i ...@@ -102,7 +102,7 @@ D:\projects\PaddleSeg\inference\build> msbuild /m /p:Configuration=Release cpp_i
上述`Visual Studio 2015`编译产出的可执行文件在`build\release`目录下,切换到该目录: 上述`Visual Studio 2015`编译产出的可执行文件在`build\release`目录下,切换到该目录:
``` ```
cd /d D:\projects\PaddleSeg\inference\build\release cd /d D:\projects\PaddleSeg\deploy\cpp\build\release
``` ```
之后执行命令: 之后执行命令:
......
...@@ -15,7 +15,7 @@ Windows 平台下,我们使用`Visual Studio 2015` 和 `Visual Studio 2019 Com ...@@ -15,7 +15,7 @@ Windows 平台下,我们使用`Visual Studio 2015` 和 `Visual Studio 2019 Com
### Step1: 下载代码 ### Step1: 下载代码
1. 点击下载源代码:[下载地址](https://github.com/PaddlePaddle/PaddleSeg/archive/release/v0.2.0.zip) 1. 点击下载源代码:[下载地址](https://github.com/PaddlePaddle/PaddleSeg/archive/release/v0.3.0.zip)
2. 解压,解压后目录重命名为`PaddleSeg` 2. 解压,解压后目录重命名为`PaddleSeg`
以下代码目录路径为`D:\projects\PaddleSeg` 为例。 以下代码目录路径为`D:\projects\PaddleSeg` 为例。
...@@ -27,9 +27,9 @@ PaddlePaddle C++ 预测库主要分为两大版本:CPU版本和GPU版本。其 ...@@ -27,9 +27,9 @@ PaddlePaddle C++ 预测库主要分为两大版本:CPU版本和GPU版本。其
| 版本 | 链接 | | 版本 | 链接 |
| ---- | ---- | | ---- | ---- |
| CPU版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_install_dir_win_cpu_1.6.zip) | | CPU版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.1/win-infer/mkl/cpu/fluid_inference_install_dir.zip) |
| CUDA 9.0版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_install_dir_win_cuda9_1.6.1.zip) | | CUDA 9.0版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.1/win-infer/mkl/post97/fluid_inference_install_dir.zip) |
| CUDA 10.0版本 | [fluid_inference_install_dir.zip](https://bj.bcebos.com/paddlehub/paddle_inference_lib/fluid_inference_install_dir_win_cuda10_1.6.1.zip) | | CUDA 10.0版本 | [fluid_inference_install_dir.zip](https://paddle-wheel.bj.bcebos.com/1.6.1/win-infer/mkl/post107/fluid_inference_install_dir.zip) |
解压后`D:\projects\fluid_inference`目录包含内容为: 解压后`D:\projects\fluid_inference`目录包含内容为:
``` ```
...@@ -74,6 +74,7 @@ fluid_inference ...@@ -74,6 +74,7 @@ fluid_inference
| *CUDA_LIB | CUDA的库路径 | | *CUDA_LIB | CUDA的库路径 |
| OPENCV_DIR | OpenCV的安装路径 | | OPENCV_DIR | OpenCV的安装路径 |
| PADDLE_DIR | Paddle预测库的路径 | | PADDLE_DIR | Paddle预测库的路径 |
**注意**在使用CPU版本预测库时,需要把CUDA_LIB的勾去掉。 **注意**在使用CPU版本预测库时,需要把CUDA_LIB的勾去掉。
![step4](https://paddleseg.bj.bcebos.com/inference/vs2019_step5.png) ![step4](https://paddleseg.bj.bcebos.com/inference/vs2019_step5.png)
...@@ -89,7 +90,7 @@ fluid_inference ...@@ -89,7 +90,7 @@ fluid_inference
上述`Visual Studio 2019`编译产出的可执行文件在`out\build\x64-Release`目录下,打开`cmd`,并切换到该目录: 上述`Visual Studio 2019`编译产出的可执行文件在`out\build\x64-Release`目录下,打开`cmd`,并切换到该目录:
``` ```
cd /d D:\projects\PaddleSeg\inference\out\build\x64-Release cd /d D:\projects\PaddleSeg\deploy\cpp\out\build\x64-Release
``` ```
之后执行命令: 之后执行命令:
......
...@@ -36,7 +36,6 @@ namespace PaddleSolution { ...@@ -36,7 +36,6 @@ namespace PaddleSolution {
const auto& model_dir = _model_config._model_path; const auto& model_dir = _model_config._model_path;
const auto& model_filename = _model_config._model_file_name; const auto& model_filename = _model_config._model_file_name;
const auto& params_filename = _model_config._param_file_name; const auto& params_filename = _model_config._param_file_name;
// load paddle model file // load paddle model file
if (_model_config._predictor_mode == "NATIVE") { if (_model_config._predictor_mode == "NATIVE") {
paddle::NativeConfig config; paddle::NativeConfig config;
...@@ -52,6 +51,12 @@ namespace PaddleSolution { ...@@ -52,6 +51,12 @@ namespace PaddleSolution {
paddle::AnalysisConfig config; paddle::AnalysisConfig config;
if (use_gpu) { if (use_gpu) {
config.EnableUseGpu(100, 0); config.EnableUseGpu(100, 0);
if (TRT_MAP.find(_model_config._trt_mode) != TRT_MAP.end()) {
auto precision = TRT_MAP[_model_config._trt_mode];
bool use_cab = (precision == paddle::AnalysisConfig::Precision::kInt8);
config.EnableTensorRtEngine(1 << 30, _model_config._batch_size, 40,
precision, false, use_cab);
}
} }
auto prog_file = utils::path_join(model_dir, model_filename); auto prog_file = utils::path_join(model_dir, model_filename);
auto param_file = utils::path_join(model_dir, params_filename); auto param_file = utils::path_join(model_dir, params_filename);
...@@ -83,7 +88,6 @@ namespace PaddleSolution { ...@@ -83,7 +88,6 @@ namespace PaddleSolution {
int blob_out_len = length; int blob_out_len = length;
int seg_out_len = eval_height * eval_width * eval_num_class; int seg_out_len = eval_height * eval_width * eval_num_class;
if (blob_out_len != seg_out_len) { if (blob_out_len != seg_out_len) {
LOG(ERROR) << " [FATAL] unequal: input vs output [" << LOG(ERROR) << " [FATAL] unequal: input vs output [" <<
seg_out_len << "|" << blob_out_len << "]" << std::endl; seg_out_len << "|" << blob_out_len << "]" << std::endl;
...@@ -97,25 +101,22 @@ namespace PaddleSolution { ...@@ -97,25 +101,22 @@ namespace PaddleSolution {
cv::Mat mask_png = cv::Mat(eval_height, eval_width, CV_8UC1); cv::Mat mask_png = cv::Mat(eval_height, eval_width, CV_8UC1);
mask_png.data = _mask.data(); mask_png.data = _mask.data();
std::string nname(fname); std::string nname(fname);
auto pos = fname.find("."); auto pos = fname.rfind(".");
nname[pos] = '_'; nname[pos] = '_';
std::string mask_save_name = nname + ".png"; std::string mask_save_name = nname + "_mask.png";
cv::imwrite(mask_save_name, mask_png); cv::imwrite(mask_save_name, mask_png);
cv::Mat scoremap_png = cv::Mat(eval_height, eval_width, CV_8UC1); cv::Mat scoremap_png = cv::Mat(eval_height, eval_width, CV_8UC1);
scoremap_png.data = _scoremap.data(); scoremap_png.data = _scoremap.data();
std::string scoremap_save_name = nname std::string scoremap_save_name = nname + std::string("_scoremap.png");
+ std::string("_scoremap.png");
cv::imwrite(scoremap_save_name, scoremap_png); cv::imwrite(scoremap_save_name, scoremap_png);
std::cout << "save mask of [" << fname << "] done" << std::endl; std::cout << "save mask of [" << fname << "] done" << std::endl;
if (height && width) { if (height && width) {
int recover_height = *height; int recover_height = *height;
int recover_width = *width; int recover_width = *width;
cv::Mat recover_png = cv::Mat(recover_height, cv::Mat recover_png = cv::Mat(recover_height, recover_width, CV_8UC1);
recover_width, CV_8UC1);
cv::resize(scoremap_png, recover_png, cv::resize(scoremap_png, recover_png,
cv::Size(recover_width, recover_height), cv::Size(recover_width, recover_height), 0, 0, cv::INTER_CUBIC);
0, 0, cv::INTER_CUBIC);
std::string recover_name = nname + std::string("_recover.png"); std::string recover_name = nname + std::string("_recover.png");
cv::imwrite(recover_name, recover_png); cv::imwrite(recover_name, recover_png);
} }
...@@ -176,8 +177,13 @@ namespace PaddleSolution { ...@@ -176,8 +177,13 @@ namespace PaddleSolution {
} }
paddle::PaddleTensor im_tensor; paddle::PaddleTensor im_tensor;
im_tensor.name = "image"; im_tensor.name = "image";
if (!_model_config._use_pr) {
im_tensor.shape = std::vector<int>{ batch_size, channels, im_tensor.shape = std::vector<int>{ batch_size, channels,
eval_height, eval_width }; eval_height, eval_width };
} else {
im_tensor.shape = std::vector<int>{ batch_size, eval_height,
eval_width, channels};
}
im_tensor.data.Reset(input_buffer.data(), im_tensor.data.Reset(input_buffer.data(),
real_buffer_size * sizeof(float)); real_buffer_size * sizeof(float));
im_tensor.dtype = paddle::PaddleDType::FLOAT32; im_tensor.dtype = paddle::PaddleDType::FLOAT32;
...@@ -202,19 +208,45 @@ namespace PaddleSolution { ...@@ -202,19 +208,45 @@ namespace PaddleSolution {
std::cout << _outputs[0].shape[j] << ","; std::cout << _outputs[0].shape[j] << ",";
} }
std::cout << ")" << std::endl; std::cout << ")" << std::endl;
const size_t nums = _outputs.front().data.length()
/ sizeof(float); size_t nums = _outputs.front().data.length() / sizeof(float);
if (out_num % batch_size != 0 || out_num != nums) { if (_model_config._use_pr) {
LOG(ERROR) << "outputs data size mismatch with shape size."; nums = _outputs.front().data.length() / sizeof(int64_t);
}
// size mismatch checking
bool size_mismatch = out_num % batch_size;
size_mismatch |= (!_model_config._use_pr) && (nums != out_num);
size_mismatch |= _model_config._use_pr && (nums != eval_height * eval_width);
if (size_mismatch) {
LOG(ERROR) << "output with a unexpected size";
return -1; return -1;
} }
if (_model_config._use_pr) {
std::vector<uchar> out_data;
out_data.resize(out_num);
auto addr = reinterpret_cast<int64_t*>(_outputs[0].data.data());
for (int r = 0; r < out_num; ++r) {
out_data[r] = (int)(addr[r]);
}
for (int r = 0; r < batch_size; ++r) {
cv::Mat mask_png = cv::Mat(eval_height, eval_width, CV_8UC1);
mask_png.data = out_data.data() + eval_height*eval_width*r;
auto name = imgs_batch[r];
auto pos = name.rfind(".");
name[pos] = '_';
std::string mask_save_name = name + "_mask.png";
cv::imwrite(mask_save_name, mask_png);
}
continue;
}
for (int i = 0; i < batch_size; ++i) { for (int i = 0; i < batch_size; ++i) {
float* output_addr = reinterpret_cast<float*>( float* output_addr = reinterpret_cast<float*>(
_outputs[0].data.data()) _outputs[0].data.data())
+ i * (out_num / batch_size); + i * (nums / batch_size);
output_mask(imgs_batch[i], output_addr, output_mask(imgs_batch[i], output_addr,
out_num / batch_size, nums / batch_size,
&org_height[i], &org_height[i],
&org_width[i]); &org_width[i]);
} }
...@@ -278,8 +310,14 @@ namespace PaddleSolution { ...@@ -278,8 +310,14 @@ namespace PaddleSolution {
return -1; return -1;
} }
auto im_tensor = _main_predictor->GetInputTensor("image"); auto im_tensor = _main_predictor->GetInputTensor("image");
if (!_model_config._use_pr) {
im_tensor->Reshape({ batch_size, channels, im_tensor->Reshape({ batch_size, channels,
eval_height, eval_width }); eval_height, eval_width });
} else {
im_tensor->Reshape({ batch_size, eval_height,
eval_width, channels});
}
im_tensor->copy_from_cpu(input_buffer.data()); im_tensor->copy_from_cpu(input_buffer.data());
auto t1 = std::chrono::high_resolution_clock::now(); auto t1 = std::chrono::high_resolution_clock::now();
...@@ -292,7 +330,6 @@ namespace PaddleSolution { ...@@ -292,7 +330,6 @@ namespace PaddleSolution {
auto output_names = _main_predictor->GetOutputNames(); auto output_names = _main_predictor->GetOutputNames();
auto output_t = _main_predictor->GetOutputTensor( auto output_t = _main_predictor->GetOutputTensor(
output_names[0]); output_names[0]);
std::vector<float> out_data;
std::vector<int> output_shape = output_t->shape(); std::vector<int> output_shape = output_t->shape();
int out_num = 1; int out_num = 1;
...@@ -303,6 +340,30 @@ namespace PaddleSolution { ...@@ -303,6 +340,30 @@ namespace PaddleSolution {
} }
std::cout << ")" << std::endl; std::cout << ")" << std::endl;
if (_model_config._use_pr) {
std::vector<int64_t> out_data;
out_data.resize(out_num);
output_t->copy_to_cpu(out_data.data());
std::vector<uchar> mask_data;
mask_data.resize(out_num);
auto addr = reinterpret_cast<int64_t*>(out_data.data());
for (int r = 0; r < out_num; ++r) {
mask_data[r] = (int)(addr[r]);
}
for (int r = 0; r < batch_size; ++r) {
cv::Mat mask_png = cv::Mat(eval_height, eval_width, CV_8UC1);
mask_png.data = mask_data.data() + eval_height*eval_width*r;
auto name = imgs_batch[r];
auto pos = name.rfind(".");
name[pos] = '_';
std::string mask_save_name = name + "_mask.png";
cv::imwrite(mask_save_name, mask_png);
}
continue;
}
std::vector<float> out_data;
out_data.resize(out_num); out_data.resize(out_num);
output_t->copy_to_cpu(out_data.data()); output_t->copy_to_cpu(out_data.data());
for (int i = 0; i < batch_size; ++i) { for (int i = 0; i < batch_size; ++i) {
......
...@@ -55,5 +55,10 @@ class Predictor { ...@@ -55,5 +55,10 @@ class Predictor {
PaddleSolution::PaddleSegModelConfigPaser _model_config; PaddleSolution::PaddleSegModelConfigPaser _model_config;
std::shared_ptr<PaddleSolution::ImagePreProcessor> _preprocessor; std::shared_ptr<PaddleSolution::ImagePreProcessor> _preprocessor;
std::unique_ptr<paddle::PaddlePredictor> _main_predictor; std::unique_ptr<paddle::PaddlePredictor> _main_predictor;
std::map<std::string, paddle::AnalysisConfig::Precision> TRT_MAP = {
{"FP32", paddle::AnalysisConfig::Precision::kFloat32},
{"FP16", paddle::AnalysisConfig::Precision::kHalf},
{"INT8", paddle::AnalysisConfig::Precision::kInt8}
};
}; };
} // namespace PaddleSolution } // namespace PaddleSolution
...@@ -40,14 +40,18 @@ namespace PaddleSolution { ...@@ -40,14 +40,18 @@ namespace PaddleSolution {
LOG(ERROR) << "Only support rgb(gray) and rgba image."; LOG(ERROR) << "Only support rgb(gray) and rgba image.";
return false; return false;
} }
cv::Size resize_size(_config->_resize[0], _config->_resize[1]); cv::Size resize_size(_config->_resize[0], _config->_resize[1]);
int rw = resize_size.width; int rw = resize_size.width;
int rh = resize_size.height; int rh = resize_size.height;
if (*ori_h != rh || *ori_w != rw) { if (*ori_h != rh || *ori_w != rw) {
cv::resize(im, im, resize_size, 0, 0, cv::INTER_LINEAR); cv::resize(im, im, resize_size, 0, 0, cv::INTER_LINEAR);
} }
if (!_config->_use_pr) {
utils::normalize(im, data, _config->_mean, _config->_std); utils::normalize(im, data, _config->_mean, _config->_std);
} else {
utils::flatten_mat(im, data);
}
return true; return true;
} }
......
...@@ -25,6 +25,7 @@ class PaddleSegModelConfigPaser { ...@@ -25,6 +25,7 @@ class PaddleSegModelConfigPaser {
:_class_num(0), :_class_num(0),
_channels(0), _channels(0),
_use_gpu(0), _use_gpu(0),
_use_pr(0),
_batch_size(1), _batch_size(1),
_model_file_name("__model__"), _model_file_name("__model__"),
_param_file_name("__params__") { _param_file_name("__params__") {
...@@ -40,10 +41,12 @@ class PaddleSegModelConfigPaser { ...@@ -40,10 +41,12 @@ class PaddleSegModelConfigPaser {
_class_num = 0; _class_num = 0;
_channels = 0; _channels = 0;
_use_gpu = 0; _use_gpu = 0;
_use_pr = 0;
_batch_size = 1; _batch_size = 1;
_model_file_name.clear(); _model_file_name.clear();
_model_path.clear(); _model_path.clear();
_param_file_name.clear(); _param_file_name.clear();
_trt_mode.clear();
} }
std::string process_parenthesis(const std::string& str) { std::string process_parenthesis(const std::string& str) {
...@@ -70,43 +73,120 @@ class PaddleSegModelConfigPaser { ...@@ -70,43 +73,120 @@ class PaddleSegModelConfigPaser {
bool load_config(const std::string& conf_file) { bool load_config(const std::string& conf_file) {
reset(); reset();
YAML::Node config;
YAML::Node config = YAML::LoadFile(conf_file); try {
config = YAML::LoadFile(conf_file);
} catch(...) {
return false;
}
// 1. get resize // 1. get resize
if (config["DEPLOY"]["EVAL_CROP_SIZE"].IsDefined()) {
auto str = config["DEPLOY"]["EVAL_CROP_SIZE"].as<std::string>(); auto str = config["DEPLOY"]["EVAL_CROP_SIZE"].as<std::string>();
_resize = parse_str_to_vec<int>(process_parenthesis(str)); _resize = parse_str_to_vec<int>(process_parenthesis(str));
} else {
std::cerr << "Please set EVAL_CROP_SIZE: (xx, xx)" << std::endl;
return false;
}
// 2. get mean // 2. get mean
if (config["DEPLOY"]["MEAN"].IsDefined()) {
for (const auto& item : config["DEPLOY"]["MEAN"]) { for (const auto& item : config["DEPLOY"]["MEAN"]) {
_mean.push_back(item.as<float>()); _mean.push_back(item.as<float>());
} }
} else {
std::cerr << "Please set MEAN: [xx, xx, xx]" << std::endl;
return false;
}
// 3. get std // 3. get std
if(config["DEPLOY"]["STD"].IsDefined()) {
for (const auto& item : config["DEPLOY"]["STD"]) { for (const auto& item : config["DEPLOY"]["STD"]) {
_std.push_back(item.as<float>()); _std.push_back(item.as<float>());
} }
} else {
std::cerr << "Please set STD: [xx, xx, xx]" << std::endl;
return false;
}
// 4. get image type // 4. get image type
if (config["DEPLOY"]["IMAGE_TYPE"].IsDefined()) {
_img_type = config["DEPLOY"]["IMAGE_TYPE"].as<std::string>(); _img_type = config["DEPLOY"]["IMAGE_TYPE"].as<std::string>();
} else {
std::cerr << "Please set IMAGE_TYPE: \"rgb\" or \"rgba\"" << std::endl;
return false;
}
// 5. get class number // 5. get class number
if (config["DEPLOY"]["NUM_CLASSES"].IsDefined()) {
_class_num = config["DEPLOY"]["NUM_CLASSES"].as<int>(); _class_num = config["DEPLOY"]["NUM_CLASSES"].as<int>();
} else {
std::cerr << "Please set NUM_CLASSES: x" << std::endl;
return false;
}
// 7. set model path // 7. set model path
if (config["DEPLOY"]["MODEL_PATH"].IsDefined()) {
_model_path = config["DEPLOY"]["MODEL_PATH"].as<std::string>(); _model_path = config["DEPLOY"]["MODEL_PATH"].as<std::string>();
} else {
std::cerr << "Please set MODEL_PATH: \"/path/to/model_dir\"" << std::endl;
return false;
}
// 8. get model file_name // 8. get model file_name
if (config["DEPLOY"]["MODEL_FILENAME"].IsDefined()) {
_model_file_name = config["DEPLOY"]["MODEL_FILENAME"].as<std::string>(); _model_file_name = config["DEPLOY"]["MODEL_FILENAME"].as<std::string>();
} else {
_model_file_name = "__model__";
}
// 9. get model param file name // 9. get model param file name
_param_file_name = if (config["DEPLOY"]["PARAMS_FILENAME"].IsDefined()) {
config["DEPLOY"]["PARAMS_FILENAME"].as<std::string>(); _param_file_name
= config["DEPLOY"]["PARAMS_FILENAME"].as<std::string>();
} else {
_param_file_name = "__params__";
}
// 10. get pre_processor // 10. get pre_processor
if (config["DEPLOY"]["PRE_PROCESSOR"].IsDefined()) {
_pre_processor = config["DEPLOY"]["PRE_PROCESSOR"].as<std::string>(); _pre_processor = config["DEPLOY"]["PRE_PROCESSOR"].as<std::string>();
} else {
std::cerr << "Please set PRE_PROCESSOR: \"DetectionPreProcessor\"" << std::endl;
return false;
}
// 11. use_gpu // 11. use_gpu
if (config["DEPLOY"]["USE_GPU"].IsDefined()) {
_use_gpu = config["DEPLOY"]["USE_GPU"].as<int>(); _use_gpu = config["DEPLOY"]["USE_GPU"].as<int>();
} else {
_use_gpu = 0;
}
// 12. predictor_mode // 12. predictor_mode
if (config["DEPLOY"]["PREDICTOR_MODE"].IsDefined()) {
_predictor_mode = config["DEPLOY"]["PREDICTOR_MODE"].as<std::string>(); _predictor_mode = config["DEPLOY"]["PREDICTOR_MODE"].as<std::string>();
} else {
std::cerr << "Please set PREDICTOR_MODE: \"NATIVE\" or \"ANALYSIS\"" << std::endl;
return false;
}
// 13. batch_size // 13. batch_size
if (config["DEPLOY"]["BATCH_SIZE"].IsDefined()) {
_batch_size = config["DEPLOY"]["BATCH_SIZE"].as<int>(); _batch_size = config["DEPLOY"]["BATCH_SIZE"].as<int>();
} else {
_batch_size = 1;
}
// 14. channels // 14. channels
if (config["DEPLOY"]["CHANNELS"].IsDefined()) {
_channels = config["DEPLOY"]["CHANNELS"].as<int>(); _channels = config["DEPLOY"]["CHANNELS"].as<int>();
} else {
std::cerr << "Please set CHANNELS: x" << std::endl;
return false;
}
// 15. use_pr
if (config["DEPLOY"]["USE_PR"].IsDefined()) {
_use_pr = config["DEPLOY"]["USE_PR"].as<int>();
} else {
_use_pr = 0;
}
// 16. trt_mode
if (config["DEPLOY"]["TRT_MODE"].IsDefined()) {
_trt_mode = config["DEPLOY"]["TRT_MODE"].as<std::string>();
} else {
_trt_mode = "";
}
return true; return true;
} }
...@@ -173,6 +253,10 @@ class PaddleSegModelConfigPaser { ...@@ -173,6 +253,10 @@ class PaddleSegModelConfigPaser {
std::string _predictor_mode; std::string _predictor_mode;
// DEPLOY.BATCH_SIZE // DEPLOY.BATCH_SIZE
int _batch_size; int _batch_size;
// DEPLOY.USE_PR: OP Optimized model
int _use_pr;
// DEPLOY.TRT_MODE: TRT Precesion
std::string _trt_mode;
}; };
} // namespace PaddleSolution } // namespace PaddleSolution
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
#ifdef _WIN32 #ifdef _WIN32
#include <filesystem> #define GLOG_NO_ABBREVIATED_SEVERITIES
#include <windows.h>
#else #else
#include <dirent.h> #include <dirent.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -67,15 +68,21 @@ namespace utils { ...@@ -67,15 +68,21 @@ namespace utils {
// scan a directory and get all files with input extensions // scan a directory and get all files with input extensions
inline std::vector<std::string> get_directory_images( inline std::vector<std::string> get_directory_images(
const std::string& path, const std::string& exts) { const std::string& path, const std::string& exts) {
std::string pattern(path);
pattern.append("\\*");
std::vector<std::string> imgs; std::vector<std::string> imgs;
for (const auto& item : WIN32_FIND_DATA data;
std::experimental::filesystem::directory_iterator(path)) { HANDLE hFind;
auto suffix = item.path().extension().string(); if ((hFind = FindFirstFile(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE) {
if (exts.find(suffix) != std::string::npos && suffix.size() > 0) { do {
auto fullname = path_join(path, auto fname = std::string(data.cFileName);
item.path().filename().string()); auto pos = fname.rfind(".");
imgs.push_back(item.path().string()); auto ext = fname.substr(pos + 1);
} if (ext.size() > 1 && exts.find(ext) != std::string::npos) {
imgs.push_back(path + "\\" + data.cFileName);
}
} while (FindNextFile(hFind, &data) != 0);
FindClose(hFind);
} }
return imgs; return imgs;
} }
...@@ -103,6 +110,25 @@ namespace utils { ...@@ -103,6 +110,25 @@ namespace utils {
} }
} }
// flatten a cv::mat
inline void flatten_mat(cv::Mat& im, float* data) {
int rh = im.rows;
int rw = im.cols;
int rc = im.channels();
#pragma omp parallel for
for (int h = 0; h < rh; ++h) {
const uchar* ptr = im.ptr<uchar>(h);
int im_index = 0;
int top_index = h * rw * rc;
for (int w = 0; w < rw; ++w) {
for (int c = 0; c < rc; ++c) {
float pixel = static_cast<float>(ptr[im_index++]);
data[top_index++] = pixel;
}
}
}
}
// argmax // argmax
inline void argmax(float* out, std::vector<int>& shape, inline void argmax(float* out, std::vector<int>& shape,
std::vector<uchar>& mask, std::vector<uchar>& scoremap) { std::vector<uchar>& mask, std::vector<uchar>& scoremap) {
......
# 人像分割在移动端的部署
## 1.介绍
以人像分割在安卓端的部署为例,介绍如何使用[Paddle-Lite](https://github.com/PaddlePaddle/Paddle-Lite)对分割模型进行移动端的部署。文档第二节介绍如何使用人像分割安卓端的demo,后面几章节介绍如何将PaddleSeg的Model部署到安卓设备。
## 2.安卓Demo使用
### 2.1 要求
* Android Studio 3.4;
* Android手机或开发板;
### 2.2 安装
* git clone https://github.com/PaddlePaddle/PaddleSeg.git ;
* 打开Android Studio,在"Welcome to Android Studio"窗口点击"Open an existing Android Studio project",在弹出的路径选择窗口中进入"/PaddleSeg/lite/humanseg-android-demo/"目录,然后点击右下角的"Open"按钮即可导入工程
* 通过USB连接Android手机或开发板;
* 载入工程后,点击菜单栏的Run->Run 'App'按钮,在弹出的"Select Deployment Target"窗口选择已经连接的Android设备,然后点击"OK"按钮;
* 手机上会出现Demo的主界面,选择"Image Segmentation"图标,进入的人像分割示例程序;
* 在人像分割Demo中,默认会载入一张人像图像,并会在图像下方给出CPU的预测结果;
* 在人像分割Demo中,你还可以通过上方的"Gallery"和"Take Photo"按钮从相册或相机中加载测试图像;
### 2.3 其他
此安卓demo基于[Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)开发,更多的细节请参考该repo。<br>
*注意:demo中拍照时照片会自动压缩,想测试拍照原图效果,可使用手机相机拍照后从相册中打开进行预测。*
### 2.4 效果展示
<img src="example/human_1.png" width="20%" ><img src="example/human_2.png" width="20%" ><img src="example/human_3.png" width="20%" >
## 3.模型导出
此demo的人像分割模型为[下载链接](https://paddleseg.bj.bcebos.com/models/humanseg_mobilenetv2_1_0_bn_freeze_model_pr_po.zip),是基于Deeplab_v3+mobileNet_v2的humanseg模型,关于humanseg的介绍移步[特色垂类分割模型](./contrib),更多的分割模型导出可参考:[模型导出](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v0.2.0/docs/model_export.md)
## 4.模型转换
### 4.1模型转换工具
准备好PaddleSeg导出来的模型和参数文件后,需要使用Paddle-Lite提供的model_optimize_tool对模型进行优化,并转换成Paddle-Lite支持的文件格式,这里有两种方式来实现:
* 手动编译model_optimize_tool
详细的模型转换方法参考paddlelite提供的官方文档:[模型转化方法](https://paddlepaddle.github.io/Paddle-Lite/v2.0.0/model_optimize_tool/),从PaddleSeg里面导出来的模型使用model_optimize_tool即可导出model.nb和param.nb文件。
* 使用预编译版本的model_optimize_tool,最新的预编译文件参考[release](https://github.com/PaddlePaddle/Paddle-Lite/releases/),此demo使用的版本为[model_optimize_tool](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.0.0/model_optimize_tool)
*注意:如果运行失败,请在[Paddle-Lite源码编译](https://paddlepaddle.github.io/Paddle-Lite/v2.0.0/source_compile/)的开发环境中使用model_optimize_tool*
### 4.2 更新模型
将优化好的model.nb和param.nb文件,替换app/src/main/assets/image_segmentation/
models/deeplab_mobilenet_for_cpu下面的文件即可。
## 5. 更新预测库
Paddle-Lite的编译目前支持Docker,Linux和Mac OS开发环境,建议使用Docker开发环境,以免存在各种依赖问题,同时也提供了预编译版本的预测库。准备Paddle-Lite在安卓端的预测库,主要包括三个文件:
* PaddlePredictor.jar;
* arm64-v8a/libpaddle_lite_jni.so;
* armeabi-v7a/libpaddle_lite_jni.so;
下面分别介绍两种方法:
* 使用预编译版本的预测库,最新的预编译文件参考:[release](https://github.com/PaddlePaddle/Paddle-Lite/releases/),此demo使用的版本:
* arm64-v8a: [inference_lite_lib.android.armv8](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.0.0/inference_lite_lib.android.armv8.gcc.c++_shared.with_extra.full_publish.tar.gz) ;
* armeabi-v7a: [inference_lite_lib.android.armv7](https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.0.0/inference_lite_lib.android.armv7.gcc.c++_shared.with_extra.full_publish.tar.gz) ;
解压上面两个文件,PaddlePredictor.jar位于任一文件夹:inference_lite_lib.android.xxx/java/jar/PaddlePredictor.jar;
解压上述inference_lite_lib.android.armv8文件,arm64-v8a/libpaddle_lite_jni.so位于:inference_lite_lib.android.armv8/java/so/libpaddle_lite_jni.so;
解压上述inference_lite_lib.android.armv7文件,armeabi-v7a/libpaddle_lite_jni.so位于:inference_lite_lib.android.armv7/java/so/libpaddle_lite_jni.so;
* 手动编译Paddle-Lite预测库
开发环境的准备和编译方法参考:[Paddle-Lite源码编译](https://paddlepaddle.github.io/Paddle-Lite/v2.0.0/source_compile/)
准备好上述文件,即可参考[java_api](https://paddlepaddle.github.io/Paddle-Lite/v2.0.0/java_api_doc/)在安卓端进行推理。具体使用预测库的方法可参考[Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)中更新预测库部分的文档。
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.baidu.paddle.lite.demo"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation files('libs/PaddlePredictor.jar')
}
#Mon Nov 25 17:01:58 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package com.baidu.paddle.lite.demo;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.baidu.paddle.lite.demo", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baidu.paddle.lite.demo">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:allowBackup="true"
android:largeHeap="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".segmentation.ImgSegActivity"
android:label="Image Segmentation"/>
<activity
android:name=".segmentation.ImgSegSettingsActivity"
android:label="Settings">
</activity>
</application>
</manifest>
\ No newline at end of file
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.
*/
package com.baidu.paddle.lite.demo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
* to be used with AppCompat.
* <p>
* This technique can be used with an {@link android.app.Activity} class, not just
* {@link android.preference.PreferenceActivity}.
*/
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
private AppCompatDelegate mDelegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
public void setSupportActionBar(@Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
}
@Override
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().addContentView(view, params);
}
@Override
protected void onPostResume() {
super.onPostResume();
getDelegate().onPostResume();
}
@Override
protected void onTitleChanged(CharSequence title, int color) {
super.onTitleChanged(title, color);
getDelegate().setTitle(title);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getDelegate().onConfigurationChanged(newConfig);
}
@Override
protected void onStop() {
super.onStop();
getDelegate().onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
public void invalidateOptionsMenu() {
getDelegate().invalidateOptionsMenu();
}
private AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null);
}
return mDelegate;
}
}
package com.baidu.paddle.lite.demo;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CommonActivity extends AppCompatActivity {
private static final String TAG = CommonActivity.class.getSimpleName();
public static final int OPEN_GALLERY_REQUEST_CODE = 0;
public static final int TAKE_PHOTO_REQUEST_CODE = 1;
public static final int REQUEST_LOAD_MODEL = 0;
public static final int REQUEST_RUN_MODEL = 1;
public static final int RESPONSE_LOAD_MODEL_SUCCESSED = 0;
public static final int RESPONSE_LOAD_MODEL_FAILED = 1;
public static final int RESPONSE_RUN_MODEL_SUCCESSED = 2;
public static final int RESPONSE_RUN_MODEL_FAILED = 3;
protected ProgressDialog pbLoadModel = null;
protected ProgressDialog pbRunModel = null;
protected Handler receiver = null; // receive messages from worker thread
protected Handler sender = null; // send command to worker thread
protected HandlerThread worker = null; // worker thread to load&run model
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
receiver = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case RESPONSE_LOAD_MODEL_SUCCESSED:
pbLoadModel.dismiss();
onLoadModelSuccessed();
break;
case RESPONSE_LOAD_MODEL_FAILED:
pbLoadModel.dismiss();
Toast.makeText(CommonActivity.this, "Load model failed!", Toast.LENGTH_SHORT).show();
onLoadModelFailed();
break;
case RESPONSE_RUN_MODEL_SUCCESSED:
pbRunModel.dismiss();
onRunModelSuccessed();
break;
case RESPONSE_RUN_MODEL_FAILED:
pbRunModel.dismiss();
Toast.makeText(CommonActivity.this, "Run model failed!", Toast.LENGTH_SHORT).show();
onRunModelFailed();
break;
default:
break;
}
}
};
worker = new HandlerThread("Predictor Worker");
worker.start();
sender = new Handler(worker.getLooper()) {
public void handleMessage(Message msg) {
switch (msg.what) {
case REQUEST_LOAD_MODEL:
// load model and reload test image
if (onLoadModel()) {
receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_SUCCESSED);
} else {
receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_FAILED);
}
break;
case REQUEST_RUN_MODEL:
// run model if model is loaded
if (onRunModel()) {
receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_SUCCESSED);
} else {
receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_FAILED);
}
break;
default:
break;
}
}
};
}
public void loadModel() {
pbLoadModel = ProgressDialog.show(this, "", "Loading model...", false, false);
sender.sendEmptyMessage(REQUEST_LOAD_MODEL);
}
public void runModel() {
pbRunModel = ProgressDialog.show(this, "", "Running model...", false, false);
sender.sendEmptyMessage(REQUEST_RUN_MODEL);
}
public boolean onLoadModel() {
return true;
}
public boolean onRunModel() {
return true;
}
public void onLoadModelSuccessed() {
}
public void onLoadModelFailed() {
}
public void onRunModelSuccessed() {
}
public void onRunModelFailed() {
}
public void onImageChanged(Bitmap image) {
}
public void onImageChanged(String path) {
}
public void onSettingsClicked() {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_action_options, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
case R.id.open_gallery:
if (requestAllPermissions()) {
openGallery();
}
break;
case R.id.take_photo:
if (requestAllPermissions()) {
takePhoto();
}
break;
case R.id.settings:
if (requestAllPermissions()) {
// make sure we have SDCard r&w permissions to load model from SDCard
onSettingsClicked();
}
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
}
}
private boolean requestAllPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA},
0);
return false;
}
return true;
}
private void openGallery() {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, OPEN_GALLERY_REQUEST_CODE);
}
private void takePhoto() {
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePhotoIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) {
switch (requestCode) {
case OPEN_GALLERY_REQUEST_CODE:
try {
ContentResolver resolver = getContentResolver();
Uri uri = data.getData();
Bitmap image = MediaStore.Images.Media.getBitmap(resolver, uri);
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(uri, proj, null, null, null);
cursor.moveToFirst();
onImageChanged(image);
} catch (IOException e) {
Log.e(TAG, e.toString());
}
break;
case TAKE_PHOTO_REQUEST_CODE:
Bitmap image = (Bitmap) data.getParcelableExtra("data");
onImageChanged(image);
break;
default:
break;
}
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
worker.quit();
super.onDestroy();
}
}
package com.baidu.paddle.lite.demo;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.baidu.paddle.lite.demo.segmentation.ImgSegActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// clear all setting items to avoid app crashing due to the incorrect settings
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.clear();
editor.commit();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.v_img_seg: {
Intent intent = new Intent(MainActivity.this, ImgSegActivity.class);
startActivity(intent);
} break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
System.exit(0);
}
}
package com.baidu.paddle.lite.demo;
import android.content.Context;
import android.util.Log;
import com.baidu.paddle.lite.*;
import java.util.ArrayList;
import java.util.Date;
public class Predictor {
private static final String TAG = Predictor.class.getSimpleName();
public boolean isLoaded = false;
public int warmupIterNum = 0;
public int inferIterNum = 1;
protected Context appCtx = null;
public int cpuThreadNum = 1;
public String cpuPowerMode = "LITE_POWER_HIGH";
public String modelPath = "";
public String modelName = "";
protected PaddlePredictor paddlePredictor = null;
protected float inferenceTime = 0;
public Predictor() {
}
public boolean init(Context appCtx, String modelPath, int cpuThreadNum, String cpuPowerMode) {
this.appCtx = appCtx;
isLoaded = loadModel(modelPath, cpuThreadNum, cpuPowerMode);
return isLoaded;
}
protected boolean loadModel(String modelPath, int cpuThreadNum, String cpuPowerMode) {
// release model if exists
releaseModel();
// load model
if (modelPath.isEmpty()) {
return false;
}
String realPath = modelPath;
if (!modelPath.substring(0, 1).equals("/")) {
// read model files from custom file_paths if the first character of mode file_paths is '/'
// otherwise copy model to cache from assets
realPath = appCtx.getCacheDir() + "/" + modelPath;
Utils.copyDirectoryFromAssets(appCtx, modelPath, realPath);
}
if (realPath.isEmpty()) {
return false;
}
MobileConfig config = new MobileConfig();
config.setModelDir(realPath);
config.setThreads(cpuThreadNum);
if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_HIGH")) {
config.setPowerMode(PowerMode.LITE_POWER_HIGH);
} else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_LOW")) {
config.setPowerMode(PowerMode.LITE_POWER_LOW);
} else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_FULL")) {
config.setPowerMode(PowerMode.LITE_POWER_FULL);
} else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_NO_BIND")) {
config.setPowerMode(PowerMode.LITE_POWER_NO_BIND);
} else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_RAND_HIGH")) {
config.setPowerMode(PowerMode.LITE_POWER_RAND_HIGH);
} else if (cpuPowerMode.equalsIgnoreCase("LITE_POWER_RAND_LOW")) {
config.setPowerMode(PowerMode.LITE_POWER_RAND_LOW);
} else {
Log.e(TAG, "unknown cpu power mode!");
return false;
}
paddlePredictor = PaddlePredictor.createPaddlePredictor(config);
this.cpuThreadNum = cpuThreadNum;
this.cpuPowerMode = cpuPowerMode;
this.modelPath = realPath;
this.modelName = realPath.substring(realPath.lastIndexOf("/") + 1);
return true;
}
public void releaseModel() {
paddlePredictor = null;
isLoaded = false;
cpuThreadNum = 1;
cpuPowerMode = "LITE_POWER_HIGH";
modelPath = "";
modelName = "";
}
public Tensor getInput(int idx) {
if (!isLoaded()) {
return null;
}
return paddlePredictor.getInput(idx);
}
public Tensor getOutput(int idx) {
if (!isLoaded()) {
return null;
}
return paddlePredictor.getOutput(idx);
}
public boolean runModel() {
if (!isLoaded()) {
return false;
}
// warm up
for (int i = 0; i < warmupIterNum; i++){
paddlePredictor.run();
}
// inference
Date start = new Date();
for (int i = 0; i < inferIterNum; i++) {
paddlePredictor.run();
}
Date end = new Date();
inferenceTime = (end.getTime() - start.getTime()) / (float) inferIterNum;
return true;
}
public boolean isLoaded() {
return paddlePredictor != null && isLoaded;
}
public String modelPath() {
return modelPath;
}
public String modelName() {
return modelName;
}
public int cpuThreadNum() {
return cpuThreadNum;
}
public String cpuPowerMode() {
return cpuPowerMode;
}
public float inferenceTime() {
return inferenceTime;
}
}
package com.baidu.paddle.lite.demo;
import android.content.Context;
import android.os.Environment;
import java.io.*;
public class Utils {
private static final String TAG = Utils.class.getSimpleName();
public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
if (srcPath.isEmpty() || dstPath.isEmpty()) {
return;
}
InputStream is = null;
OutputStream os = null;
try {
is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
os = new BufferedOutputStream(new FileOutputStream(new File(dstPath)));
byte[] buffer = new byte[1024];
int length = 0;
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
if (srcDir.isEmpty() || dstDir.isEmpty()) {
return;
}
try {
if (!new File(dstDir).exists()) {
new File(dstDir).mkdirs();
}
for (String fileName : appCtx.getAssets().list(srcDir)) {
String srcSubPath = srcDir + File.separator + fileName;
String dstSubPath = dstDir + File.separator + fileName;
if (new File(srcSubPath).isDirectory()) {
copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
} else {
copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static float[] parseFloatsFromString(String string, String delimiter) {
String[] pieces = string.trim().toLowerCase().split(delimiter);
float[] floats = new float[pieces.length];
for (int i = 0; i < pieces.length; i++) {
floats[i] = Float.parseFloat(pieces[i].trim());
}
return floats;
}
public static long[] parseLongsFromString(String string, String delimiter) {
String[] pieces = string.trim().toLowerCase().split(delimiter);
long[] longs = new long[pieces.length];
for (int i = 0; i < pieces.length; i++) {
longs[i] = Long.parseLong(pieces[i].trim());
}
return longs;
}
public static String getSDCardDirectory() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
public static boolean isSupportedNPU() {
String hardware = android.os.Build.HARDWARE;
return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
}
}
package com.baidu.paddle.lite.demo.segmentation;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.baidu.paddle.lite.demo.CommonActivity;
import com.baidu.paddle.lite.demo.R;
import com.baidu.paddle.lite.demo.Utils;
import com.baidu.paddle.lite.demo.segmentation.config.Config;
import com.baidu.paddle.lite.demo.segmentation.preprocess.Preprocess;
import com.baidu.paddle.lite.demo.segmentation.visual.Visualize;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class ImgSegActivity extends CommonActivity {
private static final String TAG = ImgSegActivity.class.getSimpleName();
protected TextView tvInputSetting;
protected ImageView ivInputImage;
protected TextView tvOutputResult;
protected TextView tvInferenceTime;
// model config
Config config = new Config();
protected ImgSegPredictor predictor = new ImgSegPredictor();
Preprocess preprocess = new Preprocess();
Visualize visualize = new Visualize();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_img_seg);
tvInputSetting = findViewById(R.id.tv_input_setting);
ivInputImage = findViewById(R.id.iv_input_image);
tvInferenceTime = findViewById(R.id.tv_inference_time);
tvOutputResult = findViewById(R.id.tv_output_result);
tvInputSetting.setMovementMethod(ScrollingMovementMethod.getInstance());
tvOutputResult.setMovementMethod(ScrollingMovementMethod.getInstance());
}
@Override
public boolean onLoadModel() {
return super.onLoadModel() && predictor.init(ImgSegActivity.this, config);
}
@Override
public boolean onRunModel() {
return super.onRunModel() && predictor.isLoaded() && predictor.runModel(preprocess,visualize);
}
@Override
public void onLoadModelSuccessed() {
super.onLoadModelSuccessed();
// load test image from file_paths and run model
try {
if (config.imagePath.isEmpty()) {
return;
}
Bitmap image = null;
// read test image file from custom file_paths if the first character of mode file_paths is '/', otherwise read test
// image file from assets
if (!config.imagePath.substring(0, 1).equals("/")) {
InputStream imageStream = getAssets().open(config.imagePath);
image = BitmapFactory.decodeStream(imageStream);
} else {
if (!new File(config.imagePath).exists()) {
return;
}
image = BitmapFactory.decodeFile(config.imagePath);
}
if (image != null && predictor.isLoaded()) {
predictor.setInputImage(image);
runModel();
}
} catch (IOException e) {
Toast.makeText(ImgSegActivity.this, "Load image failed!", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
@Override
public void onLoadModelFailed() {
super.onLoadModelFailed();
}
@Override
public void onRunModelSuccessed() {
super.onRunModelSuccessed();
// obtain results and update UI
tvInferenceTime.setText("Inference time: " + predictor.inferenceTime() + " ms");
Bitmap outputImage = predictor.outputImage();
if (outputImage != null) {
ivInputImage.setImageBitmap(outputImage);
}
tvOutputResult.setText(predictor.outputResult());
tvOutputResult.scrollTo(0, 0);
}
@Override
public void onRunModelFailed() {
super.onRunModelFailed();
}
@Override
public void onImageChanged(Bitmap image) {
super.onImageChanged(image);
// rerun model if users pick test image from gallery or camera
if (image != null && predictor.isLoaded()) {
// predictor.setConfig(config);
predictor.setInputImage(image);
runModel();
}
}
@Override
public void onImageChanged(String path) {
super.onImageChanged(path);
Bitmap image = BitmapFactory.decodeFile(path);
predictor.setInputImage(image);
runModel();
}
public void onSettingsClicked() {
super.onSettingsClicked();
startActivity(new Intent(ImgSegActivity.this, ImgSegSettingsActivity.class));
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isLoaded = predictor.isLoaded();
menu.findItem(R.id.open_gallery).setEnabled(isLoaded);
menu.findItem(R.id.take_photo).setEnabled(isLoaded);
return super.onPrepareOptionsMenu(menu);
}
@Override
protected void onResume() {
Log.i(TAG,"begin onResume");
super.onResume();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean settingsChanged = false;
String model_path = sharedPreferences.getString(getString(R.string.ISG_MODEL_PATH_KEY),
getString(R.string.ISG_MODEL_PATH_DEFAULT));
String label_path = sharedPreferences.getString(getString(R.string.ISG_LABEL_PATH_KEY),
getString(R.string.ISG_LABEL_PATH_DEFAULT));
String image_path = sharedPreferences.getString(getString(R.string.ISG_IMAGE_PATH_KEY),
getString(R.string.ISG_IMAGE_PATH_DEFAULT));
settingsChanged |= !model_path.equalsIgnoreCase(config.modelPath);
settingsChanged |= !label_path.equalsIgnoreCase(config.labelPath);
settingsChanged |= !image_path.equalsIgnoreCase(config.imagePath);
int cpu_thread_num = Integer.parseInt(sharedPreferences.getString(getString(R.string.ISG_CPU_THREAD_NUM_KEY),
getString(R.string.ISG_CPU_THREAD_NUM_DEFAULT)));
settingsChanged |= cpu_thread_num != config.cpuThreadNum;
String cpu_power_mode =
sharedPreferences.getString(getString(R.string.ISG_CPU_POWER_MODE_KEY),
getString(R.string.ISG_CPU_POWER_MODE_DEFAULT));
settingsChanged |= !cpu_power_mode.equalsIgnoreCase(config.cpuPowerMode);
String input_color_format =
sharedPreferences.getString(getString(R.string.ISG_INPUT_COLOR_FORMAT_KEY),
getString(R.string.ISG_INPUT_COLOR_FORMAT_DEFAULT));
settingsChanged |= !input_color_format.equalsIgnoreCase(config.inputColorFormat);
long[] input_shape =
Utils.parseLongsFromString(sharedPreferences.getString(getString(R.string.ISG_INPUT_SHAPE_KEY),
getString(R.string.ISG_INPUT_SHAPE_DEFAULT)), ",");
settingsChanged |= input_shape.length != config.inputShape.length;
if (!settingsChanged) {
for (int i = 0; i < input_shape.length; i++) {
settingsChanged |= input_shape[i] != config.inputShape[i];
}
}
if (settingsChanged) {
config.init(model_path,label_path,image_path,cpu_thread_num,cpu_power_mode,
input_color_format,input_shape);
preprocess.init(config);
// update UI
tvInputSetting.setText("Model: " + config.modelPath.substring(config.modelPath.lastIndexOf("/") + 1) + "\n" + "CPU" +
" Thread Num: " + Integer.toString(config.cpuThreadNum) + "\n" + "CPU Power Mode: " + config.cpuPowerMode);
tvInputSetting.scrollTo(0, 0);
// reload model if configure has been changed
loadModel();
}
}
@Override
protected void onDestroy() {
if (predictor != null) {
predictor.releaseModel();
}
super.onDestroy();
}
}
package com.baidu.paddle.lite.demo.segmentation;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import com.baidu.paddle.lite.Tensor;
import com.baidu.paddle.lite.demo.Predictor;
import com.baidu.paddle.lite.demo.segmentation.config.Config;
import com.baidu.paddle.lite.demo.segmentation.preprocess.Preprocess;
import com.baidu.paddle.lite.demo.segmentation.visual.Visualize;
import java.io.InputStream;
import java.util.Date;
import java.util.Vector;
import static android.graphics.Color.blue;
import static android.graphics.Color.green;
import static android.graphics.Color.red;
public class ImgSegPredictor extends Predictor {
private static final String TAG = ImgSegPredictor.class.getSimpleName();
protected Vector<String> wordLabels = new Vector<String>();
Config config;
protected Bitmap inputImage = null;
protected Bitmap scaledImage = null;
protected Bitmap outputImage = null;
protected String outputResult = "";
protected float preprocessTime = 0;
protected float postprocessTime = 0;
public ImgSegPredictor() {
super();
}
public boolean init(Context appCtx, Config config) {
if (config.inputShape.length != 4) {
Log.i(TAG, "size of input shape should be: 4");
return false;
}
if (config.inputShape[0] != 1) {
Log.i(TAG, "only one batch is supported in the image classification demo, you can use any batch size in " +
"your Apps!");
return false;
}
if (config.inputShape[1] != 1 && config.inputShape[1] != 3) {
Log.i(TAG, "only one/three channels are supported in the image classification demo, you can use any " +
"channel size in your Apps!");
return false;
}
if (!config.inputColorFormat.equalsIgnoreCase("RGB") && !config.inputColorFormat.equalsIgnoreCase("BGR")) {
Log.i(TAG, "only RGB and BGR color format is supported.");
return false;
}
super.init(appCtx, config.modelPath, config.cpuThreadNum, config.cpuPowerMode);
if (!super.isLoaded()) {
return false;
}
this.config = config;
return isLoaded;
}
protected boolean loadLabel(String labelPath) {
wordLabels.clear();
// load word labels from file
try {
InputStream assetsInputStream = appCtx.getAssets().open(labelPath);
int available = assetsInputStream.available();
byte[] lines = new byte[available];
assetsInputStream.read(lines);
assetsInputStream.close();
String words = new String(lines);
String[] contents = words.split("\n");
for (String content : contents) {
wordLabels.add(content);
}
Log.i(TAG, "word label size: " + wordLabels.size());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return false;
}
return true;
}
public Tensor getInput(int idx) {
return super.getInput(idx);
}
public Tensor getOutput(int idx) {
return super.getOutput(idx);
}
public boolean runModel(Bitmap image) {
setInputImage(image);
return runModel();
}
public boolean runModel(Preprocess preprocess, Visualize visualize) {
if (inputImage == null) {
return false;
}
// set input shape
Tensor inputTensor = getInput(0);
inputTensor.resize(config.inputShape);
// pre-process image
Date start = new Date();
preprocess.init(config);
preprocess.to_array(scaledImage);
// feed input tensor with pre-processed data
inputTensor.setData(preprocess.inputData);
Date end = new Date();
preprocessTime = (float) (end.getTime() - start.getTime());
// inference
super.runModel();
Tensor outputTensor = getOutput(0);
// post-process
this.outputImage = visualize.draw(inputImage,outputTensor);
postprocessTime = (float) (end.getTime() - start.getTime());
start = new Date();
outputResult = new String();
end = new Date();
return true;
}
public void setConfig(Config config){
this.config = config;
}
public Bitmap inputImage() {
return inputImage;
}
public Bitmap outputImage() {
return outputImage;
}
public String outputResult() {
return outputResult;
}
public float preprocessTime() {
return preprocessTime;
}
public float postprocessTime() {
return postprocessTime;
}
public void setInputImage(Bitmap image) {
if (image == null) {
return;
}
// scale image to the size of input tensor
Bitmap rgbaImage = image.copy(Bitmap.Config.ARGB_8888, true);
Bitmap scaleImage = Bitmap.createScaledBitmap(rgbaImage, (int) this.config.inputShape[3], (int) this.config.inputShape[2], true);
this.inputImage = rgbaImage;
this.scaledImage = scaleImage;
}
}
package com.baidu.paddle.lite.demo.segmentation;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.support.v7.app.ActionBar;
import com.baidu.paddle.lite.demo.AppCompatPreferenceActivity;
import com.baidu.paddle.lite.demo.R;
import com.baidu.paddle.lite.demo.Utils;
import java.util.ArrayList;
import java.util.List;
public class ImgSegSettingsActivity extends AppCompatPreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
ListPreference lpChoosePreInstalledModel = null;
CheckBoxPreference cbEnableCustomSettings = null;
EditTextPreference etModelPath = null;
EditTextPreference etLabelPath = null;
EditTextPreference etImagePath = null;
ListPreference lpCPUThreadNum = null;
ListPreference lpCPUPowerMode = null;
ListPreference lpInputColorFormat = null;
EditTextPreference etInputShape = null;
EditTextPreference etInputMean = null;
EditTextPreference etInputStd = null;
List<String> preInstalledModelPaths = null;
List<String> preInstalledLabelPaths = null;
List<String> preInstalledImagePaths = null;
List<String> preInstalledInputShapes = null;
List<String> preInstalledCPUThreadNums = null;
List<String> preInstalledCPUPowerModes = null;
List<String> preInstalledInputColorFormats = null;
List<String> preInstalledInputMeans = null;
List<String> preInstalledInputStds = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_img_seg);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
// initialized pre-installed models
preInstalledModelPaths = new ArrayList<String>();
preInstalledLabelPaths = new ArrayList<String>();
preInstalledImagePaths = new ArrayList<String>();
preInstalledInputShapes = new ArrayList<String>();
preInstalledCPUThreadNums = new ArrayList<String>();
preInstalledCPUPowerModes = new ArrayList<String>();
preInstalledInputColorFormats = new ArrayList<String>();
preInstalledInputMeans = new ArrayList<String>();
preInstalledInputStds = new ArrayList<String>();
// add deeplab_mobilenet_for_cpu
preInstalledModelPaths.add(getString(R.string.ISG_MODEL_PATH_DEFAULT));
preInstalledLabelPaths.add(getString(R.string.ISG_LABEL_PATH_DEFAULT));
preInstalledImagePaths.add(getString(R.string.ISG_IMAGE_PATH_DEFAULT));
preInstalledCPUThreadNums.add(getString(R.string.ISG_CPU_THREAD_NUM_DEFAULT));
preInstalledCPUPowerModes.add(getString(R.string.ISG_CPU_POWER_MODE_DEFAULT));
preInstalledInputColorFormats.add(getString(R.string.ISG_INPUT_COLOR_FORMAT_DEFAULT));
preInstalledInputShapes.add(getString(R.string.ISG_INPUT_SHAPE_DEFAULT));
// initialize UI components
lpChoosePreInstalledModel =
(ListPreference) findPreference(getString(R.string.ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY));
String[] preInstalledModelNames = new String[preInstalledModelPaths.size()];
for (int i = 0; i < preInstalledModelPaths.size(); i++) {
preInstalledModelNames[i] =
preInstalledModelPaths.get(i).substring(preInstalledModelPaths.get(i).lastIndexOf("/") + 1);
}
lpChoosePreInstalledModel.setEntries(preInstalledModelNames);
lpChoosePreInstalledModel.setEntryValues(preInstalledModelPaths.toArray(new String[preInstalledModelPaths.size()]));
cbEnableCustomSettings =
(CheckBoxPreference) findPreference(getString(R.string.ISG_ENABLE_CUSTOM_SETTINGS_KEY));
etModelPath = (EditTextPreference) findPreference(getString(R.string.ISG_MODEL_PATH_KEY));
etModelPath.setTitle("Model Path (SDCard: " + Utils.getSDCardDirectory() + ")");
etLabelPath = (EditTextPreference) findPreference(getString(R.string.ISG_LABEL_PATH_KEY));
etImagePath = (EditTextPreference) findPreference(getString(R.string.ISG_IMAGE_PATH_KEY));
lpCPUThreadNum =
(ListPreference) findPreference(getString(R.string.ISG_CPU_THREAD_NUM_KEY));
lpCPUPowerMode =
(ListPreference) findPreference(getString(R.string.ISG_CPU_POWER_MODE_KEY));
lpInputColorFormat =
(ListPreference) findPreference(getString(R.string.ISG_INPUT_COLOR_FORMAT_KEY));
etInputShape = (EditTextPreference) findPreference(getString(R.string.ISG_INPUT_SHAPE_KEY));
}
private void reloadPreferenceAndUpdateUI() {
SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
boolean enableCustomSettings =
sharedPreferences.getBoolean(getString(R.string.ISG_ENABLE_CUSTOM_SETTINGS_KEY), false);
String modelPath = sharedPreferences.getString(getString(R.string.ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY),
getString(R.string.ISG_MODEL_PATH_DEFAULT));
int modelIdx = lpChoosePreInstalledModel.findIndexOfValue(modelPath);
if (modelIdx >= 0 && modelIdx < preInstalledModelPaths.size()) {
if (!enableCustomSettings) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(getString(R.string.ISG_MODEL_PATH_KEY), preInstalledModelPaths.get(modelIdx));
editor.putString(getString(R.string.ISG_LABEL_PATH_KEY), preInstalledLabelPaths.get(modelIdx));
editor.putString(getString(R.string.ISG_IMAGE_PATH_KEY), preInstalledImagePaths.get(modelIdx));
editor.putString(getString(R.string.ISG_CPU_THREAD_NUM_KEY), preInstalledCPUThreadNums.get(modelIdx));
editor.putString(getString(R.string.ISG_CPU_POWER_MODE_KEY), preInstalledCPUPowerModes.get(modelIdx));
editor.putString(getString(R.string.ISG_INPUT_COLOR_FORMAT_KEY),
preInstalledInputColorFormats.get(modelIdx));
editor.putString(getString(R.string.ISG_INPUT_SHAPE_KEY), preInstalledInputShapes.get(modelIdx));
editor.commit();
}
lpChoosePreInstalledModel.setSummary(modelPath);
}
cbEnableCustomSettings.setChecked(enableCustomSettings);
etModelPath.setEnabled(enableCustomSettings);
etLabelPath.setEnabled(enableCustomSettings);
etImagePath.setEnabled(enableCustomSettings);
lpCPUThreadNum.setEnabled(enableCustomSettings);
lpCPUPowerMode.setEnabled(enableCustomSettings);
lpInputColorFormat.setEnabled(enableCustomSettings);
etInputShape.setEnabled(enableCustomSettings);
etInputMean.setEnabled(enableCustomSettings);
etInputStd.setEnabled(enableCustomSettings);
modelPath = sharedPreferences.getString(getString(R.string.ISG_MODEL_PATH_KEY),
getString(R.string.ISG_MODEL_PATH_DEFAULT));
String labelPath = sharedPreferences.getString(getString(R.string.ISG_LABEL_PATH_KEY),
getString(R.string.ISG_LABEL_PATH_DEFAULT));
String imagePath = sharedPreferences.getString(getString(R.string.ISG_IMAGE_PATH_KEY),
getString(R.string.ISG_IMAGE_PATH_DEFAULT));
String cpuThreadNum = sharedPreferences.getString(getString(R.string.ISG_CPU_THREAD_NUM_KEY),
getString(R.string.ISG_CPU_THREAD_NUM_DEFAULT));
String cpuPowerMode = sharedPreferences.getString(getString(R.string.ISG_CPU_POWER_MODE_KEY),
getString(R.string.ISG_CPU_POWER_MODE_DEFAULT));
String inputColorFormat = sharedPreferences.getString(getString(R.string.ISG_INPUT_COLOR_FORMAT_KEY),
getString(R.string.ISG_INPUT_COLOR_FORMAT_DEFAULT));
String inputShape = sharedPreferences.getString(getString(R.string.ISG_INPUT_SHAPE_KEY),
getString(R.string.ISG_INPUT_SHAPE_DEFAULT));
etModelPath.setSummary(modelPath);
etModelPath.setText(modelPath);
etLabelPath.setSummary(labelPath);
etLabelPath.setText(labelPath);
etImagePath.setSummary(imagePath);
etImagePath.setText(imagePath);
lpCPUThreadNum.setValue(cpuThreadNum);
lpCPUThreadNum.setSummary(cpuThreadNum);
lpCPUPowerMode.setValue(cpuPowerMode);
lpCPUPowerMode.setSummary(cpuPowerMode);
lpInputColorFormat.setValue(inputColorFormat);
lpInputColorFormat.setSummary(inputColorFormat);
etInputShape.setSummary(inputShape);
etInputShape.setText(inputShape);
}
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
reloadPreferenceAndUpdateUI();
}
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY))) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(getString(R.string.ISG_ENABLE_CUSTOM_SETTINGS_KEY), false);
editor.commit();
}
reloadPreferenceAndUpdateUI();
}
}
package com.baidu.paddle.lite.demo.segmentation.config;
import android.graphics.Bitmap;
public class Config {
public String modelPath = "";
public String labelPath = "";
public String imagePath = "";
public int cpuThreadNum = 1;
public String cpuPowerMode = "";
public String inputColorFormat = "";
public long[] inputShape = new long[]{};
public void init(String modelPath, String labelPath, String imagePath, int cpuThreadNum,
String cpuPowerMode, String inputColorFormat,long[] inputShape){
this.modelPath = modelPath;
this.labelPath = labelPath;
this.imagePath = imagePath;
this.cpuThreadNum = cpuThreadNum;
this.cpuPowerMode = cpuPowerMode;
this.inputColorFormat = inputColorFormat;
this.inputShape = inputShape;
}
public void setInputShape(Bitmap inputImage){
this.inputShape[0] = 1;
this.inputShape[1] = 3;
this.inputShape[2] = inputImage.getHeight();
this.inputShape[3] = inputImage.getWidth();
}
}
package com.baidu.paddle.lite.demo.segmentation.preprocess;
import android.graphics.Bitmap;
import android.util.Log;
import com.baidu.paddle.lite.demo.segmentation.config.Config;
import static android.graphics.Color.blue;
import static android.graphics.Color.green;
import static android.graphics.Color.red;
public class Preprocess {
private static final String TAG = Preprocess.class.getSimpleName();
Config config;
int channels;
int width;
int height;
public float[] inputData;
public void init(Config config){
this.config = config;
this.channels = (int) config.inputShape[1];
this.height = (int) config.inputShape[2];
this.width = (int) config.inputShape[3];
this.inputData = new float[channels * width * height];
}
public boolean to_array(Bitmap inputImage){
if (channels == 3) {
int[] channelIdx = null;
if (config.inputColorFormat.equalsIgnoreCase("RGB")) {
channelIdx = new int[]{0, 1, 2};
} else if (config.inputColorFormat.equalsIgnoreCase("BGR")) {
channelIdx = new int[]{2, 1, 0};
} else {
Log.i(TAG, "unknown color format " + config.inputColorFormat + ", only RGB and BGR color format is " +
"supported!");
return false;
}
int[] channelStride = new int[]{width * height, width * height * 2};
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int color = inputImage.getPixel(x, y);
float[] rgb = new float[]{(float) red(color) , (float) green(color) ,
(float) blue(color)};
inputData[y * width + x] = rgb[channelIdx[0]] ;
inputData[y * width + x + channelStride[0]] = rgb[channelIdx[1]] ;
inputData[y * width + x + channelStride[1]] = rgb[channelIdx[2]];
}
}
} else if (channels == 1) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int color = inputImage.getPixel(x, y);
float gray = (float) (red(color) + green(color) + blue(color));
inputData[y * width + x] = gray;
}
}
} else {
Log.i(TAG, "unsupported channel size " + Integer.toString(channels) + ", only channel 1 and 3 is " +
"supported!");
return false;
}
return true;
}
}
package com.baidu.paddle.lite.demo.segmentation.visual;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
import com.baidu.paddle.lite.Tensor;
public class Visualize {
private static final String TAG = Visualize.class.getSimpleName();
public Bitmap draw(Bitmap inputImage, Tensor outputTensor){
final int[] colors_map = {0xFF000000, 0xFFFFFF00};
float[] output = outputTensor.getFloatData();
long outputShape[] = outputTensor.shape();
long outputSize = 1;
for (long s : outputShape) {
outputSize *= s;
}
int[] objectColor = new int[(int)outputSize];
for(int i=0;i<output.length;i++){
objectColor[i] = colors_map[(int)output[i]];
}
Bitmap.Config config = inputImage.getConfig();
Bitmap outputImage = null;
if(outputShape.length==3){
outputImage = Bitmap.createBitmap(objectColor, (int)outputShape[2], (int)outputShape[1], config);
outputImage = Bitmap.createScaledBitmap(outputImage, inputImage.getWidth(), inputImage.getHeight(),true);
}
else if (outputShape.length==4){
outputImage = Bitmap.createBitmap(objectColor, (int)outputShape[3], (int)outputShape[2], config);
}
Bitmap bmOverlay = Bitmap.createBitmap(inputImage.getWidth(), inputImage.getHeight() , inputImage.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(inputImage, new Matrix(), null);
Paint paint = new Paint();
paint.setAlpha(0x80);
canvas.drawBitmap(outputImage, 0, 0, paint);
return bmOverlay;
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".segmentation.ImgSegActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/v_input_info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="vertical">
<TextView
android:id="@+id/tv_input_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:lineSpacingExtra="4dp"
android:singleLine="false"
android:maxLines="6"
android:text=""/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/v_output_info"
android:layout_below="@+id/v_input_info">
<ImageView
android:id="@+id/iv_input_image"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/v_output_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true">
<TextView
android:id="@+id/tv_output_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:scrollbars="vertical"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:textAlignment="center"
android:lineSpacingExtra="5dp"
android:singleLine="false"
android:maxLines="5"
android:text=""/>
<TextView
android:id="@+id/tv_inference_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_output_result"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textAlignment="center"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:text=""/>
</RelativeLayout>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadingEdge="vertical"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="300dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/v_img_seg"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:clickable="true"
android:onClick="onClick">
<ImageView
android:id="@+id/iv_img_seg_image"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="12dp"
android:adjustViewBounds="true"
android:src="@drawable/image_segementation"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/iv_img_seg_title"
android:layout_below="@id/iv_img_seg_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="8dp"
android:text="Image Segmentation"
android:textStyle="bold"
android:textAllCaps="false"
android:singleLine="false"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="@+id/pick_image">
<item
android:id="@+id/open_gallery"
android:title="Open Gallery"
app:showAsAction="withText"/>
<item
android:id="@+id/take_photo"
android:title="Take Photo"
app:showAsAction="withText"/>
</group>
<group>
<item
android:id="@+id/settings"
android:title="Settings..."
app:showAsAction="withText"/>
</group>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="cpu_thread_num_entries">
<item>1 threads</item>
<item>2 threads</item>
<item>4 threads</item>
<item>8 threads</item>
</string-array>
<string-array name="cpu_thread_num_values">
<item>1</item>
<item>2</item>
<item>4</item>
<item>8</item>
</string-array>
<string-array name="cpu_power_mode_entries">
<item>HIGH(only big cores)</item>
<item>LOW(only LITTLE cores)</item>
<item>FULL(all cores)</item>
<item>NO_BIND(depends on system)</item>
<item>RAND_HIGH</item>
<item>RAND_LOW</item>
</string-array>
<string-array name="cpu_power_mode_values">
<item>LITE_POWER_HIGH</item>
<item>LITE_POWER_LOW</item>
<item>LITE_POWER_FULL</item>
<item>LITE_POWER_NO_BIND</item>
<item>LITE_POWER_RAND_HIGH</item>
<item>LITE_POWER_RAND_LOW</item>
</string-array>
<string-array name="input_color_format_entries">
<item>BGR color format</item>
<item>RGB color format</item>
</string-array>
<string-array name="input_color_format_values">
<item>BGR</item>
<item>RGB</item>
</string-array>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
<resources>
<string name="app_name">Segmentation-demo</string>
<!-- image segmentation settings -->
<string name="ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY">ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY</string>
<string name="ISG_ENABLE_CUSTOM_SETTINGS_KEY">ISG_ENABLE_CUSTOM_SETTINGS_KEY</string>
<string name="ISG_MODEL_PATH_KEY">ISG_MODEL_PATH_KEY</string>
<string name="ISG_LABEL_PATH_KEY">ISG_LABEL_PATH_KEY</string>
<string name="ISG_IMAGE_PATH_KEY">ISG_IMAGE_PATH_KEY</string>
<string name="ISG_CPU_THREAD_NUM_KEY">ISG_CPU_THREAD_NUM_KEY</string>
<string name="ISG_CPU_POWER_MODE_KEY">ISG_CPU_POWER_MODE_KEY</string>
<string name="ISG_INPUT_COLOR_FORMAT_KEY">ISG_INPUT_COLOR_FORMAT_KEY</string>
<string name="ISG_INPUT_SHAPE_KEY">ISG_INPUT_SHAPE_KEY</string>
<string name="ISG_MODEL_PATH_DEFAULT">image_segmentation/models/deeplab_mobilenet_for_cpu</string>
<string name="ISG_LABEL_PATH_DEFAULT">image_segmentation/labels/label_list</string>
<string name="ISG_IMAGE_PATH_DEFAULT">image_segmentation/images/human.jpg</string>
<string name="ISG_CPU_THREAD_NUM_DEFAULT">1</string>
<string name="ISG_CPU_POWER_MODE_DEFAULT">LITE_POWER_HIGH</string>
<string name="ISG_INPUT_COLOR_FORMAT_DEFAULT">RGB</string>
<string name="ISG_INPUT_SHAPE_DEFAULT">1,3,513,513</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
</style>
<style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
<item name="overlapAnchor">false</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="Model Settings">
<ListPreference
android:defaultValue="@string/ISG_MODEL_PATH_DEFAULT"
android:key="@string/ISG_CHOOSE_PRE_INSTALLED_MODEL_KEY"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="Choose pre-installed models" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/ISG_ENABLE_CUSTOM_SETTINGS_KEY"
android:summaryOn="Enable"
android:summaryOff="Disable"
android:title="Enable custom settings"/>
<EditTextPreference
android:key="@string/ISG_MODEL_PATH_KEY"
android:defaultValue="@string/ISG_MODEL_PATH_DEFAULT"
android:title="Model Path" />
<EditTextPreference
android:key="@string/ISG_LABEL_PATH_KEY"
android:defaultValue="@string/ISG_LABEL_PATH_DEFAULT"
android:title="Label Path" />
<EditTextPreference
android:key="@string/ISG_IMAGE_PATH_KEY"
android:defaultValue="@string/ISG_IMAGE_PATH_DEFAULT"
android:title="Image Path" />
</PreferenceCategory>
<PreferenceCategory android:title="CPU Settings">
<ListPreference
android:defaultValue="@string/ISG_CPU_THREAD_NUM_DEFAULT"
android:key="@string/ISG_CPU_THREAD_NUM_KEY"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="CPU Thread Num"
android:entries="@array/cpu_thread_num_entries"
android:entryValues="@array/cpu_thread_num_values"/>
<ListPreference
android:defaultValue="@string/ISG_CPU_POWER_MODE_DEFAULT"
android:key="@string/ISG_CPU_POWER_MODE_KEY"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="CPU Power Mode"
android:entries="@array/cpu_power_mode_entries"
android:entryValues="@array/cpu_power_mode_values"/>
</PreferenceCategory>
<PreferenceCategory android:title="Input Settings">
<ListPreference
android:defaultValue="@string/ISG_INPUT_COLOR_FORMAT_DEFAULT"
android:key="@string/ISG_INPUT_COLOR_FORMAT_KEY"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="Input Color Format: BGR or RGB"
android:entries="@array/input_color_format_entries"
android:entryValues="@array/input_color_format_values"/>
<EditTextPreference
android:key="@string/ISG_INPUT_SHAPE_KEY"
android:defaultValue="@string/ISG_INPUT_SHAPE_DEFAULT"
android:title="Input Shape: (1,1,h,w) or (1,3,h,w)" />
</PreferenceCategory>
</PreferenceScreen>
package com.baidu.paddle.lite.demo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Thu Aug 22 15:05:37 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
# PaddleSeg Python 预测部署方案
## 1. 说明
本方案旨在提供一个`PaddlePaddle`跨平台图像分割模型的`Python`预测部署方案作为参考,用户通过一定的配置,加上少量的代码,即可把模型集成到自己的服务中,完成图像分割的任务。
如果**硬件支持**(如`Tesla V100 GPU`等),本程序支持使用`Nvidia TensorRT`进行`FP32``FP16`两种精度进行推理性能加速。
## 2. 依赖前置条件
* Python2.7/Python3
## 3. 目录结构和文件说明
```
├── infer.py # 核心代码,完成分割模型的预测以及结果可视化
├── requirements.txt # 依赖的Python包
└── README.md # 说明文档
```
## 4. 环境安装和准备
### 4.1 安装 PaddlePaddle
如何选择合适版本的`PaddlePaddle`版本进行安装,可参考: [PaddlePaddle安装教程](https://www.paddlepaddle.org.cn/install/doc/)
**注意**: 如硬件支持且需要使用`TensorRT`支持`FP16`半精度优化等, 则**本步骤**需要自行安装`TensorRT`并编译`PaddlePaddle`, 点击查看[编译安装参考文档](docs/compile_paddle_with_tensorrt.md)
### 4.2:安装Python依赖包
**当前**目录下, 使用`pip`安装`Python`依赖包
```bash
pip install -r requirements.txt
```
### 4.3 安装`OpenCV` 相关依赖库
预测代码中需要使用`OpenCV`,所以还需要`OpenCV`安装相关的动态链接库。
`Ubuntu`下安装相关链接库:
```bash
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev
```
CentOS 下安装相关链接库:
```bash
yum install -y libXext libSM libXrender
```
## 5. 开始预测
### 5.1 准备模型
请使用[模型导出工具](../../docs/model_export.md) 导出您的模型, 或点击下载我们的[人像分割样例模型](https://bj.bcebos.com/paddleseg/inference/human_freeze_model.zip)用于测试。
模型导出的目录通常包括三个文件:
```
├── model # 模型文件
├── params # 参数文件
└── deploy.yaml # 配置文件,用于C++或Python预测
```
配置文件的主要字段及其含义如下:
```yaml
DEPLOY:
# 是否使用GPU预测
USE_GPU: 1
# 模型和参数文件所在目录路径
MODEL_PATH: "/root/projects/models/deeplabv3p_xception65_humanseg"
# 模型文件名
MODEL_FILENAME: "__model__"
# 参数文件名
PARAMS_FILENAME: "__params__"
# 预测图片的的标准输入尺寸,输入尺寸不一致会做resize
EVAL_CROP_SIZE: (513, 513)
# 均值
MEAN: [0.5, 0.5, 0.5]
# 方差
STD: [0.5, 0.5, 0.5]
# 分类类型数
NUM_CLASSES: 2
# 图片通道数
CHANNELS : 3
# 预测模式,支持 NATIVE 和 ANALYSIS
PREDICTOR_MODE: "ANALYSIS"
# 每次预测的 batch_size
BATCH_SIZE : 3
```
### 5.2 执行预测程序
在终端输入以下命令进行预测:
```bash
python infer.py --conf=/path/to/deploy.yaml --input_dir/path/to/images_directory --use_pr=False
```
参数说明如下:
| 参数 | 是否必须|含义 |
|-------|-------|----------|
| conf | Yes|模型配置的Yaml文件路径 |
| input_dir |Yes| 需要预测的图片目录 |
| use_pr |No|是否使用优化模型,默认为False|
* 优化模型:使用`PaddleSeg 0.3.0`版导出的为优化模型, 此前版本导出的模型即为未优化版本。优化模型把图像的预处理以及后处理部分融入到模型网络中使用`GPU` 完成,相比原来`CPU` 中的处理提升了计算性能。
**注意**: 如果硬件支持且安装的是从源码编译集成`TensorRT``PaddlePaddle`, 则可以使用参数`--trt_mode=fp16` 表示开启`FP16` 精度优化, 使用`trt_mode=fp32` 表示使用`FP32` 精度。
运行后程序会扫描`input_dir` 目录下所有指定格式图片,并生成`预测mask``可视化的结果`
对于图片`a.jpeg`, `预测mask` 存在`a_jpeg.png` 中,而可视化结果则在`a_jpeg_result.png` 中。
输入样例:
![avatar](../cpp/images/humanseg/demo2.jpeg)
输出结果:
![avatar](../cpp/images/humanseg/demo2.jpeg_result.png)
# PaddleSeg 分割模型预测性能测试
## 测试软件环境
- CUDA 9.0
- CUDNN 7.6
- TensorRT-5.1.5
- PaddlePaddle v1.6.1
- Ubuntu 16.04
- GPU: Tesla V100
- CPU:Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz
## 测试方法
- 输入采用 1000张RGB图片,batch_size 统一为 1。
- 重复跑多轮,去掉第一轮预热时间,计后续几轮的平均时间:包括数据拷贝到GPU,预测引擎计算时间,预测结果拷贝回CPU 时间。
- 采用Fluid C++预测引擎
- 测试时开启了 FLAGS_cudnn_exhaustive_search=True,使用exhaustive方式搜索卷积计算算法。
- 对于每个模型,同事测试了`OP`优化模型和原生模型的推理速度, 并分别就是否开启`FP16``FP32`的进行了测试
## 推理速度测试数据
**说明**`OP优化模型`指的是`PaddleSeg 0.3.0`版以后导出的新版模型,把图像的预处理和后处理部分放入 GPU 中进行加速,提高性能。每个模型包含了三种`eval_crop_size``192x192`/`512x512`/`768x768`
<table width="1440">
<tbody>
<tr>
<td rowspan="2" width="432">
<p>模型</p>
</td>
<td colspan="3" width="535">
<p>原始模型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(单位 ms/image)</p>
</td>
<td colspan="3" width="588">
<p>OP 优化模型&nbsp;&nbsp;&nbsp;&nbsp;(单位 ms/image)</p>
</td>
</tr>
<tr>
<td>
<p>Fluid</p>
</td>
<td>
<p>Fluid-TRT FP32</p>
</td>
<td>
<p>Fluid-TRT FP16</p>
</td>
<td>
<p>Fluid</p>
</td>
<td>
<p>Fluid-TRT FP32</p>
</td>
<td>
<p>Fluid-TRT FP16</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_mobilenetv2-1-0_bn_192x192</p>
</td>
<td>
<p>4.717</p>
</td>
<td>
<p>3.085</p>
</td>
<td>
<p>2.607</p>
</td>
<td>
<p>3.705</p>
</td>
<td>
<p>2.09</p>
</td>
<td>
<p>1.775</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_mobilenetv2-1-0_bn_512x512</p>
</td>
<td>
<p>15.848</p>
</td>
<td>
<p>14.243</p>
</td>
<td>
<p>13.699</p>
</td>
<td>
<p>8.284</p>
</td>
<td>
<p>6.972</p>
</td>
<td>
<p>6.013</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_mobilenetv2-1-0_bn_768x768</p>
</td>
<td>
<p>63.148</p>
</td>
<td>
<p>61.133</p>
</td>
<td>
<p>59.262</p>
</td>
<td>
<p>16.242</p>
</td>
<td>
<p>13.624</p>
</td>
<td>
<p>12.018</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_xception65_bn_192x192</p>
</td>
<td>
<p>9.703</p>
</td>
<td>
<p>9.393</p>
</td>
<td>
<p>6.46</p>
</td>
<td>
<p>8.555</p>
</td>
<td>
<p>8.202</p>
</td>
<td>
<p>5.15</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_xception65_bn_512x512</p>
</td>
<td>
<p>30.944</p>
</td>
<td>
<p>30.031</p>
</td>
<td>
<p>20.716</p>
</td>
<td>
<p>23.571</p>
</td>
<td>
<p>22.601</p>
</td>
<td>
<p>13.327</p>
</td>
</tr>
<tr>
<td>
<p>deeplabv3p_xception65_bn_768x768</p>
</td>
<td>
<p>92.109</p>
</td>
<td>
<p>89.338</p>
</td>
<td>
<p>43.342</p>
</td>
<td>
<p>44.341</p>
</td>
<td>
<p>41.945</p>
</td>
<td>
<p>25.486</p>
</td>
</tr>
<tr>
<td>
<p>icnet_bn_192x192</p>
</td>
<td>
<p>5.706</p>
</td>
<td>
<p>5.057</p>
</td>
<td>
<p>4.515</p>
</td>
<td>
<p>4.694</p>
</td>
<td>
<p>4.066</p>
</td>
<td>
<p>3.369</p>
</td>
</tr>
<tr>
<td>
<p>icnet_bn_512x512</p>
</td>
<td>
<p>18.326</p>
</td>
<td>
<p>16.971</p>
</td>
<td>
<p>16.663</p>
</td>
<td>
<p>10.576</p>
</td>
<td>
<p>9.779</p>
</td>
<td>
<p>9.389</p>
</td>
</tr>
<tr>
<td>
<p>icnet_bn_768x768</p>
</td>
<td>
<p>67.542</p>
</td>
<td>
<p>65.436</p>
</td>
<td>
<p>64.197</p>
</td>
<td>
<p>18.464</p>
</td>
<td>
<p>17.881</p>
</td>
<td>
<p>16.958</p>
</td>
</tr>
<tr>
<td>
<p>pspnet101_bn_192x192</p>
</td>
<td>
<p>20.978</p>
</td>
<td>
<p>18.089</p>
</td>
<td>
<p>11.946</p>
</td>
<td>
<p>20.102</p>
</td>
<td>
<p>17.128</p>
</td>
<td>
<p>11.011</p>
</td>
</tr>
<tr>
<td>
<p>pspnet101_bn_512x512</p>
</td>
<td>
<p>72.085</p>
</td>
<td>
<p>71.114</p>
</td>
<td>
<p>43.009</p>
</td>
<td>
<p>64.584</p>
</td>
<td>
<p>63.715</p>
</td>
<td>
<p>35.806</p>
</td>
</tr>
<tr>
<td>
<p>pspnet101_bn_768x768</p>
</td>
<td>
<p>160.552</p>
</td>
<td>
<p>157.791</p>
</td>
<td>
<p>110.544</p>
</td>
<td>
<p>111.996</p>
</td>
<td>
<p>111.22</p>
</td>
<td>
<p>69.646</p>
</td>
</tr>
<tr>
<td>
<p>pspnet50_bn_192x192</p>
</td>
<td>
<p>13.854</p>
</td>
<td>
<p>12.491</p>
</td>
<td>
<p>9.357</p>
</td>
<td>
<p>12.889</p>
</td>
<td>
<p>11.479</p>
</td>
<td>
<p>8.516</p>
</td>
</tr>
<tr>
<td>
<p>pspnet50_bn_512x512</p>
</td>
<td>
<p>55.868</p>
</td>
<td>
<p>55.205</p>
</td>
<td>
<p>39.659</p>
</td>
<td>
<p>48.647</p>
</td>
<td>
<p>48.076</p>
</td>
<td>
<p>32.403</p>
</td>
</tr>
<tr>
<td>
<p>pspnet50_bn_768x768</p>
</td>
<td>
<p>135.268</p>
</td>
<td>
<p>131.268</p>
</td>
<td>
<p>109.732</p>
</td>
<td>
<p>85.167</p>
</td>
<td>
<p>84.615</p>
</td>
<td>
<p>65.483</p>
</td>
</tr>
<tr>
<td>
<p>unet_bn_coco_192x192</p>
</td>
<td>
<p>7.557</p>
</td>
<td>
<p>7.979</p>
</td>
<td>
<p>8.049</p>
</td>
<td>
<p>4.933</p>
</td>
<td>
<p>4.952</p>
</td>
<td>
<p>4.959</p>
</td>
</tr>
<tr>
<td>
<p>unet_bn_coco_512x512</p>
</td>
<td>
<p>37.131</p>
</td>
<td>
<p>36.668</p>
</td>
<td>
<p>36.706</p>
</td>
<td>
<p>26.857</p>
</td>
<td>
<p>26.917</p>
</td>
<td>
<p>26.928</p>
</td>
</tr>
<tr>
<td>
<p>unet_bn_coco_768x768</p>
</td>
<td>
<p>110.578</p>
</td>
<td>
<p>110.031</p>
</td>
<td>
<p>109.979</p>
</td>
<td>
<p>59.118</p>
</td>
<td>
<p>59.173</p>
</td>
<td>
<p>59.124</p>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
## 数据分析
### 1. 新版OP优化模型的加速效果
下图是`PaddleSeg 0.3.0`进行OP优化的模型和原模型的性能数据对比(以512x512 为例):
![OP加速对比](https://paddleseg.bj.bcebos.com/inference/benchmark/op_opt_512x512.png)
`分析`
- 优化模型的加速效果在各模型上都很明显,最高优化效果可达100%
- 模型的 `eval_crop_size`越大,加速效果越明显
### 2. 使用 TensorRT 开启 FP16 和 FP32 优化效果分析
在原始模型上的加速效果:
![优化模型](https://paddleseg.bj.bcebos.com/inference/benchmark/trt_opt_origin_512x512.png)
在优化模型上的加速效果:
![原始模型](https://paddleseg.bj.bcebos.com/inference/benchmark/trt_opt_new_512x512.png)
`分析`
- unet和icnet模型,使用Fluid-TensorRT的加速效果不明显,甚至没有加速。
- deeplabv3p_mobilenetv2模型,Fluid-TensorRT在原生模型的加速效果不明显,仅3%-5%的加速效果。在优化模型的加速效果可以达到20%。
- `deeplabv3_xception``pspnet50``pspnet101`模型,`fp16`加速效果很明显,在`768x768` 的size下加速效果最高可达110%。
### 3. 不同的EVAL_CROP_SIZE对图片想能的影响
`deeplabv3p_xception`上的数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/xception.png)
`deeplabv3p_mobilenet`上的数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/mobilenet.png)
`unet`上的测试数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/unet.png)
`icnet`上的测试数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/unet.png)
`pspnet101`上的测试数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/pspnet101.png)
`pspnet50`上的测试数据对比图:
![xception](https://paddleseg.bj.bcebos.com/inference/benchmark/pspnet50.png)
`分析`
- 对于同一模型,`eval_crop_size`越大,推理速度越慢
- 同一模型,不管是 TensorRT 优化还是 OP 优化,`eval_crop_size`越大效果越明显
# PaddlePaddle 集成TensorRT 编译安装文档
本文以`Ubuntu 16.04` 为例说明如何编译支持`TensorRT``PaddlePaddle`包。
## 1. 确认依赖的基础软件环境
- Python 2.7+ / Python 3.5+
- CUDA 9.0
- CuDNN 7.5
- cmake 3.10
- gcc 4.8.3
## 2. 安装 TensorRT 5.1
请参考`Nvidia`[官方安装教程](https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html)
## 3. 编译 PaddlePaddle
这里假设`Python`版本为`3.7`以及`cuda` `cudnn` `tensorRT`安装路径如下:
```bash
# 假设 cuda 安装路径
/usr/local/cuda-9.0/
# 假设 cudnn 安装路径
/usr/local/cudnn-7.5/
# 假设 tensorRT 安装路径
/usr/local/TensorRT-5.1/
```
那么执行如下命令进行编译(请根据实际情况修改):
```bash
# 下载 Paddle 代码
git clone https://github.com/PaddlePaddle/Paddle.git
# 进入 Paddle 目录
cd Paddle
# 创建编译目录
mkdir build
cd build
# cmake 编译
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCUDNN_ROOT=/usr/local/cudnn-7.5/ \
-DCMAKE_INSTALL_PREFIX=`pwd`/output \
-DWITH_PYTHON=ON \
-DON_INFER=ON \
-DWITH_GPU=ON \
-DCUDA_ARCH_NAME=Auto \
-DTENSORRT_INCLUDE_DIR=/usr/local/TensorRT-5.1.5.0/include \
-DTENSORRT_LIBRARY=/usr/local/TensorRT-5.1.5.0/lib \
-DPY_VERSION=3.7 \
make -j20
make install
```
编译完成后,在`build/python/dist`目录下会生成一个`whl`包,执行下面的命令安装即可:
```bash
pip install -U xxxx.whl
```
## 4. 验证安装
进入 `python`, 执行以下代码:
```python
import paddle.fluid as fluid
fluid.install_check.run_check()
```
如果出现`Your Paddle Fluid is installed succesfully!`,说明您已成功安装。
# coding: utf8
# copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve.
#
# 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 sys
import ast
import time
import gflags
import yaml
import cv2
import numpy as np
import paddle.fluid as fluid
from concurrent.futures import ThreadPoolExecutor, as_completed
gflags.DEFINE_string("conf", default="", help="Configuration File Path")
gflags.DEFINE_string("input_dir", default="", help="Directory of Input Images")
gflags.DEFINE_boolean("use_pr", default=False, help="Use optimized model")
gflags.DEFINE_string("trt_mode", default="", help="Use optimized model")
gflags.DEFINE_string("ext", default=".jpeg|.jpg", help="Input Image File Extensions")
gflags.FLAGS = gflags.FLAGS
# Generate ColorMap for visualization
def generate_colormap(num_classes):
color_map = num_classes * [0, 0, 0]
for i in range(0, num_classes):
j = 0
lab = i
while lab:
color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j))
color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j))
color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j))
j += 1
lab >>= 3
color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)]
return color_map
# Paddle-TRT Precision Map
trt_precision_map = {
"int8": fluid.core.AnalysisConfig.Precision.Int8,
"fp32": fluid.core.AnalysisConfig.Precision.Float32,
"fp16": fluid.core.AnalysisConfig.Precision.Half
}
# scan a directory and get all images with support extensions
def get_images_from_dir(img_dir, support_ext=".jpg|.jpeg"):
if (not os.path.exists(img_dir) or not os.path.isdir(img_dir)):
raise Exception("Image Directory [%s] invalid" % img_dir)
imgs = []
for item in os.listdir(img_dir):
ext = os.path.splitext(item)[1][1:].strip().lower()
if (len(ext) > 0 and ext in support_ext):
item_path = os.path.join(img_dir, item)
imgs.append(item_path)
return imgs
# Deploy Configuration File Parser
class DeployConfig:
def __init__(self, conf_file):
if not os.path.exists(conf_file):
raise Exception('Config file path [%s] invalid!' % conf_file)
with open(conf_file) as fp:
configs = yaml.load(fp, Loader=yaml.FullLoader)
deploy_conf = configs["DEPLOY"]
# 1. get eval_crop_size
self.eval_crop_size = ast.literal_eval(
deploy_conf["EVAL_CROP_SIZE"])
# 2. get mean
self.mean = deploy_conf["MEAN"]
# 3. get std
self.std = deploy_conf["STD"]
# 4. get class_num
self.class_num = deploy_conf["NUM_CLASSES"]
# 5. get paddle model and params file path
self.model_file = os.path.join(deploy_conf["MODEL_PATH"],
deploy_conf["MODEL_FILENAME"])
self.param_file = os.path.join(deploy_conf["MODEL_PATH"],
deploy_conf["PARAMS_FILENAME"])
# 6. use_gpu
self.use_gpu = deploy_conf["USE_GPU"]
# 7. predictor_mode
self.predictor_mode = deploy_conf["PREDICTOR_MODE"]
# 8. batch_size
self.batch_size = deploy_conf["BATCH_SIZE"]
# 9. channels
self.channels = deploy_conf["CHANNELS"]
class ImageReader:
def __init__(self, configs):
self.config = configs
self.threads_pool = ThreadPoolExecutor(configs.batch_size)
# image processing thread worker
def process_worker(self, imgs, idx, use_pr=False):
image_path = imgs[idx]
im = cv2.imread(image_path, -1)
channels = im.shape[2]
ori_h = im.shape[0]
ori_w = im.shape[1]
if channels == 1:
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
channels = im.shape[2]
if channels != 3 and channels != 4:
print("Only support rgb(gray) or rgba image.")
return -1
# resize to eval_crop_size
eval_crop_size = self.config.eval_crop_size
if (ori_h != eval_crop_size[0] or ori_w != eval_crop_size[1]):
im = cv2.resize(
im, eval_crop_size, fx=0, fy=0, interpolation=cv2.INTER_LINEAR)
# if use models with no pre-processing/post-processing op optimizations
if not use_pr:
im_mean = np.array(self.config.mean).reshape((3, 1, 1))
im_std = np.array(self.config.std).reshape((3, 1, 1))
# HWC -> CHW, don't use transpose((2, 0, 1))
im = im.swapaxes(1, 2)
im = im.swapaxes(0, 1)
im = im[:, :, :].astype('float32') / 255.0
im -= im_mean
im /= im_std
im = im[np.newaxis, :, :, :]
info = [image_path, im, (ori_w, ori_h)]
return info
# process multiple images with multithreading
def process(self, imgs, use_pr=False):
imgs_data = []
with ThreadPoolExecutor(max_workers=self.config.batch_size) as exec:
tasks = [
exec.submit(self.process_worker, imgs, idx, use_pr)
for idx in range(len(imgs))
]
for task in as_completed(tasks):
imgs_data.append(task.result())
return imgs_data
class Predictor:
def __init__(self, conf_file):
self.config = DeployConfig(conf_file)
self.image_reader = ImageReader(self.config)
if self.config.predictor_mode == "NATIVE":
predictor_config = fluid.core.NativeConfig()
predictor_config.prog_file = self.config.model_file
predictor_config.param_file = self.config.param_file
predictor_config.use_gpu = self.config.use_gpu
predictor_config.device = 0
predictor_config.fraction_of_gpu_memory = 0
elif self.config.predictor_mode == "ANALYSIS":
predictor_config = fluid.core.AnalysisConfig(
self.config.model_file, self.config.param_file)
if self.config.use_gpu:
predictor_config.enable_use_gpu(100, 0)
predictor_config.switch_ir_optim(True)
if gflags.FLAGS.trt_mode != "":
precision_type = trt_precision_map[gflags.FLAGS.trt_mode]
use_calib = (gflags.FLAGS.trt_mode == "int8")
predictor_config.enable_tensorrt_engine(
workspace_size=1 << 30,
max_batch_size=self.config.batch_size,
min_subgraph_size=40,
precision_mode=precision_type,
use_static=False,
use_calib_mode=use_calib)
else:
predictor_config.disable_gpu()
predictor_config.switch_specify_input_names(True)
predictor_config.enable_memory_optim()
self.predictor = fluid.core.create_paddle_predictor(predictor_config)
def create_tensor(self, inputs, batch_size, use_pr=False):
im_tensor = fluid.core.PaddleTensor()
im_tensor.name = "image"
if not use_pr:
im_tensor.shape = [
batch_size, self.config.channels, self.config.eval_crop_size[1],
self.config.eval_crop_size[0]
]
else:
im_tensor.shape = [
batch_size, self.config.eval_crop_size[1],
self.config.eval_crop_size[0], self.config.channels
]
im_tensor.dtype = fluid.core.PaddleDType.FLOAT32
im_tensor.data = fluid.core.PaddleBuf(inputs.ravel().astype("float32"))
return [im_tensor]
# save prediction results and visualization them
def output_result(self, imgs_data, infer_out, use_pr=False):
for idx in range(len(imgs_data)):
img_name = imgs_data[idx][0]
ori_shape = imgs_data[idx][2]
mask = infer_out[idx]
if not use_pr:
mask = np.argmax(mask, axis=0)
mask = mask.astype('uint8')
mask_png = mask
score_png = mask_png[:, :, np.newaxis]
score_png = np.concatenate([score_png] * 3, axis=2)
# visualization score png
color_map = generate_colormap(self.config.class_num)
for i in range(score_png.shape[0]):
for j in range(score_png.shape[1]):
score_png[i, j] = color_map[score_png[i, j, 0]]
# save the mask
# mask of xxx.jpeg will be saved as xxx_jpeg_mask.png
ext_pos = img_name.rfind(".")
img_name_fix = img_name[:ext_pos] + "_" + img_name[ext_pos + 1:]
mask_save_name = img_name_fix + "_mask.png"
cv2.imwrite(mask_save_name, mask_png, [cv2.CV_8UC1])
# save the visualized result
# result of xxx.jpeg will be saved as xxx_jpeg_result.png
vis_result_name = img_name_fix + "_result.png"
result_png = score_png
# if not use_pr:
result_png = cv2.resize(
result_png,
ori_shape,
fx=0,
fy=0,
interpolation=cv2.INTER_CUBIC)
cv2.imwrite(vis_result_name, result_png, [cv2.CV_8UC1])
print("save result of [" + img_name + "] done.")
def predict(self, images):
# image reader preprocessing time cost
reader_time = 0
# inference time cost
infer_time = 0
# post_processing: generate mask and visualize it
post_time = 0
# total time cost: preprocessing + inference + postprocessing
total_runtime = 0
# record starting time point
total_start = time.time()
batch_size = self.config.batch_size
for i in range(0, len(images), batch_size):
real_batch_size = batch_size
if i + batch_size >= len(images):
real_batch_size = len(images) - i
reader_start = time.time()
img_datas = self.image_reader.process(images[i:i + real_batch_size],
gflags.FLAGS.use_pr)
input_data = np.concatenate([item[1] for item in img_datas])
input_data = self.create_tensor(
input_data, real_batch_size, use_pr=gflags.FLAGS.use_pr)
reader_end = time.time()
infer_start = time.time()
output_data = self.predictor.run(input_data)[0]
infer_end = time.time()
output_data = output_data.as_ndarray()
post_start = time.time()
self.output_result(img_datas, output_data, gflags.FLAGS.use_pr)
post_end = time.time()
reader_time += (reader_end - reader_start)
infer_time += (infer_end - infer_start)
post_time += (post_end - post_start)
# finishing process all images
total_end = time.time()
# compute whole processing time
total_runtime = (total_end - total_start)
print(
"images_num=[%d],preprocessing_time=[%f],infer_time=[%f],postprocessing_time=[%f],total_runtime=[%f]"
% (len(images), reader_time, infer_time, post_time, total_runtime))
def run(deploy_conf, imgs_dir, support_extensions=".jpg|.jpeg"):
# 1. scan and get all images with valid extensions in directory imgs_dir
imgs = get_images_from_dir(imgs_dir)
if len(imgs) == 0:
print("No Image (with extensions : %s) found in [%s]" %
(support_extensions, imgs_dir))
return -1
# 2. create a predictor
seg_predictor = Predictor(deploy_conf)
# 3. do a inference on images
seg_predictor.predict(imgs)
return 0
if __name__ == "__main__":
# 0. parse the arguments
gflags.FLAGS(sys.argv)
if (gflags.FLAGS.conf == "" or gflags.FLAGS.input_dir == ""):
print("Usage: python infer.py --conf=/config/path/to/your/model " +
"--input_dir=/directory/of/your/input/images [--use_pr=True]")
exit(-1)
# set empty to turn off as default
trt_mode = gflags.FLAGS.trt_mode
if (trt_mode != "" and trt_mode not in trt_precision_map):
print(
"Invalid trt_mode [%s], only support[int8, fp16, fp32]" % trt_mode)
exit(-1)
# run inference
run(gflags.FLAGS.conf, gflags.FLAGS.input_dir, gflags.FLAGS.ext)
python-gflags
pyyaml
numpy
opencv-python
futures
\ No newline at end of file
...@@ -5,10 +5,11 @@ MODEL Group存放所有和模型相关的配置,该Group还包含三个子Grou ...@@ -5,10 +5,11 @@ MODEL Group存放所有和模型相关的配置,该Group还包含三个子Grou
* [DeepLabv3p](./model_deeplabv3p_group.md) * [DeepLabv3p](./model_deeplabv3p_group.md)
* [UNet](./model_unet_group.md) * [UNet](./model_unet_group.md)
* [ICNet](./model_icnet_group.md) * [ICNet](./model_icnet_group.md)
* [HRNet](./model_hrnet_group.md)
## `MODEL_NAME` ## `MODEL_NAME`
所选模型,支持`deeplabv3p` `unet` `icnet`种模型 所选模型,支持`deeplabv3p` `unet` `icnet` `hrnet`种模型
### 默认值 ### 默认值
......
# cfg.MODEL.HRNET
MODEL.HRNET 子Group存放所有和HRNet模型相关的配置
## `STAGE2.NUM_MODULES`
HRNet在第二阶段执行modularized block(multi-resolution parallel convolution + multi-resolution fusion)的重复次数
### 默认值
1
<br/>
<br/>
## `STAGE2.NUM_CHANNELS`
HRNet在第二阶段各个分支的通道数
### 默认值
[40, 80]
<br/>
<br/>
## `STAGE3.NUM_MODULES`
HRNet在第三阶段执行modularized block的重复次数
### 默认值
4
<br/>
<br/>
## `STAGE3.NUM_CHANNELS`
HRNet在第三阶段各个分支的通道数
### 默认值
[40, 80, 160]
<br/>
<br/>
## `STAGE4.NUM_MODULES`
HRNet在第四阶段执行modularized block的重复次数
### 默认值
3
<br/>
<br/>
## `STAGE4.NUM_CHANNELS`
HRNet在第四阶段各个分支的通道数
### 默认值
[40, 80, 160, 320]
<br/>
<br/>
\ No newline at end of file
...@@ -13,6 +13,28 @@ SOLVER Group定义所有和训练优化相关的配置 ...@@ -13,6 +13,28 @@ SOLVER Group定义所有和训练优化相关的配置
<br/> <br/>
<br/> <br/>
## `LR_WARMUP`
学习率是否经过warmup过程,如果设置为True,则学习率会从0开始,经过`LR_WARMUP_STEPS`步后线性增长到指定的初始学习率
### 默认值
False
<br/>
<br/>
## `LR_WARMUP_STEPS`
学习率warmup步数
### 默认值
2000
<br/>
<br/>
## `LR_POLICY` ## `LR_POLICY`
学习率的衰减策略,支持`poly` `piecewise` `cosine`三种策略 学习率的衰减策略,支持`poly` `piecewise` `cosine`三种策略
...@@ -22,7 +44,7 @@ SOLVER Group定义所有和训练优化相关的配置 ...@@ -22,7 +44,7 @@ SOLVER Group定义所有和训练优化相关的配置
`poly` `poly`
### 示例 ### 示例
* 当使用`poly`衰减时,假设初始学习率为0.1,训练总步数为10000,则在power分别为`0.4``0.8``1``1.2``1.6`时,衰减曲线如下图: * 当使用`poly`衰减时,假设初始学习率为0.1,训练总步数为10000,则在power分别为`0.4` `0.8` `1` `1.2` `1.6`时,衰减曲线如下图:
* power = 1 衰减曲线为直线 * power = 1 衰减曲线为直线
* power > 1 衰减曲线内凹 * power > 1 衰减曲线内凹
* power < 1 衰减曲线外凸 * power < 1 衰减曲线外凸
...@@ -31,6 +53,12 @@ SOLVER Group定义所有和训练优化相关的配置 ...@@ -31,6 +53,12 @@ SOLVER Group定义所有和训练优化相关的配置
<img src="../imgs/poly_decay_example.png" hspace='10' height="400" width="800"/> <br /> <img src="../imgs/poly_decay_example.png" hspace='10' height="400" width="800"/> <br />
</p> </p>
* 当使用`poly`衰减时,假设初始学习率为0.1,训练总步数为10000,power为`1`,开启了LR_WARMUP,且LR_WARMUP_STEP为2000时,衰减曲线如下图:
<p align="center">
<img src="../imgs/warmup_with_poly_decay_example.png" hspace='10' height="400" width="800"/> <br />
</p>
* 当使用`piecewise`衰减时,假设初始学习率为0.1,GAMMA为0.9,总EPOCH数量为100,DECAY_EPOCH为[10, 20],衰减曲线如下图: * 当使用`piecewise`衰减时,假设初始学习率为0.1,GAMMA为0.9,总EPOCH数量为100,DECAY_EPOCH为[10, 20],衰减曲线如下图:
<p align="center"> <p align="center">
......
docs/imgs/cosine_decay_example.png

18.4 KB | W: | H:

docs/imgs/cosine_decay_example.png

17.1 KB | W: | H:

docs/imgs/cosine_decay_example.png
docs/imgs/cosine_decay_example.png
docs/imgs/cosine_decay_example.png
docs/imgs/cosine_decay_example.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -18,4 +18,4 @@ ...@@ -18,4 +18,4 @@
python pdseg/export_model.py --cfg configs/unet_pet.yaml TEST.TEST_MODEL test/saved_models/unet_pet/final python pdseg/export_model.py --cfg configs/unet_pet.yaml TEST.TEST_MODEL test/saved_models/unet_pet/final
``` ```
预测模型会导出到`freeze_model`目录,用于C++预测的模型配置会导出到`freeze_model/deploy.yaml` 预测模型会导出到`freeze_model`目录,用于`C++`或者`Python`预测的模型配置会导出到`freeze_model/deploy.yaml`
...@@ -22,6 +22,16 @@ PaddleSeg对所有内置的分割模型都提供了公开数据集下的预训 ...@@ -22,6 +22,16 @@ PaddleSeg对所有内置的分割模型都提供了公开数据集下的预训
| Xception65 | ImageNet | [Xception65_pretrained.tgz](https://paddleseg.bj.bcebos.com/models/Xception65_pretrained.tgz) | 80.32%/94.47% | | Xception65 | ImageNet | [Xception65_pretrained.tgz](https://paddleseg.bj.bcebos.com/models/Xception65_pretrained.tgz) | 80.32%/94.47% |
| Xception71 | ImageNet | coming soon | -- | | Xception71 | ImageNet | coming soon | -- |
| 模型 | 数据集合 | 下载地址 | Accuray Top1/5 Error |
|---|---|---|---|
| HRNet_W18 | ImageNet | [hrnet_w18_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w18_imagenet.tar) | 76.92%/93.39% |
| HRNet_W30 | ImageNet | [hrnet_w30_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w30_imagenet.tar) | 78.04%/94.02% |
| HRNet_W32 | ImageNet | [hrnet_w32_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w32_imagenet.tar) | 78.28%/94.24% |
| HRNet_W40 | ImageNet | [hrnet_w40_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w40_imagenet.tar) | 78.77%/94.47% |
| HRNet_W44 | ImageNet | [hrnet_w44_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w44_imagenet.tar) | 79.00%/94.51% |
| HRNet_W48 | ImageNet | [hrnet_w48_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w48_imagenet.tar) | 78.95%/94.42% |
| HRNet_W64 | ImageNet | [hrnet_w64_imagenet.tar](https://paddleseg.bj.bcebos.com/models/hrnet_w64_imagenet.tar) | 79.30%/94.61% |
## COCO预训练模型 ## COCO预训练模型
数据集为COCO实例分割数据集合转换成的语义分割数据集合 数据集为COCO实例分割数据集合转换成的语义分割数据集合
...@@ -46,3 +56,4 @@ train数据集合为Cityscapes训练集合,测试为Cityscapes的验证集合 ...@@ -46,3 +56,4 @@ train数据集合为Cityscapes训练集合,测试为Cityscapes的验证集合
| ICNet/bn | Cityscapes |[icnet_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/icnet_cityscapes.tar.gz) |16|false| 0.6831 | | ICNet/bn | Cityscapes |[icnet_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/icnet_cityscapes.tar.gz) |16|false| 0.6831 |
| PSPNet/bn | Cityscapes |[pspnet50_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/pspnet50_cityscapes.tgz) |16|false| 0.7013 | | PSPNet/bn | Cityscapes |[pspnet50_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/pspnet50_cityscapes.tgz) |16|false| 0.7013 |
| PSPNet/bn | Cityscapes |[pspnet101_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/pspnet101_cityscapes.tgz) |16|false| 0.7734 | | PSPNet/bn | Cityscapes |[pspnet101_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/pspnet101_cityscapes.tgz) |16|false| 0.7734 |
| HRNet_W18/bn | Cityscapes |[hrnet_w18_bn_cityscapes.tgz](https://paddleseg.bj.bcebos.com/models/hrnet_w18_bn_cityscapes.tgz) | 4 | false | 0.7936 |
...@@ -15,6 +15,7 @@ import imghdr ...@@ -15,6 +15,7 @@ import imghdr
import logging import logging
from utils.config import cfg from utils.config import cfg
from reader import pil_imread
def init_global_variable(): def init_global_variable():
...@@ -452,7 +453,7 @@ def check_train_dataset(): ...@@ -452,7 +453,7 @@ def check_train_dataset():
grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name) grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name)
try: try:
img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED) img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED)
grt = cv2_imread(grt_path, cv2.IMREAD_UNCHANGED) grt = pil_imread(grt_path)
except Exception as e: except Exception as e:
imread_failed.append((line, str(e))) imread_failed.append((line, str(e)))
continue continue
...@@ -502,7 +503,7 @@ def check_val_dataset(): ...@@ -502,7 +503,7 @@ def check_val_dataset():
grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name) grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name)
try: try:
img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED) img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED)
grt = cv2_imread(grt_path, cv2.IMREAD_UNCHANGED) grt = pil_imread(grt_path)
except Exception as e: except Exception as e:
imread_failed.append((line, str(e))) imread_failed.append((line, str(e)))
continue continue
...@@ -561,7 +562,7 @@ def check_test_dataset(): ...@@ -561,7 +562,7 @@ def check_test_dataset():
grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name) grt_path = os.path.join(cfg.DATASET.DATA_DIR, grt_name)
try: try:
img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED) img = cv2_imread(img_path, cv2.IMREAD_UNCHANGED)
grt = cv2_imread(grt_path, cv2.IMREAD_UNCHANGED) grt = pil_imread(grt_path)
except Exception as e: except Exception as e:
imread_failed.append((line, str(e))) imread_failed.append((line, str(e)))
continue continue
......
...@@ -111,6 +111,9 @@ def evaluate(cfg, ckpt_dir=None, use_gpu=False, use_mpio=False, **kwargs): ...@@ -111,6 +111,9 @@ def evaluate(cfg, ckpt_dir=None, use_gpu=False, use_mpio=False, **kwargs):
ckpt_dir = cfg.TEST.TEST_MODEL if not ckpt_dir else ckpt_dir ckpt_dir = cfg.TEST.TEST_MODEL if not ckpt_dir else ckpt_dir
if not os.path.exists(ckpt_dir):
raise ValueError('The TEST.TEST_MODEL {} is not found'.format(ckpt_dir))
if ckpt_dir is not None: if ckpt_dir is not None:
print('load test model:', ckpt_dir) print('load test model:', ckpt_dir)
fluid.io.load_params(exe, ckpt_dir, main_program=test_prog) fluid.io.load_params(exe, ckpt_dir, main_program=test_prog)
......
...@@ -52,6 +52,7 @@ def parse_args(): ...@@ -52,6 +52,7 @@ def parse_args():
def export_inference_config(): def export_inference_config():
deploy_cfg = '''DEPLOY: deploy_cfg = '''DEPLOY:
USE_GPU : 1 USE_GPU : 1
USE_PR : 1
MODEL_PATH : "%s" MODEL_PATH : "%s"
MODEL_FILENAME : "%s" MODEL_FILENAME : "%s"
PARAMS_FILENAME : "%s" PARAMS_FILENAME : "%s"
......
...@@ -13,9 +13,7 @@ ...@@ -13,9 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import sys
import struct import struct
import importlib
import paddle.fluid as fluid import paddle.fluid as fluid
import numpy as np import numpy as np
...@@ -26,6 +24,7 @@ from utils.config import cfg ...@@ -26,6 +24,7 @@ from utils.config import cfg
from loss import multi_softmax_with_loss from loss import multi_softmax_with_loss
from loss import multi_dice_loss from loss import multi_dice_loss
from loss import multi_bce_loss from loss import multi_bce_loss
from models.modeling import deeplab, unet, icnet, pspnet, hrnet
class ModelPhase(object): class ModelPhase(object):
...@@ -70,40 +69,23 @@ class ModelPhase(object): ...@@ -70,40 +69,23 @@ class ModelPhase(object):
return False return False
def map_model_name(model_name): def seg_model(image, class_num):
name_dict = { model_name = cfg.MODEL.MODEL_NAME
"unet": "unet.unet", if model_name == 'unet':
"deeplabv3p": "deeplab.deeplabv3p", logits = unet.unet(image, class_num)
"icnet": "icnet.icnet", elif model_name == 'deeplabv3p':
"pspnet": "pspnet.pspnet", logits = deeplab.deeplabv3p(image, class_num)
"hrnet": "hrnet.hrnet" elif model_name == 'icnet':
} logits = icnet.icnet(image, class_num)
if model_name in name_dict.keys(): elif model_name == 'pspnet':
return name_dict[model_name] logits = pspnet.pspnet(image, class_num)
elif model_name == 'hrnet':
logits = hrnet.hrnet(image, class_num)
else: else:
raise Exception( raise Exception(
"unknow model name, only support unet, deeplabv3p, icnet") "unknow model name, only support unet, deeplabv3p, icnet, pspnet, hrnet"
)
return logits
def get_func(func_name):
"""Helper to return a function object by name. func_name must identify a
function in this module or the path to a function relative to the base
'modeling' module.
"""
if func_name == '':
return None
try:
parts = func_name.split('.')
# Refers to a function in this module
if len(parts) == 1:
return globals()[parts[0]]
# Otherwise, assume we're referencing a module under modeling
module_name = 'models.' + '.'.join(parts[:-1])
module = importlib.import_module(module_name)
return getattr(module, parts[-1])
except Exception:
print('Failed to find function: {}'.format(func_name))
return module
def softmax(logit): def softmax(logit):
...@@ -112,6 +94,7 @@ def softmax(logit): ...@@ -112,6 +94,7 @@ def softmax(logit):
logit = fluid.layers.transpose(logit, [0, 3, 1, 2]) logit = fluid.layers.transpose(logit, [0, 3, 1, 2])
return logit return logit
def sigmoid_to_softmax(logit): def sigmoid_to_softmax(logit):
""" """
one channel to two channel one channel to two channel
...@@ -124,6 +107,53 @@ def sigmoid_to_softmax(logit): ...@@ -124,6 +107,53 @@ def sigmoid_to_softmax(logit):
return logit return logit
def export_preprocess(image):
"""导出模型的预处理流程"""
image = fluid.layers.transpose(image, [0, 3, 1, 2])
origin_shape = fluid.layers.shape(image)[-2:]
# 不同AUG_METHOD方法的resize
if cfg.AUG.AUG_METHOD == 'unpadding':
h_fix = cfg.AUG.FIX_RESIZE_SIZE[1]
w_fix = cfg.AUG.FIX_RESIZE_SIZE[0]
image = fluid.layers.resize_bilinear(
image, out_shape=[h_fix, w_fix], align_corners=False, align_mode=0)
elif cfg.AUG.AUG_METHOD == 'rangescaling':
size = cfg.AUG.INF_RESIZE_VALUE
value = fluid.layers.reduce_max(origin_shape)
scale = float(size) / value.astype('float32')
image = fluid.layers.resize_bilinear(
image, scale=scale, align_corners=False, align_mode=0)
# 存储resize后图像shape
valid_shape = fluid.layers.shape(image)[-2:]
# padding到eval_crop_size大小
width = cfg.EVAL_CROP_SIZE[0]
height = cfg.EVAL_CROP_SIZE[1]
pad_target = fluid.layers.assign(
np.array([height, width]).astype('float32'))
up = fluid.layers.assign(np.array([0]).astype('float32'))
down = pad_target[0] - valid_shape[0]
left = up
right = pad_target[1] - valid_shape[1]
paddings = fluid.layers.concat([up, down, left, right])
paddings = fluid.layers.cast(paddings, 'int32')
image = fluid.layers.pad2d(image, paddings=paddings, pad_value=127.5)
# normalize
mean = np.array(cfg.MEAN).reshape(1, len(cfg.MEAN), 1, 1)
mean = fluid.layers.assign(mean.astype('float32'))
std = np.array(cfg.STD).reshape(1, len(cfg.STD), 1, 1)
std = fluid.layers.assign(std.astype('float32'))
image = (image / 255 - mean) / std
# 使后面的网络能通过类似image.shape获取特征图的shape
image = fluid.layers.reshape(
image, shape=[-1, cfg.DATASET.DATA_DIM, height, width])
return image, valid_shape, origin_shape
def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN):
if not ModelPhase.is_valid_phase(phase): if not ModelPhase.is_valid_phase(phase):
raise ValueError("ModelPhase {} is not valid!".format(phase)) raise ValueError("ModelPhase {} is not valid!".format(phase))
...@@ -140,6 +170,18 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): ...@@ -140,6 +170,18 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN):
with fluid.program_guard(main_prog, start_prog): with fluid.program_guard(main_prog, start_prog):
with fluid.unique_name.guard(): with fluid.unique_name.guard():
# 在导出模型的时候,增加图像标准化预处理,减小预测部署时图像的处理流程
# 预测部署时只须对输入图像增加batch_size维度即可
if ModelPhase.is_predict(phase):
origin_image = fluid.layers.data(
name='image',
shape=[-1, -1, -1, cfg.DATASET.DATA_DIM],
dtype='float32',
append_batch_size=False)
image, valid_shape, origin_shape = export_preprocess(
origin_image)
else:
image = fluid.layers.data( image = fluid.layers.data(
name='image', shape=image_shape, dtype='float32') name='image', shape=image_shape, dtype='float32')
label = fluid.layers.data( label = fluid.layers.data(
...@@ -155,31 +197,35 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): ...@@ -155,31 +197,35 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN):
iterable=False, iterable=False,
use_double_buffer=True) use_double_buffer=True)
model_name = map_model_name(cfg.MODEL.MODEL_NAME)
model_func = get_func("modeling." + model_name)
loss_type = cfg.SOLVER.LOSS loss_type = cfg.SOLVER.LOSS
if not isinstance(loss_type, list): if not isinstance(loss_type, list):
loss_type = list(loss_type) loss_type = list(loss_type)
if class_num > 2 and (("dice_loss" in loss_type) or ("bce_loss" in loss_type)): # dice_loss或bce_loss只适用两类分割中
raise Exception("dice loss and bce loss is only applicable to binary classfication") if class_num > 2 and (("dice_loss" in loss_type) or
("bce_loss" in loss_type)):
raise Exception(
"dice loss and bce loss is only applicable to binary classfication"
)
# 在两类分割情况下,当loss函数选择dice_loss或bce_loss的时候,最后logit输出通道数设置为1
if ("dice_loss" in loss_type) or ("bce_loss" in loss_type): if ("dice_loss" in loss_type) or ("bce_loss" in loss_type):
class_num = 1 class_num = 1
if "softmax_loss" in loss_type: if "softmax_loss" in loss_type:
raise Exception("softmax loss can not combine with dice loss or bce loss") raise Exception(
"softmax loss can not combine with dice loss or bce loss"
logits = model_func(image, class_num) )
logits = seg_model(image, class_num)
# 根据选择的loss函数计算相应的损失函数
if ModelPhase.is_train(phase) or ModelPhase.is_eval(phase): if ModelPhase.is_train(phase) or ModelPhase.is_eval(phase):
loss_valid = False loss_valid = False
avg_loss_list = [] avg_loss_list = []
valid_loss = [] valid_loss = []
if "softmax_loss" in loss_type: if "softmax_loss" in loss_type:
weight = cfg.SOLVER.CROSS_ENTROPY_WEIGHT weight = cfg.SOLVER.CROSS_ENTROPY_WEIGHT
avg_loss_list.append(multi_softmax_with_loss(logits, avg_loss_list.append(
label, mask, class_num, weight)) multi_softmax_with_loss(logits, label, mask, class_num, weight))
loss_valid = True loss_valid = True
valid_loss.append("softmax_loss") valid_loss.append("softmax_loss")
if "dice_loss" in loss_type: if "dice_loss" in loss_type:
...@@ -191,13 +237,17 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): ...@@ -191,13 +237,17 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN):
loss_valid = True loss_valid = True
valid_loss.append("bce_loss") valid_loss.append("bce_loss")
if not loss_valid: if not loss_valid:
raise Exception("SOLVER.LOSS: {} is set wrong. it should " raise Exception(
"SOLVER.LOSS: {} is set wrong. it should "
"include one of (softmax_loss, bce_loss, dice_loss) at least" "include one of (softmax_loss, bce_loss, dice_loss) at least"
" example: ['softmax_loss'], ['dice_loss'], ['bce_loss', 'dice_loss']".format(cfg.SOLVER.LOSS)) " example: ['softmax_loss'], ['dice_loss'], ['bce_loss', 'dice_loss']"
.format(cfg.SOLVER.LOSS))
invalid_loss = [x for x in loss_type if x not in valid_loss] invalid_loss = [x for x in loss_type if x not in valid_loss]
if len(invalid_loss) > 0: if len(invalid_loss) > 0:
print("Warning: the loss {} you set is invalid. it will not be included in loss computed.".format(invalid_loss)) print(
"Warning: the loss {} you set is invalid. it will not be included in loss computed."
.format(invalid_loss))
avg_loss = 0 avg_loss = 0
for i in range(0, len(avg_loss_list)): for i in range(0, len(avg_loss_list)):
...@@ -214,11 +264,23 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): ...@@ -214,11 +264,23 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN):
# return image input and logit output for inference graph prune # return image input and logit output for inference graph prune
if ModelPhase.is_predict(phase): if ModelPhase.is_predict(phase):
# 两类分割中,使用dice_loss或bce_loss返回的logit为单通道,进行到两通道的变换
if class_num == 1: if class_num == 1:
logit = sigmoid_to_softmax(logit) logit = sigmoid_to_softmax(logit)
else: else:
logit = softmax(logit) logit = softmax(logit)
return image, logit
# 获取有效部分
logit = fluid.layers.slice(
logit, axes=[2, 3], starts=[0, 0], ends=valid_shape)
logit = fluid.layers.resize_bilinear(
logit,
out_shape=origin_shape,
align_corners=False,
align_mode=0)
logit = fluid.layers.argmax(logit, axis=1)
return origin_image, logit
if class_num == 1: if class_num == 1:
out = sigmoid_to_softmax(logit) out = sigmoid_to_softmax(logit)
......
...@@ -146,7 +146,7 @@ def layer1(input, name=None): ...@@ -146,7 +146,7 @@ def layer1(input, name=None):
name=name + '_' + str(i + 1)) name=name + '_' + str(i + 1))
return conv return conv
def highResolutionNet(input, num_classes): def high_resolution_net(input, num_classes):
channels_2 = cfg.MODEL.HRNET.STAGE2.NUM_CHANNELS channels_2 = cfg.MODEL.HRNET.STAGE2.NUM_CHANNELS
channels_3 = cfg.MODEL.HRNET.STAGE3.NUM_CHANNELS channels_3 = cfg.MODEL.HRNET.STAGE3.NUM_CHANNELS
...@@ -198,7 +198,7 @@ def highResolutionNet(input, num_classes): ...@@ -198,7 +198,7 @@ def highResolutionNet(input, num_classes):
def hrnet(input, num_classes): def hrnet(input, num_classes):
logit = highResolutionNet(input, num_classes) logit = high_resolution_net(input, num_classes)
return logit return logit
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -27,6 +27,7 @@ import numpy as np ...@@ -27,6 +27,7 @@ import numpy as np
import paddle import paddle
import paddle.fluid as fluid import paddle.fluid as fluid
import cv2 import cv2
from PIL import Image
import data_aug as aug import data_aug as aug
from utils.config import cfg from utils.config import cfg
...@@ -34,6 +35,13 @@ from data_utils import GeneratorEnqueuer ...@@ -34,6 +35,13 @@ from data_utils import GeneratorEnqueuer
from models.model_builder import ModelPhase from models.model_builder import ModelPhase
import copy import copy
def pil_imread(file_path):
"""read pseudo-color label"""
im = Image.open(file_path)
return np.asarray(im)
def cv2_imread(file_path, flag=cv2.IMREAD_COLOR): def cv2_imread(file_path, flag=cv2.IMREAD_COLOR):
# resolve cv2.imread open Chinese file path issues on Windows Platform. # resolve cv2.imread open Chinese file path issues on Windows Platform.
return cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), flag) return cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), flag)
...@@ -179,7 +187,7 @@ class SegDataset(object): ...@@ -179,7 +187,7 @@ class SegDataset(object):
if grt_name is not None: if grt_name is not None:
grt_path = os.path.join(src_dir, grt_name) grt_path = os.path.join(src_dir, grt_name)
grt = cv2_imread(grt_path, cv2.IMREAD_GRAYSCALE) grt = pil_imread(grt_path)
else: else:
grt = None grt = None
......
...@@ -18,7 +18,10 @@ import paddle.fluid as fluid ...@@ -18,7 +18,10 @@ import paddle.fluid as fluid
import numpy as np import numpy as np
import importlib import importlib
from utils.config import cfg from utils.config import cfg
from paddle.fluid.contrib.mixed_precision.decorator import OptimizerWithMixedPrecison, decorate, AutoMixedPrecisionLists try:
from paddle.fluid.contrib.mixed_precision.decorator import OptimizerWithMixedPrecison, decorate, AutoMixedPrecisionLists
except:
from paddle.fluid.contrib.mixed_precision.decorator import OptimizerWithMixedPrecision, decorate, AutoMixedPrecisionLists
class Solver(object): class Solver(object):
...@@ -33,8 +36,11 @@ class Solver(object): ...@@ -33,8 +36,11 @@ class Solver(object):
self.total_step = cfg.SOLVER.NUM_EPOCHS * self.step_per_epoch self.total_step = cfg.SOLVER.NUM_EPOCHS * self.step_per_epoch
self.main_prog = main_prog self.main_prog = main_prog
self.start_prog = start_prog self.start_prog = start_prog
self.warmup_step = cfg.SOLVER.LR_WARMUP_STEPS if cfg.SOLVER.LR_WARMUP else -1
self.decay_step = self.total_step - self.warmup_step
self.decay_epochs = cfg.SOLVER.NUM_EPOCHS - self.warmup_step / self.step_per_epoch
def lr_warmup(self, learning_rate, warmup_steps, start_lr, end_lr): def lr_warmup(self, learning_rate, start_lr, end_lr):
linear_step = end_lr - start_lr linear_step = end_lr - start_lr
lr = fluid.layers.tensor.create_global_var( lr = fluid.layers.tensor.create_global_var(
shape=[1], shape=[1],
...@@ -44,11 +50,19 @@ class Solver(object): ...@@ -44,11 +50,19 @@ class Solver(object):
name="learning_rate_warmup") name="learning_rate_warmup")
global_step = fluid.layers.learning_rate_scheduler._decay_step_counter() global_step = fluid.layers.learning_rate_scheduler._decay_step_counter()
warmup_counter = fluid.layers.autoincreased_step_counter(
counter_name='@LR_DECAY_COUNTER_WARMUP_IN_SEG@', begin=1, step=1)
global_counter = fluid.default_main_program().global_block(
).vars['@LR_DECAY_COUNTER@']
warmup_counter = fluid.layers.cast(warmup_counter, 'float32')
with fluid.layers.control_flow.Switch() as switch: with fluid.layers.control_flow.Switch() as switch:
with switch.case(global_step < warmup_steps): with switch.case(warmup_counter <= self.warmup_step):
decayed_lr = start_lr + linear_step * (global_step / warmup_steps) decayed_lr = start_lr + linear_step * (
warmup_counter / self.warmup_step)
fluid.layers.tensor.assign(decayed_lr, lr) fluid.layers.tensor.assign(decayed_lr, lr)
# hold the global_step to 0 during the warm-up phase
fluid.layers.increment(global_counter, value=-1)
with switch.default(): with switch.default():
fluid.layers.tensor.assign(learning_rate, lr) fluid.layers.tensor.assign(learning_rate, lr)
return lr return lr
...@@ -63,12 +77,12 @@ class Solver(object): ...@@ -63,12 +77,12 @@ class Solver(object):
def poly_decay(self): def poly_decay(self):
power = cfg.SOLVER.POWER power = cfg.SOLVER.POWER
decayed_lr = fluid.layers.polynomial_decay( decayed_lr = fluid.layers.polynomial_decay(
cfg.SOLVER.LR, self.total_step, end_learning_rate=0, power=power) cfg.SOLVER.LR, self.decay_step, end_learning_rate=0, power=power)
return decayed_lr return decayed_lr
def cosine_decay(self): def cosine_decay(self):
decayed_lr = fluid.layers.cosine_decay( decayed_lr = fluid.layers.cosine_decay(
cfg.SOLVER.LR, self.step_per_epoch, cfg.SOLVER.NUM_EPOCHS) cfg.SOLVER.LR, self.step_per_epoch, self.decay_epochs)
return decayed_lr return decayed_lr
def get_lr(self, lr_policy): def get_lr(self, lr_policy):
...@@ -83,11 +97,7 @@ class Solver(object): ...@@ -83,11 +97,7 @@ class Solver(object):
"unsupport learning decay policy! only support poly,piecewise,cosine" "unsupport learning decay policy! only support poly,piecewise,cosine"
) )
if cfg.SOLVER.LR_WARMUP: decayed_lr = self.lr_warmup(decayed_lr, 0, cfg.SOLVER.LR)
start_lr = 0
end_lr = cfg.SOLVER.LR
warmup_steps = cfg.SOLVER.LR_WARMUP_STEPS
decayed_lr = self.lr_warmup(decayed_lr, warmup_steps, start_lr, end_lr)
return decayed_lr return decayed_lr
def sgd_optimizer(self, lr_policy, loss): def sgd_optimizer(self, lr_policy, loss):
...@@ -103,16 +113,26 @@ class Solver(object): ...@@ -103,16 +113,26 @@ class Solver(object):
custom_black_list = {"pool2d"} custom_black_list = {"pool2d"}
else: else:
custom_black_list = {} custom_black_list = {}
amp_lists = AutoMixedPrecisionLists(custom_black_list=custom_black_list) amp_lists = AutoMixedPrecisionLists(
custom_black_list=custom_black_list)
assert isinstance(cfg.MODEL.SCALE_LOSS, float) or isinstance(cfg.MODEL.SCALE_LOSS, str), \ assert isinstance(cfg.MODEL.SCALE_LOSS, float) or isinstance(cfg.MODEL.SCALE_LOSS, str), \
"data type of MODEL.SCALE_LOSS must be float or str" "data type of MODEL.SCALE_LOSS must be float or str"
if isinstance(cfg.MODEL.SCALE_LOSS, float): if isinstance(cfg.MODEL.SCALE_LOSS, float):
optimizer = decorate(optimizer, amp_lists=amp_lists, init_loss_scaling=cfg.MODEL.SCALE_LOSS, optimizer = decorate(
optimizer,
amp_lists=amp_lists,
init_loss_scaling=cfg.MODEL.SCALE_LOSS,
use_dynamic_loss_scaling=False) use_dynamic_loss_scaling=False)
else: else:
assert cfg.MODEL.SCALE_LOSS.lower() in ['dynamic'], "if MODEL.SCALE_LOSS is a string,\ assert cfg.MODEL.SCALE_LOSS.lower() in [
'dynamic'
], "if MODEL.SCALE_LOSS is a string,\
must be set as 'DYNAMIC'!" must be set as 'DYNAMIC'!"
optimizer = decorate(optimizer, amp_lists=amp_lists, use_dynamic_loss_scaling=True)
optimizer = decorate(
optimizer,
amp_lists=amp_lists,
use_dynamic_loss_scaling=True)
optimizer.minimize(loss) optimizer.minimize(loss)
return decayed_lr return decayed_lr
......
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse
import glob
import os
import os.path as osp
import sys
import numpy as np
from PIL import Image
from pdseg.vis import get_color_map_list
def parse_args():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument('dir_or_file',
help='input gray label directory or file list path')
parser.add_argument('output_dir',
help='output colorful label directory')
parser.add_argument('--dataset_dir',
help='dataset directory')
parser.add_argument('--file_separator',
help='file list separator')
return parser.parse_args()
def gray2pseudo_color(args):
"""将灰度标注图片转换为伪彩色图片"""
input = args.dir_or_file
output_dir = args.output_dir
if not osp.exists(output_dir):
os.makedirs(output_dir)
print('Creating colorful label directory:', output_dir)
color_map = get_color_map_list(256)
if os.path.isdir(input):
for grt_path in glob.glob(osp.join(input, '*.png')):
print('Converting original label:', grt_path)
basename = osp.basename(grt_path)
im = Image.open(grt_path)
lbl = np.asarray(im)
lbl_pil = Image.fromarray(lbl.astype(np.uint8), mode='P')
lbl_pil.putpalette(color_map)
new_file = osp.join(output_dir, basename)
lbl_pil.save(new_file)
elif os.path.isfile(input):
if args.dataset_dir is None or args.file_separator is None:
print('No dataset_dir or file_separator input!')
sys.exit()
with open(input) as f:
for line in f:
parts = line.strip().split(args.file_separator)
grt_name = parts[1]
grt_path = os.path.join(args.dataset_dir, grt_name)
print('Converting original label:', grt_path)
basename = osp.basename(grt_path)
im = Image.open(grt_path)
lbl = np.asarray(im)
lbl_pil = Image.fromarray(lbl.astype(np.uint8), mode='P')
lbl_pil.putpalette(color_map)
new_file = osp.join(output_dir, basename)
lbl_pil.save(new_file)
else:
print('It\'s neither a dir nor a file')
if __name__ == '__main__':
args = parse_args()
gray2pseudo_color(args)
...@@ -10,9 +10,10 @@ import os.path as osp ...@@ -10,9 +10,10 @@ import os.path as osp
import numpy as np import numpy as np
import PIL.Image import PIL.Image
import labelme import labelme
from pdseg.vis import get_color_map_list
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
...@@ -55,6 +56,8 @@ def main(args): ...@@ -55,6 +56,8 @@ def main(args):
f.writelines('\n'.join(class_names)) f.writelines('\n'.join(class_names))
print('Saved class_names:', out_class_names_file) print('Saved class_names:', out_class_names_file)
color_map = get_color_map_list(256)
for label_file in glob.glob(osp.join(args.input_dir, '*.json')): for label_file in glob.glob(osp.join(args.input_dir, '*.json')):
print('Generating dataset from:', label_file) print('Generating dataset from:', label_file)
with open(label_file) as f: with open(label_file) as f:
...@@ -78,6 +81,8 @@ def main(args): ...@@ -78,6 +81,8 @@ def main(args):
shape = {'label': name, 'points': points, 'shape_type': 'polygon'} shape = {'label': name, 'points': points, 'shape_type': 'polygon'}
data_shapes.append(shape) data_shapes.append(shape)
if 'size' not in data:
continue
data_size = data['size'] data_size = data['size']
img_shape = (data_size['height'], data_size['width'], data_size['depth']) img_shape = (data_size['height'], data_size['width'], data_size['depth'])
...@@ -91,7 +96,8 @@ def main(args): ...@@ -91,7 +96,8 @@ def main(args):
out_png_file += '.png' out_png_file += '.png'
# Assume label ranges [0, 255] for uint8, # Assume label ranges [0, 255] for uint8,
if lbl.min() >= 0 and lbl.max() <= 255: if lbl.min() >= 0 and lbl.max() <= 255:
lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='L') lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='P')
lbl_pil.putpalette(color_map)
lbl_pil.save(out_png_file) lbl_pil.save(out_png_file)
else: else:
raise ValueError( raise ValueError(
......
...@@ -10,9 +10,10 @@ import os.path as osp ...@@ -10,9 +10,10 @@ import os.path as osp
import numpy as np import numpy as np
import PIL.Image import PIL.Image
import labelme import labelme
from pdseg.vis import get_color_map_list
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
...@@ -35,7 +36,6 @@ def main(args): ...@@ -35,7 +36,6 @@ def main(args):
with open(label_file) as f: with open(label_file) as f:
data = json.load(f) data = json.load(f)
for shape in data['shapes']: for shape in data['shapes']:
points = shape['points']
label = shape['label'] label = shape['label']
cls_name = label cls_name = label
if not cls_name in class_names: if not cls_name in class_names:
...@@ -55,6 +55,8 @@ def main(args): ...@@ -55,6 +55,8 @@ def main(args):
f.writelines('\n'.join(class_names)) f.writelines('\n'.join(class_names))
print('Saved class_names:', out_class_names_file) print('Saved class_names:', out_class_names_file)
color_map = get_color_map_list(256)
for label_file in glob.glob(osp.join(args.input_dir, '*.json')): for label_file in glob.glob(osp.join(args.input_dir, '*.json')):
print('Generating dataset from:', label_file) print('Generating dataset from:', label_file)
with open(label_file) as f: with open(label_file) as f:
...@@ -77,7 +79,8 @@ def main(args): ...@@ -77,7 +79,8 @@ def main(args):
out_png_file += '.png' out_png_file += '.png'
# Assume label ranges [0, 255] for uint8, # Assume label ranges [0, 255] for uint8,
if lbl.min() >= 0 and lbl.max() <= 255: if lbl.min() >= 0 and lbl.max() <= 255:
lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='L') lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='P')
lbl_pil.putpalette(color_map)
lbl_pil.save(out_png_file) lbl_pil.save(out_png_file)
else: else:
raise ValueError( raise ValueError(
......
...@@ -24,6 +24,7 @@ os.environ['FLAGS_eager_delete_tensor_gb'] = "0.0" ...@@ -24,6 +24,7 @@ os.environ['FLAGS_eager_delete_tensor_gb'] = "0.0"
import sys import sys
import argparse import argparse
import pprint import pprint
import random
import shutil import shutil
import functools import functools
...@@ -95,6 +96,12 @@ def parse_args(): ...@@ -95,6 +96,12 @@ def parse_args():
help='See utils/config.py for all options', help='See utils/config.py for all options',
default=None, default=None,
nargs=argparse.REMAINDER) nargs=argparse.REMAINDER)
parser.add_argument(
'--enable_ce',
dest='enable_ce',
help='If set True, enable continuous evaluation job.'
'This flag is only used for internal test.',
action='store_true')
return parser.parse_args() return parser.parse_args()
...@@ -179,6 +186,13 @@ def load_checkpoint(exe, program): ...@@ -179,6 +186,13 @@ def load_checkpoint(exe, program):
return begin_epoch return begin_epoch
def update_best_model(ckpt_dir):
best_model_dir = os.path.join(cfg.TRAIN.MODEL_SAVE_DIR, 'best_model')
if os.path.exists(best_model_dir):
shutil.rmtree(best_model_dir)
shutil.copytree(ckpt_dir, best_model_dir)
def print_info(*msg): def print_info(*msg):
if cfg.TRAINER_ID == 0: if cfg.TRAINER_ID == 0:
print(*msg) print(*msg)
...@@ -187,6 +201,9 @@ def print_info(*msg): ...@@ -187,6 +201,9 @@ def print_info(*msg):
def train(cfg): def train(cfg):
startup_prog = fluid.Program() startup_prog = fluid.Program()
train_prog = fluid.Program() train_prog = fluid.Program()
if args.enable_ce:
startup_prog.random_seed = 1000
train_prog.random_seed = 1000
drop_last = True drop_last = True
dataset = SegDataset( dataset = SegDataset(
...@@ -341,6 +358,8 @@ def train(cfg): ...@@ -341,6 +358,8 @@ def train(cfg):
all_step *= (cfg.SOLVER.NUM_EPOCHS - begin_epoch + 1) all_step *= (cfg.SOLVER.NUM_EPOCHS - begin_epoch + 1)
avg_loss = 0.0 avg_loss = 0.0
best_mIoU = 0.0
timer = Timer() timer = Timer()
timer.start() timer.start()
if begin_epoch > cfg.SOLVER.NUM_EPOCHS: if begin_epoch > cfg.SOLVER.NUM_EPOCHS:
...@@ -429,7 +448,8 @@ def train(cfg): ...@@ -429,7 +448,8 @@ def train(cfg):
except Exception as e: except Exception as e:
print(e) print(e)
if epoch % cfg.TRAIN.SNAPSHOT_EPOCH == 0 and cfg.TRAINER_ID == 0: if (epoch % cfg.TRAIN.SNAPSHOT_EPOCH == 0
or epoch == cfg.SOLVER.NUM_EPOCHS) and cfg.TRAINER_ID == 0:
ckpt_dir = save_checkpoint(exe, train_prog, epoch) ckpt_dir = save_checkpoint(exe, train_prog, epoch)
if args.do_eval: if args.do_eval:
...@@ -445,6 +465,14 @@ def train(cfg): ...@@ -445,6 +465,14 @@ def train(cfg):
log_writer.add_scalar('Evaluate/mean_acc', mean_acc, log_writer.add_scalar('Evaluate/mean_acc', mean_acc,
global_step) global_step)
if mean_iou > best_mIoU:
best_mIoU = mean_iou
update_best_model(ckpt_dir)
print_info("Save best model {} to {}, mIoU = {:.4f}".format(
ckpt_dir,
os.path.join(cfg.TRAIN.MODEL_SAVE_DIR, 'best_model'),
mean_iou))
# Use Tensorboard to visualize results # Use Tensorboard to visualize results
if args.use_tb and cfg.DATASET.VIS_FILE_LIST is not None: if args.use_tb and cfg.DATASET.VIS_FILE_LIST is not None:
visualize( visualize(
...@@ -465,6 +493,9 @@ def main(args): ...@@ -465,6 +493,9 @@ def main(args):
cfg.update_from_file(args.cfg_file) cfg.update_from_file(args.cfg_file)
if args.opts: if args.opts:
cfg.update_from_list(args.opts) cfg.update_from_list(args.opts)
if args.enable_ce:
random.seed(0)
np.random.seed(0)
cfg.TRAINER_ID = int(os.getenv("PADDLE_TRAINER_ID", 0)) cfg.TRAINER_ID = int(os.getenv("PADDLE_TRAINER_ID", 0))
cfg.NUM_TRAINERS = int(os.environ.get('PADDLE_TRAINERS_NUM', 1)) cfg.NUM_TRAINERS = int(os.environ.get('PADDLE_TRAINERS_NUM', 1))
......
...@@ -122,6 +122,12 @@ class SegConfig(dict): ...@@ -122,6 +122,12 @@ class SegConfig(dict):
len(self.MODEL.MULTI_LOSS_WEIGHT) != 3: len(self.MODEL.MULTI_LOSS_WEIGHT) != 3:
self.MODEL.MULTI_LOSS_WEIGHT = [1.0, 0.4, 0.16] self.MODEL.MULTI_LOSS_WEIGHT = [1.0, 0.4, 0.16]
if self.AUG.AUG_METHOD not in ['unpadding', 'stepscaling', 'rangescaling']:
raise ValueError(
'AUG.AUG_METHOD config error, only support `unpadding`, `unpadding` and `rangescaling`'
)
def update_from_list(self, config_list): def update_from_list(self, config_list):
if len(config_list) % 2 != 0: if len(config_list) % 2 != 0:
raise ValueError( raise ValueError(
......
...@@ -18,21 +18,19 @@ from __future__ import division ...@@ -18,21 +18,19 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import os import os
# GPU memory garbage collection optimization flags # GPU memory garbage collection optimization flags
os.environ['FLAGS_eager_delete_tensor_gb'] = "0.0" os.environ['FLAGS_eager_delete_tensor_gb'] = "0.0"
import sys import sys
import time
import argparse import argparse
import pprint import pprint
import cv2 import cv2
import numpy as np import numpy as np
import paddle
import paddle.fluid as fluid import paddle.fluid as fluid
from PIL import Image as PILImage from PIL import Image as PILImage
from utils.config import cfg from utils.config import cfg
from metrics import ConfusionMatrix
from reader import SegDataset from reader import SegDataset
from models.model_builder import build_model from models.model_builder import build_model
from models.model_builder import ModelPhase from models.model_builder import ModelPhase
...@@ -54,11 +52,6 @@ def parse_args(): ...@@ -54,11 +52,6 @@ def parse_args():
help='visual save dir', help='visual save dir',
type=str, type=str,
default='visual') default='visual')
parser.add_argument(
'--also_save_raw_results',
dest='also_save_raw_results',
help='whether to save raw result',
action='store_true')
parser.add_argument( parser.add_argument(
'--local_test', '--local_test',
dest='local_test', dest='local_test',
...@@ -80,7 +73,7 @@ def makedirs(directory): ...@@ -80,7 +73,7 @@ def makedirs(directory):
os.makedirs(directory) os.makedirs(directory)
def get_color_map(num_classes): def get_color_map_list(num_classes):
""" Returns the color map for visualizing the segmentation mask, """ Returns the color map for visualizing the segmentation mask,
which can support arbitrary number of classes. which can support arbitrary number of classes.
Args: Args:
...@@ -88,36 +81,20 @@ def get_color_map(num_classes): ...@@ -88,36 +81,20 @@ def get_color_map(num_classes):
Returns: Returns:
The color map The color map
""" """
#color_map = num_classes * 3 * [0] color_map = num_classes * [0, 0, 0]
color_map = num_classes * [[0, 0, 0]]
for i in range(0, num_classes): for i in range(0, num_classes):
j = 0 j = 0
color_map[i] = [0, 0, 0]
lab = i lab = i
while lab: while lab:
color_map[i][0] |= (((lab >> 0) & 1) << (7 - j)) color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j))
color_map[i][1] |= (((lab >> 1) & 1) << (7 - j)) color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j))
color_map[i][2] |= (((lab >> 2) & 1) << (7 - j)) color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j))
j += 1 j += 1
lab >>= 3 lab >>= 3
return color_map return color_map
def colorize(image, shape, color_map):
"""
Convert segment result to color image.
"""
color_map = np.array(color_map).astype("uint8")
# Use OpenCV LUT for color mapping
c1 = cv2.LUT(image, color_map[:, 0])
c2 = cv2.LUT(image, color_map[:, 1])
c3 = cv2.LUT(image, color_map[:, 2])
color_res = np.dstack((c1, c2, c3))
return color_res
def to_png_fn(fn): def to_png_fn(fn):
""" """
Append png as filename postfix Append png as filename postfix
...@@ -131,8 +108,7 @@ def to_png_fn(fn): ...@@ -131,8 +108,7 @@ def to_png_fn(fn):
def visualize(cfg, def visualize(cfg,
vis_file_list=None, vis_file_list=None,
use_gpu=False, use_gpu=False,
vis_dir="visual", vis_dir="visual_predict",
also_save_raw_results=False,
ckpt_dir=None, ckpt_dir=None,
log_writer=None, log_writer=None,
local_test=False, local_test=False,
...@@ -151,7 +127,7 @@ def visualize(cfg, ...@@ -151,7 +127,7 @@ def visualize(cfg,
test_prog = test_prog.clone(for_test=True) test_prog = test_prog.clone(for_test=True)
# Generator full colormap for maximum 256 classes # Generator full colormap for maximum 256 classes
color_map = get_color_map(256) color_map = get_color_map_list(256)
# Get device environment # Get device environment
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace() place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
...@@ -162,11 +138,8 @@ def visualize(cfg, ...@@ -162,11 +138,8 @@ def visualize(cfg,
fluid.io.load_params(exe, ckpt_dir, main_program=test_prog) fluid.io.load_params(exe, ckpt_dir, main_program=test_prog)
save_dir = os.path.join(vis_dir, 'visual_results') save_dir = os.path.join('visual', vis_dir)
makedirs(save_dir) makedirs(save_dir)
if also_save_raw_results:
raw_save_dir = os.path.join(vis_dir, 'raw_results')
makedirs(raw_save_dir)
fetch_list = [pred.name] fetch_list = [pred.name]
test_reader = dataset.batch(dataset.generator, batch_size=1, is_test=True) test_reader = dataset.batch(dataset.generator, batch_size=1, is_test=True)
...@@ -185,7 +158,6 @@ def visualize(cfg, ...@@ -185,7 +158,6 @@ def visualize(cfg,
# Add more comments # Add more comments
res_map = np.squeeze(pred[i, :, :, :]).astype(np.uint8) res_map = np.squeeze(pred[i, :, :, :]).astype(np.uint8)
img_name = img_names[i] img_name = img_names[i]
grt = grts[i]
res_shape = (res_map.shape[0], res_map.shape[1]) res_shape = (res_map.shape[0], res_map.shape[1])
if res_shape[0] != pred_shape[0] or res_shape[1] != pred_shape[1]: if res_shape[0] != pred_shape[0] or res_shape[1] != pred_shape[1]:
res_map = cv2.resize( res_map = cv2.resize(
...@@ -197,28 +169,16 @@ def visualize(cfg, ...@@ -197,28 +169,16 @@ def visualize(cfg,
res_map, (org_shape[1], org_shape[0]), res_map, (org_shape[1], org_shape[0]),
interpolation=cv2.INTER_NEAREST) interpolation=cv2.INTER_NEAREST)
if grt is not None: png_fn = to_png_fn(img_name)
grt = grt[0:valid_shape[0], 0:valid_shape[1]]
grt = cv2.resize(
grt, (org_shape[1], org_shape[0]),
interpolation=cv2.INTER_NEAREST)
png_fn = to_png_fn(img_names[i])
if also_save_raw_results:
raw_fn = os.path.join(raw_save_dir, png_fn)
dirname = os.path.dirname(raw_save_dir)
makedirs(dirname)
cv2.imwrite(raw_fn, res_map)
# colorful segment result visualization # colorful segment result visualization
vis_fn = os.path.join(save_dir, png_fn) vis_fn = os.path.join(save_dir, png_fn)
dirname = os.path.dirname(vis_fn) dirname = os.path.dirname(vis_fn)
makedirs(dirname) makedirs(dirname)
pred_mask = colorize(res_map, org_shapes[i], color_map) pred_mask = PILImage.fromarray(res_map.astype(np.uint8), mode='P')
if grt is not None: pred_mask.putpalette(color_map)
grt = colorize(grt, org_shapes[i], color_map) pred_mask.save(vis_fn)
cv2.imwrite(vis_fn, pred_mask)
img_cnt += 1 img_cnt += 1
print("#{} visualize image path: {}".format(img_cnt, vis_fn)) print("#{} visualize image path: {}".format(img_cnt, vis_fn))
...@@ -228,25 +188,33 @@ def visualize(cfg, ...@@ -228,25 +188,33 @@ def visualize(cfg,
# Calulate epoch from ckpt_dir folder name # Calulate epoch from ckpt_dir folder name
epoch = int(os.path.split(ckpt_dir)[-1]) epoch = int(os.path.split(ckpt_dir)[-1])
print("Tensorboard visualization epoch", epoch) print("Tensorboard visualization epoch", epoch)
pred_mask_np = np.array(pred_mask.convert("RGB"))
log_writer.add_image( log_writer.add_image(
"Predict/{}".format(img_names[i]), "Predict/{}".format(img_name),
pred_mask[..., ::-1], pred_mask_np,
epoch, epoch,
dataformats='HWC') dataformats='HWC')
# Original image # Original image
# BGR->RGB # BGR->RGB
img = cv2.imread( img = cv2.imread(
os.path.join(cfg.DATASET.DATA_DIR, img_names[i]))[..., ::-1] os.path.join(cfg.DATASET.DATA_DIR, img_name))[..., ::-1]
log_writer.add_image( log_writer.add_image(
"Images/{}".format(img_names[i]), "Images/{}".format(img_name),
img, img,
epoch, epoch,
dataformats='HWC') dataformats='HWC')
#add ground truth (label) images # add ground truth (label) images
grt = grts[i]
if grt is not None: if grt is not None:
grt = grt[0:valid_shape[0], 0:valid_shape[1]]
grt_pil = PILImage.fromarray(grt.astype(np.uint8), mode='P')
grt_pil.putpalette(color_map)
grt_pil = grt_pil.resize((org_shape[1], org_shape[0]))
grt = np.array(grt_pil.convert("RGB"))
log_writer.add_image( log_writer.add_image(
"Label/{}".format(img_names[i]), "Label/{}".format(img_name),
grt[..., ::-1], grt,
epoch, epoch,
dataformats='HWC') dataformats='HWC')
......
...@@ -37,6 +37,20 @@ model_urls = { ...@@ -37,6 +37,20 @@ model_urls = {
"https://paddleseg.bj.bcebos.com/models/Xception41_pretrained.tgz", "https://paddleseg.bj.bcebos.com/models/Xception41_pretrained.tgz",
"xception65_imagenet": "xception65_imagenet":
"https://paddleseg.bj.bcebos.com/models/Xception65_pretrained.tgz", "https://paddleseg.bj.bcebos.com/models/Xception65_pretrained.tgz",
"hrnet_w18_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w18_imagenet.tar",
"hrnet_w30_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w30_imagenet.tar",
"hrnet_w32_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w32_imagenet.tar" ,
"hrnet_w40_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w40_imagenet.tar",
"hrnet_w44_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w44_imagenet.tar",
"hrnet_w48_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w48_imagenet.tar",
"hrnet_w64_bn_imagenet":
"https://paddleseg.bj.bcebos.com/models/hrnet_w64_imagenet.tar",
# COCO pretrained # COCO pretrained
"deeplabv3p_mobilenetv2-1-0_bn_coco": "deeplabv3p_mobilenetv2-1-0_bn_coco":
...@@ -65,6 +79,8 @@ model_urls = { ...@@ -65,6 +79,8 @@ model_urls = {
"https://paddleseg.bj.bcebos.com/models/pspnet50_cityscapes.tgz", "https://paddleseg.bj.bcebos.com/models/pspnet50_cityscapes.tgz",
"pspnet101_bn_cityscapes": "pspnet101_bn_cityscapes":
"https://paddleseg.bj.bcebos.com/models/pspnet101_cityscapes.tgz", "https://paddleseg.bj.bcebos.com/models/pspnet101_cityscapes.tgz",
"hrnet_w18_bn_cityscapes":
"https://paddleseg.bj.bcebos.com/models/hrnet_w18_bn_cityscapes.tgz",
} }
if __name__ == "__main__": if __name__ == "__main__":
......
# HRNet模型训练教程
* 本教程旨在介绍如何通过使用PaddleSeg提供的 ***`HRNet`*** 预训练模型在自定义数据集上进行训练。
* 在阅读本教程前,请确保您已经了解过PaddleSeg的[快速入门](../README.md#快速入门)[基础功能](../README.md#基础功能)等章节,以便对PaddleSeg有一定的了解
* 本教程的所有命令都基于PaddleSeg主目录进行执行
## 一. 准备待训练数据
我们提前准备好了一份数据集,通过以下代码进行下载
```shell
python dataset/download_pet.py
```
## 二. 下载预训练模型
关于PaddleSeg支持的所有预训练模型的列表,我们可以从[模型组合](#模型组合)中查看我们所需模型的名字和配置
接着下载对应的预训练模型
```shell
python pretrained_model/download_model.py hrnet_w18_bn_cityscapes
```
## 三. 准备配置
接着我们需要确定相关配置,从本教程的角度,配置分为三部分:
* 数据集
* 训练集主目录
* 训练集文件列表
* 测试集文件列表
* 评估集文件列表
* 预训练模型
* 预训练模型名称
* 预训练模型各阶段通道数设置
* 预训练模型的Normalization类型
* 预训练模型路径
* 其他
* 学习率
* Batch大小
* ...
在三者中,预训练模型的配置尤为重要,如果模型配置错误,会导致预训练的参数没有加载,进而影响收敛速度。预训练模型相关的配置如第二步所展示。
数据集的配置和数据路径有关,在本教程中,数据存放在`dataset/mini_pet`
其他配置则根据数据集和机器环境的情况进行调节,最终我们保存一个如下内容的yaml配置文件,存放路径为**configs/hrnet_w18_pet.yaml**
```yaml
# 数据集配置
DATASET:
DATA_DIR: "./dataset/mini_pet/"
NUM_CLASSES: 3
TEST_FILE_LIST: "./dataset/mini_pet/file_list/test_list.txt"
TRAIN_FILE_LIST: "./dataset/mini_pet/file_list/train_list.txt"
VAL_FILE_LIST: "./dataset/mini_pet/file_list/val_list.txt"
VIS_FILE_LIST: "./dataset/mini_pet/file_list/test_list.txt"
# 预训练模型配置
MODEL:
MODEL_NAME: "hrnet"
DEFAULT_NORM_TYPE: "bn"
HRNET:
STAGE2:
NUM_CHANNELS: [18, 36]
STAGE3:
NUM_CHANNELS: [18, 36, 72]
STAGE4:
NUM_CHANNELS: [18, 36, 72, 144]
# 其他配置
TRAIN_CROP_SIZE: (512, 512)
EVAL_CROP_SIZE: (512, 512)
AUG:
AUG_METHOD: "unpadding"
FIX_RESIZE_SIZE: (512, 512)
BATCH_SIZE: 4
TRAIN:
PRETRAINED_MODEL_DIR: "./pretrained_model/hrnet_w18_bn_cityscapes/"
MODEL_SAVE_DIR: "./saved_model/hrnet_w18_bn_pet/"
SNAPSHOT_EPOCH: 10
TEST:
TEST_MODEL: "./saved_model/hrnet_w18_bn_pet/final"
SOLVER:
NUM_EPOCHS: 100
LR: 0.005
LR_POLICY: "poly"
OPTIMIZER: "sgd"
```
## 四. 配置/数据校验
在开始训练和评估之前,我们还需要对配置和数据进行一次校验,确保数据和配置是正确的。使用下述命令启动校验流程
```shell
python pdseg/check.py --cfg ./configs/hrnet_w18_pet.yaml
```
## 五. 开始训练
校验通过后,使用下述命令启动训练
```shell
python pdseg/train.py --use_gpu --cfg ./configs/hrnet_w18_pet.yaml
```
## 六. 进行评估
模型训练完成,使用下述命令启动评估
```shell
python pdseg/eval.py --use_gpu --cfg ./configs/hrnet_w18_pet.yaml
```
## 模型组合
|预训练模型名称|BackBone|Norm Type|数据集|配置|
|-|-|-|-|-|
|hrnet_w18_bn_cityscapes|-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [18, 36] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [18, 36, 72] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [18, 36, 72, 144] <br> MODEL.DEFAULT_NORM_TYPE: bn|
| hrnet_w18_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [18, 36] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [18, 36, 72] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [18, 36, 72, 144] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w30_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [30, 60] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [30, 60, 120] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [30, 60, 120, 240] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w32_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [32, 64] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [32, 64, 128] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [32, 64, 128, 256] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w40_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [40, 80] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [40, 80, 160] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [40, 80, 160, 320] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w44_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [44, 88] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [44, 88, 176] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [44, 88, 176, 352] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w48_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [48, 96] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [48, 96, 192] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [48, 96, 192, 384] <br> MODEL.DEFAULT_NORM_TYPE: bn |
| hrnet_w64_bn_imagenet |-|bn| ImageNet | MODEL.MODEL_NAME: hrnet <br> MODEL.HRNET.STAGE2.NUM_CHANNELS: [64, 128] <br> MODEL.HRNET.STAGE3.NUM_CHANNELS: [64, 128, 256] <br> MODEL.HRNET.STAGE4.NUM_CHANNELS: [64, 128, 256, 512] <br> MODEL.DEFAULT_NORM_TYPE: bn |
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册