hc
2024-08-12 233ab1bd4c5697f5cdec94e60206e8c6ac609b4c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
// 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) 2018 Intel Corporation
 
 
#ifndef OPENCV_GAPI_GCOMPUTATION_HPP
#define OPENCV_GAPI_GCOMPUTATION_HPP
 
#include <functional>
 
#include <opencv2/gapi/util/util.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/gproto.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gcompiled.hpp>
#include <opencv2/gapi/gstreaming.hpp>
 
namespace cv {
 
namespace detail
{
    // FIXME: move to algorithm, cover with separate tests
    // FIXME: replace with O(1) version (both memory and compilation time)
    template<typename...>
    struct last_type;
 
    template<typename T>
    struct last_type<T> { using type = T;};
 
    template<typename T, typename... Ts>
    struct last_type<T, Ts...> { using type = typename last_type<Ts...>::type; };
 
    template<typename... Ts>
    using last_type_t = typename last_type<Ts...>::type;
}
 
/**
 * \addtogroup gapi_main_classes
 * @{
 *
 * @brief G-API classes for constructed and compiled graphs.
 */
/**
 * @brief GComputation class represents a captured computation
 * graph. GComputation objects form boundaries for expression code
 * user writes with G-API, allowing to compile and execute it.
 *
 * G-API computations are defined with input/output data
 * objects. G-API will track automatically which operations connect
 * specified outputs to the inputs, forming up a call graph to be
 * executed. The below example expresses calculation of Sobel operator
 * for edge detection (\f$G = \sqrt{G_x^2 + G_y^2}\f$):
 *
 * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_def
 *
 * Full pipeline can be now captured with this object declaration:
 *
 * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_full
 *
 * Input/output data objects on which a call graph should be
 * reconstructed are passed using special wrappers cv::GIn and
 * cv::GOut. G-API will track automatically which operations form a
 * path from inputs to outputs and build the execution graph appropriately.
 *
 * Note that cv::GComputation doesn't take ownership on data objects
 * it is defined. Moreover, multiple GComputation objects may be
 * defined on the same expressions, e.g. a smaller pipeline which
 * expects that image gradients are already pre-calculated may be
 * defined like this:
 *
 * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_sub
 *
 * The resulting graph would expect two inputs and produce one
 * output. In this case, it doesn't matter if gx/gy data objects are
 * results of cv::gapi::Sobel operators -- G-API will stop unrolling
 * expressions and building the underlying graph one reaching this
 * data objects.
 *
 * The way how GComputation is defined is important as its definition
 * specifies graph _protocol_ -- the way how the graph should be
 * used. Protocol is defined by number of inputs, number of outputs,
 * and shapes of inputs and outputs.
 *
 * In the above example, sobelEdge expects one Mat on input and
 * produces one Mat; while sobelEdgeSub expects two Mats on input and
 * produces one Mat. GComputation's protocol defines how other
 * computation methods should be used -- cv::GComputation::compile() and
 * cv::GComputation::apply(). For example, if a graph is defined on
 * two GMat inputs, two cv::Mat objects have to be passed to apply()
 * for execution. GComputation checks protocol correctness in runtime
 * so passing a different number of objects in apply() or passing
 * cv::Scalar instead of cv::Mat there would compile well as a C++
 * source but raise an exception in run-time. G-API also comes with a
 * typed wrapper cv::GComputationT<> which introduces this type-checking in
 * compile-time.
 *
 * cv::GComputation itself is a thin object which just captures what
 * the graph is. The compiled graph (which actually process data) is
 * represented by class GCompiled. Use compile() method to generate a
 * compiled graph with given compile options. cv::GComputation can
 * also be used to process data with implicit graph compilation
 * on-the-fly, see apply() for details.
 *
 * GComputation is a reference-counted object -- once defined, all its
 * copies will refer to the same instance.
 *
 * @sa GCompiled
 */
class GAPI_EXPORTS GComputation
{
public:
    class Priv;
    typedef std::function<GComputation()> Generator;
 
