Skip to content

Commit

Permalink
Add new model PaddleSeg (#30)
Browse files Browse the repository at this point in the history
* Support new model PaddleSeg

* Fix conflict

* PaddleSeg add visulization function

* fix bug

* Fix BindPPSeg wrong name

* Fix variable name

* Update by comments

* Add ppseg-unet example python version

Co-authored-by: Jason <jiangjiajun@baidu.com>
  • Loading branch information
felixhjh and jiangjiajun authored Jul 21, 2022
1 parent 8b0a0c6 commit a8458e6
Show file tree
Hide file tree
Showing 15 changed files with 453 additions and 8 deletions.
59 changes: 59 additions & 0 deletions examples/vision/ppseg_unet.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "fastdeploy/vision.h"
#include "yaml-cpp/yaml.h"

int main() {
namespace vis = fastdeploy::vision;

std::string model_file = "../resources/models/unet_Cityscapes/model.pdmodel";
std::string params_file =
"../resources/models/unet_Cityscapes/model.pdiparams";
std::string config_file = "../resources/models/unet_Cityscapes/deploy.yaml";
std::string img_path = "../resources/images/cityscapes_demo.png";
std::string vis_path = "../resources/outputs/vis.jpeg";

auto model = vis::ppseg::Model(model_file, params_file, config_file);
if (!model.Initialized()) {
std::cerr << "Init Failed." << std::endl;
return -1;
}

cv::Mat im = cv::imread(img_path);
cv::Mat vis_im;

vis::SegmentationResult res;
if (!model.Predict(&im, &res)) {
std::cerr << "Prediction Failed." << std::endl;
return -1;
} else {
std::cout << "Prediction Done!" << std::endl;
}

// 输出预测框结果
std::cout << res.Str() << std::endl;

YAML::Node cfg = YAML::LoadFile(config_file);
int num_classes = 19;
if (cfg["Deploy"]["num_classes"]) {
num_classes = cfg["Deploy"]["num_classes"].as<int>();
}

// 可视化预测结果
vis::Visualize::VisSegmentation(im, res, &vis_im, num_classes);
cv::imwrite(vis_path, vis_im);
std::cout << "Inference Done! Saved: " << vis_path << std::endl;
return 0;
}
1 change: 1 addition & 0 deletions fastdeploy/vision.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "fastdeploy/vision/meituan/yolov6.h"
#include "fastdeploy/vision/ppcls/model.h"
#include "fastdeploy/vision/ppdet/ppyoloe.h"
#include "fastdeploy/vision/ppseg/model.h"
#include "fastdeploy/vision/ultralytics/yolov5.h"
#include "fastdeploy/vision/wongkinyiu/yolor.h"
#include "fastdeploy/vision/wongkinyiu/yolov7.h"
Expand Down
1 change: 1 addition & 0 deletions fastdeploy/vision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from . import evaluation
from . import ppcls
from . import ppdet
from . import ppseg
from . import ultralytics
from . import meituan
from . import megvii
Expand Down
22 changes: 22 additions & 0 deletions fastdeploy/vision/common/result.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,27 @@ std::string DetectionResult::Str() {
return out;
}

void SegmentationResult::Clear() {
std::vector<std::vector<int64_t>>().swap(masks);
}

void SegmentationResult::Resize(int64_t height, int64_t width) {
masks.resize(height, std::vector<int64_t>(width));
}

std::string SegmentationResult::Str() {
std::string out;
out = "SegmentationResult Image masks 10 rows x 10 cols: \n";
for (size_t i = 0; i < 10; ++i) {
out += "[";
for (size_t j = 0; j < 10; ++j) {
out = out + std::to_string(masks[i][j]) + ", ";
}
out += ".....]\n";
}
out += "...........\n";
return out;
}

} // namespace vision
} // namespace fastdeploy
13 changes: 13 additions & 0 deletions fastdeploy/vision/common/result.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,18 @@ struct FASTDEPLOY_DECL DetectionResult : public BaseResult {
std::string Str();
};

struct FASTDEPLOY_DECL SegmentationResult : public BaseResult {
// mask
std::vector<std::vector<int64_t>> masks;

ResultType type = ResultType::SEGMENTATION;

void Clear();

void Resize(int64_t height, int64_t width);

std::string Str();
};

} // namespace vision
} // namespace fastdeploy
37 changes: 37 additions & 0 deletions fastdeploy/vision/ppseg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import
import logging
from ... import FastDeployModel, Frontend
from ... import fastdeploy_main as C


