Merge pull request #16118 from smirnov-alexey:as/gopaque
authorAlexey Smirnov <alexey.smirnov@intel.com>
Thu, 30 Jan 2020 18:08:11 +0000 (21:08 +0300)
committerGitHub <noreply@github.com>
Thu, 30 Jan 2020 18:08:11 +0000 (21:08 +0300)
G-API: GOpaque implementation

* Stub initial copypasted solution

* Fix mov test and add a couple of others

* Fix warnings

* More code coverage and tests

* fix macos warning

* address review comments

* Address review comments and fix indentation

* Fix build on armv7

33 files changed:
modules/gapi/CMakeLists.txt
modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp
modules/gapi/include/opencv2/gapi/garg.hpp
modules/gapi/include/opencv2/gapi/gcall.hpp
modules/gapi/include/opencv2/gapi/gcommon.hpp
modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp
modules/gapi/include/opencv2/gapi/gkernel.hpp
modules/gapi/include/opencv2/gapi/gmat.hpp
modules/gapi/include/opencv2/gapi/gmetaarg.hpp
modules/gapi/include/opencv2/gapi/gopaque.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gproto.hpp
modules/gapi/include/opencv2/gapi/gtype_traits.hpp
modules/gapi/include/opencv2/gapi/gtyped.hpp
modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp
modules/gapi/src/api/gbackend.cpp
modules/gapi/src/api/gcall.cpp
modules/gapi/src/api/gopaque.cpp [new file with mode: 0644]
modules/gapi/src/api/gproto.cpp
modules/gapi/src/backends/common/gbackend.hpp
modules/gapi/src/backends/cpu/gcpubackend.cpp
modules/gapi/src/backends/cpu/gcpukernel.cpp
modules/gapi/src/backends/fluid/gfluidbackend.cpp
modules/gapi/src/backends/fluid/gfluidbackend.hpp
modules/gapi/src/backends/ie/giebackend.cpp
modules/gapi/src/backends/ocl/goclbackend.cpp
modules/gapi/src/backends/plaidml/gplaidmlbackend.cpp
modules/gapi/src/compiler/gcompiler.cpp
modules/gapi/src/compiler/gobjref.hpp
modules/gapi/src/compiler/passes/dump_dot.cpp
modules/gapi/test/gapi_opaque_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_transform_tests.cpp
modules/gapi/test/internal/gapi_int_garg_test.cpp

index 1fb6d38..1f14b2a 100644 (file)
@@ -44,6 +44,7 @@ set(gapi_srcs
     src/api/gorigin.cpp
     src/api/gmat.cpp
     src/api/garray.cpp
+    src/api/gopaque.cpp
     src/api/gscalar.cpp
     src/api/gkernel.cpp
     src/api/gbackend.cpp
index 85a90ce..e9c6e26 100644 (file)
@@ -94,9 +94,14 @@ public:
     {
         return outVecRef(output).wref<T>();
     }
+    template<typename T> T& outOpaqueR(int output) // FIXME: the same issue
+    {
+        return outOpaqueRef(output).wref<T>();
+    }
 
 protected:
     detail::VectorRef& outVecRef(int output);
+    detail::OpaqueRef& outOpaqueRef(int output);
 
     std::vector<GArg> m_args;
 
@@ -145,12 +150,31 @@ template<typename U> struct get_in<cv::GArray<U> >
 {
     static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
 };
+template<typename U> struct get_in<cv::GOpaque<U> >
+{
+    static const U& get(GCPUContext &ctx, int idx) { return ctx.inArg<OpaqueRef>(idx).rref<U>(); }
+};
 
 //FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
 template<> struct get_in<cv::GArray<cv::GMat> >: public get_in<cv::GArray<cv::Mat> >
 {
 };
 
+//FIXME(dm): GArray<Scalar>/GArray<GScalar> conversion should be done more gracefully in the system
+template<> struct get_in<cv::GArray<cv::GScalar> >: public get_in<cv::GArray<cv::Scalar> >
+{
+};
+
+//FIXME(dm): GOpaque<Mat>/GOpaque<GMat> conversion should be done more gracefully in the system
+template<> struct get_in<cv::GOpaque<cv::GMat> >: public get_in<cv::GOpaque<cv::Mat> >
+{
+};
+
+//FIXME(dm): GOpaque<Scalar>/GOpaque<GScalar> conversion should be done more gracefully in the system
+template<> struct get_in<cv::GOpaque<cv::GScalar> >: public get_in<cv::GOpaque<cv::Mat> >
+{
+};
+
 template<class T> struct get_in
 {
     static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
@@ -229,6 +253,13 @@ template<typename U> struct get_out<cv::GArray<U>>
         return ctx.outVecR<U>(idx);
     }
 };
+template<typename U> struct get_out<cv::GOpaque<U>>
+{
+    static U& get(GCPUContext &ctx, int idx)
+    {
+        return ctx.outOpaqueR<U>(idx);
+    }
+};
 
 template<typename, typename, typename>
 struct OCVCallHelper;
index 3f7a0f8..4d2e4a6 100644 (file)
@@ -200,6 +200,14 @@ template<typename U> struct fluid_get_in<cv::GArray<U>>
     }
 };
 
+template<typename U> struct fluid_get_in<cv::GOpaque<U>>
+{
+    static const U& get(const cv::GArgs &in_args, int idx)
+    {
+        return in_args.at(idx).unsafe_get<cv::detail::OpaqueRef>().rref<U>();
+    }
+};
+
 template<class T> struct fluid_get_in
 {
     static const T& get(const cv::GArgs &in_args, int idx)
index 9b835d9..7228ac6 100644 (file)
@@ -20,6 +20,7 @@
 #include <opencv2/gapi/gmat.hpp>
 #include <opencv2/gapi/gscalar.hpp>
 #include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gopaque.hpp>
 #include <opencv2/gapi/gtype_traits.hpp>
 #include <opencv2/gapi/gmetaarg.hpp>
 #include <opencv2/gapi/own/scalar.hpp>
@@ -96,7 +97,8 @@ using GRunArg  = util::variant<
     cv::gapi::wip::IStreamSource::Ptr,
     cv::gapi::own::Mat,
     cv::gapi::own::Scalar,
-    cv::detail::VectorRef
+    cv::detail::VectorRef,
+    cv::detail::OpaqueRef
     >;
 using GRunArgs = std::vector<GRunArg>;
 
@@ -128,7 +130,8 @@ using GRunArgP = util::variant<
 #endif // !defined(GAPI_STANDALONE)
     cv::gapi::own::Mat*,
     cv::gapi::own::Scalar*,
-    cv::detail::VectorRef
+    cv::detail::VectorRef,
+    cv::detail::OpaqueRef
     >;
 using GRunArgsP = std::vector<GRunArgP>;
 
index 87cba52..ed5ba5f 100644 (file)
@@ -12,6 +12,7 @@
 #include <opencv2/gapi/gmat.hpp>      // GMat
 #include <opencv2/gapi/gscalar.hpp>   // GScalar
 #include <opencv2/gapi/garray.hpp>    // GArray<T>
+#include <opencv2/gapi/gopaque.hpp>   // GOpaque<T>
 
 namespace cv {
 
@@ -46,6 +47,11 @@ public:
         return GArray<T>(yieldArray(output));
     }
 
+    template<class T> GOpaque<T> yieldOpaque(int output = 0)
+    {
+        return GOpaque<T>(yieldOpaque(output));
+    }
+
     // Internal use only
     Priv& priv();
     const Priv& priv() const;
@@ -55,8 +61,9 @@ protected:
 
     void setArgs(std::vector<GArg> &&args);
 
-    // Public version returns a typed array, this one is implementation detail
+    // Public versions return a typed array or opaque, those are implementation details
     detail::GArrayU yieldArray(int output = 0);
+    detail::GOpaqueU yieldOpaque(int output = 0);
 };
 
 } // namespace cv
index 40c6d36..9ee75f7 100644 (file)
@@ -44,6 +44,7 @@ enum class GShape: int
     GMAT,
     GSCALAR,
     GARRAY,
+    GOPAQUE,
 };
 
 struct GCompileArg;
index 84b8780..2f17064 100644 (file)
@@ -65,6 +65,16 @@ template<typename U> struct get_compound_in<cv::GArray<U>>
     }
 };
 
