Merge pull request #18419 from TolyaTalamanov:at/generic-inference

[G-API] Introduce generic version for cv::gapi::infer

* Introduce generic infer

* Move Generic to infer.hpp

* Removew num_outs

* Fix windows warnings

* Fix comments to review

* Fix doxygen

* Add comment

* Fix comments to review

* standoalone ifdef in ginfer.cpp

* Fix test
pull/18552/head
Anatoliy Talamanov 5 years ago committed by GitHub
parent 6a51e3b39a
commit 76be3529f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      modules/gapi/include/opencv2/gapi/gcall.hpp
  2. 78
      modules/gapi/include/opencv2/gapi/infer.hpp
  3. 32
      modules/gapi/include/opencv2/gapi/infer/ie.hpp
  4. 10
      modules/gapi/src/api/gcall.cpp
  5. 3
      modules/gapi/src/api/gcall_priv.hpp
  6. 30
      modules/gapi/src/api/ginfer.cpp
  7. 18
      modules/gapi/src/backends/ie/giebackend.cpp
  8. 8
      modules/gapi/src/compiler/gmodel.cpp
  9. 7
      modules/gapi/src/compiler/gmodel.hpp
  10. 2
      modules/gapi/src/compiler/gmodelbuilder.cpp
  11. 53
      modules/gapi/test/infer/gapi_infer_ie_test.cpp

@ -56,11 +56,16 @@ public:
Priv& priv();
const Priv& priv() const;
protected:
std::shared_ptr<Priv> m_priv;
// GKernel and params can be modified, it's needed for infer<Generic>,
// because information about output shapes doesn't exist in compile time
GKernel& kernel();
cv::util::any& params();
void setArgs(std::vector<GArg> &&args);
protected:
std::shared_ptr<Priv> m_priv;
// Public versions return a typed array or opaque, those are implementation details
detail::GArrayU yieldArray(int output = 0);
detail::GOpaqueU yieldOpaque(int output = 0);

@ -121,6 +121,45 @@ struct GInferBase {
}
};
// Struct stores network input/output names.
// Used by infer<Generic>
struct InOutInfo
{
std::vector<std::string> in_names;
std::vector<std::string> out_names;
};
/**
* @{
* @brief G-API object used to collect network inputs
*/
class GAPI_EXPORTS GInferInputs
{
public:
cv::GMat& operator[](const std::string& name);
const std::unordered_map<std::string, cv::GMat>& getBlobs() const;
private:
std::unordered_map<std::string, cv::GMat> in_blobs;
};
/** @} */
/**
* @{
* @brief G-API object used to collect network outputs
*/
struct GAPI_EXPORTS GInferOutputs
{
public:
GInferOutputs(std::shared_ptr<cv::GCall> call);
cv::GMat at(const std::string& name);
private:
std::shared_ptr<cv::GCall> m_call;
InOutInfo* m_info = nullptr;
std::unordered_map<std::string, cv::GMat> out_blobs;
};
/** @} */
// Base "Infer list" kernel.
// All notes from "Infer" kernel apply here as well.
@ -254,6 +293,45 @@ typename Net::Result infer(Args&&... args) {
return GInfer<Net>::on(std::forward<Args>(args)...);
}
/**
* @brief Special network type
*/
struct Generic { };
/**
* @brief Calculates response for generic network
*
* @param tag a network tag
* @param inputs networks's inputs
* @return a GInferOutputs
*/
template<typename T = Generic> GInferOutputs
infer(const std::string& tag, const GInferInputs& inputs)
{
std::vector<GArg> input_args;
std::vector<std::string> input_names;
const auto& blobs = inputs.getBlobs();
for (auto&& p : blobs)
{
input_names.push_back(p.first);
input_args.emplace_back(p.second);
}
GKinds kinds(blobs.size(), cv::detail::OpaqueKind::CV_MAT);
auto call = std::make_shared<cv::GCall>(GKernel{
GInferBase::id(),
tag,
GInferBase::getOutMeta,
{}, // outShape will be filled later
std::move(kinds)
});
call->setArgs(std::move(input_args));
call->params() = InOutInfo{input_names, {}};
return GInferOutputs{std::move(call)};
}
} // namespace gapi
} // namespace cv

