瀏覽代碼

fix conflicts

FlyingQianMM 4 年之前
父節點
當前提交
6973e5994d
共有 72 個文件被更改,包括 2933 次插入922 次删除
  1. 1 1
      README.md
  2. 1 1
      dygraph/PaddleClas
  3. 1 1
      dygraph/PaddleDetection
  4. 28 2
      dygraph/deploy/cpp/CMakeLists.txt
  5. 3 3
      dygraph/deploy/cpp/CMakeSettings.json
  6. 3 3
      dygraph/deploy/cpp/README.md
  7. 92 0
      dygraph/deploy/cpp/demo/batch_infer.cpp
  8. 16 62
      dygraph/deploy/cpp/demo/model_infer.cpp
  9. 9 8
      dygraph/deploy/cpp/demo/multi_gpu_model_infer.cpp
  10. 1 1
      dygraph/deploy/cpp/demo/onnx_tensorrt/CMakeLists.txt
  11. 8 6
      dygraph/deploy/cpp/demo/onnx_tensorrt/model_infer.cpp
  12. 1 1
      dygraph/deploy/cpp/demo/onnx_triton/CMakeLists.txt
  13. 6 2
      dygraph/deploy/cpp/demo/onnx_triton/model_infer.cpp
  14. 78 0
      dygraph/deploy/cpp/demo/tensorrt_infer.cpp
  15. 146 32
      dygraph/deploy/cpp/docs/apis/model.md
  16. 24 25
      dygraph/deploy/cpp/docs/compile/paddle/linux.md
  17. 13 15
      dygraph/deploy/cpp/docs/compile/paddle/windows.md
  18. 11 15
      dygraph/deploy/cpp/docs/demo/model_infer.md
  19. 2 2
      dygraph/deploy/cpp/docs/demo/multi_gpu_model_infer.md
  20. 150 0
      dygraph/deploy/cpp/docs/demo/tensorrt_infer.md
  21. 二進制
      dygraph/deploy/cpp/docs/images/tensorrt.png
  22. 3 2
      dygraph/deploy/cpp/docs/models/paddleclas.md
  23. 19 17
      dygraph/deploy/cpp/docs/models/paddledetection.md
  24. 6 8
      dygraph/deploy/cpp/docs/models/paddleseg.md
  25. 11 18
      dygraph/deploy/cpp/model_deploy/common/include/base_model.h
  26. 5 6
      dygraph/deploy/cpp/model_deploy/common/include/multi_gpu_model.h
  27. 1 0
      dygraph/deploy/cpp/model_deploy/common/include/paddle_deploy.h
  28. 21 4
      dygraph/deploy/cpp/model_deploy/engine/include/engine_config.h
  29. 1 0
      dygraph/deploy/cpp/model_deploy/engine/include/tensorrt_engine.h
  30. 10 11
      dygraph/deploy/cpp/model_deploy/engine/src/ppinference_engine.cpp
  31. 16 19
      dygraph/deploy/cpp/model_deploy/engine/src/tensorrt_engine.cpp
  32. 2 8
      dygraph/deploy/cpp/model_deploy/engine/src/triton_engine.cpp
  33. 5 0
      dygraph/deploy/cpp/model_deploy/ppseg/include/seg_postprocess.h
  34. 32 0
      dygraph/deploy/cpp/model_deploy/ppseg/src/seg_postprocess.cpp
  35. 1 1
      dygraph/deploy/cpp/scripts/tensorrt_build.sh
  36. 1 1
      dygraph/deploy/cpp/scripts/triton_build.sh
  37. 209 9
      dygraph/paddlex/cls.py
  38. 7 0
      dygraph/paddlex/cv/datasets/coco.py
  39. 7 0
      dygraph/paddlex/cv/datasets/voc.py
  40. 73 9
      dygraph/paddlex/cv/models/base.py
  41. 92 5
      dygraph/paddlex/cv/models/classifier.py
  42. 148 68
      dygraph/paddlex/cv/models/detector.py
  43. 7 0
      dygraph/paddlex/cv/models/load_model.py
  44. 55 0
      dygraph/paddlex/cv/models/segmenter.py
  45. 4 6
      dygraph/paddlex/cv/models/utils/det_metrics/coco_utils.py
  46. 0 126
      dygraph/paddlex/cv/models/utils/det_metrics/json_results.py
  47. 0 305
      dygraph/paddlex/cv/models/utils/det_metrics/map_utils.py
  48. 10 10
      dygraph/paddlex/cv/models/utils/det_metrics/metrics.py
  49. 20 69
      dygraph/paddlex/cv/transforms/batch_operators.py
  50. 164 0
      dygraph/paddlex/cv/transforms/cls_transforms.py
  51. 152 0
      dygraph/paddlex/cv/transforms/det_transforms.py
  52. 536 0
      dygraph/paddlex/cv/transforms/seg_transforms.py
  53. 175 9
      dygraph/paddlex/det.py
  54. 214 9
      dygraph/paddlex/seg.py
  55. 1 1
      dygraph/paddlex/utils/__init__.py
  56. 11 0
      dygraph/paddlex/utils/checkpoint.py
  57. 5 5
      dygraph/requirements.txt
  58. 43 0
      dygraph/tutorials/slim/quantize/image_classification/mobilenetv2_qat.py
  59. 47 0
      dygraph/tutorials/slim/quantize/image_classification/mobilenetv2_train.py
  60. 55 0
      dygraph/tutorials/slim/quantize/object_detection/yolov3_qat.py
  61. 58 0
      dygraph/tutorials/slim/quantize/object_detection/yolov3_train.py
  62. 49 0
      dygraph/tutorials/slim/quantize/semantic_segmentation/unet_qat.py
  63. 52 0
      dygraph/tutorials/slim/quantize/semantic_segmentation/unet_train.py
  64. 0 1
      dygraph/tutorials/train/README.md
  65. 1 1
      dygraph/tutorials/train/image_classification/alexnet.py
  66. 5 6
      dygraph/tutorials/train/object_detection/faster_rcnn_hrnet_w18.py
  67. 0 1
      dygraph/tutorials/train/object_detection/ppyolo.py
  68. 0 1
      dygraph/tutorials/train/object_detection/ppyolotiny.py
  69. 0 1
      dygraph/tutorials/train/object_detection/ppyolov2.py
  70. 2 2
      paddlex/__init__.py
  71. 2 2
      paddlex_restful/restful/project/operate.py
  72. 1 1
      setup.py

+ 1 - 1
README.md

@@ -16,7 +16,7 @@
  ![QQGroup](https://img.shields.io/badge/QQ_Group-1045148026-52B6EF?style=social&logo=tencent-qq&logoColor=000&logoWidth=20)
 
 
-## PaddleX dygraph mode is ready! Static mode is set by default and dygraph graph code base is in [dygraph](https://github.com/PaddlePaddle/PaddleX/tree/develop/dygraph). If you want to use static mode, the version 1.3.10 can be installed by pip. The version 2.0.0rc0 corresponds to the dygraph mode.
+## PaddleX dygraph mode is ready! Static mode is set by default and dynamic graph code base is in [dygraph](https://github.com/PaddlePaddle/PaddleX/tree/develop/dygraph). If you want to use static mode, the version 1.3.10 can be installed by pip. The version 2.0.0rc0 corresponds to the dygraph mode.
 
 
 :hugs:  PaddleX integrated the abilities of **Image classification**, **Object detection**, **Semantic segmentation**, and **Instance segmentation** in the Paddle CV toolkits, and get through the whole-process development from **Data preparation** and **Model training and optimization** to **Multi-end deployment**. At the same time, PaddleX provides **Succinct APIs** and a **Graphical User Interface**. Developers can quickly complete the end-to-end process development of the Paddle in a form of **low-code**  without installing different libraries.

+ 1 - 1
dygraph/PaddleClas

@@ -1 +1 @@
-Subproject commit b5de5322b9f40449db4d55078044a1e1b44c0644
+Subproject commit 58a788db127c2692622a9279c1e46616ec2ce794

+ 1 - 1
dygraph/PaddleDetection

@@ -1 +1 @@
-Subproject commit 57e9f917dd8191834bcfc98a5df239d7fb42c941
+Subproject commit 6ec36068ee450df33c0038fb0cdc575b21510acd

+ 28 - 2
dygraph/deploy/cpp/CMakeLists.txt

@@ -133,6 +133,9 @@ if(WITH_GPU)
       message(FATAL_ERROR "please set CUDNN_LIB with -DCUDNN_LIB=/path/cudnn/")
     endif()
 
+    set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
+    set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
+
     if (WITH_TENSORRT)
       include_directories("${TENSORRT_DIR}/include")
       link_directories("${TENSORRT_DIR}/lib")
@@ -140,12 +143,20 @@ if(WITH_GPU)
       set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
       set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX})
     endif()
-    set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
-    set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
+
   else()
     set(DEPS ${DEPS} ${CUDA_LIB}/cudart${CMAKE_STATIC_LIBRARY_SUFFIX} )
     set(DEPS ${DEPS} ${CUDA_LIB}/cublas${CMAKE_STATIC_LIBRARY_SUFFIX} )
     set(DEPS ${DEPS} ${CUDA_LIB}/cudnn${CMAKE_STATIC_LIBRARY_SUFFIX})
+
+    if (WITH_TENSORRT)
+      include_directories("${TENSORRT_DIR}/include")
+      link_directories("${TENSORRT_DIR}/lib")
+
+      set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/nvinfer${CMAKE_STATIC_LIBRARY_SUFFIX})
+      set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/nvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX})
+      set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/myelin64_1${CMAKE_STATIC_LIBRARY_SUFFIX})
+    endif()
   endif()
 endif()
 
