Skip to content

Commit

Permalink
[Feature] Add C++ API for SDK (#831)
Browse files Browse the repository at this point in the history
* add C++ API

* unify result type & add examples

* minor fix

* install cxx API headers

* fix Mat, add more examples

* fix monolithic build & fix lint

* install examples correctly

* fix lint
  • Loading branch information
lzhangzz authored Jul 29, 2022
1 parent f54da4e commit f54854e
Show file tree
Hide file tree
Showing 23 changed files with 1,104 additions and 28 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ if (MMDEPLOY_BUILD_SDK)
endif ()

if (MMDEPLOY_BUILD_EXAMPLES)
include(${CMAKE_SOURCE_DIR}/cmake/opencv.cmake)
add_subdirectory(demo/csrc)
endif ()

Expand Down
1 change: 1 addition & 0 deletions cmake/MMDeployConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(MMDEPLOY_TARGET_DEVICES @MMDEPLOY_TARGET_DEVICES@)
set(MMDEPLOY_TARGET_BACKENDS @MMDEPLOY_TARGET_BACKENDS@)
set(MMDEPLOY_BUILD_TYPE @CMAKE_BUILD_TYPE@)
set(MMDEPLOY_BUILD_SHARED @MMDEPLOY_SHARED_LIBS@)
set(MMDEPLOY_BUILD_SDK_CXX_API @MMDEPLOY_BUILD_SDK_CXX_API@)
set(MMDEPLOY_BUILD_SDK_MONOLITHIC @MMDEPLOY_BUILD_SDK_MONOLITHIC@)
set(MMDEPLOY_VERSION_MAJOR @MMDEPLOY_VERSION_MAJOR@)
set(MMDEPLOY_VERSION_MINOR @MMDEPLOY_VERSION_MINOR@)
Expand Down
1 change: 1 addition & 0 deletions csrc/mmdeploy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ if (MMDEPLOY_BUILD_SDK)
add_subdirectory(net)
add_subdirectory(codebase)
add_subdirectory(apis/c/mmdeploy)
add_subdirectory(apis/cxx)
endif ()
24 changes: 24 additions & 0 deletions csrc/mmdeploy/apis/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) OpenMMLab. All rights reserved.

cmake_minimum_required(VERSION 3.14)
project(mmdeploy_cxx_api)

if (MMDEPLOY_BUILD_SDK_CXX_API)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17)
target_link_libraries(${PROJECT_NAME} INTERFACE mmdeploy::core)
foreach (task ${MMDEPLOY_TASKS})
target_link_libraries(mmdeploy_${task} INTERFACE ${PROJECT_NAME})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mmdeploy/${task}.hpp
DESTINATION include/mmdeploy)
endforeach ()
if (TARGET mmdeploy)
target_link_libraries(mmdeploy INTERFACE ${PROJECT_NAME})
endif ()
mmdeploy_export(${PROJECT_NAME})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mmdeploy/common.hpp
DESTINATION include/mmdeploy)
endif ()
67 changes: 67 additions & 0 deletions csrc/mmdeploy/apis/cxx/mmdeploy/classifier.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) OpenMMLab. All rights reserved.

#ifndef MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_CLASSIFIER_HPP_
#define MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_CLASSIFIER_HPP_

#include "mmdeploy/classifier.h"
#include "mmdeploy/common.hpp"

namespace mmdeploy {

using Classification = mmdeploy_classification_t;

class Classifier : public NonMovable {
public:
Classifier(const Model& model, const Device& device) {
auto ec = mmdeploy_classifier_create(model, device.name(), device.index(), &classifier_);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}
}

~Classifier() {
if (classifier_) {
mmdeploy_classifier_destroy(classifier_);
classifier_ = {};
}
}

using Result = Result_<Classification>;

std::vector<Result> Apply(Span<const Mat> images) {
if (images.empty()) {
return {};
}

Classification* results{};
int* result_count{};
auto ec = mmdeploy_classifier_apply(classifier_, reinterpret(images.data()),
static_cast<int>(images.size()), &results, &result_count);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}

std::vector<Result> rets;
rets.reserve(images.size());

std::shared_ptr<Classification> data(results, [result_count, count = images.size()](auto p) {
mmdeploy_classifier_release_result(p, result_count, count);
});

size_t offset = 0;
for (size_t i = 0; i < images.size(); ++i) {
offset += rets.emplace_back(offset, result_count[i], data).size();
}

return rets;
}

Result Apply(const Mat& img) { return Apply(Span{img})[0]; }

private:
mmdeploy_classifier_t classifier_{};
};

} // namespace mmdeploy

#endif // MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_CLASSIFIER_HPP_
145 changes: 145 additions & 0 deletions csrc/mmdeploy/apis/cxx/mmdeploy/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) OpenMMLab. All rights reserved.

#ifndef MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_COMMON_H_
#define MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_COMMON_H_

#include <memory>
#include <type_traits>
#include <utility>

#include "mmdeploy/common.h"
#include "mmdeploy/core/mpl/span.h"
#include "mmdeploy/core/status_code.h"
#include "mmdeploy/core/types.h"
#include "mmdeploy/model.h"

#ifndef MMDEPLOY_CXX_USE_OPENCV
#define MMDEPLOY_CXX_USE_OPENCV 1
#endif

#if MMDEPLOY_CXX_USE_OPENCV
#include "opencv2/core/core.hpp"
#endif

