Skip to content

Commit

Permalink
Merge pull request opencv#18716 from dmatveev:dm/upstream_onnx
Browse files Browse the repository at this point in the history
* G-API: Introduce ONNX backend for Inference

- Basic operations are implemented (Infer, -ROI, -List, -List2);
- Implemented automatic preprocessing for ONNX models;
- Test suite is extended with `OPENCV_GAPI_ONNX_MODEL_PATH` env for test data
  (test data is an ONNX Model Zoo repo snapshot);
- Fixed kernel lookup logic in core G-API:
  - Lookup NN kernels not in the default package, but in the associated
    backend's aux package. Now two NN backends can work in the same graph.
- Added Infer SSD demo and a combined ONNX/IE demo;

* G-API/ONNX: Fix some of CMake issues

Co-authored-by: Pashchenkov, Maxim <maxim.pashchenkov@intel.com>
  • Loading branch information
dmatveev and Pashchenkov, Maxim authored Nov 3, 2020
1 parent 2a3cdba commit a110ede
Show file tree
Hide file tree
Showing 10 changed files with 1,920 additions and 6 deletions.
17 changes: 17 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ OCV_OPTION(WITH_ANDROID_MEDIANDK "Use Android Media NDK for Video I/O (Android)"
OCV_OPTION(WITH_TENGINE "Include Arm Inference Tengine support" OFF
VISIBLE_IF (ARM OR AARCH64) AND (UNIX OR ANDROID) AND NOT IOS
VERIFY HAVE_TENGINE)
OCV_OPTION(WITH_ONNX "Include Microsoft ONNX Runtime support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_ONNX)

# OpenCV build components
# ===================================================
Expand Down Expand Up @@ -775,6 +778,11 @@ if(WITH_QUIRC)
add_subdirectory(3rdparty/quirc)
set(HAVE_QUIRC TRUE)
endif()

if(WITH_ONNX)
include(cmake/FindONNX.cmake)
endif()

# ----------------------------------------------------------------------------
# OpenCV HAL
# ----------------------------------------------------------------------------
Expand Down Expand Up @@ -1556,6 +1564,15 @@ if(WITH_OPENCL OR HAVE_OPENCL)
endif()
endif()

if(WITH_ONNX OR HAVE_ONNX)
status("")
status(" ONNX:" HAVE_ONNX THEN "YES" ELSE "NO")
if(HAVE_ONNX)
status(" Include path:" ONNX_INCLUDE_DIR THEN "${ONNX_INCLUDE_DIR}" ELSE "NO")
status(" Link libraries:" ONNX_LIBRARIES THEN "${ONNX_LIBRARIES}" ELSE "NO")
endif()
endif()

# ========================== python ==========================
if(BUILD_opencv_python2)
status("")
Expand Down
36 changes: 36 additions & 0 deletions cmake/FindONNX.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ocv_clear_vars(HAVE_ONNX)

set(ONNXRT_ROOT_DIR "" CACHE PATH "ONNX Runtime install directory")

# For now, check the old name ORT_INSTALL_DIR
if(ORT_INSTALL_DIR AND NOT ONNXRT_ROOT_DIR)
set(ONNXRT_ROOT_DIR ORT_INSTALL_DIR)
endif()

if(ONNXRT_ROOT_DIR)
find_library(ORT_LIB onnxruntime
${ONNXRT_ROOT_DIR}/lib
CMAKE_FIND_ROOT_PATH_BOTH)
find_path(ORT_INCLUDE onnxruntime_cxx_api.h
${ONNXRT_ROOT_DIR}/include/onnxruntime/core/session
CMAKE_FIND_ROOT_PATH_BOTH)
endif()

if(ORT_LIB AND ORT_INCLUDE)
set(HAVE_ONNX TRUE)
# For CMake output only
set(ONNX_LIBRARIES "${ORT_LIB}" CACHE STRING "ONNX Runtime libraries")
set(ONNX_INCLUDE_DIR "${ORT_INCLUDE}" CACHE STRING "ONNX Runtime include path")

# Link target with associated interface headers
set(ONNX_LIBRARY "onnxruntime" CACHE STRING "ONNX Link Target")
ocv_add_library(${ONNX_LIBRARY} SHARED IMPORTED)
set_target_properties(${ONNX_LIBRARY} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${ORT_INCLUDE}
IMPORTED_LOCATION ${ORT_LIB}
IMPORTED_IMPLIB ${ORT_LIB})
endif()

if(NOT HAVE_ONNX)
ocv_clear_vars(HAVE_ONNX ORT_LIB ORT_INCLUDE_DIR)
endif()
13 changes: 13 additions & 0 deletions modules/gapi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ set(gapi_srcs
src/backends/ie/giebackend.cpp
src/backends/ie/giebackend/giewrapper.cpp

# ONNX Backend.
src/backends/onnx/gonnxbackend.cpp

# Render Backend.
src/backends/render/grenderocv.cpp
src/backends/render/ft_render.cpp
Expand Down Expand Up @@ -205,10 +208,20 @@ if(HAVE_PLAIDML)
ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${PLAIDML_INCLUDE_DIRS})
endif()


if(WIN32)
# Required for htonl/ntohl on Windows
ocv_target_link_libraries(${the_module} PRIVATE wsock32 ws2_32)
endif()

if(HAVE_ONNX)
ocv_target_link_libraries(${the_module} PRIVATE ${ONNX_LIBRARY})
ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX=1)
if(TARGET opencv_test_gapi)
ocv_target_compile_definitions(opencv_test_gapi PRIVATE HAVE_ONNX=1)
ocv_target_link_libraries(opencv_test_gapi PRIVATE ${ONNX_LIBRARY})
endif()
endif()

ocv_add_perf_tests()
ocv_add_samples()
138 changes: 138 additions & 0 deletions modules/gapi/include/opencv2/gapi/infer/onnx.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation

#ifndef OPENCV_GAPI_INFER_ONNX_HPP
#define OPENCV_GAPI_INFER_ONNX_HPP

#include <unordered_map>
#include <string>
#include <array>
#include <tuple> // tuple, tuple_size

#include <opencv2/gapi/opencv_includes.hpp>
#include <opencv2/gapi/util/any.hpp>

#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage

namespace cv {
namespace gapi {
namespace onnx {

GAPI_EXPORTS cv::gapi::GBackend backend();

enum class TraitAs: int {
TENSOR, //!< G-API traits an associated cv::Mat as a raw tensor
// and passes dimensions as-is
IMAGE //!< G-API traits an associated cv::Mat as an image so
// creates an "image" blob (NCHW/NHWC, etc)
};

using PostProc = std::function<void(const std::unordered_map<std::string, cv::Mat> &,
std::unordered_map<std::string, cv::Mat> &)>;


namespace detail {
struct ParamDesc {
std::string model_path;

// NB: nun_* may differ from topology's real input/output port numbers
// (e.g. topology's partial execution)
std::size_t num_in; // How many inputs are defined in the operation
std::size_t num_out; // How many outputs are defined in the operation

// NB: Here order follows the `Net` API
std::vector<std::string> input_names;
std::vector<std::string> output_names;

using ConstInput = std::pair<cv::Mat, TraitAs>;
std::unordered_map<std::string, ConstInput> const_inputs;

std::vector<cv::Scalar> mean;
std::vector<cv::Scalar> stdev;

std::vector<cv::GMatDesc> out_metas;
PostProc custom_post_proc;

std::vector<bool> normalize;
};
} // namespace detail

template<typename Net>
struct PortCfg {
using In = std::array
< std::string
, std::tuple_size<typename Net::InArgs>::value >;
using Out = std::array
< std::string
, std::tuple_size<typename Net::OutArgs>::value >;
using NormCoefs = std::array
< cv::Scalar
, std::tuple_size<typename Net::InArgs>::value >;
using Normalize = std::array
< bool
, std::tuple_size<typename Net::InArgs>::value >;
};

template<typename Net> class Params {
public:
Params(const std::string &model) {
desc.model_path = model;
desc.num_in = std::tuple_size<typename Net::InArgs>::value;
desc.num_out = std::tuple_size<typename Net::OutArgs>::value;
};

// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::onnx::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { desc }; }
// END(G-API's network parametrization API)

Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &ll) {
desc.input_names.assign(ll.begin(), ll.end());
return *this;
}

Params<Net>& cfgOutputLayers(const typename PortCfg<Net>::Out &ll) {
desc.output_names.assign(ll.begin(), ll.end());
return *this;
}

Params<Net>& constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint = TraitAs::TENSOR) {
desc.const_inputs[layer_name] = {data, hint};
return *this;
}

Params<Net>& cfgMeanStd(const typename PortCfg<Net>::NormCoefs &m,
const typename PortCfg<Net>::NormCoefs &s) {
desc.mean.assign(m.begin(), m.end());
desc.stdev.assign(s.begin(), s.end());
return *this;
}

Params<Net>& cfgPostProc(const std::vector<cv::GMatDesc> &outs,
const PostProc &pp) {
desc.out_metas = outs;
desc.custom_post_proc = pp;
return *this;
}

Params<Net>& cfgNormalize(const typename PortCfg<Net>::Normalize &n) {
desc.normalize.assign(n.begin(), n.end());
return *this;
}

protected:
detail::ParamDesc desc;
};

} // namespace onnx
} // namespace gapi
} // namespace cv

#endif // OPENCV_GAPI_INFER_HPP
Loading

0 comments on commit a110ede

Please sign in to comment.