1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2023-07-27 18:38:25 +03:00
151 changed files with 5199 additions and 1111 deletions
+5
View File
@@ -162,6 +162,7 @@ set(gapi_srcs
# ONNX backend
src/backends/onnx/gonnxbackend.cpp
src/backends/onnx/dml_ep.cpp
# Render backend
src/backends/render/grenderocv.cpp
@@ -254,6 +255,7 @@ ocv_target_link_libraries(${the_module} PRIVATE ade)
if(TARGET ocv.3rdparty.openvino AND OPENCV_GAPI_WITH_OPENVINO)
ocv_target_link_libraries(${the_module} PRIVATE ocv.3rdparty.openvino)
ocv_install_used_external_targets(ocv.3rdparty.openvino)
endif()
if(HAVE_TBB)
@@ -365,6 +367,9 @@ endif()
if(HAVE_ONNX)
ocv_target_link_libraries(${the_module} PRIVATE ${ONNX_LIBRARY})
ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX=1)
if(HAVE_ONNX_DML)
ocv_target_compile_definitions(${the_module} PRIVATE HAVE_ONNX_DML=1)
endif()
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})
@@ -51,6 +51,7 @@ struct GAPI_EXPORTS GKernel
GShapes outShapes; // types (shapes) kernel's outputs
GKinds inKinds; // kinds of kernel's inputs (fixme: below)
GCtors outCtors; // captured constructors for template output types
GKinds outKinds; // kinds of kernel's outputs (fixme: below)
};
// TODO: It's questionable if inKinds should really be here. Instead,
// this information could come from meta.
@@ -227,7 +228,8 @@ public:
, &K::getOutMeta
, {detail::GTypeTraits<R>::shape...}
, {detail::GTypeTraits<Args>::op_kind...}
, {detail::GObtainCtor<R>::get()...}});
, {detail::GObtainCtor<R>::get()...}
, {detail::GTypeTraits<R>::op_kind...}});
call.pass(args...); // TODO: std::forward() here?
return yield(call, typename detail::MkSeq<sizeof...(R)>::type());
}
@@ -251,7 +253,8 @@ public:
, &K::getOutMeta
, {detail::GTypeTraits<R>::shape}
, {detail::GTypeTraits<Args>::op_kind...}
, {detail::GObtainCtor<R>::get()}});
, {detail::GObtainCtor<R>::get()}
, {detail::GTypeTraits<R>::op_kind}});
call.pass(args...);
return detail::Yield<R>::yield(call, 0);
}
@@ -101,8 +101,10 @@ public:
if (it == m_priv->blobs.end()) {
// FIXME: Avoid modifying GKernel
auto shape = cv::detail::GTypeTraits<OutT>::shape;
auto kind = cv::detail::GTypeTraits<OutT>::op_kind;
m_priv->call->kernel().outShapes.push_back(shape);
m_priv->call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<OutT>::get());
m_priv->call->kernel().outKinds.emplace_back(kind);
auto out_idx = static_cast<int>(m_priv->blobs.size());
it = m_priv->blobs.emplace(name,
cv::detail::Yield<OutT>::yield(*(m_priv->call), out_idx)).first;
@@ -175,6 +177,7 @@ std::shared_ptr<cv::GCall> makeCall(const std::string &tag,
{}, // outShape will be filled later
std::move(kinds),
{}, // outCtors will be filled later
{}, // outKinds will be filled later
});
call->setArgs(std::move(args));
@@ -33,6 +33,15 @@ public:
GAPI_WRAP
PyParams& cfgNormalize(const std::string &layer_name, bool flag);
GAPI_WRAP
PyParams& cfgAddExecutionProvider(ep::OpenVINO ep);
GAPI_WRAP
PyParams& cfgAddExecutionProvider(ep::DirectML ep);
GAPI_WRAP
PyParams& cfgDisableMemPattern();
GBackend backend() const;
std::string tag() const;
cv::util::any params() const;
@@ -27,6 +27,126 @@ namespace gapi {
*/
namespace onnx {
/**
* @brief This namespace contains Execution Providers structures for G-API ONNX Runtime backend.
*/
namespace ep {
/**
* @brief This structure provides functions
* that fill inference options for ONNX OpenVINO Execution Provider.
* Please follow https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html#summary-of-options
*/
struct GAPI_EXPORTS_W_SIMPLE OpenVINO {
// NB: Used from python.
/// @private -- Exclude this constructor from OpenCV documentation
GAPI_WRAP
OpenVINO() = default;
/** @brief Class constructor.
Constructs OpenVINO parameters based on device type information.
@param dev_type Target device type to use. ("CPU_FP32", "GPU_FP16", etc)
*/
GAPI_WRAP
explicit OpenVINO(const std::string &dev_type)
: device_type(dev_type) {
}
/** @brief Specifies OpenVINO Execution Provider cache dir.
This function is used to explicitly specify the path to save and load
the blobs enabling model caching feature.
@param dir Path to the directory what will be used as cache.
@return reference to this parameter structure.
*/
GAPI_WRAP
OpenVINO& cfgCacheDir(const std::string &dir) {
cache_dir = dir;
return *this;
}
/** @brief Specifies OpenVINO Execution Provider number of threads.
This function is used to override the accelerator default value
of number of threads with this value at runtime.
@param nthreads Number of threads.
@return reference to this parameter structure.
*/
GAPI_WRAP
OpenVINO& cfgNumThreads(size_t nthreads) {
num_of_threads = nthreads;
return *this;
}
/** @brief Enables OpenVINO Execution Provider opencl throttling.
This function is used to enable OpenCL queue throttling for GPU devices
(reduces CPU utilization when using GPU).
@return reference to this parameter structure.
*/
GAPI_WRAP
OpenVINO& cfgEnableOpenCLThrottling() {
enable_opencl_throttling = true;
return *this;
}
/** @brief Enables OpenVINO Execution Provider dynamic shapes.
This function is used to enable OpenCL queue throttling for GPU devices
(reduces CPU utilization when using GPU).
This function is used to enable work with dynamic shaped models
whose shape will be set dynamically based on the infer input
image/data shape at run time in CPU.
@return reference to this parameter structure.
*/
GAPI_WRAP
OpenVINO& cfgEnableDynamicShapes() {
enable_dynamic_shapes = true;
return *this;
}
std::string device_type;
std::string cache_dir;
size_t num_of_threads = 0;
bool enable_opencl_throttling = false;
bool enable_dynamic_shapes = false;
};
/**
* @brief This structure provides functions
* that fill inference options for ONNX DirectML Execution Provider.
* Please follow https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html#directml-execution-provider
*/
class GAPI_EXPORTS_W_SIMPLE DirectML {
public:
// NB: Used from python.
/// @private -- Exclude this constructor from OpenCV documentation
GAPI_WRAP
DirectML() = default;
/** @brief Class constructor.
Constructs DirectML parameters based on device id.
@param device_id Target device id to use. ("0", "1", etc)
*/
GAPI_WRAP
explicit DirectML(const int device_id) : ddesc(device_id) { };
using DeviceDesc = cv::util::variant<int>;
DeviceDesc ddesc;
};
using EP = cv::util::variant<cv::util::monostate, OpenVINO, DirectML>;
} // namespace ep
GAPI_EXPORTS cv::gapi::GBackend backend();
enum class TraitAs: int {
@@ -78,6 +198,9 @@ struct ParamDesc {
// when the generic infer parameters are unpacked (see GONNXBackendImpl::unpackKernel)
std::unordered_map<std::string, std::pair<cv::Scalar, cv::Scalar> > generic_mstd;
std::unordered_map<std::string, bool> generic_norm;
std::vector<cv::gapi::onnx::ep::EP> execution_providers;
bool disable_mem_pattern;
};
} // namespace detail
@@ -115,6 +238,7 @@ public:
desc.num_in = std::tuple_size<typename Net::InArgs>::value;
desc.num_out = std::tuple_size<typename Net::OutArgs>::value;
desc.is_generic = false;
desc.disable_mem_pattern = false;
};
/** @brief Specifies sequence of network input layers names for inference.
@@ -279,6 +403,43 @@ public:
return *this;
}
/** @brief Adds execution provider for runtime.
The function is used to add ONNX Runtime OpenVINO Execution Provider options.
@param ep OpenVINO Execution Provider options.
@see cv::gapi::onnx::ep::OpenVINO.
@return the reference on modified object.
*/
Params<Net>& cfgAddExecutionProvider(ep::OpenVINO&& ep) {
desc.execution_providers.emplace_back(std::move(ep));
return *this;
}
/** @brief Adds execution provider for runtime.
The function is used to add ONNX Runtime DirectML Execution Provider options.
@param ep DirectML Execution Provider options.
@see cv::gapi::onnx::ep::DirectML.
@return the reference on modified object.
*/
Params<Net>& cfgAddExecutionProvider(ep::DirectML&& ep) {
desc.execution_providers.emplace_back(std::move(ep));
return *this;
}
/** @brief Disables the memory pattern optimization.
@return the reference on modified object.
*/
Params<Net>& cfgDisableMemPattern() {
desc.disable_mem_pattern = true;
return *this;
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::onnx::backend(); }
std::string tag() const { return Net::tag(); }
@@ -306,18 +467,35 @@ public:
@param model_path path to model file (.onnx file).
*/
Params(const std::string& tag, const std::string& model_path)
: desc{model_path, 0u, 0u, {}, {}, {}, {}, {}, {}, {}, {}, {}, true, {}, {} }, m_tag(tag) {}
: desc{model_path, 0u, 0u, {}, {}, {}, {}, {}, {}, {}, {}, {}, true, {}, {}, {}, false }, m_tag(tag) {}
/** @see onnx::Params::cfgMeanStdDev. */
void cfgMeanStdDev(const std::string &layer,
const cv::Scalar &m,
const cv::Scalar &s) {
desc.generic_mstd[layer] = std::make_pair(m, s);
}
/** @see onnx::Params::cfgNormalize. */
void cfgNormalize(const std::string &layer, bool flag) {
desc.generic_norm[layer] = flag;
}
/** @see onnx::Params::cfgAddExecutionProvider. */
void cfgAddExecutionProvider(ep::OpenVINO&& ep) {
desc.execution_providers.emplace_back(std::move(ep));
}
/** @see onnx::Params::cfgAddExecutionProvider. */
void cfgAddExecutionProvider(ep::DirectML&& ep) {
desc.execution_providers.emplace_back(std::move(ep));
}
/** @see onnx::Params::cfgDisableMemPattern. */
void cfgDisableMemPattern() {
desc.disable_mem_pattern = true;
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::onnx::backend(); }
std::string tag() const { return m_tag; }
@@ -46,6 +46,7 @@ G desync(const G &g) {
, {cv::detail::GTypeTraits<G>::shape} // output Shape
, {cv::detail::GTypeTraits<G>::op_kind} // input data kinds
, {cv::detail::GObtainCtor<G>::get()} // output template ctors
, {cv::detail::GTypeTraits<G>::op_kind} // output data kinds
};
cv::GCall call(std::move(k));
call.pass(g);
@@ -50,6 +50,7 @@ cv::GOpaque<T> meta(G g, const std::string &tag) {
, {cv::detail::GTypeTraits<O>::shape} // output Shape
, {cv::detail::GTypeTraits<G>::op_kind} // input data kinds
, {cv::detail::GObtainCtor<O>::get()} // output template ctors
, {cv::detail::GTypeTraits<O>::op_kind} // output data kind
};
cv::GCall call(std::move(k));
call.pass(g);
@@ -509,6 +509,11 @@ namespace util
return v.index() == util::variant<Types...>::template index_of<T>();
}
#if defined(__GNUC__) && (__GNUC__ == 11 || __GNUC__ == 12)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
template<typename... Us> bool operator==(const variant<Us...> &lhs,
const variant<Us...> &rhs)
{
@@ -524,6 +529,10 @@ namespace util
return (eqs[lhs.index()])(lhs.memory, rhs.memory);
}
#if defined(__GNUC__) && (__GNUC__ == 11 || __GNUC__ == 12)
#pragma GCC diagnostic pop
#endif
template<typename... Us> bool operator!=(const variant<Us...> &lhs,
const variant<Us...> &rhs)
{
@@ -29,6 +29,8 @@ using map_string_and_string = std::map<std::string, std::string>;
using map_string_and_vector_size_t = std::map<std::string, std::vector<size_t>>;
using map_string_and_vector_float = std::map<std::string, std::vector<float>>;
using map_int_and_double = std::map<int, double>;
using ep_OpenVINO = cv::gapi::onnx::ep::OpenVINO;
using ep_DirectML = cv::gapi::onnx::ep::DirectML;
// NB: Python wrapper generate T_U for T<U>
// This behavior is only observed for inputs
+18 -9
View File
@@ -267,13 +267,14 @@ cv::gapi::wip::GOutputs::Priv::Priv(const std::string& id, cv::GKernel::M outMet
std::transform(args.begin(), args.end(), std::back_inserter(kinds),
[](const cv::GArg& arg) { return arg.opaque_kind; });
m_call.reset(new cv::GCall{cv::GKernel{id, {}, outMeta, {}, std::move(kinds), {}}});
m_call.reset(new cv::GCall{cv::GKernel{id, {}, outMeta, {}, std::move(kinds), {}, {}}});
m_call->setArgs(std::move(args));
}
cv::GMat cv::gapi::wip::GOutputs::Priv::getGMat()
{
m_call->kernel().outShapes.push_back(cv::GShape::GMAT);
m_call->kernel().outKinds.push_back(cv::detail::OpaqueKind::CV_UNKNOWN);
// ...so _empty_ constructor is passed here.
m_call->kernel().outCtors.emplace_back(cv::util::monostate{});
return m_call->yield(output++);
@@ -282,6 +283,7 @@ cv::GMat cv::gapi::wip::GOutputs::Priv::getGMat()
cv::GScalar cv::gapi::wip::GOutputs::Priv::getGScalar()
{
m_call->kernel().outShapes.push_back(cv::GShape::GSCALAR);
m_call->kernel().outKinds.push_back(cv::detail::OpaqueKind::CV_UNKNOWN);
// ...so _empty_ constructor is passed here.
m_call->kernel().outCtors.emplace_back(cv::util::monostate{});
return m_call->yieldScalar(output++);
@@ -290,10 +292,14 @@ cv::GScalar cv::gapi::wip::GOutputs::Priv::getGScalar()
cv::GArrayT cv::gapi::wip::GOutputs::Priv::getGArray(cv::gapi::ArgType type)
{
m_call->kernel().outShapes.push_back(cv::GShape::GARRAY);
#define HC(T, K) \
case K: \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GArray<T>>::get()); \
return cv::GArrayT(m_call->yieldArray<T>(output++)); \
#define HC(T, K) \
case K: { \
const auto kind = cv::detail::GTypeTraits<cv::GArray<T>>::op_kind; \
m_call->kernel().outKinds.emplace_back(kind); \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GArray<T>>::get()); \
return cv::GArrayT(m_call->yieldArray<T>(output++)); \
}
SWITCH(type, GARRAY_TYPE_LIST_G, HC)
#undef HC
@@ -302,10 +308,13 @@ cv::GArrayT cv::gapi::wip::GOutputs::Priv::getGArray(cv::gapi::ArgType type)
cv::GOpaqueT cv::gapi::wip::GOutputs::Priv::getGOpaque(cv::gapi::ArgType type)
{
m_call->kernel().outShapes.push_back(cv::GShape::GOPAQUE);
#define HC(T, K) \
case K: \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GOpaque<T>>::get()); \
return cv::GOpaqueT(m_call->yieldOpaque<T>(output++)); \
#define HC(T, K) \
case K: { \
const auto kind = cv::detail::GTypeTraits<cv::GOpaque<T>>::op_kind; \
m_call->kernel().outKinds.emplace_back(kind); \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GOpaque<T>>::get()); \
return cv::GOpaqueT(m_call->yieldOpaque<T>(output++)); \
}
SWITCH(type, GOPAQUE_TYPE_LIST_G, HC)
#undef HC
@@ -207,7 +207,48 @@ try:
return Op
# NB: Just mock operation to test different kinds for output G-types.
@cv.gapi.op('custom.square_mean', in_types=[cv.GArray.Int], out_types=[cv.GOpaque.Float, cv.GArray.Int])
class GSquareMean:
@staticmethod
def outMeta(desc):
return cv.empty_gopaque_desc(), cv.empty_array_desc()
@cv.gapi.kernel(GSquareMean)
class GSquareMeanImpl:
@staticmethod
def run(arr):
squares = [val**2 for val in arr]
return sum(arr) / len(arr), squares
@cv.gapi.op('custom.squares', in_types=[cv.GArray.Int], out_types=[cv.GArray.Int])
class GSquare:
@staticmethod
def outMeta(desc):
return cv.empty_array_desc()
@cv.gapi.kernel(GSquare)
class GSquareImpl:
@staticmethod
def run(arr):
squares = [val**2 for val in arr]
return squares
class gapi_sample_pipelines(NewOpenCVTests):
def test_different_output_opaque_kinds(self):
g_in = cv.GArray.Int()
g_mean, g_squares = GSquareMean.on(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_mean, g_squares))
pkg = cv.gapi.kernels(GSquareMeanImpl)
mean, squares = comp.apply(cv.gin([1,2,3]), args=cv.gapi.compile_args(pkg))
self.assertEqual([1,4,9], list(squares))
self.assertEqual(2.0, mean)
def test_custom_op_add(self):
sz = (3, 3)
+31 -2
View File
@@ -949,7 +949,11 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
auto y_blob = ctx.uu.rctx->CreateBlob(blob_params->first.first, blob_params->first.second);
auto uv_blob = ctx.uu.rctx->CreateBlob(blob_params->second.first, blob_params->second.second);
#if INF_ENGINE_RELEASE >= 2021010000
#if INF_ENGINE_RELEASE > 2023000000
cv::util::throw_error(std::logic_error(
"IE Backend: NV12 feature has been deprecated in OpenVINO 1.0 API."
" The last version which supports this is 2023.0"));
#elif INF_ENGINE_RELEASE >= 2021010000
return IE::make_shared_blob<IE::NV12Blob>(y_blob, uv_blob);
#else
return IE::make_shared_blob<InferenceEngine::NV12Blob>(y_blob, uv_blob);
@@ -982,7 +986,14 @@ static void setBlob(InferenceEngine::InferRequest& req,
req.SetBlob(layer_name, blob);
} else {
GAPI_Assert(ctx.uu.params.kind == ParamDesc::Kind::Import);
#if INF_ENGINE_RELEASE > 2023000000
// NB: SetBlob overload which accepts IE::PreProcessInfo
// has been deprecated - preprocessing can't be configured
// for "Import" networks anymore.
req.SetBlob(layer_name, blob);
#else
req.SetBlob(layer_name, blob, ctx.uu.preproc_map.at(layer_name));
#endif
}
}
@@ -1370,7 +1381,14 @@ static void cfgImagePreprocessing(const IE::InputInfo::Ptr &ii,
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
const auto &meta = util::get<cv::GFrameDesc>(mm);
if (meta.fmt == cv::MediaFormat::NV12) {
#if INF_ENGINE_RELEASE > 2023000000
cv::util::throw_error(std::logic_error(
"IE Backend: cv::MediaFrame with NV12 format is no longer supported"
" because NV12 feature has been deprecated in OpenVINO 1.0 API."
" The last version which supports this is 2023.0"));
#else
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
#endif
}
}
}
@@ -1426,7 +1444,14 @@ static IE::PreProcessInfo createImagePreProcInfo(const cv::GMetaArg &mm,
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
const auto &meta = util::get<cv::GFrameDesc>(mm);
if (meta.fmt == cv::MediaFormat::NV12) {
#if INF_ENGINE_RELEASE > 2023000000
cv::util::throw_error(std::logic_error(
"IE Backend: cv::MediaFrame with NV12 format is no longer supported"
" because NV12 feature has been deprecated in OpenVINO 1.0 API."
" The last version which supports this is 2023.0"));
#else
info.setColorFormat(IE::ColorFormat::NV12);
#endif
}
}
return info;
@@ -2299,7 +2324,11 @@ IE::Blob::Ptr cv::gapi::ie::util::to_ie(const cv::Mat &blob) {
IE::Blob::Ptr cv::gapi::ie::util::to_ie(const cv::Mat &y_plane, const cv::Mat &uv_plane) {
auto y_blob = wrapIE(y_plane, cv::gapi::ie::TraitAs::IMAGE);
auto uv_blob = wrapIE(uv_plane, cv::gapi::ie::TraitAs::IMAGE);
#if INF_ENGINE_RELEASE >= 2021010000
#if INF_ENGINE_RELEASE > 2023000000
cv::util::throw_error(std::logic_error(
"IE Backend: NV12 feature has been deprecated in OpenVINO 1.0 API."
" The last version which supports this is 2023.0"));
#elif INF_ENGINE_RELEASE >= 2021010000
return IE::make_shared_blob<IE::NV12Blob>(y_blob, uv_blob);
#else
return IE::make_shared_blob<InferenceEngine::NV12Blob>(y_blob, uv_blob);
@@ -21,6 +21,24 @@ cv::gapi::onnx::PyParams& cv::gapi::onnx::PyParams::cfgNormalize(const std::stri
return *this;
}
cv::gapi::onnx::PyParams&
cv::gapi::onnx::PyParams::cfgAddExecutionProvider(cv::gapi::onnx::ep::OpenVINO ep) {
m_priv->cfgAddExecutionProvider(std::move(ep));
return *this;
}
cv::gapi::onnx::PyParams&
cv::gapi::onnx::PyParams::cfgAddExecutionProvider(cv::gapi::onnx::ep::DirectML ep) {
m_priv->cfgAddExecutionProvider(std::move(ep));
return *this;
}
cv::gapi::onnx::PyParams&
cv::gapi::onnx::PyParams::cfgDisableMemPattern() {
m_priv->cfgDisableMemPattern();
return *this;
}
cv::gapi::GBackend cv::gapi::onnx::PyParams::backend() const {
return m_priv->backend();
}
+40
View File
@@ -0,0 +1,40 @@
// 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) 2023 Intel Corporation
#include "backends/onnx/dml_ep.hpp"
#include "logger.hpp"
#ifdef HAVE_ONNX
#include <onnxruntime_cxx_api.h>
#ifdef HAVE_ONNX_DML
#include "../providers/dml/dml_provider_factory.h"
void cv::gimpl::onnx::addDMLExecutionProvider(Ort::SessionOptions *session_options,
const cv::gapi::onnx::ep::DirectML &dml_ep) {
namespace ep = cv::gapi::onnx::ep;
GAPI_Assert(cv::util::holds_alternative<int>(dml_ep.ddesc));
const int device_id = cv::util::get<int>(dml_ep.ddesc);
try {
OrtSessionOptionsAppendExecutionProvider_DML(*session_options, device_id);
} catch (const std::exception &e) {
std::stringstream ss;
ss << "ONNX Backend: Failed to enable DirectML"
<< " Execution Provider: " << e.what();
cv::util::throw_error(std::runtime_error(ss.str()));
}
}
#else // HAVE_ONNX_DML
void cv::gimpl::onnx::addDMLExecutionProvider(Ort::SessionOptions*,
const cv::gapi::onnx::ep::DirectML&) {
util::throw_error(std::runtime_error("G-API has been compiled with ONNXRT"
" without DirectML support"));
}
#endif // HAVE_ONNX_DML
#endif // HAVE_ONNX
+23
View File
@@ -0,0 +1,23 @@
// 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) 2023 Intel Corporation
#ifndef OPENCV_GAPI_DML_EP_HPP
#define OPENCV_GAPI_DML_EP_HPP
#include "opencv2/gapi/infer/onnx.hpp"
#ifdef HAVE_ONNX
#include <onnxruntime_cxx_api.h>
namespace cv {
namespace gimpl {
namespace onnx {
void addDMLExecutionProvider(Ort::SessionOptions *session_options,
const cv::gapi::onnx::ep::DirectML &dml_ep);
}}}
#endif // HAVE_ONNX
#endif // OPENCV_GAPI_DML_EP_HPP
@@ -9,6 +9,8 @@
#ifdef HAVE_ONNX
#include "backends/onnx/dml_ep.hpp"
#include <ade/util/algorithm.hpp> // any_of
#include <ade/util/zip_range.hpp>
#include <opencv2/gapi/infer.hpp>
@@ -143,6 +145,48 @@ public:
void run();
};
static void addOpenVINOExecutionProvider(Ort::SessionOptions *session_options,
const cv::gapi::onnx::ep::OpenVINO &ov_ep) {
OrtOpenVINOProviderOptions options;
options.device_type = ov_ep.device_type.c_str();
options.cache_dir = ov_ep.cache_dir.c_str();
options.num_of_threads = ov_ep.num_of_threads;
options.enable_opencl_throttling = ov_ep.enable_opencl_throttling;
options.enable_dynamic_shapes = ov_ep.enable_dynamic_shapes;
options.context = nullptr;
try {
session_options->AppendExecutionProvider_OpenVINO(options);
} catch (const std::exception &e) {
std::stringstream ss;
ss << "ONNX Backend: Failed to enable OpenVINO"
<< " Execution Provider: " << e.what();
cv::util::throw_error(std::runtime_error(ss.str()));
}
}
static void addExecutionProvider(Ort::SessionOptions *session_options,
const cv::gapi::onnx::ep::EP &execution_provider) {
namespace ep = cv::gapi::onnx::ep;
switch (execution_provider.index()) {
case ep::EP::index_of<ep::OpenVINO>(): {
GAPI_LOG_INFO(NULL, "OpenVINO Execution Provider is added.");
const auto &ov_ep = cv::util::get<ep::OpenVINO>(execution_provider);
addOpenVINOExecutionProvider(session_options, ov_ep);
break;
}
case ep::EP::index_of<ep::DirectML>(): {
GAPI_LOG_INFO(NULL, "DirectML Execution Provider is added.");
const auto &dml_ep = cv::util::get<ep::DirectML>(execution_provider);
addDMLExecutionProvider(session_options, dml_ep);
break;
}
default:
GAPI_LOG_INFO(NULL, "CPU Execution Provider is added.");
break;
}
}
} // namespace onnx
} // namespace gimpl
} // namespace cv
@@ -592,9 +636,16 @@ ONNXCompiled::ONNXCompiled(const gapi::onnx::detail::ParamDesc &pp)
cv::util::throw_error(std::logic_error("Please specify output layer names for "
+ params.model_path));
}
// Create and initialize the ONNX session
Ort::SessionOptions session_options;
GAPI_LOG_INFO(NULL, "Adding Execution Providers for \"" << pp.model_path << "\"");
for (const auto &ep : pp.execution_providers) {
cv::gimpl::onnx::addExecutionProvider(&session_options, ep);
}
if (pp.disable_mem_pattern) {
session_options.DisableMemPattern();
}
this_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "");
#ifndef _WIN32
this_session = Ort::Session(this_env, params.model_path.data(), session_options);
+661 -159
View File
@@ -18,6 +18,7 @@
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/infer/ov.hpp>
#include <opencv2/core/utils/configuration.private.hpp> // getConfigurationParameterBool
#if defined(HAVE_TBB)
# include <tbb/concurrent_queue.h> // FIXME: drop it from here!
@@ -37,11 +38,37 @@ template<typename T> using QueueClass = cv::gapi::own::concurrent_bounded_queue<
using ParamDesc = cv::gapi::ov::detail::ParamDesc;
static ov::Core getCore() {
// NB: Some of OV plugins fail during ov::Core destroying in specific cases.
// Solution is allocate ov::Core in heap and doesn't destroy it, which cause
// leak, but fixes tests on CI. This behaviour is configurable by using
// OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND=0
static ov::Core create_OV_Core_pointer() {
// NB: 'delete' is never called
static ov::Core* core = new ov::Core();
return *core;
}
static ov::Core create_OV_Core_instance() {
static ov::Core core;
return core;
}
ov::Core cv::gapi::ov::wrap::getCore() {
// NB: to make happy memory leak tools use:
// - OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND=0
static bool param_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND =
utils::getConfigurationParameterBool(
"OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND",
#if defined(_WIN32) || defined(__APPLE__)
true
#else
false
#endif
);
return param_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND
? create_OV_Core_pointer() : create_OV_Core_instance();
}
static ov::AnyMap toOV(const ParamDesc::PluginConfigT &config) {
return {config.begin(), config.end()};
}
@@ -101,8 +128,8 @@ static int toCV(const ov::element::Type &type) {
static void copyFromOV(const ov::Tensor &tensor, cv::Mat &mat) {
const auto total = mat.total() * mat.channels();
if (tensor.get_element_type() != toOV(mat.depth()) ||
tensor.get_size() != total ) {
if (toCV(tensor.get_element_type()) != mat.depth() ||
tensor.get_size() != total ) {
std::stringstream ss;
ss << "Failed to copy data from ov::Tensor to cv::Mat."
<< " Data type or number of elements mismatch."
@@ -128,8 +155,8 @@ static void copyToOV(const cv::Mat &mat, ov::Tensor &tensor) {
// TODO: Ideally there should be check that mat and tensor
// dimensions are compatible.
const auto total = mat.total() * mat.channels();
if (tensor.get_element_type() != toOV(mat.depth()) ||
tensor.get_size() != total) {
if (toCV(tensor.get_element_type()) != mat.depth() ||
tensor.get_size() != total) {
std::stringstream ss;
ss << "Failed to copy data from cv::Mat to ov::Tensor."
<< " Data type or number of elements mismatch."
@@ -158,6 +185,14 @@ int cv::gapi::ov::util::to_ocv(const ::ov::element::Type &type) {
return toCV(type);
}
void cv::gapi::ov::util::to_ov(const cv::Mat &mat, ::ov::Tensor &tensor) {
copyToOV(mat, tensor);
}
void cv::gapi::ov::util::to_ocv(const ::ov::Tensor &tensor, cv::Mat &mat) {
copyFromOV(tensor, mat);
}
struct OVUnit {
static const char *name() { return "OVUnit"; }
@@ -167,7 +202,8 @@ struct OVUnit {
// FIXME: Can this logic be encapsulated to prevent checking every time?
if (cv::util::holds_alternative<ParamDesc::Model>(params.kind)) {
const auto desc = cv::util::get<ParamDesc::Model>(params.kind);
model = getCore().read_model(desc.model_path, desc.bin_path);
model = cv::gapi::ov::wrap::getCore()
.read_model(desc.model_path, desc.bin_path);
GAPI_Assert(model);
if (params.num_in == 1u && params.input_names.empty()) {
@@ -182,9 +218,8 @@ struct OVUnit {
std::ifstream file(cv::util::get<ParamDesc::CompiledModel>(params.kind).blob_path,
std::ios_base::in | std::ios_base::binary);
GAPI_Assert(file.is_open());
compiled_model = getCore().import_model(file,
params.device,
toOV(params.config));
compiled_model = cv::gapi::ov::wrap::getCore()
.import_model(file, params.device, toOV(params.config));
if (params.num_in == 1u && params.input_names.empty()) {
params.input_names = { compiled_model.inputs().begin()->get_any_name() };
@@ -197,9 +232,8 @@ struct OVUnit {
cv::gimpl::ov::OVCompiled compile() {
if (cv::util::holds_alternative<ParamDesc::Model>(params.kind)) {
compiled_model = getCore().compile_model(model,
params.device,
toOV(params.config));
compiled_model = cv::gapi::ov::wrap::getCore()
.compile_model(model, params.device, toOV(params.config));
}
return {compiled_model};
}
@@ -343,6 +377,15 @@ cv::GArg OVCallContext::packArg(const cv::GArg &arg) {
switch (ref.shape)
{
case cv::GShape::GMAT: return cv::GArg(m_res.slot<cv::Mat>()[ref.id]);
// Note: .at() is intentional for GArray as object MUST be already there
// (and constructed by either bindIn/Out or resetInternal)
case cv::GShape::GARRAY: return cv::GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
// Note: .at() is intentional for GOpaque as object MUST be already there
// (and constructed by either bindIn/Out or resetInternal)
case cv::GShape::GOPAQUE: return cv::GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id));
default:
cv::util::throw_error(std::logic_error("Unsupported GShape type"));
break;
@@ -547,6 +590,62 @@ static void PostOutputs(::ov::InferRequest &infer_request,
}
}
class PostOutputsList {
public:
PostOutputsList(size_t size,
std::shared_ptr<OVCallContext> ctx);
void operator()(::ov::InferRequest &infer_request,
std::exception_ptr eptr,
size_t pos) const;
private:
struct Priv {
std::atomic<size_t> finished{0u};
size_t size;
std::shared_ptr<OVCallContext> ctx;
};
std::shared_ptr<Priv> m_priv;
};
PostOutputsList::PostOutputsList(size_t size,
std::shared_ptr<OVCallContext> ctx)
: m_priv(new Priv{}) {
m_priv->size = size;
m_priv->ctx = ctx;
}
void PostOutputsList::operator()(::ov::InferRequest &infer_request,
std::exception_ptr eptr,
size_t pos) const {
auto&& ctx = m_priv->ctx;
auto&& finished = m_priv->finished;
auto&& size = m_priv->size;
ctx->eptr = eptr;
if (!ctx->eptr) {
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
std::vector<cv::Mat> &out_vec = ctx->outVecR<cv::Mat>(i);
const auto &out_name = ctx->uu.params.output_names[i];
const auto &out_tensor = infer_request.get_tensor(out_name);
out_vec[pos].create(toCV(out_tensor.get_shape()),
toCV(out_tensor.get_element_type()));
copyFromOV(out_tensor, out_vec[pos]);
}
}
++finished;
if (finished == size) {
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
auto output = ctx->output(i);
ctx->out.meta(output, ctx->getMeta());
ctx->out.post(std::move(output), ctx->eptr);
}
}
}
namespace cv {
namespace gimpl {
namespace ov {
@@ -594,6 +693,34 @@ cv::optional<V> lookUp(const std::map<K, V> &map, const K& key) {
return cv::util::make_optional(std::move(it->second));
}
// NB: This function is used to preprocess input image
// for InferROI, InferList, InferList2 kernels.
static cv::Mat preprocess(const cv::Mat &in_mat,
const cv::Rect &roi,
const ::ov::Shape &model_shape) {
cv::Mat out;
// FIXME: Since there is no information about H and W positions
// among tensor dimmensions assume that model layout is "NHWC".
// (In fact "NHWC" is the only right layout for preprocessing because
// it works only with images.
GAPI_Assert(model_shape.size() == 4u);
const auto H = model_shape[1];
const auto W = model_shape[2];
const auto C = model_shape[3];
// NB: Soft check that at least number of channels matches.
if (static_cast<int>(C) != in_mat.channels()) {
std::stringstream ss;
ss << "OV Backend: Failed to preprocess input data "
" (Number of channels mismatch)."
" Provided data: " << cv::descr_of(in_mat) <<
" and Model shape: " << model_shape;
util::throw_error(std::logic_error(ss.str()));
}
// NB: Crop roi and resize to model size.
cv::resize(in_mat(roi), out, cv::Size(W, H));
return out;
}
static bool isImage(const cv::GMatDesc &desc,
const ::ov::Shape &model_shape) {
return (model_shape.size() == 4u) &&
@@ -603,6 +730,203 @@ static bool isImage(const cv::GMatDesc &desc,
(desc.depth == CV_8U);
}
class PrePostProcWrapper {
public:
PrePostProcWrapper(std::shared_ptr<::ov::Model> &model,
const ParamDesc::Model &model_info,
const std::vector<std::string> &input_names,
const std::vector<std::string> &output_names)
: m_ppp(model),
m_model(model),
m_model_info(model_info),
m_input_names(input_names),
m_output_names(output_names) {
// NB: Do Reshape right away since it must be the first step of model modification
// and applicable for all infer kernels.
const auto new_shapes = broadcastLayerAttr(model_info.new_shapes, input_names);
m_model->reshape(toOV(new_shapes));
const auto &mi = m_model_info;
m_input_tensor_layout = broadcastLayerAttr(mi.input_tensor_layout, m_input_names);
m_input_model_layout = broadcastLayerAttr(mi.input_model_layout, m_input_names);
m_interpolation = broadcastLayerAttr(mi.interpolation, m_input_names);
m_mean_values = broadcastLayerAttr(mi.mean_values, m_input_names);
m_scale_values = broadcastLayerAttr(mi.scale_values, m_input_names);
m_interpolation = broadcastLayerAttr(mi.interpolation, m_input_names);
m_output_tensor_layout = broadcastLayerAttr(mi.output_tensor_layout, m_output_names);
m_output_model_layout = broadcastLayerAttr(mi.output_model_layout, m_output_names);
m_output_tensor_precision = broadcastLayerAttr(mi.output_tensor_precision, m_output_names);
};
void cfgLayouts(const std::string &input_name) {
auto &input_info = m_ppp.input(input_name);
const auto explicit_in_model_layout = lookUp(m_input_model_layout, input_name);
if (explicit_in_model_layout) {
input_info.model().set_layout(::ov::Layout(*explicit_in_model_layout));
} else if (m_model->input(input_name).get_shape().size() == 4u) {
// NB: Back compatibility with IR's without any layout information.
// Note that default is only applicable for 4D inputs in order to
// support auto resize for image use cases.
GAPI_LOG_WARNING(NULL, "Failed to find layout for input layer \""
<< input_name << "\" - NCHW is set by default");
const std::string default_layout = "NCHW";
input_info.model().set_layout(::ov::Layout(default_layout));
m_input_model_layout.emplace(input_name, default_layout);
}
const auto explicit_in_tensor_layout = lookUp(m_input_tensor_layout, input_name);
if (explicit_in_tensor_layout) {
input_info.tensor().set_layout(::ov::Layout(*explicit_in_tensor_layout));
}
}
void cfgScaleMean(const std::string &input_name) {
auto &input_info = m_ppp.input(input_name);
const auto mean_vec = lookUp(m_mean_values, input_name);
if (mean_vec) {
input_info.preprocess().mean(*mean_vec);
}
const auto scale_vec = lookUp(m_scale_values, input_name);
if (scale_vec) {
input_info.preprocess().scale(*scale_vec);
}
}
// FIXME: Decompose this...
void cfgPreProcessing(const std::string &input_name,
const cv::GMetaArg &input_meta,
const bool disable_img_resize = false) {
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(input_meta));
const auto &matdesc = cv::util::get<cv::GMatDesc>(input_meta);
const auto explicit_in_tensor_layout = lookUp(m_input_tensor_layout, input_name);
const auto explicit_in_model_layout = lookUp(m_input_model_layout, input_name);
const auto explicit_resize = lookUp(m_interpolation, input_name);
if (disable_img_resize && explicit_resize.has_value()) {
std::stringstream ss;
util::throw_error(std::logic_error(
"OV Backend: Resize for layer \"" + input_name + "\" will be performed"
" on host via OpenCV so explicitly configured resize is prohibited."));
}
const auto &input_shape = m_model->input(input_name).get_shape();
auto &input_info = m_ppp.input(input_name);
m_ppp.input(input_name).tensor().set_element_type(toOV(matdesc.depth));
if (isImage(matdesc, input_shape)) {
// NB: Image case - all necessary preprocessng is configured automatically.
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is image.");
if (explicit_in_tensor_layout &&
*explicit_in_tensor_layout != "NHWC") {
std::stringstream ss;
ss << "OV Backend: Provided tensor layout " << *explicit_in_tensor_layout
<< " is not compatible with input data " << matdesc << " for layer \""
<< input_name << "\". Expecting NHWC";
util::throw_error(std::logic_error(ss.str()));
} else {
input_info.tensor().set_layout(::ov::Layout("NHWC"));
}
if (!disable_img_resize) {
input_info.tensor().set_spatial_static_shape(matdesc.size.height,
matdesc.size.width);
// NB: Even though resize is automatically configured
// user have an opportunity to specify the interpolation algorithm.
auto interp = explicit_resize
? toOVInterp(*explicit_resize)
: ::ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR;
input_info.preprocess().resize(interp);
}
} else {
// NB: Tensor case - resize or layout conversions must be explicitly specified.
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is tensor.");
if (explicit_resize) {
if (matdesc.isND()) {
// NB: ND case - need to obtain "H" and "W" positions
// in order to configure resize.
const auto model_layout = explicit_in_model_layout
? ::ov::Layout(*explicit_in_model_layout)
: ::ov::layout::get_layout(m_model->input(input_name));
if (!explicit_in_tensor_layout && model_layout.empty()) {
std::stringstream ss;
ss << "Resize for input layer: " << input_name
<< "can't be configured."
<< " Failed to extract H and W positions from layout.";
util::throw_error(std::logic_error(ss.str()));
} else {
const auto layout = explicit_in_tensor_layout
? ::ov::Layout(*explicit_in_tensor_layout) : model_layout;
auto H_idx = ::ov::layout::height_idx(layout);
auto W_idx = ::ov::layout::width_idx(layout);
// NB: If layout is "...HW", H position is -2.
if (H_idx < 0) H_idx = matdesc.dims.size() + H_idx;
if (W_idx < 0) W_idx = matdesc.dims.size() + W_idx;
GAPI_Assert(H_idx >= 0 && H_idx < static_cast<int>(matdesc.dims.size()));
GAPI_Assert(W_idx >= 0 && W_idx < static_cast<int>(matdesc.dims.size()));
input_info.tensor().set_spatial_static_shape(matdesc.dims[H_idx],
matdesc.dims[W_idx]);
input_info.preprocess().resize(toOVInterp(*explicit_resize));
}
} else {
// NB: 2D case - We know exactly where H and W...
input_info.tensor().set_spatial_static_shape(matdesc.size.height,
matdesc.size.width);
input_info.preprocess().resize(toOVInterp(*explicit_resize));
}
}
}
}
void cfgPostProcessing() {
for (const auto &output_name : m_output_names) {
const auto explicit_out_tensor_layout =
lookUp(m_output_tensor_layout, output_name);
if (explicit_out_tensor_layout) {
m_ppp.output(output_name).tensor()
.set_layout(::ov::Layout(*explicit_out_tensor_layout));
}
const auto explicit_out_model_layout =
lookUp(m_output_model_layout, output_name);
if (explicit_out_model_layout) {
m_ppp.output(output_name).model()
.set_layout(::ov::Layout(*explicit_out_model_layout));
}
const auto explicit_out_tensor_prec =
lookUp(m_output_tensor_precision, output_name);
if (explicit_out_tensor_prec) {
m_ppp.output(output_name).tensor()
.set_element_type(toOV(*explicit_out_tensor_prec));
}
}
}
void finalize() {
GAPI_LOG_DEBUG(NULL, "OV Backend: PrePostProcessor: " << m_ppp);
m_model = m_ppp.build();
}
private:
::ov::preprocess::PrePostProcessor m_ppp;
std::shared_ptr<::ov::Model> &m_model;
const ParamDesc::Model &m_model_info;
const std::vector<std::string> &m_input_names;
const std::vector<std::string> &m_output_names;
cv::gimpl::ov::AttrMap<std::string> m_input_tensor_layout;
cv::gimpl::ov::AttrMap<std::string> m_input_model_layout;
cv::gimpl::ov::AttrMap<int> m_interpolation;
cv::gimpl::ov::AttrMap<std::vector<float>> m_mean_values;
cv::gimpl::ov::AttrMap<std::vector<float>> m_scale_values;
cv::gimpl::ov::AttrMap<std::string> m_output_tensor_layout;
cv::gimpl::ov::AttrMap<std::string> m_output_model_layout;
cv::gimpl::ov::AttrMap<int> m_output_tensor_precision;
};
struct Infer: public cv::detail::KernelTag {
using API = cv::GInferBase;
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
@@ -625,156 +949,21 @@ struct Infer: public cv::detail::KernelTag {
// NB: Pre/Post processing configuration avaiable only for read models.
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
const auto new_shapes =
broadcastLayerAttr(model_info.new_shapes,
uu.params.input_names);
const_cast<std::shared_ptr<::ov::Model>&>(uu.model)->reshape(toOV(new_shapes));
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
PrePostProcWrapper ppp {model, model_info,
uu.params.input_names, uu.params.output_names};
const auto input_tensor_layout =
broadcastLayerAttr(model_info.input_tensor_layout,
uu.params.input_names);
const auto input_model_layout =
broadcastLayerAttr(model_info.input_model_layout,
uu.params.input_names);
const auto interpolation = broadcastLayerAttr(model_info.interpolation,
uu.params.input_names);
const auto mean_values = broadcastLayerAttr(model_info.mean_values,
uu.params.input_names);
const auto scale_values = broadcastLayerAttr(model_info.scale_values,
uu.params.input_names);
// FIXME: Pre/Post processing step shouldn't be configured in this method.
::ov::preprocess::PrePostProcessor ppp(uu.model);
for (auto &&it : ade::util::zip(ade::util::toRange(uu.params.input_names),
ade::util::toRange(in_metas))) {
const auto &mm = std::get<1>(it);
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(mm));
const auto &matdesc = cv::util::get<cv::GMatDesc>(mm);
const auto &input_name = std::get<0>(it);
auto &input_info = ppp.input(input_name);
input_info.tensor().set_element_type(toOV(matdesc.depth));
const auto &mm = std::get<1>(it);
const auto explicit_in_model_layout = lookUp(input_model_layout, input_name);
if (explicit_in_model_layout) {
input_info.model().set_layout(::ov::Layout(*explicit_in_model_layout));
}
const auto explicit_in_tensor_layout = lookUp(input_tensor_layout, input_name);
if (explicit_in_tensor_layout) {
input_info.tensor().set_layout(::ov::Layout(*explicit_in_tensor_layout));
}
const auto explicit_resize = lookUp(interpolation, input_name);
// NB: Note that model layout still can't be empty.
// e.g If model converted to IRv11 without any additional
// info about layout via Model Optimizer.
const auto model_layout = ::ov::layout::get_layout(uu.model->input(input_name));
const auto &input_shape = uu.model->input(input_name).get_shape();
if (isImage(matdesc, input_shape)) {
// NB: Image case - all necessary preprocessng is configured automatically.
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is image.");
// NB: Layout is already set just double check that
// user provided the correct one. In fact, there is only one correct for image.
if (explicit_in_tensor_layout &&
*explicit_in_tensor_layout != "NHWC") {
std::stringstream ss;
ss << "OV Backend: Provided tensor layout " << *explicit_in_tensor_layout
<< " is not compatible with input data " << matdesc << " for layer \""
<< input_name << "\". Expecting NHWC";
util::throw_error(std::logic_error(ss.str()));
}
input_info.tensor().set_layout(::ov::Layout("NHWC"));
input_info.tensor().set_spatial_static_shape(matdesc.size.height,
matdesc.size.width);
// NB: Even though resize is automatically configured
// user have an opportunity to specify the interpolation algorithm.
auto interp = explicit_resize
? toOVInterp(*explicit_resize)
: ::ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR;
input_info.preprocess().resize(interp);
} else {
// NB: Tensor case - resize or layout conversions must be explicitly specified.
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is tensor.");
if (explicit_resize) {
if (matdesc.isND()) {
// NB: ND case - need to obtain "H" and "W" positions
// in order to configure resize.
if (!explicit_in_tensor_layout && model_layout.empty()) {
std::stringstream ss;
ss << "Resize for input layer: " << input_name
<< "can't be configured."
<< " Failed to extract H and W positions from layout.";
util::throw_error(std::logic_error(ss.str()));
} else {
const auto layout = explicit_in_tensor_layout
? ::ov::Layout(*explicit_in_tensor_layout) : model_layout;
auto H_idx = ::ov::layout::height_idx(layout);
auto W_idx = ::ov::layout::width_idx(layout);
// NB: If layout is "...HW", H position is -2.
if (H_idx < 0) H_idx = matdesc.dims.size() + H_idx;
if (W_idx < 0) W_idx = matdesc.dims.size() + W_idx;
GAPI_Assert(H_idx >= 0 && H_idx < static_cast<int>(matdesc.dims.size()));
GAPI_Assert(W_idx >= 0 && W_idx < static_cast<int>(matdesc.dims.size()));
input_info.tensor().set_spatial_static_shape(matdesc.dims[H_idx],
matdesc.dims[W_idx]);
input_info.preprocess().resize(toOVInterp(*explicit_resize));
}
} else {
// NB: 2D case - We know exactly where H and W...
input_info.tensor().set_spatial_static_shape(matdesc.size.height,
matdesc.size.width);
input_info.preprocess().resize(toOVInterp(*explicit_resize));
}
}
}
// NB: Apply mean/scale as the last step of the preprocessing.
// Note that this can be applied to any input data if the
// position of "C" dimension is known.
const auto mean_vec = lookUp(mean_values, input_name);
if (mean_vec) {
input_info.preprocess().mean(*mean_vec);
}
const auto scale_vec = lookUp(scale_values, input_name);
if (scale_vec) {
input_info.preprocess().scale(*scale_vec);
}
ppp.cfgLayouts(input_name);
ppp.cfgPreProcessing(input_name, mm);
ppp.cfgScaleMean(input_name);
}
const auto output_tensor_layout =
broadcastLayerAttr(model_info.output_tensor_layout,
uu.params.output_names);
const auto output_model_layout =
broadcastLayerAttr(model_info.output_model_layout,
uu.params.output_names);
const auto output_tensor_precision =
broadcastLayerAttr(model_info.output_tensor_precision,
uu.params.output_names);
for (const auto &output_name : uu.params.output_names) {
const auto explicit_out_tensor_layout =
lookUp(output_tensor_layout, output_name);
if (explicit_out_tensor_layout) {
ppp.output(output_name).tensor()
.set_layout(::ov::Layout(*explicit_out_tensor_layout));
}
const auto explicit_out_model_layout =
lookUp(output_model_layout, output_name);
if (explicit_out_model_layout) {
ppp.output(output_name).model()
.set_layout(::ov::Layout(*explicit_out_model_layout));
}
const auto explicit_out_tensor_prec =
lookUp(output_tensor_precision, output_name);
if (explicit_out_tensor_prec) {
ppp.output(output_name).tensor()
.set_element_type(toOV(*explicit_out_tensor_prec));
}
}
GAPI_LOG_DEBUG(NULL, "OV Backend: PrePostProcessor: " << ppp);
const_cast<std::shared_ptr<::ov::Model>&>(uu.model) = ppp.build();
ppp.cfgPostProcessing();
ppp.finalize();
}
for (const auto &out_name : uu.params.output_names) {
@@ -815,6 +1004,313 @@ struct Infer: public cv::detail::KernelTag {
}
};
struct InferROI: public cv::detail::KernelTag {
using API = cv::GInferROIBase;
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
static KImpl kernel() { return KImpl{outMeta, run}; }
static cv::GMetaArgs outMeta(const ade::Graph &gr,
const ade::NodeHandle &nh,
const cv::GMetaArgs &in_metas,
const cv::GArgs &/*in_args*/) {
cv::GMetaArgs result;
GConstGOVModel gm(gr);
const auto &uu = gm.metadata(nh).get<OVUnit>();
// Initialize input information
// FIXME: So far it is pretty limited
GAPI_Assert(1u == uu.params.input_names.size());
GAPI_Assert(2u == in_metas.size());
const auto &input_name = uu.params.input_names.at(0);
const auto &mm = in_metas.at(1u);
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(mm));
const auto &matdesc = cv::util::get<cv::GMatDesc>(mm);
const bool is_model = cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind);
const auto &input_shape = is_model ? uu.model->input(input_name).get_shape()
: uu.compiled_model.input(input_name).get_shape();
if (!isImage(matdesc, input_shape)) {
util::throw_error(std::runtime_error(
"OV Backend: InferROI supports only image as the 1th argument"));
}
if (is_model) {
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
PrePostProcWrapper ppp {model, model_info,
uu.params.input_names, uu.params.output_names};
ppp.cfgLayouts(input_name);
ppp.cfgPreProcessing(input_name, mm, true /*disable_img_resize*/);
ppp.cfgScaleMean(input_name);
ppp.cfgPostProcessing();
ppp.finalize();
}
for (const auto &out_name : uu.params.output_names) {
cv::GMatDesc outm;
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
const auto &out = uu.model->output(out_name);
outm = cv::GMatDesc(toCV(out.get_element_type()),
toCV(out.get_shape()));
} else {
GAPI_Assert(cv::util::holds_alternative<ParamDesc::CompiledModel>(uu.params.kind));
const auto &out = uu.compiled_model.output(out_name);
outm = cv::GMatDesc(toCV(out.get_element_type()),
toCV(out.get_shape()));
}
result.emplace_back(std::move(outm));
}
return result;
}
static void run(std::shared_ptr<OVCallContext> ctx,
cv::gimpl::ov::RequestPool &reqPool) {
using namespace std::placeholders;
reqPool.getIdleRequest()->execute(
IInferExecutor::Task {
[ctx](::ov::InferRequest &infer_request) {
GAPI_Assert(ctx->uu.params.num_in == 1);
const auto &input_name = ctx->uu.params.input_names[0];
auto input_tensor = infer_request.get_tensor(input_name);
const auto &shape = input_tensor.get_shape();
const auto &roi = ctx->inArg<cv::detail::OpaqueRef>(0).rref<cv::Rect>();
const auto roi_mat = preprocess(ctx->inMat(1), roi, shape);
copyToOV(roi_mat, input_tensor);
},
std::bind(PostOutputs, _1, _2, ctx)
}
);
}
};
struct InferList: public cv::detail::KernelTag {
using API = cv::GInferListBase;
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
static KImpl kernel() { return KImpl{outMeta, run}; }
static cv::GMetaArgs outMeta(const ade::Graph &gr,
const ade::NodeHandle &nh,
const cv::GMetaArgs &in_metas,
const cv::GArgs &/*in_args*/) {
GConstGOVModel gm(gr);
const auto &uu = gm.metadata(nh).get<OVUnit>();
// Initialize input information
// Note our input layers list order matches the API order and so
// meta order.
GAPI_Assert(uu.params.input_names.size() == (in_metas.size() - 1u)
&& "Known input layers count doesn't match input meta count");
// NB: Pre/Post processing configuration avaiable only for read models.
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
PrePostProcWrapper ppp {model, model_info,
uu.params.input_names, uu.params.output_names};
size_t idx = 1u;
for (auto &&input_name : uu.params.input_names) {
const auto &mm = in_metas[idx++];
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(mm));
const auto &matdesc = cv::util::get<cv::GMatDesc>(mm);
const auto &input_shape = uu.model->input(input_name).get_shape();
if (!isImage(matdesc, input_shape)) {
util::throw_error(std::runtime_error(
"OV Backend: Only image is supported"
" as the " + std::to_string(idx) + "th argument for InferList"));
}
ppp.cfgLayouts(input_name);
ppp.cfgPreProcessing(input_name, mm, true /*disable_img_resize*/);
ppp.cfgScaleMean(input_name);
}
ppp.cfgPostProcessing();
ppp.finalize();
}
// roi-list version is much easier at the moment.
// All our outputs are vectors which don't have
// metadata at the moment - so just create a vector of
// "empty" array metadatas of the required size.
return cv::GMetaArgs(uu.params.output_names.size(),
cv::GMetaArg{cv::empty_array_desc()});
}
static void run(std::shared_ptr<OVCallContext> ctx,
cv::gimpl::ov::RequestPool &reqPool) {
const auto& in_roi_vec = ctx->inArg<cv::detail::VectorRef>(0u).rref<cv::Rect>();
// NB: In case there is no input data need to post output anyway
if (in_roi_vec.empty()) {
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
auto output = ctx->output(i);
ctx->out.meta(output, ctx->getMeta());
ctx->out.post(std::move(output));
}
return;
}
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
// FIXME: Isn't this should be done automatically
// by some resetInternalData(), etc? (Probably at the GExecutor level)
auto& out_vec = ctx->outVecR<cv::Mat>(i);
out_vec.clear();
out_vec.resize(in_roi_vec.size());
}
PostOutputsList callback(in_roi_vec.size(), ctx);
for (auto&& it : ade::util::indexed(in_roi_vec)) {
const auto pos = ade::util::index(it);
const auto &rc = ade::util::value(it);
reqPool.getIdleRequest()->execute(
IInferExecutor::Task {
[ctx, rc](::ov::InferRequest &infer_request) {
const auto &input_name = ctx->uu.params.input_names[0];
auto input_tensor = infer_request.get_tensor(input_name);
const auto &shape = input_tensor.get_shape();
const auto roi_mat = preprocess(ctx->inMat(1), rc, shape);
copyToOV(roi_mat, input_tensor);
},
std::bind(callback, std::placeholders::_1, std::placeholders::_2, pos)
}
);
}
}
};
struct InferList2: public cv::detail::KernelTag {
using API = cv::GInferList2Base;
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
static KImpl kernel() { return KImpl{outMeta, run}; }
static cv::GMetaArgs outMeta(const ade::Graph &gr,
const ade::NodeHandle &nh,
const cv::GMetaArgs &in_metas,
const cv::GArgs &/*in_args*/) {
GConstGOVModel gm(gr);
const auto &uu = gm.metadata(nh).get<OVUnit>();
// Initialize input information
// Note our input layers list order matches the API order and so
// meta order.
GAPI_Assert(uu.params.input_names.size() == (in_metas.size() - 1u)
&& "Known input layers count doesn't match input meta count");
const auto &op = gm.metadata(nh).get<Op>();
// In contrast to InferList, the InferList2 has only one
// "full-frame" image argument, and all the rest are arrays of
// ether ROI or blobs. So here we set the 0th arg image format
// to all inputs which are ROI-based (skipping the
// "blob"-based ones)
// FIXME: this is filtering not done, actually! GArrayDesc has
// no hint for its underlying type!
const auto &input_name_0 = uu.params.input_names.front();
const auto &mm_0 = in_metas[0u];
const auto &matdesc = cv::util::get<cv::GMatDesc>(mm_0);
const bool is_model = cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind);
const auto &input_shape = is_model ? uu.model->input(input_name_0).get_shape()
: uu.compiled_model.input(input_name_0).get_shape();
if (!isImage(matdesc, input_shape)) {
util::throw_error(std::runtime_error(
"OV Backend: InferList2 supports only image as the 0th argument"));
}
if (is_model) {
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
PrePostProcWrapper ppp {model, model_info,
uu.params.input_names, uu.params.output_names};
size_t idx = 1u;
for (auto &&input_name : uu.params.input_names) {
GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(in_metas[idx])
&& "Non-array inputs are not supported");
ppp.cfgLayouts(input_name);
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
ppp.cfgPreProcessing(input_name, mm_0, true /*disable_img_resize*/);
} else {
// This is a cv::GMat (equals to: cv::Mat)
// Just validate that it is really the type
// (other types are prohibited here)
GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT);
}
ppp.cfgScaleMean(input_name);
idx++; // NB: Never forget to increment the counter
}
ppp.cfgPostProcessing();
ppp.finalize();
}
// roi-list version is much easier at the moment.
// All our outputs are vectors which don't have
// metadata at the moment - so just create a vector of
// "empty" array metadatas of the required size.
return cv::GMetaArgs(uu.params.output_names.size(),
cv::GMetaArg{cv::empty_array_desc()});
}
static void run(std::shared_ptr<OVCallContext> ctx,
cv::gimpl::ov::RequestPool &reqPool) {
GAPI_Assert(ctx->inArgs().size() > 1u
&& "This operation must have at least two arguments");
// NB: This blob will be used to make roi from its, so
// it should be treated as image
const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size();
if (list_size == 0u) {
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
auto output = ctx->output(i);
ctx->out.meta(output, ctx->getMeta());
ctx->out.post(std::move(output));
}
return;
}
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
// FIXME: Isn't this should be done automatically
// by some resetInternalData(), etc? (Probably at the GExecutor level)
auto& out_vec = ctx->outVecR<cv::Mat>(i);
out_vec.clear();
out_vec.resize(list_size);
}
PostOutputsList callback(list_size, ctx);
for (const auto &list_idx : ade::util::iota(list_size)) {
reqPool.getIdleRequest()->execute(
IInferExecutor::Task {
[ctx, list_idx, list_size](::ov::InferRequest &infer_request) {
for (auto in_idx : ade::util::iota(ctx->uu.params.num_in)) {
const auto &this_vec = ctx->inArg<cv::detail::VectorRef>(in_idx+1u);
GAPI_Assert(this_vec.size() == list_size);
const auto &input_name = ctx->uu.params.input_names[in_idx];
auto input_tensor = infer_request.get_tensor(input_name);
const auto &shape = input_tensor.get_shape();
if (this_vec.getKind() == cv::detail::OpaqueKind::CV_RECT) {
const auto &vec = this_vec.rref<cv::Rect>();
const auto roi_mat = preprocess(ctx->inMat(0), vec[list_idx], shape);
copyToOV(roi_mat, input_tensor);
} else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) {
const auto &vec = this_vec.rref<cv::Mat>();
const auto &mat = vec[list_idx];
copyToOV(mat, input_tensor);
} else {
GAPI_Assert(false &&
"OV Backend: Only Rect and Mat types are supported for InferList2");
}
}
},
std::bind(callback, std::placeholders::_1, std::placeholders::_2, list_idx)
} // task
);
} // for
}
};
} // namespace ov
} // namespace gimpl
} // namespace cv
@@ -858,7 +1354,10 @@ class GOVBackendImpl final: public cv::gapi::GBackend::Priv {
}
virtual cv::GKernelPackage auxiliaryKernels() const override {
return cv::gapi::kernels< cv::gimpl::ov::Infer >();
return cv::gapi::kernels< cv::gimpl::ov::Infer
, cv::gimpl::ov::InferROI
, cv::gimpl::ov::InferList
, cv::gimpl::ov::InferList2 >();
}
virtual bool controlsMerge() const override {
@@ -904,8 +1403,10 @@ cv::gimpl::ov::GOVExecutable::GOVExecutable(const ade::Graph &g,
case NodeType::OP:
if (this_nh == nullptr) {
this_nh = nh;
compiled = const_cast<OVUnit&>(ovm.metadata(this_nh).get<OVUnit>()).compile();
m_reqPool.reset(new RequestPool(createInferRequests(compiled.compiled_model, 1)));
const auto &unit = ovm.metadata(this_nh).get<OVUnit>();
compiled = const_cast<OVUnit&>(unit).compile();
m_reqPool.reset(new RequestPool(createInferRequests(
compiled.compiled_model, unit.params.nireq)));
}
else
util::throw_error(std::logic_error("Multi-node inference is not supported!"));
@@ -937,6 +1438,7 @@ void cv::gimpl::ov::GOVExecutable::run(cv::gimpl::GIslandExecutable::IInput &in
if (cv::util::holds_alternative<cv::gimpl::EndOfStream>(in_msg))
{
m_reqPool->waitAll();
out.post(cv::gimpl::EndOfStream{});
return;
}
+9 -3
View File
@@ -22,13 +22,19 @@ namespace cv {
namespace gapi {
namespace ov {
namespace util {
// NB: These functions are EXPORTed to make them accessible by the
// test suite only.
GAPI_EXPORTS std::vector<int> to_ocv(const ::ov::Shape &shape);
GAPI_EXPORTS int to_ocv(const ::ov::element::Type &type);
}}}}
GAPI_EXPORTS void to_ov(const cv::Mat &mat, ::ov::Tensor &tensor);
GAPI_EXPORTS void to_ocv(const ::ov::Tensor &tensor, cv::Mat &mat);
} // namespace util
namespace wrap {
GAPI_EXPORTS ::ov::Core getCore();
} // namespace wrap
} // namespace ov
} // namespace gapi
} // namespace cv
#endif // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
+9 -9
View File
@@ -59,7 +59,6 @@ private:
} // namespace
cv::gimpl::Unrolled cv::gimpl::unrollExpr(const GProtoArgs &ins,
const GProtoArgs &outs)
{
@@ -135,18 +134,19 @@ cv::gimpl::Unrolled cv::gimpl::unrollExpr(const GProtoArgs &ins,
// Put the outputs object description of the node
// so that they are not lost if they are not consumed by other operations
GAPI_Assert(call_p.m_k.outCtors.size() == call_p.m_k.outShapes.size());
for (const auto it : ade::util::indexed(call_p.m_k.outShapes))
for (const auto it : ade::util::indexed(ade::util::zip(call_p.m_k.outShapes,
call_p.m_k.outCtors,
call_p.m_k.outKinds)))
{
std::size_t port = ade::util::index(it);
GShape shape = ade::util::value(it);
// FIXME: then use ZIP
HostCtor ctor = call_p.m_k.outCtors[port];
auto port = ade::util::index(it);
auto &val = ade::util::value(it);
auto shape = std::get<0>(val);
auto ctor = std::get<1>(val);
auto kind = std::get<2>(val);
// NB: Probably this fixes all other "missing host ctor"
// problems.
// TODO: Clean-up the old workarounds if it really is.
GOrigin org {shape, node, port, std::move(ctor), origin.kind};
GOrigin org {shape, node, port, std::move(ctor), kind};
origins.insert(org);
}
+126 -7
View File
@@ -62,6 +62,11 @@ public:
return cv::MediaFrame::View(std::move(pp), std::move(ss), Cb{m_cb});
}
cv::util::any blobParams() const override {
#if INF_ENGINE_RELEASE > 2023000000
// NB: blobParams() shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
GAPI_Assert(false && "NV12 feature has been deprecated in OpenVINO 1.0 API.");
#else
return std::make_pair<InferenceEngine::TensorDesc,
InferenceEngine::ParamMap>({IE::Precision::U8,
{1, 3, 300, 300},
@@ -69,6 +74,7 @@ public:
{{"HELLO", 42},
{"COLOR_FORMAT",
InferenceEngine::ColorFormat::NV12}});
#endif // INF_ENGINE_RELEASE > 2023000000
}
};
@@ -138,7 +144,13 @@ void setNetParameters(IE::CNNNetwork& net, bool is_nv12 = false) {
ii->setPrecision(IE::Precision::U8);
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
if (is_nv12) {
#if INF_ENGINE_RELEASE > 2023000000
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
GAPI_Assert(false && "NV12 feature has been deprecated in OpenVINO 1.0 API.");
#else
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
#endif // INF_ENGINE_RELEASE > 2023000000
}
}
@@ -392,10 +404,14 @@ struct InferWithReshapeNV12: public InferWithReshape {
cv::randu(m_in_y, 0, 255);
m_in_uv = cv::Mat{sz / 2, CV_8UC2};
cv::randu(m_in_uv, 0, 255);
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
#if INF_ENGINE_RELEASE <= 2023000000
setNetParameters(net, true);
net.reshape({{"data", reshape_dims}});
auto frame_blob = cv::gapi::ie::util::to_ie(m_in_y, m_in_uv);
inferROIs(frame_blob);
#endif // INF_ENGINE_RELEASE <= 2023000000
}
};
@@ -505,8 +521,11 @@ struct ROIListNV12: public ::testing::Test {
cv::Rect(cv::Point{50, 32}, cv::Size{128, 160}),
};
// Load & run IE network
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
#if INF_ENGINE_RELEASE <= 2023000000
{
// Load & run IE network
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
auto net = cv::gimpl::ie::wrap::readNetwork(params);
setNetParameters(net, true);
@@ -530,9 +549,11 @@ struct ROIListNV12: public ::testing::Test {
m_out_ie_genders.push_back(to_ocv(infer_request.GetBlob("prob")).clone());
}
} // namespace IE = ..
#endif // INF_ENGINE_RELEASE <= 2023000000
} // ROIList()
void validate() {
#if INF_ENGINE_RELEASE <= 2023000000
// Validate with IE itself (avoid DNN module dependency here)
ASSERT_EQ(2u, m_out_ie_ages.size());
ASSERT_EQ(2u, m_out_ie_genders.size());
@@ -543,6 +564,10 @@ struct ROIListNV12: public ::testing::Test {
normAssert(m_out_ie_genders[0], m_out_gapi_genders[0], "0: Test gender output");
normAssert(m_out_ie_ages [1], m_out_gapi_ages [1], "1: Test age output");
normAssert(m_out_ie_genders[1], m_out_gapi_genders[1], "1: Test gender output");
#else
GAPI_Assert(false && "Reference hasn't been calculated because"
" NV12 feature has been deprecated.");
#endif // INF_ENGINE_RELEASE <= 2023000000
}
};
@@ -631,6 +656,9 @@ struct SingleROINV12: public ::testing::Test {
m_roi = cv::Rect(cv::Point{64, 60}, cv::Size{96, 96});
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
#if INF_ENGINE_RELEASE <= 2023000000
// Load & run IE network
IE::Blob::Ptr ie_age, ie_gender;
{
@@ -657,12 +685,18 @@ struct SingleROINV12: public ::testing::Test {
m_out_ie_age = to_ocv(infer_request.GetBlob("age_conv3")).clone();
m_out_ie_gender = to_ocv(infer_request.GetBlob("prob")).clone();
}
#endif // INF_ENGINE_RELEASE <= 2023000000
}
void validate() {
#if INF_ENGINE_RELEASE <= 2023000000
// Validate with IE itself (avoid DNN module dependency here)
normAssert(m_out_ie_age , m_out_gapi_age , "Test age output");
normAssert(m_out_ie_gender, m_out_gapi_gender, "Test gender output");
#else
GAPI_Assert(false && "Reference hasn't been calculated because"
" NV12 feature has been deprecated.");
#endif
}
};
@@ -962,11 +996,20 @@ TEST_F(ROIListNV12, MediaInputNV12)
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" });
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST(TestAgeGenderIE, MediaInputNV12)
@@ -986,6 +1029,9 @@ TEST(TestAgeGenderIE, MediaInputNV12)
cv::Mat gapi_age, gapi_gender;
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
#if INF_ENGINE_RELEASE <= 2023000000
// Load & run IE network
IE::Blob::Ptr ie_age, ie_gender;
{
@@ -999,6 +1045,7 @@ TEST(TestAgeGenderIE, MediaInputNV12)
ie_age = infer_request.GetBlob("age_conv3");
ie_gender = infer_request.GetBlob("prob");
}
#endif
// Configure & run G-API
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
@@ -1014,13 +1061,20 @@ TEST(TestAgeGenderIE, MediaInputNV12)
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" });
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Validate with IE itself (avoid DNN module dependency here)
normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" );
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST(TestAgeGenderIE, MediaInputBGR)
@@ -1155,6 +1209,9 @@ TEST(InferROI, MediaInputNV12)
cv::Mat gapi_age, gapi_gender;
cv::Rect rect(cv::Point{64, 60}, cv::Size{96, 96});
// NB: NV12 feature shouldn't be used in tests
// if OpenVINO versions is higher than 2023.0
#if INF_ENGINE_RELEASE <= 2023000000
// Load & run IE network
IE::Blob::Ptr ie_age, ie_gender;
{
@@ -1176,6 +1233,7 @@ TEST(InferROI, MediaInputNV12)
ie_age = infer_request.GetBlob("age_conv3");
ie_gender = infer_request.GetBlob("prob");
}
#endif
// Configure & run G-API
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
@@ -1192,13 +1250,20 @@ TEST(InferROI, MediaInputNV12)
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" });
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, rect), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Validate with IE itself (avoid DNN module dependency here)
normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" );
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, rect), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST_F(ROIList, Infer2MediaInputBGR)
@@ -1233,10 +1298,20 @@ TEST_F(ROIListNV12, Infer2MediaInputNV12)
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" });
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST_F(SingleROI, GenericInfer)
@@ -1310,10 +1385,19 @@ TEST_F(SingleROINV12, GenericInferMediaNV12)
pp.cfgNumRequests(2u);
auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi), cv::gout(m_out_gapi_age, m_out_gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi),
cv::gout(m_out_gapi_age, m_out_gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST_F(ROIList, GenericInfer)
@@ -1386,11 +1470,20 @@ TEST_F(ROIListNV12, GenericInferMediaNV12)
pp.cfgNumRequests(2u);
auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST_F(ROIList, GenericInfer2)
@@ -1461,10 +1554,20 @@ TEST_F(ROIListNV12, GenericInfer2MediaInputNV12)
pp.cfgNumRequests(2u);
auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST(Infer, SetInvalidNumberOfRequests)
@@ -2050,11 +2153,20 @@ TEST_F(InferWithReshapeNV12, TestInferListYUV)
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" }).cfgInputReshape({{"data", reshape_dims}});
// NB: NV12 feature has been deprecated in OpenVINO versions higher
// than 2023.0 so G-API must throw error in that case.
#if INF_ENGINE_RELEASE <= 2023000000
comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp)));
// Validate
validate();
#else
EXPECT_ANY_THROW(comp.apply(cv::gin(frame, m_roi_list),
cv::gout(m_out_gapi_ages, m_out_gapi_genders),
cv::compile_args(cv::gapi::networks(pp))));
#endif
}
TEST_F(ROIList, CallInferMultipleTimes)
@@ -2079,6 +2191,7 @@ TEST_F(ROIList, CallInferMultipleTimes)
validate();
}
#if INF_ENGINE_RELEASE <= 2023000000
TEST(IEFrameAdapter, blobParams)
{
cv::Mat bgr = cv::Mat::eye(240, 320, CV_8UC3);
@@ -2093,6 +2206,7 @@ TEST(IEFrameAdapter, blobParams)
EXPECT_EQ(expected, actual);
}
#endif
namespace
{
@@ -2281,6 +2395,10 @@ TEST(TestAgeGenderIE, InferWithBatch)
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
}
// NB: All tests below use preprocessing for "Import" networks
// passed as the last argument to SetBLob. This overload has
// been deprecated in OpenVINO 1.0 API.
#if INF_ENGINE_RELEASE <= 2023000000
TEST(ImportNetwork, Infer)
{
const std::string device = "MYRIAD";
@@ -2820,6 +2938,7 @@ TEST(ImportNetwork, InferList2NV12)
normAssert(out_ie_genders[i], out_gapi_genders[i], "Test gender output");
}
}
#endif
TEST(TestAgeGender, ThrowBlobAndInputPrecisionMismatch)
{
+296 -174
View File
@@ -41,20 +41,6 @@ void initDLDTDataPath()
static const std::string SUBDIR = "intel/age-gender-recognition-retail-0013/FP32/";
void copyFromOV(ov::Tensor &tensor, cv::Mat &mat) {
GAPI_Assert(tensor.get_byte_size() == mat.total() * mat.elemSize());
std::copy_n(reinterpret_cast<uint8_t*>(tensor.data()),
tensor.get_byte_size(),
mat.ptr<uint8_t>());
}
void copyToOV(const cv::Mat &mat, ov::Tensor &tensor) {
GAPI_Assert(tensor.get_byte_size() == mat.total() * mat.elemSize());
std::copy_n(mat.ptr<uint8_t>(),
tensor.get_byte_size(),
reinterpret_cast<uint8_t*>(tensor.data()));
}
// FIXME: taken from the DNN module
void normAssert(cv::InputArray ref, cv::InputArray test,
const char *comment /*= ""*/,
@@ -66,15 +52,10 @@ void normAssert(cv::InputArray ref, cv::InputArray test,
EXPECT_LE(normInf, lInf) << comment;
}
ov::Core getCore() {
static ov::Core core;
return core;
}
// TODO: AGNetGenComp, AGNetTypedComp, AGNetOVComp, AGNetOVCompiled
// can be generalized to work with any model and used as parameters for tests.
struct AGNetGenComp {
struct AGNetGenParams {
static constexpr const char* tag = "age-gender-generic";
using Params = cv::gapi::ov::Params<cv::gapi::Generic>;
@@ -88,19 +69,9 @@ struct AGNetGenComp {
const std::string &device) {
return {tag, blob_path, device};
}
static cv::GComputation create() {
cv::GMat in;
GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>(tag, inputs);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
return cv::GComputation{cv::GIn(in), cv::GOut(age, gender)};
}
};
struct AGNetTypedComp {
struct AGNetTypedParams {
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "typed-age-gender");
using Params = cv::gapi::ov::Params<AgeGender>;
@@ -112,7 +83,9 @@ struct AGNetTypedComp {
xml_path, bin_path, device
}.cfgOutputLayers({ "age_conv3", "prob" });
}
};
struct AGNetTypedComp : AGNetTypedParams {
static cv::GComputation create() {
cv::GMat in;
cv::GMat age, gender;
@@ -121,30 +94,104 @@ struct AGNetTypedComp {
}
};
struct AGNetGenComp : public AGNetGenParams {
static cv::GComputation create() {
cv::GMat in;
GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>(tag, inputs);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
return cv::GComputation{cv::GIn(in), cv::GOut(age, gender)};
}
};
struct AGNetROIGenComp : AGNetGenParams {
static cv::GComputation create() {
cv::GMat in;
cv::GOpaque<cv::Rect> roi;
GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>(tag, roi, inputs);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
return cv::GComputation{cv::GIn(in, roi), cv::GOut(age, gender)};
}
};
struct AGNetListGenComp : AGNetGenParams {
static cv::GComputation create() {
cv::GMat in;
cv::GArray<cv::Rect> rois;
GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>(tag, rois, inputs);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
return cv::GComputation{cv::GIn(in, rois), cv::GOut(age, gender)};
}
};
struct AGNetList2GenComp : AGNetGenParams {
static cv::GComputation create() {
cv::GMat in;
cv::GArray<cv::Rect> rois;
GInferListInputs list;
list["data"] = rois;
auto outputs = cv::gapi::infer2<cv::gapi::Generic>(tag, in, list);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
return cv::GComputation{cv::GIn(in, rois), cv::GOut(age, gender)};
}
};
class AGNetOVCompiled {
public:
AGNetOVCompiled(ov::CompiledModel &&compiled_model)
: m_compiled_model(std::move(compiled_model)) {
: m_compiled_model(std::move(compiled_model)),
m_infer_request(m_compiled_model.create_infer_request()) {
}
void operator()(const cv::Mat &in_mat,
const cv::Rect &roi,
cv::Mat &age_mat,
cv::Mat &gender_mat) {
// FIXME: W & H could be extracted from model shape
// but it's anyway used only for Age Gender model.
// (Well won't work in case of reshape)
const int W = 62;
const int H = 62;
cv::Mat resized_roi;
cv::resize(in_mat(roi), resized_roi, cv::Size(W, H));
(*this)(resized_roi, age_mat, gender_mat);
}
void operator()(const cv::Mat &in_mat,
const std::vector<cv::Rect> &rois,
std::vector<cv::Mat> &age_mats,
std::vector<cv::Mat> &gender_mats) {
for (size_t i = 0; i < rois.size(); ++i) {
(*this)(in_mat, rois[i], age_mats[i], gender_mats[i]);
}
}
void operator()(const cv::Mat &in_mat,
cv::Mat &age_mat,
cv::Mat &gender_mat) {
auto infer_request = m_compiled_model.create_infer_request();
auto input_tensor = infer_request.get_input_tensor();
copyToOV(in_mat, input_tensor);
auto input_tensor = m_infer_request.get_input_tensor();
cv::gapi::ov::util::to_ov(in_mat, input_tensor);
infer_request.infer();
m_infer_request.infer();
auto age_tensor = infer_request.get_tensor("age_conv3");
auto age_tensor = m_infer_request.get_tensor("age_conv3");
age_mat.create(cv::gapi::ov::util::to_ocv(age_tensor.get_shape()),
cv::gapi::ov::util::to_ocv(age_tensor.get_element_type()));
copyFromOV(age_tensor, age_mat);
cv::gapi::ov::util::to_ocv(age_tensor, age_mat);
auto gender_tensor = infer_request.get_tensor("prob");
auto gender_tensor = m_infer_request.get_tensor("prob");
gender_mat.create(cv::gapi::ov::util::to_ocv(gender_tensor.get_shape()),
cv::gapi::ov::util::to_ocv(gender_tensor.get_element_type()));
copyFromOV(gender_tensor, gender_mat);
cv::gapi::ov::util::to_ocv(gender_tensor, gender_mat);
}
void export_model(const std::string &outpath) {
@@ -155,6 +202,7 @@ public:
private:
ov::CompiledModel m_compiled_model;
ov::InferRequest m_infer_request;
};
struct ImageInputPreproc {
@@ -175,7 +223,8 @@ public:
const std::string &bin_path,
const std::string &device)
: m_device(device) {
m_model = getCore().read_model(xml_path, bin_path);
m_model = cv::gapi::ov::wrap::getCore()
.read_model(xml_path, bin_path);
}
using PrePostProcessF = std::function<void(ov::preprocess::PrePostProcessor&)>;
@@ -187,7 +236,8 @@ public:
}
AGNetOVCompiled compile() {
auto compiled_model = getCore().compile_model(m_model, m_device);
auto compiled_model = cv::gapi::ov::wrap::getCore()
.compile_model(m_model, m_device);
return {std::move(compiled_model)};
}
@@ -202,19 +252,78 @@ private:
std::shared_ptr<ov::Model> m_model;
};
struct BaseAgeGenderOV: public ::testing::Test {
BaseAgeGenderOV() {
initDLDTDataPath();
xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
device = "CPU";
blob_path = "age-gender-recognition-retail-0013.blob";
}
cv::Mat getRandomImage(const cv::Size &sz) {
cv::Mat image(sz, CV_8UC3);
cv::randu(image, 0, 255);
return image;
}
cv::Mat getRandomTensor(const std::vector<int> &dims,
const int depth) {
cv::Mat tensor(dims, depth);
cv::randu(tensor, -1, 1);
return tensor;
}
std::string xml_path;
std::string bin_path;
std::string blob_path;
std::string device;
};
struct TestAgeGenderOV : public BaseAgeGenderOV {
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
void validate() {
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
};
struct TestAgeGenderListOV : public BaseAgeGenderOV {
std::vector<cv::Mat> ov_age, ov_gender,
gapi_age, gapi_gender;
std::vector<cv::Rect> roi_list = {
cv::Rect(cv::Point{64, 60}, cv::Size{ 96, 96}),
cv::Rect(cv::Point{50, 32}, cv::Size{128, 160}),
};
TestAgeGenderListOV() {
ov_age.resize(roi_list.size());
ov_gender.resize(roi_list.size());
gapi_age.resize(roi_list.size());
gapi_gender.resize(roi_list.size());
}
void validate() {
ASSERT_EQ(ov_age.size(), ov_gender.size());
ASSERT_EQ(ov_age.size(), gapi_age.size());
ASSERT_EQ(ov_gender.size(), gapi_gender.size());
for (size_t i = 0; i < ov_age.size(); ++i) {
normAssert(ov_age[i], gapi_age[i], "Test age output");
normAssert(ov_gender[i], gapi_gender[i], "Test gender output");
}
}
};
} // anonymous namespace
// TODO: Make all of tests below parmetrized to avoid code duplication
TEST(TestAgeGenderOV, InferTypedTensor) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, Infer_Tensor) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.apply(in_mat, ov_age, ov_gender);
@@ -226,19 +335,11 @@ TEST(TestAgeGenderOV, InferTypedTensor) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferTypedImage) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat(300, 300, CV_8UC3);
cv::randu(in_mat, 0, 255);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, Infer_Image) {
const auto in_mat = getRandomImage({300, 300});
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -252,19 +353,11 @@ TEST(TestAgeGenderOV, InferTypedImage) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferGenericTensor) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGeneric_Tensor) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -277,19 +370,11 @@ TEST(TestAgeGenderOV, InferGenericTensor) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferGenericImage) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat(300, 300, CV_8UC3);
cv::randu(in_mat, 0, 255);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGenericImage) {
const auto in_mat = getRandomImage({300, 300});
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -303,20 +388,11 @@ TEST(TestAgeGenderOV, InferGenericImage) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferGenericImageBlob) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
const std::string device = "CPU";
cv::Mat in_mat(300, 300, CV_8UC3);
cv::randu(in_mat, 0, 255);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGeneric_ImageBlob) {
const auto in_mat = getRandomImage({300, 300});
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -333,20 +409,11 @@ TEST(TestAgeGenderOV, InferGenericImageBlob) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferGenericTensorBlob) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
const std::string device = "CPU";
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGeneric_TensorBlob) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -361,19 +428,11 @@ TEST(TestAgeGenderOV, InferGenericTensorBlob) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferBothOutputsFP16) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGeneric_BothOutputsFP16) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -392,19 +451,11 @@ TEST(TestAgeGenderOV, InferBothOutputsFP16) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, InferOneOutputFP16) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, InferGeneric_OneOutputFP16) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
// OpenVINO
const std::string fp16_output_name = "prob";
@@ -423,17 +474,10 @@ TEST(TestAgeGenderOV, InferOneOutputFP16) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST(TestAgeGenderOV, ThrowCfgOutputPrecForBlob) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
const std::string device = "CPU";
TEST_F(TestAgeGenderOV, InferGeneric_ThrowCfgOutputPrecForBlob) {
// OpenVINO (Just for blob compilation)
AGNetOVComp ref(xml_path, bin_path, device);
auto cc_ref = ref.compile();
@@ -446,12 +490,7 @@ TEST(TestAgeGenderOV, ThrowCfgOutputPrecForBlob) {
EXPECT_ANY_THROW(pp.cfgOutputTensorPrecision(CV_16F));
}
TEST(TestAgeGenderOV, ThrowInvalidConfigIR) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
TEST_F(TestAgeGenderOV, InferGeneric_ThrowInvalidConfigIR) {
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
@@ -461,13 +500,7 @@ TEST(TestAgeGenderOV, ThrowInvalidConfigIR) {
cv::compile_args(cv::gapi::networks(pp))));
}
TEST(TestAgeGenderOV, ThrowInvalidConfigBlob) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
const std::string device = "CPU";
TEST_F(TestAgeGenderOV, InferGeneric_ThrowInvalidConfigBlob) {
// OpenVINO (Just for blob compilation)
AGNetOVComp ref(xml_path, bin_path, device);
auto cc_ref = ref.compile();
@@ -482,16 +515,8 @@ TEST(TestAgeGenderOV, ThrowInvalidConfigBlob) {
cv::compile_args(cv::gapi::networks(pp))));
}
TEST(TestAgeGenderOV, ThrowInvalidImageLayout) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
// NB: This mat may only have "NHWC" layout.
cv::Mat in_mat(300, 300, CV_8UC3);
cv::randu(in_mat, 0, 255);
cv::Mat gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, Infer_ThrowInvalidImageLayout) {
const auto in_mat = getRandomImage({300, 300});
auto comp = AGNetTypedComp::create();
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
@@ -501,15 +526,8 @@ TEST(TestAgeGenderOV, ThrowInvalidImageLayout) {
cv::compile_args(cv::gapi::networks(pp))));
}
TEST(TestAgeGenderOV, InferTensorWithPreproc) {
initDLDTDataPath();
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
const std::string device = "CPU";
cv::Mat in_mat({1, 240, 320, 3}, CV_32F);
cv::randu(in_mat, -1, 1);
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
TEST_F(TestAgeGenderOV, Infer_TensorWithPreproc) {
const auto in_mat = getRandomTensor({1, 240, 320, 3}, CV_32F);
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
@@ -531,8 +549,112 @@ TEST(TestAgeGenderOV, InferTensorWithPreproc) {
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
validate();
}
TEST_F(TestAgeGenderOV, InferROIGeneric_Image) {
const auto in_mat = getRandomImage({300, 300});
cv::Rect roi(cv::Rect(cv::Point{64, 60}, cv::Size{96, 96}));
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp) {
ppp.input().tensor().set_element_type(ov::element::u8);
ppp.input().tensor().set_layout("NHWC");
});
ref.compile()(in_mat, roi, ov_age, ov_gender);
// G-API
auto comp = AGNetROIGenComp::create();
auto pp = AGNetROIGenComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat, roi), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
validate();
}
TEST_F(TestAgeGenderOV, InferROIGeneric_ThrowIncorrectLayout) {
const auto in_mat = getRandomImage({300, 300});
cv::Rect roi(cv::Rect(cv::Point{64, 60}, cv::Size{96, 96}));
// G-API
auto comp = AGNetROIGenComp::create();
auto pp = AGNetROIGenComp::params(xml_path, bin_path, device);
pp.cfgInputTensorLayout("NCHW");
EXPECT_ANY_THROW(comp.apply(cv::gin(in_mat, roi), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
}
TEST_F(TestAgeGenderOV, InferROIGeneric_ThrowTensorInput) {
const auto in_mat = getRandomTensor({1, 3, 62, 62}, CV_32F);
cv::Rect roi(cv::Rect(cv::Point{64, 60}, cv::Size{96, 96}));
// G-API
auto comp = AGNetROIGenComp::create();
auto pp = AGNetROIGenComp::params(xml_path, bin_path, device);
EXPECT_ANY_THROW(comp.apply(cv::gin(in_mat, roi), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
}
TEST_F(TestAgeGenderOV, InferROIGeneric_ThrowExplicitResize) {
const auto in_mat = getRandomImage({300, 300});
cv::Rect roi(cv::Rect(cv::Point{64, 60}, cv::Size{96, 96}));
// G-API
auto comp = AGNetROIGenComp::create();
auto pp = AGNetROIGenComp::params(xml_path, bin_path, device);
pp.cfgResize(cv::INTER_LINEAR);
EXPECT_ANY_THROW(comp.apply(cv::gin(in_mat, roi), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
}
TEST_F(TestAgeGenderListOV, InferListGeneric_Image) {
const auto in_mat = getRandomImage({300, 300});
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp) {
ppp.input().tensor().set_element_type(ov::element::u8);
ppp.input().tensor().set_layout("NHWC");
});
ref.compile()(in_mat, roi_list, ov_age, ov_gender);
// G-API
auto comp = AGNetListGenComp::create();
auto pp = AGNetListGenComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat, roi_list), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
validate();
}
TEST_F(TestAgeGenderListOV, InferList2Generic_Image) {
const auto in_mat = getRandomImage({300, 300});
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp) {
ppp.input().tensor().set_element_type(ov::element::u8);
ppp.input().tensor().set_layout("NHWC");
});
ref.compile()(in_mat, roi_list, ov_age, ov_gender);
// G-API
auto comp = AGNetList2GenComp::create();
auto pp = AGNetList2GenComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat, roi_list), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
validate();
}
} // namespace opencv_test
@@ -30,7 +30,8 @@ namespace
, nullptr
, { GShape::GMAT }
, { D::OpaqueKind::CV_UNKNOWN }
, { cv::detail::HostCtor{cv::util::monostate{}} }
, { D::HostCtor{cv::util::monostate{}} }
, { D::OpaqueKind::CV_UNKNOWN }
}).pass(m).yield(0);
}
@@ -41,7 +42,8 @@ namespace
, nullptr
, { GShape::GMAT }
, { D::OpaqueKind::CV_UNKNOWN, D::OpaqueKind::CV_UNKNOWN }
, { cv::detail::HostCtor{cv::util::monostate{}} }
, { D::HostCtor{cv::util::monostate{}} }
, { D::OpaqueKind::CV_UNKNOWN}
}).pass(m1, m2).yield(0);
}