+template<typename U> struct get_compound_in<cv::GOpaque<U>>
+{
+    static cv::GOpaque<U> get(GCompoundContext &ctx, int idx)
+    {
+        auto opaq = cv::GOpaque<U>();
+        ctx.m_args[idx] = GArg(opaq);
+        return opaq;
+    }
+};
+
 template<typename, typename, typename>
 struct GCompoundCallHelper;
 
index 91f5731..b7ff883 100644 (file)
@@ -74,6 +74,10 @@ namespace detail
         {
             static inline cv::GArray<U> yield(cv::GCall &call, int i) { return call.yieldArray<U>(i); }
         };
+        template<typename U> struct Yield<cv::GOpaque<U> >
+        {
+            static inline cv::GOpaque<U> yield(cv::GCall &call, int i) { return call.yieldOpaque<U>(i); }
+        };
     } // anonymous namespace
 
     ////////////////////////////////////////////////////////////////////////////
@@ -87,7 +91,8 @@ namespace detail
     template<> struct MetaType<cv::GMat>    { using type = GMatDesc; };
     template<> struct MetaType<cv::GMatP>   { using type = GMatDesc; };
     template<> struct MetaType<cv::GScalar> { using type = GScalarDesc; };
-    template<typename U> struct MetaType<cv::GArray<U> > { using type = GArrayDesc; };
+    template<typename U> struct MetaType<cv::GArray<U> >  { using type = GArrayDesc; };
+    template<typename U> struct MetaType<cv::GOpaque<U> > { using type = GOpaqueDesc; };
     template<typename T> struct MetaType    { using type = T; }; // opaque args passed as-is
 
     // 2. Hacky test based on MetaType to check if we operate on G-* type or not
index 4f67126..d05d151 100644 (file)
@@ -46,6 +46,7 @@ struct GOrigin;
  *    cv::GMat           | cv::Mat
  *    cv::GScalar        | cv::Scalar
  *    `cv::GArray<T>`    | std::vector<T>
