From 7f9a9f2a09c1ae0a9e994e26aaf355bf5bc382ea Mon Sep 17 00:00:00 2001 From: Alexey Smirnov Date: Mon, 17 Jun 2019 16:26:28 +0300 Subject: [PATCH] Merge pull request #14648 from smirnov-alexey:as/gapi_transform * Introduce GAPI_TRANSFORM initial interface Comes along with simple tests and kernel package changes * Fix documentation and adjust combine() function * Fix stuff after rebasing on master * Remove redundant functionality * Refactoring according to review feedback provided * Fixes according to review feedback * Reconsider transformations return and fix a warning * Fixes from code review * Add a new simple test * Cleanup, added tests on GScalar, GMatP, GArray --- .../include/opencv2/gapi/cpu/gcpukernel.hpp | 6 +- .../opencv2/gapi/fluid/gfluidkernel.hpp | 4 +- modules/gapi/include/opencv2/gapi/gcommon.hpp | 6 + .../include/opencv2/gapi/gcompoundkernel.hpp | 21 +- modules/gapi/include/opencv2/gapi/gkernel.hpp | 89 ++++++--- .../gapi/include/opencv2/gapi/gtransform.hpp | 98 ++++++++++ .../include/opencv2/gapi/ocl/goclkernel.hpp | 5 +- .../gapi/include/opencv2/gapi/util/util.hpp | 20 +- modules/gapi/src/api/gkernel.cpp | 10 +- modules/gapi/src/compiler/passes/kernels.cpp | 2 +- modules/gapi/test/gapi_transform_tests.cpp | 183 ++++++++++++++++++ 11 files changed, 391 insertions(+), 53 deletions(-) create mode 100644 modules/gapi/include/opencv2/gapi/gtransform.hpp create mode 100644 modules/gapi/test/gapi_transform_tests.cpp diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index 95979422b4..4205776ebe 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -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-2019 Intel Corporation #ifndef OPENCV_GAPI_GCPUKERNEL_HPP @@ -19,6 +19,7 @@ #include #include //to_ocv #include //suppress_unused_warning +#include // FIXME: namespace scheme for backends? namespace cv { @@ -258,7 +259,8 @@ struct OCVCallHelper, std::tuple > } // namespace detail template -class GCPUKernelImpl: public detail::OCVCallHelper +class GCPUKernelImpl: public cv::detail::OCVCallHelper, + public cv::detail::KernelTag { using P = detail::OCVCallHelper; diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp index b6adf9e115..1d8bfd80f0 100644 --- a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -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-2019 Intel Corporation #ifndef OPENCV_GAPI_FLUID_KERNEL_HPP @@ -275,7 +275,7 @@ struct FluidCallHelper, std::tuple, UseScratch template -class GFluidKernelImpl +class GFluidKernelImpl : public cv::detail::KernelTag { static const int LPI = 1; static const auto Kind = GFluidKernel::Kind::Filter; diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp index e97e10e922..dac640a2f7 100644 --- a/modules/gapi/include/opencv2/gapi/gcommon.hpp +++ b/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -29,6 +29,12 @@ namespace detail { 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 diff --git a/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp b/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp index f4c02344ee..7d960cd6fa 100644 --- a/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp @@ -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-2019 Intel Corporation #ifndef OPENCV_GAPI_GCOMPOUNDKERNEL_HPP @@ -65,22 +65,6 @@ template struct get_compound_in> } }; -// 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 -struct tuple_wrap_helper; - -template struct tuple_wrap_helper -{ - static std::tuple get(T&& obj) { return std::make_tuple(std::move(obj)); } -}; - -template -struct tuple_wrap_helper> -{ - static std::tuple get(std::tuple&& objs) { return std::forward>(objs); } -}; - template struct GCompoundCallHelper; @@ -104,7 +88,8 @@ struct GCompoundCallHelper, std::tuple > }; template -class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper +class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper, + public cv::detail::KernelTag { using P = cv::detail::GCompoundCallHelper; diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp index db9ac7606b..b8d1dbbea4 100644 --- a/modules/gapi/include/opencv2/gapi/gkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -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-2019 Intel Corporation #ifndef OPENCV_GAPI_GKERNEL_HPP @@ -22,6 +22,7 @@ #include // GMetaArg #include // GTypeTraits #include //suppress_unused_warning +#include namespace cv { @@ -170,12 +171,12 @@ namespace detail // 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 class GKernelTypeM(Args...)> >: - public detail::MetaHelper, std::tuple > + public detail::MetaHelper, std::tuple> { template static std::tuple yield(cv::GCall &call, detail::Seq) @@ -199,7 +200,7 @@ template class GKernelType; template class GKernelType >: - public detail::MetaHelper, R > + public detail::MetaHelper, R> { public: using InArgs = std::tuple; @@ -240,7 +241,7 @@ public: #define G_TYPED_KERNEL_M(Class, API, Id) \ G_ID_HELPER_BODY(Class, Id) \ struct Class final: public cv::GKernelTypeM, \ - public detail::G_ID_HELPER_CLASS(Class) \ + public detail::G_ID_HELPER_CLASS(Class) // {body} is to be defined by user namespace cv @@ -296,12 +297,13 @@ namespace gapi { // 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 @@ -313,7 +315,8 @@ namespace gapi { * 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(). @@ -327,6 +330,9 @@ namespace gapi { /// @private M m_id_kernels; + /// @private + std::vector m_transformations; + protected: /// @private // Check if package contains ANY implementation of a kernel API @@ -337,26 +343,61 @@ namespace gapi { // Remove ALL implementations of the given API (identified by ID) void removeAPI(const std::string &id); + /// @private + // Partial include() specialization for kernels + template + typename std::enable_if<(std::is_base_of::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 std::enable_if<(std::is_base_of::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& 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 bool includes() const { + static_assert(std::is_base_of::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(); @@ -417,17 +458,13 @@ namespace gapi { // 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 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(); } /** @@ -452,15 +489,15 @@ namespace gapi { /** * @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 * @@ -470,6 +507,10 @@ namespace gapi { */ template 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::value, "Kernels API must be unique"); + GKernelPackage pkg; // For those who wonder - below is a trick to call a number of @@ -478,8 +519,6 @@ namespace gapi { // 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::value, "Kernels API must be unique"); int unused[] = { 0, (pkg.include(), 0)... }; cv::util::suppress_unused_warning(unused); return pkg; diff --git a/modules/gapi/include/opencv2/gapi/gtransform.hpp b/modules/gapi/include/opencv2/gapi/gtransform.hpp new file mode 100644 index 0000000000..e8ce161a24 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/gtransform.hpp @@ -0,0 +1,98 @@ +// 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 +#include +#include + +#include +#include +#include +#include +#include + +namespace cv +{ + +struct GAPI_EXPORTS GTransform +{ + using F = std::function; + + 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 +struct TransHelper; + +template +struct TransHelper, Out> +{ + template + static GArgs invoke(Callable f, const GArgs &in_args, Seq, Seq) + { + const auto r = tuple_wrap_helper::get(f(in_args.at(IIs).template get()...)); + return GArgs{GArg(std::get(r))...}; + } + + static GArgs get_pattern(const GArgs &in_args) + { + return invoke(K::pattern, in_args, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } + static GArgs get_substitute(const GArgs &in_args) + { + return invoke(K::substitute, in_args, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } +}; +} // namespace detail + +template +class GTransformImpl; + +template +class GTransformImpl> : public cv::detail::TransHelper, 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, \ + public detail::G_DESCR_HELPER_CLASS(Class) + +#endif // OPENCV_GAPI_GTRANSFORM_HPP diff --git a/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp index ea2cda0bf8..69274921e8 100644 --- a/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp @@ -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-2019 Intel Corporation #ifndef OPENCV_GAPI_GOCLKERNEL_HPP @@ -226,7 +226,8 @@ struct OCLCallHelper, std::tuple > } // namespace detail template -class GOCLKernelImpl: public detail::OCLCallHelper +class GOCLKernelImpl: public cv::detail::OCLCallHelper, + public cv::detail::KernelTag { using P = detail::OCLCallHelper; diff --git a/modules/gapi/include/opencv2/gapi/util/util.hpp b/modules/gapi/include/opencv2/gapi/util/util.hpp index a62c0c5cf9..f68b738cdc 100644 --- a/modules/gapi/include/opencv2/gapi/util/util.hpp +++ b/modules/gapi/include/opencv2/gapi/util/util.hpp @@ -2,13 +2,13 @@ // 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 // std::tuple +#include // \cond HIDDEN_SYMBOLS // This header file contains some generic utility functions which are @@ -97,6 +97,22 @@ namespace detail template struct all_unique : std::integral_constant::value && all_unique::value> {}; + + template + struct tuple_wrap_helper; + + template struct tuple_wrap_helper + { + using type = std::tuple; + static type get(T&& obj) { return std::make_tuple(std::move(obj)); } + }; + + template + struct tuple_wrap_helper> + { + using type = std::tuple; + static type get(std::tuple&& objs) { return std::forward>(objs); } + }; } // namespace detail } // namespace cv diff --git a/modules/gapi/src/api/gkernel.cpp b/modules/gapi/src/api/gkernel.cpp index ef682e80ae..6993e95807 100644 --- a/modules/gapi/src/api/gkernel.cpp +++ b/modules/gapi/src/api/gkernel.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-2019 Intel Corporation #include "precomp.hpp" @@ -50,6 +50,11 @@ std::size_t cv::gapi::GKernelPackage::size() const return m_id_kernels.size(); } +const std::vector &cv::gapi::GKernelPackage::get_transformations() const +{ + return m_transformations; +} + cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs, const GKernelPackage &rhs) { @@ -66,6 +71,9 @@ cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs, result.m_id_kernels.emplace(kernel.first, kernel.second); } } + for (const auto &transforms : lhs.m_transformations){ + result.m_transformations.push_back(transforms); + } return result; } diff --git a/modules/gapi/src/compiler/passes/kernels.cpp b/modules/gapi/src/compiler/passes/kernels.cpp index e00deaab29..f5f0098cae 100644 --- a/modules/gapi/src/compiler/passes/kernels.cpp +++ b/modules/gapi/src/compiler/passes/kernels.cpp @@ -35,7 +35,7 @@ namespace // 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) diff --git a/modules/gapi/test/gapi_transform_tests.cpp b/modules/gapi/test/gapi_transform_tests.cpp new file mode 100644 index 0000000000..f946499a7f --- /dev/null +++ b/modules/gapi/test/gapi_transform_tests.cpp @@ -0,0 +1,183 @@ +// 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 + +#include "test_precomp.hpp" +#include "opencv2/gapi/gtransform.hpp" + +namespace opencv_test +{ + +namespace +{ +using GMat = cv::GMat; +using GMat2 = std::tuple; +using GMat3 = std::tuple; +using GScalar = cv::GScalar; +template using GArray = cv::GArray; + +GAPI_TRANSFORM(gmat_in_gmat_out, , "gmat_in_gmat_out") +{ + static GMat pattern(GMat) { return {}; } + static GMat substitute(GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmat2_in_gmat_out, , "gmat2_in_gmat_out") +{ + static GMat pattern(GMat, GMat) { return {}; } + static GMat substitute(GMat, GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmat2_in_gmat3_out, , "gmat2_in_gmat3_out") +{ + static GMat3 pattern(GMat, GMat) { return {}; } + static GMat3 substitute(GMat, GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmatp_in_gmatp_out, , "gmatp_in_gmatp_out") +{ + static GMatP pattern(GMatP) { return {}; } + static GMatP substitute(GMatP) { return {}; } +}; + +GAPI_TRANSFORM(gsc_in_gmat_out, , "gsc_in_gmat_out") +{ + static GMat pattern(GScalar) { return {}; } + static GMat substitute(GScalar) { return {}; } +}; + +GAPI_TRANSFORM(gmat_in_gsc_out, , "gmat_in_gsc_out") +{ + static GScalar pattern(GMat) { return {}; } + static GScalar substitute(GMat) { return {}; } +}; + +GAPI_TRANSFORM(garr_in_gmat_out, )>, "garr_in_gmat_out") +{ + static GMat pattern(GArray) { return {}; } + static GMat substitute(GArray) { return {}; } +}; + +GAPI_TRANSFORM(gmat_in_garr_out, (GMat)>, "gmat_in_garr_out") +{ + static GArray pattern(GMat) { return {}; } + static GArray 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(); + pkg.include(); + pkg.include(); + auto tr = pkg.get_transformations(); + EXPECT_EQ(3u, tr.size()); +} + +TEST(KernelPackageTransform, Combine) +{ + auto pkg1 = cv::gapi::kernels(); + auto pkg2 = cv::gapi::kernels(); + 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()); + } +} + +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()); + } +} + +template +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::kind); + EXPECT_NO_THROW(garg.get()); + }; + + checkOut(pattern[0]); + checkOut(subst[0]); +} + +TEST(KernelPackageTransform, GMat) +{ + transformTest(); +} + +TEST(KernelPackageTransform, GMatP) +{ + transformTest(); +} + +TEST(KernelPackageTransform, GScalarIn) +{ + transformTest(); +} + +TEST(KernelPackageTransform, GScalarOut) +{ + transformTest(); +} + +TEST(KernelPackageTransform, DISABLED_GArrayIn) +{ + transformTest, GMat>(); +} + +TEST(KernelPackageTransform, DISABLED_GArrayOut) +{ + transformTest>(); +} + +} // namespace opencv_test