mirror of
https://github.com/opencv/opencv.git
synced 2025-08-06 14:36:36 +08:00
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
This commit is contained in:
parent
36598677cf
commit
93c3775927
@ -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;
|
||||
|
56
modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp
Normal file
56
modules/gapi/include/opencv2/gapi/infer/bindings_ie.hpp
Normal file
@ -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
|
||||
|
62
modules/gapi/misc/python/test/test_gapi_infer.py
Normal file
62
modules/gapi/misc/python/test/test_gapi_infer.py
Normal file
@ -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::GMat& cv::GInferInputs::operator[](const std::string& name) {
|
||||
return in_blobs[name];
|
||||
cv::GInferInputs::GInferInputs()
|
||||
: in_blobs(std::make_shared<Map>())
|
||||
{
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, cv::GMat>& cv::GInferInputs::getBlobs() const {
|
||||
return in_blobs;
|
||||
cv::GMat& cv::GInferInputs::operator[](const std::string& name) {
|
||||
return (*in_blobs)[name];
|
||||
}
|
||||
|
||||
const cv::GInferInputs::Map& cv::GInferInputs::getBlobs() const {
|
||||
return *in_blobs;
|
||||
}
|
||||
|
||||
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_call(std::move(call)), m_info(cv::util::any_cast<InOutInfo>(&m_call->params()))
|
||||
: 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
|
||||
|
39
modules/gapi/src/backends/ie/bindings_ie.cpp
Normal file
39
modules/gapi/src/backends/ie/bindings_ie.cpp
Normal file
@ -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…
Reference in New Issue
Block a user