+ *    `cv::GOpaque<T>`   | T
  */
 class GAPI_EXPORTS GMat
 {
index 0bd6767..39f087f 100644 (file)
@@ -17,6 +17,7 @@
 #include <opencv2/gapi/gmat.hpp>
 #include <opencv2/gapi/gscalar.hpp>
 #include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gopaque.hpp>
 
 namespace cv
 {
@@ -36,6 +37,7 @@ using GMetaArg = util::variant
     , GMatDesc
     , GScalarDesc
     , GArrayDesc
+    , GOpaqueDesc
     >;
 GAPI_EXPORTS std::ostream& operator<<(std::ostream& os, const GMetaArg &);
 
@@ -52,6 +54,7 @@ namespace detail
     template<> struct is_meta_descr<GMatDesc>    : std::true_type {};
     template<> struct is_meta_descr<GScalarDesc> : std::true_type {};
     template<> struct is_meta_descr<GArrayDesc>  : std::true_type {};
+    template<> struct is_meta_descr<GOpaqueDesc> : std::true_type {};
 
     template<typename... Ts>
     using are_meta_descrs = all_satisfy<is_meta_descr, Ts...>;
diff --git a/modules/gapi/include/opencv2/gapi/gopaque.hpp b/modules/gapi/include/opencv2/gapi/gopaque.hpp
new file mode 100644 (file)
index 0000000..a8b0c59
--- /dev/null
@@ -0,0 +1,289 @@
+// 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) 2019 Intel Corporation
+
+
+#ifndef OPENCV_GAPI_GOPAQUE_HPP
+#define OPENCV_GAPI_GOPAQUE_HPP
+
+#include <functional>
+#include <ostream>
+#include <memory>
+
+#include <opencv2/gapi/own/exports.hpp>
+#include <opencv2/gapi/opencv_includes.hpp>
+
+#include <opencv2/gapi/util/variant.hpp>
+#include <opencv2/gapi/util/throw.hpp>
+#include <opencv2/gapi/own/assert.hpp>
+
+namespace cv
+{
+// Forward declaration; GNode and GOrigin are an internal
+// (user-inaccessible) classes.
+class GNode;
+struct GOrigin;
+
+template<typename T> class GOpaque;
+
+/**
+ * \addtogroup gapi_meta_args
+ * @{
+ */
+struct GOpaqueDesc
+{
+    // FIXME: Body
+    // FIXME: Also implement proper operator== then
+    bool operator== (const GOpaqueDesc&) const { return true; }
+};
+template<typename U> GOpaqueDesc descr_of(const U &) { return {};}
+static inline GOpaqueDesc empty_gopaque_desc() {return {}; }
+/** @} */
+
+std::ostream& operator<<(std::ostream& os, const cv::GOpaqueDesc &desc);
+
+namespace detail
+{
+    // ConstructOpaque is a callback which stores information about T and is used by
+    // G-API runtime to construct an object in host memory (T remains opaque for G-API).
+    // ConstructOpaque is carried into G-API internals by GOpaqueU.
+    // Currently it is suitable for Host (CPU) plugins only, real offload may require
+    // more information for manual memory allocation on-device.
+    class OpaqueRef;
+    using ConstructOpaque = std::function<void(OpaqueRef&)>;
+
+    // FIXME: garray.hpp already contains hint classes (for actual T type verification),
+    // need to think where it can be moved (currently opaque uses it from garray)
+
+    // This class strips type information from GOpaque<T> and makes it usable
+    // in the G-API graph compiler (expression unrolling, graph generation, etc).
+    // Part of GProtoArg.
+    class GAPI_EXPORTS GOpaqueU
+    {
+    public:
+        GOpaqueU(const GNode &n, std::size_t out); // Operation result constructor
+
+        template <typename T>
+        bool holds() const;                       // Check if was created from GOpaque<T>
+
+        GOrigin& priv();                          // Internal use only
+        const GOrigin& priv() const;              // Internal use only
+
+    protected:
+        GOpaqueU();                                // Default constructor
+        template<class> friend class cv::GOpaque;  // (available for GOpaque<T> only)
+
+        void setConstructFcn(ConstructOpaque &&cv);  // Store T-aware constructor
+
+        template <typename T>
+        void specifyType();                       // Store type of initial GOpaque<T>
+
+        std::shared_ptr<GOrigin> m_priv;
+        std::shared_ptr<TypeHintBase> m_hint;
+    };
+
+    template <typename T>
+    bool GOpaqueU::holds() const{
+        GAPI_Assert(m_hint != nullptr);
+        using U = typename std::decay<T>::type;
+        return dynamic_cast<TypeHint<U>*>(m_hint.get()) != nullptr;
+    };
+
+    template <typename T>
+    void GOpaqueU::specifyType(){
+        m_hint.reset(new TypeHint<typename std::decay<T>::type>);
+    };
+
+    // This class represents a typed object reference.
+    // Depending on origins, this reference may be either "just a" reference to
+    // an object created externally, OR actually own the underlying object
+    // (be value holder).
+    class BasicOpaqueRef
+    {
+    public:
+        cv::GOpaqueDesc m_desc;
+        virtual ~BasicOpaqueRef() {}
+
+        virtual void mov(BasicOpaqueRef &ref) = 0;
+    };
+
+    template<typename T> class OpaqueRefT final: public BasicOpaqueRef
+    {
+        using empty_t  = util::monostate;
+        using ro_ext_t = const T *;
+        using rw_ext_t =       T *;
+        using rw_own_t =       T  ;
+        util::variant<empty_t, ro_ext_t, rw_ext_t, rw_own_t> m_ref;
+
+        inline bool isEmpty() const { return util::holds_alternative<empty_t>(m_ref);  }
+        inline bool isROExt() const { return util::holds_alternative<ro_ext_t>(m_ref); }
+        inline bool isRWExt() const { return util::holds_alternative<rw_ext_t>(m_ref); }
+        inline bool isRWOwn() const { return util::holds_alternative<rw_own_t>(m_ref); }
+
+        void init(const T* obj = nullptr)
+        {
+            if (obj) m_desc = cv::descr_of(*obj);
+        }
+
+    public:
+        OpaqueRefT() { init(); }
+        virtual ~OpaqueRefT() {}
+
+        explicit OpaqueRefT(const T&  obj) : m_ref(&obj)           { init(&obj); }
+        explicit OpaqueRefT(      T&  obj) : m_ref(&obj)           { init(&obj); }
+        explicit OpaqueRefT(      T&& obj) : m_ref(std::move(obj)) { init(&obj); }
+
+        // Reset a OpaqueRefT. Called only for objects instantiated
+        // internally in G-API (e.g. temporary GOpaque<T>'s within a
+        // computation).  Reset here means both initialization
+        // (creating an object) and reset (discarding its existing
+        // content before the next execution). Must never be called
+        // for external OpaqueRefTs.
+        void reset()
+        {
+            if (isEmpty())
+            {
+                T empty_obj{};
+                m_desc = cv::descr_of(empty_obj);
+                m_ref  = std::move(empty_obj);
+                GAPI_Assert(isRWOwn());
+            }
+            else if (isRWOwn())
+            {
+                util::get<rw_own_t>(m_ref) = {};
+            }
+            else GAPI_Assert(false); // shouldn't be called in *EXT modes
+        }
+
+        // Obtain a WRITE reference to underlying object
+        // Used by CPU kernel API wrappers when a kernel execution frame
+        // is created
+        T& wref()
+        {
+            GAPI_Assert(isRWExt() || isRWOwn());
+            if (isRWExt()) return *util::get<rw_ext_t>(m_ref);
+            if (isRWOwn()) return  util::get<rw_own_t>(m_ref);
+            util::throw_error(std::logic_error("Impossible happened"));
+        }
+
+        // Obtain a READ reference to underlying object
+        // Used by CPU kernel API wrappers when a kernel execution frame
+        // is created
+        const T& rref() const
+        {
+            // ANY object can be accessed for reading, even if it declared for
+            // output. Example -- a GComputation from [in] to [out1,out2]
+            // where [out2] is a result of operation applied to [out1]:
+            //
+            //            GComputation boundary
+            //            . . . . . . .
+            //            .           .
+            //     [in] ----> foo() ----> [out1]
+            //            .           .    :
+            //            .           . . .:. . .
+            //            .                V    .
+            //            .              bar() ---> [out2]
+            //            . . . . . . . . . . . .
+            //
+            if (isROExt()) return *util::get<ro_ext_t>(m_ref);
+            if (isRWExt()) return *util::get<rw_ext_t>(m_ref);
+            if (isRWOwn()) return  util::get<rw_own_t>(m_ref);
+            util::throw_error(std::logic_error("Impossible happened"));
+        }
+
+        virtual void mov(BasicOpaqueRef &v) override {
+            OpaqueRefT<T> *tv = dynamic_cast<OpaqueRefT<T>*>(&v);
+            GAPI_Assert(tv != nullptr);
+            wref() = std::move(tv->wref());
+        }
+    };
+
+    // This class strips type information from OpaqueRefT<> and makes it usable
+    // in the G-API executables (carrying run-time data/information to kernels).
+    // Part of GRunArg.
+    // Its methods are typed proxies to OpaqueRefT<T>.
+    // OpaqueRef maintains "reference" semantics so two copies of OpaqueRef refer
+    // to the same underlying object.
+    class OpaqueRef
+    {
+        std::shared_ptr<BasicOpaqueRef> m_ref;
+
+        template<typename T> inline void check() const
+        {
+            GAPI_DbgAssert(dynamic_cast<OpaqueRefT<T>*>(m_ref.get()) != nullptr);
+        }
+
+    public:
+        OpaqueRef() = default;
+
+        template<typename T> explicit OpaqueRef(T&& obj) :
+            m_ref(new OpaqueRefT<typename std::decay<T>::type>(std::forward<T>(obj))) {}
+
+        template<typename T> void reset()
+        {
+            if (!m_ref) m_ref.reset(new OpaqueRefT<T>());
+
+            check<T>();
+            static_cast<OpaqueRefT<T>&>(*m_ref).reset();
+        }
+
+        template<typename T> T& wref()
+        {
+            check<T>();
+            return static_cast<OpaqueRefT<T>&>(*m_ref).wref();
+        }
+
+        template<typename T> const T& rref() const
+        {
+            check<T>();
+            return static_cast<OpaqueRefT<T>&>(*m_ref).rref();
+        }
+
+        void mov(OpaqueRef &v)
+        {
+            m_ref->mov(*v.m_ref);
+        }
+
+        cv::GOpaqueDesc descr_of() const
+        {
+            return m_ref->m_desc;
+        }
+    };
+} // namespace detail
+
+/** \addtogroup gapi_data_objects
+ * @{
+ */
+
+template<typename T> class GOpaque
+{
+public:
+    GOpaque() { putDetails(); }              // Empty constructor
+    explicit GOpaque(detail::GOpaqueU &&ref) // GOpaqueU-based constructor
+        : m_ref(ref) { putDetails(); }       // (used by GCall, not for users)
+
+    detail::GOpaqueU strip() const { return m_ref; }
+
+private:
+    // Host type (or Flat type) - the type this GOpaque is actually
+    // specified to.
+    using HT = typename detail::flatten_g<typename std::decay<T>::type>::type;
+
+    static void CTor(detail::OpaqueRef& ref) {
+        ref.reset<HT>();
+    }
+    void putDetails() {
+        m_ref.setConstructFcn(&CTor);
+        m_ref.specifyType<HT>();
+    }
+
+    detail::GOpaqueU m_ref;
+};
+
+/** @} */
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GOPAQUE_HPP
index b9e206a..fe4d3dc 100644 (file)
@@ -17,6 +17,7 @@
 #include <opencv2/gapi/gmat.hpp>
 #include <opencv2/gapi/gscalar.hpp>
 #include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gopaque.hpp>
 #include <opencv2/gapi/garg.hpp>
 #include <opencv2/gapi/gmetaarg.hpp>
 
@@ -36,7 +37,8 @@ using GProtoArg = util::variant
     < GMat
     , GMatP
     , GScalar
-    , detail::GArrayU // instead of GArray<T>
+    , detail::GArrayU  // instead of GArray<T>
+    , detail::GOpaqueU // instead of GOpaque<T>
     >;
 
 using GProtoArgs = std::vector<GProtoArg>;
index a1373f3..fa871d1 100644 (file)
@@ -14,6 +14,7 @@
 #include <opencv2/gapi/gmat.hpp>
 #include <opencv2/gapi/gscalar.hpp>
 #include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gopaque.hpp>
 #include <opencv2/gapi/streaming/source.hpp>
 #include <opencv2/gapi/gcommon.hpp>
 #include <opencv2/gapi/own/convert.hpp>
@@ -36,7 +37,8 @@ namespace detail
         GMAT,         // a cv::GMat
         GMATP,        // a cv::GMatP
         GSCALAR,      // a cv::GScalar
-        GARRAY,       // a cv::GArrayU (note - exactly GArrayU, not GArray<T>!)
+        GARRAY,       // a cv::GArrayU  (note - exactly GArrayU,  not GArray<T>!)
+        GOPAQUE,      // a cv::GOpaqueU (note - exactly GOpaqueU, not GOpaque<T>!)
     };
 
     // Describe G-API types (G-types) with traits.  Mostly used by
@@ -73,6 +75,16 @@ namespace detail
         static cv::detail::VectorRef wrap_in   (const std::vector<T> &t) { return detail::VectorRef(t); }
         static cv::detail::VectorRef wrap_out  (      std::vector<T> &t) { return detail::VectorRef(t); }
     };
+    template<class T> struct GTypeTraits<cv::GOpaque<T> >
+    {
+        static constexpr const ArgKind kind = ArgKind::GOPAQUE;
+        static constexpr const GShape shape = GShape::GOPAQUE;
+        using host_type  = T;
+        using strip_type = cv::detail::OpaqueRef;
+        static cv::detail::GOpaqueU  wrap_value(const cv::GOpaque<T>  &t) { return t.strip();}
+        static cv::detail::OpaqueRef wrap_in   (const T &t) { return detail::OpaqueRef(t); }
+        static cv::detail::OpaqueRef wrap_out  (      T &t) { return detail::OpaqueRef(t); }
+    };
 
     // Tests if Trait for type T requires extra marshalling ("custom wrap") or not.
     // If Traits<T> has wrap_value() defined, it does.
@@ -100,6 +112,7 @@ namespace detail
     template<>           struct GTypeOf<cv::gapi::own::Mat>    { using type = cv::GMat;      };
     template<>           struct GTypeOf<cv::gapi::own::Scalar> { using type = cv::GScalar;   };
     template<typename U> struct GTypeOf<std::vector<U> >       { using type = cv::GArray<U>; };
+    template<typename U> struct GTypeOf                        { using type = cv::GOpaque<U>;};
     // FIXME: This is not quite correct since IStreamSource may produce not only Mat but also Scalar
     // and vector data. TODO: Extend the type dispatching on these types too.
     template<>           struct GTypeOf<cv::gapi::wip::IStreamSource::Ptr> { using type = cv::GMat;};
@@ -164,7 +177,6 @@ namespace detail
 
     template<typename T> using wrap_gapi_helper = WrapValue<typename std::decay<T>::type>;
     template<typename T> using wrap_host_helper = WrapValue<typename std::decay<g_type_of_t<T> >::type>;
-
 } // namespace detail
 } // namespace cv
 
