Skip to content

Commit c1d8ba5

Browse files
authored
[Feature] Add onnxruntime inference (#1743)
Add a demo of onnxruntime inference with cpp api
1 parent 86cdd07 commit c1d8ba5

File tree

5 files changed

+250
-0
lines changed

5 files changed

+250
-0
lines changed

deploy/onnxruntime_cpp/CMakeLists.txt

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.0)
2+
project(onnxruntime_cpp_inference_demo CXX C)
3+
4+
set(onnxruntime_INSTALL_PREFIX /usr/local)
5+
set(onnxruntime_INCLUDE_DIRS
6+
${onnxruntime_INSTALL_PREFIX}/include/onnxruntime
7+
${onnxruntime_INSTALL_PREFIX}/include/onnxruntime/core/session
8+
)
9+
10+
find_library(onnxruntime_LIBS NAMES onnxruntime PATHS /usr/local/lib)
11+
12+
find_package(OpenCV REQUIRED)
13+
14+
add_executable(${PROJECT_NAME}_app
15+
${PROJECT_SOURCE_DIR}/src/test_seg.cpp
16+
${PROJECT_SOURCE_DIR}/src/ort_session_handler.cpp
17+
)
18+
19+
target_link_libraries(${PROJECT_NAME}_app
20+
${OpenCV_LIBRARIES}
21+
${onnxruntime_LIBS}
22+
)
23+
24+
target_include_directories(${PROJECT_NAME}_app
25+
SYSTEM PUBLIC
26+
${OpenCV_INCLUDE_DIRS}
27+
${onnxruntime_INCLUDE_DIRS}
28+
)

