G-API: Integrated cv::MediaFrame as I/O type + CPU backend
[platform/upstream/opencv.git] / modules / gapi / include / opencv2 / gapi / cpu / gcpukernel.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_GCPUKERNEL_HPP
9 #define OPENCV_GAPI_GCPUKERNEL_HPP
10
11 #include <functional>
12 #include <unordered_map>
13 #include <utility>
14 #include <vector>
15
16 #include <opencv2/core/mat.hpp>
17 #include <opencv2/gapi/gcommon.hpp>
18 #include <opencv2/gapi/gkernel.hpp>
19 #include <opencv2/gapi/garg.hpp>
20 #include <opencv2/gapi/gmetaarg.hpp>
21 #include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
22 #include <opencv2/gapi/util/util.hpp>
23
24 // FIXME: namespace scheme for backends?
25 namespace cv {
26
27 namespace gimpl
28 {
29     // Forward-declare an internal class
30     class GCPUExecutable;
31
32     namespace render
33     {
34     namespace ocv
35     {
36         class GRenderExecutable;
37     }
38     }
39 } // namespace gimpl
40
41 namespace gapi
42 {
43 namespace cpu
44 {
45     /**
46      * \addtogroup gapi_std_backends
47      * @{
48      *
49      * @brief G-API backends available in this OpenCV version
50      *
51      * G-API backends play a corner stone role in G-API execution
52      * stack. Every backend is hardware-oriented and thus can run its
53      * kernels efficiently on the target platform.
54      *
55      * Backends are usually "black boxes" for G-API users -- on the API
56      * side, all backends are represented as different objects of the
57      * same class cv::gapi::GBackend.
58      * User can manipulate with backends by specifying which kernels to use.
59      *
60      * @sa @ref gapi_hld
61      */
62
63     /**
64      * @brief Get a reference to CPU (OpenCV) backend.
65      *
66      * This is the default backend in G-API at the moment, providing
67      * broader functional coverage but losing some graph model
68      * advantages. Provided mostly for reference and prototyping
69      * purposes.
70      *
71      * @sa gapi_std_backends
72      */
73     GAPI_EXPORTS cv::gapi::GBackend backend();
74     /** @} */
75
76     class GOCVFunctor;
77
78     //! @cond IGNORED
79     template<typename K, typename Callable>
80     GOCVFunctor ocv_kernel(const Callable& c);
81
82     template<typename K, typename Callable>
83     GOCVFunctor ocv_kernel(Callable& c);
84     //! @endcond
85
86 } // namespace cpu
87 } // namespace gapi
88
89 // Represents arguments which are passed to a wrapped CPU function
90 // FIXME: put into detail?
91 class GAPI_EXPORTS GCPUContext
92 {
93 public:
94     // Generic accessor API
95     template<typename T>
96     const T& inArg(int input) { return m_args.at(input).get<T>(); }
97
98     // Syntax sugar
99     const cv::Mat&   inMat(int input);
100     cv::Mat&         outMatR(int output); // FIXME: Avoid cv::Mat m = ctx.outMatR()
101
102     const cv::Scalar& inVal(int input);
103     cv::Scalar& outValR(int output); // FIXME: Avoid cv::Scalar s = ctx.outValR()
104     template<typename T> std::vector<T>& outVecR(int output) // FIXME: the same issue
105     {
106         return outVecRef(output).wref<T>();
107     }
108     template<typename T> T& outOpaqueR(int output) // FIXME: the same issue
109     {
110         return outOpaqueRef(output).wref<T>();
111     }
112
113     GArg state()
114     {
115         return m_state;
116     }
117
118 protected:
119     detail::VectorRef& outVecRef(int output);
120     detail::OpaqueRef& outOpaqueRef(int output);
121
122     std::vector<GArg> m_args;
123     GArg m_state;
124
125     //FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call
126     //to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
127     //once on enter for input and output arguments, and once before return for output arguments only
128     std::unordered_map<std::size_t, GRunArgP> m_results;
129
130     friend class gimpl::GCPUExecutable;
131     friend class gimpl::render::ocv::GRenderExecutable;
132 };
133
134 class GAPI_EXPORTS GCPUKernel
135 {
136 public:
137     // This function is a kernel's execution entry point (does the processing work)
138     using RunF = std::function<void(GCPUContext &)>;
139     // This function is a stateful kernel's setup routine (configures state)
140     using SetupF = std::function<void(const GMetaArgs &, const GArgs &,
141                                       GArg &, const GCompileArgs &)>;
142
143     GCPUKernel();
144     GCPUKernel(const RunF& runF, const SetupF& setupF = nullptr);
145
146     RunF m_runF = nullptr;
147     SetupF m_setupF = nullptr;
148
149     bool m_isStateful = false;
150 };
151
152 // FIXME: This is an ugly ad-hoc implementation. TODO: refactor
153
154 namespace detail
155 {
156 template<class T> struct get_in;
157 template<> struct get_in<cv::GMat>
158 {
159     static cv::Mat    get(GCPUContext &ctx, int idx) { return ctx.inMat(idx); }
160 };
161 template<> struct get_in<cv::GMatP>
162 {
163     static cv::Mat    get(GCPUContext &ctx, int idx) { return get_in<cv::GMat>::get(ctx, idx); }
164 };
165 template<> struct get_in<cv::GFrame>
166 {
167     static cv::MediaFrame get(GCPUContext &ctx, int idx) { return ctx.inArg<cv::MediaFrame>(idx); }
168 };
169 template<> struct get_in<cv::GScalar>
170 {
171     static cv::Scalar get(GCPUContext &ctx, int idx) { return ctx.inVal(idx); }
172 };
173 template<typename U> struct get_in<cv::GArray<U> >
174 {
175     static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
176 };
177 template<typename U> struct get_in<cv::GOpaque<U> >
178 {
179     static const U& get(GCPUContext &ctx, int idx) { return ctx.inArg<OpaqueRef>(idx).rref<U>(); }
180 };
181
182 //FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
183 template<> struct get_in<cv::GArray<cv::GMat> >: public get_in<cv::GArray<cv::Mat> >
184 {
185 };
186
187 //FIXME(dm): GArray<Scalar>/GArray<GScalar> conversion should be done more gracefully in the system
188 template<> struct get_in<cv::GArray<cv::GScalar> >: public get_in<cv::GArray<cv::Scalar> >
189 {
190 };
191
192 //FIXME(dm): GOpaque<Mat>/GOpaque<GMat> conversion should be done more gracefully in the system
193 template<> struct get_in<cv::GOpaque<cv::GMat> >: public get_in<cv::GOpaque<cv::Mat> >
194 {
195 };
196
197 //FIXME(dm): GOpaque<Scalar>/GOpaque<GScalar> conversion should be done more gracefully in the system
198 template<> struct get_in<cv::GOpaque<cv::GScalar> >: public get_in<cv::GOpaque<cv::Mat> >
199 {
200 };
201
202 template<class T> struct get_in
203 {
204     static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
205 };
206
207 struct tracked_cv_mat{
208     tracked_cv_mat(cv::Mat& m) : r{m}, original_data{m.data} {}
209     cv::Mat r;
210     uchar* original_data;
211
212     operator cv::Mat& (){ return r;}
213     void validate() const{
214         if (r.data != original_data)
215         {
216             util::throw_error
217                 (std::logic_error
218                  ("OpenCV kernel output parameter was reallocated. \n"
219                   "Incorrect meta data was provided ?"));
220         }
221     }
222 };
223
224 template<typename... Outputs>
225 void postprocess(Outputs&... outs)
226 {
227     struct
228     {
229         void operator()(tracked_cv_mat* bm) { bm->validate();  }
230         void operator()(...)                {                  }
231
232     } validate;
233     //dummy array to unfold parameter pack
234     int dummy[] = { 0, (validate(&outs), 0)... };
235     cv::util::suppress_unused_warning(dummy);
236 }
237
238 template<class T> struct get_out;
239 template<> struct get_out<cv::GMat>
240 {
241     static tracked_cv_mat get(GCPUContext &ctx, int idx)
242     {
243         auto& r = ctx.outMatR(idx);
244         return {r};
245     }
246 };
247 template<> struct get_out<cv::GMatP>
248 {
249     static tracked_cv_mat get(GCPUContext &ctx, int idx)
250     {
251         return get_out<cv::GMat>::get(ctx, idx);
252     }
253 };
254 template<> struct get_out<cv::GScalar>
255 {
256     static cv::Scalar& get(GCPUContext &ctx, int idx)
257     {
258         return ctx.outValR(idx);
259     }
260 };
261 template<typename U> struct get_out<cv::GArray<U>>
262 {
263     static std::vector<U>& get(GCPUContext &ctx, int idx)
264     {
265         return ctx.outVecR<U>(idx);
266     }
267 };
268
269 //FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
270 template<> struct get_out<cv::GArray<cv::GMat> >: public get_out<cv::GArray<cv::Mat> >
271 {
272 };
273
274 template<typename U> struct get_out<cv::GOpaque<U>>
275 {
276     static U& get(GCPUContext &ctx, int idx)
277     {
278         return ctx.outOpaqueR<U>(idx);
279     }
280 };
281
282 template<typename, typename>
283 struct OCVSetupHelper;
284
285 template<typename Impl, typename... Ins>
286 struct OCVSetupHelper<Impl, std::tuple<Ins...>>
287 {
288     // Using 'auto' return type and 'decltype' specifier in both 'setup_impl' versions
289     // to check existence of required 'Impl::setup' functions.
290     // While 'decltype' specifier accepts expression we pass expression with 'comma-operator'
291     // where first operand of comma-operator is call attempt to desired 'Impl::setup' and
292     // the second operand is 'void()' expression.
293     //
294     // SFINAE for 'Impl::setup' which accepts compile arguments.
295     template<int... IIs>
296     static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
297                            GArg &state, const GCompileArgs &compileArgs,
298                            detail::Seq<IIs...>) ->
299         decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
300                              std::declval<typename std::add_lvalue_reference<
301                                               std::shared_ptr<typename Impl::State>
302                                                                             >::type
303                                          >(),
304                             compileArgs)
305                  , void())
306     {
307         // TODO: unique_ptr <-> shared_ptr conversion ?
308         // To check: Conversion is possible only if the state which should be passed to
309         // 'setup' user callback isn't required to have previous value
310         std::shared_ptr<typename Impl::State> stPtr;
311         Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr, compileArgs);
312         state = GArg(stPtr);
313     }
314
315     // SFINAE for 'Impl::setup' which doesn't accept compile arguments.
316     template<int... IIs>
317     static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
318                            GArg &state, const GCompileArgs &/* compileArgs */,
319                            detail::Seq<IIs...>) ->
320         decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
321                              std::declval<typename std::add_lvalue_reference<
322                                               std::shared_ptr<typename Impl::State>
323                                                                             >::type
324                                          >()
325                             )
326                  , void())
327     {
328         // The same comment as in 'setup' above.
329         std::shared_ptr<typename Impl::State> stPtr;
330         Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr);
331         state = GArg(stPtr);
332     }
333
334     static void setup(const GMetaArgs &metaArgs, const GArgs &args,
335                       GArg& state, const GCompileArgs &compileArgs)
336     {
337         setup_impl(metaArgs, args, state, compileArgs,
338                    typename detail::MkSeq<sizeof...(Ins)>::type());
339     }
340 };
341
342 // OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors.
343 template<typename, typename, typename>
344 struct OCVCallHelper;
345
346 // FIXME: probably can be simplified with std::apply or analogue.
347 template<typename Impl, typename... Ins, typename... Outs>
348 struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
349 {
350     template<typename... Inputs>
351     struct call_and_postprocess
352     {
353         template<typename... Outputs>
354         static void call(Inputs&&... ins, Outputs&&... outs)
355         {
356             //not using a std::forward on outs is deliberate in order to
357             //cause compilation error, by trying to bind rvalue references to lvalue references
358             Impl::run(std::forward<Inputs>(ins)..., outs...);
359             postprocess(outs...);
360         }
361
362         template<typename... Outputs>
363         static void call(Impl& impl, Inputs&&... ins, Outputs&&... outs)
364         {
365             impl(std::forward<Inputs>(ins)..., outs...);
366         }
367     };
368
369     template<int... IIs, int... OIs>
370     static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
371     {
372         //Make sure that OpenCV kernels do not reallocate memory for output parameters
373         //by comparing it's state (data ptr) before and after the call.
374         //This is done by converting each output Mat into tracked_cv_mat object, and binding
375         //them to parameters of ad-hoc function
376         call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
377             ::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
378     }
379
380     template<int... IIs, int... OIs>
381     static void call_impl(cv::GCPUContext &ctx, Impl& impl,
382                           detail::Seq<IIs...>, detail::Seq<OIs...>)
383     {
384         call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
385             ::call(impl, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
386     }
387
388     static void call(GCPUContext &ctx)
389     {
390         call_impl(ctx,
391                   typename detail::MkSeq<sizeof...(Ins)>::type(),
392                   typename detail::MkSeq<sizeof...(Outs)>::type());
393     }
394
395     // NB: Same as call but calling the object
396     // This necessary for kernel implementations that have a state
397     // and are represented as an object
398     static void callFunctor(cv::GCPUContext &ctx, Impl& impl)
399     {
400         call_impl(ctx, impl,
401                   typename detail::MkSeq<sizeof...(Ins)>::type(),
402                   typename detail::MkSeq<sizeof...(Outs)>::type());
403     }
404 };
405
406 // OCVStCallHelper is a helper class to call stateful OCV kernels.
407 template<typename, typename, typename>
408 struct OCVStCallHelper;
409
410 template<typename Impl, typename... Ins, typename... Outs>
411 struct OCVStCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>> :
412     OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
413 {
414     template<typename... Inputs>
415     struct call_and_postprocess
416     {
417         template<typename... Outputs>
418         static void call(typename Impl::State& st, Inputs&&... ins, Outputs&&... outs)
419         {
420             Impl::run(std::forward<Inputs>(ins)..., outs..., st);
421             postprocess(outs...);
422         }
423     };
424
425     template<int... IIs, int... OIs>
426     static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
427     {
428         auto& st = *ctx.state().get<std::shared_ptr<typename Impl::State>>();
429         call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
430             ::call(st, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
431     }
432
433     static void call(GCPUContext &ctx)
434     {
435         call_impl(ctx,
436                   typename detail::MkSeq<sizeof...(Ins)>::type(),
437                   typename detail::MkSeq<sizeof...(Outs)>::type());
438     }
439 };
440
441 } // namespace detail
442
443 template<class Impl, class K>
444 class GCPUKernelImpl: public cv::detail::KernelTag
445 {
446     using CallHelper = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
447
448 public:
449     using API = K;
450
451     static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
452     static cv::GCPUKernel      kernel() { return GCPUKernel(&CallHelper::call); }
453 };
454
455 template<class Impl, class K, class S>
456 class GCPUStKernelImpl: public cv::detail::KernelTag
457 {
458     using StSetupHelper = detail::OCVSetupHelper<Impl, typename K::InArgs>;
459     using StCallHelper  = detail::OCVStCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
460
461 public:
462     using API = K;
463     using State = S;
464
465     static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
466     static cv::GCPUKernel     kernel()  { return GCPUKernel(&StCallHelper::call,
467                                                             &StSetupHelper::setup); }
468 };
469
470 #define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
471
472 // TODO: Reuse Anatoliy's logic for support of types with commas in macro.
473 //       Retrieve the common part from Anatoliy's logic to the separate place.
474 #define GAPI_OCV_KERNEL_ST(Name, API, State)                   \
475     struct Name: public cv::GCPUStKernelImpl<Name, API, State> \
476
477
478 class gapi::cpu::GOCVFunctor : public gapi::GFunctor
479 {
480 public:
481     using Impl = std::function<void(GCPUContext &)>;
482     using Meta = cv::GKernel::M;
483
484     GOCVFunctor(const char* id, const Meta &meta, const Impl& impl)
485         : gapi::GFunctor(id), impl_{GCPUKernel(impl), meta}
486     {
487     }
488
489     GKernelImpl    impl()    const override { return impl_;                }
490     gapi::GBackend backend() const override { return gapi::cpu::backend(); }
491
492 private:
493     GKernelImpl impl_;
494 };
495
496 //! @cond IGNORED
497 template<typename K, typename Callable>
498 gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(Callable& c)
499 {
500     using P = detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
501     return GOCVFunctor{ K::id()
502                       , &K::getOutMeta
503                       , std::bind(&P::callFunctor, std::placeholders::_1, std::ref(c))
504                       };
505 }
506
507 template<typename K, typename Callable>
508 gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c)
509 {
510     using P = detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
511     return GOCVFunctor{ K::id()
512                       , &K::getOutMeta
513                       , std::bind(&P::callFunctor, std::placeholders::_1, c)
514                       };
515 }
516 //! @endcond
517
518 } // namespace cv
519
520 #endif // OPENCV_GAPI_GCPUKERNEL_HPP