@ -17,6 +17,7 @@
#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/infer.hpp> // Generic
namespace cv {
namespace gapi {
@ -58,6 +59,8 @@ namespace detail {
// (e.g. topology's partial execution)
std::size_t num_in; // How many inputs are defined in the operation
std::size_t num_out; // How many outputs are defined in the operation
bool is_generic;
};
} // namespace detail
@ -80,7 +83,7 @@ public:
: desc{ model, weights, device, {}, {}, {}
, std::tuple_size<typename Net::InArgs>::value // num_in
, std::tuple_size<typename Net::OutArgs>::value // num_out
} {
, false} {
};
Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &ll) {
@ -107,13 +110,34 @@ public:
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ie::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { desc }; }
GBackend backend() const { return cv::gapi::ie::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { desc }; }
// END(G-API's network parametrization API)
protected:
detail::ParamDesc desc;
};
template<>
class Params<cv::gapi::Generic> {
public:
Params(const std::string& tag,
const std::string &model,
const std::string &weights,
const std::string &device)
: desc{ model, weights, device, {}, {}, {}, 0u, 0u, true}, m_tag(tag) {
};
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::ie::backend(); }
std::string tag() const { return m_tag; }
cv::util::any params() const { return { desc }; }
// END(G-API's network parametrization API)
protected:
detail::ParamDesc desc;
std::string m_tag;
};
} // namespace ie

@ -78,3 +78,13 @@ const cv::GCall::Priv& cv::GCall::priv() const
{
return *m_priv;
}
cv::GKernel& cv::GCall::kernel()
{
return m_priv->m_k;
}
cv::util::any& cv::GCall::params()
{
return m_priv->m_params;
}

@ -42,10 +42,11 @@ class GCall::Priv
{
public:
std::vector<GArg> m_args;
const GKernel m_k;
GKernel m_k;
// TODO: Rename to "constructionNode" or smt to reflect its lifetime
GNode m_node;
cv::util::any m_params;
explicit Priv(const GKernel &k);
};

@ -25,3 +25,33 @@ std::vector<cv::gapi::GBackend> cv::gapi::GNetPackage::backends() const {
for (const auto &nn : networks) unique_set.insert(nn.backend);
return std::vector<cv::gapi::GBackend>(unique_set.begin(), unique_set.end());
}
// FIXME: Inference API is currently only available in full mode
#if !defined(GAPI_STANDALONE)
cv::GMat& cv::GInferInputs::operator[](const std::string& name) {
return in_blobs[name];
}
const std::unordered_map<std::string, cv::GMat>& cv::GInferInputs::getBlobs() const {
return in_blobs;
}
cv::GInferOutputs::GInferOutputs(std::shared_ptr<cv::GCall> call)
: m_call(std::move(call)), m_info(cv::util::any_cast<InOutInfo>(&m_call->params()))
{
};
cv::GMat cv::GInferOutputs::at(const std::string& name)
{
auto it = out_blobs.find(name);
if (it == out_blobs.end()) {
// FIXME: Avoid modifying GKernel
m_call->kernel().outShapes.push_back(cv::GShape::GMAT);
int out_idx = static_cast<int>(out_blobs.size());
it = out_blobs.emplace(name, m_call->yield(out_idx)).first;
m_info->out_names.push_back(name);
}
return it->second;
};
#endif // GAPI_STANDALONE

