Browse Source

fix conflicts

FlyingQianMM 5 years ago
parent
commit
3dd571dd02
100 changed files with 2504 additions and 563 deletions
  1. 1 2
      .github/ISSUE_TEMPLATE/1_data.md
  2. 5 1
      .github/ISSUE_TEMPLATE/2_train.md
  3. 6 1
      .github/ISSUE_TEMPLATE/3_deploy.md
  4. 4 1
      .github/ISSUE_TEMPLATE/4_gui.md
  5. 7 0
      .github/ISSUE_TEMPLATE/5_other.md
  6. 7 3
      README.md
  7. 19 31
      deploy/cpp/CMakeLists.txt
  8. 6 0
      deploy/cpp/demo/classifier.cpp
  9. 6 0
      deploy/cpp/demo/detector.cpp
  10. 6 0
      deploy/cpp/demo/segmenter.cpp
  11. 6 0
      deploy/cpp/demo/video_classifier.cpp
  12. 6 0
      deploy/cpp/demo/video_detector.cpp
  13. 6 0
      deploy/cpp/demo/video_segmenter.cpp
  14. 17 2
      deploy/cpp/include/paddlex/paddlex.h
  15. 2 2
      deploy/cpp/include/paddlex/results.h
  16. 33 2
      deploy/cpp/include/paddlex/transforms.h
  17. 28 1
      deploy/cpp/scripts/bootstrap.sh
  18. 9 5
      deploy/cpp/scripts/build.sh
  19. 102 59
      deploy/cpp/src/paddlex.cpp
  20. 68 20
      deploy/cpp/src/transforms.cpp
  21. 9 11
      deploy/cpp/src/visualize.cpp
  22. 2 10
      deploy/openvino/CMakeLists.txt
  23. 1 6
      deploy/openvino/CMakeSettings.json
  24. 3 3
      deploy/openvino/demo/classifier.cpp
  25. 4 5
      deploy/openvino/demo/detector.cpp
  26. 2 4
      deploy/openvino/demo/segmenter.cpp
  27. 30 0
      deploy/openvino/include/paddlex/transforms.h
  28. 26 30
      deploy/openvino/python/converter.py
  29. 7 7
      deploy/openvino/python/deploy.py
  30. 10 5
      deploy/openvino/python/transforms/ops.py
  31. 197 72
      deploy/openvino/python/transforms/seg_transforms.py
  32. 0 3
      deploy/openvino/scripts/build.sh
  33. 1 12
      deploy/openvino/scripts/install_third-party.sh
  34. 37 29
      deploy/openvino/src/paddlex.cpp
  35. 84 22
      deploy/openvino/src/transforms.cpp
  36. 4 4
      deploy/openvino/src/visualize.cpp
  37. 3 4
      deploy/raspberry/CMakeLists.txt
  38. 0 1
      deploy/raspberry/cmake/yaml-cpp.cmake
  39. 3 3
      deploy/raspberry/demo/classifier.cpp
  40. 4 5
      deploy/raspberry/demo/detector.cpp
  41. 2 2
      deploy/raspberry/demo/segmenter.cpp
  42. 1 11
      deploy/raspberry/python/demo.py
  43. 222 0
      deploy/raspberry/python/deploy.py
  44. 0 2
      deploy/raspberry/scripts/build.sh
  45. 1 12
      deploy/raspberry/scripts/install_third-party.sh
  46. 10 13
      deploy/raspberry/src/paddlex.cpp
  47. 3 3
      deploy/raspberry/src/transforms.cpp
  48. 48 0
      docs/apis/analysis.md
  49. 21 0
      docs/apis/datasets.md
  50. 2 4
      docs/apis/deploy.md
  51. 1 0
      docs/apis/index.rst
  52. 4 0
      docs/apis/interpret.md
  53. 2 3
      docs/apis/models/classification.md
  54. 3 6
      docs/apis/models/detection.md
  55. 1 2
      docs/apis/models/instance_segmentation.md
  56. 39 7
      docs/apis/models/semantic_segmentation.md
  57. 24 11
      docs/apis/transforms/seg_transforms.md
  58. 4 3
      docs/apis/visualize.md
  59. 85 30
      docs/appendix/how_to_offline_run.md
  60. 1 1
      docs/appendix/index.rst
  61. 2 2
      docs/appendix/parameters.md
  62. 21 2
      docs/change_log.md
  63. 26 0
      docs/data/annotation.md
  64. 53 0
      docs/data/format/change_detection.md
  65. 1 0
      docs/data/format/index.rst
  66. 6 6
      docs/deploy/hub_serving.md
  67. 3 0
      docs/deploy/index.rst
  68. 1 1
      docs/deploy/nvidia-jetson.md
  69. 69 0
      docs/deploy/opencv.md
  70. 14 9
      docs/deploy/openvino/export_openvino_model.md
  71. 7 6
      docs/deploy/openvino/introduction.md
  72. 7 9
      docs/deploy/openvino/linux.md
  73. 9 8
      docs/deploy/openvino/windows.md
  74. 6 6
      docs/deploy/raspberry/Raspberry.md
  75. 1 1
      docs/deploy/raspberry/index.rst
  76. 5 4
      docs/deploy/raspberry/python.md
  77. 135 0
      docs/deploy/server/cpp/api.md
  78. 1 0
      docs/deploy/server/cpp/index.rst
  79. 13 10
      docs/deploy/server/cpp/linux.md
  80. 12 8
      docs/deploy/server/cpp/windows.md
  81. 2 2
      docs/deploy/server/encryption.md
  82. 100 0
      docs/examples/change_detection.md
  83. 3 0
      docs/examples/index.rst
  84. 5 6
      docs/examples/meter_reader.md
  85. 125 0
      docs/examples/multi-channel_remote_sensing/README.md
  86. 121 0
      docs/examples/multi-channel_remote_sensing/analysis.md
  87. 82 0
      docs/examples/remote_sensing.md
  88. 0 5
      docs/examples/remote_sensing/index.rst
  89. 1 1
      docs/gui/download.md
  90. 11 1
      docs/gui/faq.md
  91. 2 0
      docs/gui/how_to_use.md
  92. BIN
      docs/gui/images/LIME.png
  93. BIN
      docs/gui/images/paddlexoverview.png
  94. 107 0
      examples/change_detection/README.md
  95. 72 0
      examples/change_detection/eval.py
  96. BIN
      examples/change_detection/images/change_det_data.jpg
  97. BIN
      examples/change_detection/images/change_det_prediction.jpg
  98. 43 0
      examples/change_detection/predict.py
  99. 130 0
      examples/change_detection/prepara_data.py
  100. 63 0
      examples/change_detection/train.py

+ 1 - 2
.github/ISSUE_TEMPLATE/1_data.md

@@ -2,5 +2,4 @@
 name: 1. 数据类问题
 about: 数据标注、格式转换等问题
 ---
-
-说明数据类型(图像分类、目标检测、实例分割或语义分割)
+数据类型:请说明你的数据类型,如图像分类、目标检测、实例分割或语义分割

+ 5 - 1
.github/ISSUE_TEMPLATE/2_train.md

@@ -3,4 +3,8 @@ name: 2. 模型训练
 about: 模型训练中的问题
 ---
 
-如模型训练出错,建议贴上模型训练代码,以便开发人员分析,并快速响应
+问题类型:模型训练  
+**问题描述**  
+
+====================  
+请在这里描述您在使用过程中的问题,如模型训练出错,建议贴上模型训练代码,以便开发人员分析,并快速响应

+ 6 - 1
.github/ISSUE_TEMPLATE/3_deploy.md

@@ -3,4 +3,9 @@ name: 3. 模型部署
 about: 模型部署相关问题,包括C++、Python、Paddle Lite等
 ---
 
-说明您的部署环境,部署需求,模型类型和应用场景等,便于开发人员快速响应。
+问题类型:模型部署
+**问题描述**  
+
+========================  
+
+请在这里描述您在使用过程中的问题,说明您的部署环境,部署需求,模型类型和应用场景等,便于开发人员快速响应。

+ 4 - 1
.github/ISSUE_TEMPLATE/4_gui.md

@@ -2,5 +2,8 @@
 name: 4. PaddleX GUI使用问题
 about: Paddle GUI客户端使用问题
 ---
+问题类型:PaddleX GUI  
+**问题描述**  
 
-PaddleX GUI: https://www.paddlepaddle.org.cn/paddle/paddleX (请在ISSUE内容中保留此行内容)
+===================================  
+请在这里描述您在使用GUI过程中的问题

+ 7 - 0
.github/ISSUE_TEMPLATE/5_other.md

@@ -2,3 +2,10 @@
 name: 5. 其它类型问题
 about: 所有问题都可以在这里提
 ---
+
+问题类型:其它  
+**问题描述**  
+
+========================
+
+请在这里描述您的问题

+ 7 - 3
README.md

