Merge pull request #18491 from TolyaTalamanov:at/wrap-inference

[G-API] Wrap cv::gapi::infer<Generic> into python

* 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

* Wrap inference to python

* Add default ctor to Params

* Add test

* Fix clang build

* Implement GInferInputs/GInferOutputs as Pimpl

* Add checkIEtarget to infer test

* Fix path

* Supress warning

* Use getAvailableDevices insted of checkIETarget

* Move PyParams to bindings_ie

* Add namespace

* Update CMakeLists.txt
pull/18662/head
Anatoliy Talamanov 4 years ago committed by GitHub
parent 36598677cf
commit 93c3775927
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      modules/gapi/CMakeLists.txt
  2. 29
      modules/gapi/include/opencv2/gapi/infer.hpp
  3. 56
      modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp
  4. 2
      modules/gapi/include/opencv2/gapi/infer/ie.hpp
  5. 2
      modules/gapi/misc/python/pyopencv_gapi.hpp
  6. 10
      modules/gapi/misc/python/shadow_gapi.hpp
  7. 62
      modules/gapi/misc/python/test/test_gapi_infer.py
  8. 47
      modules/gapi/src/api/ginfer.cpp
  9. 39
      modules/gapi/src/backends/ie/bindings_ie.cpp

@ -145,6 +145,9 @@ set(gapi_srcs
# Serialization API and routines
src/api/s11n.cpp
src/backends/common/serialization.cpp
# Python bridge
src/backends/ie/bindings_ie.cpp
)
ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2)

@ -133,14 +133,18 @@ struct InOutInfo
* @{
* @brief G-API object used to collect network inputs
*/
class GAPI_EXPORTS GInferInputs
class GAPI_EXPORTS_W_SIMPLE GInferInputs
{
using Map = std::unordered_map<std::string, GMat>;
public:
GAPI_WRAP GInferInputs();
GAPI_WRAP void setInput(const std::string& name, const cv::GMat& value);
cv::GMat& operator[](const std::string& name);
const std::unordered_map<std::string, cv::GMat>& getBlobs() const;
const Map& getBlobs() const;
private:
std::unordered_map<std::string, cv::GMat> in_blobs;
std::shared_ptr<Map> in_blobs;
};
/** @} */
@ -148,16 +152,16 @@ private:
* @{
* @brief G-API object used to collect network outputs
*/
struct GAPI_EXPORTS GInferOutputs
struct GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs() = default;
GInferOutputs(std::shared_ptr<cv::GCall> call);
cv::GMat at(const std::string& name);
GAPI_WRAP 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;
struct Priv;
std::shared_ptr<Priv> m_priv;
};
/** @} */
@ -333,6 +337,11 @@ infer(const std::string& tag, const GInferInputs& inputs)
return GInferOutputs{std::move(call)};
}
GAPI_EXPORTS_W inline GInferOutputs infer(const String& name, const GInferInputs& inputs)
{
return infer<Generic>(name, inputs);
}
} // namespace gapi
} // namespace cv
@ -361,8 +370,8 @@ struct GAPI_EXPORTS GNetParam {
*
* @sa cv::gapi::networks
*/
struct GAPI_EXPORTS GNetPackage {
GNetPackage() : GNetPackage({}) {}
struct GAPI_EXPORTS_W_SIMPLE GNetPackage {
GAPI_WRAP GNetPackage() : GNetPackage({}) {}
explicit GNetPackage(std::initializer_list<GNetParam> &&ii);
std::vector<GBackend> backends() const;
std::vector<GNetParam> networks;

@ -0,0 +1,56 @@
// 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) 2020 Intel Corporation
#ifndef OPENCV_GAPI_INFER_BINDINGS_IE_HPP
#define OPENCV_GAPI_INFER_BINDINGS_IE_HPP
#include <opencv2/gapi/util/any.hpp>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/infer/ie.hpp> // Params
#include <string>
namespace cv {
namespace gapi {
namespace ie {
// NB: Used by python wrapper
// This class can be marked as SIMPLE, because it's implemented as pimpl
class GAPI_EXPORTS_W_SIMPLE PyParams {
public:
PyParams() = default;
PyParams(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device);
PyParams(const std::string &tag,
const std::string &model,
const std::string &device);
GBackend backend() const;
std::string tag() const;
cv::util::any params() const;
private:
std::shared_ptr<Params<cv::gapi::Generic>> m_priv;
};
GAPI_EXPORTS_W PyParams params(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device);
GAPI_EXPORTS_W PyParams params(const std::string &tag,
const std::string &model,
const std::string &device);
} // namespace ie
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_INFER_BINDINGS_IE_HPP

@ -162,4 +162,4 @@ protected:
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_INFER_HPP
#endif // OPENCV_GAPI_INFER_IE_HPP

@ -5,6 +5,8 @@
// NB: Python wrapper replaces :: with _ for classes
using gapi_GKernelPackage = cv::gapi::GKernelPackage;
using gapi_GNetPackage = cv::gapi::GNetPackage;
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
// FIXME: Python wrapper generate code without namespace std,

@ -6,23 +6,25 @@ namespace cv
struct GAPI_EXPORTS_W_SIMPLE GCompileArg { };
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GKernelPackage pkg);
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GNetPackage pkg);
// NB: This classes doesn't exist in *.so
// HACK: Mark them as a class to force python wrapper generate code for this entities
class GAPI_EXPORTS_W_SIMPLE GProtoArg { };
class GAPI_EXPORTS_W_SIMPLE GProtoInputArgs { };
class GAPI_EXPORTS_W_SIMPLE GProtoOutputArgs { };
class GAPI_EXPORTS_W_SIMPLE GRunArg { };
class GAPI_EXPORTS_W_SIMPLE GMetaArg { };
class GAPI_EXPORTS_W_SIMPLE GRunArg { };
class GAPI_EXPORTS_W_SIMPLE GMetaArg { };
using GProtoInputArgs = GIOProtoArgs<In_Tag>;
using GProtoOutputArgs = GIOProtoArgs<Out_Tag>;
namespace gapi
{
GAPI_EXPORTS_W gapi::GNetPackage networks(const cv::gapi::ie::PyParams& params);
namespace wip
{
class GAPI_EXPORTS_W IStreamSource { };
}
}
} // namespace wip
} // namespace gapi
} // namespace cv

