mirror of https://github.com/opencv/opencv.git
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 CMakepull/23772/head
parent
36d6ca529d
commit
5330112f05
15 changed files with 2933 additions and 4 deletions
@ -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
|
@ -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() |
@ -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
|
Loading…
Reference in new issue