Browse Source

Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleX into develop_qh

FlyingQianMM 5 years ago
parent
commit
1af3e044b5
44 changed files with 1177 additions and 209 deletions
  1. 3 1
      deploy/README.md
  2. 8 8
      deploy/cpp/CMakeLists.txt
  3. 3 1
      deploy/cpp/include/paddlex/paddlex.h
  4. 4 6
      deploy/cpp/include/paddlex/transforms.h
  5. 11 1
      deploy/cpp/scripts/build.sh
  6. 2 1
      deploy/cpp/src/classifier.cpp
  7. 4 3
      deploy/cpp/src/detector.cpp
  8. 21 6
      deploy/cpp/src/paddlex.cpp
  9. 3 1
      deploy/cpp/src/segmenter.cpp
  10. 7 10
      deploy/cpp/src/transforms.cpp
  11. 63 4
      docs/apis/datasets.md
  12. 1 1
      docs/apis/load_model.md
  13. 3 1
      docs/apis/transforms/cls_transforms.md
  14. 9 3
      docs/apis/transforms/det_transforms.md
  15. 3 1
      docs/apis/transforms/seg_transforms.md
  16. 173 10
      docs/datasets.md
  17. 14 5
      docs/deploy/deploy.md
  18. 34 14
      docs/deploy/deploy_cpp_linux.md
  19. 18 8
      docs/deploy/deploy_cpp_win_vs2019.md
  20. 5 0
      paddlex/__init__.py
  21. 20 2
      paddlex/command.py
  22. 3 0
      paddlex/cv/datasets/__init__.py
  23. 2 2
      paddlex/cv/datasets/coco.py
  24. 86 0
      paddlex/cv/datasets/easydata_cls.py
  25. 190 0
      paddlex/cv/datasets/easydata_det.py
  26. 116 0
      paddlex/cv/datasets/easydata_seg.py
  27. 1 1
      paddlex/cv/datasets/imagenet.py
  28. 1 1
      paddlex/cv/datasets/seg_dataset.py
  29. 2 2
      paddlex/cv/datasets/voc.py
  30. 5 2
      paddlex/cv/models/base.py
  31. 10 2
      paddlex/cv/models/classifier.py
  32. 10 9
      paddlex/cv/models/deeplabv3p.py
  33. 7 4
      paddlex/cv/models/faster_rcnn.py
  34. 31 1
      paddlex/cv/models/load_model.py
  35. 6 3
      paddlex/cv/models/mask_rcnn.py
  36. 3 1
      paddlex/cv/models/unet.py
  37. 7 4
      paddlex/cv/models/yolo_v3.py
  38. 13 3
      paddlex/cv/nets/detection/faster_rcnn.py
  39. 13 3
      paddlex/cv/nets/detection/mask_rcnn.py
  40. 12 3
      paddlex/cv/nets/detection/yolo_v3.py
  41. 14 3
      paddlex/cv/nets/segmentation/deeplabv3p.py
  42. 14 3
      paddlex/cv/nets/segmentation/unet.py
  43. 78 51
      paddlex/cv/transforms/det_transforms.py
  44. 144 24
      paddlex/cv/transforms/seg_transforms.py

+ 3 - 1
deploy/README.md

@@ -1,3 +1,5 @@
 # 模型部署
 # 模型部署
 
 
-本目录为PaddleX模型部署代码。
+本目录为PaddleX模型部署代码, 编译和使用的教程参考:
+
+- [C++部署文档](../docs/deploy/deploy.md#C部署)

+ 8 - 8
deploy/cpp/CMakeLists.txt

@@ -3,9 +3,10 @@ project(PaddleX CXX C)
 
 
 option(WITH_MKL        "Compile demo with MKL/OpenBlas support,defaultuseMKL."          ON)
 option(WITH_MKL        "Compile demo with MKL/OpenBlas support,defaultuseMKL."          ON)
 option(WITH_GPU        "Compile demo with GPU/CPU, default use CPU."                    ON)
 option(WITH_GPU        "Compile demo with GPU/CPU, default use CPU."                    ON)
-option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static."   ON)
+option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static."   OFF)
 option(WITH_TENSORRT "Compile demo with TensorRT."   OFF)
 option(WITH_TENSORRT "Compile demo with TensorRT."   OFF)
 
 
+SET(TENSORRT_DIR "" CACHE PATH "Compile demo with TensorRT")
 SET(PADDLE_DIR "" CACHE PATH "Location of libraries")
 SET(PADDLE_DIR "" CACHE PATH "Location of libraries")
 SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
 SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
 SET(CUDA_LIB "" CACHE PATH "Location of libraries")
 SET(CUDA_LIB "" CACHE PATH "Location of libraries")
@@ -111,8 +112,8 @@ endif()
 
 
 if (NOT WIN32)
 if (NOT WIN32)
   if (WITH_TENSORRT AND WITH_GPU)
   if (WITH_TENSORRT AND WITH_GPU)
-      include_directories("${PADDLE_DIR}/third_party/install/tensorrt/include")
-      link_directories("${PADDLE_DIR}/third_party/install/tensorrt/lib")
+      include_directories("${TENSORRT_DIR}/include")
+      link_directories("${TENSORRT_DIR}/lib")
   endif()
   endif()
 endif(NOT WIN32)
 endif(NOT WIN32)
 
 
@@ -169,7 +170,7 @@ endif()
 
 
 if (NOT WIN32)
 if (NOT WIN32)
     set(DEPS ${DEPS}
     set(DEPS ${DEPS}
-        ${MATH_LIB} ${MKLDNN_LIB} 
+        ${MATH_LIB} ${MKLDNN_LIB}
         glog gflags protobuf z xxhash yaml-cpp
         glog gflags protobuf z xxhash yaml-cpp
         )
         )
     if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib")
     if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib")
@@ -194,8 +195,8 @@ endif(NOT WIN32)
 if(WITH_GPU)
 if(WITH_GPU)
   if(NOT WIN32)
   if(NOT WIN32)
     if (WITH_TENSORRT)
     if (WITH_TENSORRT)
-      set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer${CMAKE_STATIC_LIBRARY_SUFFIX})
-      set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX})
+      set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
+      set(DEPS ${DEPS} ${TENSORRT_DIR}/lib/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX})
     endif()
     endif()
     set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
     set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
     set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
     set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
@@ -211,7 +212,7 @@ if (NOT WIN32)
     set(DEPS ${DEPS} ${EXTERNAL_LIB})
     set(DEPS ${DEPS} ${EXTERNAL_LIB})
 endif()
 endif()
 
 
-set(DEPS ${DEPS} ${OpenCV_LIBS}) 
+set(DEPS ${DEPS} ${OpenCV_LIBS})
 add_executable(classifier src/classifier.cpp src/transforms.cpp src/paddlex.cpp)
 add_executable(classifier src/classifier.cpp src/transforms.cpp src/paddlex.cpp)
 ADD_DEPENDENCIES(classifier ext-yaml-cpp)
 ADD_DEPENDENCIES(classifier ext-yaml-cpp)
 target_link_libraries(classifier ${DEPS})
 target_link_libraries(classifier ${DEPS})
@@ -251,4 +252,3 @@ if (WIN32 AND WITH_MKL)
     )
     )
 
 
 endif()
 endif()
-

+ 3 - 1
deploy/cpp/include/paddlex/paddlex.h