    // Various constructors enable different ways to define a computation: /////
    // 1. Generic constructors
    /**
     * @brief Define a computation using a generator function.
     *
     * Graph can be defined in-place directly at the moment of its
     * construction with a lambda:
     *
     * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_gen
     *
     * This may be useful since all temporary objects (cv::GMats) and
     * namespaces can be localized to scope of lambda, without
     * contaminating the parent scope with probably unnecessary objects
     * and information.
     *
     * @param gen generator function which returns a cv::GComputation,
     * see Generator.
     */
    GComputation(const Generator& gen);                // Generator
                                                       // overload
 
    /**
     * @brief Generic GComputation constructor.
     *
     * Constructs a new graph with a given protocol, specified as a
     * flow of operations connecting input/output objects. Throws if
     * the passed boundaries are invalid, e.g. if there's no
     * functional dependency (path) between given outputs and inputs.
     *
     * @param ins Input data vector.
     * @param outs Output data vector.
     *
     * @note Don't construct GProtoInputArgs/GProtoOutputArgs objects
     * directly, use cv::GIn()/cv::GOut() wrapper functions instead.
     *
     * @sa @ref gapi_data_objects
     */
    GComputation(GProtoInputArgs &&ins,
                 GProtoOutputArgs &&outs);             // Arg-to-arg overload
 
    // 2. Syntax sugar and compatibility overloads
    /**
     * @brief Defines an unary (one input -- one output) computation
     *
     * @overload
     * @param in input GMat of the defined unary computation
     * @param out output GMat of the defined unary computation
     */
    GComputation(GMat in, GMat out);                   // Unary overload
 
    /**
     * @brief Defines an unary (one input -- one output) computation
     *
     * @overload
     * @param in input GMat of the defined unary computation
     * @param out output GScalar of the defined unary computation
     */
    GComputation(GMat in, GScalar out);                // Unary overload (scalar)
 
    /**
     * @brief Defines a binary (two inputs -- one output) computation
     *
     * @overload
     * @param in1 first input GMat of the defined binary computation
     * @param in2 second input GMat of the defined binary computation
     * @param out output GMat of the defined binary computation
     */
    GComputation(GMat in1, GMat in2, GMat out);        // Binary overload
 
    /**
     * @brief Defines a binary (two inputs -- one output) computation
     *
     * @overload
     * @param in1 first input GMat of the defined binary computation
     * @param in2 second input GMat of the defined binary computation
     * @param out output GScalar of the defined binary computation
     */
    GComputation(GMat in1, GMat in2, GScalar out);     // Binary
                                                       // overload
                                                       // (scalar)
 
    /**
     * @brief Defines a computation with arbitrary input/output number.
     *
     * @overload
     * @param ins vector of inputs GMats for this computation
     * @param outs vector of outputs GMats for this computation
     *
     * Use this overload for cases when number of computation
     * inputs/outputs is not known in compile-time -- e.g. when graph
     * is programmatically generated to build an image pyramid with
     * the given number of levels, etc.
     */
    GComputation(const std::vector<GMat> &ins,         // Compatibility overload
                 const std::vector<GMat> &outs);
 
    // Various versions of apply(): ////////////////////////////////////////////
    // 1. Generic apply()
    /**
     * @brief Compile graph on-the-fly and immediately execute it on
     * the inputs data vectors.
     *
     * Number of input/output data objects must match GComputation's
     * protocol, also types of host data objects (cv::Mat, cv::Scalar)
     * must match the shapes of data objects from protocol (cv::GMat,
     * cv::GScalar). If there's a mismatch, a run-time exception will
     * be generated.
     *
     * Internally, a cv::GCompiled object is created for the given
     * input format configuration, which then is executed on the input
     * data immediately. cv::GComputation caches compiled objects
     * produced within apply() -- if this method would be called next
     * time with the same input parameters (image formats, image
     * resolution, etc), the underlying compiled graph will be reused
     * without recompilation. If new metadata doesn't match the cached
     * one, the underlying compiled graph is regenerated.
     *
     * @note compile() always triggers a compilation process and
     * produces a new GCompiled object regardless if a similar one has
     * been cached via apply() or not.
     *
     * @param ins vector of input data to process. Don't create
     * GRunArgs object manually, use cv::gin() wrapper instead.
     * @param outs vector of output data to fill results in. cv::Mat
     * objects may be empty in this vector, G-API will automatically
     * initialize it with the required format & dimensions. Don't
     * create GRunArgsP object manually, use cv::gout() wrapper instead.
     * @param args a list of compilation arguments to pass to the
     * underlying compilation process. Don't create GCompileArgs
     * object manually, use cv::compile_args() wrapper instead.
     *
     * @sa @ref gapi_data_objects, @ref gapi_compile_args
     */
    void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {});       // Arg-to-arg overload
 
