提交 eb68476a 编写于 作者: M Maxim Pashchenkov 提交者: Alexander Alekhin

G-API: Python. Desync.

* Desync. GMat.

* Alignment

original commit: 05f1939b
上级 8d0fc8bf
......@@ -71,6 +71,15 @@ using GOptRunArgP = util::variant<
>;
using GOptRunArgsP = std::vector<GOptRunArgP>;
using GOptRunArg = util::variant<
optional<cv::Mat>,
optional<cv::RMat>,
optional<cv::Scalar>,
optional<cv::detail::VectorRef>,
optional<cv::detail::OpaqueRef>
>;
using GOptRunArgs = std::vector<GOptRunArg>;
namespace detail {
template<typename T> inline GOptRunArgP wrap_opt_arg(optional<T>& arg) {
......@@ -255,7 +264,7 @@ public:
// NB: Used from python
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP std::tuple<bool, cv::GRunArgs> pull();
GAPI_WRAP std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
/**
* @brief Get some next available data from the pipeline.
......
......@@ -131,7 +131,8 @@ PyObject* pyopencv_from(const cv::detail::PyObjectHolder& v)
template <>
PyObject* pyopencv_from(const cv::gapi::wip::draw::Prim& prim)
{
switch (prim.index()) {
switch (prim.index())
{
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Rect>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Rect>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Text>():
......@@ -319,40 +320,69 @@ PyObject* pyopencv_from(const GRunArg& v)
return pyopencv_from(util::get<cv::detail::OpaqueRef>(v));
}
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs. Index of variant is unknown");
return NULL;
}
template<>
PyObject* pyopencv_from(const GRunArgs& value)
template <typename T>
PyObject* pyopencv_from(const cv::optional<T>& opt)
{
size_t i, n = value.size();
if (!opt.has_value())
{
Py_RETURN_NONE;
}
return pyopencv_from(*opt);
}
// NB: It doesn't make sense to return list with a single element
if (n == 1)
template <>
PyObject* pyopencv_from(const GOptRunArg& v)
{
switch (v.index())
{
PyObject* item = pyopencv_from(value[0]);
if(!item)
{
return NULL;
}
return item;
case GOptRunArg::index_of<cv::optional<cv::Mat>>():
return pyopencv_from(util::get<cv::optional<cv::Mat>>(v));
case GOptRunArg::index_of<cv::optional<cv::Scalar>>():
return pyopencv_from(util::get<cv::optional<cv::Scalar>>(v));
case GOptRunArg::index_of<optional<cv::detail::VectorRef>>():
return pyopencv_from(util::get<optional<cv::detail::VectorRef>>(v));
case GOptRunArg::index_of<optional<cv::detail::OpaqueRef>>():
return pyopencv_from(util::get<optional<cv::detail::OpaqueRef>>(v));
}
PyObject* list = PyList_New(n);
for(i = 0; i < n; ++i)
PyErr_SetString(PyExc_TypeError, "Failed to unpack GOptRunArg. Index of variant is unknown");
return NULL;
}
template<>
PyObject* pyopencv_from(const GRunArgs& value)
{
return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
}
template<>
PyObject* pyopencv_from(const GOptRunArgs& value)
{
return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
}
// FIXME: cv::variant should be wrapped once for all types.
template <>
PyObject* pyopencv_from(const cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>& v)
{
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
switch (v.index())
{
PyObject* item = pyopencv_from(value[i]);
if(!item)
{
Py_DECREF(list);
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
return NULL;
}
PyList_SetItem(list, i, item);
case RunArgs::index_of<cv::GRunArgs>():
return pyopencv_from(util::get<cv::GRunArgs>(v));
case RunArgs::index_of<cv::GOptRunArgs>():
return pyopencv_from(util::get<cv::GOptRunArgs>(v));
}
return list;
PyErr_SetString(PyExc_TypeError, "Failed to recognize kind of RunArgs. Index of variant is unknown");
return NULL;
}
template <typename T>
......@@ -634,7 +664,8 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
cv::detail::PyObjectHolder result(
PyObject_CallObject(kernel.get(), args.get()), false);
if (PyErr_Occurred()) {
if (PyErr_Occurred())
{
PyErr_PrintEx(0);
PyErr_Clear();
throw std::logic_error("Python kernel failed with error!");
......@@ -717,8 +748,9 @@ static cv::GMetaArgs get_meta_args(PyObject* tuple)
}
static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
const cv::GMetaArgs &meta,
const cv::GArgs &gargs) {
const cv::GMetaArgs &meta,
const cv::GArgs &gargs)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
......@@ -760,7 +792,8 @@ static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
cv::detail::PyObjectHolder result(
PyObject_CallObject(out_meta.get(), args.get()), false);
if (PyErr_Occurred()) {
if (PyErr_Occurred())
{
PyErr_PrintEx(0);
PyErr_Clear();
throw std::logic_error("Python outMeta failed with error!");
......@@ -792,21 +825,24 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec
PyObject* user_kernel = PyTuple_GetItem(py_args, i);
PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id");
if (!id_obj) {
if (!id_obj)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain id, please use cv.gapi.kernel to define kernel");
return NULL;
}
PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta");
if (!out_meta) {
if (!out_meta)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel");
return NULL;
}
PyObject* run = PyObject_GetAttrString(user_kernel, "run");
if (!run) {
if (!run)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain run, please use cv.gapi.kernel to define kernel");
return NULL;
......@@ -951,9 +987,12 @@ struct PyOpenCV_Converter<cv::GArray<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
{
auto& array = reinterpret_cast<pyopencv_GArrayT_t*>(obj)->v;
try {
try
{
value = cv::util::get<cv::GArray<T>>(array.arg());
} catch (...) {
}
catch (...)
{
return false;
}
return true;
......@@ -974,9 +1013,12 @@ struct PyOpenCV_Converter<cv::GOpaque<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
{
auto& opaque = reinterpret_cast<pyopencv_GOpaqueT_t*>(obj)->v;
try {
try
{
value = cv::util::get<cv::GOpaque<T>>(opaque.arg());
} catch (...) {
}
catch (...)
{
return false;
}
return true;
......
......@@ -3,39 +3,40 @@
namespace cv
{
struct GAPI_EXPORTS_W_SIMPLE GCompileArg {
GAPI_WRAP GCompileArg(gapi::GKernelPackage pkg);
GAPI_WRAP GCompileArg(gapi::GNetPackage pkg);
struct GAPI_EXPORTS_W_SIMPLE GCompileArg
{
GAPI_WRAP GCompileArg(gapi::GKernelPackage pkg);
GAPI_WRAP GCompileArg(gapi::GNetPackage pkg);
};
class GAPI_EXPORTS_W_SIMPLE GInferInputs
{
public:
GAPI_WRAP GInferInputs();
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GMat& value);
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GFrame& value);
GAPI_WRAP GInferInputs();
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GMat& value);
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GFrame& value);
};
class GAPI_EXPORTS_W_SIMPLE GInferListInputs
{
public:
GAPI_WRAP GInferListInputs();
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::GMat>& value);
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::Rect>& value);
GAPI_WRAP GInferListInputs();
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::GMat>& value);
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::Rect>& value);
};
class GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
};
class GAPI_EXPORTS_W_SIMPLE GInferListOutputs
{
public:
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
};
namespace gapi
......@@ -69,11 +70,13 @@ namespace streaming
cv::GOpaque<int64_t> GAPI_EXPORTS_W timestamp(cv::GMat);
cv::GOpaque<int64_t> GAPI_EXPORTS_W seqNo(cv::GMat);
cv::GOpaque<int64_t> GAPI_EXPORTS_W seq_id(cv::GMat);
GAPI_EXPORTS_W cv::GMat desync(const cv::GMat &g);
} // namespace streaming
} // namespace gapi
namespace detail
{
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
} // namespace detail
} // namespace cv
......@@ -5,16 +5,35 @@ import cv2 as cv
import os
import sys
import unittest
import time
from tests_common import NewOpenCVTests
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
@cv.gapi.op('custom.delay', in_types=[cv.GMat], out_types=[cv.GMat])
class GDelay:
"""Delay for 10 ms."""
@staticmethod
def outMeta(desc):
return desc
@cv.gapi.kernel(GDelay)
class GDelayImpl:
"""Implementation for GDelay operation."""
@staticmethod
def run(img):
time.sleep(0.01)
return img
class test_gapi_streaming(NewOpenCVTests):
def test_image_input(self):
......@@ -148,7 +167,7 @@ try:
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
break
def test_video_good_features_to_track(self):
......@@ -242,6 +261,51 @@ try:
if curr_frame_number == max_num_frames:
break
def test_desync(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# G-API
g_in = cv.GMat()
g_out1 = cv.gapi.copy(g_in)
des = cv.gapi.streaming.desync(g_in)
g_out2 = GDelay.on(des)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out1, g_out2))
kernels = cv.gapi.kernels(GDelayImpl)
ccomp = c.compileStreaming(args=cv.gapi.compile_args(kernels))
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
out_counter = 0
desync_out_counter = 0
none_counter = 0
while True:
has_frame, (out1, out2) = ccomp.pull()
if not has_frame:
break
if not out1 is None:
out_counter += 1
if not out2 is None:
desync_out_counter += 1
else:
none_counter += 1
proc_num_frames += 1
if proc_num_frames == max_num_frames:
ccomp.stop()
break
self.assertLess(0, proc_num_frames)
self.assertLess(desync_out_counter, out_counter)
self.assertLess(0, none_counter)
except unittest.SkipTest as e:
......
......@@ -75,6 +75,11 @@ bool cv::GStreamingCompiled::Priv::pull(cv::GOptRunArgsP &&outs)
return m_exec->pull(std::move(outs));
}
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::GStreamingCompiled::Priv::pull()
{
return m_exec->pull();
}
bool cv::GStreamingCompiled::Priv::try_pull(cv::GRunArgsP &&outs)
{
return m_exec->try_pull(std::move(outs));
......@@ -123,18 +128,9 @@ bool cv::GStreamingCompiled::pull(cv::GRunArgsP &&outs)
return m_priv->pull(std::move(outs));
}
std::tuple<bool, cv::GRunArgs> cv::GStreamingCompiled::pull()
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::GStreamingCompiled::pull()
{
GRunArgs run_args;
GRunArgsP outs;
const auto& out_info = m_priv->outInfo();
run_args.reserve(out_info.size());
outs.reserve(out_info.size());
cv::detail::constructGraphOutputs(m_priv->outInfo(), run_args, outs);
bool is_over = m_priv->pull(std::move(outs));
return std::make_tuple(is_over, run_args);
return m_priv->pull();
}
bool cv::GStreamingCompiled::pull(cv::GOptRunArgsP &&outs)
......
......@@ -46,6 +46,7 @@ public:
void start();
bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs);
void stop();
......
......@@ -1017,6 +1017,49 @@ void check_DesyncObjectConsumedByMultipleIslands(const cv::gimpl::GIslandModel::
} // for(nodes)
}
// NB: Construct GRunArgsP based on passed info and store the memory in passed cv::GRunArgs.
// Needed for python bridge, because in case python user doesn't pass output arguments to apply.
void constructOptGraphOutputs(const cv::GTypesInfo &out_info,
cv::GOptRunArgs &args,
cv::GOptRunArgsP &outs)
{
for (auto&& info : out_info)
{
switch (info.shape)
{
case cv::GShape::GMAT:
{
args.emplace_back(cv::optional<cv::Mat>{});
outs.emplace_back(&cv::util::get<cv::optional<cv::Mat>>(args.back()));
break;
}
case cv::GShape::GSCALAR:
{
args.emplace_back(cv::optional<cv::Scalar>{});
outs.emplace_back(&cv::util::get<cv::optional<cv::Scalar>>(args.back()));
break;
}
case cv::GShape::GARRAY:
{
cv::detail::VectorRef ref;
cv::util::get<cv::detail::ConstructVec>(info.ctor)(ref);
args.emplace_back(cv::util::make_optional(std::move(ref)));
outs.emplace_back(wrap_opt_arg(cv::util::get<cv::optional<cv::detail::VectorRef>>(args.back())));
break;
}
case cv::GShape::GOPAQUE:
{
cv::detail::OpaqueRef ref;
cv::util::get<cv::detail::ConstructOpaque>(info.ctor)(ref);
args.emplace_back(cv::util::make_optional(std::move(ref)));
outs.emplace_back(wrap_opt_arg(cv::util::get<cv::optional<cv::detail::OpaqueRef>>(args.back())));
break;
}
default:
cv::util::throw_error(std::logic_error("Unsupported optional output shape for Python"));
}
}
}
} // anonymous namespace
class cv::gimpl::GStreamingExecutor::Synchronizer final {
......@@ -1320,6 +1363,16 @@ cv::gimpl::GStreamingExecutor::GStreamingExecutor(std::unique_ptr<ade::Graph> &&
// per the same input frame, so the output traffic multiplies)
GAPI_Assert(m_collector_map.size() > 0u);
m_out_queue.set_capacity(queue_capacity * m_collector_map.size());
// FIXME: The code duplicates logic of collectGraphInfo()
cv::gimpl::GModel::ConstGraph cgr(*m_orig_graph);
auto meta = cgr.metadata().get<cv::gimpl::Protocol>().out_nhs;
out_info.reserve(meta.size());
ade::util::transform(meta, std::back_inserter(out_info), [&cgr](const ade::NodeHandle& nh) {
const auto& data = cgr.metadata(nh).get<cv::gimpl::Data>();
return cv::GTypeInfo{data.shape, data.kind, data.ctor};
});
}
cv::gimpl::GStreamingExecutor::~GStreamingExecutor()
......@@ -1653,6 +1706,31 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs)
return true;
}
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::gimpl::GStreamingExecutor::pull()
{
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
bool is_over = false;
if (m_desync) {
GOptRunArgs opt_run_args;
GOptRunArgsP opt_outs;
opt_outs.reserve(out_info.size());
opt_run_args.reserve(out_info.size());
constructOptGraphOutputs(out_info, opt_run_args, opt_outs);
is_over = pull(std::move(opt_outs));
return std::make_tuple(is_over, RunArgs(opt_run_args));
}
GRunArgs run_args;
GRunArgsP outs;
run_args.reserve(out_info.size());
outs.reserve(out_info.size());
constructGraphOutputs(out_info, run_args, outs);
is_over = pull(std::move(outs));
return std::make_tuple(is_over, RunArgs(run_args));
}
bool cv::gimpl::GStreamingExecutor::try_pull(cv::GRunArgsP &&outs)
{
......
......@@ -195,6 +195,8 @@ protected:
void wait_shutdown();
cv::GTypesInfo out_info;
public:
explicit GStreamingExecutor(std::unique_ptr<ade::Graph> &&g_model,
const cv::GCompileArgs &comp_args);
......@@ -203,6 +205,7 @@ public:
void start();
bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs);
void stop();
bool running() const;
......
......@@ -244,6 +244,35 @@ public:
}
};
void checkPullOverload(const cv::Mat& ref,
const bool has_output,
cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>& args) {
EXPECT_TRUE(has_output);
using runArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
cv::Mat out_mat;
switch (args.index()) {
case runArgs::index_of<cv::GRunArgs>():
{
auto outputs = util::get<cv::GRunArgs>(args);
EXPECT_EQ(1u, outputs.size());
out_mat = cv::util::get<cv::Mat>(outputs[0]);
break;
}
case runArgs::index_of<cv::GOptRunArgs>():
{
auto outputs = util::get<cv::GOptRunArgs>(args);
EXPECT_EQ(1u, outputs.size());
auto opt_mat = cv::util::get<cv::optional<cv::Mat>>(outputs[0]);
ASSERT_TRUE(opt_mat.has_value());
out_mat = *opt_mat;
break;
}
default: GAPI_Assert(false && "Incorrect type of Args");
}
EXPECT_EQ(0., cv::norm(ref, out_mat, cv::NORM_INF));
}
} // anonymous namespace
TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat)
......@@ -1336,13 +1365,45 @@ TEST(Streaming, Python_Pull_Overload)
bool has_output;
cv::GRunArgs outputs;
std::tie(has_output, outputs) = ccomp.pull();
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
EXPECT_TRUE(has_output);
EXPECT_EQ(1u, outputs.size());
std::tie(has_output, args) = ccomp.pull();
checkPullOverload(in_mat, has_output, args);
ccomp.stop();
EXPECT_FALSE(ccomp.running());
}
TEST(GAPI_Streaming_Desync, Python_Pull_Overload)
{
cv::GMat in;
cv::GMat out = cv::gapi::streaming::desync(in);
cv::GComputation c(in, out);
cv::Size sz(3,3);
cv::Mat in_mat(sz, CV_8UC3);
cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar(255));
auto out_mat = cv::util::get<cv::Mat>(outputs[0]);
EXPECT_EQ(0., cv::norm(in_mat, out_mat, cv::NORM_INF));
auto ccomp = c.compileStreaming();
EXPECT_TRUE(ccomp);
EXPECT_FALSE(ccomp.running());
ccomp.setSource(cv::gin(in_mat));
ccomp.start();
EXPECT_TRUE(ccomp.running());
bool has_output;
cv::GRunArgs outputs;
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
std::tie(has_output, args) = ccomp.pull();
checkPullOverload(in_mat, has_output, args);
ccomp.stop();
EXPECT_FALSE(ccomp.running());
......@@ -2132,9 +2193,17 @@ TEST(GAPI_Streaming, TestPythonAPI)
bool is_over = false;
cv::GRunArgs out_args;
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
// NB: Used by python bridge
std::tie(is_over, out_args) = cc.pull();
std::tie(is_over, args) = cc.pull();
switch (args.index()) {
case RunArgs::index_of<cv::GRunArgs>():
out_args = util::get<cv::GRunArgs>(args); break;
default: GAPI_Assert(false && "Incorrect type of return value");
}
ASSERT_EQ(1u, out_args.size());
ASSERT_TRUE(cv::util::holds_alternative<cv::Mat>(out_args[0]));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册