index 5b941c7..1ce6201 100644 (file)
@@ -25,13 +25,15 @@ namespace detail
     template<typename T> struct ProtoToParam;
     template<> struct ProtoToParam<cv::GMat>    { using type = cv::Mat; };
     template<> struct ProtoToParam<cv::GScalar> { using type = cv::Scalar; };
-    template<typename U> struct ProtoToParam<cv::GArray<U> > { using type = std::vector<U>; };
+    template<typename U> struct ProtoToParam<cv::GArray<U> >  { using type = std::vector<U>; };
+    template<typename U> struct ProtoToParam<cv::GOpaque<U> > { using type = U; };
     template<typename T> using ProtoToParamT = typename ProtoToParam<T>::type;
 
     template<typename T> struct ProtoToMeta;
     template<> struct ProtoToMeta<cv::GMat>     { using type = cv::GMatDesc; };
     template<> struct ProtoToMeta<cv::GScalar>  { using type = cv::GScalarDesc; };
-    template<typename U> struct ProtoToMeta<cv::GArray<U> > { using type = cv::GArrayDesc; };
+    template<typename U> struct ProtoToMeta<cv::GArray<U> >  { using type = cv::GArrayDesc; };
+    template<typename U> struct ProtoToMeta<cv::GOpaque<U> > { using type = cv::GOpaqueDesc; };
     template<typename T> using ProtoToMetaT = typename ProtoToMeta<T>::type;
 
     //workaround for MSVC 19.0 bug
index b729171..e80854a 100644 (file)
@@ -68,9 +68,14 @@ public:
     {
         return outVecRef(output).wref<T>();
     }
+    template<typename T> T& outOpaqueR(int output) // FIXME: the same issue
+    {
+        return outOpaqueRef(output).wref<T>();
+    }
 
 protected:
     detail::VectorRef& outVecRef(int output);
+    detail::VectorRef& outOpaqueRef(int output);
 
     std::vector<GArg> m_args;
     std::unordered_map<std::size_t, GRunArgP> m_results;
@@ -111,6 +116,10 @@ template<typename U> struct ocl_get_in<cv::GArray<U> >
 {
     static const std::vector<U>& get(GOCLContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
 };
+template<typename U> struct ocl_get_in<cv::GOpaque<U> >
+{
+    static const U& get(GOCLContext &ctx, int idx) { return ctx.inArg<OpaqueRef>(idx).rref<U>(); }
+};
 template<class T> struct ocl_get_in
 {
     static T get(GOCLContext &ctx, int idx) { return ctx.inArg<T>(idx); }
@@ -184,6 +193,10 @@ template<typename U> struct ocl_get_out<cv::GArray<U> >
 {
     static std::vector<U>& get(GOCLContext &ctx, int idx) { return ctx.outVecR<U>(idx);  }
 };
+template<typename U> struct ocl_get_out<cv::GOpaque<U> >
+{
+    static U& get(GOCLContext &ctx, int idx) { return ctx.outOpaqueR<U>(idx);  }
+};
 
 template<typename, typename, typename>
 struct OCLCallHelper;
index a2bf0d0..304c581 100644 (file)
@@ -168,6 +168,10 @@ void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat)
         mag.template slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg);
         break;
 
+    case GShape::GOPAQUE:
+        mag.template slot<cv::detail::OpaqueRef>()[rc.id] = util::get<cv::detail::OpaqueRef>(arg);
+        break;
+
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
     }
@@ -233,6 +237,10 @@ void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat)
         mag.template slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg);
         break;
 
+    case GShape::GOPAQUE:
+        mag.template slot<cv::detail::OpaqueRef>()[rc.id] = util::get<cv::detail::OpaqueRef>(arg);
+        break;
+
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
@@ -251,6 +259,11 @@ void resetInternalData(Mag& mag, const Data &d)
             (mag.template slot<cv::detail::VectorRef>()[d.rc]);
         break;
 
+    case GShape::GOPAQUE:
+        util::get<cv::detail::ConstructOpaque>(d.ctor)
+            (mag.template slot<cv::detail::OpaqueRef>()[d.rc]);
+        break;
+
     case GShape::GSCALAR:
         mag.template slot<cv::gapi::own::Scalar>()[d.rc] = cv::gapi::own::Scalar();
         break;
@@ -272,9 +285,10 @@ cv::GRunArg getArg(const Mag& mag, const RcDesc &ref)
     {
     case GShape::GMAT:    return GRunArg(mag.template slot<cv::gapi::own::Mat>().at(ref.id));
     case GShape::GSCALAR: return GRunArg(mag.template slot<cv::gapi::own::Scalar>().at(ref.id));
-    // Note: .at() is intentional for GArray as object MUST be already there
+    // Note: .at() is intentional for GArray and GOpaque as objects MUST be already there
     //   (and constructed by either bindIn/Out or resetInternal)
     case GShape::GARRAY:  return GRunArg(mag.template slot<cv::detail::VectorRef>().at(ref.id));
+    case GShape::GOPAQUE: return GRunArg(mag.template slot<cv::detail::OpaqueRef>().at(ref.id));
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
@@ -297,7 +311,7 @@ cv::GRunArgP getObjPtr(Mag& mag, const RcDesc &rc, bool is_umat)
         else
             return GRunArgP(&mag.template slot<cv::gapi::own::Mat>()[rc.id]);
     case GShape::GSCALAR: return GRunArgP(&mag.template slot<cv::gapi::own::Scalar>()[rc.id]);
-    // Note: .at() is intentional for GArray as object MUST be already there
+    // Note: .at() is intentional for GArray and GOpaque as objects MUST be already there
     //   (and constructor by either bindIn/Out or resetInternal)
     case GShape::GARRAY:
         // FIXME(DM): For some absolutely unknown to me reason, move
@@ -307,6 +321,14 @@ cv::GRunArgP getObjPtr(Mag& mag, const RcDesc &rc, bool is_umat)
         // debugging this!!!1
         return GRunArgP(const_cast<const Mag&>(mag)
                         .template slot<cv::detail::VectorRef>().at(rc.id));
+    case GShape::GOPAQUE:
+        // FIXME(DM): For some absolutely unknown to me reason, move
+        // semantics is involved here without const_cast to const (and
+        // value from map is moved into return value GRunArgP, leaving
+        // map with broken value I've spent few late Friday hours
+        // debugging this!!!1
+        return GRunArgP(const_cast<const Mag&>(mag)
+                        .template slot<cv::detail::OpaqueRef>().at(rc.id));
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
@@ -320,6 +342,9 @@ void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat)
     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:
     {
index 4c052ff..6f5f65b 100644 (file)
@@ -64,6 +64,11 @@ cv::detail::GArrayU cv::GCall::yieldArray(int output)
     return cv::detail::GArrayU(m_priv->m_node, output);
 }
 