@@ -38,12 +38,14 @@ class Model {
  public:
  public:
   void Init(const std::string& model_dir,
   void Init(const std::string& model_dir,
             bool use_gpu = false,
             bool use_gpu = false,
+            bool use_trt = false,
             int gpu_id = 0) {
             int gpu_id = 0) {
-    create_predictor(model_dir, use_gpu, gpu_id);
+    create_predictor(model_dir, use_gpu, use_trt, gpu_id);
   }
   }
 
 
   void create_predictor(const std::string& model_dir,
   void create_predictor(const std::string& model_dir,
                         bool use_gpu = false,
                         bool use_gpu = false,
+                        bool use_trt = false,
                         int gpu_id = 0);
                         int gpu_id = 0);
 
 
   bool load_config(const std::string& model_dir);
   bool load_config(const std::string& model_dir);

+ 4 - 6
deploy/cpp/include/paddlex/transforms.h

@@ -35,10 +35,8 @@ class ImageBlob {
   std::vector<int> ori_im_size_ = std::vector<int>(2);
   std::vector<int> ori_im_size_ = std::vector<int>(2);
   // Newest image height and width after process
   // Newest image height and width after process
   std::vector<int> new_im_size_ = std::vector<int>(2);
   std::vector<int> new_im_size_ = std::vector<int>(2);
-  // Image height and width before padding
-  std::vector<int> im_size_before_padding_ = std::vector<int>(2);
   // Image height and width before resize
   // Image height and width before resize
-  std::vector<int> im_size_before_resize_ = std::vector<int>(2);
+  std::vector<std::vector<int>> im_size_before_resize_;
   // Reshape order
   // Reshape order
   std::vector<std::string> reshape_order_;
   std::vector<std::string> reshape_order_;
   // Resize scale
   // Resize scale
@@ -49,7 +47,6 @@ class ImageBlob {
   void clear() {
   void clear() {
     ori_im_size_.clear();
     ori_im_size_.clear();
     new_im_size_.clear();
     new_im_size_.clear();
-    im_size_before_padding_.clear();
     im_size_before_resize_.clear();
     im_size_before_resize_.clear();
     reshape_order_.clear();
     reshape_order_.clear();
     im_data_.clear();
     im_data_.clear();
@@ -155,12 +152,13 @@ class Padding : public Transform {
   virtual void Init(const YAML::Node& item) {
   virtual void Init(const YAML::Node& item) {
     if (item["coarsest_stride"].IsDefined()) {
     if (item["coarsest_stride"].IsDefined()) {
       coarsest_stride_ = item["coarsest_stride"].as<int>();
       coarsest_stride_ = item["coarsest_stride"].as<int>();
-      if (coarsest_stride_ <= 1) {
+      if (coarsest_stride_ < 1) {
         std::cerr << "[Padding] coarest_stride should greater than 0"
         std::cerr << "[Padding] coarest_stride should greater than 0"
                   << std::endl;
                   << std::endl;
         exit(-1);
         exit(-1);
       }
       }
-    } else {
+    }
+    if (item["target_size"].IsDefined()) {
       if (item["target_size"].IsScalar()) {
       if (item["target_size"].IsScalar()) {
         width_ = item["target_size"].as<int>();
         width_ = item["target_size"].as<int>();
         height_ = item["target_size"].as<int>();
         height_ = item["target_size"].as<int>();

+ 11 - 1
deploy/cpp/scripts/build.sh

@@ -1,9 +1,16 @@
 # 是否使用GPU(即是否使用 CUDA)
 # 是否使用GPU(即是否使用 CUDA)
-WITH_GPU=ON
+WITH_GPU=OFF
+# 使用MKL or openblas
+WITH_MKL=ON
 # 是否集成 TensorRT(仅WITH_GPU=ON 有效)
 # 是否集成 TensorRT(仅WITH_GPU=ON 有效)
 WITH_TENSORRT=OFF
 WITH_TENSORRT=OFF
+# TensorRT 的lib路径
+TENSORRT_DIR=/path/to/TensorRT/
 # Paddle 预测库路径
 # Paddle 预测库路径
 PADDLE_DIR=/path/to/fluid_inference/
 PADDLE_DIR=/path/to/fluid_inference/
+# Paddle 的预测库是否使用静态库来编译
+# 使用TensorRT时,Paddle的预测库通常为动态库
+WITH_STATIC_LIB=OFF
 # CUDA 的 lib 路径
 # CUDA 的 lib 路径
 CUDA_LIB=/path/to/cuda/lib/
 CUDA_LIB=/path/to/cuda/lib/
 # CUDNN 的 lib 路径
 # CUDNN 的 lib 路径
@@ -19,8 +26,11 @@ mkdir -p build
 cd build
 cd build
 cmake .. \
 cmake .. \
     -DWITH_GPU=${WITH_GPU} \
     -DWITH_GPU=${WITH_GPU} \
+    -DWITH_MKL=${WITH_MKL} \
     -DWITH_TENSORRT=${WITH_TENSORRT} \
     -DWITH_TENSORRT=${WITH_TENSORRT} \
+    -DTENSORRT_DIR=${TENSORRT_DIR} \
     -DPADDLE_DIR=${PADDLE_DIR} \
     -DPADDLE_DIR=${PADDLE_DIR} \
+    -DWITH_STATIC_LIB=${WITH_STATIC_LIB} \
     -DCUDA_LIB=${CUDA_LIB} \
     -DCUDA_LIB=${CUDA_LIB} \
     -DCUDNN_LIB=${CUDNN_LIB} \
     -DCUDNN_LIB=${CUDNN_LIB} \
     -DOPENCV_DIR=${OPENCV_DIR}
     -DOPENCV_DIR=${OPENCV_DIR}

+ 2 - 1
deploy/cpp/src/classifier.cpp

@@ -23,6 +23,7 @@
 
 
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
+DEFINE_bool(use_trt, false, "Infering with TensorRT");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image_list, "", "Path of test image list file");
 DEFINE_string(image_list, "", "Path of test image list file");
@@ -42,7 +43,7 @@ int main(int argc, char** argv) {
 
 
   // 加载模型
   // 加载模型
   PaddleX::Model model;
   PaddleX::Model model;
-  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_gpu_id);
+  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_use_trt, FLAGS_gpu_id);
 
 
   // 进行预测
   // 进行预测
   if (FLAGS_image_list != "") {
   if (FLAGS_image_list != "") {

+ 4 - 3
deploy/cpp/src/detector.cpp

@@ -24,6 +24,7 @@
 
 
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
+DEFINE_bool(use_trt, false, "Infering with TensorRT");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image_list, "", "Path of test image list file");
 DEFINE_string(image_list, "", "Path of test image list file");
@@ -44,7 +45,7 @@ int main(int argc, char** argv) {
 
 
   // 加载模型
   // 加载模型
   PaddleX::Model model;
   PaddleX::Model model;
-  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_gpu_id);
+  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_use_trt, FLAGS_gpu_id);
 
 
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
   std::string save_dir = "output";
   std::string save_dir = "output";
@@ -68,7 +69,7 @@ int main(int argc, char** argv) {
                   << result.boxes[i].coordinate[0] << ", "
                   << result.boxes[i].coordinate[0] << ", "
                   << result.boxes[i].coordinate[1] << ", "
                   << result.boxes[i].coordinate[1] << ", "
                   << result.boxes[i].coordinate[2] << ", "
                   << result.boxes[i].coordinate[2] << ", "
-                  << result.boxes[i].coordinate[3] << std::endl;
+                  << result.boxes[i].coordinate[3] << ")" << std::endl;
       }
       }
 
 
       // 可视化
       // 可视化
@@ -91,7 +92,7 @@ int main(int argc, char** argv) {
                 << result.boxes[i].coordinate[0] << ", "
                 << result.boxes[i].coordinate[0] << ", "
                 << result.boxes[i].coordinate[1] << ", "
                 << result.boxes[i].coordinate[1] << ", "
                 << result.boxes[i].coordinate[2] << ", "
                 << result.boxes[i].coordinate[2] << ", "
-                << result.boxes[i].coordinate[3] << std::endl;
+                << result.boxes[i].coordinate[3] << ")" << std::endl;
     }
     }
 
 
     // 可视化
     // 可视化

+ 21 - 6
deploy/cpp/src/paddlex.cpp

@@ -18,6 +18,7 @@ namespace PaddleX {
 
 
 void Model::create_predictor(const std::string& model_dir,
 void Model::create_predictor(const std::string& model_dir,
                              bool use_gpu,
                              bool use_gpu,
+                             bool use_trt,
                              int gpu_id) {
                              int gpu_id) {
   // 读取配置文件
   // 读取配置文件
   if (!load_config(model_dir)) {
   if (!load_config(model_dir)) {
@@ -37,6 +38,15 @@ void Model::create_predictor(const std::string& model_dir,
   config.SwitchSpecifyInputNames(true);
   config.SwitchSpecifyInputNames(true);
   // 开启内存优化
   // 开启内存优化
   config.EnableMemoryOptim();
   config.EnableMemoryOptim();
+  if (use_trt) {
+    config.EnableTensorRtEngine(
+        1 << 20 /* workspace_size*/,
+        32 /* max_batch_size*/,
+        20 /* min_subgraph_size*/,
+        paddle::AnalysisConfig::Precision::kFloat32 /* precision*/,
+        true /* use_static*/,
+        false /* use_calib_mode*/);
+  }
   predictor_ = std::move(CreatePaddlePredictor(config));
   predictor_ = std::move(CreatePaddlePredictor(config));
 }
 }
 
 
@@ -246,7 +256,6 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
   auto im_tensor = predictor_->GetInputTensor("image");
   auto im_tensor = predictor_->GetInputTensor("image");
   im_tensor->Reshape({1, 3, h, w});
   im_tensor->Reshape({1, 3, h, w});
   im_tensor->copy_from_cpu(inputs_.im_data_.data());
   im_tensor->copy_from_cpu(inputs_.im_data_.data());
-  std::cout << "input image: " << h << " " << w << std::endl;
 
 
   // 使用加载的模型进行预测
   // 使用加载的模型进行预测
   predictor_->ZeroCopyRun();
   predictor_->ZeroCopyRun();
@@ -286,19 +295,24 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
                      result->score_map.shape[3],
                      result->score_map.shape[3],
                      CV_32FC1,
                      CV_32FC1,
                      result->score_map.data.data());
                      result->score_map.data.data());
-
+  int idx = 1;
+  int len_postprocess = inputs_.im_size_before_resize_.size();
   for (std::vector<std::string>::reverse_iterator iter =
   for (std::vector<std::string>::reverse_iterator iter =
            inputs_.reshape_order_.rbegin();
            inputs_.reshape_order_.rbegin();
        iter != inputs_.reshape_order_.rend();
        iter != inputs_.reshape_order_.rend();
        ++iter) {
        ++iter) {
     if (*iter == "padding") {
     if (*iter == "padding") {
-      auto padding_w = inputs_.im_size_before_padding_[0];
-      auto padding_h = inputs_.im_size_before_padding_[1];
+      auto before_shape = inputs_.im_size_before_resize_[len_postprocess - idx];
+      inputs_.im_size_before_resize_.pop_back();
+      auto padding_w = before_shape[0];
+      auto padding_h = before_shape[1];
       mask_label = mask_label(cv::Rect(0, 0, padding_w, padding_h));
       mask_label = mask_label(cv::Rect(0, 0, padding_w, padding_h));
       mask_score = mask_score(cv::Rect(0, 0, padding_w, padding_h));
       mask_score = mask_score(cv::Rect(0, 0, padding_w, padding_h));
     } else if (*iter == "resize") {
     } else if (*iter == "resize") {
-      auto resize_w = inputs_.im_size_before_resize_[0];
-      auto resize_h = inputs_.im_size_before_resize_[1];
+      auto before_shape = inputs_.im_size_before_resize_[len_postprocess - idx];
+      inputs_.im_size_before_resize_.pop_back();
+      auto resize_w = before_shape[0];
+      auto resize_h = before_shape[1];
       cv::resize(mask_label,
       cv::resize(mask_label,
                  mask_label,
                  mask_label,
                  cv::Size(resize_h, resize_w),
                  cv::Size(resize_h, resize_w),
@@ -312,6 +326,7 @@ bool Model::predict(const cv::Mat& im, SegResult* result) {
                  0,
                  0,
                  cv::INTER_NEAREST);
                  cv::INTER_NEAREST);
     }
     }
+    ++idx;
   }
   }
   result->label_map.data.assign(mask_label.begin<uint8_t>(),
   result->label_map.data.assign(mask_label.begin<uint8_t>(),
                                 mask_label.end<uint8_t>());
                                 mask_label.end<uint8_t>());

+ 3 - 1
deploy/cpp/src/segmenter.cpp

@@ -24,6 +24,7 @@
 
 
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_string(model_dir, "", "Path of inference model");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
 DEFINE_bool(use_gpu, false, "Infering with GPU or CPU");
+DEFINE_bool(use_trt, false, "Infering with TensorRT");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_int32(gpu_id, 0, "GPU card id");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image, "", "Path of test image file");
 DEFINE_string(image_list, "", "Path of test image list file");
 DEFINE_string(image_list, "", "Path of test image list file");
@@ -44,7 +45,8 @@ int main(int argc, char** argv) {
 
 
   // 加载模型
   // 加载模型
   PaddleX::Model model;
   PaddleX::Model model;
-  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_gpu_id);
+  model.Init(FLAGS_model_dir, FLAGS_use_gpu, FLAGS_use_trt, FLAGS_gpu_id);
+
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
   auto colormap = PaddleX::GenerateColorMap(model.labels.size());
   // 进行预测
   // 进行预测
   if (FLAGS_image_list != "") {
   if (FLAGS_image_list != "") {

+ 7 - 10
deploy/cpp/src/transforms.cpp

@@ -56,8 +56,7 @@ float ResizeByShort::GenerateScale(const cv::Mat& im) {
 }
 }
 
 
 bool ResizeByShort::Run(cv::Mat* im, ImageBlob* data) {
 bool ResizeByShort::Run(cv::Mat* im, ImageBlob* data) {
-  data->im_size_before_resize_[0] = im->rows;
-  data->im_size_before_resize_[1] = im->cols;
+  data->im_size_before_resize_.push_back({im->rows, im->cols});
   data->reshape_order_.push_back("resize");
   data->reshape_order_.push_back("resize");
 
 
   float scale = GenerateScale(*im);
   float scale = GenerateScale(*im);
@@ -88,21 +87,21 @@ bool CenterCrop::Run(cv::Mat* im, ImageBlob* data) {
 }
 }
 
 
 bool Padding::Run(cv::Mat* im, ImageBlob* data) {
 bool Padding::Run(cv::Mat* im, ImageBlob* data) {
-  data->im_size_before_padding_[0] = im->rows;
-  data->im_size_before_padding_[1] = im->cols;
+  data->im_size_before_resize_.push_back({im->rows, im->cols});
   data->reshape_order_.push_back("padding");
   data->reshape_order_.push_back("padding");
 
 
   int padding_w = 0;
   int padding_w = 0;
   int padding_h = 0;
   int padding_h = 0;
-  if (width_ > 0 & height_ > 0) {
+  if (width_ > 1 & height_ > 1) {
     padding_w = width_ - im->cols;
     padding_w = width_ - im->cols;
     padding_h = height_ - im->rows;
     padding_h = height_ - im->rows;
-  } else if (coarsest_stride_ > 0) {
+  } else if (coarsest_stride_ > 1) {
     padding_h =
     padding_h =
         ceil(im->rows * 1.0 / coarsest_stride_) * coarsest_stride_ - im->rows;
         ceil(im->rows * 1.0 / coarsest_stride_) * coarsest_stride_ - im->rows;
     padding_w =
     padding_w =
         ceil(im->cols * 1.0 / coarsest_stride_) * coarsest_stride_ - im->cols;
         ceil(im->cols * 1.0 / coarsest_stride_) * coarsest_stride_ - im->cols;
   }
   }
+
   if (padding_h < 0 || padding_w < 0) {
   if (padding_h < 0 || padding_w < 0) {
     std::cerr << "[Padding] Computed padding_h=" << padding_h
     std::cerr << "[Padding] Computed padding_h=" << padding_h
               << ", padding_w=" << padding_w
               << ", padding_w=" << padding_w
@@ -122,8 +121,7 @@ bool ResizeByLong::Run(cv::Mat* im, ImageBlob* data) {
               << std::endl;
               << std::endl;
     return false;
     return false;
   }
   }
-  data->im_size_before_resize_[0] = im->rows;
-  data->im_size_before_resize_[1] = im->cols;
+  data->im_size_before_resize_.push_back({im->rows, im->cols});
   data->reshape_order_.push_back("resize");
   data->reshape_order_.push_back("resize");
   int origin_w = im->cols;
   int origin_w = im->cols;
   int origin_h = im->rows;
   int origin_h = im->rows;
@@ -149,8 +147,7 @@ bool Resize::Run(cv::Mat* im, ImageBlob* data) {
               << std::endl;
               << std::endl;
     return false;
     return false;
   }
   }
-  data->im_size_before_resize_[0] = im->rows;
-  data->im_size_before_resize_[1] = im->cols;
+  data->im_size_before_resize_.push_back({im->rows, im->cols});
   data->reshape_order_.push_back("resize");
   data->reshape_order_.push_back("resize");
 
 
   cv::resize(
   cv::resize(

+ 63 - 4
docs/apis/datasets.md

@@ -16,7 +16,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_
 > * **transforms** (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。  
 > * **transforms** (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
-> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 
 
 ## VOCDetection类
 ## VOCDetection类
@@ -37,7 +37,7 @@ paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None,
 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。  
 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
-> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 
 
 ## CocoDetection类
 ## CocoDetection类
@@ -57,7 +57,7 @@ paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers=
 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。  
 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
-> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 
 
 ## SegDataset类
 ## SegDataset类
@@ -78,5 +78,64 @@ paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, nu
 > * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。  
 > * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。  
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
-> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 
+
+## EasyDataCls类
+```
+paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
+```
+读取EasyData图像分类数据集,并对样本进行相应的处理。EasyData图像分类任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)  
+
+
+### 参数
+
+> * **data_dir** (str): 数据集所在的目录路径。  
+> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
+> * **label_list** (str): 描述数据集包含的类别信息文件路径。  
+> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。  
+> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
+> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
+
+## EasyDataDet类
+
+```
+paddlex.datasets.EasyDataDet(data_dir, file_list, label_list, transforms=None, num_workers=‘auto’, buffer_size=100, parallel_method='thread', shuffle=False)
+```
+
+读取EasyData目标检测格式数据集,并对样本进行相应的处理,该格式的数据集同样可以应用到实例分割模型的训练中。EasyData目标检测或实例分割任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)  
+
+
+### 参数
+
+> * **data_dir** (str): 数据集所在的目录路径。  
+> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
+> * **label_list** (str): 描述数据集包含的类别信息文件路径。  
+> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。  
+> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
+> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。  
+
+
+## EasyDataSeg类
+
+```
+paddlex.datasets.EasyDataSeg(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
+```
+
+读取EasyData语分分割任务数据集,并对样本进行相应的处理。EasyData语义分割任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)  
+
+
+### 参数
+
+> * **data_dir** (str): 数据集所在的目录路径。  
+> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
+> * **label_list** (str): 描述数据集包含的类别信息文件路径。  
+> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。  
+> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
+> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。  
+> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。  
+> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 

+ 1 - 1
docs/apis/load_model.md

@@ -34,7 +34,7 @@ pred_result = model.predict('./xiaoduxiong_ins_det/JPEGImages/WechatIMG114.jpeg'
 
 
 # 在验证集上进行评估
 # 在验证集上进行评估
 eval_reader = pdx.cv.datasets.CocoDetection(data_dir=data_dir,
 eval_reader = pdx.cv.datasets.CocoDetection(data_dir=data_dir,
-                                            ann_file=ann_file
+                                            ann_file=ann_file,
                                             transforms=model.eval_transforms)
                                             transforms=model.eval_transforms)
 eval_result = model.evaluate(eval_reader, batch_size=1)
 eval_result = model.evaluate(eval_reader, batch_size=1)
 ```
 ```

+ 3 - 1
docs/apis/transforms/cls_transforms.md

@@ -109,7 +109,9 @@ paddlex.cls.transforms.RandomDistort(brightness_range=0.9, brightness_prob=0.5,
 
 
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。  
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。  
 1. 对变换的操作顺序进行随机化操作。
 1. 对变换的操作顺序进行随机化操作。
-2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
+2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。  
+
+【注意】该数据增强必须在数据增强Normalize之前使用。
 
 
 ### 参数
 ### 参数
 * **brightness_range** (float): 明亮度因子的范围。默认为0.9。
 * **brightness_range** (float): 明亮度因子的范围。默认为0.9。

+ 9 - 3
docs/apis/transforms/det_transforms.md

@@ -85,7 +85,9 @@ paddlex.det.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
 
 
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。  
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。  
 1. 对变换的操作顺序进行随机化操作。
 1. 对变换的操作顺序进行随机化操作。
-2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
+2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。  
+
+【注意】该数据增强必须在数据增强Normalize之前使用。
 
 
 ### 参数
 ### 参数
 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。
 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。
@@ -135,7 +137,9 @@ paddlex.det.transforms.RandomExpand(ratio=4., prob=0.5, fill_value=[123.675, 116
 ### 参数
 ### 参数
 * **ratio** (float): 图像扩张的最大比例。默认为4.0。
 * **ratio** (float): 图像扩张的最大比例。默认为4.0。
 * **prob** (float): 随机扩张的概率。默认为0.5。
 * **prob** (float): 随机扩张的概率。默认为0.5。
-* **fill_value** (list): 扩张图像的初始填充值(0-255)。默认为[123.675, 116.28, 103.53]。
+* **fill_value** (list): 扩张图像的初始填充值(0-255)。默认为[123.675, 116.28, 103.53]。  
+
+【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
 
 
 ## RandomCrop类
 ## RandomCrop类
 ```python
 ```python
@@ -152,7 +156,9 @@ paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3,
     (4) 如果cover_all_box为True且存在真实标注框的IoU小于thresh,则继续第3步。
     (4) 如果cover_all_box为True且存在真实标注框的IoU小于thresh,则继续第3步。
     (5) 筛选出位于候选裁剪区域内的真实标注框,若有效框的个数为0,则继续第3步,否则进行第4步。
     (5) 筛选出位于候选裁剪区域内的真实标注框,若有效框的个数为0,则继续第3步,否则进行第4步。
 4. 换算有效真值标注框相对候选裁剪区域的位置坐标。
 4. 换算有效真值标注框相对候选裁剪区域的位置坐标。
-5. 换算有效分割区域相对候选裁剪区域的位置坐标。
+5. 换算有效分割区域相对候选裁剪区域的位置坐标。  
+
+【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
 
 
 ### 参数
 ### 参数
 * **aspect_ratio** (list): 裁剪后短边缩放比例的取值范围,以[min, max]形式表示。默认值为[.5, 2.]。
 * **aspect_ratio** (list): 裁剪后短边缩放比例的取值范围,以[min, max]形式表示。默认值为[.5, 2.]。

+ 3 - 1
docs/apis/transforms/seg_transforms.md

@@ -153,7 +153,9 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。
 以一定的概率对图像进行随机像素内容变换,模型训练时的数据增强操作。
 
 
 1.对变换的操作顺序进行随机化操作。
 1.对变换的操作顺序进行随机化操作。
-2.按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
+2.按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。  
+
+【注意】该数据增强必须在数据增强Normalize之前使用。
 
 
 ### 参数
 ### 参数
 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。
 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。

+ 173 - 10
docs/datasets.md

@@ -41,8 +41,8 @@ labelA
 labelB
 labelB
 ...
 ...
 ```
 ```
-[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集
-在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集
+[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集
+在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集
 
 
 ## 目标检测VOC
 ## 目标检测VOC
 目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
 目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
@@ -81,8 +81,8 @@ labelA
 labelB
 labelB
 ...
 ...
 ```
 ```
-[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集
-在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集
+[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集
+在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集
 
 
 ## 目标检测和实例分割COCO
 ## 目标检测和实例分割COCO
 目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
 目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
@@ -135,7 +135,7 @@ labelB
   ]
   ]
 }
 }
 ```
 ```
-每个字段的含义如下所示:
+其中,每个字段的含义如下所示:
 
 
 | 域名 | 字段名 | 含义 | 数据类型 | 备注 |
 | 域名 | 字段名 | 含义 | 数据类型 | 备注 |
 |:-----|:--------|:------------|------|:-----|
 |:-----|:--------|:------------|------|:-----|
@@ -155,8 +155,8 @@ labelB
 | categories | supercategory | 类别父类的标签名 | str |  |
 | categories | supercategory | 类别父类的标签名 | str |  |
 
 
 
 
-[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集
-在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集
+[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集
+在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集
 
 
 ## 语义分割数据
 ## 语义分割数据
 语义分割数据集包含原图、标注图及相应的文件列表文件。
 语义分割数据集包含原图、标注图及相应的文件列表文件。
@@ -191,13 +191,176 @@ images/xxx2.png annotations/xxx2.png
 
 
 `labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
 `labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
 ```
 ```
+background
 labelA
 labelA
 labelB
 labelB
 ...
 ...
 ```
 ```
 
 
-标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增,
+标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增(一般第一个类别为`background`)
 例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
 例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
 
 
-[点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集
-在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集
+[点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集。
+
+
+## 图像分类EasyDataCls
+
+图像分类EasyDataCls数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+{"labels": [{"name": "labelA"}]}
+```
+其中,`name`字段代表对应图像的类别。  
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataCls数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataCls`([API说明](./apis/datasets.html#easydatacls))加载分类数据集。
+
+
+## 目标检测和实例分割EasyDataDet
+
+目标检测和实例分割EasyDataDet数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录ß
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA", 
+            "mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"}, 
+           {"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
+            "mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"}, 
+           ...]}
+```
+其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示: 
+
+| 字段名 | 含义 | 数据类型 | 备注 |
+|:--------|:------------|------|:-----|
+| x1 | 标注框左下角横坐标 | int | |
+| y1 | 标注框左下角纵坐标 | int | |
+| x2 | 标注框右上角横坐标 | int | |
+| y2 | 标注框右上角纵坐标 | int | |
+| name | 标注框中物体类标 | str | |
+| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段可以不存在,当不存在时只能进行目标检测 |
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataDet数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataDet`([API说明](./apis/datasets.html#easydatadet))加载分类数据集。
+
+## 语义分割EasyDataSeg
+
+语义分割EasyDataSeg数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录ß
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA", 
+            "mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"}, 
+           {"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
+            "mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"}, 
+           ...]}
+```
+其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示: 
+
+| 字段名 | 含义 | 数据类型 | 备注 |
+|:--------|:------------|------|:-----|
+| x1 | 标注框左下角横坐标 | int | |
+| y1 | 标注框左下角纵坐标 | int | |
+| x2 | 标注框右上角横坐标 | int | |
+| y2 | 标注框右上角纵坐标 | int | |
+| name | 标注框中物体类标 | str | |
+| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段必须存在 |
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataSeg数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataSeg`([API说明](./apis/datasets.html#easydataseg))加载分类数据集。

+ 14 - 5
docs/deploy/deploy.md

@@ -7,20 +7,29 @@
 ### 导出inference模型
 ### 导出inference模型
 
 
 在服务端部署的模型需要首先将模型导出为inference格式模型,导出的模型将包括`__model__`、`__params__`和`model.yml`三个文名,分别为模型的网络结构,模型权重和模型的配置文件(包括数据预处理参数等等)。在安装完PaddleX后,在命令行终端使用如下命令导出模型到当前目录`inferece_model`下。
 在服务端部署的模型需要首先将模型导出为inference格式模型,导出的模型将包括`__model__`、`__params__`和`model.yml`三个文名,分别为模型的网络结构,模型权重和模型的配置文件(包括数据预处理参数等等)。在安装完PaddleX后,在命令行终端使用如下命令导出模型到当前目录`inferece_model`下。
+> 可直接下载小度熊分拣模型测试本文档的流程[xiaoduxiong_epoch_12.tar.gz](https://bj.bcebos.com/paddlex/models/xiaoduxiong_epoch_12.tar.gz)
 
 
-> 可直接下载垃圾检测模型测试本文档的流程[garbage_epoch_12.tar.gz](https://bj.bcebos.com/paddlex/models/garbage_epoch_12.tar.gz)
+```
+paddlex --export_inference --model_dir=./xiaoduxiong_epoch_12 --save_dir=./inference_model
+```
+
+使用TensorRT预测时,需指定模型的图像输入shape:[w,h]。
+**注**:
+- 分类模型请保持于训练时输入的shape一致。
+- 指定[w,h]时,w和h中间逗号隔开,不允许存在空格等其他字符
 
 
 ```
 ```
-paddlex --export_inference --model_dir=./garbage_epoch_12 --save_dir=./inference_model
+paddlex --export_inference --model_dir=./xiaoduxiong_epoch_12 --save_dir=./inference_model --fixed_input_shape=[640,960]
 ```
 ```
 
 
 ### Python部署
 ### Python部署
 PaddleX已经集成了基于Python的高性能预测接口,在安装PaddleX后,可参照如下代码示例,进行预测。相关的接口文档可参考[paddlex.deploy](apis/deploy.md)
 PaddleX已经集成了基于Python的高性能预测接口,在安装PaddleX后,可参照如下代码示例,进行预测。相关的接口文档可参考[paddlex.deploy](apis/deploy.md)
-> 点击下载测试图片 [garbage.bmp](https://bj.bcebos.com/paddlex/datasets/garbage.bmp)
+> 点击下载测试图片 [xiaoduxiong_test_image.tar.gz](https://bj.bcebos.com/paddlex/datasets/xiaoduxiong_test_image.tar.gz)
+
 ```
 ```
 import paddlex as pdx
 import paddlex as pdx
-predictorpdx.deploy.create_predictor('./inference_model')
-result = predictor.predict(image='garbage.bmp')
+predictor = pdx.deploy.create_predictor('./inference_model')
+result = predictor.predict(image='xiaoduxiong_test_image/JPEGImages/WeChatIMG110.jpeg')
 ```
 ```
 
 
 ### C++部署
 ### C++部署

+ 34 - 14
docs/deploy/deploy_cpp_linux.md

@@ -19,8 +19,18 @@
 
 
 ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
 ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
 
 
-PaddlePaddle C++ 预测库针对不同的`CPU`,`CUDA`,以及是否支持TensorRT,提供了不同的预编译版本,请根据实际情况下载:  [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/build_and_install_lib_cn.html#id1)
+PaddlePaddle C++ 预测库针对不同的`CPU`,`CUDA`,以及是否支持TensorRT,提供了不同的预编译版本,目前PaddleX依赖于Paddle1.7版本,以下提供了多个不同版本的Paddle预测库:
 
 
+|  版本说明   | 预测库(1.7.2版本)  |
+|  ----  | ----  |
+| ubuntu14.04_cpu_avx_mkl  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-cpu-avx-mkl/fluid_inference.tgz) |
+| ubuntu14.04_cpu_avx_openblas  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-cpu-avx-openblas/fluid_inference.tgz) |
+| ubuntu14.04_cpu_noavx_openblas  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-cpu-noavx-openblas/fluid_inference.tgz) |
+| ubuntu14.04_cuda9.0_cudnn7_avx_mkl  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-gpu-cuda9-cudnn7-avx-mkl/fluid_inference.tgz) |
+| ubuntu14.04_cuda10.0_cudnn7_avx_mkl  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-gpu-cuda10-cudnn7-avx-mkl/fluid_inference.tgz ) |
+| ubuntu14.04_cuda10.1_cudnn7.6_avx_mkl_trt6  | [fluid_inference.tgz](https://paddle-inference-lib.bj.bcebos.com/1.7.2-gpu-cuda10.1-cudnn7.6-avx-mkl-trt6%2Ffluid_inference.tgz) |
+
+更多和更新的版本,请根据实际情况下载:  [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/windows_cpp_inference.html#id1)
 
 
 下载并解压后`/root/projects/fluid_inference`目录包含内容为:
 下载并解压后`/root/projects/fluid_inference`目录包含内容为:
 ```
 ```
@@ -40,17 +50,24 @@ fluid_inference
 编译`cmake`的命令在`scripts/build.sh`中,请根据实际情况修改主要参数,其主要内容说明如下:
 编译`cmake`的命令在`scripts/build.sh`中,请根据实际情况修改主要参数,其主要内容说明如下:
 ```
 ```
 # 是否使用GPU(即是否使用 CUDA)
 # 是否使用GPU(即是否使用 CUDA)
-WITH_GPU=ON
+WITH_GPU=OFF
+# 使用MKL or openblas
+WITH_MKL=ON
 # 是否集成 TensorRT(仅WITH_GPU=ON 有效)
 # 是否集成 TensorRT(仅WITH_GPU=ON 有效)
 WITH_TENSORRT=OFF
 WITH_TENSORRT=OFF
-# 上一步下载的 Paddle 预测库路径
-PADDLE_DIR=/root/projects/deps/fluid_inference/
+# TensorRT 的lib路径
+TENSORRT_DIR=/path/to/TensorRT/
+# Paddle 预测库路径
+PADDLE_DIR=/path/to/fluid_inference/
+# Paddle 的预测库是否使用静态库来编译
+# 使用TensorRT时,Paddle的预测库通常为动态库
+WITH_STATIC_LIB=ON
 # CUDA 的 lib 路径
 # CUDA 的 lib 路径
-CUDA_LIB=/usr/local/cuda/lib64/
+CUDA_LIB=/path/to/cuda/lib/
 # CUDNN 的 lib 路径
 # CUDNN 的 lib 路径
-CUDNN_LIB=/usr/local/cudnn/lib64/
+CUDNN_LIB=/path/to/cudnn/lib/
 
 
-# OPENCV 路径, 如果使用自带预编译版本可不设置
+# OPENCV 路径, 如果使用自带预编译版本可不修改
 OPENCV_DIR=$(pwd)/deps/opencv3gcc4.8/
 OPENCV_DIR=$(pwd)/deps/opencv3gcc4.8/
 sh $(pwd)/scripts/bootstrap.sh
 sh $(pwd)/scripts/bootstrap.sh
 
 
@@ -60,8 +77,11 @@ mkdir -p build
 cd build
 cd build
 cmake .. \
 cmake .. \
     -DWITH_GPU=${WITH_GPU} \
     -DWITH_GPU=${WITH_GPU} \
+    -DWITH_MKL=${WITH_MKL} \
     -DWITH_TENSORRT=${WITH_TENSORRT} \
     -DWITH_TENSORRT=${WITH_TENSORRT} \
+    -DTENSORRT_DIR=${TENSORRT_DIR} \
     -DPADDLE_DIR=${PADDLE_DIR} \
     -DPADDLE_DIR=${PADDLE_DIR} \
+    -DWITH_STATIC_LIB=${WITH_STATIC_LIB} \
     -DCUDA_LIB=${CUDA_LIB} \
     -DCUDA_LIB=${CUDA_LIB} \
     -DCUDNN_LIB=${CUDNN_LIB} \
     -DCUDNN_LIB=${CUDNN_LIB} \
     -DOPENCV_DIR=${OPENCV_DIR}
     -DOPENCV_DIR=${OPENCV_DIR}
@@ -83,19 +103,20 @@ make
 | image  | 要预测的图片文件路径 |
 | image  | 要预测的图片文件路径 |
 | image_list  | 按行存储图片路径的.txt文件 |
 | image_list  | 按行存储图片路径的.txt文件 |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
 | use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |
+| use_trt  | 是否使用 TensorTr 预测, 支持值为0或1(默认值为0) |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | gpu_id  | GPU 设备ID, 默认值为0 |
 | save_dir | 保存可视化结果的路径, 默认值为"output",classfier无该参数 |
 | save_dir | 保存可视化结果的路径, 默认值为"output",classfier无该参数 |
 
 
 ## 样例
 ## 样例
 
 
-可使用[垃圾检测模型](deploy.md#导出inference模型)中生成的`inference_model`模型和测试图片进行预测。
+可使用[小度熊识别模型](deploy.md#导出inference模型)中导出的`inference_model`和测试图片进行预测。
 
 
 `样例一`:
 `样例一`:
 
 
-不使用`GPU`测试图片 `/path/to/garbage.bmp`  
+不使用`GPU`测试图片 `/path/to/xiaoduxiong.jpeg`  
 
 
 ```shell
 ```shell
-./build/detector --model_dir=/path/to/inference_model --image=/path/to/garbage.bmp --save_dir=output
+./build/detector --model_dir=/path/to/inference_model --image=/path/to/xiaoduxiong.jpeg --save_dir=output
 ```
 ```
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
 
 
@@ -104,13 +125,12 @@ make
 
 
 使用`GPU`预测多个图片`/path/to/image_list.txt`,image_list.txt内容的格式如下:
 使用`GPU`预测多个图片`/path/to/image_list.txt`,image_list.txt内容的格式如下:
 ```
 ```
-/path/to/images/garbage1.jpeg
-/path/to/images/garbage2.jpeg
+/path/to/images/xiaoduxiong1.jpeg
+/path/to/images/xiaoduxiong2.jpeg
 ...
 ...
-/path/to/images/garbagen.jpeg
+/path/to/images/xiaoduxiongn.jpeg
 ```
 ```
 ```shell
 ```shell
 ./build/detector --model_dir=/path/to/models/inference_model --image_list=/root/projects/images_list.txt --use_gpu=1 --save_dir=output
 ./build/detector --model_dir=/path/to/models/inference_model --image_list=/root/projects/images_list.txt --use_gpu=1 --save_dir=output
 ```
 ```
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
-

+ 18 - 8
docs/deploy/deploy_cpp_win_vs2019.md

@@ -27,7 +27,18 @@ git clone https://github.com/PaddlePaddle/PaddleX.git
 
 
 ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
 ### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference
 
 
-PaddlePaddle C++ 预测库针对不同的`CPU`和`CUDA`版本提供了不同的预编译版本,请根据实际情况下载:  [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/windows_cpp_inference.html)
+PaddlePaddle C++ 预测库针对不同的`CPU`,`CUDA`,以及是否支持TensorRT,提供了不同的预编译版本,目前PaddleX依赖于Paddle1.7版本,以下提供了多个不同版本的Paddle预测库:
+
+|  版本说明   | 预测库(1.7.2版本)  | 编译器 | 构建工具| cuDNN | CUDA
+|  ----  |  ----  |  ----  |  ----  | ---- | ---- |
+| cpu_avx_mkl  | [fluid_inference.zip](https://paddle-wheel.bj.bcebos.com/1.7.2/win-infer/mkl/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
+| cpu_avx_openblas  | [fluid_inference.zip](https://paddle-wheel.bj.bcebos.com/1.7.2/win-infer/open/cpu/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 |
+| cuda9.0_cudnn7_avx_mkl  | [fluid_inference.zip](https://paddle-wheel.bj.bcebos.com/1.7.2/win-infer/mkl/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
+| cuda9.0_cudnn7_avx_openblas  | [fluid_inference.zip](https://paddle-wheel.bj.bcebos.com/1.7.2/win-infer/open/post97/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.4.1 | 9.0 |
+| cuda10.0_cudnn7_avx_mkl  | [fluid_inference.zip](https://paddle-wheel.bj.bcebos.com/1.7.2/win-infer/mkl/post107/fluid_inference_install_dir.zip) | MSVC 2015 update 3 | CMake v3.16.0 | 7.5.0 | 9.0 |
+
+
+更多和更新的版本,请根据实际情况下载:  [C++预测库下载列表](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/build_and_install_lib_cn.html#id1)
 
 
 解压后`D:\projects\fluid_inference*\`目录下主要包含的内容为:
 解压后`D:\projects\fluid_inference*\`目录下主要包含的内容为:
 ```
 ```
@@ -109,14 +120,14 @@ cd D:\projects\PaddleX\deploy\cpp\out\build\x64-Release
 
 
 ## 样例
 ## 样例
 
 
-可使用[垃圾检测模型](deploy.md#导出inference模型)中生成的`inference_model`模型和测试图片进行预测。
+可使用[小度熊识别模型](deploy.md#导出inference模型)中导出的`inference_model`和测试图片进行预测。
 
 
 `样例一`:
 `样例一`:
 
 
-不使用`GPU`测试图片  `\\path\\to\\garbage.bmp`  
+不使用`GPU`测试图片  `\\path\\to\\xiaoduxiong.jpeg`  
 
 
 ```shell
 ```shell
-.\detector --model_dir=\\path\\to\\inference_model --image=D:\\images\\garbage.bmp --save_dir=output
+.\detector --model_dir=\\path\\to\\inference_model --image=D:\\images\\xiaoduxiong.jpeg --save_dir=output
 
 
 ```
 ```
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
@@ -126,13 +137,12 @@ cd D:\projects\PaddleX\deploy\cpp\out\build\x64-Release
 
 
 使用`GPU`预测多个图片`\\path\\to\\image_list.txt`,image_list.txt内容的格式如下:
 使用`GPU`预测多个图片`\\path\\to\\image_list.txt`,image_list.txt内容的格式如下:
 ```
 ```
-\\path\\to\\images\\garbage1.jpeg
-\\path\\to\\images\\garbage2.jpeg
+\\path\\to\\images\\xiaoduxiong1.jpeg
+\\path\\to\\images\\xiaoduxiong2.jpeg
 ...
 ...
-\\path\\to\\images\\garbagen.jpeg
+\\path\\to\\images\\xiaoduxiongn.jpeg
 ```
 ```
 ```shell
 ```shell
 .\detector --model_dir=\\path\\to\\inference_model --image_list=\\path\\to\\images_list.txt --use_gpu=1 --save_dir=output
 .\detector --model_dir=\\path\\to\\inference_model --image_list=\\path\\to\\images_list.txt --use_gpu=1 --save_dir=output
 ```
 ```
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
 图片文件`可视化预测结果`会保存在`save_dir`参数设置的目录下。
-

+ 5 - 0
paddlex/__init__.py

@@ -38,6 +38,11 @@ except:
         "[WARNING] pycocotools install: https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/install.md"
         "[WARNING] pycocotools install: https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/install.md"
     )
     )
 
 
+import paddlehub as hub
+if hub.version.hub_version < '1.6.2':
+    raise Exception("[ERROR] paddlehub >= 1.6.2 is required")
+
+
 env_info = get_environ_info()
 env_info = get_environ_info()
 load_model = cv.models.load_model
 load_model = cv.models.load_model
 datasets = cv.datasets
 datasets = cv.datasets

+ 20 - 2
paddlex/command.py

@@ -29,7 +29,11 @@ def arg_parser():
         action="store_true",
         action="store_true",
         default=False,
         default=False,
         help="export inference model for C++/Python deployment")
         help="export inference model for C++/Python deployment")
-
+    parser.add_argument(
+        "--fixed_input_shape",
+        "-fs",
+        default=None,
+        help="export inference model with fixed input shape:[w,h]")
     return parser
     return parser
 
 
 
 
@@ -53,9 +57,23 @@ def main():
     if args.export_inference:
     if args.export_inference:
         assert args.model_dir is not None, "--model_dir should be defined while exporting inference model"
         assert args.model_dir is not None, "--model_dir should be defined while exporting inference model"
         assert args.save_dir is not None, "--save_dir should be defined to save inference model"
         assert args.save_dir is not None, "--save_dir should be defined to save inference model"
-        model = pdx.load_model(args.model_dir)
+        fixed_input_shape = eval(args.fixed_input_shape)
+        assert len(
+            fixed_input_shape) == 2, "len of fixed input shape must == 2"
+
+        model = pdx.load_model(args.model_dir, fixed_input_shape)
         model.export_inference_model(args.save_dir)
         model.export_inference_model(args.save_dir)
 
 
+    if args.export_onnx:
+        assert args.model_dir is not None, "--model_dir should be defined while exporting onnx model"
+        assert args.save_dir is not None, "--save_dir should be defined to save onnx model"
+        fixed_input_shape = eval(args.fixed_input_shape)
+        assert len(
+            fixed_input_shape) == 2, "len of fixed input shape must == 2"
+
+        model = pdx.load_model(args.model_dir, fixed_input_shape)
+        model.export_onnx_model(args.save_dir)
+
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     main()
     main()

+ 3 - 0
paddlex/cv/datasets/__init__.py

@@ -16,3 +16,6 @@ from .imagenet import ImageNet
 from .voc import VOCDetection
 from .voc import VOCDetection
 from .coco import CocoDetection
 from .coco import CocoDetection
 from .seg_dataset import SegDataset
 from .seg_dataset import SegDataset
+from .easydata_cls import EasyDataCls
+from .easydata_det import EasyDataDet
+from .easydata_seg import EasyDataSeg

+ 2 - 2
paddlex/cv/datasets/coco.py

@@ -34,7 +34,7 @@ class CocoDetection(VOCDetection):
             系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
             系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
-            线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
     """
     """
 
 
@@ -112,7 +112,7 @@ class CocoDetection(VOCDetection):
 
 
             im_info = {
             im_info = {
                 'im_id': np.array([img_id]).astype('int32'),
                 'im_id': np.array([img_id]).astype('int32'),
-                'origin_shape': np.array([im_h, im_w]).astype('int32'),
+                'image_shape': np.array([im_h, im_w]).astype('int32'),
             }
             }
             label_info = {
             label_info = {
                 'is_crowd': is_crowd,
                 'is_crowd': is_crowd,

+ 86 - 0
paddlex/cv/datasets/easydata_cls.py

@@ -0,0 +1,86 @@
+# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os.path as osp
+import random
+import copy
+import json
+import paddlex.utils.logging as logging
+from .imagenet import ImageNet
+from .dataset import is_pic
+from .dataset import get_encoding
+
+
+class EasyDataCls(ImageNet):
+    """读取EasyDataCls格式的分类数据集,并对样本进行相应的处理。
+
+    Args:
+        data_dir (str): 数据集所在的目录路径。
+        file_list (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对data_dir的相对路)。
+        label_list (str): 描述数据集包含的类别信息文件路径。
+        transforms (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子。
+        num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
+            系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核
+            数的一半。
+        buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
+        parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
+        shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
+    """
+    
+    def __init__(self,
+                 data_dir,
+                 file_list,
+                 label_list,
+                 transforms=None,
+                 num_workers='auto',
+                 buffer_size=100,
+                 parallel_method='process',
+                 shuffle=False):
+        super(ImageNet, self).__init__(
+            transforms=transforms,
+            num_workers=num_workers,
+            buffer_size=buffer_size,
+            parallel_method=parallel_method,
+            shuffle=shuffle)
+        self.file_list = list()
+        self.labels = list()
+        self._epoch = 0
+        
+        with open(label_list, encoding=get_encoding(label_list)) as f:
+            for line in f:
+                item = line.strip()
+                self.labels.append(item)
+        logging.info("Starting to read file list from dataset...")
+        with open(file_list, encoding=get_encoding(file_list)) as f:
+            for line in f:
+                img_file, json_file = [osp.join(data_dir, x) \
+                        for x in line.strip().split()[:2]]
+                if not is_pic(img_file):
+                    continue
+                if not osp.isfile(json_file):
+                    continue
+                if not osp.exists(img_file):
+                    raise IOError(
+                        'The image file {} is not exist!'.format(img_file))
+                with open(json_file, mode='r', \
+                          encoding=get_encoding(json_file)) as j:
+                    json_info = json.load(j)
+                label = json_info['labels'][0]['name']
+                self.file_list.append([img_file, self.labels.index(label)])
+        self.num_samples = len(self.file_list)
+        logging.info("{} samples in file {}".format(
+            len(self.file_list), file_list))
+    

+ 190 - 0
paddlex/cv/datasets/easydata_det.py

@@ -0,0 +1,190 @@
+# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os.path as osp
+import random
+import copy
+import json
+import cv2
+import numpy as np
+import paddlex.utils.logging as logging
+from .voc import VOCDetection
+from .dataset import is_pic
+from .dataset import get_encoding
+
+class EasyDataDet(VOCDetection):
+    """读取EasyDataDet格式的检测数据集,并对样本进行相应的处理。
+
+    Args:
+        data_dir (str): 数据集所在的目录路径。
+        file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
+        label_list (str): 描述数据集包含的类别信息文件路径。
+        transforms (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子。
+        num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
+            系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的
+            一半。
+        buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
+        parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
+        shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
+    """
+    
+    def __init__(self,
+                 data_dir,
+                 file_list,
+                 label_list,
+                 transforms=None,
+                 num_workers='auto',
+                 buffer_size=100,
+                 parallel_method='process',
+                 shuffle=False):
+        super(VOCDetection, self).__init__(
+            transforms=transforms,
+            num_workers=num_workers,
+            buffer_size=buffer_size,
+            parallel_method=parallel_method,
+            shuffle=shuffle)
+        self.file_list = list()
+        self.labels = list()
+        self._epoch = 0
+        
+        annotations = {}
+        annotations['images'] = []
+        annotations['categories'] = []
+        annotations['annotations'] = []
+        
+        cname2cid = {}
+        label_id = 1
+        with open(label_list, encoding=get_encoding(label_list)) as fr:
+            for line in fr.readlines():
+                cname2cid[line.strip()] = label_id
+                label_id += 1
+                self.labels.append(line.strip())
+        logging.info("Starting to read file list from dataset...")
+        for k, v in cname2cid.items():
+            annotations['categories'].append({
+                'supercategory': 'component',
+                'id': v,
+                'name': k
+            })
+            
+        from pycocotools.mask import decode
+        ct = 0
+        ann_ct = 0
+        with open(file_list, encoding=get_encoding(file_list)) as f:
+            for line in f:
+                img_file, json_file = [osp.join(data_dir, x) \
+                        for x in line.strip().split()[:2]]
+                if not is_pic(img_file):
+                    continue
+                if not osp.isfile(json_file):
+                    continue
+                if not osp.exists(img_file):
+                    raise IOError(
+                        'The image file {} is not exist!'.format(img_file))
+                with open(json_file, mode='r', \
+                          encoding=get_encoding(json_file)) as j:
+                    json_info = json.load(j)
+                im_id = np.array([ct])
+                im = cv2.imread(img_file)
+                im_w = im.shape[1]
+                im_h = im.shape[0]
+                objs = json_info['labels']
+                gt_bbox = np.zeros((len(objs), 4), dtype=np.float32)
+                gt_class = np.zeros((len(objs), 1), dtype=np.int32)
+                gt_score = np.ones((len(objs), 1), dtype=np.float32)
+                is_crowd = np.zeros((len(objs), 1), dtype=np.int32)
+                difficult = np.zeros((len(objs), 1), dtype=np.int32)
+                gt_poly = [None] * len(objs)
+                for i, obj in enumerate(objs):
+                    cname = obj['name']
+                    gt_class[i][0] = cname2cid[cname]
+                    x1 = max(0, obj['x1'])
+                    y1 = max(0, obj['y1'])
+                    x2 = min(im_w - 1, obj['x2'])
+                    y2 = min(im_h - 1, obj['y2'])
+                    gt_bbox[i] = [x1, y1, x2, y2]
+                    is_crowd[i][0] = 0
+                    if 'mask' in obj:
+                        mask_dict = {}
+                        mask_dict['size'] = [im_h, im_w]
+                        mask_dict['counts'] = obj['mask'].encode()
+                        mask = decode(mask_dict)
+                        gt_poly[i] = self.mask2polygon(mask)
+                    annotations['annotations'].append({
+                        'iscrowd':
+                        0,
+                        'image_id':
+                        int(im_id[0]),
+                        'bbox': [x1, y1, x2 - x1 + 1, y2 - y1 + 1],
+                        'area':
+                        float((x2 - x1 + 1) * (y2 - y1 + 1)),
+                        'segmentation':
+                        [[x1, y1, x1, y2, x2, y2, x2, y1]] if gt_poly[i] is None else gt_poly[i],
+                        'category_id':
+                        cname2cid[cname],
+                        'id':
+                        ann_ct,
+                        'difficult':
+                        0
+                    })
+                    ann_ct += 1
+                im_info = {
+                    'im_id': im_id,
+                    'image_shape': np.array([im_h, im_w]).astype('int32'),
+                }
+                label_info = {
+                    'is_crowd': is_crowd,
+                    'gt_class': gt_class,
+                    'gt_bbox': gt_bbox,
+                    'gt_score': gt_score,
+                    'difficult': difficult
+                }
+                if None not in gt_poly:
+                    label_info['gt_poly'] = gt_poly
+                voc_rec = (im_info, label_info)
+                if len(objs) != 0:
+                    self.file_list.append([img_file, voc_rec])
+                    ct += 1
+                    annotations['images'].append({
+                        'height':
+                        im_h,
+                        'width':
+                        im_w,
+                        'id':
+                        int(im_id[0]),
+                        'file_name':
+                        osp.split(img_file)[1]
+                    })
+
+        if not len(self.file_list) > 0:
+            raise Exception('not found any voc record in %s' % (file_list))
+        logging.info("{} samples in file {}".format(
+            len(self.file_list), file_list))
+        self.num_samples = len(self.file_list)
+        from pycocotools.coco import COCO
+        self.coco_gt = COCO()
+        self.coco_gt.dataset = annotations
+        self.coco_gt.createIndex()
+        
+    def mask2polygon(self, mask):
+        contours, hierarchy = cv2.findContours(
+            (mask).astype(np.uint8), cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
+        segmentation = []
+        for contour in contours:
+            contour_list = contour.flatten().tolist()
+            if len(contour_list) > 4:
+                segmentation.append(contour_list)
+        return segmentation

+ 116 - 0
paddlex/cv/datasets/easydata_seg.py

@@ -0,0 +1,116 @@
+# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os.path as osp
+import random
+import copy
+import json
+import cv2
+import numpy as np
+import paddlex.utils.logging as logging
+from .dataset import Dataset
+from .dataset import get_encoding
+from .dataset import is_pic
+
+class EasyDataSeg(Dataset):
+    """读取EasyDataSeg语义分割任务数据集,并对样本进行相应的处理。
+
+    Args:
+        data_dir (str): 数据集所在的目录路径。
+        file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
+        label_list (str): 描述数据集包含的类别信息文件路径。
+        transforms (list): 数据集中每个样本的预处理/增强算子。
+        num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
+        buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
+        parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
+        shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
+    """
+
+    def __init__(self,
+                 data_dir,
+                 file_list,
+                 label_list,
+                 transforms=None,
+                 num_workers='auto',
+                 buffer_size=100,
+                 parallel_method='process',
+                 shuffle=False):
+        super(EasyDataSeg, self).__init__(
+            transforms=transforms,
+            num_workers=num_workers,
+            buffer_size=buffer_size,
+            parallel_method=parallel_method,
+            shuffle=shuffle)
+        self.file_list = list()
+        self.labels = list()
+        self._epoch = 0
+
+        from pycocotools.mask import decode
+        cname2cid = {}
+        label_id = 0
+        with open(label_list, encoding=get_encoding(label_list)) as fr:
+            for line in fr.readlines():
+                cname2cid[line.strip()] = label_id
+                label_id += 1
+                self.labels.append(line.strip())
+                
+        with open(file_list, encoding=get_encoding(file_list)) as f:
+            for line in f:
+                img_file, json_file = [osp.join(data_dir, x) \
+                        for x in line.strip().split()[:2]]
+                if not is_pic(img_file):
+                    continue
+                if not osp.isfile(json_file):
+                    continue
+                if not osp.exists(img_file):
+                    raise IOError(
+                        'The image file {} is not exist!'.format(img_file))
+                with open(json_file, mode='r', \
+                          encoding=get_encoding(json_file)) as j:
+                    json_info = json.load(j)
+                im = cv2.imread(img_file)
+                im_w = im.shape[1]
+                im_h = im.shape[0]
+                objs = json_info['labels']
+                lable_npy = np.zeros([im_h, im_w]).astype('uint8')
+                for i, obj in enumerate(objs):
+                    cname = obj['name']
+                    cid = cname2cid[cname]
+                    mask_dict = {}
+                    mask_dict['size'] = [im_h, im_w]
+                    mask_dict['counts'] = obj['mask'].encode()
+                    mask = decode(mask_dict)
+                    mask *= cid
+                    conflict_index = np.where(((lable_npy > 0) & (mask == cid)) == True)
+                    mask[conflict_index] = 0
+                    lable_npy += mask
+                self.file_list.append([img_file, lable_npy])
+        self.num_samples = len(self.file_list)
+        logging.info("{} samples in file {}".format(
+            len(self.file_list), file_list))
+
+    def iterator(self):
+        self._epoch += 1
+        self._pos = 0
+        files = copy.deepcopy(self.file_list)
+        if self.shuffle:
+            random.shuffle(files)
+        files = files[:self.num_samples]
+        self.num_samples = len(files)
+        for f in files:
+            lable_npy = f[1]
+            sample = [f[0], None, lable_npy]
+            yield sample

+ 1 - 1
paddlex/cv/datasets/imagenet.py

@@ -35,7 +35,7 @@ class ImageNet(Dataset):
             数的一半。
             数的一半。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
-            线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
     """
     """
 
 

+ 1 - 1
paddlex/cv/datasets/seg_dataset.py

@@ -33,7 +33,7 @@ class SegDataset(Dataset):
         num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
         num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
-            线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
     """
     """
 
 

+ 2 - 2
paddlex/cv/datasets/voc.py

@@ -38,7 +38,7 @@ class VOCDetection(Dataset):
             一半。
             一半。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
         parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
-            线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。
+            线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
         shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
     """
     """
 
 
@@ -146,7 +146,7 @@ class VOCDetection(Dataset):
 
 
                 im_info = {
                 im_info = {
                     'im_id': im_id,
                     'im_id': im_id,
-                    'origin_shape': np.array([im_h, im_w]).astype('int32'),
+                    'image_shape': np.array([im_h, im_w]).astype('int32'),
                 }
                 }
                 label_info = {
                 label_info = {
                     'is_crowd': is_crowd,
                     'is_crowd': is_crowd,

+ 5 - 2
paddlex/cv/models/base.py

@@ -255,7 +255,10 @@ class BaseAPI:
             if osp.exists(save_dir):
             if osp.exists(save_dir):
                 os.remove(save_dir)
                 os.remove(save_dir)
             os.makedirs(save_dir)
             os.makedirs(save_dir)
-        fluid.save(self.train_prog, osp.join(save_dir, 'model'))
+        if self.train_prog is not None:
+            fluid.save(self.train_prog, osp.join(save_dir, 'model'))
+        else:
+            fluid.save(self.test_prog, osp.join(save_dir, 'model'))
         model_info = self.get_model_info()
         model_info = self.get_model_info()
         model_info['status'] = self.status
         model_info['status'] = self.status
         with open(
         with open(
@@ -317,11 +320,11 @@ class BaseAPI:
         model_info['_ModelInputsOutputs']['test_outputs'] = [
         model_info['_ModelInputsOutputs']['test_outputs'] = [
             [k, v.name] for k, v in self.test_outputs.items()
             [k, v.name] for k, v in self.test_outputs.items()
         ]
         ]
-
         with open(
         with open(
                 osp.join(save_dir, 'model.yml'), encoding='utf-8',
                 osp.join(save_dir, 'model.yml'), encoding='utf-8',
                 mode='w') as f:
                 mode='w') as f:
             yaml.dump(model_info, f)
             yaml.dump(model_info, f)
+
         # 模型保存成功的标志
         # 模型保存成功的标志
         open(osp.join(save_dir, '.success'), 'w').close()
         open(osp.join(save_dir, '.success'), 'w').close()
         logging.info(
         logging.info(

+ 10 - 2
paddlex/cv/models/classifier.py

@@ -46,10 +46,18 @@ class BaseClassifier(BaseAPI):
         self.model_name = model_name
         self.model_name = model_name
         self.labels = None
         self.labels = None
         self.num_classes = num_classes
         self.num_classes = num_classes
+        self.fixed_input_shape = None
 
 
     def build_net(self, mode='train'):
     def build_net(self, mode='train'):
-        image = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            image = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            image = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if mode != 'test':
         if mode != 'test':
             label = fluid.data(dtype='int64', shape=[None, 1], name='label')
             label = fluid.data(dtype='int64', shape=[None, 1], name='label')
         model = getattr(paddlex.cv.nets, str.lower(self.model_name))
         model = getattr(paddlex.cv.nets, str.lower(self.model_name))

+ 10 - 9
paddlex/cv/models/deeplabv3p.py

@@ -48,7 +48,6 @@ class DeepLabv3p(BaseAPI):
             自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None时,各类的权重1,
             自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None时,各类的权重1,
             即平时使用的交叉熵损失函数。
             即平时使用的交叉熵损失函数。
         ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。默认255。
         ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。默认255。
-
     Raises:
     Raises:
         ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
         ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
         ValueError: backbone取值不在['Xception65', 'Xception41', 'MobileNetV2_x0.25',
         ValueError: backbone取值不在['Xception65', 'Xception41', 'MobileNetV2_x0.25',
@@ -118,6 +117,7 @@ class DeepLabv3p(BaseAPI):
         self.enable_decoder = enable_decoder
         self.enable_decoder = enable_decoder
         self.labels = None
         self.labels = None
         self.sync_bn = True
         self.sync_bn = True
+        self.fixed_input_shape = None
 
 
     def _get_backbone(self, backbone):
     def _get_backbone(self, backbone):
         def mobilenetv2(backbone):
         def mobilenetv2(backbone):
@@ -182,7 +182,8 @@ class DeepLabv3p(BaseAPI):
             use_bce_loss=self.use_bce_loss,
             use_bce_loss=self.use_bce_loss,
             use_dice_loss=self.use_dice_loss,
             use_dice_loss=self.use_dice_loss,
             class_weight=self.class_weight,
             class_weight=self.class_weight,
-            ignore_index=self.ignore_index)
+            ignore_index=self.ignore_index,
+            fixed_input_shape=self.fixed_input_shape)
         inputs = model.generate_inputs()
         inputs = model.generate_inputs()
         model_out = model.build_net(inputs)
         model_out = model.build_net(inputs)
         outputs = OrderedDict()
         outputs = OrderedDict()
@@ -396,13 +397,13 @@ class DeepLabv3p(BaseAPI):
             fetch_list=list(self.test_outputs.values()))
             fetch_list=list(self.test_outputs.values()))
         pred = result[0]
         pred = result[0]
         pred = np.squeeze(pred).astype('uint8')
         pred = np.squeeze(pred).astype('uint8')
-        keys = list(im_info.keys())
-        for k in keys[::-1]:
-            if k == 'shape_before_resize':
-                h, w = im_info[k][0], im_info[k][1]
+        for info in im_info[::-1]:
+            if info[0] == 'resize':
+                w, h = info[1][1], info[1][0]
                 pred = cv2.resize(pred, (w, h), cv2.INTER_NEAREST)
                 pred = cv2.resize(pred, (w, h), cv2.INTER_NEAREST)
-            elif k == 'shape_before_padding':
-                h, w = im_info[k][0], im_info[k][1]
+            elif info[0] == 'padding':
+                w, h = info[1][1], info[1][0]
                 pred = pred[0:h, 0:w]
                 pred = pred[0:h, 0:w]
-
+            else:
+                raise Exception("Unexpected info '{}' in im_info".format(info[0]))
         return {'label_map': pred, 'score_map': result[1]}
         return {'label_map': pred, 'score_map': result[1]}

+ 7 - 4
paddlex/cv/models/faster_rcnn.py

@@ -57,6 +57,7 @@ class FasterRCNN(BaseAPI):
         self.aspect_ratios = aspect_ratios
         self.aspect_ratios = aspect_ratios
         self.anchor_sizes = anchor_sizes
         self.anchor_sizes = anchor_sizes
         self.labels = None
         self.labels = None
+        self.fixed_input_shape = None
 
 
     def _get_backbone(self, backbone_name):
     def _get_backbone(self, backbone_name):
         norm_type = None
         norm_type = None
@@ -109,7 +110,8 @@ class FasterRCNN(BaseAPI):
             aspect_ratios=self.aspect_ratios,
             aspect_ratios=self.aspect_ratios,
             anchor_sizes=self.anchor_sizes,
             anchor_sizes=self.anchor_sizes,
             train_pre_nms_top_n=train_pre_nms_top_n,
             train_pre_nms_top_n=train_pre_nms_top_n,
-            test_pre_nms_top_n=test_pre_nms_top_n)
+            test_pre_nms_top_n=test_pre_nms_top_n,
+            fixed_input_shape=self.fixed_input_shape)
         inputs = model.generate_inputs()
         inputs = model.generate_inputs()
         if mode == 'train':
         if mode == 'train':
             model_out = model.build_net(inputs)
             model_out = model.build_net(inputs)
@@ -178,7 +180,7 @@ class FasterRCNN(BaseAPI):
             log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为20。
             log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为20。
             save_dir (str): 模型保存路径。默认值为'output'。
             save_dir (str): 模型保存路径。默认值为'output'。
             pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
             pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
-                则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为None
+                则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为'IMAGENET'
             optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
             optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
                 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
                 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
             learning_rate (float): 默认优化器的初始学习率。默认为0.0025。
             learning_rate (float): 默认优化器的初始学习率。默认为0.0025。
@@ -199,11 +201,12 @@ class FasterRCNN(BaseAPI):
         if metric is None:
         if metric is None:
             if isinstance(train_dataset, paddlex.datasets.CocoDetection):
             if isinstance(train_dataset, paddlex.datasets.CocoDetection):
                 metric = 'COCO'
                 metric = 'COCO'
-            elif isinstance(train_dataset, paddlex.datasets.VOCDetection):
+            elif isinstance(train_dataset, paddlex.datasets.VOCDetection) or \
+                    isinstance(train_dataset, paddlex.datasets.EasyDataDet):
                 metric = 'VOC'
                 metric = 'VOC'
             else:
             else:
                 raise ValueError(
                 raise ValueError(
-                    "train_dataset should be datasets.VOCDetection or datasets.COCODetection."
+                    "train_dataset should be datasets.VOCDetection or datasets.COCODetection or datasets.EasyDataDet."
                 )
                 )
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         self.metric = metric
         self.metric = metric

+ 31 - 1
paddlex/cv/models/load_model.py

@@ -23,7 +23,7 @@ import paddlex
 import paddlex.utils.logging as logging
 import paddlex.utils.logging as logging
 
 
 
 
-def load_model(model_dir):
+def load_model(model_dir, fixed_input_shape=None):
     if not osp.exists(osp.join(model_dir, "model.yml")):
     if not osp.exists(osp.join(model_dir, "model.yml")):
         raise Exception("There's not model.yml in {}".format(model_dir))
         raise Exception("There's not model.yml in {}".format(model_dir))
     with open(osp.join(model_dir, "model.yml")) as f:
     with open(osp.join(model_dir, "model.yml")) as f:
@@ -44,6 +44,7 @@ def load_model(model_dir):
     else:
     else:
         model = getattr(paddlex.cv.models,
         model = getattr(paddlex.cv.models,
                         info['Model'])(**info['_init_params'])
                         info['Model'])(**info['_init_params'])
+    model.fixed_input_shape = fixed_input_shape
     if status == "Normal" or \
     if status == "Normal" or \
             status == "Prune" or status == "fluid.save":
             status == "Prune" or status == "fluid.save":
         startup_prog = fluid.Program()
         startup_prog = fluid.Program()
@@ -78,6 +79,8 @@ def load_model(model_dir):
             model.test_outputs[var_desc[0]] = out
             model.test_outputs[var_desc[0]] = out
     if 'Transforms' in info:
     if 'Transforms' in info:
         transforms_mode = info.get('TransformsMode', 'RGB')
         transforms_mode = info.get('TransformsMode', 'RGB')
+        # 固定模型的输入shape
+        fix_input_shape(info, fixed_input_shape=fixed_input_shape)
         if transforms_mode == 'RGB':
         if transforms_mode == 'RGB':
             to_rgb = True
             to_rgb = True
         else:
         else:
@@ -102,6 +105,33 @@ def load_model(model_dir):
     return model
     return model
 
 
 
 
+def fix_input_shape(info, fixed_input_shape=None):
+    if fixed_input_shape is not None:
+        resize = {'ResizeByShort': {}}
+        padding = {'Padding': {}}
+        if info['_Attributes']['model_type'] == 'classifier':
+            crop_size = 0
+            for transform in info['Transforms']:
+                if 'CenterCrop' in transform:
+                    crop_size = transform['CenterCrop']['crop_size']
+                    break
+            assert crop_size == fixed_input_shape[
+                0], "fixed_input_shape must == CenterCrop:crop_size:{}".format(
+                    crop_size)
+            assert crop_size == fixed_input_shape[
+                1], "fixed_input_shape must == CenterCrop:crop_size:{}".format(
+                    crop_size)
+            if crop_size == 0:
+                logging.warning(
+                    "fixed_input_shape must == input shape when trainning")
+        else:
+            resize['ResizeByShort']['short_size'] = min(fixed_input_shape)
+            resize['ResizeByShort']['max_size'] = max(fixed_input_shape)
+            padding['Padding']['target_size'] = list(fixed_input_shape)
+            info['Transforms'].append(resize)
+            info['Transforms'].append(padding)
+
+
 def build_transforms(model_type, transforms_info, to_rgb=True):
 def build_transforms(model_type, transforms_info, to_rgb=True):
     if model_type == "classifier":
     if model_type == "classifier":
         import paddlex.cv.transforms.cls_transforms as T
         import paddlex.cv.transforms.cls_transforms as T

+ 6 - 3
paddlex/cv/models/mask_rcnn.py

@@ -60,6 +60,7 @@ class MaskRCNN(FasterRCNN):
             self.mask_head_resolution = 28
             self.mask_head_resolution = 28
         else:
         else:
             self.mask_head_resolution = 14
             self.mask_head_resolution = 14
+        self.fixed_input_shape = None
 
 
     def build_net(self, mode='train'):
     def build_net(self, mode='train'):
         train_pre_nms_top_n = 2000 if self.with_fpn else 12000
         train_pre_nms_top_n = 2000 if self.with_fpn else 12000
@@ -73,7 +74,8 @@ class MaskRCNN(FasterRCNN):
             train_pre_nms_top_n=train_pre_nms_top_n,
             train_pre_nms_top_n=train_pre_nms_top_n,
             test_pre_nms_top_n=test_pre_nms_top_n,
             test_pre_nms_top_n=test_pre_nms_top_n,
             num_convs=num_convs,
             num_convs=num_convs,
-            mask_head_resolution=self.mask_head_resolution)
+            mask_head_resolution=self.mask_head_resolution,
+            fixed_input_shape=self.fixed_input_shape)
         inputs = model.generate_inputs()
         inputs = model.generate_inputs()
         if mode == 'train':
         if mode == 'train':
             model_out = model.build_net(inputs)
             model_out = model.build_net(inputs)
@@ -162,11 +164,12 @@ class MaskRCNN(FasterRCNN):
             ValueError: 模型从inference model进行加载。
             ValueError: 模型从inference model进行加载。
         """
         """
         if metric is None:
         if metric is None:
-            if isinstance(train_dataset, paddlex.datasets.CocoDetection):
+            if isinstance(train_dataset, paddlex.datasets.CocoDetection) or \
+                    isinstance(train_dataset, paddlex.datasets.EasyDataDet):
                 metric = 'COCO'
                 metric = 'COCO'
             else:
             else:
                 raise Exception(
                 raise Exception(
-                    "train_dataset should be datasets.COCODetection.")
+                    "train_dataset should be datasets.COCODetection or datasets.EasyDataDet.")
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         self.metric = metric
         self.metric = metric
         if not self.trainable:
         if not self.trainable:

+ 3 - 1
paddlex/cv/models/unet.py

@@ -77,6 +77,7 @@ class UNet(DeepLabv3p):
         self.class_weight = class_weight
         self.class_weight = class_weight
         self.ignore_index = ignore_index
         self.ignore_index = ignore_index
         self.labels = None
         self.labels = None
+        self.fixed_input_shape = None
 
 
     def build_net(self, mode='train'):
     def build_net(self, mode='train'):
         model = paddlex.cv.nets.segmentation.UNet(
         model = paddlex.cv.nets.segmentation.UNet(
@@ -86,7 +87,8 @@ class UNet(DeepLabv3p):
             use_bce_loss=self.use_bce_loss,
             use_bce_loss=self.use_bce_loss,
             use_dice_loss=self.use_dice_loss,
             use_dice_loss=self.use_dice_loss,
             class_weight=self.class_weight,
             class_weight=self.class_weight,
-            ignore_index=self.ignore_index)
+            ignore_index=self.ignore_index,
+            fixed_input_shape=self.fixed_input_shape)
         inputs = model.generate_inputs()
         inputs = model.generate_inputs()
         model_out = model.build_net(inputs)
         model_out = model.build_net(inputs)
         outputs = OrderedDict()
         outputs = OrderedDict()

+ 7 - 4
paddlex/cv/models/yolo_v3.py

@@ -80,6 +80,7 @@ class YOLOv3(BaseAPI):
         self.label_smooth = label_smooth
         self.label_smooth = label_smooth
         self.sync_bn = True
         self.sync_bn = True
         self.train_random_shapes = train_random_shapes
         self.train_random_shapes = train_random_shapes
+        self.fixed_input_shape = None
 
 
     def _get_backbone(self, backbone_name):
     def _get_backbone(self, backbone_name):
         if backbone_name == 'DarkNet53':
         if backbone_name == 'DarkNet53':
@@ -113,7 +114,8 @@ class YOLOv3(BaseAPI):
             nms_topk=self.nms_topk,
             nms_topk=self.nms_topk,
             nms_keep_topk=self.nms_keep_topk,
             nms_keep_topk=self.nms_keep_topk,
             nms_iou_threshold=self.nms_iou_threshold,
             nms_iou_threshold=self.nms_iou_threshold,
-            train_random_shapes=self.train_random_shapes)
+            train_random_shapes=self.train_random_shapes,
+            fixed_input_shape=self.fixed_input_shape)
         inputs = model.generate_inputs()
         inputs = model.generate_inputs()
         model_out = model.build_net(inputs)
         model_out = model.build_net(inputs)
         outputs = OrderedDict([('bbox', model_out)])
         outputs = OrderedDict([('bbox', model_out)])
@@ -177,7 +179,7 @@ class YOLOv3(BaseAPI):
             log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为10。
             log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为10。
             save_dir (str): 模型保存路径。默认值为'output'。
             save_dir (str): 模型保存路径。默认值为'output'。
             pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
             pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
-                则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为None
+                则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为'IMAGENET'
             optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
             optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
                 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
                 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
             learning_rate (float): 默认优化器的学习率。默认为1.0/8000。
             learning_rate (float): 默认优化器的学习率。默认为1.0/8000。
@@ -203,11 +205,12 @@ class YOLOv3(BaseAPI):
         if metric is None:
         if metric is None:
             if isinstance(train_dataset, paddlex.datasets.CocoDetection):
             if isinstance(train_dataset, paddlex.datasets.CocoDetection):
                 metric = 'COCO'
                 metric = 'COCO'
-            elif isinstance(train_dataset, paddlex.datasets.VOCDetection):
+            elif isinstance(train_dataset, paddlex.datasets.VOCDetection) or \
+                    isinstance(train_dataset, paddlex.datasets.EasyDataDet):
                 metric = 'VOC'
                 metric = 'VOC'
             else:
             else:
                 raise ValueError(
                 raise ValueError(
-                    "train_dataset should be datasets.VOCDetection or datasets.COCODetection."
+                    "train_dataset should be datasets.VOCDetection or datasets.COCODetection or datasets.EasyDataDet."
                 )
                 )
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
         self.metric = metric
         self.metric = metric

+ 13 - 3
paddlex/cv/nets/detection/faster_rcnn.py

@@ -76,7 +76,8 @@ class FasterRCNN(object):
             fg_thresh=.5,
             fg_thresh=.5,
             bg_thresh_hi=.5,
             bg_thresh_hi=.5,
             bg_thresh_lo=0.,
             bg_thresh_lo=0.,
-            bbox_reg_weights=[0.1, 0.1, 0.2, 0.2]):
+            bbox_reg_weights=[0.1, 0.1, 0.2, 0.2],
+            fixed_input_shape=None):
         super(FasterRCNN, self).__init__()
         super(FasterRCNN, self).__init__()
         self.backbone = backbone
         self.backbone = backbone
         self.mode = mode
         self.mode = mode
@@ -148,6 +149,7 @@ class FasterRCNN(object):
         self.bg_thresh_lo = bg_thresh_lo
         self.bg_thresh_lo = bg_thresh_lo
         self.bbox_reg_weights = bbox_reg_weights
         self.bbox_reg_weights = bbox_reg_weights
         self.rpn_only = rpn_only
         self.rpn_only = rpn_only
+        self.fixed_input_shape = fixed_input_shape
 
 
     def build_net(self, inputs):
     def build_net(self, inputs):
         im = inputs['image']
         im = inputs['image']
@@ -219,8 +221,16 @@ class FasterRCNN(object):
 
 
     def generate_inputs(self):
     def generate_inputs(self):
         inputs = OrderedDict()
         inputs = OrderedDict()
-        inputs['image'] = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if self.mode == 'train':
         if self.mode == 'train':
             inputs['im_info'] = fluid.data(
             inputs['im_info'] = fluid.data(
                 dtype='float32', shape=[None, 3], name='im_info')
                 dtype='float32', shape=[None, 3], name='im_info')

+ 13 - 3
paddlex/cv/nets/detection/mask_rcnn.py

@@ -86,7 +86,8 @@ class MaskRCNN(object):
             fg_thresh=.5,
             fg_thresh=.5,
             bg_thresh_hi=.5,
             bg_thresh_hi=.5,
             bg_thresh_lo=0.,
             bg_thresh_lo=0.,
-            bbox_reg_weights=[0.1, 0.1, 0.2, 0.2]):
+            bbox_reg_weights=[0.1, 0.1, 0.2, 0.2],
+            fixed_input_shape=None):
         super(MaskRCNN, self).__init__()
         super(MaskRCNN, self).__init__()
         self.backbone = backbone
         self.backbone = backbone
         self.mode = mode
         self.mode = mode
@@ -167,6 +168,7 @@ class MaskRCNN(object):
         self.bg_thresh_lo = bg_thresh_lo
         self.bg_thresh_lo = bg_thresh_lo
         self.bbox_reg_weights = bbox_reg_weights
         self.bbox_reg_weights = bbox_reg_weights
         self.rpn_only = rpn_only
         self.rpn_only = rpn_only
+        self.fixed_input_shape = fixed_input_shape
 
 
     def build_net(self, inputs):
     def build_net(self, inputs):
         im = inputs['image']
         im = inputs['image']
@@ -306,8 +308,16 @@ class MaskRCNN(object):
 
 
     def generate_inputs(self):
     def generate_inputs(self):
         inputs = OrderedDict()
         inputs = OrderedDict()
-        inputs['image'] = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if self.mode == 'train':
         if self.mode == 'train':
             inputs['im_info'] = fluid.data(
             inputs['im_info'] = fluid.data(
                 dtype='float32', shape=[None, 3], name='im_info')
                 dtype='float32', shape=[None, 3], name='im_info')

+ 12 - 3
paddlex/cv/nets/detection/yolo_v3.py

@@ -33,7 +33,8 @@ class YOLOv3:
                  nms_iou_threshold=0.45,
                  nms_iou_threshold=0.45,
                  train_random_shapes=[
                  train_random_shapes=[
                      320, 352, 384, 416, 448, 480, 512, 544, 576, 608
                      320, 352, 384, 416, 448, 480, 512, 544, 576, 608
-                 ]):
+                 ],
+                 fixed_input_shape=None):
         if anchors is None:
         if anchors is None:
             anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
             anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
                        [59, 119], [116, 90], [156, 198], [373, 326]]
                        [59, 119], [116, 90], [156, 198], [373, 326]]
@@ -54,6 +55,7 @@ class YOLOv3:
         self.norm_decay = 0.0
         self.norm_decay = 0.0
         self.prefix_name = ''
         self.prefix_name = ''
         self.train_random_shapes = train_random_shapes
         self.train_random_shapes = train_random_shapes
+        self.fixed_input_shape = fixed_input_shape
 
 
     def _head(self, feats):
     def _head(self, feats):
         outputs = []
         outputs = []
@@ -247,8 +249,15 @@ class YOLOv3:
 
 
     def generate_inputs(self):
     def generate_inputs(self):
         inputs = OrderedDict()
         inputs = OrderedDict()
-        inputs['image'] = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if self.mode == 'train':
         if self.mode == 'train':
             inputs['gt_box'] = fluid.data(
             inputs['gt_box'] = fluid.data(
                 dtype='float32', shape=[None, None, 4], name='gt_box')
                 dtype='float32', shape=[None, None, 4], name='gt_box')

+ 14 - 3
paddlex/cv/nets/segmentation/deeplabv3p.py

@@ -61,6 +61,7 @@ class DeepLabv3p(object):
             自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,
             自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,
             即平时使用的交叉熵损失函数。
             即平时使用的交叉熵损失函数。
         ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。
         ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。
+        fixed_input_shape (list): 长度为2,维度为1的list,如:[640,720],用来固定模型输入:'image'的shape,默认为None。
 
 
     Raises:
     Raises:
         ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
         ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
@@ -81,7 +82,8 @@ class DeepLabv3p(object):
                  use_bce_loss=False,
                  use_bce_loss=False,
                  use_dice_loss=False,
                  use_dice_loss=False,
                  class_weight=None,
                  class_weight=None,
-                 ignore_index=255):
+                 ignore_index=255,
+                 fixed_input_shape=None):
         # dice_loss或bce_loss只适用两类分割中
         # dice_loss或bce_loss只适用两类分割中
         if num_classes > 2 and (use_bce_loss or use_dice_loss):
         if num_classes > 2 and (use_bce_loss or use_dice_loss):
             raise ValueError(
             raise ValueError(
@@ -115,6 +117,7 @@ class DeepLabv3p(object):
         self.decoder_use_sep_conv = decoder_use_sep_conv
         self.decoder_use_sep_conv = decoder_use_sep_conv
         self.encoder_with_aspp = encoder_with_aspp
         self.encoder_with_aspp = encoder_with_aspp
         self.enable_decoder = enable_decoder
         self.enable_decoder = enable_decoder
+        self.fixed_input_shape = fixed_input_shape
 
 
     def _encoder(self, input):
     def _encoder(self, input):
         # 编码器配置,采用ASPP架构,pooling + 1x1_conv + 三个不同尺度的空洞卷积并行, concat后1x1conv
         # 编码器配置,采用ASPP架构,pooling + 1x1_conv + 三个不同尺度的空洞卷积并行, concat后1x1conv
@@ -310,8 +313,16 @@ class DeepLabv3p(object):
 
 
     def generate_inputs(self):
     def generate_inputs(self):
         inputs = OrderedDict()
         inputs = OrderedDict()
-        inputs['image'] = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if self.mode == 'train':
         if self.mode == 'train':
             inputs['label'] = fluid.data(
             inputs['label'] = fluid.data(
                 dtype='int32', shape=[None, 1, None, None], name='label')
                 dtype='int32', shape=[None, 1, None, None], name='label')

+ 14 - 3
paddlex/cv/nets/segmentation/unet.py

@@ -54,6 +54,7 @@ class UNet(object):
                 自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,
                 自行计算相应的权重,每一类的权重为:每类的比例 * num_classes。class_weight取默认值None是,各类的权重1,
                 即平时使用的交叉熵损失函数。
                 即平时使用的交叉熵损失函数。
             ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。
             ignore_index (int): label上忽略的值,label为ignore_index的像素不参与损失函数的计算。
+            fixed_input_shape (list): 长度为2,维度为1的list,如:[640,720],用来固定模型输入:'image'的shape,默认为None。
 
 
         Raises:
         Raises:
             ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
             ValueError: use_bce_loss或use_dice_loss为真且num_calsses > 2。
@@ -69,7 +70,8 @@ class UNet(object):
                  use_bce_loss=False,
                  use_bce_loss=False,
                  use_dice_loss=False,
                  use_dice_loss=False,
                  class_weight=None,
                  class_weight=None,
-                 ignore_index=255):
+                 ignore_index=255,
+                 fixed_input_shape=None):
         # dice_loss或bce_loss只适用两类分割中
         # dice_loss或bce_loss只适用两类分割中
         if num_classes > 2 and (use_bce_loss or use_dice_loss):
         if num_classes > 2 and (use_bce_loss or use_dice_loss):
             raise Exception(
             raise Exception(
@@ -97,6 +99,7 @@ class UNet(object):
         self.use_dice_loss = use_dice_loss
         self.use_dice_loss = use_dice_loss
         self.class_weight = class_weight
         self.class_weight = class_weight
         self.ignore_index = ignore_index
         self.ignore_index = ignore_index
+        self.fixed_input_shape = fixed_input_shape
 
 
     def _double_conv(self, data, out_ch):
     def _double_conv(self, data, out_ch):
         param_attr = fluid.ParamAttr(
         param_attr = fluid.ParamAttr(
@@ -226,8 +229,16 @@ class UNet(object):
 
 
     def generate_inputs(self):
     def generate_inputs(self):
         inputs = OrderedDict()
         inputs = OrderedDict()
-        inputs['image'] = fluid.data(
-            dtype='float32', shape=[None, 3, None, None], name='image')
+
+        if self.fixed_input_shape is not None:
+            input_shape = [
+                None, 3, self.fixed_input_shape[1], self.fixed_input_shape[0]
+            ]
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=input_shape, name='image')
+        else:
+            inputs['image'] = fluid.data(
+                dtype='float32', shape=[None, 3, None, None], name='image')
         if self.mode == 'train':
         if self.mode == 'train':
             inputs['label'] = fluid.data(
             inputs['label'] = fluid.data(
                 dtype='int32', shape=[None, 1, None, None], name='label')
                 dtype='int32', shape=[None, 1, None, None], name='label')

+ 78 - 51
paddlex/cv/transforms/det_transforms.py

@@ -58,8 +58,8 @@ class Compose:
             im (str/np.ndarray): 图像路径/图像np.ndarray数据。
             im (str/np.ndarray): 图像路径/图像np.ndarray数据。
             im_info (dict): 存储与图像相关的信息,dict中的字段如下:
             im_info (dict): 存储与图像相关的信息,dict中的字段如下:
                 - im_id (np.ndarray): 图像序列号,形状为(1,)。
                 - im_id (np.ndarray): 图像序列号,形状为(1,)。
-                - origin_shape (np.ndarray): 图像原始大小,形状为(2,),
-                                        origin_shape[0]为高,origin_shape[1]为宽。
+                - image_shape (np.ndarray): 图像原始大小,形状为(2,),
+                                        image_shape[0]为高,image_shape[1]为宽。
                 - mixup (list): list为[im, im_info, label_info],分别对应
                 - mixup (list): list为[im, im_info, label_info],分别对应
                                 与当前图像进行mixup的图像np.ndarray数据、图像相关信息、标注框相关信息;
                                 与当前图像进行mixup的图像np.ndarray数据、图像相关信息、标注框相关信息;
                                 注意,当前epoch若无需进行mixup,则无该字段。
                                 注意,当前epoch若无需进行mixup,则无该字段。
@@ -93,9 +93,8 @@ class Compose:
             # make default im_info with [h, w, 1]
             # make default im_info with [h, w, 1]
             im_info['im_resize_info'] = np.array(
             im_info['im_resize_info'] = np.array(
                 [im.shape[0], im.shape[1], 1.], dtype=np.float32)
                 [im.shape[0], im.shape[1], 1.], dtype=np.float32)
-            # copy augment_shape from origin_shape
-            im_info['augment_shape'] = np.array([im.shape[0],
-                                                 im.shape[1]]).astype('int32')
+            im_info['image_shape'] = np.array([im.shape[0],
+                                               im.shape[1]]).astype('int32')
             if not self.use_mixup:
             if not self.use_mixup:
                 if 'mixup' in im_info:
                 if 'mixup' in im_info:
                     del im_info['mixup']
                     del im_info['mixup']
@@ -196,11 +195,16 @@ class ResizeByShort:
 
 
 
 
 class Padding:
 class Padding:
-    """将图像的长和宽padding至coarsest_stride的倍数。如输入图像为[300, 640],
+    """1.将图像的长和宽padding至coarsest_stride的倍数。如输入图像为[300, 640],
        `coarest_stride`为32,则由于300不为32的倍数,因此在图像最右和最下使用0值
        `coarest_stride`为32,则由于300不为32的倍数,因此在图像最右和最下使用0值
        进行padding,最终输出图像为[320, 640]。
        进行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则直接返回。
+    1. 如果coarsest_stride为1,target_size为None则直接返回。
     2. 获取图像的高H、宽W。
     2. 获取图像的高H、宽W。
     3. 计算填充后图像的高H_new、宽W_new。
     3. 计算填充后图像的高H_new、宽W_new。
     4. 构建大小为(H_new, W_new, 3)像素值为0的np.ndarray,
     4. 构建大小为(H_new, W_new, 3)像素值为0的np.ndarray,
@@ -208,10 +212,26 @@ class Padding:
 
 
     Args:
     Args:
         coarsest_stride (int): 填充后的图像长、宽为该参数的倍数,默认为1。
         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):
+    def __init__(self, coarsest_stride=1, target_size=None):
         self.coarsest_stride = coarsest_stride
         self.coarsest_stride = coarsest_stride
+        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.")
+        self.target_size = target_size
 
 
     def __call__(self, im, im_info=None, label_info=None):
     def __call__(self, im, im_info=None, label_info=None):
         """
         """
@@ -228,13 +248,9 @@ class Padding:
         Raises:
         Raises:
             TypeError: 形参数据类型不满足需求。
             TypeError: 形参数据类型不满足需求。
             ValueError: 数据长度不匹配。
             ValueError: 数据长度不匹配。
+            ValueError: coarsest_stride,target_size需有且只有一个被指定。
+            ValueError: target_size小于原图的大小。
         """
         """
-
-        if self.coarsest_stride == 1:
-            if label_info is None:
-                return (im, im_info)
-            else:
-                return (im, im_info, label_info)
         if im_info is None:
         if im_info is None:
             im_info = dict()
             im_info = dict()
         if not isinstance(im, np.ndarray):
         if not isinstance(im, np.ndarray):
@@ -242,11 +258,29 @@ class Padding:
         if len(im.shape) != 3:
         if len(im.shape) != 3:
             raise ValueError('Padding: image is not 3-dimensional.')
             raise ValueError('Padding: image is not 3-dimensional.')
         im_h, im_w, im_c = im.shape[:]
         im_h, im_w, im_c = im.shape[:]
-        if self.coarsest_stride > 1:
+
+        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(
             padding_im_h = int(
                 np.ceil(im_h / self.coarsest_stride) * self.coarsest_stride)
                 np.ceil(im_h / self.coarsest_stride) * self.coarsest_stride)
             padding_im_w = int(
             padding_im_w = int(
                 np.ceil(im_w / self.coarsest_stride) * self.coarsest_stride)
                 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))
         padding_im = np.zeros((padding_im_h, padding_im_w, im_c),
         padding_im = np.zeros((padding_im_h, padding_im_w, im_c),
                               dtype=np.float32)
                               dtype=np.float32)
         padding_im[:im_h, :im_w, :] = im
         padding_im[:im_h, :im_w, :] = im
@@ -387,16 +421,13 @@ class RandomHorizontalFlip:
             raise TypeError(
             raise TypeError(
                 'Cannot do RandomHorizontalFlip! ' +
                 'Cannot do RandomHorizontalFlip! ' +
                 'Becasuse the im_info and label_info can not be None!')
                 'Becasuse the im_info and label_info can not be None!')
-        if 'augment_shape' not in im_info:
-            raise TypeError('Cannot do RandomHorizontalFlip! ' + \
-                            'Becasuse augment_shape is not in im_info!')
         if 'gt_bbox' not in label_info:
         if 'gt_bbox' not in label_info:
             raise TypeError('Cannot do RandomHorizontalFlip! ' + \
             raise TypeError('Cannot do RandomHorizontalFlip! ' + \
                             'Becasuse gt_bbox is not in label_info!')
                             'Becasuse gt_bbox is not in label_info!')
-        augment_shape = im_info['augment_shape']
+        image_shape = im_info['image_shape']
         gt_bbox = label_info['gt_bbox']
         gt_bbox = label_info['gt_bbox']
-        height = augment_shape[0]
-        width = augment_shape[1]
+        height = image_shape[0]
+        width = image_shape[1]
 
 
         if np.random.uniform(0, 1) < self.prob:
         if np.random.uniform(0, 1) < self.prob:
             im = horizontal_flip(im)
             im = horizontal_flip(im)
@@ -545,7 +576,7 @@ class RandomDistort:
             params = params_dict[ops[id].__name__]
             params = params_dict[ops[id].__name__]
             prob = prob_dict[ops[id].__name__]
             prob = prob_dict[ops[id].__name__]
             params['im'] = im
             params['im'] = im
-            
+
             if np.random.uniform(0, 1) < prob:
             if np.random.uniform(0, 1) < prob:
                 im = ops[id](**params)
                 im = ops[id](**params)
         if label_info is None:
         if label_info is None:
@@ -567,7 +598,7 @@ class MixupImage:
             (2)拼接原图像标注框和mixup图像标注框。
             (2)拼接原图像标注框和mixup图像标注框。
             (3)拼接原图像标注框类别和mixup图像标注框类别。
             (3)拼接原图像标注框类别和mixup图像标注框类别。
             (4)原图像标注框混合得分乘以factor,mixup图像标注框混合得分乘以(1-factor),叠加2个结果。
             (4)原图像标注框混合得分乘以factor,mixup图像标注框混合得分乘以(1-factor),叠加2个结果。
-    3. 更新im_info中的augment_shape信息。
+    3. 更新im_info中的image_shape信息。
 
 
     Args:
     Args:
         alpha (float): 随机beta分布的下限。默认为1.5。
         alpha (float): 随机beta分布的下限。默认为1.5。
@@ -610,7 +641,7 @@ class MixupImage:
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    存储与标注框相关信息的字典。
                    存储与标注框相关信息的字典。
                    其中,im_info更新字段为:
                    其中,im_info更新字段为:
-                       - augment_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。
+                       - image_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。
                    im_info删除的字段:
                    im_info删除的字段:
                        - mixup (list): 与当前字段进行mixup的图像相关信息。
                        - mixup (list): 与当前字段进行mixup的图像相关信息。
                    label_info更新字段为:
                    label_info更新字段为:
@@ -674,8 +705,8 @@ class MixupImage:
         label_info['gt_score'] = gt_score
         label_info['gt_score'] = gt_score
         label_info['gt_class'] = gt_class
         label_info['gt_class'] = gt_class
         label_info['is_crowd'] = is_crowd
         label_info['is_crowd'] = is_crowd
-        im_info['augment_shape'] = np.array([im.shape[0],
-                                             im.shape[1]]).astype('int32')
+        im_info['image_shape'] = np.array([im.shape[0],
+                                           im.shape[1]]).astype('int32')
         im_info.pop('mixup')
         im_info.pop('mixup')
         if label_info is None:
         if label_info is None:
             return (im, im_info)
             return (im, im_info)
@@ -721,7 +752,7 @@ class RandomExpand:
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    存储与标注框相关信息的字典。
                    存储与标注框相关信息的字典。
                    其中,im_info更新字段为:
                    其中,im_info更新字段为:
-                       - augment_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。
+                       - image_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。
                    label_info更新字段为:
                    label_info更新字段为:
                        - gt_bbox (np.ndarray): 随机扩张后真实标注框坐标,形状为(n, 4),
                        - gt_bbox (np.ndarray): 随机扩张后真实标注框坐标,形状为(n, 4),
                                           其中n代表真实标注框的个数。
                                           其中n代表真实标注框的个数。
@@ -734,9 +765,6 @@ class RandomExpand:
             raise TypeError(
             raise TypeError(
                 'Cannot do RandomExpand! ' +
                 'Cannot do RandomExpand! ' +
                 'Becasuse the im_info and label_info can not be None!')
                 'Becasuse the im_info and label_info can not be None!')
-        if 'augment_shape' not in im_info:
-            raise TypeError('Cannot do RandomExpand! ' + \
-                            'Becasuse augment_shape is not in im_info!')
         if 'gt_bbox' not in label_info or \
         if 'gt_bbox' not in label_info or \
                 'gt_class' not in label_info:
                 'gt_class' not in label_info:
             raise TypeError('Cannot do RandomExpand! ' + \
             raise TypeError('Cannot do RandomExpand! ' + \
@@ -744,9 +772,9 @@ class RandomExpand:
         if np.random.uniform(0., 1.) < self.prob:
         if np.random.uniform(0., 1.) < self.prob:
             return (im, im_info, label_info)
             return (im, im_info, label_info)
 
 
-        augment_shape = im_info['augment_shape']
-        height = int(augment_shape[0])
-        width = int(augment_shape[1])
+        image_shape = im_info['image_shape']
+        height = int(image_shape[0])
+        width = int(image_shape[1])
 
 
         expand_ratio = np.random.uniform(1., self.ratio)
         expand_ratio = np.random.uniform(1., self.ratio)
         h = int(height * expand_ratio)
         h = int(height * expand_ratio)
@@ -759,7 +787,7 @@ class RandomExpand:
         canvas *= np.array(self.fill_value, dtype=np.float32)
         canvas *= np.array(self.fill_value, dtype=np.float32)
         canvas[y:y + height, x:x + width, :] = im
         canvas[y:y + height, x:x + width, :] = im
 
 
-        im_info['augment_shape'] = np.array([h, w]).astype('int32')
+        im_info['image_shape'] = np.array([h, w]).astype('int32')
         if 'gt_bbox' in label_info and len(label_info['gt_bbox']) > 0:
         if 'gt_bbox' in label_info and len(label_info['gt_bbox']) > 0:
             label_info['gt_bbox'] += np.array([x, y] * 2, dtype=np.float32)
             label_info['gt_bbox'] += np.array([x, y] * 2, dtype=np.float32)
         if 'gt_poly' in label_info and len(label_info['gt_poly']) > 0:
         if 'gt_poly' in label_info and len(label_info['gt_poly']) > 0:
@@ -815,12 +843,14 @@ class RandomCrop:
             tuple: 当label_info为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
             tuple: 当label_info为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
                    存储与标注框相关信息的字典。
                    存储与标注框相关信息的字典。
-                   其中,label_info更新字段为:
-                       - gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4),
+                   其中,im_info更新字段为:
+                           - image_shape (np.ndarray): 扩裁剪的图像高、宽二者组成的np.ndarray,形状为(2,)。
+                       label_info更新字段为:
+                           - gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4),
                                           其中n代表真实标注框的个数。
                                           其中n代表真实标注框的个数。
-                       - gt_class (np.ndarray): 随机裁剪后每个真实标注框对应的类别序号,形状为(n, 1),
+                           - gt_class (np.ndarray): 随机裁剪后每个真实标注框对应的类别序号,形状为(n, 1),
                                            其中n代表真实标注框的个数。
                                            其中n代表真实标注框的个数。
-                       - gt_score (np.ndarray): 随机裁剪后每个真实标注框对应的混合得分,形状为(n, 1),
+                           - gt_score (np.ndarray): 随机裁剪后每个真实标注框对应的混合得分,形状为(n, 1),
                                            其中n代表真实标注框的个数。
                                            其中n代表真实标注框的个数。
 
 
         Raises:
         Raises:
@@ -830,9 +860,6 @@ class RandomCrop:
             raise TypeError(
             raise TypeError(
                 'Cannot do RandomCrop! ' +
                 'Cannot do RandomCrop! ' +
                 'Becasuse the im_info and label_info can not be None!')
                 'Becasuse the im_info and label_info can not be None!')
-        if 'augment_shape' not in im_info:
-            raise TypeError('Cannot do RandomCrop! ' + \
-                            'Becasuse augment_shape is not in im_info!')
         if 'gt_bbox' not in label_info or \
         if 'gt_bbox' not in label_info or \
                 'gt_class' not in label_info:
                 'gt_class' not in label_info:
             raise TypeError('Cannot do RandomCrop! ' + \
             raise TypeError('Cannot do RandomCrop! ' + \
@@ -841,9 +868,9 @@ class RandomCrop:
         if len(label_info['gt_bbox']) == 0:
         if len(label_info['gt_bbox']) == 0:
             return (im, im_info, label_info)
             return (im, im_info, label_info)
 
 
-        augment_shape = im_info['augment_shape']
-        w = augment_shape[1]
-        h = augment_shape[0]
+        image_shape = im_info['image_shape']
+        w = image_shape[1]
+        h = image_shape[0]
         gt_bbox = label_info['gt_bbox']
         gt_bbox = label_info['gt_bbox']
         thresholds = list(self.thresholds)
         thresholds = list(self.thresholds)
         if self.allow_no_crop:
         if self.allow_no_crop:
@@ -902,7 +929,7 @@ class RandomCrop:
                 label_info['gt_bbox'] = np.take(cropped_box, valid_ids, axis=0)
                 label_info['gt_bbox'] = np.take(cropped_box, valid_ids, axis=0)
                 label_info['gt_class'] = np.take(
                 label_info['gt_class'] = np.take(
                     label_info['gt_class'], valid_ids, axis=0)
                     label_info['gt_class'], valid_ids, axis=0)
-                im_info['augment_shape'] = np.array(
+                im_info['image_shape'] = np.array(
                     [crop_box[3] - crop_box[1],
                     [crop_box[3] - crop_box[1],
                      crop_box[2] - crop_box[0]]).astype('int32')
                      crop_box[2] - crop_box[0]]).astype('int32')
                 if 'gt_score' in label_info:
                 if 'gt_score' in label_info:
@@ -973,7 +1000,7 @@ class ArrangeFasterRCNN:
             im_resize_info = im_info['im_resize_info']
             im_resize_info = im_info['im_resize_info']
             im_id = im_info['im_id']
             im_id = im_info['im_id']
             im_shape = np.array(
             im_shape = np.array(
-                (im_info['augment_shape'][0], im_info['augment_shape'][1], 1),
+                (im_info['image_shape'][0], im_info['image_shape'][1], 1),
                 dtype=np.float32)
                 dtype=np.float32)
             gt_bbox = label_info['gt_bbox']
             gt_bbox = label_info['gt_bbox']
             gt_class = label_info['gt_class']
             gt_class = label_info['gt_class']
@@ -986,7 +1013,7 @@ class ArrangeFasterRCNN:
                                 'Becasuse the im_info can not be None!')
                                 'Becasuse the im_info can not be None!')
             im_resize_info = im_info['im_resize_info']
             im_resize_info = im_info['im_resize_info']
             im_shape = np.array(
             im_shape = np.array(
-                (im_info['augment_shape'][0], im_info['augment_shape'][1], 1),
+                (im_info['image_shape'][0], im_info['image_shape'][1], 1),
                 dtype=np.float32)
                 dtype=np.float32)
             outputs = (im, im_resize_info, im_shape)
             outputs = (im, im_resize_info, im_shape)
         return outputs
         return outputs
@@ -1066,7 +1093,7 @@ class ArrangeMaskRCNN:
                                 'Becasuse the im_info can not be None!')
                                 'Becasuse the im_info can not be None!')
             im_resize_info = im_info['im_resize_info']
             im_resize_info = im_info['im_resize_info']
             im_shape = np.array(
             im_shape = np.array(
-                (im_info['augment_shape'][0], im_info['augment_shape'][1], 1),
+                (im_info['image_shape'][0], im_info['image_shape'][1], 1),
                 dtype=np.float32)
                 dtype=np.float32)
             if self.mode == 'eval':
             if self.mode == 'eval':
                 im_id = im_info['im_id']
                 im_id = im_info['im_id']
@@ -1117,7 +1144,7 @@ class ArrangeYOLOv3:
                 raise TypeError(
                 raise TypeError(
                     'Cannot do ArrangeYolov3! ' +
                     'Cannot do ArrangeYolov3! ' +
                     'Becasuse the im_info and label_info can not be None!')
                     'Becasuse the im_info and label_info can not be None!')
-            im_shape = im_info['augment_shape']
+            im_shape = im_info['image_shape']
             if len(label_info['gt_bbox']) != len(label_info['gt_class']):
             if len(label_info['gt_bbox']) != len(label_info['gt_class']):
                 raise ValueError("gt num mismatch: bbox and class.")
                 raise ValueError("gt num mismatch: bbox and class.")
             if len(label_info['gt_bbox']) != len(label_info['gt_score']):
             if len(label_info['gt_bbox']) != len(label_info['gt_score']):
@@ -1141,7 +1168,7 @@ class ArrangeYOLOv3:
                 raise TypeError(
                 raise TypeError(
                     'Cannot do ArrangeYolov3! ' +
                     'Cannot do ArrangeYolov3! ' +
                     'Becasuse the im_info and label_info can not be None!')
                     'Becasuse the im_info and label_info can not be None!')
-            im_shape = im_info['augment_shape']
+            im_shape = im_info['image_shape']
             if len(label_info['gt_bbox']) != len(label_info['gt_class']):
             if len(label_info['gt_bbox']) != len(label_info['gt_class']):
                 raise ValueError("gt num mismatch: bbox and class.")
                 raise ValueError("gt num mismatch: bbox and class.")
             im_id = im_info['im_id']
             im_id = im_info['im_id']
@@ -1160,6 +1187,6 @@ class ArrangeYOLOv3:
             if im_info is None:
             if im_info is None:
                 raise TypeError('Cannot do ArrangeYolov3! ' +
                 raise TypeError('Cannot do ArrangeYolov3! ' +
                                 'Becasuse the im_info can not be None!')
                                 'Becasuse the im_info can not be None!')
-            im_shape = im_info['augment_shape']
+            im_shape = im_info['image_shape']
             outputs = (im, im_shape)
             outputs = (im, im_shape)
         return outputs
         return outputs

+ 144 - 24
paddlex/cv/transforms/seg_transforms.py

@@ -48,9 +48,10 @@ class Compose:
         """
         """
         Args:
         Args:
             im (str/np.ndarray): 图像路径/图像np.ndarray数据。
             im (str/np.ndarray): 图像路径/图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息,dict中的字段如下:
-                - shape_before_resize (tuple): 图像resize之前的大小(h, w)。
-                - shape_before_padding (tuple): 图像padding之前的大小(h, w)。
+            im_info (list): 存储图像reisze或padding前的shape信息,如
+                [('resize', [200, 300]), ('padding', [400, 600])]表示
+                图像在过resize前shape为(200, 300), 过padding前shape为
+                (400, 600)
             label (str/np.ndarray): 标注图像路径/标注图像np.ndarray数据。
             label (str/np.ndarray): 标注图像路径/标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -58,7 +59,7 @@ class Compose:
         """
         """
 
 
         if im_info is None:
         if im_info is None:
-            im_info = dict()
+            im_info = list()
         try:
         try:
             im = cv2.imread(im).astype('float32')
             im = cv2.imread(im).astype('float32')
         except:
         except:
@@ -66,8 +67,8 @@ class Compose:
         if self.to_rgb:
         if self.to_rgb:
             im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
             im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
         if label is not None:
         if label is not None:
-            label = np.asarray(Image.open(label))
-
+            if not isinstance(label, np.ndarray):
+                label = np.asarray(Image.open(label))
         for op in self.transforms:
         for op in self.transforms:
             outputs = op(im, im_info, label)
             outputs = op(im, im_info, label)
             im = outputs[0]
             im = outputs[0]
@@ -93,7 +94,10 @@ class RandomHorizontalFlip:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -125,7 +129,10 @@ class RandomVerticalFlip:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -191,7 +198,10 @@ class Resize:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -208,7 +218,7 @@ class Resize:
         """
         """
         if im_info is None:
         if im_info is None:
             im_info = OrderedDict()
             im_info = OrderedDict()
-        im_info['shape_before_resize'] = im.shape[:2]
+        im_info.append(('resize', im.shape[:2]))
 
 
         if not isinstance(im, np.ndarray):
         if not isinstance(im, np.ndarray):
             raise TypeError("ResizeImage: image type is not np.ndarray.")
             raise TypeError("ResizeImage: image type is not np.ndarray.")
@@ -264,7 +274,10 @@ class ResizeByLong:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -272,12 +285,12 @@ class ResizeByLong:
                 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
                 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
                 存储与图像相关信息的字典和标注图像np.ndarray数据。
                 存储与图像相关信息的字典和标注图像np.ndarray数据。
                 其中,im_info新增字段为:
                 其中,im_info新增字段为:
-                    -shape_before_resize (tuple): 保存resize之前图像的形状(h, w
+                    -shape_before_resize (tuple): 保存resize之前图像的形状(h, w)
         """
         """
         if im_info is None:
         if im_info is None:
             im_info = OrderedDict()
             im_info = OrderedDict()
 
 
-        im_info['shape_before_resize'] = im.shape[:2]
+        im_info.append(('resize', im.shape[:2]))
         im = resize_long(im, self.long_size)
         im = resize_long(im, self.long_size)
         if label is not None:
         if label is not None:
             label = resize_long(label, self.long_size, cv2.INTER_NEAREST)
             label = resize_long(label, self.long_size, cv2.INTER_NEAREST)
@@ -288,6 +301,83 @@ class ResizeByLong:
             return (im, im_info, label)
             return (im, im_info, label)
 
 
 
 
+class ResizeByShort:
+    """根据图像的短边调整图像大小(resize)。
+
+    1. 获取图像的长边和短边长度。
+    2. 根据短边与short_size的比例,计算长边的目标长度,
+       此时高、宽的resize比例为short_size/原图短边长度。
+    3. 如果max_size>0,调整resize比例:
+       如果长边的目标长度>max_size,则高、宽的resize比例为max_size/原图长边长度。
+    4. 根据调整大小的比例对图像进行resize。
+
+    Args:
+        target_size (int): 短边目标长度。默认为800。
+        max_size (int): 长边目标长度的最大限制。默认为1333。
+
+     Raises:
+        TypeError: 形参数据类型不满足需求。
+    """
+
+    def __init__(self, short_size=800, max_size=1333):
+        self.max_size = int(max_size)
+        if not isinstance(short_size, int):
+            raise TypeError(
+                "Type of short_size is invalid. Must be Integer, now is {}".
+                format(type(short_size)))
+        self.short_size = short_size
+        if not (isinstance(self.max_size, int)):
+            raise TypeError("max_size: input type is invalid.")
+
+    def __call__(self, im, im_info=None, label=None):
+        """
+        Args:
+            im (numnp.ndarraypy): 图像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数据。
+                   其中,im_info更新字段为:
+                       -shape_before_resize (tuple): 保存resize之前图像的形状(h, w)。
+
+        Raises:
+            TypeError: 形参数据类型不满足需求。
+            ValueError: 数据长度不匹配。
+        """
+        if im_info is None:
+            im_info = OrderedDict()
+        if not isinstance(im, np.ndarray):
+            raise TypeError("ResizeByShort: image type is not numpy.")
+        if len(im.shape) != 3:
+            raise ValueError('ResizeByShort: image is not 3-dimensional.')
+        im_info.append(('resize', im.shape[:2]))
+        im_short_size = min(im.shape[0], im.shape[1])
+        im_long_size = max(im.shape[0], im.shape[1])
+        scale = float(self.short_size) / im_short_size
+        if self.max_size > 0 and np.round(
+                scale * im_long_size) > self.max_size:
+            scale = float(self.max_size) / float(im_long_size)
+        resized_width = int(round(im.shape[1] * scale))
+        resized_height = int(round(im.shape[0] * scale))
+        im = cv2.resize(
+            im, (resized_width, resized_height),
+            interpolation=cv2.INTER_NEAREST)
+        if label is not None:
+            im = cv2.resize(
+                label, (resized_width, resized_height),
+                interpolation=cv2.INTER_NEAREST)
+        if label is None:
+            return (im, im_info)
+        else:
+            return (im, im_info, label)
+
+
 class ResizeRangeScaling:
 class ResizeRangeScaling:
     """对图像长边随机resize到指定范围内,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
     """对图像长边随机resize到指定范围内,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
 
 
@@ -311,7 +401,10 @@ class ResizeRangeScaling:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -364,7 +457,10 @@ class ResizeStepScaling:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -432,7 +528,10 @@ class Normalize:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
          Returns:
          Returns:
@@ -486,7 +585,10 @@ class Padding:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -501,7 +603,7 @@ class Padding:
         """
         """
         if im_info is None:
         if im_info is None:
             im_info = OrderedDict()
             im_info = OrderedDict()
-        im_info['shape_before_padding'] = im.shape[:2]
+        im_info.append(('padding', im.shape[:2]))
 
 
         im_height, im_width = im.shape[0], im.shape[1]
         im_height, im_width = im.shape[0], im.shape[1]
         if isinstance(self.target_size, int):
         if isinstance(self.target_size, int):
@@ -574,7 +676,10 @@ class RandomPaddingCrop:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
          Returns:
          Returns:
@@ -650,7 +755,10 @@ class RandomBlur:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -703,7 +811,10 @@ class RandomRotate:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -765,7 +876,10 @@ class RandomScaleAspect:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -847,7 +961,10 @@ class RandomDistort:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns:
@@ -922,7 +1039,10 @@ class ArrangeSegmenter:
         """
         """
         Args:
         Args:
             im (np.ndarray): 图像np.ndarray数据。
             im (np.ndarray): 图像np.ndarray数据。
-            im_info (dict): 存储与图像相关的信息。
+            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数据。
             label (np.ndarray): 标注图像np.ndarray数据。
 
 
         Returns:
         Returns: