Merge pull request #23595 from TolyaTalamanov:at/implement-openvino-backend

[G-API] Implement OpenVINO 2.0 backend #23595

### Pull Request Readiness Checklist

Implemented basic functionality for `OpenVINO` 2.0 G-API backend.

#### Overview
- [x] Implement `Infer` kernel with some of essential configurable parameters + IR/Blob models format support.
- [ ] Implement the rest of kernels: `InferList`, `InferROI`, `Infer2` + other configurable params (e.g reshape)
- [x] Asyncrhonous execution support
- [ ] Remote context support

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

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
pull/23772/head
Anatoliy Talamanov 1 year ago committed by GitHub
parent 36d6ca529d
commit 5330112f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      modules/gapi/CMakeLists.txt
  2. 128
      modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp
  3. 685
      modules/gapi/include/opencv2/gapi/infer/ov.hpp
  4. 6
      modules/gapi/misc/python/pyopencv_gapi.hpp
  5. 1
      modules/gapi/misc/python/shadow_gapi.hpp
  6. 238
      modules/gapi/misc/python/test/test_gapi_infer_ov.py
  7. 16
      modules/gapi/src/api/gmat.cpp
  8. 6
      modules/gapi/src/backends/common/gbackend.hpp
  9. 168
      modules/gapi/src/backends/ov/bindings_ov.cpp
  10. 1001
      modules/gapi/src/backends/ov/govbackend.cpp
  11. 66
      modules/gapi/src/backends/ov/govbackend.hpp
  12. 35
      modules/gapi/src/backends/ov/util.hpp
  13. 540
      modules/gapi/test/infer/gapi_infer_ov_tests.cpp
  14. 34
      modules/python/src2/cv2_convert.hpp
  15. 8
      modules/python/src2/typing_stubs_generation/predefined_types.py