+cv::detail::GOpaqueU cv::GCall::yieldOpaque(int output)
+{
+    return cv::detail::GOpaqueU(m_priv->m_node, output);
+}
+
 cv::GCall::Priv& cv::GCall::priv()
 {
     return *m_priv;
diff --git a/modules/gapi/src/api/gopaque.cpp b/modules/gapi/src/api/gopaque.cpp
new file mode 100644 (file)
index 0000000..9dff6e7
--- /dev/null
@@ -0,0 +1,45 @@
+// 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) 2019 Intel Corporation
+
+
+#include "precomp.hpp"
+#include <opencv2/gapi/gopaque.hpp>
+#include "api/gorigin.hpp"
+
+// cv::detail::GOpaqueU public implementation ///////////////////////////////////
+cv::detail::GOpaqueU::GOpaqueU()
+    : m_priv(new GOrigin(GShape::GOPAQUE, cv::GNode::Param()))
+{
+}
+
+cv::detail::GOpaqueU::GOpaqueU(const GNode &n, std::size_t out)
+    : m_priv(new GOrigin(GShape::GOPAQUE, n, out))
+{
+}
+
+cv::GOrigin& cv::detail::GOpaqueU::priv()
+{
+    return *m_priv;
+}
+
+const cv::GOrigin& cv::detail::GOpaqueU::priv() const
+{
+    return *m_priv;
+}
+
+void cv::detail::GOpaqueU::setConstructFcn(ConstructOpaque &&co)
+{
+    m_priv->ctor = std::move(co);
+}
+
+namespace cv {
+std::ostream& operator<<(std::ostream& os, const cv::GOpaqueDesc &)
+{
+    // FIXME: add type information here
+    os << "(Opaque)";
+    return os;
+}
+}
index 3e5e0e6..c4cfca6 100644 (file)
@@ -34,6 +34,9 @@ const cv::GOrigin& cv::gimpl::proto::origin_of(const cv::GProtoArg &arg)
     case cv::GProtoArg::index_of<cv::detail::GArrayU>():
         return util::get<cv::detail::GArrayU>(arg).priv();
 
+    case cv::GProtoArg::index_of<cv::detail::GOpaqueU>():
+        return util::get<cv::detail::GOpaqueU>(arg).priv();
+
     default:
         util::throw_error(std::logic_error("Unsupported GProtoArg type"));
     }
@@ -59,6 +62,7 @@ bool cv::gimpl::proto::is_dynamic(const cv::GArg& arg)
     case detail::ArgKind::GMATP:
     case detail::ArgKind::GSCALAR:
     case detail::ArgKind::GARRAY:
+    case detail::ArgKind::GOPAQUE:
         return true;
 
     default:
@@ -85,6 +89,7 @@ cv::GProtoArg cv::gimpl::proto::rewrap(const cv::GArg &arg)
     case detail::ArgKind::GMATP:   return GProtoArg(arg.get<cv::GMatP>());
     case detail::ArgKind::GSCALAR: return GProtoArg(arg.get<cv::GScalar>());
     case detail::ArgKind::GARRAY:  return GProtoArg(arg.get<cv::detail::GArrayU>());
+    case detail::ArgKind::GOPAQUE:  return GProtoArg(arg.get<cv::detail::GOpaqueU>());
     default: util::throw_error(std::logic_error("Unsupported GArg type"));
     }
 }
@@ -110,6 +115,9 @@ cv::GMetaArg cv::descr_of(const cv::GRunArg &arg)
         case GRunArg::index_of<cv::detail::VectorRef>():
             return cv::GMetaArg(util::get<cv::detail::VectorRef>(arg).descr_of());
 
+        case GRunArg::index_of<cv::detail::OpaqueRef>():
+            return cv::GMetaArg(util::get<cv::detail::OpaqueRef>(arg).descr_of());
+
         case GRunArg::index_of<cv::gapi::wip::IStreamSource::Ptr>():
             return cv::util::get<cv::gapi::wip::IStreamSource::Ptr>(arg)->descr_of();
 
@@ -136,6 +144,7 @@ cv::GMetaArg cv::descr_of(const cv::GRunArgP &argp)
     case GRunArgP::index_of<cv::gapi::own::Mat*>():    return GMetaArg(descr_of(*util::get<cv::gapi::own::Mat*>(argp)));
     case GRunArgP::index_of<cv::gapi::own::Scalar*>(): return GMetaArg(descr_of(*util::get<cv::gapi::own::Scalar*>(argp)));
     case GRunArgP::index_of<cv::detail::VectorRef>(): return GMetaArg(util::get<cv::detail::VectorRef>(argp).descr_of());
+    case GRunArgP::index_of<cv::detail::OpaqueRef>(): return GMetaArg(util::get<cv::detail::OpaqueRef>(argp).descr_of());
     default: util::throw_error(std::logic_error("Unsupported GRunArgP type"));
     }
 }
@@ -154,6 +163,7 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArgP& argp)
                                                               util::get<GMatDesc>(meta).canDescribe(*util::get<cv::gapi::own::Mat*>(argp));
     case GRunArgP::index_of<cv::gapi::own::Scalar*>(): return meta == GMetaArg(descr_of(*util::get<cv::gapi::own::Scalar*>(argp)));
     case GRunArgP::index_of<cv::detail::VectorRef>():  return meta == GMetaArg(util::get<cv::detail::VectorRef>(argp).descr_of());
+    case GRunArgP::index_of<cv::detail::OpaqueRef>():  return meta == GMetaArg(util::get<cv::detail::OpaqueRef>(argp).descr_of());
     default: util::throw_error(std::logic_error("Unsupported GRunArgP type"));
     }
 }
@@ -172,6 +182,7 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg)
                                                             util::get<GMatDesc>(meta).canDescribe(util::get<cv::gapi::own::Mat>(arg));
     case GRunArg::index_of<cv::gapi::own::Scalar>(): return meta == cv::GMetaArg(descr_of(util::get<cv::gapi::own::Scalar>(arg)));
     case GRunArg::index_of<cv::detail::VectorRef>(): return meta == cv::GMetaArg(util::get<cv::detail::VectorRef>(arg).descr_of());
+    case GRunArg::index_of<cv::detail::OpaqueRef>(): return meta == cv::GMetaArg(util::get<cv::detail::OpaqueRef>(arg).descr_of());
     case GRunArg::index_of<cv::gapi::wip::IStreamSource::Ptr>(): return util::holds_alternative<GMatDesc>(meta); // FIXME(?) may be not the best option
     default: util::throw_error(std::logic_error("Unsupported GRunArg type"));
     }
@@ -207,6 +218,10 @@ std::ostream& operator<<(std::ostream& os, const cv::GMetaArg &arg)
     case cv::GMetaArg::index_of<cv::GArrayDesc>():
         os << util::get<cv::GArrayDesc>(arg);
         break;
+
+    case cv::GMetaArg::index_of<cv::GOpaqueDesc>():
+        os << util::get<cv::GOpaqueDesc>(arg);
+        break;
     default:
         GAPI_Assert(false);
     }
index 0c9c20e..dd5624b 100644 (file)
@@ -46,9 +46,9 @@ namespace magazine {
 
 } // namespace magazine
 #if !defined(GAPI_STANDALONE)
-using Mag = magazine::Class<cv::gapi::own::Mat, cv::UMat, cv::gapi::own::Scalar, cv::detail::VectorRef>;
+using Mag = magazine::Class<cv::gapi::own::Mat, cv::UMat, cv::gapi::own::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
 #else
-using Mag = magazine::Class<cv::gapi::own::Mat, cv::gapi::own::Scalar, cv::detail::VectorRef>;
+using Mag = magazine::Class<cv::gapi::own::Mat, cv::gapi::own::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
 #endif
 
 namespace magazine
index 052b47f..14eeaaa 100644 (file)
@@ -113,7 +113,8 @@ cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg)
     // FIXME: this check has to be done somewhere in compilation stage.
     GAPI_Assert(   arg.kind != cv::detail::ArgKind::GMAT
               && arg.kind != cv::detail::ArgKind::GSCALAR
-              && arg.kind != cv::detail::ArgKind::GARRAY);
+              && arg.kind != cv::detail::ArgKind::GARRAY
+              && arg.kind != cv::detail::ArgKind::GOPAQUE);
 
     if (arg.kind != cv::detail::ArgKind::GOBJREF)
     {
@@ -129,9 +130,10 @@ cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg)
     {
     case GShape::GMAT:    return GArg(m_res.slot<cv::gapi::own::Mat>()   [ref.id]);
     case GShape::GSCALAR: return GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
-    // Note: .at() is intentional for GArray as object MUST be already there
+    // Note: .at() is intentional for GArray and GOpaque as objects MUST be already there
     //   (and constructed by either bindIn/Out or resetInternal)
     case GShape::GARRAY:  return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
+    case GShape::GOPAQUE: return GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id));
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
index 5bc77aa..52137d8 100644 (file)
@@ -36,6 +36,11 @@ cv::detail::VectorRef& cv::GCPUContext::outVecRef(int output)
     return util::get<cv::detail::VectorRef>(m_results.at(output));
 }
 
