mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00

Fix typos #26038 Fix typos ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
1654 lines
66 KiB
C++
1654 lines
66 KiB
C++
// This file is part of OpenCV project.
|
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
// of this distribution and at http://opencv.org/license.html.
|
|
//
|
|
// Copyright (C) 2023 Intel Corporation
|
|
|
|
#include "precomp.hpp"
|
|
|
|
// needs to be included regardless if IE is present or not
|
|
// (cv::gapi::ov::backend() is still there and is defined always)
|
|
#include "backends/ov/govbackend.hpp"
|
|
|
|
#if defined HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
|
|
|
#include "backends/ov/util.hpp"
|
|
#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
|
|
#include "logger.hpp"
|
|
|
|
#include <opencv2/gapi/gcommon.hpp>
|
|
#include <opencv2/gapi/infer/ov.hpp>
|
|
#include <opencv2/core/utils/configuration.private.hpp> // getConfigurationParameterBool
|
|
|
|
#if defined(HAVE_TBB)
|
|
# include <tbb/concurrent_queue.h> // FIXME: drop it from here!
|
|
template<typename T> using QueueClass = tbb::concurrent_bounded_queue<T>;
|
|
#else
|
|
# include "executor/conc_queue.hpp"
|
|
template<typename T> using QueueClass = cv::gapi::own::concurrent_bounded_queue<T>;
|
|
#endif // TBB
|
|
|
|
#include "utils/itt.hpp"
|
|
|
|
#include <ade/util/zip_range.hpp>
|
|
|
|
#include <openvino/openvino.hpp>
|
|
|
|
#include <fstream>
|
|
|
|
using ParamDesc = cv::gapi::ov::detail::ParamDesc;
|
|
|
|
// NB: Some of OV plugins fail during ov::Core destroying in specific cases.
|
|
// Solution is allocate ov::Core in heap and doesn't destroy it, which cause
|
|
// leak, but fixes tests on CI. This behaviour is configurable by using
|
|
// OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND=0
|
|
static ov::Core create_OV_Core_pointer() {
|
|
// NB: 'delete' is never called
|
|
static ov::Core* core = new ov::Core();
|
|
return *core;
|
|
}
|
|
|
|
static ov::Core create_OV_Core_instance() {
|
|
static ov::Core core;
|
|
return core;
|
|
}
|
|
|
|
ov::Core cv::gapi::ov::wrap::getCore() {
|
|
// NB: to make happy memory leak tools use:
|
|
// - OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND=0
|
|
static bool param_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND =
|
|
utils::getConfigurationParameterBool(
|
|
"OPENCV_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND",
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
true
|
|
#else
|
|
false
|
|
#endif
|
|
);
|
|
return param_GAPI_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND
|
|
? create_OV_Core_pointer() : create_OV_Core_instance();
|
|
}
|
|
|
|
static ov::AnyMap toOV(const ParamDesc::PluginConfigT &config) {
|
|
return {config.begin(), config.end()};
|
|
}
|
|
|
|
static std::map<std::string, ::ov::PartialShape>
|
|
toOV(const std::map<std::string, std::vector<size_t>> &shapes) {
|
|
std::map<std::string, ::ov::PartialShape> ov_shapes;
|
|
for (const auto &it : shapes) {
|
|
ov_shapes.emplace(it.first, ::ov::Shape(it.second));
|
|
}
|
|
return ov_shapes;
|
|
}
|
|
|
|
static ov::element::Type toOV(int depth) {
|
|
switch (depth) {
|
|
case CV_8U: return ov::element::u8;
|
|
case CV_32S: return ov::element::i32;
|
|
case CV_32F: return ov::element::f32;
|
|
case CV_16F: return ov::element::f16;
|
|
default: GAPI_Error("OV Backend: Unsupported data type");
|
|
}
|
|
return ov::element::undefined;
|
|
}
|
|
|
|
static ov::preprocess::ResizeAlgorithm toOVInterp(int interpolation) {
|
|
namespace pp = ov::preprocess;
|
|
switch (interpolation) {
|
|
case cv::INTER_LINEAR: return pp::ResizeAlgorithm::RESIZE_LINEAR;
|
|
case cv::INTER_NEAREST: return pp::ResizeAlgorithm::RESIZE_NEAREST;
|
|
case cv::INTER_CUBIC: return pp::ResizeAlgorithm::RESIZE_CUBIC;
|
|
default: GAPI_Error("OV Backend: Unsupported resize algorithm");
|
|
}
|
|
// Unreachable code
|
|
GAPI_Assert(false);
|
|
}
|
|
|
|
static std::vector<int> toCV(const ov::Shape &shape) {
|
|
std::vector<int> result;
|
|
result.reserve(shape.size());
|
|
for (auto dim : shape) {
|
|
result.push_back(ade::util::checked_cast<int>(dim));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int toCV(const ov::element::Type &type) {
|
|
switch (type) {
|
|
case ov::element::u8: return CV_8U;
|
|
case ov::element::f32: return CV_32F;
|
|
case ov::element::i32: return CV_32S;
|
|
case ov::element::i64: return CV_32S;
|
|
case ov::element::f16: return CV_16F;
|
|
default: GAPI_Error("OV Backend: Unsupported data type");
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void copyFromOV(const ov::Tensor &tensor, cv::Mat &mat) {
|
|
const auto total = mat.total() * mat.channels();
|
|
if (toCV(tensor.get_element_type()) != mat.depth() ||
|
|
tensor.get_size() != total) {
|
|
std::stringstream ss;
|
|
ss << "Failed to copy data from ov::Tensor to cv::Mat."
|
|
<< " Data type or number of elements mismatch."
|
|
<< " cv::Mat: " << cv::descr_of(mat) << " and"
|
|
<< " ov::Tensor: " << tensor.get_element_type() << " "
|
|
<< tensor.get_shape();
|
|
cv::util::throw_error(std::logic_error(ss.str()));
|
|
}
|
|
|
|
if (tensor.get_element_type() == ov::element::i64) {
|
|
GAPI_LOG_WARNING(NULL, "INT64 isn't supported for cv::Mat. Conversion to INT32 is used.");
|
|
cv::gimpl::convertInt64ToInt32(tensor.data<int64_t>(),
|
|
mat.ptr<int>(),
|
|
total);
|
|
} else {
|
|
std::copy_n(reinterpret_cast<uint8_t*>(tensor.data()),
|
|
tensor.get_byte_size(),
|
|
mat.ptr<uint8_t>());
|
|
}
|
|
}
|
|
|
|
static cv::Mat wrapOV(const cv::MediaFrame::View& view,
|
|
const cv::GFrameDesc& desc) {
|
|
cv::Mat out;
|
|
switch (desc.fmt) {
|
|
case cv::MediaFormat::BGR: {
|
|
out = cv::Mat(desc.size, CV_8UC3, view.ptr[0], view.stride[0]);
|
|
return out;
|
|
}
|
|
case cv::MediaFormat::NV12: {
|
|
auto y_plane = cv::Mat(desc.size, CV_8UC1, view.ptr[0], view.stride[0]);
|
|
auto uv_plane = cv::Mat(desc.size / 2, CV_8UC2, view.ptr[1], view.stride[1]);
|
|
cvtColorTwoPlane(y_plane, uv_plane, out, cv::COLOR_YUV2BGR_NV12);
|
|
return out;
|
|
}
|
|
case cv::MediaFormat::GRAY: {
|
|
out = cv::Mat(desc.size, CV_8UC1, view.ptr[0], view.stride[0]);
|
|
return out;
|
|
}
|
|
default:
|
|
GAPI_Error("OV Backend: Unsupported media format");
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static void copyToOV(const cv::Mat &mat, ov::Tensor &tensor) {
|
|
// TODO: Ideally there should be check that mat and tensor
|
|
// dimensions are compatible.
|
|
const auto total = mat.total() * mat.channels();
|
|
if (toCV(tensor.get_element_type()) != mat.depth() ||
|
|
tensor.get_size() != total) {
|
|
std::stringstream ss;
|
|
ss << "Failed to copy data from cv::Mat to ov::Tensor."
|
|
<< " Data type or number of elements mismatch."
|
|
<< " ov::Tensor: " << tensor.get_element_type() << " "
|
|
<< tensor.get_shape() << " and"
|
|
<< " cv::Mat: " << cv::descr_of(mat);
|
|
cv::util::throw_error(std::logic_error(ss.str()));
|
|
}
|
|
|
|
if (tensor.get_element_type() == ov::element::i64) {
|
|
cv::gimpl::convertInt32ToInt64(mat.ptr<int>(),
|
|
tensor.data<int64_t>(),
|
|
total);
|
|
} else {
|
|
std::copy_n(mat.ptr<uint8_t>(),
|
|
tensor.get_byte_size(),
|
|
reinterpret_cast<uint8_t*>(tensor.data()));
|
|
}
|
|
}
|
|
|
|
static void copyToOV(const cv::MediaFrame &frame, ov::Tensor &tensor) {
|
|
const auto view = cv::MediaFrame::View(frame.access(cv::MediaFrame::Access::R));
|
|
auto matFromFrame = wrapOV(view, frame.desc());
|
|
copyToOV(matFromFrame, tensor);
|
|
}
|
|
|
|
std::vector<int> cv::gapi::ov::util::to_ocv(const ::ov::Shape &shape) {
|
|
return toCV(shape);
|
|
}
|
|
|
|
int cv::gapi::ov::util::to_ocv(const ::ov::element::Type &type) {
|
|
return toCV(type);
|
|
}
|
|
|
|
void cv::gapi::ov::util::to_ov(const cv::Mat &mat, ::ov::Tensor &tensor) {
|
|
copyToOV(mat, tensor);
|
|
}
|
|
|
|
void cv::gapi::ov::util::to_ocv(const ::ov::Tensor &tensor, cv::Mat &mat) {
|
|
copyFromOV(tensor, mat);
|
|
}
|
|
|
|
struct OVUnit {
|
|
static const char *name() { return "OVUnit"; }
|
|
|
|
explicit OVUnit(const ParamDesc &pd)
|
|
: params(pd) {
|
|
|
|
// FIXME: Can this logic be encapsulated to prevent checking every time?
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(params.kind)) {
|
|
const auto desc = cv::util::get<ParamDesc::Model>(params.kind);
|
|
model = cv::gapi::ov::wrap::getCore()
|
|
.read_model(desc.model_path, desc.bin_path);
|
|
GAPI_Assert(model);
|
|
|
|
if (params.num_in == 1u && params.input_names.empty()) {
|
|
params.input_names = { model->inputs().begin()->get_any_name() };
|
|
}
|
|
if (params.num_out == 1u && params.output_names.empty()) {
|
|
params.output_names = { model->outputs().begin()->get_any_name() };
|
|
}
|
|
|
|
} else {
|
|
GAPI_Assert(cv::util::holds_alternative<ParamDesc::CompiledModel>(params.kind));
|
|
std::ifstream file(cv::util::get<ParamDesc::CompiledModel>(params.kind).blob_path,
|
|
std::ios_base::in | std::ios_base::binary);
|
|
GAPI_Assert(file.is_open());
|
|
compiled_model = cv::gapi::ov::wrap::getCore()
|
|
.import_model(file, params.device, toOV(params.config));
|
|
|
|
if (params.num_in == 1u && params.input_names.empty()) {
|
|
params.input_names = { compiled_model.inputs().begin()->get_any_name() };
|
|
}
|
|
if (params.num_out == 1u && params.output_names.empty()) {
|
|
params.output_names = { compiled_model.outputs().begin()->get_any_name() };
|
|
}
|
|
}
|
|
};
|
|
|
|
cv::gimpl::ov::OVCompiled compile() {
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(params.kind)) {
|
|
compiled_model = cv::gapi::ov::wrap::getCore()
|
|
.compile_model(model, params.device, toOV(params.config));
|
|
}
|
|
return {compiled_model};
|
|
}
|
|
|
|
cv::gapi::ov::detail::ParamDesc params;
|
|
std::shared_ptr<ov::Model> model;
|
|
ov::CompiledModel compiled_model;
|
|
};
|
|
|
|
class OVCallContext
|
|
{
|
|
public:
|
|
OVCallContext(const OVUnit & unit,
|
|
cv::gimpl::GIslandExecutable::IOutput & output,
|
|
const cv::GArgs & args,
|
|
const std::vector<cv::gimpl::RcDesc> & outs,
|
|
cv::GRunArg::Meta && meta,
|
|
std::vector<cv::gimpl::GIslandExecutable::InObj> && input_objs,
|
|
std::vector<cv::gimpl::GIslandExecutable::OutObj> && output_objs,
|
|
const cv::gimpl::ov::Options & options);
|
|
|
|
const cv::GArgs& inArgs() const;
|
|
|
|
// Generic accessor API
|
|
template<typename T>
|
|
const T& inArg(std::size_t input) const {
|
|
return m_args.at(input).get<T>();
|
|
}
|
|
|
|
template<typename T>
|
|
std::vector<T>& outVecR(std::size_t output) {
|
|
return outVecRef(output).wref<T>();
|
|
}
|
|
|
|
// Syntax sugar
|
|
cv::GShape inShape (std::size_t input) const;
|
|
const cv::Mat& inMat (std::size_t input) const;
|
|
const cv::MediaFrame& inFrame (std::size_t input) const;
|
|
|
|
cv::GRunArgP output (std::size_t idx);
|
|
cv::Mat& outMatR(std::size_t idx);
|
|
|
|
const OVUnit &uu;
|
|
cv::gimpl::GIslandExecutable::IOutput &out;
|
|
|
|
// To store exception appeared in callback.
|
|
std::exception_ptr eptr;
|
|
|
|
const cv::GRunArg::Meta& getMeta() { return m_meta; };
|
|
|
|
const cv::gimpl::ov::Options& getOptions() const { return m_options; };
|
|
|
|
private:
|
|
cv::detail::VectorRef& outVecRef(std::size_t idx);
|
|
|
|
cv::GArg packArg(const cv::GArg &arg);
|
|
|
|
// To propagate accumulated meta from all inputs to output.
|
|
cv::GRunArg::Meta m_meta;
|
|
|
|
// To store input/output data from frames
|
|
std::vector<cv::gimpl::GIslandExecutable::InObj> m_input_objs;
|
|
std::vector<cv::gimpl::GIslandExecutable::OutObj> m_output_objs;
|
|
|
|
// To simplify access to cv::Mat inside cv::RMat
|
|
cv::gimpl::Mag m_res;
|
|
|
|
std::unordered_map<std::size_t, cv::GRunArgP> m_results;
|
|
|
|
// Input parameters passed to an inference operation.
|
|
cv::GArgs m_args;
|
|
cv::GShapes m_in_shapes;
|
|
|
|
cv::gimpl::ov::Options m_options;
|
|
};
|
|
|
|
OVCallContext::OVCallContext(const OVUnit & unit,
|
|
cv::gimpl::GIslandExecutable::IOutput & output,
|
|
const cv::GArgs & args,
|
|
const std::vector<cv::gimpl::RcDesc> & outs,
|
|
cv::GRunArg::Meta && meta,
|
|
std::vector<cv::gimpl::GIslandExecutable::InObj> && input_objs,
|
|
std::vector<cv::gimpl::GIslandExecutable::OutObj> && output_objs,
|
|
const cv::gimpl::ov::Options & options)
|
|
: uu(unit), out(output), m_meta(std::move(meta)),
|
|
m_input_objs(std::move(input_objs)), m_output_objs(std::move(output_objs)),
|
|
m_options(options)
|
|
{
|
|
for (auto& it : m_input_objs) cv::gimpl::magazine::bindInArg (m_res, it.first, it.second);
|
|
for (auto& it : m_output_objs) cv::gimpl::magazine::bindOutArg(m_res, it.first, it.second);
|
|
|
|
m_args.reserve(args.size());
|
|
using namespace std::placeholders;
|
|
ade::util::transform(args,
|
|
std::back_inserter(m_args),
|
|
std::bind(&OVCallContext::packArg, this, _1));
|
|
|
|
ade::util::transform(args, std::back_inserter(m_in_shapes),
|
|
[](const cv::GArg& arg) {
|
|
return arg.get<cv::gimpl::RcDesc>().shape;
|
|
});
|
|
|
|
for (const auto out_it : ade::util::indexed(outs)) {
|
|
// FIXME: Can the same GArg type resolution mechanism be reused here?
|
|
const auto port = ade::util::index(out_it);
|
|
const auto desc = ade::util::value(out_it);
|
|
m_results[port] = cv::gimpl::magazine::getObjPtr(m_res, desc);
|
|
}
|
|
}
|
|
|
|
const cv::GArgs& OVCallContext::inArgs() const {
|
|
return m_args;
|
|
}
|
|
|
|
cv::GShape OVCallContext::inShape(std::size_t i) const {
|
|
return m_in_shapes[i];
|
|
}
|
|
|
|
const cv::Mat& OVCallContext::inMat(std::size_t input) const {
|
|
return inArg<cv::Mat>(input);
|
|
}
|
|
|
|
const cv::MediaFrame& OVCallContext::inFrame(std::size_t input) const {
|
|
return inArg<cv::MediaFrame>(input);
|
|
}
|
|
|
|
cv::Mat& OVCallContext::outMatR(std::size_t idx) {
|
|
return *cv::util::get<cv::Mat*>(m_results.at(idx));
|
|
}
|
|
|
|
cv::GRunArgP OVCallContext::output(std::size_t idx) {
|
|
return m_output_objs[idx].second;
|
|
};
|
|
|
|
cv::detail::VectorRef& OVCallContext::outVecRef(std::size_t idx) {
|
|
return cv::util::get<cv::detail::VectorRef>(m_results.at(idx));
|
|
}
|
|
|
|
cv::GArg OVCallContext::packArg(const cv::GArg &arg) {
|
|
// No API placeholders allowed at this point
|
|
// FIXME: this check has to be done somewhere in compilation stage.
|
|
GAPI_Assert( arg.kind != cv::detail::ArgKind::GMAT
|
|
&& arg.kind != cv::detail::ArgKind::GSCALAR
|
|
&& arg.kind != cv::detail::ArgKind::GARRAY);
|
|
|
|
if (arg.kind != cv::detail::ArgKind::GOBJREF) {
|
|
cv::util::throw_error(std::logic_error("Inference supports G-types ONLY!"));
|
|
}
|
|
GAPI_Assert(arg.kind == cv::detail::ArgKind::GOBJREF);
|
|
|
|
// Wrap associated CPU object (either host or an internal one)
|
|
// FIXME: object can be moved out!!! GExecutor faced that.
|
|
const cv::gimpl::RcDesc &ref = arg.get<cv::gimpl::RcDesc>();
|
|
switch (ref.shape)
|
|
{
|
|
case cv::GShape::GMAT: return cv::GArg(m_res.slot<cv::Mat>()[ref.id]);
|
|
|
|
// Note: .at() is intentional for GArray as object MUST be already there
|
|
// (and constructed by either bindIn/Out or resetInternal)
|
|
case cv::GShape::GARRAY: return cv::GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
|
|
|
|
// Note: .at() is intentional for GOpaque as object MUST be already there
|
|
// (and constructed by either bindIn/Out or resetInternal)
|
|
case cv::GShape::GOPAQUE: return cv::GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id));
|
|
|
|
case cv::GShape::GFRAME: return cv::GArg(m_res.slot<cv::MediaFrame>()[ref.id]);
|
|
|
|
default:
|
|
cv::util::throw_error(std::logic_error("Unsupported GShape type"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct OVCallable {
|
|
static const char *name() { return "OVRequestCallable"; }
|
|
using Run = std::function<void(std::shared_ptr<OVCallContext>,
|
|
cv::gimpl::ov::RequestPool&)>;
|
|
Run run;
|
|
};
|
|
|
|
struct KImpl {
|
|
cv::gimpl::CustomMetaFunction::CM customMetaFunc;
|
|
OVCallable::Run run;
|
|
};
|
|
|
|
using GOVModel = ade::TypedGraph
|
|
< cv::gimpl::Protocol
|
|
, cv::gimpl::Op
|
|
, cv::gimpl::NetworkParams
|
|
, cv::gimpl::CustomMetaFunction
|
|
, OVUnit
|
|
, OVCallable
|
|
>;
|
|
|
|
// FIXME: Same issue with Typed and ConstTyped
|
|
using GConstGOVModel = ade::ConstTypedGraph
|
|
< cv::gimpl::Protocol
|
|
, cv::gimpl::Op
|
|
, cv::gimpl::NetworkParams
|
|
, cv::gimpl::CustomMetaFunction
|
|
, OVUnit
|
|
, OVCallable
|
|
>;
|
|
|
|
namespace {
|
|
class IInferExecutor {
|
|
public:
|
|
using Ptr = std::shared_ptr<IInferExecutor>;
|
|
using NotifyCallbackF = std::function<void()>;
|
|
using SetInputDataF = std::function<void(::ov::InferRequest&)>;
|
|
using ReadOutputDataF = std::function<void(::ov::InferRequest&, std::exception_ptr)>;
|
|
|
|
// NB: The task is represented by:
|
|
// SetInputDataF - function which set input data.
|
|
// ReadOutputDataF - function which read output data.
|
|
struct Task {
|
|
SetInputDataF set_input_data;
|
|
ReadOutputDataF read_output_data;
|
|
};
|
|
|
|
IInferExecutor(::ov::InferRequest request, NotifyCallbackF notify)
|
|
: m_request(std::move(request)),
|
|
m_notify(std::move(notify)) {
|
|
};
|
|
|
|
virtual void execute(const Task& task) = 0;
|
|
virtual ~IInferExecutor() = default;
|
|
|
|
protected:
|
|
::ov::InferRequest m_request;
|
|
NotifyCallbackF m_notify;
|
|
};
|
|
|
|
class SyncInferExecutor : public IInferExecutor {
|
|
using IInferExecutor::IInferExecutor;
|
|
virtual void execute(const IInferExecutor::Task &task) override;
|
|
};
|
|
|
|
void SyncInferExecutor::execute(const IInferExecutor::Task &task) {
|
|
try {
|
|
task.set_input_data(m_request);
|
|
m_request.infer();
|
|
task.read_output_data(m_request, nullptr);
|
|
} catch (...) {
|
|
m_notify();
|
|
throw;
|
|
}
|
|
// NB: Notify pool that executor has finished.
|
|
m_notify();
|
|
}
|
|
|
|
class AsyncInferExecutor : public IInferExecutor {
|
|
public:
|
|
using IInferExecutor::IInferExecutor;
|
|
virtual void execute(const IInferExecutor::Task& task) override;
|
|
|
|
private:
|
|
void callback(Task task,
|
|
::ov::InferRequest request,
|
|
std::exception_ptr eptr) noexcept;
|
|
};
|
|
|
|
void AsyncInferExecutor::execute(const IInferExecutor::Task& task) {
|
|
using namespace std::placeholders;
|
|
using callback_t = std::function<void(std::exception_ptr)>;
|
|
m_request.set_callback(
|
|
static_cast<callback_t>(
|
|
std::bind(&AsyncInferExecutor::callback, this, task, m_request, _1)));
|
|
try {
|
|
task.set_input_data(m_request);
|
|
m_request.start_async();
|
|
} catch (...) {
|
|
m_request.set_callback([](std::exception_ptr){});
|
|
m_notify();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void AsyncInferExecutor::callback(IInferExecutor::Task task,
|
|
::ov::InferRequest request,
|
|
std::exception_ptr eptr) noexcept {
|
|
task.read_output_data(request, eptr);
|
|
request.set_callback([](std::exception_ptr){});
|
|
// NB: Notify pool that executor has finished.
|
|
m_notify();
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// TODO: Make it generic to reuse in IE and ONNX backends.
|
|
class cv::gimpl::ov::RequestPool {
|
|
public:
|
|
explicit RequestPool(std::vector<::ov::InferRequest>&& requests);
|
|
|
|
IInferExecutor::Ptr getIdleRequest();
|
|
void waitAll();
|
|
|
|
private:
|
|
void setup();
|
|
void release(const size_t id);
|
|
|
|
QueueClass<size_t> m_idle_ids;
|
|
std::vector<IInferExecutor::Ptr> m_requests;
|
|
};
|
|
|
|
void cv::gimpl::ov::RequestPool::release(const size_t id) {
|
|
m_idle_ids.push(id);
|
|
}
|
|
|
|
cv::gimpl::ov::RequestPool::RequestPool(std::vector<::ov::InferRequest>&& requests) {
|
|
GAPI_Assert(!requests.empty());
|
|
if (requests.size() == 1u) {
|
|
m_requests.push_back(
|
|
std::make_shared<SyncInferExecutor>(
|
|
requests.front(), std::bind(&RequestPool::release, this, 0u)));
|
|
} else {
|
|
for (size_t i = 0; i < requests.size(); ++i) {
|
|
m_requests.push_back(
|
|
std::make_shared<AsyncInferExecutor>(
|
|
requests[i], std::bind(&RequestPool::release, this, i)));
|
|
}
|
|
}
|
|
setup();
|
|
}
|
|
|
|
void cv::gimpl::ov::RequestPool::setup() {
|
|
for (size_t i = 0; i < m_requests.size(); ++i) {
|
|
m_idle_ids.push(i);
|
|
}
|
|
}
|
|
|
|
IInferExecutor::Ptr cv::gimpl::ov::RequestPool::getIdleRequest() {
|
|
size_t id = 0u;
|
|
m_idle_ids.pop(id);
|
|
return m_requests[id];
|
|
}
|
|
|
|
// NB: Not thread-safe.
|
|
void cv::gimpl::ov::RequestPool::waitAll() {
|
|
// NB: It will be blocked if at least one request is busy.
|
|
for (size_t i = 0; i < m_requests.size(); ++i) {
|
|
size_t id = 0u;
|
|
m_idle_ids.pop(id);
|
|
}
|
|
setup();
|
|
}
|
|
|
|
|
|
// NB: This is a callback used by async infer
|
|
// to post outputs blobs (cv::GMat's).
|
|
static void PostOutputs(::ov::InferRequest &infer_request,
|
|
std::exception_ptr eptr,
|
|
std::shared_ptr<OVCallContext> ctx) {
|
|
GAPI_ITT_STATIC_LOCAL_HANDLE(ov_cb_post_outputs_hndl, "OV_async_callback_PostOutputs");
|
|
GAPI_ITT_AUTO_TRACE_GUARD(ov_cb_post_outputs_hndl);
|
|
|
|
ctx->eptr = std::move(eptr);
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
// NB: Copy data back only if execution finished successfully
|
|
// and inference only mode is disabled.
|
|
// Otherwise just post outputs to maintain streaming executor contract.
|
|
if (!ctx->eptr && !ctx->getOptions().inference_only) {
|
|
const auto& out_name = ctx->uu.params.output_names[i];
|
|
copyFromOV(infer_request.get_tensor(out_name),
|
|
ctx->outMatR(i));
|
|
}
|
|
auto output = ctx->output(i);
|
|
ctx->out.meta(output, ctx->getMeta());
|
|
ctx->out.post(std::move(output), ctx->eptr);
|
|
}
|
|
}
|
|
|
|
class PostOutputsList {
|
|
public:
|
|
PostOutputsList(size_t size,
|
|
std::shared_ptr<OVCallContext> ctx);
|
|
|
|
void operator()(::ov::InferRequest &infer_request,
|
|
std::exception_ptr eptr,
|
|
size_t pos) const;
|
|
|
|
private:
|
|
struct Priv {
|
|
std::atomic<size_t> finished{0u};
|
|
size_t size;
|
|
std::shared_ptr<OVCallContext> ctx;
|
|
};
|
|
std::shared_ptr<Priv> m_priv;
|
|
};
|
|
|
|
PostOutputsList::PostOutputsList(size_t size,
|
|
std::shared_ptr<OVCallContext> ctx)
|
|
: m_priv(new Priv{}) {
|
|
m_priv->size = size;
|
|
m_priv->ctx = ctx;
|
|
}
|
|
|
|
void PostOutputsList::operator()(::ov::InferRequest &infer_request,
|
|
std::exception_ptr eptr,
|
|
size_t pos) const {
|
|
auto&& ctx = m_priv->ctx;
|
|
auto&& finished = m_priv->finished;
|
|
auto&& size = m_priv->size;
|
|
|
|
ctx->eptr = eptr;
|
|
if (!ctx->eptr) {
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
std::vector<cv::Mat> &out_vec = ctx->outVecR<cv::Mat>(i);
|
|
|
|
const auto &out_name = ctx->uu.params.output_names[i];
|
|
const auto &out_tensor = infer_request.get_tensor(out_name);
|
|
|
|
out_vec[pos].create(toCV(out_tensor.get_shape()),
|
|
toCV(out_tensor.get_element_type()));
|
|
copyFromOV(out_tensor, out_vec[pos]);
|
|
}
|
|
}
|
|
++finished;
|
|
|
|
if (finished == size) {
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
auto output = ctx->output(i);
|
|
ctx->out.meta(output, ctx->getMeta());
|
|
ctx->out.post(std::move(output), ctx->eptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void copyToOV(std::shared_ptr<OVCallContext> ctx, uint32_t input_idx, ov::Tensor &tensor) {
|
|
switch (ctx->inShape(input_idx)) {
|
|
case cv::GShape::GMAT:
|
|
copyToOV(ctx->inMat(input_idx), tensor);
|
|
break;
|
|
case cv::GShape::GFRAME:
|
|
copyToOV(ctx->inFrame(input_idx), tensor);
|
|
break;
|
|
default:
|
|
GAPI_Assert("Unsupported input shape for OV backend");
|
|
}
|
|
}
|
|
|
|
namespace cv {
|
|
namespace gimpl {
|
|
namespace ov {
|
|
|
|
template <typename Attr>
|
|
using AttrMap = cv::gapi::ov::detail::AttrMap<Attr>;
|
|
|
|
template <typename Attr>
|
|
using LayerVariantAttr = cv::gapi::ov::detail::LayerVariantAttr<Attr>;
|
|
|
|
template <typename Attr> AttrMap<Attr>
|
|
broadcastLayerAttr(const LayerVariantAttr<Attr> &layer_attr,
|
|
const std::vector<std::string> &layer_names) {
|
|
AttrMap<Attr> map;
|
|
if (cv::util::holds_alternative<AttrMap<Attr>>(layer_attr)) {
|
|
map = cv::util::get<AttrMap<Attr>>(layer_attr);
|
|
// NB: Validate map:
|
|
std::unordered_set<std::string> existing_layers =
|
|
{layer_names.begin(), layer_names.end()};
|
|
|
|
for (const auto &p : map) {
|
|
const auto it = existing_layers.find(p.first);
|
|
if (it == existing_layers.end()) {
|
|
cv::util::throw_error(
|
|
std::logic_error("OV Backend: Failed to"
|
|
" find layer with name: " + p.first));
|
|
}
|
|
}
|
|
} else if (cv::util::holds_alternative<Attr>(layer_attr)) {
|
|
// NB: Broadcast value to all layers.
|
|
auto elem = cv::util::get<Attr>(layer_attr);
|
|
for (auto &&layer_name : layer_names) {
|
|
map.emplace(layer_name, elem);
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
template <typename K, typename V>
|
|
cv::optional<V> lookUp(const std::map<K, V> &map, const K& key) {
|
|
const auto it = map.find(key);
|
|
if (it == map.end()) {
|
|
return {};
|
|
}
|
|
return cv::util::make_optional(std::move(it->second));
|
|
}
|
|
|
|
// NB: This function is used to preprocess input image
|
|
// for InferROI, InferList, InferList2 kernels.
|
|
static cv::Mat preprocess(const cv::Mat &in_mat,
|
|
const cv::Rect &roi,
|
|
const ::ov::Shape &model_shape) {
|
|
cv::Mat out;
|
|
// FIXME: Since there is no information about H and W positions
|
|
// among tensor dimmensions assume that model layout is "NHWC".
|
|
// (In fact "NHWC" is the only right layout for preprocessing because
|
|
// it works only with images.
|
|
GAPI_Assert(model_shape.size() == 4u);
|
|
const auto H = model_shape[1];
|
|
const auto W = model_shape[2];
|
|
const auto C = model_shape[3];
|
|
// NB: Soft check that at least number of channels matches.
|
|
if (static_cast<int>(C) != in_mat.channels()) {
|
|
std::stringstream ss;
|
|
ss << "OV Backend: Failed to preprocess input data "
|
|
" (Number of channels mismatch)."
|
|
" Provided data: " << cv::descr_of(in_mat) <<
|
|
" and Model shape: " << model_shape;
|
|
util::throw_error(std::logic_error(ss.str()));
|
|
}
|
|
// NB: Crop roi and resize to model size.
|
|
cv::resize(in_mat(roi), out, cv::Size(W, H));
|
|
return out;
|
|
}
|
|
|
|
// NB: This function is used to preprocess input image
|
|
// for InferROI, InferList, InferList2 kernels.
|
|
static cv::Mat preprocess(MediaFrame::View& view,
|
|
const cv::GFrameDesc& desc,
|
|
const cv::Rect& roi,
|
|
const ::ov::Shape &model_shape) {
|
|
return preprocess(wrapOV(view, desc), roi, model_shape);
|
|
}
|
|
|
|
static void preprocess_and_copy(std::shared_ptr<OVCallContext> ctx,
|
|
uint32_t input_idx,
|
|
const cv::Rect &roi,
|
|
const ::ov::Shape &model_shape,
|
|
::ov::Tensor& tensor) {
|
|
switch (ctx->inShape(input_idx)) {
|
|
case cv::GShape::GMAT: {
|
|
auto roi_mat = preprocess(ctx->inMat(input_idx), roi, model_shape);
|
|
copyToOV(roi_mat, tensor);
|
|
break;
|
|
}
|
|
case cv::GShape::GFRAME: {
|
|
auto currentFrame = ctx->inFrame(input_idx);
|
|
auto view = cv::MediaFrame::View(currentFrame.access(cv::MediaFrame::Access::R));
|
|
auto roi_mat = preprocess(view, currentFrame.desc(), roi, model_shape);
|
|
copyToOV(roi_mat, tensor);
|
|
break;
|
|
}
|
|
default:
|
|
GAPI_Assert("Unsupported input shape for OV backend");
|
|
}
|
|
}
|
|
|
|
static bool isImage(const cv::GMatDesc &desc,
|
|
const ::ov::Shape &model_shape) {
|
|
return (model_shape.size() == 4u) &&
|
|
(!desc.isND()) /* dims == 2 */ &&
|
|
(desc.chan == 1 || desc.chan == 3) &&
|
|
(desc.size.height != 1 && desc.size.width != 1) &&
|
|
(desc.depth == CV_8U);
|
|
}
|
|
|
|
static bool isImage(const cv::GMetaArg &meta,
|
|
const ::ov::Shape &shape) {
|
|
if (cv::util::holds_alternative<GFrameDesc>(meta)) {
|
|
return true;
|
|
}
|
|
GAPI_Assert(cv::util::holds_alternative<GMatDesc>(meta));
|
|
auto matdesc = cv::util::get<GMatDesc>(meta);
|
|
return isImage(matdesc, shape);
|
|
}
|
|
|
|
class PrePostProcWrapper {
|
|
public:
|
|
PrePostProcWrapper(std::shared_ptr<::ov::Model> &model,
|
|
const ParamDesc::Model &model_info,
|
|
const std::vector<std::string> &input_names,
|
|
const std::vector<std::string> &output_names)
|
|
: m_ppp(model),
|
|
m_model(model),
|
|
m_model_info(model_info),
|
|
m_input_names(input_names),
|
|
m_output_names(output_names) {
|
|
// NB: Do Reshape right away since it must be the first step of model modification
|
|
// and applicable for all infer kernels.
|
|
const auto new_shapes = broadcastLayerAttr(model_info.new_shapes, input_names);
|
|
m_model->reshape(toOV(new_shapes));
|
|
|
|
const auto &mi = m_model_info;
|
|
m_input_tensor_layout = broadcastLayerAttr(mi.input_tensor_layout, m_input_names);
|
|
m_input_model_layout = broadcastLayerAttr(mi.input_model_layout, m_input_names);
|
|
m_interpolation = broadcastLayerAttr(mi.interpolation, m_input_names);
|
|
m_mean_values = broadcastLayerAttr(mi.mean_values, m_input_names);
|
|
m_scale_values = broadcastLayerAttr(mi.scale_values, m_input_names);
|
|
m_interpolation = broadcastLayerAttr(mi.interpolation, m_input_names);
|
|
|
|
m_output_tensor_layout = broadcastLayerAttr(mi.output_tensor_layout, m_output_names);
|
|
m_output_model_layout = broadcastLayerAttr(mi.output_model_layout, m_output_names);
|
|
m_output_tensor_precision = broadcastLayerAttr(mi.output_tensor_precision, m_output_names);
|
|
};
|
|
|
|
void cfgLayouts(const std::string &input_name) {
|
|
auto &input_info = m_ppp.input(input_name);
|
|
const auto explicit_in_model_layout = lookUp(m_input_model_layout, input_name);
|
|
if (explicit_in_model_layout) {
|
|
input_info.model().set_layout(::ov::Layout(*explicit_in_model_layout));
|
|
} else if (m_model->input(input_name).get_shape().size() == 4u) {
|
|
const auto& input_layout = ::ov::layout::get_layout(m_model->input(input_name));
|
|
if (!input_layout.empty()) {
|
|
GAPI_LOG_INFO(NULL, "Model input layout " << input_name << " found: " << input_layout.to_string() << ".");
|
|
} else {
|
|
// NB: Back compatibility with IR's without any layout information.
|
|
// Note that default is only applicable for 4D inputs in order to
|
|
// support auto resize for image use cases.
|
|
GAPI_LOG_WARNING(NULL, "Failed to find layout for input layer \""
|
|
<< input_name << "\" - NCHW is set by default");
|
|
const std::string default_layout = "NCHW";
|
|
input_info.model().set_layout(::ov::Layout(default_layout));
|
|
m_input_model_layout.emplace(input_name, default_layout);
|
|
}
|
|
}
|
|
const auto explicit_in_tensor_layout = lookUp(m_input_tensor_layout, input_name);
|
|
if (explicit_in_tensor_layout) {
|
|
input_info.tensor().set_layout(::ov::Layout(*explicit_in_tensor_layout));
|
|
}
|
|
}
|
|
|
|
void cfgScaleMean(const std::string &input_name,
|
|
const GMetaArg &input_meta) {
|
|
auto &input_info = m_ppp.input(input_name);
|
|
|
|
const auto mean_vec = lookUp(m_mean_values, input_name);
|
|
const auto scale_vec = lookUp(m_scale_values, input_name);
|
|
|
|
if (mean_vec || scale_vec) {
|
|
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(input_meta));
|
|
const auto depth = cv::util::get<cv::GMatDesc>(input_meta).depth;
|
|
const bool depth_is_real = (depth == CV_32F) || (depth == CV_16F);
|
|
if (!depth_is_real) {
|
|
input_info.preprocess().convert_element_type(toOV(CV_32F));
|
|
}
|
|
}
|
|
if (mean_vec) {
|
|
input_info.preprocess().mean(*mean_vec);
|
|
}
|
|
if (scale_vec) {
|
|
input_info.preprocess().scale(*scale_vec);
|
|
}
|
|
}
|
|
|
|
// FIXME: Decompose this...
|
|
void cfgPreProcessing(const std::string &input_name,
|
|
const cv::GMetaArg &input_meta,
|
|
const bool disable_img_resize = false) {
|
|
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(input_meta) ||
|
|
cv::util::holds_alternative<cv::GFrameDesc>(input_meta));
|
|
const auto explicit_in_tensor_layout = lookUp(m_input_tensor_layout, input_name);
|
|
const auto explicit_in_model_layout = lookUp(m_input_model_layout, input_name);
|
|
const auto explicit_resize = lookUp(m_interpolation, input_name);
|
|
|
|
if (disable_img_resize && explicit_resize.has_value()) {
|
|
std::stringstream ss;
|
|
util::throw_error(std::logic_error(
|
|
"OV Backend: Resize for layer \"" + input_name + "\" will be performed"
|
|
" on host via OpenCV so explicitly configured resize is prohibited."));
|
|
}
|
|
|
|
const auto &input_shape = m_model->input(input_name).get_shape();
|
|
auto &input_info = m_ppp.input(input_name);
|
|
|
|
auto isMat = cv::util::holds_alternative<cv::GMatDesc>(input_meta);
|
|
auto prec = isMat ? cv::util::get<cv::GMatDesc>(input_meta).depth : CV_8U;
|
|
m_ppp.input(input_name).tensor().set_element_type(toOV(prec));
|
|
|
|
const auto &matdesc = isMat ? cv::util::get<cv::GMatDesc>(input_meta) : cv::GMatDesc();
|
|
const auto &framedesc = !isMat ? cv::util::get<cv::GFrameDesc>(input_meta) : cv::GFrameDesc();
|
|
if (isImage(input_meta, input_shape)) {
|
|
// NB: Image case - all necessary preprocessng is configured automatically.
|
|
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is image.");
|
|
if (explicit_in_tensor_layout && *explicit_in_tensor_layout != "NHWC") {
|
|
std::stringstream desc_str;
|
|
if (isMat) {
|
|
desc_str << matdesc;
|
|
} else {
|
|
desc_str << framedesc;
|
|
}
|
|
std::stringstream ss;
|
|
ss << "OV Backend: Provided tensor layout " << *explicit_in_tensor_layout
|
|
<< " is not compatible with input data " << desc_str.str() << " for layer \""
|
|
<< input_name << "\". Expecting NHWC";
|
|
util::throw_error(std::logic_error(ss.str()));
|
|
} else {
|
|
input_info.tensor().set_layout(::ov::Layout("NHWC"));
|
|
}
|
|
|
|
if (!disable_img_resize) {
|
|
const auto size = isMat ? cv::util::get<cv::GMatDesc>(input_meta).size : cv::util::get<cv::GFrameDesc>(input_meta).size;
|
|
input_info.tensor().set_spatial_static_shape(size.height,
|
|
size.width);
|
|
// NB: Even though resize is automatically configured
|
|
// user have an opportunity to specify the interpolation algorithm.
|
|
auto interp = explicit_resize
|
|
? toOVInterp(*explicit_resize)
|
|
: ::ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR;
|
|
input_info.preprocess().resize(interp);
|
|
}
|
|
} else {
|
|
// NB: Tensor case - resize or layout conversions must be explicitly specified.
|
|
GAPI_LOG_DEBUG(NULL, "OV Backend: Input: \"" << input_name << "\" is tensor.");
|
|
|
|
if (explicit_resize) {
|
|
if (matdesc.isND()) {
|
|
// NB: ND case - need to obtain "H" and "W" positions
|
|
// in order to configure resize.
|
|
const auto model_layout = explicit_in_model_layout
|
|
? ::ov::Layout(*explicit_in_model_layout)
|
|
: ::ov::layout::get_layout(m_model->input(input_name));
|
|
if (!explicit_in_tensor_layout && model_layout.empty()) {
|
|
std::stringstream ss;
|
|
ss << "Resize for input layer: " << input_name
|
|
<< "can't be configured."
|
|
<< " Failed to extract H and W positions from layout.";
|
|
util::throw_error(std::logic_error(ss.str()));
|
|
} else {
|
|
const auto layout = explicit_in_tensor_layout
|
|
? ::ov::Layout(*explicit_in_tensor_layout) : model_layout;
|
|
auto H_idx = ::ov::layout::height_idx(layout);
|
|
auto W_idx = ::ov::layout::width_idx(layout);
|
|
// NB: If layout is "...HW", H position is -2.
|
|
if (H_idx < 0) H_idx = matdesc.dims.size() + H_idx;
|
|
if (W_idx < 0) W_idx = matdesc.dims.size() + W_idx;
|
|
GAPI_Assert(H_idx >= 0 && H_idx < static_cast<int>(matdesc.dims.size()));
|
|
GAPI_Assert(W_idx >= 0 && W_idx < static_cast<int>(matdesc.dims.size()));
|
|
input_info.tensor().set_spatial_static_shape(matdesc.dims[H_idx],
|
|
matdesc.dims[W_idx]);
|
|
input_info.preprocess().resize(toOVInterp(*explicit_resize));
|
|
}
|
|
} else {
|
|
// NB: 2D case - We know exactly where H and W...
|
|
input_info.tensor().set_spatial_static_shape(matdesc.size.height,
|
|
matdesc.size.width);
|
|
input_info.preprocess().resize(toOVInterp(*explicit_resize));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cfgPostProcessing() {
|
|
for (const auto &output_name : m_output_names) {
|
|
const auto explicit_out_tensor_layout =
|
|
lookUp(m_output_tensor_layout, output_name);
|
|
if (explicit_out_tensor_layout) {
|
|
m_ppp.output(output_name).tensor()
|
|
.set_layout(::ov::Layout(*explicit_out_tensor_layout));
|
|
}
|
|
|
|
const auto explicit_out_model_layout =
|
|
lookUp(m_output_model_layout, output_name);
|
|
if (explicit_out_model_layout) {
|
|
m_ppp.output(output_name).model()
|
|
.set_layout(::ov::Layout(*explicit_out_model_layout));
|
|
}
|
|
|
|
const auto explicit_out_tensor_prec =
|
|
lookUp(m_output_tensor_precision, output_name);
|
|
if (explicit_out_tensor_prec) {
|
|
m_ppp.output(output_name).tensor()
|
|
.set_element_type(toOV(*explicit_out_tensor_prec));
|
|
}
|
|
}
|
|
}
|
|
|
|
void finalize() {
|
|
GAPI_LOG_DEBUG(NULL, "OV Backend: PrePostProcessor: " << m_ppp);
|
|
m_model = m_ppp.build();
|
|
}
|
|
|
|
private:
|
|
::ov::preprocess::PrePostProcessor m_ppp;
|
|
|
|
std::shared_ptr<::ov::Model> &m_model;
|
|
const ParamDesc::Model &m_model_info;
|
|
const std::vector<std::string> &m_input_names;
|
|
const std::vector<std::string> &m_output_names;
|
|
|
|
cv::gimpl::ov::AttrMap<std::string> m_input_tensor_layout;
|
|
cv::gimpl::ov::AttrMap<std::string> m_input_model_layout;
|
|
cv::gimpl::ov::AttrMap<int> m_interpolation;
|
|
cv::gimpl::ov::AttrMap<std::vector<float>> m_mean_values;
|
|
cv::gimpl::ov::AttrMap<std::vector<float>> m_scale_values;
|
|
cv::gimpl::ov::AttrMap<std::string> m_output_tensor_layout;
|
|
cv::gimpl::ov::AttrMap<std::string> m_output_model_layout;
|
|
cv::gimpl::ov::AttrMap<int> m_output_tensor_precision;
|
|
};
|
|
|
|
struct Infer: public cv::detail::KernelTag {
|
|
using API = cv::GInferBase;
|
|
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
|
|
static KImpl kernel() { return KImpl{outMeta, run}; }
|
|
|
|
static cv::GMetaArgs outMeta(const ade::Graph &gr,
|
|
const ade::NodeHandle &nh,
|
|
const cv::GMetaArgs &in_metas,
|
|
const cv::GArgs &/*in_args*/) {
|
|
cv::GMetaArgs result;
|
|
|
|
GConstGOVModel gm(gr);
|
|
const auto &uu = gm.metadata(nh).get<OVUnit>();
|
|
// Initialize input information
|
|
// Note our input layers list order matches the API order and so
|
|
// meta order.
|
|
GAPI_Assert(uu.params.input_names.size() == in_metas.size()
|
|
&& "Known input layers count doesn't match input meta count");
|
|
|
|
// NB: Pre/Post processing configuration avaiable only for read models.
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
|
|
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
|
|
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
|
|
PrePostProcWrapper ppp {model, model_info,
|
|
uu.params.input_names, uu.params.output_names};
|
|
|
|
for (auto &&it : ade::util::zip(ade::util::toRange(uu.params.input_names),
|
|
ade::util::toRange(in_metas))) {
|
|
const auto &input_name = std::get<0>(it);
|
|
const auto &mm = std::get<1>(it);
|
|
ppp.cfgLayouts(input_name);
|
|
ppp.cfgPreProcessing(input_name, mm);
|
|
ppp.cfgScaleMean(input_name, mm);
|
|
}
|
|
ppp.cfgPostProcessing();
|
|
ppp.finalize();
|
|
}
|
|
|
|
for (const auto &out_name : uu.params.output_names) {
|
|
cv::GMatDesc outm;
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
|
|
const auto &out = uu.model->output(out_name);
|
|
outm = cv::GMatDesc(toCV(out.get_element_type()),
|
|
toCV(out.get_shape()));
|
|
} else {
|
|
GAPI_Assert(cv::util::holds_alternative<ParamDesc::CompiledModel>(uu.params.kind));
|
|
const auto &out = uu.compiled_model.output(out_name);
|
|
outm = cv::GMatDesc(toCV(out.get_element_type()),
|
|
toCV(out.get_shape()));
|
|
}
|
|
result.emplace_back(std::move(outm));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void run(std::shared_ptr<OVCallContext> ctx,
|
|
cv::gimpl::ov::RequestPool &reqPool) {
|
|
using namespace std::placeholders;
|
|
reqPool.getIdleRequest()->execute(
|
|
IInferExecutor::Task {
|
|
[ctx](::ov::InferRequest &infer_request) {
|
|
// NB: No need to populate model inputs with data
|
|
// if it's inference only mode.
|
|
if (ctx->getOptions().inference_only) {
|
|
return;
|
|
}
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_in)) {
|
|
const auto& input_name = ctx->uu.params.input_names[i];
|
|
auto input_tensor = infer_request.get_tensor(input_name);
|
|
// TODO: In some cases wrapping existing data pointer
|
|
// might be faster than copy. Make it a strategy.
|
|
copyToOV(ctx, i, input_tensor);
|
|
}
|
|
},
|
|
std::bind(PostOutputs, _1, _2, ctx)
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
struct InferROI: public cv::detail::KernelTag {
|
|
using API = cv::GInferROIBase;
|
|
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
|
|
static KImpl kernel() { return KImpl{outMeta, run}; }
|
|
|
|
static cv::GMetaArgs outMeta(const ade::Graph &gr,
|
|
const ade::NodeHandle &nh,
|
|
const cv::GMetaArgs &in_metas,
|
|
const cv::GArgs &/*in_args*/) {
|
|
cv::GMetaArgs result;
|
|
|
|
GConstGOVModel gm(gr);
|
|
const auto &uu = gm.metadata(nh).get<OVUnit>();
|
|
// Initialize input information
|
|
// FIXME: So far it is pretty limited
|
|
GAPI_Assert(1u == uu.params.input_names.size());
|
|
GAPI_Assert(2u == in_metas.size());
|
|
|
|
const auto &input_name = uu.params.input_names.at(0);
|
|
const auto &mm = in_metas.at(1u);
|
|
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(mm) ||
|
|
cv::util::holds_alternative<cv::GFrameDesc>(mm));
|
|
const bool is_model = cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind);
|
|
const auto &input_shape = is_model ? uu.model->input(input_name).get_shape()
|
|
: uu.compiled_model.input(input_name).get_shape();
|
|
|
|
if (!isImage(mm, input_shape)) {
|
|
util::throw_error(std::runtime_error(
|
|
"OV Backend: InferROI supports only image as the 1th argument"));
|
|
}
|
|
|
|
if (is_model) {
|
|
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
|
|
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
|
|
PrePostProcWrapper ppp {model, model_info,
|
|
uu.params.input_names, uu.params.output_names};
|
|
|
|
ppp.cfgLayouts(input_name);
|
|
ppp.cfgPreProcessing(input_name, mm, true /*disable_img_resize*/);
|
|
ppp.cfgScaleMean(input_name, mm);
|
|
ppp.cfgPostProcessing();
|
|
ppp.finalize();
|
|
}
|
|
|
|
for (const auto &out_name : uu.params.output_names) {
|
|
cv::GMatDesc outm;
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
|
|
const auto &out = uu.model->output(out_name);
|
|
outm = cv::GMatDesc(toCV(out.get_element_type()),
|
|
toCV(out.get_shape()));
|
|
} else {
|
|
GAPI_Assert(cv::util::holds_alternative<ParamDesc::CompiledModel>(uu.params.kind));
|
|
const auto &out = uu.compiled_model.output(out_name);
|
|
outm = cv::GMatDesc(toCV(out.get_element_type()),
|
|
toCV(out.get_shape()));
|
|
}
|
|
result.emplace_back(std::move(outm));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void run(std::shared_ptr<OVCallContext> ctx,
|
|
cv::gimpl::ov::RequestPool &reqPool) {
|
|
using namespace std::placeholders;
|
|
if (ctx->getOptions().inference_only) {
|
|
cv::util::throw_error(
|
|
std::logic_error("OV Backend: Inference only mode is not supported for InferROI!"));
|
|
}
|
|
reqPool.getIdleRequest()->execute(
|
|
IInferExecutor::Task {
|
|
[ctx](::ov::InferRequest &infer_request) {
|
|
GAPI_Assert(ctx->uu.params.num_in == 1);
|
|
const auto &input_name = ctx->uu.params.input_names[0];
|
|
auto input_tensor = infer_request.get_tensor(input_name);
|
|
const auto &shape = input_tensor.get_shape();
|
|
const auto &roi = ctx->inArg<cv::detail::OpaqueRef>(0).rref<cv::Rect>();
|
|
preprocess_and_copy(ctx, 1, roi, shape, input_tensor);
|
|
},
|
|
std::bind(PostOutputs, _1, _2, ctx)
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
struct InferList: public cv::detail::KernelTag {
|
|
using API = cv::GInferListBase;
|
|
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
|
|
static KImpl kernel() { return KImpl{outMeta, run}; }
|
|
|
|
static cv::GMetaArgs outMeta(const ade::Graph &gr,
|
|
const ade::NodeHandle &nh,
|
|
const cv::GMetaArgs &in_metas,
|
|
const cv::GArgs &/*in_args*/) {
|
|
GConstGOVModel gm(gr);
|
|
const auto &uu = gm.metadata(nh).get<OVUnit>();
|
|
// Initialize input information
|
|
// Note our input layers list order matches the API order and so
|
|
// meta order.
|
|
GAPI_Assert(uu.params.input_names.size() == (in_metas.size() - 1u)
|
|
&& "Known input layers count doesn't match input meta count");
|
|
|
|
// NB: Pre/Post processing configuration avaiable only for read models.
|
|
if (cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind)) {
|
|
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
|
|
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
|
|
PrePostProcWrapper ppp {model, model_info,
|
|
uu.params.input_names, uu.params.output_names};
|
|
|
|
size_t idx = 1u;
|
|
for (auto &&input_name : uu.params.input_names) {
|
|
const auto &mm = in_metas[idx++];
|
|
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(mm) ||
|
|
cv::util::holds_alternative<cv::GFrameDesc>(mm));
|
|
const auto &input_shape = uu.model->input(input_name).get_shape();
|
|
|
|
if (!isImage(mm, input_shape)) {
|
|
util::throw_error(std::runtime_error(
|
|
"OV Backend: Only image is supported"
|
|
" as the " + std::to_string(idx) + "th argument for InferList"));
|
|
}
|
|
|
|
ppp.cfgLayouts(input_name);
|
|
ppp.cfgPreProcessing(input_name, mm, true /*disable_img_resize*/);
|
|
ppp.cfgScaleMean(input_name, mm);
|
|
}
|
|
ppp.cfgPostProcessing();
|
|
ppp.finalize();
|
|
}
|
|
|
|
// roi-list version is much easier at the moment.
|
|
// All our outputs are vectors which don't have
|
|
// metadata at the moment - so just create a vector of
|
|
// "empty" array metadatas of the required size.
|
|
return cv::GMetaArgs(uu.params.output_names.size(),
|
|
cv::GMetaArg{cv::empty_array_desc()});
|
|
}
|
|
|
|
static void run(std::shared_ptr<OVCallContext> ctx,
|
|
cv::gimpl::ov::RequestPool &reqPool) {
|
|
if (ctx->getOptions().inference_only) {
|
|
cv::util::throw_error(
|
|
std::logic_error("OV Backend: Inference only mode is not supported for InferList!"));
|
|
}
|
|
const auto& in_roi_vec = ctx->inArg<cv::detail::VectorRef>(0u).rref<cv::Rect>();
|
|
// NB: In case there is no input data need to post output anyway
|
|
if (in_roi_vec.empty()) {
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
auto output = ctx->output(i);
|
|
ctx->out.meta(output, ctx->getMeta());
|
|
ctx->out.post(std::move(output));
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
// FIXME: Isn't this should be done automatically
|
|
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
|
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
|
out_vec.clear();
|
|
out_vec.resize(in_roi_vec.size());
|
|
}
|
|
|
|
PostOutputsList callback(in_roi_vec.size(), ctx);
|
|
for (auto&& it : ade::util::indexed(in_roi_vec)) {
|
|
const auto pos = ade::util::index(it);
|
|
const auto &rc = ade::util::value(it);
|
|
reqPool.getIdleRequest()->execute(
|
|
IInferExecutor::Task {
|
|
[ctx, rc](::ov::InferRequest &infer_request) {
|
|
const auto &input_name = ctx->uu.params.input_names[0];
|
|
auto input_tensor = infer_request.get_tensor(input_name);
|
|
const auto &shape = input_tensor.get_shape();
|
|
preprocess_and_copy(ctx, 1, rc, shape, input_tensor);
|
|
},
|
|
std::bind(callback, std::placeholders::_1, std::placeholders::_2, pos)
|
|
}
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct InferList2: public cv::detail::KernelTag {
|
|
using API = cv::GInferList2Base;
|
|
static cv::gapi::GBackend backend() { return cv::gapi::ov::backend(); }
|
|
static KImpl kernel() { return KImpl{outMeta, run}; }
|
|
|
|
static cv::GMetaArgs outMeta(const ade::Graph &gr,
|
|
const ade::NodeHandle &nh,
|
|
const cv::GMetaArgs &in_metas,
|
|
const cv::GArgs &/*in_args*/) {
|
|
GConstGOVModel gm(gr);
|
|
const auto &uu = gm.metadata(nh).get<OVUnit>();
|
|
// Initialize input information
|
|
// Note our input layers list order matches the API order and so
|
|
// meta order.
|
|
GAPI_Assert(uu.params.input_names.size() == (in_metas.size() - 1u)
|
|
&& "Known input layers count doesn't match input meta count");
|
|
|
|
const auto &op = gm.metadata(nh).get<Op>();
|
|
|
|
// In contrast to InferList, the InferList2 has only one
|
|
// "full-frame" image argument, and all the rest are arrays of
|
|
// ether ROI or blobs. So here we set the 0th arg image format
|
|
// to all inputs which are ROI-based (skipping the
|
|
// "blob"-based ones)
|
|
// FIXME: this is filtering not done, actually! GArrayDesc has
|
|
// no hint for its underlying type!
|
|
|
|
const auto &input_name_0 = uu.params.input_names.front();
|
|
const auto &mm_0 = in_metas[0u];
|
|
|
|
if (!(cv::util::holds_alternative<cv::GMatDesc>(mm_0) ||
|
|
cv::util::holds_alternative<cv::GFrameDesc>(mm_0))) {
|
|
util::throw_error(std::runtime_error(
|
|
"OV Backend: Unsupported input meta"
|
|
" for 0th argument in OV backend"));
|
|
}
|
|
|
|
const bool is_model = cv::util::holds_alternative<ParamDesc::Model>(uu.params.kind);
|
|
const auto &input_shape = is_model ? uu.model->input(input_name_0).get_shape()
|
|
: uu.compiled_model.input(input_name_0).get_shape();
|
|
if (!isImage(mm_0, input_shape)) {
|
|
util::throw_error(std::runtime_error(
|
|
"OV Backend: InferList2 supports only image as the 0th argument"));
|
|
}
|
|
|
|
if (is_model) {
|
|
const auto &model_info = cv::util::get<ParamDesc::Model>(uu.params.kind);
|
|
auto& model = const_cast<std::shared_ptr<::ov::Model>&>(uu.model);
|
|
PrePostProcWrapper ppp {model, model_info,
|
|
uu.params.input_names, uu.params.output_names};
|
|
|
|
size_t idx = 1u;
|
|
for (auto &&input_name : uu.params.input_names) {
|
|
GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(in_metas[idx])
|
|
&& "Non-array inputs are not supported");
|
|
|
|
ppp.cfgLayouts(input_name);
|
|
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
|
|
ppp.cfgPreProcessing(input_name, mm_0, true /*disable_img_resize*/);
|
|
} else {
|
|
// This is a cv::GMat (equals to: cv::Mat)
|
|
// Just validate that it is really the type
|
|
// (other types are prohibited here)
|
|
GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT);
|
|
}
|
|
|
|
ppp.cfgScaleMean(input_name, mm_0);
|
|
idx++; // NB: Never forget to increment the counter
|
|
}
|
|
ppp.cfgPostProcessing();
|
|
ppp.finalize();
|
|
}
|
|
|
|
// roi-list version is much easier at the moment.
|
|
// All our outputs are vectors which don't have
|
|
// metadata at the moment - so just create a vector of
|
|
// "empty" array metadatas of the required size.
|
|
return cv::GMetaArgs(uu.params.output_names.size(),
|
|
cv::GMetaArg{cv::empty_array_desc()});
|
|
}
|
|
|
|
static void run(std::shared_ptr<OVCallContext> ctx,
|
|
cv::gimpl::ov::RequestPool &reqPool) {
|
|
if (ctx->getOptions().inference_only) {
|
|
cv::util::throw_error(
|
|
std::logic_error("OV Backend: Inference only mode is not supported for InferList2!"));
|
|
}
|
|
GAPI_Assert(ctx->inArgs().size() > 1u
|
|
&& "This operation must have at least two arguments");
|
|
// NB: This blob will be used to make roi from its, so
|
|
// it should be treated as image
|
|
const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size();
|
|
if (list_size == 0u) {
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
auto output = ctx->output(i);
|
|
ctx->out.meta(output, ctx->getMeta());
|
|
ctx->out.post(std::move(output));
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
|
|
// FIXME: Isn't this should be done automatically
|
|
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
|
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
|
out_vec.clear();
|
|
out_vec.resize(list_size);
|
|
}
|
|
|
|
PostOutputsList callback(list_size, ctx);
|
|
for (const auto &list_idx : ade::util::iota(list_size)) {
|
|
reqPool.getIdleRequest()->execute(
|
|
IInferExecutor::Task {
|
|
[ctx, list_idx, list_size](::ov::InferRequest &infer_request) {
|
|
for (auto in_idx : ade::util::iota(ctx->uu.params.num_in)) {
|
|
const auto &this_vec = ctx->inArg<cv::detail::VectorRef>(in_idx+1u);
|
|
GAPI_Assert(this_vec.size() == list_size);
|
|
const auto &input_name = ctx->uu.params.input_names[in_idx];
|
|
auto input_tensor = infer_request.get_tensor(input_name);
|
|
const auto &shape = input_tensor.get_shape();
|
|
if (this_vec.getKind() == cv::detail::OpaqueKind::CV_RECT) {
|
|
const auto &vec = this_vec.rref<cv::Rect>();
|
|
const auto roi_mat = preprocess(ctx->inMat(0), vec[list_idx], shape);
|
|
copyToOV(roi_mat, input_tensor);
|
|
} else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) {
|
|
const auto &vec = this_vec.rref<cv::Mat>();
|
|
const auto &mat = vec[list_idx];
|
|
copyToOV(mat, input_tensor);
|
|
} else {
|
|
GAPI_Assert(false &&
|
|
"OV Backend: Only Rect and Mat types are supported for InferList2");
|
|
}
|
|
}
|
|
},
|
|
std::bind(callback, std::placeholders::_1, std::placeholders::_2, list_idx)
|
|
} // task
|
|
);
|
|
} // for
|
|
}
|
|
};
|
|
|
|
} // namespace ov
|
|
} // namespace gimpl
|
|
} // namespace cv
|
|
|
|
// IE backend implementation of GBackend::Priv ///////////////////////
|
|
namespace {
|
|
class GOVBackendImpl final: public cv::gapi::GBackend::Priv {
|
|
virtual void unpackKernel(ade::Graph &gr,
|
|
const ade::NodeHandle &nh,
|
|
const cv::GKernelImpl &ii) override {
|
|
using namespace cv::gimpl;
|
|
// FIXME: Introduce a DNNBackend interface which'd specify
|
|
// the framework for this???
|
|
GOVModel gm(gr);
|
|
auto &np = gm.metadata(nh).get<NetworkParams>();
|
|
auto &pp = cv::util::any_cast<ParamDesc>(np.opaque);
|
|
const auto &ki = cv::util::any_cast<KImpl>(ii.opaque);
|
|
|
|
GModel::Graph model(gr);
|
|
auto& op = model.metadata(nh).get<Op>();
|
|
|
|
// NB: In case generic infer, info about in/out names is stored in operation (op.params)
|
|
if (pp.is_generic)
|
|
{
|
|
auto& info = cv::util::any_cast<cv::detail::InOutInfo>(op.params);
|
|
pp.input_names = info.in_names;
|
|
pp.output_names = info.out_names;
|
|
pp.num_in = info.in_names.size();
|
|
pp.num_out = info.out_names.size();
|
|
}
|
|
|
|
gm.metadata(nh).set(OVUnit{pp});
|
|
gm.metadata(nh).set(OVCallable{ki.run});
|
|
gm.metadata(nh).set(CustomMetaFunction{ki.customMetaFunc});
|
|
}
|
|
|
|
virtual EPtr compile(const ade::Graph &graph,
|
|
const cv::GCompileArgs &compileArgs,
|
|
const std::vector<ade::NodeHandle> &nodes) const override {
|
|
return EPtr{new cv::gimpl::ov::GOVExecutable(graph, compileArgs, nodes)};
|
|
}
|
|
|
|
virtual cv::GKernelPackage auxiliaryKernels() const override {
|
|
return cv::gapi::kernels< cv::gimpl::ov::Infer
|
|
, cv::gimpl::ov::InferROI
|
|
, cv::gimpl::ov::InferList
|
|
, cv::gimpl::ov::InferList2 >();
|
|
}
|
|
|
|
virtual bool controlsMerge() const override {
|
|
return true;
|
|
}
|
|
|
|
virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &,
|
|
const ade::NodeHandle &,
|
|
const ade::NodeHandle &,
|
|
const ade::NodeHandle &) const override {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
cv::gapi::GBackend cv::gapi::ov::backend() {
|
|
static cv::gapi::GBackend this_backend(std::make_shared<GOVBackendImpl>());
|
|
return this_backend;
|
|
}
|
|
|
|
static std::vector<::ov::InferRequest>
|
|
createInferRequests(::ov::CompiledModel &compiled_model,
|
|
size_t num_infer_requests) {
|
|
std::vector<::ov::InferRequest> infer_requests;
|
|
for (size_t i = 0; i < num_infer_requests; ++i) {
|
|
infer_requests.push_back(compiled_model.create_infer_request());
|
|
}
|
|
return infer_requests;
|
|
}
|
|
|
|
// GOVExecutable implementation //////////////////////////////////////////////
|
|
cv::gimpl::ov::GOVExecutable::GOVExecutable(const ade::Graph &g,
|
|
const cv::GCompileArgs &compileArgs,
|
|
const std::vector<ade::NodeHandle> &nodes)
|
|
: m_g(g), m_gm(m_g) {
|
|
|
|
m_options.inference_only =
|
|
cv::gapi::getCompileArg<cv::gapi::wip::ov::benchmark_mode>(compileArgs).has_value();
|
|
// FIXME: Currently this backend is capable to run a single inference node only.
|
|
// Need to extend our island fusion with merge/not-to-merge decision making parametrization
|
|
GConstGOVModel ovm(g);
|
|
|
|
for (auto &nh : nodes) {
|
|
switch (m_gm.metadata(nh).get<NodeType>().t) {
|
|
case NodeType::OP:
|
|
if (this_nh == nullptr) {
|
|
this_nh = nh;
|
|
const auto &unit = ovm.metadata(this_nh).get<OVUnit>();
|
|
compiled = const_cast<OVUnit&>(unit).compile();
|
|
m_reqPool.reset(new RequestPool(createInferRequests(
|
|
compiled.compiled_model, unit.params.nireq)));
|
|
}
|
|
else
|
|
util::throw_error(std::logic_error("Multi-node inference is not supported!"));
|
|
break;
|
|
|
|
case NodeType::DATA: {
|
|
m_dataNodes.push_back(nh);
|
|
const auto &desc = m_gm.metadata(nh).get<Data>();
|
|
if (desc.storage == Data::Storage::CONST_VAL) {
|
|
util::throw_error(std::logic_error("No const data please!"));
|
|
}
|
|
if (desc.storage == Data::Storage::INTERNAL) {
|
|
util::throw_error(std::logic_error("No internal data please!"));
|
|
}
|
|
break;
|
|
}
|
|
default: util::throw_error(std::logic_error("Unsupported NodeType type"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void cv::gimpl::ov::GOVExecutable::run(cv::gimpl::GIslandExecutable::IInput &in,
|
|
cv::gimpl::GIslandExecutable::IOutput &out) {
|
|
std::vector<InObj> input_objs;
|
|
std::vector<OutObj> output_objs;
|
|
|
|
const auto &in_desc = in.desc();
|
|
auto in_msg = in.get();
|
|
|
|
if (cv::util::holds_alternative<cv::gimpl::EndOfStream>(in_msg))
|
|
{
|
|
m_reqPool->waitAll();
|
|
out.post(cv::gimpl::EndOfStream{});
|
|
return;
|
|
}
|
|
|
|
GAPI_Assert(cv::util::holds_alternative<cv::GRunArgs>(in_msg));
|
|
const auto in_vector = cv::util::get<cv::GRunArgs>(in_msg);
|
|
cv::GRunArg::Meta stub_meta;
|
|
for (auto &&in_arg : in_vector)
|
|
{
|
|
stub_meta.insert(in_arg.meta.begin(), in_arg.meta.end());
|
|
}
|
|
|
|
input_objs.reserve(in_desc.size());
|
|
for (auto &&it: ade::util::zip(ade::util::toRange(in_desc),
|
|
ade::util::toRange(in_vector)))
|
|
{
|
|
input_objs.emplace_back(std::get<0>(it), std::get<1>(it));
|
|
}
|
|
|
|
const auto &out_desc = out.desc();
|
|
output_objs.reserve(out_desc.size());
|
|
for (auto &&it: ade::util::indexed(ade::util::toRange(out_desc)))
|
|
{
|
|
output_objs.emplace_back(ade::util::value(it),
|
|
out.get(ade::util::checked_cast<int>(ade::util::index(it))));
|
|
}
|
|
|
|
GConstGOVModel giem(m_g);
|
|
const auto &uu = giem.metadata(this_nh).get<OVUnit>();
|
|
const auto &op = m_gm.metadata(this_nh).get<Op>();
|
|
|
|
auto ctx = std::make_shared<OVCallContext>(uu, out, op.args, op.outs,
|
|
std::move(stub_meta), std::move(input_objs), std::move(output_objs), m_options);
|
|
|
|
const auto &kk = giem.metadata(this_nh).get<OVCallable>();
|
|
|
|
try {
|
|
kk.run(ctx, *m_reqPool);
|
|
} catch (...) {
|
|
auto eptr = std::current_exception();
|
|
for (auto i : ade::util::iota(ctx->uu.params.num_out))
|
|
{
|
|
auto output = ctx->output(i);
|
|
ctx->out.meta(output, ctx->getMeta());
|
|
ctx->out.post(std::move(output), eptr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_gm.metadata().contains<Streaming>()) {
|
|
m_reqPool->waitAll();
|
|
}
|
|
}
|
|
|
|
#else // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
|
|
|
cv::gapi::GBackend cv::gapi::ov::backend() {
|
|
// Still provide this symbol to avoid linking issues
|
|
util::throw_error(std::runtime_error("G-API has been compiled without OpenVINO support"));
|
|
}
|
|
|
|
#endif // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|