@@ -14,10 +14,13 @@
 ![support os](https://img.shields.io/badge/os-linux%2C%20win%2C%20mac-yellow.svg)
 ![QQGroup](https://img.shields.io/badge/QQ_Group-1045148026-52B6EF?style=social&logo=tencent-qq&logoColor=000&logoWidth=20)
 
+[完整PaddleX在线使用文档目录](https://paddlex.readthedocs.io/zh_CN/develop/index.html)
+
 集成飞桨智能视觉领域**图像分类**、**目标检测**、**语义分割**、**实例分割**任务能力,将深度学习开发全流程从**数据准备**、**模型训练与优化**到**多端部署**端到端打通,并提供**统一任务API接口**及**图形化开发界面Demo**。开发者无需分别安装不同套件,以**低代码**的形式即可快速完成飞桨全流程开发。
 
 **PaddleX** 经过**质检**、**安防**、**巡检**、**遥感**、**零售**、**医疗**等十多个行业实际应用场景验证,沉淀产业实际经验,**并提供丰富的案例实践教程**,全程助力开发者产业实践落地。
 
+![](./docs/gui/images/paddlexoverview.png)
 
 
 ## 安装
@@ -29,7 +32,7 @@
    通过简洁易懂的Python API,在兼顾功能全面性、开发灵活性、集成方便性的基础上,给开发者最流畅的深度学习开发体验。<br>
 
   **前置依赖**
-> - paddlepaddle >= 1.8.0
+> - paddlepaddle >= 1.8.4
 > - python >= 3.6
 > - cython
 > - pycocotools
@@ -44,10 +47,11 @@ pip install paddlex -i https://mirror.baidu.com/pypi/simple
 
    无代码开发的可视化客户端,应用Paddle API实现,使开发者快速进行产业项目验证,并为用户开发自有深度学习软件/应用提供参照。
 
-- 前往[PaddleX官网](https://www.paddlepaddle.org.cn/paddle/paddlex),申请下载Paddle X GUI一键绿色安装包。
+- 前往[PaddleX官网](https://www.paddlepaddle.org.cn/paddle/paddlex),申请下载PaddleX GUI一键绿色安装包。
 
 - 前往[PaddleX GUI使用教程](./docs/gui/how_to_use.md)了解PaddleX GUI使用详情。
 
+- [PaddleX GUI安装环境说明](./docs/gui/download.md)
   
 
 ## 产品模块说明
@@ -112,7 +116,7 @@ pip install paddlex -i https://mirror.baidu.com/pypi/simple
 ## 更新日志
 
 > [历史版本及更新内容](https://paddlex.readthedocs.io/zh_CN/develop/change_log.html)
-
+- 2020.09.05 v1.2.0
 - 2020.07.13 v1.1.0
 - 2020.07.12 v1.0.8
 - 2020.05.20 v1.0.0

+ 19 - 31
deploy/cpp/CMakeLists.txt

@@ -228,7 +228,7 @@ endif()
 if (NOT WIN32)
     set(DEPS ${DEPS}
         ${MATH_LIB} ${MKLDNN_LIB}
-        glog gflags protobuf z xxhash yaml-cpp
+        glog gflags protobuf xxhash yaml-cpp
         )
     if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib")
         set(DEPS ${DEPS} snappystream)
@@ -320,46 +320,34 @@ target_link_libraries(video_segmenter ${DEPS})
 
 if (WIN32 AND WITH_MKL)
     add_custom_command(TARGET classifier POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/mkldnn.dll
     )
     add_custom_command(TARGET detector POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/mkldnn.dll
     )
     add_custom_command(TARGET segmenter POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/mkldnn.dll
     )
     add_custom_command(TARGET video_classifier POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/mkldnn.dll
     )
     add_custom_command(TARGET video_detector POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/kldnn.dll
     )
     add_custom_command(TARGET video_segmenter POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/Release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/Release/libiomp5md.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/Release/mkldnn.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./paddlex_inference/mklml.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./paddlex_inference/libiomp5md.dll
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./paddlex_inference/mkldnn.dll
     )
     # for encryption
     if (EXISTS "${ENCRYPTION_DIR}/lib/pmodel-decrypt.dll")

+ 6 - 0
deploy/cpp/demo/classifier.cpp

@@ -29,6 +29,10 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(key, "", "key of encryption");
 DEFINE_string(image, "", "Path of test image file");
@@ -56,6 +60,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
 

+ 6 - 0
deploy/cpp/demo/detector.cpp

@@ -31,6 +31,10 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(key, "", "key of encryption");
 DEFINE_string(image, "", "Path of test image file");
@@ -61,6 +65,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
   int imgs = 1;

+ 6 - 0
deploy/cpp/demo/segmenter.cpp

@@ -30,6 +30,10 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(key, "", "key of encryption");
 DEFINE_string(image, "", "Path of test image file");
@@ -58,6 +62,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
   int imgs = 1;

+ 6 - 0
deploy/cpp/demo/video_classifier.cpp

@@ -35,8 +35,12 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(key, "", "key of encryption");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_bool(use_camera, false, "Infering with Camera");
 DEFINE_int32(camera_id, 0, "Camera id");
 DEFINE_string(video_path, "", "Path of input video");
@@ -62,6 +66,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
 

+ 6 - 0
deploy/cpp/demo/video_detector.cpp

@@ -35,6 +35,7 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_bool(use_camera, false, "Infering with Camera");
 DEFINE_int32(camera_id, 0, "Camera id");
@@ -42,6 +43,9 @@ DEFINE_string(video_path, "", "Path of input video");
 DEFINE_bool(show_result, false, "show the result of each frame with a window");
 DEFINE_bool(save_result, true, "save the result of each frame to a video");
 DEFINE_string(key, "", "key of encryption");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_string(save_dir, "output", "Path to save visualized image");
 DEFINE_double(threshold,
               0.5,
@@ -64,6 +68,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
   // Open video

+ 6 - 0
deploy/cpp/demo/video_segmenter.cpp

@@ -35,8 +35,12 @@ using namespace std::chrono;  // NOLINT
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_trt, false, "Infering with TensorRT");
+DEFINE_bool(use_mkl, true, "Infering with MKL");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(key, "", "key of encryption");
+DEFINE_int32(mkl_thread_num,
+             omp_get_num_procs(),
+             "Number of mkl threads");
 DEFINE_bool(use_camera, false, "Infering with Camera");
 DEFINE_int32(camera_id, 0, "Camera id");
 DEFINE_string(video_path, "", "Path of input video");
@@ -62,6 +66,8 @@ int main(int argc, char** argv) {
   model.Init(FLAGS_model_dir,
              FLAGS_use_gpu,
              FLAGS_use_trt,
+             FLAGS_use_mkl,
+             FLAGS_mkl_thread_num,
              FLAGS_gpu_id,
              FLAGS_key);
   // Open video

+ 17 - 2
deploy/cpp/include/paddlex/paddlex.h

@@ -70,6 +70,8 @@ class Model {
    * @param model_dir: the directory which contains model.yml
    * @param use_gpu: use gpu or not when infering
    * @param use_trt: use Tensor RT or not when infering
+   * @param use_mkl: use mkl or not when infering
+   * @param mkl_thread_num: number of threads for mkldnn when infering
    * @param gpu_id: the id of gpu when infering with using gpu
    * @param key: the key of encryption when using encrypted model
    * @param use_ir_optim: use ir optimization when infering
@@ -77,15 +79,26 @@ class Model {
   void Init(const std::string& model_dir,
             bool use_gpu = false,
             bool use_trt = false,
+            bool use_mkl = true,
+            int mkl_thread_num = 4,
             int gpu_id = 0,
             std::string key = "",
             bool use_ir_optim = true) {
-    create_predictor(model_dir, use_gpu, use_trt, gpu_id, key, use_ir_optim);
+    create_predictor(
+                     model_dir,
+                     use_gpu,
+                     use_trt,
+                     use_mkl,
+                     mkl_thread_num,
+                     gpu_id,
+                     key,
+                     use_ir_optim);
   }
-
   void create_predictor(const std::string& model_dir,
                         bool use_gpu = false,
                         bool use_trt = false,
+                        bool use_mkl = true,
+                        int mkl_thread_num = 4,
                         int gpu_id = 0,
                         std::string key = "",
                         bool use_ir_optim = true);
@@ -219,5 +232,7 @@ class Model {
   std::vector<float> outputs_;
   // a predictor which run the model predicting
   std::unique_ptr<paddle::PaddlePredictor> predictor_;
+  // input channel
+  int input_channel_;
 };
 }  // namespace PaddleX

+ 2 - 2
deploy/cpp/include/paddlex/results.h

@@ -37,7 +37,7 @@ struct Mask {
 };
 
 /*
- * @brief 
+ * @brief
  * This class represents target box in detection or instance segmentation tasks.
  * */
 struct Box {
@@ -47,7 +47,7 @@ struct Box {
   // confidence score
   float score;
   std::vector<float> coordinate;
-  Mask<float> mask;
+  Mask<int> mask;
 };
 
 /*

+ 33 - 2
deploy/cpp/include/paddlex/transforms.h

@@ -21,6 +21,7 @@
 #include <unordered_map>
 #include <utility>
 #include <vector>
+#include <iostream>
 
 #include <opencv2/core/core.hpp>
 #include <opencv2/highgui/highgui.hpp>
@@ -81,6 +82,16 @@ class Normalize : public Transform {
   virtual void Init(const YAML::Node& item) {
     mean_ = item["mean"].as<std::vector<float>>();
     std_ = item["std"].as<std::vector<float>>();
+    if (item["min_val"].IsDefined()) {
+      min_val_ = item["min_val"].as<std::vector<float>>();
+    } else {
+      min_val_ = std::vector<float>(mean_.size(), 0.);
+    }
+    if (item["max_val"].IsDefined()) {
+      max_val_ = item["max_val"].as<std::vector<float>>();
+    } else {
+      max_val_ = std::vector<float>(mean_.size(), 255.);
+    }
   }
 
   virtual bool Run(cv::Mat* im, ImageBlob* data);
@@ -88,6 +99,8 @@ class Normalize : public Transform {
  private:
   std::vector<float> mean_;
   std::vector<float> std_;
+  std::vector<float> min_val_;
+  std::vector<float> max_val_;
 };
 
 /*
@@ -216,8 +229,7 @@ class Padding : public Transform {
     }
     if (item["im_padding_value"].IsDefined()) {
       im_value_ = item["im_padding_value"].as<std::vector<float>>();
-    }
-    else {
+    } else {
       im_value_ = {0, 0, 0};
     }
   }
@@ -229,6 +241,25 @@ class Padding : public Transform {
   int height_ = 0;
   std::vector<float> im_value_;
 };
+
+/*
+ * @brief
+ * This class execute clip operation on image matrix
+ * */
+class Clip : public Transform {
+ public:
+  virtual void Init(const YAML::Node& item) {
+    min_val_ = item["min_val"].as<std::vector<float>>();
+    max_val_ = item["max_val"].as<std::vector<float>>();
+  }
+
+  virtual bool Run(cv::Mat* im, ImageBlob* data);
+
+ private:
+  std::vector<float> min_val_;
+  std::vector<float> max_val_;
+};
+
 /*
  * @brief
  * This class is transform operations manager. It stores all neccessary

+ 28 - 1
deploy/cpp/scripts/bootstrap.sh

@@ -8,10 +8,37 @@ fi
 
 # download pre-compiled opencv lib
 OPENCV_URL=https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2
+{
+    system_name=`awk -F= '/^NAME/{print $2}' /etc/os-release `
+} || {
+    echo "[ERROR] There's some problems, maybe caused by your system is not Ubuntu, refer this doc for more informat: https://github.com/PaddlePaddle/PaddleX/tree/develop/docs/deploy/opencv.md"
+    exit -1
+}
+
+# download pre-compiled opencv lib
+OPENCV_URL=https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2
+if [ $system_name == '"Ubuntu"' ]
+then
+    system_version=`awk -F= '/^VERSION_ID/{print $2}' /etc/os-release `
+    if [ $system_version == '"18.04"' ]
+    then
+        OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/opencv3.4.6gcc4.8ffmpeg_ubuntu_18.04.tar.gz2
+    elif [ $system_version == '"16.04"' ]
+    then
+        OPENCV_URL=https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2
+    else
+        echo "[ERROR] Cannot find pre-comipled opencv lib for your system environment, refer this doc for more information: https://github.com/PaddlePaddle/PaddleX/tree/develop/docs/deploy/opencv.md"
+        exit -1
+    fi
+else
+    echo "[ERROR] Cannot find pre-comipled opencv lib for your system environment, refer this doc for more information: https://github.com/PaddlePaddle/PaddleX/tree/develop/docs/deploy/opencv.md"
+    exit -1
+fi
+
 if [ ! -d "./deps/opencv3.4.6gcc4.8ffmpeg/" ]; then
     mkdir -p deps
     cd deps
-    wget -c ${OPENCV_URL}
+    wget -c ${OPENCV_URL} -O opencv3.4.6gcc4.8ffmpeg.tar.gz2
     tar xvfj opencv3.4.6gcc4.8ffmpeg.tar.gz2
     rm -rf opencv3.4.6gcc4.8ffmpeg.tar.gz2
     cd ..

+ 9 - 5
deploy/cpp/scripts/build.sh

@@ -5,9 +5,9 @@ WITH_MKL=ON
 # 是否集成 TensorRT(仅WITH_GPU=ON 有效)
 WITH_TENSORRT=OFF
 # TensorRT 的路径,如果需要集成TensorRT,需修改为您实际安装的TensorRT路径
-TENSORRT_DIR=/root/projects/TensorRT/
+TENSORRT_DIR=$(pwd)/TensorRT/
 # Paddle 预测库路径, 请修改为您实际安装的预测库路径
-PADDLE_DIR=/root/projects/fluid_inference
+PADDLE_DIR=$(pwd)/fluid_inference
 # Paddle 的预测库是否使用静态库来编译
 # 使用TensorRT时,Paddle的预测库通常为动态库
 WITH_STATIC_LIB=OFF
@@ -16,14 +16,18 @@ CUDA_LIB=/usr/local/cuda/lib64
 # CUDNN 的 lib 路径
 CUDNN_LIB=/usr/local/cuda/lib64
 
+{
+    bash $(pwd)/scripts/bootstrap.sh # 下载预编译版本的加密工具和opencv依赖库
+} || {
+    echo "Fail to execute script/bootstrap.sh"
+    exit -1
+}
+
 # 是否加载加密后的模型
 WITH_ENCRYPTION=ON
 # 加密工具的路径, 如果使用自带预编译版本可不修改
-sh $(pwd)/scripts/bootstrap.sh # 下载预编译版本的加密工具
 ENCRYPTION_DIR=$(pwd)/paddlex-encryption
-
 # OPENCV 路径, 如果使用自带预编译版本可不修改
-sh $(pwd)/scripts/bootstrap.sh  # 下载预编译版本的opencv
 OPENCV_DIR=$(pwd)/deps/opencv3.4.6gcc4.8ffmpeg/
 
 # 以下无需改动

+ 102 - 59
deploy/cpp/src/paddlex.cpp

@@ -11,16 +11,25 @@
 // 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.
+
+#include <math.h>
 #include <omp.h>
 #include <algorithm>
 #include <fstream>
 #include <cstring>
 #include "include/paddlex/paddlex.h"
+
+#include <opencv2/core/core.hpp>
+#include <opencv2/highgui/highgui.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+
 namespace PaddleX {
 
 void Model::create_predictor(const std::string& model_dir,
                              bool use_gpu,
                              bool use_trt,
+                             bool use_mkl,
+                             int mkl_thread_num,
                              int gpu_id,
                              std::string key,
                              bool use_ir_optim) {
@@ -40,7 +49,7 @@ void Model::create_predictor(const std::string& model_dir,
   }
 #endif
   if (yaml_input == "") {
-    // 读取配置文件
+    // read yaml file
     std::ifstream yaml_fin(yaml_file);
     yaml_fin.seekg(0, std::ios::end);
     size_t yaml_file_size = yaml_fin.tellg();
@@ -48,7 +57,7 @@ void Model::create_predictor(const std::string& model_dir,
     yaml_fin.seekg(0);
     yaml_fin.read(&yaml_input[0], yaml_file_size);
   }
-  // 读取配置文件内容
+  // load yaml file
   if (!load_config(yaml_input)) {
     std::cerr << "Parse file 'model.yml' failed!" << std::endl;
     exit(-1);
@@ -57,6 +66,15 @@ void Model::create_predictor(const std::string& model_dir,
   if (key == "") {
     config.SetModel(model_file, params_file);
   }
+  if (use_mkl && !use_gpu) {
+    if (name != "HRNet" && name != "DeepLabv3p" && name != "PPYOLO") {
+        config.EnableMKLDNN();
+        config.SetCpuMathLibraryNumThreads(mkl_thread_num);
+    } else {
+        std::cerr << "HRNet/DeepLabv3p/PPYOLO are not supported "
+                  << "for the use of mkldnn" << std::endl;
+    }
+  }
   if (use_gpu) {
     config.EnableUseGpu(100, gpu_id);
   } else {
@@ -64,15 +82,15 @@ void Model::create_predictor(const std::string& model_dir,
   }
   config.SwitchUseFeedFetchOps(false);
   config.SwitchSpecifyInputNames(true);
-  // 开启图优化
+  // enable graph Optim
 #if defined(__arm__) || defined(__aarch64__)
   config.SwitchIrOptim(false);
 #else
   config.SwitchIrOptim(use_ir_optim);
 #endif
-  // 开启内存优化
+  // enable Memory Optim
   config.EnableMemoryOptim();
-  if (use_trt) {
+  if (use_trt && use_gpu) {
     config.EnableTensorRtEngine(
         1 << 20 /* workspace_size*/,
         32 /* max_batch_size*/,
@@ -108,14 +126,19 @@ bool Model::load_config(const std::string& yaml_input) {
       return false;
     }
   }
-  // 构建数据处理流
+  // build data preprocess stream
   transforms_.Init(config["Transforms"], to_rgb);
-  // 读入label list
+  // read label list
   labels.clear();
   for (const auto& item : config["_Attributes"]["labels"]) {
     int index = labels.size();
     labels[index] = item.as<std::string>();
   }
+  if (config["_init_params"]["input_channel"].IsDefined()) {
+    input_channel_ = config["_init_params"]["input_channel"].as<int>();
+  } else {
+    input_channel_ = 3;
+  }
   return true;
 }
 
@@ -152,19 +175,19 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
                  "to function predict()!" << std::endl;
     return false;
   }
-  // 处理输入图像
+  // im preprocess
   if (!preprocess(im, &inputs_)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
-  // 使用加载的模型进行预测
+  // predict
   auto in_tensor = predictor_->GetInputTensor("image");
   int h = inputs_.new_im_size_[0];
   int w = inputs_.new_im_size_[1];
-  in_tensor->Reshape({1, 3, h, w});
+  in_tensor->Reshape({1, input_channel_, h, w});
   in_tensor->copy_from_cpu(inputs_.im_data_.data());
   predictor_->ZeroCopyRun();
-  // 取出模型的输出结果
+  // get result
   auto output_names = predictor_->GetOutputNames();
   auto output_tensor = predictor_->GetOutputTensor(output_names[0]);
   std::vector<int> output_shape = output_tensor->shape();
@@ -174,7 +197,7 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
   }
   outputs_.resize(size);
   output_tensor->copy_to_cpu(outputs_.data());
-  // 对模型输出结果进行后处理
+  // postprocess
   auto ptr = std::max_element(std::begin(outputs_), std::end(outputs_));
   result->category_id = std::distance(std::begin(outputs_), ptr);
   result->score = *ptr;
@@ -198,27 +221,27 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
     return false;
   }
   inputs_batch_.assign(im_batch.size(), ImageBlob());
-  // 处理输入图像
+  // preprocess
   if (!preprocess(im_batch, &inputs_batch_, thread_num)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
-  // 使用加载的模型进行预测
+  // predict
   int batch_size = im_batch.size();
   auto in_tensor = predictor_->GetInputTensor("image");
   int h = inputs_batch_[0].new_im_size_[0];
   int w = inputs_batch_[0].new_im_size_[1];
-  in_tensor->Reshape({batch_size, 3, h, w});
-  std::vector<float> inputs_data(batch_size * 3 * h * w);
+  in_tensor->Reshape({batch_size, input_channel_, h, w});
+  std::vector<float> inputs_data(batch_size * input_channel_ * h * w);
   for (int i = 0; i < batch_size; ++i) {
     std::copy(inputs_batch_[i].im_data_.begin(),
               inputs_batch_[i].im_data_.end(),
-              inputs_data.begin() + i * 3 * h * w);
+              inputs_data.begin() + i * input_channel_ * h * w);
   }
   in_tensor->copy_from_cpu(inputs_data.data());
   // in_tensor->copy_from_cpu(inputs_.im_data_.data());
   predictor_->ZeroCopyRun();
-  // 取出模型的输出结果
+  // get result
   auto output_names = predictor_->GetOutputNames();
   auto output_tensor = predictor_->GetOutputTensor(output_names[0]);
   std::vector<int> output_shape = output_tensor->shape();
@@ -228,7 +251,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
   }
   outputs_.resize(size);
   output_tensor->copy_to_cpu(outputs_.data());
-  // 对模型输出结果进行后处理
+  // postprocess
   (*results).clear();
   (*results).resize(batch_size);
   int single_batch_size = size / batch_size;
@@ -258,7 +281,7 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     return false;
   }
 
-  // 处理输入图像
+  // preprocess
   if (!preprocess(im, &inputs_)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
@@ -267,10 +290,10 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
   int h = inputs_.new_im_size_[0];
   int w = inputs_.new_im_size_[1];
   auto im_tensor = predictor_->GetInputTensor("image");
-  im_tensor->Reshape({1, 3, h, w});
+  im_tensor->Reshape({1, input_channel_, h, w});
   im_tensor->copy_from_cpu(inputs_.im_data_.data());
 
-  if (name == "YOLOv3") {
+  if (name == "YOLOv3" || name == "PPYOLO") {
     auto im_size_tensor = predictor_->GetInputTensor("im_size");
     im_size_tensor->Reshape({1, 2});
     im_size_tensor->copy_from_cpu(inputs_.ori_im_size_.data());
@@ -288,7 +311,7 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     im_info_tensor->copy_from_cpu(im_info);
     im_shape_tensor->copy_from_cpu(im_shape);
   }
-  // 使用加载的模型进行预测
+  // predict
   predictor_->ZeroCopyRun();
 
   std::vector<float> output_box;
@@ -306,7 +329,7 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     return true;
   }
   int num_boxes = size / 6;
-  // 解析预测框box
+  // box postprocess
   for (int i = 0; i < num_boxes; ++i) {
     Box box;
     box.category_id = static_cast<int>(round(output_box[i * 6]));
@@ -321,7 +344,7 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     box.coordinate = {xmin, ymin, w, h};
     result->boxes.push_back(std::move(box));
   }
-  // 实例分割需解析mask
+  // mask postprocess
   if (name == "MaskRCNN") {
     std::vector<float> output_mask;
     auto output_mask_tensor = predictor_->GetOutputTensor(output_names[1]);
@@ -337,12 +360,22 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     result->mask_resolution = output_mask_shape[2];
     for (int i = 0; i < result->boxes.size(); ++i) {
       Box* box = &result->boxes[i];
-      auto begin_mask =
-          output_mask.begin() + (i * classes + box->category_id) * mask_pixels;
-      auto end_mask = begin_mask + mask_pixels;
-      box->mask.data.assign(begin_mask, end_mask);
       box->mask.shape = {static_cast<int>(box->coordinate[2]),
                          static_cast<int>(box->coordinate[3])};
+      auto begin_mask =
+          output_mask.data() + (i * classes + box->category_id) * mask_pixels;
+      cv::Mat bin_mask(result->mask_resolution,
+                     result->mask_resolution,
+                     CV_32FC1,
+                     begin_mask);
+      cv::resize(bin_mask,
+               bin_mask,
+               cv::Size(box->mask.shape[0], box->mask.shape[1]));
+      cv::threshold(bin_mask, bin_mask, 0.5, 1, cv::THRESH_BINARY);
+      auto mask_int_begin = reinterpret_cast<float*>(bin_mask.data);
+      auto mask_int_end =
+        mask_int_begin + box->mask.shape[0] * box->mask.shape[1];
+      box->mask.data.assign(mask_int_begin, mask_int_end);
     }
   }
   return true;
@@ -366,12 +399,12 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
 
   inputs_batch_.assign(im_batch.size(), ImageBlob());
   int batch_size = im_batch.size();
-  // 处理输入图像
+  // preprocess
   if (!preprocess(im_batch, &inputs_batch_, thread_num)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
-  // 对RCNN类模型做批量padding
+  // RCNN model padding
   if (batch_size > 1) {
     if (name == "FasterRCNN" || name == "MaskRCNN") {
       int max_h = -1;
@@ -411,15 +444,15 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
   int h = inputs_batch_[0].new_im_size_[0];
   int w = inputs_batch_[0].new_im_size_[1];
   auto im_tensor = predictor_->GetInputTensor("image");
-  im_tensor->Reshape({batch_size, 3, h, w});
-  std::vector<float> inputs_data(batch_size * 3 * h * w);
+  im_tensor->Reshape({batch_size, input_channel_, h, w});
+  std::vector<float> inputs_data(batch_size * input_channel_ * h * w);
   for (int i = 0; i < batch_size; ++i) {
     std::copy(inputs_batch_[i].im_data_.begin(),
               inputs_batch_[i].im_data_.end(),
-              inputs_data.begin() + i * 3 * h * w);
+              inputs_data.begin() + i * input_channel_ * h * w);
   }
   im_tensor->copy_from_cpu(inputs_data.data());
-  if (name == "YOLOv3") {
+  if (name == "YOLOv3" || name == "PPYOLO") {
     auto im_size_tensor = predictor_->GetInputTensor("im_size");
     im_size_tensor->Reshape({batch_size, 2});
     std::vector<int> inputs_data_size(batch_size * 2);
@@ -452,10 +485,10 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
     im_info_tensor->copy_from_cpu(im_info.data());
     im_shape_tensor->copy_from_cpu(im_shape.data());
   }
-  // 使用加载的模型进行预测
+  // predict
   predictor_->ZeroCopyRun();
 
-  // 读取所有box
+  // get all box
   std::vector<float> output_box;
   auto output_names = predictor_->GetOutputNames();
   auto output_box_tensor = predictor_->GetOutputTensor(output_names[0]);
@@ -472,7 +505,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
   }
   auto lod_vector = output_box_tensor->lod();
   int num_boxes = size / 6;
-  // 解析预测框box
+  // box postprocess
   (*results).clear();
   (*results).resize(batch_size);
   for (int i = 0; i < lod_vector[0].size() - 1; ++i) {
@@ -492,7 +525,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
     }
   }
 
-  // 实例分割需解析mask
+  // mask postprocess
   if (name == "MaskRCNN") {
     std::vector<float> output_mask;
     auto output_mask_tensor = predictor_->GetOutputTensor(output_names[1]);
@@ -509,14 +542,24 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
     for (int i = 0; i < lod_vector[0].size() - 1; ++i) {
       (*results)[i].mask_resolution = output_mask_shape[2];
       for (int j = 0; j < (*results)[i].boxes.size(); ++j) {
-        Box* box = &(*results)[i].boxes[j];
+        Box* box = &(*results)[i].boxes[i];
         int category_id = box->category_id;
-        auto begin_mask = output_mask.begin() +
-                          (mask_idx * classes + category_id) * mask_pixels;
-        auto end_mask = begin_mask + mask_pixels;
-        box->mask.data.assign(begin_mask, end_mask);
         box->mask.shape = {static_cast<int>(box->coordinate[2]),
-                           static_cast<int>(box->coordinate[3])};
+                          static_cast<int>(box->coordinate[3])};
+        auto begin_mask =
+          output_mask.data() + (i * classes + box->category_id) * mask_pixels;
+        cv::Mat bin_mask(output_mask_shape[2],
+                      output_mask_shape[2],
+                      CV_32FC1,
+                      begin_mask);
+        cv::resize(bin_mask,
+                bin_mask,
+                cv::Size(box->mask.shape[0], box->mask.shape[1]));
+        cv::threshold(bin_mask, bin_mask, 0.5, 1, cv::THRESH_BINARY);
+        auto mask_int_begin = reinterpret_cast<float*>(bin_mask.data);
+        auto mask_int_end =
+          mask_int_begin + box->mask.shape[0] * box->mask.shape[1];
+        box->mask.data.assign(mask_int_begin, mask_int_end);
         mask_idx++;
       }
     }
@@ -537,7 +580,7 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
     return false;
   }
 
-  // 处理输入图像
+  // preprocess
   if (!preprocess(im, &inputs_)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
@@ -546,13 +589,13 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
   int h = inputs_.new_im_size_[0];
   int w = inputs_.new_im_size_[1];
   auto im_tensor = predictor_->GetInputTensor("image");
-  im_tensor->Reshape({1, 3, h, w});
+  im_tensor->Reshape({1, input_channel_, h, w});
   im_tensor->copy_from_cpu(inputs_.im_data_.data());
 
-  // 使用加载的模型进行预测
+  // predict
   predictor_->ZeroCopyRun();
 
-  // 获取预测置信度,经过argmax后的labelmap
+  // get labelmap
   auto output_names = predictor_->GetOutputNames();
   auto output_label_tensor = predictor_->GetOutputTensor(output_names[0]);
   std::vector<int> output_label_shape = output_label_tensor->shape();
@@ -565,7 +608,7 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
   result->label_map.data.resize(size);
   output_label_tensor->copy_to_cpu(result->label_map.data.data());
 
-  // 获取预测置信度scoremap
+  // get scoremap
   auto output_score_tensor = predictor_->GetOutputTensor(output_names[1]);
   std::vector<int> output_score_shape = output_score_tensor->shape();
   size = 1;
@@ -577,7 +620,7 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
   result->score_map.data.resize(size);
   output_score_tensor->copy_to_cpu(result->score_map.data.data());
 
-  // 解析输出结果到原图大小
+  // get origin image result
   std::vector<uint8_t> label_map(result->label_map.data.begin(),
                                  result->label_map.data.end());
   cv::Mat mask_label(result->label_map.shape[1],
@@ -647,7 +690,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
     return false;
   }
 
-  // 处理输入图像
+  // preprocess
   inputs_batch_.assign(im_batch.size(), ImageBlob());
   if (!preprocess(im_batch, &inputs_batch_, thread_num)) {
     std::cerr << "Preprocess failed!" << std::endl;
@@ -660,20 +703,20 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
   int h = inputs_batch_[0].new_im_size_[0];
   int w = inputs_batch_[0].new_im_size_[1];
   auto im_tensor = predictor_->GetInputTensor("image");
-  im_tensor->Reshape({batch_size, 3, h, w});
-  std::vector<float> inputs_data(batch_size * 3 * h * w);
+  im_tensor->Reshape({batch_size, input_channel_, h, w});
+  std::vector<float> inputs_data(batch_size * input_channel_ * h * w);
   for (int i = 0; i < batch_size; ++i) {
     std::copy(inputs_batch_[i].im_data_.begin(),
               inputs_batch_[i].im_data_.end(),
-              inputs_data.begin() + i * 3 * h * w);
+              inputs_data.begin() + i * input_channel_ * h * w);
   }
   im_tensor->copy_from_cpu(inputs_data.data());
   // im_tensor->copy_from_cpu(inputs_.im_data_.data());
 
-  // 使用加载的模型进行预测
+  // predict
   predictor_->ZeroCopyRun();
 
-  // 获取预测置信度,经过argmax后的labelmap
+  // get labelmap
   auto output_names = predictor_->GetOutputNames();
   auto output_label_tensor = predictor_->GetOutputTensor(output_names[0]);
   std::vector<int> output_label_shape = output_label_tensor->shape();
@@ -698,7 +741,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
               (*results)[i].label_map.data.data());
   }
 
-  // 获取预测置信度scoremap
+  // get scoremap
   auto output_score_tensor = predictor_->GetOutputTensor(output_names[1]);
   std::vector<int> output_score_shape = output_score_tensor->shape();
   size = 1;
@@ -722,7 +765,7 @@ bool Model::predict(const std::vector<cv::Mat>& im_batch,
               (*results)[i].score_map.data.data());
   }
 
-  // 解析输出结果到原图大小
+  // get origin image result
   for (int i = 0; i < batch_size; ++i) {
     std::vector<uint8_t> label_map((*results)[i].label_map.data.begin(),
                                    (*results)[i].label_map.data.end());

+ 68 - 20
deploy/cpp/src/transforms.cpp

@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "include/paddlex/transforms.h"
+
+#include <math.h>
+
 #include <iostream>
 #include <string>
 #include <vector>
-#include <math.h>
-
-#include "include/paddlex/transforms.h"
 
 namespace PaddleX {
 
@@ -28,16 +29,20 @@ std::map<std::string, int> interpolations = {{"LINEAR", cv::INTER_LINEAR},
                                              {"LANCZOS4", cv::INTER_LANCZOS4}};
 
 bool Normalize::Run(cv::Mat* im, ImageBlob* data) {
-  for (int h = 0; h < im->rows; h++) {
-    for (int w = 0; w < im->cols; w++) {
-      im->at<cv::Vec3f>(h, w)[0] =
-          (im->at<cv::Vec3f>(h, w)[0] / 255.0 - mean_[0]) / std_[0];
-      im->at<cv::Vec3f>(h, w)[1] =
-          (im->at<cv::Vec3f>(h, w)[1] / 255.0 - mean_[1]) / std_[1];
-      im->at<cv::Vec3f>(h, w)[2] =
-          (im->at<cv::Vec3f>(h, w)[2] / 255.0 - mean_[2]) / std_[2];
-    }
+  std::vector<float> range_val;
+  for (int c = 0; c < im->channels(); c++) {
+    range_val.push_back(max_val_[c] - min_val_[c]);
   }
+
+  std::vector<cv::Mat> split_im;
+  cv::split(*im, split_im);
+  for (int c = 0; c < im->channels(); c++) {
+    cv::subtract(split_im[c], cv::Scalar(min_val_[c]), split_im[c]);
+    cv::divide(split_im[c], cv::Scalar(range_val[c]), split_im[c]);
+    cv::subtract(split_im[c], cv::Scalar(mean_[c]), split_im[c]);
+    cv::divide(split_im[c], cv::Scalar(std_[c]), split_im[c]);
+  }
+  cv::merge(split_im, *im);
   return true;
 }
 
@@ -111,11 +116,22 @@ bool Padding::Run(cv::Mat* im, ImageBlob* data) {
               << ", but they should be greater than 0." << std::endl;
     return false;
   }
-  cv::Scalar value = cv::Scalar(im_value_[0], im_value_[1], im_value_[2]);
-  cv::copyMakeBorder(
-      *im, *im, 0, padding_h, 0, padding_w, cv::BORDER_CONSTANT, value);
+  std::vector<cv::Mat> padded_im_per_channel;
+  for (size_t i = 0; i < im->channels(); i++) {
+    const cv::Mat per_channel = cv::Mat(im->rows + padding_h,
+                                        im->cols + padding_w,
+                                        CV_32FC1,
+                                        cv::Scalar(im_value_[i]));
+    padded_im_per_channel.push_back(per_channel);
+  }
+  cv::Mat padded_im;
+  cv::merge(padded_im_per_channel, padded_im);
+  cv::Rect im_roi = cv::Rect(0, 0, im->cols, im->rows);
+  im->copyTo(padded_im(im_roi));
+  *im = padded_im;
   data->new_im_size_[0] = im->rows;
   data->new_im_size_[1] = im->cols;
+
   return true;
 }
 
@@ -161,12 +177,42 @@ bool Resize::Run(cv::Mat* im, ImageBlob* data) {
   return true;
 }
 
+bool Clip::Run(cv::Mat* im, ImageBlob* data) {
+  std::vector<cv::Mat> split_im;
+  cv::split(*im, split_im);
+  for (int c = 0; c < im->channels(); c++) {
+    cv::threshold(split_im[c], split_im[c], max_val_[c], max_val_[c],
+                  cv::THRESH_TRUNC);
+    cv::subtract(cv::Scalar(0), split_im[c], split_im[c]);
+    cv::threshold(split_im[c], split_im[c], min_val_[c], min_val_[c],
+                  cv::THRESH_TRUNC);
+    cv::divide(split_im[c], cv::Scalar(-1), split_im[c]);
+  }
+  cv::merge(split_im, *im);
+  return true;
+}
+
 void Transforms::Init(const YAML::Node& transforms_node, bool to_rgb) {
   transforms_.clear();
   to_rgb_ = to_rgb;
   for (const auto& item : transforms_node) {
     std::string name = item.begin()->first.as<std::string>();
-    std::cout << "trans name: " << name << std::endl;
+    if (name == "ArrangeClassifier") {
+      continue;
+    }
+    if (name == "ArrangeSegmenter") {
+      continue;
+    }
+    if (name == "ArrangeFasterRCNN") {
+      continue;
+    }
+    if (name == "ArrangeMaskRCNN") {
+      continue;
+    }
+    if (name == "ArrangeYOLOv3") {
+      continue;
+    }
+
     std::shared_ptr<Transform> transform = CreateTransform(name);
     transform->Init(item.begin()->second);
     transforms_.push_back(transform);
@@ -187,6 +233,8 @@ std::shared_ptr<Transform> Transforms::CreateTransform(
     return std::make_shared<Padding>();
   } else if (transform_name == "ResizeByLong") {
     return std::make_shared<ResizeByLong>();
+  } else if (transform_name == "Clip") {
+    return std::make_shared<Clip>();
   } else {
     std::cerr << "There's unexpected transform(name='" << transform_name
               << "')." << std::endl;
@@ -195,11 +243,11 @@ std::shared_ptr<Transform> Transforms::CreateTransform(
 }
 
 bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
-  // 按照transforms中预处理算子顺序处理图像
+  // do all preprocess ops by order
   if (to_rgb_) {
     cv::cvtColor(*im, *im, cv::COLOR_BGR2RGB);
   }
-  (*im).convertTo(*im, CV_32FC3);
+  (*im).convertTo(*im, CV_32FC(im->channels()));
   data->ori_im_size_[0] = im->rows;
   data->ori_im_size_[1] = im->cols;
   data->new_im_size_[0] = im->rows;
@@ -211,8 +259,8 @@ bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
     }
   }
 
-  // 将图像由NHWC转为NCHW格式
-  // 同时转为连续的内存块存储到ImageBlob
+  // data format NHWC to NCHW
+  // img data save to ImageBlob
   int h = im->rows;
   int w = im->cols;
   int c = im->channels();

+ 9 - 11
deploy/cpp/src/visualize.cpp

@@ -47,7 +47,7 @@ cv::Mat Visualize(const cv::Mat& img,
                             boxes[i].coordinate[2],
                             boxes[i].coordinate[3]);
 
-    // 生成预测框和标题
+    // draw box and title
     std::string text = boxes[i].category;
     int c1 = colormap[3 * boxes[i].category_id + 0];
     int c2 = colormap[3 * boxes[i].category_id + 1];
@@ -63,13 +63,13 @@ cv::Mat Visualize(const cv::Mat& img,
     origin.x = roi.x;
     origin.y = roi.y;
 
-    // 生成预测框标题的背景
+    // background
     cv::Rect text_back = cv::Rect(boxes[i].coordinate[0],
                                   boxes[i].coordinate[1] - text_size.height,
                                   text_size.width,
                                   text_size.height);
 
-    // 绘图和文字
+    // draw
     cv::rectangle(vis_img, roi, roi_color, 2);
     cv::rectangle(vis_img, text_back, roi_color, -1);
     cv::putText(vis_img,
@@ -80,18 +80,16 @@ cv::Mat Visualize(const cv::Mat& img,
                 cv::Scalar(255, 255, 255),
                 thickness);
 
-    // 生成实例分割mask
+    // mask
     if (boxes[i].mask.data.size() == 0) {
       continue;
     }
-    cv::Mat bin_mask(result.mask_resolution,
-                     result.mask_resolution,
+    std::vector<float> mask_data;
+    mask_data.assign(boxes[i].mask.data.begin(), boxes[i].mask.data.end());
+    cv::Mat bin_mask(boxes[i].mask.shape[1],
+                     boxes[i].mask.shape[0],
                      CV_32FC1,
-                     boxes[i].mask.data.data());
-    cv::resize(bin_mask,
-               bin_mask,
-               cv::Size(boxes[i].mask.shape[0], boxes[i].mask.shape[1]));
-    cv::threshold(bin_mask, bin_mask, 0.5, 1, cv::THRESH_BINARY);
+                     mask_data.data());
     cv::Mat full_mask = cv::Mat::zeros(vis_img.size(), CV_8UC1);
     bin_mask.copyTo(full_mask(roi));
     cv::Mat mask_ch[3];

+ 2 - 10
deploy/openvino/CMakeLists.txt

@@ -8,7 +8,6 @@ SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 SET(OPENVINO_DIR "" CACHE PATH "Location of libraries")
 SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
 SET(GFLAGS_DIR "" CACHE PATH "Location of libraries")
-SET(GLOG_DIR "" CACHE PATH "Location of libraries")
 SET(NGRAPH_LIB "" CACHE PATH "Location of libraries")
 SET(ARCH "" CACHE PATH "Location of libraries")
 
@@ -47,9 +46,6 @@ if (NOT DEFINED GFLAGS_DIR OR ${GFLAGS_DIR} STREQUAL "")
     message(FATAL_ERROR "please set GFLAGS_DIR with -DGFLAGS_DIR=/path/gflags")
 endif()
 
-if (NOT DEFINED GLOG_DIR OR ${GLOG_DIR} STREQUAL "")
-    message(FATAL_ERROR "please set GLOG_DIR with -DLOG_DIR=/path/glog")
-endif()
 
 if (NOT DEFINED NGRAPH_LIB OR ${NGRAPH_LIB} STREQUAL "")
     message(FATAL_ERROR "please set NGRAPH_DIR with -DNGRAPH_DIR=/path/ngraph")
@@ -70,9 +66,6 @@ endif()
 link_directories("${GFLAGS_DIR}/lib")
 include_directories("${GFLAGS_DIR}/include")
 
-link_directories("${GLOG_DIR}/lib")
-include_directories("${GLOG_DIR}/include")
-
 link_directories("${NGRAPH_LIB}")
 link_directories("${NGRAPH_LIB}/lib")
 
@@ -86,7 +79,6 @@ endif ()
 include_directories(${OpenCV_INCLUDE_DIRS})
 
 if (WIN32)
-    add_definitions("/DGOOGLE_GLOG_DLL_DECL=")
     set(CMAKE_C_FLAGS_DEBUG   "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd")
     set(CMAKE_C_FLAGS_RELEASE  "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT")
     set(CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd")
@@ -126,11 +118,11 @@ endif(WIN32)
 
 if (NOT WIN32)
     set(DEPS ${DEPS}
-        glog gflags  z  yaml-cpp
+        gflags yaml-cpp
         )
 else()
     set(DEPS ${DEPS}
-        glog gflags_static  libyaml-cppmt)
+        gflags_static  libyaml-cppmt)
     set(DEPS ${DEPS} libcmt shlwapi)
 endif(NOT WIN32)
 

+ 1 - 6
deploy/openvino/CMakeSettings.json

@@ -35,13 +35,8 @@
           "name": "WITH_STATIC_LIB",
           "value": "True",
           "type": "BOOL"
-        },
-        {
-          "name": "GLOG_DIR",
-          "value": "/path/to/glog",
-          "type": "PATH"
         }
       ]
     }
   ]
-}
+}

+ 3 - 3
deploy/openvino/demo/classifier.cpp

@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
+#include <gflags/gflags.h>
 
 #include <fstream>
 #include <iostream>
@@ -44,11 +44,11 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  // 加载模型
+  // load model
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_device);
 