+cv::detail::OpaqueRef& cv::GCPUContext::outOpaqueRef(int output)
+{
+    return util::get<cv::detail::OpaqueRef>(m_results.at(output));
+}
+
 cv::GCPUKernel::GCPUKernel()
 {
 }
index bdd4432..1c926b4 100644 (file)
@@ -1250,6 +1250,7 @@ void cv::gimpl::GFluidExecutable::bindInArg(const cv::gimpl::RcDesc &rc, const G
     case GShape::GMAT:    m_buffers[m_id_map.at(rc.id)].priv().bindTo(util::get<cv::gapi::own::Mat>(arg), true); break;
     case GShape::GSCALAR: m_res.slot<cv::gapi::own::Scalar>()[rc.id] = util::get<cv::gapi::own::Scalar>(arg); break;
     case GShape::GARRAY:  m_res.slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg); break;
+    case GShape::GOPAQUE: m_res.slot<cv::detail::OpaqueRef>()[rc.id] = util::get<cv::detail::OpaqueRef>(arg); break;
     }
 }
 
@@ -1292,7 +1293,8 @@ void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_a
 {
     GAPI_Assert(op_arg.kind != cv::detail::ArgKind::GMAT
            && op_arg.kind != cv::detail::ArgKind::GSCALAR
-           && op_arg.kind != cv::detail::ArgKind::GARRAY);
+           && op_arg.kind != cv::detail::ArgKind::GARRAY
+           && op_arg.kind != cv::detail::ArgKind::GOPAQUE);
 
     if (op_arg.kind == cv::detail::ArgKind::GOBJREF)
     {
@@ -1305,6 +1307,10 @@ void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_a
         {
             in_arg = GArg(m_res.slot<cv::detail::VectorRef>()[ref.id]);
         }
+        else if (ref.shape == GShape::GOPAQUE)
+        {
+            in_arg = GArg(m_res.slot<cv::detail::OpaqueRef>()[ref.id]);
+        }
     }
 }
 
index c8598d7..63ca9de 100644 (file)
@@ -129,7 +129,7 @@ class GFluidExecutable final: public GIslandExecutable
 
     std::vector<FluidAgent*> m_script;
 
-    using Magazine = detail::magazine<cv::gapi::own::Scalar, cv::detail::VectorRef>;
+    using Magazine = detail::magazine<cv::gapi::own::Scalar, cv::detail::VectorRef, cv::detail::OpaqueRef>;
     Magazine m_res;
 
     std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch)
index df6e347..3941642 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <opencv2/gapi/gcommon.hpp>
 #include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gopaque.hpp>
 #include <opencv2/gapi/util/any.hpp>
 #include <opencv2/gapi/gtype_traits.hpp>
 #include <opencv2/gapi/infer.hpp>