    /// @private -- Exclude this function from OpenCV documentation
    void apply(const std::vector<cv::gapi::own::Mat>& ins,                        // Compatibility overload
               const std::vector<cv::gapi::own::Mat>& outs,
               GCompileArgs &&args = {});
 
    // 2. Syntax sugar and compatibility overloads
#if !defined(GAPI_STANDALONE)
    /**
     * @brief Execute an unary computation (with compilation on the fly)
     *
     * @overload
     * @param in input cv::Mat for unary computation
     * @param out output cv::Mat for unary computation
     * @param args compilation arguments for underlying compilation
     * process.
     */
    void apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args = {});               // Unary overload
 
    /**
     * @brief Execute an unary computation (with compilation on the fly)
     *
     * @overload
     * @param in input cv::Mat for unary computation
     * @param out output cv::Scalar for unary computation
     * @param args compilation arguments for underlying compilation
     * process.
     */
    void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {});            // Unary overload (scalar)
 
    /**
     * @brief Execute a binary computation (with compilation on the fly)
     *
     * @overload
     * @param in1 first input cv::Mat for binary computation
     * @param in2 second input cv::Mat for binary computation
     * @param out output cv::Mat for binary computation
     * @param args compilation arguments for underlying compilation
     * process.
     */
    void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload
 
    /**
     * @brief Execute an binary computation (with compilation on the fly)
     *
     * @overload
     * @param in1 first input cv::Mat for binary computation
     * @param in2 second input cv::Mat for binary computation
     * @param out output cv::Scalar for binary computation
     * @param args compilation arguments for underlying compilation
     * process.
     */
    void apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args = {}); // Binary overload (scalar)
 
    /**
     * @brief Execute a computation with arbitrary number of
     * inputs/outputs (with compilation on-the-fly).
     *
     * @overload
     * @param ins vector of input cv::Mat objects to process by the
     * computation.
     * @param outs vector of output cv::Mat objects to produce by the
     * computation.
     * @param args compilation arguments for underlying compilation
     * process.
     *
     * Numbers of elements in ins/outs vectos must match numbers of
     * inputs/outputs which were used to define this GComputation.
     */
    void apply(const std::vector<cv::Mat>& ins,         // Compatibility overload
                     std::vector<cv::Mat>& outs,
               GCompileArgs &&args = {});
