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);
}
};
+// 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.
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
#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
+#include <opencv2/gapi/infer.hpp> // Generic
namespace cv {
namespace gapi {
// (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
: 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) {
}
// 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
{
return *m_priv;
}
+
+cv::GKernel& cv::GCall::kernel()
+{
+ return m_priv->m_k;
+}
+
+cv::util::any& cv::GCall::params()
+{
+ return m_priv->m_params;
+}
{
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);
};
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
// 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});
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 ¶ms,
+ 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;
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
// 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);
{
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;
}
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