namespace mmdeploy {

using Rect = mmdeploy_rect_t;

namespace { // for now, avoid conflict with internal classes, for now

class Model {
public:
explicit Model(const char* path) {
mmdeploy_model_t model{};
auto ec = mmdeploy_model_create_by_path(path, &model);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}
model_.reset(model, [](auto p) { mmdeploy_model_destroy(p); });
}

Model(const void* buffer, size_t size) {
mmdeploy_model_t model{};
auto ec = mmdeploy_model_create(buffer, static_cast<int>(size), &model);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}
model_.reset(model, [](auto p) { mmdeploy_model_destroy(p); });
}

operator mmdeploy_model_t() const noexcept { return model_.get(); }

private:
std::shared_ptr<mmdeploy_model> model_{};
};

class Device {
public:
explicit Device(std::string name, int index = 0) : name_(std::move(name)), index_(index) {}

const char* name() const noexcept { return name_.c_str(); }
int index() const noexcept { return index_; }

private:
std::string name_;
int index_;
};

class Mat {
public:
Mat() : desc_{} {}

Mat(int height, int width, int channels, mmdeploy_pixel_format_t format,
mmdeploy_data_type_t type, uint8_t* data)
: desc_{data, height, width, channels, format, type} {}

const mmdeploy_mat_t& desc() const noexcept { return desc_; }

#if MMDEPLOY_CXX_USE_OPENCV
Mat(const cv::Mat& mat, mmdeploy_pixel_format_t pixel_format)
: desc_{mat.data, mat.rows, mat.cols, mat.channels(), pixel_format, GetCvType(mat.depth())} {
if (pixel_format == MMDEPLOY_PIXEL_FORMAT_COUNT) {
throw_exception(eNotSupported);
}
if (desc_.type == MMDEPLOY_DATA_TYPE_COUNT) {
throw_exception(eNotSupported);
}
}
Mat(const cv::Mat& mat) : Mat(mat, GetCvFormat(mat.channels())) {}

static mmdeploy_data_type_t GetCvType(int depth) {
switch (depth) {
case CV_8U:
return MMDEPLOY_DATA_TYPE_UINT8;
case CV_32F:
return MMDEPLOY_DATA_TYPE_FLOAT;
default:
return MMDEPLOY_DATA_TYPE_COUNT;
}
}
static mmdeploy_pixel_format_t GetCvFormat(int channels) {
switch (channels) {
case 1:
return MMDEPLOY_PIXEL_FORMAT_GRAYSCALE;
case 3:
return MMDEPLOY_PIXEL_FORMAT_BGR;
case 4:
return MMDEPLOY_PIXEL_FORMAT_BGRA;
default:
return MMDEPLOY_PIXEL_FORMAT_COUNT;
}
}
#endif
private:
mmdeploy_mat_t desc_;
};

template <typename T>
class Result_ {
public:
Result_(size_t offset, size_t size, std::shared_ptr<T> data)
: offset_(offset), size_(size), data_(std::move(data)) {}

T& operator[](size_t index) const noexcept { return *(data_.get() + offset_ + index); }
size_t size() const noexcept { return size_; }
T* begin() const noexcept { return data_.get() + offset_; }
T* end() const noexcept { return begin() + size_; }

T* operator->() const noexcept { return data_.get(); }
T& operator*() const noexcept { return *data_; }

private:
size_t offset_;
size_t size_;
std::shared_ptr<T> data_;
};

inline const mmdeploy_mat_t* reinterpret(const Mat* p) {
return reinterpret_cast<const mmdeploy_mat_t*>(p);
}

} // namespace

} // namespace mmdeploy

#endif // MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_COMMON_H_
67 changes: 67 additions & 0 deletions csrc/mmdeploy/apis/cxx/mmdeploy/detector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) OpenMMLab. All rights reserved.

#ifndef MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_DETECTOR_HPP_
#define MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_DETECTOR_HPP_

#include "mmdeploy/common.hpp"
#include "mmdeploy/detector.h"

namespace mmdeploy {

using Detection = mmdeploy_detection_t;

class Detector : public NonMovable {
public:
Detector(const Model& model, const Device& device) {
auto ec = mmdeploy_detector_create(model, device.name(), device.index(), &detector_);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}
}

~Detector() {
if (detector_) {
mmdeploy_detector_destroy(detector_);
detector_ = {};
}
}

using Result = Result_<Detection>;

std::vector<Result> Apply(Span<const Mat> images) {
if (images.empty()) {
return {};
}

Detection* results{};
int* result_count{};
auto ec = mmdeploy_detector_apply(detector_, reinterpret(images.data()),
static_cast<int>(images.size()), &results, &result_count);
if (ec != MMDEPLOY_SUCCESS) {
throw_exception(static_cast<ErrorCode>(ec));
}

std::shared_ptr<Detection> data(results, [result_count, count = images.size()](auto p) {
mmdeploy_detector_release_result(p, result_count, count);
});

std::vector<Result> rets;
rets.reserve(images.size());

size_t offset = 0;
for (size_t i = 0; i < images.size(); ++i) {
offset += rets.emplace_back(offset, result_count[i], data).size();
}

return rets;
}

Result Apply(const Mat& image) { return Apply(Span{image})[0]; }

private:
mmdeploy_detector_t detector_{};
};

} // namespace mmdeploy

#endif // MMDEPLOY_CSRC_MMDEPLOY_APIS_CXX_DETECTOR_HPP_
Loading

0 comments on commit f54854e

Please sign in to comment.