From 7521f207b182744e9354a61e969b2e4f87903cd2 Mon Sep 17 00:00:00 2001 From: Anatoliy Talamanov Date: Fri, 27 Nov 2020 20:39:46 +0300 Subject: [PATCH] Merge pull request #18762 from TolyaTalamanov:at/support-garray [G-API] Wrap GArray * Wrap GArray for output * Collect in/out info in graph * Add imgproc tests * Add cv::Point2f * Update test_gapi_imgproc.py * Fix comments to review --- modules/gapi/include/opencv2/gapi/garray.hpp | 2 + modules/gapi/include/opencv2/gapi/gcommon.hpp | 6 +- modules/gapi/include/opencv2/gapi/gkernel.hpp | 13 +++- modules/gapi/include/opencv2/gapi/imgproc.hpp | 4 +- .../gapi/include/opencv2/gapi/opencv_includes.hpp | 11 +-- modules/gapi/include/opencv2/gapi/own/types.hpp | 10 +++ modules/gapi/include/opencv2/gapi/s11n.hpp | 3 + modules/gapi/misc/python/pyopencv_gapi.hpp | 21 +++++- modules/gapi/misc/python/shadow_gapi.hpp | 2 + modules/gapi/misc/python/test/test_gapi_core.py | 75 ++++++++++---------- modules/gapi/misc/python/test/test_gapi_imgproc.py | 79 ++++++++++++++++++++++ .../misc/python/test/test_gapi_sample_pipelines.py | 20 +++--- .../gapi/misc/python/test/test_gapi_streaming.py | 73 ++++++++++++++++++++ modules/gapi/src/api/gcomputation.cpp | 30 ++++++-- modules/gapi/src/backends/common/serialization.cpp | 52 ++++++++------ modules/gapi/src/compiler/gcompiled_priv.hpp | 10 +++ modules/gapi/src/compiler/gcompiler.cpp | 39 +++++++++-- modules/gapi/src/compiler/gstreaming.cpp | 24 +++++-- modules/gapi/src/compiler/gstreaming_priv.hpp | 14 ++-- modules/gapi/test/own/gapi_types_tests.cpp | 16 +++++ 20 files changed, 400 insertions(+), 104 deletions(-) create mode 100644 modules/gapi/misc/python/test/test_gapi_imgproc.py diff --git a/modules/gapi/include/opencv2/gapi/garray.hpp b/modules/gapi/include/opencv2/gapi/garray.hpp index 0798655..5d4b3c5 100644 --- a/modules/gapi/include/opencv2/gapi/garray.hpp +++ b/modules/gapi/include/opencv2/gapi/garray.hpp @@ -368,6 +368,8 @@ private: detail::GArrayU m_ref; }; +using GArrayP2f = GArray; + /** @} */ } // namespace cv diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp index 0242020..a474140 100644 --- a/modules/gapi/include/opencv2/gapi/gcommon.hpp +++ b/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -49,6 +49,7 @@ namespace detail CV_UINT64, // uint64_t user G-API data CV_STRING, // std::string user G-API data CV_POINT, // cv::Point user G-API data + CV_POINT2F, // cv::Point2f user G-API data CV_SIZE, // cv::Size user G-API data CV_RECT, // cv::Rect user G-API data CV_SCALAR, // cv::Scalar user G-API data @@ -68,15 +69,16 @@ namespace detail template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_SIZE; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_SCALAR; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT; }; + template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_POINT2F; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_RECT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_MAT; }; template<> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_DRAW_PRIM; }; - using GOpaqueTraitsArrayTypes = std::tuple; // GOpaque is not supporting cv::Mat and cv::Scalar since there are GScalar and GMat types - using GOpaqueTraitsOpaqueTypes = std::tuple; } // namespace detail diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp index d4c3e6c..0ec7dd0 100644 --- a/modules/gapi/include/opencv2/gapi/gkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -26,9 +26,16 @@ namespace cv { -using GShapes = std::vector; -using GKinds = std::vector; -using GCtors = std::vector; +struct GTypeInfo +{ + GShape shape; + cv::detail::OpaqueKind kind; +}; + +using GShapes = std::vector; +using GKinds = std::vector; +using GCtors = std::vector; +using GTypesInfo = std::vector; // GKernel describes kernel API to the system // FIXME: add attributes of a kernel, (e.g. number and types diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp index e41c250..7435ec1 100644 --- a/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -1035,7 +1035,7 @@ or #cornerMinEigenVal. @return vector of detected corners. */ -GAPI_EXPORTS GArray goodFeaturesToTrack(const GMat &image, +GAPI_EXPORTS_W GArray goodFeaturesToTrack(const GMat &image, int maxCorners, double qualityLevel, double minDistance, @@ -1350,7 +1350,7 @@ Resulting gray color value computed as @param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1. @sa RGB2YUV */ -GAPI_EXPORTS GMat RGB2Gray(const GMat& src); +GAPI_EXPORTS_W GMat RGB2Gray(const GMat& src); /** @overload Resulting gray color value computed as diff --git a/modules/gapi/include/opencv2/gapi/opencv_includes.hpp b/modules/gapi/include/opencv2/gapi/opencv_includes.hpp index 5f25fe4..08b2d6e 100644 --- a/modules/gapi/include/opencv2/gapi/opencv_includes.hpp +++ b/modules/gapi/include/opencv2/gapi/opencv_includes.hpp @@ -21,11 +21,12 @@ # include // replacement of cv's structures: namespace cv { - using Rect = gapi::own::Rect; - using Size = gapi::own::Size; - using Point = gapi::own::Point; - using Scalar = gapi::own::Scalar; - using Mat = gapi::own::Mat; + using Rect = gapi::own::Rect; + using Size = gapi::own::Size; + using Point = gapi::own::Point; + using Point2f = gapi::own::Point2f; + using Scalar = gapi::own::Scalar; + using Mat = gapi::own::Mat; } // namespace cv #endif // !defined(GAPI_STANDALONE) diff --git a/modules/gapi/include/opencv2/gapi/own/types.hpp b/modules/gapi/include/opencv2/gapi/own/types.hpp index 20445ee..c77a62c 100644 --- a/modules/gapi/include/opencv2/gapi/own/types.hpp +++ b/modules/gapi/include/opencv2/gapi/own/types.hpp @@ -28,6 +28,16 @@ public: int y = 0; }; +class Point2f +{ +public: + Point2f() = default; + Point2f(float _x, float _y) : x(_x), y(_y) {}; + + float x = 0.f; + float y = 0.f; +}; + class Rect { public: diff --git a/modules/gapi/include/opencv2/gapi/s11n.hpp b/modules/gapi/include/opencv2/gapi/s11n.hpp index 0e3e382..0e2c4c2 100644 --- a/modules/gapi/include/opencv2/gapi/s11n.hpp +++ b/modules/gapi/include/opencv2/gapi/s11n.hpp @@ -121,6 +121,9 @@ GAPI_EXPORTS std::unique_ptr getInStream(const std::vector &p); GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point &pt); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point &pt); +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Point2f &pt); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Point2f &pt); + GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::Size &sz); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Size &sz); diff --git a/modules/gapi/misc/python/pyopencv_gapi.hpp b/modules/gapi/misc/python/pyopencv_gapi.hpp index 57c0b3d..e25328e 100644 --- a/modules/gapi/misc/python/pyopencv_gapi.hpp +++ b/modules/gapi/misc/python/pyopencv_gapi.hpp @@ -47,8 +47,20 @@ static PyObject* from_grunarg(const GRunArg& v) const auto& s = util::get(v); return pyopencv_from(s); } - + case GRunArg::index_of(): + { + const auto& vref = util::get(v); + switch (vref.getKind()) + { + case cv::detail::OpaqueKind::CV_POINT2F: + return pyopencv_from(vref.rref()); + default: + PyErr_SetString(PyExc_TypeError, "Unsupported kind for GArray"); + return NULL; + } + } default: + PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs"); return NULL; } GAPI_Assert(false); @@ -65,7 +77,6 @@ PyObject* pyopencv_from(const GRunArgs& value) PyObject* item = from_grunarg(value[0]); if(!item) { - PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs"); return NULL; } return item; @@ -117,9 +128,13 @@ static PyObject* extract_proto_args(PyObject* py_args, PyObject* kw) { args.emplace_back(reinterpret_cast(item)->v); } + else if (PyObject_TypeCheck(item, reinterpret_cast(pyopencv_GArrayP2f_TypePtr))) + { + args.emplace_back(reinterpret_cast(item)->v.strip()); + } else { - PyErr_SetString(PyExc_TypeError, "cv.GIn() supports only cv.GMat and cv.GScalar"); + PyErr_SetString(PyExc_TypeError, "Unsupported type for cv.GIn()/cv.GOut()"); return NULL; } } diff --git a/modules/gapi/misc/python/shadow_gapi.hpp b/modules/gapi/misc/python/shadow_gapi.hpp index 0fac222..7923145 100644 --- a/modules/gapi/misc/python/shadow_gapi.hpp +++ b/modules/gapi/misc/python/shadow_gapi.hpp @@ -16,6 +16,8 @@ namespace cv class GAPI_EXPORTS_W_SIMPLE GRunArg { }; class GAPI_EXPORTS_W_SIMPLE GMetaArg { }; + class GAPI_EXPORTS_W_SIMPLE GArrayP2f { }; + using GProtoInputArgs = GIOProtoArgs; using GProtoOutputArgs = GIOProtoArgs; diff --git a/modules/gapi/misc/python/test/test_gapi_core.py b/modules/gapi/misc/python/test/test_gapi_core.py index cd85d9c..267037a 100644 --- a/modules/gapi/misc/python/test/test_gapi_core.py +++ b/modules/gapi/misc/python/test/test_gapi_core.py @@ -2,26 +2,27 @@ import numpy as np import cv2 as cv +import os 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() - ] + ('ocl' , cv.gapi.core.ocl.kernels()), + ('cpu' , cv.gapi.core.cpu.kernels()), + ('fluid' , cv.gapi.core.fluid.kernels()) + # ('plaidml', cv.gapi.core.plaidml.kernels()) + ] class gapi_core_test(NewOpenCVTests): def test_add(self): # TODO: Extend to use any type and size here - sz = (1280, 720) - in1 = np.random.randint(0, 100, sz) - in2 = np.random.randint(0, 100, sz) + sz = (720, 1280) + in1 = np.full(sz, 100) + in2 = np.full(sz, 50) # OpenCV expected = cv.add(in1, in2) @@ -32,17 +33,18 @@ class gapi_core_test(NewOpenCVTests): g_out = cv.gapi.add(g_in1, g_in2) comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out)) - for pkg in pkgs: + for pkg_name, pkg in pkgs: actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg)) # Comparison - self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) - self.assertEqual(expected.dtype, actual.dtype) + self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend') def test_add_uint8(self): - sz = (1280, 720) - in1 = np.random.randint(0, 100, sz).astype(np.uint8) - in2 = np.random.randint(0, 100, sz).astype(np.uint8) + sz = (720, 1280) + in1 = np.full(sz, 100, dtype=np.uint8) + in2 = np.full(sz, 50 , dtype=np.uint8) # OpenCV expected = cv.add(in1, in2) @@ -53,16 +55,17 @@ class gapi_core_test(NewOpenCVTests): g_out = cv.gapi.add(g_in1, g_in2) comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out)) - for pkg in pkgs: + for pkg_name, pkg in pkgs: actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg)) # Comparison - self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) - self.assertEqual(expected.dtype, actual.dtype) + self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend') def test_mean(self): - sz = (1280, 720, 3) - in_mat = np.random.randint(0, 100, sz) + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in_mat = cv.imread(img_path) # OpenCV expected = cv.mean(in_mat) @@ -72,15 +75,16 @@ class gapi_core_test(NewOpenCVTests): g_out = cv.gapi.mean(g_in) comp = cv.GComputation(g_in, g_out) - for pkg in pkgs: + for pkg_name, pkg in pkgs: actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg)) # Comparison - self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) + self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') def test_split3(self): - sz = (1280, 720, 3) - in_mat = np.random.randint(0, 100, sz) + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in_mat = cv.imread(img_path) # OpenCV expected = cv.split(in_mat) @@ -90,19 +94,19 @@ class gapi_core_test(NewOpenCVTests): b, g, r = cv.gapi.split3(g_in) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r)) - for pkg in pkgs: + for pkg_name, 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)) - self.assertEqual(e.dtype, a.dtype) + self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + self.assertEqual(e.dtype, a.dtype, 'Failed on ' + pkg_name + ' backend') 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) + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in_mat = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY) + maxv = (30, 30) # OpenCV expected_thresh, expected_mat = cv.threshold(in_mat, maxv[0], maxv[0], cv.THRESH_TRIANGLE) @@ -113,12 +117,15 @@ class gapi_core_test(NewOpenCVTests): 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: + for pkg_name, 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_mat.dtype, actual_mat.dtype) - self.assertEqual(expected_thresh, actual_thresh[0]) + self.assertEqual(0.0, cv.norm(expected_mat, actual_mat, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + self.assertEqual(expected_mat.dtype, actual_mat.dtype, + 'Failed on ' + pkg_name + ' backend') + self.assertEqual(expected_thresh, actual_thresh[0], + 'Failed on ' + pkg_name + ' backend') if __name__ == '__main__': diff --git a/modules/gapi/misc/python/test/test_gapi_imgproc.py b/modules/gapi/misc/python/test/test_gapi_imgproc.py new file mode 100644 index 0000000..dd1e397 --- /dev/null +++ b/modules/gapi/misc/python/test/test_gapi_imgproc.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +import numpy as np +import cv2 as cv +import os + +from tests_common import NewOpenCVTests + + +# Plaidml is an optional backend +pkgs = [ + ('ocl' , cv.gapi.core.ocl.kernels()), + ('cpu' , cv.gapi.core.cpu.kernels()), + ('fluid' , cv.gapi.core.fluid.kernels()) + # ('plaidml', cv.gapi.core.plaidml.kernels()) + ] + + +class gapi_imgproc_test(NewOpenCVTests): + + def test_good_features_to_track(self): + # TODO: Extend to use any type and size here + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in1 = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY) + + # NB: goodFeaturesToTrack configuration + max_corners = 50 + quality_lvl = 0.01 + min_distance = 10 + block_sz = 3 + use_harris_detector = True + k = 0.04 + mask = None + + # OpenCV + expected = cv.goodFeaturesToTrack(in1, max_corners, quality_lvl, + min_distance, mask=mask, + blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k) + + # G-API + g_in = cv.GMat() + g_out = cv.gapi.goodFeaturesToTrack(g_in, max_corners, quality_lvl, + min_distance, mask, block_sz, use_harris_detector, k) + + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + + for pkg_name, pkg in pkgs: + actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg)) + # NB: OpenCV & G-API have different output shapes: + # OpenCV - (num_points, 1, 2) + # G-API - (num_points, 2) + # Comparison + self.assertEqual(0.0, cv.norm(expected.flatten(), actual.flatten(), cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + + + def test_rgb2gray(self): + # TODO: Extend to use any type and size here + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in1 = cv.imread(img_path) + + # OpenCV + expected = cv.cvtColor(in1, cv.COLOR_RGB2GRAY) + + # G-API + g_in = cv.GMat() + g_out = cv.gapi.RGB2Gray(g_in) + + comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + + for pkg_name, pkg in pkgs: + actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg)) + # Comparison + self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') + + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py index 8000496..53304fc 100644 --- a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py +++ b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py @@ -2,25 +2,26 @@ import numpy as np import cv2 as cv +import os 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() - ] + ('ocl' , cv.gapi.core.ocl.kernels()), + ('cpu' , cv.gapi.core.cpu.kernels()), + ('fluid' , cv.gapi.core.fluid.kernels()) + # ('plaidml', 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) + img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) + in_mat = cv.imread(img_path) # # OpenCV _, _, r_ch = cv.split(in_mat) @@ -32,10 +33,11 @@ class gapi_sample_pipelines(NewOpenCVTests): g_out = cv.gapi.mean(r) comp = cv.GComputation(g_in, g_out) - for pkg in pkgs: + for pkg_name, pkg in pkgs: actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg)) # Comparison - self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) + self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), + 'Failed on ' + pkg_name + ' backend') if __name__ == '__main__': diff --git a/modules/gapi/misc/python/test/test_gapi_streaming.py b/modules/gapi/misc/python/test/test_gapi_streaming.py index bf182d9..ae7ef5d 100644 --- a/modules/gapi/misc/python/test/test_gapi_streaming.py +++ b/modules/gapi/misc/python/test/test_gapi_streaming.py @@ -47,6 +47,8 @@ class test_gapi_streaming(NewOpenCVTests): ccomp.start() # Assert + max_num_frames = 10 + proc_num_frames = 0 while cap.isOpened(): has_expected, expected = cap.read() has_actual, actual = ccomp.pull() @@ -58,6 +60,10 @@ class test_gapi_streaming(NewOpenCVTests): self.assertEqual(0.0, cv.norm(cv.medianBlur(expected, ksize), actual, cv.NORM_INF)) + proc_num_frames += 1 + if proc_num_frames == max_num_frames: + break; + def test_video_split3(self): path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']]) @@ -76,6 +82,8 @@ class test_gapi_streaming(NewOpenCVTests): ccomp.start() # Assert + max_num_frames = 10 + proc_num_frames = 0 while cap.isOpened(): has_expected, frame = cap.read() has_actual, actual = ccomp.pull() @@ -89,6 +97,10 @@ class test_gapi_streaming(NewOpenCVTests): for e, a in zip(expected, actual): self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF)) + proc_num_frames += 1 + if proc_num_frames == max_num_frames: + break; + def test_video_add(self): sz = (576, 768, 3) @@ -111,6 +123,8 @@ class test_gapi_streaming(NewOpenCVTests): ccomp.start() # Assert + max_num_frames = 10 + proc_num_frames = 0 while cap.isOpened(): has_expected, frame = cap.read() has_actual, actual = ccomp.pull() @@ -123,6 +137,65 @@ class test_gapi_streaming(NewOpenCVTests): expected = cv.add(frame, in_mat) self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) + proc_num_frames += 1 + if proc_num_frames == max_num_frames: + break; + + + def test_video_good_features_to_track(self): + path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']]) + + # NB: goodFeaturesToTrack configuration + max_corners = 50 + quality_lvl = 0.01 + min_distance = 10 + block_sz = 3 + use_harris_detector = True + k = 0.04 + mask = None + + # OpenCV + cap = cv.VideoCapture(path) + + # G-API + g_in = cv.GMat() + g_gray = cv.gapi.RGB2Gray(g_in) + g_out = cv.gapi.goodFeaturesToTrack(g_gray, max_corners, quality_lvl, + min_distance, mask, block_sz, use_harris_detector, k) + + c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out)) + + ccomp = c.compileStreaming() + source = cv.gapi.wip.make_capture_src(path) + ccomp.setSource(source) + ccomp.start() + + # Assert + max_num_frames = 10 + proc_num_frames = 0 + while cap.isOpened(): + has_expected, frame = cap.read() + has_actual, actual = ccomp.pull() + + self.assertEqual(has_expected, has_actual) + + if not has_actual: + break + + # OpenCV + frame = cv.cvtColor(frame, cv.COLOR_RGB2GRAY) + expected = cv.goodFeaturesToTrack(frame, max_corners, quality_lvl, + min_distance, mask=mask, + blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k) + for e, a in zip(expected, actual): + # NB: OpenCV & G-API have different output shapes: + # OpenCV - (num_points, 1, 2) + # G-API - (num_points, 2) + self.assertEqual(0.0, cv.norm(e.flatten(), a.flatten(), cv.NORM_INF)) + + proc_num_frames += 1 + if proc_num_frames == max_num_frames: + break; if __name__ == '__main__': diff --git a/modules/gapi/src/api/gcomputation.cpp b/modules/gapi/src/api/gcomputation.cpp index 9ff0273..5668cdd 100644 --- a/modules/gapi/src/api/gcomputation.cpp +++ b/modules/gapi/src/api/gcomputation.cpp @@ -9,6 +9,7 @@ #include // remove_if #include // isspace (non-locale version) #include +#include // util::indexed #include "logger.hpp" // GAPI_LOG @@ -21,6 +22,7 @@ #include "compiler/gmodelbuilder.hpp" #include "compiler/gcompiler.hpp" +#include "compiler/gcompiled_priv.hpp" // cv::GComputation private implementation ///////////////////////////////////// // @@ -174,28 +176,42 @@ 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(); + const auto& out_info = m_priv->m_lastCompiled.priv().outInfo(); + GRunArgs run_args; GRunArgsP outs; - run_args.reserve(out_metas.size()); - outs.reserve(out_metas.size()); + run_args.reserve(out_info.size()); + outs.reserve(out_info.size()); - for (auto&& meta : out_metas) + for (auto&& info : out_info) { - switch (meta.index()) + switch (info.shape) { - case cv::GMetaArg::index_of(): + case cv::GShape::GMAT: { run_args.emplace_back(cv::Mat{}); outs.emplace_back(&cv::util::get(run_args.back())); break; } - case cv::GMetaArg::index_of(): + case cv::GShape::GSCALAR: { run_args.emplace_back(cv::Scalar{}); outs.emplace_back(&cv::util::get(run_args.back())); break; } + case cv::GShape::GARRAY: + { + switch (info.kind) + { + case cv::detail::OpaqueKind::CV_POINT2F: + run_args.emplace_back(cv::detail::VectorRef{std::vector{}}); + outs.emplace_back(cv::util::get(run_args.back())); + break; + default: + util::throw_error(std::logic_error("Unsupported kind for GArray")); + } + break; + } default: util::throw_error(std::logic_error("Only cv::GMat and cv::GScalar are supported for python output")); } diff --git a/modules/gapi/src/backends/common/serialization.cpp b/modules/gapi/src/backends/common/serialization.cpp index 592c03c..8c2313b 100644 --- a/modules/gapi/src/backends/common/serialization.cpp +++ b/modules/gapi/src/backends/common/serialization.cpp @@ -152,6 +152,13 @@ IIStream& operator>> (IIStream& is, cv::Point& pt) { return is >> pt.x >> pt.y; } +IOStream& operator<< (IOStream& os, const cv::Point2f &pt) { + return os << pt.x << pt.y; +} +IIStream& operator>> (IIStream& is, cv::Point2f& pt) { + return is >> pt.x >> pt.y; +} + IOStream& operator<< (IOStream& os, const cv::Size &sz) { return os << sz.width << sz.height; } @@ -516,17 +523,17 @@ IOStream& operator<< (IOStream& os, const cv::GArg &arg) { GAPI_Assert(arg.kind == cv::detail::ArgKind::OPAQUE_VAL); GAPI_Assert(arg.opaque_kind != cv::detail::OpaqueKind::CV_UNKNOWN); switch (arg.opaque_kind) { - case cv::detail::OpaqueKind::CV_BOOL: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_INT: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_UINT64: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_DOUBLE: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_FLOAT: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_STRING: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_POINT: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_SIZE: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_RECT: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_SCALAR: os << arg.get(); break; - case cv::detail::OpaqueKind::CV_MAT: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_BOOL: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_INT: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_UINT64: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_DOUBLE: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_FLOAT: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_STRING: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_POINT: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_SIZE: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_RECT: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_SCALAR: os << arg.get(); break; + case cv::detail::OpaqueKind::CV_MAT: os << arg.get(); break; default: GAPI_Assert(false && "GArg: Unsupported (unknown?) opaque value type"); } } @@ -550,17 +557,18 @@ IIStream& operator>> (IIStream& is, cv::GArg &arg) { switch (arg.opaque_kind) { #define HANDLE_CASE(E,T) case cv::detail::OpaqueKind::CV_##E: \ { T t{}; is >> t; arg = (cv::GArg(t)); } break - HANDLE_CASE(BOOL , bool); - HANDLE_CASE(INT , int); - HANDLE_CASE(UINT64 , uint64_t); - HANDLE_CASE(DOUBLE , double); - HANDLE_CASE(FLOAT , float); - HANDLE_CASE(STRING , std::string); - HANDLE_CASE(POINT , cv::Point); - HANDLE_CASE(SIZE , cv::Size); - HANDLE_CASE(RECT , cv::Rect); - HANDLE_CASE(SCALAR , cv::Scalar); - HANDLE_CASE(MAT , cv::Mat); + HANDLE_CASE(BOOL , bool); + HANDLE_CASE(INT , int); + HANDLE_CASE(UINT64 , uint64_t); + HANDLE_CASE(DOUBLE , double); + HANDLE_CASE(FLOAT , float); + HANDLE_CASE(STRING , std::string); + HANDLE_CASE(POINT , cv::Point); + HANDLE_CASE(POINT2F , cv::Point2f); + HANDLE_CASE(SIZE , cv::Size); + HANDLE_CASE(RECT , cv::Rect); + HANDLE_CASE(SCALAR , cv::Scalar); + HANDLE_CASE(MAT , cv::Mat); #undef HANDLE_CASE default: GAPI_Assert(false && "GArg: Unsupported (unknown?) opaque value type"); } diff --git a/modules/gapi/src/compiler/gcompiled_priv.hpp b/modules/gapi/src/compiler/gcompiled_priv.hpp index f21bfc8..b08b1f9 100644 --- a/modules/gapi/src/compiler/gcompiled_priv.hpp +++ b/modules/gapi/src/compiler/gcompiled_priv.hpp @@ -38,6 +38,10 @@ class GAPI_EXPORTS GCompiled::Priv GMetaArgs m_outMetas; // inferred by compiler std::unique_ptr m_exec; + // NB: Used by python wrapper to clarify input/output types + GTypesInfo m_out_info; + GTypesInfo m_in_info; + void checkArgs(const cv::gimpl::GRuntimeArgs &args) const; public: @@ -55,6 +59,12 @@ public: const GMetaArgs& outMetas() const; const cv::gimpl::GModel::Graph& model() const; + + void setOutInfo(const GTypesInfo& info) { m_out_info = std::move(info); } + const GTypesInfo& outInfo() const { return m_out_info; } + + void setInInfo(const GTypesInfo& info) { m_in_info = std::move(info); } + const GTypesInfo& inInfo() const { return m_in_info; } }; } diff --git a/modules/gapi/src/compiler/gcompiler.cpp b/modules/gapi/src/compiler/gcompiler.cpp index f6fa398..4d050db 100644 --- a/modules/gapi/src/compiler/gcompiler.cpp +++ b/modules/gapi/src/compiler/gcompiler.cpp @@ -417,6 +417,19 @@ void cv::gimpl::GCompiler::compileIslands(ade::Graph &g, const cv::GCompileArgs GIslandModel::compileIslands(gim, g, args); } +static cv::GTypesInfo collectInfo(const cv::gimpl::GModel::ConstGraph& g, + const std::vector& nhs) { + cv::GTypesInfo info; + info.reserve(nhs.size()); + + ade::util::transform(nhs, std::back_inserter(info), [&g](const ade::NodeHandle& nh) { + const auto& data = g.metadata(nh).get(); + return cv::GTypeInfo{data.shape, data.kind}; + }); + + return info; +} + cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg) { // This is the final compilation step. Here: @@ -435,6 +448,8 @@ cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg) // an execution plan for it (backend-specific execution) // ...before call to produceCompiled(); + GModel::ConstGraph cgr(*pg); + const auto &outMetas = GModel::ConstGraph(*pg).metadata() .get().outMeta; std::unique_ptr pE(new GExecutor(std::move(pg))); @@ -443,6 +458,14 @@ cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg) GCompiled compiled; compiled.priv().setup(m_metas, outMetas, std::move(pE)); + + // NB: Need to store input/output GTypeInfo to allocate output arrays for python bindings + auto out_meta = collectInfo(cgr, cgr.metadata().get().out_nhs); + auto in_meta = collectInfo(cgr, cgr.metadata().get().in_nhs); + + compiled.priv().setOutInfo(std::move(out_meta)); + compiled.priv().setInInfo(std::move(in_meta)); + return compiled; } @@ -458,13 +481,15 @@ cv::GStreamingCompiled cv::gimpl::GCompiler::produceStreamingCompiled(GPtr &&pg) outMetas = GModel::ConstGraph(*pg).metadata().get().outMeta; } - auto out_desc = GModel::ConstGraph(*pg).metadata().get().outputs; - GShapes out_shapes; - for (auto&& desc : out_desc) - { - out_shapes.push_back(desc.shape); - } - compiled.priv().setOutShapes(std::move(out_shapes)); + + GModel::ConstGraph cgr(*pg); + + // NB: Need to store input/output GTypeInfo to allocate output arrays for python bindings + auto out_meta = collectInfo(cgr, cgr.metadata().get().out_nhs); + auto in_meta = collectInfo(cgr, cgr.metadata().get().in_nhs); + + compiled.priv().setOutInfo(std::move(out_meta)); + compiled.priv().setInInfo(std::move(in_meta)); std::unique_ptr pE(new GStreamingExecutor(std::move(pg), m_args)); diff --git a/modules/gapi/src/compiler/gstreaming.cpp b/modules/gapi/src/compiler/gstreaming.cpp index eb06f3f..fa736d5 100644 --- a/modules/gapi/src/compiler/gstreaming.cpp +++ b/modules/gapi/src/compiler/gstreaming.cpp @@ -8,6 +8,7 @@ #include "precomp.hpp" #include +#include // util::indexed #include // can_describe #include @@ -121,13 +122,13 @@ std::tuple cv::GStreamingCompiled::pull() // FIXME: Why it is not @ priv?? GRunArgs run_args; GRunArgsP outs; - const auto& out_shapes = m_priv->outShapes(); - run_args.reserve(out_shapes.size()); - outs.reserve(out_shapes.size()); + const auto& out_info = m_priv->outInfo(); + run_args.reserve(out_info.size()); + outs.reserve(out_info.size()); - for (auto&& shape : out_shapes) + for (auto&& info : out_info) { - switch (shape) + switch (info.shape) { case cv::GShape::GMAT: { @@ -141,6 +142,19 @@ std::tuple cv::GStreamingCompiled::pull() outs.emplace_back(&cv::util::get(run_args.back())); break; } + case cv::GShape::GARRAY: + { + switch (info.kind) + { + case cv::detail::OpaqueKind::CV_POINT2F: + run_args.emplace_back(cv::detail::VectorRef{std::vector{}}); + outs.emplace_back(cv::util::get(run_args.back())); + break; + default: + util::throw_error(std::logic_error("Unsupported kind for GArray")); + } + break; + } default: util::throw_error(std::logic_error("Only cv::GMat and cv::GScalar are supported for python output")); } diff --git a/modules/gapi/src/compiler/gstreaming_priv.hpp b/modules/gapi/src/compiler/gstreaming_priv.hpp index 2f195ca..be0869e 100644 --- a/modules/gapi/src/compiler/gstreaming_priv.hpp +++ b/modules/gapi/src/compiler/gstreaming_priv.hpp @@ -27,7 +27,10 @@ class GAPI_EXPORTS GStreamingCompiled::Priv GMetaArgs m_metas; // passed by user GMetaArgs m_outMetas; // inferred by compiler std::unique_ptr m_exec; - GShapes m_out_shapes; + + // NB: Used by python wrapper to clarify input/output types + GTypesInfo m_out_info; + GTypesInfo m_in_info; public: void setup(const GMetaArgs &metaArgs, @@ -48,10 +51,11 @@ public: bool running() const; - // NB: std::tuple pull() creates GRunArgs for outputs, - // so need to know out shapes to create corresponding GRunArg - void setOutShapes(GShapes shapes) { m_out_shapes = std::move(shapes); } - const GShapes& outShapes() const { return m_out_shapes; } + void setOutInfo(const GTypesInfo& info) { m_out_info = std::move(info); } + const GTypesInfo& outInfo() const { return m_out_info; } + + void setInInfo(const GTypesInfo& info) { m_in_info = std::move(info); } + const GTypesInfo& inInfo() const { return m_in_info; } }; } // namespace cv diff --git a/modules/gapi/test/own/gapi_types_tests.cpp b/modules/gapi/test/own/gapi_types_tests.cpp index b40bb1d..602a931 100644 --- a/modules/gapi/test/own/gapi_types_tests.cpp +++ b/modules/gapi/test/own/gapi_types_tests.cpp @@ -27,6 +27,22 @@ TEST(Point, CreateWithParams) EXPECT_EQ(2, p.y); } +TEST(Point2f, CreateEmpty) +{ + cv::gapi::own::Point2f p; + + EXPECT_EQ(0.f, p.x); + EXPECT_EQ(0.f, p.y); +} + +TEST(Point2f, CreateWithParams) +{ + cv::gapi::own::Point2f p = {3.14f, 2.71f}; + + EXPECT_EQ(3.14f, p.x); + EXPECT_EQ(2.71f, p.y); +} + TEST(Rect, CreateEmpty) { cv::gapi::own::Rect r; -- 2.7.4