deploy/onnxruntime_cpp/README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# sample deploy app using [onnxruntime](https://github.com/microsoft/onnxruntime) #
2+
***
3+
4+
This sample uses Cpp api of onnxruntime.
5+
6+
## Dependencies ##
7+
***
8+
9+
- [onnxruntime](https://github.com/microsoft/onnxruntime)
10+
* tested with v1.10.0
11+
* to build onnxruntime from source you need cmake>=3.18
12+
* you can build from source with the [build script](https://github.com/microsoft/onnxruntime/blob/master/build.sh)
13+
* here is the sample procedure to build cpu onnxruntime
14+
```bash
15+
readonly ONNXRUNTIME_VERSION="v1.10.0"
16+
git clone --recursive -b ${ONNXRUNTIME_VERSION} https://github.com/Microsoft/onnxruntime
17+
./build.sh --config RelWithDebInfo --build_shared_lib --skip_tests --parallel `nproc`
18+
cd build/Linux/RelWithDebInfo
19+
make install
20+
```
21+
22+
- opencv
23+
```bash
24+
sudo apt-get install libopencv-dev
25+
```
26+
27+
## How to Build ##
28+
***
29+
```bash
30+
mkdir build && cd build && cmake ../ && make -j`nproc`
31+
```
32+
33+
## How to Run ##
34+
***
35+
36+
- Download test images
37+
```bash
38+
wget https://paddleseg.bj.bcebos.com/dygraph/demo/cityscapes_demo.png
39+
```
40+
41+
- Export PaddleSeg Model to onnx format
42+
* [export PaddleSeg model](https://github.com/PaddlePaddle/PaddleSeg/blob/develop/docs/model_export.md)
43+
* convert exported model to onnx format with [Paddle2ONNX](https://github.com/PaddlePaddle/Paddle2ONNX)
44+
45+
- You can download the bisenetv2 onnx model for this demo directly from the following url:
46+
```bash
47+
wget https://github.com/PaddlePaddle/PaddleSeg/files/7918707/bisenetv2_cityscapes.zip && unzip bisenetv2_cityscapes.zip
48+
```
49+
50+
- Run app
51+
52+
```bash
53+
./build/onnxruntime_cpp_inference_demo_app /path/to/test/image /path/to/onnx/model
54+
```
55+
56+
The result will be saved as *out_img.jpg*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "ort_session_handler.hpp"
2+
3+
namespace deploy {
4+
OrtSessionHandler::OrtSessionHandler(const std::string &model_path,
5+
const std::vector<std::vector<int64_t>> &input_tensor_shapes)
6+
: _model_path(model_path),
7+
_input_tensor_shapes(input_tensor_shapes),
8+
_env(Ort::Env(ORT_LOGGING_LEVEL_WARNING, "ort session handler")),
9+
_session(nullptr) {
10+
Ort::SessionOptions session_options;
11+
12+
// if onnxruntime is built with cuda provider, the following function can be added to use cuda gpu
13+
// Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, gpu_index));
14+
15+
std::basic_string<ORTCHAR_T> ort_model_path;
16+
std::copy(model_path.begin(), model_path.end(), std::back_inserter(ort_model_path));
17+
_session.reset(new Ort::Experimental::Session(_env, ort_model_path, session_options));
18+
19+
if (_session->GetInputCount() != input_tensor_shapes.size()) {
20+
throw std::runtime_error("invalid input size");
21+
}
22+
}
23+
24+
std::vector<float> OrtSessionHandler::preprocess(const cv::Mat &image, int target_height, int target_width,
25+
const std::vector<float> &mean_val,
26+
const std::vector<float> &std_val) const {
27+
if (image.empty() || image.channels() != 3) {
28+
throw std::runtime_error("invalid image");
29+
}
30+
31+
if (target_height * target_width == 0) {
32+
throw std::runtime_error("invalid target dimension");
33+
}
34+
35+
cv::Mat processed = image.clone();
36+
37+
if (image.rows != target_height || image.cols != target_width) {
38+
cv::resize(processed, processed, cv::Size(target_width, target_height), 0, 0, cv::INTER_CUBIC);
39+
}
40+
cv::cvtColor(processed, processed, cv::COLOR_BGR2RGB);
41+
std::vector<float> data(3 * target_height * target_width);
42+
43+
for (int i = 0; i < target_height; ++i) {
44+
for (int j = 0; j < target_width; ++j) {
45+
for (int c = 0; c < 3; ++c) {
46+
data[c * target_height * target_width + i * target_width + j] =
47+
(processed.data[i * target_width * 3 + j * 3 + c] / 255.0 - mean_val[c]) / std_val[c];
48+
}
49+
}
50+
}
51+
52+
return data;
53+
}
54+
} // namespace deploy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include <onnxruntime/core/session/experimental_onnxruntime_cxx_api.h>
2+
3+
// if onnxruntime is built with cuda provider, the following header can be added to use cuda gpu
4+
// #include <onnxruntime/core/providers/cuda/cuda_provider_factory.h>
5+
6+
#include <opencv2/opencv.hpp>
7+
8+
namespace deploy {
9+
class OrtSessionHandler {
10+
public:
11+
/**
12+
* @param model_path path to onnx model
13+
*/
14+
OrtSessionHandler(const std::string &model_path, const std::vector<std::vector<int64_t>> &input_tensor_shapes);
15+
16+
virtual std::vector<float> preprocess(const cv::Mat &image, int target_height, int target_width,
17+
const std::vector<float> &mean_val = {0.5, 0.5, 0.5},
18+
const std::vector<float> &std_val = {0.5, 0.5, 0.5}) const;
19+
20+
/**
21+
* @file function to get output tensors
22+
* @brief each std::pair<DataType *, std::vector<int64_t>> is a pair of output tensor's data and its dimension
23+
* most semantic segmentation networks will have only one output tensor
24+
*/
25+
template <typename DataType = float>
26+
std::vector<std::pair<DataType *, std::vector<int64_t>>> run(const std::vector<std::vector<float>> &input_data) const;
27+
28+
private:
29+
std::string _model_path;
30+
std::vector<std::vector<int64_t>> _input_tensor_shapes;
31+
Ort::Env _env;
32+
std::unique_ptr<Ort::Experimental::Session> _session;
33+
};
34+
35+
template <typename DataType>
36+
std::vector<std::pair<DataType *, std::vector<int64_t>>> OrtSessionHandler::run(
37+
const std::vector<std::vector<float>> &input_data) const {
38+
if (_session->GetInputCount() != input_data.size()) {
39+
throw std::runtime_error("invalid input size");
40+
}
41+
42+
std::vector<Ort::Value> input_tensors;
43+
for (int i = 0; i < _session->GetInputCount(); ++i) {
44+
input_tensors.emplace_back(Ort::Experimental::Value::CreateTensor<float>(
45+
const_cast<float *>(input_data[i].data()), input_data[i].size(), _input_tensor_shapes[i]));
46+
}
47+
48+
std::vector<Ort::Value> output_tensors =
49+
_session->Run(_session->GetInputNames(), input_tensors, _session->GetOutputNames());
50+
51+
std::vector<std::pair<DataType *, std::vector<int64_t>>> output(_session->GetOutputCount());
52+
std::vector<std::vector<int64_t>> output_shapes = _session->GetOutputShapes();
53+
for (int i = 0; i < _session->GetOutputCount(); ++i) {
54+
output[i] = std::make_pair(std::move(output_tensors[i].GetTensorMutableData<DataType>()), output_shapes[i]);
55+
}
56+
57+
return output;
58+
}
59+
} // namespace deploy
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "ort_session_handler.hpp"
2+
3+
namespace {
4+
constexpr int BISENETV2_CITYSCAPES_IMAGE_HEIGHT = 1024;
5+
constexpr int BISENETV2_CITYSCAPES_IMAGE_WIDTH = 1024;
6+
7+
static const std::vector<std::vector<uint8_t>> CITYSCAPES_COLORS = {
8+
{128, 64, 128}, {244, 35, 232}, {70, 70, 70}, {102, 102, 156}, {190, 153, 153}, {153, 153, 153}, {250, 170, 30},
9+
{220, 220, 0}, {107, 142, 35}, {152, 251, 152}, {70, 130, 180}, {220, 20, 60}, {255, 0, 0}, {0, 0, 142},
10+
{0, 0, 70}, {0, 60, 100}, {0, 80, 100}, {0, 0, 230}, {119, 11, 32}};
11+
} // namespace
12+
13+
int main(int argc, char *argv[]) {
14+
if (argc != 3) {
15+
std::cerr << "Usage: [app] [/path/to/image] [path/to/onnx/model]" << std::endl;
16+
return EXIT_FAILURE;
17+
}
18+
const std::string image_path = argv[1];
19+
cv::Mat image = cv::imread(image_path);
20+
21+
if (image.empty()) {
22+
std::cerr << "failed to load " << image_path << std::endl;
23+
return EXIT_FAILURE;
24+
}
25+
26+
const std::string onnx_model_path = argv[2];
27+
28+
std::vector<std::vector<int64_t>> input_tensor_shapes{
29+
{1, 3, BISENETV2_CITYSCAPES_IMAGE_HEIGHT, BISENETV2_CITYSCAPES_IMAGE_WIDTH}};
30+
deploy::OrtSessionHandler ort_session_handler(onnx_model_path, input_tensor_shapes);
31+
std::vector<float> input_data =
32+
ort_session_handler.preprocess(image, BISENETV2_CITYSCAPES_IMAGE_HEIGHT, BISENETV2_CITYSCAPES_IMAGE_WIDTH);
33+
34+
// output data's type might change for each different model
35+
auto output_data = ort_session_handler.run<int64_t>({input_data});
36+
37+
// postprocess
38+
// this might change for each different model
39+
cv::Mat segm(BISENETV2_CITYSCAPES_IMAGE_HEIGHT, BISENETV2_CITYSCAPES_IMAGE_WIDTH, CV_8UC(3));
40+
for (int i = 0; i < BISENETV2_CITYSCAPES_IMAGE_HEIGHT; ++i) {
41+
cv::Vec3b *ptr_segm = segm.ptr<cv::Vec3b>(i);
42+
for (int j = 0; j < BISENETV2_CITYSCAPES_IMAGE_WIDTH; ++j) {
43+
const auto &color = CITYSCAPES_COLORS[output_data[0].first[i * BISENETV2_CITYSCAPES_IMAGE_WIDTH + j]];
44+
ptr_segm[j] = cv::Vec3b(color[0], color[1], color[2]);
45+
}
46+
}
47+
cv::resize(segm, segm, image.size(), 0, 0, cv::INTER_NEAREST);
48+
float blended_alpha = 0.4;
49+
segm = (1 - blended_alpha) * image + blended_alpha * segm;
50+
cv::imwrite("out_img.jpg", segm);
51+
52+
return EXIT_SUCCESS;
53+
}

0 commit comments

Comments
 (0)