#endif // !defined(GAPI_STANDALONE)
    // Various versions of compile(): //////////////////////////////////////////
    // 1. Generic compile() - requires metas to be passed as vector
    /**
     * @brief Compile the computation for specific input format(s).
     *
     * This method triggers compilation process and produces a new
     * GCompiled object which then can process data of the given
     * format. Passing data with different format to the compiled
     * computation will generate a run-time exception.
     *
     * @param in_metas vector of input metadata configuration. Grab
     * metadata from real data objects (like cv::Mat or cv::Scalar)
     * using cv::descr_of(), or create it on your own.
     * @param args compilation arguments for this compilation
     * process. Compilation arguments directly affect what kind of
     * executable object would be produced, e.g. which kernels (and
     * thus, devices) would be used to execute computation.
     *
     * @return GCompiled, an executable computation compiled
     * specifically for the given input parameters.
     *
     * @sa @ref gapi_compile_args
     */
    GCompiled compile(GMetaArgs &&in_metas, GCompileArgs &&args = {});
 
    // 2. Syntax sugar - variadic list of metas, no extra compile args
    // FIXME: SFINAE looks ugly in the generated documentation
    /**
     * @overload
     *
     * Takes a variadic parameter pack with metadata
     * descriptors for which a compiled object needs to be produced.
     *
     * @return GCompiled, an executable computation compiled
     * specifically for the given input parameters.
     */
    template<typename... Ts>
    auto compile(const Ts&... metas) ->
        typename std::enable_if<detail::are_meta_descrs<Ts...>::value, GCompiled>::type
    {
        return compile(GMetaArgs{GMetaArg(metas)...}, GCompileArgs());
    }
 
    // 3. Syntax sugar - variadic list of metas, extra compile args
    // (seems optional parameters don't work well when there's an variadic template
    // comes first)
    //
    // Ideally it should look like:
    //
    //     template<typename... Ts>
    //     GCompiled compile(const Ts&... metas, GCompileArgs &&args)
    //
    // But not all compilers can hande this (and seems they shouldn't be able to).
    // FIXME: SFINAE looks ugly in the generated documentation
    /**
     * @overload
     *
     * Takes a  variadic parameter pack with metadata
     * descriptors for which a compiled object needs to be produced,
     * followed by GCompileArgs object representing compilation
     * arguments for this process.
     *
     * @return GCompiled, an executable computation compiled
     * specifically for the given input parameters.
     */
    template<typename... Ts>
    auto compile(const Ts&... meta_and_compile_args) ->
        typename std::enable_if<detail::are_meta_descrs_but_last<Ts...>::value
                                && std::is_same<GCompileArgs, detail::last_type_t<Ts...> >::value,
                                GCompiled>::type
    {
        //FIXME: wrapping meta_and_compile_args into a tuple to unwrap them inside a helper function is the overkill
        return compile(std::make_tuple(meta_and_compile_args...),
                       typename detail::MkSeq<sizeof...(Ts)-1>::type());
    }
 
 
    // FIXME: Document properly in the Doxygen format
    // Video-oriented pipeline compilation:
    // 1. A generic version
    /**
     * @brief Compile the computation for streaming mode.
     *
     * This method triggers compilation process and produces a new
     * GStreamingCompiled object which then can process video stream
     * data of the given format. Passing a stream in a different
     * format to the compiled computation will generate a run-time
     * exception.
     *
     * @param in_metas vector of input metadata configuration. Grab
     * metadata from real data objects (like cv::Mat or cv::Scalar)
     * using cv::descr_of(), or create it on your own.
     *
     * @param args compilation arguments for this compilation
     * process. Compilation arguments directly affect what kind of
     * executable object would be produced, e.g. which kernels (and
     * thus, devices) would be used to execute computation.
     *
     * @return GStreamingCompiled, a streaming-oriented executable
     * computation compiled specifically for the given input
     * parameters.
     *
     * @sa @ref gapi_compile_args
     */
    GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {});
 
    /**
     * @brief Compile the computation for streaming mode.
     *
     * This method triggers compilation process and produces a new
     * GStreamingCompiled object which then can process video stream
     * data in any format. Underlying mechanisms will be adjusted to
     * every new input video stream automatically, but please note that
     * _not all_ existing backends support this (see reshape()).
     *
     * @param args compilation arguments for this compilation
     * process. Compilation arguments directly affect what kind of
     * executable object would be produced, e.g. which kernels (and
     * thus, devices) would be used to execute computation.
     *
     * @return GStreamingCompiled, a streaming-oriented executable
     * computation compiled for any input image format.
     *
     * @sa @ref gapi_compile_args
     */
    GStreamingCompiled compileStreaming(GCompileArgs &&args = {});
 
    // 2. Direct metadata version
    /**
     * @overload
     *
     * Takes a variadic parameter pack with metadata
     * descriptors for which a compiled object needs to be produced.
     *
     * @return GStreamingCompiled, a streaming-oriented executable
     * computation compiled specifically for the given input
     * parameters.
     */
    template<typename... Ts>
    auto compileStreaming(const Ts&... metas) ->
        typename std::enable_if<detail::are_meta_descrs<Ts...>::value, GStreamingCompiled>::type
    {
        return compileStreaming(GMetaArgs{GMetaArg(metas)...}, GCompileArgs());
    }
 
    // 2. Direct metadata + compile arguments version
    /**
     * @overload
     *
     * Takes a  variadic parameter pack with metadata
     * descriptors for which a compiled object needs to be produced,
     * followed by GCompileArgs object representing compilation
     * arguments for this process.
     *
     * @return GStreamingCompiled, a streaming-oriented executable
     * computation compiled specifically for the given input
     * parameters.
     */
    template<typename... Ts>
    auto compileStreaming(const Ts&... meta_and_compile_args) ->
        typename std::enable_if<detail::are_meta_descrs_but_last<Ts...>::value
                                && std::is_same<GCompileArgs, detail::last_type_t<Ts...> >::value,
                                GStreamingCompiled>::type
    {
        //FIXME: wrapping meta_and_compile_args into a tuple to unwrap them inside a helper function is the overkill
        return compileStreaming(std::make_tuple(meta_and_compile_args...),
                                typename detail::MkSeq<sizeof...(Ts)-1>::type());
    }
 
    // Internal use only
    /// @private
    Priv& priv();
    /// @private
    const Priv& priv() const;
 