@ -0,0 +1,62 @@
#!/usr/bin/env python
import numpy as np
import cv2 as cv
import os
from tests_common import NewOpenCVTests
class test_gapi_infer(NewOpenCVTests):
def test_getAvailableTargets(self):
targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV)
self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets)
def test_age_gender_infer(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (62,62))
# OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
blob = cv.dnn.blobFromImage(img)
net.setInput(blob)
dnn_age, dnn_gender = net.forward(net.getUnconnectedOutLayersNames())
# OpenCV G-API
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
nets = cv.gapi.networks(pp)
args = cv.compile_args(nets)
gapi_age, gapi_gender = comp.apply(cv.gin(img), args=cv.compile_args(cv.gapi.networks(pp)))
# Check
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
if __name__ == '__main__':
NewOpenCVTests.bootstrap()

@ -29,29 +29,52 @@ std::vector<cv::gapi::GBackend> cv::gapi::GNetPackage::backends() const {
// FIXME: Inference API is currently only available in full mode
#if !defined(GAPI_STANDALONE)
cv::GInferInputs::GInferInputs()
: in_blobs(std::make_shared<Map>())
{
}
cv::GMat& cv::GInferInputs::operator[](const std::string& name) {
return in_blobs[name];
return (*in_blobs)[name];
}
const std::unordered_map<std::string, cv::GMat>& cv::GInferInputs::getBlobs() const {
return in_blobs;
const cv::GInferInputs::Map& 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()))
void cv::GInferInputs::setInput(const std::string& name, const cv::GMat& value) {
in_blobs->emplace(name, value);
}
struct cv::GInferOutputs::Priv
{
Priv(std::shared_ptr<cv::GCall>);
std::shared_ptr<cv::GCall> call;
InOutInfo* info = nullptr;
std::unordered_map<std::string, cv::GMat> out_blobs;
};
cv::GInferOutputs::Priv::Priv(std::shared_ptr<cv::GCall> c)
: call(std::move(c)), info(cv::util::any_cast<InOutInfo>(&call->params()))
{
}
cv::GInferOutputs::GInferOutputs(std::shared_ptr<cv::GCall> call)
: m_priv(std::make_shared<cv::GInferOutputs::Priv>(std::move(call)))
{
}
cv::GMat cv::GInferOutputs::at(const std::string& name)
{
auto it = out_blobs.find(name);
if (it == out_blobs.end()) {
auto it = m_priv->out_blobs.find(name);
if (it == m_priv->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);
m_priv->call->kernel().outShapes.push_back(cv::GShape::GMAT);
int out_idx = static_cast<int>(m_priv->out_blobs.size());
it = m_priv->out_blobs.emplace(name, m_priv->call->yield(out_idx)).first;
m_priv->info->out_names.push_back(name);
}
return it->second;
};
}
#endif // GAPI_STANDALONE

@ -0,0 +1,39 @@
#include <opencv2/gapi/infer/bindings_ie.hpp>
cv::gapi::ie::PyParams::PyParams(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device)
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, model, weights, device)) {
}
cv::gapi::ie::PyParams::PyParams(const std::string &tag,
const std::string &model,
const std::string &device)
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, model, device)) {
}
cv::gapi::GBackend cv::gapi::ie::PyParams::backend() const {
return m_priv->backend();
}
std::string cv::gapi::ie::PyParams::tag() const {
return m_priv->tag();
}
cv::util::any cv::gapi::ie::PyParams::params() const {
return m_priv->params();
}
cv::gapi::ie::PyParams cv::gapi::ie::params(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device) {
return {tag, model, weights, device};
}
cv::gapi::ie::PyParams cv::gapi::ie::params(const std::string &tag,
const std::string &model,
const std::string &device) {
return {tag, model, device};
}
Loading…
Cancel
Save