class Model(FastDeployModel):
def __init__(self,
model_file,
params_file,
config_file,
backend_option=None,
model_format=Frontend.PADDLE):
super(Model, self).__init__(backend_option)

assert model_format == Frontend.PADDLE, "PaddleSeg only support model format of Frontend.Paddle now."
self._model = C.vision.ppseg.Model(model_file, params_file,
config_file, self._runtime_option,
model_format)
assert self.initialized, "PaddleSeg model initialize failed."

def predict(self, input_image):
return self._model.predict(input_image)
140 changes: 140 additions & 0 deletions fastdeploy/vision/ppseg/model.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "fastdeploy/vision/ppseg/model.h"
#include "fastdeploy/vision.h"
#include "fastdeploy/vision/utils/utils.h"
#include "yaml-cpp/yaml.h"

namespace fastdeploy {
namespace vision {
namespace ppseg {

Model::Model(const std::string& model_file, const std::string& params_file,
const std::string& config_file, const RuntimeOption& custom_option,
const Frontend& model_format) {
config_file_ = config_file;
valid_cpu_backends = {Backend::ORT, Backend::PDINFER};
valid_gpu_backends = {Backend::ORT, Backend::PDINFER};
runtime_option = custom_option;
runtime_option.model_format = model_format;
runtime_option.model_file = model_file;
runtime_option.params_file = params_file;
initialized = Initialize();
}

bool Model::Initialize() {
if (!BuildPreprocessPipelineFromConfig()) {
FDERROR << "Failed to build preprocess pipeline from configuration file."
<< std::endl;
return false;
}
if (!InitRuntime()) {
FDERROR << "Failed to initialize fastdeploy backend." << std::endl;
return false;
}
return true;
}

bool Model::BuildPreprocessPipelineFromConfig() {
processors_.clear();
YAML::Node cfg;
processors_.push_back(std::make_shared<BGR2RGB>());
try {
cfg = YAML::LoadFile(config_file_);
} catch (YAML::BadFile& e) {
FDERROR << "Failed to load yaml file " << config_file_
<< ", maybe you should check this file." << std::endl;
return false;
}

if (cfg["Deploy"]["transforms"]) {
auto preprocess_cfg = cfg["Deploy"]["transforms"];
for (const auto& op : preprocess_cfg) {
FDASSERT(op.IsMap(),
"Require the transform information in yaml be Map type.");
if (op["type"].as<std::string>() == "Normalize") {
std::vector<float> mean = {0.5, 0.5, 0.5};
std::vector<float> std = {0.5, 0.5, 0.5};
if (op["mean"]) {
mean = op["mean"].as<std::vector<float>>();
}
if (op["std"]) {
std = op["std"].as<std::vector<float>>();
}
processors_.push_back(std::make_shared<Normalize>(mean, std));

} else if (op["type"].as<std::string>() == "Resize") {
const auto& target_size = op["target_size"];
int resize_width = target_size[0].as<int>();
int resize_height = target_size[1].as<int>();
processors_.push_back(
std::make_shared<Resize>(resize_width, resize_height));
}
}
processors_.push_back(std::make_shared<HWC2CHW>());
}
return true;
}

bool Model::Preprocess(Mat* mat, FDTensor* output) {
for (size_t i = 0; i < processors_.size(); ++i) {
if (!(*(processors_[i].get()))(mat)) {
FDERROR << "Failed to process image data in " << processors_[i]->Name()
<< "." << std::endl;
return false;
}
}
mat->ShareWithTensor(output);
output->shape.insert(output->shape.begin(), 1);
output->name = InputInfoOfRuntime(0).name;
return true;
}

bool Model::Postprocess(const FDTensor& infer_result,
SegmentationResult* result) {
FDASSERT(infer_result.dtype == FDDataType::INT64,
"Require the data type of output is int64, but now it's " +
Str(const_cast<fastdeploy::FDDataType&>(infer_result.dtype)) +
".");
result->Clear();
std::vector<int64_t> output_shape = infer_result.shape;
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
std::multiplies<int>());
const int64_t* infer_result_buffer =
reinterpret_cast<const int64_t*>(infer_result.data.data());
int64_t height = output_shape[1];
int64_t width = output_shape[2];
result->Resize(height, width);
for (int64_t i = 0; i < height; i++) {
int64_t begin = i * width;
int64_t end = (i + 1) * width - 1;
std::copy(infer_result_buffer + begin, infer_result_buffer + end,
result->masks[i].begin());
}

return true;
}

bool Model::Predict(cv::Mat* im, SegmentationResult* result) {
Mat mat(*im);
std::vector<FDTensor> processed_data(1);
if (!Preprocess(&mat, &(processed_data[0]))) {
FDERROR << "Failed to preprocess input data while using model:"
<< ModelName() << "." << std::endl;
return false;
}
std::vector<FDTensor> infer_result(1);
if (!Infer(processed_data, &infer_result)) {
FDERROR << "Failed to inference while using model:" << ModelName() << "."
<< std::endl;
return false;
}
if (!Postprocess(infer_result[0], result)) {
FDERROR << "Failed to postprocess while using model:" << ModelName() << "."
<< std::endl;
return false;
}
return true;
}

} // namespace ppseg
} // namespace vision
} // namespace fastdeploy
35 changes: 35 additions & 0 deletions fastdeploy/vision/ppseg/model.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include "fastdeploy/fastdeploy_model.h"
#include "fastdeploy/vision/common/processors/transform.h"
#include "fastdeploy/vision/common/result.h"

