Merge pull request #23786 from TolyaTalamanov:at/expose-preprocessing-to-ie-backend

G-API: Expose explicit preprocessing for IE Backend #23786

### Pull Request Readiness Checklist

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

- [ ] I agree to contribute to the project under Apache 2 License.
- [ ] 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
- [ ] 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
pull/23795/head
Anatoliy Talamanov 2 years ago committed by GitHub
parent b522148bd9
commit b854d4ecd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 143
      modules/gapi/include/opencv2/gapi/infer/ie.hpp
  2. 480
      modules/gapi/src/backends/ie/giebackend.cpp
  3. 69
      modules/gapi/test/infer/gapi_infer_ie_test.cpp

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // of this distribution and at http://opencv.org/license.html.
// //
// Copyright (C) 2019-2021 Intel Corporation // Copyright (C) 2019-2023 Intel Corporation
#ifndef OPENCV_GAPI_INFER_IE_HPP #ifndef OPENCV_GAPI_INFER_IE_HPP
#define OPENCV_GAPI_INFER_IE_HPP #define OPENCV_GAPI_INFER_IE_HPP
@ -55,6 +55,21 @@ using IEConfig = std::map<std::string, std::string>;
enum InferMode {Sync, Async}; enum InferMode {Sync, Async};
namespace detail { namespace detail {
template <typename T>
using AttrMap = std::map<std::string, T>;
// NB: This type is 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 ParamDesc {
std::string model_path; std::string model_path;
std::string weights_path; std::string weights_path;
@ -103,7 +118,11 @@ struct ParamDesc {
using PrecisionVariantT = cv::util::variant<cv::util::monostate, using PrecisionVariantT = cv::util::variant<cv::util::monostate,
PrecisionT, PrecisionT,
PrecisionMapT>; PrecisionMapT>;
PrecisionVariantT output_precision; PrecisionVariantT output_precision;
LayerVariantAttr<std::string> input_layout;
LayerVariantAttr<std::string> output_layout;
LayerVariantAttr<int> interpolation;
}; };
} // namespace detail } // namespace detail
@ -150,6 +169,9 @@ public:
, {} , {}
, {} , {}
, InferMode::Async , InferMode::Async
, {}
, {}
, {}
, {} } { , {} } {
}; };
@ -176,6 +198,9 @@ public:
, {} , {}
, {} , {}
, InferMode::Async , InferMode::Async
, {}
, {}
, {}
, {} } { , {} } {
}; };
@ -412,6 +437,80 @@ public:
return *this; return *this;
} }
/** @brief Specifies the input layout for model.
The function is used to set an input layout for model.
@param layout Layout in string representation ("NCHW", "NHWC", etc)
will be applied to all input layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputLayout(std::string layout) {
desc.input_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding input layer
and its layout in string representation ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgInputLayout(detail::AttrMap<std::string> layout_map) {
desc.input_layout = std::move(layout_map);
return *this;
}
/** @brief Specifies the output layout for model.
The function is used to set an output layout for model.
@param layout Layout in string representation ("NCHW", "NHWC", etc)
will be applied to all output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputLayout(std::string layout) {
desc.output_layout = std::move(layout);
return *this;
}
/** @overload
@param layout_map Map of pairs: name of corresponding output layer
and its layout in string representation ("NCHW", "NHWC", etc)
@return reference to this parameter structure.
*/
Params<Net>&
cfgOutputLayout(detail::AttrMap<std::string> layout_map) {
desc.output_layout = std::move(layout_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_LINEAR, #INTER_AREA.
@return reference to this parameter structure.
*/
Params<Net>& cfgResize(int interpolation) {
desc.interpolation = 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) {
desc.interpolation = std::move(interpolation);
return *this;
}
// BEGIN(G-API's network parametrization API) // BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ie::backend(); } GBackend backend() const { return cv::gapi::ie::backend(); }
std::string tag() const { return Net::tag(); } std::string tag() const { return Net::tag(); }
@ -446,7 +545,7 @@ public:
const std::string &device) const std::string &device)
: desc{ model, weights, device, {}, {}, {}, 0u, 0u, : desc{ model, weights, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u, detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u,
{}, {}, {}, {}, InferMode::Async, {} }, {}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} },
m_tag(tag) { m_tag(tag) {
}; };
@ -464,7 +563,7 @@ public:
const std::string &device) const std::string &device)
: desc{ model, {}, device, {}, {}, {}, 0u, 0u, : desc{ model, {}, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u, detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u,
{}, {}, {}, {}, InferMode::Async, {} }, {}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} },
m_tag(tag) { m_tag(tag) {
}; };
@ -556,6 +655,44 @@ public:
return *this; return *this;
} }
/** @see ie::Params::cfgInputLayout */
Params& cfgInputLayout(std::string layout) {
desc.input_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgInputLayout(detail::AttrMap<std::string> layout_map) {
desc.input_layout = std::move(layout_map);
return *this;
}
/** @see ie::Params::cfgOutputLayout */
Params& cfgOutputLayout(std::string layout) {
desc.output_layout = std::move(layout);
return *this;
}
/** @overload */
Params&
cfgOutputLayout(detail::AttrMap<std::string> layout_map) {
desc.output_layout = std::move(layout_map);
return *this;
}
/** @see ie::Params::cfgResize */
Params& cfgResize(int interpolation) {
desc.interpolation = interpolation;
return *this;
}
/** @overload */
Params& cfgResize(detail::AttrMap<int> interpolation) {
desc.interpolation = std::move(interpolation);
return *this;
}
// BEGIN(G-API's network parametrization API) // BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ie::backend(); } GBackend backend() const { return cv::gapi::ie::backend(); }
std::string tag() const { return m_tag; } std::string tag() const { return m_tag; }

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // of this distribution and at http://opencv.org/license.html.
// //
// Copyright (C) 2018-2022 Intel Corporation // Copyright (C) 2018-2023 Intel Corporation
#include "precomp.hpp" #include "precomp.hpp"
@ -71,6 +71,28 @@ namespace IE = InferenceEngine;
namespace { namespace {
IE::Layout toIE(const std::string &layout) {
const std::unordered_map<std::string, IE::Layout> layouts = {
{"NCDHW", IE::Layout::NCDHW},
{"NDHWC", IE::Layout::NDHWC},
{"NHWC" , IE::Layout::NHWC },
{"NCHW" , IE::Layout::NCHW },
{"CHW" , IE::Layout::CHW },
{"HWC" , IE::Layout::HWC },
{"HW" , IE::Layout::HW },
{"NC" , IE::Layout::NC },
{"CN" , IE::Layout::CN },
{"C" , IE::Layout::C },
};
const auto it = layouts.find(layout);
if (it == layouts.end()) {
cv::util::throw_error(
std::logic_error("IE Backend: Unsupported layout: " + layout));
}
return it->second;
};
inline IE::ROI toIE(const cv::Rect &rc) { inline IE::ROI toIE(const cv::Rect &rc) {
return IE::ROI return IE::ROI
{ 0u { 0u
@ -130,52 +152,90 @@ inline int toCV(IE::Precision prec) {
return -1; return -1;
} }
// NB: In short: Tensor - ND or 2D + precision != U8. inline IE::ResizeAlgorithm toIEInterp(int interpolation) {
cv::gapi::ie::TraitAs clarifyTrait(const cv::GMatDesc &mat_desc, switch (interpolation) {
const IE::TensorDesc &tensor_desc) { case cv::INTER_LINEAR: return IE::RESIZE_BILINEAR;
// NB: This check does not include 2D matrices like {32, 16}, case cv::INTER_AREA: return IE::RESIZE_AREA;
// which also falls under the category of tensors. default: GAPI_Error("IE Backend: Unsupported resize algorithm");
// The reason for this is that RGB images are also considered 2D }
// as the channel component is not taken into account in this case. // Unreachable code
if (mat_desc.isND() || mat_desc.planar) { GAPI_Assert(false);
return cv::gapi::ie::TraitAs::TENSOR; }
}
// NB: If user provided 2D data in U8 precision template <typename Attr>
// and network expects NHWC/NCHW layout - data is image (most likely). using AttrMap = cv::gapi::ie::detail::AttrMap<Attr>;
const auto layout = tensor_desc.getLayout();
if (layout == IE::Layout::NCHW || layout == IE::Layout::NHWC) { template <typename Attr>
if (mat_desc.depth == CV_8U) { using LayerVariantAttr = cv::gapi::ie::detail::LayerVariantAttr<Attr>;
return cv::gapi::ie::TraitAs::IMAGE;
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("IE Backend: Failed to"
" find layer with name: " + p.first));
}
} }
// NB: 2D data with precision != U8 provided for 4D layout. } else if (cv::util::holds_alternative<Attr>(layer_attr)) {
std::stringstream ss; // NB: Broadcast value to all layers.
ss << "IE Backend: An inconsistency has been detected between" auto elem = cv::util::get<Attr>(layer_attr);
" the provided data: " << mat_desc << for (auto &&layer_name : layer_names) {
" and the network layout: " << layout; map.emplace(layer_name, elem);
cv::util::throw_error(std::logic_error(ss.str())); }
} }
// NB: Otherwise trait is TENSOR. return map;
// If there is an inconsistency between the data dimensions }
// and the network layout, the "setBlob" will fail.
// TODO: Move it to some common place
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));
}
static bool isImage(const cv::GMatDesc &desc,
const IE::SizeVector &model_dims) {
return (model_dims.size() == 4u) &&
(!desc.isND()) /* dims == 2 */ &&
(desc.chan == 1 || desc.chan == 3) &&
(desc.size.height != 1 && desc.size.width != 1) &&
(desc.depth == CV_8U);
}
cv::gapi::ie::TraitAs clarifyTrait(const cv::GMatDesc &mat_desc,
const IE::SizeVector &model_dims) {
if (isImage(mat_desc, model_dims)) {
return cv::gapi::ie::TraitAs::IMAGE;
}
return cv::gapi::ie::TraitAs::TENSOR; return cv::gapi::ie::TraitAs::TENSOR;
} }
cv::gapi::ie::TraitAs clarifyTrait(const cv::GMetaArg &meta, cv::gapi::ie::TraitAs clarifyTrait(const cv::GMetaArg &meta,
const IE::TensorDesc &tensor_desc) { const IE::SizeVector &model_dims) {
// NB: All media formats: BGR, NV12, Gray // NB: All media formats: BGR, NV12, Gray
// are traited as image. // are traited as image.
if (cv::util::holds_alternative<cv::GFrameDesc>(meta)) { if (cv::util::holds_alternative<cv::GFrameDesc>(meta)) {
return cv::gapi::ie::TraitAs::IMAGE; return cv::gapi::ie::TraitAs::IMAGE;
} }
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(meta)); GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(meta));
return clarifyTrait(cv::util::get<cv::GMatDesc>(meta), tensor_desc); return clarifyTrait(cv::util::get<cv::GMatDesc>(meta), model_dims);
} }
inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) { inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
const auto &sz = mat.size; const auto &sz = mat.size;
// NB: For some reason RGB image is 2D image
// (since channel component is not counted here).
// Note: regular 2D vectors also fall into this category
if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE) if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE)
{ {
// NB: This logic is mainly taken from IE samples // NB: This logic is mainly taken from IE samples
@ -196,8 +256,72 @@ inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
return IE::TensorDesc(toIE(mat.depth()), toIE(sz), toIELayout(sz.dims())); return IE::TensorDesc(toIE(mat.depth()), toIE(sz), toIELayout(sz.dims()));
} }
inline IE::Blob::Ptr wrapIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) { // NB: Inference dimmensions always follow NCDHW order
const auto tDesc = toIE(mat, hint); // even though the real layout is different.
// E.g if user provided Mat({1, 240, 320, 3}, CV_8U) + NHWC layout
// need to create Blob(U8, {1, 3, 240, 320}, NHWC).
inline IE::SizeVector toIEDims(const IE::SizeVector &dims,
const IE::Layout layout) {
switch (layout) {
case IE::Layout::NDHWC: // NCDHW
return {dims[0], dims[4], dims[1], dims[2], dims[3]};
case IE::Layout::NHWC: // NCHW
return {dims[0], dims[3], dims[1], dims[2]};
case IE::Layout::HWC: // CHW
return {dims[2], dims[0], dims[1]};
default: return dims;
}
GAPI_Assert(false);
}
// NB: Inference dimmensions always follow NCDHW order
// even though the real layout is different.
// E.g if U8 blob has {1, 3, 240, 320} dims and NHWC layout
// need to create cv::Mat({1, 240, 320, 3}, CV_8U);
inline std::vector<int> toCVDims(const std::vector<int> &dims,
const IE::Layout layout) {
switch (layout) {
case IE::Layout::NDHWC: // NCDHW
return {dims[0], dims[2], dims[3], dims[4], dims[1]};
case IE::Layout::NHWC: // NCHW
return {dims[0], dims[2], dims[3], dims[1]};
case IE::Layout::HWC: // CHW
return {dims[1], dims[2], dims[0]};
default: return dims;
}
GAPI_Assert(false);
}
inline IE::TensorDesc toIE(const cv::Mat &mat,
const cv::gapi::ie::TraitAs hint,
const IE::Layout layout) {
const auto &sz = mat.size;
if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE)
{
// NB: This logic is mainly taken from IE samples
const size_t channels = mat.channels();
const size_t height = mat.size().height;
const size_t width = mat.size().width;
const size_t strideH = mat.step1();
IE::BlockingDesc bdesc({1, height, width, channels} /* blocking dims */,
{0, 2, 3, 1} /* order for NHWC */,
0 /* offset */,
{0, 0, 0, 0} /* offsets for dims */,
{strideH * height, strideH, channels, 1} /* strides for dims */);
return IE::TensorDesc(toIE(mat.depth()),
IE::SizeVector{1, channels, height, width}, bdesc);
}
return IE::TensorDesc(toIE(mat.depth()),
toIEDims(toIE(sz), layout),
layout);
}
inline IE::Blob::Ptr wrapIE(const cv::Mat &mat,
cv::gapi::ie::TraitAs hint,
const IE::Layout layout = IE::Layout::ANY) {
const auto tDesc = toIE(mat, hint, layout);
switch (mat.depth()) { switch (mat.depth()) {
// NB: Seems there's no way to create an untyped (T-less) Blob::Ptr // NB: Seems there's no way to create an untyped (T-less) Blob::Ptr
// in IE given only precision via TensorDesc. So we have to do this: // in IE given only precision via TensorDesc. So we have to do this:
@ -344,6 +468,7 @@ struct IEUnit {
}; };
InputFramesDesc net_input_params; InputFramesDesc net_input_params;
std::unordered_map<std::string, cv::gapi::ie::TraitAs> inputs_type;
explicit IEUnit(const cv::gapi::ie::detail::ParamDesc &pp) explicit IEUnit(const cv::gapi::ie::detail::ParamDesc &pp)
: params(pp) { : params(pp) {
@ -522,6 +647,8 @@ public:
cv::GRunArgP output (std::size_t idx); cv::GRunArgP output (std::size_t idx);
cv::Mat& outMatR(std::size_t idx); cv::Mat& outMatR(std::size_t idx);
cv::gapi::ie::TraitAs getInputType(const std::string &layer_name) const;
const IEUnit &uu; const IEUnit &uu;
cv::gimpl::GIslandExecutable::IOutput &out; cv::gimpl::GIslandExecutable::IOutput &out;
@ -565,6 +692,9 @@ private:
// keep alive preprocessed frames // keep alive preprocessed frames
std::mutex keep_alive_frames_mutex; std::mutex keep_alive_frames_mutex;
std::unordered_map<req_key_t, cv::MediaFrame> keep_alive_pp_frames; std::unordered_map<req_key_t, cv::MediaFrame> keep_alive_pp_frames;
// NB: Hint to wrap input data properly into IE::Blob (see: wrapIE)
std::unordered_map<std::string, cv::gapi::ie::TraitAs> input_type;
}; };
IECallContext::IECallContext(const IEUnit & unit, IECallContext::IECallContext(const IEUnit & unit,
@ -599,6 +729,16 @@ IECallContext::IECallContext(const IEUnit &
} }
} }
cv::gapi::ie::TraitAs
IECallContext::getInputType(const std::string &layer_name) const {
const auto it = uu.inputs_type.find(layer_name);
if (it == uu.inputs_type.end()) {
cv::util::throw_error(std::logic_error(
"Failed to find input type for layer: \"" + layer_name + "\""));
}
return it->second;
}
const cv::GArgs& IECallContext::inArgs() const { const cv::GArgs& IECallContext::inArgs() const {
return m_args; return m_args;
} }
@ -773,7 +913,8 @@ cv::MediaFrame preprocess_frame_impl(cv::MediaFrame &&in_frame, const std::strin
inline IE::Blob::Ptr extractBlob(IECallContext& ctx, inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
std::size_t i, std::size_t i,
cv::gapi::ie::TraitAs hint, const cv::gapi::ie::TraitAs hint,
const IE::Layout &layout,
const std::string& layer_name, const std::string& layer_name,
const cv::util::optional<cv::Rect> &opt_roi, const cv::util::optional<cv::Rect> &opt_roi,
cv::MediaFrame* out_keep_alive_frame = nullptr, cv::MediaFrame* out_keep_alive_frame = nullptr,
@ -821,7 +962,7 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
return wrapIE(*(ctx.views.back()), frame.desc()); return wrapIE(*(ctx.views.back()), frame.desc());
} }
case cv::GShape::GMAT: { case cv::GShape::GMAT: {
return wrapIE(ctx.inMat(i), hint); return wrapIE(ctx.inMat(i), hint, layout);
} }
default: default:
GAPI_Assert("Unsupported input shape for IE backend"); GAPI_Assert("Unsupported input shape for IE backend");
@ -829,7 +970,6 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
GAPI_Error("InternalError"); GAPI_Error("InternalError");
} }
static void setBlob(InferenceEngine::InferRequest& req, static void setBlob(InferenceEngine::InferRequest& req,
const std::string& layer_name, const std::string& layer_name,
const IE::Blob::Ptr& blob, const IE::Blob::Ptr& blob,
@ -1203,79 +1343,110 @@ static void configureInputReshapeByImage(const IE::InputInfo::Ptr& ii,
input_reshape_table.emplace(layer_name, input_dims); input_reshape_table.emplace(layer_name, input_dims);
} }
// NB: This function is used in order to configure static void cfgInputPrecision(const IE::InputInfo::Ptr& ii, const cv::GMetaArg mm) {
// preprocessing for "Load" case networks.
static void configureInputInfo(const IE::InputInfo::Ptr& ii, const cv::GMetaArg mm) {
switch (mm.index()) { switch (mm.index()) {
case cv::GMetaArg::index_of<cv::GMatDesc>(): case cv::GMetaArg::index_of<cv::GMatDesc>(): {
{
const auto &desc = util::get<cv::GMatDesc>(mm); const auto &desc = util::get<cv::GMatDesc>(mm);
ii->setPrecision(toIE(desc.depth)); ii->setPrecision(toIE(desc.depth));
// NB: Configure resize only for images.
const auto trait = clarifyTrait(desc, ii->getTensorDesc());
if (trait == cv::gapi::ie::TraitAs::IMAGE) {
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
}
break; break;
} }
case cv::GMetaArg::index_of<cv::GFrameDesc>(): case cv::GMetaArg::index_of<cv::GFrameDesc>():
{
const auto &meta = util::get<cv::GFrameDesc>(mm);
switch (meta.fmt) {
case cv::MediaFormat::NV12:
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
break;
case cv::MediaFormat::BGR:
// NB: Do nothing
break;
case cv::MediaFormat::GRAY:
// NB: Do nothing
break;
default:
GAPI_Error("Unsupported media format for IE backend");
}
ii->setPrecision(toIE(CV_8U)); ii->setPrecision(toIE(CV_8U));
// NB: Always configure resize because media formats are images.
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
break; break;
}
default: default:
util::throw_error(std::runtime_error("Unsupported input meta for IE backend")); util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
} }
} }
static void cfgImagePreprocessing(const IE::InputInfo::Ptr &ii,
const cv::GMetaArg &mm,
const IE::ResizeAlgorithm interp) {
if (!cv::util::holds_alternative<cv::GMatDesc>(mm) &&
!cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
}
ii->getPreProcess().setResizeAlgorithm(interp);
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
const auto &meta = util::get<cv::GFrameDesc>(mm);
if (meta.fmt == cv::MediaFormat::NV12) {
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
}
}
}
// NB: This function is used in order to configure // NB: This function is used in order to configure
// preprocessing for "Import" case networks. // preprocessing for "Load" case networks.
static IE::PreProcessInfo configurePreProcInfo(const IE::InputInfo::CPtr& ii, static void cfgInputPreprocessing(const cv::gapi::ie::TraitAs trait,
const cv::GMetaArg& mm) { const IE::InputInfo::Ptr &ii,
IE::PreProcessInfo info; const cv::GMetaArg &mm,
switch (mm.index()) { const std::string &layer_name,
// NB: Note that it doesn't specify precision. const AttrMap<std::string> &layout_map,
case cv::GMetaArg::index_of<cv::GMatDesc>(): const AttrMap<int> &interp_map) {
{ cfgInputPrecision(ii, mm);
// NB: Configure resize only for images. const auto explicit_input_layout = lookUp(layout_map, layer_name);
const auto &desc = cv::util::get<cv::GMatDesc>(mm); const auto explicit_resize = lookUp(interp_map, layer_name);
const auto trait = clarifyTrait(desc, ii->getTensorDesc()); if (trait == cv::gapi::ie::TraitAs::IMAGE) {
if (trait == cv::gapi::ie::TraitAs::IMAGE) { // NB: Image case - preprocessing is configured automatically.
info.setResizeAlgorithm(IE::RESIZE_BILINEAR); GAPI_LOG_DEBUG(NULL, "IE Backend: Input: \"" <<
} layer_name << " " << mm << "\" is image.");
break; // NB: BlockingDesc is used instead (see wrapIE)
if (explicit_input_layout) {
util::throw_error(std::logic_error("Input data provided for layer: \"" +
layer_name + "\" is recognized as \"image\". Explicitly" +
" specified layout is prohibited."));
} }
// NB: Note that it doesn't specify precision. const auto interp = explicit_resize ? toIEInterp(*explicit_resize)
case cv::GMetaArg::index_of<cv::GFrameDesc>(): : IE::RESIZE_BILINEAR;
{ cfgImagePreprocessing(ii, mm, interp);
const auto &desc = cv::util::get<cv::GFrameDesc>(mm); } else {
if (desc.fmt == cv::MediaFormat::NV12) { // NB: Tensor case - preprocessing is configured only if user asked.
info.setColorFormat(IE::ColorFormat::NV12); GAPI_LOG_DEBUG(NULL, "IE Backend: Input: \"" <<
} layer_name << "\" " << mm << " is tensor.");
// NB: Always configure resize because media formats are images. if (explicit_input_layout) {
info.setResizeAlgorithm(IE::RESIZE_BILINEAR); GAPI_LOG_DEBUG(NULL, "IE Backend: Set input layout \"" <<
break; *explicit_input_layout << "\" for layer \"" << layer_name << "\"");
ii->setLayout(toIE(*explicit_input_layout));
}
if (explicit_resize) {
GAPI_LOG_DEBUG(NULL, "IE Backend: Set resize for layer \"" << layer_name << "\"");
ii->getPreProcess().setResizeAlgorithm(toIEInterp(*explicit_resize));
} }
default:
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
} }
}
static IE::PreProcessInfo createImagePreProcInfo(const cv::GMetaArg &mm,
const IE::ResizeAlgorithm interp) {
if (!cv::util::holds_alternative<cv::GMatDesc>(mm) &&
!cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
}
IE::PreProcessInfo info;
info.setResizeAlgorithm(interp);
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
const auto &meta = util::get<cv::GFrameDesc>(mm);
if (meta.fmt == cv::MediaFormat::NV12) {
info.setColorFormat(IE::ColorFormat::NV12);
}
}
return info;
}
// NB: This function is used in order to create
// preprocessing for "Import" case networks.
static IE::PreProcessInfo createPreProcInfo(const cv::gapi::ie::TraitAs trait,
const cv::GMetaArg& mm,
const cv::optional<int> explicit_resize) {
if (trait == cv::gapi::ie::TraitAs::IMAGE) {
const auto interp = explicit_resize ? toIEInterp(*explicit_resize)
: IE::RESIZE_BILINEAR;
return createImagePreProcInfo(mm, interp);
}
// NB: In case "tensor" only resize can't be spefied for "import" models.
IE::PreProcessInfo info;
if (explicit_resize) {
info.setResizeAlgorithm(toIEInterp(*explicit_resize));
}
return info; return info;
} }
@ -1301,6 +1472,13 @@ static void configureOutputPrecision(const IE::OutputsDataMap &outputs
); );
} }
static void configureOutputLayout(const IE::OutputsDataMap &outputs_info,
const AttrMap<std::string> &output_layout) {
for (const auto it : output_layout) {
outputs_info.at(it.first)->setLayout(toIE(it.second));
}
}
// NB: This is a callback used by async infer // NB: This is a callback used by async infer
// to post outputs blobs (cv::GMat's). // to post outputs blobs (cv::GMat's).
static void PostOutputs(InferenceEngine::InferRequest &request, static void PostOutputs(InferenceEngine::InferRequest &request,
@ -1420,6 +1598,10 @@ struct Infer: public cv::detail::KernelTag {
GAPI_Assert(uu.params.input_names.size() == in_metas.size() GAPI_Assert(uu.params.input_names.size() == in_metas.size()
&& "Known input layers count doesn't match input meta count"); && "Known input layers count doesn't match input meta count");
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
uu.params.input_names);
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
uu.params.input_names);
// NB: Configuring input/output precision and network reshape must be done // NB: Configuring input/output precision and network reshape must be done
// only in the loadNetwork case. // only in the loadNetwork case.
using namespace cv::gapi::ie::detail; using namespace cv::gapi::ie::detail;
@ -1429,21 +1611,24 @@ struct Infer: public cv::detail::KernelTag {
ade::util::toRange(in_metas))) { ade::util::toRange(in_metas))) {
const auto &input_name = std::get<0>(it); const auto &input_name = std::get<0>(it);
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
const auto & mm = std::get<1>(it); const auto &mm = std::get<1>(it);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) != if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) { uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table); configureInputReshapeByImage(ii, mm, input_reshape_table);
} }
const auto trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
// FIXME: This is the only place where information about input type
// can be stored for the futher execution.
const_cast<IEUnit&>(uu).inputs_type.emplace(input_name, trait);
cfgInputPreprocessing(trait, ii, mm, input_name,
input_layout, interpolation);
// NB: configure input param for further preproc // NB: configure input param for further preproc
if (uu.net_input_params.is_applicable(mm)) { if (uu.net_input_params.is_applicable(mm)) {
const_cast<IEUnit::InputFramesDesc &>(uu.net_input_params) const_cast<IEUnit::InputFramesDesc &>(uu.net_input_params)
.set_param(input_name, ii->getTensorDesc()); .set_param(input_name, ii->getTensorDesc());
} }
} }
for (auto &&p : uu.params.const_inputs) { for (auto &&p : uu.params.const_inputs) {
const auto ii = inputs.at(p.first); const auto ii = inputs.at(p.first);
ii->setPrecision(toIE(p.second.first.depth())); ii->setPrecision(toIE(p.second.first.depth()));
@ -1455,6 +1640,10 @@ struct Infer: public cv::detail::KernelTag {
if (!input_reshape_table.empty()) { if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table); const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
} }
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
uu.params.output_names);
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
} else { } else {
GAPI_Assert(uu.params.kind == ParamDesc::Kind::Import); GAPI_Assert(uu.params.kind == ParamDesc::Kind::Import);
@ -1466,7 +1655,13 @@ struct Infer: public cv::detail::KernelTag {
const auto &input_name = std::get<0>(it); const auto &input_name = std::get<0>(it);
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
const auto & mm = std::get<1>(it); const auto & mm = std::get<1>(it);
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm)); const auto trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
// FIXME: This is the only place where information about input type
// can be stored for the futher execution.
const_cast<IEUnit&>(uu).inputs_type.emplace(input_name, trait);
const auto explicit_resize = lookUp(interpolation, input_name);
non_const_prepm->emplace(
input_name, createPreProcInfo(trait, mm, explicit_resize));
// NB: configure input param for further preproc // NB: configure input param for further preproc
if (uu.net_input_params.is_applicable(mm)) { if (uu.net_input_params.is_applicable(mm)) {
@ -1488,7 +1683,7 @@ struct Infer: public cv::detail::KernelTag {
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc(); : uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
cv::GMatDesc outm(toCV(desc.getPrecision()), cv::GMatDesc outm(toCV(desc.getPrecision()),
toCV(desc.getDims())); toCVDims(toCV(desc.getDims()), desc.getLayout()));
result.emplace_back(outm); result.emplace_back(outm);
} }
return result; return result;
@ -1504,15 +1699,10 @@ struct Infer: public cv::detail::KernelTag {
// - assumes all inputs/outputs are always Mats // - assumes all inputs/outputs are always Mats
for (auto i : ade::util::iota(ctx->uu.params.num_in)) { for (auto i : ade::util::iota(ctx->uu.params.num_in)) {
const auto& layer_name = ctx->uu.params.input_names[i]; const auto& layer_name = ctx->uu.params.input_names[i];
auto layout = const auto hint = ctx->getInputType(layer_name);
ctx->uu.this_network.GetInputsInfo(). const auto layout = req.GetBlob(layer_name)->getTensorDesc().getLayout();
at(layer_name)->getTensorDesc().getLayout();
auto hint =
(layout == IE::Layout::NCHW || layout == IE::Layout::NHWC)
? cv::gapi::ie::TraitAs::IMAGE : cv::gapi::ie::TraitAs::TENSOR;
IE::Blob::Ptr this_blob = extractBlob(*ctx, i, hint, IE::Blob::Ptr this_blob = extractBlob(*ctx, i, hint,
layer_name, layout, layer_name,
cv::util::optional<cv::Rect>{}); cv::util::optional<cv::Rect>{});
setBlob(req, layer_name, this_blob, *ctx); setBlob(req, layer_name, this_blob, *ctx);
} }
@ -1552,7 +1742,7 @@ struct InferROI: public cv::detail::KernelTag {
if (cv::util::holds_alternative<cv::GMatDesc>(mm) || if (cv::util::holds_alternative<cv::GMatDesc>(mm) ||
cv::util::holds_alternative<cv::GFrameDesc>(mm)) { cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
const auto trait = clarifyTrait(mm, tensor_desc); const auto trait = clarifyTrait(mm, tensor_desc.getDims());
if (trait != cv::gapi::ie::TraitAs::IMAGE) { if (trait != cv::gapi::ie::TraitAs::IMAGE) {
util::throw_error(std::runtime_error( util::throw_error(std::runtime_error(
"IE Backend: Only image is supported" "IE Backend: Only image is supported"
@ -1566,16 +1756,22 @@ struct InferROI: public cv::detail::KernelTag {
// NB: Configuring input precision and network reshape must be done // NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case. // only in the loadNetwork case.
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
uu.params.input_names);
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
uu.params.input_names);
const auto trait = cv::gapi::ie::TraitAs::IMAGE;
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) { if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
// 0th is ROI, 1st is input image // 0th is ROI, 1st is input image
auto inputs = uu.net.getInputsInfo(); auto inputs = uu.net.getInputsInfo();
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) != if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) { uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table); configureInputReshapeByImage(ii, mm, input_reshape_table);
} }
cfgInputPreprocessing(trait, ii, mm, input_name,
input_layout, interpolation);
// FIXME: This isn't the best place to call reshape function. // FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network, // Сorrect solution would be to do this in compile() method of network,
@ -1594,6 +1790,9 @@ struct InferROI: public cv::detail::KernelTag {
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth())); inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
} }
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
uu.params.output_names);
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
} else { } else {
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
@ -1601,8 +1800,9 @@ struct InferROI: public cv::detail::KernelTag {
// FIXME: This isn't the best place to collect PreProcMap. // FIXME: This isn't the best place to collect PreProcMap.
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map); auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
const auto explicit_resize = lookUp(interpolation, input_name);
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm)); non_const_prepm->emplace(
input_name, createPreProcInfo(trait, mm, explicit_resize));
// NB: configure intput param for further preproc // NB: configure intput param for further preproc
if (uu.net_input_params.is_applicable(mm)) { if (uu.net_input_params.is_applicable(mm)) {
@ -1623,7 +1823,7 @@ struct InferROI: public cv::detail::KernelTag {
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc(); : uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
cv::GMatDesc outm(toCV(desc.getPrecision()), cv::GMatDesc outm(toCV(desc.getPrecision()),
toCV(desc.getDims())); toCVDims(toCV(desc.getDims()), desc.getLayout()));
result.emplace_back(outm); result.emplace_back(outm);
} }
return result; return result;
@ -1646,6 +1846,7 @@ struct InferROI: public cv::detail::KernelTag {
bool preprocessed = false; bool preprocessed = false;
IE::Blob::Ptr this_blob = IE::Blob::Ptr this_blob =
extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE, extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
IE::Layout::ANY,
*(ctx->uu.params.input_names.begin()), *(ctx->uu.params.input_names.begin()),
cv::util::make_optional(this_roi), cv::util::make_optional(this_roi),
slot_ptr, &preprocessed); slot_ptr, &preprocessed);
@ -1691,6 +1892,10 @@ struct InferList: public cv::detail::KernelTag {
// NB: Configuring input precision and network reshape must be done // NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case. // only in the loadNetwork case.
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
uu.params.input_names);
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
uu.params.input_names);
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) { if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
std::size_t idx = 1u; std::size_t idx = 1u;
auto inputs = uu.net.getInputsInfo(); auto inputs = uu.net.getInputsInfo();
@ -1699,20 +1904,19 @@ struct InferList: public cv::detail::KernelTag {
const auto & mm = in_metas[idx++]; const auto & mm = in_metas[idx++];
// NB: InferList expects the input starts with index 1 wil be the images. // NB: InferList expects the input starts with index 1 wil be the images.
const auto input_trait = clarifyTrait(mm, ii->getTensorDesc()); const auto input_trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
if (input_trait != cv::gapi::ie::TraitAs::IMAGE) { if (input_trait != cv::gapi::ie::TraitAs::IMAGE) {
util::throw_error(std::runtime_error( util::throw_error(std::runtime_error(
"IE Backend: Only image is supported" "IE Backend: Only image is supported"
" as the " + std::to_string(idx) + "th argument for InferList")); " as the " + std::to_string(idx) + "th argument for InferList"));
} }
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) != if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) { uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table); configureInputReshapeByImage(ii, mm, input_reshape_table);
} }
cfgInputPreprocessing(input_trait, ii, mm,
input_name, input_layout, interpolation);
} }
// FIXME: This isn't the best place to call reshape function. // FIXME: This isn't the best place to call reshape function.
@ -1727,6 +1931,9 @@ struct InferList: public cv::detail::KernelTag {
ii->setPrecision(toIE(p.second.first.depth())); ii->setPrecision(toIE(p.second.first.depth()));
} }
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
uu.params.output_names);
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
} else { } else {
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
@ -1738,14 +1945,16 @@ struct InferList: public cv::detail::KernelTag {
const auto & mm = in_metas[idx++]; const auto & mm = in_metas[idx++];
// NB: InferList expects the input starts with index 1 wil be the images. // NB: InferList expects the input starts with index 1 wil be the images.
const auto input_trait = clarifyTrait(mm, ii->getTensorDesc()); const auto input_trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
if (input_trait != cv::gapi::ie::TraitAs::IMAGE) { if (input_trait != cv::gapi::ie::TraitAs::IMAGE) {
util::throw_error(std::runtime_error( util::throw_error(std::runtime_error(
"IE Backend: Only image is supported" "IE Backend: Only image is supported"
" as the " + std::to_string(idx) + "th argument for InferList")); " as the " + std::to_string(idx) + "th argument for InferList"));
} }
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm)); const auto explicit_resize = lookUp(interpolation, input_name);
non_const_prepm->emplace(
input_name, createPreProcInfo(input_trait, mm, explicit_resize));
} }
} }
@ -1773,6 +1982,7 @@ struct InferList: public cv::detail::KernelTag {
// NB: This blob will be used to make roi from its, so // NB: This blob will be used to make roi from its, so
// it should be treated as image // it should be treated as image
IE::Blob::Ptr this_blob = extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE, IE::Blob::Ptr this_blob = extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
IE::Layout::ANY,
ctx->uu.params.input_names[0u], ctx->uu.params.input_names[0u],
cv::util::optional<cv::Rect>{}); cv::util::optional<cv::Rect>{});
@ -1783,7 +1993,7 @@ struct InferList: public cv::detail::KernelTag {
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc() ? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc(); : ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
cached_dims[i] = toCV(desc.getDims()); cached_dims[i] = toCVDims(toCV(desc.getDims()), desc.getLayout());
// FIXME: Isn't this should be done automatically // FIXME: Isn't this should be done automatically
// by some resetInternalData(), etc? (Probably at the GExecutor level) // by some resetInternalData(), etc? (Probably at the GExecutor level)
auto& out_vec = ctx->outVecR<cv::Mat>(i); auto& out_vec = ctx->outVecR<cv::Mat>(i);
@ -1849,7 +2059,7 @@ struct InferList2: public cv::detail::KernelTag {
if (cv::util::holds_alternative<cv::GMatDesc>(mm_0) || if (cv::util::holds_alternative<cv::GMatDesc>(mm_0) ||
cv::util::holds_alternative<cv::GFrameDesc>(mm_0)) { cv::util::holds_alternative<cv::GFrameDesc>(mm_0)) {
const auto trait = clarifyTrait(mm_0, tensor_desc_0); const auto trait = clarifyTrait(mm_0, tensor_desc_0.getDims());
if (trait != cv::gapi::ie::TraitAs::IMAGE) { if (trait != cv::gapi::ie::TraitAs::IMAGE) {
util::throw_error(std::runtime_error( util::throw_error(std::runtime_error(
"IE Backend: Only images is" "IE Backend: Only images is"
@ -1862,23 +2072,29 @@ struct InferList2: public cv::detail::KernelTag {
} }
std::size_t idx = 1u; std::size_t idx = 1u;
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
uu.params.input_names);
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
uu.params.input_names);
for (auto &&input_name : uu.params.input_names) { for (auto &&input_name : uu.params.input_names) {
const auto &mm = in_metas[idx]; const auto &mm = in_metas[idx];
GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(mm) GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(mm)
&& "Non-array inputs are not supported"); && "Non-array inputs are not supported");
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) { if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
const auto input_trait = cv::gapi::ie::TraitAs::IMAGE;
// NB: Configuring input precision and network reshape must be done // NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case. // only in the loadNetwork case.
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) { if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
auto inputs = uu.net.getInputsInfo(); auto inputs = uu.net.getInputsInfo();
// This is a cv::Rect -- configure the IE preprocessing // This is a cv::Rect -- configure the IE preprocessing
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
configureInputInfo(ii, mm_0);
if (uu.params.layer_names_to_reshape.find(input_name) != if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) { uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm_0, input_reshape_table); configureInputReshapeByImage(ii, mm_0, input_reshape_table);
} }
cfgInputPreprocessing(input_trait, ii, mm_0,
input_name, input_layout, interpolation);
for (auto &&p : uu.params.const_inputs) { for (auto &&p : uu.params.const_inputs) {
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth())); inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
@ -1890,19 +2106,32 @@ struct InferList2: public cv::detail::KernelTag {
if (!input_reshape_table.empty()) { if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table); const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
} }
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
uu.params.output_names);
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision); configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
} else { } else {
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import); GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
auto inputs = uu.this_network.GetInputsInfo(); auto inputs = uu.this_network.GetInputsInfo();
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map); auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
auto ii = inputs.at(input_name); auto ii = inputs.at(input_name);
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm_0)); const auto explicit_resize = lookUp(interpolation, input_name);
non_const_prepm->emplace(
input_name, createPreProcInfo(input_trait, mm_0, explicit_resize));
} }
} else { } else {
// This is a cv::GMat (equals to: cv::Mat) // This is a cv::GMat (equals to: cv::Mat)
// Just validate that it is really the type // Just validate that it is really the type
// (other types are prohibited here) // (other types are prohibited here)
GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT); GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT);
// NB: Well, it's even impossible to specify the precision since
// there is not such info in GArray<cv::GMat>
const auto explicit_resize = lookUp(interpolation, input_name);
const auto explicit_layout = lookUp(input_layout , input_name);
if (explicit_resize || explicit_layout) {
util::throw_error(std::logic_error(
"InferList2 doesn't support preprocessing for \"tensor\"'s arguments!"));
}
} }
idx++; // NB: Never forget to increment the counter idx++; // NB: Never forget to increment the counter
} }
@ -1922,6 +2151,7 @@ struct InferList2: public cv::detail::KernelTag {
// NB: This blob will be used to make roi from its, so // NB: This blob will be used to make roi from its, so
// it should be treated as image // it should be treated as image
IE::Blob::Ptr blob_0 = extractBlob(*ctx, 0, cv::gapi::ie::TraitAs::IMAGE, IE::Blob::Ptr blob_0 = extractBlob(*ctx, 0, cv::gapi::ie::TraitAs::IMAGE,
IE::Layout::ANY,
ctx->uu.params.input_names[0u], ctx->uu.params.input_names[0u],
cv::util::optional<cv::Rect>{}); cv::util::optional<cv::Rect>{});
const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size(); const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size();
@ -1941,7 +2171,7 @@ struct InferList2: public cv::detail::KernelTag {
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc() ? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc(); : ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
cached_dims[i] = toCV(desc.getDims()); cached_dims[i] = toCVDims(toCV(desc.getDims()), desc.getLayout());
// FIXME: Isn't this should be done automatically // FIXME: Isn't this should be done automatically
// by some resetInternalData(), etc? (Probably at the GExecutor level) // by some resetInternalData(), etc? (Probably at the GExecutor level)
auto& out_vec = ctx->outVecR<cv::Mat>(i); auto& out_vec = ctx->outVecR<cv::Mat>(i);
@ -1964,8 +2194,10 @@ struct InferList2: public cv::detail::KernelTag {
} else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) { } else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) {
const auto &vec = this_vec.rref<cv::Mat>(); const auto &vec = this_vec.rref<cv::Mat>();
const auto &mat = vec[list_idx]; const auto &mat = vec[list_idx];
setBlob(req, ctx->uu.params.input_names[in_idx], const auto layer_name = ctx->uu.params.input_names[in_idx];
wrapIE(mat, cv::gapi::ie::TraitAs::TENSOR), const auto layout = req.GetBlob(layer_name)->getTensorDesc().getLayout();
setBlob(req, layer_name,
wrapIE(mat, cv::gapi::ie::TraitAs::TENSOR, layout),
*ctx); *ctx);
} else { } else {
GAPI_Assert(false && GAPI_Assert(false &&

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // of this distribution and at http://opencv.org/license.html.
// //
// Copyright (C) 2019-2021 Intel Corporation // Copyright (C) 2019-2023 Intel Corporation
#include "../test_precomp.hpp" #include "../test_precomp.hpp"
@ -3057,6 +3057,73 @@ TEST_F(AgeGenderInferTest, ChangeSpecificOutputPrecison) {
validate(); validate();
} }
TEST_F(AgeGenderInferTest, ThrowIfSetLayoutForImage) {
auto pp = cv::gapi::ie::Params<AgeGender> {
m_params.model_path, m_params.weights_path, m_params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" })
.cfgOutputPrecision({{"prob", CV_8U}})
.cfgInputLayout("NHWC");
EXPECT_ANY_THROW(buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender),
cv::compile_args(cv::gapi::networks(pp))));
}
TEST(TestAgeGenderIE, InferTensorWithPreproc) {
initDLDTDataPath();
cv::gapi::ie::detail::ParamDesc params;
params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
params.device_id = "CPU";
// Load IE network, initialize input data using that.
cv::Mat in_mat({1, 240, 320, 3}, CV_8U);
cv::randu(in_mat, 0, 255);
cv::Mat gapi_age, gapi_gender;
IE::Blob::Ptr ie_age, ie_gender;
{
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
auto net = cv::gimpl::ie::wrap::readNetwork(params);
auto ii = net.getInputsInfo().at("data");
ii->setPrecision(IE::Precision::U8);
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
ii->setLayout(IE::Layout::NHWC);
auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params);
auto infer_request = this_network.CreateInferRequest();
IE::TensorDesc desc{IE::Precision::U8, {1, 3, 240, 320}, IE::Layout::NHWC};
auto blob = IE::make_shared_blob<uint8_t>(desc, const_cast<uint8_t*>(in_mat.ptr<uint8_t>()));
infer_request.SetBlob("data", blob);
infer_request.Infer();
ie_age = infer_request.GetBlob("age_conv3");
ie_gender = infer_request.GetBlob("prob");
}
// Configure & run G-API
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "test-age-gender");
cv::GMat in;
cv::GMat age, gender;
std::tie(age, gender) = cv::gapi::infer<AgeGender>(in);
cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender));
auto pp = cv::gapi::ie::Params<AgeGender> {
params.model_path, params.weights_path, params.device_id
}.cfgOutputLayers({ "age_conv3", "prob" })
.cfgResize(cv::INTER_LINEAR)
.cfgInputLayout("NHWC");
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
cv::compile_args(cv::gapi::networks(pp)));
// Validate with IE itself (avoid DNN module dependency here)
normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" );
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
}
} // namespace opencv_test } // namespace opencv_test
#endif // HAVE_INF_ENGINE #endif // HAVE_INF_ENGINE

Loading…
Cancel
Save