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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user