@ -721,9 +721,23 @@ namespace {
// FIXME: Introduce a DNNBackend interface which'd specify
// the framework for this???
GIEModel gm(gr);
const auto &np = gm.metadata(nh).get<NetworkParams>();
const auto &pp = cv::util::any_cast<cv::gapi::ie::detail::ParamDesc>(np.opaque);
auto &np = gm.metadata(nh).get<NetworkParams>();
auto &pp = cv::util::any_cast<cv::gapi::ie::detail::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::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(IEUnit{pp});
gm.metadata(nh).set(IECallable{ki.run});
gm.metadata(nh).set(CustomMetaFunction{ki.customMetaFunc});

@ -23,12 +23,16 @@
namespace cv { namespace gimpl {
ade::NodeHandle GModel::mkOpNode(GModel::Graph &g, const GKernel &k, const std::vector<GArg> &args, const std::string &island)
ade::NodeHandle GModel::mkOpNode(GModel::Graph &g,
const GKernel &k,
const std::vector<GArg> &args,
const cv::util::any &params,
const std::string &island)
{
ade::NodeHandle op_h = g.createNode();
g.metadata(op_h).set(NodeType{NodeType::OP});
//These extra empty {} are to please GCC (-Wmissing-field-initializers)
g.metadata(op_h).set(Op{k, args, {}, {}});
g.metadata(op_h).set(Op{k, args, {}, {}, params});
if (!island.empty())
g.metadata(op_h).set(Island{island});
return op_h;

@ -61,6 +61,7 @@ struct Op
std::vector<RcDesc> outs; // TODO: Introduce a new type for resource references
cv::gapi::GBackend backend;
cv::util::any params; // Operation specific information
};
struct Data
@ -262,7 +263,11 @@ namespace GModel
// GAPI_EXPORTS for tests
GAPI_EXPORTS void init (Graph& g);
GAPI_EXPORTS ade::NodeHandle mkOpNode(Graph &g, const GKernel &k, const std::vector<GArg>& args, const std::string &island);
GAPI_EXPORTS ade::NodeHandle mkOpNode(Graph &g,
const GKernel &k,
const std::vector<GArg>& args,
const cv::util::any& params,
const std::string &island);
// Isn't used by the framework or default backends, required for external backend development
GAPI_EXPORTS ade::NodeHandle mkDataNode(Graph &g, const GShape shape);

@ -286,7 +286,7 @@ ade::NodeHandle cv::gimpl::GModelBuilder::put_OpNode(const cv::GNode &node)
{
GAPI_Assert(node.shape() == GNode::NodeShape::CALL);
const auto &call_p = node.call().priv();
auto nh = cv::gimpl::GModel::mkOpNode(m_gm, call_p.m_k, call_p.m_args, node_p.m_island);
auto nh = cv::gimpl::GModel::mkOpNode(m_gm, call_p.m_k, call_p.m_args, call_p.m_params, node_p.m_island);
m_graph_ops[&node_p] = nh;
return nh;
}

@ -350,6 +350,59 @@ TEST(DISABLED_TestTwoIENNPipeline, InferBasicImage)
normAssert(cv::gapi::ie::util::to_ocv(ie_gender2), gapi_gender2, "Test gender output 2");
}
TEST(TestAgeGenderIE, GenericInfer)
{
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";
cv::Mat in_mat(cv::Size(320, 240), CV_8UC3);
cv::randu(in_mat, 0, 255);
cv::Mat gapi_age, gapi_gender;
// Load & run IE network
IE::Blob::Ptr ie_age, ie_gender;
{
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
auto net = cv::gimpl::ie::wrap::readNetwork(params);
setNetParameters(net);
auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params);
auto infer_request = this_network.CreateInferRequest();
infer_request.SetBlob("data", cv::gapi::ie::util::to_ie(in_mat));
infer_request.Infer();
ie_age = infer_request.GetBlob("age_conv3");
ie_gender = infer_request.GetBlob("prob");
}
// Configure & run G-API
cv::GMat in;
GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>("age-gender-generic", inputs);
auto age = outputs.at("age_conv3");
auto gender = outputs.at("prob");
cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender));
cv::gapi::ie::Params<cv::gapi::Generic> pp{"age-gender-generic",
params.model_path,
params.weights_path,
params.device_id};
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
#endif // HAVE_INF_ENGINE

Loading…
Cancel
Save