@ -156,6 +156,10 @@ set(gapi_srcs
src/backends/ie/giebackend.cpp
src/backends/ie/giebackend/giewrapper.cpp
# OV Backend. FIXME: should be included by CMake
# if and only if OV support is enabled
src/backends/ov/govbackend.cpp
# ONNX backend
src/backends/onnx/gonnxbackend.cpp
@ -182,6 +186,7 @@ set(gapi_srcs
# Python bridge
src/backends/ie/bindings_ie.cpp
src/backends/onnx/bindings_onnx.cpp
src/backends/ov/bindings_ov.cpp
src/backends/python/gpythonbackend.cpp
# OpenVPL Streaming source

@ -0,0 +1,128 @@
// 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_INFER_BINDINGS_OV_HPP
#define OPENCV_GAPI_INFER_BINDINGS_OV_HPP
#include <opencv2/gapi/util/any.hpp>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/infer/ov.hpp> // Params
#include <string>
namespace cv {
namespace gapi {
namespace ov {
// NB: Used by python wrapper
// This class can be marked as SIMPLE, because it's implemented as pimpl
class GAPI_EXPORTS_W_SIMPLE PyParams {
public:
GAPI_WRAP
PyParams() = default;
GAPI_WRAP
PyParams(const std::string &tag,
const std::string &model_path,
const std::string &bin_path,
const std::string &device);
GAPI_WRAP
PyParams(const std::string &tag,
const std::string &blob_path,
const std::string &device);
GAPI_WRAP
PyParams& cfgPluginConfig(
const std::map<std::string, std::string> &config);
GAPI_WRAP
PyParams& cfgInputTensorLayout(std::string tensor_layout);
GAPI_WRAP
PyParams& cfgInputTensorLayout(
std::map<std::string, std::string> layout_map);
GAPI_WRAP
PyParams& cfgInputModelLayout(std::string tensor_layout);
GAPI_WRAP
PyParams& cfgInputModelLayout(
std::map<std::string, std::string> layout_map);
GAPI_WRAP
PyParams& cfgOutputTensorLayout(std::string tensor_layout);
GAPI_WRAP
PyParams& cfgOutputTensorLayout(
std::map<std::string, std::string> layout_map);
GAPI_WRAP
PyParams& cfgOutputModelLayout(std::string tensor_layout);
GAPI_WRAP
PyParams& cfgOutputModelLayout(
std::map<std::string, std::string> layout_map);
GAPI_WRAP
PyParams& cfgOutputTensorPrecision(int precision);
GAPI_WRAP
PyParams& cfgOutputTensorPrecision(
std::map<std::string, int> precision_map);
GAPI_WRAP
PyParams& cfgReshape(std::vector<size_t> new_shape);
GAPI_WRAP
PyParams& cfgReshape(
std::map<std::string, std::vector<size_t>> new_shape_map);
GAPI_WRAP
PyParams& cfgNumRequests(const size_t nireq);
GAPI_WRAP
PyParams& cfgMean(std::vector<float> mean_values);
GAPI_WRAP
PyParams& cfgMean(
std::map<std::string, std::vector<float>> mean_map);
GAPI_WRAP
PyParams& cfgScale(std::vector<float> scale_values);
GAPI_WRAP
PyParams& cfgScale(
std::map<std::string, std::vector<float>> scale_map);
GAPI_WRAP
PyParams& cfgResize(int interpolation);
GAPI_WRAP
PyParams& cfgResize(std::map<std::string, int> interpolation);
GBackend backend() const;
std::string tag() const;
cv::util::any params() const;
private:
std::shared_ptr<Params<cv::gapi::Generic>> m_priv;
};
GAPI_EXPORTS_W PyParams params(const std::string &tag,
const std::string &model_path,
const std::string &weights,
const std::string &device);
GAPI_EXPORTS_W PyParams params(const std::string &tag,
const std::string &bin_path,
const std::string &device);
} // namespace ov
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_INFER_BINDINGS_OV_HPP

@ -0,0 +1,685 @@
// 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_INFER_OV_HPP
#define OPENCV_GAPI_INFER_OV_HPP
#include <string>
#include <opencv2/gapi/util/any.hpp>
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelType[M], GBackend
#include <opencv2/gapi/infer.hpp> // Generic
#include <map>
namespace cv {
namespace gapi {
/**
* @brief This namespace contains G-API OpenVINO 2.0 backend functions,
* structures, and symbols.
*/
namespace ov {
GAPI_EXPORTS cv::gapi::GBackend backend();
namespace detail {
template <typename T>
using AttrMap = std::map<std::string, T>;
// NB: This type is supposed to be used to hold in/out layers
// attributes such as precision, layout, shape etc.
//
// User can provide attributes either:
// 1. cv::util::monostate - No value specified explicitly.
// 2. Attr - value specified explicitly that should be broadcasted to all layers.
// 3. AttrMap[str->T] - map specifies value for particular layer.
template <typename Attr>
using LayerVariantAttr = cv::util::variant< cv::util::monostate
, AttrMap<Attr>
, Attr>;
struct ParamDesc {
struct Model {
Model(const std::string &model_path_,
const std::string &bin_path_)
: model_path(model_path_), bin_path(bin_path_) {
}
std::string model_path;
std::string bin_path;
LayerVariantAttr<std::string> input_tensor_layout;
LayerVariantAttr<std::string> input_model_layout;
LayerVariantAttr<std::string> output_tensor_layout;
LayerVariantAttr<std::string> output_model_layout;
LayerVariantAttr<int> output_tensor_precision;
LayerVariantAttr<std::vector<size_t>> new_shapes;
LayerVariantAttr<std::vector<float>> mean_values;
LayerVariantAttr<std::vector<float>> scale_values;
LayerVariantAttr<int> interpolation;
};
struct CompiledModel {
std::string blob_path;
};
using Kind = cv::util::variant<Model, CompiledModel>;
ParamDesc(Kind &&kind_,
const std::string &device_,
const bool is_generic_,
const size_t num_in_,
const size_t num_out_)
: kind(std::move(kind_)), device(device_),
is_generic(is_generic_),
num_in(num_in_), num_out(num_out_) {
}
Kind kind;
std::string device;
bool is_generic;
std::size_t num_in;
std::size_t num_out;
std::vector<std::string> input_names;
std::vector<std::string> output_names;
using PluginConfigT = std::map<std::string, std::string>;
PluginConfigT config;
size_t nireq = 1;
};
// NB: Just helper to avoid code duplication.
static detail::ParamDesc::Model&
getModelToSetAttrOrThrow(detail::ParamDesc::Kind &kind,
const std::string &attr_name) {
if (cv::util::holds_alternative<detail::ParamDesc::CompiledModel>(kind)) {
cv::util::throw_error(
std::logic_error("Specifying " + attr_name + " isn't"
" possible for compiled model."));
}
GAPI_Assert(cv::util::holds_alternative<detail::ParamDesc::Model>(kind));
return cv::util::get<detail::ParamDesc::Model>(kind);
}
} // namespace detail
/**
* @brief This structure provides functions
* that fill inference parameters for "OpenVINO Toolkit" model.
*/
template<typename Net> struct Params {
public:
/** @brief Class constructor.
Constructs Params based on model information and specifies default values for other
inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit".
@param model_path Path to a model.
@param bin_path Path to a data file.
For IR format (*.bin):
If path is empty, will try to read a bin file with the same name as xml.
If the bin file with the same name is not found, will load IR without weights.
For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used.
@param device target device to use.
*/
Params(const std::string &model_path,
const std::string &bin_path,
const std::string &device)
: m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}}
, device
, false /* is generic */
, std::tuple_size<typename Net::InArgs>::value
, std::tuple_size<typename Net::OutArgs>::value) {
}
/** @overload
Use this constructor to work with pre-compiled network.
Model is imported from a pre-compiled blob.
@param blob_path path to the compiled model (*.blob).
@param device target device to use.
*/
Params(const std::string &blob_path,
const std::string &device)
: m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}}
, device
, false /* is generic */
, std::tuple_size<typename Net::InArgs>::value
, std::tuple_size<typename Net::OutArgs>::value) {
}
/** @brief Specifies sequence of network input layers names for inference.
The function is used to associate cv::gapi::infer<> inputs with the model inputs.
Number of names has to match the number of network inputs as defined in G_API_NET().
In case a network has only single input layer, there is no need to specify name manually.
@param layer_names std::array<std::string, N> where N is the number of inputs
as defined in the @ref G_API_NET. Contains names of input layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputLayers(const std::vector<std::string> &layer_names) {
m_desc.input_names = layer_names;
return *this;
}
/** @brief Specifies sequence of network output layers names for inference.
The function is used to associate cv::gapi::infer<> outputs with the model outputs.
Number of names has to match the number of network outputs as defined in G_API_NET().
In case a network has only single output layer, there is no need to specify name manually.
@param layer_names std::array<std::string, N> where N is the number of outputs
as defined in the @ref G_API_NET. Contains names of output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputLayers(const std::vector<std::string> &layer_names) {
m_desc.output_names = layer_names;
return *this;
}
/** @brief Specifies OpenVINO plugin configuration.
The function is used to set configuration for OpenVINO plugin. Some parameters
can be different for each plugin. Please follow https://docs.openvinotoolkit.org/latest/index.html
to check information about specific plugin.
@param config Map of pairs: (config parameter name, config parameter value).
@return reference to this parameter structure.
*/
Params<Net>& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) {
m_desc.config = config;
return *this;
}
/** @brief Specifies tensor layout for an input layer.
The function is used to set tensor layout for an input layer.
@param layout Tensor layout ("NCHW", "NWHC", etc)
will be applied to all input layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputTensorLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
.input_tensor_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding input layer
and its tensor layout represented in std::string ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgInputTensorLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
.input_tensor_layout = std::move(layout_map);
return *this;
}
/** @brief Specifies model layout for an input layer.
The function is used to set model layout for an input layer.
@param layout Model layout ("NCHW", "NHWC", etc)
will be applied to all input layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputModelLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
.input_model_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding input layer
and its model layout ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgInputModelLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
.input_model_layout = std::move(layout_map);
return *this;
}
/** @brief Specifies tensor layout for an output layer.
The function is used to set tensor layout for an output layer.
@param layout Tensor layout ("NCHW", "NWHC", etc)
will be applied to all output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputTensorLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
.output_tensor_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding output layer
and its tensor layout represented in std::string ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgOutputTensorLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
.output_tensor_layout = std::move(layout_map);
return *this;
}
/** @brief Specifies model layout for an output layer.
The function is used to set model layout for an output layer.
@param layout Model layout ("NCHW", "NHWC", etc)
will be applied to all output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputModelLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
.output_model_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding output layer
and its model layout ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgOutputModelLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
.output_model_layout = std::move(layout_map);
return *this;
}
/** @brief Specifies tensor precision for an output layer.
The function is used to set tensor precision for an output layer..
@param precision Precision in OpenCV format (CV_8U, CV_32F, ...)
will be applied to all output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputTensorPrecision(int precision) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
.output_tensor_precision = precision;
return *this;
}
/** @overload
@param precision_map Map of pairs: name of corresponding output layer
and its precision in OpenCV format (CV_8U, CV_32F, ...)
@return reference to this parameter structure.
*/
Params<Net>&
cfgOutputTensorPrecision(detail::AttrMap<int> precision_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
.output_tensor_precision = std::move(precision_map);
return *this;
}
/** @brief Specifies the new shape for input layers.
The function is used to set new shape for input layers.
@param new_shape New shape will be applied to all input layers.
@return reference to this parameter structure.
*/
Params<Net>&
cfgReshape(std::vector<size_t> new_shape) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
.new_shapes = std::move(new_shape);
return *this;
}
/** @overload
@param new_shape_map Map of pairs: name of corresponding output layer
and its new shape.
@return reference to this parameter structure.
*/
Params<Net>&
cfgReshape(detail::AttrMap<std::vector<size_t>> new_shape_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
.new_shapes = std::move(new_shape_map);
return *this;
}
/** @brief Specifies number of asynchronous inference requests.
@param nireq Number of inference asynchronous requests.
@return reference to this parameter structure.
*/
Params<Net>& cfgNumRequests(const size_t nireq) {
if (nireq == 0) {
cv::util::throw_error(
std::logic_error("Number of inference requests"
" must be greater than zero."));
}
m_desc.nireq = nireq;
return *this;
}
/** @brief Specifies mean values for preprocessing.
*
The function is used to set mean values for input layer preprocessing.
@param mean_values Float vector contains mean values
@return reference to this parameter structure.
*/
Params<Net>& cfgMean(std::vector<float> mean_values) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
.mean_values = std::move(mean_values);
return *this;
}
/** @overload
@param mean_map Map of pairs: name of corresponding input layer
and its mean values.
@return reference to this parameter structure.
*/
Params<Net>& cfgMean(detail::AttrMap<std::vector<float>> mean_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
.mean_values = std::move(mean_map);
return *this;
}
/** @brief Specifies scale values for preprocessing.
*
The function is used to set scale values for input layer preprocessing.
@param scale_values Float vector contains scale values
@return reference to this parameter structure.
*/
Params<Net>& cfgScale(std::vector<float> scale_values) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
.scale_values = std::move(scale_values);
return *this;
}
/** @overload
@param scale_map Map of pairs: name of corresponding input layer
and its mean values.
@return reference to this parameter structure.
*/
Params<Net>& cfgScale(detail::AttrMap<std::vector<float>> scale_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
.scale_values = std::move(scale_map);
return *this;
}
/** @brief Specifies resize interpolation algorithm.
*
The function is used to configure resize preprocessing for input layer.
@param interpolation Resize interpolation algorithm.
Supported algorithms: #INTER_NEAREST, #INTER_LINEAR, #INTER_CUBIC.
@return reference to this parameter structure.
*/
Params<Net>& cfgResize(int interpolation) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
.interpolation = std::move(interpolation);
return *this;
}
/** @overload
@param interpolation Map of pairs: name of corresponding input layer
and its resize algorithm.
@return reference to this parameter structure.
*/
Params<Net>& cfgResize(detail::AttrMap<int> interpolation) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
.interpolation = std::move(interpolation);
return *this;
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ov::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { m_desc }; }
// END(G-API's network parametrization API)
protected:
detail::ParamDesc m_desc;
};
/*
* @brief This structure provides functions for generic network type that
* fill inference parameters.
* @see struct Generic
*/
template<>
class Params<cv::gapi::Generic> {
public:
/** @brief Class constructor.
Constructs Params based on model information and specifies default values for other
inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit".
@param tag string tag of the network for which these parameters are intended.
@param model_path Path to a model.
@param bin_path Path to a data file.
For IR format (*.bin):
If path is empty, will try to read a bin file with the same name as xml.
If the bin file with the same name is not found, will load IR without weights.
For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used.
@param device target device to use.
*/
Params(const std::string &tag,
const std::string &model_path,
const std::string &bin_path,
const std::string &device)
: m_tag(tag),
m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}}
, device
, true /* is generic */
, 0u
, 0u) {
}
/** @overload
This constructor for pre-compiled networks. Model is imported from pre-compiled
blob.
@param tag string tag of the network for which these parameters are intended.
@param blob_path path to the compiled model (*.blob).
@param device target device to use.
*/
Params(const std::string &tag,
const std::string &blob_path,
const std::string &device)
: m_tag(tag),
m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}}
, device
, true /* is generic */
, 0u
, 0u) {
}
/** @see ov::Params::cfgPluginConfig. */
Params& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) {
m_desc.config = config;
return *this;
}
/** @see ov::Params::cfgInputTensorLayout. */
Params& cfgInputTensorLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
.input_tensor_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgInputTensorLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
.input_tensor_layout = std::move(layout_map);
return *this;
}
/** @see ov::Params::cfgInputModelLayout. */
Params& cfgInputModelLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
.input_model_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgInputModelLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
.input_model_layout = std::move(layout_map);
return *this;
}
/** @see ov::Params::cfgOutputTensorLayout. */
Params& cfgOutputTensorLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
.output_tensor_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgOutputTensorLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
.output_tensor_layout = std::move(layout_map);
return *this;
}
/** @see ov::Params::cfgOutputModelLayout. */
Params& cfgOutputModelLayout(std::string layout) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
.output_model_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgOutputModelLayout(detail::AttrMap<std::string> layout_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
.output_model_layout = std::move(layout_map);
return *this;
}
/** @see ov::Params::cfgOutputTensorPrecision. */
Params& cfgOutputTensorPrecision(int precision) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
.output_tensor_precision = precision;
return *this;
}
/** @overload */
Params&
cfgOutputTensorPrecision(detail::AttrMap<int> precision_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
.output_tensor_precision = std::move(precision_map);
return *this;
}
/** @see ov::Params::cfgReshape. */
Params& cfgReshape(std::vector<size_t> new_shape) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
.new_shapes = std::move(new_shape);
return *this;
}
/** @overload */
Params&
cfgReshape(detail::AttrMap<std::vector<size_t>> new_shape_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
.new_shapes = std::move(new_shape_map);
return *this;
}
/** @see ov::Params::cfgNumRequests. */
Params& cfgNumRequests(const size_t nireq) {
if (nireq == 0) {
cv::util::throw_error(
std::logic_error("Number of inference requests"
" must be greater than zero."));
}
m_desc.nireq = nireq;
return *this;
}
/** @see ov::Params::cfgMean. */
Params& cfgMean(std::vector<float> mean_values) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
.mean_values = std::move(mean_values);
return *this;
}
/** @overload */
Params& cfgMean(detail::AttrMap<std::vector<float>> mean_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
.mean_values = std::move(mean_map);
return *this;
}
/** @see ov::Params::cfgScale. */
Params& cfgScale(std::vector<float> scale_values) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
.scale_values = std::move(scale_values);
return *this;
}
/** @overload */
Params& cfgScale(detail::AttrMap<std::vector<float>> scale_map) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
.scale_values = std::move(scale_map);
return *this;
}
/** @see ov::Params::cfgResize. */
Params& cfgResize(int interpolation) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
.interpolation = std::move(interpolation);
return *this;
}
/** @overload */
Params& cfgResize(detail::AttrMap<int> interpolation) {
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
.interpolation = std::move(interpolation);
return *this;
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ov::backend(); }
std::string tag() const { return m_tag; }
cv::util::any params() const { return { m_desc }; }
// END(G-API's network parametrization API)
protected:
std::string m_tag;
detail::ParamDesc m_desc;
};
} // namespace ov
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_INFER_OV_HPP

