Support tuple for python bindings
authorAnatoliy Talamanov <anatoliy.talamanov@intel.com>
Tue, 15 Sep 2020 11:49:35 +0000 (14:49 +0300)
committerAnatoliy Talamanov <anatoliy.talamanov@intel.com>
Wed, 16 Sep 2020 13:22:46 +0000 (16:22 +0300)
modules/gapi/include/opencv2/gapi/core.hpp
modules/gapi/include/opencv2/gapi/gcomputation.hpp
modules/gapi/misc/python/test/test_gapi_sample_pipelines.py [new file with mode: 0644]
modules/python/src2/cv2.cpp

index 5b9fb5a..a4db7e1 100644 (file)
@@ -1434,7 +1434,7 @@ All output matrices must be in @ref CV_8UC1.
 @sa merge3, merge4
 */
 GAPI_EXPORTS std::tuple<GMat, GMat, GMat,GMat> split4(const GMat& src);
-GAPI_EXPORTS std::tuple<GMat, GMat, GMat> split3(const GMat& src);
+GAPI_EXPORTS_W std::tuple<GMat, GMat, GMat> split3(const GMat& src);
 
 /** @brief Applies a generic geometrical transformation to an image.
 
index d6b8561..bae2ad7 100644 (file)
@@ -172,7 +172,7 @@ public:
      * @param in input GMat of the defined unary computation
      * @param out output GMat of the defined unary computation
      */
-    GComputation(GMat in, GMat out);                   // Unary overload
+    GAPI_WRAP GComputation(GMat in, GMat out);  // Unary overload
 
     /**
      * @brief Defines an unary (one input -- one output) computation
diff --git a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py
new file mode 100644 (file)
index 0000000..d24a0de
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+import numpy as np
+import cv2 as cv
+
+from tests_common import NewOpenCVTests
+
+
+# Plaidml is an optional backend
+pkgs = [
+         cv.gapi.core.ocl.kernels(),
+         cv.gapi.core.cpu.kernels(),
+         cv.gapi.core.fluid.kernels()
+         # cv.gapi.core.plaidml.kernels()
+       ]
+
+
+class gapi_sample_pipelines(NewOpenCVTests):
+
+    # NB: This test check multiple outputs for operation
+    def test_mean_over_r(self):
+        sz = (100, 100, 3)
+        in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
+
+        # # OpenCV
+        _, _, r_ch = cv.split(in_mat)
+        expected = cv.mean(r_ch)
+
+        # G-API
+        g_in = cv.GMat()
+        b, g, r = cv.gapi.split3(g_in)
+        g_out = cv.gapi.mean(r)
+        comp = cv.GComputation(g_in, g_out)
+
+        actual = comp.apply(in_mat)
+
+        for pkg in pkgs:
+            actual = comp.apply(in_mat, args=cv.compile_args(pkg))
+            # Comparison
+            self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
+
+
+if __name__ == '__main__':
+    NewOpenCVTests.bootstrap()
index 89dcbda..3d49a95 100644 (file)
@@ -1486,6 +1486,41 @@ template<typename _Tp> static inline PyObject* pyopencv_from_generic_vec(const s
     return seq;
 }
 
+template<std::size_t I = 0, typename... Tp>
+inline typename std::enable_if<I == sizeof...(Tp), void>::type
+convert_to_python_tuple(const std::tuple<Tp...>&, PyObject*) {  }
+
+template<std::size_t I = 0, typename... Tp>
+inline typename std::enable_if<I < sizeof...(Tp), void>::type
+convert_to_python_tuple(const std::tuple<Tp...>& cpp_tuple, PyObject* py_tuple)
+{
+    PyObject* item = pyopencv_from(std::get<I>(cpp_tuple));
+
+    if (!item)
+        return;
+
+    PyTuple_SET_ITEM(py_tuple, I, item);
+    convert_to_python_tuple<I + 1, Tp...>(cpp_tuple, py_tuple);
+}
+
+
+template<typename... Ts>
+PyObject* pyopencv_from(const std::tuple<Ts...>& cpp_tuple)
+{
+    size_t size = sizeof...(Ts);
+    PyObject* py_tuple = PyTuple_New(size);
+    convert_to_python_tuple(cpp_tuple, py_tuple);
+    size_t actual_size = PyTuple_Size(py_tuple);
+
+    if (actual_size < size)
+    {
+        Py_DECREF(py_tuple);
+        return NULL;
+    }
+
+    return py_tuple;
+}
+
 template<>
 PyObject* pyopencv_from(const std::pair<int, double>& src)
 {