1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Merge pull request #28444 from abhishek-gola:added_ORT_wrapper

Added ONNX Runtime as an optional wrapper #28444

This PR adds ONNXRuntime (ORT) as an _optional_ wrapper, which can be enabled by adding **WITH_ONNXRUNTIME** flag in CMake command.

Using ORT wrapper the inference time for _resnet50.onnx model_ has come to _**~7ms**_ from _**~14ms**_.
Also, we are able to run models like `ssd_mobilenet_v1.onnx`.
### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Abhishek Gola
2026-02-27 18:17:12 +05:30
committed by GitHub
parent 54b743dca8
commit 95c66292b5
13 changed files with 957 additions and 47 deletions
+15 -5
View File
@@ -390,9 +390,19 @@ OCV_OPTION(WITH_ANDROID_MEDIANDK "Use Android Media NDK for Video I/O (Android)"
OCV_OPTION(WITH_ANDROID_NATIVE_CAMERA "Use Android NDK for Camera I/O (Android)" (ANDROID_NATIVE_API_LEVEL GREATER 23)
VISIBLE_IF ANDROID
VERIFY HAVE_ANDROID_NATIVE_CAMERA)
OCV_OPTION(WITH_ONNX "Include Microsoft ONNX Runtime support" OFF
OCV_OPTION(WITH_ONNXRUNTIME "Include Microsoft ONNX Runtime support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_ONNX)
OCV_OPTION(DOWNLOAD_ONNXRUNTIME "Download ONNX Runtime prebuilt binaries" OFF
VISIBLE_IF WITH_ONNXRUNTIME)
OCV_OPTION(ONNXRUNTIME_PREFER_STATIC "Prefer static ONNX Runtime library when available" ON
VISIBLE_IF WITH_ONNXRUNTIME)
set(ONNXRUNTIME_VERSION "1.24.2" CACHE STRING "ONNX Runtime version to download (prebuilt binaries)")
# Backward compatibility for previous option name.
if(DEFINED WITH_ONNX AND WITH_ONNX AND NOT WITH_ONNXRUNTIME)
set(WITH_ONNXRUNTIME ON CACHE BOOL "Include Microsoft ONNX Runtime support" FORCE)
endif()
OCV_OPTION(WITH_TIMVX "Include Tim-VX support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_TIMVX)
@@ -849,7 +859,7 @@ if(WITH_VTK)
include(cmake/OpenCVDetectVTK.cmake)
endif()
if(WITH_ONNX)
if(WITH_ONNXRUNTIME)
include(cmake/FindONNX.cmake)
endif()
@@ -1848,10 +1858,10 @@ if(WITH_OPENCL OR HAVE_OPENCL)
endif()
endif()
if(WITH_ONNX OR HAVE_ONNX)
if(DEFINED WITH_ONNXRUNTIME OR DEFINED HAVE_ONNXRUNTIME)
status("")
status(" ONNX:" HAVE_ONNX THEN "YES" ELSE "NO")
if(HAVE_ONNX)
status(" ONNX Runtime:" HAVE_ONNXRUNTIME THEN "YES (ver ${ONNX_VERSION})" ELSE "NO")
if(HAVE_ONNXRUNTIME)
status(" Include path:" ONNX_INCLUDE_DIR THEN "${ONNX_INCLUDE_DIR}" ELSE "NO")
status(" Link libraries:" ONNX_LIBRARIES THEN "${ONNX_LIBRARIES}" ELSE "NO")
endif()
+133 -16
View File
@@ -1,4 +1,4 @@
ocv_clear_vars(HAVE_ONNX)
ocv_clear_vars(HAVE_ONNX ORT_LIB ORT_INCLUDE ONNX_LIBRARIES ONNX_INCLUDE_DIR ONNX_VERSION)
set(ONNXRT_ROOT_DIR "" CACHE PATH "ONNX Runtime install directory")
@@ -7,17 +7,74 @@ if(ORT_INSTALL_DIR AND NOT ONNXRT_ROOT_DIR)
set(ONNXRT_ROOT_DIR ${ORT_INSTALL_DIR})
endif()
if(NOT ONNXRT_ROOT_DIR AND DEFINED OpenCV_BINARY_DIR)
if(EXISTS "${OpenCV_BINARY_DIR}/3rdparty/onnxruntime")
file(GLOB __ort_candidates LIST_DIRECTORIES true
"${OpenCV_BINARY_DIR}/3rdparty/onnxruntime/onnxruntime-*")
list(LENGTH __ort_candidates __ort_candidates_len)
if(__ort_candidates_len GREATER 0)
list(GET __ort_candidates 0 ONNXRT_ROOT_DIR)
endif()
unset(__ort_candidates)
unset(__ort_candidates_len)
endif()
endif()
set(__ort_hint_roots "")
if(ONNXRT_ROOT_DIR)
find_library(ORT_LIB onnxruntime
${ONNXRT_ROOT_DIR}/lib
CMAKE_FIND_ROOT_PATH_BOTH)
# The location of headers varies across different versions of ONNX Runtime
find_path(ORT_INCLUDE onnxruntime_cxx_api.h
${ONNXRT_ROOT_DIR}/include/onnxruntime/
${ONNXRT_ROOT_DIR}/include/onnxruntime/core/session
list(APPEND __ort_hint_roots "${ONNXRT_ROOT_DIR}")
endif()
# Prefer CMake config packages if present i.e system-installed ORT
find_package(onnxruntime CONFIG QUIET)
find_package(ONNXRuntime CONFIG QUIET)
set(__ort_target "")
foreach(t
onnxruntime::onnxruntime
ONNXRuntime::onnxruntime
ONNXRuntime::onnxruntime_shared
)
if(TARGET ${t})
set(__ort_target "${t}")
break()
endif()
endforeach()
if(__ort_target)
get_target_property(ORT_INCLUDE ${__ort_target} INTERFACE_INCLUDE_DIRECTORIES)
if(ORT_INCLUDE AND NOT IS_DIRECTORY "${ORT_INCLUDE}")
list(GET ORT_INCLUDE 0 ORT_INCLUDE)
endif()
get_target_property(ORT_LIB ${__ort_target} IMPORTED_LOCATION)
if(NOT ORT_LIB)
get_target_property(ORT_LIB ${__ort_target} IMPORTED_LOCATION_RELEASE)
endif()
if(NOT ORT_LIB)
get_target_property(ORT_LIB ${__ort_target} LOCATION)
endif()
endif()
# Locate headers and libraries via find_* in system paths and/or ONNXRT_ROOT_DIR.
if(NOT ORT_LIB)
find_library(ORT_LIB NAMES onnxruntime
HINTS ${__ort_hint_roots}
PATH_SUFFIXES lib lib64
CMAKE_FIND_ROOT_PATH_BOTH)
endif()
if(NOT ORT_INCLUDE)
find_path(ORT_INCLUDE NAMES onnxruntime_cxx_api.h
HINTS ${__ort_hint_roots}
PATH_SUFFIXES
include
include/onnxruntime
include/onnxruntime/core/session
CMAKE_FIND_ROOT_PATH_BOTH)
endif()
unset(__ort_hint_roots)
macro(detect_onxxrt_ep filename dir have_ep_var)
find_path(ORT_EP_INCLUDE ${filename} ${dir} CMAKE_FIND_ROOT_PATH_BOTH)
if(ORT_EP_INCLUDE)
@@ -26,8 +83,14 @@ macro(detect_onxxrt_ep filename dir have_ep_var)
endmacro()
if(ORT_LIB AND ORT_INCLUDE)
set(__ort_root_for_ep "${ONNXRT_ROOT_DIR}")
if(NOT __ort_root_for_ep)
set(__ort_root_for_ep "${ORT_INCLUDE}")
string(REGEX REPLACE "(/include/onnxruntime.*)$" "" __ort_root_for_ep "${__ort_root_for_ep}")
endif()
# Check DirectML Execution Provider availability
get_filename_component(dml_dir ${ONNXRT_ROOT_DIR}/include/onnxruntime/core/providers/dml ABSOLUTE)
get_filename_component(dml_dir ${__ort_root_for_ep}/include/onnxruntime/core/providers/dml ABSOLUTE)
detect_onxxrt_ep(
dml_provider_factory.h
${dml_dir}
@@ -35,7 +98,7 @@ if(ORT_LIB AND ORT_INCLUDE)
)
# Check CoreML Execution Provider availability
get_filename_component(coreml_dir ${ONNXRT_ROOT_DIR}/include/onnxruntime/core/providers/coreml ABSOLUTE)
get_filename_component(coreml_dir ${__ort_root_for_ep}/include/onnxruntime/core/providers/coreml ABSOLUTE)
detect_onxxrt_ep(
coreml_provider_factory.h
${coreml_dir}
@@ -43,19 +106,73 @@ if(ORT_LIB AND ORT_INCLUDE)
)
set(HAVE_ONNX TRUE)
# Try to report a human-readable ONNX Runtime version in diagnostics/build info.
if(DEFINED onnxruntime_VERSION AND onnxruntime_VERSION)
set(ONNX_VERSION "${onnxruntime_VERSION}")
elseif(DEFINED ONNXRuntime_VERSION AND ONNXRuntime_VERSION)
set(ONNX_VERSION "${ONNXRuntime_VERSION}")
elseif(DEFINED ONNXRUNTIME_VERSION AND ONNXRUNTIME_VERSION)
set(ONNX_VERSION "${ONNXRUNTIME_VERSION}")
else()
set(__ort_version_candidates "")
if(ONNXRT_ROOT_DIR)
list(APPEND __ort_version_candidates "${ONNXRT_ROOT_DIR}")
endif()
if(ORT_LIB)
list(APPEND __ort_version_candidates "${ORT_LIB}")
endif()
foreach(__ort_version_candidate ${__ort_version_candidates})
string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+([.-][0-9A-Za-z]+)?)" __ort_version_match "${__ort_version_candidate}")
if(__ort_version_match)
set(ONNX_VERSION "${__ort_version_match}")
break()
endif()
endforeach()
unset(__ort_version_candidate)
unset(__ort_version_candidates)
unset(__ort_version_match)
endif()
# 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")
if(NOT ONNX_VERSION)
set(ONNX_VERSION "unknown")
endif()
set(ONNX_VERSION "${ONNX_VERSION}" CACHE STRING "ONNX Runtime version")
# 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})
if(NOT TARGET ${ONNX_LIBRARY})
ocv_add_library(${ONNX_LIBRARY} SHARED IMPORTED)
endif()
if(WIN32)
# ORT_LIB is typically the import library (.lib). Prefer matching runtime DLL if available.
set(__ort_dll "")
if(ONNXRT_ROOT_DIR)
find_file(__ort_dll NAMES onnxruntime.dll HINTS "${ONNXRT_ROOT_DIR}" PATH_SUFFIXES bin)
endif()
if(__ort_dll)
set_target_properties(${ONNX_LIBRARY} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ORT_INCLUDE}"
IMPORTED_LOCATION "${__ort_dll}"
IMPORTED_IMPLIB "${ORT_LIB}")
else()
set_target_properties(${ONNX_LIBRARY} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ORT_INCLUDE}"
IMPORTED_LOCATION "${ORT_LIB}"
IMPORTED_IMPLIB "${ORT_LIB}")
endif()
unset(__ort_dll)
else()
set_target_properties(${ONNX_LIBRARY} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ORT_INCLUDE}"
IMPORTED_LOCATION "${ORT_LIB}")
endif()
unset(__ort_root_for_ep)
endif()
if(NOT HAVE_ONNX)
ocv_clear_vars(HAVE_ONNX ORT_LIB ORT_INCLUDE_DIR)
ocv_clear_vars(HAVE_ONNX ORT_LIB ORT_INCLUDE ONNX_LIBRARIES ONNX_INCLUDE_DIR ONNX_VERSION)
endif()
@@ -524,6 +524,10 @@ OpenCV have own DNN inference module which have own build-in engine, but can als
| `INF_ENGINE_RELEASE` | _2020040000_ | **Deprecated since OpenVINO 2022.1** Defines version of Inference Engine library which is tied to OpenVINO toolkit version. Must be a 10-digit string, e.g. _2020040000_ for OpenVINO 2020.4. |
| `WITH_NGRAPH` | _OFF_ | **Deprecated since OpenVINO 2022.1** Enables Intel NGraph library support. This library is part of Inference Engine backend which allows executing arbitrary networks read from files in multiple formats supported by OpenCV: Caffe, TensorFlow, PyTorch, Darknet, etc.. NGraph library must be installed, it is included into Inference Engine. |
| `WITH_OPENVINO` | _OFF_ | Enable Intel OpenVINO Toolkit support. Should be used for OpenVINO>=2022.1 instead of `WITH_INF_ENGINE` and `WITH_NGRAPH`. |
| `WITH_ONNXRUNTIME` | _OFF_ | Enable Microsoft ONNX Runtime backend support for OpenCV DNN. |
| `DOWNLOAD_ONNXRUNTIME` | _OFF_ | Download official ONNX Runtime prebuilt binaries when enabled (or when ONNX Runtime is not available in system paths). |
| `ONNXRUNTIME_PREFER_STATIC` | _ON_ | Prefer static `libonnxruntime.a` when both static and shared ONNX Runtime libraries are available. |
| `ONNXRUNTIME_VERSION` | _1.24.2_ | ONNX Runtime version to download for prebuilt packages. |
| `OPENCV_DNN_CUDA` | _OFF_ | Enable CUDA backend. [CUDA](https://en.wikipedia.org/wiki/CUDA), CUBLAS and [CUDNN](https://developer.nvidia.com/cudnn) must be installed. |
| `WITH_VULKAN` | _OFF_ | Enable experimental [Vulkan](https://en.wikipedia.org/wiki/Vulkan_(API)) backend. Does not require additional dependencies, but can use external Vulkan headers (`VULKAN_INCLUDE_DIRS`). |
+172
View File
@@ -110,6 +110,178 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS
set(include_dirs "")
set(libs "")
if(WITH_ONNXRUNTIME)
include("${OpenCV_SOURCE_DIR}/cmake/FindONNX.cmake")
set(_ort_download_requested OFF)
set(_ort_download_forced OFF)
if(DOWNLOAD_ONNXRUNTIME)
set(_ort_download_requested ON)
set(_ort_download_forced ON)
elseif(NOT HAVE_ONNXRUNTIME)
set(_ort_download_requested ON)
message(STATUS "DNN: ONNX Runtime was not found in system paths, attempting to download prebuilt package")
endif()
if(_ort_download_requested)
set(_ort_filename "")
set(_ort_md5 "")
if(WIN32)
if(X86_64 OR CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_ort_filename "onnxruntime-win-x64-${ONNXRUNTIME_VERSION}.zip")
set(_ort_md5 "c535ec65cf6f850f2a70b4029acdc5d7")
elseif(ARM64 OR AARCH64)
set(_ort_filename "onnxruntime-win-arm64-${ONNXRUNTIME_VERSION}.zip")
set(_ort_md5 "01df64c14cf9285a6b23744f1b2ddf63")
endif()
elseif(APPLE)
if(ARM64 OR AARCH64 OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
set(_ort_filename "onnxruntime-osx-arm64-${ONNXRUNTIME_VERSION}.tgz")
set(_ort_md5 "44302ee5651926ba6b0a3e92a3a303db")
endif()
elseif(UNIX)
if(X86_64)
set(_ort_filename "onnxruntime-linux-x64-${ONNXRUNTIME_VERSION}.tgz")
set(_ort_md5 "8e444d3ed1ea286013e339132ed4f08e")
elseif(AARCH64 OR ARM64)
set(_ort_filename "onnxruntime-linux-aarch64-${ONNXRUNTIME_VERSION}.tgz")
set(_ort_md5 "857b0fbb35f9d4cc117033242fc2a3da")
endif()
endif()
if(NOT _ort_filename)
if(_ort_download_forced OR NOT HAVE_ONNXRUNTIME)
message(FATAL_ERROR
"DNN: there is no official ONNX Runtime prebuilt package for "
"CMAKE_SYSTEM_NAME='${CMAKE_SYSTEM_NAME}', CMAKE_SYSTEM_PROCESSOR='${CMAKE_SYSTEM_PROCESSOR}'. "
"Provide an installed ORT via ONNXRT_ROOT_DIR, or use a supported platform."
)
endif()
else()
set(_ort_url "https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/${_ort_filename}")
string(REGEX REPLACE "\\.(tgz|zip)$" "" _ort_unpack_dirname "${_ort_filename}")
ocv_download(
FILENAME ${_ort_filename}
URL ${_ort_url}
DESTINATION_DIR "${OpenCV_BINARY_DIR}/3rdparty/onnxruntime"
ID "ORT"
HASH ${_ort_md5}
UNPACK
)
set(ONNXRT_ROOT_DIR "${OpenCV_BINARY_DIR}/3rdparty/onnxruntime/${_ort_unpack_dirname}"
CACHE PATH "ONNX Runtime install directory" FORCE)
if(NOT APPLE AND NOT WIN32)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath,'$ORIGIN/../3rdparty/onnxruntime/${_ort_unpack_dirname}/lib'")
endif()
include("${OpenCV_SOURCE_DIR}/cmake/FindONNX.cmake")
endif()
endif() # _ort_download_requested
if(HAVE_ONNX)
set(HAVE_ONNXRUNTIME 1 CACHE INTERNAL "ONNX Runtime availability")
if(ONNXRUNTIME_PREFER_STATIC)
set(_ort_static_lib "")
if(ONNXRT_ROOT_DIR)
find_file(_ort_static_lib
NAMES libonnxruntime.a
HINTS "${ONNXRT_ROOT_DIR}"
PATH_SUFFIXES lib lib64
NO_DEFAULT_PATH
)
endif()
if(NOT _ort_static_lib AND ONNX_LIBRARIES)
foreach(_ort_lib ${ONNX_LIBRARIES})
get_filename_component(_ort_lib_dir "${_ort_lib}" DIRECTORY)
find_file(_ort_static_candidate
NAMES libonnxruntime.a
HINTS "${_ort_lib_dir}"
NO_DEFAULT_PATH
)
if(_ort_static_candidate AND _ort_static_candidate MATCHES "\\.a$")
set(_ort_static_lib "${_ort_static_candidate}")
break()
endif()
endforeach()
endif()
if(_ort_static_lib AND _ort_static_lib MATCHES "\\.a$")
set(ONNX_LIBRARIES "${_ort_static_lib}" CACHE STRING "ONNX Runtime libraries" FORCE)
message(STATUS "DNN: ONNX Runtime static library selected: ${_ort_static_lib}")
endif()
unset(_ort_static_candidate)
unset(_ort_static_lib)
endif()
if(ONNX_INCLUDE_DIR)
list(APPEND include_dirs "${ONNX_INCLUDE_DIR}")
endif()
if(ONNX_LIBRARIES)
list(APPEND libs "${ONNX_LIBRARIES}")
endif()
add_definitions(-DHAVE_ONNXRUNTIME=1)
message(STATUS "DNN: ONNX Runtime enabled")
# Ensure runtime ORT binaries are deployed into OpenCV install tree
set(_ort_runtime_libs "")
if(WIN32)
if(ONNXRT_ROOT_DIR)
file(GLOB _ort_runtime_libs "${ONNXRT_ROOT_DIR}/bin/onnxruntime*.dll")
endif()
if(_ort_runtime_libs)
install(FILES ${_ort_runtime_libs} DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs)
endif()
elseif(APPLE)
if(ONNXRT_ROOT_DIR)
file(GLOB _ort_runtime_libs "${ONNXRT_ROOT_DIR}/lib/libonnxruntime*.dylib")
endif()
if(NOT _ort_runtime_libs AND ONNX_LIBRARIES)
foreach(_ort_lib ${ONNX_LIBRARIES})
get_filename_component(_ort_lib_dir "${_ort_lib}" DIRECTORY)
file(GLOB _ort_runtime_libs "${_ort_lib_dir}/libonnxruntime*.dylib")
if(_ort_runtime_libs)
break()
endif()
endforeach()
endif()
if(_ort_runtime_libs)
install(FILES ${_ort_runtime_libs} DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs)
endif()
else()
if(ONNXRT_ROOT_DIR)
file(GLOB _ort_runtime_libs "${ONNXRT_ROOT_DIR}/lib/libonnxruntime.so*")
endif()
if(NOT _ort_runtime_libs AND ONNX_LIBRARIES)
foreach(_ort_lib ${ONNX_LIBRARIES})
get_filename_component(_ort_lib_dir "${_ort_lib}" DIRECTORY)
file(GLOB _ort_runtime_libs "${_ort_lib_dir}/libonnxruntime.so*")
if(_ort_runtime_libs)
break()
endif()
endforeach()
endif()
if(_ort_runtime_libs)
install(FILES ${_ort_runtime_libs} DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs)
endif()
endif()
unset(_ort_runtime_libs)
else()
message(SEND_ERROR
"DNN: ONNX Runtime support was requested (WITH_ONNXRUNTIME=ON), but it was not found. "
"Set ONNXRT_ROOT_DIR to an existing installation. "
"OpenCV attempts to download supported prebuilt ONNX Runtime packages automatically when needed."
)
endif()
unset(_ort_download_requested)
unset(_ort_download_forced)
endif()
if(HAVE_PROTOBUF)
ocv_target_compile_definitions(${the_module} PRIVATE "HAVE_PROTOBUF=1")
+2 -1
View File
@@ -1020,7 +1020,8 @@ CV__DNN_INLINE_NS_BEGIN
{
ENGINE_CLASSIC=1, //!< Force use the old dnn engine similar to 4.x branch
ENGINE_NEW=2, //!< Force use the new dnn engine. The engine does not support non CPU back-ends for now.
ENGINE_AUTO=3 //!< Try to use the new engine and then fall back to the classic version.
ENGINE_AUTO=3, //!< Try to use the new engine and then fall back to the classic version.
ENGINE_ORT=4 //!< Try to use ONNX Runtime wrapper (ONNX only, requires build with WITH_ONNXRUNTIME=ON).
};
/** @brief Reads a network model stored in <a href="https://pjreddie.com/darknet/">Darknet</a> model files.
+33 -11
View File
@@ -732,19 +732,39 @@ struct TextRecognitionModel_Impl : public Model::Impl
return decodeSeq;
}
static Mat ensureFloat32Prediction(const Mat& prediction)
{
CV_Assert(!prediction.empty());
if (prediction.type() == CV_32FC1)
return prediction;
const int depth = prediction.depth();
if (depth == CV_16F || depth == CV_16BF)
{
Mat prediction32f;
prediction.convertTo(prediction32f, CV_32F);
return prediction32f;
}
CV_CheckType(prediction.type(), CV_32FC1, "");
return prediction;
}
virtual
std::string ctcGreedyDecode(const Mat& prediction)
{
Mat prediction32f = ensureFloat32Prediction(prediction);
const Mat& probs = prediction32f;
std::string decodeSeq;
CV_CheckEQ(prediction.dims, 3, "");
CV_CheckType(prediction.type(), CV_32FC1, "");
CV_CheckEQ(probs.dims, 3, "");
const int vocLength = (int)(vocabulary.size());
CV_CheckLE(prediction.size[1], vocLength, "");
CV_CheckLE(probs.size[1], vocLength, "");
bool ctcFlag = true;
int lastLoc = 0;
for (int i = 0; i < prediction.size[0]; i++)
for (int i = 0; i < probs.size[0]; i++)
{
const float* pred = prediction.ptr<float>(i);
const float* pred = probs.ptr<float>(i);
int maxLoc = 0;
float maxScore = pred[0];
for (int j = 1; j < vocLength + 1; j++)
@@ -853,6 +873,9 @@ struct TextRecognitionModel_Impl : public Model::Impl
virtual
std::string ctcPrefixBeamSearchDecode(const Mat& prediction) {
Mat prediction32f = ensureFloat32Prediction(prediction);
const Mat& probs = prediction32f;
// CTC prefix beam search decode.
// For more detail, refer to:
// https://distill.pub/2017/ctc/#inference
@@ -860,18 +883,17 @@ struct TextRecognitionModel_Impl : public Model::Impl
using Beam = std::vector<std::pair<std::vector<int>, PrefixScore>>;
using BeamInDict = std::unordered_map<std::vector<int>, PrefixScore, PrefixHash>;
CV_CheckType(prediction.type(), CV_32FC1, "");
CV_CheckEQ(prediction.dims, 3, "");
CV_CheckEQ(prediction.size[1], 1, "");
CV_CheckEQ(prediction.size[2], (int)vocabulary.size() + 1, ""); // Length add 1 for ctc blank
CV_CheckEQ(probs.dims, 3, "");
CV_CheckEQ(probs.size[1], 1, "");
CV_CheckEQ(probs.size[2], (int)vocabulary.size() + 1, ""); // Length add 1 for ctc blank
std::string decodeSeq;
Beam beam = {std::make_pair(std::vector<int>(), PrefixScore(0.0, kNegativeInfinity))};
for (int i = 0; i < prediction.size[0]; i++)
for (int i = 0; i < probs.size[0]; i++)
{
// Loop over time
BeamInDict nextBeam;
const float* pred = prediction.ptr<float>(i);
const float* pred = probs.ptr<float>(i);
std::vector<std::pair<float, int>> topkPreds =
TopK(pred, vocabulary.size() + 1, vocPruneSize);
for (const auto& each : topkPreds)
+27
View File
@@ -27,6 +27,14 @@
#include <unordered_map>
#ifdef HAVE_ONNXRUNTIME
namespace Ort {
class Env;
class Session;
class SessionOptions;
}
#endif
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
@@ -36,6 +44,10 @@ using std::string;
typedef std::unordered_map<std::string, int64_t> NamesHash;
#ifdef HAVE_ONNXRUNTIME
struct OrtNamesCache;
#endif
// NB: Implementation is divided between of multiple .cpp files
struct Net::Impl : public detail::NetImplBase
{
@@ -232,6 +244,13 @@ struct Net::Impl : public detail::NetImplBase
void initCUDABackend(const std::vector<LayerPin>& blobsToKeep_);
#endif
#ifdef HAVE_ONNXRUNTIME
std::shared_ptr<Ort::Env> ort_env;
std::shared_ptr<Ort::Session> ort_session;
std::shared_ptr<Ort::SessionOptions> ort_session_options;
std::shared_ptr<OrtNamesCache> ort_names_cache;
#endif
void allocateLayer(int lid, const LayersShapesMap& layersShapes);
// TODO add getter
@@ -373,6 +392,11 @@ struct Net::Impl : public detail::NetImplBase
void forwardGraph(Ptr<Graph>& graph, InputArrayOfArrays inputs, OutputArrayOfArrays outputs, bool isMainGraph);
// run the whole model
void forwardMainGraph(InputArrayOfArrays inputs, OutputArrayOfArrays outputs);
#ifdef HAVE_ONNXRUNTIME
// Run inference through ONNX Runtime session (if configured).
// If outIdxs is empty, returns all ORT outputs in ORT-defined order.
std::vector<Mat> runOrtSession(std::vector<Mat> inputBlobs, const std::vector<int>& outIdxs);
#endif
// run the whole model, convenience wrapper
Mat forwardWithSingleOutput(const std::string& outname);
// run the whole model, convenience wrapper
@@ -427,6 +451,9 @@ inline Net::Impl* getNetImpl(const Layer* layer)
Net readNetFromONNX2(const String&);
Net readNetFromONNX2(const char*, size_t);
Net readNetFromONNX2(const std::vector<uchar>&);
#ifdef HAVE_ONNXRUNTIME
Net readNetFromONNX2_ORT(const String& onnxFile);
#endif
CV__DNN_INLINE_NS_END
}} // namespace cv::dnn
+389 -8
View File
@@ -6,10 +6,209 @@
#include "net_impl.hpp"
#include <limits>
#ifdef HAVE_ONNXRUNTIME
#include <onnxruntime_cxx_api.h>
#endif
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
#ifdef HAVE_ONNXRUNTIME
struct OrtNamesCache
{
std::vector<std::string> input_names;
std::vector<std::string> output_names;
std::unordered_map<std::string, int> input_name_to_index;
std::unordered_map<std::string, int> output_name_to_index;
explicit OrtNamesCache(Ort::Session& session)
{
Ort::AllocatorWithDefaultOptions allocator;
const size_t ninputs = session.GetInputCount();
input_names.reserve(ninputs);
for (size_t i = 0; i < ninputs; ++i)
{
Ort::AllocatedStringPtr in = session.GetInputNameAllocated(i, allocator);
std::string n = in ? std::string(in.get()) : std::string();
input_name_to_index[n] = (int)i;
input_names.push_back(std::move(n));
}
const size_t noutputs = session.GetOutputCount();
output_names.reserve(noutputs);
for (size_t i = 0; i < noutputs; ++i)
{
Ort::AllocatedStringPtr out = session.GetOutputNameAllocated(i, allocator);
std::string n = out ? std::string(out.get()) : std::string();
output_name_to_index[n] = (int)i;
output_names.push_back(std::move(n));
}
}
};
#endif
#ifdef HAVE_ONNXRUNTIME
static int cvTypeFromONNXElemType(const ONNXTensorElementDataType t)
{
switch (t)
{
case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return CV_32F;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: return CV_8U;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: return CV_8S;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: return CV_16U;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: return CV_16S;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return CV_32S;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return CV_64S;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: return CV_8U;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: return CV_64F;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: return CV_16F;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16:return CV_16BF;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: return CV_32U;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: return CV_64U;
default:
return -1;
}
}
static Ort::Value createOrtTensorFromMat(Ort::Session& session,
size_t inputIdx,
const Ort::MemoryInfo& memory_info,
Mat& inputBlob,
std::vector<int64_t>& inputDims,
ONNXTensorElementDataType& in_elem_type)
{
Ort::TypeInfo in_type_info = session.GetInputTypeInfo(inputIdx);
Ort::ConstTensorTypeAndShapeInfo in_tensor_info = in_type_info.GetTensorTypeAndShapeInfo();
in_elem_type = in_tensor_info.GetElementType();
const int cvInType = cvTypeFromONNXElemType(in_elem_type);
if (cvInType < 0)
CV_Error_(Error::StsNotImplemented, ("DNN/ORT: unsupported ORT input element type: %d", (int)in_elem_type));
if (inputBlob.type() != cvInType)
inputBlob.convertTo(inputBlob, cvInType);
if (!inputBlob.isContinuous())
inputBlob = inputBlob.clone();
inputDims.clear();
inputDims.reserve((size_t)inputBlob.dims);
for (int i = 0; i < inputBlob.dims; i++)
inputDims.push_back((int64_t)inputBlob.size[i]);
const size_t nbytes = (size_t)inputBlob.total() * inputBlob.elemSize();
OrtValue* input_tensor_raw = nullptr;
Ort::ThrowOnError(Ort::GetApi().CreateTensorWithDataAsOrtValue(
memory_info,
inputBlob.data,
nbytes,
inputDims.data(),
inputDims.size(),
in_elem_type,
&input_tensor_raw));
return Ort::Value(input_tensor_raw);
}
std::vector<Mat> Net::Impl::runOrtSession(std::vector<Mat> inputBlobs, const std::vector<int>& outIdxs)
{
CV_Assert(this->ort_session);
Ort::Session& session = *this->ort_session;
if (!this->ort_names_cache)
this->ort_names_cache = std::make_shared<OrtNamesCache>(session);
OrtNamesCache& names = *this->ort_names_cache;
if (names.input_names.empty())
CV_Error(Error::StsError, "DNN/ORT: ORT session has no inputs");
if (names.output_names.empty())
CV_Error(Error::StsError, "DNN/ORT: ORT session has no outputs");
const size_t ninputs = names.input_names.size();
if (inputBlobs.size() != ninputs)
CV_Error_(Error::StsBadArg, ("DNN/ORT: expected %zu inputs, but got %zu", ninputs, inputBlobs.size()));
std::vector<const char*> in_names;
in_names.reserve(ninputs);
for (size_t i = 0; i < ninputs; ++i)
in_names.push_back(names.input_names[i].c_str());
std::vector<const char*> out_names;
if (outIdxs.empty())
{
out_names.reserve(names.output_names.size());
for (const std::string& n : names.output_names)
out_names.push_back(n.c_str());
}
else
{
out_names.reserve(outIdxs.size());
for (int idx : outIdxs)
{
CV_CheckGE(idx, 0, "DNN/ORT: output index must be non-negative");
CV_CheckLT((size_t)idx, names.output_names.size(), "DNN/ORT: output index is out of range");
out_names.push_back(names.output_names[(size_t)idx].c_str());
}
}
static const Ort::MemoryInfo memory_info =
Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
std::vector<Ort::Value> input_tensors;
input_tensors.reserve(ninputs);
for (size_t i = 0; i < ninputs; ++i)
{
if (inputBlobs[i].empty())
CV_Error_(Error::StsError, ("DNN/ORT: input '%s' is empty", names.input_names[i].c_str()));
std::vector<int64_t> inputDims;
ONNXTensorElementDataType in_elem_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
input_tensors.push_back(createOrtTensorFromMat(session, i, memory_info, inputBlobs[i], inputDims, in_elem_type));
}
std::vector<Ort::Value> output_tensors = session.Run(
Ort::RunOptions{nullptr},
in_names.data(), input_tensors.data(), input_tensors.size(),
out_names.data(), out_names.size());
CV_CheckEQ(output_tensors.size(), out_names.size(), "DNN/ORT: ORT returned unexpected number of outputs");
std::vector<Mat> results;
results.reserve(output_tensors.size());
for (Ort::Value& outv : output_tensors)
{
Ort::TensorTypeAndShapeInfo shape_info = outv.GetTensorTypeAndShapeInfo();
std::vector<int64_t> out_shape = shape_info.GetShape();
std::vector<int> out_dims;
out_dims.reserve(out_shape.size());
for (int64_t d : out_shape)
{
if (d < 0)
CV_Error(Error::StsError, "DNN/ORT: dynamic output shapes are not supported at runtime");
if (d > (int64_t)std::numeric_limits<int>::max())
CV_Error(Error::StsError, "DNN/ORT: output shape dimension is too large");
out_dims.push_back((int)d);
}
const ONNXTensorElementDataType out_elem_type = shape_info.GetElementType();
const int cvOutType = cvTypeFromONNXElemType(out_elem_type);
if (cvOutType < 0)
CV_Error_(Error::StsNotImplemented, ("DNN/ORT: unsupported ORT output element type: %d", (int)out_elem_type));
uint8_t* out_bytes = outv.GetTensorMutableData<uint8_t>();
Mat view(out_dims, cvOutType, out_bytes);
results.push_back(view.clone()); // detach from ORT-owned memory
}
return results;
}
#endif
std::string modelFormatToString(ModelFormat modelFormat)
{
return
@@ -288,6 +487,15 @@ Ptr<Graph> Net::Impl::newGraph(const std::string& name_, const std::vector<Arg>&
void Net::Impl::prepareForInference()
{
#ifdef HAVE_ONNXRUNTIME
if (this->ort_session)
{
prepared = true;
finalizeLayers = false;
return;
}
#endif
if (!prepared) {
constFold();
//inferTypes();
@@ -364,6 +572,42 @@ void Net::Impl::allocateLayerOutputs(
void Net::Impl::forwardMainGraph(InputArrayOfArrays inputs, OutputArrayOfArrays outputs)
{
#ifdef HAVE_ONNXRUNTIME
if (this->ort_session)
{
if (!netInputLayer || netInputLayer->blobs.empty())
CV_Error(Error::StsError, "DNN/ORT: No input data found");
std::vector<Mat> ortOuts = runOrtSession(netInputLayer->blobs, std::vector<int>());
std::vector<Mat>* outMats = nullptr;
std::vector<UMat>* outUMats = nullptr;
_InputArray::KindFlag outKind = outputs.kind();
if (outKind == _InputArray::STD_VECTOR_MAT)
{
outMats = &outputs.getMatVecRef();
*outMats = ortOuts;
}
else if (outKind == _InputArray::STD_VECTOR_UMAT)
{
outUMats = &outputs.getUMatVecRef();
outUMats->resize(ortOuts.size());
for (size_t i = 0; i < ortOuts.size(); ++i)
ortOuts[i].copyTo(outUMats->at(i));
}
else if (outKind == _InputArray::MAT || outKind == _InputArray::UMAT)
{
CV_CheckEQ((int)ortOuts.size(), 1, "DNN/ORT: single Mat/UMat output requires exactly one ORT output");
ortOuts[0].copyTo(outputs);
}
else
{
CV_Error(Error::StsBadArg, "DNN/ORT: outputs must be Mat, UMat, a vector of Mat's or a vector of UMat's");
}
return;
}
#endif
if (!mainGraph) {
CV_Error(Error::StsNullPtr, "the model was not loaded");
}
@@ -385,22 +629,115 @@ void Net::Impl::forwardMainGraph(InputArrayOfArrays inputs, OutputArrayOfArrays
Mat Net::Impl::forwardWithSingleOutput(const std::string& outname)
{
if (!mainGraph) {
CV_Error(Error::StsNullPtr, "the model was not loaded");
#ifdef HAVE_ONNXRUNTIME
if (this->ort_session)
{
if (!netInputLayer || netInputLayer->blobs.empty())
CV_Error(Error::StsError, "DNN/ORT: No input data found");
if (!this->ort_names_cache)
this->ort_names_cache = std::make_shared<OrtNamesCache>(*this->ort_session);
int outIdx = 0;
if (!outname.empty())
{
OrtNamesCache& names = *this->ort_names_cache;
auto it = names.output_name_to_index.find(outname);
if (it == names.output_name_to_index.end())
CV_Error_(Error::StsObjectNotFound, ("DNN/ORT: output '%s' is not found", outname.c_str()));
outIdx = it->second;
}
std::vector<int> outIdxs(1, outIdx);
std::vector<Mat> outs = runOrtSession(netInputLayer->blobs, outIdxs);
CV_Assert(outs.size() == 1);
return outs[0];
}
const std::vector<Arg>& outargs = mainGraph->outputs();
CV_Assert(outargs.size() > 0);
if (!outname.empty()) {
const ArgData& outdata = args.at(outargs[0].idx);
CV_Assert(outdata.name == outname);
#endif
{
if (!mainGraph) {
CV_Error(Error::StsNullPtr, "the model was not loaded");
}
const std::vector<Arg>& outargs = mainGraph->outputs();
CV_Assert(outargs.size() > 0);
if (!outname.empty()) {
const ArgData& outdata = args.at(outargs[0].idx);
CV_Assert(outdata.name == outname);
}
}
std::vector<Mat> inps={}, outs;
std::vector<Mat> inps, outs;
forwardMainGraph(inps, outs);
CV_Assert(!outs.empty());
return outs[0];
}
void Net::Impl::forwardWithMultipleOutputs(OutputArrayOfArrays outblobs, const std::vector<std::string>& outnames)
{
#ifdef HAVE_ONNXRUNTIME
if (this->ort_session)
{
if (!netInputLayer || netInputLayer->blobs.empty())
CV_Error(Error::StsError, "DNN/ORT: No input data found");
if (!this->ort_names_cache)
this->ort_names_cache = std::make_shared<OrtNamesCache>(*this->ort_session);
OrtNamesCache& names = *this->ort_names_cache;
const int totalOutputs = (int)names.output_names.size();
if (totalOutputs <= 0)
CV_Error(Error::StsError, "DNN/ORT: ORT session has no outputs");
std::vector<int> outIdxs;
if (outnames.empty())
{
outIdxs.resize((size_t)totalOutputs);
for (int i = 0; i < totalOutputs; ++i)
outIdxs[(size_t)i] = i;
}
else
{
outIdxs.reserve(outnames.size());
for (const std::string& n : outnames)
{
auto it = names.output_name_to_index.find(n);
if (it == names.output_name_to_index.end())
CV_Error_(Error::StsObjectNotFound, ("DNN/ORT: output '%s' is not found", n.c_str()));
outIdxs.push_back(it->second);
}
}
std::vector<Mat> outs = runOrtSession(netInputLayer->blobs, outIdxs);
std::vector<Mat>* outMats = nullptr;
std::vector<UMat>* outUMats = nullptr;
_InputArray::KindFlag outKind = outblobs.kind();
if (outKind == _InputArray::STD_VECTOR_MAT) {
outMats = &outblobs.getMatVecRef();
outMats->resize(outs.size());
} else if (outKind == _InputArray::STD_VECTOR_UMAT) {
outUMats = &outblobs.getUMatVecRef();
outUMats->resize(outs.size());
} else if (outKind == _InputArray::MAT || outKind == _InputArray::UMAT) {
CV_CheckEQ((int)outs.size(), 1, "DNN/ORT: Mat/UMat output requires exactly one output");
} else {
CV_Error(Error::StsBadArg, "outputs must be Mat, UMat, a vector of Mat's or a vector of UMat's");
}
for (size_t i = 0; i < outs.size(); ++i) {
Mat src = outs[i];
if (outMats) {
src.copyTo(outMats->at(i));
} else if (outUMats) {
src.copyTo(outUMats->at(i));
} else {
src.copyTo(outblobs);
}
}
return;
}
#endif
if (!mainGraph) {
CV_Error(Error::StsNullPtr, "the model was not loaded");
}
@@ -515,6 +852,50 @@ void Net::Impl::traceArg(std::ostream& strm_, const char* prefix, size_t i, Arg
void Net::Impl::setMainGraphInput(InputArray m, const std::string& inpname)
{
#ifdef HAVE_ONNXRUNTIME
if (this->ort_session)
{
if (!this->ort_names_cache)
this->ort_names_cache = std::make_shared<OrtNamesCache>(*this->ort_session);
OrtNamesCache& names = *this->ort_names_cache;
const size_t ninputs = names.input_names.size();
if (ninputs == 0)
CV_Error(Error::StsError, "DNN/ORT: ORT session has no inputs");
if (!netInputLayer) {
netInputLayer = Ptr<DataLayer>(new DataLayer());
netInputLayer->name = "ort_data_layer";
netInputLayer->type = "Data";
}
Mat inputMat = m.getMat();
if (inputMat.empty())
CV_Error(Error::StsBadArg, "DNN/ORT: Input blob is empty");
if (netInputLayer->blobs.size() != ninputs)
netInputLayer->blobs.resize(ninputs);
size_t inputIdx = 0;
if (inpname.empty())
{
if (ninputs != 1)
CV_Error(Error::StsBadArg, "DNN/ORT: input name must be specified for models with multiple inputs");
inputIdx = 0;
}
else
{
auto it = names.input_name_to_index.find(inpname);
if (it == names.input_name_to_index.end())
CV_Error_(Error::StsObjectNotFound, ("DNN/ORT: input '%s' is not found", inpname.c_str()));
inputIdx = (size_t)it->second;
}
inputMat.copyTo(netInputLayer->blobs[inputIdx]);
return;
}
#endif
CV_Assert(mainGraph);
const std::vector<Arg>& gr_inputs = mainGraph->inputs();
size_t i, ninputs = gr_inputs.size();
+28
View File
@@ -4131,6 +4131,20 @@ Net readNetFromONNX(const String& onnxFile, int engine)
return detail::readNetDiagnostic<ONNXImporter>(onnxFile.c_str());
case ENGINE_NEW:
return readNetFromONNX2(onnxFile);
case ENGINE_ORT:
{
#ifdef HAVE_ONNXRUNTIME
Net net = readNetFromONNX2_ORT(onnxFile);
if (net.empty())
CV_Error(Error::StsError, "DNN/ONNX/ORT: failed to load model");
if (!net.getImpl() || !net.getImpl()->ort_session)
CV_Error(Error::StsError, "DNN/ONNX/ORT: ONNX Runtime session was not initialized");
return net;
#else
CV_LOG_WARNING(NULL, "DNN/ONNX/ORT: OpenCV was built without ONNX Runtime (WITH_ONNXRUNTIME=OFF). Falling back to ENGINE_AUTO.");
#endif
}
/* fall through */
case ENGINE_AUTO:
{
Net net = readNetFromONNX2(onnxFile);
@@ -4156,6 +4170,13 @@ Net readNetFromONNX(const char* buffer, size_t sizeBuffer, int engine)
return detail::readNetDiagnostic<ONNXImporter>(buffer, sizeBuffer);
case ENGINE_NEW:
return readNetFromONNX2(buffer, sizeBuffer);
case ENGINE_ORT:
#ifdef HAVE_ONNXRUNTIME
CV_Error(Error::StsNotImplemented, "DNN/ONNX/ORT: loading from memory buffer is not supported");
#else
CV_LOG_WARNING(NULL, "DNN/ONNX/ORT: OpenCV was built without ONNX Runtime (WITH_ONNXRUNTIME=OFF). Falling back to ENGINE_AUTO.");
#endif
/* fall through */
case ENGINE_AUTO:
{
Net net = readNetFromONNX2(buffer, sizeBuffer);
@@ -4181,6 +4202,13 @@ Net readNetFromONNX(const std::vector<uchar>& buffer, int engine)
return readNetFromONNX(reinterpret_cast<const char*>(buffer.data()), buffer.size());
case ENGINE_NEW:
return readNetFromONNX2(buffer);
case ENGINE_ORT:
#ifdef HAVE_ONNXRUNTIME
CV_Error(Error::StsNotImplemented, "DNN/ONNX/ORT: loading from memory buffer is not supported");
#else
CV_LOG_WARNING(NULL, "DNN/ONNX/ORT: OpenCV was built without ONNX Runtime (WITH_ONNXRUNTIME=OFF). Falling back to ENGINE_AUTO.");
#endif
/* fall through */
case ENGINE_AUTO:
{
Net net = readNetFromONNX2(buffer);
+60
View File
@@ -5,6 +5,10 @@
#include "../precomp.hpp"
#include "../net_impl.hpp"
#ifdef HAVE_ONNXRUNTIME
#include <onnxruntime_cxx_api.h>
#endif
#include <opencv2/dnn/shape_utils.hpp>
#include <opencv2/dnn/layer_reg.private.hpp>
#include <opencv2/core/utils/filesystem.hpp>
@@ -2827,6 +2831,62 @@ Net readNetFromONNX2(const String& onnxFile)
return net;
}
#ifdef HAVE_ONNXRUNTIME
Net readNetFromONNX2_ORT(const String& onnxFile)
{
try
{
static auto s_ort_env = std::make_shared<Ort::Env>(ORT_LOGGING_LEVEL_WARNING, "OpenCV_DNN_ORT");
Net net;
auto impl = net.getImpl();
impl->ort_env = s_ort_env;
impl->ort_session_options = std::make_shared<Ort::SessionOptions>();
#ifdef _WIN32
std::wstring w_onnxFile(onnxFile.begin(), onnxFile.end());
impl->ort_session = std::make_shared<Ort::Session>(*s_ort_env, w_onnxFile.c_str(), *impl->ort_session_options);
#else
impl->ort_session = std::make_shared<Ort::Session>(*s_ort_env, onnxFile.c_str(), *impl->ort_session_options);
#endif
impl->modelFileName = onnxFile;
impl->modelFormat = DNN_MODEL_ONNX;
Ptr<Graph> g = impl->newGraph("ort_session_active", {}, true);
Ort::Session& session = *impl->ort_session;
Ort::AllocatorWithDefaultOptions allocator;
const size_t noutputs = session.GetOutputCount();
if (noutputs == 0)
CV_Error(Error::StsError, "DNN/ONNX/ORT: ORT session has no outputs");
std::vector<Arg> outs;
outs.reserve(noutputs);
for (size_t i = 0; i < noutputs; ++i)
{
Ort::AllocatedStringPtr out = session.GetOutputNameAllocated(i, allocator);
std::string n = out ? std::string(out.get()) : std::string();
if (n.empty())
n = format("output_%zu", i);
if (impl->haveArg(n))
n = format("%s_%zu", n.c_str(), i);
outs.push_back(impl->newArg(n, DNN_ARG_OUTPUT));
}
if (g)
g->setOutputs(outs);
CV_LOG_INFO(NULL, "DNN/ONNX: Successfully initialized ORT Session for " << onnxFile);
return net;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "DNN/ONNX/ORT: ORT initialization failed (" << e.what() << ")");
return Net();
}
}
#endif // HAVE_ONNXRUNTIME
Net readNetFromONNX2(const char* buffer, size_t size)
{
ONNXImporter2 importer;
@@ -0,0 +1,90 @@
// 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.
#include "test_precomp.hpp"
#include "npy_blob.hpp"
namespace opencv_test { namespace {
#ifdef HAVE_ONNXRUNTIME
static std::string _tf(const std::string& filename, bool required = true)
{
return findDataFile(std::string("dnn/onnx/") + filename, required);
}
static cv::dnn::Net readNetFromONNX_ORT(const std::string& onnxModelPath)
{
cv::dnn::Net net = cv::dnn::readNetFromONNX(onnxModelPath, cv::dnn::ENGINE_ORT);
EXPECT_FALSE(net.empty());
return net;
}
TEST(Test_ONNX_ORT_Wrapper, SingleInputSingleOutput)
{
const std::string basename = "convolution";
const std::string onnxmodel = _tf("models/" + basename + ".onnx", true);
cv::Mat input = blobFromNPY(_tf("data/input_" + basename + ".npy"));
cv::Mat ref = blobFromNPY(_tf("data/output_" + basename + ".npy"));
cv::dnn::Net net = readNetFromONNX_ORT(onnxmodel);
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
net.setInput(input);
cv::Mat out = net.forward();
normAssert(ref, out, "ORT 1in/1out convolution", 1e-5, 1e-4);
}
TEST(Test_ONNX_ORT_Wrapper, MultipleInputSingleOutput)
{
const std::string basename = "min";
const std::string onnxmodel = _tf("models/" + basename + ".onnx", true);
cv::Mat inp0 = blobFromNPY(_tf("data/input_" + basename + "_0.npy"));
cv::Mat inp1 = blobFromNPY(_tf("data/input_" + basename + "_1.npy"));
cv::Mat ref = blobFromNPY(_tf("data/output_" + basename + ".npy"));
cv::dnn::Net net = readNetFromONNX_ORT(onnxmodel);
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
net.setInput(inp0, "0");
net.setInput(inp1, "1");
cv::Mat out = net.forward();
normAssert(ref, out, "ORT 2in/1out min", 1e-5, 1e-4);
}
TEST(Test_ONNX_ORT_Wrapper, SingleInputMultipleOutput)
{
const std::string basename = "top_k";
const std::string onnxmodel = _tf("models/" + basename + ".onnx", true);
cv::Mat input = cv::dnn::readTensorFromONNX(_tf("data/input_" + basename + ".pb"));
cv::Mat ref_val = cv::dnn::readTensorFromONNX(_tf("data/output_" + basename + "_0.pb"));
cv::Mat ref_ind = cv::dnn::readTensorFromONNX(_tf("data/output_" + basename + "_1.pb"));
cv::dnn::Net net = readNetFromONNX_ORT(onnxmodel);
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
net.setInput(input);
std::vector<cv::Mat> outputs;
net.forward(outputs, std::vector<std::string>{"values", "indices"});
ASSERT_EQ(outputs.size(), 2u);
normAssert(ref_val, outputs[0], "ORT top_k values", 1e-5, 1e-4);
normAssert(ref_ind, outputs[1], "ORT top_k indices", 0.0, 0.0);
}
#else // HAVE_ONNXRUNTIME
TEST(Test_ONNX_ORT_Wrapper, DISABLED_NoONNXRuntime) {}
#endif
}} // namespace
+2 -3
View File
@@ -257,6 +257,7 @@ int main(int argc, char **argv)
//! [Set input blob]
net.setInput(blob);
//! [Set input blob]
int64 t0 = getTickCount();
if (modelName == "u2netp")
{
@@ -291,9 +292,7 @@ int main(int argc, char **argv)
}
// Put efficiency information.
vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
double t = (getTickCount() - t0) * 1000.0 / getTickFrequency();
string label = format("Inference time: %.2f ms", t);
Rect r = getTextSize(Size(), label, Point(), fontFace, fontSize, fontWeight);
r.height += fontSize; // padding
+2 -3
View File
@@ -135,6 +135,7 @@ def main(func_args=None):
blob = cv.dnn.blobFromImage(frame, args.scale, (inpWidth, inpHeight), args.mean, args.rgb, crop=False)
net.setInput(blob)
t0 = cv.getTickCount()
if args.alias == 'u2netp':
output = net.forward(net.getUnconnectedOutLayersNames())
pred = output[0][0, 0, :, :]
@@ -167,9 +168,7 @@ def main(func_args=None):
showLegend(labels, colors, legend)
# Put efficiency information.
t, _ = net.getPerfProfile()
label = 'Inference time: %.2f ms' % (t * 1000.0 / cv.getTickFrequency())
label = 'Inference time: %.2f ms' % ((cv.getTickCount() - t0) * 1000.0 / cv.getTickFrequency())
labelSize, _ = cv.getTextSize(label, cv.FONT_HERSHEY_SIMPLEX, fontSize, fontThickness)
cv.rectangle(frame, (0, 0), (labelSize[0]+10, labelSize[1]), (255,255,255), cv.FILLED)
cv.putText(frame, label, (10, int(25*fontSize)), cv.FONT_HERSHEY_SIMPLEX, fontSize, (0, 0, 0), fontThickness)