@ -15,6 +15,7 @@ using gapi_GKernelPackage = cv::GKernelPackage;
using gapi_GNetPackage = cv::gapi::GNetPackage;
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
using gapi_onnx_PyParams = cv::gapi::onnx::PyParams;
using gapi_ov_PyParams = cv::gapi::ov::PyParams;
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
@ -22,6 +23,11 @@ using vector_GNetParam = std::vector<cv::gapi::GNetParam>;
using vector_GMat = std::vector<cv::GMat>;
using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity;
using GStreamerSource_OutputType = cv::gapi::wip::GStreamerSource::OutputType;
using map_string_and_int = std::map<std::string, int>;
using map_string_and_string = std::map<std::string, std::string>;
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>>;
// NB: Python wrapper generate T_U for T<U>
// This behavior is only observed for inputs

@ -80,5 +80,6 @@ namespace detail
{
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::onnx::PyParams params);
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ov::PyParams params);
} // namespace detail
} // namespace cv

@ -0,0 +1,238 @@
#!/usr/bin/env python
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
openvino_is_available = True
try:
from openvino.runtime import Core, Type, Layout, PartialShape
from openvino.preprocess import ResizeAlgorithm, PrePostProcessor
except ImportError:
openvino_is_available = False
def skip_if_openvino_not_available():
if not openvino_is_available:
raise unittest.SkipTest("OpenVINO isn't available from python.")
class AgeGenderOV:
def __init__(self, model_path, bin_path, device):
self.device = device
self.core = Core()
self.model = self.core.read_model(model_path, bin_path)
def reshape(self, new_shape):
self.model.reshape(new_shape)
def cfgPrePostProcessing(self, pp_callback):
ppp = PrePostProcessor(self.model)
pp_callback(ppp)
self.model = ppp.build()
def apply(self, in_data):
compiled_model = self.core.compile_model(self.model, self.device)
infer_request = compiled_model.create_infer_request()
results = infer_request.infer(in_data)
ov_age = results['age_conv3'].squeeze()
ov_gender = results['prob'].squeeze()
return ov_age, ov_gender
class AgeGenderGAPI:
tag = 'age-gender-net'
def __init__(self, model_path, bin_path, device):
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
# TODO: It'd be nice to pass dict instead.
# E.g cv.gapi.infer("net", {'data': g_in})
outputs = cv.gapi.infer(AgeGenderGAPI.tag, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
self.comp = cv.GComputation(cv.GIn(g_in), cv.GOut(age_g, gender_g))
self.pp = cv.gapi.ov.params(AgeGenderGAPI.tag, \
model_path, bin_path, device)
def apply(self, in_data):
compile_args = cv.gapi.compile_args(cv.gapi.networks(self.pp))
gapi_age, gapi_gender = self.comp.apply(cv.gin(in_data), compile_args)
gapi_gender = gapi_gender.squeeze()
gapi_age = gapi_age.squeeze()
return gapi_age, gapi_gender
class test_gapi_infer_ov(NewOpenCVTests):
def test_age_gender_infer_image(self):
skip_if_openvino_not_available()
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
# OpenVINO
def preproc(ppp):
ppp.input().model().set_layout(Layout("NCHW"))
ppp.input().tensor().set_element_type(Type.u8) \
.set_spatial_static_shape(img.shape[0], img.shape[1]) \
.set_layout(Layout("NHWC"))
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
ref = AgeGenderOV(model_path, bin_path, device_id)
ref.cfgPrePostProcessing(preproc)
ov_age, ov_gender = ref.apply(np.expand_dims(img, 0))
# OpenCV G-API (No preproc required)
comp = AgeGenderGAPI(model_path, bin_path, device_id)
gapi_age, gapi_gender = comp.apply(img)
# Check
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer_tensor(self):
skip_if_openvino_not_available()
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
# Prepare data manually
tensor = cv.resize(img, (62, 62)).astype(np.float32)
tensor = np.transpose(tensor, (2, 0, 1))
tensor = np.expand_dims(tensor, 0)
# OpenVINO (No preproce required)
ref = AgeGenderOV(model_path, bin_path, device_id)
ov_age, ov_gender = ref.apply(tensor)
# OpenCV G-API (No preproc required)
comp = AgeGenderGAPI(model_path, bin_path, device_id)
gapi_age, gapi_gender = comp.apply(tensor)
# Check
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer_batch(self):
skip_if_openvino_not_available()
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
img_path1 = self.find_file('cv/face/david1.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img_path2 = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img1 = cv.imread(img_path1)
img2 = cv.imread(img_path2)
# img1 and img2 have the same size
batch_img = np.array([img1, img2])
# OpenVINO
def preproc(ppp):
ppp.input().model().set_layout(Layout("NCHW"))
ppp.input().tensor().set_element_type(Type.u8) \
.set_spatial_static_shape(img1.shape[0], img2.shape[1]) \
.set_layout(Layout("NHWC"))
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
ref = AgeGenderOV(model_path, bin_path, device_id)
ref.reshape(PartialShape([2, 3, 62, 62]))
ref.cfgPrePostProcessing(preproc)
ov_age, ov_gender = ref.apply(batch_img)
# OpenCV G-API
comp = AgeGenderGAPI(model_path, bin_path, device_id)
comp.pp.cfgReshape([2, 3, 62, 62]) \
.cfgInputModelLayout("NCHW") \
.cfgInputTensorLayout("NHWC") \
.cfgResize(cv.INTER_LINEAR)
gapi_age, gapi_gender = comp.apply(batch_img)
# Check
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer_planar(self):
skip_if_openvino_not_available()
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
planar_img = np.transpose(img, (2, 0, 1))
planar_img = np.expand_dims(planar_img, 0)
# OpenVINO
def preproc(ppp):
ppp.input().tensor().set_element_type(Type.u8) \
.set_spatial_static_shape(img.shape[0], img.shape[1])
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
ref = AgeGenderOV(model_path, bin_path, device_id)
ref.cfgPrePostProcessing(preproc)
ov_age, ov_gender = ref.apply(planar_img)
# OpenCV G-API
comp = AgeGenderGAPI(model_path, bin_path, device_id)
comp.pp.cfgResize(cv.INTER_LINEAR)
gapi_age, gapi_gender = comp.apply(planar_img)
# Check
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':
NewOpenCVTests.bootstrap()

@ -153,10 +153,18 @@ std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc)
break;
}
os << "C" << desc.chan;
if (desc.planar) os << "p";
os << " ";
os << desc.size.width << "x" << desc.size.height;
if (desc.isND()) {
os << " [";
for (size_t i = 0; i < desc.dims.size() - 1; ++i) {
os << desc.dims[i] << "x";
}
os << desc.dims.back() << "]";
} else {
os << "C" << desc.chan;
if (desc.planar) os << "p";
os << " ";
os << desc.size.width << "x" << desc.size.height;
}
return os;
}

