提交 1af3e044 编写于 作者: F FlyingQianMM

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

# 模型部署 # 模型部署
本目录为PaddleX模型部署代码。 本目录为PaddleX模型部署代码, 编译和使用的教程参考:
- [C++部署文档](../docs/deploy/deploy.md#C部署)
...@@ -3,9 +3,10 @@ project(PaddleX CXX C) ...@@ -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() ...@@ -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") include_directories("${TENSORRT_DIR}/include")
link_directories("${PADDLE_DIR}/third_party/install/tensorrt/lib") link_directories("${TENSORRT_DIR}/lib")
endif() endif()
endif(NOT WIN32) endif(NOT WIN32)
...@@ -169,7 +170,7 @@ endif() ...@@ -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) ...@@ -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} ${TENSORRT_DIR}/lib/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX})
set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX}) set(DEPS ${DEPS} ${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) ...@@ -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) ...@@ -251,4 +252,3 @@ if (WIN32 AND WITH_MKL)
) )
endif() endif()
...@@ -38,12 +38,14 @@ class Model { ...@@ -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);
......
...@@ -35,10 +35,8 @@ class ImageBlob { ...@@ -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 { ...@@ -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 { ...@@ -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>();
......
# 是否使用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 ...@@ -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}
......
...@@ -23,6 +23,7 @@ ...@@ -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) { ...@@ -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 != "") {
......
...@@ -24,6 +24,7 @@ ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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;
} }
// 可视化 // 可视化
......
...@@ -18,6 +18,7 @@ namespace PaddleX { ...@@ -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, ...@@ -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) { ...@@ -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) { ...@@ -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 before_shape = inputs_.im_size_before_resize_[len_postprocess - idx];
auto padding_h = inputs_.im_size_before_padding_[1]; 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 before_shape = inputs_.im_size_before_resize_[len_postprocess - idx];
auto resize_h = inputs_.im_size_before_resize_[1]; 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) { ...@@ -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>());
......
...@@ -24,6 +24,7 @@ ...@@ -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) { ...@@ -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 != "") {
......
...@@ -56,8 +56,7 @@ float ResizeByShort::GenerateScale(const cv::Mat& im) { ...@@ -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_.push_back({im->rows, im->cols});
data->im_size_before_resize_[1] = 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) { ...@@ -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_resize_.push_back({im->rows, im->cols});
data->im_size_before_padding_[1] = 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) { ...@@ -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_.push_back({im->rows, im->cols});
data->im_size_before_resize_[1] = 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) { ...@@ -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_.push_back({im->rows, im->cols});
data->im_size_before_resize_[1] = im->cols;
data->reshape_order_.push_back("resize"); data->reshape_order_.push_back("resize");
cv::resize( cv::resize(
......
...@@ -16,7 +16,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_ ...@@ -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, ...@@ -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= ...@@ -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 ...@@ -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。
\ No newline at end of file
...@@ -34,7 +34,7 @@ pred_result = model.predict('./xiaoduxiong_ins_det/JPEGImages/WechatIMG114.jpeg' ...@@ -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)
``` ```
...@@ -109,7 +109,9 @@ paddlex.cls.transforms.RandomDistort(brightness_range=0.9, brightness_prob=0.5, ...@@ -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。
......
...@@ -85,7 +85,9 @@ paddlex.det.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, ...@@ -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 ...@@ -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, ...@@ -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.]。
......
...@@ -153,7 +153,9 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, ...@@ -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。
......
...@@ -41,8 +41,8 @@ labelA ...@@ -41,8 +41,8 @@ labelA
labelB labelB
... ...
``` ```
[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集
在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集 在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集
## 目标检测VOC ## 目标检测VOC
目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。 目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
...@@ -81,8 +81,8 @@ labelA ...@@ -81,8 +81,8 @@ labelA
labelB labelB
... ...
``` ```
[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集
在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集 在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集
## 目标检测和实例分割COCO ## 目标检测和实例分割COCO
目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。 目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
...@@ -135,7 +135,7 @@ labelB ...@@ -135,7 +135,7 @@ labelB
] ]
} }
``` ```
每个字段的含义如下所示: 其中,每个字段的含义如下所示:
| 域名 | 字段名 | 含义 | 数据类型 | 备注 | | 域名 | 字段名 | 含义 | 数据类型 | 备注 |
|:-----|:--------|:------------|------|:-----| |:-----|:--------|:------------|------|:-----|
...@@ -155,8 +155,8 @@ labelB ...@@ -155,8 +155,8 @@ labelB
| categories | supercategory | 类别父类的标签名 | str | | | categories | supercategory | 类别父类的标签名 | str | |
[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集
在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集 在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集
## 语义分割数据 ## 语义分割数据
语义分割数据集包含原图、标注图及相应的文件列表文件。 语义分割数据集包含原图、标注图及相应的文件列表文件。
...@@ -191,13 +191,176 @@ images/xxx2.png annotations/xxx2.png ...@@ -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),下载视盘语义分割数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集。
在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集 在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))加载分类数据集。
\ No newline at end of file
...@@ -7,20 +7,29 @@ ...@@ -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') predictor = pdx.deploy.create_predictor('./inference_model')
result = predictor.predict(image='garbage.bmp') result = predictor.predict(image='xiaoduxiong_test_image/JPEGImages/WeChatIMG110.jpeg')
``` ```
### C++部署 ### C++部署
......
...@@ -19,8 +19,18 @@ ...@@ -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 ...@@ -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 预测库路径 # TensorRT 的lib路径
PADDLE_DIR=/root/projects/deps/fluid_inference/ 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 ...@@ -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 ...@@ -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 ...@@ -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/xiaoduxiong1.jpeg
/path/to/images/garbage2.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`参数设置的目录下。
...@@ -27,7 +27,18 @@ git clone https://github.com/PaddlePaddle/PaddleX.git ...@@ -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 ...@@ -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 ...@@ -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\\xiaoduxiong1.jpeg
\\path\\to\\images\\garbage2.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`参数设置的目录下。
...@@ -38,6 +38,11 @@ except: ...@@ -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
......
...@@ -29,7 +29,11 @@ def arg_parser(): ...@@ -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(): ...@@ -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()
...@@ -16,3 +16,6 @@ from .imagenet import ImageNet ...@@ -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
\ No newline at end of file
...@@ -34,7 +34,7 @@ class CocoDetection(VOCDetection): ...@@ -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): ...@@ -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,
......
# 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))
\ No newline at end of file
# 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
\ No newline at end of file
# 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
...@@ -35,7 +35,7 @@ class ImageNet(Dataset): ...@@ -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。
""" """
......
...@@ -33,7 +33,7 @@ class SegDataset(Dataset): ...@@ -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。
""" """
......
...@@ -38,7 +38,7 @@ class VOCDetection(Dataset): ...@@ -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): ...@@ -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,
......
...@@ -255,7 +255,10 @@ class BaseAPI: ...@@ -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: ...@@ -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(
......
...@@ -46,10 +46,18 @@ class BaseClassifier(BaseAPI): ...@@ -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( if self.fixed_input_shape is not None:
dtype='float32', shape=[None, 3, None, None], name='image') 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))
......
...@@ -48,7 +48,6 @@ class DeepLabv3p(BaseAPI): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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 info in im_info[::-1]:
for k in keys[::-1]: if info[0] == 'resize':
if k == 'shape_before_resize': w, h = info[1][1], info[1][0]
h, w = im_info[k][0], im_info[k][1]
pred = cv2.resize(pred, (w, h), cv2.INTER_NEAREST) pred = cv2.resize(pred, (w, h), cv2.INTER_NEAREST)
elif k == 'shape_before_padding': elif info[0] == 'padding':
h, w = im_info[k][0], im_info[k][1] 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]}
...@@ -57,6 +57,7 @@ class FasterRCNN(BaseAPI): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -23,7 +23,7 @@ import paddlex ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -60,6 +60,7 @@ class MaskRCNN(FasterRCNN): ...@@ -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): ...@@ -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): ...@@ -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:
......
...@@ -77,6 +77,7 @@ class UNet(DeepLabv3p): ...@@ -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): ...@@ -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()
......
...@@ -80,6 +80,7 @@ class YOLOv3(BaseAPI): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -76,7 +76,8 @@ class FasterRCNN(object): ...@@ -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): ...@@ -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): ...@@ -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')
......
...@@ -86,7 +86,8 @@ class MaskRCNN(object): ...@@ -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): ...@@ -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): ...@@ -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')
......
...@@ -33,7 +33,8 @@ class YOLOv3: ...@@ -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: ...@@ -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: ...@@ -247,8 +249,15 @@ class YOLOv3:
def generate_inputs(self): def generate_inputs(self):
inputs = OrderedDict() inputs = OrderedDict()
inputs['image'] = fluid.data( if self.fixed_input_shape is not None:
dtype='float32', shape=[None, 3, None, None], name='image') 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')
......
...@@ -61,6 +61,7 @@ class DeepLabv3p(object): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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')
......
...@@ -54,6 +54,7 @@ class UNet(object): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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')
......
...@@ -58,8 +58,8 @@ class Compose: ...@@ -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,), - image_shape (np.ndarray): 图像原始大小,形状为(2,),
origin_shape[0]为高,origin_shape[1]为宽。 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: ...@@ -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['image_shape'] = np.array([im.shape[0],
im_info['augment_shape'] = np.array([im.shape[0], im.shape[1]]).astype('int32')
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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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] height = image_shape[0]
width = augment_shape[1] 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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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_info['image_shape'] = np.array([im.shape[0],
im.shape[1]]).astype('int32') 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: ...@@ -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: ...@@ -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: ...@@ -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'] image_shape = im_info['image_shape']
height = int(augment_shape[0]) height = int(image_shape[0])
width = int(augment_shape[1]) 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: ...@@ -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: ...@@ -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更新字段为: 其中,im_info更新字段为:
- gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4), - 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: ...@@ -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: ...@@ -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'] image_shape = im_info['image_shape']
w = augment_shape[1] w = image_shape[1]
h = augment_shape[0] 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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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
...@@ -48,9 +48,10 @@ class Compose: ...@@ -48,9 +48,10 @@ class Compose:
""" """
Args: Args:
im (str/np.ndarray): 图像路径/图像np.ndarray数据。 im (str/np.ndarray): 图像路径/图像np.ndarray数据。
im_info (dict): 存储与图像相关的信息,dict中的字段如下: im_info (list): 存储图像reisze或padding前的shape信息,如
- shape_before_resize (tuple): 图像resize之前的大小(h, w)。 [('resize', [200, 300]), ('padding', [400, 600])]表示
- shape_before_padding (tuple): 图像padding之前的大小(h, w)。 图像在过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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册