@@ -396,6 +397,10 @@ cv::GArg cv::gimpl::ie::GIEExecutable::packArg(const cv::GArg &arg) {
     //   (and constructed by either bindIn/Out or resetInternal)
     case GShape::GARRAY:  return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
 
+    // Note: .at() is intentional for GOpaque as object MUST be already there
+    //   (and constructed by either bindIn/Out or resetInternal)
+    case GShape::GOPAQUE:  return GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id));
+
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
index ea1e4d4..560d844 100644 (file)
@@ -113,7 +113,8 @@ cv::GArg cv::gimpl::GOCLExecutable::packArg(const GArg &arg)
     // FIXME: this check has to be done somewhere in compilation stage.
     GAPI_Assert(   arg.kind != cv::detail::ArgKind::GMAT
               && arg.kind != cv::detail::ArgKind::GSCALAR
-              && arg.kind != cv::detail::ArgKind::GARRAY);
+              && arg.kind != cv::detail::ArgKind::GARRAY
+              && arg.kind != cv::detail::ArgKind::GOPAQUE);
 
     if (arg.kind != cv::detail::ArgKind::GOBJREF)
     {
@@ -129,9 +130,12 @@ cv::GArg cv::gimpl::GOCLExecutable::packArg(const GArg &arg)
     {
     case GShape::GMAT:    return GArg(m_res.slot<cv::UMat>()[ref.id]);
     case GShape::GSCALAR: return GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
-        // Note: .at() is intentional for GArray as object MUST be already there
+    // Note: .at() is intentional for GArray as object MUST be already there
     //   (and constructed by either bindIn/Out or resetInternal)
     case GShape::GARRAY:  return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
+    // Note: .at() is intentional for GOpaque as object MUST be already there
+    //   (and constructed by either bindIn/Out or resetInternal)
+    case GShape::GOPAQUE:  return GArg(m_res.slot<cv::detail::OpaqueRef>().at(ref.id));
     default:
         util::throw_error(std::logic_error("Unsupported GShape type"));
         break;
index 4de99a1..fcd52c9 100644 (file)
@@ -276,7 +276,8 @@ cv::GArg cv::gimpl::GPlaidMLExecutable::packArg(const GArg &arg)
 {
     GAPI_Assert(   arg.kind != cv::detail::ArgKind::GMAT
               && arg.kind != cv::detail::ArgKind::GSCALAR
-              && arg.kind != cv::detail::ArgKind::GARRAY);
+              && arg.kind != cv::detail::ArgKind::GARRAY
+              && arg.kind != cv::detail::ArgKind::GOPAQUE);
 
     if (arg.kind != cv::detail::ArgKind::GOBJREF)
     {
index 9d48be6..1ee710c 100644 (file)
@@ -332,6 +332,9 @@ void cv::gimpl::GCompiler::validateInputMeta()
         case GProtoArg::index_of<cv::detail::GArrayU>():
             return util::holds_alternative<cv::GArrayDesc>(meta);
 
+        case GProtoArg::index_of<cv::detail::GOpaqueU>():
+            return util::holds_alternative<cv::GOpaqueDesc>(meta);
+
         default:
             GAPI_Assert(false);
         }
index bcc5047..04686e2 100644 (file)
@@ -16,13 +16,14 @@ namespace cv
 
 namespace gimpl
 {
-    // Union type for various user-defined type constructors (GArray<T>, etc)
+    // Union type for various user-defined type constructors (GArray<T>, GOpaque<T>, etc)
     // FIXME: Replace construct-only API with a more generic one
     //    (probably with bits of introspection)
     // Not required for non-user-defined types (GMat, GScalar, etc)
     using HostCtor = util::variant
     < util::monostate
     , detail::ConstructVec
+    , detail::ConstructOpaque
     >;
 
     using ConstVal = util::variant
index 86e008b..15e6a9f 100644 (file)
@@ -32,6 +32,7 @@ void dumpDot(const ade::Graph &g, std::ostream& os)
         {cv::GShape::GMAT,    "GMat"},
         {cv::GShape::GSCALAR, "GScalar"},
         {cv::GShape::GARRAY,  "GArray"},
+        {cv::GShape::GOPAQUE, "GOpaque"},
     };
 
     auto format_op_label  = [&gr](ade::NodeHandle nh) -> std::string {
diff --git a/modules/gapi/test/gapi_opaque_tests.cpp b/modules/gapi/test/gapi_opaque_tests.cpp
new file mode 100644 (file)
index 0000000..d2ab22f
--- /dev/null
@@ -0,0 +1,197 @@
+// 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) 2019 Intel Corporation
+
+#include "test_precomp.hpp"
+#include <string>
+#include <utility>
+
+namespace opencv_test
+{
+
+namespace ThisTest
+{
+using GPointOpaque = cv::GOpaque<cv::Point>;
+
+G_TYPED_KERNEL(GeneratePoint, <GPointOpaque(GMat)>, "test.opaque.gen_point")
+{
+    static GOpaqueDesc outMeta(const GMatDesc&) { return empty_gopaque_desc(); }
+};
+
+G_TYPED_KERNEL(FillMat, <GMat(cv::GOpaque<int>, int, int, cv::Size)>, "test.opaque.fill_mat")
+{
+    static GMatDesc outMeta(const GOpaqueDesc&, int depth, int chan, cv::Size size)
+    {
+        return cv::GMatDesc{depth, chan, size};
+    }
+};
+
+G_TYPED_KERNEL(PaintPoint, <GMat(GPointOpaque, int, int, cv::Size)>, "test.opaque.paint_point")
+{
+    static GMatDesc outMeta(const GOpaqueDesc&, int depth, int chan, cv::Size size)
+    {
+        return cv::GMatDesc{depth, chan, size};
+    }
+};
+
+struct MyCustomType{
+    int num;
+    std::string s;
+};
+
+using GOpaq2 = std::tuple<GOpaque<MyCustomType>,GOpaque<MyCustomType>>;
+
+G_TYPED_KERNEL_M(GenerateOpaque, <GOpaq2(GMat, GMat, std::string)>, "test.opaque.gen_point_multy")
+{
+    static std::tuple<GOpaqueDesc, GOpaqueDesc> outMeta(const GMatDesc&, const GMatDesc&, std::string)
+    {
+        return std::make_tuple(empty_gopaque_desc(), empty_gopaque_desc());
+    }
+};
+
+} // namespace ThisTest
+
+namespace
+{
+GAPI_OCV_KERNEL(OCVGeneratePoint, ThisTest::GeneratePoint)
+{
+    static void run(const cv::Mat&, cv::Point& out)
+    {
+        out = cv::Point(42, 42);
+    }
+};
+
+GAPI_OCV_KERNEL(OCVFillMat, ThisTest::FillMat)
+{
+    static void run(int a, int, int, cv::Size, cv::Mat& out)
+    {
+        out = cv::Scalar(a);
+    }
+};
+
+GAPI_OCV_KERNEL(OCVPaintPoint, ThisTest::PaintPoint)
+{
+    static void run(cv::Point a, int, int, cv::Size, cv::Mat& out)
+    {
+        out.at<uint8_t>(a) = 77;
+    }
+};
+
+GAPI_OCV_KERNEL(OCVGenerateOpaque, ThisTest::GenerateOpaque)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, const std::string& s,
+                    ThisTest::MyCustomType &out1, ThisTest::MyCustomType &out2)
+    {
+        out1.num = a.size().width * a.size().height;
+        out1.s = s;
+
+        out2.num = b.size().width * b.size().height;
+        auto s2 = s;
+        std::reverse(s2.begin(), s2.end());
+        out2.s = s2;
+    }
+};
+} // (anonymous namespace)
+
+TEST(GOpaque, TestOpaqueOut)
+{
+    cv::Mat input = cv::Mat(52, 52, CV_8U);
+    cv::Point point;
+
+    cv::GMat in;
+    auto out = ThisTest::GeneratePoint::on(in);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+    c.apply(cv::gin(input), cv::gout(point), cv::compile_args(cv::gapi::kernels<OCVGeneratePoint>()));
+
+    EXPECT_TRUE(point == cv::Point(42, 42));
+}
+
+TEST(GOpaque, TestOpaqueIn)
+{
+    cv::Size sz = {42, 42};
+    int depth = CV_8U;
+    int chan = 1;
+    cv::Mat mat = cv::Mat(sz, CV_MAKETYPE(depth, chan));
+    int fill = 0;
+
+    cv::GOpaque<int> in;
+    auto out = ThisTest::FillMat::on(in, depth, chan, sz);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+    c.apply(cv::gin(fill), cv::gout(mat), cv::compile_args(cv::gapi::kernels<OCVFillMat>()));
+
+    auto diff = cv::Mat(sz, CV_MAKETYPE(depth, chan), cv::Scalar(fill)) - mat;
+    EXPECT_EQ(cv::countNonZero(diff), 0);
+}
+
+TEST(GOpaque, TestOpaqueBetween)
+{
+    cv::Size sz = {50, 50};
+    int depth = CV_8U;
+    int chan = 1;
+    cv::Mat mat_in = cv::Mat::zeros(sz, CV_MAKETYPE(depth, chan));
+    cv::Mat mat_out = cv::Mat::zeros(sz, CV_MAKETYPE(depth, chan));
+
+    cv::GMat in, out;
+    auto betw = ThisTest::GeneratePoint::on(in);
+    out = ThisTest::PaintPoint::on(betw, depth, chan, sz);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+    c.apply(cv::gin(mat_in), cv::gout(mat_out), cv::compile_args(cv::gapi::kernels<OCVGeneratePoint, OCVPaintPoint>()));
+
+    int painted = mat_out.at<uint8_t>(42, 42);
+    EXPECT_EQ(painted, 77);
+}
+
+TEST(GOpaque, TestOpaqueCustomOut2)
+{
+    cv::Mat input1 = cv::Mat(52, 52, CV_8U);
+    cv::Mat input2 = cv::Mat(42, 42, CV_8U);
+    std::string str = "opaque";
+    std::string str2 = str;
+    std::reverse(str2.begin(), str2.end());
+
+    ThisTest::MyCustomType out1, out2;
+
+    cv::GMat in1, in2;
+    auto out = ThisTest::GenerateOpaque::on(in1, in2, str);
+
+    cv::GComputation c(cv::GIn(in1, in2), cv::GOut(std::get<0>(out), std::get<1>(out)));
+    c.apply(cv::gin(input1, input2), cv::gout(out1, out2), cv::compile_args(cv::gapi::kernels<OCVGenerateOpaque>()));
+
+    EXPECT_EQ(out1.num, input1.size().width * input1.size().height);
+    EXPECT_EQ(out1.s, str);
+
+    EXPECT_EQ(out2.num, input2.size().width * input2.size().height);
+    EXPECT_EQ(out2.s, str2);
+}
+
+TEST(GOpaque_OpaqueRef, TestMov)
+{
+    // Warning: this test is testing some not-very-public APIs
+    // Test how OpaqueRef's mov() (aka poor man's move()) is working.
+
+    using I = std::string;
+
+    std::string str = "this string must be long due to short string optimization";
+    const I gold(str);
+
+    I test = gold;
+    const char* ptr = test.data();
+
+    cv::detail::OpaqueRef ref(test);
+    cv::detail::OpaqueRef mov;
+    mov.reset<I>();
+
+    EXPECT_EQ(gold, ref.rref<I>());         // ref = gold
+
+    mov.mov(ref);
+    EXPECT_EQ(gold, mov.rref<I>());         // mov obtained the data
+    EXPECT_EQ(ptr,  mov.rref<I>().data());  // pointer is unchanged (same data)
+    EXPECT_EQ(test, ref.rref<I>());         // ref = test
+    EXPECT_NE(test, mov.rref<I>());         // ref lost the data
+}
+} // namespace opencv_test
index 01492e5..ad1a6aa 100644 (file)
@@ -22,10 +22,27 @@ using GMat2 = std::tuple<GMat, GMat>;
 using GMat3 = std::tuple<GMat, GMat, GMat>;
 using GScalar = cv::GScalar;
 template <typename T> using GArray = cv::GArray<T>;
+template <typename T> using GOpaque = cv::GOpaque<T>;
 
 using ArrayT = int;
 using WrongArrayT = char;
 
+struct CustomType{
+    cv::Mat mat;
+    int i;
+    void *v;
+    CustomType* next;
+};
+
+struct AnotherCustomType{
+    cv::Mat mat;
+    int i;
+    void *v;
+};
+
+using OpaqueT = CustomType;
+using WrongOpaqueT = AnotherCustomType;
+
 GAPI_TRANSFORM(gmat_in_gmat_out, <GMat(GMat)>, "gmat_in_gmat_out")
 {
     static GMat pattern(GMat) { return {}; }
@@ -92,6 +109,36 @@ GAPI_TRANSFORM(gmat_gsc_garray_in_gmat2_out, <GMat2(GMat, GScalar, GArray<ArrayT
     static GMat2 substitute(GMat, GScalar, GArray<ArrayT>) { return {}; }
 };
 
+GAPI_TRANSFORM(gop_in_gmat_out, <GMat(GOpaque<OpaqueT>)>, "gop_in_gmat_out")
+{
+    static GMat pattern(GOpaque<OpaqueT>) { return {}; }
+    static GMat substitute(GOpaque<OpaqueT>) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat_in_gop_out, <GOpaque<OpaqueT>(GMat)>, "gmat_in_gop_out")
+{
+    static GOpaque<OpaqueT> pattern(GMat) { return {}; }
+    static GOpaque<OpaqueT> substitute(GMat) { return {}; }
+};
+
+GAPI_TRANSFORM(gop_in_gscalar_out, <GScalar(GOpaque<OpaqueT>)>, "gop_in_gscalar_out")
+{
+    static GScalar pattern(GOpaque<OpaqueT>) { return {}; }
+    static GScalar substitute(GOpaque<OpaqueT>) { return {}; }
+};
+
+GAPI_TRANSFORM(gscalar_in_gop_out, <GOpaque<OpaqueT>(GScalar)>, "gscalar_in_gop_out")
+{
+    static GOpaque<OpaqueT> pattern(GScalar) { return {}; }
+    static GOpaque<OpaqueT> substitute(GScalar) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat_gsc_gopaque_in_gmat2_out, <GMat2(GMat, GScalar, GOpaque<OpaqueT>)>, "gmat_gsc_gopaque_in_gmat2_out")
+{
+    static GMat2 pattern(GMat, GScalar, GOpaque<OpaqueT>) { return {}; }
+    static GMat2 substitute(GMat, GScalar, GOpaque<OpaqueT>) { return {}; }
+};
+
 } // anonymous namespace
 
 TEST(KernelPackageTransform, CreatePackage)
@@ -108,10 +155,15 @@ TEST(KernelPackageTransform, CreatePackage)
         , garr_in_gscalar_out
         , gscalar_in_garr_out
         , gmat_gsc_garray_in_gmat2_out
+        , gop_in_gmat_out
+        , gmat_in_gop_out
+        , gop_in_gscalar_out
+        , gscalar_in_gop_out
+        , gmat_gsc_gopaque_in_gmat2_out
         >();
 
     auto tr = pkg.get_transformations();
-    EXPECT_EQ(11u, tr.size());
+    EXPECT_EQ(16u, tr.size());
 }
 
 TEST(KernelPackageTransform, Include)
@@ -164,6 +216,29 @@ TEST(KernelPackageTransform, gmat_gsc_garray_in_gmat2_out)
     check(tr.substitute());
 }
 
