// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_GCPUKERNEL_HPP
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/own/convert.hpp> //to_ocv
#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+#include <opencv2/gapi/util/util.hpp>
// FIXME: namespace scheme for backends?
namespace cv {
} // namespace detail
template<class Impl, class K>
-class GCPUKernelImpl: public detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
+class GCPUKernelImpl: public cv::detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
+ public cv::detail::KernelTag
{
using P = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_KERNEL_HPP
template<class Impl, class K, bool UseScratch>
-class GFluidKernelImpl
+class GFluidKernelImpl : public cv::detail::KernelTag
{
static const int LPI = 1;
static const auto Kind = GFluidKernel::Kind::Filter;
{
static const char* tag() { return ""; };
};
+
+ // These structures are tags which separate kernels and transformations
+ struct KernelTag
+ {};
+ struct TransformTag
+ {};
}
// This definition is here because it is reused by both public(?) and internal
// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_GCOMPOUNDKERNEL_HPP
}
};
-// Kernel may return one object(GMat, GScalar) or a tuple of objects.
-// This helper is needed to cast return value to the same form(tuple)
-template<typename>
-struct tuple_wrap_helper;
-
-template<typename T> struct tuple_wrap_helper
-{
- static std::tuple<T> get(T&& obj) { return std::make_tuple(std::move(obj)); }
-};
-
-template<typename... Objs>
-struct tuple_wrap_helper<std::tuple<Objs...>>
-{
- static std::tuple<Objs...> get(std::tuple<Objs...>&& objs) { return std::forward<std::tuple<Objs...>>(objs); }
-};
-
template<typename, typename, typename>
struct GCompoundCallHelper;
};
template<class Impl, class K>
-class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
+class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
+ public cv::detail::KernelTag
{
using P = cv::detail::GCompoundCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_GKERNEL_HPP
#include <opencv2/gapi/gmetaarg.hpp> // GMetaArg
#include <opencv2/gapi/gtype_traits.hpp> // GTypeTraits
#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+#include <opencv2/gapi/gtransform.hpp>
namespace cv {
// GKernelType and GKernelTypeM are base classes which implement typed ::on()
// method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels
//
-// G_TYPED_KERNEL and G_TYPED_KERNEK_M macros inherit user classes from GKernelType and
+// G_TYPED_KERNEL and G_TYPED_KERNEL_M macros inherit user classes from GKernelType and
// GKernelTypeM respectively.
template<typename K, typename... R, typename... Args>
class GKernelTypeM<K, std::function<std::tuple<R...>(Args...)> >:
- public detail::MetaHelper<K, std::tuple<Args...>, std::tuple<R...> >
+ public detail::MetaHelper<K, std::tuple<Args...>, std::tuple<R...>>
{
template<int... IIs>
static std::tuple<R...> yield(cv::GCall &call, detail::Seq<IIs...>)
template<typename K, typename R, typename... Args>
class GKernelType<K, std::function<R(Args...)> >:
- public detail::MetaHelper<K, std::tuple<Args...>, R >
+ public detail::MetaHelper<K, std::tuple<Args...>, R>
{
public:
using InArgs = std::tuple<Args...>;
#define G_TYPED_KERNEL_M(Class, API, Id) \
G_ID_HELPER_BODY(Class, Id) \
struct Class final: public cv::GKernelTypeM<Class, std::function API >, \
- public detail::G_ID_HELPER_CLASS(Class) \
+ public detail::G_ID_HELPER_CLASS(Class)
// {body} is to be defined by user
namespace cv
// FIXME: Hide implementation
/**
* @brief A container class for heterogeneous kernel
- * implementation collections.
+ * implementation collections and graph transformations.
*
* GKernelPackage is a special container class which stores kernel
- * _implementations_. Objects of this class are created and passed
- * to cv::GComputation::compile() to specify which kernels to use
- * in the compiled graph. GKernelPackage may contain kernels of
+ * _implementations_ and graph _transformations_. Objects of this class
+ * are created and passed to cv::GComputation::compile() to specify
+ * which kernels to use and which transformations to apply in the
+ * compiled graph. GKernelPackage may contain kernels of
* different backends, e.g. be heterogeneous.
*
* The most easy way to create a kernel package is to use function
* with an empty package (created with the default constructor)
* and then by populating it with kernels via call to
* GKernelPackage::include(). Note this method is also a template
- * one since G-API kernel implementations are _types_, not objects.
+ * one since G-API kernel and transformation implementations are _types_,
+ * not objects.
*
* Finally, two kernel packages can be combined into a new one
* with function cv::gapi::combine().
/// @private
M m_id_kernels;
+ /// @private
+ std::vector<GTransform> m_transformations;
+
protected:
/// @private
// Check if package contains ANY implementation of a kernel API
// Remove ALL implementations of the given API (identified by ID)
void removeAPI(const std::string &id);
+ /// @private
+ // Partial include() specialization for kernels
+ template <typename KImpl>
+ typename std::enable_if<(std::is_base_of<detail::KernelTag, KImpl>::value), void>::type
+ includeHelper()
+ {
+ auto backend = KImpl::backend();
+ auto kernel_id = KImpl::API::id();
+ auto kernel_impl = GKernelImpl{KImpl::kernel()};
+ removeAPI(kernel_id);
+
+ m_id_kernels[kernel_id] = std::make_pair(backend, kernel_impl);
+ }
+
+ /// @private
+ // Partial include() specialization for transformations
+ template <typename TImpl>
+ typename std::enable_if<(std::is_base_of<detail::TransformTag, TImpl>::value), void>::type
+ includeHelper()
+ {
+ m_transformations.emplace_back(TImpl::transformation());
+ }
+
public:
/**
- * @brief Returns total number of kernels in the package
- * (across all backends included)
+ * @brief Returns total number of kernels
+ * in the package (across all backends included)
*
* @return a number of kernels in the package
*/
std::size_t size() const;
/**
+ * @brief Returns vector of transformations included in the package
+ *
+ * @return vector of transformations included in the package
+ */
+ const std::vector<GTransform>& get_transformations() const;
+
+ /**
* @brief Test if a particular kernel _implementation_ KImpl is
* included in this kernel package.
*
* @sa includesAPI()
*
+ * @note cannot be applied to transformations
+ *
* @return true if there is such kernel, false otherwise.
*/
template<typename KImpl>
bool includes() const
{
+ static_assert(std::is_base_of<detail::KernelTag, KImpl>::value,
+ "includes() can be applied to kernels only");
+
auto kernel_it = m_id_kernels.find(KImpl::API::id());
return kernel_it != m_id_kernels.end() &&
kernel_it->second.first == KImpl::backend();
// FIXME: No overwrites allowed?
/**
- * @brief Put a new kernel implementation KImpl into package.
+ * @brief Put a new kernel implementation or a new transformation
+ * KImpl into the package.
*/
template<typename KImpl>
void include()
{
- auto backend = KImpl::backend();
- auto kernel_id = KImpl::API::id();
- auto kernel_impl = GKernelImpl{KImpl::kernel()};
- removeAPI(kernel_id);
-
- m_id_kernels[kernel_id] = std::make_pair(backend, kernel_impl);
+ includeHelper<KImpl>();
}
/**
/**
* @brief Create a kernel package object containing kernels
- * specified in variadic template argument.
+ * and transformations specified in variadic template argument.
*
- * In G-API, kernel implementations are _types_. Every backend has
- * its own kernel API (like GAPI_OCV_KERNEL() and
+ * In G-API, kernel implementations and transformations are _types_.
+ * Every backend has its own kernel API (like GAPI_OCV_KERNEL() and
* GAPI_FLUID_KERNEL()) but all of that APIs define a new type for
* each kernel implementation.
*
* Use this function to pass kernel implementations (defined in
- * either way) to the system. Example:
+ * either way) and transformations to the system. Example:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet
*
*/
template<typename... KK> GKernelPackage kernels()
{
+ // FIXME: currently there is no check that transformations' signatures are unique
+ // and won't be any intersection in graph compilation stage
+ static_assert(detail::all_unique<typename KK::API...>::value, "Kernels API must be unique");
+
GKernelPackage pkg;
// For those who wonder - below is a trick to call a number of
// Just note that `f(),a` always equals to `a` (with f() called!)
// and parentheses are used to hide function call in the expanded sequence.
// Leading 0 helps to handle case when KK is an empty list (kernels<>()).
-
- static_assert(detail::all_unique<typename KK::API...>::value, "Kernels API must be unique");
int unused[] = { 0, (pkg.include<KK>(), 0)... };
cv::util::suppress_unused_warning(unused);
return pkg;
--- /dev/null
+// 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_GTRANSFORM_HPP
+#define OPENCV_GAPI_GTRANSFORM_HPP
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <opencv2/gapi/gcommon.hpp>
+#include <opencv2/gapi/util/util.hpp>
+#include <opencv2/gapi/garg.hpp>
+#include <opencv2/gapi/gtype_traits.hpp>
+#include <opencv2/gapi/util/compiler_hints.hpp>
+
+namespace cv
+{
+
+struct GAPI_EXPORTS GTransform
+{
+ using F = std::function<GArgs(const GArgs &)>;
+
+ std::string description;
+ F pattern;
+ F substitute;
+
+ GTransform(const std::string& d, const F &p, const F &s) : description(d), pattern(p), substitute(s){};
+};
+
+namespace detail
+{
+
+template <typename, typename, typename>
+struct TransHelper;
+
+template <typename K, typename... Ins, typename Out>
+struct TransHelper<K, std::tuple<Ins...>, Out>
+{
+ template <typename Callable, int... IIs, int... OIs>
+ static GArgs invoke(Callable f, const GArgs &in_args, Seq<IIs...>, Seq<OIs...>)
+ {
+ const auto r = tuple_wrap_helper<Out>::get(f(in_args.at(IIs).template get<Ins>()...));
+ return GArgs{GArg(std::get<OIs>(r))...};
+ }
+
+ static GArgs get_pattern(const GArgs &in_args)
+ {
+ return invoke(K::pattern, in_args, typename MkSeq<sizeof...(Ins)>::type(),
+ typename MkSeq<std::tuple_size<typename tuple_wrap_helper<Out>::type>::value>::type());
+ }
+ static GArgs get_substitute(const GArgs &in_args)
+ {
+ return invoke(K::substitute, in_args, typename MkSeq<sizeof...(Ins)>::type(),
+ typename MkSeq<std::tuple_size<typename tuple_wrap_helper<Out>::type>::value>::type());
+ }
+};
+} // namespace detail
+
+template <typename, typename>
+class GTransformImpl;
+
+template <typename K, typename R, typename... Args>
+class GTransformImpl<K, std::function<R(Args...)>> : public cv::detail::TransHelper<K, std::tuple<Args...>, R>,
+ public cv::detail::TransformTag
+{
+public:
+ // FIXME: currently there is no check that transformations' signatures are unique
+ // and won't be any intersection in graph compilation stage
+ using API = K;
+
+ static GTransform transformation()
+ {
+ return GTransform(K::descr(), &K::get_pattern, &K::get_substitute);
+ }
+};
+} // namespace cv
+
+#define G_DESCR_HELPER_CLASS(Class) Class##DescrHelper
+
+#define G_DESCR_HELPER_BODY(Class, Descr) \
+ namespace detail \
+ { \
+ struct G_DESCR_HELPER_CLASS(Class) \
+ { \
+ static constexpr const char *descr() { return Descr; }; \
+ }; \
+ }
+
+#define GAPI_TRANSFORM(Class, API, Descr) \
+ G_DESCR_HELPER_BODY(Class, Descr) \
+ struct Class final : public cv::GTransformImpl<Class, std::function API>, \
+ public detail::G_DESCR_HELPER_CLASS(Class)
+
+#endif // OPENCV_GAPI_GTRANSFORM_HPP
// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_GOCLKERNEL_HPP
} // namespace detail
template<class Impl, class K>
-class GOCLKernelImpl: public detail::OCLCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
+class GOCLKernelImpl: public cv::detail::OCLCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
+ public cv::detail::KernelTag
{
using P = detail::OCLCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
// 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-2019 Intel Corporation
#ifndef OPENCV_GAPI_UTIL_HPP
#define OPENCV_GAPI_UTIL_HPP
-#include <utility> // std::tuple
+#include <tuple>
// \cond HIDDEN_SYMBOLS
// This header file contains some generic utility functions which are
template <typename T1, typename... Ts>
struct all_unique<T1, Ts...> : std::integral_constant<bool, !contains<T1, Ts...>::value &&
all_unique<Ts...>::value> {};
+
+ template<typename>
+ struct tuple_wrap_helper;
+
+ template<typename T> struct tuple_wrap_helper
+ {
+ using type = std::tuple<T>;
+ static type get(T&& obj) { return std::make_tuple(std::move(obj)); }
+ };
+
+ template<typename... Objs>
+ struct tuple_wrap_helper<std::tuple<Objs...>>
+ {
+ using type = std::tuple<Objs...>;
+ static type get(std::tuple<Objs...>&& objs) { return std::forward<std::tuple<Objs...>>(objs); }
+ };
} // namespace detail
} // namespace cv
// 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-2019 Intel Corporation
#include "precomp.hpp"
return m_id_kernels.size();
}
+const std::vector<cv::GTransform> &cv::gapi::GKernelPackage::get_transformations() const
+{
+ return m_transformations;
+}
+
cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs,
const GKernelPackage &rhs)
{
result.m_id_kernels.emplace(kernel.first, kernel.second);
}
}
+ for (const auto &transforms : lhs.m_transformations){
+ result.m_transformations.push_back(transforms);
+ }
return result;
}
// 1. Get GCompoundKernel implementation
// 2. Create GCompoundContext
// 3. Run GCompoundKernel with GCompoundContext
- // 4. Build subgraph from imputs/outputs GCompoundKernel
+ // 4. Build subgraph from inputs/outputs GCompoundKernel
// 5. Replace compound node to subgraph
void expand(ade::Graph& g, ade::NodeHandle nh, const ImplInfo& impl_info)
--- /dev/null
+// 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 <tuple>
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/gtransform.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+using GMat = cv::GMat;
+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>;
+
+GAPI_TRANSFORM(gmat_in_gmat_out, <GMat(GMat)>, "gmat_in_gmat_out")
+{
+ static GMat pattern(GMat) { return {}; }
+ static GMat substitute(GMat) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat2_in_gmat_out, <GMat(GMat, GMat)>, "gmat2_in_gmat_out")
+{
+ static GMat pattern(GMat, GMat) { return {}; }
+ static GMat substitute(GMat, GMat) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat2_in_gmat3_out, <GMat3(GMat, GMat)>, "gmat2_in_gmat3_out")
+{
+ static GMat3 pattern(GMat, GMat) { return {}; }
+ static GMat3 substitute(GMat, GMat) { return {}; }
+};
+
+GAPI_TRANSFORM(gmatp_in_gmatp_out, <GMatP(GMatP)>, "gmatp_in_gmatp_out")
+{
+ static GMatP pattern(GMatP) { return {}; }
+ static GMatP substitute(GMatP) { return {}; }
+};
+
+GAPI_TRANSFORM(gsc_in_gmat_out, <GMat(GScalar)>, "gsc_in_gmat_out")
+{
+ static GMat pattern(GScalar) { return {}; }
+ static GMat substitute(GScalar) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat_in_gsc_out, <GScalar(GMat)>, "gmat_in_gsc_out")
+{
+ static GScalar pattern(GMat) { return {}; }
+ static GScalar substitute(GMat) { return {}; }
+};
+
+GAPI_TRANSFORM(garr_in_gmat_out, <GMat(GArray<int>)>, "garr_in_gmat_out")
+{
+ static GMat pattern(GArray<int>) { return {}; }
+ static GMat substitute(GArray<int>) { return {}; }
+};
+
+GAPI_TRANSFORM(gmat_in_garr_out, <GArray<int>(GMat)>, "gmat_in_garr_out")
+{
+ static GArray<int> pattern(GMat) { return {}; }
+ static GArray<int> substitute(GMat) { return {}; }
+};
+
+} // anonymous namespace
+
+TEST(KernelPackageTransform, CreatePackage)
+{
+ auto pkg = cv::gapi::kernels
+ < gmat_in_gmat_out
+ , gmat2_in_gmat_out
+ , gmat2_in_gmat3_out
+ , gsc_in_gmat_out
+ , gmat_in_gsc_out
+ >();
+
+ auto tr = pkg.get_transformations();
+ EXPECT_EQ(5u, tr.size());
+}
+
+TEST(KernelPackageTransform, Include)
+{
+ cv::gapi::GKernelPackage pkg;
+ pkg.include<gmat_in_gmat_out>();
+ pkg.include<gmat2_in_gmat_out>();
+ pkg.include<gmat2_in_gmat3_out>();
+ auto tr = pkg.get_transformations();
+ EXPECT_EQ(3u, tr.size());
+}
+
+TEST(KernelPackageTransform, Combine)
+{
+ auto pkg1 = cv::gapi::kernels<gmat_in_gmat_out>();
+ auto pkg2 = cv::gapi::kernels<gmat2_in_gmat_out>();
+ auto pkg_comb = cv::gapi::combine(pkg1, pkg2);
+ auto tr = pkg_comb.get_transformations();
+ EXPECT_EQ(2u, tr.size());
+}
+
+TEST(KernelPackageTransform, Pattern)
+{
+ auto tr = gmat2_in_gmat3_out::transformation();
+ GMat a, b;
+ auto pattern = tr.pattern({cv::GArg(a), cv::GArg(b)});
+
+ // return type of '2gmat_in_gmat3_out' is GMat3
+ EXPECT_EQ(3u, pattern.size());
+ for (const auto& p : pattern)
+ {
+ EXPECT_NO_THROW(p.get<GMat>());
+ }
+}
+
+TEST(KernelPackageTransform, Substitute)
+{
+ auto tr = gmat2_in_gmat3_out::transformation();
+ GMat a, b;
+ auto subst = tr.substitute({cv::GArg(a), cv::GArg(b)});
+
+ EXPECT_EQ(3u, subst.size());
+ for (const auto& s : subst)
+ {
+ EXPECT_NO_THROW(s.get<GMat>());
+ }
+}
+
+template <typename Transformation, typename InType, typename OutType>
+static void transformTest()
+{
+ auto tr = Transformation::transformation();
+ InType in;
+ auto pattern = tr.pattern({cv::GArg(in)});
+ auto subst = tr.substitute({cv::GArg(in)});
+
+ EXPECT_EQ(1u, pattern.size());
+ EXPECT_EQ(1u, subst.size());
+
+ auto checkOut = [](GArg& garg) {
+ EXPECT_TRUE(garg.kind == cv::detail::GTypeTraits<OutType>::kind);
+ EXPECT_NO_THROW(garg.get<OutType>());
+ };
+
+ checkOut(pattern[0]);
+ checkOut(subst[0]);
+}
+
+TEST(KernelPackageTransform, GMat)
+{
+ transformTest<gmat_in_gmat_out, GMat, GMat>();
+}
+
+TEST(KernelPackageTransform, GMatP)
+{
+ transformTest<gmatp_in_gmatp_out, GMatP, GMatP>();
+}
+
+TEST(KernelPackageTransform, GScalarIn)
+{
+ transformTest<gsc_in_gmat_out, GScalar, GMat>();
+}
+
+TEST(KernelPackageTransform, GScalarOut)
+{
+ transformTest<gmat_in_gsc_out, GMat, GScalar>();
+}
+
+TEST(KernelPackageTransform, DISABLED_GArrayIn)
+{
+ transformTest<garr_in_gmat_out, GArray<int>, GMat>();
+}
+
+TEST(KernelPackageTransform, DISABLED_GArrayOut)
+{
+ transformTest<gmat_in_garr_out, GMat, GArray<int>>();
+}
+
+} // namespace opencv_test