-  // 进行预测
+  // predict
   if (FLAGS_image_list != "") {
     std::ifstream inf(FLAGS_image_list);
     if (!inf) {

+ 4 - 5
deploy/openvino/demo/detector.cpp

@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
-#include <omp.h>
+#include <gflags/gflags.h>
 
 #include <algorithm>
 #include <chrono>  // NOLINT
@@ -54,13 +53,13 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  //
+  // load model
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_device);
 
   int imgs = 1;
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
-  // 进行预测
+  // predict
   if (FLAGS_image_list != "") {
     std::ifstream inf(FLAGS_image_list);
     if (!inf) {
@@ -96,7 +95,7 @@ int main(int argc, char** argv) {
                 << result.boxes[i].coordinate[3] << ")" << std::endl;
     }
     if (FLAGS_save_dir != "") {
-    // 可视化
+    // visualize
       cv::Mat vis_img = PaddleX::Visualize(
         im, result, model.labels, colormap, FLAGS_threshold);
       std::string save_path =

+ 2 - 4
deploy/openvino/demo/segmenter.cpp

@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
+#include <gflags/gflags.h>
 
 #include <algorithm>
 #include <fstream>
@@ -48,11 +48,9 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  //
-  std::cout << "init start" << std::endl;
+  // load model
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_device);
-  std::cout << "init done" << std::endl;
   int imgs = 1;
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
 

+ 30 - 0
deploy/openvino/include/paddlex/transforms.h

@@ -71,6 +71,16 @@ class Normalize : public Transform {
   virtual void Init(const YAML::Node& item) {
     mean_ = item["mean"].as<std::vector<float>>();
     std_ = item["std"].as<std::vector<float>>();
+    if (item["min_val"].IsDefined()) {
+      min_val_ = item["min_val"].as<std::vector<float>>();
+    } else {
+      min_val_ = std::vector<float>(mean_.size(), 0.);
+    }
+    if (item["max_val"].IsDefined()) {
+      max_val_ = item["max_val"].as<std::vector<float>>();
+    } else {
+      max_val_ = std::vector<float>(mean_.size(), 255.);
+    }
   }
 
   virtual bool Run(cv::Mat* im, ImageBlob* data);
@@ -78,6 +88,8 @@ class Normalize : public Transform {
  private:
   std::vector<float> mean_;
   std::vector<float> std_;
+  std::vector<float> min_val_;
+  std::vector<float> max_val_;
 };
 
 class ResizeByShort : public Transform {
@@ -209,6 +221,24 @@ class Padding : public Transform {
   std::vector<float> im_value_;
 };
 
+/*
+ * @brief
+ * This class execute clip operation on image matrix
+ * */
+class Clip : public Transform {
+ public:
+  virtual void Init(const YAML::Node& item) {
+    min_val_ = item["min_val"].as<std::vector<float>>();
+    max_val_ = item["max_val"].as<std::vector<float>>();
+  }
+
+  virtual bool Run(cv::Mat* im, ImageBlob* data);
+
+ private:
+  std::vector<float> min_val_;
+  std::vector<float> max_val_;
+};
+
 class Transforms {
  public:
   void Init(const YAML::Node& node, std::string type, bool to_rgb = true);

+ 26 - 30
deploy/openvino/python/convertor.py → deploy/openvino/python/converter.py

@@ -13,12 +13,14 @@
 # limitations under the License.
 
 import os
+import os.path as osp
 from six import text_type as _text_type
 import argparse
 import sys
-from utils import logging 
+import yaml
 import paddlex as pdx
 
+
 def arg_parser():
     parser = argparse.ArgumentParser()
     parser.add_argument(
@@ -46,41 +48,36 @@ def arg_parser():
     return parser
 
 
-    
-
-
 def export_openvino_model(model, args):
-    if model.model_type == "detector" or model.__class__.__name__ == "FastSCNN":
-        logging.error(
-            "Only image classifier models and semantic segmentation models(except FastSCNN) are supported to export to openvino")
-    try:
-        import x2paddle
-        if x2paddle.__version__ < '0.7.4':
-            logging.error("You need to upgrade x2paddle >= 0.7.4")
-    except:
-        logging.error(
-            "You need to install x2paddle first, pip install x2paddle>=0.7.4")
-        
-    import x2paddle.convert as x2pc
-    x2pc.paddle2onnx(args.model_dir, args.save_dir)
+
+    if model.__class__.__name__ == "YOLOv3":
+        pdx.converter.export_onnx_model(model, args.save_dir)
+    else:
+        pdx.converter.export_onnx_model(model, args.save_dir, 11)
 
     import mo.main as mo
     from mo.utils.cli_parser import get_onnx_cli_parser
     onnx_parser = get_onnx_cli_parser()
-    onnx_parser.add_argument("--model_dir",type=_text_type)
-    onnx_parser.add_argument("--save_dir",type=_text_type)
+    onnx_parser.add_argument("--model_dir", type=_text_type)
+    onnx_parser.add_argument("--save_dir", type=_text_type)
     onnx_parser.add_argument("--fixed_input_shape")
-    onnx_input = os.path.join(args.save_dir, 'x2paddle_model.onnx')
+    onnx_input = os.path.join(args.save_dir, 'paddle2onnx_model.onnx')
     onnx_parser.set_defaults(input_model=onnx_input)
     onnx_parser.set_defaults(output_dir=args.save_dir)
-    shape = '[1,3,'
-    shape =  shape + args.fixed_input_shape[1:]
-    if model.__class__.__name__ == "YOLOV3":
+    shape_list = args.fixed_input_shape[1:-1].split(',')
+    with open(osp.join(args.model_dir, "model.yml")) as f:
+        info = yaml.load(f.read(), Loader=yaml.Loader)
+    input_channel = 3
+    if 'input_channel' in info['_init_params']:
+        input_channel = info['_init_params']['input_channel']
+    shape = '[1,{},' + shape_list[1] + ',' + shape_list[0] + ']'
+    shape = shape.format(input_channel)
+    if model.__class__.__name__ == "YOLOv3":
         shape = shape + ",[1,2]"
         inputs = "image,im_size"
-        onnx_parser.set_defaults(input = inputs)
-    onnx_parser.set_defaults(input_shape = shape)
-    mo.main(onnx_parser,'onnx')
+        onnx_parser.set_defaults(input=inputs)
+    onnx_parser.set_defaults(input_shape=shape)
+    mo.main(onnx_parser, 'onnx')
 
 
 def main():
@@ -90,12 +87,11 @@ def main():
     assert args.save_dir is not None, "--save_dir should be defined to create openvino model"
     model = pdx.load_model(args.model_dir)
     if model.status == "Normal" or model.status == "Prune":
-        logging.error(
+        print(
             "Only support inference model, try to export model first as below,",
             exit=False)
     export_openvino_model(model, args)
 
-if  __name__ == "__main__":
-    main()
-
 
+if __name__ == "__main__":
+    main()

+ 7 - 7
deploy/openvino/python/deploy.py

@@ -173,7 +173,6 @@ class Predictor:
             'category': self.labels[l],
             'score': preds[output_name][0][l],
         } for l in pred_label]
-        print(result)
         return result
 
     def segmenter_postprocess(self, preds, preprocessed_inputs):
@@ -181,11 +180,11 @@ class Predictor:
         """
         it = iter(self.net.outputs)
         next(it)
+        label_name = next(it)
+        label_map = np.squeeze(preds[label_name]).astype('uint8')
         score_name = next(it)
         score_map = np.squeeze(preds[score_name])
         score_map = np.transpose(score_map, (1, 2, 0))
-        label_name = next(it)
-        label_map = np.squeeze(preds[label_name]).astype('uint8')
         im_info = preprocessed_inputs['im_info']
         for info in im_info[::-1]:
             if info[0] == 'resize':
@@ -204,15 +203,16 @@ class Predictor:
     def detector_postprocess(self, preds, preprocessed_inputs):
         """对图像检测结果做后处理
         """
-        output_name = next(iter(self.net.outputs))
-        outputs = preds[output_name][0]
+        outputs = self.net.outputs
+        for name in outpus:
+            if (len(outputs[name].shape == 3)):
+                output = preds[name][0]
         result = []
-        for out in outputs:
+        for out in output:
             if (out[0] > 0):
                 result.append(out.tolist())
             else:
                 pass
-        print(result)
         return result
 
     def predict(self, image, topk=1, threshold=0.5):

+ 10 - 5
deploy/openvino/python/transforms/ops.py

@@ -18,11 +18,15 @@ import numpy as np
 from PIL import Image, ImageEnhance
 
 
-def normalize(im, mean, std):
-    im = im / 255.0
+def normalize(im, mean, std, min_value=[0, 0, 0], max_value=[255, 255, 255]):
+    # Rescaling (min-max normalization)
+    range_value = [max_value[i] - min_value[i] for i in range(len(max_value))]
+    im = (im - min_value) / range_value
+
+    # Standardization (Z-score Normalization)
     im -= mean
     im /= std
-    return im
+    return im.astype('float32')
 
 
 def permute(im, to_bgr=False):
@@ -69,8 +73,8 @@ def random_crop(im,
                 (float(im.shape[1]) / im.shape[0]) / (w**2))
     scale_max = min(scale[1], bound)
     scale_min = min(scale[0], bound)
-    target_area = im.shape[0] * im.shape[1] * np.random.uniform(
-        scale_min, scale_max)
+    target_area = im.shape[0] * im.shape[1] * np.random.uniform(scale_min,
+                                                                scale_max)
     target_size = math.sqrt(target_area)
     w = int(target_size * w)
     h = int(target_size * h)
@@ -146,6 +150,7 @@ def brightness(im, brightness_lower, brightness_upper):
     im += delta
     return im
 
+
 def rotate(im, rotate_lower, rotate_upper):
     rotate_delta = np.random.uniform(rotate_lower, rotate_upper)
     im = im.rotate(int(rotate_delta))

+ 197 - 72
deploy/openvino/python/transforms/seg_transforms.py

@@ -19,6 +19,7 @@ import os.path as osp
 import numpy as np
 from PIL import Image
 import cv2
+import imghdr
 from collections import OrderedDict
 
 
@@ -52,6 +53,62 @@ class Compose(SegTransform):
         self.transforms = transforms
         self.to_rgb = False
 
+    @staticmethod
+    def read_img(img_path):
+        img_format = imghdr.what(img_path)
+        name, ext = osp.splitext(img_path)
+        if img_format == 'tiff' or ext == '.img':
+            try:
+                import gdal
+            except:
+                six.reraise(*sys.exc_info())
+                raise Exception(
+                    "Please refer to https://github.com/PaddlePaddle/PaddleX/tree/develop/examples/multi-channel_remote_sensing/README.md to install gdal"
+                )
+
+            dataset = gdal.Open(img_path)
+            if dataset == None:
+                raise Exception('Can not open', img_path)
+            im_data = dataset.ReadAsArray()
+            return im_data.transpose((1, 2, 0))
+        elif img_format in ['jpeg', 'bmp', 'png']:
+            return cv2.imread(img_path)
+        elif ext == '.npy':
+            return np.load(img_path)
+        else:
+            raise Exception('Image format {} is not supported!'.format(ext))
+
+    @staticmethod
+    def decode_image(im, label):
+        if isinstance(im, np.ndarray):
+            if len(im.shape) != 3:
+                raise Exception(
+                    "im should be 3-dimensions, but now is {}-dimensions".
+                    format(len(im.shape)))
+        else:
+            try:
+                im = Compose.read_img(im)
+            except:
+                raise ValueError('Can\'t read The image file {}!'.format(im))
+        im = im.astype('float32')
+        if label is not None:
+            if isinstance(label, np.ndarray):
+                if len(label.shape) != 2:
+                    raise Exception(
+                        "label should be 2-dimensions, but now is {}-dimensions".
+                        format(len(label.shape)))
+
+            else:
+                try:
+                    label = np.asarray(Image.open(label))
+                except:
+                    ValueError('Can\'t read The label file {}!'.format(label))
+            im_height, im_width, _ = im.shape
+            label_height, label_width = label.shape
+            if im_height != label_height or im_width != label_width:
+                raise Exception(
+                    "The height or width of the image is not same as the label")
+        return (im, label)
 
     def __call__(self, im, im_info=None, label=None):
         """
@@ -67,23 +124,13 @@ class Compose(SegTransform):
             tuple: 根据网络所需字段所组成的tuple;字段由transforms中的最后一个数据预处理操作决定。
         """
 
-        if im_info is None:
-            im_info = list()
-        if isinstance(im, np.ndarray):
-            if len(im.shape) != 3:
-                raise Exception(
-                    "im should be 3-dimensions, but now is {}-dimensions".
-                    format(len(im.shape)))
-        else:
-            try:
-                im = cv2.imread(im).astype('float32')
-            except:
-                raise ValueError('Can\'t read The image file {}!'.format(im))
+        im, label = self.decode_image(im, label)
         if self.to_rgb:
             im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
+        if im_info is None:
+            im_info = [('origin_shape', im.shape[0:2])]
         if label is not None:
-            if not isinstance(label, np.ndarray):
-                label = np.asarray(Image.open(label))
+            origin_label = label.copy()
         for op in self.transforms:
             if isinstance(op, SegTransform):
                 outputs = op(im, im_info, label)
@@ -98,6 +145,10 @@ class Compose(SegTransform):
                     outputs = (im, im_info, label)
                 else:
                     outputs = (im, im_info)
+        if self.transforms[-1].__class__.__name__ == 'ArrangeSegmenter':
+            if self.transforms[-1].mode == 'eval':
+                if label is not None:
+                    outputs = (im, im_info, origin_label)
         return outputs
 
     def add_augmenters(self, augmenters):
@@ -107,7 +158,9 @@ class Compose(SegTransform):
         transform_names = [type(x).__name__ for x in self.transforms]
         for aug in augmenters:
             if type(aug).__name__ in transform_names:
-                print("{} is already in ComposedTransforms, need to remove it from add_augmenters().".format(type(aug).__name__))
+                print(
+                    "{} is already in ComposedTransforms, need to remove it from add_augmenters().".
+                    format(type(aug).__name__))
         self.transforms = augmenters + self.transforms
 
 
@@ -536,22 +589,35 @@ class ResizeStepScaling(SegTransform):
 
 class Normalize(SegTransform):
     """对图像进行标准化。
-    1.尺度缩放到 [0,1]。
-    2.对图像进行减均值除以标准差操作。
+    1.像素值减去min_val
+    2.像素值除以(max_val-min_val)
+    3.对图像进行减均值除以标准差操作。
 
     Args:
         mean (list): 图像数据集的均值。默认值[0.5, 0.5, 0.5]。
         std (list): 图像数据集的标准差。默认值[0.5, 0.5, 0.5]。
+        min_val (list): 图像数据集的最小值。默认值[0, 0, 0]。
+        max_val (list): 图像数据集的最大值。默认值[255.0, 255.0, 255.0]。
 
     Raises:
         ValueError: mean或std不是list对象。std包含0。
     """
 
-    def __init__(self, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]):
+    def __init__(self,
+                 mean=[0.5, 0.5, 0.5],
+                 std=[0.5, 0.5, 0.5],
+                 min_val=[0, 0, 0],
+                 max_val=[255.0, 255.0, 255.0]):
+        self.min_val = min_val
+        self.max_val = max_val
         self.mean = mean
         self.std = std
         if not (isinstance(self.mean, list) and isinstance(self.std, list)):
             raise ValueError("{}: input type is invalid.".format(self))
+        if not (isinstance(self.min_val, list) and isinstance(self.max_val,
+                                                              list)):
+            raise ValueError("{}: input type is invalid.".format(self))
+
         from functools import reduce
         if reduce(lambda x, y: x * y, self.std) == 0:
             raise ValueError('{}: std is invalid!'.format(self))
@@ -574,7 +640,8 @@ class Normalize(SegTransform):
 
         mean = np.array(self.mean)[np.newaxis, np.newaxis, :]
         std = np.array(self.std)[np.newaxis, np.newaxis, :]
-        im = normalize(im, mean, std)
+        im = normalize(im, mean, std, self.min_val, self.max_val)
+        im = im.astype('float32')
 
         if label is None:
             return (im, im_info)
@@ -646,28 +713,29 @@ class Padding(SegTransform):
             target_width = self.target_size[0]
         pad_height = target_height - im_height
         pad_width = target_width - im_width
-        if pad_height < 0 or pad_width < 0:
-            raise ValueError(
-                'the size of image should be less than target_size, but the size of image ({}, {}), is larger than target_size ({}, {})'
-                .format(im_width, im_height, target_width, target_height))
-        else:
-            im = cv2.copyMakeBorder(
-                im,
-                0,
-                pad_height,
-                0,
-                pad_width,
-                cv2.BORDER_CONSTANT,
-                value=self.im_padding_value)
+        pad_height = max(pad_height, 0)
+        pad_width = max(pad_width, 0)
+        if (pad_height > 0 or pad_width > 0):
+            im_channel = im.shape[2]
+            import copy
+            orig_im = copy.deepcopy(im)
+            im = np.zeros((im_height + pad_height, im_width + pad_width,
+                           im_channel)).astype(orig_im.dtype)
+            for i in range(im_channel):
+                im[:, :, i] = np.pad(
+                    orig_im[:, :, i],
+                    pad_width=((0, pad_height), (0, pad_width)),
+                    mode='constant',
+                    constant_values=(self.im_padding_value[i],
+                                     self.im_padding_value[i]))
+
             if label is not None:
-                label = cv2.copyMakeBorder(
-                    label,
-                    0,
-                    pad_height,
-                    0,
-                    pad_width,
-                    cv2.BORDER_CONSTANT,
-                    value=self.label_padding_value)
+                label = np.pad(label,
+                               pad_width=((0, pad_height), (0, pad_width)),
+                               mode='constant',
+                               constant_values=(self.label_padding_value,
+                                                self.label_padding_value))
+
         if label is None:
             return (im, im_info)
         else:
@@ -738,23 +806,26 @@ class RandomPaddingCrop(SegTransform):
             pad_height = max(crop_height - img_height, 0)
             pad_width = max(crop_width - img_width, 0)
             if (pad_height > 0 or pad_width > 0):
-                im = cv2.copyMakeBorder(
-                    im,
-                    0,
-                    pad_height,
-                    0,
-                    pad_width,
-                    cv2.BORDER_CONSTANT,
-                    value=self.im_padding_value)
+                img_channel = im.shape[2]
+                import copy
+                orig_im = copy.deepcopy(im)
+                im = np.zeros((img_height + pad_height, img_width + pad_width,
+                               img_channel)).astype(orig_im.dtype)
+                for i in range(img_channel):
+                    im[:, :, i] = np.pad(
+                        orig_im[:, :, i],
+                        pad_width=((0, pad_height), (0, pad_width)),
+                        mode='constant',
+                        constant_values=(self.im_padding_value[i],
+                                         self.im_padding_value[i]))
+
                 if label is not None:
-                    label = cv2.copyMakeBorder(
-                        label,
-                        0,
-                        pad_height,
-                        0,
-                        pad_width,
-                        cv2.BORDER_CONSTANT,
-                        value=self.label_padding_value)
+                    label = np.pad(label,
+                                   pad_width=((0, pad_height), (0, pad_width)),
+                                   mode='constant',
+                                   constant_values=(self.label_padding_value,
+                                                    self.label_padding_value))
+
                 img_height = im.shape[0]
                 img_width = im.shape[1]
 
@@ -819,8 +890,6 @@ class RandomBlur(SegTransform):
             return (im, im_info, label)
 
 
-
-
 class RandomScaleAspect(SegTransform):
     """裁剪并resize回原始尺寸的图像和标注图像。
     按照一定的面积比和宽高比对图像进行裁剪,并reszie回原始图像的图像,当存在标注图时,同步进行。
@@ -974,6 +1043,34 @@ class RandomDistort(SegTransform):
             params['im'] = im
             if np.random.uniform(0, 1) < prob:
                 im = ops[id](**params)
+        im = im.astype('float32')
+        if label is None:
+            return (im, im_info)
+        else:
+            return (im, im_info, label)
+
+
+class Clip(SegTransform):
+    """
+    对图像上超出一定范围的数据进行截断。
+
+    Args:
+        min_val (list): 裁剪的下限,小于min_val的数值均设为min_val. 默认值0.
+        max_val (list): 裁剪的上限,大于max_val的数值均设为max_val. 默认值255.0.
+    """
+
+    def __init__(self, min_val=[0, 0, 0], max_val=[255.0, 255.0, 255.0]):
+        self.min_val = min_val
+        self.max_val = max_val
+        if not (isinstance(self.min_val, list) and isinstance(self.max_val,
+                                                              list)):
+            raise ValueError("{}: input type is invalid.".format(self))
+
+    def __call__(self, im, im_info=None, label=None):
+        for k in range(im.shape[2]):
+            np.clip(
+                im[:, :, k], self.min_val[k], self.max_val[k], out=im[:, :, k])
+
         if label is None:
             return (im, im_info)
         else:
@@ -1013,9 +1110,12 @@ class ArrangeSegmenter(SegTransform):
                 'quant'时,返回的tuple为(im,),为图像np.ndarray数据。
         """
         im = permute(im, False)
-        if self.mode == 'train' or self.mode == 'eval':
+        if self.mode == 'train':
             label = label[np.newaxis, :, :]
             return (im, label)
+        if self.mode == 'eval':
+            label = label[np.newaxis, :, :]
+            return (im, im_info, label)
         elif self.mode == 'test':
             return (im, im_info)
         else:
@@ -1025,30 +1125,55 @@ class ArrangeSegmenter(SegTransform):
 class ComposedSegTransforms(Compose):
     """ 语义分割模型(UNet/DeepLabv3p)的图像处理流程,具体如下
         训练阶段:
-        1. 随机对图像以0.5的概率水平翻转
-        2. 按不同的比例随机Resize原图
+        1. 随机对图像以0.5的概率水平翻转,若random_horizontal_flip为False,则跳过此步骤
+        2. 按不同的比例随机Resize原图, 处理方式参考[paddlex.seg.transforms.ResizeRangeScaling](#resizerangescaling)。若min_max_size为None,则跳过此步骤
         3. 从原图中随机crop出大小为train_crop_size大小的子图,如若crop出来的图小于train_crop_size,则会将图padding到对应大小
         4. 图像归一化
-        预测阶段:
-        1. 图像归一化
+       预测阶段:
+        1. 将图像的最长边resize至(min_max_size[0] + min_max_size[1])//2, 短边按比例resize。若min_max_size为None,则跳过此步骤
+        2. 图像归一化
 
         Args:
-            mode(str): 图像处理所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
-            train_crop_size(list): 模型训练阶段,随机从原图crop的大小
-            mean(list): 图像均值
-            std(list): 图像方差
+            mode(str): Transforms所处的阶段,包括`train', 'eval'或'test'
+            min_max_size(list): 用于对图像进行resize,具体作用参见上述步骤。
+            train_crop_size(list): 训练过程中随机裁剪原图用于训练,具体作用参见上述步骤。此参数仅在mode为`train`时生效。
+            mean(list): 图像均值, 默认为[0.485, 0.456, 0.406]。
+            std(list): 图像方差,默认为[0.229, 0.224, 0.225]。
+            random_horizontal_flip(bool): 数据增强,是否随机水平翻转图像,此参数仅在mode为`train`时生效。
     """
 
     def __init__(self,
                  mode,
-                 train_crop_size=[769, 769],
+                 min_max_size=[400, 600],
+                 train_crop_size=[512, 512],
                  mean=[0.5, 0.5, 0.5],
-                 std=[0.5, 0.5, 0.5]):
+                 std=[0.5, 0.5, 0.5],
+                 random_horizontal_flip=True):
         if mode == 'train':
             # 训练时的transforms,包含数据增强
-            pass
+            if min_max_size is None:
+                transforms = [
+                    RandomPaddingCrop(crop_size=train_crop_size), Normalize(
+                        mean=mean, std=std)
+                ]
+            else:
+                transforms = [
+                    ResizeRangeScaling(
+                        min_value=min(min_max_size),
+                        max_value=max(min_max_size)),
+                    RandomPaddingCrop(crop_size=train_crop_size), Normalize(
+                        mean=mean, std=std)
+                ]
+            if random_horizontal_flip:
+                transforms.insert(0, RandomHorizontalFlip())
         else:
             # 验证/预测时的transforms
-            transforms = [Normalize(mean=mean, std=std)]
-
+            if min_max_size is None:
+                transforms = [Normalize(mean=mean, std=std)]
+            else:
+                long_size = (min(min_max_size) + max(min_max_size)) // 2
+                transforms = [
+                    ResizeByLong(long_size=long_size), Normalize(
+                        mean=mean, std=std)
+                ]
         super(ComposedSegTransforms, self).__init__(transforms)

+ 0 - 3
deploy/openvino/scripts/build.sh

@@ -6,8 +6,6 @@ NGRAPH_LIB=$INTEL_OPENVINO_DIR/deployment_tools/ngraph/lib
 
 # gflags预编译库的路径
 GFLAGS_DIR=$(pwd)/deps/gflags
-# glog预编译库的路径
-GLOG_DIR=$(pwd)/deps/glog
 
 # opencv使用自带预编译版本
 OPENCV_DIR=$(pwd)/deps/opencv/
@@ -25,7 +23,6 @@ cd build
 cmake .. \
     -DOPENCV_DIR=${OPENCV_DIR} \
     -DGFLAGS_DIR=${GFLAGS_DIR} \
-    -DGLOG_DIR=${GLOG_DIR} \
     -DOPENVINO_DIR=${OPENVINO_DIR} \
     -DNGRAPH_LIB=${NGRAPH_LIB} \
     -DARCH=${ARCH}

+ 1 - 12
deploy/openvino/scripts/install_third-party.sh

@@ -11,22 +11,11 @@ if [ ! -d "./deps/gflag" ]; then
     cd ..
     cd ..
 fi
-if [ ! -d "./deps/glog" ]; then
-    cd deps
-    git clone https://github.com/google/glog
-    sudo apt-get install autoconf automake libtool
-    cd glog
-    ./autogen.sh
-    ./configure
-    make -j 8
-    cd ..
-    cd ..
-fi
 
 if [ "$ARCH" = "x86" ]; then
     OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/x86opencv/opencv.tar.bz2
 else
-    OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/armopencv/opencv.tar.bz2
+    OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/armlinux/opencv.tar.bz2
 fi
 if [ ! -d "./deps/opencv" ]; then
     cd deps

+ 37 - 29
deploy/openvino/src/paddlex.cpp

@@ -67,9 +67,9 @@ bool Model::load_config(const std::string& cfg_file) {
       return false;
     }
   }
-  // 构建数据处理流
+  // init preprocess ops
   transforms_.Init(config["Transforms"], type, to_rgb);
-  // 读入label lis
+  // read label list
   for (const auto& item : config["_Attributes"]["labels"]) {
     int index = labels.size();
     labels[index] = item.as<std::string>();
@@ -98,7 +98,7 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
               << std::endl;
     return false;
   }
-  // 处理输入图像
+  // preprocess
   InferenceEngine::InferRequest infer_request =
     executable_network_.CreateInferRequest();
   std::string input_name = network_.getInputsInfo().begin()->first;
@@ -109,6 +109,7 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
     return false;
   }
 
+  // predict
   infer_request.Infer();
 
   std::string output_name = network_.getOutputsInfo().begin()->first;
@@ -118,7 +119,7 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
   auto moutputHolder = moutput->rmap();
   float* outputs_data = moutputHolder.as<float *>();
 
-  // 对模型输出结果进行后处理
+  // post process
   auto ptr = std::max_element(outputs_data, outputs_data+sizeof(outputs_data));
   result->category_id = std::distance(outputs_data, ptr);
   result->score = *ptr;
@@ -160,9 +161,17 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
 
   infer_request.Infer();
 
-  InferenceEngine::OutputsDataMap out_map = network_.getOutputsInfo();
-  auto iter = out_map.begin();
-  std::string outputName = iter->first;
+  InferenceEngine::OutputsDataMap out_maps = network_.getOutputsInfo();
+  std::string outputName;
+  for (const auto & output_map : out_maps) {
+    if (output_map.second->getTensorDesc().getDims().size() == 3) {
+      outputName = output_map.first;
+    }
+  }
+  if (outputName.empty()) {
+    std::cerr << "get result node failed!" << std::endl:
+    return false;
+  }
   InferenceEngine::Blob::Ptr output = infer_request.GetBlob(outputName);
   InferenceEngine::MemoryBlob::CPtr moutput =
     InferenceEngine::as<InferenceEngine::MemoryBlob>(output);
@@ -206,43 +215,26 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
                  "function predict()!" << std::endl;
     return false;
   }
-  //
+  // init infer
   InferenceEngine::InferRequest infer_request =
     executable_network_.CreateInferRequest();
   std::string input_name = network_.getInputsInfo().begin()->first;
   inputs_.blob = infer_request.GetBlob(input_name);
 
-  //
+  // preprocess
   cv::Mat im_clone = im.clone();
   if (!preprocess(&im_clone, &inputs_)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
 
-  //
+  // predict
   infer_request.Infer();
 
   InferenceEngine::OutputsDataMap out_map = network_.getOutputsInfo();
   auto iter = out_map.begin();
   iter++;
-  std::string output_name_score = iter->first;
-  InferenceEngine::Blob::Ptr output_score =
-    infer_request.GetBlob(output_name_score);
-  InferenceEngine::MemoryBlob::CPtr moutput_score =
-    InferenceEngine::as<InferenceEngine::MemoryBlob>(output_score);
-  InferenceEngine::TensorDesc blob_score = moutput_score->getTensorDesc();
-  std::vector<size_t> output_score_shape = blob_score.getDims();
-  int size = 1;
-  for (auto& i : output_score_shape) {
-    size *= static_cast<int>(i);
-    result->score_map.shape.push_back(static_cast<int>(i));
-  }
-  result->score_map.data.resize(size);
-  auto moutputHolder_score = moutput_score->rmap();
-  float* score_data = moutputHolder_score.as<float *>();
-  memcpy(result->score_map.data.data(), score_data, moutput_score->byteSize());
 
-  iter++;
   std::string output_name_label = iter->first;
   InferenceEngine::Blob::Ptr output_label =
     infer_request.GetBlob(output_name_label);
@@ -250,7 +242,7 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
     InferenceEngine::as<InferenceEngine::MemoryBlob>(output_label);
   InferenceEngine::TensorDesc blob_label = moutput_label->getTensorDesc();
   std::vector<size_t> output_label_shape = blob_label.getDims();
-  size = 1;
+  int size = 1;
   for (auto& i : output_label_shape) {
     size *= static_cast<int>(i);
     result->label_map.shape.push_back(static_cast<int>(i));
@@ -260,7 +252,23 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
   int* label_data = moutputHolder_label.as<int *>();
   memcpy(result->label_map.data.data(), label_data, moutput_label->byteSize());
 
-
+  iter++;
+  std::string output_name_score = iter->first;
+  InferenceEngine::Blob::Ptr output_score =
+    infer_request.GetBlob(output_name_score);
+  InferenceEngine::MemoryBlob::CPtr moutput_score =
+    InferenceEngine::as<InferenceEngine::MemoryBlob>(output_score);
+  InferenceEngine::TensorDesc blob_score = moutput_score->getTensorDesc();
+  std::vector<size_t> output_score_shape = blob_score.getDims();
+  size = 1;
+  for (auto& i : output_score_shape) {
+    size *= static_cast<int>(i);
+    result->score_map.shape.push_back(static_cast<int>(i));
+  }
+  result->score_map.data.resize(size);
+  auto moutputHolder_score = moutput_score->rmap();
+  float* score_data = moutputHolder_score.as<float *>();
+  memcpy(result->score_map.data.data(), score_data, moutput_score->byteSize());
 
   std::vector<uint8_t> label_map(result->label_map.data.begin(),
                                  result->label_map.data.end());

+ 84 - 22
deploy/openvino/src/transforms.cpp

@@ -31,21 +31,25 @@ std::map<std::string, int> interpolations = {{"LINEAR", cv::INTER_LINEAR},
                                              {"LANCZOS4", cv::INTER_LANCZOS4}};
 
 bool Normalize::Run(cv::Mat* im, ImageBlob* data) {
-  for (int h = 0; h < im->rows; h++) {
-    for (int w = 0; w < im->cols; w++) {
-      im->at<cv::Vec3f>(h, w)[0] =
-          (im->at<cv::Vec3f>(h, w)[0] / 255.0 - mean_[0]) / std_[0];
-      im->at<cv::Vec3f>(h, w)[1] =
-          (im->at<cv::Vec3f>(h, w)[1] / 255.0 - mean_[1]) / std_[1];
-      im->at<cv::Vec3f>(h, w)[2] =
-          (im->at<cv::Vec3f>(h, w)[2] / 255.0 - mean_[2]) / std_[2];
-    }
+  std::vector<float> range_val;
+  for (int c = 0; c < im->channels(); c++) {
+    range_val.push_back(max_val_[c] - min_val_[c]);
+  }
+
+  std::vector<cv::Mat> split_im;
+  cv::split(*im, split_im);
+  for (int c = 0; c < im->channels(); c++) {
+    cv::subtract(split_im[c], cv::Scalar(min_val_[c]), split_im[c]);
+    cv::divide(split_im[c], cv::Scalar(range_val[c]), split_im[c]);
+    cv::subtract(split_im[c], cv::Scalar(mean_[c]), split_im[c]);
+    cv::divide(split_im[c], cv::Scalar(std_[c]), split_im[c]);
   }
+  cv::merge(split_im, *im);
+
   return true;
 }
 
 
-
 float ResizeByShort::GenerateScale(const cv::Mat& im) {
   int origin_w = im.cols;
   int origin_h = im.rows;
@@ -71,6 +75,7 @@ bool ResizeByShort::Run(cv::Mat* im, ImageBlob* data) {
   data->new_im_size_[0] = im->rows;
   data->new_im_size_[1] = im->cols;
   data->scale = scale;
+
   return true;
 }
 
@@ -115,11 +120,22 @@ bool Padding::Run(cv::Mat* im, ImageBlob* data) {
               << ", but they should be greater than 0." << std::endl;
     return false;
   }
-  cv::Scalar value = cv::Scalar(im_value_[0], im_value_[1], im_value_[2]);
-  cv::copyMakeBorder(
-      *im, *im, 0, padding_h, 0, padding_w, cv::BORDER_CONSTANT, value);
+  std::vector<cv::Mat> padded_im_per_channel;
+  for (size_t i = 0; i < im->channels(); i++) {
+    const cv::Mat per_channel = cv::Mat(im->rows + padding_h,
+                                        im->cols + padding_w,
+                                        CV_32FC1,
+                                        cv::Scalar(im_value_[i]));
+    padded_im_per_channel.push_back(per_channel);
+  }
+  cv::Mat padded_im;
+  cv::merge(padded_im_per_channel, padded_im);
+  cv::Rect im_roi = cv::Rect(0, 0, im->cols, im->rows);
+  im->copyTo(padded_im(im_roi));
+  *im = padded_im;
   data->new_im_size_[0] = im->rows;
   data->new_im_size_[1] = im->cols;
+
   return true;
 }
 
@@ -165,6 +181,22 @@ bool Resize::Run(cv::Mat* im, ImageBlob* data) {
   return true;
 }
 
+bool Clip::Run(cv::Mat* im, ImageBlob* data) {
+  std::vector<cv::Mat> split_im;
+  cv::split(*im, split_im);
+  for (int c = 0; c < im->channels(); c++) {
+    cv::threshold(split_im[c], split_im[c], max_val_[c], max_val_[c],
+                  cv::THRESH_TRUNC);
+    cv::subtract(cv::Scalar(0), split_im[c], split_im[c]);
+    cv::threshold(split_im[c], split_im[c], min_val_[c], min_val_[c],
+                  cv::THRESH_TRUNC);
+    cv::divide(split_im[c], cv::Scalar(-1), split_im[c]);
+  }
+  cv::merge(split_im, *im);
+
+  return true;
+}
+
 void Transforms::Init(
   const YAML::Node& transforms_node, std::string type, bool to_rgb) {
   transforms_.clear();
@@ -172,6 +204,21 @@ void Transforms::Init(
   type_ = type;
   for (const auto& item : transforms_node) {
     std::string name = item.begin()->first.as<std::string>();
+    if (name == "ArrangeClassifier") {
+      continue;
+    }
+    if (name == "ArrangeSegmenter") {
+      continue;
+    }
+    if (name == "ArrangeFasterRCNN") {
+      continue;
+    }
+    if (name == "ArrangeMaskRCNN") {
+      continue;
+    }
+    if (name == "ArrangeYOLOv3") {
+      continue;
+    }
     std::cout << "trans name: " << name << std::endl;
     std::shared_ptr<Transform> transform = CreateTransform(name);
     transform->Init(item.begin()->second);
@@ -193,6 +240,8 @@ std::shared_ptr<Transform> Transforms::CreateTransform(
     return std::make_shared<Padding>();
   } else if (transform_name == "ResizeByLong") {
     return std::make_shared<ResizeByLong>();
+  } else if (transform_name == "Clip") {
+    return std::make_shared<Clip>();
   } else {
     std::cerr << "There's unexpected transform(name='" << transform_name
               << "')." << std::endl;
@@ -201,11 +250,11 @@ std::shared_ptr<Transform> Transforms::CreateTransform(
 }
 
 bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
-  // 按照transforms中预处理算子顺序处理图像
+  // preprocess by order
   if (to_rgb_) {
     cv::cvtColor(*im, *im, cv::COLOR_BGR2RGB);
   }
-  (*im).convertTo(*im, CV_32FC3);
+  (*im).convertTo(*im, CV_32FC(im->channels()));
   if (type_ == "detector") {
     InferenceEngine::LockedMemory<void> input2Mapped =
       InferenceEngine::as<InferenceEngine::MemoryBlob>(
@@ -224,8 +273,8 @@ bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
     }
   }
 
-  // 将图像由NHWC转为NCHW格式
-  // 同时转为连续的内存块存储到Blob
+  // image format NHWC to NCHW
+  // img data save to ImageBlob
   InferenceEngine::SizeVector blobSize = data->blob->getTensorDesc().getDims();
   const size_t width = blobSize[3];
   const size_t height = blobSize[2];
@@ -234,14 +283,27 @@ bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
     InferenceEngine::as<InferenceEngine::MemoryBlob>(data->blob);
   auto mblobHolder = mblob->wmap();
   float *blob_data = mblobHolder.as<float *>();
-  for (size_t c = 0; c < channels; c++) {
+  if (channels == 3) {
+    for (size_t c = 0; c < channels; c++) {
       for (size_t  h = 0; h < height; h++) {
-          for (size_t w = 0; w < width; w++) {
-              blob_data[c * width * height + h * width + w] =
-                      im->at<cv::Vec3f>(h, w)[c];
-          }
+        for (size_t w = 0; w < width; w++) {
+          blob_data[c * width * height + h * width + w] =
+            im->at<cv::Vec3f>(h, w)[c];
+        }
       }
+    }
+  } else {
+    for (size_t  h = 0; h < height; h++) {
+      float *pixelPtr = im->ptr<float>(h);
+      for (size_t w = 0; w < width; w++) {
+        for (size_t c = 0; c < channels; c++) {
+          blob_data[c * width * height + h * width + w] =
+            pixelPtr[w*channels + c];
+        }
+      }
+    }
   }
+
   return true;
 }
 }  // namespace PaddleX

+ 4 - 4
deploy/openvino/src/visualize.cpp

@@ -47,7 +47,7 @@ cv::Mat Visualize(const cv::Mat& img,
                             boxes[i].coordinate[2],
                             boxes[i].coordinate[3]);
 
-    // 生成预测框和标题
+    // draw box and title
     std::string text = boxes[i].category;
     int c1 = colormap[3 * boxes[i].category_id + 0];
     int c2 = colormap[3 * boxes[i].category_id + 1];
@@ -63,13 +63,13 @@ cv::Mat Visualize(const cv::Mat& img,
     origin.x = roi.x;
     origin.y = roi.y;
 
-    // 生成预测框标题的背景
+    // background
     cv::Rect text_back = cv::Rect(boxes[i].coordinate[0],
                                   boxes[i].coordinate[1] - text_size.height,
                                   text_size.width,
                                   text_size.height);
 
-    // 绘图和文字
+    // draw
     cv::rectangle(vis_img, roi, roi_color, 2);
     cv::rectangle(vis_img, text_back, roi_color, -1);
     cv::putText(vis_img,
@@ -80,7 +80,7 @@ cv::Mat Visualize(const cv::Mat& img,
                 cv::Scalar(255, 255, 255),
                 thickness);
 
-    // 生成实例分割mask
+    // mask
     if (boxes[i].mask.data.size() == 0) {
       continue;
     }

+ 3 - 4
deploy/raspberry/CMakeLists.txt

@@ -58,13 +58,12 @@ if (WIN32)
   find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/build/ NO_DEFAULT_PATH)
   unset(OpenCV_DIR CACHE)
 else ()
-  find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/cmake NO_DEFAULT_PATH)
+	find_package(OpenCV REQUIRED PATHS ${OPENCV_DIR}/share/OpenCV NO_DEFAULT_PATH)
 endif ()
 
 include_directories(${OpenCV_INCLUDE_DIRS})
 
 if (WIN32)
-    add_definitions("/DGOOGLE_GLOG_DLL_DECL=")
     set(CMAKE_C_FLAGS_DEBUG   "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd")
     set(CMAKE_C_FLAGS_RELEASE  "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT")
     set(CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd")
@@ -87,11 +86,11 @@ endif()
 
 if (NOT WIN32)
     set(DEPS ${DEPS}
-        glog gflags  z  yaml-cpp
+        gflags yaml-cpp
         )
 else()
     set(DEPS ${DEPS}
-        glog gflags_static libprotobuf zlibstatic xxhash libyaml-cppmt)
+        gflags_static libprotobuf zlibstatic xxhash libyaml-cppmt)
     set(DEPS ${DEPS} libcmt shlwapi)
 endif(NOT WIN32)
 

+ 0 - 1
deploy/raspberry/cmake/yaml-cpp.cmake

@@ -1,4 +1,3 @@
-find_package(Git REQUIRED)
 
 include(ExternalProject)
 

+ 3 - 3
deploy/raspberry/demo/classifier.cpp

@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
+#include <gflags/gflags.h>
 
 #include <fstream>
 #include <iostream>
@@ -44,11 +44,11 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  // 加载模型
+  // load model
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_thread_num);
   std::cout << "init is done" << std::endl;
-  // 进行预测
+  // predict
   if (FLAGS_image_list != "") {
     std::ifstream inf(FLAGS_image_list);
     if (!inf) {

+ 4 - 5
deploy/raspberry/demo/detector.cpp

@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
-#include <omp.h>
+#include <gflags/gflags.h>
 
 #include <algorithm>
 #include <chrono>  // NOLINT
@@ -54,13 +53,13 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  //
+  // load model
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_thread_num);
 
   int imgs = 1;
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
-  // 进行预测
+  // predict
   if (FLAGS_image_list != "") {
     std::ifstream inf(FLAGS_image_list);
     if (!inf) {
@@ -97,7 +96,7 @@ int main(int argc, char** argv) {
                 << result.boxes[i].coordinate[3] << ")" << std::endl;
     }
     if (FLAGS_save_dir != "") {
-    // 可视化
+    // visualize
       cv::Mat vis_img = PaddleX::Visualize(
         im, result, model.labels, colormap, FLAGS_threshold);
       std::string save_path =

+ 2 - 2
deploy/raspberry/demo/segmenter.cpp

@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
+#include <gflags/gflags.h>
 
 #include <algorithm>
 #include <fstream>
@@ -47,7 +47,7 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  //
+  // load model
   std::cout << "init start" << std::endl;
   PaddleX::Model model;
   model.Init(FLAGS_model_dir, FLAGS_cfg_file, FLAGS_thread_num);

+ 1 - 11
deploy/raspberry/python/demo.py

@@ -46,13 +46,6 @@ def arg_parser():
         default=1,
         help="Path to PaddelX model yml file")
 
-    parser.add_argument(
-        "--input_shape",
-        "-ip",
-        type=str,
-        default=None,
-        help=" image input shape of model [NCHW] like [1,3,224,244] ")
-
     return parser
 
 
@@ -62,11 +55,8 @@ def main():
     model_nb = args.model_dir
     model_yaml = args.cfg_file
     thread_num = args.thread_num
-    input_shape = args.input_shape
-    input_shape = input_shape[1:-1].split(",", 3)
-    shape = list(map(int, input_shape))
     #model init
-    predictor = deploy.Predictor(model_nb, model_yaml, thread_num, shape)
+    predictor = deploy.Predictor(model_nb, model_yaml, thread_num)
 
     #predict
     if (args.img_list != None):

+ 222 - 0
deploy/raspberry/python/deploy.py

@@ -0,0 +1,222 @@
+# copyright (c) 2020 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 sys
+import os
+import os.path as osp
+import time
+import cv2
+import numpy as np
+import yaml
+from six import text_type as _text_type
+from paddlelite.lite import *
+
+
+class Predictor:
+    def __init__(self, model_nb, model_yaml, thread_num):
+        if not osp.exists(model_nb):
+            print("model nb file is not exists in {}".format(model_xml))
+        self.model_nb = model_nb
+        config = MobileConfig()
+        config.set_model_from_file(model_nb)
+        config.set_threads(thread_num)
+        if not osp.exists(model_yaml):
+            print("model yaml file is not exists in {}".format(model_yaml))
+        with open(model_yaml) as f:
+            self.info = yaml.load(f.read(), Loader=yaml.Loader)
+        self.model_type = self.info['_Attributes']['model_type']
+        self.model_name = self.info['Model']
+        self.num_classes = self.info['_Attributes']['num_classes']
+        self.labels = self.info['_Attributes']['labels']
+        if self.info['Model'] == 'MaskRCNN':
+            if self.info['_init_params']['with_fpn']:
+                self.mask_head_resolution = 28
+            else:
+                self.mask_head_resolution = 14
+        transforms_mode = self.info.get('TransformsMode', 'RGB')
+        if transforms_mode == 'RGB':
+            to_rgb = True
+        else:
+            to_rgb = False
+        self.transforms = self.build_transforms(self.info['Transforms'],
+                                                to_rgb)
+        self.predictor = create_paddle_predictor(config)
+        self.total_time = 0
+        self.count_num = 0
+
+    def build_transforms(self, transforms_info, to_rgb=True):
+        if self.model_type == "classifier":
+            import transforms.cls_transforms as transforms
+        elif self.model_type == "detector":
+            import transforms.det_transforms as transforms
+        elif self.model_type == "segmenter":
+            import transforms.seg_transforms as transforms
+        op_list = list()
+        for op_info in transforms_info:
+            op_name = list(op_info.keys())[0]
+            op_attr = op_info[op_name]
+            if not hasattr(transforms, op_name):
+                raise Exception(
+                    "There's no operator named '{}' in transforms of {}".
+                    format(op_name, self.model_type))
+            op_list.append(getattr(transforms, op_name)(**op_attr))
+        eval_transforms = transforms.Compose(op_list)
+        if hasattr(eval_transforms, 'to_rgb'):
+            eval_transforms.to_rgb = to_rgb
+        self.arrange_transforms(eval_transforms)
+        return eval_transforms
+
+    def arrange_transforms(self, eval_transforms):
+        if self.model_type == 'classifier':
+            import transforms.cls_transforms as transforms
+            arrange_transform = transforms.ArrangeClassifier
+        elif self.model_type == 'segmenter':
+            import transforms.seg_transforms as transforms
+            arrange_transform = transforms.ArrangeSegmenter
+        elif self.model_type == 'detector':
+            import transforms.det_transforms as transforms
+            arrange_name = 'Arrange{}'.format(self.model_name)
+            arrange_transform = getattr(transforms, arrange_name)
+        else:
+            raise Exception("Unrecognized model type: {}".format(
+                self.model_type))
+        if type(eval_transforms.transforms[-1]).__name__.startswith('Arrange'):
+            eval_transforms.transforms[-1] = arrange_transform(mode='test')
+        else:
+            eval_transforms.transforms.append(arrange_transform(mode='test'))
+
+    def raw_predict(self, preprocessed_input):
+        self.count_num += 1
+        input_tensor = self.predictor.get_input(0)
+        im_shape = preprocessed_input['image'].shape
+        input_tensor.resize(self.shape)
+        input_tensor.set_float_data(preprocessed_input['image'])
+        if self.model_name == "YOLOv3":
+            input_size_tensor = self.predictor.get_input(1)
+            input_size_tensor.resize([1, 2])
+            input_size_tensor.set_float_data(preprocessed_input['im_size'])
+        #Start  inference
+        start_time = time.time()
+        self.predictor.run()
+        time_use = time.time() - start_time
+        if (self.count_num >= 20):
+            self.total_time += time_use
+        if (self.count_num >= 120):
+            print("avgtime:", self.total_time * 10)
+
+        #Processing output blob
+        print("Processing output blob")
+        return
+
+    def preprocess(self, image):
+        res = dict()
+        if self.model_type == "classifier":
+            im, = self.transforms(image)
+            self.shape = [1] + list(im.shape)
+            im = np.expand_dims(im, axis=0).copy()
+            im = im.flatten()
+            res['image'] = im
+        elif self.model_type == "detector":
+            if self.model_name == "YOLOv3":
+                im, im_shape = self.transforms(image)
+                self.shape = [1] + list(im.shape)
+                im = np.expand_dims(im, axis=0).copy()
+                im_shape = np.expand_dims(im_shape, axis=0).copy()
+                im = im.flatten()
+                im_shape = im_shape.flatten()
+                res['image'] = im
+                res['im_size'] = im_shape
+            if self.model_name.count('RCNN') > 0:
+                im, im_resize_info, im_shape = self.transforms(image)
+                self.shape = [1] + list(im.shape)
+                im = np.expand_dims(im, axis=0).copy()
+                im_resize_info = np.expand_dims(im_resize_info, axis=0).copy()
+                im_shape = np.expand_dims(im_shape, axis=0).copy()
+                res['image'] = im
+                res['im_info'] = im_resize_info
+                res['im_shape'] = im_shape
+        elif self.model_type == "segmenter":
+            im, im_info = self.transforms(image)
+            self.shape = [1] + list(im.shape)
+            im = np.expand_dims(im, axis=0).copy()
+            #np.savetxt('./input_data.txt',im.flatten())
+            res['image'] = im
+            res['im_info'] = im_info
+        return res
+
+    def classifier_postprocess(self, topk=1):
+        output_tensor = self.predictor.get_output(0)
+        output_data = output_tensor.float_data()
+        true_topk = min(self.num_classes, topk)
+        pred_label = np.argsort(-np.array(output_data))[:true_topk]
+        result = [{
+            'category_id': l,
+            'category': self.labels[l],
+            'score': output_data[l],
+        } for l in pred_label]
+        print(result)
+        return result
+
+    def segmenter_postprocess(self, preprocessed_inputs):
+        out_label_tensor = self.predictor.get_output(0)
+        out_label = out_label_tensor.float_data()
+        label_shape = tuple(out_label_tensor.shape())
+        label_map = np.array(out_label).astype('uint8')
+        label_map = label_map.reshape(label_shape)
+        label_map = np.squeeze(label_map)
+
+        out_score_tensor = self.predictor.get_output(1)
+        out_score = out_score_tensor.float_data()
+        score_shape = tuple(out_score_tensor.shape())
+        score_map = np.array(out_score)
+        score_map = score_map.reshap(score_shape)
+        score_map = np.transpose(score_map, (1, 2, 0))
+
+        im_info = preprocessed_inputs['im_info']
+        for info in im_info[::-1]:
+            if info[0] == 'resize':
+                w, h = info[1][1], info[1][0]
+                label_map = cv2.resize(label_map, (w, h), cv2.INTER_NEAREST)
+                score_map = cv2.resize(score_map, (w, h), cv2.INTER_LINEAR)
+            elif info[0] == 'padding':
+                w, h = info[1][1], info[1][0]
+                label_map = label_map[0:h, 0:w]
+                score_map = score_map[0:h, 0:w, :]
+            else:
+                raise Exception("Unexpected info '{}' in im_info".format(info[
+                    0]))
+        return {'label_map': label_map, 'score_map': score_map}
+
+    def detector_postprocess(self, preprocessed_inputs):
+        out_tensor = self.predictor.get_output(0)
+        out_data = out_tensor.float_data()
+        out_shape = tuple(out_tensor.shape())
+        out_data = np.array(out_data)
+        outputs = out_data.reshape(out_shape)
+
+        result = []
+        for out in outputs:
+            result.append(out.tolist())
+        #print(result)
+        return result
+
+    def predict(self, image, topk=1, threshold=0.5):
+        preprocessed_input = self.preprocess(image)
+        self.raw_predict(preprocessed_input)
+        if self.model_type == "classifier":
+            results = self.classifier_postprocess(topk)
+        elif self.model_type == "detector":
+            results = self.detector_postprocess(preprocessed_input)
+        elif self.model_type == "segmenter":
+            results = self.segmenter_postprocess(preprocessed_input)

+ 0 - 2
deploy/raspberry/scripts/build.sh

@@ -3,8 +3,6 @@ LITE_DIR=/path/to/Paddle-Lite/inference/lib
 
 # gflags预编译库的路径
 GFLAGS_DIR=$(pwd)/deps/gflags
-# glog预编译库的路径
-GLOG_DIR=$(pwd)/deps/glog
 
 # opencv预编译库的路径, 如果使用自带预编译版本可不修改
 OPENCV_DIR=$(pwd)/deps/opencv

+ 1 - 12
deploy/raspberry/scripts/install_third-party.sh

@@ -11,18 +11,7 @@ if [ ! -d "./deps/gflag" ]; then
     cd ..
     cd ..
 fi
-if [ ! -d "./deps/glog" ]; then
-    cd deps
-    git clone https://github.com/google/glog
-    sudo apt-get install autoconf automake libtool
-    cd glog
-    ./autogen.sh
-    ./configure
-    make -j 4
-    cd ..
-    cd ..
-fi
-OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/armopencv/opencv.tar.bz2
+OPENCV_URL=https://bj.bcebos.com/paddlex/deploy/armlinux/opencv.tar.bz2
 if [ ! -d "./deps/opencv" ]; then
     cd deps
     wget -c ${OPENCV_URL}

+ 10 - 13
deploy/raspberry/src/paddlex.cpp

@@ -46,9 +46,9 @@ bool Model::load_config(const std::string& cfg_file) {
       return false;
     }
   }
-  // 构建数据处理流
+  // init preprocess ops
   transforms_.Init(config["Transforms"], to_rgb);
-  // 读入label lis
+  // read label list
   for (const auto& item : config["_Attributes"]["labels"]) {
     int index = labels.size();
     labels[index] = item.as<std::string>();
@@ -77,14 +77,14 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
               << std::endl;
     return false;
   }
-  // 处理输入图像
+  // preprocess
   inputs_.input_tensor_ = std::move(predictor_->GetInput(0));
   cv::Mat im_clone = im.clone();
   if (!preprocess(&im_clone, &inputs_)) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
-
+  // predict
   predictor_->Run();
 
   std::unique_ptr<const paddle::lite_api::Tensor> output_tensor(
@@ -92,7 +92,7 @@ bool Model::predict(const cv::Mat& im, ClsResult* result) {
   const float *outputs_data = output_tensor->mutable_data<float>();
 
 
-  // 对模型输出结果进行后处理
+  // postprocess
   auto ptr = std::max_element(outputs_data, outputs_data+sizeof(outputs_data));
   result->category_id = std::distance(outputs_data, ptr);
   result->score = *ptr;
@@ -118,26 +118,23 @@ bool Model::predict(const cv::Mat& im, DetResult* result) {
     std::cerr << "Preprocess failed!" << std::endl;
     return false;
   }
-  int h = inputs_.new_im_size_[0];
-  int w = inputs_.new_im_size_[1];
   if (name == "YOLOv3") {
     std::unique_ptr<paddle::lite_api::Tensor> im_size_tensor(
       std::move(predictor_->GetInput(1)));
-    const std::vector<int64_t> IM_SIZE_SHAPE = {1, 2};
-    im_size_tensor->Resize(IM_SIZE_SHAPE);
+    im_size_tensor->Resize({1,2});
     auto *im_size_data = im_size_tensor->mutable_data<int>();
     memcpy(im_size_data, inputs_.ori_im_size_.data(), 1*2*sizeof(int));
   }
   predictor_->Run();
   auto output_names = predictor_->GetOutputNames();
   auto output_box_tensor = predictor_->GetTensor(output_names[0]);
-  const float *output_box = output_box_tensor->mutable_data<float>();
-  std::vector<int64_t> output_box_shape = output_box_tensor->shape();
-  int size = 1;
+  auto *output_box = output_box_tensor->mutable_data<float>();
+  auto output_box_shape = output_box_tensor->shape();
+  int64_t size = 1;
   for (const auto& i : output_box_shape) {
     size *= i;
   }
-  int num_boxes = size / 6;
+  auto num_boxes = static_cast<int>(size / 6);
   for (int i = 0; i < num_boxes; ++i) {
     Box box;
     box.category_id = static_cast<int>(round(output_box[i * 6]));

+ 3 - 3
deploy/raspberry/src/transforms.cpp

@@ -201,7 +201,7 @@ std::shared_ptr<Transform> Transforms::CreateTransform(
 }
 
 bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
-  // 按照transforms中预处理算子顺序处理图像
+  // preprocess by order
   if (to_rgb_) {
     cv::cvtColor(*im, *im, cv::COLOR_BGR2RGB);
   }
@@ -218,8 +218,8 @@ bool Transforms::Run(cv::Mat* im, ImageBlob* data) {
     }
   }
 
-  // 将图像由NHWC转为NCHW格式
-  // 同时转为连续的内存块存储到Blob
+  // image format NHWC to NCHW
+  // img data save to ImageBlob
   int height = im->rows;
   int width = im->cols;
   int channels = im->channels();

+ 48 - 0
docs/apis/analysis.md

@@ -0,0 +1,48 @@
+# 数据集分析
+
+## paddlex.datasets.analysis.Seg
+```python
+paddlex.datasets.analysis.Seg(data_dir, file_list, label_list)
+```
+
+构建统计分析语义分类数据集的分析器。
+
+> **参数**
+> > * **data_dir** (str): 数据集所在的目录路径。  
+> > * **file_list** (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对`data_dir`的相对路径)。  
+> > * **label_list** (str): 描述数据集包含的类别信息文件路径。  
+
+### analysis
+```python
+analysis(self)
+```
+
+Seg分析器的分析接口,完成以下信息的分析统计:
+
+> * 图像数量
+> * 图像最大和最小的尺寸
+> * 图像通道数量
+> * 图像各通道的最小值和最大值
+> * 图像各通道的像素值分布
+> * 图像各通道归一化后的均值和方差
+> * 标注图中各类别的数量及比重
+
+[代码示例](https://github.com/PaddlePaddle/PaddleX/blob/develop/examples/multi-channel_remote_sensing/tools/analysis.py)
+
+[统计信息示例](../../examples/multi-channel_remote_sensing/analysis.html#id2)
+
+### cal_clipped_mean_std
+```python
+cal_clipped_mean_std(self, clip_min_value, clip_max_value, data_info_file)
+```
+
+Seg分析器用于计算图像截断后的均值和方差的接口。
+
+> **参数**
+> > * **clip_min_value** (list):  截断的下限,小于min_val的数值均设为min_val。
+> > * **clip_max_value** (list): 截断的上限,大于max_val的数值均设为max_val。
+> > * **data_info_file** (str): 在analysis()接口中保存的分析结果文件(名为`train_information.pkl`)的路径。
+
+[代码示例](https://github.com/PaddlePaddle/PaddleX/blob/develop/examples/multi-channel_remote_sensing/tools/cal_clipped_mean_std.py)
+
+[计算结果示例](../../examples/multi-channel_remote_sensing/analysis.html#id4)

+ 21 - 0
docs/apis/datasets.md

@@ -140,3 +140,24 @@ paddlex.datasets.EasyDataSeg(data_dir, file_list, label_list, transforms=None, n
 > > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
 > > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
+
+## paddlex.datasets.ChangeDetDataset
+> **用于完成变化检测的语义分割模型**  
+```
+paddlex.datasets.ChangeDetDataset(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='process', shuffle=False)
+```
+
+> 读取用于完成变化检测的语义分割数据集,并对样本进行相应的处理。地块检测数据集格式的介绍可查看文档:[数据集格式说明](../data/format/change_det.md)  
+
+> 示例:[代码文件](https://github.com/PaddlePaddle/PaddleX/blob/develop/examples/change_detection/train.py)
+
+> **参数**
+
+> > * **data_dir** (str): 数据集所在的目录路径。  
+> > * **file_list** (str): 描述数据集图片1文件、图片2文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
+> > * **label_list** (str): 描述数据集包含的类别信息文件路径。  
+> > * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。  
+> > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
+> > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
+> > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
+> > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。

+ 2 - 4
docs/apis/deploy.md

@@ -33,7 +33,7 @@ paddlex.deploy.Predictor(model_dir, use_gpu=False, gpu_id=0, use_mkl=False, mkl_
 ### predict 接口
 
 ```
-predict(image, topk=1)
+predict(image)
 ```
 
 单张图片预测接口。
@@ -41,11 +41,10 @@ predict(image, topk=1)
 > **参数**
 >
 > > * **image** (str|np.ndarray): 待预测的图片路径或numpy数组(HWC排列,BGR格式)。
-> > * **topk** (int): 图像分类时使用的参数,表示预测前topk个可能的分类。
 
 ### batch_predict 接口
 ```
-batch_predict(image_list, topk=1, thread_num=2)
+batch_predict(image_list)
 ```
 批量图片预测接口。
 
@@ -53,4 +52,3 @@ batch_predict(image_list, topk=1, thread_num=2)
 >
 > > * **image_list** (list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素可以是图像路径或numpy数组(HWC排列,BGR格式)。
 > > * **topk** (int): 图像分类时使用的参数,表示预测前topk个可能的分类。
-> > * **thread_num** (int): 并发执行各图像预处理时的线程数。

+ 1 - 0
docs/apis/index.rst

@@ -6,6 +6,7 @@ API接口说明
 
    transforms/index.rst
    datasets.md
+   analysis.md
    models/index.rst
    slim.md
    visualize.md

+ 4 - 0
docs/apis/interpret.md

@@ -23,8 +23,12 @@ LIME表示与模型无关的局部可解释性,可以解释任何模型。LIME
 >* **batch_size** (int): 预测数据batch大小,默认为50。
 >* **save_dir** (str): 可解释性可视化结果(保存为png格式文件)和中间文件存储路径。
 
+### 可视化效果
+
+![](./docs/gui/images/LIME.png)
 
 ### 使用示例
+
 > 对预测可解释性结果可视化的过程可参见[代码](https://github.com/PaddlePaddle/PaddleX/blob/develop/tutorials/interpret/lime.py)。
 
 

+ 2 - 3
docs/apis/models/classification.md

@@ -62,7 +62,7 @@ evaluate(self, eval_dataset, batch_size=1, epoch_id=None, return_details=False)
 ### predict
 
 ```python
-predict(self, img_file, transforms=None, topk=5)
+predict(self, img_file, transforms=None, topk=1)
 ```
 
 > 分类模型预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`ResNet50.test_transforms`和`ResNet50.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`predict`接口时,用户需要再重新定义test_transforms传入给`predict`接口。
@@ -81,7 +81,7 @@ predict(self, img_file, transforms=None, topk=5)
 ### batch_predict
 
 ```python
-batch_predict(self, img_file_list, transforms=None, topk=5, thread_num=2)
+batch_predict(self, img_file_list, transforms=None, topk=1)
 ```
 
 > 分类模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`ResNet50.test_transforms`和`ResNet50.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义test_transforms传入给`batch_predict`接口。
@@ -91,7 +91,6 @@ batch_predict(self, img_file_list, transforms=None, topk=5, thread_num=2)
 > > - **img_file_list** (list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素可以是图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.cls.transforms): 数据预处理操作。
 > > - **topk** (int): 预测时前k个最大值。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 
 > **返回值**
 >

+ 3 - 6
docs/apis/models/detection.md

@@ -108,7 +108,7 @@ predict(self, img_file, transforms=None)
 ### batch_predict
 
 ```python
-batch_predict(self, img_file_list, transforms=None, thread_num=2)
+batch_predict(self, img_file_list, transforms=None)
 ```
 
 > PPYOLO模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`YOLOv3.test_transforms`和`YOLOv3.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义`test_transforms`传入给`batch_predict`接口
@@ -117,7 +117,6 @@ batch_predict(self, img_file_list, transforms=None, thread_num=2)
 >
 > > - **img_file_list** (str|np.ndarray): 对列表(或元组)中的图像同时进行预测,列表中的元素是预测图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.det.transforms): 数据预处理操作。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 >
 > **返回值**
 >
@@ -222,7 +221,7 @@ predict(self, img_file, transforms=None)
 ### batch_predict
 
 ```python
-batch_predict(self, img_file_list, transforms=None, thread_num=2)
+batch_predict(self, img_file_list, transforms=None)
 ```
 
 > YOLOv3模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`YOLOv3.test_transforms`和`YOLOv3.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义`test_transforms`传入给`batch_predict`接口
@@ -231,7 +230,6 @@ batch_predict(self, img_file_list, transforms=None, thread_num=2)
 >
 > > - **img_file_list** (str|np.ndarray): 对列表(或元组)中的图像同时进行预测,列表中的元素是预测图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.det.transforms): 数据预处理操作。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 >
 > **返回值**
 >
@@ -327,7 +325,7 @@ predict(self, img_file, transforms=None)
 ### batch_predict
 
 ```python
-batch_predict(self, img_file_list, transforms=None, thread_num=2)
+batch_predict(self, img_file_list, transforms=None)
 ```
 
 > FasterRCNN模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`FasterRCNN.test_transforms`和`FasterRCNN.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义test_transforms传入给`batch_predict`接口。
@@ -336,7 +334,6 @@ batch_predict(self, img_file_list, transforms=None, thread_num=2)
 >
 > > - **img_file_list** (list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素是预测图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.det.transforms): 数据预处理操作。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 >
 > **返回值**
 >

+ 1 - 2
docs/apis/models/instance_segmentation.md

@@ -88,7 +88,7 @@ predict(self, img_file, transforms=None)
 #### batch_predict
 
 ```python
-batch_predict(self, img_file_list, transforms=None, thread_num=2)
+batch_predict(self, img_file_list, transforms=None)
 ```
 
 > MaskRCNN模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`FasterRCNN.test_transforms`和`FasterRCNN.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义test_transforms传入给`batch_predict`接口。
@@ -97,7 +97,6 @@ batch_predict(self, img_file_list, transforms=None, thread_num=2)
 >
 > > - **img_file_list** (list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素可以是预测图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.det.transforms): 数据预处理操作。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 >
 > **返回值**
 >

+ 39 - 7
docs/apis/models/semantic_segmentation.md

@@ -3,8 +3,7 @@
 ## paddlex.seg.DeepLabv3p
 
 ```python
-paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride=16, aspp_with_sep_conv=True, decoder_use_sep_conv=True, encoder_with_aspp=True, enable_decoder=True, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, pooling_crop_size=None)
-
+paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride=16, aspp_with_sep_conv=True, decoder_use_sep_conv=True, encoder_with_aspp=True, enable_decoder=True, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, pooling_crop_size=None, input_channel=3)
 ```
 
 > 构建DeepLabv3p分割器。
@@ -23,6 +22,7 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride
 > > - **class_weight** (list/str): 交叉熵损失函数各类损失的权重。当`class_weight`为list的时候,长度应为`num_classes`。当`class_weight`为str时, weight.lower()应为'dynamic',这时会根据每一轮各类像素的比重自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,即平时使用的交叉熵损失函数。
 > > - **ignore_index** (int): label上忽略的值,label为`ignore_index`的像素不参与损失函数的计算。默认255。
 > > - **pooling_crop_size** (int):当backbone为`MobileNetV3_large_x1_0_ssld`时,需设置为训练过程中模型输入大小,格式为[W, H]。例如模型输入大小为[512, 512], 则`pooling_crop_size`应该设置为[512, 512]。在encoder模块中获取图像平均值时被用到,若为None,则直接求平均值;若为模型输入大小,则使用`avg_pool`算子得到平均值。默认值None。
+> > - **input_channel** (int): 输入图像通道数。默认值3。
 
 ### train
 
@@ -95,7 +95,7 @@ predict(self, img_file, transforms=None):
 ### batch_predict
 
 ```
-batch_predict(self, img_file_list, transforms=None, thread_num=2):
+batch_predict(self, img_file_list, transforms=None):
 ```
 
 > DeepLabv3p模型批量预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`DeepLabv3p.test_transforms`和`DeepLabv3p.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`batch_predict`接口时,用户需要再重新定义test_transforms传入给`batch_predict`接口。
@@ -104,18 +104,44 @@ batch_predict(self, img_file_list, transforms=None, thread_num=2):
 > >
 > > - **img_file_list** (list|tuple): 对列表(或元组)中的图像同时进行预测,列表中的元素可以是预测图像路径或numpy数组(HWC排列,BGR格式)。
 > > - **transforms** (paddlex.seg.transforms): 数据预处理操作。
-> > - **thread_num** (int): 并发执行各图像预处理时的线程数。
 
 > **返回值**
 > >
 > > - **dict**: 每个元素都为列表,表示各图像的预测结果。各图像的预测结果用字典表示,包含关键字'label_map'和'score_map', 'label_map'存储预测结果灰度图,像素值表示对应的类别,'score_map'存储各类别的概率,shape=(h, w, num_classes)。
 
 
+### overlap_tile_predict
+
+```
+overlap_tile_predict(self, img_file, tile_size=[512, 512], pad_size=[64, 64], batch_size=32, transforms=None)
+```
+
+> DeepLabv3p模型的滑动预测接口, 支持有重叠和无重叠两种方式。
+
+> **无重叠的滑动窗口预测**:在输入图片上以固定大小的窗口滑动,分别对每个窗口下的图像进行预测,最后将各窗口的预测结果拼接成输入图片的预测结果。**使用时需要把参数`pad_size`设置为`[0, 0]`**。
+
+> **有重叠的滑动窗口预测**:在Unet论文中,作者提出一种有重叠的滑动窗口预测策略(Overlap-tile strategy)来消除拼接处的裂痕感。对各滑动窗口预测时,会向四周扩展一定的面积,对扩展后的窗口进行预测,例如下图中的蓝色部分区域,到拼接时只取各窗口中间部分的预测结果,例如下图中的黄色部分区域。位于输入图像边缘处的窗口,其扩展面积下的像素则通过将边缘部分像素镜像填补得到。
+
+![](../../../examples/remote_sensing/images/overlap_tile.png)
+
+> 需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在`DeepLabv3p.test_transforms`和`DeepLabv3p.eval_transforms`中。如未在训练时定义eval_dataset,那在调用预测`overlap_tile_predict`接口时,用户需要再重新定义test_transforms传入给`overlap_tile_predict`接口。
+
+> **参数**
+> >
+> > - **img_file** (str|np.ndarray): 预测图像路径或numpy数组(HWC排列,BGR格式)。
+> > - **tile_size** (list|tuple): 滑动窗口的大小,该区域内用于拼接预测结果,格式为(W,H)。默认值为[512, 512]。
+> > - **pad_size** (list|tuple): 滑动窗口向四周扩展的大小,扩展区域内不用于拼接预测结果,格式为(W,H)。默认值为[64, 64]。
+> > - **batch_size** (int):对窗口进行批量预测时的批量大小。默认值为32。
+> > - **transforms** (paddlex.seg.transforms): 数据预处理操作。
+
+> **返回值**
+> >
+> > - **dict**: 包含关键字'label_map'和'score_map', 'label_map'存储预测结果灰度图,像素值表示对应的类别,'score_map'存储各类别的概率,shape=(h, w, num_classes)。
 
 ## paddlex.seg.UNet
 
 ```python
-paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255)
+paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, input_channel=3)
 ```
 
 > 构建UNet分割器。
@@ -128,16 +154,18 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us
 > > - **use_dice_loss** (bool): 是否使用dice loss作为网络的损失函数,只能用于两类分割,可与bce loss同时使用。当use_bce_loss和use_dice_loss都为False时,使用交叉熵损失函数。默认False。
 > > - **class_weight** (list/str): 交叉熵损失函数各类损失的权重。当`class_weight`为list的时候,长度应为`num_classes`。当`class_weight`为str时, weight.lower()应为'dynamic',这时会根据每一轮各类像素的比重自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,即平时使用的交叉熵损失函数。
 > > - **ignore_index** (int): label上忽略的值,label为`ignore_index`的像素不参与损失函数的计算。默认255。
+> > - **input_channel** (int): 输入图像通道数。默认值3。
 
 > - train 训练接口说明同 [DeepLabv3p模型train接口](#train)
 > - evaluate 评估接口说明同 [DeepLabv3p模型evaluate接口](#evaluate)
 > - predict 预测接口说明同 [DeepLabv3p模型predict接口](#predict)
 > - batch_predict 批量预测接口说明同 [DeepLabv3p模型predict接口](#batch-predict)
+> - overlap_tile_predict 滑动窗口预测接口同 [DeepLabv3p模型poverlap_tile_predict接口](#overlap-tile-predict)
 
 ## paddlex.seg.HRNet
 
 ```python
-paddlex.seg.HRNet(num_classes=2, width=18, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255)
+paddlex.seg.HRNet(num_classes=2, width=18, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, input_channel=3)
 ```
 
 > 构建HRNet分割器。
@@ -150,16 +178,18 @@ paddlex.seg.HRNet(num_classes=2, width=18, use_bce_loss=False, use_dice_loss=Fal
 > > - **use_dice_loss** (bool): 是否使用dice loss作为网络的损失函数,只能用于两类分割,可与bce loss同时使用。当use_bce_loss和use_dice_loss都为False时,使用交叉熵损失函数。默认False。
 > > - **class_weight** (list|str): 交叉熵损失函数各类损失的权重。当`class_weight`为list的时候,长度应为`num_classes`。当`class_weight`为str时, weight.lower()应为'dynamic',这时会根据每一轮各类像素的比重自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,即平时使用的交叉熵损失函数。
 > > - **ignore_index** (int): label上忽略的值,label为`ignore_index`的像素不参与损失函数的计算。默认255。
+> > - **input_channel** (int): 输入图像通道数。默认值3。
 
 > - train 训练接口说明同 [DeepLabv3p模型train接口](#train)
 > - evaluate 评估接口说明同 [DeepLabv3p模型evaluate接口](#evaluate)
 > - predict 预测接口说明同 [DeepLabv3p模型predict接口](#predict)
 > - batch_predict 批量预测接口说明同 [DeepLabv3p模型predict接口](#batch-predict)
+> - overlap_tile_predict 滑动窗预测接口同 [DeepLabv3p模型poverlap_tile_predict接口](#overlap-tile-predict)
 
 ## paddlex.seg.FastSCNN
 
 ```python
-paddlex.seg.FastSCNN(num_classes=2, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, multi_loss_weight=[1.0])
+paddlex.seg.FastSCNN(num_classes=2, use_bce_loss=False, use_dice_loss=False, class_weight=None, ignore_index=255, multi_loss_weight=[1.0], input_channel=3)
 ```
 
 > 构建FastSCNN分割器。
@@ -172,8 +202,10 @@ paddlex.seg.FastSCNN(num_classes=2, use_bce_loss=False, use_dice_loss=False, cla
 > > - **class_weight** (list/str): 交叉熵损失函数各类损失的权重。当`class_weight`为list的时候,长度应为`num_classes`。当`class_weight`为str时, weight.lower()应为'dynamic',这时会根据每一轮各类像素的比重自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,即平时使用的交叉熵损失函数。
 > > - **ignore_index** (int): label上忽略的值,label为`ignore_index`的像素不参与损失函数的计算。默认255。
 > > - **multi_loss_weight** (list): 多分支上的loss权重。默认计算一个分支上的loss,即默认值为[1.0]。也支持计算两个分支或三个分支上的loss,权重按[fusion_branch_weight, higher_branch_weight, lower_branch_weight]排列,fusion_branch_weight为空间细节分支和全局上下文分支融合后的分支上的loss权重,higher_branch_weight为空间细节分支上的loss权重,lower_branch_weight为全局上下文分支上的loss权重,若higher_branch_weight和lower_branch_weight未设置则不会计算这两个分支上的loss。
+> > - **input_channel** (int): 输入图像通道数。默认值3。
 
 > - train 训练接口说明同 [DeepLabv3p模型train接口](#train)
 > - evaluate 评估接口说明同 [DeepLabv3p模型evaluate接口](#evaluate)
 > - predict 预测接口说明同 [DeepLabv3p模型predict接口](#predict)
 > - batch_predict 批量预测接口说明同 [DeepLabv3p模型predict接口](#batch-predict)
+> - overlap_tile_predict 滑动窗预测接口同 [DeepLabv3p模型poverlap_tile_predict接口](#overlap-tile-predict)

+ 24 - 11
docs/apis/transforms/seg_transforms.md

@@ -78,16 +78,19 @@ paddlex.seg.transforms.ResizeStepScaling(min_scale_factor=0.75, max_scale_factor
 
 ## Normalize
 ```python
-paddlex.seg.transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
+paddlex.seg.transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5], min_val=[0, 0, 0], max_val=[255.0, 255.0, 255.0])
 ```
 对图像进行标准化。
 
-1.图像像素归一化到区间 [0.0, 1.0]。
-2.对图像进行减均值除以标准差操作。
-### 参数
-* **mean** (list): 图像数据集的均值。默认值[0.5, 0.5, 0.5]。
-* **std** (list): 图像数据集的标准差。默认值[0.5, 0.5, 0.5]。
+1.像素值减去min_val
+2.像素值除以(max_val-min_val), 归一化到区间 [0.0, 1.0]。
+3.对图像进行减均值除以标准差操作。
 
+### 参数
+* **mean** (list): 图像数据集的均值。默认值[0.5, 0.5, 0.5]。长度应与图像通道数量相同。
+* **std** (list): 图像数据集的标准差。默认值[0.5, 0.5, 0.5]。长度应与图像通道数量相同。
+* **min_val** (list): 图像数据集的最小值。默认值[0, 0, 0]。长度应与图像通道数量相同。
+* **max_val** (list): 图像数据集的最大值。默认值[255.0, 255.0, 255.0]。长度应与图像通道数量相同。
 
 ## Padding
 ```python
@@ -96,7 +99,7 @@ paddlex.seg.transforms.Padding(target_size, im_padding_value=[127.5, 127.5, 127.
 对图像或标注图像进行padding,padding方向为右和下。根据提供的值对图像或标注图像进行padding操作。
 ### 参数
 * **target_size** (int|list|tuple): padding后图像的大小。
-* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。长度应与图像通道数量相同。
 * **label_padding_value** (int): 标注图像padding的值。默认值为255(仅在训练时需要设定该参数)。
 
 
@@ -107,7 +110,7 @@ paddlex.seg.transforms.RandomPaddingCrop(crop_size=512, im_padding_value=[127.5,
 对图像和标注图进行随机裁剪,当所需要的裁剪尺寸大于原图时,则进行padding操作,模型训练时的数据增强操作。
 ### 参数
 * **crop_size**(int|list|tuple): 裁剪图像大小。默认为512。
-* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。长度应与图像通道数量相同。
 * **label_padding_value** (int): 标注图像padding的值。默认值为255。
 
 
@@ -124,13 +127,13 @@ paddlex.seg.transforms.RandomBlur(prob=0.1)
 ```python
 paddlex.seg.transforms.RandomRotate(rotate_range=15, im_padding_value=[127.5, 127.5, 127.5], label_padding_value=255)
 ```
-对图像进行随机旋转,模型训练时的数据增强操作。
+对图像进行随机旋转,模型训练时的数据增强操作。目前支持多通道的RGB图像,例如支持多张RGB图像沿通道轴做concatenate后的图像数据,不支持通道数量不是3的倍数的图像数据。
 
 在旋转区间[-rotate_range, rotate_range]内,对图像进行随机旋转,当存在标注图像时,同步进行,
 并对旋转后的图像和标注图像进行相应的padding。
 ### 参数
 * **rotate_range** (float): 最大旋转角度。默认为15度。
-* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+* **im_padding_value** (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。长度应与图像通道数量相同。
 * **label_padding_value** (int): 标注图像padding的值。默认为255。
 
 
@@ -150,7 +153,7 @@ paddlex.seg.transforms.RandomScaleAspect(min_scale=0.5, aspect_ratio=0.33)
 ```python
 paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, contrast_range=0.5, contrast_prob=0.5, saturation_range=0.5, saturation_prob=0.5, hue_range=18, hue_prob=0.5)
 ```
-以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。
+以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。目前支持多通道的RGB图像,例如支持多张RGB图像沿通道轴做concatenate后的图像数据,不支持通道数量不是3的倍数的图像数据。
 
 1.对变换的操作顺序进行随机化操作。
 2.按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。  
@@ -167,6 +170,16 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
 * **hue_range** (int): 色调因子的范围。默认为18。
 * **hue_prob** (float): 随机调整色调的概率。默认为0.5。
 
+## Clip
+```python
+paddlex.seg.transforms.Clip(min_val=[0, 0, 0], max_val=[255.0, 255.0, 255.0])
+```
+对图像上超出一定范围的数据进行截断。
+
+### 参数
+* **min_val** (list): 裁剪的下限,小于min_val的数值均设为min_val. 默认值0。
+* **max_val** (list): 裁剪的上限,大于max_val的数值均设为max_val. 默认值255.0。
+
 <!--
 ## ComposedSegTransforms
 ```python

+ 4 - 3
docs/apis/visualize.md

@@ -27,7 +27,7 @@ pdx.det.visualize('./xiaoduxiong_epoch_12/xiaoduxiong.jpeg', result, save_dir='.
 ## paddlex.seg.visualize
 > **语义分割模型预测结果可视化**  
 ```
-paddlex.seg.visualize(image, result, weight=0.6, save_dir='./')
+paddlex.seg.visualize(image, result, weight=0.6, save_dir='./', color=None)
 ```
 将语义分割模型预测得到的Mask在原图上进行可视化。
 
@@ -36,6 +36,7 @@ paddlex.seg.visualize(image, result, weight=0.6, save_dir='./')
 > * **result** (str): 模型预测结果。
 > * **weight**(float): mask可视化结果与原图权重因子,weight表示原图的权重。默认0.6。
 > * **save_dir**(str): 可视化结果保存路径。若为None,则表示不保存,该函数将可视化的结果以np.ndarray的形式返回;若设为目录路径,则将可视化结果保存至该目录下。默认值为'./'。
+> * **color** (list): 各类别的BGR颜色值组成的列表。例如两类时可设置为[255, 255, 255, 0, 0, 0]。默认值为None,则使用默认生成的颜色列表。
 
 ### 使用示例
 > 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/cityscape_deeplab.tar.gz)和[测试图片](https://bj.bcebos.com/paddlex/datasets/city.png)
@@ -130,8 +131,8 @@ paddlex.transforms.visualize(dataset,
 ```
 对数据预处理/增强中间结果进行可视化。
 可使用VisualDL查看中间结果:
-1. VisualDL启动方式: visualdl --logdir vdl_output --port 8001
-2. 浏览器打开 https://0.0.0.0:8001 即可,
+1. VisualDL启动方式: visualdl --logdir vdl_output/image_transforms --port 8001
+2. 浏览器打开 https://0.0.0.0:8001 ,在页面上面点击`『样本数据-图像』`即可。
     其中0.0.0.0为本机访问,如为远程服务, 改成相应机器IP
 
 ### 参数

+ 85 - 30
docs/appendix/how_to_offline_run.md

@@ -4,36 +4,91 @@ PaddleX在模型训练时,存在以下两种情况需要进行联网下载
 > 1.训练模型时,用户没有配置自定义的预训练模型权重`pretrain_weights`,此时PaddleX会自动联网下载在标准数据集上的预训练模型;
 > 2.模型裁剪训练时,用户没有配置自定义的参数敏感度信息文件`sensitivities_file`,并将`sensitivities_file`配置成了'DEFAULT'字符串,此时PaddleX会自动联网下载模型在标准数据集上计算得到的参数敏感度信息文件。
 
+## PaddleX Python API离线训练
+> 通过如下代码先下载好PaddleX的所有预训练模型,下载完共约7.5G  
+```
+from paddlex.cv.models.utils.pretrain_weights import image_pretrain
+from paddlex.cv.models.utils.pretrain_weights import coco_pretrain
+from paddlex.cv.models.utils.pretrain_weights import cityscapes_pretrain
+import paddlehub as hub
 
-## 如何在没联网的情况下进行模型训练
-> 在训练模型时,不管是正常训练还是裁剪训练,用户可以提前准备好预训练权重或参数敏感度信息文档,只需自定义`pretrain_weights`或`sensitivities_file`, 将其设为本地的路径即可。
+save_dir = '/home/work/paddlex_pretrain'
+for name, url in image_pretrain.items():
+    hub.download(name, save_dir)
+for name, url in coco_pretrain.items():
+    hub.download(name, save_dir)
+for name, url in cityscapes_pretrain.items():
+    hub.download(name, save_dir)
+```
 
+用户在可联网的机器上,执行如上代码,所有的预训练模型将会下载至指定的`save_dir`(代码示例中为`/home/work/paddlex_pretrain`),之后在通过Python代码使用PaddleX训练代码时,只需要在import paddlex的同时,配置如下参数,模型在训练时便会优先在此目录下寻找已经下载好的预训练模型。
+```
+import paddlex as pdx
+pdx.pretrain_dir = '/home/work/paddlex_pretrain'
+```
 
-## 预训练模型下载地址
-> 以下模型均为分类模型权重(UNet除外),用户在训练模型时,需要**根据分类模型的种类或backbone的种类**,选择对应的模型权重进行下载(目标检测在使用ResNet50作为Backbone时,使用下面表格中的ResNet50_cos作为预训练模型)
-
-| 模型(点击下载) | 数据集 |
-| :------------|:------|
-| [ResNet18](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet18_pretrained.tar) | ImageNet |
-| [ResNet34](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet34_pretrained.tar) | ImageNet |
-| [ResNet50](http://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_pretrained.tar) | ImageNet |
-| [ResNet101](http://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar) | ImageNet |
-| [ResNet50_vd](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar) | ImageNet |
-| [ResNet101_vd](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar) | ImageNet |
-| [MobileNetV1](http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar) | ImageNet |
-| [MobileNetV2_x1.0](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_pretrained.tar) | ImageNet |
-| [MobileNetV2_x0.5](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x0_5_pretrained.tar) | ImageNet |
-| [MobileNetV2_x2.0](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x2_0_pretrained.tar) | ImageNet |
-| [MobileNetV2_x0.25](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x0_25_pretrained.tar) | ImageNet |
-| [MobileNetV2_x1.5](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x1_5_pretrained.tar) | ImageNet |
-| [MobileNetV3_small](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_small_x1_0_pretrained.tar) | ImageNet |
-| [MobileNetV3_large](https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_large_x1_0_pretrained.tar) | ImageNet |
-| [DarkNet53](https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_ImageNet1k_pretrained.tar) | ImageNet |
-| [DenseNet121](https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet121_pretrained.tar) | ImageNet |
-| [DenseNet161](https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet161_pretrained.tar) | ImageNet |
-| [DenseNet201](https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet201_pretrained.tar) | ImageNet |
-| [ResNet50_cos](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar) | ImageNet |
-| [Xception41](https://paddle-imagenet-models-name.bj.bcebos.com/Xception41_deeplab_pretrained.tar) | ImageNet |
-| [Xception65](https://paddle-imagenet-models-name.bj.bcebos.com/Xception65_deeplab_pretrained.tar) | ImageNet |
-| [ShuffleNetV2](https://paddle-imagenet-models-name.bj.bcebos.com/ShuffleNetV2_pretrained.tar) | ImageNet |
-| [UNet](https://paddleseg.bj.bcebos.com/models/unet_coco_v3.tgz) | MSCOCO |
+## PaddleX GUI离线训练
+> PaddleX GUI在打开后,需要用户设定工作空间,假设当前用户设定的工作空间为`D:\PaddleX_Workspace`,为了离线训练,用户需手动下载如下所有文件(下载后无需再做解压操作)至`D:\PaddleX_Workspace\pretrain`目录,之后在训练模型时,便不再需要联网  
+```
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet18_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet34_pretrained.tar
+http://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_pretrained.tar
+http://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_ssld_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_ssld_pretrained.tar
+http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x0_5_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x2_0_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x0_25_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV2_x1_5_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_small_x1_0_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_large_x1_0_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_small_x1_0_ssld_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV3_large_x1_0_ssld_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_ImageNet1k_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet121_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet161_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/DenseNet201_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/Xception41_deeplab_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/Xception65_deeplab_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/ShuffleNetV2_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W18_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W30_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W32_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W40_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W44_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W48_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W60_C_pretrained.tar
+https://paddle-imagenet-models-name.bj.bcebos.com/HRNet_W64_C_pretrained.tar
+http://paddle-imagenet-models-name.bj.bcebos.com/AlexNet_pretrained.tar
+https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar
+https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar
+https://bj.bcebos.com/paddlex/models/yolov3_mobilenet_v3.tar
+https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar
+https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r50vd_dcn.tar
+https://bj.bcebos.com/paddlex/pretrained_weights/faster_rcnn_r18_fpn_1x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_vd_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_vd_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_hrnetv2p_w18_2x.tar
+https://bj.bcebos.com/paddlex/pretrained_weights/mask_rcnn_r18_fpn_1x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_vd_fpn_2x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_fpn_1x.tar
+https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_vd_fpn_1x.tar
+https://bj.bcebos.com/paddlex/pretrained_weights/mask_rcnn_hrnetv2p_w18_2x.tar
+https://paddleseg.bj.bcebos.com/models/unet_coco_v3.tgz
+https://bj.bcebos.com/v1/paddleseg/deeplab_mobilenet_x1_0_coco.tgz
+https://paddleseg.bj.bcebos.com/models/xception65_coco.tgz
+https://paddlemodels.bj.bcebos.com/object_detection/ppyolo_2x.pdparams
+https://paddleseg.bj.bcebos.com/models/deeplabv3p_mobilenetv3_large_cityscapes.tar.gz
+https://paddleseg.bj.bcebos.com/models/mobilenet_cityscapes.tgz
+https://paddleseg.bj.bcebos.com/models/xception65_bn_cityscapes.tgz
+https://paddleseg.bj.bcebos.com/models/hrnet_w18_bn_cityscapes.tgz
+https://paddleseg.bj.bcebos.com/models/fast_scnn_cityscape.tar
+```

+ 1 - 1
docs/appendix/index.rst

@@ -11,4 +11,4 @@
    metrics.md
    interpret.md
    parameters.md
-
+   how_to_offline_run.md

+ 2 - 2
docs/appendix/parameters.md

@@ -28,8 +28,8 @@ num_epochs是模型训练迭代的总轮数(模型对训练集全部样本过一
 ## 5.参数设定时的约束
 根据上述几个参数,可以了解到学习率的变化分为WarmUp热身阶段和Decay衰减阶段,
 > - Wamup热身阶段:随着训练迭代,学习率从较低的值逐渐线性增长至设定的值,以step为单位
-> - Decay衰减阶段:随着训练迭代,学习率逐步衰减,如每次衰减为之前的0.1, 以epoch为单位
-> step与epoch的关系:1个epoch由多个step组成,例如训练样本有800张图像,`train_batch_size`为8, 那么每个epoch都要完整用这800张图片训一次模型,而每个epoch总共包含800//8即100个step
+> - Decay衰减阶段:随着训练迭代,学习率逐步衰减,如每次衰减为之前的0.1, 以epoch为单位  
+> - step与epoch的关系:1个epoch由多个step组成,例如训练样本有800张图像,`train_batch_size`为8, 那么每个epoch都要完整用这800张图片训一次模型,而每个epoch总共包含800//8即100个step
 
 在PaddleX中,约束warmup必须在Decay之前结束,因此各参数设置需要满足下面条件
 ```

+ 21 - 2
docs/change_log.md

@@ -1,5 +1,24 @@
 # 更新日志
 
+**v1.2.0** 2020.09.07
+- 模型更新
+  > - 新增产业最实用目标检测模型PP-YOLO,深入考虑产业应用对精度速度的双重面诉求,COCO数据集精度45.2%,Tesla V100预测速度72.9FPS。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#paddlex-det-ppyolo)
+  > - FasterRCNN、MaskRCNN、YOLOv3、DeepLabv3p等模型新增内置COCO数据集预训练模型,适用于小数据集的微调训练。
+  > - 目标检测模型FasterRCNN和MaskRCNN新增backbone HRNet_W18,适用于对细节预测要求较高的应用场景。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#paddlex-det-fasterrcnn)
+  > - 语义分割模型DeepLabv3p新增backbone MobileNetV3_large_ssld,模型体积9.3MB,Cityscapes数据集精度仍保持有73.28%。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#paddlex-seg-deeplabv3p)
+
+- 模型部署更新
+  > - 新增模型通过OpenVINO预测加速的部署方案,CPU上相比mkldnn加速库预测速度提升1.5~2倍左右。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/deploy/openvino/index.html)
+  > - 新增模型在树莓派上的部署方案,进一步丰富边缘侧的部署方案。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/deploy/raspberry/index.html)
+  > - 优化PaddleLite Android部署的数据预处理和后处理代码性能,预处理速度提升10倍左右,后处理速度提升4倍左右。
+  > - 优化Paddle服务端C++代码部署代码,增加use_mkl等参数,CPU上相比未开启mkldnn预测速度提升10~50倍左右。
+
+- 产业案例更新
+  > - 新增大尺寸RGB图像遥感分割案例,提供滑动窗口预测接口,不仅能避免显存不足的发生,而且能通过配置重叠程度消除最终预测结果中各窗口拼接处的裂痕感。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/examples/remote_sensing.html)
+  > - 新增多通道遥感影像分割案例,打通语义分割任务对任意通道数量的数据分析、模型训练、模型部署全流程。[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/examples/multi-channel_remote_sensing/README.html)
+
+- 其它
+  > - 新增数据集切分功能,支持通过命令行一键切分ImageNet、PascalVOC、MSCOCO和语义分割数据集[详情链接](https://paddlex.readthedocs.io/zh_CN/develop/data/format/classification.html#id2)
 
 **v1.1.0** 2020.07.12
 
@@ -13,8 +32,8 @@
 > - 新增Jetson、Paddle Lite模型部署预测方案
 > - C++部署代码新增batch批预测,并采用OpenMP对预处理进行并行加速
 - 新增2个PaddleX产业案例
-> - [人像分割案例]()
-> - [工业表计读数案例]()
+> - [人像分割案例](https://paddlex.readthedocs.io/zh_CN/develop/examples/human_segmentation.html)
+> - [工业表计读数案例](https://paddlex.readthedocs.io/zh_CN/develop/examples/meter_reader.html)
 - 新增数据格式转换功能,LabelMe、精灵标注助手和EasyData平台标注的数据转为PaddleX支持加载的数据格式
 - PaddleX文档更新,优化文档结构
 

+ 26 - 0
docs/data/annotation.md

@@ -39,3 +39,29 @@ paddlex --data_conversion --source labelme --to PascalVOC --pics ./pics --annota
 **注意**:  
 1. 精灵标注的目标检测数据可以在工具内部导出为PascalVOC格式,因此paddlex未提供精灵标注数据到PascalVOC格式的转换  
 2. 在将LabelMe数据集转换为COCO数据集时,LabelMe的图像文件名和json文件名需要一一对应,才可正确转换
+
+## 手机拍照标注说明
+
+当您收集的样本图像来源于手机拍照时,请注意由于手机拍照信息内附带水平垂直方向信息,这可能会使得在标注和训练时出现问题,因此在拍完照后注意根据方向对照片进行处理,使用如下函数即可解决
+```
+from PIL import Image, ExifTags
+def rotate(im):
+    try:
+        for orientation in ExifTags.TAGS.keys():
+            if ExifTags.TAGS[orientation] == 'Orientation':
+                break
+        exif = dict(im._getexif().items())
+        if exif[orientation] == 3:
+            im = im.rotate(180, expand=True)
+        if exif[orientation] == 6:
+            im = im.rotate(270, expand=True)
+        if exif[orientation] == 8:
+            im = im.rotate(90, expand=True)
+    except:
+        pass
+
+img_file = '1.jpeg'
+im = Image.open(img_file)
+rotate(im)
+im.save('new_1.jpeg')
+```

+ 53 - 0
docs/data/format/change_detection.md

@@ -0,0 +1,53 @@
+# 地块检测ChangeDet
+
+## 数据集文件夹结构
+
+在PaddleX中,**标注文件为png文件**。建议用户将数据集按照如下方式进行组织,同一地块不同时期的地貌原图均放在同一目录,如`JPEGImages`,标注的同名png文件均放在同一目录,如`Annotations`,示例如下
+```
+MyDataset/ # 语义分割数据集根目录
+|--JPEGImages/ # 原图文件所在目录,包含同一物体前期和后期的图片
+|  |--1_1.jpg
+|  |--1_2.jpg
+|  |--2_1.jpg
+|  |--2_2.jpg
+|  |--...
+|  |--...
+|
+|--Annotations/ # 标注文件所在目录
+|  |--1.png
+|  |--2.png
+|  |--...
+|  |--...
+```
+同一地块不同时期的地貌原图,如1_1.jpg和1_2.jpg,可以是RGB彩色图像、灰度图、或tiff格式的多通道图像。语义分割的标注图像,如1.png,为单通道图像,像素标注类别需要从0开始递增(一般0表示background背景), 例如0, 1, 2, 3表示4种类别,标注类别最多255个类别(其中像素值255不参与训练和评估)。
+
+## 划分训练集验证集
+
+**为了用于训练,我们需要在`MyDataset`目录下准备`train_list.txt`, `val_list.txt`和`labels.txt`三个文件**,分别用于表示训练集列表,验证集列表和类别标签列表。
+
+**labels.txt**  
+
+labels.txt用于列出所有类别,类别对应行号表示模型训练过程中类别的id(行号从0开始计数),例如labels.txt为以下内容
+```
+unchanged
+changed
+```
+表示该检测数据集中共有2个分割类别,分别为`unchanged`和`changed`,在模型训练中`unchanged`对应的类别id为0, `changed`对应1,以此类推,如不知具体类别标签,可直接在labels.txt逐行写0,1,2...序列即可。
+
+**train_list.txt**  
+
+train_list.txt列出用于训练时的图片集合,与其对应的标注文件,示例如下
+```
+JPEGImages/1_1.jpg JPEGImages/1_2.jpg Annotations/1.png
+JPEGImages/2_1.jpg JPEGImages/2_2.jpg Annotations/2.png
+... ...
+```
+其中第一列和第二列为原图相对`MyDataset`的相对路径,对应同一地块不同时期的地貌图像,第三列为标注文件相对`MyDataset`的相对路径
+
+**val_list.txt**  
+
+val_list列出用于验证时的图片集成,与其对应的标注文件,格式与val_list.txt一致
+
+## PaddleX数据集加载  
+
+[示例代码](https://github.com/PaddlePaddle/PaddleX/blob/develop/examples/change_detection/train.py)

+ 1 - 0
docs/data/format/index.rst

@@ -10,3 +10,4 @@
    detection.md
    instance_segmentation.md
    segmentation.md
+   change_detection.md

+ 6 - 6
docs/deploy/hub_serving.md

@@ -10,12 +10,12 @@
 下面,我们按照步骤,实现将一个图像分类模型[MobileNetV3_small_ssld](https://bj.bcebos.com/paddlex/models/mobilenetv3_small_ssld_imagenet.tar.gz)转换成`PaddleHub`的预训练模型,并利用`PaddleHub-Serving`实现一键部署。
 
 
-# 模型部署
+## 模型部署
 
-## 1 部署模型准备
+### 1 部署模型准备
 部署模型的格式均为目录下包含`__model__`,`__params__`和`model.yml`三个文件,如若不然,则参照[部署模型导出文档](./export_model.md)进行导出。
 
-## 2 模型转换
+### 2 模型转换
 首先,我们将`PaddleX`的`Inference Model`转换成`PaddleHub`的预训练模型,使用命令`hub convert`即可一键转换,对此命令的说明如下:
 
 ```shell
@@ -45,7 +45,7 @@ $ The converted module is stored in `MobileNetV3_small_ssld_hub_1596077881.86850
 ```
 等待生成成功的提示后,我们就在输出目录中得到了一个`PaddleHub`的一个预训练模型。
 
-## 3 模型安装
+### 3 模型安装
 在模型转换一步中,我们得到了一个`.tar.gz`格式的预训练模型压缩包,在进行部署之前需要先安装到本机,使用命令`hub install`即可一键安装,对此命令的说明如下:
 ```shell
 $ hub install ${MODULE}
@@ -61,7 +61,7 @@ hub install MobileNetV3_small_ssld_hub_1596077881.868501/mobilenetv3_small_ssld_
 $ Successfully installed mobilenetv3_small_ssld_imagenet_hub
 ```
 
-## 4 模型部署
+### 4 模型部署
 下面,我们只需要使用`hub serving`命令即可完成模型的一键部署,对此命令的说明如下:
 ```shell
 $ hub serving start --modules/-m [Module1==Version1, Module2==Version2, ...] \
@@ -107,7 +107,7 @@ $ hub serving start -m mobilenetv3_small_ssld_imagenet_hub
 |modules_info|PaddleHub Serving预安装模型,以字典列表形式列出,key为模型名称。其中:<br>`init_args`为模型加载时输入的参数,等同于`paddlehub.Module(**init_args)`<br>`predict_args`为模型预测时输入的参数,以`mobilenetv3_small_ssld_imagenet_hub`为例,等同于`mobilenetv3_small_ssld_imagenet_hub.batch_predict(**predict_args)`
 |port|服务端口,默认为8866|
 
-## 5 测试
+### 5 测试
 在第二步模型安装的同时,会生成一个客户端请求示例,存放在模型安装目录,默认为`${HUB_HOME}/.paddlehub/modules`,对于此例,我们可以在`~/.paddlehub/modules/mobilenetv3_small_ssld_imagenet_hub`找到此客户端示例`serving_client_demo.py`,代码如下:
 
 ```python

+ 3 - 0
docs/deploy/index.rst

@@ -11,3 +11,6 @@
    server/index
    nvidia-jetson.md
    paddlelite/index
+   openvino/index
+   raspberry/index
+   opencv.md

+ 1 - 1
docs/deploy/nvidia-jetson.md

@@ -14,7 +14,7 @@
 
  `git clone https://github.com/PaddlePaddle/PaddleX.git`
 
-**说明**:其中`C++`预测代码在`/root/projects/PaddleX/deploy/cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。
+**说明**:其中`C++`预测代码在`PaddleX/deploy/cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。
 
 
 ### Step2: 下载PaddlePaddle C++ 预测库 paddle_inference

+ 69 - 0
docs/deploy/opencv.md

@@ -0,0 +1,69 @@
+# 部署编译说明
+
+目前PaddleX所有的模型部署测试环境为
+- Ubuntu 16.04/18.04  / Windows 10
+- gcc 4.8.5 / Microsoft Visual Studio 2019
+
+如果切换至其它Linux系统环境(gcc版本不变),预期会遇到opencv的问题。
+
+在Linux编译脚本中,例如`deploy/cpp/script/build.sh`中,依赖`deploy/cpp/script/bootstrap.sh`去自动下载预先已经编译好的依赖的opencv库和加密库。而目前`bootstrap.sh`只提供了OpenCV在Ubuntu16.04/18.04两个系统环境下的预编译包,如果你的系统与此不同,尝试按照如下方式解决。
+
+
+## Linux下自编译OpenCV
+
+### 1. 下载OpenCV Source Code  
+前往OpenCV官方网站下载OpenCV 3.4.6 Source Code,或者直接[点击这里](https://bj.bcebos.com/paddlex/deploy/opencv-3.4.6.zip)下载我们已经上传至服务器的源码压缩包。
+
+### 2. 编译OpenCV
+确认自己的gcc/g++版本为4.8.5版本,编译过程参考如下代码  
+
+当前opencv-3.4.6.zip存放路径为`/home/paddlex/opencv-3.4.6.zip`
+```
+unzip opencv-3.4.6.zip
+cd opencv-3.4.6
+mkdir build && cd build
+mkdir opencv3.4.6gcc4.8ffmpeg
+cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=/home/paddlex/opencv-3.4.6/build/opencv3.4.6gcc4.8ffmpeg -D WITH_FFMPEG=ON ..
+make -j5
+make install
+```
+编译好的opencv会存放在设定的`/home/paddlex/opencv-3.4.6/build/opencv3.4.6gcc4.8ffmpeg`下
+
+### 3. 编译PaddleX预测代码依赖自己的opencv
+修改`deploy/cpp/script/build.sh`
+
+1. 注释或删除掉如下代码
+
+```
+{
+    bash $(pwd)/scripts/bootstrap.sh # 下载预编译版本的加密工具和opencv依赖库
+} || {
+    echo "Fail to execute script/bootstrap.sh"
+    exit -1
+}
+```
+
+2. 模型加密开关设置
+如果您不需要用到PaddleX的模型加密功能,则将如下开关修改为OFF即可
+```
+WITH_ENCRYPTION=OFF
+```
+如果需要用到加密,则请手动下载加密库后解压,[点击下载](https://bj.bcebos.com/paddlex/tools/paddlex-encryption.zip)
+
+3. 设置依赖库路径
+将`OPENCV_DIR`设置为自己编译好的路径,如
+```
+OPENCV_DIR=/home/paddlex/opencv-3.4.6/build/opencv3.4.6gcc4.8ffmpeg
+```
+如果您还需要用到模型加密,已经将`WITH_ENCRYPTION`设为`ON`的前提下,也同时将`ENCRYPTION_DIR`设置为自己下载解压后的路径,如
+```
+ENCRYPTION_DIR=/home/paddlex/paddlex-encryption
+```
+
+4. 执行`sh script/build.sh`编译即可
+
+## 反馈
+
+如在使用中仍然存在问题,请前往PaddleX的Github提ISSUE反馈给我们。
+
+- [PaddleX Issue](https://github.com/PaddlePaddle/PaddleX/issues)

+ 14 - 9
docs/deploy/openvino/export_openvino_model.md

@@ -3,11 +3,11 @@
 
 ## 环境依赖
 
-* ONNX 1.5.0+
-* PaddleX 1.0+
-* OpenVINO 2020.4
+* ONNX 1.6.0+
+* PaddleX 1.2+
+* OpenVINO 2021.1+
 
-**说明**:PaddleX安装请参考[PaddleX](https://paddlex.readthedocs.io/zh_CN/develop/install.html) , OpenVINO安装请参考[OpenVINO](https://docs.openvinotoolkit.org/latest/index.html),ONNX请安装1.5.0以上版本否则会出现转模型错误。
+**说明**:PaddleX安装请参考[PaddleX](https://paddlex.readthedocs.io/zh_CN/develop/install.html) , OpenVINO安装请参考[OpenVINO](https://docs.openvinotoolkit.org/latest/index.html),ONNX请安装1.6.0以上版本否则会出现转模型错误。
 
 请确保系统已经安装好上述基本软件,**下面所有示例以工作目录 `/root/projects/`演示**。
 
@@ -20,11 +20,14 @@ paddlex --export_inference --model_dir=/path/to/paddle_model --save_dir=./infere
 ## 导出OpenVINO模型
 
 ```
-cd /root/projects/python
+mkdir -p /root/projects
+cd /root/projects
+git clone https://github.com/PaddlePaddle/PaddleX.git
+cd PaddleX/deploy/openvino/python
 
-python convertor.py --model_dir /path/to/inference_model --save_dir /path/to/openvino_model --fixed_input_shape [w,h]
+python converter.py --model_dir /path/to/inference_model --save_dir /path/to/openvino_model --fixed_input_shape [w,h]
 ```
-**转换成功后会在save_dir下出现后缀名为.xml、.bin、.mapping三个文件**   
+**转换成功后会在save_dir下出现后缀名为.xml、.bin、.mapping三个文件**  
 转换参数说明如下:
 
 |  参数   | 说明  |
@@ -32,6 +35,8 @@ python convertor.py --model_dir /path/to/inference_model --save_dir /path/to/ope
 | --model_dir  | Paddle模型路径,请确保__model__, \_\_params__model.yml在同一个目录|
 | --save_dir  | OpenVINO模型保存路径 |
 | --fixed_input_shape  | 模型输入的[W,H] |
-| --data type(option)  | FP32、FP16,默认为FP32,VPU下的IR需要为FP16 |
-
+| --data type(option)  | FP32、FP16,默认为FP32,VPU下的IR需要为FP16 |  
 
+**注意**:
+- 由于OpenVINO 从2021.1版本开始支持ONNX的resize-11 OP的原因,请下载OpenVINO 2021.1+的版本
+- YOLOv3在通过OpenVINO部署时,由于OpenVINO对ONNX OP的支持限制,我们在将YOLOv3的Paddle模型导出时,对最后一层multiclass_nms进行了特殊处理,导出的ONNX模型,最终输出的Box结果包括背景类别(而Paddle模型不包含),此处在OpenVINO的部署代码中,我们通过后处理过滤了背景类别。

+ 7 - 6
docs/deploy/openvino/introduction.md

@@ -1,32 +1,33 @@
 # OpenVINO部署简介
-PaddleX支持将训练好的Paddle模型通过OpenVINO实现模型的预测加速,OpenVINO详细资料与安装流程请参考[OpenVINO](https://docs.openvinotoolkit.org/latest/index.html)
+PaddleX支持将训练好的Paddle模型通过OpenVINO实现模型的预测加速,OpenVINO详细资料与安装流程请参考[OpenVINO](https://docs.openvinotoolkit.org/latest/index.html),本文档使用OpenVINO 2020.4测试通过。
 
 ## 部署支持情况
 下表提供了PaddleX在不同环境下对使用OpenVINO加速的支持情况  
 
 |硬件平台|Linux|Windows|Raspbian OS|c++|python |分类|检测|分割|
 | ----|  ---- | ---- | ----|  ---- | ---- |---- | ---- |---- |
-|CPU|支持|支持|不支持|支持|支持|支持|支持|支持|
+|CPU|支持|支持|不支持|支持|支持|支持|支持|支持|
 |VPU|支持|支持|支持|支持|支持|支持|不支持|不支持|  
 
 
-**注意**:其中Raspbian OS为树莓派操作系统。检测模型仅支持YOLOV3,由于OpenVINO不支持ONNX的resize-11 OP的原因,目前还不支持Paddle的分割模型
+**注意**:其中Raspbian OS为树莓派操作系统。检测模型仅支持YOLOv3
 
 ## 部署流程
-**PaddleX到OpenVINO的部署流程可以分为如下两步**: 
+**PaddleX到OpenVINO的部署流程可以分为如下两步**:
 
   * **模型转换**:将Paddle的模型转换为OpenVINO的Inference Engine
   * **预测部署**:使用Inference Engine进行预测
 
-## 模型转换 
+## 模型转换
 **模型转换请参考文档[模型转换](./export_openvino_model.md)**  
 **说明**:由于不同软硬件平台下OpenVINO模型转换方法一致,故如何转换模型后续文档中不再赘述。
 
 ## 预测部署
 由于不同软硬下部署OpenVINO实现预测的方式不完全一致,具体请参考:  
+
 **[Linux](./linux.md)**:介绍了PaddleX在操作系统为Linux或者Raspbian OS,编程语言为C++,硬件平台为
 CPU或者VPU的情况下使用OpenVINO进行预测加速  
 
 **[Windows](./windows.md)**:介绍了PaddleX在操作系统为Window,编程语言为C++,硬件平台为CPU或者VPU的情况下使用OpenVINO进行预测加速  
 
-**[Python](./python.md)**:介绍了PaddleX在python下使用OpenVINO进行预测加速
+**[Python](./python.md)**:介绍了PaddleX在python下使用OpenVINO进行预测加速

+ 7 - 9
docs/deploy/openvino/linux.md

@@ -29,17 +29,15 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 **说明**:其中C++预测代码在PaddleX/deploy/openvino 目录,该目录不依赖任何PaddleX下其他目录。
 
 ### Step2 软件依赖
-提供了依赖软件预编包或者一键编译,用户不需要单独下载或编译第三方依赖软件。若需要自行编译第三方依赖软件请参考:
 
-- gflags:编译请参考 [编译文档](https://gflags.github.io/gflags/#download)  
+Step3中的编译脚本会一键安装第三方依赖软件的预编译包,用户不需要单独下载或编译这些依赖软件。若需要自行编译第三方依赖软件请参考:
 
-- glog:编译请参考[编译文档](https://github.com/google/glog)
+- gflags:编译请参考 [编译文档](https://gflags.github.io/gflags/#download)  
 
 - opencv: 编译请参考
 [编译文档](https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html)
 
 
-
 ### Step3: 编译
 编译`cmake`的命令在`scripts/build.sh`中,若在树莓派(Raspbian OS)上编译请修改ARCH参数x86为armv7,若自行编译第三方依赖软件请根据Step1中编译软件的实际情况修改主要参数,其主要内容说明如下:
 ```
@@ -47,8 +45,6 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 OPENVINO_DIR=$INTEL_OPENVINO_DIR/inference_engine
 # gflags预编译库的路径
 GFLAGS_DIR=$(pwd)/deps/gflags
-# glog预编译库的路径
-GLOG_DIR=$(pwd)/deps/glog
 # ngraph lib预编译库的路径
 NGRAPH_LIB=$INTEL_OPENVINO_DIR/deployment_tools/ngraph/lib
 # opencv预编译库的路径
@@ -63,7 +59,7 @@ ARCH=x86
 
 ### Step4: 预测
 
-编译成功后,分类任务的预测可执行程序为`classifier`,检测任务的预测可执行程序为`detector`,其主要命令参数说明如下:
+编译成功后,分类任务的预测可执行程序为`classifier`,检测任务的预测可执行程序为`detector`,分割任务的预测可执行程序为`segmenter`,其主要命令参数说明如下:
 
 |  参数   | 说明  |
 |  ----  | ----  |
@@ -72,7 +68,7 @@ ARCH=x86
 | --image_list  | 按行存储图片路径的.txt文件 |
 | --device  | 运行的平台,可选项{"CPU","MYRIAD"},默认值为"CPU",如在VPU上请使用"MYRIAD"|
 | --cfg_file | PaddleX model 的.yml配置文件 |
-| --save_dir | 可视化结果图片保存地址,仅适用于检测任务,默认值为" "不保存可视化结果 |
+| --save_dir | 可视化结果图片保存地址,仅适用于检测任务,默认值为" ",即不保存可视化结果 |
 
 ### 样例
 `样例一`:
@@ -118,7 +114,9 @@ linux系统在CPU下做多张图片的检测任务预测,并保存预测可视
 |---|---|---|---|
 |resnet-50 | 20.56 | 16.12 | 224*224 |
 |mobilenet-V2 | 5.16 | 2.31 |224*224|
-|yolov3-mobilnetv1 |76.63| 46.26|608*608 |  
+|yolov3-mobilnetv1 |76.63| 46.26|608*608 |
+|unet| 276.40| 211.49| 512*512|  
+
 
 `测试二`:
 在PC机上插入VPU架构的神经计算棒(NCS2),通过Openvino加速。

+ 9 - 8
docs/deploy/openvino/windows.md

@@ -14,8 +14,11 @@ Windows 平台下,我们使用`Visual Studio 2019 Community` 进行了测试
 - 我的电脑->属性->高级系统设置->环境变量
     - 在系统变量中找到Path(如没有,自行创建),并双击编辑
     - 新建,分别将OpenVINO以下路径填入并保存:  
+
       `C:\Program File (x86)\IntelSWTools\openvino\inference_engine\bin\intel64\Release`  
+
       `C:\Program File (x86)\IntelSWTools\openvino\inference_engine\external\tbb\bin`  
+
       `C:\Program File (x86)\IntelSWTools\openvino\deployment_tools\ngraph\lib`  
 
 请确保系统已经安装好上述基本软件,并配置好相应环境,**下面所有示例以工作目录为 `D:\projects`演示。**
@@ -37,11 +40,10 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 
 ### Step2 软件依赖
 提供了依赖软件预编译库:
-- [gflas-glog](https://bj.bcebos.com/paddlex/deploy/windows/third-parts.zip)  
+- [gflas](https://bj.bcebos.com/paddlex/deploy/windows/third-parts.zip)  
 - [opencv](https://bj.bcebos.com/paddleseg/deploy/opencv-3.4.6-vc14_vc15.exe)  
 请下载上面两个连接的预编译库。若需要自行下载请参考:
 - gflags:[下载地址](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/gflags)
-- glog:[编译文档](https://github.com/google/glog)
 - opencv:[下载地址](https://opencv.org/releases/)  
 下载完opencv后需要配置环境变量,如下流程所示  
     - 我的电脑->属性->高级系统设置->环境变量
@@ -50,17 +52,16 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 
 ### Step3: 使用Visual Studio 2019直接编译CMake
 1. 打开Visual Studio 2019 Community,点击`继续但无需代码`
-2. 点击: `文件`->`打开`->`CMake` 选择C++预测代码所在路径(例如`D:\projects\PaddleX\deploy\openvino`),并打开`CMakeList.txt`
+2. 点击: `文件`->`打开`->`CMake` 选择C++预测代码所在路径(例如`D:\projects\PaddleX\deploy\openvino`),并打开`CMakeList.txt`  
 3. 点击:`项目`->`CMake设置`
-4. 点击`浏览`,分别设置编译选项指定`OpenVINO`、`Gflags`、`GLOG`、`NGRAPH`、`OPENCV`的路径  
+4. 点击`浏览`,分别设置编译选项指定`OpenVINO`、`Gflags`、`NGRAPH`、`OPENCV`的路径  
 
 |  参数名   | 含义  |
 |  ----  | ----  |
-| OPENCV_DIR  | opencv库路径 |
+| OPENCV_DIR  | OpenCV库路径 |
 | OPENVINO_DIR | OpenVINO推理库路径,在OpenVINO安装目录下的deployment/inference_engine目录,若未修改OpenVINO默认安装目录可以不用修改 |
 | NGRAPH_LIB | OpenVINO的ngraph库路径,在OpenVINO安装目录下的deployment/ngraph/lib目录,若未修改OpenVINO默认安装目录可以不用修改 |
 | GFLAGS_DIR | gflags库路径 |
-| GLOG_DIR  | glog库路径 |
 | WITH_STATIC_LIB | 是否静态编译,默认为True |  
 
 **设置完成后**, 点击`保存并生成CMake缓存以加载变量`。
@@ -73,7 +74,7 @@ D:
 cd D:\projects\PaddleX\deploy\openvino\out\build\x64-Release
 ```
 
-* 编译成功后,图片预测demo的入口程序为`detector.exe`,`classifier.exe`,用户可根据自己的模型类型选择,其主要命令参数说明如下:
+* 编译成功后,图片预测demo的入口程序为`detector.exe`,`classifier.exe`,`segmenter.exe`,用户可根据自己的模型类型选择,其主要命令参数说明如下:
 
 |  参数   | 说明  |
 |  ----  | ----  |
@@ -82,7 +83,7 @@ cd D:\projects\PaddleX\deploy\openvino\out\build\x64-Release
 | --image_list  | 按行存储图片路径的.txt文件 |
 | --device  | 运行的平台,可选项{"CPU","MYRIAD"},默认值为"CPU",如在VPU上请使用"MYRIAD"|
 | --cfg_file | PaddleX model 的.yml配置文件 |
-| --save_dir | 可视化结果图片保存地址,仅适用于检测任务,默认值为" "不保存可视化结果 |
+| --save_dir | 可视化结果图片保存地址,仅适用于检测任务,默认值为" ",即不保存可视化结果 |
 
 ### 样例
 `样例一`:

+ 6 - 6
docs/deploy/raspberry/Raspberry.md

@@ -23,7 +23,8 @@ sudo apt-get upgrade
 ```
 
 ## Paddle-Lite部署
-基于Paddle-Lite的部署目前可以支持PaddleX的分类、分割与检测模型,其实检测模型仅支持YOLOV3  
+基于Paddle-Lite的部署目前可以支持PaddleX的分类、分割与检测模型,其中检测模型仅支持YOLOV3  
+
 部署的流程包括:PaddleX模型转换与转换后的模型部署  
 
 **说明**:PaddleX安装请参考[PaddleX](https://paddlex.readthedocs.io/zh_CN/develop/install.html),Paddle-Lite详细资料请参考[Paddle-Lite](https://paddle-lite.readthedocs.io/zh/latest/index.html)
@@ -60,8 +61,6 @@ sudo ./lite/tools/build.sh  --arm_os=armlinux --arm_abi=armv7hf --arm_lang=gcc
 
 - gflags:编译请参考 [编译文档](https://gflags.github.io/gflags/#download)  
 
-- glog:编译请参考[编译文档](https://github.com/google/glog)
-
 - opencv: 编译请参考
 [编译文档](https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html)
 ### Step4: 编译
@@ -71,8 +70,6 @@ sudo ./lite/tools/build.sh  --arm_os=armlinux --arm_abi=armv7hf --arm_lang=gcc
 LITE_DIR=/path/to/Paddle-Lite/inference/lib
 # gflags预编译库的路径
 GFLAGS_DIR=$(pwd)/deps/gflags
-# glog预编译库的路径
-GLOG_DIR=$(pwd)/deps/glog
 # opencv预编译库的路径
 OPENCV_DIR=$(pwd)/deps/opencv/
 ```
@@ -85,6 +82,7 @@ OPENCV_DIR=$(pwd)/deps/opencv/
 ### Step3: 预测
 
 编译成功后,分类任务的预测可执行程序为`classifier`,分割任务的预测可执行程序为`segmenter`,检测任务的预测可执行程序为`detector`,其主要命令参数说明如下:  
+
 |  参数   | 说明  |
 |  ----  | ----  |
 | --model_dir  | 模型转换生成的.xml文件路径,请保证模型转换生成的三个文件在同一路径下|
@@ -125,7 +123,8 @@ OPENCV_DIR=$(pwd)/deps/opencv/
 系统:raspbian OS
 软件:paddle-lite 2.6.1
 ### 测试结果
-单位ms,num表示paddle-lite下使用的线程数
+单位ms,num表示paddle-lite下使用的线程数  
+
 |模型|lite(num=4)|输入图片大小|
 | ----|  ---- | ----|
 |mobilenet-v2|136.19|224*224|
@@ -148,6 +147,7 @@ OPENCV_DIR=$(pwd)/deps/opencv/
 |Xception41|1418.29|224*224|
 |Xception65|2094.7|224*224|  
 
+
 从测试结果看建议用户在树莓派上使用MobileNetV1-V3,ShuffleNetV2这类型的小型网络
 
 ## NCS2部署

+ 1 - 1
docs/deploy/raspberry/index.rst

@@ -8,4 +8,4 @@
 
    Raspberry.md
    python.md
-   export_nb_model.md 
+   export_nb_model.md 

+ 5 - 4
docs/deploy/raspberry/python.md

@@ -23,8 +23,9 @@ python -m pip install paddlelite
 | --img  | 要预测的图片文件路径 |
 | --image_list  | 按行存储图片路径的.txt文件 |
 | --cfg_file | PaddleX model 的.yml配置文件 |
-| --thread_num  | 预测的线程数, 默认值为1 |
-| --input_shape  | 模型输入中图片输入的大小[N,C,H.W] |
+| --thread_num  | 预测的线程数, 默认值为1 |  
+
+**注意**:由于Paddle-lite的python api尚不支持int64数据的输入,目前树莓派在python下不支持部署YoloV3,如需要请使用C++代码部署YoloV3模型
 
 ### 样例
 `样例一`:  
@@ -33,7 +34,7 @@ python -m pip install paddlelite
 ```
 cd /root/projects/python  
 
-python demo.py --model_dir /path/to/openvino_model --img /path/to/test_img.jpeg --cfg_file /path/to/PadlleX_model.yml --thread_num 4 --input_shape [1,3,224,224]
+python demo.py --model_dir /path/to/openvino_model --img /path/to/test_img.jpeg --cfg_file /path/to/PadlleX_model.yml --thread_num 4 
 ```  
 
 样例二`:
@@ -50,5 +51,5 @@ python demo.py --model_dir /path/to/openvino_model --img /path/to/test_img.jpeg
 ```
 cd /root/projects/python  
 
-python demo.py --model_dir /path/to/models/openvino_model --image_list /root/projects/images_list.txt --cfg_file=/path/to/PadlleX_model.yml --thread_num 4 --input_shape [1,3,224,224]
+python demo.py --model_dir /path/to/models/openvino_model --image_list /root/projects/images_list.txt --cfg_file=/path/to/PadlleX_model.yml --thread_num 4 
 ```

+ 135 - 0
docs/deploy/server/cpp/api.md

@@ -0,0 +1,135 @@
+# C++代码接口说明
+
+## 头文件
+`include/paddlex/paddlex.h`
+
+## 类 PaddleX::Model
+
+模型类,用于加载PaddleX训练的模型。
+
+### 模型加载
+```
+PaddleX::Model::Init(const std::string& model_dir,
+                     bool use_gpu = false,
+                     bool use_trt = false,
+                     bool use_mkl = true,
+                     bool mkl_thread_num = 4,
+                     int gpu_id = 0,
+                     std::string key = "",
+                     bool use_ir_optim = true)
+```
+
+**参数**  
+- model_dir: 模型目录路径
+- use_gpu: 是否使用gpu预测
+- use_trt: 是否使用TensorRT
+- use_mkl: 是否使用MKLDNN加速模型在CPU上的预测性能
+- mkl_thread_num: 使用MKLDNN时,线程数量
+- gpu_id: 使用gpu的id号
+- key: 模型解密密钥,此参数用于加载加密的PaddleX模型时使用
+- use_ir_optim: 是否加速模型后进行图优化
+
+**返回值**
+- 返回true或false,表示模型是否加载成功
+
+### 模型预测推断
+
+**分类模型单张图片预测**
+```
+PaddleX::Model::predict(const cv::Mat& im, ClsResult* result)
+```
+**分类模型多张图片批预测**
+```
+PaddleX::Model::predict(const std::vector<cv::Mat>& im_batch, std::vector<ClsResult>* results)
+```
+**目标检测/实例分割模型单张图片预测**
+```
+PaddleX::Model::predict(const cv::Mat& im, DetResult* result)
+```
+**目标检测/实例分割模型多张图片批预测**
+```
+PaddleX::Model::predict(const std::vector<cv::Mat>& im_batch, std::vector<DetResult>* results)
+```
+**语义分割模型单张图片预测**
+```
+PaddleX::Model::predict(const cv::Mat& im, SegResult* result)
+```
+**语义分割模型多张图片批预测**
+```
+PaddleX::Model::predict(const std::vector<cv::Mat>& im_batch, std::vector<SegResult>* results)
+```
+各接口返回值为true或false,用于表示是否预测成功
+
+预测时,需传入cv::Mat结构体,结构需与如下示代码加载的结构体一致
+```
+cv::Mat im = cv::imread('test.jpg', 1);
+```
+当使用批预测时,注意会传入的vector中所有数据作为一个批次进行预测,因此vector越大,所需要使用的GPU显存会越高。
+
+预测时,同时传入ClsResult/DetResult/SegResult结构体,用于存放模型的预测结果,各结构体说明如下
+```
+// 分类模型预测结果
+class ClsResult {
+ public:
+  int category_id; // 类别id
+  std::string category; // 类别标签
+  float score; // 预测置信度
+  std::string type = "cls";
+}
+
+// 目标检测/实例分割模型预测结果
+class DetResult {
+ public:
+  std::vector<Box> boxes; // 预测结果中的各个目标框
+  int mask_resolution; 
+  std::string type = "det";
+}
+
+// 语义分割模型预测结果
+class SegResult : public BaseResult {
+ public:
+  Mask<int64_t> label_map; // 预测分割中各像素的类别
+  Mask<float> score_map; // 预测分割中各像素的置信度
+  std::string type = "seg";
+}
+
+struct Box {
+  int category_id; // 类别id
+  std::string category; // 类别标签
+  float score; // 置信度
+  std::vector<float> coordinate; // 4个元素值,表示xmin, ymin, width, height
+  Mask<int> mask; // 实例分割中,用于表示Box内的分割结果
+}
+
+struct Mask {
+  std::vector<T> data; // 分割中的label map或score map
+  std::vector<int> shape; // 表示分割图的shape
+}
+```
+
+## 预测结果可视化
+
+### 目标检测/实例分割结果可视化
+```
+PaddleX::Visualize(const cv::Mat& img, // 原图
+				   const DetResult& result, // 预测结果
+				   const std::map<int, std::string>& labels // 各类别信息<id, label_name>
+				  )
+```
+返回cv::Mat结构体,即为可视化后的结果
+
+### 语义分割结果可视化
+```
+PaddleX::Visualize(const cv::Mat& img, // 原图
+				   const SegResult& result, // 预测结果
+                   const std::map<int, std::string>& labels // 各类别信息<id, label_name>
+                  )
+```
+返回cv::Mat结构体,即为可视化后的结果
+
+
+## 代码示例
+
+- 图像分类 [PaddleX/deploy/cpp/demo/classifier.cpp](https://github.com/PaddlePaddle/PaddleX/blob/develop/deploy/cpp/demo/classifier.cpp)  
+- 目标检测/实例分割 [PaddleX/deploy/cpp/demo/detector.cpp](https://github.com/PaddlePaddle/PaddleX/blob/develop/deploy/cpp/demo/detector.cpp)
+- 语义分割 [PaddleX/deploy/cpp/demo/segmenter.cpp](https://github.com/PaddlePaddle/PaddleX/blob/develop/deploy/cpp/demo/segmenter.cpp)

+ 1 - 0
docs/deploy/server/cpp/index.rst

@@ -8,3 +8,4 @@ C++部署
 
    windows.md
    linux.md
+   api.md

+ 13 - 10
docs/deploy/server/cpp/linux.md

@@ -19,16 +19,15 @@
 
 ### Step2: 下载PaddlePaddle C++ 预测库 paddle_inference
 
-PaddlePaddle C++ 预测库针对不同的`CPU`,`CUDA`,以及是否支持TensorRT,提供了不同的预编译版本,目前PaddleX依赖于Paddle1.8版本,以下提供了多个不同版本的Paddle预测库:
+PaddlePaddle C++ 预测库针对不同的`CPU`,`CUDA`,以及是否支持TensorRT,提供了不同的预编译版本,目前PaddleX依赖于Paddle1.8.4版本,以下提供了多个不同版本的Paddle预测库:
 
-|  版本说明   | 预测库(1.8.2版本)  |
+|  版本说明   | 预测库(1.8.4版本)  |
 |  ----  | ----  |
-| ubuntu14.04_cpu_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-cpu-avx-mkl/fluid_inference.tgz) |
-| ubuntu14.04_cpu_avx_openblas  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-cpu-avx-openblas/fluid_inference.tgz) |
-| ubuntu14.04_cpu_noavx_openblas  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-cpu-noavx-openblas/fluid_inference.tgz) |
-| ubuntu14.04_cuda9.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-gpu-cuda9-cudnn7-avx-mkl/fluid_inference.tgz) |
-| ubuntu14.04_cuda10.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-gpu-cuda10-cudnn7-avx-mkl/fluid_inference.tgz ) |
-| ubuntu14.04_cuda10.1_cudnn7.6_avx_mkl_trt6  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/1.8.2-gpu-cuda10.1-cudnn7.6-avx-mkl-trt6%2Ffluid_inference.tgz) |
+| ubuntu14.04_cpu_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/latest-cpu-avx-mkl/fluid_inference.tgz) |
+| ubuntu14.04_cpu_avx_openblas  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/latest-cpu-avx-openblas/fluid_inference.tgz) |
+| ubuntu14.04_cpu_noavx_openblas  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/latest-cpu-noavx-openblas/fluid_inference.tgz) |
+| ubuntu14.04_cuda9.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/latest-gpu-cuda9-cudnn7-avx-mkl/fluid_inference.tgz) |
+| ubuntu14.04_cuda10.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-inference-lib.bj.bcebos.com/latest-gpu-cuda10-cudnn7-avx-mkl/fluid_inference.tgz) |
 
 更多和更新的版本,请根据实际情况下载:  [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/build_and_install_lib_cn.html)
 
@@ -97,8 +96,8 @@ make
 ```
 **注意:** linux环境下编译会自动下载OPENCV, PaddleX-Encryption和YAML,如果编译环境无法访问外网,可手动下载:
 
-- [opencv3gcc4.8.tar.bz2](https://paddleseg.bj.bcebos.com/deploy/docker/opencv3gcc4.8.tar.bz2)
-- [paddlex-encryption.zip](https://bj.bcebos.com/paddlex/tools/paddlex-encryption.zip)
+- [opencv3.4.6gcc4.8ffmpeg.tar.gz2](https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2)
+- [paddlex-encryption.zip](https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip)
 - [yaml-cpp.zip](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip)
 
 opencv3gcc4.8.tar.bz2文件下载后解压,然后在script/build.sh中指定`OPENCE_DIR`为解压后的路径。
@@ -125,6 +124,8 @@ yaml-cpp.zip文件下载后无需解压,在cmake/yaml.cmake中将`URL https://
 | image_list  | 按行存储图片路径的.txt文件 |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
 | use_trt  | 是否使用 TensorRT 预测, 支持值为0或1(默认值为0) |
+| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |
+| mkl_thread_num | MKL推理的线程数,默认为cpu处理器个数 |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | save_dir | 保存可视化结果的路径, 默认值为"output",**classfier无该参数** |
 | key | 加密过程中产生的密钥信息,默认值为""表示加载的是未加密的模型 |
@@ -141,6 +142,8 @@ yaml-cpp.zip文件下载后无需解压,在cmake/yaml.cmake中将`URL https://
 | video_path | 视频文件的路径 |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
 | use_trt  | 是否使用 TensorRT 预测, 支持值为0或1(默认值为0) |
+| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |
+| mkl_thread_num | MKL推理的线程数,默认为cpu处理器个数 |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | show_result | 对视频文件做预测时,是否在屏幕上实时显示预测可视化结果(因加入了延迟处理,故显示结果不能反映真实的帧率),支持值为0或1(默认值为0) |
 | save_result | 是否将每帧的预测可视结果保存为视频文件,支持值为0或1(默认值为1) |

+ 12 - 8
docs/deploy/server/cpp/windows.md

@@ -26,15 +26,15 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 
 ### Step2: 下载PaddlePaddle C++ 预测库 paddle_inference
 
-PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX依赖于Paddle 1.8,基于Paddle 1.8的Paddle预测库下载链接如下所示:
+PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX依赖于Paddle 1.8.4,基于Paddle 1.8.4的Paddle预测库下载链接如下所示:
 
-|  版本说明   | 预测库(1.8.2版本)  | 编译器 | 构建工具| cuDNN | CUDA |
+|  版本说明   | 预测库(1.8.4版本)  | 编译器 | 构建工具| cuDNN | CUDA |
 |  ----  |  ----  |  ----  |  ----  | ---- | ---- |
-| cpu_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.2/win-infer/mkl/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
-| cpu_avx_openblas  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.2/win-infer/open/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
-| cuda9.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.2/win-infer/mkl/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
-| cuda9.0_cudnn7_avx_openblas  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.2/win-infer/open/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
-| cuda10.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.2/win-infer/mkl/post107/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.5.0 | 9.0 |
+| cpu_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.4/win-infer/mkl/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
+| cpu_avx_openblas  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.4/win-infer/open/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
+| cuda9.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.4/win-infer/mkl/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
+| cuda9.0_cudnn7_avx_openblas  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.4/win-infer/open/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
+| cuda10.0_cudnn7_avx_mkl  | [paddle_inference](https://paddle-wheel.bj.bcebos.com/1.8.4/win-infer/mkl/post107/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.5.0 | 10.0 |
 
 请根据实际情况选择下载,如若以上版本不满足您的需求,请至[C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/windows_cpp_inference.html)选择符合的版本。
 
@@ -82,7 +82,7 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 1. 如果使用`CPU`版预测库,请把`WITH_GPU`的`值`去掉勾
 2. 如果使用的是`openblas`版本,请把`WITH_MKL`的`值`去掉勾
 3. Windows环境下编译会自动下载YAML,如果编译环境无法访问外网,可手动下载: [yaml-cpp.zip](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip)。YAML文件下载后无需解压,在`cmake/yaml.cmake`中将`URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip` 中的网址,改为下载文件的路径。
-4. 如果需要使用模型加密功能,需要手动下载[Windows预测模型加密工具](https://bj.bcebos.com/paddlex/tools/win/paddlex-encryption.zip)。例如解压到`D:/projects`,解压后目录为`D:/projects/paddlex-encryption`。编译时需勾选`WITH_EBNCRYPTION`并且在`ENCRTYPTION_DIR`填入`D:/projects/paddlex-encryption`。
+4. 如果需要使用模型加密功能,需要手动下载[Windows预测模型加密工具](https://bj.bcebos.com/paddlex/tools/win/1.2.0/paddlex-encryption.zip)。例如解压到`D:/projects`,解压后目录为`D:/projects/paddlex-encryption`。编译时需勾选`WITH_EBNCRYPTION`并且在`ENCRTYPTION_DIR`填入`D:/projects/paddlex-encryption`。
 ![](../../images/vs2019_step_encryption.png)
 ![](../../images/vs2019_step6.png)
 **设置完成后**, 点击上图中`保存并生成CMake缓存以加载变量`。
@@ -109,6 +109,8 @@ cd D:\projects\PaddleX\deploy\cpp\out\build\x64-Release
 | image  | 要预测的图片文件路径 |
 | image_list  | 按行存储图片路径的.txt文件 |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
+| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |
+| mkl_thread_num | MKL推理的线程数,默认为cpu处理器个数 |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | save_dir | 保存可视化结果的路径, 默认值为"output",classifier无该参数 |
 | key | 加密过程中产生的密钥信息,默认值为""表示加载的是未加密的模型 |
@@ -124,6 +126,8 @@ cd D:\projects\PaddleX\deploy\cpp\out\build\x64-Release
 | camera_id | 摄像头设备ID,默认值为0 |
 | video_path | 视频文件的路径 |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
+| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |
+| mkl_thread_num | MKL推理的线程数,默认为cpu处理器个数 |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | show_result | 对视频文件做预测时,是否在屏幕上实时显示预测可视化结果(因加入了延迟处理,故显示结果不能反映真实的帧率),支持值为0或1(默认值为0) |
 | save_result | 是否将每帧的预测可视结果保存为视频文件,支持值为0或1(默认值为1) |

+ 2 - 2
docs/deploy/server/encryption.md

@@ -40,9 +40,9 @@ PaddleX提供一个轻量级的模型加密部署方案,通过PaddleX内置的
 
 ### 1.2 加密工具
 
-[Linux版本 PaddleX模型加密工具](https://bj.bcebos.com/paddlex/tools/paddlex-encryption.zip),编译脚本会自动下载该版本加密工具,您也可以选择手动下载。
+[Linux版本 PaddleX模型加密工具](https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip),编译脚本会自动下载该版本加密工具,您也可以选择手动下载。
 
-[Windows版本 PaddleX模型加密工具](https://bj.bcebos.com/paddlex/tools/win/paddlex-encryption.zip),该版本加密工具需手动下载,如果您在使用Visual Studio 2019编译C++预测代码的过程中已经下载过该工具,此处可不必重复下载。
+[Windows版本 PaddleX模型加密工具](https://bj.bcebos.com/paddlex/tools/win/1.2.0/paddlex-encryption.zip),该版本加密工具需手动下载,如果您在使用Visual Studio 2019编译C++预测代码的过程中已经下载过该工具,此处可不必重复下载。
 
 Linux加密工具包含内容为:
 ```

+ 100 - 0
docs/examples/change_detection.md

@@ -0,0 +1,100 @@
+# 地块变化检测
+
+本案例基于PaddleX实现地块变化检测,将同一地块的前期与后期两张图片进行拼接,而后输入给语义分割网络进行变化区域的预测。在训练阶段,使用随机缩放尺寸、旋转、裁剪、颜色空间扰动、水平翻转、竖直翻转多种数据增强策略。在验证和预测阶段,使用滑动窗口预测方式,以避免在直接对大尺寸图片进行预测时显存不足的发生。
+
+#### 前置依赖
+
+* Paddle paddle >= 1.8.4
+* Python >= 3.5
+* PaddleX >= 1.3.0
+
+安装的相关问题参考[PaddleX安装](../install.md)
+
+下载PaddleX源码:
+
+```
+git clone https://github.com/PaddlePaddle/PaddleX
+```
+
+该案例所有脚本均位于`PaddleX/examples/change_detection/`,进入该目录:
+
+```
+cd PaddleX/examples/change_detection/
+```
+
+## 数据准备
+
+本案例使用[Daifeng Peng等人](https://ieeexplore.ieee.org/document/9161009)开放的[Google Dataset](https://github.com/daifeng2016/Change-Detection-Dataset-for-High-Resolution-Satellite-Imagery), 该数据集涵盖了广州部分区域于2006年至2019年期间的房屋建筑物的变化情况,用于分析城市化进程。一共有20对高清图片,图片有红、绿、蓝三个波段,空间分辨率为0.55m,图片大小有1006x1168至4936x5224不等。
+
+由于Google Dataset仅标注了房屋建筑物是否发生变化,因此本案例是二分类变化检测任务,可根据实际需求修改类别数量即可拓展为多分类变化检测。
+
+本案例将15张图片划分入训练集,5张图片划分入验证集。由于图片尺寸过大,直接训练会发生显存不足的问题,因此以滑动窗口为(1024,1024)、步长为(512, 512)对训练图片进行切分,切分后的训练集一共有743张图片。以滑动窗口为(769, 769)、步长为(769,769)对验证图片进行切分,得到108张子图片,用于训练过程中的验证。
+
+运行以下脚本,下载原始数据集,并完成数据集的切分:
+
+```
+python prepare_data.py
+```
+
+切分后的数据示意如下:
+
+![](../../examples/change_detection/images/change_det_data.jpg)
+
+
+**注意:**
+
+* tiff格式的图片PaddleX统一使用gdal库读取,gdal安装可参考[文档](https://paddlex.readthedocs.io/zh_CN/develop/examples/multi-channel_remote_sensing/README.html#id2)。若数据是tiff格式的三通道RGB图像,如果不想安装gdal,需自行转成jpeg、bmp、png格式图片。
+
+* label文件需为单通道的png格式图片,且标注从0开始计数,标注255表示该类别不参与计算。例如本案例中,0表示`unchanged`类,1表示`changed`类。
+
+## 模型训练
+
+由于数据量较小,分割模型选择较好兼顾浅层细节信息和深层语义信息的UNet模型。运行以下脚本,进行模型训练:
+
+```
+python train.py
+```
+
+本案例使用0,1,2,3号GPU卡完成训练,可根据实际显存大小更改训练脚本中的GPU卡数量和`train_batch_size`的设置值,按`train_batch_size`的调整比例相应地调整学习率`learning_rate`,例如`train_batch_size`由16减少至8时,`learning_rate`则由0.1减少至0.05。此外,不同数据集上能获得最优精度所对应`learning_rate`可能有所不同,可以尝试调整。
+
+也可以跳过模型训练步骤,直接下载预训练模型进行后续的模型评估和预测:
+
+```
+wget https://bj.bcebos.com/paddlex/examples/change_detection/models/google_change_det_model.tar.gz
+tar -xvf google_change_det_model.tar.gz
+```
+
+## 模型评估
+
+在训练过程中,每隔10个迭代轮数会评估一次模型在验证集的精度。由于已事先将原始大尺寸图片切分成小块,相当于使用无重叠的滑动窗口预测方式,最优模型精度:
+
+| mean_iou | category__iou | overall_accuracy | category_accuracy | category_F1-score | kappa |
+| -- | -- | -- | -- | --| -- |
+| 84.24% | 97.54%、70.94%| 97.68% | 98.50%、85.99% | 98.75%、83% | 81.76% |
+
+category分别对应`unchanged`和`changed`两类。
+
+运行以下脚本,将采用有重叠的滑动窗口预测方式,重新评估原始大尺寸图片的模型精度,此时模型精度为:
+
+| mean_iou | category__iou | overall_accuracy | category_accuracy | category_F1-score | kappa |
+| -- | -- | -- | -- | --| -- |
+| 85.33% | 97.79%、72.87% | 97.97% | 98.66%、87.06% | 98.99%、84.30% | 83.19% |
+
+
+```
+python eval.py
+```
+
+滑动窗口预测接口说明详见[API说明](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict),已有的使用场景可参考[RGB遥感分割案例](https://paddlex.readthedocs.io/zh_CN/develop/examples/remote_sensing.html#id4)。可根据实际显存大小修改评估脚本中`tile_size`,`pad_size`和`batch_size`。
+
+## 模型预测
+
+执行以下脚本,使用有重叠的滑动预测窗口对验证集进行预测。可根据实际显存大小修改评估脚本中`tile_size`,`pad_size`和`batch_size`。
+
+```
+python predict.py
+```
+
+预测可视化结果如下图所示:
+
+![](../../examples/change_detection/images/change_det_prediction.jpg)

+ 3 - 0
docs/examples/index.rst

@@ -12,3 +12,6 @@ PaddleX精选飞桨视觉开发套件在产业实践中的成熟模型结构,
    solutions.md
    meter_reader.md
    human_segmentation.md
+   multi-channel_remote_sensing/README.md
+   remote_sensing.md
+   change_detection.md

+ 5 - 6
docs/examples/meter_reader.md

@@ -70,7 +70,6 @@ cd PaddleX/examples/meter_reader/
 | save_dir	| 保存可视化结果的路径, 默认值为"output"|
 | score_threshold | 检测模型输出结果中,预测得分低于该阈值的框将被滤除,默认值为0.5|
 | seg_batch_size | 分割的批量大小,默认为2 |
-| seg_thread_num	| 分割预测的线程数,默认为cpu处理器个数 |
 | use_camera | 是否使用摄像头采集图片,默认为False |
 | camera_id | 摄像头设备ID,默认值为0 |
 | use_erode | 是否使用图像腐蚀对分割预测图进行细分,默认为False |
@@ -91,19 +90,19 @@ export CUDA_VISIBLE_DEVICES=
 * 预测单张图片
 
 ```shell
-python3 reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --image /path/to/meter_test/20190822_168.jpg --save_dir ./output --use_erode
+python reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --image /path/to/meter_test/20190822_168.jpg --save_dir ./output --use_erode
 ```
 
 * 预测多张图片
 
 ```shell
-python3 reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --image_dir /path/to/meter_test --save_dir ./output --use_erode
+python reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --image_dir /path/to/meter_test --save_dir ./output --use_erode
 ```
 
 * 开启摄像头预测
 
 ```shell
-python3 reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --save_dir ./output --use_erode --use_camera
+python reader_infer.py --detector_dir /path/to/det_inference_model --segmenter_dir /path/to/seg_inference_model --save_dir ./output --use_erode --use_camera
 ```
 
 ## 推理部署
@@ -260,12 +259,12 @@ step 5. 推理预测:
 
 * 表盘检测的训练
 ```
-python3 /path/to/PaddleX/examples/meter_reader/train_detection.py
+python /path/to/PaddleX/examples/meter_reader/train_detection.py
 ```
 * 指针和刻度分割的训练
 
 ```
-python3 /path/to/PaddleX/examples/meter_reader/train_segmentation.py
+python /path/to/PaddleX/examples/meter_reader/train_segmentation.py
 
 ```
 

+ 125 - 0
docs/examples/multi-channel_remote_sensing/README.md

@@ -0,0 +1,125 @@
+# 多通道遥感影像分割
+遥感影像分割是图像分割领域中的重要应用场景,广泛应用于土地测绘、环境监测、城市建设等领域。遥感影像分割的目标多种多样,有诸如积雪、农作物、道路、建筑、水源等地物目标,也有例如云层的空中目标。
+
+本案例基于PaddleX实现多通道遥感影像分割,涵盖数据分析、模型训练、模型预测等流程,旨在帮助用户利用深度学习技术解决多通道遥感影像分割问题。
+
+
+## 前置依赖
+* Paddle paddle >= 1.8.4
+* Python >= 3.5
+* PaddleX >= 1.1.4
+
+安装的相关问题参考[PaddleX安装](../../install.md)
+
+**另外还需安装gdal**, 使用pip安装gdal可能出错,推荐使用conda进行安装:
+
+```
+conda install gdal
+```
+
+下载PaddleX源码:  
+
+```  
+git clone https://github.com/PaddlePaddle/PaddleX
+```
+
+该案例所有脚本均位于`PaddleX/examples/channel_remote_sensing/`,进入该目录:  
+
+```
+cd PaddleX/examples/channel_remote_sensing/  
+```
+
+## 数据准备
+遥感影像的格式多种多样,不同传感器产生的数据格式也可能不同。PaddleX现已兼容以下4种格式图片读取:
+
+- `tif`
+- `png`
+- `img`
+- `npy`
+
+标注图要求必须为单通道的png格式图像,像素值即为对应的类别,像素标注类别需要从0开始递增。例如0,1,2,3表示有4种类别,255用于指定不参与训练和评估的像素,标注类别最多为256类。
+
+本案例使用[L8 SPARCS公开数据集](https://www.usgs.gov/land-resources/nli/landsat/spatial-procedures-automated-removal-cloud-and-shadow-sparcs-validation)进行云雪分割,该数据集包含80张卫星影像,涵盖10个波段。原始标注图片包含7个类别,分别是`cloud`, `cloud shadow`, `shadow over water`, `snow/ice`, `water`, `land`和`flooded`。由于`flooded`和`shadow over water`2个类别占比仅为`1.8%`和`0.24%`,我们将其进行合并,`flooded`归为`land`,`shadow over water`归为`shadow`,合并后标注包含5个类别。
+
+数值、类别、颜色对应表:
+
+|Pixel value|Class|Color|
+|---|---|---|
+|0|cloud|white|
+|1|shadow|black|
+|2|snow/ice|cyan|
+|3|water|blue|
+|4|land|grey|
+
+ ![](../../../examples/multi-channel_remote_sensing/docs/images/dataset.png)
+
+
+执行以下命令下载并解压经过类别合并后的数据集:
+```shell script
+mkdir dataset && cd dataset
+wget https://paddleseg.bj.bcebos.com/dataset/remote_sensing_seg.zip
+unzip remote_sensing_seg.zip
+cd ..
+```
+其中`data`目录存放遥感影像,`data_vis`目录存放彩色合成预览图,`mask`目录存放标注图。
+
+## 数据分析  
+遥感影像往往由许多波段组成,不同波段数据分布可能大相径庭,例如可见光波段和热红外波段分布十分不同。为了更深入了解数据的分布来优化模型训练效果,需要对数据进行分析。
+
+参考文档[数据分析](./analysis.md)对训练集进行统计分析,确定图像像素值的截断范围,并统计截断后的均值和方差。
+
+## 模型训练
+本案例选择`UNet`语义分割模型完成云雪分割,运行以下步骤完成模型训练,模型的最优精度`miou`为`78.38%`。
+
+* 设置GPU卡号
+```shell script
+export CUDA_VISIBLE_DEVICES=0
+```
+
+* 运行以下脚本开始训练
+```shell script
+python train.py --data_dir dataset/remote_sensing_seg \
+--train_file_list dataset/remote_sensing_seg/train.txt \
+--eval_file_list dataset/remote_sensing_seg/val.txt \
+--label_list dataset/remote_sensing_seg/labels.txt \
+--save_dir saved_model/remote_sensing_unet \
+--num_classes 5 \
+--channel 10 \
+--lr 0.01 \
+--clip_min_value 7172 6561 5777 5103 4291 4000 4000 4232 6934 7199 \
+--clip_max_value 50000 50000 50000 50000 50000 40000 30000 18000 40000 36000 \
+--mean 0.15163569 0.15142828 0.15574491 0.1716084  0.2799778  0.27652043 0.28195933 0.07853807 0.56333154 0.5477584 \
+--std  0.09301891 0.09818967 0.09831126 0.1057784  0.10842132 0.11062996 0.12791838 0.02637859 0.0675052  0.06168227 \
+--num_epochs 500 \
+--train_batch_size 3
+```
+
+也可以跳过模型训练步骤,下载预训练模型直接进行模型预测:
+
+```
+wget https://bj.bcebos.com/paddlex/examples/multi-channel_remote_sensing/models/l8sparcs_remote_model.tar.gz
+tar -xvf l8sparcs_remote_model.tar.gz
+```
+
+## 模型预测
+运行以下脚本,对遥感图像进行预测并可视化预测结果,相应地也将对应的标注文件进行可视化,以比较预测效果。
+
+```shell script
+export CUDA_VISIBLE_DEVICES=0
+python predict.py
+```
+可视化效果如下所示:
+
+
+![](../../../examples/multi-channel_remote_sensing/docs/images/prediction.jpg)
+
+
+数值、类别、颜色对应表:
+
+|Pixel value|Class|Color|
+|---|---|---|
+|0|cloud|white|
+|1|shadow|black|
+|2|snow/ice|cyan|
+|3|water|blue|
+|4|land|grey|

+ 121 - 0
docs/examples/multi-channel_remote_sensing/analysis.md

@@ -0,0 +1,121 @@
+# 数据分析
+
+遥感影像往往由许多波段组成,不同波段数据分布可能大相径庭,例如可见光波段和热红外波段分布十分不同。为了更深入了解数据的分布来优化模型训练效果,需要对数据进行分析。
+
+## 统计分析
+执行以下脚本,对训练集进行统计分析,屏幕会输出分析结果,同时结果也会保存至文件`train_information.pkl`中:
+
+```
+python tools/analysis.py
+```
+
+数据统计分析内容如下:
+
+* 图像数量
+
+例如统计出训练集中有64张图片:
+```
+64 samples in file dataset/remote_sensing_seg/train.txt
+```
+* 图像最大和最小的尺寸
+
+例如统计出训练集中最大的高宽和最小的高宽分别是(1000, 1000)和(1000, 1000):
+```
+Minimal image height: 1000 Minimal image width: 1000.
+Maximal image height: 1000 Maximal image width: 1000.
+```
+* 图像通道数量
+
+例如统计出图像的通道数量为10:
+
+```
+Image channel is 10.
+```
+* 图像各通道的最小值和最大值
+
+最小值和最大值分别以列表的形式输出,按照通道从小到大排列。例如:
+
+```
+Minimal image value: [7.172e+03 6.561e+03 5.777e+03 5.103e+03 4.291e+03 1.000e+00 1.000e+00 4.232e+03 6.934e+03 7.199e+03]
+Maximal image value: [65535. 65535. 65535. 65535. 65535. 65535. 65535. 56534. 65535. 63215.]
+
+```
+* 图像各通道的像素值分布
+
+针对各个通道,统计出各像素值的数量,并以柱状图的形式呈现在以'distribute.png'结尾的图片中。**需要注意的是,为便于观察,纵坐标为对数坐标**。用户可以查看这些图片来选择是否需要对分布在头部和尾部的像素值进行截断。
+
+```
+Image pixel distribution of each channel is saved with 'distribute.png' in the dataset/remote_sensing_seg
+```
+
+* 图像各通道归一化后的均值和方差
+
+各通道归一化系数为各通道最大值与最小值之差,均值和方差以列别形式输出,按照通道从小到大排列。例如:
+
+```
+Image mean value: [0.23417574 0.22283101 0.2119595  0.2119887  0.27910388 0.21294892 0.17294037 0.10158925 0.43623915 0.41019192]
+Image standard deviation: [0.06831269 0.07243951 0.07284761 0.07875261 0.08120818 0.0609302 0.05110716 0.00696064 0.03849307 0.03205579]
+```
+
+* 标注图中各类别的数量及比重
+
+统计各类别的像素数量和在数据集全部像素的占比,以(类别值,该类别的数量,该类别的占比)的格式输出。例如:
+
+```
+Label pixel information is shown in a format of (label_id, the number of label_id, the ratio of label_id):
+(0, 13302870, 0.20785734374999995)
+(1, 4577005, 0.07151570312500002)
+(2, 3955012, 0.0617970625)
+(3, 2814243, 0.04397254687499999)
+(4, 39350870, 0.6148573437500001)
+
+```
+
+## 2 确定像素值截断范围
+
+遥感影像数据分布范围广,往往存在一些异常值,这会影响算法对实际数据分布的拟合效果。为更好地对数据进行归一化,可以抑制遥感影像中少量的异常值。根据`图像各通道的像素值分布`来确定像素值的截断范围,并在后续图像预处理过程中对超出范围的像素值通过截断进行校正,从而去除异常值带来的干扰。**注意:该步骤是否执行根据数据集实际分布来决定。**
+
+例如各通道的像素值分布可视化效果如下:
+
+![](../../../examples/multi-channel_remote_sensing/docs/images/image_pixel_distribution.png)
+**需要注意的是,为便于观察,纵坐标为对数坐标。**
+
+
+对于上述分布,我们选取的截断范围是(按照通道从小到大排列):
+
+```
+截断范围最小值: clip_min_value = [7172,  6561,  5777, 5103, 4291, 4000, 4000, 4232, 6934, 7199]
+截断范围最大值: clip_max_value = [50000, 50000, 50000, 50000, 50000, 40000, 30000, 18000, 40000, 36000]
+```
+
+## 3 确定像素值截断范围
+
+为避免数据截断范围选取不当带来的影响,应该统计异常值像素占比,确保受影响的像素比例不要过高。接着对截断后的数据计算归一化后的均值和方差,**用于后续模型训练时的图像预处理参数设置**。
+
+执行以下脚本:
+```
+python tools/cal_clipped_mean_std.py
+```
+
+截断像素占比统计结果如下:
+
+```
+Channel 0, the ratio of pixels to be clipped = 0.00054778125
+Channel 1, the ratio of pixels to be clipped = 0.0011129375
+Channel 2, the ratio of pixels to be clipped = 0.000843703125
+Channel 3, the ratio of pixels to be clipped = 0.00127125
+Channel 4, the ratio of pixels to be clipped = 0.001330140625
+Channel 5, the ratio of pixels to be clipped = 8.1375e-05
+Channel 6, the ratio of pixels to be clipped = 0.0007348125
+Channel 7, the ratio of pixels to be clipped = 6.5625e-07
+Channel 8, the ratio of pixels to be clipped = 0.000185921875
+Channel 9, the ratio of pixels to be clipped = 0.000139671875
+```
+可看出,被截断像素占比均不超过0.2%。
+
+裁剪后数据的归一化系数如下:
+```
+Image mean value: [0.15163569 0.15142828 0.15574491 0.1716084  0.2799778  0.27652043 0.28195933 0.07853807 0.56333154 0.5477584 ]
+Image standard deviation: [0.09301891 0.09818967 0.09831126 0.1057784  0.10842132 0.11062996 0.12791838 0.02637859 0.0675052  0.06168227]
+(normalized by (clip_max_value - clip_min_value), arranged in 0-10 channel order)
+```

+ 82 - 0
docs/examples/remote_sensing.md

@@ -0,0 +1,82 @@
+# RGB遥感影像分割
+
+本案例基于PaddleX实现遥感影像分割,提供滑动窗口预测方式,以避免在直接对大尺寸图片进行预测时显存不足的发生。此外,滑动窗口之间的重叠程度可配置,以此消除最终预测结果中各窗口拼接处的裂痕感。
+
+## 前置依赖
+
+* Paddle paddle >= 1.8.4
+* Python >= 3.5
+* PaddleX >= 1.1.4
+
+安装的相关问题参考[PaddleX安装](../install.md)
+
+下载PaddleX源码:
+
+```
+git clone https://github.com/PaddlePaddle/PaddleX
+```
+
+该案例所有脚本均位于`PaddleX/examples/remote_sensing/`,进入该目录:
+
+```
+cd PaddleX/examples/remote_sensing/
+```
+
+## 数据准备
+
+本案例使用2015 CCF大数据比赛提供的高清遥感影像,包含5张带标注的RGB图像,图像尺寸最大有7969 × 7939、最小有4011 × 2470。该数据集共标注了5类物体,分别是背景(标记为0)、植被(标记为1)、建筑(标记为2)、水体(标记为3)、道路 (标记为4)。
+
+本案例将前4张图片划分入训练集,第5张图片作为验证集。为增加训练时的批量大小,以滑动窗口为(1024,1024)、步长为(512, 512)对前4张图片进行切分,加上原本的4张大尺寸图片,训练集一共有688张图片。在训练过程中直接对大图片进行验证会导致显存不足,为避免此类问题的出现,针对验证集以滑动窗口为(769, 769)、步长为(769,769)对第5张图片进行切分,得到40张子图片。
+
+运行以下脚本,下载原始数据集,并完成数据集的切分:
+
+```
+python prepare_data.py
+```
+
+## 模型训练
+
+分割模型选择Backbone为MobileNetv3_large_ssld的Deeplabv3模型,该模型兼备高性能高精度的优点。运行以下脚本,进行模型训练:
+```
+python train.py
+```
+
+也可以跳过模型训练步骤,直接下载预训练模型进行后续的模型预测和评估:
+```
+wget https://bj.bcebos.com/paddlex/examples/remote_sensing/models/ccf_remote_model.tar.gz
+tar -xvf ccf_remote_model.tar.gz
+```
+
+## 模型预测
+
+直接对大尺寸图片进行预测会导致显存不足,为避免此类问题的出现,本案例提供了滑动窗口预测接口,支持有重叠和无重叠两种方式。
+
+* 无重叠的滑动窗口预测
+
+在输入图片上以固定大小的窗口滑动,分别对每个窗口下的图像进行预测,最后将各窗口的预测结果拼接成输入图片的预测结果。由于每个窗口边缘部分的预测效果会比中间部分的差,因此每个窗口拼接处可能会有明显的裂痕感。
+
+该预测方式的API接口详见[overlap_tile_predict](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict),**使用时需要把参数`pad_size`设置为`[0, 0]`**。
+
+* 有重叠的滑动窗口预测
+
+在Unet论文中,作者提出一种有重叠的滑动窗口预测策略(Overlap-tile strategy)来消除拼接处的裂痕感。对各滑动窗口预测时,会向四周扩展一定的面积,对扩展后的窗口进行预测,例如下图中的蓝色部分区域,到拼接时只取各窗口中间部分的预测结果,例如下图中的黄色部分区域。位于输入图像边缘处的窗口,其扩展面积下的像素则通过将边缘部分像素镜像填补得到。
+
+该预测方式的API接口说明详见[overlap_tile_predict](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict)。
+
+![](../../examples/remote_sensing/images/overlap_tile.png)
+
+相比无重叠的滑动窗口预测,有重叠的滑动窗口预测策略将本案例的模型精度miou从80.58%提升至81.52%,并且将预测可视化结果中裂痕感显著消除,可见下图中两种预测方式的效果对比。
+
+![](../../examples/remote_sensing/images/visualize_compare.jpg)
+
+运行以下脚本使用有重叠的滑动窗口进行预测:
+```
+python predict.py
+```
+
+## 模型评估
+
+在训练过程中,每隔10个迭代轮数会评估一次模型在验证集的精度。由于已事先将原始大尺寸图片切分成小块,此时相当于使用无重叠的大图切小图预测方式,最优模型精度miou为80.58%。运行以下脚本,将采用有重叠的大图切小图的预测方式,重新评估原始大尺寸图片的模型精度,此时miou为81.52%。
+```
+python eval.py
+```

+ 0 - 5
docs/examples/remote_sensing/index.rst

@@ -1,5 +0,0 @@
-遥感分割案例
-=======================================
-
-
-这里面写遥感分割案例,可根据需求拆分为多个文档

+ 1 - 1
docs/gui/download.md

@@ -24,5 +24,5 @@
   * **内存**:建议8G以上  
   * **硬盘空间**:建议SSD剩余空间1T以上(非必须)  
 
-***注:PaddleX在Windows及Mac OS系统只支持单卡模型。Windows系统暂不支持NCCL。***
+***注:PaddleX在Mac OS系统只支持单卡模型。Windows系统暂不支持NCCL。***
 

+ 11 - 1
docs/gui/faq.md

@@ -1,4 +1,4 @@
-## FAQ
+
 
 1. **为什么训练速度这么慢?**
 
@@ -28,8 +28,18 @@
 5. **如何调用后端代码?**
 
    PaddleX 团队为您整理了相关的API接口文档,方便您学习和使用。具体请参见[PaddleX API说明文档](https://paddlex.readthedocs.io/zh_CN/latest/apis/index.html)
+   
+   
+   
+6. **如何在离线环境下使用PaddleX?**
+
+   PaddleX是支撑用户在本地离线环境中训练模型的,但是如果大家希望使用PaddleX团队为大家准备好的在标准数据集上训练的预训练模型,则需要在线环境进行下载。大家可以参照完整的无联网情况下进行模型训练的[文档](https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/appendix/how_to_offline_run.md)查看如何一键快速下载所有预训练模型。
+
+   
 
+7. **有没有行业应用案例,或者实现好的工程实例?**
 
+   有的,PaddleX提供丰富的行业应用案例和完整的示例项目,请参考[PaddleX产业案例集](https://paddlex.readthedocs.io/zh_CN/develop/examples/index.html)
 
 **如果您有任何问题或建议,欢迎以issue的形式,或加入PaddleX官方QQ群(1045148026)直接反馈您的问题和需求**
 

+ 2 - 0
docs/gui/how_to_use.md

@@ -1,5 +1,7 @@
 # PaddleX GUI使用教程
 
+*注:如果你的系统是 Mac OS 10.15.5及以上,在双击客户端icon后,需要在Terminal中执行 sudo xattr -r -d com.apple.quarantine /Users/username/PaddleX ,并稍等几秒来启动客户端,其中 /Users/username/PaddleX 为您保存PaddleX的文件夹路径*
+
 **第一步:准备数据**
 
 在开始模型训练前,您需要根据不同的任务类型,将数据标注为相应的格式。目前PaddleX支持【图像分类】、【目标检测】、【语义分割】、【实例分割】四种任务类型。不同类型任务的数据处理方式可查看[数据标注方式](https://paddlex.readthedocs.io/zh_CN/latest/appendix/datasets.html)。

BIN
docs/gui/images/LIME.png


BIN
docs/gui/images/paddlexoverview.png


+ 107 - 0
examples/change_detection/README.md

@@ -0,0 +1,107 @@
+# 地块变化检测
+
+本案例基于PaddleX实现地块变化检测,将同一地块的前期与后期两张图片进行拼接,而后输入给语义分割网络进行变化区域的预测。在训练阶段,使用随机缩放尺寸、旋转、裁剪、颜色空间扰动、水平翻转、竖直翻转多种数据增强策略。在验证和预测阶段,使用滑动窗口预测方式,以避免在直接对大尺寸图片进行预测时显存不足的发生。
+
+## 目录
+* [数据准备](#1)
+* [模型训练](#2)
+* [模型评估](#3)
+* [模型预测](#4)
+
+
+#### 前置依赖
+
+* Paddle paddle >= 1.8.4
+* Python >= 3.5
+* PaddleX >= 1.3.0
+
+安装的相关问题参考[PaddleX安装](../install.md)
+
+下载PaddleX源码:
+
+```
+git clone https://github.com/PaddlePaddle/PaddleX
+```
+
+该案例所有脚本均位于`PaddleX/examples/change_detection/`,进入该目录:
+
+```
+cd PaddleX/examples/change_detection/
+```
+
+## <h2 id="1">数据准备</h2>
+
+本案例使用[Daifeng Peng等人](https://ieeexplore.ieee.org/document/9161009)开放的[Google Dataset](https://github.com/daifeng2016/Change-Detection-Dataset-for-High-Resolution-Satellite-Imagery), 该数据集涵盖了广州部分区域于2006年至2019年期间的房屋建筑物的变化情况,用于分析城市化进程。一共有20对高清图片,图片有红、绿、蓝三个波段,空间分辨率为0.55m,图片大小有1006x1168至4936x5224不等。
+
+由于Google Dataset仅标注了房屋建筑物是否发生变化,因此本案例是二分类变化检测任务,可根据实际需求修改类别数量即可拓展为多分类变化检测。
+
+本案例将15张图片划分入训练集,5张图片划分入验证集。由于图片尺寸过大,直接训练会发生显存不足的问题,因此以滑动窗口为(1024,1024)、步长为(512, 512)对训练图片进行切分,切分后的训练集一共有743张图片。以滑动窗口为(769, 769)、步长为(769,769)对验证图片进行切分,得到108张子图片,用于训练过程中的验证。
+
+运行以下脚本,下载原始数据集,并完成数据集的切分:
+
+```
+python prepare_data.py
+```
+
+切分后的数据示意如下:
+
+<img src="./images/change_det_data.jpg" alt="变化检测数据" align=center />
+
+
+**注意:**
+
+* tiff格式的图片PaddleX统一使用gdal库读取,gdal安装可参考[文档](https://paddlex.readthedocs.io/zh_CN/develop/examples/multi-channel_remote_sensing/README.html#id2)。若数据是tiff格式的三通道RGB图像,如果不想安装gdal,需自行转成jpeg、bmp、png格式图片。
+
+* label文件需为单通道的png格式图片,且标注从0开始计数,标注255表示该类别不参与计算。例如本案例中,0表示`unchanged`类,1表示`changed`类。
+
+## <h2 id="2">模型训练</h2>
+
+由于数据量较小,分割模型选择较好兼顾浅层细节信息和深层语义信息的UNet模型。运行以下脚本,进行模型训练:
+
+```
+python train.py
+```
+
+本案例使用0,1,2,3号GPU卡完成训练,可根据实际显存大小更改训练脚本中的GPU卡数量和`train_batch_size`的设置值,按`train_batch_size`的调整比例相应地调整学习率`learning_rate`,例如`train_batch_size`由16减少至8时,`learning_rate`则由0.1减少至0.05。此外,不同数据集上能获得最优精度所对应`learning_rate`可能有所不同,可以尝试调整。
+
+也可以跳过模型训练步骤,直接下载预训练模型进行后续的模型评估和预测:
+
+```
+wget https://bj.bcebos.com/paddlex/examples/change_detection/models/google_change_det_model.tar.gz
+tar -xvf google_change_det_model.tar.gz
+```
+
+## <h2 id="3">模型评估</h2>
+
+在训练过程中,每隔10个迭代轮数会评估一次模型在验证集的精度。由于已事先将原始大尺寸图片切分成小块,相当于使用无重叠的滑动窗口预测方式,最优模型精度:
+
+| mean_iou | category__iou | overall_accuracy | category_accuracy | category_F1-score | kappa |
+| -- | -- | -- | -- | --| -- |
+| 84.24% | 97.54%、70.94%| 97.68% | 98.50%、85.99% | 98.75%、83% | 81.76% |
+
+category分别对应`unchanged`和`changed`两类。
+
+运行以下脚本,将采用有重叠的滑动窗口预测方式,重新评估原始大尺寸图片的模型精度,此时模型精度为:
+
+| mean_iou | category__iou | overall_accuracy | category_accuracy | category_F1-score | kappa |
+| -- | -- | -- | -- | --| -- |
+| 85.33% | 97.79%、72.87% | 97.97% | 98.66%、87.06% | 98.99%、84.30% | 83.19% |
+
+
+```
+python eval.py
+```
+
+滑动窗口预测接口说明详见[API说明](https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict),已有的使用场景可参考[RGB遥感分割案例](https://paddlex.readthedocs.io/zh_CN/develop/examples/remote_sensing.html#id4)。可根据实际显存大小修改评估脚本中`tile_size`,`pad_size`和`batch_size`。
+
+## <h2 id="4">模型预测</h2>
+
+执行以下脚本,使用有重叠的滑动预测窗口对验证集进行预测。可根据实际显存大小修改评估脚本中`tile_size`,`pad_size`和`batch_size`。
+
+```
+python predict.py
+```
+
+预测可视化结果如下图所示:
+
+<img src="./images/change_det_prediction.jpg" alt="变化检测预测图" align=center />

+ 72 - 0
examples/change_detection/eval.py

@@ -0,0 +1,72 @@
+# 环境变量配置,用于控制是否使用GPU
+# 说明文档:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html#gpu
+import os
+import os.path as osp
+os.environ['CUDA_VISIBLE_DEVICES'] = '0'
+
+import numpy as np
+import cv2
+from PIL import Image
+from collections import OrderedDict
+
+import paddlex as pdx
+import paddlex.utils.logging as logging
+from paddlex.seg import transforms
+from paddlex.cv.models.utils.seg_eval import ConfusionMatrix
+
+model_dir = 'output/unet/best_model'
+data_dir = 'google_change_det_dataset'
+file_list = 'google_change_det_dataset/val_list.txt'
+
+
+def update_confusion_matrix(confusion_matrix, predction, label):
+    pred = predction["label_map"]
+    pred = pred[np.newaxis, :, :, np.newaxis]
+    pred = pred.astype(np.int64)
+    label = label[np.newaxis, np.newaxis, :, :]
+    mask = label != model.ignore_index
+    confusion_matrix.calculate(pred=pred, label=label, ignore=mask)
+
+
+model = pdx.load_model(model_dir)
+
+conf_mat = ConfusionMatrix(model.num_classes, streaming=True)
+
+with open(file_list, 'r') as f:
+    for line in f:
+        items = line.strip().split()
+        full_path_im1 = osp.join(data_dir, items[0])
+        full_path_im2 = osp.join(data_dir, items[1])
+        full_path_label = osp.join(data_dir, items[2])
+
+        # 原图是tiff格式的图片,PaddleX统一使用gdal库读取
+        # 因训练数据已经转换成bmp格式,故此处使用opencv读取三通道的tiff图片
+        #image1 = transforms.Compose.read_img(full_path_im1)
+        #image2 = transforms.Compose.read_img(full_path_im2)
+        image1 = cv2.imread(full_path_im1)
+        image2 = cv2.imread(full_path_im2)
+        image = np.concatenate((image1, image2), axis=-1)
+
+        # API说明:https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict
+        overlap_tile_predict = model.overlap_tile_predict(
+            img_file=image,
+            tile_size=(769, 769),
+            pad_size=[512, 512],
+            batch_size=4)
+
+        # 将三通道的label图像转换成单通道的png格式图片
+        # 且将标注0和255转换成0和1
+        label = cv2.imread(full_path_label)
+        label = label[:, :, 0]
+        label = label != 0
+        label = label.astype(np.uint8)
+        update_confusion_matrix(conf_mat, overlap_tile_predict, label)
+
+category_iou, miou = conf_mat.mean_iou()
+category_acc, oacc = conf_mat.accuracy()
+category_f1score = conf_mat.f1_score()
+
+logging.info(
+    "miou={:.6f} category_iou={} oacc={:.6f} category_acc={} kappa={:.6f} category_F1-score={}".
+    format(miou, category_iou, oacc, category_acc,
+           conf_mat.kappa(), conf_mat.f1_score()))

BIN
examples/change_detection/images/change_det_data.jpg


BIN
examples/change_detection/images/change_det_prediction.jpg


+ 43 - 0
examples/change_detection/predict.py

@@ -0,0 +1,43 @@
+# 环境变量配置,用于控制是否使用GPU
+# 说明文档:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html#gpu
+import os
+os.environ['CUDA_VISIBLE_DEVICES'] = '0'
+import cv2
+import numpy as np
+
+import paddlex as pdx
+
+model_dir = 'output/unet_3/best_model'
+data_dir = 'google_change_det_dataset'
+file_list = 'google_change_det_dataset/val_list.txt'
+save_dir = 'output/unet/pred'
+
+if not os.path.exists(save_dir):
+    os.makedirs(save_dir)
+color = [0, 0, 0, 255, 255, 255]
+
+model = pdx.load_model(model_dir)
+
+with open(file_list, 'r') as f:
+    for line in f:
+        items = line.strip().split()
+        img_file_1 = os.path.join(data_dir, items[0])
+        img_file_2 = os.path.join(data_dir, items[1])
+
+        # 原图是tiff格式的图片,PaddleX统一使用gdal库读取
+        # 因训练数据已经转换成bmp格式,故此处使用opencv读取三通道的tiff图片
+        #image1 = transforms.Compose.read_img(img_file_1)
+        #image2 = transforms.Compose.read_img(img_file_2)
+        image1 = cv2.imread(img_file_1)
+        image2 = cv2.imread(img_file_2)
+        image = np.concatenate((image1, image2), axis=-1)
+
+        # API说明:https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#overlap-tile-predict
+        pred = model.overlap_tile_predict(
+            img_file=image,
+            tile_size=(769, 769),
+            pad_size=[512, 512],
+            batch_size=4)
+
+        pdx.seg.visualize(
+            img_file_1, pred, weight=0., save_dir=save_dir, color=color)

+ 130 - 0
examples/change_detection/prepara_data.py

@@ -0,0 +1,130 @@
+import os
+import os.path as osp
+import numpy as np
+import cv2
+import shutil
+import random
+# 为保证每次运行该脚本时划分的样本一致,故固定随机种子
+random.seed(0)
+
+import paddlex as pdx
+
+# 定义训练集切分时的滑动窗口大小和步长,格式为(W, H)
+train_tile_size = (1024, 1024)
+train_stride = (512, 512)
+# 定义验证集切分时的滑动窗口大小和步长,格式(W, H)
+val_tile_size = (769, 769)
+val_stride = (769, 769)
+# 训练集和验证集比例
+train_ratio = 0.75
+val_ratio = 0.25
+# 切分后的数据集保存路径
+tiled_dataset = './tiled_dataset'
+# 切分后的图像文件保存路径
+tiled_image_dir = osp.join(tiled_dataset, 'JPEGImages')
+# 切分后的标注文件保存路径
+tiled_anno_dir = osp.join(tiled_dataset, 'Annotations')
+
+# 下载和解压Google Dataset数据集
+change_det_dataset = 'https://bj.bcebos.com/paddlex/examples/change_detection/dataset/google_change_det_dataset.tar.gz'
+pdx.utils.download_and_decompress(change_det_dataset, path='./')
+change_det_dataset = './google_change_det_dataset'
+image1_dir = osp.join(change_det_dataset, 'T1')
+image2_dir = osp.join(change_det_dataset, 'T2')
+label_dir = osp.join(change_det_dataset, 'labels_change')
+
+if not osp.exists(tiled_image_dir):
+    os.makedirs(tiled_image_dir)
+if not osp.exists(tiled_anno_dir):
+    os.makedirs(tiled_anno_dir)
+
+# 划分数据集
+im1_file_list = os.listdir(image1_dir)
+im2_file_list = os.listdir(image2_dir)
+label_file_list = os.listdir(label_dir)
+im1_file_list = sorted(
+    im1_file_list, key=lambda k: int(k.split('test')[-1].split('_')[0]))
+im2_file_list = sorted(
+    im2_file_list, key=lambda k: int(k.split('test')[-1].split('_')[0]))
+label_file_list = sorted(
+    label_file_list, key=lambda k: int(k.split('test')[-1].split('_')[0]))
+
+file_list = list()
+for im1_file, im2_file, label_file in zip(im1_file_list, im2_file_list,
+                                          label_file_list):
+    im1_file = osp.join(image1_dir, im1_file)
+    im2_file = osp.join(image2_dir, im2_file)
+    label_file = osp.join(label_dir, label_file)
+    file_list.append((im1_file, im2_file, label_file))
+random.shuffle(file_list)
+train_num = int(len(file_list) * train_ratio)
+
+# 将大图切分成小图
+for i, item in enumerate(file_list):
+    if i < train_num:
+        stride = train_stride
+        tile_size = train_tile_size
+    else:
+        stride = val_stride
+        tile_size = val_tile_size
+    set_name = 'train' if i < train_num else 'val'
+
+    # 生成原图的file_list
+    im1_file, im2_file, label_file = item[:]
+    mode = 'w' if i in [0, train_num] else 'a'
+    with open(
+            osp.join(change_det_dataset, '{}_list.txt'.format(set_name)),
+            mode) as f:
+        f.write("T1/{} T2/{} labels_change/{}\n".format(
+            osp.split(im1_file)[-1],
+            osp.split(im2_file)[-1], osp.split(label_file)[-1]))
+
+    im1 = cv2.imread(im1_file)
+    im2 = cv2.imread(im2_file)
+    # 将三通道的label图像转换成单通道的png格式图片
+    # 且将标注0和255转换成0和1
+    label = cv2.imread(label_file, cv2.IMREAD_GRAYSCALE)
+    label = label != 0
+    label = label.astype(np.uint8)
+
+    H, W, C = im1.shape
+    tile_id = 1
+    im1_name = osp.split(im1_file)[-1].split('.')[0]
+    im2_name = osp.split(im2_file)[-1].split('.')[0]
+    label_name = osp.split(label_file)[-1].split('.')[0]
+    for h in range(0, H, stride[1]):
+        for w in range(0, W, stride[0]):
+            left = w
+            upper = h
+            right = min(w + tile_size[0], W)
+            lower = min(h + tile_size[1], H)
+            tile_im1 = im1[upper:lower, left:right, :]
+            tile_im2 = im2[upper:lower, left:right, :]
+            cv2.imwrite(
+                osp.join(tiled_image_dir,
+                         "{}_{}.bmp".format(im1_name, tile_id)), tile_im1)
+            cv2.imwrite(
+                osp.join(tiled_image_dir,
+                         "{}_{}.bmp".format(im2_name, tile_id)), tile_im2)
+            cut_label = label[upper:lower, left:right]
+            cv2.imwrite(
+                osp.join(tiled_anno_dir,
+                         "{}_{}.png".format(label_name, tile_id)), cut_label)
+            mode = 'w' if i in [0, train_num] and tile_id == 1 else 'a'
+            with open(
+                    osp.join(tiled_dataset, '{}_list.txt'.format(set_name)),
+                    mode) as f:
+                f.write(
+                    "JPEGImages/{}_{}.bmp JPEGImages/{}_{}.bmp Annotations/{}_{}.png\n".
+                    format(im1_name, tile_id, im2_name, tile_id, label_name,
+                           tile_id))
+            tile_id += 1
+
+# 生成labels.txt
+label_list = ['unchanged', 'changed']
+for i, label in enumerate(label_list):
+    mode = 'w' if i == 0 else 'a'
+    with open(osp.join(tiled_dataset, 'labels.txt'), 'a') as f:
+        name = "{}\n".format(label) if i < len(
+            label_list) - 1 else "{}".format(label)
+        f.write(name)

+ 63 - 0
examples/change_detection/train.py

@@ -0,0 +1,63 @@
+# 环境变量配置,用于控制是否使用GPU
+# 说明文档:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html#gpu
+import os
+os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
+
+import paddlex as pdx
+from paddlex.seg import transforms
+
+# 定义训练和验证时的transforms
+# API说明 https://paddlex.readthedocs.io/zh_CN/develop/apis/transforms/seg_transforms.html
+train_transforms = transforms.Compose([
+    transforms.ResizeStepScaling(
+        min_scale_factor=0.5, max_scale_factor=2.,
+        scale_step_size=0.25), transforms.RandomRotate(
+            rotate_range=180,
+            im_padding_value=[127.5] * 6), transforms.RandomPaddingCrop(
+                crop_size=769, im_padding_value=[127.5] * 6),
+    transforms.RandomDistort(), transforms.RandomHorizontalFlip(),
+    transforms.RandomVerticalFlip(), transforms.Normalize(
+        mean=[0.5] * 6, std=[0.5] * 6, min_val=[0] * 6, max_val=[255] * 6)
+])
+
+eval_transforms = transforms.Compose([
+    transforms.Padding(
+        target_size=769, im_padding_value=[127.5] * 6), transforms.Normalize(
+            mean=[0.5] * 6, std=[0.5] * 6, min_val=[0] * 6, max_val=[255] * 6)
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://paddlex.readthedocs.io/zh_CN/develop/apis/datasets.html#paddlex-datasets-changedetdataset
+train_dataset = pdx.datasets.ChangeDetDataset(
+    data_dir='tiled_dataset',
+    file_list='tiled_dataset/train_list.txt',
+    label_list='tiled_dataset/labels.txt',
+    transforms=train_transforms,
+    num_workers=8,
+    shuffle=True)
+eval_dataset = pdx.datasets.ChangeDetDataset(
+    data_dir='tiled_dataset',
+    file_list='tiled_dataset/val_list.txt',
+    label_list='tiled_dataset/labels.txt',
+    num_workers=8,
+    transforms=eval_transforms)
+
+# 初始化模型,并进行训练
+# 可使用VisualDL查看训练指标,参考https://paddlex.readthedocs.io/zh_CN/develop/train/visualdl.html
+num_classes = len(train_dataset.labels)
+
+# API说明:https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#paddlex-seg-unet
+model = pdx.seg.UNet(num_classes=num_classes, input_channel=6)
+
+# API说明:https://paddlex.readthedocs.io/zh_CN/develop/apis/models/semantic_segmentation.html#train
+# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
+model.train(
+    num_epochs=400,
+    train_dataset=train_dataset,
+    train_batch_size=16,
+    eval_dataset=eval_dataset,
+    learning_rate=0.1,
+    save_interval_epochs=10,
+    pretrain_weights='CITYSCAPES',
+    save_dir='output/unet',
+    use_vdl=True)

Some files were not shown because too many files changed in this diff