G-API: Integrated cv::MediaFrame as I/O type + CPU backend
[platform/upstream/opencv.git] / modules / gapi / include / opencv2 / gapi / gkernel.hpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2018-2020 Intel Corporation
6
7
8 #ifndef OPENCV_GAPI_GKERNEL_HPP
9 #define OPENCV_GAPI_GKERNEL_HPP
10
11 #include <functional>
12 #include <iostream>
13 #include <string>  // string
14 #include <type_traits> // false_type, true_type
15 #include <unordered_map> // map (for GKernelPackage)
16 #include <utility> // tuple
17
18 #include <opencv2/gapi/gcommon.hpp> // CompileArgTag
19 #include <opencv2/gapi/util/util.hpp> // Seq
20 #include <opencv2/gapi/gcall.hpp>
21 #include <opencv2/gapi/garg.hpp>      // GArg
22 #include <opencv2/gapi/gmetaarg.hpp>  // GMetaArg
23 #include <opencv2/gapi/gtype_traits.hpp> // GTypeTraits
24 #include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
25 #include <opencv2/gapi/gtransform.hpp>
26
27 namespace cv {
28
29 using GShapes = std::vector<GShape>;
30 using GKinds = std::vector<cv::detail::OpaqueKind>;
31
32 // GKernel describes kernel API to the system
33 // FIXME: add attributes of a kernel, (e.g. number and types
34 // of inputs, etc)
35 struct GAPI_EXPORTS GKernel
36 {
37     using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>;
38
39     std::string name;       // kernel ID, defined by its API (signature)
40     std::string tag;        // some (implementation-specific) tag
41     M           outMeta;    // generic adaptor to API::outMeta(...)
42     GShapes     outShapes;  // types (shapes) kernel's outputs
43     GKinds      inKinds;    // kinds of kernel's inputs (fixme: below)
44 };
45 // TODO: It's questionable if inKinds should really be here. Instead,
46 // this information could come from meta.
47
48 // GKernelImpl describes particular kernel implementation to the system
49 struct GAPI_EXPORTS GKernelImpl
50 {
51     util::any         opaque;    // backend-specific opaque info
52     GKernel::M        outMeta;   // for deserialized graphs, the outMeta is taken here
53 };
54
55 template<typename, typename> class GKernelTypeM;
56
57 namespace detail
58 {
59     ////////////////////////////////////////////////////////////////////////////
60     // yield() is used in graph construction time as a generic method to obtain
61     // lazy "return value" of G-API operations
62     //
63     namespace
64     {
65         template<typename T> struct Yield;
66         template<> struct Yield<cv::GMat>
67         {
68             static inline cv::GMat yield(cv::GCall &call, int i) { return call.yield(i); }
69         };
70         template<> struct Yield<cv::GMatP>
71         {
72             static inline cv::GMatP yield(cv::GCall &call, int i) { return call.yieldP(i); }
73         };
74         template<> struct Yield<cv::GScalar>
75         {
76             static inline cv::GScalar yield(cv::GCall &call, int i) { return call.yieldScalar(i); }
77         };
78         template<typename U> struct Yield<cv::GArray<U> >
79         {
80             static inline cv::GArray<U> yield(cv::GCall &call, int i) { return call.yieldArray<U>(i); }
81         };
82         template<typename U> struct Yield<cv::GOpaque<U> >
83         {
84             static inline cv::GOpaque<U> yield(cv::GCall &call, int i) { return call.yieldOpaque<U>(i); }
85         };
86     } // anonymous namespace
87
88     ////////////////////////////////////////////////////////////////////////////
89     // Helper classes which brings outputMeta() marshalling to kernel
90     // implementations
91     //
92     // 1. MetaType establishes G#Type -> G#Meta mapping between G-API dynamic
93     //    types and its metadata descriptor types.
94     //    This mapping is used to transform types to call outMeta() callback.
95     template<typename T> struct MetaType;
96     template<> struct MetaType<cv::GMat>    { using type = GMatDesc; };
97     template<> struct MetaType<cv::GMatP>   { using type = GMatDesc; };
98     template<> struct MetaType<cv::GFrame>  { using type = GFrameDesc; };
99     template<> struct MetaType<cv::GScalar> { using type = GScalarDesc; };
100     template<typename U> struct MetaType<cv::GArray<U> >  { using type = GArrayDesc; };
101     template<typename U> struct MetaType<cv::GOpaque<U> > { using type = GOpaqueDesc; };
102     template<typename T> struct MetaType    { using type = T; }; // opaque args passed as-is
103     // FIXME: Move it to type traits?
104
105     // 2. Hacky test based on MetaType to check if we operate on G-* type or not
106     template<typename T> using is_nongapi_type = std::is_same<T, typename MetaType<T>::type>;
107
108     // 3. Two ways to transform input arguments to its meta - for G-* and non-G* types:
109     template<typename T>
110     typename std::enable_if<!is_nongapi_type<T>::value, typename MetaType<T>::type>
111     ::type get_in_meta(const GMetaArgs &in_meta, const GArgs &, int idx)
112     {
113         return util::get<typename MetaType<T>::type>(in_meta.at(idx));
114     }
115
116     template<typename T>
117     typename std::enable_if<is_nongapi_type<T>::value, T>
118     ::type get_in_meta(const GMetaArgs &, const GArgs &in_args, int idx)
119     {
120         return in_args.at(idx).template get<T>();
121     }
122
123     // 4. The MetaHelper itself: an entity which generates outMeta() call
124     //    based on kernel signature, with arguments properly substituted.
125     // 4.1 - case for multiple return values
126     // FIXME: probably can be simplified with std::apply or analogue.
127     template<typename, typename, typename>
128     struct MetaHelper;
129
130     template<typename K, typename... Ins, typename... Outs>
131     struct MetaHelper<K, std::tuple<Ins...>, std::tuple<Outs...> >
132     {
133         template<int... IIs, int... OIs>
134         static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
135                                          const GArgs &in_args,
136                                          detail::Seq<IIs...>,
137                                          detail::Seq<OIs...>)
138         {
139             // FIXME: decay?
140             using R   = std::tuple<typename MetaType<Outs>::type...>;
141             const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
142             return GMetaArgs{ GMetaArg(std::get<OIs>(r))... };
143         }
144         // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
145
146         static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
147                                     const GArgs &in_args)
148         {
149             return getOutMeta_impl(in_meta,
150                                    in_args,
151                                    typename detail::MkSeq<sizeof...(Ins)>::type(),
152                                    typename detail::MkSeq<sizeof...(Outs)>::type());
153         }
154     };
155
156     // 4.1 - case for a single return value
157     // FIXME: How to avoid duplication here?
158     template<typename K, typename... Ins, typename Out>
159     struct MetaHelper<K, std::tuple<Ins...>, Out >
160     {
161         template<int... IIs>
162         static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
163                                          const GArgs &in_args,
164                                          detail::Seq<IIs...>)
165         {
166             // FIXME: decay?
167             using R = typename MetaType<Out>::type;
168             const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
169             return GMetaArgs{ GMetaArg(r) };
170         }
171         // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
172
173         static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
174                                     const GArgs &in_args)
175         {
176             return getOutMeta_impl(in_meta,
177                                    in_args,
178                                    typename detail::MkSeq<sizeof...(Ins)>::type());
179         }
180     };
181
182     ////////////////////////////////////////////////////////////////////////////
183     // Helper class to introduce tags to calls. By default there's no tag
184     struct NoTag {
185         static constexpr const char *tag() { return ""; }
186     };
187
188 } // namespace detail
189
190 // GKernelType and GKernelTypeM are base classes which implement typed ::on()
191 // method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels
192 //
193 // G_TYPED_KERNEL and G_TYPED_KERNEL_M macros inherit user classes from GKernelType and
194 // GKernelTypeM respectively.
195
196 template<typename K, typename... R, typename... Args>
197 class GKernelTypeM<K, std::function<std::tuple<R...>(Args...)> >
198     : public detail::MetaHelper<K, std::tuple<Args...>, std::tuple<R...>>
199     , public detail::NoTag
200 {
201     template<int... IIs>
202     static std::tuple<R...> yield(cv::GCall &call, detail::Seq<IIs...>)
203     {
204         return std::make_tuple(detail::Yield<R>::yield(call, IIs)...);
205     }
206
207 public:
208     using InArgs  = std::tuple<Args...>;
209     using OutArgs = std::tuple<R...>;
210
211     // TODO: Args&&... here?
212     static std::tuple<R...> on(Args... args)
213     {
214         cv::GCall call(GKernel{ K::id()
215                               , K::tag()
216                               , &K::getOutMeta
217                               , {detail::GTypeTraits<R>::shape...}
218                               , {detail::GTypeTraits<Args>::op_kind...}});
219         call.pass(args...); // TODO: std::forward() here?
220         return yield(call, typename detail::MkSeq<sizeof...(R)>::type());
221     }
222 };
223
224 template<typename, typename> class GKernelType;
225
226 template<typename K, typename R, typename... Args>
227 class GKernelType<K, std::function<R(Args...)> >
228     : public detail::MetaHelper<K, std::tuple<Args...>, R>
229     , public detail::NoTag
230 {
231 public:
232     using InArgs  = std::tuple<Args...>;
233     using OutArgs = std::tuple<R>;
234
235     static_assert(!cv::detail::contains<GFrame, OutArgs>::value, "Values of GFrame type can't be used as operation outputs");
236
237     static R on(Args... args)
238     {
239         cv::GCall call(GKernel{ K::id()
240                               , K::tag()
241                               , &K::getOutMeta
242                               , {detail::GTypeTraits<R>::shape}
243                               , {detail::GTypeTraits<Args>::op_kind...}});
244         call.pass(args...);
245         return detail::Yield<R>::yield(call, 0);
246     }
247 };
248
249 namespace detail {
250 // This tiny class eliminates the semantic difference between
251 // GKernelType and GKernelTypeM.
252 template<typename, typename> class KernelTypeMedium;
253
254 template<typename K, typename... R, typename... Args>
255 class KernelTypeMedium<K, std::function<std::tuple<R...>(Args...)>> :
256     public cv::GKernelTypeM<K, std::function<std::tuple<R...>(Args...)>> {};
257
258 template<typename K, typename R, typename... Args>
259 class KernelTypeMedium<K, std::function<R(Args...)>> :
260     public cv::GKernelType<K, std::function<R(Args...)>> {};
261 } // namespace detail
262
263 } // namespace cv
264
265
266 // FIXME: I don't know a better way so far. Feel free to suggest one
267 // The problem is that every typed kernel should have ::id() but body
268 // of the class is defined by user (with outMeta, other stuff)
269
270 //! @cond IGNORED
271 #define G_ID_HELPER_CLASS(Class)  Class##IdHelper
272
273 #define G_ID_HELPER_BODY(Class, Id)                                         \
274     struct G_ID_HELPER_CLASS(Class)                                         \
275     {                                                                       \
276         static constexpr const char * id() {return Id;}                     \
277     };                                                                      \
278 //! @endcond
279
280 #define GET_G_TYPED_KERNEL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, NAME, ...) NAME
281 #define COMBINE_SIGNATURE(...) __VA_ARGS__
282 // Ensure correct __VA_ARGS__ expansion on Windows
283 #define __WRAP_VAARGS(x) x
284
285 /**
286  * Helper for G_TYPED_KERNEL declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api)
287  * for more details.
288  *
289  * @param Class type name for this operation.
290  * @param API an `std::function<>`-like signature for the operation;
291  *        return type is a single value or a tuple of multiple values.
292  * @param Id string identifier for the operation. Must be unique.
293  */
294 #define G_TYPED_KERNEL_HELPER(Class, API, Id)                                               \
295     G_ID_HELPER_BODY(Class, Id)                                                             \
296     struct Class final: public cv::detail::KernelTypeMedium<Class, std::function API >,     \
297                         public G_ID_HELPER_CLASS(Class)
298 // {body} is to be defined by user
299
300 #define G_TYPED_KERNEL_HELPER_2(Class, _1, _2, Id) \
301 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2), Id)
302
303 #define G_TYPED_KERNEL_HELPER_3(Class, _1, _2, _3, Id) \
304 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3), Id)
305
306 #define G_TYPED_KERNEL_HELPER_4(Class, _1, _2, _3, _4, Id) \
307 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4), Id)
308
309 #define G_TYPED_KERNEL_HELPER_5(Class, _1, _2, _3, _4, _5, Id) \
310 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5), Id)
311
312 #define G_TYPED_KERNEL_HELPER_6(Class, _1, _2, _3, _4, _5, _6, Id) \
313 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6), Id)
314
315 #define G_TYPED_KERNEL_HELPER_7(Class, _1, _2, _3, _4, _5, _6, _7, Id) \
316 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7), Id)
317
318 #define G_TYPED_KERNEL_HELPER_8(Class, _1, _2, _3, _4, _5, _6, _7, _8, Id) \
319 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8), Id)
320
321 #define G_TYPED_KERNEL_HELPER_9(Class, _1, _2, _3, _4, _5, _6, _7, _8, _9, Id) \
322 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8, _9), Id)
323
324 #define G_TYPED_KERNEL_HELPER_10(Class, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, Id) \
325 G_TYPED_KERNEL_HELPER(Class, COMBINE_SIGNATURE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10), Id)
326
327 /**
328  * Declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api)
329  * for more details.
330  *
331  * @param Class type name for this operation.
332  */
333 #define G_TYPED_KERNEL(Class, ...) __WRAP_VAARGS(GET_G_TYPED_KERNEL(__VA_ARGS__, \
334                                                  G_TYPED_KERNEL_HELPER_10, \
335                                                  G_TYPED_KERNEL_HELPER_9, \
336                                                  G_TYPED_KERNEL_HELPER_8, \
337                                                  G_TYPED_KERNEL_HELPER_7, \
338                                                  G_TYPED_KERNEL_HELPER_6, \
339                                                  G_TYPED_KERNEL_HELPER_5, \
340                                                  G_TYPED_KERNEL_HELPER_4, \
341                                                  G_TYPED_KERNEL_HELPER_3, \
342                                                  G_TYPED_KERNEL_HELPER_2, \
343                                                  G_TYPED_KERNEL_HELPER)(Class, __VA_ARGS__)) \
344
345 /**
346  * Declares a new G-API Operation. See [Kernel API](@ref gapi_kernel_api) for more details.
347  *
348  * @deprecated This macro is deprecated in favor of `G_TYPED_KERNEL` that is used for declaring any
349  * G-API Operation.
350  *
351  * @param Class type name for this operation.
352  */
353 #define G_TYPED_KERNEL_M G_TYPED_KERNEL
354
355 #define G_API_OP   G_TYPED_KERNEL
356 #define G_API_OP_M G_API_OP
357
358 namespace cv
359 {
360 namespace gapi
361 {
362     // Prework: model "Device" API before it gets to G-API headers.
363     // FIXME: Don't mix with internal Backends class!
364     class GAPI_EXPORTS GBackend
365     {
366     public:
367         class Priv;
368
369         // TODO: make it template (call `new` within??)
370         GBackend();
371         explicit GBackend(std::shared_ptr<Priv> &&p);
372
373         Priv& priv();
374         const Priv& priv() const;
375         std::size_t hash() const;
376
377         bool operator== (const GBackend &rhs) const;
378
379     private:
380         std::shared_ptr<Priv> m_priv;
381     };
382
383     inline bool operator != (const GBackend &lhs, const GBackend &rhs)
384     {
385         return !(lhs == rhs);
386     }
387 } // namespace gapi
388 } // namespace cv
389
390 namespace std
391 {
392     template<> struct hash<cv::gapi::GBackend>
393     {
394         std::size_t operator() (const cv::gapi::GBackend &b) const
395         {
396             return b.hash();
397         }
398     };
399 } // namespace std
400
401
402 namespace cv {
403 namespace gapi {
404     class GFunctor
405     {
406     public:
407         virtual cv::GKernelImpl impl()       const = 0;
408         virtual cv::gapi::GBackend backend() const = 0;
409         const char* id()                     const { return m_id; }
410
411         virtual ~GFunctor() = default;
412     protected:
413         GFunctor(const char* id) : m_id(id) { };
414     private:
415         const char* m_id;
416     };
417
418     /** \addtogroup gapi_compile_args
419      * @{
420      */
421
422     // FIXME: Hide implementation
423     /**
424      * @brief A container class for heterogeneous kernel
425      * implementation collections and graph transformations.
426      *
427      * GKernelPackage is a special container class which stores kernel
428      * _implementations_ and graph _transformations_. Objects of this class
429      * are created and passed to cv::GComputation::compile() to specify
430      * which kernels to use and which transformations to apply in the
431      * compiled graph. GKernelPackage may contain kernels of
432      * different backends, e.g. be heterogeneous.
433      *
434      * The most easy way to create a kernel package is to use function
435      * cv::gapi::kernels(). This template functions takes kernel
436      * implementations in form of type list (variadic template) and
437      * generates a kernel package atop of that.
438      *
439      * Kernel packages can be also generated programmatically, starting
440      * with an empty package (created with the default constructor)
441      * and then by populating it with kernels via call to
442      * GKernelPackage::include(). Note this method is also a template
443      * one since G-API kernel and transformation implementations are _types_,
444      * not objects.
445      *
446      * Finally, two kernel packages can be combined into a new one
447      * with function cv::gapi::combine().
448      */
449     class GAPI_EXPORTS_W_SIMPLE GKernelPackage
450     {
451
452         /// @private
453         using M = std::unordered_map<std::string, std::pair<GBackend, GKernelImpl>>;
454
455         /// @private
456         M m_id_kernels;
457
458         /// @private
459         std::vector<GTransform> m_transformations;
460
461     protected:
462         /// @private
463         // Check if package contains ANY implementation of a kernel API
464         // by API textual id.
465         bool includesAPI(const std::string &id) const;
466
467         /// @private
468         // Remove ALL implementations of the given API (identified by ID)
469         void removeAPI(const std::string &id);
470
471         /// @private
472         // Partial include() specialization for kernels
473         template <typename KImpl>
474         typename std::enable_if<(std::is_base_of<cv::detail::KernelTag, KImpl>::value), void>::type
475         includeHelper()
476         {
477             auto backend     = KImpl::backend();
478             auto kernel_id   = KImpl::API::id();
479             auto kernel_impl = GKernelImpl{KImpl::kernel(), &KImpl::API::getOutMeta};
480             removeAPI(kernel_id);
481
482             m_id_kernels[kernel_id] = std::make_pair(backend, kernel_impl);
483         }
484
485         /// @private
486         // Partial include() specialization for transformations
487         template <typename TImpl>
488         typename std::enable_if<(std::is_base_of<cv::detail::TransformTag, TImpl>::value), void>::type
489         includeHelper()
490         {
491             m_transformations.emplace_back(TImpl::transformation());
492         }
493
494     public:
495         void include(const GFunctor& functor)
496         {
497             m_id_kernels[functor.id()] = std::make_pair(functor.backend(), functor.impl());
498         }
499         /**
500          * @brief Returns total number of kernels
501          * in the package (across all backends included)
502          *
503          * @return a number of kernels in the package
504          */
505         std::size_t size() const;
506
507         /**
508          * @brief Returns vector of transformations included in the package
509          *
510          * @return vector of transformations included in the package
511          */
512         const std::vector<GTransform>& get_transformations() const;
513
514         /**
515          * @brief Test if a particular kernel _implementation_ KImpl is
516          * included in this kernel package.
517          *
518          * @sa includesAPI()
519          *
520          * @note cannot be applied to transformations
521          *
522          * @return true if there is such kernel, false otherwise.
523          */
524         template<typename KImpl>
525         bool includes() const
526         {
527             static_assert(std::is_base_of<cv::detail::KernelTag, KImpl>::value,
528                           "includes() can be applied to kernels only");
529
530             auto kernel_it = m_id_kernels.find(KImpl::API::id());
531             return kernel_it != m_id_kernels.end() &&
532                    kernel_it->second.first == KImpl::backend();
533         }
534
535         /**
536          * @brief Remove all kernels associated with the given backend
537          * from the package.
538          *
539          * Does nothing if there's no kernels of this backend in the package.
540          *
541          * @param backend backend which kernels to remove
542          */
543         void remove(const GBackend& backend);
544
545         /**
546          * @brief Remove all kernels implementing the given API from
547          * the package.
548          *
549          * Does nothing if there's no kernels implementing the given interface.
550          */
551         template<typename KAPI>
552         void remove()
553         {
554             removeAPI(KAPI::id());
555         }
556
557         // FIXME: Rename to includes() and distinguish API/impl case by
558         //     statically?
559         /**
560          * Check if package contains ANY implementation of a kernel API
561          * by API type.
562          */
563         template<typename KAPI>
564         bool includesAPI() const
565         {
566             return includesAPI(KAPI::id());
567         }
568
569         // FIXME: The below comment is wrong, and who needs this function?
570         /**
571          * @brief Find a kernel (by its API)
572          *
573          * Returns implementation corresponding id.
574          * Throws if nothing found.
575          *
576          * @return Backend which hosts matching kernel implementation.
577          *
578          */
579         template<typename KAPI>
580         GBackend lookup() const
581         {
582             return lookup(KAPI::id()).first;
583         }
584
585         /// @private
586         std::pair<cv::gapi::GBackend, cv::GKernelImpl>
587         lookup(const std::string &id) const;
588
589         // FIXME: No overwrites allowed?
590         /**
591          * @brief Put a new kernel implementation or a new transformation
592          * KImpl into the package.
593          */
594         template<typename KImpl>
595         void include()
596         {
597             includeHelper<KImpl>();
598         }
599
600         /**
601          * @brief Lists all backends which are included into package
602          *
603          * @return vector of backends
604          */
605         std::vector<GBackend> backends() const;
606
607         // TODO: Doxygen bug -- it wants me to place this comment
608         // here, not below.
609         /**
610          * @brief Create a new package based on `lhs` and `rhs`.
611          *
612          * @param lhs "Left-hand-side" package in the process
613          * @param rhs "Right-hand-side" package in the process
614          * @return a new kernel package.
615          */
616         friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage  &lhs,
617                                                    const GKernelPackage  &rhs);
618     };
619
620     /**
621      * @brief Create a kernel package object containing kernels
622      * and transformations specified in variadic template argument.
623      *
624      * In G-API, kernel implementations and transformations are _types_.
625      * Every backend has its own kernel API (like GAPI_OCV_KERNEL() and
626      * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for
627      * each kernel implementation.
628      *
629      * Use this function to pass kernel implementations (defined in
630      * either way) and transformations to the system. Example:
631      *
632      * @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet
633      *
634      * Note that kernels() itself is a function returning object, not
635      * a type, so having `()` at the end is important -- it must be a
636      * function call.
637      */
638     template<typename... KK> GKernelPackage kernels()
639     {
640         // FIXME: currently there is no check that transformations' signatures are unique
641         // and won't be any intersection in graph compilation stage
642         static_assert(cv::detail::all_unique<typename KK::API...>::value, "Kernels API must be unique");
643
644         GKernelPackage pkg;
645
646         // For those who wonder - below is a trick to call a number of
647         // methods based on parameter pack (zeroes just help hiding these
648         // calls into a sequence which helps to expand this parameter pack).
649         // Just note that `f(),a` always equals to `a` (with f() called!)
650         // and parentheses are used to hide function call in the expanded sequence.
651         // Leading 0 helps to handle case when KK is an empty list (kernels<>()).
652         int unused[] = { 0, (pkg.include<KK>(), 0)... };
653         cv::util::suppress_unused_warning(unused);
654         return pkg;
655     };
656
657     template<typename... FF>
658     GKernelPackage kernels(FF&... functors)
659     {
660         GKernelPackage pkg;
661         int unused[] = { 0, (pkg.include(functors), 0)... };
662         cv::util::suppress_unused_warning(unused);
663         return pkg;
664     };
665
666     /** @} */
667
668     // FYI - this function is already commented above
669     GAPI_EXPORTS GKernelPackage combine(const GKernelPackage  &lhs,
670                                         const GKernelPackage  &rhs);
671
672     /**
673      * @brief Combines multiple G-API kernel packages into one
674      *
675      * @overload
676      *
677      * This function successively combines the passed kernel packages using a right fold.
678      * Calling `combine(a, b, c)` is equal to `combine(a, combine(b, c))`.
679      *
680      * @return The resulting kernel package
681      */
682     template<typename... Ps>
683     GKernelPackage combine(const GKernelPackage &a, const GKernelPackage &b, Ps&&... rest)
684     {
685         return combine(a, combine(b, rest...));
686     }
687
688     /** \addtogroup gapi_compile_args
689      * @{
690      */
691     /**
692      * @brief cv::use_only() is a special combinator which hints G-API to use only
693      * kernels specified in cv::GComputation::compile() (and not to extend kernels available by
694      * default with that package).
695      */
696     struct GAPI_EXPORTS use_only
697     {
698         GKernelPackage pkg;
699     };
700     /** @} */
701
702 } // namespace gapi
703
704 namespace detail
705 {
706     template<> struct CompileArgTag<cv::gapi::GKernelPackage>
707     {
708         static const char* tag() { return "gapi.kernel_package"; }
709     };
710
711     template<> struct CompileArgTag<cv::gapi::use_only>
712     {
713         static const char* tag() { return "gapi.use_only"; }
714     };
715 } // namespace detail
716
717 } // namespace cv
718
719 #endif // OPENCV_GAPI_GKERNEL_HPP