From 2f6f46a4457b6e7c921e2774a8320e20147f5592 Mon Sep 17 00:00:00 2001 From: cnn Date: Sun, 21 Feb 2021 15:59:47 +0800 Subject: [PATCH] [cherry-pick] Fix deploy python/cpp infer bugs (#2242) * cherry-pick #2234, test=dygraph --- .travis.yml | 2 +- .../deploy/cpp/docs/windows_vs2019_build.md | 42 ++++--------------- dygraph/deploy/cpp/include/preprocess_op.h | 13 +++--- dygraph/deploy/cpp/scripts/build.sh | 1 - dygraph/deploy/cpp/src/object_detector.cc | 2 +- dygraph/deploy/cpp/src/preprocess_op.cc | 26 +++--------- dygraph/deploy/python/preprocess.py | 16 ++++--- dygraph/ppdet/engine/export_utils.py | 11 ++--- dygraph/ppdet/engine/trainer.py | 3 +- dygraph/ppdet/modeling/architectures/ssd.py | 9 +--- 10 files changed, 38 insertions(+), 87 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91789d5f6..2dba2b48d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ addons: before_install: - sudo pip install -U virtualenv pre-commit pip - docker pull paddlepaddle/paddle:latest - - git pull https://github.com/PaddlePaddle/PaddleDetection master + - git pull https://github.com/PaddlePaddle/PaddleDetection release/2.0-rc script: - exit_code=0 diff --git a/dygraph/deploy/cpp/docs/windows_vs2019_build.md b/dygraph/deploy/cpp/docs/windows_vs2019_build.md index 108dd71ef..7cfb63a62 100644 --- a/dygraph/deploy/cpp/docs/windows_vs2019_build.md +++ b/dygraph/deploy/cpp/docs/windows_vs2019_build.md @@ -47,24 +47,14 @@ fluid_inference ### Step4: 编译 -#### 通过图形化操作编译CMake - -1. 打开Visual Studio 2019 Community,点击`继续但无需代码` -![step2](https://paddleseg.bj.bcebos.com/inference/vs2019_step1.png) -2. 点击: `文件`->`打开`->`CMake` -![step2.1](https://paddleseg.bj.bcebos.com/inference/vs2019_step2.png) - -选择项目代码所在路径,并打开`CMakeList.txt`: - -![step2.2](https://paddleseg.bj.bcebos.com/inference/vs2019_step3.png) - -3. 点击:`项目`->`cpp_inference_demo的CMake设置` - -![step3](https://paddleseg.bj.bcebos.com/inference/vs2019_step4.png) +1. 进入到`cpp`文件夹 +``` +cd D:\projects\PaddleDetection\deploy\cpp +``` -4. 点击`浏览`,分别设置编译选项指定`CUDA`、`CUDNN_LIB`、`OpenCV`、`Paddle预测库`的路径 +2. 使用CMake生成项目文件 -三个编译参数的含义说明如下(带*表示仅在使用**GPU版本**预测库时指定, 其中CUDA库版本尽量对齐,**使用9.0、10.0版本,不使用9.2、10.1等版本CUDA库**): +编译参数的含义说明如下(带*表示仅在使用**GPU版本**预测库时指定, 其中CUDA库版本尽量对齐,**使用9.0、10.0版本,不使用9.2、10.1等版本CUDA库**): | 参数名 | 含义 | | ---- | ---- | @@ -75,23 +65,8 @@ fluid_inference | USE_PADDLE_20RC1 | 是否使用2.0rc1预测库。如果使用2.0rc1,在windows环境下预测库名称发生变化,且仅支持动态库方式编译 | **注意:** 1. 使用`CPU`版预测库,请把`WITH_GPU`的勾去掉 2. 如果使用的是`openblas`版本,请把`WITH_MKL`勾去掉 -![step4](https://paddleseg.bj.bcebos.com/inference/vs2019_step5.png) - -**设置完成后**, 点击上图中`保存并生成CMake缓存以加载变量`。 -5. 点击`生成`->`全部生成` - -![step6](https://paddleseg.bj.bcebos.com/inference/vs2019_step6.png) - - -#### 通过命令行操作编译CMake - -1. 进入到`cpp`文件夹 -``` -cd D:\projects\PaddleDetection\deploy\cpp -``` - -2. 使用CMake生成项目文件 +执行如下命令项目文件: ``` cmake . -G "Visual Studio 16 2019" -A x64 -T host=x64 -DWITH_GPU=ON -DWITH_MKL=ON -DCMAKE_BUILD_TYPE=Release -DCUDA_LIB=path_to_cuda_lib -DCUDNN_LIB=path_to_cudnn_lib -DPADDLE_DIR=path_to_paddle_lib -DOPENCV_DIR=path_to_opencv ``` @@ -102,7 +77,8 @@ cmake . -G "Visual Studio 16 2019" -A x64 -T host=x64 -DWITH_GPU=ON -DWITH_MKL=O ``` 3. 编译 -用`Visual Studio 16 2019`打开`cpp`文件夹下的`PaddleObjectDetector.sln`,点击`生成`->`全部生成` +用`Visual Studio 16 2019`打开`cpp`文件夹下的`PaddleObjectDetector.sln`,将编译模式设置为`Release`,点击`生成`->`全部生成 + ### Step5: 预测及可视化 diff --git a/dygraph/deploy/cpp/include/preprocess_op.h b/dygraph/deploy/cpp/include/preprocess_op.h index 0b943f401..fb840c868 100644 --- a/dygraph/deploy/cpp/include/preprocess_op.h +++ b/dygraph/deploy/cpp/include/preprocess_op.h @@ -58,7 +58,7 @@ class InitInfo : public PreprocessOp{ virtual void Run(cv::Mat* im, ImageBlob* data); }; -class Normalize : public PreprocessOp { +class NormalizeImage : public PreprocessOp { public: virtual void Init(const YAML::Node& item, const std::vector image_shape) { mean_ = item["mean"].as>(); @@ -133,13 +133,14 @@ class Preprocessor { } std::shared_ptr CreateOp(const std::string& name) { - if (name == "ResizeOp") { + if (name == "Resize") { return std::make_shared(); - } else if (name == "PermuteOp") { + } else if (name == "Permute") { return std::make_shared(); - } else if (name == "NormalizeImageOp") { - return std::make_shared(); - } else if (name == "PadBatchOp" || name == "PadStride") { + } else if (name == "NormalizeImage") { + return std::make_shared(); + } else if (name == "PadStride") { + // use PadStride instead of PadBatch return std::make_shared(); } std::cerr << "can not find function of OP: " << name << " and return: nullptr" << std::endl; diff --git a/dygraph/deploy/cpp/scripts/build.sh b/dygraph/deploy/cpp/scripts/build.sh index be1136288..32e2f2e83 100644 --- a/dygraph/deploy/cpp/scripts/build.sh +++ b/dygraph/deploy/cpp/scripts/build.sh @@ -69,7 +69,6 @@ cmake .. \ -DTENSORRT_LIB_DIR=${TENSORRT_LIB_DIR} \ -DTENSORRT_INC_DIR=${TENSORRT_INC_DIR} \ -DPADDLE_DIR=${PADDLE_DIR} \ - -DWITH_STATIC_LIB=${WITH_STATIC_LIB} \ -DCUDA_LIB=${CUDA_LIB} \ -DCUDNN_LIB=${CUDNN_LIB} \ -DOPENCV_DIR=${OPENCV_DIR} diff --git a/dygraph/deploy/cpp/src/object_detector.cc b/dygraph/deploy/cpp/src/object_detector.cc index b196ed71d..5faa83a2e 100644 --- a/dygraph/deploy/cpp/src/object_detector.cc +++ b/dygraph/deploy/cpp/src/object_detector.cc @@ -131,7 +131,7 @@ void ObjectDetector::Postprocess( result->clear(); int rh = 1; int rw = 1; - if (config_.arch_ == "SSD" || config_.arch_ == "Face") { + if (config_.arch_ == "Face") { rh = raw_mat.rows; rw = raw_mat.cols; } diff --git a/dygraph/deploy/cpp/src/preprocess_op.cc b/dygraph/deploy/cpp/src/preprocess_op.cc index fac0317af..462958174 100644 --- a/dygraph/deploy/cpp/src/preprocess_op.cc +++ b/dygraph/deploy/cpp/src/preprocess_op.cc @@ -31,7 +31,7 @@ void InitInfo::Run(cv::Mat* im, ImageBlob* data) { }; } -void Normalize::Run(cv::Mat* im, ImageBlob* data) { +void NormalizeImage::Run(cv::Mat* im, ImageBlob* data) { double e = 1.0; if (is_scale_) { e /= 255.0; @@ -62,6 +62,10 @@ void Permute::Run(cv::Mat* im, ImageBlob* data) { void Resize::Run(cv::Mat* im, ImageBlob* data) { auto resize_scale = GenerateScale(*im); + data->input_shape_ = { + static_cast(im->cols * resize_scale.first), + static_cast(im->rows * resize_scale.second) + }; cv::resize( *im, *im, cv::Size(), resize_scale.first, resize_scale.second, interp_); data->im_shape_ = { @@ -72,24 +76,6 @@ void Resize::Run(cv::Mat* im, ImageBlob* data) { resize_scale.second, resize_scale.first, }; - - if (keep_ratio_) { - int max_size = input_shape_[1]; - // Padding the image with 0 border - cv::copyMakeBorder( - *im, - *im, - 0, - max_size - im->rows, - 0, - max_size - im->cols, - cv::BORDER_CONSTANT, - cv::Scalar(0)); - } - data->input_shape_ = { - static_cast(im->rows), - static_cast(im->cols), - }; } std::pair Resize::GenerateScale(const cv::Mat& im) { @@ -145,7 +131,7 @@ void PadStride::Run(cv::Mat* im, ImageBlob* data) { // Preprocessor op running order const std::vector Preprocessor::RUN_ORDER = { - "InitInfo", "ResizeOp", "NormalizeImageOp", "PadStrideOp", "PermuteOp" + "InitInfo", "Resize", "NormalizeImage", "PadStride", "Permute" }; void Preprocessor::Run(cv::Mat* im, ImageBlob* data) { diff --git a/dygraph/deploy/python/preprocess.py b/dygraph/deploy/python/preprocess.py index cf31a3f3d..371b1172d 100644 --- a/dygraph/deploy/python/preprocess.py +++ b/dygraph/deploy/python/preprocess.py @@ -35,6 +35,7 @@ def decode_image(im_file, im_info): else: im = im_file im_info['im_shape'] = np.array(im.shape[:2], dtype=np.float32) + im_info['scale_factor'] = np.array([1., 1.], dtype=np.float32) return im, im_info @@ -66,8 +67,13 @@ class Resize(object): im (np.ndarray): processed image (np.ndarray) im_info (dict): info of processed image """ + assert len(self.target_size) == 2 + assert self.target_size[0] > 0 and self.target_size[1] > 0 im_channel = im.shape[2] im_scale_y, im_scale_x = self.generate_scale(im) + # set image_shape + im_info['input_shape'][1] = int(im_scale_y * im.shape[0]) + im_info['input_shape'][2] = int(im_scale_x * im.shape[1]) im = cv2.resize( im, None, @@ -78,14 +84,6 @@ class Resize(object): im_info['im_shape'] = np.array(im.shape[:2]).astype('float32') im_info['scale_factor'] = np.array( [im_scale_y, im_scale_x]).astype('float32') - # padding im when image_shape fixed by infer_cfg.yml - if self.keep_ratio and im_info['input_shape'][1] is not None: - max_size = im_info['input_shape'][1] - padding_im = np.zeros( - (max_size, max_size, im_channel), dtype=np.float32) - im_h, im_w = im.shape[:2] - padding_im[:im_h, :im_w, :] = im - im = padding_im return im, im_info def generate_scale(self, im): @@ -174,7 +172,7 @@ class Permute(object): class PadStride(object): - """ padding image for model with FPN + """ padding image for model with FPN , instead PadBatch(pad_to_stride, pad_gt) in original config Args: stride (bool): model with FPN need image shape % stride == 0 """ diff --git a/dygraph/ppdet/engine/export_utils.py b/dygraph/ppdet/engine/export_utils.py index 9d0320c0e..cfca44489 100644 --- a/dygraph/ppdet/engine/export_utils.py +++ b/dygraph/ppdet/engine/export_utils.py @@ -52,12 +52,6 @@ def _parse_reader(reader_cfg, dataset_cfg, metric, arch, image_shape): for st in sample_transforms[1:]: for key, value in st.items(): p = {'type': key} - if key == 'Resize': - if value.get('keep_ratio', - False) and image_shape[1] is not None: - max_size = max(image_shape[1:]) - image_shape = [3, max_size, max_size] - value['target_size'] = image_shape[1:] p.update(value) preprocess_list.append(p) batch_transforms = reader_cfg.get('batch_transforms', None) @@ -65,9 +59,10 @@ def _parse_reader(reader_cfg, dataset_cfg, metric, arch, image_shape): methods = [list(bt.keys())[0] for bt in batch_transforms] for bt in batch_transforms: for key, value in bt.items(): + # for deploy/infer, use PadStride(stride) instead PadBatch(pad_to_stride, pad_gt) if key == 'PadBatch': - preprocess_list.append({'type': 'PadStride'}) - preprocess_list[-1].update({ + preprocess_list.append({ + 'type': 'PadStride', 'stride': value['pad_to_stride'] }) break diff --git a/dygraph/ppdet/engine/trainer.py b/dygraph/ppdet/engine/trainer.py index 4e58d8894..d90371eda 100644 --- a/dygraph/ppdet/engine/trainer.py +++ b/dygraph/ppdet/engine/trainer.py @@ -340,8 +340,9 @@ class Trainer(object): if 'inputs_def' in self.cfg['TestReader']: inputs_def = self.cfg['TestReader']['inputs_def'] image_shape = inputs_def.get('image_shape', None) + # set image_shape=[3, -1, -1] as default if image_shape is None: - image_shape = [3, None, None] + image_shape = [3, -1, -1] self.model.eval() diff --git a/dygraph/ppdet/modeling/architectures/ssd.py b/dygraph/ppdet/modeling/architectures/ssd.py index 6ff55c54a..ac82281d0 100644 --- a/dygraph/ppdet/modeling/architectures/ssd.py +++ b/dygraph/ppdet/modeling/architectures/ssd.py @@ -54,13 +54,8 @@ class SSD(BaseArch): def get_pred(self): bbox_pred, bbox_num = self._forward() - label = bbox_pred[:, 0] - score = bbox_pred[:, 1] - bbox = bbox_pred[:, 2:] output = { - 'bbox': bbox, - 'score': score, - 'label': label, - 'bbox_num': bbox_num + "bbox": bbox_pred, + "bbox_num": bbox_num, } return output -- GitLab