+TEST(KernelPackageTransform, gmat_gsc_gopaque_in_gmat2_out)
+{
+    auto tr = gmat_gsc_gopaque_in_gmat2_out::transformation();
+
+    auto check = [](const cv::GComputation &comp){
+        const auto &p = comp.priv();
+        EXPECT_EQ(3u, p.m_ins.size());
+        EXPECT_EQ(2u, p.m_outs.size());
+
+        EXPECT_TRUE(ProtoContainsT<GMat>(p.m_ins[0]));
+        EXPECT_TRUE(ProtoContainsT<GScalar>(p.m_ins[1]));
+        EXPECT_TRUE(ProtoContainsT<cv::detail::GOpaqueU>(p.m_ins[2]));
+        EXPECT_TRUE(cv::util::get<cv::detail::GOpaqueU>(p.m_ins[2]).holds<OpaqueT>());
+        EXPECT_FALSE(cv::util::get<cv::detail::GOpaqueU>(p.m_ins[2]).holds<WrongOpaqueT>());
+
+        EXPECT_TRUE(ProtoContainsT<GMat>(p.m_outs[0]));
+        EXPECT_TRUE(ProtoContainsT<GMat>(p.m_outs[1]));
+    };
+
+    check(tr.pattern());
+    check(tr.substitute());
+}
+
 namespace
 {
     template<typename ArgT>
@@ -176,7 +251,17 @@ namespace
     }
 
     template<typename ArgT>
-    typename std::enable_if<(cv::detail::GTypeTraits<ArgT>::kind != cv::detail::ArgKind::GARRAY), void>::type
+    typename std::enable_if<(cv::detail::GTypeTraits<ArgT>::kind == cv::detail::ArgKind::GOPAQUE), void>::type
+    arg_check(const cv::GProtoArg &arg)
+    {
+        EXPECT_TRUE(ProtoContainsT<cv::detail::GOpaqueU>(arg));
+        EXPECT_TRUE(cv::util::get<cv::detail::GOpaqueU>(arg).holds<OpaqueT>());
+        EXPECT_FALSE(cv::util::get<cv::detail::GOpaqueU>(arg).holds<WrongOpaqueT>());
+    }
+
+    template<typename ArgT>
+    typename std::enable_if<(cv::detail::GTypeTraits<ArgT>::kind != cv::detail::ArgKind::GARRAY &&
+                             cv::detail::GTypeTraits<ArgT>::kind != cv::detail::ArgKind::GOPAQUE), void>::type
     arg_check(const cv::GProtoArg &arg)
     {
         EXPECT_TRUE(ProtoContainsT<ArgT>(arg));
@@ -242,4 +327,24 @@ TEST(KernelPackageTransform, gscalar_in_garr_out)
     transformTest<gscalar_in_garr_out, GScalar, GArray<ArrayT>>();
 }
 
+TEST(KernelPackageTransform, gop_in_gmat_out)
+{
+    transformTest<gop_in_gmat_out, GOpaque<OpaqueT>, GMat>();
+}
+
+TEST(KernelPackageTransform, gmat_in_gop_out)
+{
+    transformTest<gmat_in_gop_out, GMat, GOpaque<OpaqueT>>();
+}
+
+TEST(KernelPackageTransform, gop_in_gscalar_out)
+{
+    transformTest<gop_in_gscalar_out, GOpaque<OpaqueT>, GScalar>();
+}
+
+TEST(KernelPackageTransform, gscalar_in_gop_out)
+{
+    transformTest<gscalar_in_gop_out, GScalar, GOpaque<OpaqueT>>();
+}
+
 } // namespace opencv_test
index c7da788..aad1123 100644 (file)
@@ -37,6 +37,10 @@ using GArg_Test_Types = ::testing::Types
    , Expected<cv::GArray<float>,        cv::detail::ArgKind::GARRAY>
    , Expected<cv::GArray<cv::Point>,    cv::detail::ArgKind::GARRAY>
    , Expected<cv::GArray<cv::Rect>,     cv::detail::ArgKind::GARRAY>
+   , Expected<cv::GOpaque<int>,         cv::detail::ArgKind::GOPAQUE>
+   , Expected<cv::GOpaque<float>,       cv::detail::ArgKind::GOPAQUE>
+   , Expected<cv::GOpaque<cv::Point>,   cv::detail::ArgKind::GOPAQUE>
+   , Expected<cv::GOpaque<cv::Rect>,    cv::detail::ArgKind::GOPAQUE>
 
  // Built-in types
    , Expected<int,                      cv::detail::ArgKind::OPAQUE_VAL>
@@ -85,6 +89,11 @@ TEST(GArg, HasWrap)
                   "GArray<int> has custom marshalling logic");
     static_assert(cv::detail::has_custom_wrap<cv::GArray<std::string> >::value,
                   "GArray<int> has custom marshalling logic");
+
+    static_assert(cv::detail::has_custom_wrap<cv::GOpaque<int> >::value,
+                  "GOpaque<int> has custom marshalling logic");
+    static_assert(cv::detail::has_custom_wrap<cv::GOpaque<std::string> >::value,
+                  "GOpaque<int> has custom marshalling logic");
 }
 
 TEST(GArg, GArrayU)
@@ -97,5 +106,15 @@ TEST(GArg, GArrayU)
     EXPECT_NO_THROW(arg2.get<cv::detail::GArrayU>());
 }
 
+TEST(GArg, GOpaqueU)
+{
+    // Placing a GOpaque<T> into GArg automatically strips it to GOpaqueU
+    cv::GArg arg1 = cv::GArg(cv::GOpaque<int>());
+    EXPECT_NO_THROW(arg1.get<cv::detail::GOpaqueU>());
+
+    cv::GArg arg2 = cv::GArg(cv::GOpaque<cv::Point>());
+    EXPECT_NO_THROW(arg2.get<cv::detail::GOpaqueU>());
+}
+
 
 } // namespace opencv_test