This function applicable for all threshold types except CV_THRESH_OTSU and CV_THRESH_TRIANGLE
@note Function textual ID is "org.opencv.core.matrixop.thresholdOT"
*/
-GAPI_EXPORTS std::tuple<GMat, GScalar> threshold(const GMat& src, const GScalar& maxval, int type);
+GAPI_EXPORTS_W std::tuple<GMat, GScalar> threshold(const GMat& src, const GScalar& maxval, int type);
/** @brief Applies a range-level threshold to each matrix element.
*/
void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); // Arg-to-arg overload
+ /// @private -- Exclude this function from OpenCV documentation
+ GAPI_WRAP GRunArgs apply(GRunArgs &&ins, GCompileArgs &&args = {});
+
/// @private -- Exclude this function from OpenCV documentation
void apply(const std::vector<cv::Mat>& ins, // Compatibility overload
const std::vector<cv::Mat>& outs,
* @param args compilation arguments for underlying compilation
* process.
*/
- GAPI_WRAP void apply(cv::Mat in, CV_OUT cv::Scalar &out, GCompileArgs &&args = {}); // Unary overload (scalar)
+ void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {}); // Unary overload (scalar)
/**
* @brief Execute a binary computation (with compilation on the fly)
* @param args compilation arguments for underlying compilation
* process.
*/
- GAPI_WRAP void apply(cv::Mat in1, cv::Mat in2, CV_OUT cv::Mat &out, GCompileArgs &&args = {}); // Binary overload
+ void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload
/**
* @brief Execute an binary computation (with compilation on the fly)
GCompileArgs comp_args = std::get<sizeof...(Ts)-1>(meta_and_compile_args);
return compileStreaming(std::move(meta_args), std::move(comp_args));
}
+ void recompile(GMetaArgs&& in_metas, GCompileArgs &&args);
/// @private
std::shared_ptr<Priv> m_priv;
};
+#ifndef OPENCV_GAPI_PYOPENCV_GAPI_HPP
+#define OPENCV_GAPI_PYOPENCV_GAPI_HPP
+
+#ifdef HAVE_OPENCV_GAPI
+
using gapi_GKernelPackage = cv::gapi::GKernelPackage;
template<>
return pyopencv_from_generic_vec(value);
}
+template<>
+bool pyopencv_to(PyObject* obj, GRunArgs& value, const ArgInfo& info)
+{
+ return pyopencv_to_generic_vec(obj, value, info);
+}
+
+static PyObject* from_grunarg(const GRunArg& v)
+{
+ switch (v.index())
+ {
+ case GRunArg::index_of<cv::Mat>():
+ {
+ const auto& m = util::get<cv::Mat>(v);
+ return pyopencv_from(m);
+ }
+
+ case GRunArg::index_of<cv::Scalar>():
+ {
+ const auto& s = util::get<cv::Scalar>(v);
+ return pyopencv_from(s);
+ }
+
+ default:
+ return NULL;
+ }
+ GAPI_Assert(false);
+}
+
+template<>
+PyObject* pyopencv_from(const GRunArgs& value)
+{
+ size_t i, n = value.size();
+
+ // NB: It doesn't make sense to return list with a single element
+ if (n == 1)
+ {
+ PyObject* item = from_grunarg(value[0]);
+ if(!item)
+ {
+ PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
+ return NULL;
+ }
+ return item;
+ }
+
+ PyObject* list = PyList_New(n);
+ for(i = 0; i < n; ++i)
+ {
+ PyObject* item = from_grunarg(value[i]);
+ if(!item)
+ {
+ Py_DECREF(list);
+ PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
+ return NULL;
+ }
+ PyList_SetItem(list, i, item);
+ }
+
+ return list;
+}
+
template <typename T>
static PyObject* extract_proto_args(PyObject* py_args, PyObject* kw)
{
GProtoArgs args;
Py_ssize_t size = PyTuple_Size(py_args);
- for (int i = 0; i < size; ++i) {
+ for (int i = 0; i < size; ++i)
+ {
PyObject* item = PyTuple_GetItem(py_args, i);
- if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr))) {
+ if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr)))
+ {
args.emplace_back(reinterpret_cast<pyopencv_GScalar_t*>(item)->v);
- } else if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GMat_TypePtr))) {
+ }
+ else if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GMat_TypePtr)))
+ {
args.emplace_back(reinterpret_cast<pyopencv_GMat_t*>(item)->v);
- } else {
+ }
+ else
+ {
PyErr_SetString(PyExc_TypeError, "cv.GIn() supports only cv.GMat and cv.GScalar");
return NULL;
}
{
return extract_proto_args<GProtoOutputArgs>(py_args, kw);
}
+
+static PyObject* pyopencv_cv_gin(PyObject* , PyObject* py_args, PyObject* kw)
+{
+ using namespace cv;
+
+ GRunArgs args;
+ Py_ssize_t size = PyTuple_Size(py_args);
+ for (int i = 0; i < size; ++i)
+ {
+ PyObject* item = PyTuple_GetItem(py_args, i);
+ if (PyTuple_Check(item))
+ {
+ cv::Scalar s;
+ if (pyopencv_to(item, s, ArgInfo("scalar", true)))
+ {
+ args.emplace_back(s);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError, "Failed convert tuple to cv::Scalar");
+ return NULL;
+ }
+ }
+ else if (PyArray_Check(item))
+ {
+ cv::Mat m;
+ if (pyopencv_to(item, m, ArgInfo("mat", true)))
+ {
+ args.emplace_back(m);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError, "Failed convert array to cv::Mat");
+ return NULL;
+ }
+ }
+ }
+
+ return pyopencv_from_generic_vec(args);
+}
+
+static PyObject* pyopencv_cv_gout(PyObject* o, PyObject* py_args, PyObject* kw)
+{
+ return pyopencv_cv_gin(o, py_args, kw);
+}
+
+#endif // HAVE_OPENCV_GAPI
+#endif // OPENCV_GAPI_PYOPENCV_GAPI_HPP
namespace cv
{
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GKernelPackage pkg);
+
class GAPI_EXPORTS_W_SIMPLE GProtoArg { };
class GAPI_EXPORTS_W_SIMPLE GProtoInputArgs { };
class GAPI_EXPORTS_W_SIMPLE GProtoOutputArgs { };
+ class GAPI_EXPORTS_W_SIMPLE GRunArg { };
using GProtoInputArgs = GIOProtoArgs<In_Tag>;
using GProtoOutputArgs = GIOProtoArgs<Out_Tag>;
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg in pkgs:
- actual = comp.apply(in1, in2, args=cv.compile_args(pkg))
+ actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
comp = cv.GComputation(g_in, g_out)
for pkg in pkgs:
- actual = comp.apply(in_mat, args=cv.compile_args(pkg))
+ actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
+ def test_split3(self):
+ sz = (1280, 720, 3)
+ in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
+
+ # OpenCV
+ expected = cv.split(in_mat)
+
+ # G-API
+ g_in = cv.GMat()
+ b, g, r = cv.gapi.split3(g_in)
+ comp = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r))
+
+ for pkg in pkgs:
+ actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
+ # Comparison
+ for e, a in zip(expected, actual):
+ self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF))
+
+
+ def test_threshold(self):
+ sz = (1280, 720)
+ in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
+ rand_int = np.random.randint(0, 50)
+ maxv = (rand_int, rand_int)
+
+ # OpenCV
+ expected_thresh, expected_mat = cv.threshold(in_mat, maxv[0], maxv[0], cv.THRESH_TRIANGLE)
+
+ # G-API
+ g_in = cv.GMat()
+ g_sc = cv.GScalar()
+ mat, threshold = cv.gapi.threshold(g_in, g_sc, cv.THRESH_TRIANGLE)
+ comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(mat, threshold))
+
+ for pkg in pkgs:
+ actual_mat, actual_thresh = comp.apply(cv.gin(in_mat, maxv), args=cv.compile_args(pkg))
+ # Comparison
+ self.assertEqual(0.0, cv.norm(expected_mat, actual_mat, cv.NORM_INF))
+ self.assertEqual(expected_thresh, actual_thresh[0])
+
+
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
comp = cv.GComputation(g_in, g_out)
for pkg in pkgs:
- actual = comp.apply(in_mat, args=cv.compile_args(pkg))
+ actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
});
}
-void cv::GComputation::apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args)
+void cv::GComputation::recompile(GMetaArgs&& in_metas, GCompileArgs &&args)
{
- const auto in_metas = descr_of(ins);
// FIXME Graph should be recompiled when GCompileArgs have changed
if (m_priv->m_lastMetas != in_metas)
{
if (m_priv->m_lastCompiled &&
- m_priv->m_lastCompiled.canReshape() &&
- formats_are_same(m_priv->m_lastMetas, in_metas))
+ m_priv->m_lastCompiled.canReshape() &&
+ formats_are_same(m_priv->m_lastMetas, in_metas))
{
m_priv->m_lastCompiled.reshape(in_metas, args);
}
}
m_priv->m_lastMetas = in_metas;
}
+}
+
+void cv::GComputation::apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args)
+{
+ recompile(descr_of(ins), std::move(args));
m_priv->m_lastCompiled(std::move(ins), std::move(outs));
}
apply(std::move(call_ins), std::move(call_outs), std::move(args));
}
+// NB: This overload is called from python code
+cv::GRunArgs cv::GComputation::apply(GRunArgs &&ins, GCompileArgs &&args)
+{
+ recompile(descr_of(ins), std::move(args));
+
+ const auto& out_metas = m_priv->m_lastCompiled.outMetas();
+ GRunArgs run_args;
+ GRunArgsP outs;
+ run_args.reserve(out_metas.size());
+ outs.reserve(out_metas.size());
+
+ for (auto&& meta : out_metas)
+ {
+ switch (meta.index())
+ {
+ case cv::GMetaArg::index_of<cv::GMatDesc>():
+ {
+ run_args.emplace_back(cv::Mat{});
+ outs.emplace_back(&cv::util::get<cv::Mat>(run_args.back()));
+ break;
+ }
+ case cv::GMetaArg::index_of<cv::GScalarDesc>():
+ {
+ run_args.emplace_back(cv::Scalar{});
+ outs.emplace_back(&cv::util::get<cv::Scalar>(run_args.back()));
+ break;
+ }
+ default:
+ util::throw_error(std::logic_error("Only cv::GMat and cv::GScalar are supported for python output"));
+ }
+ }
+ m_priv->m_lastCompiled(std::move(ins), std::move(outs));
+ return run_args;
+}
+
#if !defined(GAPI_STANDALONE)
void cv::GComputation::apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args)
{
#include "test_precomp.hpp"
+
+#include <opencv2/gapi/s11n.hpp>
+
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <ade/util/zip_range.hpp>
}
}
};
+
+ // NB: Check an apply specifically designed to be called from Python,
+ // but can also be used from C++
+ struct GComputationPythonApplyTest: public ::testing::Test
+ {
+ cv::Size sz;
+ MatType type;
+ cv::Mat in_mat1, in_mat2, out_mat_ocv;
+ cv::GComputation m_c;
+
+ GComputationPythonApplyTest() : sz(cv::Size(300,300)), type(CV_8UC1),
+ in_mat1(sz, type), in_mat2(sz, type), out_mat_ocv(sz, type),
+ m_c([&](){
+ cv::GMat in1, in2;
+ cv::GMat out = in1 + in2;
+ return cv::GComputation(cv::GIn(in1, in2), cv::GOut(out));
+ })
+ {
+ cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+ cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+ out_mat_ocv = in_mat1 + in_mat2;
+ }
+
+ };
+ }
+
+ TEST_F(GComputationPythonApplyTest, WithoutSerialization)
+ {
+ auto output = m_c.apply(cv::gin(in_mat1, in_mat2));
+ EXPECT_EQ(1u, output.size());
+
+ const auto& out_mat_gapi = cv::util::get<cv::Mat>(output[0]);
+ EXPECT_EQ(0, cvtest::norm(out_mat_ocv, out_mat_gapi, NORM_INF));
+ }
+
+ TEST_F(GComputationPythonApplyTest, WithSerialization)
+ {
+ auto p = cv::gapi::serialize(m_c);
+ auto c = cv::gapi::deserialize<cv::GComputation>(p);
+
+ auto output = c.apply(cv::gin(in_mat1, in_mat2));
+ EXPECT_EQ(1u, output.size());
+
+ const auto& out_mat_gapi = cv::util::get<cv::Mat>(output[0]);
+ EXPECT_EQ(0, cvtest::norm(out_mat_ocv, out_mat_gapi, NORM_INF));
}
TEST_F(GComputationApplyTest, ThrowDontPassCustomKernel)
#ifdef HAVE_OPENCV_GAPI
{"GIn", CV_PY_FN_WITH_KW(pyopencv_cv_GIn), "GIn(...) -> GInputProtoArgs"},
{"GOut", CV_PY_FN_WITH_KW(pyopencv_cv_GOut), "GOut(...) -> GOutputProtoArgs"},
+ {"gin", CV_PY_FN_WITH_KW(pyopencv_cv_gin), "gin(...) -> GRunArgs"},
#endif
{NULL, NULL},
};