namespace fastdeploy {
namespace vision {
namespace ppseg {

class FASTDEPLOY_DECL Model : public FastDeployModel {
public:
Model(const std::string& model_file, const std::string& params_file,
const std::string& config_file,
const RuntimeOption& custom_option = RuntimeOption(),
const Frontend& model_format = Frontend::PADDLE);

std::string ModelName() const { return "ppseg"; }

virtual bool Predict(cv::Mat* im, SegmentationResult* result);

private:
bool Initialize();

bool BuildPreprocessPipelineFromConfig();

bool Preprocess(Mat* mat, FDTensor* outputs);

bool Postprocess(const FDTensor& infer_result, SegmentationResult* result);

std::vector<std::shared_ptr<Processor>> processors_;
std::string config_file_;
};
} // namespace ppseg
} // namespace vision
} // namespace fastdeploy
30 changes: 30 additions & 0 deletions fastdeploy/vision/ppseg/ppseg_pybind.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fastdeploy/pybind/main.h"

namespace fastdeploy {
void BindPPSeg(pybind11::module& m) {
auto ppseg_module =
m.def_submodule("ppseg", "Module to deploy PaddleSegmentation.");
pybind11::class_<vision::ppseg::Model, FastDeployModel>(ppseg_module, "Model")
.def(pybind11::init<std::string, std::string, std::string, RuntimeOption,
Frontend>())
.def("predict", [](vision::ppseg::Model& self, pybind11::array& data) {
auto mat = PyArrayToCvMat(data);
vision::SegmentationResult res;
self.Predict(&mat, &res);
return res;
});
}
} // namespace fastdeploy
8 changes: 8 additions & 0 deletions fastdeploy/vision/vision_pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace fastdeploy {
void BindPPCls(pybind11::module& m);
void BindPPDet(pybind11::module& m);
void BindWongkinyiu(pybind11::module& m);
void BindPPSeg(pybind11::module& m);
void BindUltralytics(pybind11::module& m);
void BindMeituan(pybind11::module& m);
void BindMegvii(pybind11::module& m);
Expand All @@ -42,8 +43,15 @@ void BindVision(pybind11::module& m) {
.def("__repr__", &vision::DetectionResult::Str)
.def("__str__", &vision::DetectionResult::Str);

pybind11::class_<vision::SegmentationResult>(m, "SegmentationResult")
.def(pybind11::init())
.def_readwrite("masks", &vision::SegmentationResult::masks)
.def("__repr__", &vision::SegmentationResult::Str)
.def("__str__", &vision::SegmentationResult::Str);

BindPPCls(m);
BindPPDet(m);
BindPPSeg(m);
BindUltralytics(m);
BindWongkinyiu(m);
BindMeituan(m);
Expand Down
Loading

0 comments on commit a8458e6

Please sign in to comment.