gcomputation.hpp 23.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// 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>

13 14 15 16 17
#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>
18
#include <opencv2/gapi/gstreaming.hpp>
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

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;
}

39
// Forward-declare the serialization objects
40
namespace gapi {
41
namespace s11n {
42 43
    struct IIStream;
    struct IOStream;
44
} // namespace s11n
45
} // namespace gapi
46

47 48 49
/**
 * \addtogroup gapi_main_classes
 * @{
50 51
 *
 * @brief G-API classes for constructed and compiled graphs.
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
 */
/**
 * @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
L
luz.paz 已提交
97
 * computation methods should be used -- cv::GComputation::compile() and
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
 * 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
 */
119
class GAPI_EXPORTS_W GComputation
120 121 122 123 124 125 126
{
public:
    class Priv;
    typedef std::function<GComputation()> Generator;

    // Various constructors enable different ways to define a computation: /////
    // 1. Generic constructors
127 128 129 130 131 132 133 134 135 136
    /**
     * @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
K
klemens 已提交
137
     * contaminating the parent scope with probably unnecessary objects
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
     * 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
     */
162 163
    GAPI_WRAP GComputation(GProtoInputArgs &&ins,
                           GProtoOutputArgs &&outs);             // Arg-to-arg overload
164 165

    // 2. Syntax sugar and compatibility overloads
166 167 168 169 170 171 172
    /**
     * @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
     */
173
    GAPI_WRAP GComputation(GMat in, GMat out);  // Unary overload
174 175 176 177 178 179 180 181

    /**
     * @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
     */
182
    GAPI_WRAP GComputation(GMat in, GScalar out);      // Unary overload (scalar)
183 184 185 186 187 188 189 190 191

    /**
     * @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
     */
192
    GAPI_WRAP GComputation(GMat in1, GMat in2, GMat out);        // Binary overload
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

    /**
     * @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.
     */
218 219 220 221 222
    GComputation(const std::vector<GMat> &ins,         // Compatibility overload
                 const std::vector<GMat> &outs);

    // Various versions of apply(): ////////////////////////////////////////////
    // 1. Generic apply()
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
    /**
     * @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
     */
258
    void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {});       // Arg-to-arg overload
259

260
    /// @private -- Exclude this function from OpenCV documentation
261 262
    GAPI_WRAP GRunArgs apply(const cv::detail::ExtractArgsCallback  &callback,
                                   GCompileArgs                    &&args = {});
263

264
    /// @private -- Exclude this function from OpenCV documentation
265 266
    void apply(const std::vector<cv::Mat>& ins,                                   // Compatibility overload
               const std::vector<cv::Mat>& outs,
267
               GCompileArgs &&args = {});
268 269

    // 2. Syntax sugar and compatibility overloads
270
#if !defined(GAPI_STANDALONE)
271 272 273 274 275 276 277 278 279
    /**
     * @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.
     */
280
    void apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args = {}); // Unary overload
281 282 283 284 285 286 287 288 289 290

    /**
     * @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.
     */
291
    void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {}); // Unary overload (scalar)
292 293 294 295 296 297 298 299 300 301 302

    /**
     * @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.
     */
303
    void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload
304 305 306 307 308 309 310 311 312 313 314

    /**
     * @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.
     */
315
    void apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args = {}); // Binary overload (scalar)
316 317 318 319 320 321 322 323 324 325 326 327 328

    /**
     * @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.
     *
B
Brian Wignall 已提交
329
     * Numbers of elements in ins/outs vectors must match numbers of
330 331
     * inputs/outputs which were used to define this GComputation.
     */
332
    void apply(const std::vector<cv::Mat>& ins,         // Compatibility overload
333
                     std::vector<cv::Mat>& outs,
334
               GCompileArgs &&args = {});
335
#endif // !defined(GAPI_STANDALONE)
336 337
    // Various versions of compile(): //////////////////////////////////////////
    // 1. Generic compile() - requires metas to be passed as vector
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
    /**
     * @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
     */
359 360 361
    GCompiled compile(GMetaArgs &&in_metas, GCompileArgs &&args = {});

    // 2. Syntax sugar - variadic list of metas, no extra compile args
362 363 364 365 366 367 368 369 370 371
    // 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.
     */
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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)
    //
B
Brian Wignall 已提交
388
    // But not all compilers can handle this (and seems they shouldn't be able to).
389 390 391 392 393 394 395 396 397 398 399 400
    // 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.
     */
401 402 403 404 405 406 407 408 409 410 411
    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());
    }

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426

    // 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)
427 428 429 430 431 432
     * 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.
433 434 435 436 437 438 439
     *
     * @return GStreamingCompiled, a streaming-oriented executable
     * computation compiled specifically for the given input
     * parameters.
     *
     * @sa @ref gapi_compile_args
     */
440
    GAPI_WRAP GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {});
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    /**
     * @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
     */
461
    GAPI_WRAP GStreamingCompiled compileStreaming(GCompileArgs &&args = {});
462

463 464 465 466
    /// @private -- Exclude this function from OpenCV documentation
    GAPI_WRAP GStreamingCompiled compileStreaming(const cv::detail::ExtractMetaCallback &callback,
                                                        GCompileArgs                   &&args = {});

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
    // 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());
    }

509
    // Internal use only
510
    /// @private
511
    Priv& priv();
512
    /// @private
513
    const Priv& priv() const;
514
    /// @private
515
    explicit GComputation(cv::gapi::s11n::IIStream &);
516
    /// @private
517
    void serialize(cv::gapi::s11n::IOStream &) const;
518 519 520

protected:

521
    // 4. Helper methods for (3)
522
    /// @private
523 524 525 526 527 528 529
    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));
    }
530 531 532 533 534 535 536
    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));
    }
537
    void recompile(GMetaArgs&& in_metas, GCompileArgs &&args);
538
    /// @private
539 540
    std::shared_ptr<Priv> m_priv;
};
541
/** @} */
542 543 544

namespace gapi
{
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    // 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.
     */
574
    void GAPI_EXPORTS island(const std::string &name,
575 576
                             GProtoInputArgs  &&ins,
                             GProtoOutputArgs &&outs);
577 578 579 580
} // namespace gapi

} // namespace cv
#endif // OPENCV_GAPI_GCOMPUTATION_HPP