@ -227,6 +227,12 @@ inline void convertInt64ToInt32(const int64_t* src, int* dst, size_t size)
[](int64_t el) { return static_cast<int>(el); });
}
inline void convertInt32ToInt64(const int* src, int64_t* dst, size_t size)
{
std::transform(src, src + size, dst,
[](int el) { return static_cast<int64_t>(el); });
}
}} // cv::gimpl
#endif // OPENCV_GAPI_GBACKEND_HPP

@ -0,0 +1,168 @@
#include <opencv2/gapi/infer/bindings_ov.hpp>
cv::gapi::ov::PyParams::PyParams(const std::string &tag,
const std::string &model_path,
const std::string &bin_path,
const std::string &device)
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, model_path, bin_path, device)) {
}
cv::gapi::ov::PyParams::PyParams(const std::string &tag,
const std::string &blob_path,
const std::string &device)
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, blob_path, device)) {
}
cv::gapi::GBackend cv::gapi::ov::PyParams::backend() const {
return m_priv->backend();
}
std::string cv::gapi::ov::PyParams::tag() const {
return m_priv->tag();
}
cv::util::any cv::gapi::ov::PyParams::params() const {
return m_priv->params();
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgPluginConfig(
const std::map<std::string, std::string> &config) {
m_priv->cfgPluginConfig(config);
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgInputTensorLayout(std::string tensor_layout) {
m_priv->cfgInputTensorLayout(std::move(tensor_layout));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgInputTensorLayout(
std::map<std::string, std::string> layout_map) {
m_priv->cfgInputTensorLayout(std::move(layout_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgInputModelLayout(std::string tensor_layout) {
m_priv->cfgInputModelLayout(std::move(tensor_layout));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgInputModelLayout(
std::map<std::string, std::string> layout_map) {
m_priv->cfgInputModelLayout(std::move(layout_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputTensorLayout(std::string tensor_layout) {
m_priv->cfgOutputTensorLayout(std::move(tensor_layout));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputTensorLayout(
std::map<std::string, std::string> layout_map) {
m_priv->cfgOutputTensorLayout(std::move(layout_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputModelLayout(std::string tensor_layout) {
m_priv->cfgOutputModelLayout(std::move(tensor_layout));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputModelLayout(
std::map<std::string, std::string> layout_map) {
m_priv->cfgOutputModelLayout(std::move(layout_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputTensorPrecision(int precision) {
m_priv->cfgOutputTensorPrecision(precision);
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgOutputTensorPrecision(
std::map<std::string, int> precision_map) {
m_priv->cfgOutputTensorPrecision(precision_map);
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgReshape(std::vector<size_t> new_shape) {
m_priv->cfgReshape(std::move(new_shape));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgReshape(
std::map<std::string, std::vector<size_t>> new_shape_map) {
m_priv->cfgReshape(std::move(new_shape_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgNumRequests(const size_t nireq) {
m_priv->cfgNumRequests(nireq);
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgMean(std::vector<float> mean_values) {
m_priv->cfgMean(std::move(mean_values));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgMean(
std::map<std::string, std::vector<float>> mean_map) {
m_priv->cfgMean(std::move(mean_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgScale(std::vector<float> scale_values) {
m_priv->cfgScale(std::move(scale_values));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgScale(
std::map<std::string, std::vector<float>> scale_map) {
m_priv->cfgScale(std::move(scale_map));
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgResize(int interpolation) {
m_priv->cfgResize(interpolation);
return *this;
}
cv::gapi::ov::PyParams&
cv::gapi::ov::PyParams::cfgResize(std::map<std::string, int> interpolation) {
m_priv->cfgResize(std::move(interpolation));
return *this;
}
cv::gapi::ov::PyParams cv::gapi::ov::params(const std::string &tag,
const std::string &model_path,
const std::string &weights,
const std::string &device) {
return {tag, model_path, weights, device};
}
cv::gapi::ov::PyParams cv::gapi::ov::params(const std::string &tag,
const std::string &blob_path,
const std::string &device) {
return {tag, blob_path, device};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,66 @@
// 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_GOVBACKEND_HPP
#define OPENCV_GAPI_GOVBACKEND_HPP
// Include anyway - cv::gapi::ov::backend() still needs to be defined
#include "opencv2/gapi/infer/ov.hpp"
#ifdef HAVE_INF_ENGINE
#include <openvino/openvino.hpp>
#include "backends/common/gbackend.hpp"
namespace cv {
namespace gimpl {
namespace ov {
struct OVCompiled {
::ov::CompiledModel compiled_model;
};
class RequestPool;
class GOVExecutable final: public GIslandExecutable
{
const ade::Graph &m_g;
GModel::ConstGraph m_gm;
// The only executable stuff in this graph
// (assuming it is always single-op)
ade::NodeHandle this_nh;
OVCompiled compiled;
// List of all resources in graph (both internal and external)
std::vector<ade::NodeHandle> m_dataNodes;
// To manage multiple async requests
std::unique_ptr<RequestPool> m_reqPool;
public:
GOVExecutable(const ade::Graph &graph,
const std::vector<ade::NodeHandle> &nodes);
virtual inline bool canReshape() const override { return false; }
virtual inline void reshape(ade::Graph&, const GCompileArgs&) override {
GAPI_Error("InternalError"); // Not implemented yet
}
virtual void run(std::vector<InObj> &&,
std::vector<OutObj> &&) override {
GAPI_Error("Not implemented");
}
virtual void run(GIslandExecutable::IInput &in,
GIslandExecutable::IOutput &out) override;
};
}}}
#endif // HAVE_INF_ENGINE
#endif // OPENCV_GAPI_GOVBACKEND_HPP

@ -0,0 +1,35 @@
// 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_INFER_OV_UTIL_HPP
#define OPENCV_GAPI_INFER_OV_UTIL_HPP
#ifdef HAVE_INF_ENGINE
// NOTE: This file is not included by default in infer/ov.hpp
// and won't be. infer/ov.hpp doesn't depend on OV headers itself.
// This file does -- so needs to be included separately by those who care.
#include <openvino/openvino.hpp>
#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
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);
}}}}
#endif // HAVE_INF_ENGINE
#endif // OPENCV_GAPI_INFER_OV_UTIL_HPP

@ -0,0 +1,540 @@
// 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
#ifdef HAVE_INF_ENGINE
#include "../test_precomp.hpp"
#include "backends/ov/util.hpp"
#include <opencv2/gapi/infer/ov.hpp>
#include <openvino/openvino.hpp>
namespace opencv_test
{
namespace {
// FIXME: taken from DNN module
void initDLDTDataPath()
{
#ifndef WINRT
static bool initialized = false;
if (!initialized)
{
const char* omzDataPath = getenv("OPENCV_OPEN_MODEL_ZOO_DATA_PATH");
if (omzDataPath)
cvtest::addDataSearchPath(omzDataPath);
const char* dnnDataPath = getenv("OPENCV_DNN_TEST_DATA_PATH");
if (dnnDataPath) {
// Add the dnnDataPath itself - G-API is using some images there directly
cvtest::addDataSearchPath(dnnDataPath);
cvtest::addDataSearchPath(dnnDataPath + std::string("/omz_intel_models"));
}
initialized = true;
}
#endif // WINRT
}
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 /*= ""*/,
double l1 = 0.00001, double lInf = 0.0001) {
double normL1 = cvtest::norm(ref, test, cv::NORM_L1) / ref.getMat().total();
EXPECT_LE(normL1, l1) << comment;
double normInf = cvtest::norm(ref, test, cv::NORM_INF);
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 {
static constexpr const char* tag = "age-gender-generic";
using Params = cv::gapi::ov::Params<cv::gapi::Generic>;
static Params params(const std::string &xml,
const std::string &bin,
const std::string &device) {
return {tag, xml, bin, device};
}
static Params params(const std::string &blob_path,
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 {
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>;
static Params params(const std::string &xml_path,
const std::string &bin_path,
const std::string &device) {
return Params {
xml_path, bin_path, device
}.cfgOutputLayers({ "age_conv3", "prob" });
}
static cv::GComputation create() {
cv::GMat in;
cv::GMat age, gender;
std::tie(age, gender) = cv::gapi::infer<AgeGender>(in);
return cv::GComputation{cv::GIn(in), cv::GOut(age, gender)};
}
};
class AGNetOVCompiled {
public:
AGNetOVCompiled(ov::CompiledModel &&compiled_model)
: m_compiled_model(std::move(compiled_model)) {
}
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);
infer_request.infer();
auto age_tensor = 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);
auto gender_tensor = 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);
}
void export_model(const std::string &outpath) {
std::ofstream file{outpath, std::ios::out | std::ios::binary};
GAPI_Assert(file.is_open());
m_compiled_model.export_model(file);
}
private:
ov::CompiledModel m_compiled_model;
};
struct ImageInputPreproc {
void operator()(ov::preprocess::PrePostProcessor &ppp) {
ppp.input().tensor().set_layout(ov::Layout("NHWC"))
.set_element_type(ov::element::u8)
.set_shape({1, size.height, size.width, 3});
ppp.input().model().set_layout(ov::Layout("NCHW"));
ppp.input().preprocess().resize(::ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
}
cv::Size size;
};
class AGNetOVComp {
public:
AGNetOVComp(const std::string &xml_path,
const std::string &bin_path,
const std::string &device)
: m_device(device) {
m_model = getCore().read_model(xml_path, bin_path);
}
using PrePostProcessF = std::function<void(ov::preprocess::PrePostProcessor&)>;
void cfgPrePostProcessing(PrePostProcessF f) {
ov::preprocess::PrePostProcessor ppp(m_model);
f(ppp);
m_model = ppp.build();
}
AGNetOVCompiled compile() {
auto compiled_model = getCore().compile_model(m_model, m_device);
return {std::move(compiled_model)};
}
void apply(const cv::Mat &in_mat,
cv::Mat &age_mat,
cv::Mat &gender_mat) {
compile()(in_mat, age_mat, gender_mat);
}
private:
std::string m_device;
std::shared_ptr<ov::Model> m_model;
};
} // 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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetTypedComp::create();
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetTypedComp::create();
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
auto cc_ref = ref.compile();
// NB: Output blob will contain preprocessing inside.
cc_ref.export_model(blob_path);
cc_ref(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(blob_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
auto cc_ref = ref.compile();
cc_ref.export_model(blob_path);
cc_ref(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(blob_path, device);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp){
ppp.output(0).tensor().set_element_type(ov::element::f16);
ppp.output(1).tensor().set_element_type(ov::element::f16);
});
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
pp.cfgOutputTensorPrecision(CV_16F);
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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;
// OpenVINO
const std::string fp16_output_name = "prob";
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([&](ov::preprocess::PrePostProcessor &ppp){
ppp.output(fp16_output_name).tensor().set_element_type(ov::element::f16);
});
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
pp.cfgOutputTensorPrecision({{fp16_output_name, CV_16F}});
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
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";
// OpenVINO (Just for blob compilation)
AGNetOVComp ref(xml_path, bin_path, device);
auto cc_ref = ref.compile();
cc_ref.export_model(blob_path);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(blob_path, device);
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";
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
pp.cfgPluginConfig({{"some_key", "some_value"}});
EXPECT_ANY_THROW(comp.compile(cv::GMatDesc{CV_8U,3,cv::Size{320, 240}},
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";
// OpenVINO (Just for blob compilation)
AGNetOVComp ref(xml_path, bin_path, device);
auto cc_ref = ref.compile();
cc_ref.export_model(blob_path);
// G-API
auto comp = AGNetGenComp::create();
auto pp = AGNetGenComp::params(blob_path, device);
pp.cfgPluginConfig({{"some_key", "some_value"}});
EXPECT_ANY_THROW(comp.compile(cv::GMatDesc{CV_8U,3,cv::Size{320, 240}},
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;
auto comp = AGNetTypedComp::create();
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
pp.cfgInputTensorLayout("NCHW");
EXPECT_ANY_THROW(comp.compile(cv::descr_of(in_mat),
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;
// OpenVINO
AGNetOVComp ref(xml_path, bin_path, device);
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp) {
auto& input = ppp.input();
input.tensor().set_spatial_static_shape(240, 320)
.set_layout("NHWC");
input.preprocess().resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
});
ref.apply(in_mat, ov_age, ov_gender);
// G-API
auto comp = AGNetTypedComp::create();
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
pp.cfgResize(cv::INTER_LINEAR)
.cfgInputTensorLayout("NHWC");
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Assert
normAssert(ov_age, gapi_age, "Test age output" );
normAssert(ov_gender, gapi_gender, "Test gender output");
}
} // namespace opencv_test
#endif // HAVE_INF_ENGINE

@ -6,6 +6,8 @@
#include "cv2_numpy.hpp"
#include <vector>
#include <string>
#include <unordered_map>
#include <map>
#include <type_traits> // std::enable_if
extern PyTypeObject* pyopencv_Mat_TypePtr;
@ -263,6 +265,38 @@ PyObject* pyopencv_from(const std::vector<Tp>& value)
return pyopencvVecConverter<Tp>::from(value);
}
template<typename K, typename V>
bool pyopencv_to(PyObject *obj, std::map<K,V> &map, const ArgInfo& info)
{
PyObject* py_key = nullptr;
PyObject* py_value = nullptr;
Py_ssize_t pos = 0;
if (!PyDict_Check(obj)) {
failmsg("Can't parse '%s'. Input argument isn't dict or"
" an instance of subtype of the dict type", info.name);
return false;
}
while(PyDict_Next(obj, &pos, &py_key, &py_value))
{
K cpp_key;
if (!pyopencv_to(py_key, cpp_key, ArgInfo("key", false))) {
failmsg("Can't parse dict key. Key on position %lu has a wrong type", pos);
return false;
}
V cpp_value;
if (!pyopencv_to(py_value, cpp_value, ArgInfo("value", false))) {
failmsg("Can't parse dict value. Value on position %lu has a wrong type", pos);
return false;
}
map.emplace(cpp_key, cpp_value);
}
return true;
}
template <typename Tp>
static bool pyopencv_to_generic_vec(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
{

@ -190,6 +190,14 @@ _PREDEFINED_TYPES = (
PrimitiveTypeNode.float_(),
PrimitiveTypeNode.str_())
), export_name="SearchParams"),
AliasTypeNode.dict_("map_string_and_string", PrimitiveTypeNode.str_("map_string_and_string::key"),
PrimitiveTypeNode.str_("map_string_and_string::key::value")),
AliasTypeNode.dict_("map_string_and_int", PrimitiveTypeNode.str_("map_string_and_int::key"),
PrimitiveTypeNode.int_("map_string_and_int::key::value")),
AliasTypeNode.dict_("map_string_and_vector_size_t", PrimitiveTypeNode.str_("map_string_and_vector_size_t::key"),
SequenceTypeNode("map_string_and_vector_size_t::key::value", PrimitiveTypeNode.int_("size_t"))),
AliasTypeNode.dict_("map_string_and_vector_float", PrimitiveTypeNode.str_("map_string_and_vector_float::key"),
SequenceTypeNode("map_string_and_vector_float::key::value", PrimitiveTypeNode.float_())),
)
PREDEFINED_TYPES = dict(zip((t.ctype_name for t in _PREDEFINED_TYPES), _PREDEFINED_TYPES))

Loading…
Cancel
Save