G-API: Python. Desync.
* Desync. GMat.
* Alignment
>;
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) {
// 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.
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>():
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>
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!");
}
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();
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!");
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;
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;
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;
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
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
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):
proc_num_frames += 1
if proc_num_frames == max_num_frames:
- break;
+ break
def test_video_good_features_to_track(self):
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:
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));
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)
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();
} // 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 {
// 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()
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)
{
void wait_shutdown();
+ cv::GTypesInfo out_info;
+
public:
explicit GStreamingExecutor(std::unique_ptr<ade::Graph> &&g_model,
const cv::GCompileArgs &comp_args);
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;
}
};
+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)
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());
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]));