@@ -202,15 +213,30 @@ add_executable(model_infer ${PROJECT_ROOT_DIR}/demo/model_infer.cpp ${SRC} ${ENG
 ADD_DEPENDENCIES(model_infer ext-yaml-cpp)
 target_link_libraries(model_infer ${DEPS})
 
+add_executable(batch_infer ${PROJECT_ROOT_DIR}/demo/batch_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
+ADD_DEPENDENCIES(batch_infer ext-yaml-cpp)
+target_link_libraries(batch_infer ${DEPS})
+
 add_executable(multi_gpu_model_infer ${PROJECT_ROOT_DIR}/demo/multi_gpu_model_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
 ADD_DEPENDENCIES(multi_gpu_model_infer ext-yaml-cpp)
 target_link_libraries(multi_gpu_model_infer ${DEPS})
 
+if (WITH_TENSORRT)
+  add_executable(tensorrt_infer ${PROJECT_ROOT_DIR}/demo/tensorrt_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
+  ADD_DEPENDENCIES(tensorrt_infer ext-yaml-cpp)
+  target_link_libraries(tensorrt_infer ${DEPS})
+endif()
+
 if(WIN32)
   add_custom_command(TARGET model_infer POST_BUILD
     COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ${CMAKE_BINARY_DIR}/paddle_deploy
     COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ${CMAKE_BINARY_DIR}/paddle_deploy
     COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll  ${CMAKE_BINARY_DIR}/paddle_deploy
     COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_DIR}/paddle/lib/paddle_inference.dll ${CMAKE_BINARY_DIR}/paddle_deploy
+    if (WITH_TENSORRT)
+      COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_DIR}/lib/nvinfer.dll ${CMAKE_BINARY_DIR}/paddle_deploy
+      COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_DIR}/lib/nvinfer_plugin.dll ${CMAKE_BINARY_DIR}/paddle_deploy
+      COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_DIR}/lib/myelin64_1.dll ${CMAKE_BINARY_DIR}/paddle_deploy
+    endif()
   )
 endif()

+ 3 - 3
dygraph/deploy/cpp/CMakeSettings.json

@@ -29,9 +29,9 @@
           "type": "PATH"
         },
         {
-          "name": "WITH_STATIC_LIB",
-          "value": "True",
-          "type": "BOOL"
+          "name": "TENSORRT_DIR",
+          "value": "path\\to\\TensorRT_dir",
+          "type": "PATH"
         },
         {
           "name": "WITH_MKL",

+ 3 - 3
dygraph/deploy/cpp/README.md

@@ -3,10 +3,10 @@
 本目录下代码,目前支持以下飞桨官方套件基于PaddleInference的部署。
 
 ## 模型套件支持
-- PaddleDetection([release/2.0](https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.0))
-- PaddleSeg([release/2.0](https://github.com/PaddlePaddle/PaddleSeg/tree/release/v2.0))
+- PaddleDetection([release/2.1](https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.1))
+- PaddleSeg([release/2.1](https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.1))
 - PaddleClas([release/2.1](https://github.com/PaddlePaddle/PaddleClas/tree/release/2.1))
-- PaddleX([release/2.0-rc](https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc))
+- PaddleX([release/2.0-rc](https://github.com/PaddlePaddle/PaddleX))
 
 ## 硬件支持
 - CPU(linux/windows)

+ 92 - 0
dygraph/deploy/cpp/demo/batch_infer.cpp

@@ -0,0 +1,92 @@
+// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gflags/gflags.h>
+#include <omp.h>
+#include <memory>
+#include <string>
+#include <fstream>
+
+#include "model_deploy/common/include/paddle_deploy.h"
+
+DEFINE_string(model_filename, "", "Path of det inference model");
+DEFINE_string(params_filename, "", "Path of det inference params");
+DEFINE_string(cfg_file, "", "Path of yaml file");
+DEFINE_string(model_type, "", "model type");
+DEFINE_string(image_list, "", "Path of test image file");
+DEFINE_int32(batch_size, 1, "Batch size of infering");
+DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
+DEFINE_int32(gpu_id, 0, "GPU card id");
+DEFINE_bool(use_trt, false, "Infering with TensorRT");
+
+int main(int argc, char** argv) {
+  // Parsing command-line
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  // create model
+  std::shared_ptr<PaddleDeploy::Model> model =
+        PaddleDeploy::CreateModel(FLAGS_model_type);
+
+  // model init
+  model->Init(FLAGS_cfg_file);
+
+  // inference engine init
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.model_filename = FLAGS_model_filename;
+  engine_config.params_filename = FLAGS_params_filename;
+  engine_config.use_gpu = FLAGS_use_gpu;
+  engine_config.gpu_id = FLAGS_gpu_id;
+  engine_config.use_trt = FLAGS_use_trt;
+  if (FLAGS_use_trt) {
+    engine_config.precision = 0;
+  }
+  model->PaddleEngineInit(engine_config);
+
+  // Mini-batch
+  std::vector<std::string> image_paths;
+  if (FLAGS_image_list != "") {
+    std::ifstream inf(FLAGS_image_list);
+    if (!inf) {
+      std::cerr << "Fail to open file " << FLAGS_image_list << std::endl;
+      return -1;
+    }
+    std::string image_path;
+    while (getline(inf, image_path)) {
+      image_paths.push_back(image_path);
+    }
+  }
+
+  // infer
+  std::vector<PaddleDeploy::Result> results;
+  for (int i = 0; i < image_paths.size(); i += FLAGS_batch_size) {
+    // Read image
+    int im_vec_size =
+        std::min(static_cast<int>(image_paths.size()), i + FLAGS_batch_size);
+    std::vector<cv::Mat> im_vec(im_vec_size - i);
+    #pragma omp parallel for num_threads(im_vec_size - i)
+    for (int j = i; j < im_vec_size; ++j) {
+      im_vec[j - i] = std::move(cv::imread(image_paths[j], 1));
+    }
+
+    model->Predict(im_vec, &results);
+
+    std::cout << i / FLAGS_batch_size << " group -----" << std::endl;
+    for (auto j = 0; j < results.size(); ++j) {
+      std::cout << "Result for sample " << j << std::endl;
+      std::cout << results[j] << std::endl;
+    }
+  }
+
+  return 0;
+}

+ 16 - 62
dygraph/deploy/cpp/demo/model_infer.cpp

@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <glog/logging.h>
-#include <omp.h>
-#include <memory>
+#include <gflags/gflags.h>
 #include <string>
-#include <fstream>
+#include <vector>
 
 #include "model_deploy/common/include/paddle_deploy.h"
 
@@ -25,81 +23,37 @@ DEFINE_string(params_filename, "", "Path of det inference params");
 DEFINE_string(cfg_file, "", "Path of yaml file");
 DEFINE_string(model_type, "", "model type");
 DEFINE_string(image, "", "Path of test image file");
-DEFINE_string(image_list, "", "Path of test image file");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_int32(gpu_id, 0, "GPU card id");
-DEFINE_bool(use_mkl, true, "Infering with mkl");
-DEFINE_int32(batch_size, 1, "Batch size of infering");
-DEFINE_int32(thread_num, 1, "thread num of preprocessing");
-DEFINE_int32(mkl_thread_num, 8, "thread num of mkldnn");
 
 int main(int argc, char** argv) {
   // Parsing command-line
   google::ParseCommandLineFlags(&argc, &argv, true);
-  std::cout << "ParseCommandLineFlags:FLAGS_model_type="
-            << FLAGS_model_type << " model_filename="
-            << FLAGS_model_filename << std::endl;
 
   // create model
   std::shared_ptr<PaddleDeploy::Model> model =
         PaddleDeploy::CreateModel(FLAGS_model_type);
-  if (!model) {
-    std::cout << "no model_type: " << FLAGS_model_type
-              << "  model=" << model << std::endl;
-    return 0;
-  }
-  std::cout << "start model init " << std::endl;
 
   // model init
   model->Init(FLAGS_cfg_file);
-  std::cout << "start engine init " << std::endl;
 
-  // inference engine in
-  model->PaddleEngineInit(FLAGS_model_filename,
-                          FLAGS_params_filename,
-                          FLAGS_use_gpu,
-                          FLAGS_gpu_id,
-                          FLAGS_use_mkl,
-                          FLAGS_mkl_thread_num);
+  // inference engine init
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.model_filename = FLAGS_model_filename;
+  engine_config.params_filename = FLAGS_params_filename;
+  engine_config.use_gpu = FLAGS_use_gpu;
+  engine_config.gpu_id = FLAGS_gpu_id;
+  model->PaddleEngineInit(engine_config);
 
-  // Mini-batch
-  std::vector<std::string> image_paths;
-  if (FLAGS_image_list != "") {
-    std::ifstream inf(FLAGS_image_list);
-    if (!inf) {
-      std::cerr << "Fail to open file " << FLAGS_image_list << std::endl;
-      return -1;
-    }
-    std::string image_path;
-    while (getline(inf, image_path)) {
-      image_paths.push_back(image_path);
-    }
-  } else if (FLAGS_image != "") {
-    image_paths.push_back(FLAGS_image);
-  } else {
-    std::cerr << "image_list or image should be defined" << std::endl;
-    return -1;
-  }
+  // prepare data
+  std::vector<cv::Mat> imgs;
+  imgs.push_back(std::move(cv::imread(FLAGS_image)));
 
-  std::cout << "start model predict " << image_paths.size() << std::endl;
-  // infer
+  // predict
   std::vector<PaddleDeploy::Result> results;
-  for (int i = 0; i < image_paths.size(); i += FLAGS_batch_size) {
-    // Read image
-    int im_vec_size =
-        std::min(static_cast<int>(image_paths.size()), i + FLAGS_batch_size);
-    std::vector<cv::Mat> im_vec(im_vec_size - i);
-    #pragma omp parallel for num_threads(im_vec_size - i)
-    for (int j = i; j < im_vec_size; ++j) {
-      im_vec[j - i] = std::move(cv::imread(image_paths[j], 1));
-    }
+  model->Predict(imgs, &results, 1);
+
+  std::cout << results[0] << std::endl;
 
-    model->Predict(im_vec, &results, FLAGS_thread_num);
-    std::cout << i / FLAGS_batch_size << " group" << std::endl;
-    for (auto j = 0; j < results.size(); ++j) {
-      std::cout << "Result for sample " << j << std::endl;
-      std::cout << results[j] << std::endl;
-    }
-  }
   return 0;
 }

+ 9 - 8
dygraph/deploy/cpp/demo/multi_gpu_model_infer.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 <omp.h>
 #include <memory>
 #include <string>
@@ -30,11 +30,7 @@ DEFINE_int32(batch_size, 1, "Batch size of infering");
 DEFINE_int32(thread_num, 1, "thread num of preprocessing");
 
 int main(int argc, char** argv) {
-  // Parsing command-line
   google::ParseCommandLineFlags(&argc, &argv, true);
-  std::cout << "ParseCommandLineFlags:FLAGS_model_type="
-            << FLAGS_model_type << " model_filename="
-            << FLAGS_model_filename << std::endl;
 
   std::vector<int> gpu_ids;
   std::stringstream gpu_ids_str(FLAGS_gpu_id);
@@ -54,11 +50,16 @@ int main(int argc, char** argv) {
     return -1;
   }
 
-  if (!model.PaddleEngineInit(FLAGS_model_filename,
-                              FLAGS_params_filename,
-                              gpu_ids)) {
+  // engine init
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.model_filename = FLAGS_model_filename;
+  engine_config.params_filename = FLAGS_params_filename;
+  engine_config.use_gpu = true;
+  engine_config.max_batch_size = FLAGS_batch_size;
+  if (!model.PaddleEngineInit(engine_config, gpu_ids)) {
     return -1;
   }
+
   // Mini-batch
   if (FLAGS_image_list == "") {
     std::cerr << "image_list should be defined" << std::endl;

+ 1 - 1
dygraph/deploy/cpp/demo/tensorrt/CMakeLists.txt → dygraph/deploy/cpp/demo/onnx_tensorrt/CMakeLists.txt

@@ -83,6 +83,6 @@ aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/ppseg/src DETECTOR_SRC)
 aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/ppclas/src DETECTOR_SRC)
 aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/paddlex/src DETECTOR_SRC)
 
-add_executable(model_infer ${PROJECT_ROOT_DIR}/demo/tensorrt/model_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
+add_executable(model_infer ${PROJECT_ROOT_DIR}/demo/onnx_tensorrt/model_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
 ADD_DEPENDENCIES(model_infer ext-yaml-cpp)
 target_link_libraries(model_infer ${DEPS})

+ 8 - 6
dygraph/deploy/cpp/demo/tensorrt/model_infer.cpp → dygraph/deploy/cpp/demo/onnx_tensorrt/model_infer.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 <omp.h>
 #include <memory>
 #include <string>
@@ -51,11 +51,13 @@ int main(int argc, char** argv) {
   std::cout << "start engine init " << std::endl;
 
   // inference engine init
-  model->TensorRTInit(FLAGS_model_file,
-                      FLAGS_cfg_file,
-                      FLAGS_gpu_id,
-                      FLAGS_save_engine,
-                      FLAGS_trt_cache_file);
+  PaddleDeploy::TensorRTEngineConfig engine_config;
+  engine_config.model_file_ = FLAGS_model_file;
+  engine_config.cfg_file_ = FLAGS_cfg_file;
+  engine_config.gpu_id_ = FLAGS_gpu_id;
+  engine_config.save_engine_ = FLAGS_save_engine;
+  engine_config.trt_cache_file_ = FLAGS_trt_cache_file;
+  model->TensorRTInit(engine_config);
 
   // prepare data
   std::vector<std::string> image_paths;

+ 1 - 1
dygraph/deploy/cpp/demo/triton/CMakeLists.txt → dygraph/deploy/cpp/demo/onnx_triton/CMakeLists.txt

@@ -70,6 +70,6 @@ aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/ppseg/src DETECTOR_SRC)
 aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/ppclas/src DETECTOR_SRC)
 aux_source_directory(${PROJECT_ROOT_DIR}/model_deploy/paddlex/src DETECTOR_SRC)
 
-add_executable(model_infer ${PROJECT_ROOT_DIR}/demo/triton/model_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
+add_executable(model_infer ${PROJECT_ROOT_DIR}/demo/onnx_triton/model_infer.cpp ${SRC} ${ENGINE_SRC} ${DETECTOR_SRC})
 ADD_DEPENDENCIES(model_infer ext-yaml-cpp)
 target_link_libraries(model_infer ${DEPS})

+ 6 - 2
dygraph/deploy/cpp/demo/triton/model_infer.cpp → dygraph/deploy/cpp/demo/onnx_triton/model_infer.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 <omp.h>
 #include <memory>
 #include <string>
@@ -50,7 +50,11 @@ int main(int argc, char** argv) {
   std::cout << "start engine init " << std::endl;
 
   // inference engine init
-  model->TritonEngineInit(FLAGS_url, FLAGS_model_name, FLAGS_model_version);
+  PaddleDeploy::TritonEngineConfig engine_config;
+  engine_config.url_ = FLAGS_url;
+  engine_config.model_name_ = FLAGS_model_name;
+  engine_config.model_version_ = FLAGS_model_version;
+  model->TritonEngineInit(engine_config);
 
   // prepare data
   std::vector<std::string> image_paths;

+ 78 - 0
dygraph/deploy/cpp/demo/tensorrt_infer.cpp

@@ -0,0 +1,78 @@
+// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gflags/gflags.h>
+#include <string>
+#include <vector>
+
+#include "model_deploy/common/include/paddle_deploy.h"
+
+DEFINE_string(model_filename, "", "Path of det inference model");
+DEFINE_string(params_filename, "", "Path of det inference params");
+DEFINE_string(cfg_file, "", "Path of yaml file");
+DEFINE_string(model_type, "", "model type");
+DEFINE_string(image, "", "Path of test image file");
+DEFINE_int32(gpu_id, 0, "GPU card id");
+
+int main(int argc, char** argv) {
+  // Parsing command-line
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  // create model
+  std::shared_ptr<PaddleDeploy::Model> model =
+        PaddleDeploy::CreateModel(FLAGS_model_type);
+
+  // model init
+  model->Init(FLAGS_cfg_file);
+
+  // inference engine init
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.model_filename = FLAGS_model_filename;
+  engine_config.params_filename = FLAGS_params_filename;
+  engine_config.gpu_id = FLAGS_gpu_id;
+  engine_config.use_gpu = true;
+  engine_config.use_trt = true;
+  engine_config.precision = 0;
+  engine_config.min_subgraph_size = 10;
+  engine_config.max_workspace_size = 1 << 30;
+  if ("clas" == FLAGS_model_type) {
+    // Adjust shape according to the actual model
+    engine_config.min_input_shape["inputs"] = {1, 3, 224, 224};
+    engine_config.max_input_shape["inputs"] = {1, 3, 224, 224};
+    engine_config.optim_input_shape["inputs"] = {1, 3, 224, 224};
+  } else if ("det" == FLAGS_model_type) {
+    // Adjust shape according to the actual model
+    engine_config.min_input_shape["image"] = {1, 3, 608, 608};
+    engine_config.max_input_shape["image"] = {1, 3, 608, 608};
+    engine_config.optim_input_shape["image"] = {1, 3, 608, 608};
+  } else if ("seg" == FLAGS_model_type) {
+    engine_config.min_input_shape["x"] = {1, 3, 100, 100};
+    engine_config.max_input_shape["x"] = {1, 3, 2000, 2000};
+    engine_config.optim_input_shape["x"] = {1, 3, 1024, 1024};
+    // Additional nodes need to be added, pay attention to the output prompt
+  }
+  model->PaddleEngineInit(engine_config);
+
+  // prepare data
+  std::vector<cv::Mat> imgs;
+  imgs.push_back(std::move(cv::imread(FLAGS_image)));
+
+  // predict
+  std::vector<PaddleDeploy::Result> results;
+  model->Predict(imgs, &results, 1);
+
+  std::cout << results[0] << std::endl;
+
+  return 0;
+}

+ 146 - 32
dygraph/deploy/cpp/docs/apis/model.md

@@ -8,6 +8,8 @@
 4. [预测结果字段](#004)
 5. [代码示例](#005)
 
+
+
 <span id="001"></span>
 
 ## 1. 创建模型对象
@@ -38,7 +40,7 @@ std::shared_ptr<PaddleDeploy::Model>  PaddleDeploy::ModelFactory::CreateObject(c
 
 ## 2. 模型初始化
 
-模型初始化包括2个步骤,第一步读取配置文件,初始化数据预处理和后处理相关操作;第二步初始化推理PaddleInference推理引擎;对应的接口分别为`PaddleDeploy::Model::Init()`和`PaddleDeploy::Model::PaddleEngineInit()`
+模型初始化包括2个步骤,第一步读取配置文件,初始化数据预处理和后处理相关操作;第二步初始化推理推理引擎;对应的接口分别为`PaddleDeploy::Model::Init()`和`PaddleDeploy::Model::XXXEngineInit()`
 
 ### 2.1 模型前后处理初始化
 
@@ -69,29 +71,14 @@ bool Model::Init(const std::string& cfg_file)
 ### 2.2 推理引擎初始化
 
 ```c++
-bool Model::PaddleEngineInit(const std::string& model_filename,
-                             const std::string& params_filename,
-                             bool use_gpu = false,
-                             int gpu_id = 0,
-                             bool use_mkl = true,
-                             int mkl_thread_num = 8)
+bool PaddleEngineInit(const PaddleEngineConfig& engine_config);
 ```
 
-> 初始化Paddle 推理引擎,  创建Model或者其子类对象后必须先调用它初始化,才能调推理接口。
+> 初始化Paddle推理引擎,创建Model或者其子类对象后必须先调用它初始化,才能调推理接口。
 
 **参数**
 
-> >**model_filename** 模型结构文件路径,如`model.pdmodel`或`__model__`
-> >
-> >**params_filename** 模型权重文件路径,如`model.pdiparams`或`__params__`
-> >
-> >**use_gpu** 是否使用GPU
-> >
-> >**gpu_id** GPU设备ID
-> >
-> >**use_mkl** 是否使用mkldnn(CPU上推理加速引擎)
-> >
-> >**mkl_thread_num** 使用mkldnn时的推理线程数
+> >**engine_config** Paddle推理引擎配置文件,具体参数说明请看[推理引擎配置参数](#004)
 
 **返回值**
 
@@ -100,13 +87,55 @@ bool Model::PaddleEngineInit(const std::string& model_filename,
 **代码示例**
 
 > ```c++
-> if (!modle->PaddleEngineInit("yolov3_mbv1/model/model.pdmodel",
->                              "yolov3_mbv1/model/model.pdiparams",
->                              true, 0)) {
+> if (!modle->PaddleEngineInit(engine_config)) {
 >   std::cerr << "Fail to execute model->PaddleEngineInit()" << std::endl;
 > }
 > ```
 
+```c++
+bool TritonEngineInit(const TritonEngineConfig& engine_config);
+```
+
+> 初始化Triton推理引擎,用于ONNX模型推理。创建Model或者其子类对象后必须先调用它初始化,才能调推理接口。
+
+**参数**
+
+> >**engine_config** Triton推理引擎配置文件,具体参数说明请看[推理引擎配置参数](#004)
+
+**返回值**
+
+>  `true`或`false`,表示是否正确初始化
+
+**代码示例**
+
+> ```c++
+> if (!modle->TritonEngineInit(engine_config)) {
+>   std::cerr << "Fail to execute model->TritonEngineInit()" << std::endl;
+> }
+> ```
+
+```c++
+bool TensorRTInit(const TensorRTEngineConfig& engine_config);
+```
+
+> 初始化TensorRT推理引擎,用于ONNX模型推理。创建Model或者其子类对象后必须先调用它初始化,才能调推理接口。
+
+**参数**
+
+> >**engine_config** TensorRT推理引擎配置文件,具体参数说明请看[推理引擎配置参数](#004)
+
+**返回值**
+
+>  `true`或`false`,表示是否正确初始化
+
+**代码示例**
+
+> ```c++
+> if (!modle->PaddleEngineInit(engine_config)) {
+>   std::cerr << "Fail to execute model->TensorRTEngineInit()" << std::endl;
+> }
+> ```
+
 
 
 <span id="003"></span>
@@ -119,6 +148,7 @@ bool Model::PaddleEngineInit(const std::string& model_filename,
 
 
 
+
 ## 3.1 预测接口
 
 ```c++
@@ -133,7 +163,7 @@ bool Model::PaddleEngineInit(const std::string& model_filename,
 
 > **imgs** 传入的vector,元素为cv::Mat,预测时将会对vector中所有Mat进行预处理,并作为一个batch输入给推理引擎进行预测;开发者在调用时,需考虑硬件配置,vector的size过大时,可能会由于显存或内存不足导致程序出错
 >
-> **results** 预测结果vector,其与输入的imgs长度相同,vector中每个元素说明参考[预测结果字段说明](#004)
+> **results** 预测结果vector,其与输入的imgs长度相同,vector中每个元素说明参考[预测结果字段说明](#005)
 >
 > **thread_num** 当输入vector的size大于1时,可通过thread_num来配置预处理和后处理的并行处理时的多线程数量
 
@@ -154,6 +184,7 @@ bool Model::PaddleEngineInit(const std::string& model_filename,
 
 
 
+
 ### 3.2 预处理接口
 
 ```c++
@@ -234,6 +265,7 @@ if (!model->Infer(inputs, &outputs)) {
 
 
 
+
 ### 3.4 后处理接口
 
 ```c++
@@ -287,9 +319,89 @@ bool Model::Postprocess(const std::vector<PaddleDeploy::DataBlob>& outputs,
 
 
 
+
 <span id="004"></span>
 
-## 4. 预测结果字段
+## 4. 推理引擎配置参数
+
+### 4.1 Paddle推理引擎配置
+
+```c++
+  std::string model_filename = "";  // 模型文件
+
+  std::string params_filename = ""; // 模型参数
+
+  bool use_mkl = true; // 是否开启mkl
+
+  int mkl_thread_num = 8; // mkl并行线程数
+
+  bool use_gpu = false; // 是否使用GPU进行推理
+
+  int gpu_id = 0; // 使用编号几的GPU
+
+  bool use_ir_optim = true; // 是否开启IR优化
+
+  bool use_trt = false; // 是否使用TensorRT
+
+  int max_batch_size = 1; // TensorRT最大batch大小
+
+  int min_subgraph_size = 1; // TensorRT 子图最小节点数
+
+  /*Set TensorRT data precision
+  0: FP32
+  1: FP16
+  2: Int8
+  */
+  int precision = 0;
+
+  int max_workspace_size = 1 << 10; // TensorRT申请的显存大小,1 << 10 = 1M
+
+  std::map<std::string, std::vector<int>> min_input_shape; // 模型动态shape的最下输入形状, TensorRT才需要设置
+
+  std::map<std::string, std::vector<int>> max_input_shape; // 模型动态shape的最大输入形状, TensorRT才需要设置
+
+  std::map<std::string, std::vector<int>> optim_input_shape; // 模型动态shape的最常见输入形状, TensorRT才需要设置
+```
+
+
+
+### 4.2 Triton推理引擎配置
+
+```c++
+  std::string model_name_; // 向Triton Server请求的模型名称
+
+  std::string model_version_; // Triton Server中模型的版本号
+
+  uint64_t server_timeout_ = 0; // 服务端最大计算时间
+
+  uint64_t client_timeout_ = 0; // 客户端等待时间, 默认一直等
+
+  bool verbose_ = false; // 是否打开客户端日志
+
+  std::string url_; // Triton Server地址
+```
+
+
+
+### 4.1 TensorRT推理引擎配置
+
+```c++
+  std::string model_file_; // ONNX 模型地址
+
+  std::string cfg_file_; // 模型配置文件地址
+
+  int max_workspace_size_ = 1<<28; // GPU显存大小
+
+  int max_batch_size_ = 1; // 最大batch
+
+  int gpu_id_ = 0; // 使用编号几的GPU
+```
+
+
+
+<span id="005"></span>
+
+## 5. 预测结果字段
 
 在开发者常调用的接口中,包括主要的`Model::Predict`,以及`Model::Predict`内部调用的`Model::Preprocess`、`Model::Infer`和`Model::Postprocess`接口,涉及到的结构体主要为`PaddleDeploy::Result`(预测结果),`PaddleDeploy::DataBlob`和`PaddleDeploy::ShapeInfo`。其中`DataBlob`和`ShapeInfo`开发者较少用到,可直接阅读其代码实现。
 
@@ -311,7 +423,7 @@ struct Result {
 
 
 
-### 4.1 图像分类结果
+### 5.1 图像分类结果
 
 ```C++
 struct ClasResult {
@@ -323,7 +435,7 @@ struct ClasResult {
 
 
 
-### 4.2 目标检测结果
+### 5.2 目标检测结果
 
 ```C++
 struct DetResult {
@@ -352,7 +464,7 @@ struct Mask {
 
 
 
-### 4.3 语义分割结果
+### 5.3 语义分割结果
 
 ```c++
 struct SegResult {
@@ -371,9 +483,9 @@ struct Mask {
 
 
 
-<span id="005"></span>
+<span id="006"></span>
 
-## 5. 部署代码示例
+## 6. 部署代码示例
 
 以下示例代码以目标检测模型为例
 
@@ -384,9 +496,11 @@ int main() {
   std::shared_ptr<PaddleDeploy::Model> model =
                     PaddleDeploy::ModelFactory::CreateObject("det");
   model->Init("yolov3_mbv1/model/infer_cfg.yml");
-  model->PaddleEngineInit("yolov3_mbv1/model/model.pdmodel",
-                          "yolov3_mbv1/model/model.pdiparams",
-                          true, 0);
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.model_filename = "yolov3_mbv1/model/model.pdmodel"
+  engine_config.params_filename = "yolov3_mbv1/model/model.pdiparams"
+  engine_config.use_gpu = true;
+  engine_config.gpu_id = 0;
 
   std::vector<cv::Mat> images;
   std::vector<PaddleDeploy::Result> results;

+ 24 - 25
dygraph/deploy/cpp/docs/compile/paddle/linux.md

@@ -12,27 +12,23 @@ Ubuntu 16.04/18.04
 ### Step1: 获取部署代码
 ```
 git clone https://github.com/PaddlePaddle/PaddleX.git
-cd PaddleX
-git checkout deploykit
-cd deploy/cpp
+cd PaddleX/dygraph/deploy/cpp
 ```
-**说明**:`C++`预测代码在`PaddleX/deploy/cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。所有的公共实现代码在`model_deploy`目录下,而示例demo代码为`demo/model_infer.cpp`
+**说明**:`C++`预测代码在`PaddleX/dygraph/deploy/cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。所有的公共实现代码在`model_deploy`目录下,所有示例代码都在`demo`目录下。
 
 ### Step 2. 下载PaddlePaddle C++ 预测库
-PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX支持Paddle预测库2.0+,最新2.0.2版本下载链接如下所示:
+PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX支持Paddle预测库2.0+,最新2.1版本下载链接如下所示:
 
-| 版本说明                               | 预测库(2.0.2)                                                | 编译器  |
-| -------------------------------------- | ------------------------------------------------------------ | ------- |
-| CPU版本                                | [paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-cpu-avx-mkl/paddle_inference.tgz) | gcc 8.2 |
-| GPU版本(CUDA9.0/CUDNN7)              | [paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-gpu-cuda9-cudnn7-avx-mkl/paddle_inference.tgz) | gcc 5.4 |
-| GPU版本(CUDA10.0/CUDNN7)             | [paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-gpu-cuda10-cudnn7-avx-mkl/paddle_inference.tgz) | gcc 5.4 |
-| GPU版本(CUDA10.1/CUDNN7.6/TensorRT6) | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-gpu-cuda10.1-cudnn7-avx-mkl/paddle_inference.tgz) | gcc 8.2 |
-| GPU版本(CUDA10.2/CUDNN8/TensorRT7)   | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-gpu-cuda10.2-cudnn8-avx-mkl/paddle_inference.tgz) | gcc 8.2 |
-| GPU版本(CUDA11/CUDNN8/TensorRT7)     | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.0.2-gpu-cuda11-cudnn8-avx-mkl/paddle_inference.tgz) | gcc 8.2 |
+| 版本说明                               | 预测库(2.1)                                                                                                                   | 编译器  |
+| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------- |
+| CPU版本                                | [paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.1.0-cpu-avx-mkl/paddle_inference.tgz)                     | gcc 8.2 |
+| GPU版本(CUDA10.1/CUDNN7.6/TensorRT6) | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.1.0-gpu-cuda10.1-cudnn7-mkl-gcc8.2/paddle_inference.tgz) | gcc 8.2 |
+| GPU版本(CUDA10.2/CUDNN8/TensorRT7)   | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.1.0-gpu-cuda10.2-cudnn8-mkl-gcc8.2/paddle_inference.tgz) | gcc 8.2 |
+| GPU版本(CUDA11/CUDNN8/TensorRT7)     | [ paddle_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/2.1.0-gpu-cuda11.0-cudnn8-mkl-gcc8.2/paddle_inference.tgz) | gcc 8.2 |
 
-请根据实际情况选择下载,如若以上版本不满足您的需求,请至[C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/05_inference_deployment/inference/build_and_install_lib_cn.html#linux)选择符合的版本。
+请根据实际情况选择下载,如若以上版本不满足您的需求,请至[C++预测库下载列表](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/download_lib.html)选择符合的版本。
 
-将预测库解压后,其所在目录(例如解压至`PaddleX/deploy/cpp/paddle_inferenc/`)下主要包含的内容有:
+将预测库解压后,其所在目录(例如解压至`PaddleX/dygraph/deploy/cpp/paddle_inferenc/`)下主要包含的内容有:
 
 ```
 ├── paddle/ # paddle核心库和头文件
@@ -43,16 +39,18 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 ```
 
 ### Step 3. 修改编译参数
-根据自己的系统环境,修改`PaddleX/deploy/cpp/script/build.sh`脚本中的参数,主要修改的参数为以下几个
-| 参数 | 说明 |
-| :--- | :--- |
-| WITH_GPU | ON或OFF,表示是否使用GPU,当下载的为CPU预测库时,设为OFF |
-| PADDLE_DIR | 预测库所在路径,默认为`PaddleX/deploy/cpp/paddle_inference`目录下 |
-| CUDA_LIB | cuda相关lib文件所在的目录路径 |
-| CUDNN_LIB | cudnn相关lib文件所在的目录路径 |
+根据自己的系统环境,修改`PaddleX/dygraph/deploy/cpp/script/build.sh`脚本中的参数,主要修改的参数为以下几个
+| 参数          | 说明                                                                                 |
+| :------------ | :----------------------------------------------------------------------------------- |
+| WITH_GPU      | ON或OFF,表示是否使用GPU,当下载的为CPU预测库时,设为OFF                             |
+| PADDLE_DIR    | 预测库所在路径,默认为`PaddleX/deploy/cpp/paddle_inference`目录下                    |
+| CUDA_LIB      | cuda相关lib文件所在的目录路径                                                        |
+| CUDNN_LIB     | cudnn相关lib文件所在的目录路径                                                       |
+| WITH_TENSORRT | ON或OFF,表示是否使用开启TensorRT                                                    |
+| TENSORRT_DIR  | TensorRT 的路径,如果开启TensorRT开关WITH_TENSORRT,需修改为您实际安装的TensorRT路径 |
 
 ### Step 4. 编译
-修改完build.sh后执行编译, **[注意]**: 以下命令在`PaddleX/deploy/cpp`目录下进行执行
+修改完build.sh后执行编译, **[注意]**: 以下命令在`PaddleX/dygraph/deploy/cpp`目录下进行执行
 
 ```
 sh script/build.sh
@@ -62,7 +60,7 @@ sh script/build.sh
 > 编译过程,会调用script/bootstrap.sh联网下载opencv,以及yaml依赖包,如无法联网,用户按照下操作手动下载
 >
 > 1. 根据系统版本,点击右侧链接下载不同版本的opencv依赖 [Ubuntu 16.04](https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2)/[Ubuntu 18.04](https://bj.bcebos.com/paddlex/deploy/opencv3.4.6gcc4.8ffmpeg_ubuntu_18.04.tar.gz2)
-> 2. 解压下载的opencv依赖(解压后目录名为opencv3.4.6gcc4.8ffmpeg),创建目录`PaddleX/deploy/cpp/deps`,将解压后的目录拷贝至该创建的目录下
+> 2. 解压下载的opencv依赖(解压后目录名为opencv3.4.6gcc4.8ffmpeg),创建目录`PaddleX/dygraph/deploy/cpp/deps`,将解压后的目录拷贝至该创建的目录下
 > 3. [点击下载yaml依赖包](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip),无需解压
 > 4. 修改`PaddleX/deploy/cpp/cmake/yaml.cmake`文件,将`URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip`中网址替换为第3步中下载的路径,如改为`URL /Users/Download/yaml-cpp.zip`
 > 5. 重新执行`sh script/build.sh`即可编译
@@ -71,10 +69,11 @@ sh script/build.sh
 
 ### Step 5. 编译结果
 
-编译后会在`PaddleX/deploy/cpp/build/demo`目录下生成`model_infer`和`multi_gpu_model_infer`两个可执行二进制文件示例,分别用于在单卡/多卡上加载模型进行预测,示例使用参考如下文档
+编译后会在`PaddleX/dygraph/deploy/cpp/build/demo`目录下生成`model_infer`、`multi_gpu_model_infer`和`batch_infer`等几个可执行二进制文件示例,分别用于在单卡/多卡/多batch上加载模型进行预测,示例使用参考如下文档
 
 - [单卡加载模型预测示例](../../demo/model_infer.md)
 - [多卡加载模型预测示例](../../demo/multi_gpu_model_infer.md)
+- [PaddleInference集成TensorRT加载模型预测示例](../../demo/tensorrt_infer.md)
 
 
 

+ 13 - 15
dygraph/deploy/cpp/docs/compile/paddle/windows.md

@@ -19,26 +19,23 @@ d:
 mkdir projects
 cd projects
 git clone https://github.com/PaddlePaddle/PaddleX.git
-git checkout deploykit
 ```
 
-**说明**:其中`C++`预测代码在`PaddleX\deploy\cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。所有的公共实现代码在`model_deploy`目录下,而示例demo代码为`demo/model_infer.cpp`
+**说明**:其中`C++`预测代码在`PaddleX\dygraph\deploy\cpp` 目录,该目录不依赖任何`PaddleX`下其他目录。所有的公共实现代码在`model_deploy`目录下,所有示例代码都在`demo`目录下
 
 
 ### Step2: 下载PaddlePaddle C++ 预测库
 
-PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX支持Paddle预测库2.0+,最新2.0.2版本下载链接如下所示:
+PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及不同的CUDA版本提供了已经编译好的预测库,目前PaddleX支持Paddle预测库2.0+,最新2.1版本下载链接如下所示:
 
-| 版本说明                     | 预测库(2.0.2)                                                                                                   | 编译器                | 构建工具      | cuDNN | CUDA |
-| ---------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------- | ------------- | ----- | ---- |
-| cpu_avx_mkl                  | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/mkl/cpu/paddle_inference.zip)         | Visual Studio 15 2017 | CMake v3.17.0 | -     | -    |
-| cuda10.0_cudnn7_avx_mkl      | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/mkl/post100/paddle_inference.zip)     | MSVC 2015 update 3    | CMake v3.17.0 | 7.4.1 | 10.0 |
-| cuda10.0_cudnn7_avx_mkl_trt6 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/trt_mkl/post100/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 7.4.1 | 10.0 |
-| cuda10.1_cudnn7_avx_mkl_trt6 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/trt_mkl/post101/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 7.6   | 10.1 |
-| cuda10.2_cudnn7_avx_mkl_trt7 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/trt_mkl/post102/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 7.6   | 10.2 |
-| cuda11.0_cudnn8_avx_mkl_trt7 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.0.2/win-infer/trt_mkl/post11/paddle_inference.zip)  | MSVC 2015 update 3    | CMake v3.17.0 | 8.0   | 11.0 |
+| 版本说明                     | 预测库(2.1)                                                                                                    | 编译器                | 构建工具      | cuDNN | CUDA |
+| ---------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------- | ------------- | ----- | ---- |
+| cpu_avx_mkl                  | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.1.0win/win-infer/mkl/cpu/paddle_inference.zip)     | Visual Studio 15 2017 | CMake v3.17.0 | -     | -    |
+| cuda10.1_cudnn7_avx_mkl_trt6 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.1.0win/win-infer/mkl/post101/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 7.6   | 10.1 |
+| cuda10.2_cudnn7_avx_mkl_trt7 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.1.0win/win-infer/mkl/post102/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 7.6   | 10.2 |
+| cuda11.0_cudnn8_avx_mkl_trt7 | [paddle_inference.zip](https://paddle-wheel.bj.bcebos.com/2.1.0win/win-infer/mkl/post110/paddle_inference.zip) | MSVC 2015 update 3    | CMake v3.17.0 | 8.0   | 11.0 |
 
-请根据实际情况选择下载,如若以上版本不满足您的需求,请至[C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/05_inference_deployment/inference/windows_cpp_inference.html)选择符合的版本。
+请根据实际情况选择下载,如若以上版本不满足您的需求,请至[C++预测库下载列表](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/download_lib.html)选择符合的版本。
 
 将预测库解压后,其所在目录(例如`D:\projects\paddle_inference_install_dir\`)下主要包含的内容有:
 
@@ -68,7 +65,7 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 
 ![](../../images/vs2019_step2.png)
 
-选择C++预测代码所在路径(例如`D:\projects\PaddleX\deploy\cpp`),并打开`CMakeList.txt`:
+选择C++预测代码所在路径(例如`D:\projects\PaddleX\dygraph\deploy\cpp`),并打开`CMakeList.txt`:
 ![](../../images/vs2019_step3.png)
 
 3. 打开项目时,可能会自动构建。由于没有进行下面的依赖路径设置会报错,这个报错可以先忽略。
@@ -90,7 +87,7 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 
 - 如果使用`CPU`版预测库,请把`WITH_GPU`的`值`去掉勾
 - 如果使用的是`openblas`版本,请把`WITH_MKL`的`值`去掉勾
-- 如果无法联网,请手动点击下载 [yaml-cpp.zip](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip),无需解压,并修改`PaddleX\deploy\cpp\cmake\yaml.cmake`中将`URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip` 中的网址替换为第3步中下载的路径,如改为`URL D:\projects\yaml-cpp.zip`。
+- 如果无法联网,请手动点击下载 [yaml-cpp.zip](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip),无需解压,并修改`PaddleX\dygraph\deploy\cpp\cmake\yaml.cmake`中将`URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip` 中的网址替换为第3步中下载的路径,如改为`URL D:\projects\yaml-cpp.zip`。
 
 5. 保存并生成CMake缓存
 
@@ -103,10 +100,11 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 
 ### Step5: 编译结果
 
-编译后会在`PaddleX/deploy/cpp/build/demo`目录下生成`model_infer`和`multi_gpu_model_infer`两个可执行二进制文件示例,分别用于在单卡/多卡上加载模型进行预测,示例使用参考如下文档
+编译后会在`PaddleX/dygraph/deploy/cpp/build/demo`目录下生成`model_infer`和`multi_gpu_model_infer`两个可执行二进制文件示例,分别用于在单卡/多卡上加载模型进行预测,示例使用参考如下文档
 
 - [单卡加载模型预测示例](../../demo/model_infer.md)
 - [多卡加载模型预测示例](../../demo/multi_gpu_model_infer.md)
+- [PaddleInference集成TensorRT加载模型预测示例](../../demo/tensorrt_infer.md)
 
 
 

+ 11 - 15
dygraph/deploy/cpp/docs/demo/model_infer.md

@@ -12,7 +12,7 @@
 - [PaddleDetection导出模型](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.0/deploy/EXPORT_MODEL.md)
 - [PaddleSeg导出模型](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/docs/model_export.md)
 - [PaddleClas导出模型](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.1/docs/zh_CN/tutorials/getting_started.md#4-%E4%BD%BF%E7%94%A8inference%E6%A8%A1%E5%9E%8B%E8%BF%9B%E8%A1%8C%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86)
-- [PaddleX导出模型](https://paddlex.readthedocs.io/zh_CN/develop/deploy/server/python.html#python)
+- [PaddleX导出模型](https://paddlex.readthedocs.io/zh_CN/develop/deploy/export_model.html)
 
 
 用户也可直接下载本教程中从PaddleDetection中导出的YOLOv3模型进行测试,[点击下载](https://bj.bcebos.com/paddlex/deploy2/models/yolov3_mbv1.tar.gz)。
@@ -20,7 +20,8 @@
 ## 步骤三、使用编译好的可执行文件预测
 以步骤二中下载的YOLOv3模型为例,执行如下命令即可进行模型加载和预测
 
-```
+```sh
+# 使用GPU 加参数 --use_gpu=1
 build/demo/model_infer --model_filename=yolov3_mbv1/model/model.pdmodel \
                        --params_filename=yolov3_mbv1/model/model.pdiparams \
                        --cfg_file=yolov3_mbv1/model/infer_cfg.yml \
@@ -40,20 +41,15 @@ Box(45	bowl	0.0191819	3.91156	1.276	225.888	214.273)
 ```
 ### 参数说明
 
-| 参数            | 说明                                                         |
-| --------------- | ------------------------------------------------------------ |
-| model_filename  | **[必填]** 模型结构文件路径,如`yolov3_darknet/model.pdmodel` |
-| params_filename | **[必填]** 模型权重文件路径,如`yolov3_darknet/model.pdiparams` |
-| cfg_file        | **[必填]** 模型配置文件路径,如`yolov3_darknet/infer_cfg.yml` |
+| 参数            | 说明                                                                                                         |
+| --------------- | ------------------------------------------------------------------------------------------------------------ |
+| model_filename  | **[必填]** 模型结构文件路径,如`yolov3_darknet/model.pdmodel`                                                |
+| params_filename | **[必填]** 模型权重文件路径,如`yolov3_darknet/model.pdiparams`                                              |
+| cfg_file        | **[必填]** 模型配置文件路径,如`yolov3_darknet/infer_cfg.yml`                                                |
 | model_type      | **[必填]** 模型来源,det/seg/clas/paddlex,分别表示模型来源于PaddleDetection、PaddleSeg、PaddleClas和PaddleX |
-| image           | 待预测的图片文件路径                                         |
-| image_list      | 待预测的图片路径列表文件路径,如步骤三中的`yolov3_darknet/file_list.txt` |
-| use_gpu         | 是否使用GPU,0或者1,默认为0                                 |
-| gpu_id          | 使用GPU预测时的GUI设备ID,默认为0                            |
-| use_mkl         | 是否使用mkldnn(CPU上加速),0或者1,默认为1                 |
-| mkl_thread_num  | 在批预测时,预处理的并行线程数设置,默认为1                  |
-| batch_size      | 当指定image_list时,设定每次预测时的batch大小,默认为1       |
-| thread_num      | 当预测batch大于1时,设定图像预处理时的并行线程数,默认为1    |
+| image           | 待预测的图片文件路径                                                                                         |
+| use_gpu         | 是否使用GPU,0或者1,默认为0                                                                                 |
+| gpu_id          | 使用GPU预测时的GUI设备ID,默认为0                                                                            |
 
 
 

+ 2 - 2
dygraph/deploy/cpp/docs/demo/multi_gpu_model_infer.md

@@ -1,6 +1,6 @@
 # 多GPU卡模型加载预测示例
 
-本文档说明`PaddleX/deploy/cpp/demo/multi_gpu_model_infer.cpp`编译后的使用方法,仅供用户参考进行使用,开发者可基于此demo示例进行二次开发,满足集成的需求。
+本文档说明`PaddleX/dygraph/deploy/cpp/demo/multi_gpu_model_infer.cpp`编译后的使用方法,仅供用户参考进行使用,开发者可基于此demo示例进行二次开发,满足集成的需求。
 
 在多卡上实现机制如下
 
@@ -22,7 +22,7 @@
 - [PaddleDetection导出模型](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.0/deploy/EXPORT_MODEL.md)
 - [PaddleSeg导出模型](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/docs/model_export.md)
 - [PaddleClas导出模型](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.1/docs/zh_CN/tutorials/getting_started.md#4-%E4%BD%BF%E7%94%A8inference%E6%A8%A1%E5%9E%8B%E8%BF%9B%E8%A1%8C%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86)
-- [PaddleX导出模型](https://paddlex.readthedocs.io/zh_CN/develop/deploy/server/python.html#python)
+- [PaddleX导出模型](https://paddlex.readthedocs.io/zh_CN/develop/deploy/export_model.html)
 
 
 

+ 150 - 0
dygraph/deploy/cpp/docs/demo/tensorrt_infer.md

@@ -0,0 +1,150 @@
+# TensorRT加载模型预测
+
+本文档基于`PaddleX/dygraph/deploy/cpp/demo/tensorrt_infer.cpp`示例,讲述如何用PaddleInference引擎结合TensorRT部署模型。开发者可基于此demo示例进行二次开发,满足集成的需求。
+
+## 步骤一、编译
+参考编译文档
+- [Linux系统上编译指南](../compile/paddle/linux.md)
+- [Windows系统上编译指南](../compile/paddle/windows.md)
+
+**注意**:  编译时需要把TensorRT开关打开, 并填写TensorRT 路径。TensorRT下载链接:[NVIDIA 官网](https://developer.nvidia.com/nvidia-tensorrt-download)
+
+## 步骤二、准备PaddlePaddle部署模型
+开发者可从以下套件获取部署模型,需要注意,部署时需要准备的是导出来的部署模型,一般包含`model.pdmodel`、`model.pdiparams`和`deploy.yml`三个文件,分别表示模型结构、模型权重和各套件自行定义的配置信息。
+- [PaddleDetection导出模型](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.0/deploy/EXPORT_MODEL.md)
+- [PaddleSeg导出模型](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/docs/model_export.md)
+- [PaddleClas导出模型](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.1/docs/zh_CN/tutorials/getting_started.md#4-%E4%BD%BF%E7%94%A8inference%E6%A8%A1%E5%9E%8B%E8%BF%9B%E8%A1%8C%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86)
+- [PaddleX导出模型](https://paddlex.readthedocs.io/zh_CN/develop/deploy/export_model.html)
+
+用户也可直接下载本教程中从PaddleDetection中导出的YOLOv3模型进行测试,[点击下载](https://bj.bcebos.com/paddlex/deploy2/models/yolov3_mbv1.tar.gz)。
+
+### 模型动态Shape处理
+
+目前有两种方法使用TensorRT部署动态Shape的模型:
+
+1. 在导出模型时,固定shape的大小。
+
+   例如[PaddleDetection模型部署](../models/paddledetection.md)文档中有如何固定shape的导出方法
+
+2. 配置TensorRT的动态shape参数。 PaddleSeg等套件导出模型时没提供固定shape的方法, 需要配置TensorRT参数才能加载模型。
+
+   TensorRT配置参数说明, 动态输入一定要配置`min_input_shape`、`max_input_shape`、`optim_input_shape` 三个参数
+
+   | 参数               | 说明                                                                                                                       |
+   | ------------------ | -------------------------------------------------------------------------------------------------------------------------- |
+   | precision          | 数据类型, 默认值为0。 0表示FP32 、1表示FP16、2表示INT8                                                                    |
+   | max_workspace_size | TensorRT申请的显存空间, 默认值为 1 << 10(1M),  如果大点模型需要提到1 << 30                                                |
+   | min_input_shape    | 动态输入的最小shape。 map类型, key为输入名称, value为shape                                                               |
+   | max_input_shape    | 动态输入的最大shape。 map类型, key为输入名称, value为shape                                                               |
+   | optim_input_shape  | 动态输入的最常出现的shape。 map类型, key为输入名称, value为shape                                                         |
+   | min_subgraph_size  | 最小的字图大小,用于过滤节点过小的子图。<br/>设置为10,则小于10个node的子图将不用TensorRT优化,用PaddleInference引擎运行。 |
+
+   可参考`demo/tensorrt_infer.cpp`例子:
+
+```c++
+  PaddleDeploy::PaddleEngineConfig engine_config;
+  engine_config.use_gpu = true;
+  engine_config.use_trt = true;
+  engine_config.precision = 0;
+  engine_config.min_subgraph_size = 10;
+  engine_config.max_workspace_size = 1 << 30;
+ // 分类clas和检测detection模型, 一般只需根据实际模型情况修改输入的shape即可
+ // seg分割模型一般需要填写额外的其他节点信息, 注意看提示
+   if ("clas" == FLAGS_model_type) {
+      engine_config.min_input_shape["inputs"] = {1, 3, 224, 224};
+      engine_config.max_input_shape["inputs"] = {1, 3, 224, 224};
+      engine_config.optim_input_shape["inputs"] = {1, 3, 224, 224};
+    } else if ("det" == FLAGS_model_type) {
+      // Adjust shape according to the actual model
+      engine_config.min_input_shape["image"] = {1, 3, 608, 608};
+      engine_config.max_input_shape["image"] = {1, 3, 608, 608};
+      engine_config.optim_input_shape["image"] = {1, 3, 608, 608};
+    } else if ("seg" == FLAGS_model_type) {
+      engine_config.min_input_shape["x"] = {1, 3, 100, 100};
+      engine_config.max_input_shape["x"] = {1, 3, 2000, 2000};
+      engine_config.optim_input_shape["x"] = {1, 3, 1024, 1024};
+      // Additional nodes need to be added, pay attention to the output prompt
+    }
+```
+
+如果运行时出现以下报错,则需要根据报错将提示的输入节点填入配置中
+
+![tensorrt](../images/tensorrt.png)
+
+```c++
+    // 上面报错,配置文件填写示例
+    engine_config.min_input_shape["x"] = {1,3, 100, 100};
+    engine_config.max_input_shape["x"] = {1,3, 2000, 2000};
+    engine_config.optim_input_shape["x"] = {1,3, 129, 129};
+
+    engine_config.min_input_shape["batch_norm_37.tmp_2"] = {1,64, 7, 7};
+    engine_config.max_input_shape["batch_norm_37.tmp_2"] = {1,64, 125, 125};
+    engine_config.optim_input_shape["batch_norm_37.tmp_2"] = {1,64, 12, 12};
+
+    engine_config.min_input_shape["bilinear_interp_v2_1.tmp_0"] = {1,16, 25, 25};
+    engine_config.max_input_shape["bilinear_interp_v2_1.tmp_0"] = {1,16, 500, 500};
+    engine_config.optim_input_shape["bilinear_interp_v2_1.tmp_0"] = {1,16, 48, 48};
+
+    engine_config.min_input_shape["bilinear_interp_v2_2.tmp_0"] = {1,16, 25, 25};
+    engine_config.max_input_shape["bilinear_interp_v2_2.tmp_0"] = {1,16, 500, 500};
+    engine_config.optim_input_shape["bilinear_interp_v2_2.tmp_0"] = {1,16, 48, 48};
+
+    engine_config.min_input_shape["bilinear_interp_v2_3.tmp_0"] = {1,32, 13, 13};
+    engine_config.max_input_shape["bilinear_interp_v2_3.tmp_0"] = {1,32, 250, 250};
+    engine_config.optim_input_shape["bilinear_interp_v2_3.tmp_0"] = {1,32, 24, 24};
+
+    engine_config.min_input_shape["relu_21.tmp_0"] = {1,16, 25, 25};
+    engine_config.max_input_shape["relu_21.tmp_0"] = {1,16, 500, 500};
+    engine_config.optim_input_shape["relu_21.tmp_0"] = {1,16, 48, 48};
+
+    engine_config.min_input_shape["relu_29.tmp_0"] = {1,64, 7, 7};
+    engine_config.max_input_shape["relu_29.tmp_0"] = {1,64, 125, 125};
+    engine_config.optim_input_shape["relu_29.tmp_0"] = {1,64, 12, 12};
+
+    engine_config.min_input_shape["relu_32.tmp_0"] = {1,16, 13, 13};
+    engine_config.max_input_shape["relu_32.tmp_0"] = {1,16, 250, 250};
+    engine_config.optim_input_shape["relu_32.tmp_0"] = {1,16, 24, 24};
+
+    engine_config.min_input_shape["tmp_15"] = {1,32, 13, 13};
+    engine_config.max_input_shape["tmp_15"] = {1,32, 250, 250};
+    engine_config.optim_input_shape["tmp_15"] = {1,32, 24, 24};
+```
+
+## 步骤三、使用编译好的可执行文件预测
+
+以步骤二中下载的YOLOv3模型为例,执行如下命令即可进行模型加载和预测
+
+```sh
+# 使用GPU 加参数 --use_gpu=1
+build/demo/tensorrt_infer --model_filename=yolov3_mbv1/model/model.pdmodel \
+                          --params_filename=yolov3_mbv1/model/model.pdiparams \
+                          --cfg_file=yolov3_mbv1/model/infer_cfg.yml \
+                          --image=yolov3_mbv1/images/000000010583.jpg \
+                          --model_type=det
+```
+输出结果如下(分别为类别id、标签、置信度、xmin、ymin、w, h)
+```
+Box(0	person	0.0386442	2.11425	53.4415	36.2138	197.833)
+Box(39	bottle	0.0134608	2.11425	53.4415	36.2138	197.833)
+Box(41	cup	0.0255145	2.11425	53.4415	36.2138	197.833)
+Box(43	knife	0.0824398	509.703	189.959	100.65	93.9368)
+Box(43	knife	0.0211949	448.076	167.649	162.924	143.557)
+Box(44	spoon	0.0234474	509.703	189.959	100.65	93.9368)
+Box(45	bowl	0.0461333	0	0	223.386	83.5562)
+Box(45	bowl	0.0191819	3.91156	1.276	225.888	214.273)
+```
+### 参数说明
+
+| 参数            | 说明                                                                                                         |
+| --------------- | ------------------------------------------------------------------------------------------------------------ |
+| model_filename  | **[必填]** 模型结构文件路径,如`yolov3_darknet/model.pdmodel`                                                |
+| params_filename | **[必填]** 模型权重文件路径,如`yolov3_darknet/model.pdiparams`                                              |
+| cfg_file        | **[必填]** 模型配置文件路径,如`yolov3_darknet/infer_cfg.yml`                                                |
+| model_type      | **[必填]** 模型来源,det/seg/clas/paddlex,分别表示模型来源于PaddleDetection、PaddleSeg、PaddleClas和PaddleX |
+| image           | 待预测的图片文件路径                                                                                         |
+
+
+
+## 相关文档
+
+- [部署接口和数据结构文档](../apis/model.md)

二進制
dygraph/deploy/cpp/docs/images/tensorrt.png


+ 3 - 2
dygraph/deploy/cpp/docs/models/paddleclas.md

@@ -56,8 +56,8 @@ ResNet50
 
 参考编译文档
 
-- [Linux系统上编译指南](../compile/linux.md)
-- [Windows系统上编译指南](../compile/windows.md)
+- [Linux系统上编译指南](../compile/paddle/linux.md)
+- [Windows系统上编译指南](../compile/paddle/windows.md)
 
 
 
@@ -83,3 +83,4 @@ Classify(809    sunscreen   0.939211)
 
 - [单卡加载模型预测示例](../demo/model_infer.md)
 - [多卡加载模型预测示例](../demo/multi_gpu_model_infer.md)
+- [PaddleInference集成TensorRT加载模型预测示例](../../demo/tensorrt_infer.md)

+ 19 - 17
dygraph/deploy/cpp/docs/models/paddledetection.md

@@ -1,6 +1,6 @@
 # PaddleDetection模型部署
 
-当前支持PaddleDetection release/0.5和release/2.0分支导出的模型进行部署(仅支持FasterRCNN/MaskRCNN/PPYOLO/PPYOLOv2/YOLOv3)。PaddleDetection相关详细文档可以查看[官网文档](https://github.com/PaddlePaddle/PaddleDetection/tree/release/0.5)。
+当前支持PaddleDetection release/0.5和release/2.1分支导出的模型进行部署(仅支持FasterRCNN/MaskRCNN/PPYOLO/PPYOLOv2/YOLOv3)。PaddleDetection相关详细文档可以查看[官网文档](https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.1)。
 
 下面主要以YoloV3为例,讲解从模型导出到部署的整个流程。
 
@@ -11,29 +11,29 @@
 ```sh
 git clone https://github.com/PaddlePaddle/PaddleDetection.git
 cd PaddleDetection
-git checkout realease/2.0
 ```
 
 ### 2. 导出基于COCO数据的预训练模型
 
 在进入`PaddleDetection`目录后,执行如下命令导出预训练模型
 
-```sh
-python tools/export_models.py -c configs/yolov3_darknet.yml \
-                              -o weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar \
-                              --output_dir=inference_model
+```python
+# 导出YOLOv3模型
+python tools/export_model.py -c configs/yolov3/yolov3_darknet53_270e_coco.yml \
+                             -o weights=https://paddledet.bj.bcebos.com/models/yolov3_darknet53_270e_coco.pdparams \
+                             --output_dir=./inference_model
 ```
 
 **如果你需要使用TensorRT进行部署预测**,则需要在导出模型时固定输入shape,命令如下
 
-```sh
-python tools/export_models.py -c configs/yolov3_darknet.yml \
-                              -o weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar \
+```python
+python tools/export_model.py -c configs/yolov3/yolov3_darknet53_270e_coco.yml \
+                              -o weights=https://paddledet.bj.bcebos.com/models/yolov3_darknet53_270e_coco.pdparams \
                               TestReader.inputs_def.image_shape=[3,640,640] \
-                              --output_dir=inference_model
+                              --output_dir=./inference_model
 ```
 
-导出的部署模型会保存在`inference_model/yolov3_darknet`目录,其结构如下
+导出的部署模型会保存在`inference_model/yolov3_darknet53_270e_coco`目录,其结构如下
 
 ```
 yolov3_darknet
@@ -49,8 +49,8 @@ yolov3_darknet
 
 参考编译文档
 
-- [Linux系统上编译指南](../compile/linux.md)
-- [Windows系统上编译指南](../compile/windows.md)
+- [Linux系统上编译指南](../compile/paddle/linux.md)
+- [Windows系统上编译指南](../compile/paddle/windows.md)
 
 
 
@@ -58,10 +58,11 @@ yolov3_darknet
 
 编译后即可获取可执行的二进制demo程序`model_infer`和`multi_gpu_model_infer`,分别用于在单卡/多卡上加载模型进行预测,对于分类模型,调用如下命令即可进行预测
 
-```
-./build/demo/model_infer --model_filename=inference_model/yolov3_darknet/model.pdmodel \
-                         --params_filename=inference_model/yolov3_darknet/model.pdiparams \
-                         --cfg_file=inference_model/yolov3_darknet/infer_cfg.yml \
+```sh
+# 使用gpu加 --use_gpu=1 参数
+./build/demo/model_infer --model_filename=inference_model/yolov3_darknet53_270e_coco/model.pdmodel \
+                         --params_filename=inference_model/yolov3_darknet53_270e_coco/model.pdiparams \
+                         --cfg_file=inference_model/yolov3_darknet53_270e_coco/infer_cfg.yml \
                          --image=test.jpg \
                          --model_type=det
 ```
@@ -79,3 +80,4 @@ Box(39  bottle  0.356306    551.603 288.384 34.9819 112.599)
 
 - [单卡加载模型预测示例](../demo/model_infer.md)
 - [多卡加载模型预测示例](../demo/multi_gpu_model_infer.md)
+- [PaddleInference集成TensorRT加载模型预测示例](../../demo/tensorrt_infer.md)

+ 6 - 8
dygraph/deploy/cpp/docs/models/paddleseg.md

@@ -1,6 +1,6 @@
 # PaddleSeg模型部署
 
-当前支持PaddleSeg release/2.0分支训练的模型进行导出及部署。本文档以[Deeplabv3P](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/configs/deeplabv3p)模型为例,讲述从release-2.0版本导出模型并进行cpp部署整个流程。 PaddleSeg相关详细文档查看[官网文档](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/README_CN.md)
+当前支持PaddleSeg release/2.1分支训练的模型进行导出及部署。本文档以[Deeplabv3P](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v2.0/configs/deeplabv3p)模型为例,讲述从release-2.1版本导出模型并进行cpp部署整个流程。 PaddleSeg相关详细文档查看[官网文档](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.1/README_CN.md)
 
 ## 步骤一 部署模型导出
 
@@ -9,7 +9,6 @@
 ```sh
 git clone https://github.com/PaddlePaddle/PaddleSeg.git
 cd PaddleSeg
-git checkout realease/v2.0
 ```
 
 ### 2. 导出基于Cityscape数据的预训练模型
@@ -35,16 +34,12 @@ output
   └── model.pdmodel          # 静态图模型文件
 ```
 
-
-
 ## 步骤二 编译
 
 参考编译文档
 
-- [Linux系统上编译指南](../compile/linux.md)
-- [Windows系统上编译指南](../compile/windows.md)
-
-
+- [Linux系统上编译指南](../compile/paddle/linux.md)
+- [Windows系统上编译指南](../compile/paddle/windows.md)
 
 ## 步骤三 模型预测
 
@@ -64,7 +59,10 @@ output
 ScoreMask(mean: 12.4814 std:    10.4955)    LabelMask(mean: 1.98847 std:    10.3141)
 ```
 
+**注意:**release/2.1之后,PaddleSeg导出的模型默认只有label_map, score_map的值都被填充为1.0
+
 关于demo程序的详细使用方法可分别参考以下文档
 
 - [单卡加载模型预测示例](../demo/model_infer.md)
 - [多卡加载模型预测示例](../demo/multi_gpu_model_infer.md)
+- [PaddleInference集成TensorRT加载模型预测示例](../../demo/tensorrt_infer.md)

+ 11 - 18
dygraph/deploy/cpp/model_deploy/common/include/base_model.h

@@ -64,21 +64,11 @@ class Model {
     return false;
   }
 
-  bool PaddleEngineInit(const std::string& model_filename,
-                        const std::string& params_filename,
-                        bool use_gpu = false, int gpu_id = 0,
-                        bool use_mkl = true, int mkl_thread_num = 8);
-
-  bool TritonEngineInit(const std::string& url,
-                        const std::string& model_name,
-                        const std::string& model_version,
-                        bool verbose = false);
-
-  bool TensorRTInit(const std::string& model_file,
-                    const std::string& cfg_file,
-                    const int gpu_id = 0,
-                    const bool save_engine = false,
-                    std::string trt_cache_file = "");
+  bool PaddleEngineInit(const PaddleEngineConfig& engine_config);
+
+  bool TritonEngineInit(const TritonEngineConfig& engine_config);
+
+  bool TensorRTInit(const TensorRTEngineConfig& engine_config);
 
   virtual bool PostprocessInit() {
     postprocess_ = nullptr;
@@ -104,12 +94,15 @@ class Model {
     std::vector<DataBlob> inputs;
     std::vector<DataBlob> outputs;
 
-    if (!preprocess_->Run(&imgs_clone, &inputs, &shape_infos, thread_num))
+    if (!preprocess_->Run(&imgs_clone, &inputs, &shape_infos, thread_num)) {
       return false;
-    if (!infer_engine_->Infer(inputs, &outputs))
+    }
+    if (!infer_engine_->Infer(inputs, &outputs)) {
       return false;
-    if (!postprocess_->Run(outputs, shape_infos, results, thread_num))
+    }
+    if (!postprocess_->Run(outputs, shape_infos, results, thread_num)) {
       return false;
+    }
     return true;
   }
 

+ 5 - 6
dygraph/deploy/cpp/model_deploy/common/include/multi_gpu_model.h

@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "model_deploy/common/include/model_factory.h"
+#include "model_deploy/engine/include/engine.h"
 
 namespace PaddleDeploy {
 class MultiGPUModel {
@@ -49,8 +50,7 @@ class MultiGPUModel {
     return true;
   }
 
-  bool PaddleEngineInit(const std::string& model_filename,
-                        const std::string& params_filename,
+  bool PaddleEngineInit(PaddleEngineConfig engine_config,
                         const std::vector<int> gpu_ids) {
     if (gpu_ids.size() != models_.size()) {
       std::cerr << "Paddle Engine Init gpu_ids != MultiGPUModel Init gpu_num"
@@ -58,11 +58,10 @@ class MultiGPUModel {
                 << std::endl;
       return false;
     }
+    engine_config.use_gpu = true;
     for (auto i = 0; i < gpu_ids.size(); ++i) {
-      if (!models_[i]->PaddleEngineInit(model_filename,
-                                        params_filename,
-                                        true, gpu_ids[i],
-                                        true)) {
+      engine_config.gpu_id = gpu_ids[i];
+      if (!models_[i]->PaddleEngineInit(engine_config)) {
         std::cerr << "Paddle Engine Init error:" << gpu_ids[i] << std::endl;
         return false;
       }

+ 1 - 0
dygraph/deploy/cpp/model_deploy/common/include/paddle_deploy.h

@@ -18,6 +18,7 @@
 
 #include "model_deploy/common/include/output_struct.h"
 #include "model_deploy/common/include/model_factory.h"
+#include "model_deploy/engine/include/engine.h"
 
 namespace PaddleDeploy {
 inline std::shared_ptr<Model> CreateModel(const std::string &name) {

+ 21 - 4
dygraph/deploy/cpp/model_deploy/engine/include/engine_config.h

@@ -48,7 +48,7 @@ struct PaddleEngineConfig {
   bool use_trt = false;
 
   //  Set batchsize
-  int batch_size = 1;
+  int max_batch_size = 1;
 
   //  Set TensorRT min_subgraph_size
   int min_subgraph_size = 1;
@@ -65,13 +65,25 @@ struct PaddleEngineConfig {
 
   //  Is offline calibration required, when tensorrt is used
   bool use_calib_mode = false;
+
+  //  tensorrt workspace size
+  int max_workspace_size = 1 << 10;
+
+  //  tensorrt dynamic shape ,  min input shape
+  std::map<std::string, std::vector<int>> min_input_shape;
+
+  //  tensorrt dynamic shape ,  max input shape
+  std::map<std::string, std::vector<int>> max_input_shape;
+
+  //  tensorrt dynamic shape ,  optimal input shape
+  std::map<std::string, std::vector<int>> optim_input_shape;
 };
 
 struct TritonEngineConfig {
   TritonEngineConfig() : model_name_(""), model_version_(""),
         request_id_(""), sequence_id_(0), sequence_start_(false),
         sequence_end_(false), priority_(0), server_timeout_(0),
-        client_timeout_(0) {}
+        client_timeout_(0), verbose_(false), url_("") {}
   /// The name of the model to run inference.
   std::string model_name_;
   /// The version of the model to use while running inference. The default
@@ -118,14 +130,19 @@ struct TritonEngineConfig {
   // InferenceServerGrpcClient::StartStream().
   uint64_t client_timeout_;
 
-  bool verbose_ = false;
+  // open client log
+  bool verbose_;
 
+  // Request the address
   std::string url_;
 };
 
 struct TensorRTEngineConfig {
   // onnx model path
-  std::string model_file_;
+  std::string model_file_ = "";
+
+  // paddle model config file
+  std::string cfg_file_ = "";
 
   // GPU workspace size
   int max_workspace_size_ = 1<<28;

+ 1 - 0
dygraph/deploy/cpp/model_deploy/engine/include/tensorrt_engine.h

@@ -95,6 +95,7 @@ class TensorRTInferenceEngine : public InferEngine {
              std::vector<DataBlob>* output_blobs);
 
   std::shared_ptr<nvinfer1::ICudaEngine> engine_{nullptr};
+  std::shared_ptr<nvinfer1::IExecutionContext> context_;
   NaiveLogger logger_;
 
  private:

+ 10 - 11
dygraph/deploy/cpp/model_deploy/engine/src/ppinference_engine.cpp

@@ -15,17 +15,10 @@
 #include "model_deploy/engine/include/ppinference_engine.h"
 
 namespace PaddleDeploy {
-bool Model::PaddleEngineInit(const std::string& model_filename,
-                             const std::string& params_filename, bool use_gpu,
-                             int gpu_id, bool use_mkl, int mkl_thread_num) {
+bool Model::PaddleEngineInit(const PaddleEngineConfig& engine_config) {
   infer_engine_ = std::make_shared<PaddleInferenceEngine>();
   InferenceConfig config("paddle");
-  config.paddle_config->model_filename = model_filename;
-  config.paddle_config->params_filename = params_filename;
-  config.paddle_config->use_gpu = use_gpu;
-  config.paddle_config->gpu_id = gpu_id;
-  config.paddle_config->use_mkl = use_mkl;
-  config.paddle_config->mkl_thread_num = mkl_thread_num;
+  *(config.paddle_config) = engine_config;
   return infer_engine_->Init(config);
 }
 
@@ -61,12 +54,18 @@ bool PaddleInferenceEngine::Init(const InferenceConfig& infer_config) {
       return false;
     }
     config.EnableTensorRtEngine(
-        1 << 10 /* workspace_size*/,
-        engine_config.batch_size /* max_batch_size*/,
+        engine_config.max_workspace_size /* workspace_size*/,
+        engine_config.max_batch_size /* max_batch_size*/,
         engine_config.min_subgraph_size /* min_subgraph_size*/,
         precision /* precision*/,
         engine_config.use_static /* use_static*/,
         engine_config.use_calib_mode /* use_calib_mode*/);
+
+    if (engine_config.min_input_shape.size() != 0) {
+      config.SetTRTDynamicShapeInfo(engine_config.min_input_shape,
+                                    engine_config.max_input_shape,
+                                    engine_config.optim_input_shape);
+    }
   }
   predictor_ = std::move(paddle_infer::CreatePredictor(config));
   return true;

+ 16 - 19
dygraph/deploy/cpp/model_deploy/engine/src/tensorrt_engine.cpp

@@ -30,26 +30,22 @@ int DtypeConver(const nvinfer1::DataType& dtype) {
   return -1;
 }
 
-bool Model::TensorRTInit(const std::string& model_file,
-                         const std::string& cfg_file,
-                         const int gpu_id,
-                         const bool save_engine,
-                         std::string trt_cache_file) {
+bool Model::TensorRTInit(const TensorRTEngineConfig& engine_config) {
   infer_engine_ = std::make_shared<TensorRTInferenceEngine>();
   InferenceConfig config("tensorrt");
-  config.tensorrt_config->model_file_ = model_file;
-  config.tensorrt_config->gpu_id_ = gpu_id;
-  config.tensorrt_config->save_engine_ = save_engine;
-  config.tensorrt_config->trt_cache_file_ = trt_cache_file;
-  config.tensorrt_config->yaml_config_ = YAML::LoadFile(cfg_file);
-  if (!config.tensorrt_config->yaml_config_["input"].IsDefined()) {
+
+  YAML::Node node  = YAML::LoadFile(engine_config.cfg_file_);
+  if (!node["input"].IsDefined()) {
     std::cout << "Fail to find input in yaml file!" << std::endl;
     return false;
   }
-  if (!config.tensorrt_config->yaml_config_["output"].IsDefined()) {
+  if (!node["output"].IsDefined()) {
     std::cout << "Fail to find output in yaml file!" << std::endl;
     return false;
   }
+
+  *(config.tensorrt_config) = engine_config;
+  config.tensorrt_config->yaml_config_ = node;
   return infer_engine_->Init(config);
 }
 
@@ -130,6 +126,13 @@ bool TensorRTInferenceEngine::Init(const InferenceConfig& engine_config) {
                                                    *config),
                     InferDeleter());
 
+  context_ = std::shared_ptr<nvinfer1::IExecutionContext>(
+                    engine_->createExecutionContext(),
+                    InferDeleter());
+  if (!context_) {
+    return false;
+  }
+
   if (tensorrt_config.save_engine_) {
     if (!SaveEngine(*(engine_.get()), tensorrt_config.trt_cache_file_)) {
       std::cout << "Fail save Trt Engine to "
@@ -221,16 +224,10 @@ bool TensorRTInferenceEngine::SaveEngine(const nvinfer1::ICudaEngine& engine,
 
 bool TensorRTInferenceEngine::Infer(const std::vector<DataBlob>& input_blobs,
                                     std::vector<DataBlob>* output_blobs) {
-  auto context = InferUniquePtr<nvinfer1::IExecutionContext>(
-                     engine_->createExecutionContext());
-  if (!context) {
-    return false;
-  }
-
   TensorRT::BufferManager buffers(engine_);
   FeedInput(input_blobs, buffers);
   buffers.copyInputToDevice();
-  bool status = context->executeV2(buffers.getDeviceBindings().data());
+  bool status = context_->executeV2(buffers.getDeviceBindings().data());
   if (!status) {
     return false;
   }

+ 2 - 8
dygraph/deploy/cpp/model_deploy/engine/src/triton_engine.cpp

@@ -51,16 +51,10 @@ int DtypeToInt(std::string dtype) {
   }
 }
 
-bool Model::TritonEngineInit(const std::string& url,
-                             const std::string& model_name,
-                             const std::string& model_version,
-                             bool verbose) {
+bool Model::TritonEngineInit(const TritonEngineConfig& engine_config) {
   infer_engine_ = std::make_shared<TritonInferenceEngine>();
   InferenceConfig config("triton");
-  config.triton_config->url_ = url;
-  config.triton_config->model_name_ = model_name;
-  config.triton_config->model_version_ = model_version;
-  config.triton_config->verbose_ = verbose;
+  *(config.triton_config) = engine_config;
   infer_engine_->Init(config);
 }
 

+ 5 - 0
dygraph/deploy/cpp/model_deploy/ppseg/include/seg_postprocess.h

@@ -38,10 +38,15 @@ class SegPostprocess : public BasePostprocess {
   virtual bool Run(const std::vector<DataBlob>& outputs,
                    const std::vector<ShapeInfo>& shape_infos,
                    std::vector<Result>* results, int thread_num = 1);
+
   void RestoreSegMap(const ShapeInfo& shape_info,
                      cv::Mat* label_mat,
                      cv::Mat* score_mat,
                      SegResult* result);
+
+  bool RunV2(const DataBlob& outputs,
+             const std::vector<ShapeInfo>& shape_infos,
+             std::vector<Result>* results, int thread_num);
 };
 
 }  // namespace PaddleDeploy

+ 32 - 0
dygraph/deploy/cpp/model_deploy/ppseg/src/seg_postprocess.cpp

@@ -59,6 +59,33 @@ void SegPostprocess::RestoreSegMap(const ShapeInfo& shape_info,
     score_mat->begin<float>(), score_mat->end<float>());
 }
 
+bool SegPostprocess::RunV2(const DataBlob& output,
+                           const std::vector<ShapeInfo>& shape_infos,
+                           std::vector<Result>* results, int thread_num) {
+  int batch_size = shape_infos.size();
+  std::vector<int> score_map_shape = output.shape;
+  int score_map_size = std::accumulate(output.shape.begin() + 1,
+                                       output.shape.end(), 1,
+                                       std::multiplies<int>());
+  const uint8_t* score_map_data =
+          reinterpret_cast<const uint8_t*>(output.data.data());
+  int num_map_pixels = output.shape[1] * output.shape[2];
+
+  for (int i = 0; i < batch_size; ++i) {
+    (*results)[i].model_type = "seg";
+    (*results)[i].seg_result = new SegResult();
+    const uint8_t* current_start_ptr = score_map_data + i * score_map_size;
+    cv::Mat score_mat(output.shape[1], output.shape[2],
+                      CV_32FC1, cv::Scalar(1.0));
+    cv::Mat label_mat(output.shape[1], output.shape[2],
+                      CV_8UC1, const_cast<uint8_t*>(current_start_ptr));
+
+    RestoreSegMap(shape_infos[i], &label_mat,
+                &score_mat, (*results)[i].seg_result);
+  }
+  return true;
+}
+
 bool SegPostprocess::Run(const std::vector<DataBlob>& outputs,
                          const std::vector<ShapeInfo>& shape_infos,
                          std::vector<Result>* results, int thread_num) {
@@ -72,6 +99,11 @@ bool SegPostprocess::Run(const std::vector<DataBlob>& outputs,
     index = 1;
   }
   std::vector<int> score_map_shape = outputs[index].shape;
+  // ppseg version >= 2.1  shape = [b, w, h]
+  if (score_map_shape.size() == 3) {
+    return RunV2(outputs[index], shape_infos, results, thread_num);
+  }
+
   int score_map_size = std::accumulate(score_map_shape.begin() + 1,
                     score_map_shape.end(), 1, std::multiplies<int>());
   const float* score_map_data =

+ 1 - 1
dygraph/deploy/cpp/scripts/tensorrt_build.sh

@@ -100,7 +100,7 @@ rm -rf log
 rm -rf build
 mkdir -p build
 cd build
-cmake ../demo/tensorrt/ \
+cmake ../demo/onnx_tensorrt/ \
     -DTENSORRT_DIR=${TENSORRT_DIR} \
     -DTENSORRT_HEADER=${TENSORRT_HEADER} \
     -DCUDA_DIR=${CUDA_DIR} \

+ 1 - 1
dygraph/deploy/cpp/scripts/triton_build.sh

@@ -80,7 +80,7 @@ fi
 rm -rf build
 mkdir -p build
 cd build
-cmake ../demo/triton/ \
+cmake ../demo/onnx_triton/ \
     -DTRITON_CLIENT=${TRITON_CLIENT} \
     -DOPENCV_DIR=${OPENCV_DIR}  \
     -DGLOG_DIR=${GLOG_DIR} \

+ 209 - 9
dygraph/paddlex/cls.py

@@ -12,15 +12,215 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import sys
-message = 'Your running script needs PaddleX<2.0.0, please refer to {} to solve this issue.'.format(
-    'https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7'
-)
+from . import cv
+from paddlex.cv.transforms import cls_transforms
+import paddlex.utils.logging as logging
 
+transforms = cls_transforms
 
-def __getattr__(attr):
-    if attr == 'transforms':
 
-        print("\033[1;31;40m{}\033[0m".format(message).encode("utf-8")
-              .decode("latin1"))
-        sys.exit(-1)
+class ResNet18(cv.models.ResNet18):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet18, self).__init__(num_classes=num_classes)
+
+
+class ResNet34(cv.models.ResNet34):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet34, self).__init__(num_classes=num_classes)
+
+
+class ResNet50(cv.models.ResNet50):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet50, self).__init__(num_classes=num_classes)
+
+
+class ResNet101(cv.models.ResNet101):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet101, self).__init__(num_classes=num_classes)
+
+
+class ResNet50_vd(cv.models.ResNet50_vd):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet50_vd, self).__init__(num_classes=num_classes)
+
+
+class ResNet101_vd(cv.models.ResNet101_vd):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet101_vd, self).__init__(num_classes=num_classes)
+
+
+class ResNet50_vd_ssld(cv.models.ResNet50_vd_ssld):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet50_vd_ssld, self).__init__(num_classes=num_classes)
+
+
+class ResNet101_vd_ssld(cv.models.ResNet101_vd_ssld):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ResNet101_vd_ssld, self).__init__(num_classes=num_classes)
+
+
+class DarkNet53(cv.models.DarkNet53):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(DarkNet53, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV1(cv.models.MobileNetV1):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV1, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV2(cv.models.MobileNetV2):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV2, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV3_small(cv.models.MobileNetV3_small):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV3_small, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV3_large(cv.models.MobileNetV3_large):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV3_large, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV3_small_ssld(cv.models.MobileNetV3_small_ssld):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV3_small_ssld, self).__init__(num_classes=num_classes)
+
+
+class MobileNetV3_large_ssld(cv.models.MobileNetV3_large_ssld):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(MobileNetV3_large_ssld, self).__init__(num_classes=num_classes)
+
+
+class Xception41(cv.models.Xception41):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(Xception41, self).__init__(num_classes=num_classes)
+
+
+class Xception65(cv.models.Xception65):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(Xception65, self).__init__(num_classes=num_classes)
+
+
+class DenseNet121(cv.models.DenseNet121):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(DenseNet121, self).__init__(num_classes=num_classes)
+
+
+class DenseNet161(cv.models.DenseNet161):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(DenseNet161, self).__init__(num_classes=num_classes)
+
+
+class DenseNet201(cv.models.DenseNet201):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(DenseNet201, self).__init__(num_classes=num_classes)
+
+
+class ShuffleNetV2(cv.models.ShuffleNetV2):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(ShuffleNetV2, self).__init__(num_classes=num_classes)
+
+
+class HRNet_W18(cv.models.HRNet_W18_C):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(HRNet_W18, self).__init__(num_classes=num_classes)
+
+
+class AlexNet(cv.models.AlexNet):
+    def __init__(self, num_classes=1000, input_channel=None):
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(AlexNet, self).__init__(num_classes=num_classes)

+ 7 - 0
dygraph/paddlex/cv/datasets/coco.py

@@ -57,12 +57,14 @@ class CocoDetection(VOCDetection):
         super(VOCDetection, self).__init__()
         self.data_fields = None
         self.transforms = copy.deepcopy(transforms)
+        self.num_max_boxes = 50
         self.use_mix = False
         if self.transforms is not None:
             for op in self.transforms.transforms:
                 if isinstance(op, MixupImage):
                     self.mixup_op = copy.deepcopy(op)
                     self.use_mix = True
+                    self.num_max_boxes *= 2
                     break
 
         self.batch_transforms = None
@@ -153,6 +155,11 @@ class CocoDetection(VOCDetection):
                 **
                 label_info
             }))
+        if self.use_mix:
+            self.num_max_boxes = max(self.num_max_boxes, 2 * len(instances))
+        else:
+            self.num_max_boxes = max(self.num_max_boxes, len(instances))
+
         if not len(self.file_list) > 0:
             raise Exception('not found any coco record in %s' % ann_file)
         logging.info("{} samples in file {}".format(

+ 7 - 0
dygraph/paddlex/cv/datasets/voc.py

@@ -56,6 +56,7 @@ class VOCDetection(Dataset):
         super(VOCDetection, self).__init__()
         self.data_fields = None
         self.transforms = copy.deepcopy(transforms)
+        self.num_max_boxes = 50
 
         self.use_mix = False
         if self.transforms is not None:
@@ -63,6 +64,7 @@ class VOCDetection(Dataset):
                 if isinstance(op, MixupImage):
                     self.mixup_op = copy.deepcopy(op)
                     self.use_mix = True
+                    self.num_max_boxes *= 2
                     break
 
         self.batch_transforms = None
@@ -257,6 +259,11 @@ class VOCDetection(Dataset):
                         'id': int(im_id[0]),
                         'file_name': osp.split(img_file)[1]
                     })
+                if self.use_mix:
+                    self.num_max_boxes = max(self.num_max_boxes, 2 * len(objs))
+                else:
+                    self.num_max_boxes = max(self.num_max_boxes, len(objs))
+
         if not len(self.file_list) > 0:
             raise Exception('not found any voc record in %s' % (file_list))
         logging.info("{} samples in file {}".format(

+ 73 - 9
dygraph/paddlex/cv/models/base.py

@@ -22,6 +22,7 @@ import yaml
 import json
 import paddle
 from paddle.io import DataLoader, DistributedBatchSampler
+from paddleslim import QAT
 from paddleslim.analysis import flops
 from paddleslim import L1NormFilterPruner, FPGMFilterPruner
 import paddlex
@@ -53,6 +54,8 @@ class BaseModel:
         self.completed_epochs = 0
         self.pruner = None
         self.pruning_ratios = None
+        self.quantizer = None
+        self.quant_config = None
 
     def net_initialize(self, pretrain_weights=None, save_dir='.'):
         if pretrain_weights is not None and \
@@ -122,6 +125,11 @@ class BaseModel:
         info['pruner_inputs'] = self.pruner.inputs
         return info
 
+    def get_quant_info(self):
+        info = dict()
+        info['quant_config'] = self.quant_config
+        return info
+
     def save_model(self, save_dir):
         if not osp.isdir(save_dir):
             if osp.exists(save_dir):
@@ -129,10 +137,11 @@ class BaseModel:
             os.makedirs(save_dir)
         model_info = self.get_model_info()
         model_info['status'] = self.status
+
         paddle.save(self.net.state_dict(),
-                    os.path.join(save_dir, 'model.pdparams'))
+                    osp.join(save_dir, 'model.pdparams'))
         paddle.save(self.optimizer.state_dict(),
-                    os.path.join(save_dir, 'model.pdopt'))
+                    osp.join(save_dir, 'model.pdopt'))
 
         with open(
                 osp.join(save_dir, 'model.yml'), encoding='utf-8',
@@ -151,6 +160,13 @@ class BaseModel:
                     mode='w') as f:
                 yaml.dump(pruning_info, f)
 
+        if self.status == 'Quantized' and self.quantizer is not None:
+            quant_info = self.get_quant_info()
+            with open(
+                    osp.join(save_dir, 'quant.yml'), encoding='utf-8',
+                    mode='w') as f:
+                yaml.dump(quant_info, f)
+
         # 模型保存成功的标志
         open(osp.join(save_dir, '.success'), 'w').close()
         logging.info("Model saved in {}.".format(save_dir))
@@ -204,13 +220,17 @@ class BaseModel:
         nranks = paddle.distributed.get_world_size()
         local_rank = paddle.distributed.get_rank()
         if nranks > 1:
+            find_unused_parameters = getattr(self, 'find_unused_parameters',
+                                             False)
             # Initialize parallel environment if not done.
             if not paddle.distributed.parallel.parallel_helper._is_parallel_ctx_initialized(
             ):
                 paddle.distributed.init_parallel_env()
-                ddp_net = paddle.DataParallel(self.net)
+                ddp_net = paddle.DataParallel(
+                    self.net, find_unused_parameters=find_unused_parameters)
             else:
-                ddp_net = paddle.DataParallel(self.net)
+                ddp_net = paddle.DataParallel(
+                    self.net, find_unused_parameters=find_unused_parameters)
 
         if use_vdl:
             from visualdl import LogWriter
@@ -396,8 +416,8 @@ class BaseModel:
 
         Args:
             pruned_flops(float): Ratio of FLOPs to be pruned.
-            save_dir(None or str, optional): If None, the pruned model will not be saved
-            Otherwise, the pruned model will be saved at save_dir. Defaults to None.
+            save_dir(None or str, optional): If None, the pruned model will not be saved.
+                Otherwise, the pruned model will be saved at save_dir. Defaults to None.
 
         """
         if self.status == "Pruned":
@@ -423,13 +443,57 @@ class BaseModel:
             self.save_model(save_dir)
             logging.info("Pruned model is saved at {}".format(save_dir))
 
+    def _prepare_qat(self, quant_config):
+        if quant_config is None:
+            # default quantization configuration
+            quant_config = {
+                # {None, 'PACT'}. Weight preprocess type. If None, no preprocessing is performed.
+                'weight_preprocess_type': None,
+                # {None, 'PACT'}. Activation preprocess type. If None, no preprocessing is performed.
+                'activation_preprocess_type': None,
+                # {'abs_max', 'channel_wise_abs_max', 'range_abs_max', 'moving_average_abs_max'}.
+                # Weight quantization type.
+                'weight_quantize_type': 'channel_wise_abs_max',
+                # {'abs_max', 'range_abs_max', 'moving_average_abs_max'}. Activation quantization type.
+                'activation_quantize_type': 'moving_average_abs_max',
+                # The number of bits of weights after quantization.
+                'weight_bits': 8,
+                # The number of bits of activation after quantization.
+                'activation_bits': 8,
+                # Data type after quantization, such as 'uint8', 'int8', etc.
+                'dtype': 'int8',
+                # Window size for 'range_abs_max' quantization.
+                'window_size': 10000,
+                # Decay coefficient of moving average.
+                'moving_rate': .9,
+                # Types of layers that will be quantized.
+                'quantizable_layer_type': ['Conv2D', 'Linear']
+            }
+        self.quant_config = quant_config
+        self.quantizer = QAT(config=self.quant_config)
+        logging.info("Preparing the model for quantization-aware training...")
+        self.quantizer.quantize(self.net)
+        logging.info("Model is ready for quantization-aware training.")
+        self.status = 'Quantized'
+
     def _export_inference_model(self, save_dir, image_shape=None):
         save_dir = osp.join(save_dir, 'inference_model')
         self.net.eval()
         self.test_inputs = self._get_test_inputs(image_shape)
-        static_net = paddle.jit.to_static(
-            self.net, input_spec=self.test_inputs)
-        paddle.jit.save(static_net, osp.join(save_dir, 'model'))
+
+        if self.status == 'Quantized':
+            self.quantizer.save_quantized_model(self.net,
+                                                osp.join(save_dir, 'model'),
+                                                self.test_inputs)
+            quant_info = self.get_quant_info()
+            with open(
+                    osp.join(save_dir, 'quant.yml'), encoding='utf-8',
+                    mode='w') as f:
+                yaml.dump(quant_info, f)
+        else:
+            static_net = paddle.jit.to_static(
+                self.net, input_spec=self.test_inputs)
+            paddle.jit.save(static_net, osp.join(save_dir, 'model'))
 
         if self.status == 'Pruned':
             pruning_info = self.get_pruning_info()

+ 92 - 5
dygraph/paddlex/cv/models/classifier.py

@@ -35,10 +35,11 @@ __all__ = [
     "ResNet18_vd", "ResNet34_vd", "ResNet50_vd", "ResNet50_vd_ssld",
     "ResNet101_vd", "ResNet101_vd_ssld", "ResNet152_vd", "ResNet200_vd",
     "AlexNet", "DarkNet53", "MobileNetV1", "MobileNetV2", "MobileNetV3_small",
-    "MobileNetV3_large", "DenseNet121", "DenseNet161", "DenseNet169",
-    "DenseNet201", "DenseNet264", "HRNet_W18_C", "HRNet_W30_C", "HRNet_W32_C",
-    "HRNet_W40_C", "HRNet_W44_C", "HRNet_W48_C", "HRNet_W64_C", "Xception41",
-    "Xception65", "Xception71", "ShuffleNetV2", "ShuffleNetV2_swish"
+    "MobileNetV3_small_ssld", "MobileNetV3_large", "MobileNetV3_large_ssld",
+    "DenseNet121", "DenseNet161", "DenseNet169", "DenseNet201", "DenseNet264",
+    "HRNet_W18_C", "HRNet_W30_C", "HRNet_W32_C", "HRNet_W40_C", "HRNet_W44_C",
+    "HRNet_W48_C", "HRNet_W64_C", "Xception41", "Xception65", "Xception71",
+    "ShuffleNetV2", "ShuffleNetV2_swish"
 ]
 
 
@@ -261,6 +262,70 @@ class BaseClassifier(BaseModel):
             early_stop_patience=early_stop_patience,
             use_vdl=use_vdl)
 
+    def quant_aware_train(self,
+                          num_epochs,
+                          train_dataset,
+                          train_batch_size=64,
+                          eval_dataset=None,
+                          optimizer=None,
+                          save_interval_epochs=1,
+                          log_interval_steps=10,
+                          save_dir='output',
+                          learning_rate=.000025,
+                          warmup_steps=0,
+                          warmup_start_lr=0.0,
+                          lr_decay_epochs=(30, 60, 90),
+                          lr_decay_gamma=0.1,
+                          early_stop=False,
+                          early_stop_patience=5,
+                          use_vdl=True,
+                          quant_config=None):
+        """
+        Quantization-aware training.
+        Args:
+            num_epochs(int): The number of epochs.
+            train_dataset(paddlex.dataset): Training dataset.
+            train_batch_size(int, optional): Total batch size among all cards used in training. Defaults to 64.
+            eval_dataset(paddlex.dataset, optional):
+                Evaluation dataset. If None, the model will not be evaluated during training process. Defaults to None.
+            optimizer(paddle.optimizer.Optimizer or None, optional):
+                Optimizer used for training. If None, a default optimizer is used. Defaults to None.
+            save_interval_epochs(int, optional): Epoch interval for saving the model. Defaults to 1.
+            log_interval_steps(int, optional): Step interval for printing training information. Defaults to 10.
+            save_dir(str, optional): Directory to save the model. Defaults to 'output'.
+            learning_rate(float, optional): Learning rate for training. Defaults to .025.
+            warmup_steps(int, optional): The number of steps of warm-up training. Defaults to 0.
+            warmup_start_lr(float, optional): Start learning rate of warm-up training. Defaults to 0..
+            lr_decay_epochs(List[int] or Tuple[int], optional):
+                Epoch milestones for learning rate decay. Defaults to (20, 60, 90).
+            lr_decay_gamma(float, optional): Gamma coefficient of learning rate decay, default .1.
+            early_stop(bool, optional): Whether to adopt early stop strategy. Defaults to False.
+            early_stop_patience(int, optional): Early stop patience. Defaults to 5.
+            use_vdl(bool, optional): Whether to use VisualDL to monitor the training process. Defaults to True.
+            quant_config(dict or None, optional): Quantization configuration. If None, a default rule of thumb
+                configuration will be used. Defaults to None.
+
+        """
+        self._prepare_qat(quant_config)
+        self.train(
+            num_epochs=num_epochs,
+            train_dataset=train_dataset,
+            train_batch_size=train_batch_size,
+            eval_dataset=eval_dataset,
+            optimizer=optimizer,
+            save_interval_epochs=save_interval_epochs,
+            log_interval_steps=log_interval_steps,
+            save_dir=save_dir,
+            pretrain_weights=None,
+            learning_rate=learning_rate,
+            warmup_steps=warmup_steps,
+            warmup_start_lr=warmup_start_lr,
+            lr_decay_epochs=lr_decay_epochs,
+            lr_decay_gamma=lr_decay_gamma,
+            early_stop=early_stop,
+            early_stop_patience=early_stop_patience,
+            use_vdl=use_vdl)
+
     def evaluate(self, eval_dataset, batch_size=1, return_details=False):
         """
         Evaluate the model.
@@ -441,7 +506,7 @@ class ResNet101_vd(BaseClassifier):
 class ResNet101_vd_ssld(BaseClassifier):
     def __init__(self, num_classes=1000):
         super(ResNet101_vd_ssld, self).__init__(
-            model_name='ResNet101_vd_ssld',
+            model_name='ResNet101_vd',
             num_classes=num_classes,
             lr_mult_list=[.1, .1, .2, .2, .3])
         self.model_name = 'ResNet101_vd_ssld'
@@ -531,6 +596,21 @@ class MobileNetV3_small(BaseClassifier):
             model_name=model_name, num_classes=num_classes)
 
 
+class MobileNetV3_small_ssld(BaseClassifier):
+    def __init__(self, num_classes=1000, scale=1.0):
+        supported_scale = [.35, 1.0]
+        if scale not in supported_scale:
+            logging.warning(
+                "scale={} is not supported by MobileNetV3_small_ssld, "
+                "scale is forcibly set to 1.0".format(scale))
+            scale = 1.0
+        model_name = 'MobileNetV3_small_x' + str(float(scale)).replace('.',
+                                                                       '_')
+        super(MobileNetV3_small_ssld, self).__init__(
+            model_name=model_name, num_classes=num_classes)
+        self.model_name = model_name + '_ssld'
+
+
 class MobileNetV3_large(BaseClassifier):
     def __init__(self, num_classes=1000, scale=1.0):
         supported_scale = [.35, .5, .75, 1.0, 1.25]
@@ -544,6 +624,13 @@ class MobileNetV3_large(BaseClassifier):
             model_name=model_name, num_classes=num_classes)
 
 
+class MobileNetV3_large_ssld(BaseClassifier):
+    def __init__(self, num_classes=1000):
+        super(MobileNetV3_large_ssld, self).__init__(
+            model_name='MobileNetV3_large_x1_0', num_classes=num_classes)
+        self.model_name = 'MobileNetV3_large_x1_0_ssld'
+
+
 class DenseNet121(BaseClassifier):
     def __init__(self, num_classes=1000):
         super(DenseNet121, self).__init__(

+ 148 - 68
dygraph/paddlex/cv/models/detector.py

@@ -208,9 +208,10 @@ class BaseDetector(BaseModel):
                 "Evaluation metric {} is not supported, please choose form 'COCO' and 'VOC'"
             self.metric = metric.lower()
 
+        self.labels = train_dataset.labels
+        self.num_max_boxes = train_dataset.num_max_boxes
         train_dataset.batch_transforms = self._compose_batch_transform(
             train_dataset.transforms, mode='train')
-        self.labels = train_dataset.labels
 
         # build optimizer if not defined
         if optimizer is None:
@@ -236,7 +237,7 @@ class BaseDetector(BaseModel):
                 pretrain_weights = det_pretrain_weights_dict['_'.join(
                     [self.model_name, self.backbone_name])][0]
                 logging.warning("Pretrain_weights is forcibly set to '{}'. "
-                                "If don't want to use pretrain weights, "
+                                "If you don't want to use pretrain weights, "
                                 "set pretrain_weights to be None.".format(
                                     pretrain_weights))
         pretrained_dir = osp.join(save_dir, 'pretrain')
@@ -262,6 +263,76 @@ class BaseDetector(BaseModel):
             early_stop_patience=early_stop_patience,
             use_vdl=use_vdl)
 
+    def quant_aware_train(self,
+                          num_epochs,
+                          train_dataset,
+                          train_batch_size=64,
+                          eval_dataset=None,
+                          optimizer=None,
+                          save_interval_epochs=1,
+                          log_interval_steps=10,
+                          save_dir='output',
+                          learning_rate=.00001,
+                          warmup_steps=0,
+                          warmup_start_lr=0.0,
+                          lr_decay_epochs=(216, 243),
+                          lr_decay_gamma=0.1,
+                          metric=None,
+                          use_ema=False,
+                          early_stop=False,
+                          early_stop_patience=5,
+                          use_vdl=True,
+                          quant_config=None):
+        """
+        Quantization-aware training.
+        Args:
+            num_epochs(int): The number of epochs.
+            train_dataset(paddlex.dataset): Training dataset.
+            train_batch_size(int, optional): Total batch size among all cards used in training. Defaults to 64.
+            eval_dataset(paddlex.dataset, optional):
+                Evaluation dataset. If None, the model will not be evaluated during training process. Defaults to None.
+            optimizer(paddle.optimizer.Optimizer or None, optional):
+                Optimizer used for training. If None, a default optimizer is used. Defaults to None.
+            save_interval_epochs(int, optional): Epoch interval for saving the model. Defaults to 1.
+            log_interval_steps(int, optional): Step interval for printing training information. Defaults to 10.
+            save_dir(str, optional): Directory to save the model. Defaults to 'output'.
+            learning_rate(float, optional): Learning rate for training. Defaults to .001.
+            warmup_steps(int, optional): The number of steps of warm-up training. Defaults to 0.
+            warmup_start_lr(float, optional): Start learning rate of warm-up training. Defaults to 0..
+            lr_decay_epochs(list or tuple, optional): Epoch milestones for learning rate decay. Defaults to (216, 243).
+            lr_decay_gamma(float, optional): Gamma coefficient of learning rate decay. Defaults to .1.
+            metric({'VOC', 'COCO', None}, optional):
+                Evaluation metric. If None, determine the metric according to the dataset format. Defaults to None.
+            use_ema(bool, optional): Whether to use exponential moving average strategy. Defaults to False.
+            early_stop(bool, optional): Whether to adopt early stop strategy. Defaults to False.
+            early_stop_patience(int, optional): Early stop patience. Defaults to 5.
+            use_vdl(bool, optional): Whether to use VisualDL to monitor the training process. Defaults to True.
+            quant_config(dict or None, optional): Quantization configuration. If None, a default rule of thumb
+                configuration will be used. Defaults to None.
+
+        """
+        self._prepare_qat(quant_config)
+        self.train(
+            num_epochs=num_epochs,
+            train_dataset=train_dataset,
+            train_batch_size=train_batch_size,
+            eval_dataset=eval_dataset,
+            optimizer=optimizer,
+            save_interval_epochs=save_interval_epochs,
+            log_interval_steps=log_interval_steps,
+            save_dir=save_dir,
+            pretrain_weights=None,
+            learning_rate=learning_rate,
+            warmup_steps=warmup_steps,
+            warmup_start_lr=warmup_start_lr,
+            lr_decay_epochs=lr_decay_epochs,
+            lr_decay_gamma=lr_decay_gamma,
+            metric=metric,
+            use_ema=use_ema,
+            early_stop=early_stop,
+            early_stop_patience=early_stop_patience,
+            use_vdl=use_vdl)
+
     def evaluate(self,
                  eval_dataset,
                  batch_size=1,
@@ -280,12 +351,24 @@ class BaseDetector(BaseModel):
             collections.OrderedDict with key-value pairs: {"mAP(0.50, 11point)":`mean average precision`}.
 
         """
-        if eval_dataset.__class__.__name__ == 'VOCDetection':
+
+        if metric is None:
+            if not hasattr(self, 'metric'):
+                if eval_dataset.__class__.__name__ == 'VOCDetection':
+                    self.metric = 'voc'
+                elif eval_dataset.__class__.__name__ == 'CocoDetection':
+                    self.metric = 'coco'
+        else:
+            assert metric.lower() in ['coco', 'voc'], \
+                "Evaluation metric {} is not supported, please choose form 'COCO' and 'VOC'"
+            self.metric = metric.lower()
+
+        if self.metric == 'voc':
             eval_dataset.data_fields = {
                 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
                 'difficult'
             }
-        elif eval_dataset.__class__.__name__ == 'CocoDetection':
+        elif self.metric == 'coco':
             if self.__class__.__name__ == 'MaskRCNN':
                 eval_dataset.data_fields = {
                     'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
@@ -326,41 +409,16 @@ class BaseDetector(BaseModel):
                 is_bbox_normalized = any(
                     isinstance(t, _NormalizeBox)
                     for t in eval_dataset.batch_transforms.batch_transforms)
-            if metric is None:
-                if getattr(self, 'metric', None) is not None:
-                    if self.metric == 'voc':
-                        eval_metric = VOCMetric(
-                            labels=eval_dataset.labels,
-                            coco_gt=copy.deepcopy(eval_dataset.coco_gt),
-                            is_bbox_normalized=is_bbox_normalized,
-                            classwise=False)
-                    else:
-                        eval_metric = COCOMetric(
-                            coco_gt=copy.deepcopy(eval_dataset.coco_gt),
-                            classwise=False)
-                else:
-                    if eval_dataset.__class__.__name__ == 'VOCDetection':
-                        eval_metric = VOCMetric(
-                            labels=eval_dataset.labels,
-                            coco_gt=copy.deepcopy(eval_dataset.coco_gt),
-                            is_bbox_normalized=is_bbox_normalized,
-                            classwise=False)
-                    elif eval_dataset.__class__.__name__ == 'CocoDetection':
-                        eval_metric = COCOMetric(
-                            coco_gt=copy.deepcopy(eval_dataset.coco_gt),
-                            classwise=False)
+            if self.metric == 'voc':
+                eval_metric = VOCMetric(
+                    labels=eval_dataset.labels,
+                    coco_gt=copy.deepcopy(eval_dataset.coco_gt),
+                    is_bbox_normalized=is_bbox_normalized,
+                    classwise=False)
             else:
-                assert metric.lower() in ['coco', 'voc'], \
-                    "Evaluation metric {} is not supported, please choose form 'COCO' and 'VOC'"
-                if metric.lower() == 'coco':
-                    eval_metric = COCOMetric(
-                        coco_gt=copy.deepcopy(eval_dataset.coco_gt),
-                        classwise=False)
-                else:
-                    eval_metric = VOCMetric(
-                        labels=eval_dataset.labels,
-                        is_bbox_normalized=is_bbox_normalized,
-                        classwise=False)
+                eval_metric = COCOMetric(
+                    coco_gt=copy.deepcopy(eval_dataset.coco_gt),
+                    classwise=False)
             scores = collections.OrderedDict()
             logging.info(
                 "Start to evaluate(total_samples={}, total_steps={})...".
@@ -595,8 +653,7 @@ class YOLOv3(BaseDetector):
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
             default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=-1, pad_gt=False), _NormalizeBox(),
+                _BatchPadding(pad_to_stride=-1), _NormalizeBox(),
                 _PadBox(getattr(self, 'num_max_boxes', 50)), _BboxXYXY2XYWH(),
                 _Gt2YoloTarget(
                     anchor_masks=self.anchor_masks,
@@ -606,10 +663,11 @@ class YOLOv3(BaseDetector):
                     num_classes=self.num_classes)
             ]
         else:
-            default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=-1, pad_gt=False)
-            ]
+            default_batch_transforms = [_BatchPadding(pad_to_stride=-1)]
+        if mode == 'eval' and self.metric == 'voc':
+            collate_batch = False
+        else:
+            collate_batch = True
 
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
@@ -621,8 +679,9 @@ class YOLOv3(BaseDetector):
                         "Please check the {} transforms.".format(mode))
                 custom_batch_transforms.insert(0, copy.deepcopy(op))
 
-        batch_transforms = BatchCompose(custom_batch_transforms +
-                                        default_batch_transforms)
+        batch_transforms = BatchCompose(
+            custom_batch_transforms + default_batch_transforms,
+            collate_batch=collate_batch)
 
         return batch_transforms
 
@@ -668,14 +727,22 @@ class FasterRCNN(BaseDetector):
         self.init_params = locals()
         if backbone not in [
                 'ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet34',
-                'ResNet34_vd', 'ResNet101', 'ResNet101_vd'
+                'ResNet34_vd', 'ResNet101', 'ResNet101_vd', 'HRNet_W18'
         ]:
             raise ValueError(
                 "backbone: {} is not supported. Please choose one of "
                 "('ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet34', 'ResNet34_vd', "
-                "'ResNet101', 'ResNet101_vd')".format(backbone))
-        self.backbone_name = backbone + '_fpn' if with_fpn else backbone
-        if backbone == 'ResNet50_vd_ssld':
+                "'ResNet101', 'ResNet101_vd', 'HRNet_W18')".format(backbone))
+        self.backbone_name = backbone
+        if backbone == 'HRNet_W18':
+            if not with_fpn:
+                logging.warning(
+                    "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
+                    format(backbone))
+                with_fpn = True
+            backbone = self._get_backbone(
+                'HRNet', width=18, freeze_at=0, return_idx=[0, 1, 2, 3])
+        elif backbone == 'ResNet50_vd_ssld':
             if not with_fpn:
                 logging.warning(
                     "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
@@ -738,10 +805,23 @@ class FasterRCNN(BaseDetector):
         rpn_in_channel = backbone.out_shape[0].channels
 
         if with_fpn:
-            neck = ppdet.modeling.FPN(
-                in_channels=[i.channels for i in backbone.out_shape],
-                out_channel=fpn_num_channels,
-                spatial_scales=[1.0 / i.stride for i in backbone.out_shape])
+            self.backbone_name = self.backbone_name + '_fpn'
+
+            if 'HRNet' in self.backbone_name:
+                neck = ppdet.modeling.HRFPN(
+                    in_channels=[i.channels for i in backbone.out_shape],
+                    out_channel=fpn_num_channels,
+                    spatial_scales=[
+                        1.0 / i.stride for i in backbone.out_shape
+                    ],
+                    share_conv=False)
+            else:
+                neck = ppdet.modeling.FPN(
+                    in_channels=[i.channels for i in backbone.out_shape],
+                    out_channel=fpn_num_channels,
+                    spatial_scales=[
+                        1.0 / i.stride for i in backbone.out_shape
+                    ])
             rpn_in_channel = neck.out_shape[0].channels
             anchor_generator_cfg = {
                 'aspect_ratios': aspect_ratios,
@@ -849,14 +929,14 @@ class FasterRCNN(BaseDetector):
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
             default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=32 if self.with_fpn else -1, pad_gt=True)
+                _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
+            collate_batch = False
         else:
             default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=32 if self.with_fpn else -1, pad_gt=False)
+                _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
+            collate_batch = True
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -867,8 +947,9 @@ class FasterRCNN(BaseDetector):
                         "Please check the {} transforms.".format(mode))
                 custom_batch_transforms.insert(0, copy.deepcopy(op))
 
-        batch_transforms = BatchCompose(custom_batch_transforms +
-                                        default_batch_transforms)
+        batch_transforms = BatchCompose(
+            custom_batch_transforms + default_batch_transforms,
+            collate_batch=collate_batch)
 
         return batch_transforms
 
@@ -1166,7 +1247,6 @@ class PPYOLOTiny(YOLOv3):
         self.anchors = anchors
         self.anchor_masks = anchor_masks
         self.downsample_ratios = downsample_ratios
-        self.num_max_boxes = 100
         self.model_name = 'PPYOLOTiny'
 
 
@@ -1290,7 +1370,6 @@ class PPYOLOv2(YOLOv3):
         self.anchors = anchors
         self.anchor_masks = anchor_masks
         self.downsample_ratios = downsample_ratios
-        self.num_max_boxes = 100
         self.model_name = 'PPYOLOv2'
 
 
@@ -1519,14 +1598,14 @@ class MaskRCNN(BaseDetector):
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
             default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=32 if self.with_fpn else -1, pad_gt=True)
+                _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
+            collate_batch = False
         else:
             default_batch_transforms = [
-                _BatchPadding(
-                    pad_to_stride=32 if self.with_fpn else -1, pad_gt=False)
+                _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
+            collate_batch = True
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -1537,8 +1616,9 @@ class MaskRCNN(BaseDetector):
                         "Please check the {} transforms.".format(mode))
                 custom_batch_transforms.insert(0, copy.deepcopy(op))
 
-        batch_transforms = BatchCompose(custom_batch_transforms +
-                                        default_batch_transforms)
+        batch_transforms = BatchCompose(
+            custom_batch_transforms + default_batch_transforms,
+            collate_batch=collate_batch)
 
         return batch_transforms
 

+ 7 - 0
dygraph/paddlex/cv/models/load_model.py

@@ -76,6 +76,13 @@ def load_model(model_dir):
                     ratios=model.pruning_ratios,
                     axis=paddleslim.dygraph.prune.filter_pruner.FILTER_DIM)
 
+        if status == 'Quantized':
+            with open(osp.join(model_dir, "quant.yml")) as f:
+                quant_info = yaml.load(f.read(), Loader=yaml.Loader)
+                quant_config = quant_info['quant_config']
+                model.quantizer = paddleslim.QAT(quant_config)
+                model.quantizer.quantize(model.net)
+
         if status == 'Infer':
             if model_info['Model'] in ['FasterRCNN', 'MaskRCNN']:
                 #net_state_dict = paddle.load(

+ 55 - 0
dygraph/paddlex/cv/models/segmenter.py

@@ -49,6 +49,7 @@ class BaseSegmenter(BaseModel):
         self.losses = None
         self.labels = None
         self.net = self.build_net(**params)
+        self.find_unused_parameters = True
 
     def build_net(self, **params):
         # TODO: when using paddle.utils.unique_name.guard,
@@ -254,6 +255,60 @@ class BaseSegmenter(BaseModel):
             early_stop_patience=early_stop_patience,
             use_vdl=use_vdl)
 
+    def quant_aware_train(self,
+                          num_epochs,
+                          train_dataset,
+                          train_batch_size=2,
+                          eval_dataset=None,
+                          optimizer=None,
+                          save_interval_epochs=1,
+                          log_interval_steps=2,
+                          save_dir='output',
+                          learning_rate=0.0001,
+                          lr_decay_power=0.9,
+                          early_stop=False,
+                          early_stop_patience=5,
+                          use_vdl=True,
+                          quant_config=None):
+        """
+        Quantization-aware training.
+        Args:
+            num_epochs(int): The number of epochs.
+            train_dataset(paddlex.dataset): Training dataset.
+            train_batch_size(int, optional): Total batch size among all cards used in training. Defaults to 2.
+            eval_dataset(paddlex.dataset, optional):
+                Evaluation dataset. If None, the model will not be evaluated furing training process. Defaults to None.
+            optimizer(paddle.optimizer.Optimizer or None, optional):
+                Optimizer used in training. If None, a default optimizer is used. Defaults to None.
+            save_interval_epochs(int, optional): Epoch interval for saving the model. Defaults to 1.
+            log_interval_steps(int, optional): Step interval for printing training information. Defaults to 10.
+            save_dir(str, optional): Directory to save the model. Defaults to 'output'.
+            learning_rate(float, optional): Learning rate for training. Defaults to .025.
+            lr_decay_power(float, optional): Learning decay power. Defaults to .9.
+            early_stop(bool, optional): Whether to adopt early stop strategy. Defaults to False.
+            early_stop_patience(int, optional): Early stop patience. Defaults to 5.
+            use_vdl(bool, optional): Whether to use VisualDL to monitor the training process. Defaults to True.
+            quant_config(dict or None, optional): Quantization configuration. If None, a default rule of thumb
+                configuration will be used. Defaults to None.
+
+        """
+        self._prepare_qat(quant_config)
+        self.train(
+            num_epochs=num_epochs,
+            train_dataset=train_dataset,
+            train_batch_size=train_batch_size,
+            eval_dataset=eval_dataset,
+            optimizer=optimizer,
+            save_interval_epochs=save_interval_epochs,
+            log_interval_steps=log_interval_steps,
+            save_dir=save_dir,
+            pretrain_weights=None,
+            learning_rate=learning_rate,
+            lr_decay_power=lr_decay_power,
+            early_stop=early_stop,
+            early_stop_patience=early_stop_patience,
+            use_vdl=use_vdl)
+
     def evaluate(self, eval_dataset, batch_size=1, return_details=False):
         """
         Evaluate the model.

+ 4 - 6
dygraph/paddlex/cv/models/utils/det_metrics/coco_utils.py

@@ -20,10 +20,8 @@ import sys
 import copy
 import numpy as np
 import itertools
-
-from .json_results import get_det_res, get_det_poly_res, get_seg_res, get_solov2_segm_res
-from .map_utils import _draw_pr_curve
-
+from ppdet.metrics.map_utils import draw_pr_curve
+from ppdet.metrics.json_results import get_det_res, get_det_poly_res, get_seg_res, get_solov2_segm_res
 import paddlex.utils.logging as logging
 
 
@@ -123,7 +121,7 @@ def cocoapi_eval(anns,
                 (str(nm["name"]), '{:0.3f}'.format(float(ap))))
             pr_array = precisions[0, :, idx, 0, 2]
             recall_array = np.arange(0.0, 1.01, 0.01)
-            _draw_pr_curve(
+            draw_pr_curve(
                 pr_array,
                 recall_array,
                 out_dir=style + '_pr_curve',
@@ -133,7 +131,7 @@ def cocoapi_eval(anns,
         results_flatten = list(itertools.chain(*results_per_category))
         headers = ['category', 'AP'] * (num_columns // 2)
         results_2d = itertools.zip_longest(
-            * [results_flatten[i::num_columns] for i in range(num_columns)])
+            *[results_flatten[i::num_columns] for i in range(num_columns)])
         table_data = [headers]
         table_data += [result for result in results_2d]
         table = AsciiTable(table_data)

+ 0 - 126
dygraph/paddlex/cv/models/utils/det_metrics/json_results.py

@@ -1,126 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import six
-import numpy as np
-
-
-def get_det_res(bboxes, bbox_nums, image_id, label_to_cat_id_map, bias=0):
-    det_res = []
-    k = 0
-    for i in range(len(bbox_nums)):
-        cur_image_id = int(image_id[i][0])
-        det_nums = bbox_nums[i]
-        for j in range(det_nums):
-            dt = bboxes[k]
-            k = k + 1
-            num_id, score, xmin, ymin, xmax, ymax = dt.tolist()
-            if int(num_id) < 0:
-                continue
-            category_id = label_to_cat_id_map[int(num_id)]
-            w = xmax - xmin + bias
-            h = ymax - ymin + bias
-            bbox = [xmin, ymin, w, h]
-            dt_res = {
-                'image_id': cur_image_id,
-                'category_id': category_id,
-                'bbox': bbox,
-                'score': score
-            }
-            det_res.append(dt_res)
-    return det_res
-
-
-def get_det_poly_res(bboxes, bbox_nums, image_id, label_to_cat_id_map, bias=0):
-    det_res = []
-    k = 0
-    for i in range(len(bbox_nums)):
-        cur_image_id = int(image_id[i][0])
-        det_nums = bbox_nums[i]
-        for j in range(det_nums):
-            dt = bboxes[k]
-            k = k + 1
-            num_id, score, x1, y1, x2, y2, x3, y3, x4, y4 = dt.tolist()
-            if int(num_id) < 0:
-                continue
-            category_id = label_to_cat_id_map[int(num_id)]
-            rbox = [x1, y1, x2, y2, x3, y3, x4, y4]
-            dt_res = {
-                'image_id': cur_image_id,
-                'category_id': category_id,
-                'bbox': rbox,
-                'score': score
-            }
-            det_res.append(dt_res)
-    return det_res
-
-
-def get_seg_res(masks, bboxes, mask_nums, image_id, label_to_cat_id_map):
-    import pycocotools.mask as mask_util
-    seg_res = []
-    k = 0
-    for i in range(len(mask_nums)):
-        cur_image_id = int(image_id[i][0])
-        det_nums = mask_nums[i]
-        for j in range(det_nums):
-            mask = masks[k].astype(np.uint8)
-            score = float(bboxes[k][1])
-            label = int(bboxes[k][0])
-            k = k + 1
-            if label == -1:
-                continue
-            cat_id = label_to_cat_id_map[label]
-            rle = mask_util.encode(
-                np.array(
-                    mask[:, :, None], order="F", dtype="uint8"))[0]
-            if six.PY3:
-                if 'counts' in rle:
-                    rle['counts'] = rle['counts'].decode("utf8")
-            sg_res = {
-                'image_id': cur_image_id,
-                'category_id': cat_id,
-                'segmentation': rle,
-                'score': score
-            }
-            seg_res.append(sg_res)
-    return seg_res
-
-
-def get_solov2_segm_res(results, image_id, num_id_to_cat_id_map):
-    import pycocotools.mask as mask_util
-    segm_res = []
-    # for each batch
-    segms = results['segm'].astype(np.uint8)
-    clsid_labels = results['cate_label']
-    clsid_scores = results['cate_score']
-    lengths = segms.shape[0]
-    im_id = int(image_id[0][0])
-    if lengths == 0 or segms is None:
-        return None
-    # for each sample
-    for i in range(lengths - 1):
-        clsid = int(clsid_labels[i])
-        catid = num_id_to_cat_id_map[clsid]
-        score = float(clsid_scores[i])
-        mask = segms[i]
-        segm = mask_util.encode(np.array(mask[:, :, np.newaxis], order='F'))[0]
-        segm['counts'] = segm['counts'].decode('utf8')
-        coco_res = {
-            'image_id': im_id,
-            'category_id': catid,
-            'segmentation': segm,
-            'score': score
-        }
-        segm_res.append(coco_res)
-    return segm_res

+ 0 - 305
dygraph/paddlex/cv/models/utils/det_metrics/map_utils.py

@@ -1,305 +0,0 @@
-# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
-import os
-import sys
-import numpy as np
-import itertools
-import paddlex.utils.logging as logging
-
-__all__ = [
-    '_draw_pr_curve', 'bbox_area', 'jaccard_overlap', 'prune_zero_padding',
-    'DetectionMAP'
-]
-
-
-def _draw_pr_curve(precision,
-                   recall,
-                   iou=0.5,
-                   out_dir='pr_curve',
-                   file_name='precision_recall_curve.jpg'):
-    if not os.path.exists(out_dir):
-        os.makedirs(out_dir)
-    output_path = os.path.join(out_dir, file_name)
-    try:
-        import matplotlib.pyplot as plt
-    except Exception as e:
-        logging.error('Matplotlib not found, plaese install matplotlib.'
-                      'for example: `pip install matplotlib`.')
-        raise e
-    plt.cla()
-    plt.figure('P-R Curve')
-    plt.title('Precision/Recall Curve(IoU={})'.format(iou))
-    plt.xlabel('Recall')
-    plt.ylabel('Precision')
-    plt.grid(True)
-    plt.plot(recall, precision)
-    plt.savefig(output_path)
-
-
-def bbox_area(bbox, is_bbox_normalized):
-    """
-    Calculate area of a bounding box
-    """
-    norm = 1. - float(is_bbox_normalized)
-    width = bbox[2] - bbox[0] + norm
-    height = bbox[3] - bbox[1] + norm
-    return width * height
-
-
-def jaccard_overlap(pred, gt, is_bbox_normalized=False):
-    """
-    Calculate jaccard overlap ratio between two bounding box
-    """
-    if pred[0] >= gt[2] or pred[2] <= gt[0] or \
-        pred[1] >= gt[3] or pred[3] <= gt[1]:
-        return 0.
-    inter_xmin = max(pred[0], gt[0])
-    inter_ymin = max(pred[1], gt[1])
-    inter_xmax = min(pred[2], gt[2])
-    inter_ymax = min(pred[3], gt[3])
-    inter_size = bbox_area([inter_xmin, inter_ymin, inter_xmax, inter_ymax],
-                           is_bbox_normalized)
-    pred_size = bbox_area(pred, is_bbox_normalized)
-    gt_size = bbox_area(gt, is_bbox_normalized)
-    overlap = float(inter_size) / (pred_size + gt_size - inter_size)
-    return overlap
-
-
-def prune_zero_padding(gt_box, gt_label, difficult=None):
-    valid_cnt = 0
-    for i in range(len(gt_box)):
-        if gt_box[i, 0] == 0 and gt_box[i, 1] == 0 and \
-                gt_box[i, 2] == 0 and gt_box[i, 3] == 0:
-            break
-        valid_cnt += 1
-    return (gt_box[:valid_cnt], gt_label[:valid_cnt], difficult[:valid_cnt]
-            if difficult is not None else None)
-
-
-class DetectionMAP(object):
-    """
-    Calculate detection mean average precision.
-    Currently support two types: 11point and integral
-
-    Args:
-        class_num (int): The class number.
-        overlap_thresh (float): The threshold of overlap
-            ratio between prediction bounding box and
-            ground truth bounding box for deciding
-            true/false positive. Default 0.5.
-        map_type (str): Calculation method of mean average
-            precision, currently support '11point' and
-            'integral'. Default '11point'.
-        is_bbox_normalized (bool): Whether bounding boxes
-            is normalized to range[0, 1]. Default False.
-        evaluate_difficult (bool): Whether to evaluate
-            difficult bounding boxes. Default False.
-        catid2name (dict): Mapping between category id and category name.
-        classwise (bool): Whether per-category AP and draw
-            P-R Curve or not.
-    """
-
-    def __init__(self,
-                 class_num,
-                 overlap_thresh=0.5,
-                 map_type='11point',
-                 is_bbox_normalized=False,
-                 evaluate_difficult=False,
-                 catid2name=None,
-                 classwise=False):
-        self.class_num = class_num
-        self.overlap_thresh = overlap_thresh
-        assert map_type in ['11point', 'integral'], \
-                "map_type currently only support '11point' "\
-                "and 'integral'"
-        self.map_type = map_type
-        self.is_bbox_normalized = is_bbox_normalized
-        self.evaluate_difficult = evaluate_difficult
-        self.classwise = classwise
-        self.classes = []
-        for cname in catid2name.values():
-            self.classes.append(cname)
-        self.reset()
-
-    def update(self, bbox, score, label, gt_box, gt_label, difficult=None):
-        """
-        Update metric statics from given prediction and ground
-        truth infomations.
-        """
-        if difficult is None:
-            difficult = np.zeros_like(gt_label)
-
-        # record class gt count
-        for gtl, diff in zip(gt_label, difficult):
-            if self.evaluate_difficult or int(diff) == 0:
-                self.class_gt_counts[int(np.array(gtl))] += 1
-
-        # record class score positive
-        visited = [False] * len(gt_label)
-        for b, s, l in zip(bbox, score, label):
-            xmin, ymin, xmax, ymax = b.tolist()
-            pred = [xmin, ymin, xmax, ymax]
-            max_idx = -1
-            max_overlap = -1.0
-            for i, gl in enumerate(gt_label):
-                if int(gl) == int(l):
-                    overlap = jaccard_overlap(pred, gt_box[i],
-                                              self.is_bbox_normalized)
-                    if overlap > max_overlap:
-                        max_overlap = overlap
-                        max_idx = i
-
-            if max_overlap > self.overlap_thresh:
-                if self.evaluate_difficult or \
-                        int(np.array(difficult[max_idx])) == 0:
-                    if not visited[max_idx]:
-                        self.class_score_poss[int(l)].append([s, 1.0])
-                        visited[max_idx] = True
-                    else:
-                        self.class_score_poss[int(l)].append([s, 0.0])
-            else:
-                self.class_score_poss[int(l)].append([s, 0.0])
-
-    def reset(self):
-        """
-        Reset metric statics
-        """
-        self.class_score_poss = [[] for _ in range(self.class_num)]
-        self.class_gt_counts = [0] * self.class_num
-        self.mAP = None
-
-    def accumulate(self):
-        """
-        Accumulate metric results and calculate mAP
-        """
-        mAP = 0.
-        valid_cnt = 0
-        eval_results = []
-        for score_pos, count in zip(self.class_score_poss,
-                                    self.class_gt_counts):
-            if count == 0: continue
-            if len(score_pos) == 0:
-                valid_cnt += 1
-                continue
-
-            accum_tp_list, accum_fp_list = \
-                    self._get_tp_fp_accum(score_pos)
-            precision = []
-            recall = []
-            for ac_tp, ac_fp in zip(accum_tp_list, accum_fp_list):
-                precision.append(float(ac_tp) / (ac_tp + ac_fp))
-                recall.append(float(ac_tp) / count)
-
-            one_class_ap = 0.0
-            if self.map_type == '11point':
-                max_precisions = [0.] * 11
-                start_idx = len(precision) - 1
-                for j in range(10, -1, -1):
-                    for i in range(start_idx, -1, -1):
-                        if recall[i] < float(j) / 10.:
-                            start_idx = i
-                            if j > 0:
-                                max_precisions[j - 1] = max_precisions[j]
-                                break
-                        else:
-                            if max_precisions[j] < precision[i]:
-                                max_precisions[j] = precision[i]
-                one_class_ap = sum(max_precisions) / 11.
-                mAP += one_class_ap
-                valid_cnt += 1
-            elif self.map_type == 'integral':
-                import math
-                prev_recall = 0.
-                for i in range(len(precision)):
-                    recall_gap = math.fabs(recall[i] - prev_recall)
-                    if recall_gap > 1e-6:
-                        one_class_ap += precision[i] * recall_gap
-                        prev_recall = recall[i]
-                mAP += one_class_ap
-                valid_cnt += 1
-            else:
-                logging.error("Unspported mAP type {}".format(self.map_type))
-                sys.exit(1)
-            eval_results.append({
-                'class': self.classes[valid_cnt - 1],
-                'ap': one_class_ap,
-                'precision': precision,
-                'recall': recall,
-            })
-        self.eval_results = eval_results
-        self.mAP = mAP / float(valid_cnt) if valid_cnt > 0 else mAP
-
-    def get_map(self):
-        """
-        Get mAP result
-        """
-        if self.mAP is None:
-            logging.error("mAP is not calculated.")
-        if self.classwise:
-            # Compute per-category AP and PR curve
-            try:
-                from terminaltables import AsciiTable
-            except Exception as e:
-                logging.error(
-                    'terminaltables not found, plaese install terminaltables. '
-                    'for example: `pip install terminaltables`.')
-                raise e
-            results_per_category = []
-            for eval_result in self.eval_results:
-                results_per_category.append(
-                    (str(eval_result['class']),
-                     '{:0.3f}'.format(float(eval_result['ap']))))
-                _draw_pr_curve(
-                    eval_result['precision'],
-                    eval_result['recall'],
-                    out_dir='voc_pr_curve',
-                    file_name='{}_precision_recall_curve.jpg'.format(
-                        eval_result['class']))
-
-            num_columns = min(6, len(results_per_category) * 2)
-            results_flatten = list(itertools.chain(*results_per_category))
-            headers = ['category', 'AP'] * (num_columns // 2)
-            results_2d = itertools.zip_longest(* [
-                results_flatten[i::num_columns] for i in range(num_columns)
-            ])
-            table_data = [headers]
-            table_data += [result for result in results_2d]
-            table = AsciiTable(table_data)
-            logging.info('Per-category of VOC AP: \n{}'.format(table.table))
-            logging.info(
-                "per-category PR curve has output to voc_pr_curve folder.")
-        return self.mAP
-
-    def _get_tp_fp_accum(self, score_pos_list):
-        """
-        Calculate accumulating true/false positive results from
-        [score, pos] records
-        """
-        sorted_list = sorted(score_pos_list, key=lambda s: s[0], reverse=True)
-        accum_tp = 0
-        accum_fp = 0
-        accum_tp_list = []
-        accum_fp_list = []
-        for (score, pos) in sorted_list:
-            accum_tp += int(pos)
-            accum_tp_list.append(accum_tp)
-            accum_fp += 1 - int(pos)
-            accum_fp_list.append(accum_fp)
-        return accum_tp_list, accum_fp_list

+ 10 - 10
dygraph/paddlex/cv/models/utils/det_metrics/metrics.py

@@ -21,8 +21,7 @@ import sys
 from collections import OrderedDict
 import paddle
 import numpy as np
-
-from .map_utils import prune_zero_padding, DetectionMAP
+from ppdet.metrics.map_utils import prune_zero_padding, DetectionMAP
 from .coco_utils import get_infer_results, cocoapi_eval
 import paddlex.utils.logging as logging
 
@@ -88,22 +87,23 @@ class VOCMetric(Metric):
 
         if bboxes.shape == (1, 1) or bboxes is None:
             return
-        gt_boxes = inputs['gt_bbox'].numpy()
-        gt_labels = inputs['gt_class'].numpy()
-        difficults = inputs['difficult'].numpy(
-        ) if not self.evaluate_difficult else None
+        gt_boxes = inputs['gt_bbox']
+        gt_labels = inputs['gt_class']
+        difficults = inputs['difficult'] if not self.evaluate_difficult \
+            else None
 
         scale_factor = inputs['scale_factor'].numpy(
         ) if 'scale_factor' in inputs else np.ones(
             (gt_boxes.shape[0], 2)).astype('float32')
 
         bbox_idx = 0
-        for i in range(gt_boxes.shape[0]):
-            gt_box = gt_boxes[i]
+        for i in range(len(gt_boxes)):
+            gt_box = gt_boxes[i].numpy()
             h, w = scale_factor[i]
             gt_box = gt_box / np.array([w, h, w, h])
-            gt_label = gt_labels[i]
-            difficult = None if difficults is None else difficults[i]
+            gt_label = gt_labels[i].numpy()
+            difficult = None if difficults is None \
+                else difficults[i].numpy()
             bbox_num = bbox_lengths[i]
             bbox = bboxes[bbox_idx:bbox_idx + bbox_num]
             score = scores[bbox_idx:bbox_idx + bbox_num]

+ 20 - 69
dygraph/paddlex/cv/transforms/batch_operators.py

@@ -13,7 +13,6 @@
 # limitations under the License.
 
 import traceback
-import multiprocessing as mp
 import random
 import numpy as np
 try:
@@ -27,10 +26,10 @@ from paddlex.utils import logging
 
 
 class BatchCompose(Transform):
-    def __init__(self, batch_transforms=None):
+    def __init__(self, batch_transforms=None, collate_batch=True):
         super(BatchCompose, self).__init__()
         self.batch_transforms = batch_transforms
-        self.lock = mp.Lock()
+        self.collate_batch = collate_batch
 
     def __call__(self, samples):
         if self.batch_transforms is not None:
@@ -46,7 +45,23 @@ class BatchCompose(Transform):
 
         samples = _Permute()(samples)
 
-        batch_data = default_collate_fn(samples)
+        extra_key = ['h', 'w', 'flipped']
+        for k in extra_key:
+            for sample in samples:
+                if k in sample:
+                    sample.pop(k)
+
+        if self.collate_batch:
+            batch_data = default_collate_fn(samples)
+        else:
+            batch_data = {}
+            for k in samples[0].keys():
+                tmp_data = []
+                for i in range(len(samples)):
+                    tmp_data.append(samples[i][k])
+                if not 'gt_' in k and not 'is_crowd' in k and not 'difficult' in k:
+                    tmp_data = np.stack(tmp_data, axis=0)
+                batch_data[k] = tmp_data
         return batch_data
 
 
@@ -133,10 +148,9 @@ class BatchRandomResizeByShort(Transform):
 
 
 class _BatchPadding(Transform):
-    def __init__(self, pad_to_stride=0, pad_gt=False):
+    def __init__(self, pad_to_stride=0):
         super(_BatchPadding, self).__init__()
         self.pad_to_stride = pad_to_stride
-        self.pad_gt = pad_gt
 
     def __call__(self, samples):
         coarsest_stride = self.pad_to_stride
@@ -155,69 +169,6 @@ class _BatchPadding(Transform):
             padding_im[:im_h, :im_w, :] = im
             data['image'] = padding_im
 
-        if self.pad_gt:
-            gt_num = []
-            if 'gt_poly' in data and data['gt_poly'] is not None and len(data[
-                    'gt_poly']) > 0:
-                pad_mask = True
-            else:
-                pad_mask = False
-
-            if pad_mask:
-                poly_num = []
-                poly_part_num = []
-                point_num = []
-
-            for data in samples:
-                gt_num.append(data['gt_bbox'].shape[0])
-                if pad_mask:
-                    poly_num.append(len(data['gt_poly']))
-                    for poly in data['gt_poly']:
-                        poly_part_num.append(int(len(poly)))
-                        for p_p in poly:
-                            point_num.append(int(len(p_p) / 2))
-            gt_num_max = max(gt_num)
-
-            for i, data in enumerate(samples):
-                gt_box_data = -np.ones([gt_num_max, 4], dtype=np.float32)
-                gt_class_data = -np.ones([gt_num_max], dtype=np.int32)
-                is_crowd_data = np.ones([gt_num_max], dtype=np.int32)
-
-                if pad_mask:
-                    poly_num_max = max(poly_num)
-                    poly_part_num_max = max(poly_part_num)
-                    point_num_max = max(point_num)
-                    gt_masks_data = -np.ones(
-                        [poly_num_max, poly_part_num_max, point_num_max, 2],
-                        dtype=np.float32)
-
-                gt_num = data['gt_bbox'].shape[0]
-                gt_box_data[0:gt_num, :] = data['gt_bbox']
-                gt_class_data[0:gt_num] = np.squeeze(data['gt_class'])
-                if 'is_crowd' in data:
-                    is_crowd_data[0:gt_num] = np.squeeze(data['is_crowd'])
-                    data['is_crowd'] = is_crowd_data
-
-                data['gt_bbox'] = gt_box_data
-                data['gt_class'] = gt_class_data
-
-                if pad_mask:
-                    for j, poly in enumerate(data['gt_poly']):
-                        for k, p_p in enumerate(poly):
-                            pp_np = np.array(p_p).reshape(-1, 2)
-                            gt_masks_data[j, k, :pp_np.shape[0], :] = pp_np
-                    data['gt_poly'] = gt_masks_data
-
-                if 'gt_score' in data:
-                    gt_score_data = np.zeros([gt_num_max], dtype=np.float32)
-                    gt_score_data[0:gt_num] = data['gt_score'][:gt_num, 0]
-                    data['gt_score'] = gt_score_data
-
-                if 'difficult' in data:
-                    diff_data = np.zeros([gt_num_max], dtype=np.int32)
-                    diff_data[0:gt_num] = data['difficult'][:gt_num, 0]
-                    data['difficult'] = diff_data
-
         return samples
 
 

+ 164 - 0
dygraph/paddlex/cv/transforms/cls_transforms.py

@@ -0,0 +1,164 @@
+# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+function:
+    transforms for classification in PaddleX<2.0
+"""
+
+import math
+import numpy as np
+import cv2
+from PIL import Image
+from .operators import Transform, Compose, RandomHorizontalFlip, RandomVerticalFlip, Normalize, \
+    ResizeByShort, CenterCrop, RandomDistort, ArrangeClassifier
+
+__all__ = [
+    'Compose', 'RandomHorizontalFlip', 'RandomVerticalFlip', 'Normalize',
+    'ResizeByShort', 'CenterCrop', 'RandomDistort', 'ArrangeClassifier',
+    'RandomCrop', 'RandomRotate', 'ComposedClsTransforms'
+]
+
+
+class RandomCrop(Transform):
+    """对图像进行随机剪裁,模型训练时的数据增强操作。
+    1. 根据lower_scale、lower_ratio、upper_ratio计算随机剪裁的高、宽。
+    2. 根据随机剪裁的高、宽随机选取剪裁的起始点。
+    3. 剪裁图像。
+    4. 调整剪裁后的图像的大小到crop_size*crop_size。
+    Args:
+        crop_size (int): 随机裁剪后重新调整的目标边长。默认为224。
+        lower_scale (float): 裁剪面积相对原面积比例的最小限制。默认为0.08。
+        lower_ratio (float): 宽变换比例的最小限制。默认为3. / 4。
+        upper_ratio (float): 宽变换比例的最大限制。默认为4. / 3。
+    """
+
+    def __init__(self,
+                 crop_size=224,
+                 lower_scale=0.08,
+                 lower_ratio=3. / 4,
+                 upper_ratio=4. / 3):
+        super(RandomCrop, self).__init__()
+        self.crop_size = crop_size
+        self.lower_scale = lower_scale
+        self.lower_ratio = lower_ratio
+        self.upper_ratio = upper_ratio
+
+    def apply_im(self, image):
+        scale = [self.lower_scale, 1.0]
+        ratio = [self.lower_ratio, self.upper_ratio]
+        aspect_ratio = math.sqrt(np.random.uniform(*ratio))
+        w = 1. * aspect_ratio
+        h = 1. / aspect_ratio
+        bound = min((float(image.shape[0]) / image.shape[1]) / (h**2),
+                    (float(image.shape[1]) / image.shape[0]) / (w**2))
+        scale_max = min(scale[1], bound)
+        scale_min = min(scale[0], bound)
+        target_area = image.shape[0] * image.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)
+        i = np.random.randint(0, image.shape[0] - h + 1)
+        j = np.random.randint(0, image.shape[1] - w + 1)
+        image = image[i:i + h, j:j + w, :]
+        image = cv2.resize(image, (self.crop_size, self.crop_size))
+        return image
+
+    def apply(self, sample):
+        sample['image'] = self.apply_im(sample['image'])
+        return sample
+
+
+class RandomRotate(Transform):
+    def __init__(self, rotate_range=30, prob=.5):
+        """
+        Randomly rotate image(s) by an arbitrary angle between -rotate_range and rotate_range.
+        Args:
+            rotate_range(int, optional): Range of the rotation angle. Defaults to 30.
+            prob(float, optional): Probability of operating rotation. Defaults to .5.
+        """
+        self.rotate_range = rotate_range
+        self.prob = prob
+
+    def apply_im(self, image, angle):
+        image = image.astype('uint8')
+        image = Image.fromarray(image)
+        image = image.rotate(angle)
+        image = np.asarray(image).astype('float32')
+        return image
+
+    def apply(self, sample):
+        rotate_lower = -self.rotate_range
+        rotate_upper = self.rotate_range
+
+        if np.random.uniform(0, 1) < self.prob:
+            angle = np.random.uniform(rotate_lower, rotate_upper)
+            sample['image'] = self.apply_im(sample['image'], angle)
+
+        return sample
+
+
+class ComposedClsTransforms(Compose):
+    """ 分类模型的基础Transforms流程,具体如下
+        训练阶段:
+        1. 随机从图像中crop一块子图,并resize成crop_size大小
+        2. 将1的输出按0.5的概率随机进行水平翻转
+        3. 将图像进行归一化
+        验证/预测阶段:
+        1. 将图像按比例Resize,使得最小边长度为crop_size[0] * 1.14
+        2. 从图像中心crop出一个大小为crop_size的图像
+        3. 将图像进行归一化
+        Args:
+            mode(str): 图像处理流程所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
+            crop_size(int|list): 输入模型里的图像大小
+            mean(list): 图像均值
+            std(list): 图像方差
+            random_horizontal_flip(bool): 是否以0.5的概率使用随机水平翻转增强,该仅在mode为`train`时生效,默认为True
+    """
+
+    def __init__(self,
+                 mode,
+                 crop_size=[224, 224],
+                 mean=[0.485, 0.456, 0.406],
+                 std=[0.229, 0.224, 0.225],
+                 random_horizontal_flip=True):
+        width = crop_size
+        if isinstance(crop_size, list):
+            if crop_size[0] != crop_size[1]:
+                raise Exception(
+                    "In classifier model, width and height should be equal, please modify your parameter `crop_size`"
+                )
+            width = crop_size[0]
+        if width % 32 != 0:
+            raise Exception(
+                "In classifier model, width and height should be multiple of 32, e.g 224、256、320...., please modify your parameter `crop_size`"
+            )
+
+        if mode == 'train':
+            # 训练时的transforms,包含数据增强
+            transforms = [
+                RandomCrop(crop_size=width), Normalize(
+                    mean=mean, std=std)
+            ]
+            if random_horizontal_flip:
+                transforms.insert(0, RandomHorizontalFlip())
+        else:
+            # 验证/预测时的transforms
+            transforms = [
+                ResizeByShort(short_size=int(width * 1.14)),
+                CenterCrop(crop_size=width), Normalize(
+                    mean=mean, std=std)
+            ]
+
+        super(ComposedClsTransforms, self).__init__(transforms)

+ 152 - 0
dygraph/paddlex/cv/transforms/det_transforms.py

@@ -0,0 +1,152 @@
+# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+function:
+    transforms for detection in PaddleX<2.0
+"""
+
+import numpy as np
+from .operators import Transform, Compose, ResizeByShort, Resize, RandomHorizontalFlip, Normalize
+from .operators import RandomExpand as dy_RandomExpand
+from .operators import RandomCrop as dy_RandomCrop
+from .functions import is_poly, expand_poly, expand_rle
+
+__all__ = [
+    'Compose', 'ResizeByShort', 'Resize', 'RandomHorizontalFlip', 'Normalize',
+    'Padding', 'RandomExpand', 'RandomCrop'
+]
+
+
+class Padding(Transform):
+    """1.将图像的长和宽padding至coarsest_stride的倍数。如输入图像为[300, 640],
+       `coarest_stride`为32,则由于300不为32的倍数,因此在图像最右和最下使用0值
+       进行padding,最终输出图像为[320, 640]。
+       2.或者,将图像的长和宽padding到target_size指定的shape,如输入的图像为[300,640],
+         a. `target_size` = 960,在图像最右和最下使用0值进行padding,最终输出
+            图像为[960, 960]。
+         b. `target_size` = [640, 960],在图像最右和最下使用0值进行padding,最终
+            输出图像为[640, 960]。
+    1. 如果coarsest_stride为1,target_size为None则直接返回。
+    2. 获取图像的高H、宽W。
+    3. 计算填充后图像的高H_new、宽W_new。
+    4. 构建大小为(H_new, W_new, 3)像素值为0的np.ndarray,
+       并将原图的np.ndarray粘贴于左上角。
+    Args:
+        coarsest_stride (int): 填充后的图像长、宽为该参数的倍数,默认为1。
+        target_size (int|list|tuple): 填充后的图像长、宽,默认为None,coarset_stride优先级更高。
+    Raises:
+        TypeError: 形参`target_size`数据类型不满足需求。
+        ValueError: 形参`target_size`为(list|tuple)时,长度不满足需求。
+    """
+
+    def __init__(self, coarsest_stride=1, target_size=None):
+        if target_size is not None:
+            if not isinstance(target_size, int):
+                if not isinstance(target_size, tuple) and not isinstance(
+                        target_size, list):
+                    raise TypeError(
+                        "Padding: Type of target_size must in (int|list|tuple)."
+                    )
+                elif len(target_size) != 2:
+                    raise ValueError(
+                        "Padding: Length of target_size must equal 2.")
+        super(Padding, self).__init__()
+        self.coarsest_stride = coarsest_stride
+        self.target_size = target_size
+
+    def apply_im(self, image, padding_im_h, padding_im_w):
+        im_h, im_w, im_c = image.shape
+        padding_im = np.zeros(
+            (padding_im_h, padding_im_w, im_c), dtype=np.float32)
+        padding_im[:im_h, :im_w, :] = image
+        return padding_im
+
+    def apply_bbox(self, bbox):
+        return bbox
+
+    def apply_segm(self, segms, im_h, im_w, padding_im_h, padding_im_w):
+        expanded_segms = []
+        for segm in segms:
+            if is_poly(segm):
+                # Polygon format
+                expanded_segms.append(
+                    [expand_poly(poly, 0, 0) for poly in segm])
+            else:
+                # RLE format
+                expanded_segms.append(
+                    expand_rle(segm, 0, 0, im_h, im_w, padding_im_h,
+                               padding_im_w))
+        return expanded_segms
+
+    def apply(self, sample):
+        im_h, im_w, im_c = sample['image'].shape[:]
+
+        if isinstance(self.target_size, int):
+            padding_im_h = self.target_size
+            padding_im_w = self.target_size
+        elif isinstance(self.target_size, list) or isinstance(self.target_size,
+                                                              tuple):
+            padding_im_w = self.target_size[0]
+            padding_im_h = self.target_size[1]
+        elif self.coarsest_stride > 0:
+            padding_im_h = int(
+                np.ceil(im_h / self.coarsest_stride) * self.coarsest_stride)
+            padding_im_w = int(
+                np.ceil(im_w / self.coarsest_stride) * self.coarsest_stride)
+        else:
+            raise ValueError(
+                "coarsest_stridei(>1) or target_size(list|int) need setting in Padding transform"
+            )
+        pad_height = padding_im_h - im_h
+        pad_width = padding_im_w - im_w
+        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_w, im_h, padding_im_w, padding_im_h))
+        sample['image'] = self.apply_im(sample['image'], padding_im_h,
+                                        padding_im_w)
+        if 'gt_bbox' in sample and len(sample['gt_bbox']) > 0:
+            sample['gt_bbox'] = self.apply_bbox(sample['gt_bbox'])
+        if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
+            sample['gt_poly'] = self.apply_segm(sample['gt_poly'], im_h, im_w,
+                                                padding_im_h, padding_im_w)
+
+        return sample
+
+
+class RandomExpand(dy_RandomExpand):
+    def __init__(self,
+                 ratio=4.,
+                 prob=0.5,
+                 fill_value=[123.675, 116.28, 103.53]):
+        super(RandomExpand, self).__init__(
+            upper_ratio=ratio, prob=prob, im_padding_value=fill_value)
+
+
+class RandomCrop(dy_RandomCrop):
+    def __init__(self,
+                 aspect_ratio=[.5, 2.],
+                 thresholds=[.0, .1, .3, .5, .7, .9],
+                 scaling=[.3, 1.],
+                 num_attempts=50,
+                 allow_no_crop=True,
+                 cover_all_box=False):
+        super(RandomCrop, self).__init__(
+            crop_size=None,
+            aspect_ratio=aspect_ratio,
+            thresholds=thresholds,
+            scaling=scaling,
+            num_attempts=num_attempts,
+            allow_no_crop=allow_no_crop,
+            cover_all_box=cover_all_box)

+ 536 - 0
dygraph/paddlex/cv/transforms/seg_transforms.py

@@ -0,0 +1,536 @@
+# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+function:
+    transforms for segmentation in PaddleX<2.0
+"""
+
+import numpy as np
+import cv2
+import copy
+from .operators import Transform, Compose, RandomHorizontalFlip, RandomVerticalFlip, Resize, \
+    ResizeByShort, Normalize, RandomDistort, ArrangeSegmenter
+from .operators import Padding as dy_Padding
+
+__all__ = [
+    'Compose', 'RandomHorizontalFlip', 'RandomVerticalFlip', 'Resize',
+    'ResizeByShort', 'Normalize', 'RandomDistort', 'ArrangeSegmenter',
+    'ResizeByLong', 'ResizeRangeScaling', 'ResizeStepScaling', 'Padding',
+    'RandomPaddingCrop', 'RandomBlur', 'RandomRotate', 'RandomScaleAspect',
+    'Clip', 'ComposedSegTransforms'
+]
+
+
+class ResizeByLong(Transform):
+    """对图像长边resize到固定值,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
+    Args:
+        long_size (int): resize后图像的长边大小。
+    """
+
+    def __init__(self, long_size=256):
+        super(ResizeByLong, self).__init__()
+        self.long_size = long_size
+
+    def apply_im(self, image):
+        image = _resize_long(image, long_size=self.long_size)
+        return image
+
+    def apply_mask(self, mask):
+        mask = _resize_long(
+            mask, long_size=self.long_size, interpolation=cv2.INTER_NEAREST)
+        return mask
+
+    def apply(self, sample):
+        sample['image'] = self.apply_im(sample['image'])
+        if 'mask' in sample:
+            sample['mask'] = self.apply_mask(sample['mask'])
+
+        return sample
+
+
+class ResizeRangeScaling(Transform):
+    """对图像长边随机resize到指定范围内,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
+    Args:
+        min_value (int): 图像长边resize后的最小值。默认值400。
+        max_value (int): 图像长边resize后的最大值。默认值600。
+    Raises:
+        ValueError: min_value大于max_value
+    """
+
+    def __init__(self, min_value=400, max_value=600):
+        super(ResizeRangeScaling, self).__init__()
+        if min_value > max_value:
+            raise ValueError('min_value must be less than max_value, '
+                             'but they are {} and {}.'.format(min_value,
+                                                              max_value))
+        self.min_value = min_value
+        self.max_value = max_value
+
+    def apply_im(self, image, random_size):
+        image = _resize_long(image, long_size=random_size)
+        return image
+
+    def apply_mask(self, mask, random_size):
+        mask = _resize_long(
+            mask, long_size=random_size, interpolation=cv2.INTER_NEAREST)
+        return mask
+
+    def apply(self, sample):
+        if self.min_value == self.max_value:
+            random_size = self.max_value
+        else:
+            random_size = int(
+                np.random.uniform(self.min_value, self.max_value) + 0.5)
+        sample['image'] = self.apply_im(sample['image'], random_size)
+        if 'mask' in sample:
+            sample['mask'] = self.apply_mask(sample['mask'], random_size)
+
+        return sample
+
+
+class ResizeStepScaling(Transform):
+    """对图像按照某一个比例resize,这个比例以scale_step_size为步长
+    在[min_scale_factor, max_scale_factor]随机变动。当存在标注图像时,则同步进行处理。
+    Args:
+        min_scale_factor(float), resize最小尺度。默认值0.75。
+        max_scale_factor (float), resize最大尺度。默认值1.25。
+        scale_step_size (float), resize尺度范围间隔。默认值0.25。
+    Raises:
+        ValueError: min_scale_factor大于max_scale_factor
+    """
+
+    def __init__(self,
+                 min_scale_factor=0.75,
+                 max_scale_factor=1.25,
+                 scale_step_size=0.25):
+        if min_scale_factor > max_scale_factor:
+            raise ValueError(
+                'min_scale_factor must be less than max_scale_factor, '
+                'but they are {} and {}.'.format(min_scale_factor,
+                                                 max_scale_factor))
+        super(ResizeStepScaling, self).__init__()
+        self.min_scale_factor = min_scale_factor
+        self.max_scale_factor = max_scale_factor
+        self.scale_step_size = scale_step_size
+
+    def apply_im(self, image, scale_factor):
+        image = cv2.resize(
+            image, (0, 0),
+            fx=scale_factor,
+            fy=scale_factor,
+            interpolation=cv2.INTER_LINEAR)
+        if image.ndim < 3:
+            image = np.expand_dims(image, axis=-1)
+        return image
+
+    def apply_mask(self, mask, scale_factor):
+        mask = cv2.resize(
+            mask, (0, 0),
+            fx=scale_factor,
+            fy=scale_factor,
+            interpolation=cv2.INTER_NEAREST)
+        return mask
+
+    def apply(self, sample):
+        if self.min_scale_factor == self.max_scale_factor:
+            scale_factor = self.min_scale_factor
+
+        elif self.scale_step_size == 0:
+            scale_factor = np.random.uniform(self.min_scale_factor,
+                                             self.max_scale_factor)
+
+        else:
+            num_steps = int((self.max_scale_factor - self.min_scale_factor) /
+                            self.scale_step_size + 1)
+            scale_factors = np.linspace(self.min_scale_factor,
+                                        self.max_scale_factor,
+                                        num_steps).tolist()
+            np.random.shuffle(scale_factors)
+            scale_factor = scale_factors[0]
+
+        sample['image'] = self.apply_im(sample['image'], scale_factor)
+        if 'mask' in sample:
+            sample['mask'] = self.apply_mask(sample['mask'], scale_factor)
+
+        return sample
+
+
+class Padding(dy_Padding):
+    """对图像或标注图像进行padding,padding方向为右和下。
+    根据提供的值对图像或标注图像进行padding操作。
+    Args:
+        target_size (int|list|tuple): padding后图像的大小。
+        im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+        label_padding_value (int): 标注图像padding的值。默认值为255。
+    Raises:
+        TypeError: target_size不是int|list|tuple。
+        ValueError:  target_size为list|tuple时元素个数不等于2。
+    """
+
+    def __init__(self,
+                 target_size,
+                 im_padding_value=[127.5, 127.5, 127.5],
+                 label_padding_value=255):
+        super(Padding, self).__init__(
+            target_size=target_size,
+            pad_mode=0,
+            offsets=None,
+            im_padding_value=im_padding_value,
+            label_padding_value=label_padding_value)
+
+
+class RandomPaddingCrop(Transform):
+    """对图像和标注图进行随机裁剪,当所需要的裁剪尺寸大于原图时,则进行padding操作。
+    Args:
+        crop_size (int|list|tuple): 裁剪图像大小。默认为512。
+        im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+        label_padding_value (int): 标注图像padding的值。默认值为255。
+    Raises:
+        TypeError: crop_size不是int/list/tuple。
+        ValueError:  target_size为list/tuple时元素个数不等于2。
+    """
+
+    def __init__(self,
+                 crop_size=512,
+                 im_padding_value=[127.5, 127.5, 127.5],
+                 label_padding_value=255):
+        if isinstance(crop_size, list) or isinstance(crop_size, tuple):
+            if len(crop_size) != 2:
+                raise ValueError(
+                    'when crop_size is list or tuple, it should include 2 elements, but it is {}'
+                    .format(crop_size))
+        elif not isinstance(crop_size, int):
+            raise TypeError(
+                "Type of crop_size is invalid. Must be Integer or List or tuple, now is {}"
+                .format(type(crop_size)))
+        super(RandomPaddingCrop, self).__init__()
+        self.crop_size = crop_size
+        self.im_padding_value = im_padding_value
+        self.label_padding_value = label_padding_value
+
+    def apply_im(self, image, pad_h, pad_w):
+        im_h, im_w, im_c = image.shape
+        orig_im = copy.deepcopy(image)
+        image = np.zeros(
+            (im_h + pad_h, im_w + pad_w, im_c)).astype(orig_im.dtype)
+        for i in range(im_c):
+            image[:, :, i] = np.pad(orig_im[:, :, i],
+                                    pad_width=((0, pad_h), (0, pad_w)),
+                                    mode='constant',
+                                    constant_values=(self.im_padding_value[i],
+                                                     self.im_padding_value[i]))
+        return image
+
+    def apply_mask(self, mask, pad_h, pad_w):
+        mask = np.pad(mask,
+                      pad_width=((0, pad_h), (0, pad_w)),
+                      mode='constant',
+                      constant_values=(self.label_padding_value,
+                                       self.label_padding_value))
+        return mask
+
+    def apply(self, sample):
+        """
+        Args:
+            im (np.ndarray): 图像np.ndarray数据。
+            im_info (list): 存储图像reisze或padding前的shape信息,如
+                [('resize', [200, 300]), ('padding', [400, 600])]表示
+                图像在过resize前shape为(200, 300), 过padding前shape为
+                (400, 600)
+            label (np.ndarray): 标注图像np.ndarray数据。
+         Returns:
+            tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
+                当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
+                存储与图像相关信息的字典和标注图像np.ndarray数据。
+        """
+        if isinstance(self.crop_size, int):
+            crop_width = self.crop_size
+            crop_height = self.crop_size
+        else:
+            crop_width = self.crop_size[0]
+            crop_height = self.crop_size[1]
+
+        im_h, im_w, im_c = sample['image'].shape
+
+        if im_h == crop_height and im_w == crop_width:
+            return sample
+        else:
+            pad_height = max(crop_height - im_h, 0)
+            pad_width = max(crop_width - im_w, 0)
+            if pad_height > 0 or pad_width > 0:
+                sample['image'] = self.apply_im(sample['image'], pad_height,
+                                                pad_width)
+
+                if 'mask' in sample:
+                    sample['mask'] = self.apply_mask(sample['mask'],
+                                                     pad_height, pad_width)
+
+                im_h = sample['image'].shape[0]
+                im_w = sample['image'].shape[1]
+
+            if crop_height > 0 and crop_width > 0:
+                h_off = np.random.randint(im_h - crop_height + 1)
+                w_off = np.random.randint(im_w - crop_width + 1)
+
+                sample['image'] = sample['image'][h_off:(
+                    crop_height + h_off), w_off:(w_off + crop_width), :]
+                if 'mask' in sample:
+                    sample['mask'] = sample['mask'][h_off:(
+                        crop_height + h_off), w_off:(w_off + crop_width)]
+        return sample
+
+
+class RandomBlur(Transform):
+    """以一定的概率对图像进行高斯模糊。
+    Args:
+        prob (float): 图像模糊概率。默认为0.1。
+    """
+
+    def __init__(self, prob=0.1):
+        super(RandomBlur, self).__init__()
+        self.prob = prob
+
+    def apply_im(self, image, radius):
+        image = cv2.GaussianBlur(image, (radius, radius), 0, 0)
+        return image
+
+    def apply(self, sample):
+        if self.prob <= 0:
+            n = 0
+        elif self.prob >= 1:
+            n = 1
+        else:
+            n = int(1.0 / self.prob)
+        if n > 0:
+            if np.random.randint(0, n) == 0:
+                radius = np.random.randint(3, 10)
+                if radius % 2 != 1:
+                    radius = radius + 1
+                if radius > 9:
+                    radius = 9
+                sample['image'] = self.apply_im(sample['image'], radius)
+
+        return sample
+
+
+class RandomRotate(Transform):
+    """对图像进行随机旋转, 模型训练时的数据增强操作。
+    在旋转区间[-rotate_range, rotate_range]内,对图像进行随机旋转,当存在标注图像时,同步进行,
+    并对旋转后的图像和标注图像进行相应的padding。
+    Args:
+        rotate_range (float): 最大旋转角度。默认为15度。
+        im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
+        label_padding_value (int): 标注图像padding的值。默认为255。
+    """
+
+    def __init__(self,
+                 rotate_range=15,
+                 im_padding_value=[127.5, 127.5, 127.5],
+                 label_padding_value=255):
+        super(RandomRotate, self).__init__()
+        self.rotate_range = rotate_range
+        self.im_padding_value = im_padding_value
+        self.label_padding_value = label_padding_value
+
+    def apply(self, sample):
+        if self.rotate_range > 0:
+            h, w, c = sample['image'].shape
+            do_rotation = np.random.uniform(-self.rotate_range,
+                                            self.rotate_range)
+            pc = (w // 2, h // 2)
+            r = cv2.getRotationMatrix2D(pc, do_rotation, 1.0)
+            cos = np.abs(r[0, 0])
+            sin = np.abs(r[0, 1])
+
+            nw = int((h * sin) + (w * cos))
+            nh = int((h * cos) + (w * sin))
+
+            (cx, cy) = pc
+            r[0, 2] += (nw / 2) - cx
+            r[1, 2] += (nh / 2) - cy
+            dsize = (nw, nh)
+            rot_ims = list()
+            for i in range(0, c, 3):
+                ori_im = sample['image'][:, :, i:i + 3]
+                rot_im = cv2.warpAffine(
+                    ori_im,
+                    r,
+                    dsize=dsize,
+                    flags=cv2.INTER_LINEAR,
+                    borderMode=cv2.BORDER_CONSTANT,
+                    borderValue=self.im_padding_value[i:i + 3])
+                rot_ims.append(rot_im)
+            sample['image'] = np.concatenate(rot_ims, axis=-1)
+            if 'mask' in sample:
+                sample['mask'] = cv2.warpAffine(
+                    sample['mask'],
+                    r,
+                    dsize=dsize,
+                    flags=cv2.INTER_NEAREST,
+                    borderMode=cv2.BORDER_CONSTANT,
+                    borderValue=self.label_padding_value)
+
+        return sample
+
+
+class RandomScaleAspect(Transform):
+    """裁剪并resize回原始尺寸的图像和标注图像。
+    按照一定的面积比和宽高比对图像进行裁剪,并reszie回原始图像的图像,当存在标注图时,同步进行。
+    Args:
+        min_scale (float):裁取图像占原始图像的面积比,取值[0,1],为0时则返回原图。默认为0.5。
+        aspect_ratio (float): 裁取图像的宽高比范围,非负值,为0时返回原图。默认为0.33。
+    """
+
+    def __init__(self, min_scale=0.5, aspect_ratio=0.33):
+        super(RandomScaleAspect, self).__init__()
+        self.min_scale = min_scale
+        self.aspect_ratio = aspect_ratio
+
+    def apply(self, sample):
+        if self.min_scale != 0 and self.aspect_ratio != 0:
+            img_height = sample['image'].shape[0]
+            img_width = sample['image'].shape[1]
+            for i in range(0, 10):
+                area = img_height * img_width
+                target_area = area * np.random.uniform(self.min_scale, 1.0)
+                aspectRatio = np.random.uniform(self.aspect_ratio,
+                                                1.0 / self.aspect_ratio)
+
+                dw = int(np.sqrt(target_area * 1.0 * aspectRatio))
+                dh = int(np.sqrt(target_area * 1.0 / aspectRatio))
+                if (np.random.randint(10) < 5):
+                    tmp = dw
+                    dw = dh
+                    dh = tmp
+
+                if (dh < img_height and dw < img_width):
+                    h1 = np.random.randint(0, img_height - dh)
+                    w1 = np.random.randint(0, img_width - dw)
+
+                    sample['image'] = sample['image'][h1:(h1 + dh), w1:(w1 + dw
+                                                                        ), :]
+                    sample['image'] = cv2.resize(
+                        sample['image'], (img_width, img_height),
+                        interpolation=cv2.INTER_LINEAR)
+                    if sample['image'].ndim < 3:
+                        sample['image'] = np.expand_dims(
+                            sample['image'], axis=-1)
+
+                    if 'mask' in sample:
+                        sample['mask'] = sample['mask'][h1:(h1 + dh), w1:(w1 +
+                                                                          dw)]
+                        sample['mask'] = cv2.resize(
+                            sample['mask'], (img_width, img_height),
+                            interpolation=cv2.INTER_NEAREST)
+                    break
+        return sample
+
+
+class Clip(Transform):
+    """
+    对图像上超出一定范围的数据进行截断。
+    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]):
+        if not (isinstance(min_val, list) and isinstance(max_val, list)):
+            raise ValueError("{}: input type is invalid.".format(self))
+        super(Clip, self).__init__()
+        self.min_val = min_val
+        self.max_val = max_val
+
+    def apply_im(self, image):
+        for k in range(image.shape[2]):
+            np.clip(
+                image[:, :, k],
+                self.min_val[k],
+                self.max_val[k],
+                out=image[:, :, k])
+        return image
+
+    def apply(self, sample):
+        sample['image'] = self.apply_im(sample['image'])
+        return sample
+
+
+class ComposedSegTransforms(Compose):
+    """ 语义分割模型(UNet/DeepLabv3p)的图像处理流程,具体如下
+        训练阶段:
+        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. 将图像的最长边resize至(min_max_size[0] + min_max_size[1])//2, 短边按比例resize。若min_max_size为None,则跳过此步骤
+        2. 图像归一化
+        Args:
+            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,
+                 min_max_size=[400, 600],
+                 train_crop_size=[512, 512],
+                 mean=[0.5, 0.5, 0.5],
+                 std=[0.5, 0.5, 0.5],
+                 random_horizontal_flip=True):
+        if mode == 'train':
+            # 训练时的transforms,包含数据增强
+            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
+            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)
+
+
+def _resize_long(im, long_size=224, interpolation=cv2.INTER_LINEAR):
+    value = max(im.shape[0], im.shape[1])
+    scale = float(long_size) / float(value)
+    resized_width = int(round(im.shape[1] * scale))
+    resized_height = int(round(im.shape[0] * scale))
+
+    im_dims = im.ndim
+    im = cv2.resize(
+        im, (resized_width, resized_height), interpolation=interpolation)
+    if im_dims >= 3 and im.ndim < 3:
+        im = np.expand_dims(im, axis=-1)
+    return im

+ 175 - 9
dygraph/paddlex/det.py

@@ -11,16 +11,182 @@
 # 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 logging
 
-import sys
-message = 'Your running script needs PaddleX<2.0.0, please refer to {} to solve this issue.'.format(
-    'https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7'
-)
+from . import cv
+from .cv.models.utils.visualize import visualize_detection, draw_pr_curve
+from paddlex.cv.transforms import det_transforms
 
+transforms = det_transforms
 
-def __getattr__(attr):
-    if attr == 'transforms':
+visualize = visualize_detection
+draw_pr_curve = draw_pr_curve
 
-        print("\033[1;31;40m{}\033[0m".format(message).encode("utf-8")
-              .decode("latin1"))
-        sys.exit(-1)
+
+class FasterRCNN(cv.models.FasterRCNN):
+    def __init__(self,
+                 num_classes=81,
+                 backbone='ResNet50',
+                 with_fpn=True,
+                 aspect_ratios=[0.5, 1.0, 2.0],
+                 anchor_sizes=[32, 64, 128, 256, 512],
+                 with_dcn=None,
+                 rpn_cls_loss=None,
+                 rpn_focal_loss_alpha=None,
+                 rpn_focal_loss_gamma=None,
+                 rcnn_bbox_loss=None,
+                 rcnn_nms=None,
+                 keep_top_k=100,
+                 nms_threshold=0.5,
+                 score_threshold=0.05,
+                 softnms_sigma=None,
+                 bbox_assigner=None,
+                 fpn_num_channels=256,
+                 input_channel=None,
+                 rpn_batch_size_per_im=256,
+                 rpn_fg_fraction=0.5,
+                 test_pre_nms_top_n=None,
+                 test_post_nms_top_n=1000):
+        if with_dcn is not None:
+            logging.warning(
+                "`with_dcn` is deprecated in PaddleX 2.0 and won't take effect. Defaults to False."
+            )
+        if rpn_cls_loss is not None:
+            logging.warning(
+                "`rpn_cls_loss` is deprecated in PaddleX 2.0 and won't take effect. "
+                "Defaults to 'SigmoidCrossEntropy'.")
+        if rpn_focal_loss_alpha is not None or rpn_focal_loss_gamma is not None:
+            logging.warning(
+                "Focal loss is deprecated in PaddleX 2.0."
+                " `rpn_focal_loss_alpha` and `rpn_focal_loss_gamma` won't take effect."
+            )
+        if rcnn_bbox_loss is not None:
+            logging.warning(
+                "`rcnn_bbox_loss` is deprecated in PaddleX 2.0 and won't take effect. "
+                "Defaults to 'SmoothL1Loss'")
+        if rcnn_nms is not None:
+            logging.warning(
+                "MultiClassSoftNMS is deprecated in PaddleX 2.0. "
+                "`rcnn_nms` and `softnms_sigma` won't take effect. MultiClassNMS will be used by default"
+            )
+        if bbox_assigner is not None:
+            logging.warning(
+                "`bbox_assigner` is deprecated in PaddleX 2.0 and won't take effect. "
+                "Defaults to 'BBoxAssigner'")
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(FasterRCNN, self).__init__(
+            num_classes=num_classes - 1,
+            backbone=backbone,
+            with_fpn=with_fpn,
+            aspect_ratios=aspect_ratios,
+            anchor_sizes=anchor_sizes,
+            keep_top_k=keep_top_k,
+            nms_threshold=nms_threshold,
+            score_threshold=score_threshold,
+            fpn_num_channels=fpn_num_channels,
+            rpn_batch_size_per_im=rpn_batch_size_per_im,
+            rpn_fg_fraction=rpn_fg_fraction,
+            test_pre_nms_top_n=test_pre_nms_top_n,
+            test_post_nms_top_n=test_post_nms_top_n)
+
+
+class YOLOv3(cv.models.YOLOv3):
+    def __init__(self,
+                 num_classes=80,
+                 backbone='MobileNetV1',
+                 anchors=None,
+                 anchor_masks=None,
+                 ignore_threshold=0.7,
+                 nms_score_threshold=0.01,
+                 nms_topk=1000,
+                 nms_keep_topk=100,
+                 nms_iou_threshold=0.45,
+                 label_smooth=False,
+                 train_random_shapes=None,
+                 input_channel=None):
+        if train_random_shapes is not None:
+            logging.warning(
+                "`train_random_shapes` is deprecated in PaddleX 2.0 and won't take effect. "
+                "To apply multi_scale training, please refer to paddlex.transforms.BatchRandomResize: "
+                "'https://github.com/PaddlePaddle/PaddleX/blob/develop/dygraph/paddlex/cv/transforms/batch_operators.py#L53'"
+            )
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(YOLOv3, self).__init__(
+            num_classes=num_classes,
+            backbone=backbone,
+            anchors=anchors,
+            anchor_masks=anchor_masks,
+            ignore_threshold=ignore_threshold,
+            nms_score_threshold=nms_score_threshold,
+            nms_topk=nms_topk,
+            nms_keep_topk=nms_keep_topk,
+            nms_iou_threshold=nms_iou_threshold,
+            label_smooth=label_smooth)
+
+
+class PPYOLO(cv.models.PPYOLO):
+    def __init__(
+            self,
+            num_classes=80,
+            backbone='ResNet50_vd_ssld',
+            with_dcn_v2=None,
+            # YOLO Head
+            anchors=None,
+            anchor_masks=None,
+            use_coord_conv=True,
+            use_iou_aware=True,
+            use_spp=True,
+            use_drop_block=True,
+            scale_x_y=1.05,
+            # PPYOLO Loss
+            ignore_threshold=0.7,
+            label_smooth=False,
+            use_iou_loss=True,
+            # NMS
+            use_matrix_nms=True,
+            nms_score_threshold=0.01,
+            nms_topk=1000,
+            nms_keep_topk=100,
+            nms_iou_threshold=0.45,
+            train_random_shapes=None,
+            input_channel=None):
+        if with_dcn_v2 is not None:
+            logging.warning(
+                "`with_dcn_v2` is deprecated in PaddleX 2.0 and will not take effect. "
+                "To use backbone with deformable convolutional networks, "
+                "please specify in `backbone_name`. "
+                "Currently the only backbone with dcn is 'ResNet50_vd_dcn'.")
+        if train_random_shapes is not None:
+            logging.warning(
+                "`train_random_shapes` is deprecated in PaddleX 2.0 and won't take effect. "
+                "To apply multi_scale training, please refer to paddlex.transforms.BatchRandomResize: "
+                "'https://github.com/PaddlePaddle/PaddleX/blob/develop/dygraph/paddlex/cv/transforms/batch_operators.py#L53'"
+            )
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+        super(PPYOLO, self).__init__(
+            num_classes=num_classes,
+            backbone=backbone,
+            anchors=anchors,
+            anchor_masks=anchor_masks,
+            use_coord_conv=use_coord_conv,
+            use_iou_aware=use_iou_aware,
+            use_spp=use_spp,
+            use_drop_block=use_drop_block,
+            scale_x_y=scale_x_y,
+            ignore_threshold=ignore_threshold,
+            label_smooth=label_smooth,
+            use_iou_loss=use_iou_loss,
+            use_matrix_nms=use_matrix_nms,
+            nms_score_threshold=nms_score_threshold,
+            nms_topk=nms_topk,
+            nms_keep_topk=nms_keep_topk,
+            nms_iou_threshold=nms_iou_threshold)

+ 214 - 9
dygraph/paddlex/seg.py

@@ -11,16 +11,221 @@
 # 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 logging
 
-import sys
-message = 'Your running script needs PaddleX<2.0.0, please refer to {} to solve this issue.'.format(
-    'https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7'
-)
+from . import cv
+from .cv.models.utils.visualize import visualize_segmentation
+from paddlex.cv.transforms import seg_transforms
 
+transforms = seg_transforms
 
-def __getattr__(attr):
-    if attr == 'transforms':
+visualize = visualize_segmentation
 
-        print("\033[1;31;40m{}\033[0m".format(message).encode("utf-8")
-              .decode("latin1"))
-        sys.exit(-1)
+
+class UNet(cv.models.UNet):
+    def __init__(self,
+                 num_classes=2,
+                 upsample_mode='bilinear',
+                 use_bce_loss=False,
+                 use_dice_loss=False,
+                 class_weight=None,
+                 ignore_index=None,
+                 input_channel=None):
+        if num_classes > 2 and (use_bce_loss or use_dice_loss):
+            raise ValueError(
+                "dice loss and bce loss is only applicable to binary classification"
+            )
+        elif num_classes == 2:
+            if use_bce_loss and use_dice_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1), ('DiceLoss', 1)]
+            elif use_bce_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1)]
+            elif use_dice_loss:
+                use_mixed_loss = [('DiceLoss', 1)]
+            else:
+                use_mixed_loss = False
+        else:
+            use_mixed_loss = False
+
+        if class_weight is not None:
+            logging.warning(
+                "`class_weight` is not supported in PaddleX 2.0 currently and is forcibly set to None."
+            )
+        if ignore_index is not None:
+            logging.warning(
+                "`ignore_index` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 255."
+            )
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+
+        if upsample_mode == 'bilinear':
+            use_deconv = False
+        else:
+            use_deconv = True
+        super(UNet, self).__init__(
+            num_classes=num_classes,
+            use_mixed_loss=use_mixed_loss,
+            use_deconv=use_deconv)
+
+
+class DeepLabv3p(cv.models.DeepLabV3P):
+    def __init__(self,
+                 num_classes=2,
+                 backbone='ResNet50_vd',
+                 output_stride=8,
+                 aspp_with_sep_conv=None,
+                 decoder_use_sep_conv=None,
+                 encoder_with_aspp=None,
+                 enable_decoder=None,
+                 use_bce_loss=False,
+                 use_dice_loss=False,
+                 class_weight=None,
+                 ignore_index=None,
+                 pooling_crop_size=None,
+                 input_channel=None):
+        if num_classes > 2 and (use_bce_loss or use_dice_loss):
+            raise ValueError(
+                "dice loss and bce loss is only applicable to binary classification"
+            )
+        elif num_classes == 2:
+            if use_bce_loss and use_dice_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1), ('DiceLoss', 1)]
+            elif use_bce_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1)]
+            elif use_dice_loss:
+                use_mixed_loss = [('DiceLoss', 1)]
+            else:
+                use_mixed_loss = False
+        else:
+            use_mixed_loss = False
+
+        if aspp_with_sep_conv is not None:
+            logging.warning(
+                "`aspp_with_sep_conv` is deprecated in PaddleX 2.0 and will not take effect. "
+                "Defaults to True")
+        if decoder_use_sep_conv is not None:
+            logging.warning(
+                "`decoder_use_sep_conv` is deprecated in PaddleX 2.0 and will not take effect. "
+                "Defaults to True")
+        if encoder_with_aspp is not None:
+            logging.warning(
+                "`encoder_with_aspp` is deprecated in PaddleX 2.0 and will not take effect. "
+                "Defaults to True")
+        if enable_decoder is not None:
+            logging.warning(
+                "`enable_decoder` is deprecated in PaddleX 2.0 and will not take effect. "
+                "Defaults to True")
+        if class_weight is not None:
+            logging.warning(
+                "`class_weight` is not supported in PaddleX 2.0 currently and is forcibly set to None."
+            )
+        if ignore_index is not None:
+            logging.warning(
+                "`ignore_index` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 255."
+            )
+        if pooling_crop_size is not None:
+            logging.warning(
+                "Backbone 'MobileNetV3_large_x1_0_ssld' is currently not supported in PaddleX 2.0. "
+                "`pooling_crop_size` will not take effect. Defaults to None")
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+
+        super(DeepLabv3p, self).__init__(
+            num_classes=num_classes,
+            backbone=backbone,
+            use_mixed_loss=use_mixed_loss,
+            output_stride=output_stride)
+
+
+class HRNet(cv.models.HRNet):
+    def __init__(self,
+                 num_classes=2,
+                 width=18,
+                 use_bce_loss=False,
+                 use_dice_loss=False,
+                 class_weight=None,
+                 ignore_index=None,
+                 input_channel=None):
+        if num_classes > 2 and (use_bce_loss or use_dice_loss):
+            raise ValueError(
+                "dice loss and bce loss is only applicable to binary classification"
+            )
+        elif num_classes == 2:
+            if use_bce_loss and use_dice_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1), ('DiceLoss', 1)]
+            elif use_bce_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1)]
+            elif use_dice_loss:
+                use_mixed_loss = [('DiceLoss', 1)]
+            else:
+                use_mixed_loss = False
+        else:
+            use_mixed_loss = False
+
+        if class_weight is not None:
+            logging.warning(
+                "`class_weight` is not supported in PaddleX 2.0 currently and is forcibly set to None."
+            )
+        if ignore_index is not None:
+            logging.warning(
+                "`ignore_index` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 255."
+            )
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+
+        super(HRNet, self).__init__(
+            num_classes=num_classes,
+            width=width,
+            use_mixed_loss=use_mixed_loss)
+
+
+class FastSCNN(cv.models.FastSCNN):
+    def __init__(self,
+                 num_classes=2,
+                 use_bce_loss=False,
+                 use_dice_loss=False,
+                 class_weight=None,
+                 ignore_index=255,
+                 multi_loss_weight=None,
+                 input_channel=3):
+        if num_classes > 2 and (use_bce_loss or use_dice_loss):
+            raise ValueError(
+                "dice loss and bce loss is only applicable to binary classification"
+            )
+        elif num_classes == 2:
+            if use_bce_loss and use_dice_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1), ('DiceLoss', 1)]
+            elif use_bce_loss:
+                use_mixed_loss = [('CrossEntropyLoss', 1)]
+            elif use_dice_loss:
+                use_mixed_loss = [('DiceLoss', 1)]
+            else:
+                use_mixed_loss = False
+        else:
+            use_mixed_loss = False
+
+        if class_weight is not None:
+            logging.warning(
+                "`class_weight` is not supported in PaddleX 2.0 currently and is forcibly set to None."
+            )
+        if ignore_index is not None:
+            logging.warning(
+                "`ignore_index` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 255."
+            )
+        if multi_loss_weight is not None:
+            logging.warning(
+                "`multi_loss_weight` is deprecated in PaddleX 2.0 and will not take effect. "
+                "Defaults to [1.0, 0.4]")
+        if input_channel is not None:
+            logging.warning(
+                "`input_channel` is deprecated in PaddleX 2.0 and won't take effect. Defaults to 3."
+            )
+
+        super(FastSCNN, self).__init__(
+            num_classes=num_classes, use_mixed_loss=use_mixed_loss)

+ 1 - 1
dygraph/paddlex/utils/__init__.py

@@ -19,6 +19,6 @@ from .utils import (seconds_to_hms, get_encoding, get_single_card_bs, dict2str,
                     DisablePrint)
 from .checkpoint import get_pretrain_weights, load_pretrain_weights
 from .env import get_environ_info, get_num_workers, init_parallel_env
-from .download import download_and_decompress
+from .download import download_and_decompress, decompress
 from .stats import SmoothedValue, TrainingStats
 from .shm import _get_shared_memory_size_in_M

+ 11 - 0
dygraph/paddlex/utils/checkpoint.py

@@ -43,6 +43,7 @@ det_pretrain_weights_dict = {
     'FasterRCNN_ResNet101_fpn': ['COCO', 'IMAGENET'],
     'FasterRCNN_ResNet101_vd_fpn': ['COCO', 'IMAGENET'],
     'FasterRCNN_ResNet50_vd_ssld_fpn': ['COCO', 'IMAGENET'],
+    'FasterRCNN_HRNet_W18_fpn': ['COCO', 'IMAGENET'],
     'PPYOLO_ResNet50_vd_dcn': ['COCO', 'IMAGENET'],
     'PPYOLO_ResNet18_vd': ['COCO', 'IMAGENET'],
     'PPYOLO_MobileNetV3_large': ['COCO', 'IMAGENET'],
@@ -124,12 +125,16 @@ imagenet_weights = {
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV2_x2_0_pretrained.pdparams',
     'MobileNetV3_small_x0_35_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x0_35_pretrained.pdparams',
+    'MobileNetV3_small_x0_35_ssld_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x0_35_ssld_pretrained.pdparams',
     'MobileNetV3_small_x0_5_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x0_5_pretrained.pdparams',
     'MobileNetV3_small_x0_75_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x0_75_pretrained.pdparams',
     'MobileNetV3_small_x1_0_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x1_0_pretrained.pdparams',
+    'MobileNetV3_small_x1_0_ssld_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x1_0_ssld_pretrained.pdparams',
     'MobileNetV3_small_x1_25_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_small_x1_25_pretrained.pdparams',
     'MobileNetV3_large_x0_35_IMAGENET':
@@ -142,6 +147,8 @@ imagenet_weights = {
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_large_x1_0_pretrained.pdparams',
     'MobileNetV3_large_x1_25_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_large_x1_25_pretrained.pdparams',
+    'MobileNetV3_large_x1_0_ssld':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_large_x1_0_ssld_pretrained.pdparams',
     'AlexNet_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/AlexNet_pretrained.pdparams',
     'DarkNet53_IMAGENET':
@@ -206,6 +213,8 @@ imagenet_weights = {
     'https://paddledet.bj.bcebos.com/models/pretrained/ResNet101_pretrained.pdparams',
     'FasterRCNN_ResNet101_vd_fpn_IMAGENET':
     'https://paddledet.bj.bcebos.com/models/pretrained/ResNet101_vd_pretrained.pdparams',
+    'FasterRCNN_HRNet_W18_fpn_IMAGENET':
+    'https://paddledet.bj.bcebos.com/models/pretrained/HRNet_W18_C_pretrained.pdparams',
     'YOLOv3_ResNet50_vd_dcn_IMAGENET':
     'https://paddledet.bj.bcebos.com/models/pretrained/ResNet50_vd_ssld_pretrained.pdparams',
     'YOLOv3_ResNet34_IMAGENET':
@@ -298,6 +307,8 @@ coco_weights = {
     'https://paddledet.bj.bcebos.com/models/faster_rcnn_r101_fpn_2x_coco.pdparams',
     'FasterRCNN_ResNet101_vd_fpn_COCO':
     'https://paddledet.bj.bcebos.com/models/faster_rcnn_r101_vd_fpn_1x_coco.pdparams',
+    'FasterRCNN_HRNet_W18_fpn_COCO':
+    'https://paddledet.bj.bcebos.com/models/faster_rcnn_hrnetv2p_w18_2x_coco.pdparams',
     'PPYOLO_ResNet50_vd_dcn_COCO':
     'https://paddledet.bj.bcebos.com/models/ppyolo_r50vd_dcn_2x_coco.pdparams',
     'PPYOLO_ResNet18_vd_COCO':

+ 5 - 5
dygraph/requirements.txt

@@ -1,3 +1,8 @@
+-r PaddleClas/requirements.txt
+-r ./PaddleSeg/requirements.txt
+./PaddleSeg
+-r ./PaddleDetection/requirements.txt
+./PaddleDetection
 tqdm
 scipy
 colorama
@@ -8,8 +13,3 @@ paddleslim == 2.1.0
 shapely
 paddlepaddle-gpu==2.1.0
 opencv-python
--r PaddleClas/requirements.txt
--r ./PaddleSeg/requirements.txt
-./PaddleSeg
--r ./PaddleDetection/requirements.txt
-./PaddleDetection

+ 43 - 0
dygraph/tutorials/slim/quantize/image_classification/mobilenetv2_qat.py

@@ -0,0 +1,43 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压蔬菜分类数据集
+veg_dataset = 'https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz'
+pdx.utils.download_and_decompress(veg_dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose(
+    [T.RandomCrop(crop_size=224), T.RandomHorizontalFlip(), T.Normalize()])
+
+eval_transforms = T.Compose([
+    T.ResizeByShort(short_size=256), T.CenterCrop(crop_size=224), T.Normalize()
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/imagenet.py#L21
+train_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/train_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/val_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=eval_transforms)
+
+# 加载模型
+model = pdx.load_model('output/mobilenet_v2/best_model')
+
+# 在线量化
+model.quant_aware_train(
+    num_epochs=5,
+    train_dataset=train_dataset,
+    train_batch_size=32,
+    eval_dataset=eval_dataset,
+    learning_rate=0.000025,
+    save_dir='output/mobilenet_v2/quant',
+    use_vdl=True)

+ 47 - 0
dygraph/tutorials/slim/quantize/image_classification/mobilenetv2_train.py

@@ -0,0 +1,47 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压蔬菜分类数据集
+veg_dataset = 'https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz'
+pdx.utils.download_and_decompress(veg_dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose(
+    [T.RandomCrop(crop_size=224), T.RandomHorizontalFlip(), T.Normalize()])
+
+eval_transforms = T.Compose([
+    T.ResizeByShort(short_size=256), T.CenterCrop(crop_size=224), T.Normalize()
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/imagenet.py#L21
+train_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/train_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/val_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=eval_transforms)
+
+# 初始化模型,并进行训练
+# 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#visualdl可视化训练指标
+num_classes = len(train_dataset.labels)
+model = pdx.models.MobileNetV3_large(num_classes=num_classes)
+
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/95c53dec89ab0f3769330fa445c6d9213986ca5f/paddlex/cv/models/classifier.py#L153
+# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
+model.train(
+    num_epochs=10,
+    train_dataset=train_dataset,
+    train_batch_size=32,
+    eval_dataset=eval_dataset,
+    lr_decay_epochs=[4, 6, 8],
+    learning_rate=0.025,
+    save_dir='output/mobilenet_v2',
+    use_vdl=True)

+ 55 - 0
dygraph/tutorials/slim/quantize/object_detection/yolov3_qat.py

@@ -0,0 +1,55 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压昆虫检测数据集
+dataset = 'https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz'
+pdx.utils.download_and_decompress(dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose([
+    T.MixupImage(mixup_epoch=250), T.RandomDistort(),
+    T.RandomExpand(im_padding_value=[123.675, 116.28, 103.53]), T.RandomCrop(),
+    T.RandomHorizontalFlip(), T.BatchRandomResize(
+        target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608],
+        interp='RANDOM'), T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+eval_transforms = T.Compose([
+    T.Resize(
+        608, interp='CUBIC'), T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/voc.py#L29
+train_dataset = pdx.datasets.VOCDetection(
+    data_dir='insect_det',
+    file_list='insect_det/train_list.txt',
+    label_list='insect_det/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.VOCDetection(
+    data_dir='insect_det',
+    file_list='insect_det/val_list.txt',
+    label_list='insect_det/labels.txt',
+    transforms=eval_transforms,
+    shuffle=False)
+
+# 加载模型
+model = pdx.load_model('output/yolov3_darknet53/best_model')
+
+# 在线量化
+model.quant_aware_train(
+    num_epochs=50,
+    train_dataset=train_dataset,
+    train_batch_size=8,
+    eval_dataset=eval_dataset,
+    learning_rate=0.0001 / 8,
+    warmup_steps=100,
+    warmup_start_lr=0.0,
+    save_interval_epochs=1,
+    lr_decay_epochs=[30, 45],
+    save_dir='output/yolov3_darknet53/quant')

+ 58 - 0
dygraph/tutorials/slim/quantize/object_detection/yolov3_train.py

@@ -0,0 +1,58 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压昆虫检测数据集
+dataset = 'https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz'
+pdx.utils.download_and_decompress(dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose([
+    T.MixupImage(mixup_epoch=250), T.RandomDistort(),
+    T.RandomExpand(im_padding_value=[123.675, 116.28, 103.53]), T.RandomCrop(),
+    T.RandomHorizontalFlip(), T.BatchRandomResize(
+        target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608],
+        interp='RANDOM'), T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+eval_transforms = T.Compose([
+    T.Resize(
+        608, interp='CUBIC'), T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/voc.py#L29
+train_dataset = pdx.datasets.VOCDetection(
+    data_dir='insect_det',
+    file_list='insect_det/train_list.txt',
+    label_list='insect_det/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.VOCDetection(
+    data_dir='insect_det',
+    file_list='insect_det/val_list.txt',
+    label_list='insect_det/labels.txt',
+    transforms=eval_transforms,
+    shuffle=False)
+
+# 初始化模型,并进行训练
+# 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#visualdl可视化训练指标
+num_classes = len(train_dataset.labels)
+model = pdx.models.YOLOv3(num_classes=num_classes, backbone='DarkNet53')
+
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/models/detector.py#L154
+# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
+model.train(
+    num_epochs=270,
+    train_dataset=train_dataset,
+    train_batch_size=8,
+    eval_dataset=eval_dataset,
+    learning_rate=0.001 / 8,
+    warmup_steps=1000,
+    warmup_start_lr=0.0,
+    save_interval_epochs=5,
+    lr_decay_epochs=[216, 243],
+    save_dir='output/yolov3_darknet53')

+ 49 - 0
dygraph/tutorials/slim/quantize/semantic_segmentation/unet_qat.py

@@ -0,0 +1,49 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压视盘分割数据集
+optic_dataset = 'https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz'
+pdx.utils.download_and_decompress(optic_dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose([
+    T.Resize(target_size=512),
+    T.RandomHorizontalFlip(),
+    T.Normalize(
+        mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
+])
+
+eval_transforms = T.Compose([
+    T.Resize(target_size=512),
+    T.Normalize(
+        mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/seg_dataset.py#L22
+train_dataset = pdx.datasets.SegDataset(
+    data_dir='optic_disc_seg',
+    file_list='optic_disc_seg/train_list.txt',
+    label_list='optic_disc_seg/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.SegDataset(
+    data_dir='optic_disc_seg',
+    file_list='optic_disc_seg/val_list.txt',
+    label_list='optic_disc_seg/labels.txt',
+    transforms=eval_transforms,
+    shuffle=False)
+
+# 加载模型
+model = pdx.load_model('output/unet/best_model')
+
+# 在线量化
+model.quant_aware_train(
+    num_epochs=5,
+    train_dataset=train_dataset,
+    train_batch_size=4,
+    eval_dataset=eval_dataset,
+    learning_rate=0.001,
+    save_dir='output/unet/quant')

+ 52 - 0
dygraph/tutorials/slim/quantize/semantic_segmentation/unet_train.py

@@ -0,0 +1,52 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压视盘分割数据集
+optic_dataset = 'https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz'
+pdx.utils.download_and_decompress(optic_dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/transforms/operators.py
+train_transforms = T.Compose([
+    T.Resize(target_size=512),
+    T.RandomHorizontalFlip(),
+    T.Normalize(
+        mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
+])
+
+eval_transforms = T.Compose([
+    T.Resize(target_size=512),
+    T.Normalize(
+        mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/datasets/seg_dataset.py#L22
+train_dataset = pdx.datasets.SegDataset(
+    data_dir='optic_disc_seg',
+    file_list='optic_disc_seg/train_list.txt',
+    label_list='optic_disc_seg/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.SegDataset(
+    data_dir='optic_disc_seg',
+    file_list='optic_disc_seg/val_list.txt',
+    label_list='optic_disc_seg/labels.txt',
+    transforms=eval_transforms,
+    shuffle=False)
+
+# 初始化模型,并进行训练
+# 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#visualdl可视化训练指标
+num_classes = len(train_dataset.labels)
+model = pdx.models.UNet(num_classes=num_classes)
+
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/models/segmenter.py#L150
+# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
+model.train(
+    num_epochs=10,
+    train_dataset=train_dataset,
+    train_batch_size=4,
+    eval_dataset=eval_dataset,
+    learning_rate=0.01,
+    save_dir='output/unet')

+ 0 - 1
dygraph/tutorials/train/README.md

@@ -12,7 +12,6 @@
 |image_classification/darknet53.py | 图像分类DarkNet53 | 蔬菜分类 |
 |image_classification/xception41.py | 图像分类Xception41 | 蔬菜分类 |
 |image_classification/densenet121.py | 图像分类DenseNet121 | 蔬菜分类 |
-|object_detection/faster_rcnn_r34_fpn.py | 目标检测FasterRCNN | 昆虫检测 |
 |object_detection/faster_rcnn_r50_fpn.py | 目标检测FasterRCNN | 昆虫检测 |
 |object_detection/ppyolo.py | 目标检测PPYOLO | 昆虫检测 |
 |object_detection/ppyolotiny.py | 目标检测PPYOLOTiny | 昆虫检测 |

+ 1 - 1
dygraph/tutorials/train/image_classification/alexnet.py

@@ -42,6 +42,6 @@ model.train(
     train_batch_size=32,
     eval_dataset=eval_dataset,
     lr_decay_epochs=[4, 6, 8],
-    learning_rate=0.01,
+    learning_rate=0.001,
     save_dir='output/alexnet',
     use_vdl=True)

+ 5 - 6
dygraph/tutorials/train/object_detection/faster_rcnn_r34_fpn.py → dygraph/tutorials/train/object_detection/faster_rcnn_hrnet_w18.py

@@ -40,19 +40,18 @@ eval_dataset = pdx.datasets.VOCDetection(
 # 初始化模型,并进行训练
 # 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/tree/release/2.0-rc/tutorials/train#visualdl可视化训练指标
 num_classes = len(train_dataset.labels)
-model = pdx.models.FasterRCNN(
-    num_classes=num_classes, backbone='ResNet34', with_fpn=True)
+model = pdx.models.FasterRCNN(num_classes=num_classes, backbone='HRNet_W18')
 
 # API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0-rc/paddlex/cv/models/detector.py#L154
 # 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
 model.train(
-    num_epochs=12,
+    num_epochs=24,
     train_dataset=train_dataset,
     train_batch_size=2,
     eval_dataset=eval_dataset,
     learning_rate=0.0025,
-    lr_decay_epochs=[8, 11],
-    warmup_steps=500,
+    lr_decay_epochs=[16, 22],
+    warmup_steps=1000,
     warmup_start_lr=0.00025,
-    save_dir='output/faster_rcnn_r50_fpn',
+    save_dir='output/faster_rcnn_hrnet_w18',
     use_vdl=True)

+ 0 - 1
dygraph/tutorials/train/object_detection/ppyolo.py

@@ -55,6 +55,5 @@ model.train(
     warmup_start_lr=0.0,
     save_interval_epochs=5,
     lr_decay_epochs=[243, 324],
-    use_ema=True,
     save_dir='output/ppyolo_r50vd_dcn',
     use_vdl=True)

+ 0 - 1
dygraph/tutorials/train/object_detection/ppyolotiny.py

@@ -54,7 +54,6 @@ model.train(
     warmup_steps=1000,
     warmup_start_lr=0.0,
     lr_decay_epochs=[430, 540, 610],
-    use_ema=True,
     save_interval_epochs=5,
     save_dir='output/ppyolotiny',
     use_vdl=True)

+ 0 - 1
dygraph/tutorials/train/object_detection/ppyolov2.py

@@ -58,6 +58,5 @@ model.train(
     warmup_steps=1000,
     warmup_start_lr=0.0,
     lr_decay_epochs=[243],
-    use_ema=True,
     save_interval_epochs=5,
     save_dir='output/ppyolov2_r50vd_dcn')

+ 2 - 2
paddlex/__init__.py

@@ -14,7 +14,7 @@
 
 from __future__ import absolute_import
 
-__version__ = '1.3.10'
+__version__ = '1.3.11'
 
 import os
 if 'FLAGS_eager_delete_tensor_gb' not in os.environ:
@@ -39,7 +39,7 @@ if version[0] == '1':
 
 if hasattr(paddle, 'enable_static'):
     paddle.enable_static()
-    gui_mode = False
+    gui_mode = True
 
 from .utils.utils import get_environ_info
 from . import cv

+ 2 - 2
paddlex_restful/restful/project/operate.py

@@ -315,9 +315,9 @@ def _call_paddlex_predict(task_path,
             for i in range(len(model.labels)):
                 legend[model.labels[i]] = color_map[i]
             results = model.predict(img)
-            image_pred = pdx.seg.visualize(image, results, save_dir=None)
+            image_pred = pdx.seg.visualize(img, results, save_dir=None)
             pse_pred = pdx.seg.visualize(
-                image, results, weight=0, save_dir=None)
+                img, results, weight=0, save_dir=None)
             image_ground = None
             pse_label = None
             save_name = osp.join(save_dir, 'predict_result.png')

+ 1 - 1
setup.py

@@ -19,7 +19,7 @@ long_description = "PaddlePaddle Entire Process Development Toolkit"
 
 setuptools.setup(
     name="paddlex",
-    version='1.3.10',
+    version='1.3.11',
     author="paddlex",
     author_email="paddlex@baidu.com",
     description=long_description,