From 5224d016e9aabf16dc78f0cad18b55f64cfa6117 Mon Sep 17 00:00:00 2001 From: Ruslan Garnov Date: Sun, 4 Oct 2020 21:57:41 +0300 Subject: [PATCH] Merge pull request #18339 from rgarnov:rg/rmat_integration [GAPI] RMat integration into the framework * RMat integration * Added initialization of input mat in GArray initialization tests * Fixed klocwork warnings in RMat tests, changed argument order in EXPECT_EQ --- modules/gapi/CMakeLists.txt | 1 + modules/gapi/include/opencv2/gapi/garg.hpp | 3 + modules/gapi/include/opencv2/gapi/gmat.hpp | 6 + .../include/opencv2/gapi/gtype_traits.hpp | 1 + modules/gapi/include/opencv2/gapi/own/mat.hpp | 12 ++ modules/gapi/include/opencv2/gapi/rmat.hpp | 13 +- modules/gapi/src/api/gbackend.cpp | 137 ++++++-------- modules/gapi/src/api/gmat.cpp | 10 ++ modules/gapi/src/api/gproto.cpp | 7 + modules/gapi/src/api/rmat.cpp | 23 +++ modules/gapi/src/backends/common/gbackend.hpp | 46 ++++- .../src/backends/common/serialization.cpp | 8 + .../src/backends/common/serialization.hpp | 3 + modules/gapi/src/backends/cpu/gcpubackend.cpp | 4 + .../gapi/src/backends/fluid/gfluidbackend.cpp | 44 ++--- .../gapi/src/backends/fluid/gfluidbackend.hpp | 3 +- modules/gapi/src/backends/ie/giebackend.cpp | 4 + modules/gapi/src/backends/ocl/goclbackend.cpp | 36 +++- .../src/backends/plaidml/gplaidmlbackend.cpp | 3 + .../src/backends/render/grenderocvbackend.cpp | 4 + modules/gapi/src/compiler/gislandmodel.cpp | 3 +- modules/gapi/src/compiler/gislandmodel.hpp | 3 +- modules/gapi/src/executor/gexecutor.cpp | 124 +++++++++++-- modules/gapi/src/executor/gexecutor.hpp | 5 +- .../gapi/src/executor/gstreamingexecutor.cpp | 32 +++- modules/gapi/test/gapi_array_tests.cpp | 4 +- .../test/internal/gapi_int_proto_tests.cpp | 1 + .../gapi/test/rmat/rmat_integration_tests.cpp | 170 ++++++++++++++++++ modules/gapi/test/rmat/rmat_test_common.hpp | 61 +++++++ modules/gapi/test/rmat/rmat_tests.cpp | 53 +----- modules/gapi/test/rmat/rmat_view_tests.cpp | 64 ++++--- 31 files changed, 659 insertions(+), 229 deletions(-) create mode 100644 modules/gapi/src/api/rmat.cpp create mode 100644 modules/gapi/test/rmat/rmat_integration_tests.cpp create mode 100644 modules/gapi/test/rmat/rmat_test_common.hpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index fa0b4708bc..0278d9326a 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -78,6 +78,7 @@ set(gapi_srcs src/api/ginfer.cpp src/api/ft_render.cpp src/api/media.cpp + src/api/rmat.cpp # Compiler part src/compiler/gmodel.cpp diff --git a/modules/gapi/include/opencv2/gapi/garg.hpp b/modules/gapi/include/opencv2/gapi/garg.hpp index cca080d8a3..f054e87616 100644 --- a/modules/gapi/include/opencv2/gapi/garg.hpp +++ b/modules/gapi/include/opencv2/gapi/garg.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace cv { @@ -94,6 +95,7 @@ using GRunArg = util::variant< #if !defined(GAPI_STANDALONE) cv::UMat, #endif // !defined(GAPI_STANDALONE) + cv::RMat, cv::gapi::wip::IStreamSource::Ptr, cv::Mat, cv::Scalar, @@ -144,6 +146,7 @@ using GRunArgP = util::variant< cv::UMat*, #endif // !defined(GAPI_STANDALONE) cv::Mat*, + cv::RMat*, cv::Scalar*, cv::detail::VectorRef, cv::detail::OpaqueRef diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp index b38ce48c97..f441413be5 100644 --- a/modules/gapi/include/opencv2/gapi/gmat.hpp +++ b/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -65,6 +65,8 @@ public: using GMat::GMat; }; +class RMat; + /** @} */ /** @@ -113,6 +115,8 @@ struct GAPI_EXPORTS GMatDesc // and as a 3-channel planar mat with height divided by 3) bool canDescribe(const cv::Mat& mat) const; + bool canDescribe(const cv::RMat& mat) const; + // Meta combinator: return a new GMatDesc which differs in size by delta // (all other fields are taken unchanged from this GMatDesc) // FIXME: a better name? @@ -209,6 +213,8 @@ namespace gapi { namespace own { GAPI_EXPORTS GMatDesc descr_of(const Mat &mat); }}//gapi::own +GAPI_EXPORTS GMatDesc descr_of(const RMat &mat); + #if !defined(GAPI_STANDALONE) GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat); #else diff --git a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp index 3235b1a373..35a5567213 100644 --- a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp +++ b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp @@ -121,6 +121,7 @@ namespace detail template<> struct GTypeOf { using type = cv::GMat; }; #endif // !defined(GAPI_STANDALONE) template<> struct GTypeOf { using type = cv::GMat; }; + template<> struct GTypeOf { using type = cv::GMat; }; template<> struct GTypeOf { using type = cv::GScalar; }; template struct GTypeOf > { using type = cv::GArray; }; template struct GTypeOf { using type = cv::GOpaque;}; diff --git a/modules/gapi/include/opencv2/gapi/own/mat.hpp b/modules/gapi/include/opencv2/gapi/own/mat.hpp index a10985866b..ce9c0bf362 100644 --- a/modules/gapi/include/opencv2/gapi/own/mat.hpp +++ b/modules/gapi/include/opencv2/gapi/own/mat.hpp @@ -254,6 +254,18 @@ namespace cv { namespace gapi { namespace own { *this = std::move(tmp); } + /** @brief Creates a full copy of the matrix and the underlying data. + + The method creates a full copy of the matrix. The original step[] is not taken into account. + So, the copy has a continuous buffer occupying total() * elemSize() bytes. + */ + Mat clone() const + { + Mat m; + copyTo(m); + return m; + } + /** @brief Copies the matrix to another one. The method copies the matrix data to another matrix. Before copying the data, the method invokes : diff --git a/modules/gapi/include/opencv2/gapi/rmat.hpp b/modules/gapi/include/opencv2/gapi/rmat.hpp index 2fbdf038f7..626e67e9ee 100644 --- a/modules/gapi/include/opencv2/gapi/rmat.hpp +++ b/modules/gapi/include/opencv2/gapi/rmat.hpp @@ -8,6 +8,7 @@ #define OPENCV_GAPI_RMAT_HPP #include +#include namespace cv { @@ -31,7 +32,7 @@ namespace cv { // performCalculations(in_view, out_view); // // data from out_view is transferred to the device when out_view is destroyed // } -class RMat +class GAPI_EXPORTS RMat { public: // A lightweight wrapper on image data: @@ -39,20 +40,20 @@ public: // - Doesn't implement copy semantics (it's assumed that a view is created each time // wrapped data is being accessed); // - Has an optional callback which is called when the view is destroyed. - class View + class GAPI_EXPORTS View { public: using DestroyCallback = std::function; View() = default; View(const GMatDesc& desc, uchar* data, size_t step = 0u, DestroyCallback&& cb = nullptr) - : m_desc(desc), m_data(data), m_step(step == 0u ? elemSize()*cols() : step), m_cb(cb) + : m_desc(desc), m_data(data), m_step(step == 0u ? elemSize()*cols() : step), m_cb(std::move(cb)) {} View(const View&) = delete; - View(View&&) = default; View& operator=(const View&) = delete; - View& operator=(View&&) = default; + View(View&&) = default; + View& operator=(View&& v); ~View() { if (m_cb) m_cb(); } cv::Size size() const { return m_desc.size; } @@ -88,7 +89,7 @@ public: // Implementation is responsible for setting the appropriate callback to // the view when accessed for writing, to ensure that the data from the view // is transferred to the device when the view is destroyed - virtual View access(Access) const = 0; + virtual View access(Access) = 0; }; using AdapterP = std::shared_ptr; diff --git a/modules/gapi/src/api/gbackend.cpp b/modules/gapi/src/api/gbackend.cpp index cc0b1a912b..97b91bd1ed 100644 --- a/modules/gapi/src/api/gbackend.cpp +++ b/modules/gapi/src/api/gbackend.cpp @@ -2,7 +2,7 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2020 Intel Corporation #include "precomp.hpp" @@ -103,38 +103,34 @@ namespace cv { namespace gimpl { namespace magazine { -// FIXME implement the below functions with visit()? +namespace { +// Utility function, used in both bindInArg and bindOutArg, +// implements default RMat bind behaviour (if backend doesn't handle RMats in specific way): +// view + wrapped cv::Mat are placed into the magazine +void bindRMat(Mag& mag, const RcDesc& rc, const cv::RMat& rmat, RMat::Access a) +{ + auto& matv = mag.template slot()[rc.id]; + matv = rmat.access(a); + mag.template slot()[rc.id] = asMat(matv); +} +} // anonymous namespace -void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat) +// FIXME implement the below functions with visit()? +void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, HandleRMat handleRMat) { switch (rc.shape) { case GShape::GMAT: { - switch (arg.index()) - { - case GRunArg::index_of() : - if (is_umat) - { -#if !defined(GAPI_STANDALONE) - auto& mag_umat = mag.template slot()[rc.id]; - mag_umat = util::get(arg).getUMat(ACCESS_READ); -#else - util::throw_error(std::logic_error("UMat is not supported in standalone build")); -#endif // !defined(GAPI_STANDALONE) - } - else - { - auto& mag_mat = mag.template slot()[rc.id]; - mag_mat = util::get(arg); - } - break; - default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); - } + // In case of handleRMat == SKIP + // We assume that backend can work with some device-specific RMats + // and will handle them in some specific way, so just return + if (handleRMat == HandleRMat::SKIP) return; + GAPI_Assert(arg.index() == GRunArg::index_of()); + bindRMat(mag, rc, util::get(arg), RMat::Access::R); break; } - case GShape::GSCALAR: { auto& mag_scalar = mag.template slot()[rc.id]; @@ -159,32 +155,18 @@ void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat) } } -void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat) +void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, HandleRMat handleRMat) { switch (rc.shape) { case GShape::GMAT: { - switch (arg.index()) - { - case GRunArgP::index_of() : - if (is_umat) - { -#if !defined(GAPI_STANDALONE) - auto& mag_umat = mag.template slot()[rc.id]; - mag_umat = util::get(arg)->getUMat(ACCESS_RW); -#else - util::throw_error(std::logic_error("UMat is not supported in standalone build")); -#endif // !defined(GAPI_STANDALONE) - } - else - { - auto& mag_mat = mag.template slot()[rc.id]; - mag_mat = *util::get(arg); - } - break; - default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); - } + // In case of handleRMat == SKIP + // We assume that backend can work with some device-specific RMats + // and will handle them in some specific way, so just return + if (handleRMat == HandleRMat::SKIP) return; + GAPI_Assert(arg.index() == GRunArgP::index_of()); + bindRMat(mag, rc, *util::get(arg), RMat::Access::W); break; } @@ -248,7 +230,7 @@ cv::GRunArg getArg(const Mag& mag, const RcDesc &ref) // Wrap associated CPU object (either host or an internal one) switch (ref.shape) { - case GShape::GMAT: return GRunArg(mag.template slot().at(ref.id)); + case GShape::GMAT: return GRunArg(mag.template slot().at(ref.id)); case GShape::GSCALAR: return GRunArg(mag.template slot().at(ref.id)); // Note: .at() is intentional for GArray and GOpaque as objects MUST be already there // (and constructed by either bindIn/Out or resetInternal) @@ -300,46 +282,15 @@ cv::GRunArgP getObjPtr(Mag& mag, const RcDesc &rc, bool is_umat) } } -void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat) +void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg) { switch (rc.shape) { case GShape::GARRAY: - // Do nothing - should we really do anything here? - break; - case GShape::GOPAQUE: - // Do nothing - should we really do anything here? - break; - case GShape::GMAT: - { - //simply check that memory was not reallocated, i.e. - //both instances of Mat pointing to the same memory - uchar* out_arg_data = nullptr; - switch (g_arg.index()) - { - case GRunArgP::index_of() : out_arg_data = util::get(g_arg)->data; break; -#if !defined(GAPI_STANDALONE) - case GRunArgP::index_of() : out_arg_data = (util::get(g_arg))->getMat(ACCESS_RW).data; break; -#endif // !defined(GAPI_STANDALONE) - default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); - } - if (is_umat) - { -#if !defined(GAPI_STANDALONE) - auto& in_mag = mag.template slot().at(rc.id); - GAPI_Assert((out_arg_data == (in_mag.getMat(ACCESS_RW).data)) && " data for output parameters was reallocated ?"); -#else - util::throw_error(std::logic_error("UMat is not supported in standalone build")); -#endif // !defined(GAPI_STANDALONE) - } - else - { - auto& in_mag = mag.template slot().at(rc.id); - GAPI_Assert((out_arg_data == in_mag.data) && " data for output parameters was reallocated ?"); - } + case GShape::GOPAQUE: + // Do nothing - should we really do anything here? break; - } case GShape::GSCALAR: { @@ -357,6 +308,32 @@ void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat) } } +void unbind(Mag& mag, const RcDesc &rc) +{ + switch (rc.shape) + { + case GShape::GARRAY: + case GShape::GOPAQUE: + case GShape::GSCALAR: + // TODO: Do nothing - should we really do anything here? + break; + + case GShape::GMAT: + // Clean-up everything - a cv::Mat, cv::RMat::View, a cv::UMat, and cv::RMat + // if applicable + mag.slot().erase(rc.id); +#if !defined(GAPI_STANDALONE) + mag.slot().erase(rc.id); +#endif + mag.slot().erase(rc.id); + mag.slot().erase(rc.id); + break; + + default: + GAPI_Assert(false); + } +} + } // namespace magazine void createMat(const cv::GMatDesc &desc, cv::Mat& mat) diff --git a/modules/gapi/src/api/gmat.cpp b/modules/gapi/src/api/gmat.cpp index de33c39b0d..d9f135222b 100644 --- a/modules/gapi/src/api/gmat.cpp +++ b/modules/gapi/src/api/gmat.cpp @@ -95,6 +95,11 @@ cv::GMetaArgs cv::gapi::own::descrs_of(const std::vector &vec) return vec_descr_of(vec); } +cv::GMatDesc cv::descr_of(const cv::RMat &mat) +{ + return mat.desc(); +} + namespace cv { std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc) { @@ -137,4 +142,9 @@ bool GMatDesc::canDescribe(const cv::Mat& mat) const return canDescribeHelper(*this, mat); } +bool GMatDesc::canDescribe(const cv::RMat& mat) const +{ + return *this == mat.desc(); +} + }// namespace cv diff --git a/modules/gapi/src/api/gproto.cpp b/modules/gapi/src/api/gproto.cpp index ef5162a58c..47cc47bbe0 100644 --- a/modules/gapi/src/api/gproto.cpp +++ b/modules/gapi/src/api/gproto.cpp @@ -119,6 +119,9 @@ cv::GMetaArg cv::descr_of(const cv::GRunArg &arg) case GRunArg::index_of(): return cv::util::get(arg)->descr_of(); + case GRunArg::index_of(): + return cv::GMetaArg(cv::util::get(arg).desc()); + default: util::throw_error(std::logic_error("Unsupported GRunArg type")); } } @@ -174,6 +177,8 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg) case GRunArg::index_of(): return meta == cv::GMetaArg(util::get(arg).descr_of()); case GRunArg::index_of(): return meta == cv::GMetaArg(util::get(arg).descr_of()); case GRunArg::index_of(): return util::holds_alternative(meta); // FIXME(?) may be not the best option + case GRunArg::index_of(): return util::holds_alternative(meta) && + util::get(meta).canDescribe(cv::util::get(arg)); default: util::throw_error(std::logic_error("Unsupported GRunArg type")); } } @@ -263,6 +268,8 @@ const void* cv::gimpl::proto::ptr(const GRunArgP &arg) return static_cast(cv::util::get(arg)); case GRunArgP::index_of(): return static_cast(cv::util::get(arg)); + case GRunArgP::index_of(): + return static_cast(cv::util::get(arg)); case GRunArgP::index_of(): return cv::util::get(arg).ptr(); case GRunArgP::index_of(): diff --git a/modules/gapi/src/api/rmat.cpp b/modules/gapi/src/api/rmat.cpp new file mode 100644 index 0000000000..9c2da2ebc7 --- /dev/null +++ b/modules/gapi/src/api/rmat.cpp @@ -0,0 +1,23 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#include + +using View = cv::RMat::View; + +// There is an issue with default generated operator=(View&&) on Mac: +// it doesn't nullify m_cb of a moved object +View& View::operator=(View&& v) { + m_desc = v.m_desc; + m_data = v.m_data; + m_step = v.m_step; + m_cb = v.m_cb; + v.m_desc = {}; + v.m_data = nullptr; + v.m_step = 0u; + v.m_cb = nullptr; + return *this; +} diff --git a/modules/gapi/src/backends/common/gbackend.hpp b/modules/gapi/src/backends/common/gbackend.hpp index e9a44c44f8..650ff594ff 100644 --- a/modules/gapi/src/backends/common/gbackend.hpp +++ b/modules/gapi/src/backends/common/gbackend.hpp @@ -22,6 +22,23 @@ namespace cv { namespace gimpl { + inline cv::Mat asMat(RMat::View& v) { + return v.dims().empty() ? cv::Mat(v.rows(), v.cols(), v.type(), v.ptr(), v.step()) + : cv::Mat(v.dims(), v.type(), v.ptr()); + } + inline RMat::View asView(const Mat& m, RMat::View::DestroyCallback&& cb = nullptr) { + return RMat::View(cv::descr_of(m), m.data, m.step, std::move(cb)); + } + + class RMatAdapter : public RMat::Adapter { + cv::Mat m_mat; + public: + const void* data() const { return m_mat.data; } + RMatAdapter(cv::Mat m) : m_mat(m) {} + virtual RMat::View access(RMat::Access) override { return asView(m_mat); } + virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); } + }; + // Forward declarations struct Data; struct RcDesc; @@ -44,20 +61,39 @@ namespace magazine { } // namespace magazine #if !defined(GAPI_STANDALONE) -using Mag = magazine::Class; +using Mag = magazine::Class; #else -using Mag = magazine::Class; +using Mag = magazine::Class; #endif namespace magazine { - void GAPI_EXPORTS bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat = false); - void GAPI_EXPORTS bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat = false); + enum class HandleRMat { BIND, SKIP }; + // Extracts a memory object from GRunArg, stores it in appropriate slot in a magazine + // Note: + // Only RMats are expected here as a memory object for GMat shape. + // If handleRMat is BIND, RMat will be accessed, and RMat::View and wrapping cv::Mat + // will be placed into the magazine. + // If handleRMat is SKIP, this function skips'RMat handling assuming that backend will do it on its own. + // FIXME? + // handleRMat parameter might be redundant if all device specific backends implement own bind routines + // without utilizing magazine at all + void GAPI_EXPORTS bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg, HandleRMat handleRMat = HandleRMat::BIND); + + // Extracts a memory object reference fro GRunArgP, stores it in appropriate slot in a magazine + // Note on RMat handling from bindInArg above is also applied here + void GAPI_EXPORTS bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, HandleRMat handleRMat = HandleRMat::BIND); void resetInternalData(Mag& mag, const Data &d); cv::GRunArg getArg (const Mag& mag, const RcDesc &ref); cv::GRunArgP getObjPtr ( Mag& mag, const RcDesc &rc, bool is_umat = false); - void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat = false); + void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg); + + // A mandatory clean-up procedure to force proper lifetime of wrappers (cv::Mat, cv::RMat::View) + // over not-owned data + // FIXME? Add an RAII wrapper for that? + // Or put objects which need to be cleaned-up into a separate stack allocated magazine? + void unbind(Mag &mag, const RcDesc &rc); } // namespace magazine namespace detail diff --git a/modules/gapi/src/backends/common/serialization.cpp b/modules/gapi/src/backends/common/serialization.cpp index ee8dcef1cd..0ccc78a43f 100644 --- a/modules/gapi/src/backends/common/serialization.cpp +++ b/modules/gapi/src/backends/common/serialization.cpp @@ -165,6 +165,14 @@ IOStream& operator<< (IOStream& os, const cv::Scalar &s) { IIStream& operator>> (IIStream& is, cv::Scalar& s) { return is >> s.val[0] >> s.val[1] >> s.val[2] >> s.val[3]; } +IOStream& operator<< (IOStream& os, const cv::RMat&) { + util::throw_error(std::logic_error("Serialization of RMat is not supported")); + return os; +} +IIStream& operator>> (IIStream& is, cv::RMat&) { + util::throw_error(std::logic_error("Serialization of RMat is not supported")); + return is; +} namespace { diff --git a/modules/gapi/src/backends/common/serialization.hpp b/modules/gapi/src/backends/common/serialization.hpp index ca8b372356..74afee5d71 100644 --- a/modules/gapi/src/backends/common/serialization.hpp +++ b/modules/gapi/src/backends/common/serialization.hpp @@ -88,6 +88,9 @@ GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat &); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat &); #endif // !defined(GAPI_STANDALONE) +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::RMat &r); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::RMat &r); + GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &); diff --git a/modules/gapi/src/backends/cpu/gcpubackend.cpp b/modules/gapi/src/backends/cpu/gcpubackend.cpp index 780c0e9241..032875eb35 100644 --- a/modules/gapi/src/backends/cpu/gcpubackend.cpp +++ b/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -276,4 +276,8 @@ void cv::gimpl::GCPUExecutable::run(std::vector &&input_objs, } // for(m_script) for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second); + + // In/Out args clean-up is mandatory now with RMat + for (auto &it : input_objs) magazine::unbind(m_res, it.first); + for (auto &it : output_objs) magazine::unbind(m_res, it.first); } diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/modules/gapi/src/backends/fluid/gfluidbackend.cpp index e103c70f78..9b95dff036 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -1243,41 +1243,25 @@ void cv::gimpl::GFluidExecutable::reshape(ade::Graph &g, const GCompileArgs &arg // FIXME: Document what it does void cv::gimpl::GFluidExecutable::bindInArg(const cv::gimpl::RcDesc &rc, const GRunArg &arg) { - switch (rc.shape) - { - case GShape::GMAT: m_buffers[m_id_map.at(rc.id)].priv().bindTo(util::get(arg), true); break; - case GShape::GSCALAR: m_res.slot()[rc.id] = util::get(arg); break; - case GShape::GARRAY: m_res.slot()[rc.id] = util::get(arg); break; - case GShape::GOPAQUE: m_res.slot()[rc.id] = util::get(arg); break; - default: util::throw_error(std::logic_error("Unsupported input GShape type")); + magazine::bindInArg(m_res, rc, arg); + if (rc.shape == GShape::GMAT) { + auto& mat = m_res.slot()[rc.id]; + // fluid::Buffer::bindTo() is not connected to magazine::bindIn/OutArg and unbind() calls, + // it's simply called each run() without any requirement to call some fluid-specific + // unbind() at the end of run() + m_buffers[m_id_map.at(rc.id)].priv().bindTo(mat, true); } } void cv::gimpl::GFluidExecutable::bindOutArg(const cv::gimpl::RcDesc &rc, const GRunArgP &arg) { // Only GMat is supported as return type - using T = GRunArgP; - switch (rc.shape) - { - case GShape::GMAT: - { - cv::GMatDesc desc = m_buffers[m_id_map.at(rc.id)].meta(); - auto &bref = m_buffers[m_id_map.at(rc.id)].priv(); - - switch (arg.index()) { - // FIXME: See the bindInArg comment on Streaming-related changes - case T::index_of(): { - auto &outMat = *util::get(arg); - GAPI_Assert(outMat.data != nullptr); - GAPI_Assert(cv::descr_of(outMat) == desc && "Output argument was not preallocated as it should be ?"); - bref.bindTo(outMat, false); - } break; - default: GAPI_Assert(false); - } // switch(arg.index()) - break; - } - default: util::throw_error(std::logic_error("Unsupported return GShape type")); + if (rc.shape != GShape::GMAT) { + util::throw_error(std::logic_error("Unsupported return GShape type")); } + magazine::bindOutArg(m_res, rc, arg); + auto& mat = m_res.slot()[rc.id]; + m_buffers[m_id_map.at(rc.id)].priv().bindTo(mat, false); } void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_arg) @@ -1383,6 +1367,10 @@ void cv::gimpl::GFluidExecutable::run(std::vector &input_objs, agent->doWork(); } } + + // In/Out args clean-up is mandatory now with RMat + for (auto &it : input_objs) magazine::unbind(m_res, it.first); + for (auto &it : output_objs) magazine::unbind(m_res, it.first); } cv::gimpl::GParallelFluidExecutable::GParallelFluidExecutable(const ade::Graph &g, diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/modules/gapi/src/backends/fluid/gfluidbackend.hpp index 282b0668b2..43174cc1d3 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.hpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.hpp @@ -128,8 +128,7 @@ class GFluidExecutable final: public GIslandExecutable std::vector m_script; - using Magazine = detail::magazine; - Magazine m_res; + cv::gimpl::Mag m_res; std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch) std::vector m_scratch_users; diff --git a/modules/gapi/src/backends/ie/giebackend.cpp b/modules/gapi/src/backends/ie/giebackend.cpp index df4ffb1fe0..1565d03aec 100644 --- a/modules/gapi/src/backends/ie/giebackend.cpp +++ b/modules/gapi/src/backends/ie/giebackend.cpp @@ -397,6 +397,10 @@ void cv::gimpl::ie::GIEExecutable::run(std::vector &&input_objs, kk.run(this_iec, uu, context); for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second); + + // In/Out args clean-up is mandatory now with RMat + for (auto &it : input_objs) magazine::unbind(m_res, it.first); + for (auto &it : output_objs) magazine::unbind(m_res, it.first); } namespace cv { diff --git a/modules/gapi/src/backends/ocl/goclbackend.cpp b/modules/gapi/src/backends/ocl/goclbackend.cpp index 8705deb7c2..34dba01afe 100644 --- a/modules/gapi/src/backends/ocl/goclbackend.cpp +++ b/modules/gapi/src/backends/ocl/goclbackend.cpp @@ -165,12 +165,31 @@ void cv::gimpl::GOCLExecutable::run(std::vector &&input_objs, // NB: avoid clearing the whole magazine, there's also pre-allocated internal data for (auto& it : input_objs) umats.erase(it.first.id); for (auto& it : output_objs) umats.erase(it.first.id); + + // In/Out args clean-up is mandatory now with RMat + for (auto &it : input_objs) magazine::unbind(*p, it.first); + for (auto &it : output_objs) magazine::unbind(*p, it.first); }; // RAII wrapper to clean-up m_res std::unique_ptr cleaner(&m_res, clean_up); - for (auto& it : input_objs) magazine::bindInArg (m_res, it.first, it.second, true); - for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second, true); + const auto bindUMat = [this](const RcDesc& rc) { + auto& mag_umat = m_res.template slot()[rc.id]; + mag_umat = m_res.template slot()[rc.id].getUMat(ACCESS_READ); + }; + + for (auto& it : input_objs) { + const auto& rc = it.first; + magazine::bindInArg (m_res, rc, it.second); + // There is already cv::Mat in the magazine after bindInArg call, + // extract UMat from it, put into the magazine + if (rc.shape == GShape::GMAT) bindUMat(rc); + } + for (auto& it : output_objs) { + const auto& rc = it.first; + magazine::bindOutArg(m_res, rc, it.second); + if (rc.shape == GShape::GMAT) bindUMat(rc); + } // Initialize (reset) internal data nodes with user structures // before processing a frame (no need to do it for external data structures) @@ -241,5 +260,16 @@ void cv::gimpl::GOCLExecutable::run(std::vector &&input_objs, } } // for(m_script) - for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second, true); + for (auto &it : output_objs) + { + auto& rc = it.first; + auto& g_arg = it.second; + magazine::writeBack(m_res, rc, g_arg); + if (rc.shape == GShape::GMAT) + { + uchar* out_arg_data = m_res.template slot()[rc.id].data; + auto& mag_mat = m_res.template slot().at(rc.id); + GAPI_Assert((out_arg_data == (mag_mat.getMat(ACCESS_RW).data)) && " data for output parameters was reallocated ?"); + } + } } diff --git a/modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp b/modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp index d2c78eb3d5..ebce62918c 100644 --- a/modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp +++ b/modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp @@ -198,6 +198,9 @@ void cv::gimpl::GPlaidMLExecutable::run(std::vector &&input_objs, exec_->run(); for (auto& it : output_objs) bindOutArg(it.first, it.second); + + // FIXME: + // PlaidML backend haven't been updated with RMat support } void cv::gimpl::GPlaidMLExecutable::bindInArg(const RcDesc &rc, const GRunArg &arg) diff --git a/modules/gapi/src/backends/render/grenderocvbackend.cpp b/modules/gapi/src/backends/render/grenderocvbackend.cpp index e597c49f37..413d0c3f9c 100644 --- a/modules/gapi/src/backends/render/grenderocvbackend.cpp +++ b/modules/gapi/src/backends/render/grenderocvbackend.cpp @@ -96,6 +96,10 @@ void cv::gimpl::render::ocv::GRenderExecutable::run(std::vector &&input_ k.m_runF(context); for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second); + + // In/Out args clean-up is mandatory now with RMat + for (auto &it : input_objs) magazine::unbind(m_res, it.first); + for (auto &it : output_objs) magazine::unbind(m_res, it.first); } cv::GArg cv::gimpl::render::ocv::GRenderExecutable::packArg(const cv::GArg &arg) { diff --git a/modules/gapi/src/compiler/gislandmodel.cpp b/modules/gapi/src/compiler/gislandmodel.cpp index a135123f82..aee0477e08 100644 --- a/modules/gapi/src/compiler/gislandmodel.cpp +++ b/modules/gapi/src/compiler/gislandmodel.cpp @@ -18,6 +18,7 @@ #include "compiler/gmodel.hpp" #include "compiler/gislandmodel.hpp" #include "compiler/gmodel.hpp" +#include "backends/common/gbackend.hpp" // RMatAdapter #include "logger.hpp" // GAPI_LOG @@ -348,7 +349,7 @@ void GIslandExecutable::run(GIslandExecutable::IInput &in, GIslandExecutable::IO switch (in_data_orig.index()) { case cv::GRunArg::index_of(): - in_data = cv::GRunArg{cv::util::get(in_data_orig)}; + in_data = cv::GRunArg{cv::make_rmat(cv::util::get(in_data_orig))}; break; case cv::GRunArg::index_of(): in_data = cv::GRunArg{(cv::util::get(in_data_orig))}; diff --git a/modules/gapi/src/compiler/gislandmodel.hpp b/modules/gapi/src/compiler/gislandmodel.hpp index 390bdb55f9..6cf8f98667 100644 --- a/modules/gapi/src/compiler/gislandmodel.hpp +++ b/modules/gapi/src/compiler/gislandmodel.hpp @@ -22,7 +22,6 @@ namespace cv { namespace gimpl { - // FIXME: GAPI_EXPORTS only because of tests! class GAPI_EXPORTS GIsland { @@ -122,6 +121,8 @@ public: virtual bool canReshape() const = 0; virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0; + virtual bool allocatesOutputs() const { return false; } + virtual cv::RMat allocate(const cv::GMatDesc&) const { GAPI_Assert(false && "should never be called"); } // This method is called when the GStreamingCompiled gets a new // input source to process. Normally this method is called once diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp index eb5ac27d21..d9f5cfafe6 100644 --- a/modules/gapi/src/executor/gexecutor.cpp +++ b/modules/gapi/src/executor/gexecutor.cpp @@ -72,7 +72,7 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr &&g_model) const auto orig_data_nh = m_gim.metadata(nh).get().original_data_node; // (1) - initResource(orig_data_nh); + initResource(nh, orig_data_nh); m_slots.emplace_back(DataDesc{nh, orig_data_nh}); } break; @@ -84,7 +84,82 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr &&g_model) } // for(gim nodes) } -void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh) +namespace cv { +namespace gimpl { +namespace magazine { +namespace { + +void bindInArgExec(Mag& mag, const RcDesc &rc, const GRunArg &arg) +{ + if (rc.shape != GShape::GMAT) + { + bindInArg(mag, rc, arg); + return; + } + auto& mag_rmat = mag.template slot()[rc.id]; + switch (arg.index()) + { + case GRunArg::index_of() : + mag_rmat = make_rmat(util::get(arg)); break; + case GRunArg::index_of() : + mag_rmat = util::get(arg); break; + default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); + } +} + +void bindOutArgExec(Mag& mag, const RcDesc &rc, const GRunArgP &arg) +{ + if (rc.shape != GShape::GMAT) + { + bindOutArg(mag, rc, arg); + return; + } + auto& mag_rmat = mag.template slot()[rc.id]; + switch (arg.index()) + { + case GRunArgP::index_of() : + mag_rmat = make_rmat(*util::get(arg)); break; + case GRunArgP::index_of() : + mag_rmat = *util::get(arg); break; + default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); + } +} + +cv::GRunArgP getObjPtrExec(Mag& mag, const RcDesc &rc) +{ + if (rc.shape != GShape::GMAT) + { + return getObjPtr(mag, rc); + } + return GRunArgP(&mag.template slot()[rc.id]); +} + +void writeBackExec(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg) +{ + if (rc.shape != GShape::GMAT) + { + writeBack(mag, rc, g_arg); + return; + } + auto checkOutArgData = [&](const uchar* out_arg_data) { + //simply check that memory was not reallocated, i.e. + //both Mat and View pointing to the same memory + auto mag_data = mag.template slot().at(rc.id).get()->data(); + GAPI_Assert((out_arg_data == mag_data) && " data for output parameters was reallocated ?"); + }; + + switch (g_arg.index()) + { + case GRunArgP::index_of() : checkOutArgData(util::get(g_arg)->data); break; + case GRunArgP::index_of() : /* do nothing */ break; + default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); + } +} +} // anonymous namespace +}}} // namespace cv::gimpl::magazine + + +void cv::gimpl::GExecutor::initResource(const ade::NodeHandle & nh, const ade::NodeHandle &orig_nh) { const Data &d = m_gm.metadata(orig_nh).get(); @@ -99,9 +174,19 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh) { case GShape::GMAT: { + // Let island allocate it's outputs if it can, + // allocate cv::Mat and wrap it with RMat otherwise + GAPI_Assert(!nh->inNodes().empty()); const auto desc = util::get(d.meta); - auto& mat = m_res.slot()[d.rc]; - createMat(desc, mat); + auto& exec = m_gim.metadata(nh->inNodes().front()).get().object; + auto& rmat = m_res.slot()[d.rc]; + if (exec->allocatesOutputs()) { + rmat = exec->allocate(desc); + } else { + Mat mat; + createMat(desc, mat); + rmat = make_rmat(mat); + } } break; @@ -146,7 +231,7 @@ public: class cv::gimpl::GExecutor::Output final: public cv::gimpl::GIslandExecutable::IOutput { cv::gimpl::Mag &mag; - virtual GRunArgP get(int idx) override { return magazine::getObjPtr(mag, desc()[idx]); } + virtual GRunArgP get(int idx) override { return magazine::getObjPtrExec(mag, desc()[idx]); } virtual void post(GRunArgP&&) override { } // Do nothing here virtual void post(EndOfStream&&) override {} // Do nothing here too public: @@ -186,11 +271,10 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) using cv::util::get; const auto desc = get(d.meta); - auto check_own_mat = [&desc, &args, &index]() + auto check_rmat = [&desc, &args, &index]() { - auto& out_mat = *get(args.outObjs.at(index)); - GAPI_Assert(out_mat.data != nullptr && - desc.canDescribe(out_mat)); + auto& out_mat = *get(args.outObjs.at(index)); + GAPI_Assert(desc.canDescribe(out_mat)); }; #if !defined(GAPI_STANDALONE) @@ -202,15 +286,25 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) auto& out_mat = *get(args.outObjs.at(index)); createMat(desc, out_mat); } - // In the case of own::Mat never reallocated, checked to perfectly fit required meta + // In the case of RMat check to fit required meta else { - check_own_mat(); + check_rmat(); } #else // Building standalone - output buffer should always exist, // and _exact_ match our inferred metadata - check_own_mat(); + if (cv::util::holds_alternative(args.outObjs.at(index))) + { + auto& out_mat = *get(args.outObjs.at(index)); + GAPI_Assert(out_mat.data != nullptr && + desc.canDescribe(out_mat)); + } + // In the case of RMat check to fit required meta + else + { + check_rmat(); + } #endif // !defined(GAPI_STANDALONE) } } @@ -218,12 +312,12 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) for (auto it : ade::util::zip(ade::util::toRange(proto.inputs), ade::util::toRange(args.inObjs))) { - magazine::bindInArg(m_res, std::get<0>(it), std::get<1>(it)); + magazine::bindInArgExec(m_res, std::get<0>(it), std::get<1>(it)); } for (auto it : ade::util::zip(ade::util::toRange(proto.outputs), ade::util::toRange(args.outObjs))) { - magazine::bindOutArg(m_res, std::get<0>(it), std::get<1>(it)); + magazine::bindOutArgExec(m_res, std::get<0>(it), std::get<1>(it)); } // Reset internal data @@ -246,7 +340,7 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) for (auto it : ade::util::zip(ade::util::toRange(proto.outputs), ade::util::toRange(args.outObjs))) { - magazine::writeBack(m_res, std::get<0>(it), std::get<1>(it)); + magazine::writeBackExec(m_res, std::get<0>(it), std::get<1>(it)); } } diff --git a/modules/gapi/src/executor/gexecutor.hpp b/modules/gapi/src/executor/gexecutor.hpp index d4fe96e5b1..5d797ce604 100644 --- a/modules/gapi/src/executor/gexecutor.hpp +++ b/modules/gapi/src/executor/gexecutor.hpp @@ -54,6 +54,7 @@ namespace gimpl { class GExecutor { protected: + Mag m_res; std::unique_ptr m_orig_graph; std::shared_ptr m_island_graph; @@ -80,9 +81,7 @@ protected: class Input; class Output; - Mag m_res; - - void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc? + void initResource(const ade::NodeHandle &nh, const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc? public: explicit GExecutor(std::unique_ptr &&g_model); diff --git a/modules/gapi/src/executor/gstreamingexecutor.cpp b/modules/gapi/src/executor/gstreamingexecutor.cpp index 1f9af29cc9..afdebee020 100644 --- a/modules/gapi/src/executor/gstreamingexecutor.cpp +++ b/modules/gapi/src/executor/gstreamingexecutor.cpp @@ -109,7 +109,13 @@ void sync_data(cv::GRunArgs &results, cv::GRunArgsP &outputs) switch (out_obj.index()) { case T::index_of(): - *cv::util::get(out_obj) = std::move(cv::util::get(res_obj)); + { + auto out_mat_p = cv::util::get(out_obj); + auto view = cv::util::get(res_obj).access(cv::RMat::Access::R); + *out_mat_p = cv::gimpl::asMat(view).clone(); + } break; + case T::index_of(): + *cv::util::get(out_obj) = std::move(cv::util::get(res_obj)); break; case T::index_of(): *cv::util::get(out_obj) = std::move(cv::util::get(res_obj)); @@ -408,6 +414,7 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput // These objects are owned externally const cv::GMetaArgs &m_metas; std::vector< std::vector > &m_out_queues; + std::shared_ptr m_island; // Allocate a new data object for output under idx // Prepare this object for posting @@ -430,10 +437,19 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput // FIXME: This is absolutely ugly but seem to work perfectly for its purpose. case cv::GShape::GMAT: { - MatType newMat; - cv::gimpl::createMat(cv::util::get(m_metas[idx]), newMat); - out_arg = cv::GRunArg(std::move(newMat)); - ret_val = cv::GRunArgP(&cv::util::get(out_arg)); + auto desc = cv::util::get(m_metas[idx]); + if (m_island->allocatesOutputs()) + { + out_arg = cv::GRunArg(m_island->allocate(desc)); + } + else + { + MatType newMat; + cv::gimpl::createMat(desc, newMat); + auto rmat = cv::make_rmat(newMat); + out_arg = cv::GRunArg(std::move(rmat)); + } + ret_val = cv::GRunArgP(&cv::util::get(out_arg)); } break; case cv::GShape::GSCALAR: @@ -538,9 +554,11 @@ class StreamingOutput final: public cv::gimpl::GIslandExecutable::IOutput public: explicit StreamingOutput(const cv::GMetaArgs &metas, std::vector< std::vector > &out_queues, - const std::vector &out_descs) + const std::vector &out_descs, + std::shared_ptr island) : m_metas(metas) , m_out_queues(out_queues) + , m_island(island) { set(out_descs); m_postings.resize(out_descs.size()); @@ -573,7 +591,7 @@ void islandActorThread(std::vector in_rcs, // GAPI_Assert(out_queues.size() == out_metas.size()); QueueReader qr; StreamingInput input(qr, in_queues, in_constants, in_rcs); - StreamingOutput output(out_metas, out_queues, out_rcs); + StreamingOutput output(out_metas, out_queues, out_rcs, island); while (!output.done()) { island->run(input, output); diff --git a/modules/gapi/test/gapi_array_tests.cpp b/modules/gapi/test/gapi_array_tests.cpp index de8b4f27d2..b4c8378799 100644 --- a/modules/gapi/test/gapi_array_tests.cpp +++ b/modules/gapi/test/gapi_array_tests.cpp @@ -182,7 +182,7 @@ TEST(GArray, GArrayConstValInitialization) std::vector initial_vec {Point(0,0), Point(1,1), Point(2,2)}; std::vector ref_vec {Point(1,1), Point(2,2), Point(3,3)}; std::vector out_vec; - cv::Mat in_mat; + cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1); cv::GComputationT c([&](cv::GMat in) { @@ -201,7 +201,7 @@ TEST(GArray, GArrayRValInitialization) { std::vector ref_vec {Point(1,1), Point(2,2), Point(3,3)}; std::vector out_vec; - cv::Mat in_mat; + cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1); cv::GComputationT c([&](cv::GMat in) { diff --git a/modules/gapi/test/internal/gapi_int_proto_tests.cpp b/modules/gapi/test/internal/gapi_int_proto_tests.cpp index 1fe9fbb44a..3dd7221198 100644 --- a/modules/gapi/test/internal/gapi_int_proto_tests.cpp +++ b/modules/gapi/test/internal/gapi_int_proto_tests.cpp @@ -16,6 +16,7 @@ struct ProtoPtrTest : public ::testing::Test { using Type = T; }; using ProtoPtrTestTypes = ::testing::Types< cv::Mat , cv::UMat , cv::gapi::own::Mat + , cv::RMat , cv::Scalar , std::vector , int diff --git a/modules/gapi/test/rmat/rmat_integration_tests.cpp b/modules/gapi/test/rmat/rmat_integration_tests.cpp new file mode 100644 index 0000000000..58168453ea --- /dev/null +++ b/modules/gapi/test/rmat/rmat_integration_tests.cpp @@ -0,0 +1,170 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#include "../test_precomp.hpp" +#include +#include "rmat_test_common.hpp" + +#include +#include + +namespace opencv_test +{ + +// This test set takes RMat type as a template parameter and launces simple +// blur(isl1) -> blur(isl2) computation passing RMat as input, as output +// and both input and output +template +struct RMatIntTestBase { + cv::Mat in_mat; + cv::Mat out_mat; + cv::Mat out_mat_ref; + cv::GComputation comp; + bool inCallbackCalled; + bool outCallbackCalled; + + static constexpr int w = 8; + static constexpr int h = 8; + + RMatIntTestBase() + : in_mat(h, w, CV_8UC1) + , out_mat(h, w, CV_8UC1) + , out_mat_ref(h, w, CV_8UC1) + , comp([](){ + cv::GMat in; + auto tmp = cv::gapi::blur(in, {3,3}); + auto out = cv::gapi::blur(tmp, {3,3}); + cv::gapi::island("test", cv::GIn(in), cv::GOut(tmp)); + return cv::GComputation(in, out); + }) + , inCallbackCalled(false) + , outCallbackCalled(false) { + cv::randu(in_mat, cv::Scalar::all(127), cv::Scalar::all(40)); + } + + void check() { + comp.apply(in_mat, out_mat_ref); + EXPECT_EQ(0, cvtest::norm(out_mat_ref, out_mat, NORM_INF)); + } + + RMat createRMat(cv::Mat& mat, bool& callbackCalled) { + return {cv::make_rmat(mat, callbackCalled)}; + } +}; + +template +struct RMatIntTest : public RMatIntTestBase +{ + template + void run(const In& in, Out& out, cv::GCompileArgs&& compile_args) { + for (int i = 0; i < 2; i++) { + EXPECT_FALSE(this->inCallbackCalled); + EXPECT_FALSE(this->outCallbackCalled); + auto compile_args_copy = compile_args; + this->comp.apply(cv::gin(in), cv::gout(out), std::move(compile_args_copy)); + EXPECT_FALSE(this->inCallbackCalled); + if (std::is_same::value) { + EXPECT_TRUE(this->outCallbackCalled); + } else { + EXPECT_FALSE(this->outCallbackCalled); + } + this->outCallbackCalled = false; + } + this->check(); + } +}; + +template +struct RMatIntTestStreaming : public RMatIntTestBase +{ + template + cv::GMatDesc getDesc(const M& m) { return cv::descr_of(m); } + + void checkOutput(const cv::Mat&) { this->check(); } + + void checkOutput(const RMat& rm) { + auto view = rm.access(RMat::Access::R); + this->out_mat = cv::Mat(view.size(), view.type(), view.ptr()); + this->check(); + } + + template + void run(const In& in, Out& out, cv::GCompileArgs&& compile_args) { + auto sc = this->comp.compileStreaming(getDesc(in), std::move(compile_args)); + + sc.setSource(cv::gin(in)); + sc.start(); + + std::size_t frame = 0u; + constexpr std::size_t num_frames = 10u; + EXPECT_FALSE(this->inCallbackCalled); + EXPECT_FALSE(this->outCallbackCalled); + while (sc.pull(cv::gout(out)) && frame < num_frames) { + frame++; + this->checkOutput(out); + EXPECT_FALSE(this->inCallbackCalled); + EXPECT_FALSE(this->outCallbackCalled); + } + EXPECT_EQ(num_frames, frame); + } +}; + +struct OcvKernels { + cv::gapi::GKernelPackage kernels() { return cv::gapi::imgproc::cpu::kernels(); } +}; +struct FluidKernels { + cv::gapi::GKernelPackage kernels() { return cv::gapi::imgproc::fluid::kernels(); } +}; + +struct RMatIntTestCpuRef : public + RMatIntTest, OcvKernels {}; +struct RMatIntTestCpuCopy : public + RMatIntTest, OcvKernels {}; +struct RMatIntTestCpuRefStreaming : public + RMatIntTestStreaming, OcvKernels {}; +struct RMatIntTestCpuCopyStreaming : public + RMatIntTestStreaming, OcvKernels {}; +struct RMatIntTestCpuRefFluid : public + RMatIntTest, FluidKernels {}; +struct RMatIntTestCpuCopyFluid : public + RMatIntTest, FluidKernels {}; +struct RMatIntTestCpuRefStreamingFluid : public + RMatIntTestStreaming, FluidKernels {}; +struct RMatIntTestCpuCopyStreamingFluid : public + RMatIntTestStreaming, FluidKernels {}; + +template +struct RMatIntTypedTest : public ::testing::Test, public T {}; + +using RMatIntTestTypes = ::testing::Types< RMatIntTestCpuRef + , RMatIntTestCpuCopy + , RMatIntTestCpuRefStreaming + , RMatIntTestCpuCopyStreaming + , RMatIntTestCpuRefFluid + , RMatIntTestCpuCopyFluid + , RMatIntTestCpuRefStreamingFluid + , RMatIntTestCpuCopyStreamingFluid + >; + +TYPED_TEST_CASE(RMatIntTypedTest, RMatIntTestTypes); + +TYPED_TEST(RMatIntTypedTest, In) { + auto in_rmat = this->createRMat(this->in_mat, this->inCallbackCalled); + this->run(in_rmat, this->out_mat, cv::compile_args(this->kernels())); +} + +TYPED_TEST(RMatIntTypedTest, Out) { + auto out_rmat = this->createRMat(this->out_mat, this->outCallbackCalled); + this->run(this->in_mat, out_rmat, cv::compile_args(this->kernels())); +} + +TYPED_TEST(RMatIntTypedTest, InOut) { + auto in_rmat = this->createRMat(this->in_mat, this->inCallbackCalled); + auto out_rmat = this->createRMat(this->out_mat, this->outCallbackCalled); + this->run(in_rmat, out_rmat, cv::compile_args(this->kernels())); +} + +} // namespace opencv_test diff --git a/modules/gapi/test/rmat/rmat_test_common.hpp b/modules/gapi/test/rmat/rmat_test_common.hpp new file mode 100644 index 0000000000..47a744499e --- /dev/null +++ b/modules/gapi/test/rmat/rmat_test_common.hpp @@ -0,0 +1,61 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2020 Intel Corporation + +#ifndef OPENCV_GAPI_RMAT_TESTS_COMMON_HPP +#define OPENCV_GAPI_RMAT_TESTS_COMMON_HPP + +#include "../test_precomp.hpp" +#include + +namespace opencv_test { +class RMatAdapterRef : public RMat::Adapter { + cv::Mat& m_mat; + bool& m_callbackCalled; +public: + RMatAdapterRef(cv::Mat& m, bool& callbackCalled) + : m_mat(m), m_callbackCalled(callbackCalled) + {} + virtual RMat::View access(RMat::Access access) override { + if (access == RMat::Access::W) { + return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step, + [this](){ + EXPECT_FALSE(m_callbackCalled); + m_callbackCalled = true; + }); + } else { + return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step); + } + } + virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); } +}; + +class RMatAdapterCopy : public RMat::Adapter { + cv::Mat& m_deviceMat; + cv::Mat m_hostMat; + bool& m_callbackCalled; + +public: + RMatAdapterCopy(cv::Mat& m, bool& callbackCalled) + : m_deviceMat(m), m_hostMat(m.clone()), m_callbackCalled(callbackCalled) + {} + virtual RMat::View access(RMat::Access access) override { + if (access == RMat::Access::W) { + return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step, + [this](){ + EXPECT_FALSE(m_callbackCalled); + m_callbackCalled = true; + m_hostMat.copyTo(m_deviceMat); + }); + } else { + m_deviceMat.copyTo(m_hostMat); + return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step); + } + } + virtual cv::GMatDesc desc() const override { return cv::descr_of(m_hostMat); } +}; +} // namespace opencv_test + +#endif // OPENCV_GAPI_RMAT_TESTS_COMMON_HPP diff --git a/modules/gapi/test/rmat/rmat_tests.cpp b/modules/gapi/test/rmat/rmat_tests.cpp index 8b93fbe8d1..9980925a3b 100644 --- a/modules/gapi/test/rmat/rmat_tests.cpp +++ b/modules/gapi/test/rmat/rmat_tests.cpp @@ -6,55 +6,10 @@ #include "../test_precomp.hpp" #include +#include "rmat_test_common.hpp" namespace opencv_test { namespace { -class RMatAdapterRef : public RMat::Adapter { - cv::Mat& m_mat; - bool& m_callbackCalled; -public: - RMatAdapterRef(cv::Mat& m, bool& callbackCalled) - : m_mat(m), m_callbackCalled(callbackCalled) - {} - virtual RMat::View access(RMat::Access access) const override { - if (access == RMat::Access::W) { - return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step, - [this](){ - EXPECT_FALSE(m_callbackCalled); - m_callbackCalled = true; - }); - } else { - return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step); - } - } - virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); } -}; - -class RMatAdapterCopy : public RMat::Adapter { - cv::Mat& m_deviceMat; - cv::Mat m_hostMat; - bool& m_callbackCalled; - -public: - RMatAdapterCopy(cv::Mat& m, bool& callbackCalled) - : m_deviceMat(m), m_hostMat(m.clone()), m_callbackCalled(callbackCalled) - {} - virtual RMat::View access(RMat::Access access) const override { - if (access == RMat::Access::W) { - return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step, - [this](){ - EXPECT_FALSE(m_callbackCalled); - m_callbackCalled = true; - m_hostMat.copyTo(m_deviceMat); - }); - } else { - m_deviceMat.copyTo(m_hostMat); - return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step); - } - } - virtual cv::GMatDesc desc() const override { return cv::descr_of(m_hostMat); } -}; - void randomizeMat(cv::Mat& m) { auto ref = m.clone(); while (cv::norm(m, ref, cv::NORM_INF) == 0) { @@ -66,7 +21,7 @@ template struct RMatTest { using AdapterT = RMatAdapterT; RMatTest() - : m_deviceMat(8,8,CV_8UC1) + : m_deviceMat(cv::Mat::zeros(8,8,CV_8UC1)) , m_rmat(make_rmat(m_deviceMat, m_callbackCalled)) { randomizeMat(m_deviceMat); expectNoCallbackCalled(); @@ -140,7 +95,7 @@ TYPED_TEST(RMatTypedTest, CorrectAdapterCast) { } class DummyAdapter : public RMat::Adapter { - virtual RMat::View access(RMat::Access) const override { return {}; } + virtual RMat::View access(RMat::Access) override { return {}; } virtual cv::GMatDesc desc() const override { return {}; } }; @@ -152,7 +107,7 @@ class RMatAdapterForBackend : public RMat::Adapter { int m_i; public: RMatAdapterForBackend(int i) : m_i(i) {} - virtual RMat::View access(RMat::Access) const override { return {}; } + virtual RMat::View access(RMat::Access) override { return {}; } virtual GMatDesc desc() const override { return {}; } int deviceSpecificData() const { return m_i; } }; diff --git a/modules/gapi/test/rmat/rmat_view_tests.cpp b/modules/gapi/test/rmat/rmat_view_tests.cpp index 750f5a74c9..abc251660b 100644 --- a/modules/gapi/test/rmat/rmat_view_tests.cpp +++ b/modules/gapi/test/rmat/rmat_view_tests.cpp @@ -17,22 +17,22 @@ using View = cv::RMat::View; using cv::Mat; using namespace ::testing; -static void expect_eq_desc(const View& view, const GMatDesc& desc) { - EXPECT_EQ(view.size(), desc.size); - EXPECT_EQ(view.dims(), desc.dims); - EXPECT_EQ(view.cols(), desc.size.width); - EXPECT_EQ(view.rows(), desc.size.height); - EXPECT_EQ(view.type(), CV_MAKE_TYPE(desc.depth,desc.chan)); - EXPECT_EQ(view.depth(), desc.depth); - EXPECT_EQ(view.chan(), desc.chan); +static void expect_eq_desc(const GMatDesc& desc, const View& view) { + EXPECT_EQ(desc.size, view.size()); + EXPECT_EQ(desc.dims, view.dims()); + EXPECT_EQ(desc.size.width, view.cols()); + EXPECT_EQ(desc.size.height, view.rows()); + EXPECT_EQ(CV_MAKE_TYPE(desc.depth,desc.chan), view.type()); + EXPECT_EQ(desc.depth, view.depth()); + EXPECT_EQ(desc.chan, view.chan()); } TEST(RMatView, TestDefaultConstruction) { View view; GMatDesc desc{}; - expect_eq_desc(view, desc); - EXPECT_EQ(view.ptr(), nullptr); - EXPECT_EQ(view.step(), 0u); + expect_eq_desc(desc, view); + EXPECT_EQ(nullptr, view.ptr()); + EXPECT_EQ(0u, view.step()); } struct RMatViewTest : public TestWithParam{}; @@ -41,9 +41,9 @@ TEST_P(RMatViewTest, ConstructionFromMat) { Mat mat(8,8,type); const auto desc = cv::descr_of(mat); View view(cv::descr_of(mat), mat.ptr(), mat.step1()); - expect_eq_desc(view, desc); - EXPECT_EQ(view.ptr(), mat.ptr()); - EXPECT_EQ(view.step(), mat.step1()); + expect_eq_desc(desc, view); + EXPECT_EQ(mat.ptr(), view.ptr()); + EXPECT_EQ(mat.step1(), view.step()); } TEST(RMatView, TestConstructionFromMatND) { @@ -51,8 +51,8 @@ TEST(RMatView, TestConstructionFromMatND) { Mat mat(dims, CV_8UC1); const auto desc = cv::descr_of(mat); View view(cv::descr_of(mat), mat.ptr()); - expect_eq_desc(view, desc); - EXPECT_EQ(view.ptr(), mat.ptr()); + expect_eq_desc(desc, view); + EXPECT_EQ(mat.ptr(), view.ptr()); } TEST_P(RMatViewTest, DefaultStep) { @@ -63,7 +63,7 @@ TEST_P(RMatViewTest, DefaultStep) { desc.size = {8,8}; std::vector data(desc.size.width*desc.size.height*CV_ELEM_SIZE(type)); View view(desc, data.data()); - EXPECT_EQ(view.step(), static_cast(desc.size.width)*CV_ELEM_SIZE(type)); + EXPECT_EQ(static_cast(desc.size.width)*CV_ELEM_SIZE(type), view.step()); } static Mat asMat(View& view) { @@ -104,21 +104,31 @@ INSTANTIATE_TEST_CASE_P(Test, RMatViewTest, struct RMatViewCallbackTest : public ::testing::Test { RMatViewCallbackTest() - : mat(8,8,CV_8UC1), view(cv::descr_of(mat), mat.ptr(), mat.step1(), [this](){ callbackCalls++; }) { + : mat(8,8,CV_8UC1) { cv::randn(mat, cv::Scalar::all(127), cv::Scalar::all(40)); } + View getView() { return {cv::descr_of(mat), mat.ptr(), mat.step1(), [this](){ callbackCalls++; }}; } int callbackCalls = 0; Mat mat; - View view; }; +TEST_F(RMatViewCallbackTest, MoveCtor) { + { + View copy(getView()); + cv::util::suppress_unused_warning(copy); + EXPECT_EQ(0, callbackCalls); + } + EXPECT_EQ(1, callbackCalls); +} + TEST_F(RMatViewCallbackTest, MoveCopy) { { - View copy(std::move(view)); + View copy; + copy = getView(); cv::util::suppress_unused_warning(copy); - EXPECT_EQ(callbackCalls, 0); + EXPECT_EQ(0, callbackCalls); } - EXPECT_EQ(callbackCalls, 1); + EXPECT_EQ(1, callbackCalls); } static int firstElement(const View& view) { return *view.ptr(); } @@ -128,20 +138,20 @@ TEST_F(RMatViewCallbackTest, MagazineInteraction) { cv::gimpl::magazine::Class mag; constexpr int rc = 1; constexpr uchar value = 11; - mag.slot()[rc] = std::move(view); + mag.slot()[rc] = getView(); { auto& mag_view = mag.slot()[rc]; setFirstElement(mag_view, value); auto mag_el = firstElement(mag_view); - EXPECT_EQ(mag_el, value); + EXPECT_EQ(value, mag_el); } { const auto& mag_view = mag.slot()[rc]; auto mag_el = firstElement(mag_view); - EXPECT_EQ(mag_el, value); + EXPECT_EQ(value, mag_el); } - EXPECT_EQ(callbackCalls, 0); + EXPECT_EQ(0, callbackCalls); mag.slot().erase(rc); - EXPECT_EQ(callbackCalls, 1); + EXPECT_EQ(1, callbackCalls); } } // namespace opencv_test -- GitLab