protected:
 
    // 4. Helper methods for (3)
    /// @private
    template<typename... Ts, int... IIs>
    GCompiled compile(const std::tuple<Ts...> &meta_and_compile_args, detail::Seq<IIs...>)
    {
        GMetaArgs meta_args = {GMetaArg(std::get<IIs>(meta_and_compile_args))...};
        GCompileArgs comp_args = std::get<sizeof...(Ts)-1>(meta_and_compile_args);
        return compile(std::move(meta_args), std::move(comp_args));
    }
    template<typename... Ts, int... IIs>
    GStreamingCompiled compileStreaming(const std::tuple<Ts...> &meta_and_compile_args, detail::Seq<IIs...>)
    {
        GMetaArgs meta_args = {GMetaArg(std::get<IIs>(meta_and_compile_args))...};
        GCompileArgs comp_args = std::get<sizeof...(Ts)-1>(meta_and_compile_args);
        return compileStreaming(std::move(meta_args), std::move(comp_args));
    }
    /// @private
    std::shared_ptr<Priv> m_priv;
};
/** @} */
 
namespace gapi
{
    // FIXME: all these standalone functions need to be added to some
    // common documentation section
    /**
     * @brief Define an tagged island (subgraph) within a computation.
     *
     * Declare an Island tagged with `name` and defined from `ins` to `outs`
     * (exclusively, as ins/outs are data objects, and regioning is done on
     * operations level).
     * Throws if any operation between `ins` and `outs` are already assigned
     * to another island.
     *
     * Islands allow to partition graph into subgraphs, fine-tuning
     * the way it is scheduled by the underlying executor.
     *
     * @param name name of the Island to create
     * @param ins vector of input data objects where the subgraph
     * begins
     * @param outs vector of output data objects where the subgraph
     * ends.
     *
     * The way how an island is defined is similar to how
     * cv::GComputation is defined on input/output data objects.
     * Same rules apply here as well -- if there's no functional
     * dependency between inputs and outputs or there's not enough
     * input data objects were specified to properly calculate all
     * outputs, an exception is thrown.
     *
     * Use cv::GIn() / cv::GOut() to specify input/output vectors.
     */
    void GAPI_EXPORTS island(const std::string &name,
                             GProtoInputArgs  &&ins,
                             GProtoOutputArgs &&outs);
} // namespace gapi
 
} // namespace cv
#endif // OPENCV_GAPI_GCOMPUTATION_HPP