From d7540c9a3c877dbf1b1a041f9ad2fb352677721e Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Fri, 16 Nov 2018 23:38:10 +0300 Subject: [PATCH] Merge pull request #13176 from dmatveev:gapi_doxygen G-API: Doxygen class reference * G-API Doxygen documentation: covered cv::GComputation * G-API Doxygen documentation: added sections on compile arguments * G-API Doxygen documentation: restructuring & more text * Added new sections (organized API reference into it); * Documented GCompiled, compile args, backends, etc. * G-API Doxygen documentation: documented GKernelPackage and added group for meta --- modules/gapi/include/opencv2/gapi.hpp | 12 + .../include/opencv2/gapi/cpu/gcpukernel.hpp | 30 ++ .../opencv2/gapi/fluid/gfluidkernel.hpp | 11 + modules/gapi/include/opencv2/gapi/garray.hpp | 14 +- modules/gapi/include/opencv2/gapi/gcommon.hpp | 48 +++ .../gapi/include/opencv2/gapi/gcompiled.hpp | 163 ++++++++- .../include/opencv2/gapi/gcomputation.hpp | 337 +++++++++++++++++- modules/gapi/include/opencv2/gapi/gkernel.hpp | 172 ++++++++- modules/gapi/include/opencv2/gapi/gmat.hpp | 15 + .../include/opencv2/gapi/gpu/ggpukernel.hpp | 14 + modules/gapi/include/opencv2/gapi/gscalar.hpp | 16 +- modules/gapi/samples/api_ref_snippets.cpp | 82 +++++ 12 files changed, 880 insertions(+), 34 deletions(-) create mode 100644 modules/gapi/samples/api_ref_snippets.cpp diff --git a/modules/gapi/include/opencv2/gapi.hpp b/modules/gapi/include/opencv2/gapi.hpp index 8e5bb068c8..a043a83fc1 100644 --- a/modules/gapi/include/opencv2/gapi.hpp +++ b/modules/gapi/include/opencv2/gapi.hpp @@ -10,6 +10,18 @@ #include +/** \defgroup gapi G-API framework +@{ + @defgroup gapi_main_classes G-API Main Classes + @defgroup gapi_data_objects G-API Data Objects + @{ + @defgroup gapi_meta_args G-API Metadata Descriptors + @} + @defgroup gapi_std_backends G-API Standard backends + @defgroup gapi_compile_args G-API Graph Compilation Arguments +@} + */ + #include "opencv2/gapi/gmat.hpp" #include "opencv2/gapi/garray.hpp" #include "opencv2/gapi/gcomputation.hpp" diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index 02e06122b2..facaab6aa4 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -33,7 +33,37 @@ namespace gapi { namespace cpu { + /** + * \addtogroup gapi_std_backends + * @{ + * + * @brief G-API backends available in this OpenCV version + * + * G-API backends play a corner stone role in G-API execution + * stack. Every backend is hardware-oriented and thus can run its + * kernels efficiently on the target platform. + * + * Backends are usually "back boxes" for G-API users -- on the API + * side, all backends are represented as different objects of the + * same class cv::gapi::GBackend. User can manipulate with backends + * mainly by specifying which kernels to use or where to look up + * for kernels first. + * + * @sa @ref gapi_hld, cv::gapi::lookup_order() + */ + + /** + * @brief Get a reference to CPU (OpenCV) backend. + * + * This is the default backend in G-API at the moment, providing + * broader functional coverage but losing some graph model + * advantages. Provided mostly for reference and prototyping + * purposes. + * + * @sa gapi_std_backends + */ GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ } // namespace cpu } // namespace gapi diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp index fbe0436134..c71c5aa2cc 100644 --- a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -28,10 +28,21 @@ namespace gapi { namespace fluid { + /** + * \addtogroup gapi_std_backends G-API Standard backends + * @{ + */ + /** + * @brief Get a reference to Fluid backend. + * + * @sa gapi_std_backends + */ GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ } // namespace flud } // namespace gapi + class GAPI_EXPORTS GFluidKernel { public: diff --git a/modules/gapi/include/opencv2/gapi/garray.hpp b/modules/gapi/include/opencv2/gapi/garray.hpp index 33d9b582e6..87d00155b2 100644 --- a/modules/gapi/include/opencv2/gapi/garray.hpp +++ b/modules/gapi/include/opencv2/gapi/garray.hpp @@ -29,6 +29,10 @@ struct GOrigin; template class GArray; +/** + * \addtogroup gapi_meta_args + * @{ + */ struct GArrayDesc { // FIXME: Body @@ -36,7 +40,9 @@ struct GArrayDesc bool operator== (const GArrayDesc&) const { return true; } }; template GArrayDesc descr_of(const std::vector &) { return {};} -inline GArrayDesc empty_array_desc() {return {}; } +static inline GArrayDesc empty_array_desc() {return {}; } +/** @} */ + std::ostream& operator<<(std::ostream& os, const cv::GArrayDesc &desc); namespace detail @@ -218,6 +224,10 @@ namespace detail }; } // namespace detail +/** \addtogroup gapi_data_objects + * @{ + */ + template class GArray { public: @@ -234,6 +244,8 @@ private: detail::GArrayU m_ref; }; +/** @} */ + } // namespace cv #endif // OPENCV_GAPI_GARRAY_HPP diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp index e84c82c9c8..6a3f51f779 100644 --- a/modules/gapi/include/opencv2/gapi/gcommon.hpp +++ b/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -53,6 +53,41 @@ namespace detail { // CompileArg is an unified interface over backend-specific compilation // information // FIXME: Move to a separate file? +/** \addtogroup gapi_compile_args + * @{ + * + * @brief Compilation arguments: a set of data structures which can be + * passed to control compilation process + * + * G-API comes with a number of graph compilation options which can be + * passed to cv::GComputation::apply() or + * cv::GComputation::compile(). Known compilation options are listed + * in this page, while extra backends may introduce their own + * compilation options (G-API transparently accepts _everything_ which + * can be passed to cv::compile_args(), it depends on underlying + * backends if an option would be interpreted or not). + * + * For example, if an example computation is executed like this: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_decl_apply + * + * Extra parameter specifying which kernels to compile with can be + * passed like this: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp apply_with_param + */ + +/** + * @brief Represents an arbitrary compilation argument. + * + * Any value can be wrapped into cv::GCompileArg, but only known ones + * (to G-API or its backends) can be interpreted correctly. + * + * Normally objects of this class shouldn't be created manually, use + * cv::compile_args() function which automatically wraps everything + * passed in (a variadic template parameter pack) into a vector of + * cv::GCompileArg objects. + */ struct GAPI_EXPORTS GCompileArg { public: @@ -82,15 +117,28 @@ private: using GCompileArgs = std::vector; +/** + * Wraps a list of arguments (a parameter pack) into a vector of + * compilation arguments (cv::GCompileArg). + */ template GCompileArgs compile_args(Ts&&... args) { return GCompileArgs{ GCompileArg(args)... }; } +/** + * @brief Ask G-API to dump compiled graph in Graphviz format under + * the given file name. + * + * Specifies a graph dump path (path to .dot file to be generated). + * G-API will dump a .dot file under specified path during a + * compilation process if this flag is passed. + */ struct graph_dump_path { std::string m_dump_path; }; +/** @} */ namespace detail { diff --git a/modules/gapi/include/opencv2/gapi/gcompiled.hpp b/modules/gapi/include/opencv2/gapi/gcompiled.hpp index 70011a8afc..ad491b7337 100644 --- a/modules/gapi/include/opencv2/gapi/gcompiled.hpp +++ b/modules/gapi/include/opencv2/gapi/gcompiled.hpp @@ -27,35 +27,190 @@ namespace cv { // FIXME: In future, there should be a way to name I/O objects and specify it // to GCompiled externally (for example, when it is loaded on the target system). +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @brief Represents a compiled computation (graph). Can only be used + * with image / data formats & resolutions it was compiled for, with + * some exceptions. + * + * This class represents a product of graph compilation (calling + * cv::GComputation::compile()). Objects of this class actually do + * data processing, and graph execution is incapsulated into objects + * of this class. Execution model itself depends on kernels and + * backends which were using during the compilation, see @ref + * gapi_compile_args for details. + * + * In a general case, GCompiled objects can be applied to data only in + * that formats/resolutions they were compiled for (see @ref + * gapi_meta_args). However, if the underlying backends allow, a + * compiled object can be _reshaped_ to handle data (images) of + * different resolution, though formats and types must remain the same. + * + * GCompiled is very similar to `std::function<>` in its semantics -- + * running it looks like a function call in the user code. + * + * At the moment, GCompiled objects are not reentrant -- generally, + * the objects are stateful since graph execution itself is a stateful + * process and this state is now maintained in GCompiled's own memory + * (not on the process stack). + * + * At the same time, two different GCompiled objects produced from the + * single cv::GComputation are completely independent and can be used + * concurrently. + */ class GAPI_EXPORTS GCompiled { public: + /// @private class GAPI_EXPORTS Priv; + /** + * @brief Constructs an empty object + */ GCompiled(); + /** + * @brief Run the compiled computation, a generic version. + * + * @param ins vector of inputs to process. + * @param outs vector of outputs to produce. + * + * Input/output vectors must have the same number of elements as + * defined in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::Mat needs to be passed where cv::GMat has been + * declared as input, and so on). Run-time exception is generated + * otherwise. + * + * Objects in output vector may remain empty (like cv::Mat) -- + * G-API will automatically initialize output objects to proper formats. + * + * @note Don't construct GRunArgs/GRunArgsP objects manually, use + * cv::gin()/cv::gout() wrappers instead. + */ void operator() (GRunArgs &&ins, GRunArgsP &&outs); // Generic arg-to-arg #if !defined(GAPI_STANDALONE) + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Mat for unary computation + * process. + */ void operator() (cv::Mat in, cv::Mat &out); // Unary overload + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Scalar for unary computation + * process. + */ void operator() (cv::Mat in, cv::Scalar &out); // Unary overload (scalar) + + /** + * @brief Execute a binary computation + * + * @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 + * process. + */ void operator() (cv::Mat in1, cv::Mat in2, cv::Mat &out); // Binary overload + + /** + * @brief Execute an binary computation + * + * @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 + * process. + */ void operator() (cv::Mat in1, cv::Mat in2, cv::Scalar &out); // Binary overload (scalar) + + /** + * @brief Execute a computation with arbitrary number of + * inputs/outputs. + * + * @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. + * + * Numbers of elements in ins/outs vectos must match numbers of + * inputs/outputs which were used to define the source GComputation. + */ void operator() (const std::vector &ins, // Compatibility overload const std::vector &outs); #endif // !defined(GAPI_STANDALONE) + /// @private Priv& priv(); - explicit operator bool () const; // Check if GCompiled is runnable or empty + /** + * @brief Check if compiled object is valid (non-empty) + * + * @return true if the object is runnable (valid), false otherwise + */ + explicit operator bool () const; + /** + * @brief Vector of metadata this graph was compiled for. + * + * @return Unless _reshape_ is not supported, return value is the + * same vector which was passed to cv::GComputation::compile() to + * produce this compiled object. Otherwise, it is the latest + * metadata vector passed to reshape() (if that call was + * successful). + */ const GMetaArgs& metas() const; // Meta passed to compile() - const GMetaArgs& outMetas() const; // Inferred output metadata - bool canReshape() const; // is reshape mechanism supported by GCompiled - void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); // run reshape procedure + /** + * @brief Vector of metadata descriptions of graph outputs + * + * @return vector with formats/resolutions of graph's output + * objects, auto-inferred from input metadata vector by + * operations which form this computation. + * + * @note GCompiled objects produced from the same + * cv::GComputiation graph with different input metas may return + * different values in this vector. + */ + const GMetaArgs& outMetas() const; + + /** + * @brief Check if the underlying backends support reshape or not. + * + * @return true if supported, false otherwise. + */ + bool canReshape() const; + + /** + * @brief Reshape a compiled graph to support new image + * resolutions. + * + * Throws an exception if an error occurs. + * + * @param inMetas new metadata to reshape on. Vector size and + * metadata shapes must match the computation's protocol. + * @param args compilation arguments to use. + */ + // FIXME: Why it requires compile args? + void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); protected: + /// @private std::shared_ptr m_priv; }; +/** @} */ } diff --git a/modules/gapi/include/opencv2/gapi/gcomputation.hpp b/modules/gapi/include/opencv2/gapi/gcomputation.hpp index 4457cfae8f..e89b9ae399 100644 --- a/modules/gapi/include/opencv2/gapi/gcomputation.hpp +++ b/modules/gapi/include/opencv2/gapi/gcomputation.hpp @@ -35,6 +35,76 @@ namespace detail using last_type_t = typename last_type::type; } +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @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 + * computaion 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: @@ -43,40 +113,247 @@ public: // Various constructors enable different ways to define a computation: ///// // 1. Generic constructors - GComputation(const Generator& gen); // Generator overload + /** + * @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 unecessary 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 - GComputation(GMat in1, GMat in2, GScalar out); // Binary 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 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 &ins, // Compatibility overload const std::vector &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& ins, // Compatibility overload const std::vector& 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& ins, // Compatibility overload const std::vector& 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 auto compile(const Ts&... metas) -> typename std::enable_if::value, GCompiled>::type @@ -94,6 +371,18 @@ public: // 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 auto compile(const Ts&... meta_and_compile_args) -> typename std::enable_if::value @@ -106,12 +395,15 @@ public: } // Internal use only + /// @private Priv& priv(); + /// @private const Priv& priv() const; protected: // 4. Helper method for (3) + /// @private template GCompiled compile(const std::tuple &meta_and_compile_args, detail::Seq) { @@ -119,20 +411,45 @@ protected: GCompileArgs comp_args = std::get(meta_and_compile_args); return compile(std::move(meta_args), std::move(comp_args)); } - + /// @private std::shared_ptr m_priv; }; +/** @} */ namespace gapi { - // 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. + // 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); + GProtoInputArgs &&ins, + GProtoOutputArgs &&outs); } // namespace gapi } // namespace cv diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp index 0b6c690034..adc7da3c7a 100644 --- a/modules/gapi/include/opencv2/gapi/gkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -294,34 +294,102 @@ namespace std namespace cv { namespace gapi { + /** \addtogroup gapi_compile_args + * @{ + */ + // Lookup order is in fact a vector of Backends to traverse during look-up + /** + * @brief Priority list of backends to use during kernel + * resolution process. + * + * Priority is descending -- the first backend in the list has the + * top priority, and the last one has the lowest priority. + * + * If there's multiple implementations available for a kernel at + * the moment of graph compilation, a kernel (and thus a backend) + * will be selected according to this order (if the parameter is passed). + * + * Default order is not specified (and by default, only + * CPU(OpenCV) backend is involved in graph compilation). + */ using GLookupOrder = std::vector; + /** + * @brief Create a backend lookup order -- priority list of + * backends to use during graph compilation process. + * + * @sa GLookupOrder, @ref gapi_std_backends + */ inline GLookupOrder lookup_order(std::initializer_list &&list) { return GLookupOrder(std::move(list)); } // FIXME: Hide implementation + /** + * @brief A container class for heterogeneous kernel + * implementation collections. + * + * GKernelPackage is a special container class which stores kernel + * _implementations_. Objects of this class are created and passed + * to cv::GComputation::compile() to specify which kernels to use + * in the compiled graph. GKernelPackage may contain kernels of + * different backends, e.g. be heterogeneous. + * + * The most easy way to create a kernel package is to use function + * cv::gapi::kernels(). This template functions takes kernel + * implementations in form of type list (variadic template) and + * generates a kernel package atop of that. + * + * Kernel packages can be also generated programatically, starting + * with an empty package (created with the default constructor) + * and then by populating it with kernels via call to + * GKernelPackage::include(). Note this method is also a template + * one since G-API kernel implementations are _types_, not objects. + * + * Finally, two kernel packages can be combined into a new one + * with function cv::gapi::combine(). There are different rules + * apply to this process, see also cv::gapi::unite_policy for + * details. + */ class GAPI_EXPORTS GKernelPackage { + /// @private using S = std::unordered_map; + + /// @private using M = std::unordered_map; + + /// @private M m_backend_kernels; protected: + /// @private // Check if package contains ANY implementation of a kernel API // by API textual id. bool includesAPI(const std::string &id) const; + /// @private // Remove ALL implementations of the given API (identified by ID) void removeAPI(const std::string &id); public: - // Return total number of kernels (accross all backends) + /** + * @brief Returns total number of kernels in the package + * (accross all backends included) + * + * @return a number of kernels in the package + */ std::size_t size() const; - // Check if particular kernel implementation exist in the package. - // The key word here is _particular_ - i.e., from the specific backend. + /** + * @brief Test if a particular kernel _implementation_ KImpl is + * included in this kernel package. + * + * @sa includesAPI() + * + * @return true if there is such kernel, false otherwise. + */ template bool includes() const { @@ -331,40 +399,71 @@ namespace gapi { : false; } - // Removes all the kernels related to the given backend + /** + * @brief Remove all kernels associated with the given backend + * from the package. + * + * Does nothing if there's no kernels of this backend in the package. + * + * @param backend backend which kernels to remove + */ void remove(const GBackend& backend); + /** + * @brief Remove all kernels implementing the given API from + * the package. + * + * Does nothing if there's no kernels implementing the given interface. + */ template void remove() { removeAPI(KAPI::id()); } - // Check if package contains ANY implementation of a kernel API - // by API type. // FIXME: Rename to includes() and distinguish API/impl case by // statically? + /** + * Check if package contains ANY implementation of a kernel API + * by API type. + */ template bool includesAPI() const { return includesAPI(KAPI::id()); } - // Lookup a kernel, given the look-up order. Returns Backend which - // hosts kernel implementation. Throws if nothing found. - // - // If order is empty(), returns first suitable implementation. + /** + * @brief Find a kernel (by its API), given the look-up order. + * + * If order is empty, returns first suitable implementation. + * Throws if nothing found. + * + * @return Backend which hosts matching kernel implementation. + * + * @sa cv::gapi::lookup_order + */ template GBackend lookup(const GLookupOrder &order = {}) const { return lookup(KAPI::id(), order).first; } + /// @private std::pair lookup(const std::string &id, const GLookupOrder &order = {}) const; - // Put a new kernel implementation into package // FIXME: No overwrites allowed? + /** + * @brief Put a new kernel implementation KImpl into package. + * + * @param up unite policy to use. If the package has already + * implementation for this kernel (probably from another + * backend), and cv::unite_policy::KEEP is passed, the + * existing implementation remains in package; on + * cv::unite_policy::REPLACE all other existing + * implementations are first dropped from the package. + */ template void include(const cv::unite_policy up = cv::unite_policy::KEEP) { @@ -378,14 +477,53 @@ namespace gapi { m_backend_kernels[backend][kernel_id] = std::move(kernel_impl); } - // Lists all backends which are included into package + /** + * @brief Lists all backends which are included into package + * + * @return vector of backends + */ std::vector backends() const; - friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &, - const GKernelPackage &, - const cv::unite_policy); + // TODO: Doxygen bug -- it wants me to place this comment + // here, not below. + /** + * @brief Create a new package based on `lhs` and `rhs`, + * with unity policy defined by `policy`. + * + * @param lhs "Left-hand-side" package in the process + * @param rhs "Right-hand-side" package in the process + * @param policy Unite policy which is used in case of conflicts + * -- when the same kernel API is implemented in both packages by + * different backends; cv::unite_policy::KEEP keeps both + * implementation in the resulting package, while + * cv::unite_policy::REPLACE gives precedence two kernels from + * "Right-hand-side". + * + * @return a new kernel package. + */ + friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs, + const GKernelPackage &rhs, + const cv::unite_policy policy); }; + /** + * @brief Create a kernel package object containing kernels + * specified in variadic template argument. + * + * In G-API, kernel implementations are _types_. Every backend has + * its own kernel API (like GAPI_OCV_KERNEL() and + * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for + * each kernel implementation. + * + * Use this function to pass kernel implementations (defined in + * either way) to the system. Example: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet + * + * Note that kernels() itself is a function returning object, not + * a type, so having `()` at the end is important -- it must be a + * function call. + */ template GKernelPackage kernels() { GKernelPackage pkg; @@ -402,8 +540,8 @@ namespace gapi { return pkg; }; - // Return a new package based on `lhs` and `rhs`, - // with unity policy defined by `policy`. + /** @} */ + GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs, const GKernelPackage &rhs, const cv::unite_policy policy); diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp index c8b3eb799c..0fa53427d4 100644 --- a/modules/gapi/include/opencv2/gapi/gmat.hpp +++ b/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -26,6 +26,13 @@ namespace cv class GNode; struct GOrigin; +/** \addtogroup gapi_data_objects + * @{ + * + * @brief Data-representing objects which can be used to build G-API + * expressions. + */ + class GAPI_EXPORTS GMat { public: @@ -39,6 +46,12 @@ private: std::shared_ptr m_priv; }; +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ struct GAPI_EXPORTS GMatDesc { // FIXME: Default initializers in C++14 @@ -122,6 +135,8 @@ GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat); GAPI_EXPORTS GMatDesc descr_of(const cv::UMat &mat); #endif // !defined(GAPI_STANDALONE) +/** @} */ + namespace gapi { namespace own { class Mat; GAPI_EXPORTS GMatDesc descr_of(const Mat &mat); diff --git a/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp b/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp index dfae71af33..e5a6215e3c 100644 --- a/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp @@ -31,10 +31,24 @@ namespace gapi { namespace gpu { + /** + * \addtogroup gapi_std_backends G-API Standard backends + * @{ + */ + /** + * @brief Get a reference to GPU backend. + * + * At the moment, the GPU backend is built atop of OpenCV + * "Transparent API" (T-API), see cv::UMat for details. + * + * @sa gapi_std_backends + */ GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ } // namespace gpu } // namespace gapi + // Represents arguments which are passed to a wrapped GPU function // FIXME: put into detail? class GAPI_EXPORTS GGPUContext diff --git a/modules/gapi/include/opencv2/gapi/gscalar.hpp b/modules/gapi/include/opencv2/gapi/gscalar.hpp index 944f179f3b..dd1205b638 100644 --- a/modules/gapi/include/opencv2/gapi/gscalar.hpp +++ b/modules/gapi/include/opencv2/gapi/gscalar.hpp @@ -1,4 +1,5 @@ // 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. // @@ -22,6 +23,10 @@ namespace cv class GNode; struct GOrigin; +/** \addtogroup gapi_data_objects + * @{ + */ + class GAPI_EXPORTS GScalar { public: @@ -41,6 +46,12 @@ private: std::shared_ptr m_priv; }; +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ struct GScalarDesc { // NB.: right now it is empty @@ -58,11 +69,12 @@ struct GScalarDesc static inline GScalarDesc empty_scalar_desc() { return GScalarDesc(); } -GAPI_EXPORTS GScalarDesc descr_of(const cv::gapi::own::Scalar &scalar); - #if !defined(GAPI_STANDALONE) GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar &scalar); #endif // !defined(GAPI_STANDALONE) +/** @} */ + +GAPI_EXPORTS GScalarDesc descr_of(const cv::gapi::own::Scalar &scalar); std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &desc); diff --git a/modules/gapi/samples/api_ref_snippets.cpp b/modules/gapi/samples/api_ref_snippets.cpp new file mode 100644 index 0000000000..5e8859da19 --- /dev/null +++ b/modules/gapi/samples/api_ref_snippets.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include + +G_TYPED_KERNEL(IAdd, , "test.custom.add") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +G_TYPED_KERNEL(IFilter2D, , "test.custom.filter2d") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +G_TYPED_KERNEL(IRGB2YUV, , "test.custom.add") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +GAPI_OCV_KERNEL(CustomAdd, IAdd) { static void run(cv::Mat, cv::Mat &) {} }; +GAPI_OCV_KERNEL(CustomFilter2D, IFilter2D) { static void run(cv::Mat, cv::Mat &) {} }; +GAPI_OCV_KERNEL(CustomRGB2YUV, IRGB2YUV) { static void run(cv::Mat, cv::Mat &) {} }; + +int main(int argc, char *argv[]) +{ + if (argc < 3) + return -1; + + cv::Mat input = cv::imread(argv[1]); + cv::Mat output; + + { + //! [graph_def] + cv::GMat in; + cv::GMat gx = cv::gapi::Sobel(in, CV_32F, 1, 0); + cv::GMat gy = cv::gapi::Sobel(in, CV_32F, 0, 1); + cv::GMat g = cv::gapi::sqrt(cv::gapi::mul(gx, gx) + cv::gapi::mul(gy, gy)); + cv::GMat out = cv::gapi::convertTo(g, CV_8U); + //! [graph_def] + + //! [graph_decl_apply] + //! [graph_cap_full] + cv::GComputation sobelEdge(cv::GIn(in), cv::GOut(out)); + //! [graph_cap_full] + sobelEdge.apply(input, output); + //! [graph_decl_apply] + + //! [apply_with_param] + cv::gapi::GKernelPackage kernels = cv::gapi::combine + (cv::gapi::core::fluid::kernels(), + cv::gapi::imgproc::fluid::kernels(), + cv::unite_policy::KEEP); + sobelEdge.apply(input, output, cv::compile_args(kernels)); + //! [apply_with_param] + + //! [graph_cap_sub] + cv::GComputation sobelEdgeSub(cv::GIn(gx, gy), cv::GOut(out)); + //! [graph_cap_sub] + } + //! [graph_gen] + cv::GComputation sobelEdgeGen([](){ + cv::GMat in; + cv::GMat gx = cv::gapi::Sobel(in, CV_32F, 1, 0); + cv::GMat gy = cv::gapi::Sobel(in, CV_32F, 0, 1); + cv::GMat g = cv::gapi::sqrt(cv::gapi::mul(gx, gx) + cv::gapi::mul(gy, gy)); + cv::GMat out = cv::gapi::convertTo(g, CV_8U); + return cv::GComputation(in, out); + }); + //! [graph_gen] + + cv::imwrite(argv[2], output); + + //! [kernels_snippet] + cv::gapi::GKernelPackage pkg = cv::gapi::kernels + < CustomAdd + , CustomFilter2D + , CustomRGB2YUV + >(); + //! [kernels_snippet] + return 0; +}