Merge pull request #12608 from dmatveev:gapi
authorDmitry Matveev <me@dmitrymatveev.co.uk>
Wed, 26 Sep 2018 18:50:39 +0000 (21:50 +0300)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Wed, 26 Sep 2018 18:50:39 +0000 (21:50 +0300)
* G-API Initial code upload

* Update G-API code base to Sep-24-2018

* The majority of OpenCV buildbot problems was addressed

* Update G-API code base to 24-Sep-18 EOD

* G-API code base update 25-Sep-2018

* Linux warnings should be resolved
* Documentation build should become green
* Number of Windows warnings should be reduced

* Update G-API code base to 25-Sep-18 EOD

* ARMv7 build issue should be resolved
* ADE is bumped to latest version and should fix Clang builds for macOS/iOS
* Remaining Windows warnings should be resolved
* New Linux32 / ARMv7 warnings should be resolved

* G-API code base update 25-Sep-2018-EOD2

* Final Windows warnings should be resolved now

* G-API code base update 26-Sep-2018

* Fixed issues with precompiled headers in module and its tests

166 files changed:
modules/gapi/CMakeLists.txt [new file with mode: 0644]
modules/gapi/cmake/DownloadADE.cmake [new file with mode: 0644]
modules/gapi/cmake/init.cmake [new file with mode: 0644]
modules/gapi/doc/intro.markdown [new file with mode: 0644]
modules/gapi/include/opencv2/gapi.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/core.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/cpu/core.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/garg.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/garray.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gcall.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gcommon.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gcompiled.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gcomputation.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gkernel.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gmat.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gmetaarg.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gproto.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gscalar.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gtype_traits.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/gtyped.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/imgproc.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/opencv_includes.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/operators.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/assert.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/convert.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/exports.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/mat.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/scalar.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/own/types.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/any.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/optional.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/throw.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/util.hpp [new file with mode: 0644]
modules/gapi/include/opencv2/gapi/util/variant.hpp [new file with mode: 0644]
modules/gapi/perf/common/gapi_core_perf_tests.hpp [new file with mode: 0644]
modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp [new file with mode: 0644]
modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp [new file with mode: 0644]
modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp [new file with mode: 0644]
modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp [new file with mode: 0644]
modules/gapi/perf/perf_main.cpp [new file with mode: 0644]
modules/gapi/perf/perf_precomp.hpp [new file with mode: 0644]
modules/gapi/src/api/README.md [new file with mode: 0644]
modules/gapi/src/api/gapi_priv.cpp [new file with mode: 0644]
modules/gapi/src/api/gapi_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/garray.cpp [new file with mode: 0644]
modules/gapi/src/api/gbackend.cpp [new file with mode: 0644]
modules/gapi/src/api/gbackend_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/gcall.cpp [new file with mode: 0644]
modules/gapi/src/api/gcall_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/gcomputation.cpp [new file with mode: 0644]
modules/gapi/src/api/gcomputation_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/gkernel.cpp [new file with mode: 0644]
modules/gapi/src/api/gmat.cpp [new file with mode: 0644]
modules/gapi/src/api/gnode.cpp [new file with mode: 0644]
modules/gapi/src/api/gnode.hpp [new file with mode: 0644]
modules/gapi/src/api/gnode_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/gproto.cpp [new file with mode: 0644]
modules/gapi/src/api/gproto_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/gscalar.cpp [new file with mode: 0644]
modules/gapi/src/api/kernels_core.cpp [new file with mode: 0644]
modules/gapi/src/api/kernels_imgproc.cpp [new file with mode: 0644]
modules/gapi/src/api/operators.cpp [new file with mode: 0644]
modules/gapi/src/backends/README.md [new file with mode: 0644]
modules/gapi/src/backends/common/gbackend.hpp [new file with mode: 0644]
modules/gapi/src/backends/common/gcompoundbackend.cpp [new file with mode: 0644]
modules/gapi/src/backends/common/gcompoundkernel.cpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpubackend.cpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpubackend.hpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpucore.cpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpucore.hpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpuimgproc.cpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpuimgproc.hpp [new file with mode: 0644]
modules/gapi/src/backends/cpu/gcpukernel.cpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidbackend.cpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidbackend.hpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidbuffer.cpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidcore.cpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidcore.hpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidimgproc.cpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidimgproc.hpp [new file with mode: 0644]
modules/gapi/src/backends/fluid/gfluidutils.hpp [new file with mode: 0644]
modules/gapi/src/compiler/README.md [new file with mode: 0644]
modules/gapi/src/compiler/gcompiled.cpp [new file with mode: 0644]
modules/gapi/src/compiler/gcompiled_priv.hpp [new file with mode: 0644]
modules/gapi/src/compiler/gcompiler.cpp [new file with mode: 0644]
modules/gapi/src/compiler/gcompiler.hpp [new file with mode: 0644]
modules/gapi/src/compiler/gislandmodel.cpp [new file with mode: 0644]
modules/gapi/src/compiler/gislandmodel.hpp [new file with mode: 0644]
modules/gapi/src/compiler/gmodel.cpp [new file with mode: 0644]
modules/gapi/src/compiler/gmodel.hpp [new file with mode: 0644]
modules/gapi/src/compiler/gmodelbuilder.cpp [new file with mode: 0644]
modules/gapi/src/compiler/gmodelbuilder.hpp [new file with mode: 0644]
modules/gapi/src/compiler/gobjref.hpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/dump_dot.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/exec.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/helpers.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/helpers.hpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/islands.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/kernels.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/meta.cpp [new file with mode: 0644]
modules/gapi/src/compiler/passes/passes.hpp [new file with mode: 0644]
modules/gapi/src/compiler/transactions.hpp [new file with mode: 0644]
modules/gapi/src/executor/gexecutor.cpp [new file with mode: 0644]
modules/gapi/src/executor/gexecutor.hpp [new file with mode: 0644]
modules/gapi/src/logger.hpp [new file with mode: 0644]
modules/gapi/src/precomp.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_compoundkernel_tests.cpp [new file with mode: 0644]
modules/gapi/test/common/gapi_core_tests.cpp [new file with mode: 0644]
modules/gapi/test/common/gapi_core_tests.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_core_tests_inl.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_imgproc_tests.cpp [new file with mode: 0644]
modules/gapi/test/common/gapi_imgproc_tests.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_imgproc_tests_inl.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_operators_tests.cpp [new file with mode: 0644]
modules/gapi/test/common/gapi_operators_tests.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_operators_tests_inl.hpp [new file with mode: 0644]
modules/gapi/test/common/gapi_tests_common.hpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_core_tests_cpu.cpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_core_tests_fluid.cpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp [new file with mode: 0644]
modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp [new file with mode: 0644]
modules/gapi/test/gapi_array_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_basic_hetero_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_desc_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_fluid_resize_test.cpp [new file with mode: 0644]
modules/gapi/test/gapi_fluid_roi_test.cpp [new file with mode: 0644]
modules/gapi/test/gapi_fluid_test.cpp [new file with mode: 0644]
modules/gapi/test/gapi_fluid_test_kernels.cpp [new file with mode: 0644]
modules/gapi/test/gapi_fluid_test_kernels.hpp [new file with mode: 0644]
modules/gapi/test/gapi_gcompiled_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_gcomputation_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_kernel_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_mock_kernels.hpp [new file with mode: 0644]
modules/gapi/test/gapi_sample_pipelines.cpp [new file with mode: 0644]
modules/gapi/test/gapi_scalar_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_smoke_test.cpp [new file with mode: 0644]
modules/gapi/test/gapi_typed_tests.cpp [new file with mode: 0644]
modules/gapi/test/gapi_util_tests.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_backend_tests.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_executor_tests.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_garg_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_island_tests.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_recompilation_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_int_vectorref_test.cpp [new file with mode: 0644]
modules/gapi/test/internal/gapi_transactions_test.cpp [new file with mode: 0644]
modules/gapi/test/own/gapi_types_tests.cpp [new file with mode: 0644]
modules/gapi/test/own/mat_tests.cpp [new file with mode: 0644]
modules/gapi/test/own/scalar_tests.cpp [new file with mode: 0644]
modules/gapi/test/test_main.cpp [new file with mode: 0644]
modules/gapi/test/test_precomp.hpp [new file with mode: 0644]
modules/gapi/test/util/any_tests.cpp [new file with mode: 0644]
modules/gapi/test/util/optional_tests.cpp [new file with mode: 0644]
modules/gapi/test/util/variant_tests.cpp [new file with mode: 0644]

diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt
new file mode 100644 (file)
index 0000000..de0ced4
--- /dev/null
@@ -0,0 +1,94 @@
+# FIXME: Remove CXX11 check after complete switch to OpenCV 4 branch
+# (CI, bundle, workloads, etc)
+if (NOT HAVE_CXX11 OR NOT TARGET ade)
+  # can't build G-API because of the above reasons
+  ocv_module_disable(gapi)
+  return()
+endif()
+
+set(the_description "OpenCV G-API Core Module")
+ocv_add_module(gapi opencv_imgproc)
+
+file(GLOB gapi_ext_hdrs
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/*.hpp"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.h"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/util/*.hpp"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cpu/*.hpp"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/fluid/*.hpp"
+    "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/own/*.hpp"
+    )
+
+set(gapi_srcs
+    # Front-end part
+    src/api/gapi_priv.cpp
+    src/api/gmat.cpp
+    src/api/garray.cpp
+    src/api/gscalar.cpp
+    src/api/gkernel.cpp
+    src/api/gbackend.cpp
+    src/api/gproto.cpp
+    src/api/gnode.cpp
+    src/api/gcall.cpp
+    src/api/gcomputation.cpp
+    src/api/operators.cpp
+    src/api/kernels_core.cpp
+    src/api/kernels_imgproc.cpp
+
+    # Compiler part
+    src/compiler/gmodel.cpp
+    src/compiler/gmodelbuilder.cpp
+    src/compiler/gislandmodel.cpp
+    src/compiler/gcompiler.cpp
+    src/compiler/gcompiled.cpp
+    src/compiler/passes/helpers.cpp
+    src/compiler/passes/dump_dot.cpp
+    src/compiler/passes/islands.cpp
+    src/compiler/passes/meta.cpp
+    src/compiler/passes/kernels.cpp
+    src/compiler/passes/exec.cpp
+
+    # Executor
+    src/executor/gexecutor.cpp
+
+    # CPU Backend (currently built-in)
+    src/backends/cpu/gcpubackend.cpp
+    src/backends/cpu/gcpukernel.cpp
+    src/backends/cpu/gcpuimgproc.cpp
+    src/backends/cpu/gcpucore.cpp
+
+    # Fluid Backend (also built-in, FIXME:move away)
+    src/backends/fluid/gfluidbuffer.cpp
+    src/backends/fluid/gfluidbackend.cpp
+    src/backends/fluid/gfluidimgproc.cpp
+    src/backends/fluid/gfluidcore.cpp
+
+    # Compound
+    src/backends/common/gcompoundbackend.cpp
+    src/backends/common/gcompoundkernel.cpp
+    )
+
+ocv_list_add_prefix(gapi_srcs "${CMAKE_CURRENT_LIST_DIR}/")
+
+# For IDE users
+ocv_source_group("Src"     FILES ${gapi_srcs})
+ocv_source_group("Include" FILES ${gapi_ext_hdrs})
+
+ocv_set_module_sources(HEADERS ${gapi_ext_hdrs} SOURCES ${gapi_srcs})
+ocv_module_include_directories("${CMAKE_CURRENT_LIST_DIR}/src")
+
+# Note `ade` is not a module name but link dependency for ${the_module}
+# (which is opencv_gapi)
+ocv_create_module(ade)
+
+ocv_add_accuracy_tests()
+# FIXME: test binary is linked with ADE directly since ADE symbols
+# are not exported from libopencv_gapi.so in any form - thus
+# there're two copies of ADE code in memory when tests run (!)
+# src/ is specified to include dirs for INTERNAL tests only.
+if(TARGET opencv_test_gapi)
+  target_include_directories(opencv_test_gapi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src")
+  target_link_libraries(opencv_test_gapi PRIVATE ade)
+endif()
+
+ocv_add_perf_tests()
diff --git a/modules/gapi/cmake/DownloadADE.cmake b/modules/gapi/cmake/DownloadADE.cmake
new file mode 100644 (file)
index 0000000..0ffd59e
--- /dev/null
@@ -0,0 +1,36 @@
+if(ANDROID)
+  # FIXME: Android build will be enabled separately
+  return()
+endif()
+
+set(ade_src_dir "${OpenCV_BINARY_DIR}/3rdparty/ade")
+set(ade_filename "v0.1.1c.zip")
+set(ade_subdir "ade-0.1.1c")
+set(ade_md5 "db7e6a260229ee562a1b2857df473af8")
+ocv_download(FILENAME ${ade_filename}
+             HASH ${ade_md5}
+             URL
+               "${OPENCV_ADE_URL}"
+               "$ENV{OPENCV_ADE_URL}"
+               "https://github.com/opencv/ade/archive/"
+             DESTINATION_DIR ${ade_src_dir}
+             ID ADE
+             STATUS res
+             UNPACK RELATIVE_URL)
+
+if (NOT res)
+    return()
+endif()
+
+set(ADE_root "${ade_src_dir}/${ade_subdir}/sources/ade")
+file(GLOB_RECURSE ADE_sources "${ADE_root}/source/*.cpp")
+file(GLOB_RECURSE ADE_include "${ADE_root}/include/ade/*.hpp")
+add_library(ade STATIC ${ADE_include} ${ADE_sources})
+target_include_directories(ade PUBLIC $<BUILD_INTERFACE:${ADE_root}/include>)
+set_target_properties(ade PROPERTIES POSITION_INDEPENDENT_CODE True)
+
+if(NOT BUILD_SHARED_LIBS)
+  ocv_install_target(ade EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
+endif()
+
+ocv_install_3rdparty_licenses(ade "${ade_src_dir}/${ade_subdir}/LICENSE")
diff --git a/modules/gapi/cmake/init.cmake b/modules/gapi/cmake/init.cmake
new file mode 100644 (file)
index 0000000..9f6ebef
--- /dev/null
@@ -0,0 +1,11 @@
+if (ade_DIR)
+  # if ade_DIR is set, use ADE-supplied CMake script
+  # to set up variables to the prebuilt ADE
+  find_package(ade 0.1.0)
+endif()
+
+if(NOT TARGET ade)
+  # if ade_DIR is not set, try to use automatically
+  # downloaded one (if there any)
+  include("${CMAKE_CURRENT_LIST_DIR}/DownloadADE.cmake")
+endif()
diff --git a/modules/gapi/doc/intro.markdown b/modules/gapi/doc/intro.markdown
new file mode 100644 (file)
index 0000000..ae06174
--- /dev/null
@@ -0,0 +1,3 @@
+# Graph API {#gapi}
+
+Introduction to G-API (WIP).
\ No newline at end of file
diff --git a/modules/gapi/include/opencv2/gapi.hpp b/modules/gapi/include/opencv2/gapi.hpp
new file mode 100644 (file)
index 0000000..8e5bb06
--- /dev/null
@@ -0,0 +1,21 @@
+// 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_HPP
+#define OPENCV_GAPI_HPP
+
+#include <memory>
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/garray.hpp"
+#include "opencv2/gapi/gcomputation.hpp"
+#include "opencv2/gapi/gcompiled.hpp"
+#include "opencv2/gapi/gtyped.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/operators.hpp"
+
+#endif // OPENCV_GAPI_HPP
diff --git a/modules/gapi/include/opencv2/gapi/core.hpp b/modules/gapi/include/opencv2/gapi/core.hpp
new file mode 100644 (file)
index 0000000..9ee45de
--- /dev/null
@@ -0,0 +1,1559 @@
+// 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_CORE_HPP
+#define OPENCV_GAPI_CORE_HPP
+
+#include <utility> // std::tuple
+
+#include <opencv2/imgproc.hpp>
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+/** \defgroup gapi_core G-API core (basic) functionality
+@{
+    @defgroup gapi_math Graph API: Math operations
+    @defgroup gapi_pixelwise Graph API: Pixelwise operations
+    @defgroup gapi_matrixop Graph API: Operations on matrices
+    @defgroup gapi_transform Graph API: Geometric, depth and LUT-like image transformations
+@}
+ */
+namespace cv { namespace gapi {
+namespace core {
+    using GMat2 = std::tuple<GMat,GMat>;
+    using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this?
+    using GMat4 = std::tuple<GMat,GMat,GMat,GMat>;
+    using GMatScalar = std::tuple<GMat, GScalar>;
+
+    G_TYPED_KERNEL(GAdd, <GMat(GMat, GMat, int)>, "org.opencv.core.math.add") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc b, int ddepth) {
+            if (ddepth == -1)
+            {
+                // OpenCV: When the input arrays in add/subtract/multiply/divide
+                // functions have different depths, the output array depth must be
+                // explicitly specified!
+                // See artim_op() @ arithm.cpp
+                GAPI_Assert(a.chan == b.chan);
+                GAPI_Assert(a.depth == b.depth);
+                return a;
+            }
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GAddC, <GMat(GMat, GScalar, int)>, "org.opencv.core.math.addC") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GSub, <GMat(GMat, GMat, int)>, "org.opencv.core.math.sub") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc b, int ddepth) {
+            if (ddepth == -1)
+            {
+                // This macro should select a larger data depth from a and b
+                // considering the number of channels in the same
+                // FIXME!!! Clarify if it is valid for sub()
+                GAPI_Assert(a.chan == b.chan);
+                ddepth = std::max(a.depth, b.depth);
+            }
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GSubC, <GMat(GMat, GScalar, int)>, "org.opencv.core.math.subC") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GSubRC,<GMat(GScalar, GMat, int)>, "org.opencv.core.math.subRC") {
+        static GMatDesc outMeta(GScalarDesc, GMatDesc b, int ddepth) {
+            return b.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GMul, <GMat(GMat, GMat, double, int)>, "org.opencv.core.math.mul") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc, double, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GMulCOld, <GMat(GMat, double, int)>, "org.opencv.core.math.mulCOld") {
+        static GMatDesc outMeta(GMatDesc a, double, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GMulC, <GMat(GMat, GScalar, int)>, "org.opencv.core.math.mulC"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GMulS, <GMat(GMat, GScalar)>, "org.opencv.core.math.muls") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a;
+        }
+    }; // FIXME: Merge with MulC
+
+    G_TYPED_KERNEL(GDiv, <GMat(GMat, GMat, double, int)>, "org.opencv.core.math.div") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc b, double, int ddepth) {
+            if (ddepth == -1)
+            {
+                GAPI_Assert(a.depth == b.depth);
+                return b;
+            }
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GDivC, <GMat(GMat, GScalar, double, int)>, "org.opencv.core.math.divC") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc, double, int ddepth) {
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GDivRC, <GMat(GScalar, GMat, double, int)>, "org.opencv.core.math.divRC") {
+        static GMatDesc outMeta(GScalarDesc, GMatDesc b, double, int ddepth) {
+            return b.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GMean, <GScalar(GMat)>, "org.opencv.core.math.mean") {
+        static GScalarDesc outMeta(GMatDesc) {
+            return empty_scalar_desc();
+        }
+    };
+
+    G_TYPED_KERNEL_M(GPolarToCart, <GMat2(GMat, GMat, bool)>, "org.opencv.core.math.polarToCart") {
+        static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc, GMatDesc a, bool) {
+            return std::make_tuple(a, a);
+        }
+    };
+
+    G_TYPED_KERNEL_M(GCartToPolar, <GMat2(GMat, GMat, bool)>, "org.opencv.core.math.cartToPolar") {
+        static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc x, GMatDesc, bool) {
+            return std::make_tuple(x, x);
+        }
+    };
+
+    G_TYPED_KERNEL(GMask, <GMat(GMat,GMat)>, "org.opencv.core.pixelwise.mask") {
+        static GMatDesc outMeta(GMatDesc in, GMatDesc) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpGT, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpGT") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpGE, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpGE") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpLE, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpLE") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpLT, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpLT") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpEQ, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpEQ") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpNE, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.compare.cmpNE") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpGTScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpGTScalar"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpGEScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpGEScalar"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpLEScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpLEScalar"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpLTScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpLTScalar"){
+    static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpEQScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpEQScalar"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GCmpNEScalar, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.compare.cmpNEScalar"){
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a.withDepth(CV_8U);
+        }
+    };
+
+    G_TYPED_KERNEL(GAnd, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.bitwise_and") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GAndS, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.bitwise_andS") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GOr, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.bitwise_or") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GOrS, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.bitwise_orS") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GXor, <GMat(GMat, GMat)>, "org.opencv.core.pixelwise.bitwise_xor") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GXorS, <GMat(GMat, GScalar)>, "org.opencv.core.pixelwise.bitwise_xorS") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GNot, <GMat(GMat)>, "org.opencv.core.pixelwise.bitwise_not") {
+        static GMatDesc outMeta(GMatDesc a) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GSelect, <GMat(GMat, GMat, GMat)>, "org.opencv.core.pixelwise.select") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GMin, <GMat(GMat, GMat)>, "org.opencv.core.matrixop.min") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GMax, <GMat(GMat, GMat)>, "org.opencv.core.matrixop.max") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GAbsDiff, <GMat(GMat, GMat)>, "org.opencv.core.matrixop.absdiff") {
+        static GMatDesc outMeta(GMatDesc a, GMatDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GAbsDiffC, <GMat(GMat, GScalar)>, "org.opencv.core.matrixop.absdiffC") {
+        static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
+            return a;
+        }
+    };
+
+    G_TYPED_KERNEL(GSum, <GScalar(GMat)>, "org.opencv.core.matrixop.sum") {
+        static GScalarDesc outMeta(GMatDesc) {
+            return empty_scalar_desc();
+        }
+    };
+
+    G_TYPED_KERNEL(GAddW, <GMat(GMat, double, GMat, double, double, int)>, "org.opencv.core.matrixop.addweighted") {
+        static GMatDesc outMeta(GMatDesc a, double, GMatDesc b, double, double, int ddepth) {
+            if (ddepth == -1)
+            {
+                // OpenCV: When the input arrays in add/subtract/multiply/divide
+                // functions have different depths, the output array depth must be
+                // explicitly specified!
+                // See artim_op() @ arithm.cpp
+                GAPI_Assert(a.chan == b.chan);
+                GAPI_Assert(a.depth == b.depth);
+                return a;
+            }
+            return a.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GNormL1, <GScalar(GMat)>, "org.opencv.core.matrixop.norml1") {
+        static GScalarDesc outMeta(GMatDesc) {
+            return empty_scalar_desc();
+        }
+    };
+
+    G_TYPED_KERNEL(GNormL2, <GScalar(GMat)>, "org.opencv.core.matrixop.norml2") {
+        static GScalarDesc outMeta(GMatDesc) {
+            return empty_scalar_desc();
+        }
+    };
+
+    G_TYPED_KERNEL(GNormInf, <GScalar(GMat)>, "org.opencv.core.matrixop.norminf") {
+        static GScalarDesc outMeta(GMatDesc) {
+            return empty_scalar_desc();
+        }
+    };
+
+    G_TYPED_KERNEL_M(GIntegral, <GMat2(GMat, int, int)>, "org.opencv.core.matrixop.integral") {
+        static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc in, int sd, int sqd) {
+            return std::make_tuple(in.withSizeDelta(1,1).withDepth(sd),
+                                   in.withSizeDelta(1,1).withDepth(sqd));
+        }
+    };
+
+    G_TYPED_KERNEL(GThreshold, <GMat(GMat, GScalar, GScalar, int)>, "org.opencv.core.matrixop.threshold") {
+        static GMatDesc outMeta(GMatDesc in, GScalarDesc, GScalarDesc, int) {
+            return in;
+        }
+    };
+
+
+    G_TYPED_KERNEL_M(GThresholdOT, <GMatScalar(GMat, GScalar, int)>, "org.opencv.core.matrixop.thresholdOT") {
+        static std::tuple<GMatDesc,GScalarDesc> outMeta(GMatDesc in, GScalarDesc, int) {
+            return std::make_tuple(in, empty_scalar_desc());
+        }
+    };
+
+    G_TYPED_KERNEL(GInRange, <GMat(GMat, GScalar, GScalar)>, "org.opencv.core.matrixop.inrange") {
+        static GMatDesc outMeta(GMatDesc in, GScalarDesc, GScalarDesc) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+
+    G_TYPED_KERNEL_M(GSplit3, <GMat3(GMat)>, "org.opencv.core.transform.split3") {
+        static std::tuple<GMatDesc, GMatDesc, GMatDesc> outMeta(GMatDesc in) {
+            const auto out_depth = in.depth;
+            const auto out_desc  = in.withType(out_depth, 1);
+            return std::make_tuple(out_desc, out_desc, out_desc);
+        }
+    };
+
+    G_TYPED_KERNEL_M(GSplit4, <GMat4(GMat)>,"org.opencv.core.transform.split4") {
+        static std::tuple<GMatDesc, GMatDesc, GMatDesc, GMatDesc> outMeta(GMatDesc in) {
+            const auto out_depth = in.depth;
+            const auto out_desc = in.withType(out_depth, 1);
+            return std::make_tuple(out_desc, out_desc, out_desc, out_desc);
+        }
+    };
+
+    G_TYPED_KERNEL(GResize, <GMat(GMat,Size,double,double,int)>, "org.opencv.core.transform.resize") {
+        static GMatDesc outMeta(GMatDesc in, Size sz, double fx, double fy, int) {
+            if (sz.width != 0 && sz.height != 0)
+            {
+                return in.withSize(sz);
+            }
+            else
+            {
+                GAPI_Assert(fx != 0. && fy != 0.);
+                return in.withSize
+                    (Size(static_cast<int>(std::round(in.size.width  * fx)),
+                          static_cast<int>(std::round(in.size.height * fy))));
+            }
+        }
+    };
+
+    G_TYPED_KERNEL(GMerge3, <GMat(GMat,GMat,GMat)>, "org.opencv.core.transform.merge3") {
+        static GMatDesc outMeta(GMatDesc in, GMatDesc, GMatDesc) {
+            // Preserve depth and add channel component
+            return in.withType(in.depth, 3);
+        }
+    };
+
+    G_TYPED_KERNEL(GMerge4, <GMat(GMat,GMat,GMat,GMat)>, "org.opencv.core.transform.merge4") {
+        static GMatDesc outMeta(GMatDesc in, GMatDesc, GMatDesc, GMatDesc) {
+            // Preserve depth and add channel component
+            return in.withType(in.depth, 4);
+        }
+    };
+
+    G_TYPED_KERNEL(GRemap, <GMat(GMat, Mat, Mat, int, int, Scalar)>, "org.opencv.core.transform.remap") {
+        static GMatDesc outMeta(GMatDesc in, Mat m1, Mat, int, int, Scalar) {
+            return in.withSize(m1.size());
+        }
+    };
+
+    G_TYPED_KERNEL(GFlip, <GMat(GMat, int)>, "org.opencv.core.transform.flip") {
+        static GMatDesc outMeta(GMatDesc in, int) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GCrop, <GMat(GMat, Rect)>, "org.opencv.core.transform.crop") {
+        static GMatDesc outMeta(GMatDesc in, Rect rc) {
+            return in.withSize(Size(rc.width, rc.height));
+        }
+    };
+
+    G_TYPED_KERNEL(GConcatHor, <GMat(GMat, GMat)>, "org.opencv.imgproc.transform.concatHor") {
+        static GMatDesc outMeta(GMatDesc l, GMatDesc r) {
+            return l.withSizeDelta(+r.size.width, 0);
+        }
+    };
+
+    G_TYPED_KERNEL(GConcatVert, <GMat(GMat, GMat)>, "org.opencv.imgproc.transform.concatVert") {
+        static GMatDesc outMeta(GMatDesc t, GMatDesc b) {
+            return t.withSizeDelta(0, +b.size.height);
+        }
+    };
+
+    G_TYPED_KERNEL(GLUT, <GMat(GMat, Mat)>, "org.opencv.core.transform.LUT") {
+        static GMatDesc outMeta(GMatDesc in, Mat) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GConvertTo, <GMat(GMat, int, double, double)>, "org.opencv.core.transform.convertTo") {
+        static GMatDesc outMeta(GMatDesc in, int rdepth, double, double) {
+            return rdepth < 0 ? in : in.withDepth(rdepth);
+        }
+    };
+}
+
+//! @addtogroup gapi_math
+//! @{
+
+/** @brief Calculates the per-element sum of two matrices.
+
+The function add calculates sum of two matrices of the same size and the same number of channels:
+\f[\texttt{dst}(I) =  \texttt{saturate} ( \texttt{src1}(I) +  \texttt{src2}(I)) \quad \texttt{if mask}(I) \ne0\f]
+
+The function can be replaced with matrix expressions:
+    \f[\texttt{dst} =  \texttt{src1} + \texttt{src2}\f]
+
+The input matrices and the output matrix can all have the same or different depths. For example, you
+can add a 16-bit unsigned matrix to a 8-bit signed matrix and store the sum as a 32-bit
+floating-point matrix. Depth of the output matrix is determined by the ddepth parameter.
+If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have
+the same depth as the input matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.add"
+@param src1 first input matrix.
+@param src2 second input matrix.
+@param ddepth optional depth of the output matrix.
+@sa sub, addWeighted
+*/
+GAPI_EXPORTS GMat add(const GMat& src1, const GMat& src2, int ddepth = -1);
+
+/** @brief Calculates the per-element sum of matrix and given scalar.
+
+The function addC adds a given scalar value to each element of given matrix.
+The function can be replaced with matrix expressions:
+
+    \f[\texttt{dst} =  \texttt{src1} + \texttt{c}\f]
+
+Depth of the output matrix is determined by the ddepth parameter.
+If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix.
+The matrices can be single or multi channel. Output matrix must have the same size and number of channels as the input matrix.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.addC"
+@param src1 first input matrix.
+@param c scalar value to be added.
+@param ddepth optional depth of the output matrix.
+@sa sub, addWeighted
+*/
+GAPI_EXPORTS GMat addC(const GMat& src1, const GScalar& c, int ddepth = -1);
+//! @overload
+GAPI_EXPORTS GMat addC(const GScalar& c, const GMat& src1, int ddepth = -1);
+
+/** @brief Calculates the per-element difference between two matrices.
+
+The function sub calculates difference between two matrices, when both matrices have the same size and the same number of
+channels:
+    \f[\texttt{dst}(I) =   \texttt{src1}(I) -  \texttt{src2}(I)\f]
+
+The function can be replaced with matrix expressions:
+\f[\texttt{dst} =   \texttt{src1} -  \texttt{src2}\f]
+
+The input matrices and the output matrix can all have the same or different depths. For example, you
+can subtract two 8-bit unsigned matrices store the result as a 16-bit signed matrix.
+Depth of the output matrix is determined by the ddepth parameter.
+If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have
+the same depth as the input matrices. The matrices can be single or multi channel.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.sub"
+@param src1 first input matrix.
+@param src2 second input matrix.
+@param ddepth optional depth of the output matrix.
+@sa  add, addC
+  */
+GAPI_EXPORTS GMat sub(const GMat& src1, const GMat& src2, int ddepth = -1);
+
+/** @brief Calculates the per-element difference between matrix and given scalar.
+
+The function can be replaced with matrix expressions:
+    \f[\texttt{dst} =  \texttt{src} - \texttt{c}\f]
+
+Depth of the output matrix is determined by the ddepth parameter.
+If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix.
+The matrices can be single or multi channel. Output matrix must have the same size as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.subC"
+@param src first input matrix.
+@param c scalar value to subtracted.
+@param ddepth optional depth of the output matrix.
+@sa  add, addC, subRC
+  */
+GAPI_EXPORTS GMat subC(const GMat& src, const GScalar& c, int ddepth = -1);
+
+/** @brief Calculates the per-element difference between given scalar and the matrix.
+
+The function can be replaced with matrix expressions:
+    \f[\texttt{dst} =  \texttt{val} - \texttt{src}\f]
+
+Depth of the output matrix is determined by the ddepth parameter.
+If ddepth is set to default -1, the depth of output matrix will be the same as the depth of input matrix.
+The matrices can be single or multi channel. Output matrix must have the same size as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.subRC"
+@param c scalar value to subtract from.
+@param src input matrix to be subtracted.
+@param ddepth optional depth of the output matrix.
+@sa  add, addC, subC
+  */
+GAPI_EXPORTS GMat subRC(const GScalar& c, const GMat& src, int ddepth = -1);
+
+/** @brief Calculates the per-element scaled product of two matrices.
+
+The function mul calculates the per-element product of two matrices:
+
+\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{scale} \cdot \texttt{src1} (I)  \cdot \texttt{src2} (I))\f]
+
+If src1.depth() == src2.depth(), ddepth can be set to the default -1. In this case, the output matrix will have
+the same depth as the input matrices. The matrices can be single or multi channel.
+Output matrix must have the same size as input matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.mul"
+@param src1 first input matrix.
+@param src2 second input matrix of the same size and the same depth as src1.
+@param scale optional scale factor.
+@param ddepth optional depth of the output matrix.
+@sa add, sub, div, addWeighted
+*/
+GAPI_EXPORTS GMat mul(const GMat& src1, const GMat& src2, double scale, int ddepth = -1);
+
+/** @brief Multiplies matrix by scalar.
+
+The function mulC multiplies each element of matrix src by given scalar value:
+
+\f[\texttt{dst} (I)= \texttt{saturate} (  \texttt{src1} (I)  \cdot \texttt{multiplier} )\f]
+
+The matrices can be single or multi channel. Output matrix must have the same size as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.mulC"
+@param src input matrix.
+@param multiplier factor to be multiplied.
+@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth.
+@sa add, sub, div, addWeighted
+*/
+GAPI_EXPORTS GMat mulC(const GMat& src, double multiplier, int ddepth = -1);
+//! @overload
+GAPI_EXPORTS GMat mulC(const GMat& src, const GScalar& multiplier, int ddepth = -1);   // FIXME: merge with mulc
+//! @overload
+GAPI_EXPORTS GMat mulC(const GScalar& multiplier, const GMat& src, int ddepth = -1);   // FIXME: merge with mulc
+
+/** @brief Performs per-element division of two matrices.
+
+The function divides one matrix by another:
+\f[\texttt{dst(I) = saturate(src1(I)*scale/src2(I))}\f]
+
+When src2(I) is zero, dst(I) will also be zero. Different channels of
+multi-channel matrices are processed independently.
+The matrices can be single or multi channel. Output matrix must have the same size and depth as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.div"
+@param src1 first input matrix.
+@param src2 second input matrix of the same size and depth as src1.
+@param scale scalar factor.
+@param ddepth optional depth of the output matrix; you can only pass -1 when src1.depth() == src2.depth().
+@sa  mul, add, sub
+*/
+GAPI_EXPORTS GMat div(const GMat& src1, const GMat& src2, double scale, int ddepth = -1);
+
+/** @brief Divides matrix by scalar.
+
+The function divC divides each element of matrix src by given scalar value:
+
+\f[\texttt{dst(I) = saturate(src(I)*scale/divisor)}\f]
+
+When divisor is zero, dst(I) will also be zero. Different channels of
+multi-channel matrices are processed independently.
+The matrices can be single or multi channel. Output matrix must have the same size and depth as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.divC"
+@param src input matrix.
+@param divisor number to be divided by.
+@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth.
+@param scale scale factor.
+@sa add, sub, div, addWeighted
+*/
+GAPI_EXPORTS GMat divC(const GMat& src, const GScalar& divisor, double scale, int ddepth = -1);
+
+/** @brief Divides scalar by matrix.
+
+The function divRC divides given scalar by each element of matrix src and keep the division result in new matrix of the same size and type as src:
+
+\f[\texttt{dst(I) = saturate(divident*scale/src(I))}\f]
+
+When src(I) is zero, dst(I) will also be zero. Different channels of
+multi-channel matrices are processed independently.
+The matrices can be single or multi channel. Output matrix must have the same size and depth as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.divRC"
+@param src input matrix.
+@param divident number to be divided.
+@param ddepth optional depth of the output matrix. If -1, the depth of output matrix will be the same as input matrix depth.
+@param scale scale factor
+@sa add, sub, div, addWeighted
+*/
+GAPI_EXPORTS GMat divRC(const GScalar& divident, const GMat& src, double scale, int ddepth = -1);
+
+/** @brief Applies a mask to a matrix.
+
+The function mask set value from given matrix if the corresponding pixel value in mask matrix set to true,
+and set the matrix value to 0 overwise.
+
+Supported src matrix data types are @ref CV_8UC1, @ref CV_16SC1, @ref CV_16UC1. Supported mask data type is @ref CV_8UC1.
+
+@note Function textual ID is "org.opencv.core.math.mask"
+@param src input matrix.
+@param mask input mask matrix.
+*/
+GAPI_EXPORTS GMat mask(const GMat& src, const GMat& mask);
+
+/** @brief Calculates an average (mean) of matrix elements.
+
+The function mean calculates the mean value M of matrix elements,
+independently for each channel, and return it.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.math.mean"
+@param src input matrix.
+*/
+GAPI_EXPORTS GScalar mean(const GMat& src);
+
+/** @brief Calculates x and y coordinates of 2D vectors from their magnitude and angle.
+
+The function polarToCart calculates the Cartesian coordinates of each 2D
+vector represented by the corresponding elements of magnitude and angle:
+\f[\begin{array}{l} \texttt{x} (I) =  \texttt{magnitude} (I) \cos ( \texttt{angle} (I)) \\ \texttt{y} (I) =  \texttt{magnitude} (I) \sin ( \texttt{angle} (I)) \\ \end{array}\f]
+
+The relative accuracy of the estimated coordinates is about 1e-6.
+
+First output is a matrix of x-coordinates of 2D vectors.
+Second output is a matrix of y-coordinates of 2D vectors.
+Both output must have the same size and depth as input matrices.
+
+@note Function textual ID is "org.opencv.core.math.polarToCart"
+
+@param magnitude input floating-point @ref CV_32FC1 matrix (1xN) of magnitudes of 2D vectors;
+@param angle input floating-point @ref CV_32FC1 matrix (1xN) of angles of 2D vectors.
+@param angleInDegrees when true, the input angles are measured in
+degrees, otherwise, they are measured in radians.
+@sa cartToPolar, exp, log, pow, sqrt
+*/
+GAPI_EXPORTS std::tuple<GMat, GMat> polarToCart(const GMat& magnitude, const GMat& angle,
+                                              bool angleInDegrees = false);
+
+/** @brief Calculates the magnitude and angle of 2D vectors.
+
+The function cartToPolar calculates either the magnitude, angle, or both
+for every 2D vector (x(I),y(I)):
+\f[\begin{array}{l} \texttt{magnitude} (I)= \sqrt{\texttt{x}(I)^2+\texttt{y}(I)^2} , \\ \texttt{angle} (I)= \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))[ \cdot180 / \pi ] \end{array}\f]
+
+The angles are calculated with accuracy about 0.3 degrees. For the point
+(0,0), the angle is set to 0.
+
+First output is a matrix of magnitudes of the same size and depth as input x.
+Second output is a matrix of angles that has the same size and depth as
+x; the angles are measured in radians (from 0 to 2\*Pi) or in degrees (0 to 360 degrees).
+
+@note Function textual ID is "org.opencv.core.math.cartToPolar"
+
+@param x matrix of @ref CV_32FC1 x-coordinates.
+@param y array of @ref CV_32FC1 y-coordinates.
+@param angleInDegrees a flag, indicating whether the angles are measured
+in radians (which is by default), or in degrees.
+@sa polarToCart
+*/
+GAPI_EXPORTS std::tuple<GMat, GMat> cartToPolar(const GMat& x, const GMat& y,
+                                              bool angleInDegrees = false);
+//! @} gapi_math
+//!
+//! @addtogroup gapi_pixelwise
+//! @{
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are greater compare to elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  > \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+\f[\texttt{dst} =   \texttt{src1} > \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices/matrix.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGT"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpLE, cmpGE, cmpLS
+*/
+GAPI_EXPORTS GMat cmpGT(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGTScalar"
+*/
+GAPI_EXPORTS GMat cmpGT(const GMat& src1, const GScalar& src2);
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less than elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  < \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+    \f[\texttt{dst} =   \texttt{src1} < \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices/matrix.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLT"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpLE, cmpGE, cmpGT
+*/
+GAPI_EXPORTS GMat cmpLT(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLTScalar"
+*/
+GAPI_EXPORTS GMat cmpLT(const GMat& src1, const GScalar& src2);
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are greater or equal compare to elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  >= \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+    \f[\texttt{dst} =   \texttt{src1} >= \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpGE"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpLE, cmpGT, cmpLS
+*/
+GAPI_EXPORTS GMat cmpGE(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLGEcalar"
+*/
+GAPI_EXPORTS GMat cmpGE(const GMat& src1, const GScalar& src2);
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are less or equal compare to elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  <=  \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+    \f[\texttt{dst} =   \texttt{src1} <= \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLE"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpGT, cmpGE, cmpLS
+*/
+GAPI_EXPORTS GMat cmpLE(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpLEScalar"
+*/
+GAPI_EXPORTS GMat cmpLE(const GMat& src1, const GScalar& src2);
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are equal to elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  ==  \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+    \f[\texttt{dst} =   \texttt{src1} == \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpEQ"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpNE
+*/
+GAPI_EXPORTS GMat cmpEQ(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpEQScalar"
+*/
+GAPI_EXPORTS GMat cmpEQ(const GMat& src1, const GScalar& src2);
+
+/** @brief Performs the per-element comparison of two matrices checking if elements from first matrix are not equal to elements in second.
+
+The function compares elements of two matrices src1 and src2 of the same size:
+    \f[\texttt{dst} (I) =  \texttt{src1} (I)  !=  \texttt{src2} (I)\f]
+
+When the comparison result is true, the corresponding element of output
+array is set to 255. The comparison operations can be replaced with the
+equivalent matrix expressions:
+    \f[\texttt{dst} =   \texttt{src1} != \texttt{src2}\f]
+
+Output matrix of depth @ref CV_8U must have the same size and the same number of channels as
+    the input matrices.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpNE"
+@param src1 first input matrix.
+@param src2 second input matrix/scalar of the same depth as first input matrix.
+@sa min, max, threshold, cmpEQ
+*/
+GAPI_EXPORTS GMat cmpNE(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.cmpNEScalar"
+*/
+GAPI_EXPORTS GMat cmpNE(const GMat& src1, const GScalar& src2);
+
+/** @brief computes bitwise conjunction of the two matrixes (src1 & src2)
+Calculates the per-element bit-wise logical conjunction of two matrices of the same size.
+
+In case of floating-point matrices, their machine-specific bit
+representations (usually IEEE754-compliant) are used for the operation.
+In case of multi-channel matrices, each channel is processed
+independently. Output matrix must have the same size and depth as the input
+matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.bitwise_and"
+
+@param src1 first input matrix.
+@param src2 second input matrix.
+*/
+GAPI_EXPORTS GMat bitwise_and(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.bitwise_andS"
+@param src1 first input matrix.
+@param src2 scalar, which will be per-lemenetly conjuncted with elements of src1.
+*/
+GAPI_EXPORTS GMat bitwise_and(const GMat& src1, const GScalar& src2);
+
+/** @brief computes bitwise disjunction of the two matrixes (src1 | src2)
+Calculates the per-element bit-wise logical disjunction of two matrices of the same size.
+
+In case of floating-point matrices, their machine-specific bit
+representations (usually IEEE754-compliant) are used for the operation.
+In case of multi-channel matrices, each channel is processed
+independently. Output matrix must have the same size and depth as the input
+matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.bitwise_or"
+
+@param src1 first input matrix.
+@param src2 second input matrix.
+*/
+GAPI_EXPORTS GMat bitwise_or(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.bitwise_orS"
+@param src1 first input matrix.
+@param src2 scalar, which will be per-lemenetly disjuncted with elements of src1.
+*/
+GAPI_EXPORTS GMat bitwise_or(const GMat& src1, const GScalar& src2);
+
+
+/** @brief computes bitwise logical "exclusive or" of the two matrixes (src1 ^ src2)
+Calculates the per-element bit-wise logical "exclusive or" of two matrices of the same size.
+
+In case of floating-point matrices, their machine-specific bit
+representations (usually IEEE754-compliant) are used for the operation.
+In case of multi-channel matrices, each channel is processed
+independently. Output matrix must have the same size and depth as the input
+matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.bitwise_xor"
+
+@param src1 first input matrix.
+@param src2 second input matrix.
+*/
+GAPI_EXPORTS GMat bitwise_xor(const GMat& src1, const GMat& src2);
+/** @overload
+@note Function textual ID is "org.opencv.core.pixelwise.compare.bitwise_xorS"
+@param src1 first input matrix.
+@param src2 scalar, for which per-lemenet "logical or" operation on elements of src1 will be performed.
+*/
+GAPI_EXPORTS GMat bitwise_xor(const GMat& src1, const GScalar& src2);
+
+
+/** @brief Inverts every bit of an array.
+The function bitwise_not calculates per-element bit-wise inversion of the input
+matrix:
+\f[\texttt{dst} (I) =  \neg \texttt{src} (I)\f]
+
+In case of floating-point matrices, their machine-specific bit
+representations (usually IEEE754-compliant) are used for the operation.
+In case of multi-channel matrices, each channel is processed
+independently. Output matrix must have the same size and depth as the input
+matrix.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.bitwise_not"
+
+@param src input matrix.
+*/
+GAPI_EXPORTS GMat bitwise_not(const GMat& src);
+
+/** @brief Select values from either first or second of input matrices by given mask.
+The function set to the output matrix either the value from the first input matrix if corresponding value of mask matrix is 255,
+ or value from the second input matrix (if value of mask matrix set to 0).
+
+Input mask matrix must be of @ref CV_8UC1 type, two other inout matrices and output matrix should be of the same type. The size should
+be the same for all input and output matrices.
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.pixelwise.select"
+
+@param src1 first input matrix.
+@param src2 second input matrix.
+@param mask mask input matrix.
+*/
+GAPI_EXPORTS GMat select(const GMat& src1, const GMat& src2, const GMat& mask);
+
+//! @} gapi_pixelwise
+
+
+//! @addtogroup gapi_matrixop
+//! @{
+/** @brief Calculates per-element minimum of two matrices.
+
+The function min calculates the per-element minimum of two matrices of the same size, number of channels and depth:
+\f[\texttt{dst} (I)= \min ( \texttt{src1} (I), \texttt{src2} (I))\f]
+    where I is a multi-dimensional index of matrix elements. In case of
+    multi-channel matrices, each channel is processed independently.
+Output matrix must be of the same size and depth as src1.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.min"
+@param src1 first input matrix.
+@param src2 second input matrix of the same size and depth as src1.
+@sa max, compareEqual, compareLess, compareLessEqual
+*/
+GAPI_EXPORTS GMat min(const GMat& src1, const GMat& src2);
+
+/** @brief Calculates per-element maximum of two matrices.
+
+The function max calculates the per-element maximum of two matrices of the same size, number of channels and depth:
+\f[\texttt{dst} (I)= \max ( \texttt{src1} (I), \texttt{src2} (I))\f]
+    where I is a multi-dimensional index of matrix elements. In case of
+    multi-channel matrices, each channel is processed independently.
+Output matrix must be of the same size and depth as src1.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.max"
+@param src1 first input matrix.
+@param src2 second input matrix of the same size and depth as src1.
+@sa min, compare, compareEqual, compareGreater, compareGreaterEqual
+*/
+GAPI_EXPORTS GMat max(const GMat& src1, const GMat& src2);
+
+/** @brief Calculates the per-element absolute difference between two matrices.
+
+The function absDiff calculates absolute difference between two matrices of the same size and depth:
+    \f[\texttt{dst}(I) =  \texttt{saturate} (| \texttt{src1}(I) -  \texttt{src2}(I)|)\f]
+    where I is a multi-dimensional index of matrix elements. In case of
+    multi-channel matrices, each channel is processed independently.
+Output matrix must have the same size and depth as input matrices.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.absdiff"
+@param src1 first input matrix.
+@param src2 second input matrix.
+@sa abs
+*/
+GAPI_EXPORTS GMat absDiff(const GMat& src1, const GMat& src2);
+
+/** @brief Calculates absolute value of matrix elements.
+
+The function abs calculates absolute difference between matrix elements and given scalar value:
+    \f[\texttt{dst}(I) =  \texttt{saturate} (| \texttt{src1}(I) -  \texttt{matC}(I)|)\f]
+    where matC is constructed from given scalar c and has the same sizes and depth as input matrix src.
+
+Output matrix must be of the same size and depth as src.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.absdiffC"
+@param src input matrix.
+@param c scalar to be subtracted.
+@sa min, max
+*/
+GAPI_EXPORTS GMat absDiffC(const GMat& src, const GScalar& c);
+
+/** @brief Calculates sum of all matrix elements.
+
+The function sum calculates sum of all matrix elements, independently for each channel.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.sum"
+@param src input matrix.
+@sa min, max
+*/
+GAPI_EXPORTS GScalar sum(const GMat& src);
+
+/** @brief Calculates the weighted sum of two matrices.
+
+The function addWeighted calculates the weighted sum of two matrices as follows:
+\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} +  \texttt{src2} (I)* \texttt{beta} +  \texttt{gamma} )\f]
+where I is a multi-dimensional index of array elements. In case of multi-channel matrices, each
+channel is processed independently.
+
+The function can be replaced with a matrix expression:
+    \f[\texttt{dst}(I) =  \texttt{alpha} * \texttt{src1}(I) - \texttt{beta} * \texttt{src2}(I) + \texttt{gamma} \f]
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.addweighted"
+@param src1 first input matrix.
+@param alpha weight of the first matrix elements.
+@param src2 second input matrix of the same size and channel number as src1.
+@param beta weight of the second matrix elements.
+@param gamma scalar added to each sum.
+@param ddepth optional depth of the output matrix.
+@sa  add, sub
+*/
+GAPI_EXPORTS GMat addWeighted(const GMat& src1, double alpha, const GMat& src2, double beta, double gamma, int ddepth = -1);
+
+/** @brief Calculates the  absolute L1 norm of a matrix.
+
+This version of normL1 calculates the absolute L1 norm of src.
+
+As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$.
+The \f$ L_{1} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$
+is calculated as follows
+\f{align*}
+    \| r(-1) \|_{L_1} &= |-1| + |2| = 3 \\
+\f}
+and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is
+\f{align*}
+    \| r(0.5) \|_{L_1} &= |0.5| + |0.5| = 1 \\
+\f}
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.norml1"
+@param src input matrix.
+@sa normL2, normInf
+*/
+GAPI_EXPORTS GScalar normL1(const GMat& src);
+
+/** @brief Calculates the absolute L2 norm of a matrix.
+
+This version of normL2 calculates the absolute L2 norm of src.
+
+As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$.
+The \f$ L_{2} \f$  norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$
+is calculated as follows
+\f{align*}
+    \| r(-1) \|_{L_2} &= \sqrt{(-1)^{2} + (2)^{2}} = \sqrt{5} \\
+\f}
+and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is
+\f{align*}
+    \| r(0.5) \|_{L_2} &= \sqrt{(0.5)^{2} + (0.5)^{2}} = \sqrt{0.5} \\
+\f}
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+@note Function textual ID is "org.opencv.core.matrixop.norml2"
+@param src input matrix.
+@sa normL1, normInf
+*/
+GAPI_EXPORTS GScalar normL2(const GMat& src);
+
+/** @brief Calculates the absolute infinite norm of a matrix.
+
+This version of normInf calculates the absolute infinite norm of src.
+
+As example for one array consider the function \f$r(x)= \begin{pmatrix} x \\ 1-x \end{pmatrix}, x \in [-1;1]\f$.
+The \f$ L_{\infty} \f$ norm for the sample value \f$r(-1) = \begin{pmatrix} -1 \\ 2 \end{pmatrix}\f$
+is calculated as follows
+\f{align*}
+    \| r(-1) \|_{L_\infty} &= \max(|-1|,|2|) = 2
+\f}
+and for \f$r(0.5) = \begin{pmatrix} 0.5 \\ 0.5 \end{pmatrix}\f$ the calculation is
+\f{align*}
+    \| r(0.5) \|_{L_\infty} &= \max(|0.5|,|0.5|) = 0.5.
+\f}
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.norminf"
+@param src input matrix.
+@sa normL1, normL2
+*/
+GAPI_EXPORTS GScalar normInf(const GMat& src);
+
+/** @brief Calculates the integral of an image.
+
+The function calculates one or more integral images for the source image as follows:
+
+\f[\texttt{sum} (X,Y) =  \sum _{x<X,y<Y}  \texttt{image} (x,y)\f]
+
+\f[\texttt{sqsum} (X,Y) =  \sum _{x<X,y<Y}  \texttt{image} (x,y)^2\f]
+
+The function return integral image as \f$(W+1)\times (H+1)\f$ , 32-bit integer or floating-point (32f or 64f) and
+ integral image for squared pixel values; it is \f$(W+1)\times (H+)\f$, double-precision floating-point (64f) array.
+
+@note Function textual ID is "org.opencv.core.matrixop.integral"
+
+@param src input image.
+@param sdepth desired depth of the integral and the tilted integral images, CV_32S, CV_32F, or
+CV_64F.
+@param sqdepth desired depth of the integral image of squared pixel values, CV_32F or CV_64F.
+ */
+GAPI_EXPORTS std::tuple<GMat, GMat> integral(const GMat& src, int sdepth = -1, int sqdepth = -1);
+
+/** @brief Applies a fixed-level threshold to each matrix element.
+
+The function applies fixed-level thresholding to a single- or multiple-channel matrix.
+The function is typically used to get a bi-level (binary) image out of a grayscale image ( cmp funtions could be also used for
+this purpose) or for removing a noise, that is, filtering out pixels with too small or too large
+values. There are several depths of thresholding supported by the function. They are determined by
+depth parameter.
+
+Also, the special values cv::THRESH_OTSU or cv::THRESH_TRIANGLE may be combined with one of the
+above values. In these cases, the function determines the optimal threshold value using the Otsu's
+or Triangle algorithm and uses it instead of the specified thresh . The function returns the
+computed threshold value in addititon to thresholded matrix.
+The Otsu's and Triangle methods are implemented only for 8-bit matrices.
+
+Input image should be single channel only in case of cv::THRESH_OTSU or cv::THRESH_TRIANGLE flags.
+Output matrix must be of the same size and depth as src.
+
+@note Function textual ID is "org.opencv.core.matrixop.threshold"
+
+@param src input matrix (@ref CV_8UC1, @ref CV_8UC3, or @ref CV_32FC1).
+@param thresh threshold value.
+@param maxval maximum value to use with the cv::THRESH_BINARY and cv::THRESH_BINARY_INV thresholding
+depths.
+@param depth thresholding depth (see the cv::ThresholdTypes).
+
+@sa min, max, cmpGT, cmpLE, cmpGE, cmpLS
+ */
+GAPI_EXPORTS GMat threshold(const GMat& src, const GScalar& thresh, const GScalar& maxval, int depth);
+/** @overload
+This function appicable for all threshold depths except CV_THRESH_OTSU and CV_THRESH_TRIANGLE
+@note Function textual ID is "org.opencv.core.matrixop.thresholdOT"
+*/
+GAPI_EXPORTS std::tuple<GMat, GScalar> threshold(const GMat& src, const GScalar& maxval, int depth);
+
+/** @brief Applies a range-level threshold to each matrix element.
+
+The function applies range-level thresholding to a single- or multiple-channel matrix.
+It sets output pixel value to OxFF if the corresponding pixel value of input matrix is in specified range,or 0 otherwise.
+
+Input and output matrices must be CV_8UC1.
+
+@note Function textual ID is "org.opencv.core.matrixop.inRange"
+
+@param src input matrix (CV_8UC1).
+@param threshLow lower boundary value.
+@param threshUp upper boundary value.
+
+@sa threshold
+ */
+GAPI_EXPORTS GMat inRange(const GMat& src, const GScalar& threshLow, const GScalar& threshUp);
+
+//! @} gapi_matrixop
+
+//! @addtogroup gapi_transform
+//! @{
+/** @brief Resizes an image.
+
+The function resizes the image src down to or up to the specified size.
+
+Output image size will have the size dsize (when dsize is non-zero) or the size computed from
+src.size(), fx, and fy; the depth of output is the same as of src.
+
+If you want to resize src so that it fits the pre-created dst,
+you may call the function as follows:
+@code
+    // explicitly specify dsize=dst.size(); fx and fy will be computed from that.
+    resize(src, dst, dst.size(), 0, 0, interpolation);
+@endcode
+If you want to decimate the image by factor of 2 in each direction, you can call the function this
+way:
+@code
+    // specify fx and fy and let the function compute the destination image size.
+    resize(src, dst, Size(), 0.5, 0.5, interpolation);
+@endcode
+To shrink an image, it will generally look best with cv::INTER_AREA interpolation, whereas to
+enlarge an image, it will generally look best with cv::INTER_CUBIC (slow) or cv::INTER_LINEAR
+(faster but still looks OK).
+
+@note Function textual ID is "org.opencv.core.transform.resize"
+
+@param src input image.
+@param dsize output image size; if it equals zero, it is computed as:
+ \f[\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}\f]
+ Either dsize or both fx and fy must be non-zero.
+@param fx scale factor along the horizontal axis; when it equals 0, it is computed as
+\f[\texttt{(double)dsize.width/src.cols}\f]
+@param fy scale factor along the vertical axis; when it equals 0, it is computed as
+\f[\texttt{(double)dsize.height/src.rows}\f]
+@param interpolation interpolation method, see cv::InterpolationFlags
+
+@sa  warpAffine, warpPerspective, remap
+ */
+GAPI_EXPORTS GMat resize(const GMat& src, const Size& dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR);
+
+/** @brief Creates one 3-channel (4-channel) matrix out of 3(4) single-channel ones.
+
+The function merges several matrices to make a single multi-channel matrix. That is, each
+element of the output matrix will be a concatenation of the elements of the input matrices, where
+elements of i-th input matrix are treated as mv[i].channels()-element vectors.
+Input matrix must be of @ref CV_8UC3 (@ref CV_8UC4) type.
+
+The function split3/split4 does the reverse operation.
+
+@note Function textual ID for merge3 is "org.opencv.core.transform.merge3"
+@note Function textual ID for merge4 is "org.opencv.core.transform.merge4"
+
+@param src1 first input matrix to be merged
+@param src2 second input matrix to be merged
+@param src3 third input matrix to be merged
+@param src4 fourth input matrix to be merged
+@sa  split4, split3
+*/
+GAPI_EXPORTS GMat merge4(const GMat& src1, const GMat& src2, const GMat& src3, const GMat& src4);
+GAPI_EXPORTS GMat merge3(const GMat& src1, const GMat& src2, const GMat& src3);
+
+/** @brief Divides a 3-channel (4-channel) matrix into 3(4) single-channel matrices.
+
+The function splits a 3-channel (4-channel) matrix into 3(4) single-channel matrices:
+\f[\texttt{mv} [c](I) =  \texttt{src} (I)_c\f]
+
+All output matrices must be in @ref CV_8UC1.
+
+@note Function textual for split3 ID is "org.opencv.core.transform.split3"
+@note Function textual for split4 ID is "org.opencv.core.transform.split4"
+
+@param src input @ref CV_8UC4 (@ref CV_8UC3) matrix.
+@sa merge3, merge4
+*/
+GAPI_EXPORTS std::tuple<GMat, GMat, GMat,GMat> split4(const GMat& src);
+GAPI_EXPORTS std::tuple<GMat, GMat, GMat> split3(const GMat& src);
+
+/** @brief Applies a generic geometrical transformation to an image.
+
+The function remap transforms the source image using the specified map:
+
+\f[\texttt{dst} (x,y) =  \texttt{src} (map_x(x,y),map_y(x,y))\f]
+
+where values of pixels with non-integer coordinates are computed using one of available
+interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps
+in \f$map_1\f$ and \f$map_2\f$ respectively, or interleaved floating-point maps of \f$(x,y)\f$ in
+\f$map_1\f$, or fixed-point maps created by using convertMaps. The reason you might want to
+convert from floating to fixed-point representations of a map is that they can yield much faster
+(\~2x) remapping operations. In the converted case, \f$map_1\f$ contains pairs (cvFloor(x),
+cvFloor(y)) and \f$map_2\f$ contains indices in a table of interpolation coefficients.
+Output image must be of the same size and depth as input one.
+
+@note Function textual ID is "org.opencv.core.transform.remap"
+
+@param src Source image.
+@param map1 The first map of either (x,y) points or just x values having the type CV_16SC2,
+CV_32FC1, or CV_32FC2.
+@param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map
+if map1 is (x,y) points), respectively.
+@param interpolation Interpolation method (see cv::InterpolationFlags). The method INTER_AREA is
+not supported by this function.
+@param borderMode Pixel extrapolation method (see cv::BorderTypes). When
+borderMode=BORDER_TRANSPARENT, it means that the pixels in the destination image that
+corresponds to the "outliers" in the source image are not modified by the function.
+@param borderValue Value used in case of a constant border. By default, it is 0.
+@note
+Due to current implementation limitations the size of an input and output images should be less than 32767x32767.
+ */
+GAPI_EXPORTS GMat remap(const GMat& src, const Mat& map1, const Mat& map2,
+                      int interpolation, int borderMode = BORDER_CONSTANT,
+                      const Scalar& borderValue = Scalar());
+
+/** @brief Flips a 2D matrix around vertical, horizontal, or both axes.
+
+The function flips the matrix in one of three different ways (row
+and column indices are 0-based):
+\f[\texttt{dst} _{ij} =
+\left\{
+\begin{array}{l l}
+\texttt{src} _{\texttt{src.rows}-i-1,j} & if\;  \texttt{flipCode} = 0 \\
+\texttt{src} _{i, \texttt{src.cols} -j-1} & if\;  \texttt{flipCode} > 0 \\
+\texttt{src} _{ \texttt{src.rows} -i-1, \texttt{src.cols} -j-1} & if\; \texttt{flipCode} < 0 \\
+\end{array}
+\right.\f]
+The example scenarios of using the function are the following:
+*   Vertical flipping of the image (flipCode == 0) to switch between
+    top-left and bottom-left image origin. This is a typical operation
+    in video processing on Microsoft Windows\* OS.
+*   Horizontal flipping of the image with the subsequent horizontal
+    shift and absolute difference calculation to check for a
+    vertical-axis symmetry (flipCode \> 0).
+*   Simultaneous horizontal and vertical flipping of the image with
+    the subsequent shift and absolute difference calculation to check
+    for a central symmetry (flipCode \< 0).
+*   Reversing the order of point arrays (flipCode \> 0 or
+    flipCode == 0).
+Output image must be of the same depth as input one, size should be correct for given flipCode.
+
+@note Function textual ID is "org.opencv.core.transform.flip"
+
+@param src input matrix.
+@param flipCode a flag to specify how to flip the array; 0 means
+flipping around the x-axis and positive value (for example, 1) means
+flipping around y-axis. Negative value (for example, -1) means flipping
+around both axes.
+@sa remap
+*/
+GAPI_EXPORTS GMat flip(const GMat& src, int flipCode);
+
+/** @brief Crops a 2D matrix.
+
+The function crops the matrix by given cv::Rect.
+
+Output matrix must be of the same depth as input one, size is specified by given rect size.
+
+@note Function textual ID is "org.opencv.core.transform.crop"
+
+@param src input matrix.
+@param rect a rect to crop a matrix to
+@sa resize
+*/
+GAPI_EXPORTS GMat crop(const GMat& src, const Rect& rect);
+
+/** @brief Applies horizontal concatenation to given matrices.
+
+The function horizontally concatenates two GMat matrices (with the same number of rows).
+@code{.cpp}
+    GMat A = { 1, 4,
+               2, 5,
+               3, 6 };
+    GMat B = { 7, 10,
+               8, 11,
+               9, 12 };
+
+    GMat C = gapi::concatHor(A, B);
+    //C:
+    //[1, 4, 7, 10;
+    // 2, 5, 8, 11;
+    // 3, 6, 9, 12]
+@endcode
+Output matrix must the same number of rows and depth as the src1 and src2, and the sum of cols of the src1 and src2.
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.imgproc.transform.concatHor"
+
+@param src1 first input matrix to be considered for horizontal concatenation.
+@param src2 second input matrix to be considered for horizontal concatenation.
+@sa concatVert
+*/
+GAPI_EXPORTS GMat concatHor(const GMat& src1, const GMat& src2);
+
+/** @overload
+The function horizontally concatenates given number of GMat matrices (with the same number of columns).
+Output matrix must the same number of columns and depth as the input matrices, and the sum of rows of input matrices.
+
+@param v vector of input matrices to be concatenated horizontally.
+*/
+GAPI_EXPORTS GMat concatHor(const std::vector<GMat> &v);
+
+/** @brief Applies vertical concatenation to given matrices.
+
+The function vertically concatenates two GMat matrices (with the same number of cols).
+ @code{.cpp}
+    GMat A = { 1, 7,
+               2, 8,
+               3, 9 };
+    GMat B = { 4, 10,
+               5, 11,
+               6, 12 };
+
+    GMat C = gapi::concatVert(A, B);
+    //C:
+    //[1, 7;
+    // 2, 8;
+    // 3, 9;
+    // 4, 10;
+    // 5, 11;
+    // 6, 12]
+ @endcode
+
+Output matrix must the same number of cols and depth as the src1 and src2, and the sum of rows of the src1 and src2.
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+
+@note Function textual ID is "org.opencv.imgproc.transform.concatVert"
+
+@param src1 first input matrix to be considered for vertical concatenation.
+@param src2 second input matrix to be considered for vertical concatenation.
+@sa concatHor
+*/
+GAPI_EXPORTS GMat concatVert(const GMat& src1, const GMat& src2);
+
+/** @overload
+The function vertically concatenates given number of GMat matrices (with the same number of columns).
+Output matrix must the same number of columns and depth as the input matrices, and the sum of rows of input matrices.
+
+@param v vector of input matrices to be concatenated vertically.
+*/
+GAPI_EXPORTS GMat concatVert(const std::vector<GMat> &v);
+
+
+/** @brief Performs a look-up table transform of a matrix.
+
+The function LUT fills the output matrix with values from the look-up table. Indices of the entries
+are taken from the input matrix. That is, the function processes each element of src as follows:
+\f[\texttt{dst} (I)  \leftarrow \texttt{lut(src(I))}\f]
+
+Supported matrix data types are @ref CV_8UC1.
+Output is a matrix of the same size and number of channels as src, and the same depth as lut.
+
+@note Function textual ID is "org.opencv.core.transform.LUT"
+
+@param src input matrix of 8-bit elements.
+@param lut look-up table of 256 elements; in case of multi-channel input array, the table should
+either have a single channel (in this case the same table is used for all channels) or the same
+number of channels as in the input matrix.
+*/
+GAPI_EXPORTS GMat LUT(const GMat& src, const Mat& lut);
+
+/** @brief Performs a 3D look-up table transform of a multi-channel matrix.
+
+The function LUT3D fills the output matrix with values from the look-up table. Indices of the entries
+are taken from the input matrix. Interpolation is applied for mapping 0-255 range values to 0-16 range of 3DLUT table.
+The function processes each element of src as follows:
+@code{.cpp}
+    dst[i][j][k] = lut3D[~src_r][~src_g][~src_b];
+@endcode
+where ~ means approximation.
+Output is a matrix of of @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.core.transform.LUT3D"
+
+@param src input matrix of @ref CV_8UC3.
+@param lut3D look-up table 17x17x17 3-channel elements.
+@param interpolation The depth of interpoolation to be used.
+*/
+GAPI_EXPORTS GMat LUT3D(const GMat& src, const GMat& lut3D, int interpolation = INTER_NEAREST);
+
+/** @brief Converts a matrix to another data depth with optional scaling.
+
+The method converts source pixel values to the target data depth. saturate_cast\<\> is applied at
+the end to avoid possible overflows:
+
+\f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) +  \beta )\f]
+Output matrix must be of the same size as input one.
+
+@note Function textual ID is "org.opencv.core.transform.convertTo"
+@param src input matrix to be converted from.
+@param rdepth desired output matrix depth or, rather, the depth since the number of channels are the
+same as the input has; if rdepth is negative, the output matrix will have the same depth as the input.
+@param alpha optional scale factor.
+@param beta optional delta added to the scaled values.
+ */
+GAPI_EXPORTS GMat convertTo(const GMat& src, int rdepth, double alpha=1, double beta=0);
+//! @} gapi_transform
+
+} //namespace gapi
+} //namespace cv
+
+#endif //OPENCV_GAPI_CORE_HPP
diff --git a/modules/gapi/include/opencv2/gapi/cpu/core.hpp b/modules/gapi/include/opencv2/gapi/cpu/core.hpp
new file mode 100644 (file)
index 0000000..ec76fe5
--- /dev/null
@@ -0,0 +1,27 @@
+// 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_CPU_CORE_API_HPP
+#define OPENCV_GAPI_CPU_CORE_API_HPP
+
+#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
+#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
+
+namespace cv {
+namespace gapi {
+namespace core {
+namespace cpu {
+
+GAPI_EXPORTS GKernelPackage kernels();
+
+} // namespace cpu
+} // namespace core
+} // namespace gapi
+} // namespace cv
+
+
+#endif // OPENCV_GAPI_CPU_CORE_API_HPP
diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
new file mode 100644 (file)
index 0000000..daaca26
--- /dev/null
@@ -0,0 +1,236 @@
+// 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_GCPUKERNEL_HPP
+#define OPENCV_GAPI_GCPUKERNEL_HPP
+
+#include <vector>
+#include <functional>
+#include <map>
+#include <unordered_map>
+
+#include <opencv2/core/mat.hpp>
+#include <opencv2/gapi/gcommon.hpp>
+#include <opencv2/gapi/gkernel.hpp>
+#include <opencv2/gapi/garg.hpp>
+#include <opencv2/gapi/own/convert.hpp> //to_ocv
+#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+
+// FIXME: namespace scheme for backends?
+namespace cv {
+
+namespace gimpl
+{
+    // Forward-declare an internal class
+    class GCPUExecutable;
+} // namespace gimpl
+
+namespace gapi
+{
+namespace cpu
+{
+    GAPI_EXPORTS cv::gapi::GBackend backend();
+} // namespace cpu
+} // namespace gapi
+
+// Represents arguments which are passed to a wrapped CPU function
+// FIXME: put into detail?
+class GAPI_EXPORTS GCPUContext
+{
+public:
+    // Generic accessor API
+    template<typename T>
+    const T& inArg(int input) { return m_args.at(input).get<T>(); }
+
+    // Syntax sugar
+    const cv::gapi::own::Mat&   inMat(int input);
+    cv::gapi::own::Mat&         outMatR(int output); // FIXME: Avoid cv::gapi::own::Mat m = ctx.outMatR()
+
+    const cv::gapi::own::Scalar& inVal(int input);
+    cv::gapi::own::Scalar& outValR(int output); // FIXME: Avoid cv::gapi::own::Scalar s = ctx.outValR()
+    template<typename T> std::vector<T>& outVecR(int output) // FIXME: the same issue
+    {
+        return outVecRef(output).wref<T>();
+    }
+
+protected:
+    detail::VectorRef& outVecRef(int output);
+
+    std::vector<GArg> m_args;
+
+    //FIXME: avoid conversion of arguments from internal representaion to OpenCV one on each call
+    //to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
+    //once on enter for input and output arguments, and once before return for output arguments only
+    std::unordered_map<std::size_t, GRunArgP> m_results;
+
+    friend class gimpl::GCPUExecutable;
+};
+
+class GAPI_EXPORTS GCPUKernel
+{
+public:
+    // This function is kernel's execution entry point (does the processing work)
+    using F = std::function<void(GCPUContext &)>;
+
+    GCPUKernel();
+    explicit GCPUKernel(const F& f);
+
+    void apply(GCPUContext &ctx);
+
+protected:
+    F m_f;
+};
+
+// FIXME: This is an ugly ad-hoc imlpementation. TODO: refactor
+
+namespace detail
+{
+template<class T> struct get_in;
+template<> struct get_in<cv::GMat>
+{
+    static cv::Mat    get(GCPUContext &ctx, int idx) { return to_ocv(ctx.inMat(idx)); }
+};
+template<> struct get_in<cv::GScalar>
+{
+    static cv::Scalar get(GCPUContext &ctx, int idx) { return to_ocv(ctx.inVal(idx)); }
+};
+template<typename U> struct get_in<cv::GArray<U> >
+{
+    static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
+};
+template<class T> struct get_in
+{
+    static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
+};
+
+struct tracked_cv_mat{
+    tracked_cv_mat(cv::gapi::own::Mat& m) : r{to_ocv(m)}, original_data{m.data} {}
+    cv::Mat r;
+    uchar* original_data;
+
+    operator cv::Mat& (){ return r;}
+    void validate() const{
+        if (r.data != original_data)
+        {
+            util::throw_error
+                (std::logic_error
+                 ("OpenCV kernel output parameter was reallocated. \n"
+                  "Incorrect meta data was provided ?"));
+        }
+    }
+};
+
+struct scalar_wrapper
+{
+    scalar_wrapper(cv::gapi::own::Scalar& s) : m_s{cv::gapi::own::to_ocv(s)}, m_org_s(s) {};
+    operator cv::Scalar& () { return m_s; }
+    void writeBack() const  { m_org_s = to_own(m_s); }
+
+    cv::Scalar m_s;
+    cv::gapi::own::Scalar& m_org_s;
+};
+
+template<typename... Outputs>
+void postprocess(Outputs&... outs)
+{
+    struct
+    {
+        void operator()(tracked_cv_mat* bm) { bm->validate();  }
+        void operator()(scalar_wrapper* sw) { sw->writeBack(); }
+        void operator()(...)                {                  }
+
+    } validate;
+    //dummy array to unfold parameter pack
+    int dummy[] = { 0, (validate(&outs), 0)... };
+    cv::util::suppress_unused_warning(dummy);
+}
+
+template<class T> struct get_out;
+template<> struct get_out<cv::GMat>
+{
+    static tracked_cv_mat get(GCPUContext &ctx, int idx)
+    {
+        auto& r = ctx.outMatR(idx);
+        return {r};
+    }
+};
+template<> struct get_out<cv::GScalar>
+{
+    static scalar_wrapper get(GCPUContext &ctx, int idx)
+    {
+        auto& s = ctx.outValR(idx);
+        return {s};
+    }
+};
+template<typename U> struct get_out<cv::GArray<U>>
+{
+    static std::vector<U>& get(GCPUContext &ctx, int idx)
+    {
+        return ctx.outVecR<U>(idx);
+    }
+};
+
+template<typename, typename, typename>
+struct OCVCallHelper;
+
+// FIXME: probably can be simplified with std::apply or analogue.
+template<typename Impl, typename... Ins, typename... Outs>
+struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
+{
+    template<typename... Inputs>
+    struct call_and_postprocess
+    {
+        template<typename... Outputs>
+        static void call(Inputs&&... ins, Outputs&&... outs)
+        {
+            //not using a std::forward on outs is deliberate in order to
+            //cause compilation error, by tring to bind rvalue references to lvalue references
+            Impl::run(std::forward<Inputs>(ins)..., outs...);
+
+            postprocess(outs...);
+        }
+    };
+
+    template<int... IIs, int... OIs>
+    static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
+    {
+        //Make sure that OpenCV kernels do not reallocate memory for output parameters
+        //by comparing it's state (data ptr) before and after the call.
+        //This is done by converting each output Mat into tracked_cv_mat object, and binding
+        //them to parameters of ad-hoc function
+        //Convert own::Scalar to cv::Scalar before call kernel and run kernel
+        //convert cv::Scalar to own::Scalar after call kernel and write back results
+        call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
+    }
+
+    static void call(GCPUContext &ctx)
+    {
+        call_impl(ctx,
+                  typename detail::MkSeq<sizeof...(Ins)>::type(),
+                  typename detail::MkSeq<sizeof...(Outs)>::type());
+    }
+};
+
+} // namespace detail
+
+template<class Impl, class K>
+class GCPUKernelImpl: public detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
+{
+    using P = detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
+
+public:
+    using API = K;
+
+    static cv::gapi::GBackend backend()  { return cv::gapi::cpu::backend(); }
+    static cv::GCPUKernel     kernel()   { return GCPUKernel(&P::call);     }
+};
+
+#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GCPUKERNEL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp b/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp
new file mode 100644 (file)
index 0000000..0b96db0
--- /dev/null
@@ -0,0 +1,27 @@
+// 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_CPU_IMGPROC_API_HPP
+#define OPENCV_GAPI_CPU_IMGPROC_API_HPP
+
+#include <opencv2/core/cvdef.h>     // GAPI_EXPORTS
+#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
+
+namespace cv {
+namespace gapi {
+namespace imgproc {
+namespace cpu {
+
+GAPI_EXPORTS GKernelPackage kernels();
+
+} // namespace cpu
+} // namespace imgproc
+} // namespace gapi
+} // namespace cv
+
+
+#endif // OPENCV_GAPI_CPU_IMGPROC_API_HPP
diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp
new file mode 100644 (file)
index 0000000..dbed85a
--- /dev/null
@@ -0,0 +1,120 @@
+// 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_FLUID_BUFFER_HPP
+#define OPENCV_GAPI_FLUID_BUFFER_HPP
+
+#include <list>
+#include <numeric> // accumulate
+#include <ostream> // ostream
+#include <cstdint> // uint8_t
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/gmat.hpp>
+
+#include "opencv2/gapi/util/optional.hpp"
+#include "opencv2/gapi/own/scalar.hpp"
+
+namespace cv {
+namespace gapi {
+namespace fluid {
+
+struct Border
+{
+#if 1
+    Border(int _type, cv::Scalar _val) : type(_type), value(to_own(_val)) {};
+#endif
+    Border(int _type, cv::gapi::own::Scalar _val) : type(_type), value(_val) {};
+    int type;
+    cv::gapi::own::Scalar value;
+};
+
+using BorderOpt = util::optional<Border>;
+
+bool operator == (const Border& b1, const Border& b2);
+
+class GAPI_EXPORTS Buffer;
+
+class GAPI_EXPORTS View
+{
+public:
+    View() = default;
+
+    const uint8_t* InLineB(int index) const; // -(w-1)/2...0...+(w-1)/2 for Filters
+    template<typename T> const T* InLine(int i) const
+    {
+        const uint8_t* ptr = this->InLineB(i);
+        return reinterpret_cast<const T*>(ptr);
+    }
+
+    operator bool() const;
+    bool ready() const;
+    int length() const;
+    int y() const;
+
+    GMatDesc meta() const;
+
+    class GAPI_EXPORTS Priv;      // internal use only
+    Priv& priv();               // internal use only
+    const Priv& priv() const;   // internal use only
+
+    View(Priv* p);
+
+private:
+    std::shared_ptr<Priv> m_priv;
+};
+
+class GAPI_EXPORTS Buffer
+{
+public:
+    // Default constructor (executable creation stage,
+    // all following initialization performed in Priv::init())
+    Buffer();
+    // Scratch constructor (user kernels)
+    Buffer(const cv::GMatDesc &desc);
+
+    // Constructor for intermediate buffers (for tests)
+    Buffer(const cv::GMatDesc &desc,
+           int max_line_consumption, int border_size,
+           int skew,
+           int wlpi,
+           BorderOpt border);
+    // Constructor for in/out buffers (for tests)
+    Buffer(const cv::Mat &data, bool is_input);
+
+    uint8_t* OutLineB(int index = 0);
+    template<typename T> T* OutLine(int index = 0)
+    {
+        uint8_t* ptr = this->OutLineB(index);
+        return reinterpret_cast<T*>(ptr);
+    }
+
+    int y() const;
+
+    int linesReady() const;
+    void debug(std::ostream &os) const;
+    int length() const;
+    int lpi() const;  // LPI for WRITER
+
+    GMatDesc meta() const;
+
+    View mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage);
+
+    class GAPI_EXPORTS Priv;      // internal use only
+    Priv& priv();               // internal use only
+    const Priv& priv() const;   // internal use only
+
+private:
+    std::shared_ptr<Priv> m_priv;
+
+};
+
+} // namespace cv::gapi::fluid
+} // namespace cv::gapi
+} // namespace cv
+
+#endif // OPENCV_GAPI_FLUID_BUFFER_HPP
diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp
new file mode 100644 (file)
index 0000000..220b641
--- /dev/null
@@ -0,0 +1,283 @@
+// 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_FLUID_KERNEL_HPP
+#define OPENCV_GAPI_FLUID_KERNEL_HPP
+
+#include <vector>
+#include <functional>
+#include <map>
+#include <unordered_map>
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/gcommon.hpp>
+#include <opencv2/gapi/gkernel.hpp>
+#include <opencv2/gapi/garg.hpp>
+#include <opencv2/gapi/own/types.hpp>
+
+#include <opencv2/gapi/fluid/gfluidbuffer.hpp>
+
+// FIXME: namespace scheme for backends?
+namespace cv {
+
+namespace gapi
+{
+namespace fluid
+{
+    GAPI_EXPORTS cv::gapi::GBackend backend();
+} // namespace flud
+} // namespace gapi
+
+class GAPI_EXPORTS GFluidKernel
+{
+public:
+    enum class Kind
+    {
+        Filter,
+        Resize
+    };
+
+    // This function is a generic "doWork" callback
+    using F = std::function<void(const cv::GArgs&, const std::vector<gapi::fluid::Buffer*> &)>;
+
+    // This function is a generic "initScratch" callback
+    using IS = std::function<void(const cv::GMetaArgs &, const cv::GArgs&, gapi::fluid::Buffer &)>;
+
+    // This function is a generic "resetScratch" callback
+    using RS = std::function<void(gapi::fluid::Buffer &)>;
+
+    // This function describes kernel metadata inference rule.
+    using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>;
+
+    // This function is a generic "getBorder" callback (extracts border-related data from kernel's input parameters)
+    using B = std::function<gapi::fluid::BorderOpt(const GMetaArgs&, const GArgs&)>;
+
+    // FIXME: move implementations out of header file
+    GFluidKernel() {}
+    GFluidKernel(int w, Kind k, int l, bool scratch, const F& f, const IS &is, const RS &rs, const B& b)
+        : m_window(w)
+        , m_kind(k)
+        , m_lpi(l)
+        , m_scratch(scratch)
+        , m_f(f)
+        , m_is(is)
+        , m_rs(rs)
+        , m_b(b) {}
+
+    int m_window = -1;
+    Kind m_kind;
+    const int  m_lpi     = -1;
+    const bool m_scratch = false;
+
+    const F    m_f;
+    const IS   m_is;
+    const RS   m_rs;
+    const B    m_b;
+};
+
+// FIXME!!!
+// This is the temporary and experimental API
+// which should be replaced by runtime roi-based scheduling
+struct GFluidOutputRois
+{
+    std::vector<cv::gapi::own::Rect> rois;
+};
+
+namespace detail
+{
+template<> struct CompileArgTag<GFluidOutputRois>
+{
+    static const char* tag() { return "gapi.fluid.outputRois"; }
+};
+} // namespace detail
+
+namespace detail
+{
+template<class T> struct fluid_get_in;
+template<> struct fluid_get_in<cv::GMat>
+{
+    static const cv::gapi::fluid::View& get(const cv::GArgs &in_args, int idx)
+    {
+        return in_args[idx].get<cv::gapi::fluid::View>();
+    }
+};
+
+template<> struct fluid_get_in<cv::GScalar>
+{
+    static const cv::Scalar get(const cv::GArgs &in_args, int idx)
+    {
+        return cv::gapi::own::to_ocv(in_args[idx].get<cv::gapi::own::Scalar>());
+    }
+};
+template<class T> struct fluid_get_in
+{
+    static T get(const cv::GArgs &in_args, int idx)
+    {
+        return in_args[idx].get<T>();
+    }
+};
+
+template<bool, typename Impl, typename... Ins>
+struct scratch_helper;
+
+template<typename Impl, typename... Ins>
+struct scratch_helper<true, Impl, Ins...>
+{
+    // Init
+    template<int... IIs>
+    static void help_init_impl(const cv::GMetaArgs &metas,
+                               const cv::GArgs     &in_args,
+                               gapi::fluid::Buffer &scratch_buf,
+                               detail::Seq<IIs...>)
+    {
+        Impl::initScratch(get_in_meta<Ins>(metas, in_args, IIs)..., scratch_buf);
+    }
+
+    static void help_init(const cv::GMetaArgs &metas,
+                          const cv::GArgs     &in_args,
+                          gapi::fluid::Buffer &b)
+    {
+        help_init_impl(metas, in_args, b, typename detail::MkSeq<sizeof...(Ins)>::type());
+    }
+
+    // Reset
+    static void help_reset(gapi::fluid::Buffer &b)
+    {
+        Impl::resetScratch(b);
+    }
+};
+
+template<typename Impl, typename... Ins>
+struct scratch_helper<false, Impl, Ins...>
+{
+    static void help_init(const cv::GMetaArgs &,
+                          const cv::GArgs     &,
+                          gapi::fluid::Buffer &)
+    {
+        GAPI_Assert(false);
+    }
+    static void help_reset(gapi::fluid::Buffer &)
+    {
+        GAPI_Assert(false);
+    }
+};
+
+template<typename T> struct is_gmat_type
+{
+    static const constexpr bool value = std::is_same<cv::GMat, T>::value;
+};
+
+template<bool CallCustomGetBorder, typename Impl, typename... Ins>
+struct get_border_helper;
+
+template<typename Impl, typename... Ins>
+struct get_border_helper<true, Impl, Ins...>
+{
+    template<int... IIs>
+    static gapi::fluid::BorderOpt get_border_impl(const GMetaArgs &metas,
+                                                  const cv::GArgs &in_args,
+                                                  cv::detail::Seq<IIs...>)
+    {
+        return util::make_optional(Impl::getBorder(cv::detail::get_in_meta<Ins>(metas, in_args, IIs)...));
+    }
+
+    static gapi::fluid::BorderOpt help(const GMetaArgs &metas,
+                                       const cv::GArgs &in_args)
+    {
+        return get_border_impl(metas, in_args, typename detail::MkSeq<sizeof...(Ins)>::type());
+    }
+};
+
+template<typename Impl, typename... Ins>
+struct get_border_helper<false, Impl, Ins...>
+{
+    static gapi::fluid::BorderOpt help(const cv::GMetaArgs &,
+                                       const cv::GArgs     &)
+    {
+        return {};
+    }
+};
+
+template<typename, typename, typename, bool UseScratch>
+struct FluidCallHelper;
+
+template<typename Impl, typename... Ins, typename... Outs, bool UseScratch>
+struct FluidCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>, UseScratch>
+{
+    static_assert(all_satisfy<is_gmat_type, Outs...>::value, "return type must be GMat");
+
+    // Execution dispatcher ////////////////////////////////////////////////////
+    template<int... IIs, int... OIs>
+    static void call_impl(const cv::GArgs &in_args,
+                          const std::vector<gapi::fluid::Buffer*> &out_bufs,
+                          detail::Seq<IIs...>,
+                          detail::Seq<OIs...>)
+    {
+        Impl::run(fluid_get_in<Ins>::get(in_args, IIs)..., *out_bufs[OIs]...);
+    }
+
+    static void call(const cv::GArgs &in_args,
+                     const std::vector<gapi::fluid::Buffer*> &out_bufs)
+    {
+        constexpr int numOuts = (sizeof...(Outs)) + (UseScratch ? 1 : 0);
+        call_impl(in_args, out_bufs,
+                  typename detail::MkSeq<sizeof...(Ins)>::type(),
+                  typename detail::MkSeq<numOuts>::type());
+    }
+
+    // Scratch buffer initialization dispatcher ////////////////////////////////
+    static void init_scratch(const GMetaArgs &metas,
+                             const cv::GArgs &in_args,
+                             gapi::fluid::Buffer &b)
+    {
+        scratch_helper<UseScratch, Impl, Ins...>::help_init(metas, in_args, b);
+    }
+
+    // Scratch buffer reset dispatcher /////////////////////////////////////////
+    static void reset_scratch(gapi::fluid::Buffer &scratch_buf)
+    {
+        scratch_helper<UseScratch, Impl, Ins...>::help_reset(scratch_buf);
+    }
+
+    static gapi::fluid::BorderOpt getBorder(const GMetaArgs &metas, const cv::GArgs &in_args)
+    {
+        // User must provide "init" callback if Window != 1
+        // TODO: move to constexpr if when we enable C++17
+        constexpr bool callCustomGetBorder = (Impl::Window != 1);
+        return get_border_helper<callCustomGetBorder, Impl, Ins...>::help(metas, in_args);
+    }
+};
+} // namespace detail
+
+
+template<class Impl, class K, bool UseScratch>
+class GFluidKernelImpl
+{
+    static const int LPI = 1;
+    static const auto Kind = GFluidKernel::Kind::Filter;
+    using P = detail::FluidCallHelper<Impl, typename K::InArgs, typename K::OutArgs, UseScratch>;
+
+public:
+    using API = K;
+
+    static GFluidKernel kernel()
+    {
+        // FIXME: call() and getOutMeta() needs to be renamed so it is clear these
+        // functions are internal wrappers, not user API
+        return GFluidKernel(Impl::Window, Impl::Kind, Impl::LPI,
+                            UseScratch,
+                            &P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder);
+    }
+
+    static cv::gapi::GBackend backend() { return cv::gapi::fluid::backend(); }
+};
+
+#define GAPI_FLUID_KERNEL(Name, API, Scratch) struct Name: public cv::GFluidKernelImpl<Name, API, Scratch>
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GCPUKERNEL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/garg.hpp b/modules/gapi/include/opencv2/gapi/garg.hpp
new file mode 100644 (file)
index 0000000..26c0d0a
--- /dev/null
@@ -0,0 +1,98 @@
+// 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_GARG_HPP
+#define OPENCV_GAPI_GARG_HPP
+
+#include <vector>
+#include <type_traits>
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include "opencv2/gapi/own/mat.hpp"
+
+#include "opencv2/gapi/util/any.hpp"
+#include "opencv2/gapi/util/variant.hpp"
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/garray.hpp"
+#include "opencv2/gapi/gtype_traits.hpp"
+#include "opencv2/gapi/gmetaarg.hpp"
+#include "opencv2/gapi/own/scalar.hpp"
+
+namespace cv {
+
+class GArg;
+
+namespace detail {
+    template<typename T>
+    using is_garg = std::is_same<GArg, typename std::decay<T>::type>;
+}
+
+// Parameter holder class for a node
+// Depending on platform capabilities, can either support arbitrary types
+// (as `boost::any`) or a limited number of types (as `boot::variant`).
+// FIXME: put into "details" as a user shouldn't use it in his code
+class GAPI_EXPORTS GArg
+{
+public:
+    GArg() {}
+
+    template<typename T, typename std::enable_if<!detail::is_garg<T>::value, int>::type = 0>
+    explicit GArg(const T &t)
+        : kind(detail::GTypeTraits<T>::kind)
+        , value(detail::wrap_gapi_helper<T>::wrap(t))
+    {
+    }
+
+    template<typename T, typename std::enable_if<!detail::is_garg<T>::value, int>::type = 0>
+    explicit GArg(T &&t)
+        : kind(detail::GTypeTraits<typename std::decay<T>::type>::kind)
+        , value(detail::wrap_gapi_helper<T>::wrap(t))
+    {
+    }
+
+    template<typename T> T& get()
+    {
+        return util::any_cast<typename std::remove_reference<T>::type>(value);
+    }
+
+    template<typename T> const T& get() const
+    {
+        return util::any_cast<typename std::remove_reference<T>::type>(value);
+    }
+
+    detail::ArgKind kind = detail::ArgKind::OPAQUE;
+
+protected:
+    util::any value;
+};
+
+using GArgs = std::vector<GArg>;
+
+// FIXME: Express as M<GProtoArg...>::type
+// FIXME: Move to a separate file!
+using GRunArg  = util::variant<cv::Mat, cv::gapi::own::Mat, cv::Scalar, cv::gapi::own::Scalar, cv::detail::VectorRef>;
+using GRunArgs = std::vector<GRunArg>;
+
+using GRunArgP = util::variant<cv::Mat*, cv::gapi::own::Mat*, cv::Scalar*, cv::gapi::own::Scalar*, cv::detail::VectorRef>;
+using GRunArgsP = std::vector<GRunArgP>;
+
+
+template<typename... Ts> inline GRunArgs gin(const Ts&... args)
+{
+    return GRunArgs{ GRunArg(detail::wrap_host_helper<Ts>::wrap_in(args))... };
+}
+
+template<typename... Ts> inline GRunArgsP gout(Ts&... args)
+{
+    return GRunArgsP{ GRunArgP(detail::wrap_host_helper<Ts>::wrap_out(args))... };
+}
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GARG_HPP
diff --git a/modules/gapi/include/opencv2/gapi/garray.hpp b/modules/gapi/include/opencv2/gapi/garray.hpp
new file mode 100644 (file)
index 0000000..33d9b58
--- /dev/null
@@ -0,0 +1,239 @@
+// 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_GARRAY_HPP
+#define OPENCV_GAPI_GARRAY_HPP
+
+#include <functional>
+#include <ostream>
+#include <vector>
+#include <memory>
+
+#include <opencv2/gapi/own/exports.hpp>
+#include <opencv2/gapi/opencv_includes.hpp>
+
+#include <opencv2/gapi/util/variant.hpp>
+#include <opencv2/gapi/util/throw.hpp>
+#include "opencv2/gapi/own/assert.hpp"
+
+namespace cv
+{
+// Forward declaration; GNode and GOrigin are an internal
+// (user-inaccessible) classes.
+class GNode;
+struct GOrigin;
+
+template<typename T> class GArray;
+
+struct GArrayDesc
+{
+    // FIXME: Body
+    // FIXME: Also implement proper operator== then
+    bool operator== (const GArrayDesc&) const { return true; }
+};
+template<typename U> GArrayDesc descr_of(const std::vector<U> &) { return {};}
+inline GArrayDesc empty_array_desc() {return {}; }
+std::ostream& operator<<(std::ostream& os, const cv::GArrayDesc &desc);
+
+namespace detail
+{
+    // ConstructVec is a callback which stores information about T and is used by
+    // G-API runtime to construct arrays in host memory (T remains opaque for G-API).
+    // ConstructVec is carried into G-API internals by GArrayU.
+    // Currently it is suitable for Host (CPU) plugins only, real offload may require
+    // more information for manual memory allocation on-device.
+    class VectorRef;
+    using ConstructVec = std::function<void(VectorRef&)>;
+
+
+    // This class strips type information from GArray<T> and makes it usable
+    // in the G-API graph compiler (expression unrolling, graph generation, etc).
+    // Part of GProtoArg.
+    class GAPI_EXPORTS GArrayU
+    {
+    public:
+        GArrayU(const GNode &n, std::size_t out); // Operation result constructor
+
+        GOrigin& priv();                          // Internal use only
+        const GOrigin& priv() const;              // Internal use only
+
+    protected:
+        GArrayU();                                // Default constructor
+        template<class> friend class cv::GArray;  //  (avialable to GArray<T> only)
+
+        void setConstructFcn(ConstructVec &&cv);  // Store T-aware constructor
+
+        std::shared_ptr<GOrigin> m_priv;
+    };
+
+    // This class represents a typed STL vector reference.
+    // Depending on origins, this reference may be either "just a" reference to
+    // an object created externally, OR actually own the underlying object
+    // (be value holder).
+    class BasicVectorRef
+    {
+    public:
+        std::size_t    m_elemSize = 0ul;
+        cv::GArrayDesc m_desc;
+        virtual ~BasicVectorRef() {}
+    };
+
+    template<typename T> class VectorRefT: public BasicVectorRef
+    {
+        using empty_t  = util::monostate;
+        using ro_ext_t = const std::vector<T> *;
+        using rw_ext_t =       std::vector<T> *;
+        using rw_own_t =       std::vector<T>  ;
+        util::variant<empty_t, ro_ext_t, rw_ext_t, rw_own_t> m_ref;
+
+        inline bool isEmpty() const { return util::holds_alternative<empty_t>(m_ref);  }
+        inline bool isROExt() const { return util::holds_alternative<ro_ext_t>(m_ref); }
+        inline bool isRWExt() const { return util::holds_alternative<rw_ext_t>(m_ref); }
+        inline bool isRWOwn() const { return util::holds_alternative<rw_own_t>(m_ref); }
+
+        void init(const std::vector<T>* vec = nullptr)
+        {
+            m_elemSize = sizeof(T);
+            if (vec) m_desc = cv::descr_of(*vec);
+        }
+
+    public:
+        VectorRefT() { init(); }
+        virtual ~VectorRefT() {}
+
+        explicit VectorRefT(const std::vector<T>& vec) : m_ref(&vec)      { init(&vec); }
+        explicit VectorRefT(std::vector<T>& vec)  : m_ref(&vec)           { init(&vec); }
+        explicit VectorRefT(std::vector<T>&& vec) : m_ref(std::move(vec)) { init(&vec); }
+
+        // Reset a VectorRefT. Called only for objects instantiated
+        // internally in G-API (e.g. temporary GArray<T>'s within a
+        // computation).  Reset here means both initialization
+        // (creating an object) and reset (discarding its existing
+        // content before the next execution).  Must never be called
+        // for external VectorRefTs.
+        void reset()
+        {
+            if (isEmpty())
+            {
+                std::vector<T> empty_vector;
+                m_desc = cv::descr_of(empty_vector);
+                m_ref  = std::move(empty_vector);
+                GAPI_Assert(isRWOwn());
+            }
+            else if (isRWOwn())
+            {
+                util::get<rw_own_t>(m_ref).clear();
+            }
+            else GAPI_Assert(false); // shouldn't be called in *EXT modes
+        }
+
+        // Obtain a WRITE reference to underlying object
+        // Used by CPU kernel API wrappers when a kernel execution frame
+        // is created
+        std::vector<T>& wref()
+        {
+            GAPI_Assert(isRWExt() || isRWOwn());
+            if (isRWExt()) return *util::get<rw_ext_t>(m_ref);
+            if (isRWOwn()) return  util::get<rw_own_t>(m_ref);
+            util::throw_error(std::logic_error("Impossible happened"));
+        }
+
+        // Obtain a READ reference to underlying object
+        // Used by CPU kernel API wrappers when a kernel execution frame
+        // is created
+        const std::vector<T>& rref() const
+        {
+            // ANY vector can be accessed for reading, even if it declared for
+            // output. Example -- a GComputation from [in] to [out1,out2]
+            // where [out2] is a result of operation applied to [out1]:
+            //
+            //            GComputation boundary
+            //            . . . . . . .
+            //            .           .
+            //     [in] ----> foo() ----> [out1]
+            //            .           .    :
+            //            .           . . .:. . .
+            //            .                V    .
+            //            .              bar() ---> [out2]
+            //            . . . . . . . . . . . .
+            //
+            if (isROExt()) return *util::get<ro_ext_t>(m_ref);
+            if (isRWExt()) return *util::get<rw_ext_t>(m_ref);
+            if (isRWOwn()) return  util::get<rw_own_t>(m_ref);
+            util::throw_error(std::logic_error("Impossible happened"));
+        }
+    };
+
+    // This class strips type information from VectorRefT<> and makes it usable
+    // in the G-API executables (carrying run-time data/information to kernels).
+    // Part of GRunArg.
+    // Its methods are typed proxies to VectorRefT<T>.
+    // VectorRef maintains "reference" semantics so two copies of VectoRef refer
+    // to the same underlying object.
+    // FIXME: Put a good explanation on why cv::OutputArray doesn't fit this role
+    class VectorRef
+    {
+        std::shared_ptr<BasicVectorRef> m_ref;
+
+        template<typename T> inline void check() const
+        {
+            GAPI_DbgAssert(dynamic_cast<VectorRefT<T>*>(m_ref.get()) != nullptr);
+            GAPI_Assert(sizeof(T) == m_ref->m_elemSize);
+        }
+
+    public:
+        VectorRef() = default;
+        template<typename T> explicit VectorRef(const std::vector<T>& vec) : m_ref(new VectorRefT<T>(vec)) {}
+        template<typename T> explicit VectorRef(std::vector<T>& vec)       : m_ref(new VectorRefT<T>(vec)) {}
+        template<typename T> explicit VectorRef(std::vector<T>&& vec)      : m_ref(new VectorRefT<T>(vec)) {}
+
+        template<typename T> void reset()
+        {
+            if (!m_ref) m_ref.reset(new VectorRefT<T>());
+
+            check<T>();
+            static_cast<VectorRefT<T>&>(*m_ref).reset();
+        }
+
+        template<typename T> std::vector<T>& wref()
+        {
+            check<T>();
+            return static_cast<VectorRefT<T>&>(*m_ref).wref();
+        }
+
+        template<typename T> const std::vector<T>& rref() const
+        {
+            check<T>();
+            return static_cast<VectorRefT<T>&>(*m_ref).rref();
+        }
+
+        cv::GArrayDesc descr_of() const
+        {
+            return m_ref->m_desc;
+        }
+    };
+} // namespace detail
+
+template<typename T> class GArray
+{
+public:
+    GArray() { putDetails(); }             // Empty constructor
+    explicit GArray(detail::GArrayU &&ref) // GArrayU-based constructor
+        : m_ref(ref) { putDetails(); }     //   (used by GCall, not for users)
+
+    detail::GArrayU strip() const { return m_ref; }
+
+private:
+    static void VCTor(detail::VectorRef& vref) { vref.reset<T>(); }
+    void putDetails() {m_ref.setConstructFcn(&VCTor); }
+
+    detail::GArrayU m_ref;
+};
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GARRAY_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gcall.hpp b/modules/gapi/include/opencv2/gapi/gcall.hpp
new file mode 100644 (file)
index 0000000..baf4f44
--- /dev/null
@@ -0,0 +1,63 @@
+// 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_GCALL_HPP
+#define OPENCV_GAPI_GCALL_HPP
+
+#include "opencv2/gapi/garg.hpp"      // GArg
+#include "opencv2/gapi/gmat.hpp"      // GMat
+#include "opencv2/gapi/gscalar.hpp"   // GScalar
+#include "opencv2/gapi/garray.hpp"    // GArray<T>
+
+namespace cv {
+
+struct GKernel;
+
+// The whole idea of this class is to represent an operation
+// which is applied to arguments. This is part of public API,
+// since it is what users should use to define kernel interfaces.
+
+class GAPI_EXPORTS GCall final
+{
+public:
+    class Priv;
+
+    explicit GCall(const GKernel &k);
+    ~GCall();
+
+    template<typename... Ts>
+    GCall& pass(Ts&&... args)
+    {
+        setArgs({cv::GArg(std::move(args))...});
+        return *this;
+    }
+
+    // A generic yield method - obtain a link to operator's particular GMat output
+    GMat    yield      (int output = 0);
+    GScalar yieldScalar(int output = 0);
+
+    template<class T> GArray<T> yieldArray(int output = 0)
+    {
+        return GArray<T>(yieldArray(output));
+    }
+
+    // Internal use only
+    Priv& priv();
+    const Priv& priv() const;
+
+protected:
+    std::shared_ptr<Priv> m_priv;
+
+    void setArgs(std::vector<GArg> &&args);
+
+    // Public version returns a typed array, this one is implementation detail
+    detail::GArrayU yieldArray(int output = 0);
+};
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GCALL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gcommon.hpp b/modules/gapi/include/opencv2/gapi/gcommon.hpp
new file mode 100644 (file)
index 0000000..e84c82c
--- /dev/null
@@ -0,0 +1,118 @@
+// 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_GCOMMON_HPP
+#define OPENCV_GAPI_GCOMMON_HPP
+
+#include <functional>   // std::hash
+#include <vector>       // std::vector
+#include <type_traits>  // decay
+
+#include <opencv2/gapi/opencv_includes.hpp>
+
+#include "opencv2/gapi/util/any.hpp"
+#include "opencv2/gapi/own/exports.hpp"
+#include "opencv2/gapi/own/assert.hpp"
+
+namespace cv {
+
+namespace detail
+{
+    // This is a trait-like structure to mark backend-specific compile arguments
+    // with tags
+    template<typename T> struct CompileArgTag;
+    template<typename T> struct CompileArgTag
+    {
+        static const char* tag() { return ""; };
+    };
+}
+
+// This definition is here because it is reused by both public(?) and internal
+// modules. Keeping it here wouldn't expose public details (e.g., API-level)
+// to components which are internal and operate on a lower-level entities
+// (e.g., compiler, backends).
+// FIXME: merge with ArgKind?
+// FIXME: replace with variant[format desc]?
+enum class GShape: int
+{
+    GMAT,
+    GSCALAR,
+    GARRAY,
+};
+
+struct GCompileArg;
+
+namespace detail {
+    template<typename T>
+    using is_compile_arg = std::is_same<GCompileArg, typename std::decay<T>::type>;
+}
+// CompileArg is an unified interface over backend-specific compilation
+// information
+// FIXME: Move to a separate file?
+struct GAPI_EXPORTS GCompileArg
+{
+public:
+    std::string tag;
+
+    // FIXME: use decay in GArg/other trait-based wrapper before leg is shot!
+    template<typename T, typename std::enable_if<!detail::is_compile_arg<T>::value, int>::type = 0>
+    explicit GCompileArg(T &&t)
+        : tag(detail::CompileArgTag<typename std::decay<T>::type>::tag())
+        , arg(t)
+    {
+    }
+
+    template<typename T> T& get()
+    {
+        return util::any_cast<T>(arg);
+    }
+
+    template<typename T> const T& get() const
+    {
+        return util::any_cast<T>(arg);
+    }
+
+private:
+    util::any arg;
+};
+
+using GCompileArgs = std::vector<GCompileArg>;
+
+template<typename... Ts> GCompileArgs compile_args(Ts&&... args)
+{
+    return GCompileArgs{ GCompileArg(args)... };
+}
+
+struct graph_dump_path
+{
+    std::string m_dump_path;
+};
+
+namespace detail
+{
+    template<> struct CompileArgTag<cv::graph_dump_path>
+    {
+        static const char* tag() { return "gapi.graph_dump_path"; }
+    };
+}
+
+} // namespace cv
+
+// std::hash overload for GShape
+namespace std
+{
+template<> struct hash<cv::GShape>
+{
+    size_t operator() (cv::GShape sh) const
+    {
+        return std::hash<int>()(static_cast<int>(sh));
+    }
+};
+} // namespace std
+
+
+#endif // OPENCV_GAPI_GCOMMON_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gcompiled.hpp b/modules/gapi/include/opencv2/gapi/gcompiled.hpp
new file mode 100644 (file)
index 0000000..05e32b1
--- /dev/null
@@ -0,0 +1,59 @@
+// 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_GCOMPILED_HPP
+#define OPENCV_GAPI_GCOMPILED_HPP
+
+#include <vector>
+
+#include "opencv2/gapi/own/assert.hpp"
+#include "opencv2/core/mat.hpp"
+
+#include "opencv2/gapi/garg.hpp"
+
+namespace cv {
+
+// This class represents a compiled computation.
+// In theory (and ideally), it can be used w/o the rest of APIs.
+// In theory (and ideally), it can be serialized/deserialized.
+// It can enable scenarious like deployment to an autonomous devince, FuSa, etc.
+//
+// Currently GCompiled assumes all GMats you used to pass data to G-API
+// are valid and not destroyed while you use a GCompiled object.
+//
+// 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).
+
+class GAPI_EXPORTS GCompiled
+{
+public:
+    class GAPI_EXPORTS Priv;
+
+    GCompiled();
+
+    void operator() (GRunArgs &&ins, GRunArgsP &&outs);          // Generic arg-to-arg
+    void operator() (cv::Mat in, cv::Mat &out);                  // Unary overload
+    void operator() (cv::Mat in, cv::Scalar &out);               // Unary overload (scalar)
+    void operator() (cv::Mat in1, cv::Mat in2, cv::Mat &out);    // Binary overload
+    void operator() (cv::Mat in1, cv::Mat in2, cv::Scalar &out); // Binary overload (scalar)
+    void operator() (const std::vector<cv::Mat> &ins,            // Compatibility overload
+                     const std::vector<cv::Mat> &outs);
+
+    Priv& priv();
+
+    explicit operator bool () const; // Check if GCompiled is runnable or empty
+
+    const GMetaArgs& metas() const; // Meta passed to compile()
+    const GMetaArgs& outMetas() const; // Inferred output metadata
+
+protected:
+    std::shared_ptr<Priv> m_priv;
+};
+
+}
+
+#endif // OPENCV_GAPI_GCOMPILED_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp b/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp
new file mode 100644 (file)
index 0000000..c5ac8a7
--- /dev/null
@@ -0,0 +1,123 @@
+// 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_GCOMPOUNDKERNEL_HPP
+#define OPENCV_GAPI_GCOMPOUNDKERNEL_HPP
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/gcommon.hpp>
+#include <opencv2/gapi/gkernel.hpp>
+#include <opencv2/gapi/garg.hpp>
+
+namespace cv {
+namespace gapi
+{
+namespace compound
+{
+    // FIXME User does not need to know about this function
+    // Needs that user may define compound kernels(as cpu kernels)
+    GAPI_EXPORTS cv::gapi::GBackend backend();
+} // namespace compound
+} // namespace gapi
+
+namespace detail
+{
+
+struct GCompoundContext
+{
+    explicit GCompoundContext(const GArgs& in_args);
+    template<typename T>
+    const T& inArg(int input) { return m_args.at(input).get<T>(); }
+
+    GArgs m_args;
+    GArgs m_results;
+};
+
+class GAPI_EXPORTS GCompoundKernel
+{
+// Compound kernel must use all of it's inputs
+public:
+    using F = std::function<void(GCompoundContext& ctx)>;
+
+    explicit GCompoundKernel(const F& f);
+    void apply(GCompoundContext& ctx);
+
+protected:
+    F m_f;
+};
+
+template<typename T> struct get_compound_in
+{
+    static T get(GCompoundContext &ctx, int idx) { return ctx.inArg<T>(idx); }
+};
+
+template<typename U> struct get_compound_in<cv::GArray<U>>
+{
+    static cv::GArray<U> get(GCompoundContext &ctx, int idx)
+    {
+        auto array = cv::GArray<U>();
+        ctx.m_args[idx] = GArg(array);
+        return array;
+    }
+};
+
+// Kernel may return one object(GMat, GScalar) or a tuple of objects.
+// This helper is needed to cast return value to the same form(tuple)
+template<typename>
+struct tuple_wrap_helper;
+
+template<typename T> struct tuple_wrap_helper
+{
+    static std::tuple<T> get(T&& obj) { return std::make_tuple(std::move(obj)); }
+};
+
+template<typename... Objs>
+struct tuple_wrap_helper<std::tuple<Objs...>>
+{
+    static std::tuple<Objs...> get(std::tuple<Objs...>&& objs) { return objs; }
+};
+
+template<typename, typename, typename>
+struct GCompoundCallHelper;
+
+template<typename Impl, typename... Ins, typename... Outs>
+struct GCompoundCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
+{
+    template<int... IIs, int... OIs>
+    static void expand_impl(GCompoundContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
+    {
+        auto result = Impl::expand(get_compound_in<Ins>::get(ctx, IIs)...);
+        auto tuple_return = tuple_wrap_helper<decltype(result)>::get(std::move(result));
+        ctx.m_results = { cv::GArg(std::get<OIs>(tuple_return))... };
+    }
+
+    static void expand(GCompoundContext &ctx)
+    {
+        expand_impl(ctx,
+                    typename detail::MkSeq<sizeof...(Ins)>::type(),
+                    typename detail::MkSeq<sizeof...(Outs)>::type());
+    }
+};
+
+template<class Impl, class K>
+class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper<Impl, typename K::InArgs, typename K::OutArgs>
+{
+    using P = cv::detail::GCompoundCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
+
+public:
+    using API = K;
+
+    static cv::gapi::GBackend backend() { return cv::gapi::compound::backend(); }
+    static GCompoundKernel    kernel()  { return GCompoundKernel(&P::expand);   }
+};
+
+} // namespace detail
+#define GAPI_COMPOUND_KERNEL(Name, API) struct Name: public cv::detail::GCompoundKernelImpl<Name, API>
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GCOMPOUNDKERNEL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gcomputation.hpp b/modules/gapi/include/opencv2/gapi/gcomputation.hpp
new file mode 100644 (file)
index 0000000..d5d5fd6
--- /dev/null
@@ -0,0 +1,135 @@
+// 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"
+
+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;
+}
+
+class GAPI_EXPORTS GComputation
+{
+public:
+    class Priv;
+    typedef std::function<GComputation()> Generator;
+
+    // Various constructors enable different ways to define a computation: /////
+    // 1. Generic constructors
+    GComputation(const Generator& gen);                // Generator overload
+    GComputation(GProtoInputArgs &&ins,
+                 GProtoOutputArgs &&outs);             // Arg-to-arg overload
+
+    // 2. Syntax sugar and compatibility overloads
+    GComputation(GMat in, GMat out);                   // Unary overload
+    GComputation(GMat in, GScalar out);                // Unary overload (scalar)
+    GComputation(GMat in1, GMat in2, GMat out);        // Binary overload
+    GComputation(GMat in1, GMat in2, GScalar out);     // Binary overload (scalar)
+    GComputation(const std::vector<GMat> &ins,         // Compatibility overload
+                 const std::vector<GMat> &outs);
+
+    // Various versions of apply(): ////////////////////////////////////////////
+    // 1. Generic apply()
+    void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {});       // Arg-to-arg overload
+
+    // 2. Syntax sugar and compatibility overloads
+    void apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args = {});               // Unary overload
+    void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {});            // Unary overload (scalar)
+    void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload
+    void apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args = {}); // Binary overload (scalar)
+    void apply(const std::vector<cv::Mat>& ins,         // Compatibility overload
+               const std::vector<cv::Mat>& outs,
+               GCompileArgs &&args = {});
+
+    // Various versions of compile(): //////////////////////////////////////////
+    // 1. Generic compile() - requires metas to be passed as vector
+    GCompiled compile(GMetaArgs &&in_metas, GCompileArgs &&args = {});
+
+    // 2. Syntax sugar - variadic list of metas, no extra compile args
+    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).
+    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());
+    }
+
+    // Internal use only
+    Priv& priv();
+    const Priv& priv() const;
+
+protected:
+
+    // 4. Helper method for (3)
+    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));
+    }
+
+    std::shared_ptr<Priv> 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.
+    void GAPI_EXPORTS island(const std::string &name,
+                           GProtoInputArgs  &&ins,
+                           GProtoOutputArgs &&outs);
+} // namespace gapi
+
+} // namespace cv
+#endif // OPENCV_GAPI_GCOMPUTATION_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp
new file mode 100644 (file)
index 0000000..36030f5
--- /dev/null
@@ -0,0 +1,409 @@
+// 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_GKERNEL_HPP
+#define OPENCV_GAPI_GKERNEL_HPP
+
+#include <functional>
+#include <iostream>
+#include <unordered_map> // map (for GKernelPackage)
+#include <vector>  // lookup order
+#include <string>  // string
+#include <utility> // tuple
+#include <type_traits> // false_type, true_type
+
+#include <opencv2/gapi/gcommon.hpp> // CompileArgTag
+#include <opencv2/gapi/util/util.hpp> // Seq
+#include <opencv2/gapi/gcall.hpp>
+#include <opencv2/gapi/garg.hpp>      // GArg
+#include <opencv2/gapi/gmetaarg.hpp>  // GMetaArg
+#include <opencv2/gapi/gtype_traits.hpp> // GTypeTraits
+#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+
+
+namespace cv {
+
+using GShapes = std::vector<GShape>;
+
+// GKernel describes kernel API to the system
+// FIXME: add attributes of a kernel, (e.g. number and types
+// of inputs, etc)
+struct GAPI_EXPORTS GKernel
+{
+    using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>;
+
+    const std::string name;       // kernel ID, defined by its API (signature)
+    const M           outMeta;    // generic adaptor to API::outMeta(...)
+    const GShapes     outShapes; // types (shapes) kernel's outputs
+};
+
+// GKernelImpl describes particular kernel implementation to the system
+struct GAPI_EXPORTS GKernelImpl
+{
+    util::any         opaque;    // backend-specific opaque info
+};
+
+template<typename, typename> class GKernelTypeM;
+
+namespace detail
+{
+    ////////////////////////////////////////////////////////////////////////////
+    // yield() is used in graph construction time as a generic method to obtain
+    // lazy "return value" of G-API operations
+    //
+    namespace
+    {
+
+        template<typename T> struct Yield;
+        template<> struct Yield<cv::GMat>
+        {
+            static inline cv::GMat yield(cv::GCall &call, int i) { return call.yield(i); }
+        };
+        template<> struct Yield<cv::GScalar>
+        {
+            static inline cv::GScalar yield(cv::GCall &call, int i) { return call.yieldScalar(i); }
+        };
+        template<typename U> struct Yield<cv::GArray<U> >
+        {
+            static inline cv::GArray<U> yield(cv::GCall &call, int i) { return call.yieldArray<U>(i); }
+        };
+    } // anonymous namespace
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Helper classes which brings outputMeta() marshalling to kernel
+    // implementations
+    //
+    // 1. MetaType establishes G#Type -> G#Meta mapping between G-API dynamic
+    //    types and its metadata descriptor types.
+    //    This mapping is used to transform types to call outMeta() callback.
+    template<typename T> struct MetaType;
+    template<> struct MetaType<cv::GMat>    { using type = GMatDesc; };
+    template<> struct MetaType<cv::GScalar> { using type = GScalarDesc; };
+    template<typename U> struct MetaType<cv::GArray<U> > { using type = GArrayDesc; };
+    template<typename T> struct MetaType    { using type = T; }; // opaque args passed as-is
+
+    // 2. Hacky test based on MetaType to check if we operate on G-* type or not
+    template<typename T> using is_nongapi_type = std::is_same<T, typename MetaType<T>::type>;
+
+    // 3. Two ways to transform input arguments to its meta - for G-* and non-G* types:
+    template<typename T>
+    typename std::enable_if<!is_nongapi_type<T>::value, typename MetaType<T>::type>
+    ::type get_in_meta(const GMetaArgs &in_meta, const GArgs &, int idx)
+    {
+        return util::get<typename MetaType<T>::type>(in_meta.at(idx));
+    }
+
+    template<typename T>
+    typename std::enable_if<is_nongapi_type<T>::value, T>
+    ::type get_in_meta(const GMetaArgs &, const GArgs &in_args, int idx)
+    {
+        return in_args.at(idx).template get<T>();
+    }
+
+    // 4. The MetaHelper itself: an entity which generates outMeta() call
+    //    based on kernel signature, with arguments properly substituted.
+    // 4.1 - case for multiple return values
+    // FIXME: probably can be simplified with std::apply or analogue.
+    template<typename, typename, typename>
+    struct MetaHelper;
+
+    template<typename K, typename... Ins, typename... Outs>
+    struct MetaHelper<K, std::tuple<Ins...>, std::tuple<Outs...> >
+    {
+        template<int... IIs, int... OIs>
+        static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
+                                         const GArgs &in_args,
+                                         detail::Seq<IIs...>,
+                                         detail::Seq<OIs...>)
+        {
+            // FIXME: decay?
+            using R   = std::tuple<typename MetaType<Outs>::type...>;
+            const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
+            return GMetaArgs{ GMetaArg(std::get<OIs>(r))... };
+        }
+        // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
+
+        static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
+                                    const GArgs &in_args)
+        {
+            return getOutMeta_impl(in_meta,
+                                   in_args,
+                                   typename detail::MkSeq<sizeof...(Ins)>::type(),
+                                   typename detail::MkSeq<sizeof...(Outs)>::type());
+        }
+    };
+
+    // 4.1 - case for a single return value
+    // FIXME: How to avoid duplication here?
+    template<typename K, typename... Ins, typename Out>
+    struct MetaHelper<K, std::tuple<Ins...>, Out >
+    {
+        template<int... IIs>
+        static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
+                                         const GArgs &in_args,
+                                         detail::Seq<IIs...>)
+        {
+            // FIXME: decay?
+            using R = typename MetaType<Out>::type;
+            const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
+            return GMetaArgs{ GMetaArg(r) };
+        }
+        // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
+
+        static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
+                                    const GArgs &in_args)
+        {
+            return getOutMeta_impl(in_meta,
+                                   in_args,
+                                   typename detail::MkSeq<sizeof...(Ins)>::type());
+        }
+    };
+
+} // namespace detail
+
+// GKernelType and GKernelTypeM are base classes which implement typed ::on()
+// method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels
+//
+// G_TYPED_KERNEL and G_TYPED_KERNEK_M macros inherit user classes from GKernelType and
+// GKernelTypeM respectively.
+
+template<typename K, typename... R, typename... Args>
+class GKernelTypeM<K, std::function<std::tuple<R...>(Args...)> >:
+        public detail::MetaHelper<K, std::tuple<Args...>, std::tuple<R...> >
+{
+    template<int... IIs>
+    static std::tuple<R...> yield(cv::GCall &call, detail::Seq<IIs...>)
+    {
+        return std::make_tuple(detail::Yield<R>::yield(call, IIs)...);
+    }
+
+public:
+    using InArgs  = std::tuple<Args...>;
+    using OutArgs = std::tuple<R...>;
+
+    static std::tuple<R...> on(Args... args)
+    {
+        cv::GCall call(GKernel{K::id(), &K::getOutMeta, {detail::GTypeTraits<R>::shape...}});
+        call.pass(args...);
+        return yield(call, typename detail::MkSeq<sizeof...(R)>::type());
+    }
+};
+
+template<typename, typename> class GKernelType;
+
+template<typename K, typename R, typename... Args>
+class GKernelType<K, std::function<R(Args...)> >:
+        public detail::MetaHelper<K, std::tuple<Args...>, R >
+{
+public:
+    using InArgs  = std::tuple<Args...>;
+    using OutArgs = std::tuple<R>;
+
+    static R on(Args... args)
+    {
+        cv::GCall call(GKernel{K::id(), &K::getOutMeta, {detail::GTypeTraits<R>::shape}});
+        call.pass(args...);
+        return detail::Yield<R>::yield(call, 0);
+    }
+};
+
+} // namespace cv
+
+
+// FIXME: I don't know a better way so far. Feel free to suggest one
+// The problem is that every typed kernel should have ::id() but body
+// of the class is defined by user (with outMeta, other stuff)
+
+#define G_ID_HELPER_CLASS(Class)  Class##IdHelper
+
+#define G_ID_HELPER_BODY(Class, Id)                                         \
+    namespace detail                                                        \
+    {                                                                       \
+        struct G_ID_HELPER_CLASS(Class)                                     \
+        {                                                                   \
+            static constexpr const char * id() {return Id;};                \
+        };                                                                  \
+    }
+
+#define G_TYPED_KERNEL(Class, API, Id)                                      \
+    G_ID_HELPER_BODY(Class, Id)                                             \
+    struct Class final: public cv::GKernelType<Class, std::function API >,  \
+                        public detail::G_ID_HELPER_CLASS(Class)
+// {body} is to be defined by user
+
+#define G_TYPED_KERNEL_M(Class, API, Id)                                    \
+    G_ID_HELPER_BODY(Class, Id)                                             \
+    struct Class final: public cv::GKernelTypeM<Class, std::function API >, \
+                        public detail::G_ID_HELPER_CLASS(Class)             \
+// {body} is to be defined by user
+
+namespace cv
+{
+// Declare <unite> in cv:: namespace
+enum class unite_policy
+{
+    REPLACE,
+    KEEP
+};
+
+namespace gapi
+{
+    // Prework: model "Device" API before it gets to G-API headers.
+    // FIXME: Don't mix with internal Backends class!
+    class GAPI_EXPORTS GBackend
+    {
+    public:
+        class Priv;
+
+        // TODO: make it template (call `new` within??)
+        GBackend();
+        explicit GBackend(std::shared_ptr<Priv> &&p);
+
+        Priv& priv();
+        const Priv& priv() const;
+        std::size_t hash() const;
+
+        bool operator== (const GBackend &rhs) const;
+
+    private:
+        std::shared_ptr<Priv> m_priv;
+    };
+
+    inline bool operator != (const GBackend &lhs, const GBackend &rhs)
+    {
+        return !(lhs == rhs);
+    }
+} // namespace gapi
+} // namespace cv
+
+namespace std
+{
+    template<> struct hash<cv::gapi::GBackend>
+    {
+        std::size_t operator() (const cv::gapi::GBackend &b) const
+        {
+            return b.hash();
+        }
+    };
+} // namespace std
+
+
+namespace cv {
+namespace gapi {
+    // Lookup order is in fact a vector of Backends to traverse during look-up
+    using GLookupOrder = std::vector<GBackend>;
+    inline GLookupOrder lookup_order(std::initializer_list<GBackend> &&list)
+    {
+        return GLookupOrder(std::move(list));
+    }
+
+    // FIXME: Hide implementation
+    class GAPI_EXPORTS GKernelPackage
+    {
+        using S = std::unordered_map<std::string, GKernelImpl>;
+        using M = std::unordered_map<GBackend, S>;
+        M m_backend_kernels;
+
+    protected:
+        // Check if package contains ANY implementation of a kernel API
+        // by API textual id.
+        bool includesAPI(const std::string &id) const;
+
+    public:
+        // Return total number of kernels (accross all backends)
+        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.
+        template<typename KImpl>
+        bool includes() const
+        {
+            const auto set_iter = m_backend_kernels.find(KImpl::backend());
+            return (set_iter != m_backend_kernels.end())
+                ? (set_iter->second.count(KImpl::API::id()) > 0)
+                : false;
+        }
+
+        // Removes all the kernels related to the given backend
+        void remove(const GBackend& backend);
+
+        // Check if package contains ANY implementation of a kernel API
+        // by API type.
+        template<typename KAPI>
+        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.
+        template<typename KAPI>
+        GBackend lookup(const GLookupOrder &order = {}) const
+        {
+            return lookup(KAPI::id(), order).first;
+        }
+
+        std::pair<cv::gapi::GBackend, cv::GKernelImpl>
+        lookup(const std::string &id, const GLookupOrder &order = {}) const;
+
+        // Put a new kernel implementation into package
+        // FIXME: No overwrites allowed?
+        template<typename KImpl> void include()
+        {
+            auto backend     = KImpl::backend();
+            auto kernel_id   = KImpl::API::id();
+            auto kernel_impl = GKernelImpl{KImpl::kernel()};
+            m_backend_kernels[backend][kernel_id] = std::move(kernel_impl);
+        }
+
+        // Lists all backends which are included into package
+        std::vector<GBackend> backends() const;
+
+        friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage  &,
+                                                 const GKernelPackage  &,
+                                                 const cv::unite_policy);
+    };
+
+    template<typename... KK> GKernelPackage kernels()
+    {
+        GKernelPackage pkg;
+
+        // For those who wonder - below is a trick to call a number of
+        // methods based on parameter pack (zeroes just help hiding these
+        // calls into a sequence which helps to expand this parameter pack).
+        // Just note that `f(),a` always equals to `a` (with f() called!)
+        // and parentheses are used to hide function call in the expanded sequence.
+        // Leading 0 helps to handle case when KK is an empty list (kernels<>()).
+
+        int unused[] = { 0, (pkg.include<KK>(), 0)... };
+        cv::util::suppress_unused_warning(unused);
+        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);
+} // namespace gapi
+
+namespace detail
+{
+    template<> struct CompileArgTag<cv::gapi::GKernelPackage>
+    {
+        static const char* tag() { return "gapi.kernel_package"; }
+    };
+    template<> struct CompileArgTag<cv::gapi::GLookupOrder>
+    {
+        static const char* tag() { return "gapi.lookup_order"; }
+    };
+} // namespace detail
+} // namespace cv
+
+#endif // OPENCV_GAPI_GKERNEL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp
new file mode 100644 (file)
index 0000000..cfe4e51
--- /dev/null
@@ -0,0 +1,131 @@
+// 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_GMAT_HPP
+#define OPENCV_GAPI_GMAT_HPP
+
+#include <ostream>
+#include <memory>                 // std::shared_ptr
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/gcommon.hpp> // GShape
+
+#include "opencv2/gapi/own/types.hpp" // cv::gapi::own::Size
+#include "opencv2/gapi/own/convert.hpp" // to_own
+#include "opencv2/gapi/own/assert.hpp"
+
+// TODO GAPI_EXPORTS or so
+namespace cv
+{
+// Forward declaration; GNode and GOrigin are an internal
+// (user-inaccessible) classes.
+class GNode;
+struct GOrigin;
+
+class GAPI_EXPORTS GMat
+{
+public:
+    GMat();                                 // Empty constructor
+    GMat(const GNode &n, std::size_t out);  // Operation result constructor
+
+    GOrigin& priv();                        // Internal use only
+    const GOrigin& priv()  const;           // Internal use only
+
+private:
+    std::shared_ptr<GOrigin> m_priv;
+};
+
+struct GAPI_EXPORTS GMatDesc
+{
+    // FIXME: Default initializers in C++14
+    int depth;
+    int chan;
+    cv::gapi::own::Size size; // NB.: no multi-dimensional cases covered yet
+
+    inline bool operator== (const GMatDesc &rhs) const
+    {
+        return depth == rhs.depth && chan == rhs.chan && size == rhs.size;
+    }
+
+    inline bool operator!= (const GMatDesc &rhs) const
+    {
+        return !(*this == rhs);
+    }
+
+    // Meta combinator: return a new GMatDesc which differs in size by delta
+    // (all other fields are taken unchanged from this GMatDesc)
+    // FIXME: a better name?
+    GMatDesc withSizeDelta(cv::gapi::own::Size delta) const
+    {
+        GMatDesc desc(*this);
+        desc.size += delta;
+        return desc;
+    }
+
+    GMatDesc withSizeDelta(cv::Size delta) const
+    {
+        return withSizeDelta(to_own(delta));
+    }
+
+    // Meta combinator: return a new GMatDesc which differs in size by delta
+    // (all other fields are taken unchanged from this GMatDesc)
+    //
+    // This is an overload.
+    GMatDesc withSizeDelta(int dx, int dy) const
+    {
+        return withSizeDelta(cv::gapi::own::Size{dx,dy});
+    }
+
+    GMatDesc withSize(cv::gapi::own::Size sz) const
+    {
+        GMatDesc desc(*this);
+        desc.size = sz;
+        return desc;
+    }
+
+    GMatDesc withSize(cv::Size sz) const
+    {
+        return withSize(to_own(sz));
+    }
+
+    // Meta combinator: return a new GMatDesc with specified data depth.
+    // (all other fields are taken unchanged from this GMatDesc)
+    GMatDesc withDepth(int ddepth) const
+    {
+        GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1);
+        GMatDesc desc(*this);
+        if (ddepth != -1) desc.depth = ddepth;
+        return desc;
+    }
+
+    // Meta combinator: return a new GMatDesc with specified data depth
+    // and number of channels.
+    // (all other fields are taken unchanged from this GMatDesc)
+    GMatDesc withType(int ddepth, int dchan) const
+    {
+        GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1);
+        GMatDesc desc = withDepth(ddepth);
+        desc.chan = dchan;
+        return desc;
+    }
+};
+
+static inline GMatDesc empty_gmat_desc() { return GMatDesc{-1,-1,{-1,-1}}; }
+
+class Mat;
+GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat);
+
+namespace gapi { namespace own {
+    class Mat;
+    CV_EXPORTS GMatDesc descr_of(const Mat &mat);
+}}//gapi::own
+
+std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc);
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GMAT_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gmetaarg.hpp b/modules/gapi/include/opencv2/gapi/gmetaarg.hpp
new file mode 100644 (file)
index 0000000..473be34
--- /dev/null
@@ -0,0 +1,66 @@
+// 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_GMETAARG_HPP
+#define OPENCV_GAPI_GMETAARG_HPP
+
+#include <vector>
+#include <type_traits>
+
+#include "opencv2/gapi/util/util.hpp"
+#include "opencv2/gapi/util/variant.hpp"
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/garray.hpp"
+
+namespace cv
+{
+// FIXME: Rename to GMeta?
+// FIXME: user shouldn't deal with it - put to detail?
+// GMetaArg is an union type over descriptions of G-types which can serve as
+// GComputation's in/output slots.
+//
+// GMetaArg objects are passed as arguments to GComputation::compile()
+// to specify which data a compiled computation should be specialized on.
+// For manual compile(), user must supply this metadata, in case of apply()
+// this metadata is taken from arguments computation should operate on.
+//
+// The first type (monostate) is equal to "uninitialized"/"unresolved" meta.
+using GMetaArg = util::variant
+    < util::monostate
+    , GMatDesc
+    , GScalarDesc
+    , GArrayDesc
+    >;
+std::ostream& operator<<(std::ostream& os, const GMetaArg &);
+
+using GMetaArgs = std::vector<GMetaArg>;
+
+namespace detail
+{
+    // These traits are used by GComputation::compile()
+
+    // FIXME: is_constructible<T> doesn't work as variant doesn't do any SFINAE
+    // in its current template constructor
+
+    template<typename T> struct is_meta_descr    : std::false_type {};
+    template<> struct is_meta_descr<GMatDesc>    : std::true_type {};
+    template<> struct is_meta_descr<GScalarDesc> : std::true_type {};
+    template<> struct is_meta_descr<GArrayDesc>  : std::true_type {};
+
+    template<typename... Ts>
+    using are_meta_descrs = all_satisfy<is_meta_descr, Ts...>;
+
+    template<typename... Ts>
+    using are_meta_descrs_but_last = all_satisfy<is_meta_descr, typename all_but_last<Ts...>::type>;
+
+} // namespace detail
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GMETAARG_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gproto.hpp b/modules/gapi/include/opencv2/gapi/gproto.hpp
new file mode 100644 (file)
index 0000000..8b53d9b
--- /dev/null
@@ -0,0 +1,96 @@
+// 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_GPROTO_HPP
+#define OPENCV_GAPI_GPROTO_HPP
+
+#include <type_traits>
+#include <vector>
+#include <ostream>
+
+#include "opencv2/gapi/util/variant.hpp"
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/garray.hpp"
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gmetaarg.hpp"
+
+namespace cv {
+
+// FIXME: user shouldn't deal with it - put to detail?
+// GProtoArg is an union type over G-types which can serve as
+// GComputation's in/output slots. In other words, GProtoArg
+// wraps any type which can serve as G-API exchange type.
+//
+// In Runtime, GProtoArgs are substituted with appropriate GRunArgs.
+//
+// GProtoArg objects are constructed in-place when user describes
+// (captures) computations, user doesn't interact with these types
+// directly.
+using GProtoArg = util::variant
+    < GMat
+    , GScalar
+    , detail::GArrayU // instead of GArray<T>
+    >;
+
+using GProtoArgs = std::vector<GProtoArg>;
+
+namespace detail
+{
+template<typename... Ts> inline GProtoArgs packArgs(Ts... args)
+{
+    return GProtoArgs{ GProtoArg(wrap_gapi_helper<Ts>::wrap(args))... };
+}
+
+}
+
+template<class Tag>
+struct GIOProtoArgs
+{
+public:
+    explicit GIOProtoArgs(const GProtoArgs& args) : m_args(args) {}
+    explicit GIOProtoArgs(GProtoArgs &&args)      : m_args(std::move(args)) {}
+
+    GProtoArgs m_args;
+};
+
+struct In_Tag{};
+struct Out_Tag{};
+
+using GProtoInputArgs  = GIOProtoArgs<In_Tag>;
+using GProtoOutputArgs = GIOProtoArgs<Out_Tag>;
+
+// Perfect forwarding
+template<typename... Ts> inline GProtoInputArgs GIn(Ts&&... ts)
+{
+    return GProtoInputArgs(detail::packArgs(std::forward<Ts>(ts)...));
+}
+
+template<typename... Ts> inline GProtoOutputArgs GOut(Ts&&... ts)
+{
+    return GProtoOutputArgs(detail::packArgs(std::forward<Ts>(ts)...));
+}
+
+// Extract run-time arguments from node origin
+// Can be used to extract constant values associated with G-objects
+// (like GScalar) at graph construction time
+GRunArg value_of(const GOrigin &origin);
+
+// Transform run-time computation arguments into a collection of metadata
+// extracted from that arguments
+GMetaArg  GAPI_EXPORTS descr_of(const GRunArg  &arg );
+GMetaArgs GAPI_EXPORTS descr_of(const GRunArgs &args);
+
+// Transform run-time operation result argument into metadata extracted from that argument
+// Used to compare the metadata, which generated at compile time with the metadata result operation in run time
+GMetaArg  GAPI_EXPORTS descr_of(const GRunArgP& argp);
+
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GPROTO_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gscalar.hpp b/modules/gapi/include/opencv2/gapi/gscalar.hpp
new file mode 100644 (file)
index 0000000..030b2e1
--- /dev/null
@@ -0,0 +1,68 @@
+// 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_GSCALAR_HPP
+#define OPENCV_GAPI_GSCALAR_HPP
+
+#include <ostream>
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/gcommon.hpp> // GShape
+#include <opencv2/gapi/util/optional.hpp>
+#include "opencv2/gapi/own/scalar.hpp"
+#include <opencv2/core/types.hpp>
+
+// TODO GAPI_EXPORTS or so
+namespace cv
+{
+// Forward declaration; GNode and GOrigin are an internal
+// (user-inaccessible) classes.
+class GNode;
+struct GOrigin;
+
+class GAPI_EXPORTS GScalar
+{
+public:
+    GScalar();                                         // Empty constructor
+    explicit GScalar(const cv::gapi::own::Scalar& s);  // Constant value constructor from cv::gapi::own::Scalar
+    explicit GScalar(cv::gapi::own::Scalar&& s);       // Constant value move-constructor from cv::gapi::own::Scalar
+    explicit GScalar(const cv::Scalar& s);             // Constant value constructor from cv::Scalar
+    GScalar(double v0);                                // Constant value constructor from double
+    GScalar(const GNode &n, std::size_t out);          // Operation result constructor
+
+    GOrigin& priv();                                   // Internal use only
+    const GOrigin& priv()  const;                      // Internal use only
+
+private:
+    std::shared_ptr<GOrigin> m_priv;
+};
+
+struct GScalarDesc
+{
+    // NB.: right now it is empty
+
+    inline bool operator== (const GScalarDesc &) const
+    {
+        return true; // NB: implement this method if GScalar meta appears
+    }
+
+    inline bool operator!= (const GScalarDesc &rhs) const
+    {
+        return !(*this == rhs);
+    }
+};
+
+static inline GScalarDesc empty_scalar_desc() { return GScalarDesc(); }
+
+GAPI_EXPORTS GScalarDesc descr_of(const cv::gapi::own::Scalar &scalar);
+GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar            &scalar);
+
+std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &desc);
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GSCALAR_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp
new file mode 100644 (file)
index 0000000..fcedefc
--- /dev/null
@@ -0,0 +1,150 @@
+// 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_GTYPE_TRAITS_HPP
+#define OPENCV_GAPI_GTYPE_TRAITS_HPP
+
+#include <vector>
+#include <type_traits>
+
+#include <opencv2/gapi/gmat.hpp>
+#include <opencv2/gapi/gscalar.hpp>
+#include <opencv2/gapi/garray.hpp>
+#include <opencv2/gapi/gcommon.hpp>
+#include <opencv2/gapi/own/convert.hpp>
+
+namespace cv
+{
+namespace detail
+{
+    // FIXME: These traits and enum and possible numerous switch(kind)
+    // block may be replaced with a special Handler<T> object or with
+    // a double dispatch
+    enum class ArgKind: int
+    {
+        OPAQUE,       // Unknown, generic, opaque-to-GAPI data type - STATIC
+        GOBJREF,      // <internal> reference to object
+        GMAT,         // a cv::GMat
+        GSCALAR,      // a cv::GScalar
+        GARRAY,       // a cv::GArrayU (note - exactly GArrayU, not GArray<T>!)
+    };
+
+    // Describe G-API types (G-types) with traits.  Mostly used by
+    // cv::GArg to store meta information about types passed into
+    // operation arguments. Please note that cv::GComputation is
+    // defined on GProtoArgs, not GArgs!
+    template<typename T> struct GTypeTraits;
+    template<typename T> struct GTypeTraits
+    {
+        static constexpr const ArgKind kind = ArgKind::OPAQUE;
+    };
+    template<>           struct GTypeTraits<cv::GMat>
+    {
+        static constexpr const ArgKind kind = ArgKind::GMAT;
+        static constexpr const GShape shape = GShape::GMAT;
+    };
+    template<>           struct GTypeTraits<cv::GScalar>
+    {
+        static constexpr const ArgKind kind = ArgKind::GSCALAR;
+        static constexpr const GShape shape = GShape::GSCALAR;
+    };
+    template<class T> struct GTypeTraits<cv::GArray<T> >
+    {
+        static constexpr const ArgKind kind = ArgKind::GARRAY;
+        static constexpr const GShape shape = GShape::GARRAY;
+        using host_type  = std::vector<T>;
+        using strip_type = cv::detail::VectorRef;
+        static cv::detail::GArrayU   wrap_value(const cv::GArray<T>  &t) { return t.strip();}
+        static cv::detail::VectorRef wrap_in   (const std::vector<T> &t) { return detail::VectorRef(t); }
+        static cv::detail::VectorRef wrap_out  (      std::vector<T> &t) { return detail::VectorRef(t); }
+    };
+
+    // Tests if Trait for type T requires extra marshalling ("custom wrap") or not.
+    // If Traits<T> has wrap_value() defined, it does.
+    template<class T> struct has_custom_wrap
+    {
+        template<class,class> class check;
+        template<typename C> static std::true_type  test(check<C, decltype(&GTypeTraits<C>::wrap_value)> *);
+        template<typename C> static std::false_type test(...);
+        using type = decltype(test<T>(nullptr));
+        static const constexpr bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
+    };
+
+    // Resolve a Host type back to its associated G-Type.
+    // FIXME: Probably it can be avoided
+    template<typename T> struct GTypeOf;
+    template<>           struct GTypeOf<cv::Mat>               { using type = cv::GMat;      };
+    template<>           struct GTypeOf<cv::gapi::own::Mat>    { using type = cv::GMat;      };
+    template<>           struct GTypeOf<cv::Scalar>            { using type = cv::GScalar;   };
+    template<>           struct GTypeOf<cv::gapi::own::Scalar> { using type = cv::GScalar;   };
+    template<typename U> struct GTypeOf<std::vector<U> >       { using type = cv::GArray<U>; };
+    template<class T> using g_type_of_t = typename GTypeOf<T>::type;
+
+    // Marshalling helper for G-types and its Host types. Helps G-API
+    // to store G types in internal generic containers for further
+    // processing. Implements the following callbacks:
+    //
+    // * wrap() - converts user-facing G-type into an internal one
+    //   for internal storage.
+    //   Used when G-API operation is instantiated (G<Kernel>::on(),
+    //   etc) during expressing a pipeline. Mostly returns input
+    //   value "as is" except the case when G-type is a template. For
+    //   template G-classes, calls custom wrap() from Traits.
+    //   The value returned by wrap() is then wrapped into GArg() and
+    //   stored in G-API metadata.
+    //
+    //   Example:
+    //   - cv::GMat arguments are passed as-is.
+    //   - integers, pointers, STL containers, user types are passed as-is.
+    //   - cv::GArray<T> is converted to cv::GArrayU.
+    //
+    // * wrap_in() / wrap_out() - convert Host type associated with
+    //   G-type to internal representation type.
+    //
+    //   - For "simple" (non-template) G-types, returns value as-is.
+    //     Example: cv::GMat has host type cv::Mat, when user passes a
+    //              cv::Mat, system stores it internally as cv::Mat.
+    //
+    //   - For "complex" (template) G-types, utilizes custom
+    //     wrap_in()/wrap_out() as described in Traits.
+    //     Example: cv::GArray<T> has host type std::vector<T>, when
+    //              user passes a std::vector<T>, system stores it
+    //              internally as VectorRef (with <T> stripped away).
+    template<typename T, class Custom = void> struct WrapValue
+    {
+        static auto wrap(const T& t) ->
+            typename std::remove_reference<T>::type
+        {
+            return static_cast<typename std::remove_reference<T>::type>(t);
+        }
+
+        template<typename U> static U  wrap_in (const U &u) { return  u;  }
+        template<typename U> static U* wrap_out(U &u)       { return &u;  }
+    };
+    template<typename T> struct WrapValue<T, typename std::enable_if<has_custom_wrap<T>::value>::type>
+    {
+        static auto wrap(const T& t) -> decltype(GTypeTraits<T>::wrap_value(t))
+        {
+            return GTypeTraits<T>::wrap_value(t);
+        }
+        template<typename U> static auto wrap_in (const U &u) -> typename GTypeTraits<T>::strip_type
+        {
+            return GTypeTraits<T>::wrap_in(u);
+        }
+        template<typename U> static auto wrap_out(U &u) -> typename GTypeTraits<T>::strip_type
+        {
+            return GTypeTraits<T>::wrap_out(u);
+        }
+    };
+
+    template<typename T> using wrap_gapi_helper = WrapValue<typename std::decay<T>::type>;
+    template<typename T> using wrap_host_helper = WrapValue<typename std::decay<g_type_of_t<T> >::type>;
+
+} // namespace detail
+} // namespace cv
+
+#endif // OPENCV_GAPI_GTYPE_TRAITS_HPP
diff --git a/modules/gapi/include/opencv2/gapi/gtyped.hpp b/modules/gapi/include/opencv2/gapi/gtyped.hpp
new file mode 100644 (file)
index 0000000..d691aee
--- /dev/null
@@ -0,0 +1,186 @@
+// 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_GTYPED_HPP
+#define OPENCV_GAPI_GTYPED_HPP
+
+#include <vector>
+
+#include "opencv2/gapi/gcomputation.hpp"
+#include "opencv2/gapi/gcompiled.hpp"
+#include "opencv2/gapi/gproto.hpp"
+#include "opencv2/gapi/gcommon.hpp"
+
+namespace cv {
+
+namespace detail
+{
+    // FIXME: How to prevent coolhackers from extending it by their own types?
+    // FIXME: ...Should we care?
+    template<typename T> struct ProtoToParam;
+    template<> struct ProtoToParam<cv::GMat>    { using type = cv::Mat; };
+    template<> struct ProtoToParam<cv::GScalar> { using type = cv::Scalar; };
+    template<typename U> struct ProtoToParam<cv::GArray<U> > { using type = std::vector<U>; };
+    template<typename T> using ProtoToParamT = typename ProtoToParam<T>::type;
+
+    template<typename T> struct ProtoToMeta;
+    template<> struct ProtoToMeta<cv::GMat>     { using type = cv::GMatDesc; };
+    template<> struct ProtoToMeta<cv::GScalar>  { using type = cv::GScalarDesc; };
+    template<typename U> struct ProtoToMeta<cv::GArray<U> > { using type = cv::GArrayDesc; };
+    template<typename T> using ProtoToMetaT = typename ProtoToMeta<T>::type;
+
+    //workaround for MSVC 19.0 bug
+    template <typename T>
+    auto make_default()->decltype(T{}) {return {};}
+}; // detail
+
+template<typename> class GComputationT;
+
+// Single return value implementation
+template<typename R, typename... Args> class GComputationT<R(Args...)>
+{
+public:
+    typedef std::function<R(Args...)> Gen;
+
+    class GCompiledT
+    {
+    private:
+        friend class GComputationT<R(Args...)>;
+
+        cv::GCompiled m_comp;
+
+        explicit GCompiledT(const cv::GCompiled &comp) : m_comp(comp) {}
+
+    public:
+        GCompiledT() {}
+
+        void operator()(detail::ProtoToParamT<Args>... inArgs,
+                        detail::ProtoToParamT<R> &outArg)
+        {
+            m_comp(cv::gin(inArgs...), cv::gout(outArg));
+        }
+
+        explicit operator bool() const
+        {
+            return static_cast<bool>(m_comp);
+        }
+    };
+
+private:
+    typedef std::pair<R, GProtoInputArgs > Captured;
+
+    Captured capture(const Gen& g, Args... args)
+    {
+        return Captured(g(args...), cv::GIn(args...));
+    }
+
+    Captured m_capture;
+    cv::GComputation m_comp;
+
+public:
+    GComputationT(const Gen &generator)
+        : m_capture(capture(generator, detail::make_default<Args>()...))
+        , m_comp(cv::GProtoInputArgs(std::move(m_capture.second)),
+                 cv::GOut(m_capture.first))
+    {
+    }
+
+    void apply(detail::ProtoToParamT<Args>... inArgs,
+               detail::ProtoToParamT<R> &outArg)
+    {
+        m_comp.apply(cv::gin(inArgs...), cv::gout(outArg));
+    }
+
+    GCompiledT compile(detail::ProtoToMetaT<Args>... inDescs)
+    {
+        GMetaArgs inMetas = { GMetaArg(inDescs)... };
+        return GCompiledT(m_comp.compile(std::move(inMetas), GCompileArgs()));
+    }
+
+    GCompiledT compile(detail::ProtoToMetaT<Args>... inDescs, GCompileArgs &&args)
+    {
+        GMetaArgs inMetas = { GMetaArg(inDescs)... };
+        return GCompiledT(m_comp.compile(std::move(inMetas), std::move(args)));
+    }
+};
+
+// Multiple (fixed) return value implementation. FIXME: How to avoid copy-paste?
+template<typename... R, typename... Args> class GComputationT<std::tuple<R...>(Args...)>
+{
+public:
+    typedef std::function<std::tuple<R...>(Args...)> Gen;
+
+    class GCompiledT
+    {
+    private:
+        friend class GComputationT<std::tuple<R...>(Args...)>;
+
+        cv::GCompiled m_comp;
+        explicit GCompiledT(const cv::GCompiled &comp) : m_comp(comp) {}
+
+    public:
+        GCompiledT() {}
+
+        void operator()(detail::ProtoToParamT<Args>... inArgs,
+                        detail::ProtoToParamT<R>&... outArgs)
+        {
+            m_comp(cv::gin(inArgs...), cv::gout(outArgs...));
+        }
+
+        explicit operator bool() const
+        {
+            return static_cast<bool>(m_comp);
+        }
+    };
+
+private:
+    typedef std::pair<GProtoArgs, GProtoArgs> Captured;
+
+    template<int... IIs>
+    Captured capture(GProtoArgs &&args, const std::tuple<R...> &rr, detail::Seq<IIs...>)
+    {
+        return Captured(cv::GOut(std::get<IIs>(rr)...).m_args, args);
+    }
+
+    Captured capture(const Gen& g, Args... args)
+    {
+        return capture(cv::GIn(args...).m_args, g(args...), typename detail::MkSeq<sizeof...(R)>::type());
+    }
+
+    Captured m_capture;
+    cv::GComputation m_comp;
+
+public:
+    GComputationT(const Gen &generator)
+        : m_capture(capture(generator, detail::make_default<Args>()...))
+        , m_comp(cv::GProtoInputArgs(std::move(m_capture.second)),
+                 cv::GProtoOutputArgs(std::move(m_capture.first)))
+    {
+    }
+
+    void apply(detail::ProtoToParamT<Args>... inArgs,
+               detail::ProtoToParamT<R>&... outArgs)
+    {
+        m_comp.apply(cv::gin(inArgs...), cv::gout(outArgs...));
+    }
+
+    GCompiledT compile(detail::ProtoToMetaT<Args>... inDescs)
+    {
+        GMetaArgs inMetas = { GMetaArg(inDescs)... };
+        return GCompiledT(m_comp.compile(std::move(inMetas), GCompileArgs()));
+    }
+
+    GCompiledT compile(detail::ProtoToMetaT<Args>... inDescs, GCompileArgs &&args)
+    {
+        GMetaArgs inMetas = { GMetaArg(inDescs)... };
+        return GCompiledT(m_comp.compile(std::move(inMetas), std::move(args)));
+    }
+};
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_GTYPED_HPP
diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp
new file mode 100644 (file)
index 0000000..6a3b1d9
--- /dev/null
@@ -0,0 +1,677 @@
+// 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_IMGPROC_HPP
+#define OPENCV_GAPI_IMGPROC_HPP
+
+#include "opencv2/imgproc.hpp"
+
+#include <utility> // std::tuple
+
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+
+
+/** \defgroup gapi_imgproc G-API image processing functionality
+@{
+    @defgroup gapi_filters Graph API: Image filters
+    @defgroup gapi_colorconvert Graph API: Converting image from one color space to another
+@}
+ */
+
+namespace cv { namespace gapi {
+
+namespace imgproc {
+    using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this?
+
+    G_TYPED_KERNEL(GFilter2D, <GMat(GMat,int,Mat,Point,Scalar,int,Scalar)>,"org.opencv.imgproc.filters.filter2D") {
+        static GMatDesc outMeta(GMatDesc in, int ddepth, Mat, Point, Scalar, int, Scalar) {
+            return in.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GSepFilter, <GMat(GMat,int,Mat,Mat,Point,Scalar,int,Scalar)>, "org.opencv.imgproc.filters.sepfilter") {
+        static GMatDesc outMeta(GMatDesc in, int ddepth, Mat, Mat, Point, Scalar, int, Scalar) {
+            return in.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GBoxFilter, <GMat(GMat,int,Size,Point,bool,int,Scalar)>, "org.opencv.imgproc.filters.boxfilter") {
+        static GMatDesc outMeta(GMatDesc in, int ddepth, Size, Point, bool, int, Scalar) {
+            return in.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GBlur, <GMat(GMat,Size,Point,int,Scalar)>,         "org.opencv.imgproc.filters.blur"){
+        static GMatDesc outMeta(GMatDesc in, Size, Point, int, Scalar) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GGaussBlur, <GMat(GMat,Size,double,double,int,Scalar)>, "org.opencv.imgproc.filters.gaussianBlur") {
+        static GMatDesc outMeta(GMatDesc in, Size, double, double, int, Scalar) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GMedianBlur, <GMat(GMat,int)>, "org.opencv.imgproc.filters.medianBlur") {
+        static GMatDesc outMeta(GMatDesc in, int) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GErode, <GMat(GMat,Mat,Point,int,int,Scalar)>, "org.opencv.imgproc.filters.erode") {
+        static GMatDesc outMeta(GMatDesc in, Mat, Point, int, int, Scalar) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GDilate, <GMat(GMat,Mat,Point,int,int,Scalar)>, "org.opencv.imgproc.filters.dilate") {
+        static GMatDesc outMeta(GMatDesc in, Mat, Point, int, int, Scalar) {
+            return in;
+        }
+    };
+
+    G_TYPED_KERNEL(GSobel, <GMat(GMat,int,int,int,int,double,double,int,Scalar)>, "org.opencv.imgproc.filters.sobel") {
+        static GMatDesc outMeta(GMatDesc in, int ddepth, int, int, int, double, double, int, Scalar) {
+            return in.withDepth(ddepth);
+        }
+    };
+
+    G_TYPED_KERNEL(GEqHist, <GMat(GMat)>, "org.opencv.imgproc.equalizeHist"){
+        static GMatDesc outMeta(GMatDesc in) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+
+    G_TYPED_KERNEL(GCanny, <GMat(GMat,double,double,int,bool)>, "org.opencv.imgproc.canny"){
+        static GMatDesc outMeta(GMatDesc in, double, double, int, bool) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+
+    G_TYPED_KERNEL(GRGB2YUV, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.rgb2yuv") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GYUV2RGB, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.yuv2rgb") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GRGB2Lab, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.rgb2lab") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GBGR2LUV, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.bgr2luv") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GLUV2BGR, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.luv2bgr") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GYUV2BGR, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.yuv2bgr") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GBGR2YUV, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.bgr2yuv") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in; // type still remains CV_8UC3;
+        }
+    };
+
+    G_TYPED_KERNEL(GRGB2Gray, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.rgb2gray") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+
+    G_TYPED_KERNEL(GRGB2GrayCustom, <GMat(GMat,float,float,float)>, "org.opencv.imgproc.colorconvert.rgb2graycustom") {
+        static GMatDesc outMeta(GMatDesc in, float, float, float) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+
+    G_TYPED_KERNEL(GBGR2Gray, <GMat(GMat)>, "org.opencv.imgproc.colorconvert.bgr2gray") {
+        static GMatDesc outMeta(GMatDesc in) {
+            return in.withType(CV_8U, 1);
+        }
+    };
+}
+
+
+//! @addtogroup gapi_filters
+//! @{
+/** @brief Applies a separable linear filter to a matrix(image).
+
+The function applies a separable linear filter to the matrix. That is, first, every row of src is
+filtered with the 1D kernel kernelX. Then, every column of the result is filtered with the 1D
+kernel kernelY. The final result is returned.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note In case of floating-point computation, rounding to nearest even is procedeed
+if hardware supports it (if not - to nearest value).
+
+@note Function textual ID is "org.opencv.imgproc.filters.sepfilter"
+@param src Source image.
+@param ddepth desired depth of the destination image (the following combinations of src.depth() and ddepth are supported:
+
+        src.depth() = CV_8U, ddepth = -1/CV_16S/CV_32F/CV_64F
+        src.depth() = CV_16U/CV_16S, ddepth = -1/CV_32F/CV_64F
+        src.depth() = CV_32F, ddepth = -1/CV_32F/CV_64F
+        src.depth() = CV_64F, ddepth = -1/CV_64F
+
+when ddepth=-1, the output image will have the same depth as the source)
+@param kernelX Coefficients for filtering each row.
+@param kernelY Coefficients for filtering each column.
+@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor
+is at the kernel center.
+@param delta Value added to the filtered results before storing them.
+@param borderType Pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa  boxFilter, gaussianBlur, medianBlur
+ */
+GAPI_EXPORTS GMat sepFilter(const GMat& src, int ddepth, const Mat& kernelX, const Mat& kernelY, const Point& anchor /*FIXME: = Point(-1,-1)*/,
+               const Scalar& delta /*FIXME = GScalar(0)*/, int borderType = BORDER_DEFAULT,
+               const Scalar& borderValue = Scalar(0));
+
+/** @brief Convolves an image with the kernel.
+
+The function applies an arbitrary linear filter to an image. When
+the aperture is partially outside the image, the function interpolates outlier pixel values
+according to the specified border mode.
+
+The function does actually compute correlation, not the convolution:
+
+\f[\texttt{dst} (x,y) =  \sum _{ \stackrel{0\leq x' < \texttt{kernel.cols},}{0\leq y' < \texttt{kernel.rows}} }  \texttt{kernel} (x',y')* \texttt{src} (x+x'- \texttt{anchor.x} ,y+y'- \texttt{anchor.y} )\f]
+
+That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip
+the kernel using flip and set the new anchor to `(kernel.cols - anchor.x - 1, kernel.rows -
+anchor.y - 1)`.
+
+Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+Output image must have the same size and number of channels an input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.filter2D"
+
+@param src input image.
+@param ddepth desired depth of the destination image
+@param kernel convolution kernel (or rather a correlation kernel), a single-channel floating point
+matrix; if you want to apply different kernels to different channels, split the image into
+separate color planes using split and process them individually.
+@param anchor anchor of the kernel that indicates the relative position of a filtered point within
+the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor
+is at the kernel center.
+@param delta optional value added to the filtered pixels before storing them in dst.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa  sepFilter
+ */
+GAPI_EXPORTS GMat filter2D(const GMat& src, int ddepth, const Mat& kernel, const Point& anchor = Point(-1,-1), const Scalar& delta = Scalar(0),
+              int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0));
+
+
+/** @brief Blurs an image using the box filter.
+
+The function smooths an image using the kernel:
+
+\f[\texttt{K} =  \alpha \begin{bmatrix} 1 & 1 & 1 &  \cdots & 1 & 1  \\ 1 & 1 & 1 &  \cdots & 1 & 1  \\ \hdotsfor{6} \\ 1 & 1 & 1 &  \cdots & 1 & 1 \end{bmatrix}\f]
+
+where
+
+\f[\alpha = \fork{\frac{1}{\texttt{ksize.width*ksize.height}}}{when \texttt{normalize=true}}{1}{otherwise}\f]
+
+Unnormalized box filter is useful for computing various integral characteristics over each pixel
+neighborhood, such as covariance matrices of image derivatives (used in dense optical flow
+algorithms, and so on). If you need to compute pixel sums over variable-size windows, use cv::integral.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.boxfilter"
+
+@param src Source image.
+@param dtype the output image depth (-1 to set the input image data type).
+@param ksize blurring kernel size.
+@param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor
+is at the kernel center.
+@param normalize flag, specifying whether the kernel is normalized by its area or not.
+@param borderType Pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa  sepFilter, gaussianBlur, medianBlur, integral
+ */
+GAPI_EXPORTS GMat boxFilter(const GMat& src, int dtype, const Size& ksize, const Point& anchor = Point(-1,-1),
+               bool normalize = true, int borderType = BORDER_DEFAULT,
+               const Scalar& borderValue = Scalar(0));
+
+/** @brief Blurs an image using the normalized box filter.
+
+The function smooths an image using the kernel:
+
+\f[\texttt{K} =  \frac{1}{\texttt{ksize.width*ksize.height}} \begin{bmatrix} 1 & 1 & 1 &  \cdots & 1 & 1  \\ 1 & 1 & 1 &  \cdots & 1 & 1  \\ \hdotsfor{6} \\ 1 & 1 & 1 &  \cdots & 1 & 1  \\ \end{bmatrix}\f]
+
+The call `blur(src, dst, ksize, anchor, borderType)` is equivalent to `boxFilter(src, dst, src.type(),
+anchor, true, borderType)`.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.blur"
+
+@param src Source image.
+@param ksize blurring kernel size.
+@param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel
+center.
+@param borderType border mode used to extrapolate pixels outside of the image, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa  boxFilter, bilateralFilter, GaussianBlur, medianBlur
+ */
+GAPI_EXPORTS GMat blur(const GMat& src, const Size& ksize, const Point& anchor = Point(-1,-1),
+                          int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0));
+
+
+//GAPI_EXPORTS_W void blur( InputArray src, OutputArray dst,
+ //                       Size ksize, Point anchor = Point(-1,-1),
+ //                       int borderType = BORDER_DEFAULT );
+
+
+/** @brief Blurs an image using a Gaussian filter.
+
+The function filter2Ds the source image with the specified Gaussian kernel.
+Output image must have the same type and number of channels an input image.
+
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.gaussianBlur"
+
+@param src input image;
+@param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be
+positive and odd. Or, they can be zero's and then they are computed from sigma.
+@param sigmaX Gaussian kernel standard deviation in X direction.
+@param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be
+equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height,
+respectively (see cv::getGaussianKernel for details); to fully control the result regardless of
+possible future modifications of all this semantics, it is recommended to specify all of ksize,
+sigmaX, and sigmaY.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa  sepFilter, boxFilter, medianBlur
+ */
+GAPI_EXPORTS GMat gaussianBlur(const GMat& src, const Size& ksize, double sigmaX, double sigmaY = 0,
+                  int borderType = BORDER_DEFAULT, const Scalar& borderValue = Scalar(0));
+
+/** @brief Blurs an image using the median filter.
+
+The function smoothes an image using the median filter with the \f$\texttt{ksize} \times
+\texttt{ksize}\f$ aperture. Each channel of a multi-channel image is processed independently.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+The median filter uses cv::BORDER_REPLICATE internally to cope with border pixels, see cv::BorderTypes
+
+@note Function textual ID is "org.opencv.imgproc.filters.medianBlur"
+
+@param src input matrix (image)
+@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ...
+@sa  boxFilter, gaussianBlur
+ */
+GAPI_EXPORTS GMat medianBlur(const GMat& src, int ksize);
+
+/** @brief Erodes an image by using a specific structuring element.
+
+The function erodes the source image using the specified structuring element that determines the
+shape of a pixel neighborhood over which the minimum is taken:
+
+\f[\texttt{dst} (x,y) =  \min _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
+
+Erosion can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently.
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.erode"
+
+@param src input image
+@param kernel structuring element used for erosion; if `element=Mat()`, a `3 x 3` rectangular
+structuring element is used. Kernel can be created using getStructuringElement.
+@param anchor position of the anchor within the element; default value (-1, -1) means that the
+anchor is at the element center.
+@param iterations number of times erosion is applied.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of a constant border
+@sa  dilate
+ */
+GAPI_EXPORTS GMat erode(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1,
+           int borderType = BORDER_CONSTANT,
+           const  Scalar& borderValue = morphologyDefaultBorderValue());
+
+/** @brief Erodes an image by using 3 by 3 rectangular structuring element.
+
+The function erodes the source image using the rectangular structuring element with rectangle center as an anchor.
+Erosion can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently.
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@param src input image
+@param iterations number of times erosion is applied.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of a constant border
+@sa  erode, dilate3x3
+ */
+GAPI_EXPORTS GMat erode3x3(const GMat& src, int iterations = 1,
+           int borderType = BORDER_CONSTANT,
+           const  Scalar& borderValue = morphologyDefaultBorderValue());
+
+/** @brief Dilates an image by using a specific structuring element.
+
+The function dilates the source image using the specified structuring element that determines the
+shape of a pixel neighborhood over which the maximum is taken:
+\f[\texttt{dst} (x,y) =  \max _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
+
+Dilation can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently.
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.dilate"
+
+@param src input image.
+@param kernel structuring element used for dilation; if elemenat=Mat(), a 3 x 3 rectangular
+structuring element is used. Kernel can be created using getStructuringElement
+@param anchor position of the anchor within the element; default value (-1, -1) means that the
+anchor is at the element center.
+@param iterations number of times dilation is applied.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of a constant border
+@sa  erode, morphologyEx, getStructuringElement
+ */
+GAPI_EXPORTS GMat dilate(const GMat& src, const Mat& kernel, const Point& anchor = Point(-1,-1), int iterations = 1,
+           int borderType = BORDER_CONSTANT,
+           const  Scalar& borderValue = morphologyDefaultBorderValue());
+
+/** @brief Dilates an image by using 3 by 3 rectangular structuring element.
+
+The function dilates the source image using the specified structuring element that determines the
+shape of a pixel neighborhood over which the maximum is taken:
+\f[\texttt{dst} (x,y) =  \max _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
+
+Dilation can be applied several (iterations) times. In case of multi-channel images, each channel is processed independently.
+Supported input matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref CV_16SC1, and @ref CV_32FC1.
+Output image must have the same type, size, and number of channels as the input image.
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.dilate"
+
+@param src input image.
+@param iterations number of times dilation is applied.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of a constant border
+@sa  dilate, erode3x3
+ */
+
+GAPI_EXPORTS GMat dilate3x3(const GMat& src, int iterations = 1,
+           int borderType = BORDER_CONSTANT,
+           const  Scalar& borderValue = morphologyDefaultBorderValue());
+
+/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator.
+
+In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to
+calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$
+kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first
+or the second x- or y- derivatives.
+
+There is also the special value `ksize = CV_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr
+filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is
+
+\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f]
+
+for the x-derivative, or transposed for the y-derivative.
+
+The function calculates an image derivative by convolving the image with the appropriate kernel:
+
+\f[\texttt{dst} =  \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f]
+
+The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less
+resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3)
+or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first
+case corresponds to a kernel of:
+
+\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f]
+
+The second case corresponds to a kernel of:
+
+\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f]
+
+@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
+
+@note Function textual ID is "org.opencv.imgproc.filters.sobel"
+
+@param src input image.
+@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of
+    8-bit input images it will result in truncated derivatives.
+@param dx order of the derivative x.
+@param dy order of the derivative y.
+@param ksize size of the extended Sobel kernel; it must be odd.
+@param scale optional scale factor for the computed derivative values; by default, no scaling is
+applied (see cv::getDerivKernels for details).
+@param delta optional delta value that is added to the results prior to storing them in dst.
+@param borderType pixel extrapolation method, see cv::BorderTypes
+@param borderValue border value in case of constant border type
+@sa filter2D, gaussianBlur, cartToPolar
+ */
+GAPI_EXPORTS GMat sobel(const GMat& src, int ddepth, int dx, int dy, int ksize = 3,
+                      double scale = 1, double delta = 0,
+                      int borderType = BORDER_DEFAULT,
+                      const Scalar& borderValue = Scalar(0));
+
+/** @brief Finds edges in an image using the Canny algorithm.
+
+The function finds edges in the input image and marks them in the output map edges using the
+Canny algorithm. The smallest value between threshold1 and threshold2 is used for edge linking. The
+largest value is used to find initial segments of strong edges. See
+<http://en.wikipedia.org/wiki/Canny_edge_detector>
+
+@note Function textual ID is "org.opencv.imgproc.filters.canny"
+
+@param image 8-bit input image.
+@param threshold1 first threshold for the hysteresis procedure.
+@param threshold2 second threshold for the hysteresis procedure.
+@param apertureSize aperture size for the Sobel operator.
+@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm
+\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude (
+L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough (
+L2gradient=false ).
+ */
+GAPI_EXPORTS GMat Canny(const GMat& image, double threshold1, double threshold2,
+                         int apertureSize = 3, bool L2gradient = false);
+
+/** @brief Equalizes the histogram of a grayscale image.
+
+The function equalizes the histogram of the input image using the following algorithm:
+
+- Calculate the histogram \f$H\f$ for src .
+- Normalize the histogram so that the sum of histogram bins is 255.
+- Compute the integral of the histogram:
+\f[H'_i =  \sum _{0  \le j < i} H(j)\f]
+- Transform the image using \f$H'\f$ as a look-up table: \f$\texttt{dst}(x,y) = H'(\texttt{src}(x,y))\f$
+
+The algorithm normalizes the brightness and increases the contrast of the image.
+@note The returned image is of the same size and type as input.
+
+@note Function textual ID is "org.opencv.imgproc.equalizeHist"
+
+@param src Source 8-bit single channel image.
+ */
+GAPI_EXPORTS GMat equalizeHist(const GMat& src);
+
+//! @} gapi_filters
+
+//! @addtogroup gapi_colorconvert
+//! @{
+/** @brief Converts an image from RGB color space to gray-scaled.
+The conventional ranges for R, G, and B channel values are 0 to 255.
+Resulting gray color value computed as
+\f[\texttt{dst} (I)= \texttt{0.299} * \texttt{src}(I).R + \texttt{0.587} * \texttt{src}(I).G  + \texttt{0.114} * \texttt{src}(I).B \f]
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2gray"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1.
+@sa RGB2YUV
+ */
+GAPI_EXPORTS GMat RGB2Gray(const GMat& src);
+
+/** @overload
+Resulting gray color value computed as
+\f[\texttt{dst} (I)= \texttt{rY} * \texttt{src}(I).R + \texttt{gY} * \texttt{src}(I).G  + \texttt{bY} * \texttt{src}(I).B \f]
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2graycustom"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1.
+@param rY float multiplier for R channel.
+@param gY float multiplier for G channel.
+@param bY float multiplier for B channel.
+@sa RGB2YUV
+ */
+GAPI_EXPORTS GMat RGB2Gray(const GMat& src, float rY, float gY, float bY);
+
+/** @brief Converts an image from BGR color space to gray-scaled.
+The conventional ranges for B, G, and R channel values are 0 to 255.
+Resulting gray color value computed as
+\f[\texttt{dst} (I)= \texttt{0.114} * \texttt{src}(I).B + \texttt{0.587} * \texttt{src}(I).G  + \texttt{0.299} * \texttt{src}(I).R \f]
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2gray"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1.
+@sa BGR2LUV
+ */
+GAPI_EXPORTS GMat BGR2Gray(const GMat& src);
+
+/** @brief Converts an image from RGB color space to YUV color space.
+
+The function converts an input image from RGB color space to YUV.
+The conventional ranges for R, G, and B channel values are 0 to 255.
+
+In case of linear transformations, the range does not matter. But in case of a non-linear
+transformation, an input RGB image should be normalized to the proper value range to get the correct
+results, like here, at RGB \f$\rightarrow\f$ Y\*u\*v\* transformation.
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2yuv"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+@sa YUV2RGB, RGB2Lab
+*/
+GAPI_EXPORTS GMat RGB2YUV(const GMat& src);
+
+/** @brief Converts an image from BGR color space to LUV color space.
+
+The function converts an input image from BGR color space to LUV.
+The conventional ranges for B, G, and R channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2luv"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+@sa RGB2Lab, RGB2LUV
+*/
+GAPI_EXPORTS GMat BGR2LUV(const GMat& src);
+
+/** @brief Converts an image from LUV color space to BGR color space.
+
+The function converts an input image from LUV color space to BGR.
+The conventional ranges for B, G, and R channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.luv2bgr"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+@sa BGR2LUV
+*/
+GAPI_EXPORTS GMat LUV2BGR(const GMat& src);
+
+/** @brief Converts an image from YUV color space to BGR color space.
+
+The function converts an input image from YUV color space to BGR.
+The conventional ranges for B, G, and R channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.yuv2bgr"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+@sa BGR2YUV
+*/
+GAPI_EXPORTS GMat YUV2BGR(const GMat& src);
+
+/** @brief Converts an image from BGR color space to YUV color space.
+
+The function converts an input image from BGR color space to YUV.
+The conventional ranges for B, G, and R channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.bgr2yuv"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+@sa YUV2BGR
+*/
+GAPI_EXPORTS GMat BGR2YUV(const GMat& src);
+
+/** @brief Converts an image from RGB color space to Lab color space.
+
+The function converts an input image from BGR color space to Lab.
+The conventional ranges for R, G, and B channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC1.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2lab"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC1.
+@sa RGB2YUV, RGB2LUV
+*/
+GAPI_EXPORTS GMat RGB2Lab(const GMat& src);
+
+/** @brief Converts an image from YUV color space to RGB.
+The function converts an input image from YUV color space to RGB.
+The conventional ranges for Y, U, and V channel values are 0 to 255.
+
+Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@note Function textual ID is "org.opencv.imgproc.colorconvert.yuv2rgb"
+
+@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
+
+@sa RGB2Lab, RGB2YUV
+*/
+GAPI_EXPORTS GMat YUV2RGB(const GMat& src);
+
+//! @} gapi_colorconvert
+} //namespace gapi
+} //namespace cv
+
+#endif // OPENCV_GAPI_IMGPROC_HPP
diff --git a/modules/gapi/include/opencv2/gapi/opencv_includes.hpp b/modules/gapi/include/opencv2/gapi/opencv_includes.hpp
new file mode 100644 (file)
index 0000000..9d45a7f
--- /dev/null
@@ -0,0 +1,16 @@
+// 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_OPENCV_INCLUDES_HPP
+#define OPENCV_GAPI_OPENCV_INCLUDES_HPP
+
+#include <opencv2/core/mat.hpp>
+#include <opencv2/core/cvdef.h>
+#include <opencv2/core/types.hpp>
+#include <opencv2/core/base.hpp>
+
+#endif // OPENCV_GAPI_OPENCV_INCLUDES_HPP
diff --git a/modules/gapi/include/opencv2/gapi/operators.hpp b/modules/gapi/include/opencv2/gapi/operators.hpp
new file mode 100644 (file)
index 0000000..27a1d80
--- /dev/null
@@ -0,0 +1,69 @@
+// 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_OPERATORS_HPP
+#define OPENCV_GAPI_OPERATORS_HPP
+
+#include "opencv2/gapi/gmat.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+
+GAPI_EXPORTS cv::GMat operator+(const cv::GMat&    lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator+(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator+(const cv::GScalar& lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator-(const cv::GMat&    lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator-(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator-(const cv::GScalar& lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator*(const cv::GMat&    lhs, float              rhs);
+GAPI_EXPORTS cv::GMat operator*(float              lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator*(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator*(const cv::GScalar& lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator/(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator/(const cv::GScalar& lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator/(const cv::GMat&    lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator&(const cv::GMat&    lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator|(const cv::GMat&    lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator^(const cv::GMat&    lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator~(const cv::GMat&    lhs);
+
+GAPI_EXPORTS cv::GMat operator&(const cv::GScalar& lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator|(const cv::GScalar& lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator^(const cv::GScalar& lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator&(const cv::GMat& lhs, const cv::GScalar&    rhs);
+GAPI_EXPORTS cv::GMat operator|(const cv::GMat& lhs, const cv::GScalar&    rhs);
+GAPI_EXPORTS cv::GMat operator^(const cv::GMat& lhs, const cv::GScalar&    rhs);
+
+GAPI_EXPORTS cv::GMat operator>(const cv::GMat&    lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator>=(const cv::GMat&   lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator<(const cv::GMat&    lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator<=(const cv::GMat&   lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator==(const cv::GMat&   lhs, const cv::GMat&    rhs);
+GAPI_EXPORTS cv::GMat operator!=(const cv::GMat&   lhs, const cv::GMat&    rhs);
+
+GAPI_EXPORTS cv::GMat operator>(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator>=(const cv::GMat&   lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator<(const cv::GMat&    lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator<=(const cv::GMat&   lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator==(const cv::GMat&   lhs, const cv::GScalar& rhs);
+GAPI_EXPORTS cv::GMat operator!=(const cv::GMat&   lhs, const cv::GScalar& rhs);
+
+GAPI_EXPORTS cv::GMat operator>(const cv::GScalar&    lhs, const cv::GMat& rhs);
+GAPI_EXPORTS cv::GMat operator>=(const cv::GScalar&   lhs, const cv::GMat& rhs);
+GAPI_EXPORTS cv::GMat operator<(const cv::GScalar&    lhs, const cv::GMat& rhs);
+GAPI_EXPORTS cv::GMat operator<=(const cv::GScalar&   lhs, const cv::GMat& rhs);
+GAPI_EXPORTS cv::GMat operator==(const cv::GScalar&   lhs, const cv::GMat& rhs);
+GAPI_EXPORTS cv::GMat operator!=(const cv::GScalar&   lhs, const cv::GMat& rhs);
+
+
+
+#endif // OPENCV_GAPI_OPERATORS_HPP
diff --git a/modules/gapi/include/opencv2/gapi/own/assert.hpp b/modules/gapi/include/opencv2/gapi/own/assert.hpp
new file mode 100644 (file)
index 0000000..39ad735
--- /dev/null
@@ -0,0 +1,41 @@
+// 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_OWN_ASSERT_HPP
+#define OPENCV_GAPI_OWN_ASSERT_HPP
+
+#if 0
+#include <opencv2/core/base.hpp>
+#define GAPI_Assert(expr) CV_Assert(expr)
+
+#else
+#include <stdexcept>
+#include <sstream>
+#include "opencv2/gapi/util/throw.hpp"
+
+namespace detail
+{
+    inline void assert_abort(const char* str, int line, const char* file, const char* func)
+    {
+        std::stringstream ss;
+        ss << file << ":" << line << ": Assertion " << str << " in function " << func << " failed\n";
+        cv::util::throw_error(std::logic_error(ss.str()));
+    }
+}
+
+#define GAPI_Assert(expr) \
+{ if (!(expr)) ::detail::assert_abort(#expr, __LINE__, __FILE__, __func__); }
+
+#endif
+
+#ifdef _DEBUG
+#  define GAPI_DbgAssert(expr) GAPI_Assert(expr)
+#else
+#  define GAPI_DbgAssert(expr)
+#endif
+
+#endif // OPENCV_GAPI_OWN_ASSERT_HPP
diff --git a/modules/gapi/include/opencv2/gapi/own/convert.hpp b/modules/gapi/include/opencv2/gapi/own/convert.hpp
new file mode 100644 (file)
index 0000000..fc32e19
--- /dev/null
@@ -0,0 +1,47 @@
+// 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_OWN_CONVERT_HPP
+#define OPENCV_GAPI_OWN_CONVERT_HPP
+
+#include <opencv2/core/types.hpp>
+#include <opencv2/core/mat.hpp>
+#include <opencv2/gapi/own/types.hpp>
+#include <opencv2/gapi/own/mat.hpp>
+#include "opencv2/gapi/own/scalar.hpp"
+
+namespace cv
+{
+    inline cv::gapi::own::Mat to_own(Mat const& m) { return {m.rows, m.cols, m.type(), m.data, m.step};};
+           cv::gapi::own::Mat to_own(Mat&&) = delete;
+
+    inline cv::gapi::own::Scalar to_own(const cv::Scalar& s) { return {s[0], s[1], s[2], s[3]}; };
+
+    inline cv::gapi::own::Size to_own (const Size& s) { return {s.width, s.height}; };
+
+    inline cv::gapi::own::Rect to_own (const Rect& r) { return {r.x, r.y, r.width, r.height}; };
+
+
+
+namespace gapi
+{
+namespace own
+{
+    inline cv::Mat to_ocv(Mat const& m) { return {m.rows, m.cols, m.type(), m.data, m.step};};
+           cv::Mat to_ocv(Mat&&)    = delete;
+
+    inline cv::Scalar to_ocv(const Scalar& s) { return {s[0], s[1], s[2], s[3]}; };
+
+    inline cv::Size to_ocv (const Size& s) { return cv::Size(s.width, s.height); };
+
+    inline cv::Rect to_ocv (const Rect& r) { return cv::Rect(r.x, r.y, r.width, r.height); };
+
+} // namespace own
+} // namespace gapi
+} // namespace cv
+
+#endif // OPENCV_GAPI_OWN_CONVERT_HPP
diff --git a/modules/gapi/include/opencv2/gapi/own/exports.hpp b/modules/gapi/include/opencv2/gapi/own/exports.hpp
new file mode 100644 (file)
index 0000000..0d955d0
--- /dev/null
@@ -0,0 +1,28 @@
+// 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_OWN_TYPES_HPP
+#define OPENCV_GAPI_OWN_TYPES_HPP
+
+#   if 0
+#       include <opencv2/core/base.hpp>
+#       define GAPI_EXPORTS CV_EXPORTS
+
+#   else
+#       if defined _WIN32
+#           define GAPI_EXPORTS __declspec(dllexport)
+#       elif defined __GNUC__ && __GNUC__ >= 4
+#           define GAPI_EXPORTS __attribute__ ((visibility ("default")))
+#       endif
+
+#       ifndef GAPI_EXPORTS
+#           define GAPI_EXPORTS
+#       endif
+
+#   endif
+
+#endif // OPENCV_GAPI_OWN_TYPES_HPP
diff --git a/modules/gapi/include/opencv2/gapi/own/mat.hpp b/modules/gapi/include/opencv2/gapi/own/mat.hpp
new file mode 100644 (file)
index 0000000..8cccc7c
--- /dev/null
@@ -0,0 +1,142 @@
+// 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 INCLUDE_OPENCV2_GAPI_OWN_MAT_HPP
+#define INCLUDE_OPENCV2_GAPI_OWN_MAT_HPP
+
+#include "opencv2/core/cvdef.h"
+#include "opencv2/gapi/own/types.hpp"
+#include <memory>                   //std::shared_ptr
+
+namespace cv { namespace gapi { namespace own {
+    namespace detail {
+        inline size_t default_step(int type, int cols)
+        {
+            return CV_ELEM_SIZE(type) * cols;
+        }
+        //Matrix header, i.e. fields that are unique to each Mat object.
+        //Devoted class is needed to implement custom behavior on move (erasing state of moved from object)
+        struct MatHeader{
+            enum { AUTO_STEP = 0};
+            enum { TYPE_MASK = 0x00000FFF  };
+
+            MatHeader() = default;
+
+            MatHeader(int _rows, int _cols, int type, void* _data, size_t _step)
+            : flags((type & TYPE_MASK)), rows(_rows), cols(_cols), data((uchar*)_data), step(_step == AUTO_STEP ? detail::default_step(type, _cols) : _step)
+            {}
+
+            MatHeader(const MatHeader& ) = default;
+            MatHeader(MatHeader&& src) : MatHeader(src) // reuse copy constructor here
+            {
+                MatHeader empty; //give it a name to call copy(not move) assignment below
+                src = empty;
+            }
+            MatHeader& operator=(const MatHeader& ) = default;
+            MatHeader& operator=(MatHeader&& src)
+            {
+                *this = src; //calling a copy assignment here, not move one
+                MatHeader empty; //give it a name to call copy(not move) assignment below
+                src = empty;
+                return *this;
+            }
+            /*! includes several bit-fields:
+                 - depth
+                 - number of channels
+             */
+            int flags = 0;
+
+            //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
+            int rows = 0, cols = 0;
+            //! pointer to the data
+            uchar* data = nullptr;
+            size_t step = 0;
+        };
+    }
+    //concise version of cv::Mat suitable for GAPI needs (used when no dependence on OpenCV is required)
+    class Mat : public detail::MatHeader{
+    public:
+
+        Mat() = default;
+
+        /** @overload
+        @param _rows Number of rows in a 2D array.
+        @param _cols Number of columns in a 2D array.
+        @param _type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or
+        CV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices.
+        @param _data Pointer to the user data. Matrix constructors that take data and step parameters do not
+        allocate matrix data. Instead, they just initialize the matrix header that points to the specified
+        data, which means that no data is copied. This operation is very efficient and can be used to
+        process external data using OpenCV functions. The external data is not automatically deallocated, so
+        you should take care of it.
+        @param _step Number of bytes each matrix row occupies. The value should include the padding bytes at
+        the end of each row, if any. If the parameter is missing (set to AUTO_STEP ), no padding is assumed
+        and the actual step is calculated as cols*elemSize(). See Mat::elemSize.
+        */
+        Mat(int _rows, int _cols, int _type, void* _data, size_t _step = AUTO_STEP)
+        : MatHeader (_rows, _cols, _type, _data, _step)
+        {}
+
+        Mat(Mat const& src) = default;
+        Mat(Mat&& src) = default;
+
+        Mat& operator=(Mat const& src) = default;
+        Mat& operator=(Mat&& src) = default;
+
+        /** @brief Returns the type of a matrix element.
+
+        The method returns a matrix element type. This is an identifier compatible with the CvMat type
+        system, like CV_16SC3 or 16-bit signed 3-channel array, and so on.
+         */
+        int type() const            {return CV_MAT_TYPE(flags);}
+
+        /** @brief Returns the depth of a matrix element.
+
+        The method returns the identifier of the matrix element depth (the type of each individual channel).
+        For example, for a 16-bit signed element array, the method returns CV_16S . A complete list of
+        matrix types contains the following values:
+        -   CV_8U - 8-bit unsigned integers ( 0..255 )
+        -   CV_8S - 8-bit signed integers ( -128..127 )
+        -   CV_16U - 16-bit unsigned integers ( 0..65535 )
+        -   CV_16S - 16-bit signed integers ( -32768..32767 )
+        -   CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
+        -   CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
+        -   CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
+         */
+        int depth() const           {return CV_MAT_DEPTH(flags);}
+
+        /** @brief Returns the number of matrix channels.
+
+        The method returns the number of matrix channels.
+         */
+        int channels() const        {return CV_MAT_CN(flags);}
+
+        /** @overload
+        @param _size Alternative new matrix size specification: Size(cols, rows)
+        @param _type New matrix type.
+        */
+        void create(cv::gapi::own::Size _size, int _type)
+        {
+            if (_size != cv::gapi::own::Size{cols, rows} )
+            {
+                Mat tmp{_size.height, _size.width, _type, nullptr};
+                tmp.memory.reset(new uchar[ tmp.step * tmp.rows], [](uchar * p){delete[] p;});
+                tmp.data = tmp.memory.get();
+
+                *this = std::move(tmp);
+            }
+        }
+    private:
+        //actual memory allocated for storage, or nullptr if object is non owning view to over memory
+        std::shared_ptr<uchar> memory;
+    };
+
+} //namespace own
+} //namespace gapi
+} //namespace cv
+
+#endif /* INCLUDE_OPENCV2_GAPI_OWN_MAT_HPP */
diff --git a/modules/gapi/include/opencv2/gapi/own/scalar.hpp b/modules/gapi/include/opencv2/gapi/own/scalar.hpp
new file mode 100644 (file)
index 0000000..52a45af
--- /dev/null
@@ -0,0 +1,45 @@
+// 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_GAPI_OWN_SCALAR_HPP
+#define OPENCV_GAPI_GAPI_OWN_SCALAR_HPP
+
+namespace cv
+{
+namespace gapi
+{
+namespace own
+{
+
+class CV_EXPORTS Scalar
+{
+public:
+    Scalar() = default;
+    explicit Scalar(double v0) { val[0] = v0; };
+    Scalar(double v0, double v1, double v2 = 0, double v3 = 0)
+        : val{v0, v1, v2, v3}
+    {
+    }
+
+    const double& operator[](int i) const { return val[i]; }
+          double& operator[](int i)       { return val[i]; }
+
+    static Scalar all(double v0) { return Scalar(v0, v0, v0, v0); }
+
+    double val[4] = {0};
+};
+
+inline bool operator==(const Scalar& lhs, const Scalar& rhs)
+{
+    return std::equal(std::begin(lhs.val), std::end(lhs.val), std::begin(rhs.val));
+}
+
+} // namespace own
+} // namespace gapi
+} // namespace cv
+
+#endif // OPENCV_GAPI_GAPI_OWN_SCALAR_HPP
diff --git a/modules/gapi/include/opencv2/gapi/own/types.hpp b/modules/gapi/include/opencv2/gapi/own/types.hpp
new file mode 100644 (file)
index 0000000..2f909a4
--- /dev/null
@@ -0,0 +1,137 @@
+// 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_TYPES_HPP
+#define OPENCV_GAPI_TYPES_HPP
+
+#include <algorithm>              // std::max, std::min
+
+#include "opencv2/core/base.hpp"  //for CV_DbgAssert
+#include "opencv2/gapi/own/assert.hpp"
+
+namespace cv
+{
+namespace gapi
+{
+namespace own
+{
+
+class Point
+{
+public:
+    Point() = default;
+    Point(int _x, int _y) : x(_x),  y(_y)  {};
+
+    int x = 0;
+    int y = 0;
+};
+
+class Rect
+{
+public:
+    Rect() = default;
+    Rect(int _x, int _y, int _width, int _height) : x(_x), y(_y),   width(_width),  height(_height)  {};
+#if 1
+    Rect(const cv::Rect& other) : x(other.x), y(other.y), width(other.width), height(other.height) {};
+    inline Rect& operator=(const cv::Rect& other)
+    {
+        x = other.x;
+        y = other.x;
+        width  = other.width;
+        height = other.height;
+        return *this;
+    }
+#endif
+
+    int x      = 0; //!< x coordinate of the top-left corner
+    int y      = 0; //!< y coordinate of the top-left corner
+    int width  = 0; //!< width of the rectangle
+    int height = 0; //!< height of the rectangle
+};
+
+inline bool operator==(const Rect& lhs, const Rect& rhs)
+{
+    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Rect& lhs, const Rect& rhs)
+{
+    return !(lhs == rhs);
+}
+
+inline Rect& operator&=(Rect& lhs, const Rect& rhs)
+{
+    int x1 = std::max(lhs.x, rhs.x);
+    int y1 = std::max(lhs.y, rhs.y);
+    lhs.width  = std::min(lhs.x + lhs.width,  rhs.x + rhs.width) -  x1;
+    lhs.height = std::min(lhs.y + lhs.height, rhs.y + rhs.height) - y1;
+    lhs.x = x1;
+    lhs.y = y1;
+    if( lhs.width <= 0 || lhs.height <= 0 )
+        lhs = Rect();
+    return lhs;
+}
+
+inline const Rect operator&(const Rect& lhs, const Rect& rhs)
+{
+    Rect result = lhs;
+    return result &= rhs;
+}
+
+inline std::ostream& operator<<(std::ostream& o, const Rect& rect)
+{
+    return o << "[" << rect.width << " x " << rect.height << " from (" << rect.x << ", " << rect.y << ")]";
+}
+
+class Size
+{
+public:
+    Size() = default;
+    Size(int _width, int _height) : width(_width),  height(_height)  {};
+#if 1
+    Size(const cv::Size& other) : width(other.width), height(other.height) {};
+    inline Size& operator=(const cv::Size& rhs)
+    {
+        width  = rhs.width;
+        height = rhs.height;
+        return *this;
+    }
+#endif
+
+    int width  = 0;
+    int height = 0;
+};
+
+inline Size& operator+=(Size& lhs, const Size& rhs)
+{
+    lhs.width  += rhs.width;
+    lhs.height += rhs.height;
+    return lhs;
+}
+
+inline bool operator==(const Size& lhs, const Size& rhs)
+{
+    return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs)
+{
+    return !(lhs == rhs);
+}
+
+
+inline std::ostream& operator<<(std::ostream& o, const Size& s)
+{
+    o << "[" << s.width << " x " << s.height << "]";
+    return o;
+}
+
+} // namespace own
+} // namespace gapi
+} // namespace cv
+
+#endif // OPENCV_GAPI_TYPES_HPP
diff --git a/modules/gapi/include/opencv2/gapi/util/any.hpp b/modules/gapi/include/opencv2/gapi/util/any.hpp
new file mode 100644 (file)
index 0000000..31e4011
--- /dev/null
@@ -0,0 +1,159 @@
+// 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_UTIL_ANY_HPP
+#define OPENCV_GAPI_UTIL_ANY_HPP
+
+#include <memory>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+#include "opencv2/gapi/util/throw.hpp"
+
+#if defined(_MSC_VER)
+   // disable MSVC warning on "multiple copy constructors specified"
+#  pragma warning(disable: 4521)
+#endif
+
+namespace cv
+{
+
+namespace internal
+{
+    template <class T, class Source>
+    T down_cast(Source operand)
+    {
+#if defined(__GXX_RTTI) || defined(_CPPRTTI)
+       return dynamic_cast<T>(operand);
+#else
+    #warning used static cast instead of dynamic because RTTI is disabled
+       return static_cast<T>(operand);
+#endif
+    }
+}
+
+namespace util
+{
+   class bad_any_cast : public std::bad_cast
+   {
+   public:
+       virtual const char* what() const noexcept override
+       {
+           return "Bad any cast";
+       }
+   };
+
+   //modeled against C++17 std::any
+
+   class any
+   {
+   private:
+      struct holder;
+      using holder_ptr = std::unique_ptr<holder>;
+      struct holder
+      {
+         virtual holder_ptr clone() = 0;
+         virtual ~holder() = default;
+      };
+
+      template <typename value_t>
+      struct holder_impl : holder
+      {
+         value_t v;
+         template<typename arg_t>
+         holder_impl(arg_t&& a) : v(std::forward<arg_t>(a)) {}
+         holder_ptr clone() override { return holder_ptr(new holder_impl (v));}
+      };
+
+      holder_ptr hldr;
+   public:
+      template<class value_t>
+      any(value_t&& arg) :  hldr(new holder_impl<typename std::decay<value_t>::type>( std::forward<value_t>(arg))) {}
+
+      any(any const& src) : hldr( src.hldr ? src.hldr->clone() : nullptr) {}
+      //simple hack in order not to write enable_if<not any> for the template constructor
+      any(any & src) : any (const_cast<any const&>(src)) {}
+
+      any()       = default;
+      any(any&& ) = default;
+
+      any& operator=(any&&) = default;
+
+      any& operator=(any const& src)
+      {
+         any copy(src);
+         swap(*this, copy);
+         return *this;
+      }
+
+      template<class value_t>
+      friend value_t* any_cast(any* operand);
+
+      template<class value_t>
+      friend const value_t* any_cast(const any* operand);
+
+      friend void swap(any & lhs, any& rhs)
+      {
+         swap(lhs.hldr, rhs.hldr);
+      }
+
+   };
+
+   template<class value_t>
+   value_t* any_cast(any* operand)
+   {
+      auto casted = internal::down_cast<any::holder_impl<typename std::decay<value_t>::type> *>(operand->hldr.get());
+      if (casted){
+         return & (casted->v);
+      }
+      return nullptr;
+   }
+
+   template<class value_t>
+   const value_t* any_cast(const any* operand)
+   {
+      auto casted = internal::down_cast<any::holder_impl<typename std::decay<value_t>::type> *>(operand->hldr.get());
+      if (casted){
+         return & (casted->v);
+      }
+      return nullptr;
+   }
+
+   template<class value_t>
+   value_t& any_cast(any& operand)
+   {
+      auto ptr = any_cast<value_t>(&operand);
+      if (ptr)
+      {
+         return *ptr;
+      }
+
+      throw_error(bad_any_cast());
+   }
+
+
+   template<class value_t>
+   const value_t& any_cast(const any& operand)
+   {
+      auto ptr = any_cast<value_t>(&operand);
+      if (ptr)
+      {
+         return *ptr;
+      }
+
+      throw_error(bad_any_cast());
+   }
+} // namespace util
+} // namespace cv
+
+#if defined(_MSC_VER)
+   // Enable "multiple copy constructors specified" back
+#  pragma warning(default: 4521)
+#endif
+
+#endif // OPENCV_GAPI_UTIL_ANY_HPP
diff --git a/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp b/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp
new file mode 100644 (file)
index 0000000..575655e
--- /dev/null
@@ -0,0 +1,21 @@
+// 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_UTIL_COMPILER_HINTS_HPP
+#define OPENCV_GAPI_UTIL_COMPILER_HINTS_HPP
+
+namespace cv
+{
+namespace util
+{
+    //! Utility template function to prevent "unused" warnings by various compilers.
+    template<typename T> void suppress_unused_warning( const T& ) {}
+} // namespace util
+} // namespace cv
+
+#define UNUSED(x) cv::util::suppress_unused_warning(x)
+
+#endif /* OPENCV_GAPI_UTIL_COMPILER_HINTS_HPP */
diff --git a/modules/gapi/include/opencv2/gapi/util/optional.hpp b/modules/gapi/include/opencv2/gapi/util/optional.hpp
new file mode 100644 (file)
index 0000000..54126d6
--- /dev/null
@@ -0,0 +1,178 @@
+// 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_UTIL_OPTIONAL_HPP
+#define OPENCV_GAPI_UTIL_OPTIONAL_HPP
+
+#include "opencv2/gapi/util/variant.hpp"
+
+// A poor man's `optional` implementation, incompletely modeled against C++17 spec.
+namespace cv
+{
+namespace util
+{
+    class bad_optional_access: public std::exception
+    {
+    public:
+        virtual const char *what() const noexcept override
+        {
+            return "Bad optional access";
+        }
+    };
+
+    // TODO: nullopt_t
+
+    // Interface ///////////////////////////////////////////////////////////////
+    template<typename T> class optional
+    {
+    public:
+        // Constructors
+        // NB.: there were issues with Clang 3.8 when =default() was used
+        // instead {}
+        optional() {};
+        optional(const optional&) = default;
+        explicit optional(T &&value) noexcept;
+        explicit optional(const T &value) noexcept;
+        optional(optional &&) noexcept;
+        // TODO: optional(nullopt_t) noexcept;
+        // TODO: optional(const optional<U> &)
+        // TODO: optional(optional<U> &&)
+        // TODO: optional(Args&&...)
+        // TODO: optional(initializer_list<U>)
+        // TODO: optional(U&& value);
+
+        // Assignment
+        optional& operator=(const optional& rhs) = default;
+        optional& operator=(optional&& rhs);
+
+        // Observers
+        T* operator-> ();
+        const T* operator-> () const;
+        T& operator* ();
+        const T& operator* () const;
+        // TODO: && versions
+
+        operator bool() const noexcept;
+        bool has_value() const noexcept;
+
+        T& value();
+        const T& value() const;
+        // TODO: && versions
+
+        template<class U>
+        T value_or(U &&default_value) const;
+
+        void swap(optional &other) noexcept;
+        void reset() noexcept;
+        // TODO: emplace
+
+        // TODO: operator==, !=, <, <=, >, >=
+
+    private:
+        struct nothing {};
+        util::variant<nothing, T> m_holder;
+    };
+
+    template<class T>
+    optional<typename std::decay<T>::type> make_optional(T&& value);
+
+    // TODO: Args... and initializer_list versions
+
+    // Implementation //////////////////////////////////////////////////////////
+    template<class T> optional<T>::optional(T &&v) noexcept
+        : m_holder(v)
+    {
+    }
+
+    template<class T> optional<T>::optional(const T &v) noexcept
+        : m_holder(v)
+    {
+    }
+
+    template<class T> optional<T>::optional(optional&& rhs) noexcept
+        : m_holder(std::move(rhs.m_holder))
+    {
+        rhs.reset();
+    }
+
+    template<class T> optional<T>& optional<T>::operator=(optional&& rhs)
+    {
+        m_holder = std::move(rhs.m_holder);
+        rhs.reset();
+        return *this;
+    }
+
+    template<class T> T* optional<T>::operator-> ()
+    {
+        return & *(*this);
+    }
+
+    template<class T> const T* optional<T>::operator-> () const
+    {
+        return & *(*this);
+    }
+
+    template<class T> T& optional<T>::operator* ()
+    {
+        return this->value();
+    }
+
+    template<class T> const T& optional<T>::operator* () const
+    {
+        return this->value();
+    }
+
+    template<class T> optional<T>::operator bool() const noexcept
+    {
+        return this->has_value();
+    }
+
+    template<class T> bool optional<T>::has_value() const noexcept
+    {
+        return util::holds_alternative<T>(m_holder);
+    }
+
+    template<class T> T& optional<T>::value()
+    {
+        if (!this->has_value())
+            throw_error(bad_optional_access());
+        return util::get<T>(m_holder);
+    }
+
+    template<class T> const T& optional<T>::value() const
+    {
+        if (!this->has_value())
+            throw_error(bad_optional_access());
+        return util::get<T>(m_holder);
+    }
+
+    template<class T>
+    template<class U> T optional<T>::value_or(U &&default_value) const
+    {
+        return (this->has_value() ? this->value() : T(default_value));
+    }
+
+    template<class T> void optional<T>::swap(optional<T> &other) noexcept
+    {
+        m_holder.swap(other.m_holder);
+    }
+
+    template<class T> void optional<T>::reset() noexcept
+    {
+        if (this->has_value())
+            m_holder = nothing{};
+    }
+
+    template<class T>
+    optional<typename std::decay<T>::type> make_optional(T&& value)
+    {
+        return optional<typename std::decay<T>::type>(std::forward<T>(value));
+    }
+} // namespace util
+} // namespace cv
+
+#endif // OPENCV_GAPI_UTIL_OPTIONAL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/util/throw.hpp b/modules/gapi/include/opencv2/gapi/util/throw.hpp
new file mode 100644 (file)
index 0000000..689bf58
--- /dev/null
@@ -0,0 +1,36 @@
+// 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_UTIL_THROW_HPP
+#define OPENCV_GAPI_UTIL_THROW_HPP
+
+#include <utility>  // std::forward
+
+#if !defined(__EXCEPTIONS)
+#include <stdlib.h>
+#include <stdio.h>
+#endif
+
+namespace cv
+{
+namespace util
+{
+template <class ExceptionType>
+[[noreturn]] void throw_error(ExceptionType &&e)
+{
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+    throw std::forward<ExceptionType>(e);
+#else
+    fprintf(stderr, "An exception thrown! %s\n" , e.what());
+    fflush(stderr);
+    abort();
+#endif
+}
+} // namespace util
+} // namespace cv
+
+#endif // OPENCV_GAPI_UTIL_THROW_HPP
diff --git a/modules/gapi/include/opencv2/gapi/util/util.hpp b/modules/gapi/include/opencv2/gapi/util/util.hpp
new file mode 100644 (file)
index 0000000..d0378e0
--- /dev/null
@@ -0,0 +1,92 @@
+// 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_UTIL_HPP
+#define OPENCV_GAPI_UTIL_HPP
+
+#include <utility> // std::tuple
+
+// \cond HIDDEN_SYMBOLS
+// This header file contains some generic utility functions which are
+// used in other G-API Public API headers.
+//
+// PLEASE don't put any stuff here if it is NOT used in public API headers!
+
+namespace cv
+{
+namespace detail
+{
+    // Recursive integer sequence type, useful for enumerating elements of
+    // template parameter packs.
+    template<int... I> struct Seq     { using next = Seq<I..., sizeof...(I)>; };
+    template<int Sz>   struct MkSeq   { using type = typename MkSeq<Sz-1>::type::next; };
+    template<>         struct MkSeq<0>{ using type = Seq<>; };
+
+    // Checks if elements of variadic template satisfy the given Predicate.
+    // Implemented via tuple, with an interface to accept plain type lists
+    template<template<class> class, typename, typename...> struct all_satisfy;
+
+    template<template<class> class F, typename T, typename... Ts>
+    struct all_satisfy<F, std::tuple<T, Ts...> >
+    {
+        static const constexpr bool value = F<T>::value
+            && all_satisfy<F, std::tuple<Ts...> >::value;
+    };
+    template<template<class> class F, typename T>
+    struct all_satisfy<F, std::tuple<T> >
+    {
+        static const constexpr bool value = F<T>::value;
+    };
+
+    template<template<class> class F, typename T, typename... Ts>
+    struct all_satisfy: public all_satisfy<F, std::tuple<T, Ts...> > {};
+
+    // Permute given tuple type C with given integer sequence II
+    // Sequence may be less than tuple C size.
+    template<class, class> struct permute_tuple;
+
+    template<class C, int... IIs>
+    struct permute_tuple<C, Seq<IIs...> >
+    {
+        using type = std::tuple< typename std::tuple_element<IIs, C>::type... >;
+    };
+
+    // Given T..., generates a type sequence of sizeof...(T)-1 elements
+    // which is T... without its last element
+    // Implemented via tuple, with an interface to accept plain type lists
+    template<typename T, typename... Ts> struct all_but_last;
+
+    template<typename T, typename... Ts>
+    struct all_but_last<std::tuple<T, Ts...> >
+    {
+        using C    = std::tuple<T, Ts...>;
+        using S    = typename MkSeq<std::tuple_size<C>::value - 1>::type;
+        using type = typename permute_tuple<C, S>::type;
+    };
+
+    template<typename T, typename... Ts>
+    struct all_but_last: public all_but_last<std::tuple<T, Ts...> > {};
+
+    template<typename... Ts>
+    using all_but_last_t = typename all_but_last<Ts...>::type;
+
+    // NB.: This is here because there's no constexpr std::max in C++11
+    template<std::size_t S0, std::size_t... SS> struct max_of_t
+    {
+        static constexpr const std::size_t rest  = max_of_t<SS...>::value;
+        static constexpr const std::size_t value = rest > S0 ? rest : S0;
+    };
+    template<std::size_t S> struct max_of_t<S>
+    {
+        static constexpr const std::size_t value = S;
+    };
+} // namespace detail
+} // namespace cv
+
+// \endcond
+
+#endif //  OPENCV_GAPI_UTIL_HPP
diff --git a/modules/gapi/include/opencv2/gapi/util/variant.hpp b/modules/gapi/include/opencv2/gapi/util/variant.hpp
new file mode 100644 (file)
index 0000000..cb0270a
--- /dev/null
@@ -0,0 +1,377 @@
+// 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_UTIL_VARIANT_HPP
+#define OPENCV_GAPI_UTIL_VARIANT_HPP
+
+#include <array>
+#include <type_traits>
+
+#include "opencv2/gapi/util/throw.hpp"
+#include "opencv2/gapi/util/util.hpp" // max_of_t
+
+// A poor man's `variant` implementation, incompletely modeled against C++17 spec.
+namespace cv
+{
+namespace util
+{
+    namespace detail
+    {
+        template<std::size_t I, typename Target, typename First, typename... Remaining>
+        struct type_list_index_helper
+        {
+            static const constexpr bool is_same = std::is_same<Target, First>::value;
+            static const constexpr std::size_t value =
+                std::conditional<is_same, std::integral_constant<std::size_t, I>, type_list_index_helper<I + 1, Target, Remaining...>>::type::value;
+        };
+
+        template<std::size_t I, typename Target, typename First>
+        struct type_list_index_helper<I, Target, First>
+        {
+            static_assert(std::is_same<Target, First>::value, "Type not found");
+            static const constexpr std::size_t value = I;
+        };
+
+
+        template<class T, class U, class V> using are_different =
+            std::enable_if<!std::is_same<typename std::decay<T>::type,
+                                         typename std::decay<U>::type>::value,
+                           V>;
+    }
+
+    template<typename Target, typename... Types>
+    struct type_list_index
+    {
+        static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
+    };
+
+    class bad_variant_access: public std::exception
+    {
+    public:
+        virtual const char *what() const noexcept override
+        {
+            return "Bad variant access";
+        }
+    };
+
+    // Interface ///////////////////////////////////////////////////////////////
+    struct monostate {};
+    inline bool operator==(const util::monostate&, const util::monostate&)
+    {
+        return true;
+    }
+
+    template<typename... Ts> // FIXME: no references, arrays, and void
+    class variant
+    {
+        // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped
+        static constexpr const std::size_t S = cv::detail::max_of_t<sizeof(Ts)...>::value;
+        static constexpr const std::size_t A = cv::detail::max_of_t<alignof(Ts)...>::value;
+        using Memory = typename std::aligned_storage<S, A>::type[1];
+
+        template<typename T> struct cctr_h {
+            static void help(Memory memory, const Memory from) {
+                new (memory) T(*reinterpret_cast<const T*>(from));
+            }
+        };
+
+        template<typename T> struct vctr_h {
+            static void help(Memory memory, const void* pval) {
+                new (memory) T(*reinterpret_cast<const T*>(pval));
+            }
+        };
+
+        template<typename T> struct mctr_h {
+            static void help(Memory memory, void *pval) {
+                new (memory) T(std::move(*reinterpret_cast<T*>(pval)));
+            }
+        };
+
+        template<typename T> struct copy_h {
+            static void help(Memory to, const Memory from) {
+                *reinterpret_cast<T*>(to) = *reinterpret_cast<const T*>(from);
+            }
+        };
+
+        template<typename T> struct move_h {
+            static void help(Memory to, const Memory from) {
+                *reinterpret_cast<T*>(to) = std::move(*reinterpret_cast<const T*>(from));
+            }
+        };
+
+        template<typename T> struct swap_h {
+            static void help(Memory to, Memory from) {
+                std::swap(*reinterpret_cast<T*>(to), *reinterpret_cast<T*>(from));
+            }
+        };
+
+        template<typename T> struct dtor_h {
+            static void help(Memory memory) {
+                (void) memory; // MSCV warning
+                reinterpret_cast<T*>(memory)->~T();
+            }
+        };
+
+        template<typename T> struct equal_h {
+            static bool help(const Memory lhs, const Memory rhs) {
+                const T& t_lhs = *reinterpret_cast<const T*>(lhs);
+                const T& t_rhs = *reinterpret_cast<const T*>(rhs);
+                return t_lhs == t_rhs;
+            }
+        };
+
+        typedef void (*CCtr) (Memory, const Memory);  // Copy c-tor (variant)
+        typedef void (*VCtr) (Memory, const void*);   // Copy c-tor (value)
+        typedef void (*MCtr) (Memory, void*);         // Generic move c-tor
+        typedef void (*Copy) (Memory, const Memory);  // Copy assignment
+        typedef void (*Move) (Memory, const Memory);  // Move assignment
+        typedef void (*Swap) (Memory, Memory);        // Swap
+        typedef void (*Dtor) (Memory);                // Destructor
+
+        typedef bool (*Equal)(const Memory, const Memory); // Equality test (external)
+
+        static constexpr std::array<CCtr, sizeof...(Ts)> cctrs(){ return {{(&cctr_h<Ts>::help)...}};}
+        static constexpr std::array<VCtr, sizeof...(Ts)> vctrs(){ return {{(&vctr_h<Ts>::help)...}};}
+        static constexpr std::array<MCtr, sizeof...(Ts)> mctrs(){ return {{(&mctr_h<Ts>::help)...}};}
+        static constexpr std::array<Copy, sizeof...(Ts)> cpyrs(){ return {{(&copy_h<Ts>::help)...}};}
+        static constexpr std::array<Move, sizeof...(Ts)> mvers(){ return {{(&move_h<Ts>::help)...}};}
+        static constexpr std::array<Swap, sizeof...(Ts)> swprs(){ return {{(&swap_h<Ts>::help)...}};}
+        static constexpr std::array<Dtor, sizeof...(Ts)> dtors(){ return {{(&dtor_h<Ts>::help)...}};}
+
+        std::size_t m_index = 0;
+
+    protected:
+        template<typename T, typename... Us> friend T& get(variant<Us...> &v);
+        template<typename T, typename... Us> friend const T& get(const variant<Us...> &v);
+        template<typename... Us> friend bool operator==(const variant<Us...> &lhs,
+                                                        const variant<Us...> &rhs);
+        Memory memory;
+
+    public:
+        // Constructors
+        variant() noexcept;
+        variant(const variant& other);
+        variant(variant&& other) noexcept;
+        template<typename T> explicit variant(const T& t);
+        // are_different is a SFINAE trick to avoid variant(T &&t) with T=variant
+        // for some reason, this version is called instead of variant(variant&& o) when
+        // variant is used in STL containers (examples: vector assignment)
+        template<typename T> explicit variant(T&& t, typename detail::are_different<variant, T, int>::type = 0);
+        // template<class T, class... Args> explicit variant(Args&&... args);
+        // FIXME: other constructors
+
+        // Destructor
+        ~variant();
+
+        // Assignment
+        variant& operator=(const variant& rhs);
+        variant& operator=(variant &&rhs) noexcept;
+
+        // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above
+        template<class T>
+        typename detail::are_different<variant, T, variant&>
+        ::type operator=(T&& t) noexcept;
+
+        // Observers
+        std::size_t index() const noexcept;
+        // FIXME: valueless_by_exception()
+
+        // Modifiers
+        // FIXME: emplace()
+        void swap(variant &rhs) noexcept;
+
+        // Non-C++17x!
+        template<typename T> static constexpr std::size_t index_of();
+    };
+
+    // FIMXE: visit
+
+    template<typename T, typename... Types>
+    T& get(util::variant<Types...> &v);
+
+    template<typename T, typename... Types>
+    const T& get(const util::variant<Types...> &v);
+
+    template<typename T, typename... Types>
+    bool holds_alternative(const util::variant<Types...> &v) noexcept;
+
+    // FIXME: T&&, const TT&& versions.
+
+    // Implementation //////////////////////////////////////////////////////////
+    template<typename... Ts>
+    variant<Ts...>::variant() noexcept
+    {
+        typedef typename std::tuple_element<0, std::tuple<Ts...> >::type TFirst;
+        new (memory) TFirst();
+    }
+
+    template<typename... Ts>
+    variant<Ts...>::variant(const variant &other)
+        : m_index(other.m_index)
+    {
+        (cctrs()[m_index])(memory, other.memory);
+    }
+
+    template<typename... Ts>
+    variant<Ts...>::variant(variant &&other) noexcept
+        : m_index(other.m_index)
+    {
+        (mctrs()[m_index])(memory, other.memory);
+    }
+
+    template<typename... Ts>
+    template<class T>
+    variant<Ts...>::variant(const T& t)
+        : m_index(util::type_list_index<T, Ts...>::value)
+    {
+        (vctrs()[m_index])(memory, &t);
+    }
+
+    template<typename... Ts>
+    template<class T>
+    variant<Ts...>::variant(T&& t, typename detail::are_different<variant, T, int>::type)
+        : m_index(util::type_list_index<typename std::remove_reference<T>::type, Ts...>::value)
+    {
+        (mctrs()[m_index])(memory, &t);
+    }
+
+    template<typename... Ts>
+    variant<Ts...>::~variant()
+    {
+        (dtors()[m_index])(memory);
+    }
+
+    template<typename... Ts>
+    variant<Ts...>& variant<Ts...>::operator=(const variant<Ts...> &rhs)
+    {
+        if (m_index != rhs.m_index)
+        {
+            (dtors()[    m_index])(memory);
+            (cctrs()[rhs.m_index])(memory, rhs.memory);
+            m_index = rhs.m_index;
+        }
+        else
+        {
+            (cpyrs()[rhs.m_index])(memory, rhs.memory);
+        }
+        return *this;
+    }
+
+    template<typename... Ts>
+    variant<Ts...>& variant<Ts...>::operator=(variant<Ts...> &&rhs) noexcept
+    {
+        if (m_index != rhs.m_index)
+        {
+            (dtors()[    m_index])(memory);
+            (mctrs()[rhs.m_index])(memory, rhs.memory);
+            m_index = rhs.m_index;
+        }
+        else
+        {
+            (mvers()[rhs.m_index])(memory, rhs.memory);
+        }
+        return *this;
+    }
+
+    template<typename... Ts>
+    template<class T> typename detail::are_different<variant<Ts...>, T, variant<Ts...>&>
+    ::type variant<Ts...>::operator=(T&& t) noexcept
+    {
+        // FIXME: No version with implicit type conversion available!
+        static const constexpr std::size_t t_index =
+            util::type_list_index<T, Ts...>::value;
+
+        if (t_index == m_index)
+        {
+            util::get<T>(*this) = std::move(t);
+            return *this;
+        }
+        else return (*this = variant(std::move(t)));
+    }
+
+    template<typename... Ts>
+    std::size_t util::variant<Ts...>::index() const noexcept
+    {
+        return m_index;
+    }
+
+    template<typename... Ts>
+    void variant<Ts...>::swap(variant<Ts...> &rhs) noexcept
+    {
+        if (m_index == rhs.index())
+        {
+            (swprs()[m_index](memory, rhs.memory));
+        }
+        else
+        {
+            variant<Ts...> tmp(std::move(*this));
+            *this = std::move(rhs);
+            rhs   = std::move(tmp);
+        }
+    }
+
+    template<typename... Ts>
+    template<typename T>
+    constexpr std::size_t variant<Ts...>::index_of()
+    {
+        return util::type_list_index<T, Ts...>::value; // FIXME: tests!
+    }
+
+    template<typename T, typename... Types>
+    T& get(util::variant<Types...> &v)
+    {
+        const constexpr std::size_t t_index =
+            util::type_list_index<T, Types...>::value;
+
+        if (v.index() == t_index)
+            return reinterpret_cast<T&>(v.memory);
+        else
+            throw_error(bad_variant_access());
+    }
+
+    template<typename T, typename... Types>
+    const T& get(const util::variant<Types...> &v)
+    {
+        const constexpr std::size_t t_index =
+            util::type_list_index<T, Types...>::value;
+
+        if (v.index() == t_index)
+            return reinterpret_cast<const T&>(v.memory);
+        else
+            throw_error(bad_variant_access());
+    }
+
+    template<typename T, typename... Types>
+    bool holds_alternative(const util::variant<Types...> &v) noexcept
+    {
+        return v.index() == util::variant<Types...>::template index_of<T>();
+    }
+
+    template<typename... Us> bool operator==(const variant<Us...> &lhs,
+                                             const variant<Us...> &rhs)
+    {
+        using V = variant<Us...>;
+
+        // Instantiate table only here since it requires operator== for <Us...>
+        // <Us...> should have operator== only if this one is used, not in general
+        static const std::array<typename V::Equal, sizeof...(Us)> eqs = {
+            {(&V::template equal_h<Us>::help)...}
+        };
+        if (lhs.index() != rhs.index())
+            return false;
+        return (eqs[lhs.index()])(lhs.memory, rhs.memory);
+    }
+
+    template<typename... Us> bool operator!=(const variant<Us...> &lhs,
+                                             const variant<Us...> &rhs)
+    {
+        return !(lhs == rhs);
+    }
+} // namespace cv
+} // namespace util
+
+#endif // OPENCV_GAPI_UTIL_VARIANT_HPP
diff --git a/modules/gapi/perf/common/gapi_core_perf_tests.hpp b/modules/gapi/perf/common/gapi_core_perf_tests.hpp
new file mode 100644 (file)
index 0000000..49d7791
--- /dev/null
@@ -0,0 +1,1782 @@
+// 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
+
+
+#include "../../test/common/gapi_tests_common.hpp"
+#include "opencv2/gapi/core.hpp"
+
+namespace opencv_test
+{
+  using namespace perf;
+
+  enum bitwiseOp
+  {
+      AND = 0,
+      OR = 1,
+      XOR = 2,
+      NOT = 3
+  };
+
+//------------------------------------------------------------------------------
+
+    class AddPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(AddPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::add(in_mat1, in_mat2, out_mat_ocv, cv::noArray(), dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::add(in1, in2, dtype);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class AddCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(AddCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::add(in_mat1, sc, out_mat_ocv, cv::noArray(), dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::addC(in1, sc1, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class SubPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(SubPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::subtract(in_mat1, in_mat2, out_mat_ocv, cv::noArray(), dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::sub(in1, in2, dtype);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class SubCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(SubCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::subtract(in_mat1, sc, out_mat_ocv, cv::noArray(), dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::subC(in1, sc1, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class SubRCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(SubRCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::subtract(sc, in_mat1, out_mat_ocv, cv::noArray(), dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::subRC(sc1, in1, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MulPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(MulPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::multiply(in_mat1, in_mat2, out_mat_ocv, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::mul(in1, in2, dtype);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MulDoublePerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(MulDoublePerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      auto& rng = cv::theRNG();
+      double d = rng.uniform(0.0, 10.0);
+      initMatrixRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::multiply(in_mat1, d, out_mat_ocv, 1, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      out = cv::gapi::mulC(in1, d, dtype);
+      cv::GComputation c(in1, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MulCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(MulCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::multiply(in_mat1, sc, out_mat_ocv, 1, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::mulC(in1, sc1, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class DivPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(DivPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::divide(in_mat1, in_mat2, out_mat_ocv, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::div(in1, in2, dtype);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class DivCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(DivCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::divide(in_mat1, sc, out_mat_ocv, 1.0, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::divC(in1, sc1, 1.0, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class DivRCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(DivRCPerfTest, TestPerformance)
+    {
+      Size sz = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      initMatsRandU(type, sz, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::divide(sc, in_mat1, out_mat_ocv, 1.0, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::divRC(sc1, in1, 1.0, dtype);
+      cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MaskPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(MaskPerfTest, TestPerformance)
+    {
+      Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatrixRandU(type, sz_in, type, false);
+      in_mat2 = cv::Mat(sz_in, CV_8UC1);
+      cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+      in_mat2 = in_mat2 > 128;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      out_mat_ocv = cv::Mat::zeros(in_mat1.size(), in_mat1.type());
+      in_mat1.copyTo(out_mat_ocv, in_mat2);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in, m;
+      auto out = cv::gapi::mask(in, m);
+      cv::GComputation c(cv::GIn(in, m), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MeanPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(MeanPerfTest, TestPerformance)
+    {
+      Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatrixRandU(type, sz_in, false);
+      cv::Scalar out_norm;
+      cv::Scalar out_norm_ocv;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      out_norm_ocv = cv::mean(in_mat1);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::mean(in);
+      cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1), cv::gout(out_norm));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1), cv::gout(out_norm));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(out_norm[0], out_norm_ocv[0]);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Polar2CartPerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Polar2CartPerfTest, TestPerformance)
+    {
+      Size sz_in = GetParam();
+
+      initMatsRandU(CV_32FC1, sz_in, CV_32FC1, false);
+      cv::Mat out_mat2;
+      cv::Mat out_mat_ocv2;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::polarToCart(in_mat1, in_mat2, out_mat_ocv, out_mat_ocv2);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out1, out2;
+      std::tie(out1, out2) = cv::gapi::polarToCart(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out1, out2));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Cart2PolarPerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Cart2PolarPerfTest, TestPerformance)
+    {
+      Size sz_in = GetParam();
+
+      initMatsRandU(CV_32FC1, sz_in, CV_32FC1, false);
+      cv::Mat out_mat2(sz_in, CV_32FC1);
+      cv::Mat out_mat_ocv2(sz_in, CV_32FC1);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::cartToPolar(in_mat1, in_mat2, out_mat_ocv, out_mat_ocv2);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out1, out2;
+      std::tie(out1, out2) = cv::gapi::cartToPolar(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out1, out2));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class CmpPerfTest : public TestPerfParams<tuple<CmpTypes, cv::Size, MatType>> {};
+    PERF_TEST_P_(CmpPerfTest, TestPerformance)
+    {
+      CmpTypes opType = get<0>(GetParam());
+      cv::Size sz = get<1>(GetParam());
+      MatType type = get<2>(GetParam());
+
+      initMatsRandU(type, sz, CV_8U, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::compare(in_mat1, in_mat2, out_mat_ocv, opType);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      switch(opType)
+      {
+      case CMP_EQ: out = cv::gapi::cmpEQ(in1, in2); break;
+      case CMP_GT: out = cv::gapi::cmpGT(in1, in2); break;
+      case CMP_GE: out = cv::gapi::cmpGE(in1, in2); break;
+      case CMP_LT: out = cv::gapi::cmpLT(in1, in2); break;
+      case CMP_LE: out = cv::gapi::cmpLE(in1, in2); break;
+      case CMP_NE: out = cv::gapi::cmpNE(in1, in2); break;
+      default: FAIL() << "no such compare operation type for two matrices!";
+      }
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class CmpWithScalarPerfTest : public TestPerfParams<tuple<CmpTypes, cv::Size, MatType>> {};
+    PERF_TEST_P_(CmpWithScalarPerfTest, TestPerformance)
+    {
+      CmpTypes opType = get<0>(GetParam());
+      cv::Size sz = get<1>(GetParam());
+      MatType type = get<2>(GetParam());
+
+      initMatsRandU(type, sz, CV_8U, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::compare(in_mat1, sc, out_mat_ocv, opType);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar in2;
+      switch(opType)
+      {
+      case CMP_EQ: out = cv::gapi::cmpEQ(in1, in2); break;
+      case CMP_GT: out = cv::gapi::cmpGT(in1, in2); break;
+      case CMP_GE: out = cv::gapi::cmpGE(in1, in2); break;
+      case CMP_LT: out = cv::gapi::cmpLT(in1, in2); break;
+      case CMP_LE: out = cv::gapi::cmpLE(in1, in2); break;
+      case CMP_NE: out = cv::gapi::cmpNE(in1, in2); break;
+      default: FAIL() << "no such compare operation type for matrix and scalar!";
+      }
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class BitwisePerfTest : public TestPerfParams<tuple<bitwiseOp, cv::Size, MatType>> {};
+    PERF_TEST_P_(BitwisePerfTest, TestPerformance)
+    {
+      bitwiseOp opType = get<0>(GetParam());
+      cv::Size sz = get<1>(GetParam());
+      MatType type = get<2>(GetParam());
+
+      initMatsRandU(type, sz, type, false);
+
+      // G-API code & corresponding OpenCV code ////////////////////////////////
+      cv::GMat in1, in2, out;
+      switch(opType)
+      {
+          case AND:
+          {
+              out = cv::gapi::bitwise_and(in1, in2);
+              cv::bitwise_and(in_mat1, in_mat2, out_mat_ocv);
+              break;
+          }
+          case OR:
+          {
+              out = cv::gapi::bitwise_or(in1, in2);
+              cv::bitwise_or(in_mat1, in_mat2, out_mat_ocv);
+              break;
+          }
+          case XOR:
+          {
+              out = cv::gapi::bitwise_xor(in1, in2);
+              cv::bitwise_xor(in_mat1, in_mat2, out_mat_ocv);
+              break;
+          }
+          default:
+          {
+              FAIL() << "no such bitwise operation type!";
+          }
+      }
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class BitwiseNotPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(BitwiseNotPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatrixRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::bitwise_not(in_mat1, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in, out;
+      out = cv::gapi::bitwise_not(in);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class SelectPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(SelectPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatsRandU(type, sz_in, type, false);
+      cv::Mat in_mask(sz_in, CV_8UC1);
+      cv::randu(in_mask, cv::Scalar::all(0), cv::Scalar::all(255));
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      in_mat2.copyTo(out_mat_ocv);
+      in_mat1.copyTo(out_mat_ocv, in_mask);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, in3, out;
+      out = cv::gapi::select(in1, in2, in3);
+      cv::GComputation c(GIn(in1, in2, in3), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MinPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(MinPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatsRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::min(in_mat1, in_mat2, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::min(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class MaxPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(MaxPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatsRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::max(in_mat1, in_mat2, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::max(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class AbsDiffPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(AbsDiffPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatsRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::absdiff(in_mat1, in_mat2, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, out;
+      out = cv::gapi::absDiff(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class AbsDiffCPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(AbsDiffCPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatsRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::absdiff(in_mat1, sc, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar sc1;
+      out = cv::gapi::absDiffC(in1, sc1);
+      cv::GComputation c(cv::GIn(in1, sc1), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class SumPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(SumPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatrixRandU(type, sz_in, false);
+      cv::Scalar out_sum;
+      cv::Scalar out_sum_ocv;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      out_sum_ocv = cv::sum(in_mat1);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::sum(in);
+      cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1), cv::gout(out_sum));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1), cv::gout(out_sum));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(out_sum[0], out_sum_ocv[0]);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class AddWeightedPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(AddWeightedPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int dtype = get<2>(GetParam());
+
+      auto& rng = cv::theRNG();
+      double alpha = rng.uniform(0.0, 1.0);
+      double beta = rng.uniform(0.0, 1.0);
+      double gamma = rng.uniform(0.0, 1.0);
+      initMatsRandU(type, sz_in, dtype, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::addWeighted(in_mat1, alpha, in_mat2, beta, gamma, out_mat_ocv, dtype);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2;
+      auto out = cv::gapi::addWeighted(in1, alpha, in2, beta, gamma, dtype);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class NormPerfTest : public TestPerfParams<tuple<NormTypes, cv::Size, MatType>> {};
+    PERF_TEST_P_(NormPerfTest, TestPerformance)
+    {
+      NormTypes opType = get<0>(GetParam());
+      cv::Size sz = get<1>(GetParam());
+      MatType type = get<2>(GetParam());
+
+      initMatrixRandU(type, sz, type, false);
+      cv::Scalar out_norm;
+      cv::Scalar out_norm_ocv;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      out_norm_ocv = cv::norm(in_mat1, opType);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1;
+      cv::GScalar out;
+      switch(opType)
+      {
+          case NORM_L1: out = cv::gapi::normL1(in1); break;
+          case NORM_L2: out = cv::gapi::normL2(in1); break;
+          case NORM_INF: out = cv::gapi::normInf(in1); break;
+          default: FAIL() << "no such norm operation type!";
+      }
+      cv::GComputation c(GIn(in1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1), gout(out_norm));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1), gout(out_norm));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(out_norm[0], out_norm_ocv[0]);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class IntegralPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(IntegralPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      MatType type_out = (type == CV_8U) ? CV_32SC1 : CV_64FC1;
+
+
+      in_mat1 = cv::Mat(sz_in, type);
+      cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+
+      cv::Size sz_out = cv::Size(sz_in.width + 1, sz_in.height + 1);
+      cv::Mat out_mat1(sz_out, type_out);
+      cv::Mat out_mat_ocv1(sz_out, type_out);
+
+      cv::Mat out_mat2(sz_out, CV_64FC1);
+      cv::Mat out_mat_ocv2(sz_out, CV_64FC1);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::integral(in_mat1, out_mat_ocv1, out_mat_ocv2);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out1, out2;
+      std::tie(out1, out2)  = cv::gapi::integral(in1, type_out, CV_64FC1);
+      cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv1 != out_mat1));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ThresholdPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(ThresholdPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int tt = get<2>(GetParam());
+
+      auto& rng = cv::theRNG();
+      cv::Scalar thr = cv::Scalar(rng(50),rng(50),rng(50),rng(50));
+      cv::Scalar maxval = cv::Scalar(50 + rng(50),50 + rng(50),50 + rng(50),50 + rng(50));
+      initMatrixRandU(type, sz_in, type, false);
+      cv::Scalar out_scalar;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::threshold(in_mat1, out_mat_ocv, thr.val[0], maxval.val[0], tt);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar th1, mv1;
+      out = cv::gapi::threshold(in1, th1, mv1, tt);
+      cv::GComputation c(GIn(in1, th1, mv1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ThresholdOTPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(ThresholdOTPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int tt = get<2>(GetParam());
+
+      auto& rng = cv::theRNG();
+      cv::Scalar maxval = cv::Scalar(50 + rng(50),50 + rng(50),50 + rng(50),50 + rng(50));
+      initMatrixRandU(type, sz_in, type, false);
+      cv::Scalar out_gapi_scalar;
+      double ocv_res;
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      ocv_res = cv::threshold(in_mat1, out_mat_ocv, maxval.val[0], maxval.val[0], tt);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out;
+      cv::GScalar mv1, scout;
+      std::tie<cv::GMat, cv::GScalar>(out, scout) = cv::gapi::threshold(in1, mv1, tt);
+      cv::GComputation c(cv::GIn(in1, mv1), cv::GOut(out, scout));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+      EXPECT_EQ(ocv_res, out_gapi_scalar.val[0]);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class InRangePerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(InRangePerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      auto& rng = cv::theRNG();
+      cv::Scalar thrLow = cv::Scalar(rng(100),rng(100),rng(100),rng(100));
+      cv::Scalar thrUp = cv::Scalar(100 + rng(100),100 + rng(100),100 + rng(100),100 + rng(100));
+      initMatrixRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::inRange(in_mat1, thrLow, thrUp, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1;
+      cv::GScalar th1, mv1;
+      auto out = cv::gapi::inRange(in1, th1, mv1);
+      cv::GComputation c(GIn(in1, th1, mv1), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Split3PerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Split3PerfTest, TestPerformance)
+    {
+      cv::Size sz_in = GetParam();
+
+      initMatrixRandU(CV_8UC3, sz_in, CV_8UC1);
+      cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      std::vector<cv::Mat> out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3};
+      cv::split(in_mat1, out_mats_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out1, out2, out3;
+      std::tie(out1, out2, out3)  = cv::gapi::split3(in1);
+      cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv  != out_mat_gapi));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv3 != out_mat3));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Split4PerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Split4PerfTest, TestPerformance)
+    {
+      cv::Size sz_in = GetParam();
+
+      initMatrixRandU(CV_8UC4, sz_in, CV_8UC1);
+      cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat4 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1);
+      cv::Mat out_mat_ocv4 = cv::Mat(sz_in, CV_8UC1);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      std::vector<cv::Mat> out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3, out_mat_ocv4};
+      cv::split(in_mat1, out_mats_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, out1, out2, out3, out4;
+      std::tie(out1, out2, out3, out4)  = cv::gapi::split4(in1);
+      cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3, out4));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv  != out_mat_gapi));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv3 != out_mat3));
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv4 != out_mat4));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Merge3PerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Merge3PerfTest, TestPerformance)
+    {
+      cv::Size sz_in = GetParam();
+
+      initMatsRandU(CV_8UC1, sz_in, CV_8UC3);
+      cv::Mat in_mat3(sz_in,  CV_8UC1);
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+      cv::randn(in_mat3, mean, stddev);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      std::vector<cv::Mat> in_mats_ocv = {in_mat1, in_mat2, in_mat3};
+      cv::merge(in_mats_ocv, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, in3;
+      auto out = cv::gapi::merge3(in1, in2, in3);
+      cv::GComputation c(cv::GIn(in1, in2, in3), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class Merge4PerfTest : public TestPerfParams<cv::Size> {};
+    PERF_TEST_P_(Merge4PerfTest, TestPerformance)
+    {
+      cv::Size sz_in = GetParam();
+
+      initMatsRandU(CV_8UC1, sz_in, CV_8UC3);
+      cv::Mat in_mat3(sz_in,  CV_8UC1);
+      cv::Mat in_mat4(sz_in,  CV_8UC1);
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+      cv::randn(in_mat3, mean, stddev);
+      cv::randn(in_mat4, mean, stddev);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      std::vector<cv::Mat> in_mats_ocv = {in_mat1, in_mat2, in_mat3, in_mat4};
+      cv::merge(in_mats_ocv, out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2, in3, in4;
+      auto out = cv::gapi::merge4(in1, in2, in3, in4);
+      cv::GComputation c(cv::GIn(in1, in2, in3, in4), cv::GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class RemapPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(RemapPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      initMatrixRandU(type, sz_in, type, false);
+      cv::Mat in_map1(sz_in,  CV_16SC2);
+      cv::Mat in_map2 = cv::Mat();
+      cv::randu(in_map1, cv::Scalar::all(0), cv::Scalar::all(255));
+      cv::Scalar bv = cv::Scalar();
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::remap(in_mat1, out_mat_ocv, in_map1, in_map2, cv::INTER_NEAREST, cv::BORDER_REPLICATE, bv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1;
+      auto out = cv::gapi::remap(in1, in_map1, in_map2, cv::INTER_NEAREST,  cv::BORDER_REPLICATE, bv);
+      cv::GComputation c(in1, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class FlipPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int>> {};
+    PERF_TEST_P_(FlipPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      int flipCode =  get<2>(GetParam());
+
+      initMatrixRandU(type, sz_in, type, false);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::flip(in_mat1, out_mat_ocv, flipCode);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::flip(in, flipCode);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class CropPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::Rect>> {};
+    PERF_TEST_P_(CropPerfTest, TestPerformance)
+    {
+      cv::Size sz_in = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+      cv::Rect rect_to =  get<2>(GetParam());
+
+      initMatrixRandU(type, sz_in, type, false);
+      cv::Size sz_out = cv::Size(rect_to.width, rect_to.height);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::Mat(in_mat1, rect_to).copyTo(out_mat_ocv);
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::crop(in, rect_to);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_out);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ConcatHorPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(ConcatHorPerfTest, TestPerformance)
+    {
+      cv::Size sz_out = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      int wpart = sz_out.width / 4;
+
+      cv::Size sz_in1 = cv::Size(wpart, sz_out.height);
+      cv::Size sz_in2 = cv::Size(sz_out.width - wpart, sz_out.height);
+
+      in_mat1 = cv::Mat(sz_in1, type);
+      in_mat2 = cv::Mat(sz_in2, type);
+
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+
+      cv::randn(in_mat1, mean, stddev);
+      cv::randn(in_mat2, mean, stddev);
+
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::hconcat(in_mat1, in_mat2, out_mat_ocv );
+
+      // G-API code ////////////////////////////////////////////////////////////
+      cv::GMat in1, in2;
+      auto out = cv::gapi::concatHor(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ConcatHorVecPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(ConcatHorVecPerfTest, TestPerformance)
+    {
+      cv::Size sz_out = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      int wpart1 = sz_out.width / 3;
+      int wpart2 = sz_out.width / 2;
+
+      cv::Size sz_in1 = cv::Size(wpart1, sz_out.height);
+      cv::Size sz_in2 = cv::Size(wpart2, sz_out.height);
+      cv::Size sz_in3 = cv::Size(sz_out.width - wpart1 - wpart2, sz_out.height);
+
+      in_mat1 = cv::Mat(sz_in1, type);
+      in_mat2 = cv::Mat(sz_in2, type);
+      cv::Mat in_mat3(sz_in3, type);
+
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+
+      cv::randn(in_mat1, mean, stddev);
+      cv::randn(in_mat2, mean, stddev);
+      cv::randn(in_mat3, mean, stddev);
+
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      std::vector <cv::Mat> cvmats = {in_mat1, in_mat2, in_mat3};
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::hconcat(cvmats, out_mat_ocv );
+
+      // G-API code //////////////////////////////////////////////////////////////
+      std::vector <cv::GMat> mats(3);
+      auto out = cv::gapi::concatHor(mats);
+      cv::GComputation c({mats[0], mats[1], mats[2]}, {out});
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ConcatVertPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(ConcatVertPerfTest, TestPerformance)
+    {
+      cv::Size sz_out = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      int hpart = sz_out.height * 2/3;
+
+      cv::Size sz_in1 = cv::Size(sz_out.width, hpart);
+      cv::Size sz_in2 = cv::Size(sz_out.width, sz_out.height - hpart);
+
+      in_mat1 = cv::Mat(sz_in1, type);
+      in_mat2 = cv::Mat(sz_in2, type);
+
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+
+      cv::randn(in_mat1, mean, stddev);
+      cv::randn(in_mat2, mean, stddev);
+
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::vconcat(in_mat1, in_mat2, out_mat_ocv );
+
+      // G-API code //////////////////////////////////////////////////////////////
+      cv::GMat in1, in2;
+      auto out = cv::gapi::concatVert(in1, in2);
+      cv::GComputation c(GIn(in1, in2), GOut(out));
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ConcatVertVecPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+    PERF_TEST_P_(ConcatVertVecPerfTest, TestPerformance)
+    {
+      cv::Size sz_out = get<0>(GetParam());
+      MatType type = get<1>(GetParam());
+
+      int hpart1 = sz_out.height * 2/5;
+      int hpart2 = sz_out.height / 5;
+
+      cv::Size sz_in1 = cv::Size(sz_out.width, hpart1);
+      cv::Size sz_in2 = cv::Size(sz_out.width, hpart2);
+      cv::Size sz_in3 = cv::Size(sz_out.width, sz_out.height - hpart1 - hpart2);
+
+      in_mat1 = cv::Mat(sz_in1, type);
+      in_mat2 = cv::Mat(sz_in2, type);
+      cv::Mat in_mat3 (sz_in3, type);
+
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+
+      cv::randn(in_mat1, mean, stddev);
+      cv::randn(in_mat2, mean, stddev);
+      cv::randn(in_mat3, mean, stddev);
+
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      std::vector <cv::Mat> cvmats = {in_mat1, in_mat2, in_mat3};
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::vconcat(cvmats, out_mat_ocv );
+
+      // G-API code //////////////////////////////////////////////////////////////
+      std::vector <cv::GMat> mats(3);
+      auto out = cv::gapi::concatVert(mats);
+      cv::GComputation c({mats[0], mats[1], mats[2]}, {out});
+
+      // Warm-up graph engine:
+      c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi));
+
+      TEST_CYCLE()
+      {
+        c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi));
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class LUTPerfTest : public TestPerfParams<tuple<MatType, MatType, cv::Size>> {};
+    PERF_TEST_P_(LUTPerfTest, TestPerformance)
+    {
+      MatType type_mat = get<0>(GetParam());
+      MatType type_lut = get<1>(GetParam());
+      MatType type_out = CV_MAKETYPE(CV_MAT_DEPTH(type_lut), CV_MAT_CN(type_mat));
+      cv::Size sz_in = get<2>(GetParam());
+
+      initMatrixRandU(type_mat, sz_in, type_out);
+      cv::Size sz_lut = cv::Size(1, 256);
+      cv::Mat in_lut (sz_lut, type_lut);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::LUT(in_mat1, in_lut, out_mat_ocv);
+
+      // G-API code //////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::LUT(in, in_lut);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ConvertToPerfTest : public TestPerfParams<tuple<MatType, int, cv::Size>> {};
+    PERF_TEST_P_(ConvertToPerfTest, TestPerformance)
+    {
+      MatType type_mat = get<0>(GetParam());
+      int depth_to = get<1>(GetParam());
+      cv::Size sz_in = get<2>(GetParam());
+      MatType type_out = CV_MAKETYPE(depth_to, CV_MAT_CN(type_mat));
+
+      initMatrixRandU(type_mat, sz_in, type_out);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      in_mat1.convertTo(out_mat_ocv, depth_to);
+
+      // G-API code //////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::convertTo(in, depth_to);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+      EXPECT_EQ(out_mat_gapi.size(), sz_in);
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ResizePerfTest : public TestPerfParams<tuple<MatType, int, cv::Size, cv::Size, double>> {};
+    PERF_TEST_P_(ResizePerfTest, TestPerformance)
+    {
+      MatType type = get<0>(GetParam());
+      int interp = get<1>(GetParam());
+      cv::Size sz_in = get<2>(GetParam());
+      cv::Size sz_out = get<3>(GetParam());
+      double tolerance = get<4>(GetParam());
+
+      in_mat1 = cv::Mat(sz_in, type );
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+      cv::randn(in_mat1, mean, stddev);
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::resize(in_mat1, out_mat_ocv, sz_out, 0.0, 0.0, interp);
+
+      // G-API code //////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::resize(in, sz_out, 0.0, 0.0, interp);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      cv::Mat absDiff;
+      cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+      EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+    class ResizeFxFyPerfTest : public TestPerfParams<tuple<MatType, int, cv::Size, double, double, double>> {};
+    PERF_TEST_P_(ResizeFxFyPerfTest, TestPerformance)
+    {
+      MatType type = get<0>(GetParam());
+      int interp = get<1>(GetParam());
+      cv::Size sz_in = get<2>(GetParam());
+      double fx = get<3>(GetParam());
+      double fy = get<4>(GetParam());
+      double tolerance = get<5>(GetParam());
+
+      in_mat1 = cv::Mat(sz_in, type );
+      cv::Scalar mean = cv::Scalar::all(127);
+      cv::Scalar stddev = cv::Scalar::all(40.f);
+      cv::randn(in_mat1, mean, stddev);
+      cv::Size sz_out = cv::Size(saturate_cast<int>(sz_in.width *fx),saturate_cast<int>(sz_in.height*fy));
+      out_mat_gapi = cv::Mat(sz_out, type);
+      out_mat_ocv = cv::Mat(sz_out, type);
+
+      // OpenCV code ///////////////////////////////////////////////////////////
+      cv::resize(in_mat1, out_mat_ocv, sz_out, fx, fy, interp);
+
+      // G-API code //////////////////////////////////////////////////////////////
+      cv::GMat in;
+      auto out = cv::gapi::resize(in, sz_out, fx, fy, interp);
+      cv::GComputation c(in, out);
+
+      // Warm-up graph engine:
+      c.apply(in_mat1, out_mat_gapi);
+
+      TEST_CYCLE()
+      {
+        c.apply(in_mat1, out_mat_gapi);
+      }
+
+      // Comparison ////////////////////////////////////////////////////////////
+      cv::Mat absDiff;
+      cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+      EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+
+      SANITY_CHECK_NOTHING();
+    }
+
+//------------------------------------------------------------------------------
+
+}
diff --git a/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp b/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp
new file mode 100644 (file)
index 0000000..8ba25ed
--- /dev/null
@@ -0,0 +1,882 @@
+// 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
+
+
+#include "../../test/common/gapi_tests_common.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+
+namespace opencv_test
+{
+
+  using namespace perf;
+
+//------------------------------------------------------------------------------
+
+class SepFilterPerfTest : public TestPerfParams<tuple<MatType, int, cv::Size, int>> {};
+PERF_TEST_P_(SepFilterPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0, dtype = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz, dtype) = GetParam();
+
+    cv::Mat kernelX(kernSize, 1, CV_32F);
+    cv::Mat kernelY(kernSize, 1, CV_32F);
+    randu(kernelX, -1, 1);
+    randu(kernelY, -1, 1);
+    initMatsRandN(type, sz, dtype, false);
+
+    cv::Point anchor = cv::Point(-1, -1);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::sepFilter2D(in_mat1, out_mat_ocv, dtype, kernelX, kernelY );
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::sepFilter(in, dtype, kernelX, kernelY, anchor, cv::Scalar() );
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+      c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+}
+
+//------------------------------------------------------------------------------
+
+class Filter2DPerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int,int>> {};
+PERF_TEST_P_(Filter2DPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0, borderType = 0, dtype = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz, borderType, dtype) = GetParam();
+
+    initMatsRandN(type, sz, dtype, false);
+
+    cv::Point anchor = {-1, -1};
+    double delta = 0;
+
+    cv::Mat kernel = cv::Mat(kernSize, kernSize, CV_32FC1 );
+    cv::Scalar kernMean = cv::Scalar::all(1.0);
+    cv::Scalar kernStddev = cv::Scalar::all(2.0/3);
+    randn(kernel, kernMean, kernStddev);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::filter2D(in_mat1, out_mat_ocv, dtype, kernel, anchor, delta, borderType);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::filter2D(in, dtype, kernel, anchor, delta, borderType);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class BoxFilterPerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int,int,double>> {};
+PERF_TEST_P_(BoxFilterPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int filterSize = 0, borderType = 0, dtype = 0;
+    cv::Size sz;
+    double tolerance = 0.0;
+    std::tie(type, filterSize, sz, borderType, dtype, tolerance) = GetParam();
+
+    initMatsRandN(type, sz, dtype, false);
+
+    cv::Point anchor = {-1, -1};
+    bool normalize = true;
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::boxFilter(in_mat1, out_mat_ocv, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::boxFilter(in, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        cv::Mat absDiff;
+        cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+        EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class BlurPerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int,double>> {};
+PERF_TEST_P_(BlurPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int filterSize = 0, borderType = 0;
+    cv::Size sz;
+    double tolerance = 0.0;
+    std::tie(type, filterSize, sz, borderType, tolerance) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    cv::Point anchor = {-1, -1};
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::blur(in_mat1, out_mat_ocv, cv::Size(filterSize, filterSize), anchor, borderType);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::blur(in, cv::Size(filterSize, filterSize), anchor, borderType);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        cv::Mat absDiff;
+        cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+        EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class GaussianBlurPerfTest : public TestPerfParams<tuple<MatType, int, cv::Size>> {};
+PERF_TEST_P_(GaussianBlurPerfTest, TestPerformance)
+{
+  MatType type = 0;
+  int kernSize = 0;
+  cv::Size sz;
+  std::tie(type, kernSize, sz) = GetParam();
+
+  cv::Size kSize = cv::Size(kernSize, kernSize);
+  auto& rng = cv::theRNG();
+  double sigmaX = rng();
+  initMatsRandN(type, sz, type, false);
+
+  // OpenCV code ///////////////////////////////////////////////////////////
+  cv::GaussianBlur(in_mat1, out_mat_ocv, kSize, sigmaX);
+
+  // G-API code //////////////////////////////////////////////////////////////
+  cv::GMat in;
+  auto out = cv::gapi::gaussianBlur(in, kSize, sigmaX);
+  cv::GComputation c(in, out);
+
+  // Warm-up graph engine:
+  c.apply(in_mat1, out_mat_gapi);
+
+  TEST_CYCLE()
+  {
+    c.apply(in_mat1, out_mat_gapi);
+  }
+
+  // Comparison ////////////////////////////////////////////////////////////
+  EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+  EXPECT_EQ(out_mat_gapi.size(), sz);
+
+  SANITY_CHECK_NOTHING();
+}
+
+//------------------------------------------------------------------------------
+
+class MedianBlurPerfTest : public TestPerfParams<tuple<MatType,int,cv::Size>> {};
+PERF_TEST_P_(MedianBlurPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::medianBlur(in_mat1, out_mat_ocv, kernSize);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::medianBlur(in, kernSize);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class ErodePerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int>> {};
+PERF_TEST_P_(ErodePerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0, kernType = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz, kernType) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::erode(in_mat1, out_mat_ocv, kernel);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::erode(in, kernel);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class Erode3x3PerfTest : public TestPerfParams<tuple<MatType,cv::Size,int>> {};
+PERF_TEST_P_(Erode3x3PerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int numIters = 0;
+    cv::Size sz;
+    std::tie(type, sz, numIters) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3, 3));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::erode(in_mat1, out_mat_ocv, kernel, cv::Point(-1, -1), numIters);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::erode3x3(in, numIters);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class DilatePerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int>> {};
+PERF_TEST_P_(DilatePerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0, kernType = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz, kernType) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::dilate(in_mat1, out_mat_ocv, kernel);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::dilate(in, kernel);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class Dilate3x3PerfTest : public TestPerfParams<tuple<MatType,cv::Size,int>> {};
+PERF_TEST_P_(Dilate3x3PerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int numIters = 0;
+    cv::Size sz;
+    std::tie(type, sz, numIters) = GetParam();
+
+    initMatsRandN(type, sz, type, false);
+
+    cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3, 3));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::dilate(in_mat1, out_mat_ocv, kernel, cv::Point(-1,-1), numIters);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::dilate3x3(in, numIters);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class SobelPerfTest : public TestPerfParams<tuple<MatType,int,cv::Size,int,int,int>> {};
+PERF_TEST_P_(SobelPerfTest, TestPerformance)
+{
+    MatType type = 0;
+    int kernSize = 0, dtype = 0, dx = 0, dy = 0;
+    cv::Size sz;
+    std::tie(type, kernSize, sz, dtype, dx, dy) = GetParam();
+
+    initMatsRandN(type, sz, dtype, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Sobel(in_mat1, out_mat_ocv, dtype, dx, dy, kernSize);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::sobel(in, dtype, dx, dy, kernSize );
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class CannyPerfTest : public TestPerfParams<tuple<MatType,cv::Size,double,double,int,bool>> {};
+PERF_TEST_P_(CannyPerfTest, TestPerformance)
+{
+    MatType type;
+    int apSize = 0;
+    double thrLow = 0.0, thrUp = 0.0;
+    cv::Size sz;
+    bool l2gr = false;
+    std::tie(type, sz, thrLow, thrUp, apSize, l2gr) = GetParam();
+
+    initMatsRandN(type, sz, CV_8UC1, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Canny(in_mat1, out_mat_ocv, thrLow, thrUp, apSize, l2gr);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::Canny(in, thrLow, thrUp, apSize, l2gr);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class EqHistPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(EqHistPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC1, sz, CV_8UC1, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::equalizeHist(in_mat1, out_mat_ocv);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::equalizeHist(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class RGB2GrayPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(RGB2GrayPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC1, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2GRAY);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2Gray(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class BGR2GrayPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(BGR2GrayPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC1, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2GRAY);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::BGR2Gray(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class RGB2YUVPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(RGB2YUVPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2YUV);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2YUV(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class YUV2RGBPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(YUV2RGBPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2RGB);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::YUV2RGB(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class RGB2LabPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(RGB2LabPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2Lab);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2Lab(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class BGR2LUVPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(BGR2LUVPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2Luv);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::BGR2LUV(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class LUV2BGRPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(LUV2BGRPerfTest, TestPerformance)
+{
+    cv::Size sz = GetParam();
+
+    initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_Luv2BGR);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::LUV2BGR(in);
+    cv::GComputation c(in, out);
+
+    // Warm-up graph engine:
+    c.apply(in_mat1, out_mat_gapi);
+
+    TEST_CYCLE()
+    {
+        c.apply(in_mat1, out_mat_gapi);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+      EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+      EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+
+    SANITY_CHECK_NOTHING();
+
+}
+
+//------------------------------------------------------------------------------
+
+class BGR2YUVPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(BGR2YUVPerfTest, TestPerformance)
+{
+  cv::Size sz = GetParam();
+  initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+  cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2YUV);
+
+  cv::GMat in;
+  auto out = cv::gapi::BGR2YUV(in);
+  cv::GComputation c(in, out);
+
+  // Warm-up graph engine:
+  c.apply(in_mat1, out_mat_gapi);
+
+  TEST_CYCLE()
+  {
+    c.apply(in_mat1, out_mat_gapi);
+  }
+
+  EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+  EXPECT_EQ(out_mat_gapi.size(), sz);
+
+  SANITY_CHECK_NOTHING();
+}
+
+//------------------------------------------------------------------------------
+
+class YUV2BGRPerfTest : public TestPerfParams<cv::Size> {};
+PERF_TEST_P_(YUV2BGRPerfTest, TestPerformance)
+{
+  cv::Size sz = GetParam();
+  initMatsRandN(CV_8UC3, sz, CV_8UC3, false);
+
+  cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2BGR);
+
+  cv::GMat in;
+  auto out = cv::gapi::YUV2BGR(in);
+  cv::GComputation c(in, out);
+
+  // Warm-up graph engine:
+  c.apply(in_mat1, out_mat_gapi);
+
+  TEST_CYCLE()
+  {
+    c.apply(in_mat1, out_mat_gapi);
+  }
+
+  EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+  EXPECT_EQ(out_mat_gapi.size(), sz);
+
+  SANITY_CHECK_NOTHING();
+}
+
+//------------------------------------------------------------------------------
+
+}
diff --git a/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp b/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp
new file mode 100644 (file)
index 0000000..405d7f3
--- /dev/null
@@ -0,0 +1,219 @@
+// 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
+
+
+#include "../common/gapi_core_perf_tests.hpp"
+
+namespace opencv_test
+{
+
+  INSTANTIATE_TEST_CASE_P(AddPerfTestCPU, AddPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(AddCPerfTestCPU, AddCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(SubPerfTestCPU, SubPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(SubCPerfTestCPU, SubCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(SubRCPerfTestCPU, SubRCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(MulPerfTestCPU, MulPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(MulDoublePerfTestCPU, MulDoublePerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(MulCPerfTestCPU, MulCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(DivPerfTestCPU, DivPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(DivCPerfTestCPU, DivCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(DivRCPerfTestCPU, DivRCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(MaskPerfTestCPU, MaskPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_16UC1, CV_16SC1)));
+
+  INSTANTIATE_TEST_CASE_P(MeanPerfTestCPU, MeanPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(Polar2CartPerfTestCPU, Polar2CartPerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(Cart2PolarPerfTestCPU, Cart2PolarPerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(CmpPerfTestCPU, CmpPerfTest,
+                          Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(CmpWithScalarPerfTestCPU, CmpWithScalarPerfTest,
+                          Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(BitwisePerfTestCPU, BitwisePerfTest,
+                          Combine(Values(AND, OR, XOR),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1)));
+
+  INSTANTIATE_TEST_CASE_P(BitwiseNotPerfTestCPU, BitwiseNotPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(SelectPerfTestCPU, SelectPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(MinPerfTestCPU, MinPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(MaxPerfTestCPU, MaxPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(AbsDiffPerfTestCPU, AbsDiffPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(AbsDiffCPerfTestCPU, AbsDiffCPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(SumPerfTestCPU, SumPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(AddWeightedPerfTestCPU, AddWeightedPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values( -1, CV_8U, CV_16U, CV_32F )));
+
+  INSTANTIATE_TEST_CASE_P(NormPerfTestCPU, NormPerfTest,
+                          Combine(Values(NORM_INF, NORM_L1, NORM_L2),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(IntegralPerfTestCPU, IntegralPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(ThresholdPerfTestCPU, ThresholdPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV)));
+
+  INSTANTIATE_TEST_CASE_P(ThresholdPerfTestCPU, ThresholdOTPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1 ),
+                                  Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE)));
+
+  INSTANTIATE_TEST_CASE_P(InRangePerfTestCPU, InRangePerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1 )));
+
+  INSTANTIATE_TEST_CASE_P(Split3PerfTestCPU, Split3PerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(Split4PerfTestCPU, Split4PerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(Merge3PerfTestCPU, Merge3PerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(Merge4PerfTestCPU, Merge4PerfTest, Values( szSmall128, szVGA, sz720p, sz1080p ));
+
+  INSTANTIATE_TEST_CASE_P(RemapPerfTestCPU, RemapPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(FlipPerfTestCPU, FlipPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values(0,1,-1)));
+
+  INSTANTIATE_TEST_CASE_P(CropPerfTestCPU, CropPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                  Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50))));
+
+  INSTANTIATE_TEST_CASE_P(ConcatHorPerfTestCPU, ConcatHorPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(ConcatHorVecPerfTestCPU, ConcatHorVecPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(ConcatVertPerfTestCPU, ConcatVertPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(ConcatVertVecPerfTestCPU, ConcatVertVecPerfTest,
+                          Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 )));
+
+  INSTANTIATE_TEST_CASE_P(LUTPerfTestCPU, LUTPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3),
+                                  Values(CV_8UC1),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p )));
+
+  INSTANTIATE_TEST_CASE_P(LUTPerfTestCustomCPU, LUTPerfTest,
+                          Combine(Values(CV_8UC3),
+                                  Values(CV_8UC3),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p )));
+
+  INSTANTIATE_TEST_CASE_P(ConvertToPerfTestCPU, ConvertToPerfTest,
+                          Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_32FC1),
+                                  Values(CV_8U, CV_16U, CV_16S, CV_32F),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p )));
+
+  INSTANTIATE_TEST_CASE_P(ResizePerfTestCPU, ResizePerfTest,
+                          Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                  Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values(cv::Size(64,64),
+                                         cv::Size(30,30)),
+                                  Values(0.0)));
+
+  INSTANTIATE_TEST_CASE_P(ResizeFxFyPerfTestCPU, ResizeFxFyPerfTest,
+                          Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                  Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                  Values( szSmall128, szVGA, sz720p, sz1080p ),
+                                  Values(0.5, 0.1),
+                                  Values(0.5, 0.1),
+                                  Values(0.0)));
+}
diff --git a/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp b/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp
new file mode 100644 (file)
index 0000000..ba61af5
--- /dev/null
@@ -0,0 +1,119 @@
+// 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
+
+
+#include "../common/gapi_imgproc_perf_tests.hpp"
+
+namespace opencv_test
+{
+
+  INSTANTIATE_TEST_CASE_P(SepFilterPerfTestCPU_8U, SepFilterPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3),
+                                  Values(3),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(-1, CV_16S, CV_32F)));
+
+  INSTANTIATE_TEST_CASE_P(SepFilterPerfTestCPU_other, SepFilterPerfTest,
+                          Combine(Values(CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(-1, CV_32F)));
+
+  INSTANTIATE_TEST_CASE_P(Filter2DPerfTestCPU, Filter2DPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3, 4, 5, 7),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(cv::BORDER_DEFAULT),
+                                  Values(-1, CV_32F)));
+
+  INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestCPU, BoxFilterPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3,5),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(cv::BORDER_DEFAULT),
+                                  Values(-1, CV_32F),
+                                  Values(0.0)));
+
+  INSTANTIATE_TEST_CASE_P(BlurPerfTestCPU, BlurPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3, 5),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(cv::BORDER_DEFAULT),
+                                  Values(0.0)));
+
+  INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestCPU, GaussianBlurPerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3, 5),
+                                  Values(szVGA, sz720p, sz1080p)));
+
+   INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestCPU, MedianBlurPerfTest,
+                           Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                   Values(3, 5),
+                                   Values(szVGA, sz720p, sz1080p)));
+
+  INSTANTIATE_TEST_CASE_P(ErodePerfTestCPU, ErodePerfTest,
+                          Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                  Values(3, 5),
+                                  Values(szVGA, sz720p, sz1080p),
+                                  Values(cv::MorphShapes::MORPH_RECT,
+                                         cv::MorphShapes::MORPH_CROSS,
+                                         cv::MorphShapes::MORPH_ELLIPSE)));
+
+   INSTANTIATE_TEST_CASE_P(Erode3x3PerfTestCPU, Erode3x3PerfTest,
+                           Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                   Values(szVGA, sz720p, sz1080p),
+                                   Values(1,2,4)));
+
+   INSTANTIATE_TEST_CASE_P(DilatePerfTestCPU, DilatePerfTest,
+                           Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                   Values(3, 5),
+                                   Values(szVGA, sz720p, sz1080p),
+                                   Values(cv::MorphShapes::MORPH_RECT,
+                                          cv::MorphShapes::MORPH_CROSS,
+                                          cv::MorphShapes::MORPH_ELLIPSE)));
+
+    INSTANTIATE_TEST_CASE_P(Dilate3x3PerfTestCPU, Dilate3x3PerfTest,
+                            Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                    Values(szVGA, sz720p, sz1080p),
+                                    Values(1,2,4)));
+
+    INSTANTIATE_TEST_CASE_P(SobelPerfTestCPU, SobelPerfTest,
+                            Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                    Values(3, 5),
+                                    Values(szVGA, sz720p, sz1080p),
+                                    Values(-1, CV_32F),
+                                    Values(0, 1),
+                                    Values(1, 2)));
+
+    INSTANTIATE_TEST_CASE_P(CannyPerfTestCPU, CannyPerfTest,
+                            Combine(Values(CV_8UC1, CV_8UC3),
+                                    Values(szVGA, sz720p, sz1080p),
+                                    Values(3.0, 120.0),
+                                    Values(125.0, 240.0),
+                                    Values(3, 5),
+                                    Values(true, false)));
+
+    INSTANTIATE_TEST_CASE_P(EqHistPerfTestCPU, EqHistPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestCPU, RGB2GrayPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(BGR2GrayPerfTestCPU, BGR2GrayPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(RGB2YUVPerfTestCPU, RGB2YUVPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(YUV2RGBPerfTestCPU, YUV2RGBPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(RGB2LabPerfTestCPU, RGB2LabPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(BGR2LUVPerfTestCPU, BGR2LUVPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(LUV2BGRPerfTestCPU, LUV2BGRPerfTest,  Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(BGR2YUVPerfTestCPU, BGR2YUVPerfTest, Values(szVGA, sz720p, sz1080p));
+
+    INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestCPU, YUV2BGRPerfTest, Values(szVGA, sz720p, sz1080p));
+
+}
diff --git a/modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp b/modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp
new file mode 100644 (file)
index 0000000..ef532ab
--- /dev/null
@@ -0,0 +1,46 @@
+// 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
+
+
+#include "perf_precomp.hpp"
+#include "../../test/common/gapi_tests_common.hpp"
+#include "../../src/backends/fluid/gfluidcore.hpp"
+
+namespace opencv_test
+{
+using namespace perf;
+
+class CompilerPerfTest : public TestPerfParams<tuple<cv::Size, MatType>> {};
+PERF_TEST_P_(CompilerPerfTest, TestPerformance)
+{
+  const auto params = GetParam();
+  Size sz = get<0>(params);
+  MatType type = get<1>(params);
+
+  initMatsRandU(type, sz, type, false);
+
+  // G-API code ////////////////////////////////////////////////////////////
+  cv::GMat in;
+  auto splitted = cv::gapi::split3(in);
+  auto add1 = cv::gapi::addC({1}, std::get<0>(splitted));
+  auto add2 = cv::gapi::addC({2}, std::get<1>(splitted));
+  auto add3 = cv::gapi::addC({3}, std::get<2>(splitted));
+  auto out = cv::gapi::merge3(add1, add2, add3);
+
+  TEST_CYCLE()
+  {
+      cv::GComputation c(in, out);
+      c.apply(in_mat1, out_mat_gapi, cv::compile_args(cv::gapi::core::fluid::kernels()));
+  }
+
+  SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(CompilerPerfTest, CompilerPerfTest,
+                        Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
+                                Values(CV_8UC3)));
+
+} // namespace opencv_test
diff --git a/modules/gapi/perf/perf_main.cpp b/modules/gapi/perf/perf_main.cpp
new file mode 100644 (file)
index 0000000..8d6d77e
--- /dev/null
@@ -0,0 +1,11 @@
+// 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
+
+
+#include "perf_precomp.hpp"
+//#include "../test/test_precomp.hpp"
+
+CV_PERF_TEST_MAIN(gapi)
diff --git a/modules/gapi/perf/perf_precomp.hpp b/modules/gapi/perf/perf_precomp.hpp
new file mode 100644 (file)
index 0000000..c7e79ba
--- /dev/null
@@ -0,0 +1,21 @@
+// 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_PERF_PRECOMP_HPP__
+#define __OPENCV_GAPI_PERF_PRECOMP_HPP__
+
+#include <cstdint>
+#include <vector>
+
+#include "opencv2/ts.hpp"
+#include "opencv2/gapi.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+#include "opencv2/gapi/operators.hpp"
+
+#endif
diff --git a/modules/gapi/src/api/README.md b/modules/gapi/src/api/README.md
new file mode 100644 (file)
index 0000000..970f730
--- /dev/null
@@ -0,0 +1 @@
+This directory contains implementation of G-API frontend (public API classes).
\ No newline at end of file
diff --git a/modules/gapi/src/api/gapi_priv.cpp b/modules/gapi/src/api/gapi_priv.cpp
new file mode 100644 (file)
index 0000000..38f17e9
--- /dev/null
@@ -0,0 +1,43 @@
+// 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
+
+
+#include <ade/util/assert.hpp>
+
+#include "api/gapi_priv.hpp"
+#include "api/gnode_priv.hpp"
+
+cv::GOrigin::GOrigin(GShape s,
+                    const cv::GNode& n,
+                    std::size_t p,
+                    const cv::gimpl::HostCtor c)
+    : shape(s), node(n), port(p), ctor(c)
+{
+}
+
+cv::GOrigin::GOrigin(GShape s, cv::gimpl::ConstVal v)
+    : shape(s), node(cv::GNode::Const()), value(v), port(INVALID_PORT)
+{
+}
+
+bool cv::detail::GOriginCmp::operator() (const cv::GOrigin &lhs,
+                                         const cv::GOrigin &rhs) const
+{
+    const GNode::Priv* lhs_p = &lhs.node.priv();
+    const GNode::Priv* rhs_p = &rhs.node.priv();
+    if (lhs_p == rhs_p)
+    {
+        if (lhs.port == rhs.port)
+        {
+            // A data Origin is uniquely identified by {node/port} pair.
+            // The situation when there're two Origins with same {node/port}s
+            // but with different shapes (data formats) is illegal!
+            GAPI_Assert(lhs.shape == rhs.shape);
+        }
+        return lhs.port < rhs.port;
+    }
+    else return lhs_p < rhs_p;
+}
diff --git a/modules/gapi/src/api/gapi_priv.hpp b/modules/gapi/src/api/gapi_priv.hpp
new file mode 100644 (file)
index 0000000..edab0a0
--- /dev/null
@@ -0,0 +1,77 @@
+// 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_PRIV_HPP
+#define OPENCV_GAPI_PRIV_HPP
+
+#include <set>   // set
+#include <map>   // map
+#include <limits>
+
+#include "opencv2/gapi/util/variant.hpp"   // variant
+#include "opencv2/gapi/garray.hpp"         // ConstructVec
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/gcommon.hpp"
+
+#include "opencv2/gapi/opencv_includes.hpp"
+
+#include "api/gnode.hpp"
+
+namespace cv
+{
+
+namespace gimpl
+{
+    // Union type for various user-defined type constructors (GArray<T>, etc)
+    // FIXME: Replace construct-only API with a more generic one
+    //    (probably with bits of introspection)
+    // Not required for non-user-defined types (GMat, GScalar, etc)
+    using HostCtor = util::variant
+        < util::monostate
+        , detail::ConstructVec
+        >;
+
+    using ConstVal = util::variant
+        < util::monostate
+        , cv::gapi::own::Scalar
+        >;
+}
+
+// TODO namespace gimpl?
+
+struct GOrigin
+{
+    static constexpr const std::size_t INVALID_PORT = std::numeric_limits<std::size_t>::max();
+
+    GOrigin(GShape s,
+            const GNode& n,
+            std::size_t p = INVALID_PORT,
+            const gimpl::HostCtor h = {});
+    GOrigin(GShape s, gimpl::ConstVal value);
+
+    const GShape          shape;           // Shape of a produced object
+    const GNode           node;            // a GNode which produces an object
+    const gimpl::ConstVal value;           // Node can have initial constant value, now only scalar is supported
+    const std::size_t     port;            // GNode's output number; FIXME: "= max_size" in C++14
+    gimpl::HostCtor       ctor;            // FIXME: replace with an interface?
+};
+
+namespace detail
+{
+    struct GOriginCmp
+    {
+        bool operator() (const GOrigin &lhs, const GOrigin &rhs) const;
+    };
+} // namespace cv::details
+
+// TODO introduce a hash on GOrigin and define this via unordered_ ?
+using GOriginSet = std::set<GOrigin, detail::GOriginCmp>;
+template<typename T> using GOriginMap = std::map<GOrigin, T, detail::GOriginCmp>;
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_PRIV_HPP
diff --git a/modules/gapi/src/api/garray.cpp b/modules/gapi/src/api/garray.cpp
new file mode 100644 (file)
index 0000000..e2a346c
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
+
+
+#include "opencv2/gapi/garray.hpp"
+#include "api/gapi_priv.hpp" // GOrigin
+
+// cv::detail::GArrayU public implementation ///////////////////////////////////
+cv::detail::GArrayU::GArrayU()
+    : m_priv(new GOrigin(GShape::GARRAY, cv::GNode::Param()))
+{
+}
+
+cv::detail::GArrayU::GArrayU(const GNode &n, std::size_t out)
+    : m_priv(new GOrigin(GShape::GARRAY, n, out))
+{
+}
+
+cv::GOrigin& cv::detail::GArrayU::priv()
+{
+    return *m_priv;
+}
+
+const cv::GOrigin& cv::detail::GArrayU::priv() const
+{
+    return *m_priv;
+}
+
+void cv::detail::GArrayU::setConstructFcn(ConstructVec &&cv)
+{
+    m_priv->ctor = std::move(cv);
+}
+
+namespace cv {
+std::ostream& operator<<(std::ostream& os, const cv::GArrayDesc &)
+{
+    // FIXME: add type information here
+    os << "(array)";
+    return os;
+}
+}
diff --git a/modules/gapi/src/api/gbackend.cpp b/modules/gapi/src/api/gbackend.cpp
new file mode 100644 (file)
index 0000000..72cdcad
--- /dev/null
@@ -0,0 +1,269 @@
+// 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
+
+
+#include <memory> // unique_ptr
+
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/own/convert.hpp"
+
+#include "api/gbackend_priv.hpp"
+#include "backends/common/gbackend.hpp"
+#include "compiler/gobjref.hpp"
+#include "compiler/gislandmodel.hpp"
+
+
+
+// GBackend private implementation /////////////////////////////////////////////
+void cv::gapi::GBackend::Priv::unpackKernel(ade::Graph             & /*graph  */ ,
+                                            const ade::NodeHandle  & /*op_node*/ ,
+                                            const GKernelImpl      & /*impl   */ )
+{
+    // Default implementation is still there as Priv
+    // is instantiated by some tests.
+    // Priv is even instantiated as a mock object in a number of tests
+    // as a backend and this method is called for mock objects (doing nothing).
+    // FIXME: add a warning message here
+    // FIXME: Do something with this! Ideally this function should be "=0";
+}
+
+std::unique_ptr<cv::gimpl::GIslandExecutable>
+cv::gapi::GBackend::Priv::compile(const ade::Graph&,
+                                  const GCompileArgs&,
+                                  const std::vector<ade::NodeHandle> &) const
+{
+    // ...and this method is here for the same reason!
+    GAPI_Assert(false);
+    return {};
+}
+
+void cv::gapi::GBackend::Priv::addBackendPasses(ade::ExecutionEngineSetupContext &)
+{
+    // Do nothing by default, plugins may override this to
+    // add custom (backend-specific) graph transformations
+}
+
+// GBackend public implementation //////////////////////////////////////////////
+cv::gapi::GBackend::GBackend()
+{
+}
+
+cv::gapi::GBackend::GBackend(std::shared_ptr<cv::gapi::GBackend::Priv> &&p)
+    : m_priv(std::move(p))
+{
+}
+
+cv::gapi::GBackend::Priv& cv::gapi::GBackend::priv()
+{
+    return *m_priv;
+}
+
+const cv::gapi::GBackend::Priv& cv::gapi::GBackend::priv() const
+{
+    return *m_priv;
+}
+
+std::size_t cv::gapi::GBackend::hash() const
+{
+    return std::hash<const cv::gapi::GBackend::Priv*>{}(m_priv.get());
+}
+
+bool cv::gapi::GBackend::operator== (const cv::gapi::GBackend &rhs) const
+{
+    return m_priv == rhs.m_priv;
+}
+
+// Abstract Host-side data manipulation ////////////////////////////////////////
+// Reused between CPU backend and more generic GExecutor
+namespace cv {
+namespace gimpl {
+namespace magazine {
+
+// FIXME implement the below functions with visit()?
+
+void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg)
+{
+    switch (rc.shape)
+    {
+    case GShape::GMAT:
+    {
+        auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id];
+        switch (arg.index())
+        {
+            case GRunArg::index_of<cv::gapi::own::Mat>() : mag_mat = util::get<cv::gapi::own::Mat>(arg); break;
+            case GRunArg::index_of<cv::Mat>()            : mag_mat = to_own(util::get<cv::Mat>(arg)); break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+        break;
+    }
+
+    case GShape::GSCALAR:
+    {
+        auto& mag_scalar = mag.template slot<cv::gapi::own::Scalar>()[rc.id];
+        switch (arg.index())
+        {
+            case GRunArg::index_of<cv::gapi::own::Scalar>() : mag_scalar = util::get<cv::gapi::own::Scalar>(arg); break;
+            case GRunArg::index_of<cv::Scalar>()            : mag_scalar = to_own(util::get<cv::Scalar>(arg));    break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+        break;
+    }
+
+    case GShape::GARRAY:
+        mag.template slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg);
+        break;
+
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+    }
+}
+
+void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg)
+{
+    switch (rc.shape)
+    {
+    case GShape::GMAT:
+    {
+        auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id];
+        switch (arg.index())
+        {
+            case GRunArgP::index_of<cv::gapi::own::Mat*>() : mag_mat = * util::get<cv::gapi::own::Mat*>(arg); break;
+            case GRunArgP::index_of<cv::Mat*>()            : mag_mat = to_own(* util::get<cv::Mat*>(arg)); break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+        break;
+    }
+
+    case GShape::GSCALAR:
+    {
+        auto& mag_scalar = mag.template slot<cv::gapi::own::Scalar>()[rc.id];
+        switch (arg.index())
+        {
+            case GRunArgP::index_of<cv::gapi::own::Scalar*>() : mag_scalar = *util::get<cv::gapi::own::Scalar*>(arg); break;
+            case GRunArgP::index_of<cv::Scalar*>()            : mag_scalar = to_own(*util::get<cv::Scalar*>(arg)); break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+        break;
+    }
+    case GShape::GARRAY:
+        mag.template slot<cv::detail::VectorRef>()[rc.id] = util::get<cv::detail::VectorRef>(arg);
+        break;
+
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+void resetInternalData(Mag& mag, const Data &d)
+{
+    if (d.storage != Data::Storage::INTERNAL)
+        return;
+
+    switch (d.shape)
+    {
+    case GShape::GARRAY:
+        util::get<cv::detail::ConstructVec>(d.ctor)
+            (mag.template slot<cv::detail::VectorRef>()[d.rc]);
+        break;
+
+    case GShape::GSCALAR:
+        mag.template slot<cv::gapi::own::Scalar>()[d.rc] = cv::gapi::own::Scalar();
+        break;
+
+    case GShape::GMAT:
+        // Do nothign here - FIXME unify with initInternalData?
+        break;
+
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+cv::GRunArg getArg(const Mag& mag, const RcDesc &ref)
+{
+    // Wrap associated CPU object (either host or an internal one)
+    switch (ref.shape)
+    {
+    case GShape::GMAT:    return GRunArg(mag.template slot<cv::gapi::own::Mat>().at(ref.id));
+    case GShape::GSCALAR: return GRunArg(mag.template slot<cv::gapi::own::Scalar>().at(ref.id));
+    // Note: .at() is intentional for GArray as object MUST be already there
+    //   (and constructed by either bindIn/Out or resetInternal)
+    case GShape::GARRAY:  return GRunArg(mag.template slot<cv::detail::VectorRef>().at(ref.id));
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+cv::GRunArgP getObjPtr(Mag& mag, const RcDesc &rc)
+{
+    switch (rc.shape)
+    {
+    case GShape::GMAT:    return GRunArgP(&mag.template slot<cv::gapi::own::Mat>()   [rc.id]);
+    case GShape::GSCALAR: return GRunArgP(&mag.template slot<cv::gapi::own::Scalar>()[rc.id]);
+    // Note: .at() is intentional for GArray as object MUST be already there
+    //   (and constructer by either bindIn/Out or resetInternal)
+    case GShape::GARRAY:
+        // FIXME(DM): For some absolutely unknown to me reason, move
+        // semantics is involved here without const_cast to const (and
+        // value from map is moved into return value GRunArgP, leaving
+        // map with broken value I've spent few late Friday hours
+        // debugging this!!!1
+        return GRunArgP(const_cast<const Mag&>(mag)
+                        .template slot<cv::detail::VectorRef>().at(rc.id));
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg)
+{
+    switch (rc.shape)
+    {
+    case GShape::GARRAY:
+        // Do nothing - should we really do anything here?
+        break;
+
+    case GShape::GMAT:
+    {
+        //simply check that memory was not reallocated, i.e.
+        //both instances of Mat pointing to the same memory
+        uchar* out_arg_data = nullptr;
+        switch (g_arg.index())
+        {
+            case GRunArgP::index_of<cv::gapi::own::Mat*>() : out_arg_data = util::get<cv::gapi::own::Mat*>(g_arg)->data; break;
+            case GRunArgP::index_of<cv::Mat*>()            : out_arg_data = util::get<cv::Mat*>(g_arg)->data; break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+
+        auto& in_mag  = mag.template slot<cv::gapi::own::Mat>().at(rc.id);
+        GAPI_Assert((out_arg_data == in_mag.data) && " data for output parameters was reallocated ?");
+        break;
+    }
+
+    case GShape::GSCALAR:
+    {
+        switch (g_arg.index())
+        {
+            case GRunArgP::index_of<cv::gapi::own::Scalar*>() : *util::get<cv::gapi::own::Scalar*>(g_arg) = mag.template slot<cv::gapi::own::Scalar>().at(rc.id); break;
+            case GRunArgP::index_of<cv::Scalar*>()            : *util::get<cv::Scalar*>(g_arg) = cv::gapi::own::to_ocv(mag.template slot<cv::gapi::own::Scalar>().at(rc.id)); break;
+            default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?"));
+        }
+        break;
+    }
+
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+} // namespace magazine
+} // namespace gimpl
+} // namespace cv
diff --git a/modules/gapi/src/api/gbackend_priv.hpp b/modules/gapi/src/api/gbackend_priv.hpp
new file mode 100644 (file)
index 0000000..1c6e297
--- /dev/null
@@ -0,0 +1,53 @@
+// 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 GAPI_API_GBACKEND_PRIV_HPP
+#define GAPI_API_GBACKEND_PRIV_HPP
+
+#include <memory>
+#include <unordered_set>
+
+#include <ade/graph.hpp>
+#include <ade/passes/pass_base.hpp> // passes::PassContext
+#include <ade/execution_engine/execution_engine.hpp> // ..SetupContext
+
+#include "opencv2/gapi/gcommon.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+namespace cv
+{
+namespace gimpl
+{
+    class GBackend;
+    class GIslandExecutable;
+} // namespace gimpl
+} // namespace cv
+
+// GAPI_EXPORTS is here to make tests build on Windows
+class GAPI_EXPORTS cv::gapi::GBackend::Priv
+{
+public:
+    using EPtr = std::unique_ptr<cv::gimpl::GIslandExecutable>;
+
+    virtual void unpackKernel(ade::Graph            &graph,
+                              const ade::NodeHandle &op_node,
+                              const GKernelImpl     &impl);
+
+    // FIXME: since backends are not passed to ADE anymore,
+    // there's no need in having both cv::gimpl::GBackend
+    // and cv::gapi::GBackend - these two things can be unified
+    // NOTE - nodes are guaranteed to be topologically sorted.
+    virtual EPtr compile(const ade::Graph   &graph,
+                         const GCompileArgs &args,
+                         const std::vector<ade::NodeHandle> &nodes) const;
+
+    virtual void addBackendPasses(ade::ExecutionEngineSetupContext &);
+
+    virtual ~Priv() = default;
+};
+
+#endif // GAPI_API_GBACKEND_PRIV_HPP
diff --git a/modules/gapi/src/api/gcall.cpp b/modules/gapi/src/api/gcall.cpp
new file mode 100644 (file)
index 0000000..bc23ec8
--- /dev/null
@@ -0,0 +1,64 @@
+// 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
+
+
+#include <cassert>
+#include "opencv2/gapi/gcall.hpp"
+#include "api/gcall_priv.hpp"
+
+// GCall private implementation ////////////////////////////////////////////////
+cv::GCall::Priv::Priv(const cv::GKernel &k)
+    : m_k(k)
+{
+}
+
+// GCall public implementation /////////////////////////////////////////////////
+
+cv::GCall::GCall(const cv::GKernel &k)
+    : m_priv(new Priv(k))
+{
+    // Here we have a reference to GNode,
+    // and GNode has a reference to us. Cycle! Now see destructor.
+    m_priv->m_node = GNode::Call(*this);
+}
+
+cv::GCall::~GCall()
+{
+    // When a GCall object is destroyed (and GCall::Priv is likely still alive,
+    // as there might be other references), reset m_node to break cycle.
+   m_priv->m_node = GNode();
+}
+
+void cv::GCall::setArgs(std::vector<GArg> &&args)
+{
+    // FIXME: Check if argument number is matching kernel prototype
+    m_priv->m_args = std::move(args);
+}
+
+cv::GMat cv::GCall::yield(int output)
+{
+    return cv::GMat(m_priv->m_node, output);
+}
+
+cv::GScalar cv::GCall::yieldScalar(int output)
+{
+    return cv::GScalar(m_priv->m_node, output);
+}
+
+cv::detail::GArrayU cv::GCall::yieldArray(int output)
+{
+    return cv::detail::GArrayU(m_priv->m_node, output);
+}
+
+cv::GCall::Priv& cv::GCall::priv()
+{
+    return *m_priv;
+}
+
+const cv::GCall::Priv& cv::GCall::priv() const
+{
+    return *m_priv;
+}
diff --git a/modules/gapi/src/api/gcall_priv.hpp b/modules/gapi/src/api/gcall_priv.hpp
new file mode 100644 (file)
index 0000000..ffb122e
--- /dev/null
@@ -0,0 +1,37 @@
+// 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_GCALL_PRIV_HPP
+#define OPENCV_GCALL_PRIV_HPP
+
+#include <vector>
+#include <unordered_map>
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gcall.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+#include "api/gnode.hpp"
+
+namespace cv {
+
+class GCall::Priv
+{
+public:
+    std::vector<GArg> m_args;
+    const GKernel     m_k;
+
+    // FIXME: Document that there's no recursion here.
+    // TODO: Rename to "constructionNode" or smt to reflect its lifetime
+    GNode             m_node;
+
+    explicit Priv(const GKernel &k);
+};
+
+}
+
+#endif // OPENCV_GCALL_PRIV_HPP
diff --git a/modules/gapi/src/api/gcomputation.cpp b/modules/gapi/src/api/gcomputation.cpp
new file mode 100644 (file)
index 0000000..817da8f
--- /dev/null
@@ -0,0 +1,191 @@
+// 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
+
+
+#include <algorithm> // remove_if
+#include <cctype>    // isspace (non-locale version)
+#include <ade/util/algorithm.hpp>
+
+#include "opencv2/core/cvdef.h"
+#include "logger.hpp" // GAPI_LOG
+
+#include "opencv2/gapi/gcomputation.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+#include "api/gcomputation_priv.hpp"
+#include "api/gcall_priv.hpp"
+#include "api/gnode_priv.hpp"
+
+#include "compiler/gmodelbuilder.hpp"
+#include "compiler/gcompiler.hpp"
+
+// cv::GComputation private implementation /////////////////////////////////////
+// <none>
+
+// cv::GComputation public implementation //////////////////////////////////////
+cv::GComputation::GComputation(const Generator& gen)
+    : m_priv(gen().m_priv)
+{
+}
+
+cv::GComputation::GComputation(GMat in, GMat out)
+    : cv::GComputation(cv::GIn(in), cv::GOut(out))
+{
+}
+
+
+cv::GComputation::GComputation(GMat in, GScalar out)
+    : cv::GComputation(cv::GIn(in), cv::GOut(out))
+{
+}
+
+cv::GComputation::GComputation(GMat in1, GMat in2, GMat out)
+    : cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
+{
+}
+
+cv::GComputation::GComputation(GMat in1, GMat in2, GScalar out)
+    : cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
+{
+}
+
+cv::GComputation::GComputation(const std::vector<GMat> &ins,
+                               const std::vector<GMat> &outs)
+    : m_priv(new Priv())
+{
+    const auto wrap = [](cv::GMat m) { return GProtoArg(m); };
+    ade::util::transform(ins,  std::back_inserter(m_priv->m_ins),  wrap);
+    ade::util::transform(outs, std::back_inserter(m_priv->m_outs), wrap);
+}
+
+cv::GComputation::GComputation(cv::GProtoInputArgs &&ins,
+                               cv::GProtoOutputArgs &&outs)
+    : m_priv(new Priv())
+{
+    m_priv->m_ins  = std::move(ins.m_args);
+    m_priv->m_outs = std::move(outs.m_args);
+}
+
+cv::GCompiled cv::GComputation::compile(GMetaArgs &&metas, GCompileArgs &&args)
+{
+    // FIXME: Cache gcompiled per parameters here?
+    cv::gimpl::GCompiler comp(*this, std::move(metas), std::move(args));
+    return comp.compile();
+}
+
+void cv::GComputation::apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args)
+{
+    const auto in_metas = descr_of(ins);
+    // FIXME Graph should be recompiled when GCompileArgs have changed
+    if (m_priv->m_lastMetas != in_metas)
+    {
+        // FIXME: Had to construct temporary object as compile() takes && (r-value)
+        m_priv->m_lastCompiled = compile(GMetaArgs(in_metas), std::move(args));
+        m_priv->m_lastMetas    = in_metas; // Update only here, if compile() was ok
+    }
+    m_priv->m_lastCompiled(std::move(ins), std::move(outs));
+}
+
+void cv::GComputation::apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args)
+{
+    apply(cv::gin(in), cv::gout(out), std::move(args));
+    // FIXME: The following doesn't work!
+    // Operation result is not replicated into user's object
+    // apply({GRunArg(in)}, {GRunArg(out)});
+}
+
+void cv::GComputation::apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args)
+{
+    apply(cv::gin(in), cv::gout(out), std::move(args));
+}
+
+void cv::GComputation::apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args)
+{
+    apply(cv::gin(in1, in2), cv::gout(out), std::move(args));
+}
+
+void cv::GComputation::apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args)
+{
+    apply(cv::gin(in1, in2), cv::gout(out), std::move(args));
+}
+
+void cv::GComputation::apply(const std::vector<cv::Mat> &ins,
+                             const std::vector<cv::Mat> &outs,
+                             GCompileArgs &&args)
+{
+    GRunArgs call_ins;
+    GRunArgsP call_outs;
+
+    // Make a temporary copy of vector outs - cv::Mats are copies anyway
+    auto tmp = outs;
+    for (const cv::Mat &m : ins) { call_ins.emplace_back(m);   }
+    for (      cv::Mat &m : tmp) { call_outs.emplace_back(&m); }
+
+    apply(std::move(call_ins), std::move(call_outs), std::move(args));
+}
+
+cv::GComputation::Priv& cv::GComputation::priv()
+{
+    return *m_priv;
+}
+
+const cv::GComputation::Priv& cv::GComputation::priv() const
+{
+    return *m_priv;
+}
+
+// Islands /////////////////////////////////////////////////////////////////////
+
+void cv::gapi::island(const std::string       &name,
+                            GProtoInputArgs  &&ins,
+                            GProtoOutputArgs &&outs)
+{
+    {
+        // Island must have a printable name.
+        // Forbid names which contain only spaces.
+        GAPI_Assert(!name.empty());
+        const auto first_printable_it = std::find_if_not(name.begin(), name.end(), isspace);
+        const bool likely_printable   = first_printable_it != name.end();
+        GAPI_Assert(likely_printable);
+    }
+    // Even if the name contains spaces, keep it unmodified as user will
+    // then use this string to assign affinity, etc.
+
+    // First, set island tags on all operations from `ins` to `outs`
+    auto island = cv::gimpl::unrollExpr(ins.m_args, outs.m_args);
+    if (island.all_ops.empty())
+    {
+        util::throw_error(std::logic_error("Operation range is empty"));
+    }
+    for (auto &op_expr_node : island.all_ops)
+    {
+        auto &op_expr_node_p = op_expr_node.priv();
+
+        GAPI_Assert(op_expr_node.shape() == GNode::NodeShape::CALL);
+        const GCall&       call   = op_expr_node.call();
+        const GCall::Priv& call_p = call.priv();
+
+        if (!op_expr_node_p.m_island.empty())
+        {
+            util::throw_error(std::logic_error
+                              (  "Operation " + call_p.m_k.name
+                               + " is already assigned to island \""
+                               + op_expr_node_p.m_island + "\""));
+        }
+        else
+        {
+            op_expr_node_p.m_island = name;
+            GAPI_LOG_INFO(NULL,
+                          "Assigned " << call_p.m_k.name << "_" << &call_p <<
+                          " to island \"" << name << "\"");
+        }
+    }
+
+    // Note - this function only sets islands to all operations in
+    // expression tree, it is just a first step.
+    // The second step is assigning intermediate data objects to Islands,
+    // see passes::initIslands for details.
+}
diff --git a/modules/gapi/src/api/gcomputation_priv.hpp b/modules/gapi/src/api/gcomputation_priv.hpp
new file mode 100644 (file)
index 0000000..13d1b9a
--- /dev/null
@@ -0,0 +1,29 @@
+// 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_PRIV_HPP
+#define OPENCV_GAPI_GCOMPUTATION_PRIV_HPP
+
+#include "opencv2/gapi.hpp"
+#include "opencv2/gapi/gcall.hpp"
+
+#include "opencv2/gapi/util/variant.hpp"
+
+namespace cv {
+
+class GComputation::Priv
+{
+public:
+    GCompiled   m_lastCompiled;
+    GMetaArgs   m_lastMetas; // TODO: make GCompiled remember its metas?
+    GProtoArgs  m_ins;
+    GProtoArgs  m_outs;
+};
+
+}
+
+#endif // OPENCV_GAPI_GCOMPUTATION_PRIV_HPP
diff --git a/modules/gapi/src/api/gkernel.cpp b/modules/gapi/src/api/gkernel.cpp
new file mode 100644 (file)
index 0000000..812877b
--- /dev/null
@@ -0,0 +1,141 @@
+// 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
+
+
+#include <iostream> // cerr
+#include <functional> // hash
+#include <numeric> // accumulate
+
+#include <ade/util/algorithm.hpp>
+
+#include "opencv2/core/cvdef.h"
+#include "logger.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+#include "api/gbackend_priv.hpp"
+
+// GKernelPackage public implementation ////////////////////////////////////////
+void cv::gapi::GKernelPackage::remove(const cv::gapi::GBackend& backend)
+{
+    m_backend_kernels.erase(backend);
+}
+
+bool cv::gapi::GKernelPackage::includesAPI(const std::string &id) const
+{
+    // In current form not very efficient (n * log n)
+    auto it = std::find_if(m_backend_kernels.begin(),
+                           m_backend_kernels.end(),
+                           [&id](const M::value_type &p) {
+                               return ade::util::contains(p.second, id);
+                           });
+    return (it != m_backend_kernels.end());
+}
+
+std::size_t cv::gapi::GKernelPackage::size() const
+{
+    return std::accumulate(m_backend_kernels.begin(),
+                           m_backend_kernels.end(),
+                           static_cast<std::size_t>(0u),
+                           [](std::size_t acc, const M::value_type& v) {
+                               return acc + v.second.size();
+                           });
+}
+
+cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage  &lhs,
+                                           const GKernelPackage  &rhs,
+                                           const cv::unite_policy policy)
+{
+
+    if (policy == cv::unite_policy::REPLACE)
+    {
+        // REPLACE policy: if there is a collision, prefer RHS
+        // to LHS
+        // since OTHER package has a prefernece, start with its copy
+        GKernelPackage result(rhs);
+        // now iterate over LHS package and put kernel if and only
+        // if there's no such one
+        for (const auto &backend : lhs.m_backend_kernels)
+        {
+            for (const auto &kimpl : backend.second)
+            {
+                if (!result.includesAPI(kimpl.first))
+                    result.m_backend_kernels[backend.first].insert(kimpl);
+            }
+        }
+        return result;
+    }
+    else if (policy == cv::unite_policy::KEEP)
+    {
+        // KEEP policy: if there is a collision, just keep two versions
+        // of a kernel
+        GKernelPackage result(lhs);
+        for (const auto &p : rhs.m_backend_kernels)
+        {
+            result.m_backend_kernels[p.first].insert(p.second.begin(),
+                                                     p.second.end());
+        }
+        return result;
+    }
+    else GAPI_Assert(false);
+    return GKernelPackage();
+}
+
+std::pair<cv::gapi::GBackend, cv::GKernelImpl>
+cv::gapi::GKernelPackage::lookup(const std::string &id,
+                                 const GLookupOrder &order) const
+{
+    if (order.empty())
+    {
+        // If order is empty, return what comes first
+        auto it = std::find_if(m_backend_kernels.begin(),
+                               m_backend_kernels.end(),
+                               [&id](const M::value_type &p) {
+                                   return ade::util::contains(p.second, id);
+                               });
+        if (it != m_backend_kernels.end())
+        {
+            // FIXME: Two lookups!
+            return std::make_pair(it->first, it->second.find(id)->second);
+        }
+    }
+    else
+    {
+        // There is order, so:
+        // 1. Limit search scope only to specified backends
+        //    FIXME: Currently it is not configurable if search can fall-back
+        //    to other backends (not listed in order) if kernel hasn't been found
+        //    in the look-up list
+        // 2. Query backends in the specified order
+        for (const auto &selected_backend : order)
+        {
+            const auto kernels_it = m_backend_kernels.find(selected_backend);
+            if (kernels_it == m_backend_kernels.end())
+            {
+                GAPI_LOG_WARNING(NULL,
+                                 "Backend "
+                                  << &selected_backend.priv() // FIXME: name instead
+                                  << " was listed in lookup list but was not found "
+                                     "in the package");
+                continue;
+            }
+            if (ade::util::contains(kernels_it->second, id))
+            {
+                // FIXME: two lookups!
+                return std::make_pair(selected_backend, kernels_it->second.find(id)->second);
+            }
+        }
+    }
+
+    // If reached here, kernel was not found among selected backends.
+    util::throw_error(std::logic_error("Kernel " + id + " was not found"));
+}
+
+std::vector<cv::gapi::GBackend> cv::gapi::GKernelPackage::backends() const
+{
+    std::vector<cv::gapi::GBackend> result;
+    for (const auto &p : m_backend_kernels) result.emplace_back(p.first);
+    return result;
+}
diff --git a/modules/gapi/src/api/gmat.cpp b/modules/gapi/src/api/gmat.cpp
new file mode 100644 (file)
index 0000000..5b3aa07
--- /dev/null
@@ -0,0 +1,71 @@
+// 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
+
+
+#include <opencv2/gapi/opencv_includes.hpp>
+#include <opencv2/gapi/own/mat.hpp> //gapi::own::Mat
+
+#include "opencv2/gapi/gmat.hpp"
+#include "api/gapi_priv.hpp" // GOrigin
+
+// cv::GMat public implementation //////////////////////////////////////////////
+cv::GMat::GMat()
+    : m_priv(new GOrigin(GShape::GMAT, GNode::Param()))
+{
+}
+
+cv::GMat::GMat(const GNode &n, std::size_t out)
+    : m_priv(new GOrigin(GShape::GMAT, n, out))
+{
+}
+
+cv::GOrigin& cv::GMat::priv()
+{
+    return *m_priv;
+}
+
+const cv::GOrigin& cv::GMat::priv() const
+{
+    return *m_priv;
+}
+
+cv::GMatDesc cv::descr_of(const cv::Mat &mat)
+{
+    return GMatDesc{mat.depth(), mat.channels(), {mat.cols, mat.rows}};
+}
+
+cv::GMatDesc cv::gapi::own::descr_of(const cv::gapi::own::Mat &mat)
+{
+    return GMatDesc{mat.depth(), mat.channels(), {mat.cols, mat.rows}};
+}
+
+namespace cv {
+std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc)
+{
+    switch (desc.depth)
+    {
+#define TT(X) case CV_##X: os << #X; break;
+        TT(8U);
+        TT(8S);
+        TT(16U);
+        TT(16S);
+        TT(32S);
+        TT(32F);
+        TT(64F);
+#undef TT
+    default:
+        os << "(user type "
+           << std::hex << desc.depth << std::dec
+           << ")";
+        break;
+    }
+
+    os << "C" << desc.chan << " ";
+    os << desc.size.width << "x" << desc.size.height;
+
+    return os;
+}
+}
diff --git a/modules/gapi/src/api/gnode.cpp b/modules/gapi/src/api/gnode.cpp
new file mode 100644 (file)
index 0000000..4255ea6
--- /dev/null
@@ -0,0 +1,88 @@
+// 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
+
+
+#include <cassert>
+
+#include "api/gnode.hpp"
+#include "api/gnode_priv.hpp"
+
+// GNode private implementation
+cv::GNode::Priv::Priv()
+    : m_shape(NodeShape::EMPTY)
+{
+}
+
+cv::GNode::Priv::Priv(GCall c)
+    : m_shape(NodeShape::CALL), m_spec(c)
+{
+}
+
+cv::GNode::Priv::Priv(ParamTag)
+    : m_shape(NodeShape::PARAM)
+{
+}
+
+cv::GNode::Priv::Priv(ConstTag)
+    : m_shape(NodeShape::CONST_BOUNDED)
+{
+}
+
+// GNode public implementation
+cv::GNode::GNode()
+    : m_priv(new Priv())
+{
+}
+
+cv::GNode::GNode(const GCall &c)
+    : m_priv(new Priv(c))
+{
+}
+
+cv::GNode::GNode(ParamTag)
+    : m_priv(new Priv(Priv::ParamTag()))
+{
+}
+
+cv::GNode::GNode(ConstTag)
+    : m_priv(new Priv(Priv::ConstTag()))
+{
+}
+
+cv::GNode cv::GNode::Call(const GCall &c)
+{
+    return GNode(c);
+}
+
+cv::GNode cv::GNode::Param()
+{
+    return GNode(ParamTag());
+}
+
+cv::GNode cv::GNode::Const()
+{
+    return GNode(ConstTag());
+}
+
+cv::GNode::Priv& cv::GNode::priv()
+{
+    return *m_priv;
+}
+
+const cv::GNode::Priv& cv::GNode::priv() const
+{
+    return *m_priv;
+}
+
+const cv::GNode::NodeShape& cv::GNode::shape() const
+{
+    return m_priv->m_shape;
+}
+
+const cv::GCall& cv::GNode::call()  const
+{
+    return util::get<GCall>(m_priv->m_spec);
+}
diff --git a/modules/gapi/src/api/gnode.hpp b/modules/gapi/src/api/gnode.hpp
new file mode 100644 (file)
index 0000000..bd6c790
--- /dev/null
@@ -0,0 +1,58 @@
+// 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_GNODE_HPP
+#define OPENCV_GAPI_GNODE_HPP
+
+#include <memory> // std::shared_ptr
+
+namespace cv {
+
+class GCall;
+
+// TODO Move "internal" namespace
+// TODO Expose details?
+
+// This class won't be public
+
+// data GNode = Call Operation [GNode]
+//            | Const <T>
+//            | Param <GMat|GParam>
+
+class GNode
+{
+public:
+    class Priv;
+
+    // Constructors
+    GNode();                               // Empty (invalid) constructor
+    static GNode Call (const GCall &c);    // Call constructor
+    static GNode Param();                  // Param constructor
+    static GNode Const();
+
+    // Internal use only
+    Priv& priv();
+    const Priv& priv() const;
+    enum class NodeShape: unsigned int;
+
+    const NodeShape& shape() const;
+    const GCall&     call()  const;
+
+protected:
+    struct ParamTag {};
+    struct ConstTag {};
+
+    explicit GNode(const GCall &c);
+    explicit GNode(ParamTag unused);
+    explicit GNode(ConstTag unused);
+
+    std::shared_ptr<Priv> m_priv;
+};
+
+}
+
+#endif // OPENCV_GAPI_GNODE_HPP
diff --git a/modules/gapi/src/api/gnode_priv.hpp b/modules/gapi/src/api/gnode_priv.hpp
new file mode 100644 (file)
index 0000000..5425471
--- /dev/null
@@ -0,0 +1,52 @@
+// 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_GNODE_PRIV_HPP
+#define OPENCV_GNODE_PRIV_HPP
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "opencv2/gapi/util/variant.hpp"
+
+#include "opencv2/gapi/gcall.hpp"
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+#include "api/gnode.hpp"
+
+namespace cv {
+
+enum class GNode::NodeShape: unsigned int
+{
+    EMPTY,
+    CALL,
+    PARAM,
+    CONST_BOUNDED
+};
+
+class GNode::Priv
+{
+public:
+    // TODO: replace with optional?
+    typedef util::variant<util::monostate, GCall> NodeSpec;
+    const NodeShape m_shape;
+    const NodeSpec  m_spec;
+    std::string     m_island; // user-modifiable attribute
+    struct ParamTag {};
+    struct ConstTag {};
+
+    Priv();                    // Empty (invalid) constructor
+    explicit Priv(GCall c);    // Call conctrustor
+    explicit Priv(ParamTag u); // Param constructor
+    explicit Priv(ConstTag u); // Param constructor
+};
+
+}
+
+#endif // OPENCV_GNODE_PRIV_HPP
diff --git a/modules/gapi/src/api/gproto.cpp b/modules/gapi/src/api/gproto.cpp
new file mode 100644 (file)
index 0000000..13da40e
--- /dev/null
@@ -0,0 +1,152 @@
+// 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
+
+
+#include <ade/util/algorithm.hpp>
+#include "opencv2/gapi/util/throw.hpp"
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gproto.hpp"
+
+#include "api/gapi_priv.hpp"
+#include "api/gproto_priv.hpp"
+
+// FIXME: it should be a visitor!
+// FIXME: Reimplement with traits?
+
+const cv::GOrigin& cv::gimpl::proto::origin_of(const cv::GProtoArg &arg)
+{
+    switch (arg.index())
+    {
+    case cv::GProtoArg::index_of<cv::GMat>():
+        return util::get<cv::GMat>(arg).priv();
+
+    case cv::GProtoArg::index_of<cv::GScalar>():
+        return util::get<cv::GScalar>(arg).priv();
+
+    case cv::GProtoArg::index_of<cv::detail::GArrayU>():
+        return util::get<cv::detail::GArrayU>(arg).priv();
+
+    default:
+        util::throw_error(std::logic_error("Unsupported GProtoArg type"));
+    }
+}
+
+const cv::GOrigin& cv::gimpl::proto::origin_of(const cv::GArg &arg)
+{
+    // Generic, but not very efficient implementation
+    // FIXME: Walking a thin line here!!! Here we rely that GArg and
+    // GProtoArg share the same object and this is true while objects
+    // are reference-counted, so return value is not a reference to a tmp.
+    return origin_of(rewrap(arg));
+}
+
+bool cv::gimpl::proto::is_dynamic(const cv::GArg& arg)
+{
+    // FIXME: refactor this method to be auto-generated from
+    // - GProtoArg variant parameter pack, and
+    // - traits over every type
+    switch (arg.kind)
+    {
+    case detail::ArgKind::GMAT:
+    case detail::ArgKind::GSCALAR:
+    case detail::ArgKind::GARRAY:
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+cv::GRunArg cv::value_of(const cv::GOrigin &origin)
+{
+    switch (origin.shape)
+    {
+    case GShape::GSCALAR: return GRunArg(util::get<cv::gapi::own::Scalar>(origin.value));
+    default: util::throw_error(std::logic_error("Unsupported shape for constant"));
+    }
+}
+
+cv::GProtoArg cv::gimpl::proto::rewrap(const cv::GArg &arg)
+{
+    // FIXME: replace with a more generic any->variant
+    // (or variant<T> -> variant<U>) conversion?
+    switch (arg.kind)
+    {
+    case detail::ArgKind::GMAT:    return GProtoArg(arg.get<cv::GMat>());
+    case detail::ArgKind::GSCALAR: return GProtoArg(arg.get<cv::GScalar>());
+    case detail::ArgKind::GARRAY:  return GProtoArg(arg.get<cv::detail::GArrayU>());
+    default: util::throw_error(std::logic_error("Unsupported GArg type"));
+    }
+}
+
+cv::GMetaArg cv::descr_of(const cv::GRunArg &arg)
+{
+    switch (arg.index())
+    {
+        case GRunArg::index_of<cv::Mat>():
+            return cv::GMetaArg(descr_of(util::get<cv::Mat>(arg)));
+
+        case GRunArg::index_of<cv::Scalar>():
+            return cv::GMetaArg(descr_of(util::get<cv::Scalar>(arg)));
+
+        case GRunArg::index_of<cv::gapi::own::Scalar>():
+            return cv::GMetaArg(descr_of(util::get<cv::gapi::own::Scalar>(arg)));
+
+        case GRunArg::index_of<cv::detail::VectorRef>():
+            return cv::GMetaArg(util::get<cv::detail::VectorRef>(arg).descr_of());
+
+        default: util::throw_error(std::logic_error("Unsupported GRunArg type"));
+    }
+}
+
+cv::GMetaArgs cv::descr_of(const cv::GRunArgs &args)
+{
+    cv::GMetaArgs metas;
+    ade::util::transform(args, std::back_inserter(metas), [](const cv::GRunArg &arg){ return descr_of(arg); });
+    return metas;
+}
+
+cv::GMetaArg cv::descr_of(const cv::GRunArgP &argp)
+{
+    switch (argp.index())
+    {
+    case GRunArgP::index_of<cv::Mat*>():               return GMetaArg(descr_of(*util::get<cv::Mat*>(argp)));
+    case GRunArgP::index_of<cv::gapi::own::Mat*>():    return GMetaArg(descr_of(*util::get<cv::gapi::own::Mat*>(argp)));
+    case GRunArgP::index_of<cv::Scalar*>():            return GMetaArg(descr_of(*util::get<cv::Scalar*>(argp)));
+    case GRunArgP::index_of<cv::gapi::own::Scalar*>(): return GMetaArg(descr_of(*util::get<cv::gapi::own::Scalar*>(argp)));
+    case GRunArgP::index_of<cv::detail::VectorRef>(): return GMetaArg(util::get<cv::detail::VectorRef>(argp).descr_of());
+    default: util::throw_error(std::logic_error("Unsupported GRunArgP type"));
+    }
+}
+
+namespace cv {
+std::ostream& operator<<(std::ostream& os, const cv::GMetaArg &arg)
+{
+    // FIXME: Implement via variant visitor
+    switch (arg.index())
+    {
+    case cv::GMetaArg::index_of<util::monostate>():
+        os << "(unresolved)";
+        break;
+
+    case cv::GMetaArg::index_of<cv::GMatDesc>():
+        os << util::get<cv::GMatDesc>(arg);
+        break;
+
+    case cv::GMetaArg::index_of<cv::GScalarDesc>():
+        os << util::get<cv::GScalarDesc>(arg);
+        break;
+
+    case cv::GMetaArg::index_of<cv::GArrayDesc>():
+        os << util::get<cv::GArrayDesc>(arg);
+        break;
+    default:
+        GAPI_Assert(false);
+    }
+
+    return os;
+}
+}
diff --git a/modules/gapi/src/api/gproto_priv.hpp b/modules/gapi/src/api/gproto_priv.hpp
new file mode 100644 (file)
index 0000000..2684924
--- /dev/null
@@ -0,0 +1,35 @@
+// 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_GPROTO_PRIV_HPP
+#define OPENCV_GAPI_GPROTO_PRIV_HPP
+
+#include "opencv2/gapi/gproto.hpp"
+#include "opencv2/gapi/garg.hpp"
+
+#include "api/gapi_priv.hpp"
+
+namespace cv {
+namespace gimpl {
+namespace proto {
+
+// These methods are used by GModelBuilder only
+// FIXME: Document semantics
+
+// FIXME: GAPI_EXPORTS because of tests only!
+// FIXME: Possible dangling reference alert!!!
+GAPI_EXPORTS const GOrigin& origin_of (const GProtoArg &arg);
+GAPI_EXPORTS const GOrigin& origin_of (const GArg      &arg);
+
+bool           is_dynamic(const GArg      &arg);
+GProtoArg      rewrap    (const GArg      &arg);
+
+} // proto
+} // gimpl
+} // cv
+
+#endif // OPENCV_GAPI_GPROTO_PRIV_HPP
diff --git a/modules/gapi/src/api/gscalar.cpp b/modules/gapi/src/api/gscalar.cpp
new file mode 100644 (file)
index 0000000..d8d73cd
--- /dev/null
@@ -0,0 +1,69 @@
+// 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
+
+
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/own/convert.hpp"
+#include "api/gapi_priv.hpp" // GOrigin
+
+// cv::GScalar public implementation ///////////////////////////////////////////
+cv::GScalar::GScalar()
+    : m_priv(new GOrigin(GShape::GSCALAR, cv::GNode::Param()))
+{
+}
+
+cv::GScalar::GScalar(const GNode &n, std::size_t out)
+    : m_priv(new GOrigin(GShape::GSCALAR, n, out))
+{
+}
+
+cv::GScalar::GScalar(const cv::gapi::own::Scalar& s)
+    : m_priv(new GOrigin(GShape::GSCALAR, cv::gimpl::ConstVal(s)))
+{
+}
+
+cv::GScalar::GScalar(cv::gapi::own::Scalar&& s)
+    : m_priv(new GOrigin(GShape::GSCALAR, cv::gimpl::ConstVal(std::move(s))))
+{
+}
+
+cv::GScalar::GScalar(const cv::Scalar& s)
+    : m_priv(new GOrigin(GShape::GSCALAR, cv::gimpl::ConstVal(to_own(s))))
+{
+}
+
+cv::GScalar::GScalar(double v0)
+    : m_priv(new GOrigin(GShape::GSCALAR, cv::gimpl::ConstVal(cv::gapi::own::Scalar(v0))))
+{
+}
+
+cv::GOrigin& cv::GScalar::priv()
+{
+    return *m_priv;
+}
+
+const cv::GOrigin& cv::GScalar::priv() const
+{
+    return *m_priv;
+}
+
+cv::GScalarDesc cv::descr_of(const cv::gapi::own::Scalar &)
+{
+    return empty_scalar_desc();
+}
+
+cv::GScalarDesc cv::descr_of(const cv::Scalar& s)
+{
+    return cv::descr_of(to_own(s));
+}
+
+namespace cv {
+std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &)
+{
+    os << "(scalar)";
+    return os;
+}
+}
diff --git a/modules/gapi/src/api/kernels_core.cpp b/modules/gapi/src/api/kernels_core.cpp
new file mode 100644 (file)
index 0000000..9a58706
--- /dev/null
@@ -0,0 +1,347 @@
+// 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
+
+
+#include "opencv2/gapi/gcall.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/core.hpp"
+
+#include <tuple>
+#include <numeric>
+
+namespace cv { namespace gapi {
+
+GMat add(const GMat& src1, const GMat& src2, int dtype)
+{
+    return core::GAdd::on(src1, src2, dtype);
+}
+
+GMat addC(const GMat& src1, const GScalar& c, int dtype)
+{
+    return core::GAddC::on(src1, c, dtype);
+}
+
+GMat addC(const GScalar& c, const GMat& src1, int dtype)
+{
+    return core::GAddC::on(src1, c, dtype);
+}
+
+GMat sub(const GMat& src1, const GMat& src2, int dtype)
+{
+    return core::GSub::on(src1, src2, dtype);
+}
+
+GMat subC(const GMat& src1, const GScalar& c, int dtype)
+{
+    return core::GSubC::on(src1, c, dtype);
+}
+
+GMat subRC(const GScalar& c, const GMat& src, int dtype)
+{
+    return core::GSubRC::on(c, src, dtype);
+}
+
+GMat mul(const GMat& src1, const GMat& src2, double scale, int dtype)
+{
+    return core::GMul::on(src1, src2, scale, dtype);
+}
+
+GMat mulC(const GMat& src, double scale, int dtype)
+{
+    return core::GMulCOld::on(src, scale, dtype);
+}
+
+GMat mulC(const GMat& src, const GScalar& multiplier, int dtype)
+{
+    return core::GMulC::on(src, multiplier, dtype);
+}
+
+GMat mulC(const GScalar& multiplier, const GMat& src, int dtype)
+{
+    return core::GMulC::on(src, multiplier, dtype);
+}
+
+GMat div(const GMat& src1, const GMat& src2, double scale, int dtype)
+{
+    return core::GDiv::on(src1, src2, scale, dtype);
+}
+
+GMat divC(const GMat& src, const GScalar& divisor, double scale, int dtype)
+{
+    return core::GDivC::on(src, divisor, scale, dtype);
+}
+
+GMat divRC(const GScalar& divident, const GMat& src, double scale, int dtype)
+{
+    return core::GDivRC::on(divident, src, scale, dtype);
+}
+
+GScalar mean(const GMat& src)
+{
+    return core::GMean::on(src);
+}
+
+GMat mask(const GMat& src, const GMat& mask)
+{
+    return core::GMask::on(src, mask);
+}
+
+std::tuple<GMat, GMat> polarToCart(const GMat& magnitude, const GMat& angle,
+                                   bool angleInDegrees)
+{
+    return core::GPolarToCart::on(magnitude, angle, angleInDegrees);
+}
+
+std::tuple<GMat, GMat> cartToPolar(const GMat& x, const GMat& y,
+                                   bool angleInDegrees)
+{
+    return core::GCartToPolar::on(x, y, angleInDegrees);
+}
+
+GMat cmpGT(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpGT::on(src1, src2);
+}
+
+GMat cmpLT(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpLT::on(src1, src2);
+}
+
+GMat cmpGE(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpGE::on(src1, src2);
+}
+
+GMat cmpLE(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpLE::on(src1, src2);
+}
+
+GMat cmpEQ(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpEQ::on(src1, src2);
+}
+
+GMat cmpNE(const GMat& src1, const GMat& src2)
+{
+    return core::GCmpNE::on(src1, src2);
+}
+
+GMat cmpGT(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpGTScalar::on(src1, src2);
+}
+
+GMat cmpLT(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpLTScalar::on(src1, src2);
+}
+
+GMat cmpGE(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpGEScalar::on(src1, src2);
+}
+
+GMat cmpLE(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpLEScalar::on(src1, src2);
+}
+
+GMat cmpEQ(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpEQScalar::on(src1, src2);
+}
+
+GMat cmpNE(const GMat& src1, const GScalar& src2)
+{
+    return core::GCmpNEScalar::on(src1, src2);
+}
+
+GMat min(const GMat& src1, const GMat& src2)
+{
+    return core::GMin::on(src1, src2);
+}
+
+GMat max(const GMat& src1, const GMat& src2)
+{
+    return core::GMax::on(src1, src2);
+}
+
+GMat absDiff(const GMat& src1, const GMat& src2)
+{
+    return core::GAbsDiff::on(src1, src2);
+}
+
+GMat absDiffC(const GMat& src, const GScalar& c)
+{
+    return core::GAbsDiffC::on(src, c);
+}
+
+GMat bitwise_and(const GMat& src1, const GMat& src2)
+{
+    return core::GAnd::on(src1, src2);
+}
+
+GMat bitwise_and(const GMat& src1, const GScalar& src2)
+{
+    return core::GAndS::on(src1, src2);
+}
+
+GMat bitwise_or(const GMat& src1, const GMat& src2)
+{
+    return core::GOr::on(src1, src2);
+}
+
+GMat bitwise_or(const GMat& src1, const GScalar& src2)
+{
+    return core::GOrS::on(src1, src2);
+}
+
+GMat bitwise_xor(const GMat& src1, const GMat& src2)
+{
+    return core::GXor::on(src1, src2);
+}
+
+GMat bitwise_xor(const GMat& src1, const GScalar& src2)
+{
+    return core::GXorS::on(src1, src2);
+}
+
+GMat bitwise_not(const GMat& src1)
+{
+    return core::GNot::on(src1);
+}
+
+GMat select(const GMat& src1, const GMat& src2, const GMat& mask)
+{
+    return core::GSelect::on(src1, src2, mask);
+}
+
+GScalar sum(const GMat& src)
+{
+    return core::GSum::on(src);
+}
+
+GMat addWeighted(const GMat& src1, double alpha, const GMat& src2, double beta, double gamma, int dtype)
+{
+    return core::GAddW::on(src1, alpha, src2, beta, gamma, dtype);
+}
+
+GScalar normL1(const GMat& src)
+{
+    return core::GNormL1::on(src);
+}
+
+GScalar normL2(const GMat& src)
+{
+    return core::GNormL2::on(src);
+}
+
+GScalar normInf(const GMat& src)
+{
+    return core::GNormInf::on(src);
+}
+
+std::tuple<GMat, GMat> integral(const GMat& src, int sdepth, int sqdepth)
+{
+    return core::GIntegral::on(src, sdepth, sqdepth);
+}
+
+GMat threshold(const GMat& src, const GScalar& thresh, const GScalar& maxval, int type)
+{
+    GAPI_Assert(type != cv::THRESH_TRIANGLE && type != cv::THRESH_OTSU);
+    return core::GThreshold::on(src, thresh, maxval, type);
+}
+
+std::tuple<GMat, GScalar> threshold(const GMat& src, const GScalar& maxval, int type)
+{
+    GAPI_Assert(type == cv::THRESH_TRIANGLE || type == cv::THRESH_OTSU);
+    return core::GThresholdOT::on(src, maxval, type);
+}
+
+GMat inRange(const GMat& src, const GScalar& threshLow, const GScalar& threshUp)
+{
+    return core::GInRange::on(src, threshLow, threshUp);
+}
+
+std::tuple<GMat, GMat, GMat> split3(const GMat& src)
+{
+    return core::GSplit3::on(src);
+}
+
+std::tuple<GMat, GMat, GMat, GMat> split4(const GMat& src)
+{
+    return core::GSplit4::on(src);
+}
+
+GMat merge3(const GMat& src1, const GMat& src2, const GMat& src3)
+{
+    return core::GMerge3::on(src1, src2, src3);
+}
+
+GMat merge4(const GMat& src1, const GMat& src2, const GMat& src3, const GMat& src4)
+{
+    return core::GMerge4::on(src1, src2, src3, src4);
+}
+
+GMat resize(const GMat& src, const Size& dsize, double fx, double fy, int interpolation)
+{
+    return core::GResize::on(src, dsize, fx, fy, interpolation);
+}
+
+GMat remap(const GMat& src, const Mat& map1, const Mat& map2,
+           int interpolation, int borderMode,
+           const Scalar& borderValue)
+{
+    return core::GRemap::on(src, map1, map2, interpolation, borderMode, borderValue);
+}
+
+GMat flip(const GMat& src, int flipCode)
+{
+    return core::GFlip::on(src, flipCode);
+}
+
+GMat crop(const GMat& src, const Rect& rect)
+{
+    return core::GCrop::on(src, rect);
+}
+
+GMat concatHor(const GMat& src1, const GMat& src2)
+{
+    return core::GConcatHor::on(src1, src2);
+}
+
+GMat concatHor(const std::vector<GMat>& v)
+{
+    GAPI_Assert(v.size() >= 2);
+    return std::accumulate(v.begin()+1, v.end(), v[0], core::GConcatHor::on);
+}
+
+GMat concatVert(const GMat& src1, const GMat& src2)
+{
+    return core::GConcatVert::on(src1, src2);
+}
+
+GMat concatVert(const std::vector<GMat>& v)
+{
+    GAPI_Assert(v.size() >= 2);
+    return std::accumulate(v.begin()+1, v.end(), v[0], core::GConcatVert::on);
+}
+
+GMat LUT(const GMat& src, const Mat& lut)
+{
+    return core::GLUT::on(src, lut);
+}
+
+GMat convertTo(const GMat& m, int rtype, double alpha, double beta)
+{
+    return core::GConvertTo::on(m, rtype, alpha, beta);
+}
+
+} //namespace gapi
+} //namespace cv
diff --git a/modules/gapi/src/api/kernels_imgproc.cpp b/modules/gapi/src/api/kernels_imgproc.cpp
new file mode 100644 (file)
index 0000000..0cd2b5a
--- /dev/null
@@ -0,0 +1,142 @@
+// 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
+
+
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/gcall.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+
+namespace cv { namespace gapi {
+
+GMat sepFilter(const GMat& src, int ddepth, const Mat& kernelX, const Mat& kernelY, const Point& anchor,
+               const Scalar& delta, int borderType, const Scalar& borderVal)
+{
+    return imgproc::GSepFilter::on(src, ddepth, kernelX, kernelY, anchor, delta, borderType, borderVal);
+}
+
+GMat filter2D(const GMat& src, int ddepth, const Mat& kernel, const Point& anchor, const Scalar& delta, int borderType,
+              const Scalar& bordVal)
+{
+    return imgproc::GFilter2D::on(src, ddepth, kernel, anchor, delta, borderType, bordVal);
+}
+
+GMat boxFilter(const GMat& src, int dtype, const Size& ksize, const Point& anchor,
+               bool normalize, int borderType, const Scalar& bordVal)
+{
+    return imgproc::GBoxFilter::on(src, dtype, ksize, anchor, normalize, borderType, bordVal);
+}
+
+GMat blur(const GMat& src, const Size& ksize, const Point& anchor,
+               int borderType, const Scalar& bordVal)
+{
+    return imgproc::GBlur::on(src, ksize, anchor, borderType, bordVal);
+}
+
+GMat gaussianBlur(const GMat& src, const Size& ksize, double sigmaX, double sigmaY,
+                  int borderType, const Scalar& bordVal)
+{
+    return imgproc::GGaussBlur::on(src, ksize, sigmaX, sigmaY, borderType, bordVal);
+}
+
+GMat medianBlur(const GMat& src, int ksize)
+{
+    return imgproc::GMedianBlur::on(src, ksize);
+}
+
+GMat erode(const GMat& src, const Mat& kernel, const Point& anchor, int iterations,
+           int borderType, const Scalar& borderValue )
+{
+    return imgproc::GErode::on(src, kernel, anchor, iterations, borderType, borderValue);
+}
+
+GMat erode3x3(const GMat& src, int iterations,
+           int borderType, const Scalar& borderValue )
+{
+    return erode(src, cv::Mat(), cv::Point(-1, -1), iterations, borderType, borderValue);
+}
+
+GMat dilate(const GMat& src, const Mat& kernel, const Point& anchor, int iterations,
+            int borderType, const Scalar& borderValue)
+{
+    return imgproc::GDilate::on(src, kernel, anchor, iterations, borderType, borderValue);
+}
+
+GMat dilate3x3(const GMat& src, int iterations,
+            int borderType, const Scalar& borderValue)
+{
+    return dilate(src, cv::Mat(), cv::Point(-1,-1), iterations, borderType, borderValue);
+}
+
+GMat sobel(const GMat& src, int ddepth, int dx, int dy, int ksize,
+           double scale, double delta,
+           int borderType, const Scalar& bordVal)
+{
+    return imgproc::GSobel::on(src, ddepth, dx, dy, ksize, scale, delta, borderType, bordVal);
+}
+
+GMat equalizeHist(const GMat& src)
+{
+    return imgproc::GEqHist::on(src);
+}
+
+GMat Canny(const GMat& src, double thr1, double thr2, int apertureSize, bool l2gradient)
+{
+    return imgproc::GCanny::on(src, thr1, thr2, apertureSize, l2gradient);
+}
+
+GMat RGB2Gray(const GMat& src)
+{
+    return imgproc::GRGB2Gray::on(src);
+}
+
+GMat RGB2Gray(const GMat& src, float rY, float gY, float bY)
+{
+    return imgproc::GRGB2GrayCustom::on(src, rY, gY, bY);
+}
+
+GMat BGR2Gray(const GMat& src)
+{
+    return imgproc::GBGR2Gray::on(src);
+}
+
+GMat RGB2YUV(const GMat& src)
+{
+    return imgproc::GRGB2YUV::on(src);
+}
+
+GMat BGR2LUV(const GMat& src)
+{
+    return imgproc::GBGR2LUV::on(src);
+}
+
+GMat LUV2BGR(const GMat& src)
+{
+    return imgproc::GLUV2BGR::on(src);
+}
+
+GMat BGR2YUV(const GMat& src)
+{
+    return imgproc::GBGR2YUV::on(src);
+}
+
+GMat YUV2BGR(const GMat& src)
+{
+    return imgproc::GYUV2BGR::on(src);
+}
+
+GMat YUV2RGB(const GMat& src)
+{
+    return imgproc::GYUV2RGB::on(src);
+}
+
+GMat RGB2Lab(const GMat& src)
+{
+    return imgproc::GRGB2Lab::on(src);
+}
+
+} //namespace gapi
+} //namespace cv
diff --git a/modules/gapi/src/api/operators.cpp b/modules/gapi/src/api/operators.cpp
new file mode 100644 (file)
index 0000000..78a883e
--- /dev/null
@@ -0,0 +1,211 @@
+// 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
+
+
+#include "opencv2/gapi/imgproc.hpp"
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/gscalar.hpp"
+#include "opencv2/gapi/operators.hpp"
+
+cv::GMat operator+(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::add(lhs, rhs);
+}
+
+cv::GMat operator+(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::addC(lhs, rhs);
+}
+
+cv::GMat operator+(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::addC(rhs, lhs);
+}
+
+cv::GMat operator-(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::sub(lhs, rhs);
+}
+
+cv::GMat operator-(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::subC(lhs, rhs);
+}
+
+cv::GMat operator-(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::subRC(lhs, rhs);
+}
+
+cv::GMat operator*(const cv::GMat& lhs, float rhs)
+{
+    return cv::gapi::mulC(lhs, static_cast<double>(rhs));
+}
+
+cv::GMat operator*(float lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::mulC(rhs, static_cast<double>(lhs));
+}
+
+cv::GMat operator*(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::mulC(lhs, rhs);
+}
+
+cv::GMat operator*(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::mulC(rhs, lhs);
+}
+
+cv::GMat operator/(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::divC(lhs, rhs, 1.0);
+}
+
+cv::GMat operator/(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::div(lhs, rhs, 1.0);
+}
+
+cv::GMat operator/(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::divRC(lhs, rhs, 1.0);
+}
+
+cv::GMat operator&(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_and(lhs, rhs);
+}
+
+cv::GMat operator&(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::bitwise_and(lhs, rhs);
+}
+
+cv::GMat operator&(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_and(rhs, lhs);
+}
+
+cv::GMat operator|(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_or(lhs, rhs);
+}
+
+cv::GMat operator|(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::bitwise_or(lhs, rhs);
+}
+
+cv::GMat operator|(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_or(rhs, lhs);
+}
+
+cv::GMat operator^(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_xor(lhs, rhs);
+}
+
+cv::GMat operator^(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::bitwise_xor(lhs, rhs);
+}
+
+cv::GMat operator^(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::bitwise_xor(rhs, lhs);
+}
+
+cv::GMat operator~(const cv::GMat& lhs)
+{
+    return cv::gapi::bitwise_not(lhs);
+}
+
+cv::GMat operator>(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpGT(lhs, rhs);
+}
+
+cv::GMat operator>=(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpGE(lhs, rhs);
+}
+
+cv::GMat operator<(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpLT(lhs, rhs);
+}
+
+cv::GMat operator<=(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpLE(lhs, rhs);
+}
+
+cv::GMat operator==(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpEQ(lhs, rhs);
+}
+
+cv::GMat operator!=(const cv::GMat& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpNE(lhs, rhs);
+}
+
+cv::GMat operator>(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpGT(lhs, rhs);
+}
+
+cv::GMat operator>=(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpGE(lhs, rhs);
+}
+
+cv::GMat operator<(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpLT(lhs, rhs);
+}
+
+cv::GMat operator<=(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpLE(lhs, rhs);
+}
+
+cv::GMat operator==(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpEQ(lhs, rhs);
+}
+
+cv::GMat operator!=(const cv::GMat& lhs, const cv::GScalar& rhs)
+{
+    return cv::gapi::cmpNE(lhs, rhs);
+}
+
+cv::GMat operator>(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpLT(rhs, lhs);
+}
+cv::GMat operator>=(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpLE(rhs, lhs);
+}
+cv::GMat operator<(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpGT(rhs, lhs);
+}
+cv::GMat operator<=(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpGE(rhs, lhs);
+}
+cv::GMat operator==(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpEQ(rhs, lhs);
+}
+cv::GMat operator!=(const cv::GScalar& lhs, const cv::GMat& rhs)
+{
+    return cv::gapi::cmpNE(rhs, lhs);
+}
diff --git a/modules/gapi/src/backends/README.md b/modules/gapi/src/backends/README.md
new file mode 100644 (file)
index 0000000..3aeeb1e
--- /dev/null
@@ -0,0 +1,2 @@
+This directory contains various G-API backends, which provide scheduling
+logic and kernel implementations for specific targets.
\ No newline at end of file
diff --git a/modules/gapi/src/backends/common/gbackend.hpp b/modules/gapi/src/backends/common/gbackend.hpp
new file mode 100644 (file)
index 0000000..82dcf34
--- /dev/null
@@ -0,0 +1,103 @@
+// 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_GBACKEND_HPP
+#define OPENCV_GAPI_GBACKEND_HPP
+
+#include <string>
+#include <memory>
+
+#include <ade/node.hpp>
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/own/mat.hpp"
+
+#include "opencv2/gapi/util/optional.hpp"
+#include "opencv2/gapi/own/scalar.hpp"
+
+#include "compiler/gmodel.hpp"
+
+namespace cv {
+namespace gimpl {
+
+    // Forward declarations
+    struct Data;
+    struct RcDesc;
+
+namespace magazine {
+    template<typename... Ts> struct Class
+    {
+        template<typename T> using MapT = std::unordered_map<int, T>;
+        template<typename T>       MapT<T>& slot()
+        {
+            return std::get<ade::util::type_list_index<T, Ts...>::value>(slots);
+        }
+        template<typename T> const MapT<T>& slot() const
+        {
+            return std::get<ade::util::type_list_index<T, Ts...>::value>(slots);
+        }
+    private:
+        std::tuple<MapT<Ts>...> slots;
+    };
+
+} // namespace magazine
+
+using Mag = magazine::Class<cv::gapi::own::Mat, cv::gapi::own::Scalar, cv::detail::VectorRef>;
+
+namespace magazine
+{
+    void         bindInArg (Mag& mag, const RcDesc &rc, const GRunArg  &arg);
+    void         bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg);
+
+    void         resetInternalData(Mag& mag, const Data &d);
+    cv::GRunArg  getArg    (const Mag& mag, const RcDesc &ref);
+    cv::GRunArgP getObjPtr (      Mag& mag, const RcDesc &rc);
+    void         writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg);
+} // namespace magazine
+
+namespace detail
+{
+template<typename... Ts> struct magazine
+{
+    template<typename T> using MapT = std::unordered_map<int, T>;
+    template<typename T>       MapT<T>& slot()
+    {
+        return std::get<util::type_list_index<T, Ts...>::value>(slots);
+    }
+    template<typename T> const MapT<T>& slot() const
+    {
+        return std::get<util::type_list_index<T, Ts...>::value>(slots);
+    }
+private:
+    std::tuple<MapT<Ts>...> slots;
+};
+} // namespace detail
+
+struct GRuntimeArgs
+{
+    GRunArgs   inObjs;
+    GRunArgsP outObjs;
+};
+
+template<typename T>
+inline cv::util::optional<T> getCompileArg(const cv::GCompileArgs &args)
+{
+    for (auto &compile_arg : args)
+    {
+        if (compile_arg.tag == cv::detail::CompileArgTag<T>::tag())
+        {
+            return cv::util::optional<T>(compile_arg.get<T>());
+        }
+    }
+    return cv::util::optional<T>();
+}
+
+
+
+}} // cv::gimpl
+
+#endif // OPENCV_GAPI_GBACKEND_HPP
diff --git a/modules/gapi/src/backends/common/gcompoundbackend.cpp b/modules/gapi/src/backends/common/gcompoundbackend.cpp
new file mode 100644 (file)
index 0000000..25fa8a2
--- /dev/null
@@ -0,0 +1,18 @@
+// 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
+
+
+#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
+
+#include "api/gbackend_priv.hpp"
+#include "compiler/gislandmodel.hpp" // GIslandExecutable
+
+cv::gapi::GBackend cv::gapi::compound::backend()
+{
+    // A pointer to dummy Priv is used to uniquely identify backends
+    static cv::gapi::GBackend this_backend(std::make_shared<cv::gapi::GBackend::Priv>());
+    return this_backend;
+}
diff --git a/modules/gapi/src/backends/common/gcompoundkernel.cpp b/modules/gapi/src/backends/common/gcompoundkernel.cpp
new file mode 100644 (file)
index 0000000..fd9609e
--- /dev/null
@@ -0,0 +1,45 @@
+// 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
+
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+#include "opencv2/gapi/gcompoundkernel.hpp"
+#include "compiler/gobjref.hpp"
+
+// FIXME move to backends
+
+cv::detail::GCompoundContext::GCompoundContext(const cv::GArgs& in_args)
+{
+    m_args.resize(in_args.size());
+    for (const auto& it : ade::util::indexed(in_args))
+    {
+        const auto& i      = ade::util::index(it);
+        const auto& in_arg = ade::util::value(it);
+
+        if (in_arg.kind != cv::detail::ArgKind::GOBJREF)
+        {
+            m_args[i] = in_arg;
+        }
+        else
+        {
+            const cv::gimpl::RcDesc &ref = in_arg.get<cv::gimpl::RcDesc>();
+            switch (ref.shape)
+            {
+                case GShape::GMAT   : m_args[i] = GArg(GMat());    break;
+                case GShape::GSCALAR: m_args[i] = GArg(GScalar()); break;
+                case GShape::GARRAY :/* do nothing - as handled in a special way, see gcompoundkernel.hpp for details */; break;
+                default: GAPI_Assert(false);
+            }
+        }
+    }
+    GAPI_Assert(m_args.size() == in_args.size());
+}
+
+cv::detail::GCompoundKernel::GCompoundKernel(const F& f) : m_f(f)
+{
+}
+
+void cv::detail::GCompoundKernel::apply(cv::detail::GCompoundContext& ctx) { m_f(ctx); }
diff --git a/modules/gapi/src/backends/cpu/gcpubackend.cpp b/modules/gapi/src/backends/cpu/gcpubackend.cpp
new file mode 100644 (file)
index 0000000..42e580b
--- /dev/null
@@ -0,0 +1,227 @@
+// 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
+
+
+#include <functional>
+#include <unordered_set>
+
+#include <ade/util/algorithm.hpp>
+
+#include <ade/util/range.hpp>
+#include <ade/util/zip_range.hpp>
+#include <ade/util/chain_range.hpp>
+
+#include <ade/typed_graph.hpp>
+
+#include "opencv2/gapi/gcommon.hpp"
+#include "opencv2/gapi/util/any.hpp"
+#include "opencv2/gapi/gtype_traits.hpp"
+
+#include "compiler/gobjref.hpp"
+#include "compiler/gmodel.hpp"
+
+#include "backends/cpu/gcpubackend.hpp"
+#include "backends/cpu/gcpuimgproc.hpp"
+#include "backends/cpu/gcpucore.hpp"
+
+#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
+
+// FIXME: Is there a way to take a typed graph (our GModel),
+// and create a new typed graph _ATOP_ of that (by extending with a couple of
+// new types?).
+// Alternatively, is there a way to compose types graphs?
+//
+// If not, we need to introduce that!
+using GCPUModel = ade::TypedGraph
+    < cv::gimpl::Unit
+    , cv::gimpl::Protocol
+    >;
+
+// FIXME: Same issue with Typed and ConstTyped
+using GConstGCPUModel = ade::ConstTypedGraph
+    < cv::gimpl::Unit
+    , cv::gimpl::Protocol
+    >;
+
+namespace
+{
+    class GCPUBackendImpl final: public cv::gapi::GBackend::Priv
+    {
+        virtual void unpackKernel(ade::Graph            &graph,
+                                  const ade::NodeHandle &op_node,
+                                  const cv::GKernelImpl &impl) override
+        {
+            GCPUModel gm(graph);
+            auto cpu_impl = cv::util::any_cast<cv::GCPUKernel>(impl.opaque);
+            gm.metadata(op_node).set(cv::gimpl::Unit{cpu_impl});
+        }
+
+        virtual EPtr compile(const ade::Graph &graph,
+                             const cv::GCompileArgs &,
+                             const std::vector<ade::NodeHandle> &nodes) const override
+        {
+            return EPtr{new cv::gimpl::GCPUExecutable(graph, nodes)};
+        }
+   };
+}
+
+cv::gapi::GBackend cv::gapi::cpu::backend()
+{
+    static cv::gapi::GBackend this_backend(std::make_shared<GCPUBackendImpl>());
+    return this_backend;
+}
+
+// GCPUExcecutable implementation //////////////////////////////////////////////
+cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g,
+                                          const std::vector<ade::NodeHandle> &nodes)
+    : m_g(g), m_gm(m_g)
+{
+    // Convert list of operations (which is topologically sorted already)
+    // into an execution script.
+    for (auto &nh : nodes)
+    {
+        switch (m_gm.metadata(nh).get<NodeType>().t)
+        {
+        case NodeType::OP: m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)}); break;
+        case NodeType::DATA:
+        {
+            m_dataNodes.push_back(nh);
+            const auto &desc = m_gm.metadata(nh).get<Data>();
+            if (desc.storage == Data::Storage::CONST)
+            {
+                auto rc = RcDesc{desc.rc, desc.shape, desc.ctor};
+                magazine::bindInArg(m_res, rc, m_gm.metadata(nh).get<ConstValue>().arg);
+            }
+            //preallocate internal Mats in advance
+            if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT)
+            {
+                const auto mat_desc = util::get<cv::GMatDesc>(desc.meta);
+                const auto type = CV_MAKETYPE(mat_desc.depth, mat_desc.chan);
+                m_res.slot<cv::gapi::own::Mat>()[desc.rc].create(mat_desc.size, type);
+            }
+            break;
+        }
+        default: util::throw_error(std::logic_error("Unsupported NodeType type"));
+        }
+    }
+}
+
+// FIXME: Document what it does
+cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg)
+{
+    // No API placeholders allowed at this point
+    // FIXME: this check has to be done somewhere in compilation stage.
+    GAPI_Assert(   arg.kind != cv::detail::ArgKind::GMAT
+              && arg.kind != cv::detail::ArgKind::GSCALAR
+              && arg.kind != cv::detail::ArgKind::GARRAY);
+
+    if (arg.kind != cv::detail::ArgKind::GOBJREF)
+    {
+        // All other cases - pass as-is, with no transformations to GArg contents.
+        return arg;
+    }
+    GAPI_Assert(arg.kind == cv::detail::ArgKind::GOBJREF);
+
+    // Wrap associated CPU object (either host or an internal one)
+    // FIXME: object can be moved out!!! GExecutor faced that.
+    const cv::gimpl::RcDesc &ref = arg.get<cv::gimpl::RcDesc>();
+    switch (ref.shape)
+    {
+    case GShape::GMAT:    return GArg(m_res.slot<cv::gapi::own::Mat>()   [ref.id]);
+    case GShape::GSCALAR: return GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
+    // Note: .at() is intentional for GArray as object MUST be already there
+    //   (and constructed by either bindIn/Out or resetInternal)
+    case GShape::GARRAY:  return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
+    default:
+        util::throw_error(std::logic_error("Unsupported GShape type"));
+        break;
+    }
+}
+
+void cv::gimpl::GCPUExecutable::run(std::vector<InObj>  &&input_objs,
+                                    std::vector<OutObj> &&output_objs)
+{
+    // Update resources with run-time information - what this Island
+    // has received from user (or from another Island, or mix...)
+    // FIXME: Check input/output objects against GIsland protocol
+
+    for (auto& it : input_objs)   magazine::bindInArg (m_res, it.first, it.second);
+    for (auto& it : output_objs)  magazine::bindOutArg(m_res, it.first, it.second);
+
+    // Initialize (reset) internal data nodes with user structures
+    // before processing a frame (no need to do it for external data structures)
+    GModel::ConstGraph gm(m_g);
+    for (auto nh : m_dataNodes)
+    {
+        const auto &desc = gm.metadata(nh).get<Data>();
+
+        if (   desc.storage == Data::Storage::INTERNAL
+            && !util::holds_alternative<util::monostate>(desc.ctor))
+        {
+            // FIXME: Note that compile-time constant data objects (like
+            // a value-initialized GArray<T>) also satisfy this condition
+            // and should be excluded, but now we just don't support it
+            magazine::resetInternalData(m_res, desc);
+        }
+    }
+
+    // OpenCV backend execution is not a rocket science at all.
+    // Simply invoke our kernels in the proper order.
+    GConstGCPUModel gcm(m_g);
+    for (auto &op_info : m_script)
+    {
+        const auto &op = m_gm.metadata(op_info.nh).get<Op>();
+
+        // Obtain our real execution unit
+        // TODO: Should kernels be copyable?
+        GCPUKernel k = gcm.metadata(op_info.nh).get<Unit>().k;
+
+        // Initialize kernel's execution context:
+        // - Input parameters
+        GCPUContext context;
+        context.m_args.reserve(op.args.size());
+
+        using namespace std::placeholders;
+        ade::util::transform(op.args,
+                          std::back_inserter(context.m_args),
+                          std::bind(&GCPUExecutable::packArg, this, _1));
+
+        // - Output parameters.
+        // FIXME: pre-allocate internal Mats, etc, according to the known meta
+        for (const auto &out_it : ade::util::indexed(op.outs))
+        {
+            // FIXME: Can the same GArg type resolution mechanism be reused here?
+            const auto out_port  = ade::util::index(out_it);
+            const auto out_desc  = ade::util::value(out_it);
+            context.m_results[out_port] = magazine::getObjPtr(m_res, out_desc);
+        }
+
+        // Now trigger the executable unit
+        k.apply(context);
+
+        //As Kernels are forbidden to allocate memory for (Mat) outputs,
+        //this code seems redundant, at least for Mats
+        //FIXME: unify with cv::detail::ensure_out_mats_not_reallocated
+        for (const auto &out_it : ade::util::indexed(op_info.expected_out_metas))
+        {
+            const auto out_index      = ade::util::index(out_it);
+            const auto expected_meta  = ade::util::value(out_it);
+            const auto out_meta       = descr_of(context.m_results[out_index]);
+
+            if (expected_meta != out_meta)
+            {
+                util::throw_error
+                    (std::logic_error
+                     ("Output meta doesn't "
+                      "coincide with the generated meta\n"
+                      "Expected: " + ade::util::to_string(expected_meta) + "\n"
+                      "Actual  : " + ade::util::to_string(out_meta)));
+            }
+        }
+    } // for(m_script)
+
+    for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second);
+}
diff --git a/modules/gapi/src/backends/cpu/gcpubackend.hpp b/modules/gapi/src/backends/cpu/gcpubackend.hpp
new file mode 100644 (file)
index 0000000..c5ba79e
--- /dev/null
@@ -0,0 +1,63 @@
+// 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_GCPUBACKEND_HPP
+#define OPENCV_GAPI_GCPUBACKEND_HPP
+
+#include <map>                // map
+#include <unordered_map>      // unordered_map
+#include <tuple>              // tuple
+#include <ade/util/algorithm.hpp> // type_list_index
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gproto.hpp"
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+
+#include "api/gapi_priv.hpp"
+#include "backends/common/gbackend.hpp"
+#include "compiler/gislandmodel.hpp"
+
+namespace cv { namespace gimpl {
+
+struct Unit
+{
+    static const char *name() { return "HostKernel"; }
+    GCPUKernel k;
+};
+
+class GCPUExecutable final: public GIslandExecutable
+{
+    const ade::Graph &m_g;
+    GModel::ConstGraph m_gm;
+
+    struct OperationInfo
+    {
+        ade::NodeHandle nh;
+        GMetaArgs expected_out_metas;
+    };
+
+    // Execution script, currently absolutely naive
+    std::vector<OperationInfo> m_script;
+    // List of all resources in graph (both internal and external)
+    std::vector<ade::NodeHandle> m_dataNodes;
+
+    // Actual data of all resources in graph (both internal and external)
+    Mag m_res;
+    GArg packArg(const GArg &arg);
+
+public:
+    GCPUExecutable(const ade::Graph                   &graph,
+                   const std::vector<ade::NodeHandle> &nodes);
+
+    virtual void run(std::vector<InObj>  &&input_objs,
+                     std::vector<OutObj> &&output_objs) override;
+};
+
+}}
+
+#endif // OPENCV_GAPI_GBACKEND_HPP
diff --git a/modules/gapi/src/backends/cpu/gcpucore.cpp b/modules/gapi/src/backends/cpu/gcpucore.cpp
new file mode 100644 (file)
index 0000000..39cde42
--- /dev/null
@@ -0,0 +1,577 @@
+// 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
+
+
+#include "precomp.hpp"
+
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/cpu/core.hpp"
+#include "backends/cpu/gcpucore.hpp"
+
+GAPI_OCV_KERNEL(GCPUAdd, cv::gapi::core::GAdd)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, int dtype, cv::Mat& out)
+    {
+        cv::add(a, b, out, cv::noArray(), dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAddC, cv::gapi::core::GAddC)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
+    {
+        cv::add(a, b, out, cv::noArray(), dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSub, cv::gapi::core::GSub)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, int dtype, cv::Mat& out)
+    {
+        cv::subtract(a, b, out, cv::noArray(), dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSubC, cv::gapi::core::GSubC)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
+    {
+        cv::subtract(a, b, out, cv::noArray(), dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSubRC, cv::gapi::core::GSubRC)
+{
+    static void run(const cv::Scalar& a, const cv::Mat& b, int dtype, cv::Mat& out)
+    {
+        cv::subtract(a, b, out, cv::noArray(), dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMul, cv::gapi::core::GMul)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
+    {
+        cv::multiply(a, b, out, scale, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMulCOld, cv::gapi::core::GMulCOld)
+{
+    static void run(const cv::Mat& a, double b, int dtype, cv::Mat& out)
+    {
+        cv::multiply(a, b, out, 1, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMulC, cv::gapi::core::GMulC)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, int dtype, cv::Mat& out)
+    {
+        cv::multiply(a, b, out, 1, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUDiv, cv::gapi::core::GDiv)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
+    {
+        cv::divide(a, b, out, scale, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUDivC, cv::gapi::core::GDivC)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, double scale, int dtype, cv::Mat& out)
+    {
+        cv::divide(a, b, out, scale, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUDivRC, cv::gapi::core::GDivRC)
+{
+    static void run(const cv::Scalar& a, const cv::Mat& b, double scale, int dtype, cv::Mat& out)
+    {
+        cv::divide(a, b, out, scale, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMask, cv::gapi::core::GMask)
+{
+    static void run(const cv::Mat& in, const cv::Mat& mask, cv::Mat& out)
+    {
+        out = cv::Mat::zeros(in.size(), in.type());
+        in.copyTo(out, mask);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMean, cv::gapi::core::GMean)
+{
+    static void run(const cv::Mat& in, cv::Scalar& out)
+    {
+        out = cv::mean(in);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUPolarToCart, cv::gapi::core::GPolarToCart)
+{
+    static void run(const cv::Mat& magn, const cv::Mat& angle, bool angleInDegrees, cv::Mat& outx, cv::Mat& outy)
+    {
+        cv::polarToCart(magn, angle, outx, outy, angleInDegrees);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCartToPolar, cv::gapi::core::GCartToPolar)
+{
+    static void run(const cv::Mat& x, const cv::Mat& y, bool angleInDegrees, cv::Mat& outmagn, cv::Mat& outangle)
+    {
+        cv::cartToPolar(x, y, outmagn, outangle, angleInDegrees);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpGT, cv::gapi::core::GCmpGT)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_GT);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpGE, cv::gapi::core::GCmpGE)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_GE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpLE, cv::gapi::core::GCmpLE)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_LE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpLT, cv::gapi::core::GCmpLT)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_LT);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpEQ, cv::gapi::core::GCmpEQ)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_EQ);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpNE, cv::gapi::core::GCmpNE)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_NE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpGTScalar, cv::gapi::core::GCmpGTScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_GT);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpGEScalar, cv::gapi::core::GCmpGEScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_GE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpLEScalar, cv::gapi::core::GCmpLEScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_LE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpLTScalar, cv::gapi::core::GCmpLTScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_LT);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpEQScalar, cv::gapi::core::GCmpEQScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_EQ);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCmpNEScalar, cv::gapi::core::GCmpNEScalar)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::compare(a, b, out, cv::CMP_NE);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAnd, cv::gapi::core::GAnd)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::bitwise_and(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAndS, cv::gapi::core::GAndS)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::bitwise_and(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUOr, cv::gapi::core::GOr)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::bitwise_or(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUOrS, cv::gapi::core::GOrS)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::bitwise_or(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUXor, cv::gapi::core::GXor)
+{
+    static void run(const cv::Mat& a, const cv::Mat& b, cv::Mat& out)
+    {
+        cv::bitwise_xor(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUXorS, cv::gapi::core::GXorS)
+{
+    static void run(const cv::Mat& a, const cv::Scalar& b, cv::Mat& out)
+    {
+        cv::bitwise_xor(a, b, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUNot, cv::gapi::core::GNot)
+{
+    static void run(const cv::Mat& a, cv::Mat& out)
+    {
+        cv::bitwise_not(a, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSelect, cv::gapi::core::GSelect)
+{
+    static void run(const cv::Mat& src1, const cv::Mat& src2, const cv::Mat& mask, cv::Mat& out)
+    {
+        src2.copyTo(out);
+        src1.copyTo(out, mask);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMin, cv::gapi::core::GMin)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
+    {
+        out = cv::min(in1, in2);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMax, cv::gapi::core::GMax)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
+    {
+        out = cv::max(in1, in2);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAbsDiff, cv::gapi::core::GAbsDiff)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
+    {
+        cv::absdiff(in1, in2, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAbsDiffC, cv::gapi::core::GAbsDiffC)
+{
+    static void run(const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out)
+    {
+        cv::absdiff(in1, in2, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSum, cv::gapi::core::GSum)
+{
+    static void run(const cv::Mat& in, cv::Scalar& out)
+    {
+        out = cv::sum(in);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUAddW, cv::gapi::core::GAddW)
+{
+    static void run(const cv::Mat& in1, double alpha, const cv::Mat& in2, double beta, double gamma, int dtype, cv::Mat& out)
+    {
+        cv::addWeighted(in1, alpha, in2, beta, gamma, out, dtype);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUNormL1, cv::gapi::core::GNormL1)
+{
+    static void run(const cv::Mat& in, cv::Scalar& out)
+    {
+        out = cv::norm(in, cv::NORM_L1);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUNormL2, cv::gapi::core::GNormL2)
+{
+    static void run(const cv::Mat& in, cv::Scalar& out)
+    {
+        out = cv::norm(in, cv::NORM_L2);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUNormInf, cv::gapi::core::GNormInf)
+{
+    static void run(const cv::Mat& in, cv::Scalar& out)
+    {
+        out = cv::norm(in, cv::NORM_INF);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUIntegral, cv::gapi::core::GIntegral)
+{
+    static void run(const cv::Mat& in, int sdepth, int sqdepth, cv::Mat& out, cv::Mat& outSq)
+    {
+        cv::integral(in, out, outSq, sdepth, sqdepth);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUThreshold, cv::gapi::core::GThreshold)
+{
+    static void run(const cv::Mat& in, const cv::Scalar& a, const cv::Scalar& b, int type, cv::Mat& out)
+    {
+        cv::threshold(in, out, a.val[0], b.val[0], type);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUThresholdOT, cv::gapi::core::GThresholdOT)
+{
+    static void run(const cv::Mat& in, const cv::Scalar& b, int type, cv::Mat& out, cv::Scalar& outScalar)
+    {
+        outScalar = cv::threshold(in, out, b.val[0], b.val[0], type);
+    }
+};
+
+
+GAPI_OCV_KERNEL(GCPUInRange, cv::gapi::core::GInRange)
+{
+    static void run(const cv::Mat& in, const cv::Scalar& low, const cv::Scalar& up, cv::Mat& out)
+    {
+        cv::inRange(in, low, up, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSplit3, cv::gapi::core::GSplit3)
+{
+    static void run(const cv::Mat& in, cv::Mat &m1, cv::Mat &m2, cv::Mat &m3)
+    {
+        std::vector<cv::Mat> outMats = {m1, m2, m3};
+        cv::split(in, outMats);
+
+        // Write back FIXME: Write a helper or avoid this nonsence completely!
+        m1 = outMats[0];
+        m2 = outMats[1];
+        m3 = outMats[2];
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSplit4, cv::gapi::core::GSplit4)
+{
+    static void run(const cv::Mat& in, cv::Mat &m1, cv::Mat &m2, cv::Mat &m3, cv::Mat &m4)
+    {
+        std::vector<cv::Mat> outMats = {m1, m2, m3, m4};
+        cv::split(in, outMats);
+
+        // Write back FIXME: Write a helper or avoid this nonsence completely!
+        m1 = outMats[0];
+        m2 = outMats[1];
+        m3 = outMats[2];
+        m4 = outMats[3];
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMerge3, cv::gapi::core::GMerge3)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, const cv::Mat& in3, cv::Mat &out)
+    {
+        std::vector<cv::Mat> inMats = {in1, in2, in3};
+        cv::merge(inMats, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMerge4, cv::gapi::core::GMerge4)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, const cv::Mat& in3, const cv::Mat& in4, cv::Mat &out)
+    {
+        std::vector<cv::Mat> inMats = {in1, in2, in3, in4};
+        cv::merge(inMats, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUResize, cv::gapi::core::GResize)
+{
+    static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp, cv::Mat &out)
+    {
+        cv::resize(in, out, sz, fx, fy, interp);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPURemap, cv::gapi::core::GRemap)
+{
+    static void run(const cv::Mat& in, const cv::Mat& x, const cv::Mat& y, int a, int b, cv::Scalar s, cv::Mat& out)
+    {
+        cv::remap(in, out, x, y, a, b, s);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUFlip, cv::gapi::core::GFlip)
+{
+    static void run(const cv::Mat& in, int code, cv::Mat& out)
+    {
+        cv::flip(in, out, code);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCrop, cv::gapi::core::GCrop)
+{
+    static void run(const cv::Mat& in, cv::Rect rect, cv::Mat& out)
+    {
+        cv::Mat(in, rect).copyTo(out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUConcatHor, cv::gapi::core::GConcatHor)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
+    {
+        cv::hconcat(in1, in2, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUConcatVert, cv::gapi::core::GConcatVert)
+{
+    static void run(const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out)
+    {
+        cv::vconcat(in1, in2, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPULUT, cv::gapi::core::GLUT)
+{
+    static void run(const cv::Mat& in, const cv::Mat& lut, cv::Mat& out)
+    {
+        cv::LUT(in, lut, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUConvertTo, cv::gapi::core::GConvertTo)
+{
+    static void run(const cv::Mat& in, int rtype, double alpha, double beta, cv::Mat& out)
+    {
+        in.convertTo(out, rtype, alpha, beta);
+    }
+};
+
+cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels()
+{
+    static auto pkg = cv::gapi::kernels
+        <  GCPUAdd
+         , GCPUAddC
+         , GCPUSub
+         , GCPUSubC
+         , GCPUSubRC
+         , GCPUMul
+         , GCPUMulC
+         , GCPUMulCOld
+         , GCPUDiv
+         , GCPUDivC
+         , GCPUDivRC
+         , GCPUMean
+         , GCPUMask
+         , GCPUPolarToCart
+         , GCPUCartToPolar
+         , GCPUCmpGT
+         , GCPUCmpGE
+         , GCPUCmpLE
+         , GCPUCmpLT
+         , GCPUCmpEQ
+         , GCPUCmpNE
+         , GCPUCmpGTScalar
+         , GCPUCmpGEScalar
+         , GCPUCmpLEScalar
+         , GCPUCmpLTScalar
+         , GCPUCmpEQScalar
+         , GCPUCmpNEScalar
+         , GCPUAnd
+         , GCPUAndS
+         , GCPUOr
+         , GCPUOrS
+         , GCPUXor
+         , GCPUXorS
+         , GCPUNot
+         , GCPUSelect
+         , GCPUMin
+         , GCPUMax
+         , GCPUAbsDiff
+         , GCPUAbsDiffC
+         , GCPUSum
+         , GCPUAddW
+         , GCPUNormL1
+         , GCPUNormL2
+         , GCPUNormInf
+         , GCPUIntegral
+         , GCPUThreshold
+         , GCPUThresholdOT
+         , GCPUInRange
+         , GCPUSplit3
+         , GCPUSplit4
+         , GCPUResize
+         , GCPUMerge3
+         , GCPUMerge4
+         , GCPURemap
+         , GCPUFlip
+         , GCPUCrop
+         , GCPUConcatHor
+         , GCPUConcatVert
+         , GCPULUT
+         , GCPUConvertTo
+         >();
+    return pkg;
+}
diff --git a/modules/gapi/src/backends/cpu/gcpucore.hpp b/modules/gapi/src/backends/cpu/gcpucore.hpp
new file mode 100644 (file)
index 0000000..77e9e82
--- /dev/null
@@ -0,0 +1,24 @@
+// 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_GCPUCORE_HPP
+#define OPENCV_GAPI_GCPUCORE_HPP
+
+#include <map>
+#include <string>
+
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+namespace cv { namespace gimpl {
+
+// NB: This is what a "Kernel Package" from the original Wiki doc should be.
+void loadCPUCore(std::map<std::string, cv::GCPUKernel> &kmap);
+
+}
+}
+
+#endif // OPENCV_GAPI_GCPUCORE_HPP
diff --git a/modules/gapi/src/backends/cpu/gcpuimgproc.cpp b/modules/gapi/src/backends/cpu/gcpuimgproc.cpp
new file mode 100644 (file)
index 0000000..d14584b
--- /dev/null
@@ -0,0 +1,273 @@
+// 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
+
+
+#include "precomp.hpp"
+
+#include "opencv2/gapi/imgproc.hpp"
+#include "opencv2/gapi/cpu/imgproc.hpp"
+#include "backends/cpu/gcpuimgproc.hpp"
+
+GAPI_OCV_KERNEL(GCPUSepFilter, cv::gapi::imgproc::GSepFilter)
+{
+    static void run(const cv::Mat& in, int ddepth, const cv::Mat& kernX, const cv::Mat& kernY, const cv::Point& anchor, const cv::Scalar& delta,
+                    int border, const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( border == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int width_add = (kernY.cols - 1) / 2;
+            int height_add =  (kernX.rows - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, border, bordVal);
+            cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
+            cv::sepFilter2D(temp_in(rect), out, ddepth, kernX, kernY, anchor, delta.val[0], border);
+        }
+        else
+            cv::sepFilter2D(in, out, ddepth, kernX, kernY, anchor, delta.val[0], border);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUBoxFilter, cv::gapi::imgproc::GBoxFilter)
+{
+    static void run(const cv::Mat& in, int ddepth, const cv::Size& ksize, const cv::Point& anchor, bool normalize, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( borderType == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int width_add = (ksize.width - 1) / 2;
+            int height_add =  (ksize.height - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal);
+            cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
+            cv::boxFilter(temp_in(rect), out, ddepth, ksize, anchor, normalize, borderType);
+        }
+        else
+            cv::boxFilter(in, out, ddepth, ksize, anchor, normalize, borderType);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUBlur, cv::gapi::imgproc::GBlur)
+{
+    static void run(const cv::Mat& in, const cv::Size& ksize, const cv::Point& anchor, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( borderType == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int width_add = (ksize.width - 1) / 2;
+            int height_add =  (ksize.height - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal);
+            cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
+            cv::blur(temp_in(rect), out, ksize, anchor, borderType);
+        }
+        else
+            cv::blur(in, out, ksize, anchor, borderType);
+    }
+};
+
+
+GAPI_OCV_KERNEL(GCPUFilter2D, cv::gapi::imgproc::GFilter2D)
+{
+    static void run(const cv::Mat& in, int ddepth, const cv::Mat& k, const cv::Point& anchor, const cv::Scalar& delta, int border,
+                    const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( border == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int width_add = (k.cols - 1) / 2;
+            int height_add =  (k.rows - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, border, bordVal );
+            cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
+            cv::filter2D(temp_in(rect), out, ddepth, k, anchor, delta.val[0], border);
+        }
+        else
+            cv::filter2D(in, out, ddepth, k, anchor, delta.val[0], border);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUGaussBlur, cv::gapi::imgproc::GGaussBlur)
+{
+    static void run(const cv::Mat& in, const cv::Size& ksize, double sigmaX, double sigmaY, int borderType, const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( borderType == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int width_add = (ksize.width - 1) / 2;
+            int height_add =  (ksize.height - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, height_add, height_add, width_add, width_add, borderType, bordVal );
+            cv::Rect rect = cv::Rect(height_add, width_add, in.cols, in.rows);
+            cv::GaussianBlur(temp_in(rect), out, ksize, sigmaX, sigmaY, borderType);
+        }
+        else
+            cv::GaussianBlur(in, out, ksize, sigmaX, sigmaY, borderType);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUMedianBlur, cv::gapi::imgproc::GMedianBlur)
+{
+    static void run(const cv::Mat& in, int ksize, cv::Mat &out)
+    {
+        cv::medianBlur(in, out, ksize);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUErode, cv::gapi::imgproc::GErode)
+{
+    static void run(const cv::Mat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::Mat &out)
+    {
+        cv::erode(in, out, kernel, anchor, iterations, borderType, borderValue);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUDilate, cv::gapi::imgproc::GDilate)
+{
+    static void run(const cv::Mat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::Mat &out)
+    {
+        cv::dilate(in, out, kernel, anchor, iterations, borderType, borderValue);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUSobel, cv::gapi::imgproc::GSobel)
+{
+    static void run(const cv::Mat& in, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType,
+                    const cv::Scalar& bordVal, cv::Mat &out)
+    {
+        if( borderType == cv::BORDER_CONSTANT )
+        {
+            cv::Mat temp_in;
+            int add = (ksize - 1) / 2;
+            cv::copyMakeBorder(in, temp_in, add, add, add, add, borderType, bordVal );
+            cv::Rect rect = cv::Rect(add, add, in.cols, in.rows);
+            cv::Sobel(temp_in(rect), out, ddepth, dx, dy, ksize, scale, delta, borderType);
+        }
+        else
+        cv::Sobel(in, out, ddepth, dx, dy, ksize, scale, delta, borderType);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUEqualizeHist, cv::gapi::imgproc::GEqHist)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::equalizeHist(in, out);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUCanny, cv::gapi::imgproc::GCanny)
+{
+    static void run(const cv::Mat& in, double thr1, double thr2, int apSize, bool l2gradient, cv::Mat &out)
+    {
+        cv::Canny(in, out, thr1, thr2, apSize, l2gradient);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPURGB2YUV, cv::gapi::imgproc::GRGB2YUV)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_RGB2YUV);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUYUV2RGB, cv::gapi::imgproc::GYUV2RGB)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_YUV2RGB);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPURGB2Lab, cv::gapi::imgproc::GRGB2Lab)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_RGB2Lab);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUBGR2LUV, cv::gapi::imgproc::GBGR2LUV)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_BGR2Luv);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUBGR2YUV, cv::gapi::imgproc::GBGR2YUV)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_BGR2YUV);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPULUV2BGR, cv::gapi::imgproc::GLUV2BGR)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_Luv2BGR);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUYUV2BGR, cv::gapi::imgproc::GYUV2BGR)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_YUV2BGR);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPURGB2Gray, cv::gapi::imgproc::GRGB2Gray)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_RGB2GRAY);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPUBGR2Gray, cv::gapi::imgproc::GBGR2Gray)
+{
+    static void run(const cv::Mat& in, cv::Mat &out)
+    {
+        cv::cvtColor(in, out, cv::COLOR_BGR2GRAY);
+    }
+};
+
+GAPI_OCV_KERNEL(GCPURGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom)
+{
+    static void run(const cv::Mat& in, float rY, float bY, float gY, cv::Mat &out)
+    {
+        cv::Mat planes[3];
+        cv::split(in, planes);
+        out = planes[0]*rY + planes[1]*bY + planes[2]*gY;
+    }
+};
+
+cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels()
+{
+    static auto pkg = cv::gapi::kernels
+        < GCPUFilter2D
+        , GCPUSepFilter
+        , GCPUBoxFilter
+        , GCPUBlur
+        , GCPUGaussBlur
+        , GCPUMedianBlur
+        , GCPUErode
+        , GCPUDilate
+        , GCPUSobel
+        , GCPUCanny
+        , GCPUEqualizeHist
+        , GCPURGB2YUV
+        , GCPUYUV2RGB
+        , GCPURGB2Lab
+        , GCPUBGR2LUV
+        , GCPUBGR2YUV
+        , GCPUYUV2BGR
+        , GCPULUV2BGR
+        , GCPUBGR2Gray
+        , GCPURGB2Gray
+        , GCPURGB2GrayCustom
+        >();
+    return pkg;
+}
diff --git a/modules/gapi/src/backends/cpu/gcpuimgproc.hpp b/modules/gapi/src/backends/cpu/gcpuimgproc.hpp
new file mode 100644 (file)
index 0000000..172871a
--- /dev/null
@@ -0,0 +1,23 @@
+// 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_GCPUIMGPROC_HPP
+#define OPENCV_GAPI_GCPUIMGPROC_HPP
+
+#include <map>
+#include <string>
+
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+namespace cv { namespace gimpl {
+
+// NB: This is what a "Kernel Package" from the origianl Wiki doc should be.
+void loadCPUImgProc(std::map<std::string, cv::GCPUKernel> &kmap);
+
+}}
+
+#endif // OPENCV_GAPI_GCPUIMGPROC_HPP
diff --git a/modules/gapi/src/backends/cpu/gcpukernel.cpp b/modules/gapi/src/backends/cpu/gcpukernel.cpp
new file mode 100644 (file)
index 0000000..a672892
--- /dev/null
@@ -0,0 +1,50 @@
+// 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
+
+
+#include <cassert>
+
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+const cv::gapi::own::Mat& cv::GCPUContext::inMat(int input)
+{
+    return inArg<cv::gapi::own::Mat>(input);
+}
+
+cv::gapi::own::Mat&  cv::GCPUContext::outMatR(int output)
+{
+    return *util::get<cv::gapi::own::Mat*>(m_results.at(output));
+}
+
+const cv::gapi::own::Scalar& cv::GCPUContext::inVal(int input)
+{
+    return inArg<cv::gapi::own::Scalar>(input);
+}
+
+cv::gapi::own::Scalar& cv::GCPUContext::outValR(int output)
+{
+    return *util::get<cv::gapi::own::Scalar*>(m_results.at(output));
+}
+
+cv::detail::VectorRef& cv::GCPUContext::outVecRef(int output)
+{
+    return util::get<cv::detail::VectorRef>(m_results.at(output));
+}
+
+cv::GCPUKernel::GCPUKernel()
+{
+}
+
+cv::GCPUKernel::GCPUKernel(const GCPUKernel::F &f)
+    : m_f(f)
+{
+}
+
+void cv::GCPUKernel::apply(GCPUContext &ctx)
+{
+    GAPI_Assert(m_f);
+    m_f(ctx);
+}
diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/modules/gapi/src/backends/fluid/gfluidbackend.cpp
new file mode 100644 (file)
index 0000000..758f84e
--- /dev/null
@@ -0,0 +1,1185 @@
+// 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
+
+
+#include <functional>
+#include <iostream>
+#include <iomanip> // std::fixed, std::setprecision
+#include <unordered_set>
+#include <stack>
+
+#include <ade/util/algorithm.hpp>
+#include <ade/util/chain_range.hpp>
+#include <ade/util/range.hpp>
+#include <ade/util/zip_range.hpp>
+
+#include <ade/typed_graph.hpp>
+#include <ade/execution_engine/execution_engine.hpp>
+
+#include "opencv2/gapi/gcommon.hpp"
+#include "logger.hpp"
+
+#include "opencv2/gapi/own/convert.hpp"
+#include "opencv2/gapi/gmat.hpp"    //for version of descr_of
+// PRIVATE STUFF!
+#include "compiler/gobjref.hpp"
+#include "compiler/gmodel.hpp"
+
+#include "backends/fluid/gfluidbuffer_priv.hpp"
+#include "backends/fluid/gfluidbackend.hpp"
+#include "backends/fluid/gfluidimgproc.hpp"
+#include "backends/fluid/gfluidcore.hpp"
+
+#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
+
+// FIXME: Is there a way to take a typed graph (our GModel),
+// and create a new typed graph _ATOP_ of that (by extending with a couple of
+// new types?).
+// Alternatively, is there a way to compose types graphs?
+//
+// If not, we need to introduce that!
+using GFluidModel = ade::TypedGraph
+    < cv::gimpl::FluidUnit
+    , cv::gimpl::FluidData
+    , cv::gimpl::Protocol
+    , cv::gimpl::FluidUseOwnBorderBuffer
+    >;
+
+// FIXME: Same issue with Typed and ConstTyped
+using GConstFluidModel = ade::ConstTypedGraph
+    < cv::gimpl::FluidUnit
+    , cv::gimpl::FluidData
+    , cv::gimpl::Protocol
+    , cv::gimpl::FluidUseOwnBorderBuffer
+    >;
+
+// FluidBackend middle-layer implementation ////////////////////////////////////
+namespace
+{
+    class GFluidBackendImpl final: public cv::gapi::GBackend::Priv
+    {
+        virtual void unpackKernel(ade::Graph            &graph,
+                                  const ade::NodeHandle &op_node,
+                                  const cv::GKernelImpl &impl) override
+        {
+            GFluidModel fm(graph);
+            auto fluid_impl = cv::util::any_cast<cv::GFluidKernel>(impl.opaque);
+            fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, 0, 0.0});
+        }
+
+        virtual EPtr compile(const ade::Graph &graph,
+                             const cv::GCompileArgs &args,
+                             const std::vector<ade::NodeHandle> &nodes) const override
+        {
+            const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args).value_or(cv::GFluidOutputRois());
+            return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, out_rois.rois)};
+        }
+
+        virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override;
+
+    };
+}
+
+cv::gapi::GBackend cv::gapi::fluid::backend()
+{
+    static cv::gapi::GBackend this_backend(std::make_shared<GFluidBackendImpl>());
+    return this_backend;
+}
+
+// FluidAgent implementation ///////////////////////////////////////////////////
+
+namespace cv { namespace gimpl {
+struct FluidFilterAgent : public FluidAgent
+{
+private:
+    virtual int firstWindow() const override;
+    virtual int nextWindow() const override;
+    virtual int linesRead() const override;
+public:
+    using FluidAgent::FluidAgent;
+};
+
+struct FluidResizeAgent : public FluidAgent
+{
+private:
+    virtual int firstWindow() const override;
+    virtual int nextWindow() const override;
+    virtual int linesRead() const override;
+public:
+    using FluidAgent::FluidAgent;
+};
+
+struct FluidUpscaleAgent : public FluidAgent
+{
+private:
+    virtual int firstWindow() const override;
+    virtual int nextWindow() const override;
+    virtual int linesRead() const override;
+public:
+    using FluidAgent::FluidAgent;
+};
+}} // namespace cv::gimpl
+
+cv::gimpl::FluidAgent::FluidAgent(const ade::Graph &g, ade::NodeHandle nh)
+    : k(GConstFluidModel(g).metadata(nh).get<FluidUnit>().k)        // init(0)
+    , op_handle(nh)                                                 // init(1)
+    , op_name(GModel::ConstGraph(g).metadata(nh).get<Op>().k.name)  // init(2)
+{
+    std::set<int> out_w;
+    std::set<int> out_h;
+    GModel::ConstGraph cm(g);
+    for (auto out_data : nh->outNodes())
+    {
+        const auto  &d      = cm.metadata(out_data).get<Data>();
+        cv::GMatDesc d_meta = cv::util::get<cv::GMatDesc>(d.meta);
+        out_w.insert(d_meta.size.width);
+        out_h.insert(d_meta.size.height);
+    }
+
+    // Different output sizes are not supported
+    GAPI_Assert(out_w.size() == 1 && out_h.size() == 1);
+}
+
+void cv::gimpl::FluidAgent::reset()
+{
+    m_producedLines = 0;
+
+    auto lines = firstWindow();
+    for (auto &v : in_views)
+    {
+        if (v)
+        {
+            v.priv().reset(lines);
+        }
+    }
+}
+
+namespace {
+static int calcGcd (int n1, int n2)
+{
+    return (n2 == 0) ? n1 : calcGcd (n2, n1 % n2);
+}
+
+// This is an empiric formula and this is not 100% guaranteed
+// that it produces correct results in all possible cases
+// FIXME:
+// prove correctness or switch to some trusted method
+//
+// When performing resize input/output pixels form a cyclic
+// pattern where inH/gcd input pixels are mapped to outH/gcd
+// output pixels (pattern repeats gcd times).
+//
+// Output pixel can partually cover some of the input pixels.
+// There are 3 possible cases:
+//
+// :___ ___:    :___ _:_ ___:    :___ __: ___ :__ ___:
+// |___|___|    |___|_:_|___|    |___|__:|___|:__|___|
+// :       :    :     :     :    :      :     :      :
+//
+// 1) No partial coverage, max window = scaleFactor;
+// 2) Partial coverage occurs on the one side of the output pixel,
+//    max window = scaleFactor + 1;
+// 3) Partial coverage occurs at both sides of the output pixel,
+//    max window = scaleFactor + 2;
+//
+// Type of the coverage is determined by remainder of
+// inPeriodH/outPeriodH division, but it's an heuristic
+// (howbeit didn't found the proof of the opposite so far).
+
+static int calcResizeWindow(int inH, int outH)
+{
+    CV_Assert(inH >= outH);
+    auto gcd = calcGcd(inH, outH);
+    int  inPeriodH =  inH/gcd;
+    int outPeriodH = outH/gcd;
+    int scaleFactor = inPeriodH / outPeriodH;
+
+    switch ((inPeriodH) % (outPeriodH))
+    {
+    case 0:  return scaleFactor;     break;
+    case 1:  return scaleFactor + 1; break;
+    default: return scaleFactor + 2;
+    }
+}
+
+static int maxReadWindow(const cv::GFluidKernel& k, int inH, int outH)
+{
+    switch (k.m_kind)
+    {
+    case cv::GFluidKernel::Kind::Filter: return k.m_window; break;
+    case cv::GFluidKernel::Kind::Resize:
+    {
+        if  (inH >= outH)
+        {
+            return calcResizeWindow(inH, outH);
+        }
+        else
+        {
+            // Upscale always has window of 2
+            return (inH == 1) ? 1 : 2;
+        }
+    } break;
+    default: CV_Assert(false); return 0;
+    }
+}
+
+static int borderSize(const cv::GFluidKernel& k)
+{
+    switch (k.m_kind)
+    {
+    case cv::GFluidKernel::Kind::Filter: return (k.m_window - 1) / 2; break;
+    // Resize never reads from border pixels
+    case cv::GFluidKernel::Kind::Resize: return 0; break;
+    default: CV_Assert(false); return 0;
+    }
+}
+
+double inCoord(int outIdx, double ratio)
+{
+    return outIdx * ratio;
+}
+
+int windowStart(int outIdx, double ratio)
+{
+    return static_cast<int>(inCoord(outIdx, ratio) + 1e-3);
+}
+
+int windowEnd(int outIdx, double ratio)
+{
+    return static_cast<int>(std::ceil(inCoord(outIdx + 1, ratio) - 1e-3));
+}
+
+double inCoordUpscale(int outCoord, double ratio)
+{
+    // Calculate the projection of output pixel's center
+    return (outCoord + 0.5) * ratio - 0.5;
+}
+
+int upscaleWindowStart(int outCoord, double ratio)
+{
+    int start = static_cast<int>(inCoordUpscale(outCoord, ratio));
+    CV_Assert(start >= 0);
+    return start;
+}
+
+int upscaleWindowEnd(int outCoord, double ratio, int inSz)
+{
+    int end = static_cast<int>(std::ceil(inCoordUpscale(outCoord, ratio)) + 1);
+    if (end > inSz)
+    {
+        end = inSz;
+    }
+    return end;
+}
+} // anonymous namespace
+
+int cv::gimpl::FluidFilterAgent::firstWindow() const
+{
+    return k.m_window + k.m_lpi - 1;
+}
+
+int cv::gimpl::FluidFilterAgent::nextWindow() const
+{
+    int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines - k.m_lpi);
+    return k.m_window - 1 + lpi;
+}
+
+int cv::gimpl::FluidFilterAgent::linesRead() const
+{
+    return k.m_lpi;
+}
+
+int cv::gimpl::FluidResizeAgent::firstWindow() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return windowEnd(outIdx, m_ratio) - windowStart(outIdx, m_ratio);
+}
+
+int cv::gimpl::FluidResizeAgent::nextWindow() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return windowEnd(outIdx + 1, m_ratio) - windowStart(outIdx + 1, m_ratio);
+}
+
+int cv::gimpl::FluidResizeAgent::linesRead() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return windowStart(outIdx + 1, m_ratio) - windowStart(outIdx, m_ratio);
+}
+
+int cv::gimpl::FluidUpscaleAgent::firstWindow() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return upscaleWindowEnd(outIdx, m_ratio, in_views[0].meta().size.height) - upscaleWindowStart(outIdx, m_ratio);
+}
+
+int cv::gimpl::FluidUpscaleAgent::nextWindow() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return upscaleWindowEnd(outIdx + 1, m_ratio, in_views[0].meta().size.height) - upscaleWindowStart(outIdx + 1, m_ratio);
+}
+
+int cv::gimpl::FluidUpscaleAgent::linesRead() const
+{
+    auto outIdx = out_buffers[0]->priv().y();
+    return upscaleWindowStart(outIdx + 1, m_ratio) - upscaleWindowStart(outIdx, m_ratio);
+}
+
+bool cv::gimpl::FluidAgent::canRead() const
+{
+    // An agent can work if every input buffer have enough data to start
+    for (auto in_view : in_views)
+    {
+        if (in_view)
+        {
+            if (!in_view.ready())
+                return false;
+        }
+    }
+    return true;
+}
+
+bool cv::gimpl::FluidAgent::canWrite() const
+{
+    // An agent can work if there is space to write in its output
+    // allocated buffers
+    CV_Assert(!out_buffers.empty());
+    auto out_begin = out_buffers.begin();
+    auto out_end   = out_buffers.end();
+    if (k.m_scratch) out_end--;
+    for (auto it = out_begin; it != out_end; ++it)
+    {
+        if ((*it)->priv().full())
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool cv::gimpl::FluidAgent::canWork() const
+{
+    return canRead() && canWrite();
+}
+
+void cv::gimpl::FluidAgent::doWork()
+{
+    GAPI_Assert(m_outputLines > m_producedLines);
+    for (auto in_view : in_views)
+    {
+        if (in_view) in_view.priv().prepareToRead();
+    }
+
+    k.m_f(in_args, out_buffers);
+
+    for (auto in_view : in_views)
+    {
+        if (in_view) in_view.priv().readDone(linesRead(), nextWindow());
+    }
+
+    for (auto out_buf : out_buffers)
+    {
+        out_buf->priv().writeDone();
+        // FIXME WARNING: Scratch buffers rotated here too!
+    }
+
+    m_producedLines += k.m_lpi;
+}
+
+bool cv::gimpl::FluidAgent::done() const
+{
+    // m_producedLines is a multiple of LPI, while original
+    // height may be not.
+    return m_producedLines >= m_outputLines;
+}
+
+void cv::gimpl::FluidAgent::debug(std::ostream &os)
+{
+    os << "Fluid Agent " << std::hex << this
+       << " (" << op_name << ") --"
+       << " canWork=" << std::boolalpha << canWork()
+       << " canRead=" << std::boolalpha << canRead()
+       << " canWrite=" << std::boolalpha << canWrite()
+       << " done="    << done()
+       << " lines="   << std::dec << m_producedLines << "/" << m_outputLines
+       << " {{\n";
+    for (auto out_buf : out_buffers)
+    {
+        out_buf->debug(os);
+    }
+    std::cout << "}}" << std::endl;
+}
+
+// GCPUExcecutable implementation //////////////////////////////////////////////
+cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
+                                              const std::vector<ade::NodeHandle> &nodes,
+                                              const std::vector<cv::gapi::own::Rect> &outputRois)
+    : m_g(g), m_gm(m_g), m_outputRois(outputRois)
+{
+    GConstFluidModel fg(m_g);
+
+    // Initialize vector of data buffers, build list of operations
+    // FIXME: There _must_ be a better way to [query] count number of DATA nodes
+    std::size_t mat_count = 0;
+    std::size_t last_agent = 0;
+    std::map<std::size_t, ade::NodeHandle> all_gmat_ids;
+
+    auto grab_mat_nh = [&](ade::NodeHandle nh) {
+        auto rc = m_gm.metadata(nh).get<Data>().rc;
+        if (m_id_map.count(rc) == 0)
+        {
+            all_gmat_ids[mat_count] = nh;
+            m_id_map[rc] = mat_count++;
+        }
+    };
+
+    for (const auto &nh : nodes)
+    {
+        switch (m_gm.metadata(nh).get<NodeType>().t)
+        {
+        case NodeType::DATA:
+            if (m_gm.metadata(nh).get<Data>().shape == GShape::GMAT)
+                grab_mat_nh(nh);
+            break;
+
+        case NodeType::OP:
+        {
+            const auto& fu = fg.metadata(nh).get<FluidUnit>();
+            switch (fu.k.m_kind)
+            {
+            case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break;
+            case GFluidKernel::Kind::Resize:
+            {
+                if (fu.ratio >= 1.0)
+                {
+                    m_agents.emplace_back(new FluidResizeAgent(m_g, nh));
+                }
+                else
+                {
+                    m_agents.emplace_back(new FluidUpscaleAgent(m_g, nh));
+                }
+            } break;
+            default: CV_Assert(false);
+            }
+            // NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!!
+            m_agents.back()->in_buffer_ids.resize(m_gm.metadata(nh).get<Op>().args.size(), -1);
+            for (auto eh : nh->inEdges())
+            {
+                // FIXME Only GMats are currently supported (which can be represented
+                // as fluid buffers
+                if (m_gm.metadata(eh->srcNode()).get<Data>().shape == GShape::GMAT)
+                {
+                    const auto in_port = m_gm.metadata(eh).get<Input>().port;
+                    const auto in_buf  = m_gm.metadata(eh->srcNode()).get<Data>().rc;
+
+                    m_agents.back()->in_buffer_ids[in_port] = in_buf;
+                    grab_mat_nh(eh->srcNode());
+                }
+            }
+            // FIXME: Assumption that all operation outputs MUST be connected
+            m_agents.back()->out_buffer_ids.resize(nh->outEdges().size(), -1);
+            for (auto eh : nh->outEdges())
+            {
+                const auto& data = m_gm.metadata(eh->dstNode()).get<Data>();
+                const auto out_port = m_gm.metadata(eh).get<Output>().port;
+                const auto out_buf  = data.rc;
+
+                m_agents.back()->out_buffer_ids[out_port] = out_buf;
+                if (data.shape == GShape::GMAT) grab_mat_nh(eh->dstNode());
+            }
+            if (fu.k.m_scratch)
+                m_scratch_users.push_back(last_agent);
+            last_agent++;
+            break;
+        }
+        default: GAPI_Assert(false);
+        }
+    }
+
+    // Check that IDs form a continiuos set (important for further indexing)
+    GAPI_Assert(m_id_map.size() >  0u);
+    GAPI_Assert(m_id_map.size() == mat_count);
+
+    // Actually initialize Fluid buffers
+    GAPI_LOG_INFO(NULL, "Initializing " << mat_count << " fluid buffer(s)" << std::endl);
+    m_num_int_buffers = mat_count;
+    const std::size_t num_scratch = m_scratch_users.size();
+
+    // Calculate rois for each fluid buffer
+
+    auto proto = m_gm.metadata().get<Protocol>();
+    std::vector<int> readStarts(mat_count);
+    std::vector<cv::gapi::own::Rect> rois(mat_count);
+    std::stack<ade::NodeHandle> nodesToVisit;
+
+    if (proto.outputs.size() != m_outputRois.size())
+    {
+        CV_Assert(m_outputRois.size() == 0);
+        m_outputRois.resize(proto.outputs.size());
+    }
+
+    // First, initialize rois for output nodes, add them to traversal stack
+    for (const auto& it : ade::util::indexed(proto.out_nhs))
+    {
+        const auto idx = ade::util::index(it);
+        const auto nh  = ade::util::value(it);
+
+        const auto &d  = m_gm.metadata(nh).get<Data>();
+
+        // This is not our output
+        if (m_id_map.count(d.rc) == 0)
+        {
+            continue;
+        }
+
+        if (d.shape == GShape::GMAT)
+        {
+            auto desc = util::get<GMatDesc>(d.meta);
+            if (m_outputRois[idx] == cv::gapi::own::Rect{})
+            {
+                m_outputRois[idx] = cv::gapi::own::Rect{0, 0, desc.size.width, desc.size.height};
+            }
+
+            // Only slices are supported at the moment
+            GAPI_Assert(m_outputRois[idx].x == 0);
+            GAPI_Assert(m_outputRois[idx].width == desc.size.width);
+
+            auto id = m_id_map.at(d.rc);
+            readStarts[id] = 0;
+            rois[id] = m_outputRois[idx];
+            nodesToVisit.push(nh);
+        }
+    }
+
+    // Perform a wide search from each of the output nodes
+    // And extend roi of buffers by border_size
+    // Each node can be visited multiple times
+    // (if node has been already visited, the check that inferred rois are the same is performed)
+    while (!nodesToVisit.empty())
+    {
+        const auto startNode = nodesToVisit.top();
+        nodesToVisit.pop();
+
+        if (!startNode->inNodes().empty())
+        {
+            GAPI_Assert(startNode->inNodes().size() == 1);
+            const auto& oh = startNode->inNodes().front();
+            const auto& data = m_gm.metadata(startNode).get<Data>();
+            // only GMats participate in the process so it's valid to obtain GMatDesc
+            const auto& meta = util::get<GMatDesc>(data.meta);
+
+            for (const auto& inNode : oh->inNodes())
+            {
+                const auto& in_data = m_gm.metadata(inNode).get<Data>();
+
+                if (in_data.shape == GShape::GMAT)
+                {
+                    const auto& in_meta = util::get<GMatDesc>(in_data.meta);
+                    const auto& fd = fg.metadata(inNode).get<FluidData>();
+
+                    auto adjFilterRoi = [](cv::gapi::own::Rect produced, int b, int max_height) {
+                        // Extend with border roi which should be produced, crop to logical image size
+                        cv::gapi::own::Rect roi = {produced.x, produced.y - b, produced.width, produced.height + 2*b};
+                        cv::gapi::own::Rect fullImg{ 0, 0, produced.width, max_height };
+                        return roi & fullImg;
+                    };
+
+                    auto adjResizeRoi = [](cv::gapi::own::Rect produced, cv::gapi::own::Size inSz, cv::gapi::own::Size outSz) {
+                        auto map = [](int outCoord, int producedSz, int inSize, int outSize) {
+                            double ratio = (double)inSize / outSize;
+                            int w0 = 0, w1 = 0;
+                            if (ratio >= 1.0)
+                            {
+                                w0 = windowStart(outCoord, ratio);
+                                w1 = windowEnd  (outCoord + producedSz - 1, ratio);
+                            }
+                            else
+                            {
+                                w0 = upscaleWindowStart(outCoord, ratio);
+                                w1 = upscaleWindowEnd(outCoord + producedSz - 1, ratio, inSize);
+                            }
+                            return std::make_pair(w0, w1);
+                        };
+
+                        auto mapY = map(produced.y, produced.height, inSz.height, outSz.height);
+                        auto y0 = mapY.first;
+                        auto y1 = mapY.second;
+
+                        auto mapX = map(produced.x, produced.width, inSz.width, outSz.width);
+                        auto x0 = mapX.first;
+                        auto x1 = mapX.second;
+
+                        cv::gapi::own::Rect roi = {x0, y0, x1 - x0, y1 - y0};
+                        return roi;
+                    };
+
+                    cv::gapi::own::Rect produced = rois[m_id_map.at(data.rc)];
+
+                    cv::gapi::own::Rect resized;
+                    switch (fg.metadata(oh).get<FluidUnit>().k.m_kind)
+                    {
+                    case GFluidKernel::Kind::Filter: resized = produced; break;
+                    case GFluidKernel::Kind::Resize: resized = adjResizeRoi(produced, in_meta.size, meta.size); break;
+                    default: CV_Assert(false);
+                    }
+
+                    int readStart = resized.y;
+                    cv::gapi::own::Rect roi = adjFilterRoi(resized, fd.border_size, in_meta.size.height);
+
+                    auto in_id = m_id_map.at(in_data.rc);
+                    if (rois[in_id] == cv::gapi::own::Rect{})
+                    {
+                        readStarts[in_id] = readStart;
+                        rois[in_id] = roi;
+                        nodesToVisit.push(inNode);
+                    }
+                    else
+                    {
+                        GAPI_Assert(readStarts[in_id] == readStart);
+                        GAPI_Assert(rois[in_id] == roi);
+                    }
+                } // if (in_data.shape == GShape::GMAT)
+            } // for (const auto& inNode : oh->inNodes())
+        } // if (!startNode->inNodes().empty())
+    } // while (!nodesToVisit.empty())
+
+    // NB: Allocate ALL buffer object at once, and avoid any further reallocations
+    // (since raw pointers-to-elements are taken)
+    m_buffers.resize(m_num_int_buffers + num_scratch);
+    for (const auto &it : all_gmat_ids)
+    {
+        auto id = it.first;
+        auto nh = it.second;
+        const auto & d  = m_gm.metadata(nh).get<Data>();
+        const auto &fd  = fg.metadata(nh).get<FluidData>();
+        const auto meta = cv::util::get<GMatDesc>(d.meta);
+
+        // FIXME: Only continuous set...
+        m_buffers[id].priv().init(meta, fd.max_consumption, fd.border_size, fd.skew, fd.lpi_write, readStarts[id], rois[id]);
+
+        if (d.storage == Data::Storage::INTERNAL)
+        {
+            m_buffers[id].priv().allocate(fd.border);
+            std::stringstream stream;
+            m_buffers[id].debug(stream);
+            GAPI_LOG_INFO(NULL, stream.str());
+        }
+    }
+
+    // After buffers are allocated, repack: ...
+    for (auto &agent : m_agents)
+    {
+        // a. Agent input parameters with View pointers (creating Views btw)
+        const auto &op = m_gm.metadata(agent->op_handle).get<Op>();
+        const auto &fu =   fg.metadata(agent->op_handle).get<FluidUnit>();
+        agent->in_args.resize(op.args.size());
+        agent->in_views.resize(op.args.size());
+        for (auto it : ade::util::zip(ade::util::iota(op.args.size()),
+                                      ade::util::toRange(agent->in_buffer_ids)))
+        {
+            auto in_idx  = std::get<0>(it);
+            auto buf_idx = std::get<1>(it);
+
+            if (buf_idx >= 0)
+            {
+                // IF there is input buffer, register a view (every unique
+                // reader has its own), and store it in agent Args
+                gapi::fluid::Buffer &buffer = m_buffers.at(m_id_map.at(buf_idx));
+
+                auto inEdge = GModel::getInEdgeByPort(m_g, agent->op_handle, in_idx);
+                auto ownStorage = fg.metadata(inEdge).get<FluidUseOwnBorderBuffer>().use;
+
+                gapi::fluid::View view = buffer.mkView(fu.line_consumption, fu.border_size, fu.border, ownStorage);
+                // NB: It is safe to keep ptr as view lifetime is buffer lifetime
+                agent->in_views[in_idx] = view;
+                agent->in_args[in_idx]  = GArg(view);
+                agent->m_ratio = fu.ratio;
+            }
+            else
+            {
+                // Copy(FIXME!) original args as is
+                agent->in_args[in_idx] = op.args[in_idx];
+            }
+        }
+
+        // b. Agent output parameters with Buffer pointers.
+        agent->out_buffers.resize(agent->op_handle->outEdges().size(), nullptr);
+        for (auto it : ade::util::zip(ade::util::iota(agent->out_buffers.size()),
+                                      ade::util::toRange(agent->out_buffer_ids)))
+        {
+            auto out_idx = std::get<0>(it);
+            auto buf_idx = m_id_map.at(std::get<1>(it));
+            agent->out_buffers.at(out_idx) = &m_buffers.at(buf_idx);
+            agent->m_outputLines = m_buffers.at(buf_idx).priv().outputLines();
+        }
+    }
+
+    // After parameters are there, initialize scratch buffers
+    if (num_scratch)
+    {
+        GAPI_LOG_INFO(NULL, "Initializing " << num_scratch << " scratch buffer(s)" << std::endl);
+        unsigned last_scratch_id = 0;
+
+        for (auto i : m_scratch_users)
+        {
+            auto &agent = m_agents.at(i);
+            GAPI_Assert(agent->k.m_scratch);
+
+            // Collect input metas to trigger scratch buffer initialization
+            // Array is sparse (num of elements == num of GArgs, not edges)
+            GMetaArgs in_metas(agent->in_args.size());
+            for (auto eh : agent->op_handle->inEdges())
+            {
+                const auto& in_data = m_gm.metadata(eh->srcNode()).get<Data>();
+                in_metas[m_gm.metadata(eh).get<Input>().port] = in_data.meta;
+            }
+
+            // Trigger Scratch buffer initialization method
+            const std::size_t new_scratch_idx = m_num_int_buffers + last_scratch_id;
+
+            agent->k.m_is(in_metas, agent->in_args, m_buffers.at(new_scratch_idx));
+            std::stringstream stream;
+            m_buffers[new_scratch_idx].debug(stream);
+            GAPI_LOG_INFO(NULL, stream.str());
+            agent->out_buffers.emplace_back(&m_buffers[new_scratch_idx]);
+            last_scratch_id++;
+        }
+    }
+
+    int total_size = 0;
+    for (const auto &i : ade::util::indexed(m_buffers))
+    {
+        // Check that all internal and scratch buffers are allocated
+        auto idx = ade::util::index(i);
+        auto b   = ade::util::value(i);
+        if (idx >= m_num_int_buffers ||
+            m_gm.metadata(all_gmat_ids[idx]).get<Data>().storage == Data::Storage::INTERNAL)
+        {
+            GAPI_Assert(b.priv().size() > 0);
+        }
+
+        // Buffers which will be bound to real images may have size of 0 at this moment
+        // (There can be non-zero sized const border buffer allocated in such buffers)
+        total_size += b.priv().size();
+    }
+    GAPI_LOG_INFO(NULL, "Internal buffers: " << std::fixed << std::setprecision(2) << static_cast<float>(total_size)/1024 << " KB\n");
+}
+
+// FIXME: Document what it does
+void cv::gimpl::GFluidExecutable::bindInArg(const cv::gimpl::RcDesc &rc, const GRunArg &arg)
+{
+    switch (rc.shape)
+    {
+    case GShape::GMAT:    m_buffers[m_id_map.at(rc.id)].priv().bindTo(to_ocv(util::get<cv::gapi::own::Mat>(arg)), true); break;
+    case GShape::GSCALAR: m_res.slot<cv::gapi::own::Scalar>()[rc.id] = util::get<cv::gapi::own::Scalar>(arg); break;
+    default: util::throw_error(std::logic_error("Unsupported GShape type"));
+    }
+}
+
+void cv::gimpl::GFluidExecutable::bindOutArg(const cv::gimpl::RcDesc &rc, const GRunArgP &arg)
+{
+    // Only GMat is supported as return type
+    switch (rc.shape)
+    {
+    case GShape::GMAT:
+        {
+            cv::GMatDesc desc = m_buffers[m_id_map.at(rc.id)].meta();
+            auto      &outMat = *util::get<cv::gapi::own::Mat*>(arg);
+            GAPI_Assert(outMat.data != nullptr);
+            GAPI_Assert(descr_of(outMat) == desc && "Output argument was not preallocated as it should be ?");
+            m_buffers[m_id_map.at(rc.id)].priv().bindTo(to_ocv(outMat), false);
+            break;
+        }
+    default: util::throw_error(std::logic_error("Unsupported return GShape type"));
+    }
+}
+
+void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_arg)
+{
+    GAPI_Assert(op_arg.kind != cv::detail::ArgKind::GMAT
+           && op_arg.kind != cv::detail::ArgKind::GSCALAR);
+
+    if (op_arg.kind == cv::detail::ArgKind::GOBJREF)
+    {
+        const cv::gimpl::RcDesc &ref = op_arg.get<cv::gimpl::RcDesc>();
+        if (ref.shape == GShape::GSCALAR)
+        {
+            in_arg = GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
+        }
+    }
+}
+
+void cv::gimpl::GFluidExecutable::run(std::vector<InObj>  &&input_objs,
+                                      std::vector<OutObj> &&output_objs)
+{
+    // Bind input buffers from parameters
+    for (auto& it : input_objs)  bindInArg(it.first, it.second);
+    for (auto& it : output_objs) bindOutArg(it.first, it.second);
+
+    // Reset Buffers and Agents state before we go
+    for (auto &buffer : m_buffers)
+        buffer.priv().reset();
+
+    for (auto &agent : m_agents)
+    {
+        agent->reset();
+        // Pass input cv::Scalar's to agent argument
+        const auto& op = m_gm.metadata(agent->op_handle).get<Op>();
+        for (const auto& it : ade::util::indexed(op.args))
+        {
+            const auto& arg = ade::util::value(it);
+            packArg(agent->in_args[ade::util::index(it)], arg);
+        }
+    }
+
+    // Explicitly reset Scratch buffers, if any
+    for (auto scratch_i : m_scratch_users)
+    {
+        auto &agent = m_agents[scratch_i];
+        GAPI_Assert(agent->k.m_scratch);
+        agent->k.m_rs(*agent->out_buffers.back());
+    }
+
+    // Now start executing our stuff!
+    // Fluid execution is:
+    // - run through list of Agents from Left to Right
+    // - for every Agent:
+    //   - if all input Buffers have enough data to fulfill
+    //     Agent's window - trigger Agent
+    //     - on trigger, Agent takes all input lines from input buffers
+    //       and produces a single output line
+    //     - once Agent finishes, input buffers get "readDone()",
+    //       and output buffers get "writeDone()"
+    //   - if there's not enough data, Agent is skipped
+    // Yes, THAT easy!
+    bool complete = true;
+    do {
+        complete = true;
+        bool work_done=false;
+        for (auto &agent : m_agents)
+        {
+            // agent->debug(std::cout);
+            if (!agent->done())
+            {
+                if (agent->canWork())
+                {
+                    agent->doWork(); work_done=true;
+                }
+                if (!agent->done())   complete = false;
+            }
+        }
+        GAPI_Assert(work_done || complete);
+    } while (!complete); // FIXME: number of iterations can be calculated statically
+}
+
+// FIXME: these passes operate on graph global level!!!
+// Need to fix this for heterogeneous (island-based) processing
+void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
+{
+    using namespace cv::gimpl;
+
+    // FIXME: all passes were moved to "exec" stage since Fluid
+    // should check Islands configuration first (which is now quite
+    // limited), and only then continue with all other passes.
+    //
+    // The passes/stages API must be streamlined!
+    ectx.addPass("exec", "fluid_sanity_check", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        auto isl_graph = g.metadata().get<IslandModel>().model;
+        GIslandModel::Graph gim(*isl_graph);
+
+        const auto num_non_fluid_islands = std::count_if
+            (gim.nodes().begin(),
+             gim.nodes().end(),
+             [&](const ade::NodeHandle &nh) {
+                return gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND &&
+                       gim.metadata(nh).get<FusedIsland>().object->backend() != cv::gapi::fluid::backend();
+            });
+
+        // FIXME: Break this limitation!
+        if (num_non_fluid_islands > 0)
+            cv::util::throw_error(std::logic_error("Fluid doesn't support heterogeneous execution"));
+    });
+    ectx.addPass("exec", "init_fluid_data", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+        for (const auto node : g.nodes())
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
+            {
+                fg.metadata(node).set(FluidData());
+            }
+        }
+    });
+    // FIXME:
+    // move to unpackKernel method
+    // when https://gitlab-icv.inn.intel.com/G-API/g-api/merge_requests/66 is merged
+    ectx.addPass("exec", "init_fluid_unit_borders", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+
+        auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
+        for (auto node : sorted)
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::OP)
+            {
+                // FIXME: check that op has only one data node on input
+                auto &fu = fg.metadata(node).get<FluidUnit>();
+                const auto &op = g.metadata(node).get<Op>();
+
+                // Trigger user-defined "getBorder" callback
+                fu.border = fu.k.m_b(GModel::collectInputMeta(fg, node), op.args);
+            }
+        }
+    });
+    ectx.addPass("exec", "init_fluid_units", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+
+        auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
+        for (auto node : sorted)
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::OP)
+            {
+                std::set<int> in_hs, out_ws, out_hs;
+
+                for (const auto& in : node->inNodes())
+                {
+                    const auto& d = g.metadata(in).get<Data>();
+                    if (d.shape == cv::GShape::GMAT)
+                    {
+                        const auto& meta = cv::util::get<cv::GMatDesc>(d.meta);
+                        in_hs.insert(meta.size.height);
+                    }
+                }
+
+                for (const auto& out : node->outNodes())
+                {
+                    const auto& d = g.metadata(out).get<Data>();
+                    if (d.shape == cv::GShape::GMAT)
+                    {
+                        const auto& meta = cv::util::get<cv::GMatDesc>(d.meta);
+                        out_ws.insert(meta.size.width);
+                        out_hs.insert(meta.size.height);
+                    }
+                }
+
+                CV_Assert(in_hs.size() == 1 && out_ws.size() == 1 && out_hs.size() == 1);
+
+                auto in_h  = *in_hs .cbegin();
+                auto out_h = *out_hs.cbegin();
+
+                auto &fu = fg.metadata(node).get<FluidUnit>();
+                fu.ratio = (double)in_h / out_h;
+
+                int w = maxReadWindow(fu.k, in_h, out_h);
+                int line_consumption = fu.k.m_lpi + w - 1;
+                int border_size = borderSize(fu.k);
+
+                fu.border_size = border_size;
+                fu.line_consumption = line_consumption;
+
+                GModel::log(g, node, "Line consumption: " + std::to_string(fu.line_consumption));
+                GModel::log(g, node, "Border size: " + std::to_string(fu.border_size));
+            }
+        }
+    });
+    ectx.addPass("exec", "init_line_consumption", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+        for (const auto node : g.nodes())
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::OP)
+            {
+                const auto &fu = fg.metadata(node).get<FluidUnit>();
+
+                for (auto in_data_node : node->inNodes())
+                {
+                    auto &fd = fg.metadata(in_data_node).get<FluidData>();
+
+                    // Update (not Set) fields here since a single data node may be
+                    // accessed by multiple consumers
+                    fd.max_consumption = std::max(fu.line_consumption, fd.max_consumption);
+                    fd.border_size     = std::max(fu.border_size, fd.border_size);
+
+                    GModel::log(g, in_data_node, "Line consumption: " + std::to_string(fd.max_consumption)
+                                + " (upd by " + std::to_string(fu.line_consumption) + ")", node);
+                    GModel::log(g, in_data_node, "Border size: " + std::to_string(fd.border_size), node);
+                }
+            }
+        }
+    });
+    ectx.addPass("exec", "calc_latency", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+
+        auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
+        for (auto node : sorted)
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::OP)
+            {
+                const auto &fu = fg.metadata(node).get<FluidUnit>();
+
+                const int own_latency = fu.line_consumption - fu.border_size;
+                GModel::log(g, node, "LPI: " + std::to_string(fu.k.m_lpi));
+
+                // Output latency is max(input_latency) + own_latency
+                int in_latency = 0;
+                for (auto in_data_node : node->inNodes())
+                {
+                    // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
+                    in_latency = std::max(in_latency, fg.metadata(in_data_node).get<FluidData>().latency);
+                }
+                const int out_latency = in_latency + own_latency;
+
+                for (auto out_data_node : node->outNodes())
+                {
+                    // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
+                    auto &fd     = fg.metadata(out_data_node).get<FluidData>();
+                    fd.latency   = out_latency;
+                    fd.lpi_write = fu.k.m_lpi;
+                    GModel::log(g, out_data_node, "Latency: " + std::to_string(out_latency));
+                }
+            }
+        }
+    });
+    ectx.addPass("exec", "calc_skew", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+
+        auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
+        for (auto node : sorted)
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::OP)
+            {
+                int max_latency = 0;
+                for (auto in_data_node : node->inNodes())
+                {
+                    // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
+                    max_latency = std::max(max_latency, fg.metadata(in_data_node).get<FluidData>().latency);
+                }
+                for (auto in_data_node : node->inNodes())
+                {
+                    // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA)
+                    auto &fd = fg.metadata(in_data_node).get<FluidData>();
+
+                    // Update (not Set) fields here since a single data node may be
+                    // accessed by multiple consumers
+                    fd.skew = std::max(fd.skew, max_latency - fd.latency);
+
+                    GModel::log(g, in_data_node, "Skew: " + std::to_string(fd.skew), node);
+                }
+            }
+        }
+    });
+    ectx.addPass("exec", "init_buffer_borders", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+        auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
+        for (auto node : sorted)
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
+            {
+                auto &fd = fg.metadata(node).get<FluidData>();
+
+                // Assign border stuff to FluidData
+
+                // In/out data nodes are bound to user data directly,
+                // so cannot be extended with a border
+                if (g.metadata(node).get<Data>().storage == Data::Storage::INTERNAL)
+                {
+                    // For now border of the buffer's storage is the border
+                    // of the first reader whose border size is the same.
+                    // FIXME: find more clever strategy of border picking
+                    // (it can be a border which is common for majority of the
+                    // readers, also we can calculate the number of lines which
+                    // will be copied by views on each iteration and base our choice
+                    // on this criteria)
+                    auto readers = node->outNodes();
+                    const auto &candidate = ade::util::find_if(readers, [&](ade::NodeHandle nh) {
+                        const auto &fu = fg.metadata(nh).get<FluidUnit>();
+                        return fu.border_size == fd.border_size;
+                    });
+                    GAPI_Assert(candidate != readers.end());
+
+                    const auto &fu = fg.metadata(*candidate).get<FluidUnit>();
+                    fd.border = fu.border;
+                }
+
+                if (fd.border)
+                {
+                    GModel::log(g, node, "Border type: " + std::to_string(fd.border->type), node);
+                }
+            }
+        }
+    });
+    ectx.addPass("exec", "init_view_borders", [](ade::passes::PassContext &ctx)
+    {
+        GModel::Graph g(ctx.graph);
+        if (!GModel::isActive(g, cv::gapi::fluid::backend()))  // FIXME: Rearchitect this!
+            return;
+
+        GFluidModel fg(ctx.graph);
+        for (auto node : g.nodes())
+        {
+            if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
+            {
+                auto &fd = fg.metadata(node).get<FluidData>();
+                for (auto out_edge : node->outEdges())
+                {
+                    const auto &fu = fg.metadata(out_edge->dstNode()).get<FluidUnit>();
+
+                    // There is no need in own storage for view if it's border is
+                    // the same as the buffer's (view can have equal or smaller border
+                    // size in this case)
+                    if (fu.border_size == 0 ||
+                        (fu.border && fd.border && (*fu.border == *fd.border)))
+                    {
+                        GAPI_Assert(fu.border_size <= fd.border_size);
+                        fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{false});
+                    }
+                    else
+                    {
+                        fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{true});
+                        GModel::log(g, out_edge, "OwnBufferStorage: true");
+                    }
+                }
+            }
+        }
+    });
+}
diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/modules/gapi/src/backends/fluid/gfluidbackend.hpp
new file mode 100644 (file)
index 0000000..f444640
--- /dev/null
@@ -0,0 +1,128 @@
+// 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_FLUID_BACKEND_HPP
+#define OPENCV_GAPI_FLUID_BACKEND_HPP
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gproto.hpp"
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+
+// PRIVATE STUFF!
+#include "backends/common/gbackend.hpp"
+#include "compiler/gislandmodel.hpp"
+
+namespace cv { namespace gimpl {
+
+struct FluidUnit
+{
+    static const char *name() { return "FluidKernel"; }
+    GFluidKernel k;
+    gapi::fluid::BorderOpt border;
+    int border_size;
+    int line_consumption;
+    double ratio;
+};
+
+struct FluidUseOwnBorderBuffer
+{
+    static const char *name() { return "FluidUseOwnBorderBuffer"; }
+    bool use;
+};
+
+struct FluidData
+{
+    static const char *name() { return "FluidData"; }
+
+    // FIXME: This structure starts looking like "FluidBuffer" meta
+    int latency         =  0;
+    int skew            =  0;
+    int max_consumption =  1;
+    int border_size     =  0;
+    int lpi_write       =  1;
+    gapi::fluid::BorderOpt border;
+};
+
+struct FluidAgent
+{
+public:
+    virtual ~FluidAgent() = default;
+    FluidAgent(const ade::Graph &g, ade::NodeHandle nh);
+
+    GFluidKernel k;
+    ade::NodeHandle op_handle; // FIXME: why it is here??//
+    std::string op_name;
+
+    // <  0 - not a buffer
+    // >= 0 - a buffer with RcID
+    std::vector<int> in_buffer_ids;
+    std::vector<int> out_buffer_ids;
+
+    cv::GArgs in_args;
+    std::vector<cv::gapi::fluid::View>   in_views; // sparce list of IN views
+    std::vector<cv::gapi::fluid::Buffer*> out_buffers;
+
+    // FIXME Current assumption is that outputs have EQUAL SIZES
+    int m_outputLines = 0;
+    int m_producedLines = 0;
+
+    double m_ratio = 0.0f;
+
+    // Execution methods
+    void reset();
+    bool canWork() const;
+    bool canRead() const;
+    bool canWrite() const;
+    void doWork();
+    bool done() const;
+
+    void debug(std::ostream& os);
+
+private:
+    // FIXME!!!
+    // move to another class
+    virtual int firstWindow() const = 0;
+    virtual int nextWindow() const = 0;
+    virtual int linesRead()  const = 0;
+};
+
+class GFluidExecutable final: public GIslandExecutable
+{
+    const ade::Graph &m_g;
+    GModel::ConstGraph m_gm;
+
+    std::vector<std::unique_ptr<FluidAgent>> m_agents;
+    std::vector<cv::gapi::fluid::Buffer> m_buffers;
+
+    using Magazine = detail::magazine<cv::gapi::own::Scalar>;
+    Magazine m_res;
+
+    std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch)
+    std::vector<std::size_t> m_scratch_users;
+    std::vector<cv::gapi::fluid::View> m_views;
+
+    std::vector<cv::gapi::own::Rect> m_outputRois;
+
+    std::unordered_map<int, std::size_t> m_id_map; // GMat id -> buffer idx map
+
+    void bindInArg (const RcDesc &rc, const GRunArg &arg);
+    void bindOutArg(const RcDesc &rc, const GRunArgP &arg);
+    void packArg   (GArg &in_arg, const GArg &op_arg);
+
+public:
+    GFluidExecutable(const ade::Graph &g,
+                     const std::vector<ade::NodeHandle> &nodes,
+                     const std::vector<cv::gapi::own::Rect> &outputRois);
+
+    virtual void run(std::vector<InObj>  &&input_objs,
+                     std::vector<OutObj> &&output_objs) override;
+};
+}} // cv::gimpl
+
+
+#endif // OPENCV_GAPI_FLUID_BACKEND_HPP
diff --git a/modules/gapi/src/backends/fluid/gfluidbuffer.cpp b/modules/gapi/src/backends/fluid/gfluidbuffer.cpp
new file mode 100644 (file)
index 0000000..6614091
--- /dev/null
@@ -0,0 +1,746 @@
+// 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
+
+
+#include <iomanip>   // hex, dec (debug)
+
+#include "opencv2/gapi/own/convert.hpp"
+
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+#include "backends/fluid/gfluidbuffer_priv.hpp"
+#include "opencv2/gapi/opencv_includes.hpp"
+
+#include "backends/fluid/gfluidutils.hpp" // saturate
+
+namespace cv {
+namespace gapi {
+
+namespace fluid {
+bool operator == (const fluid::Border& b1, const fluid::Border& b2)
+{
+    return b1.type == b2.type && b1.value == b2.value;
+}
+} // namespace fluid
+
+// Fluid BorderHandler implementation /////////////////////////////////////////////////
+
+namespace {
+template<typename T>
+// Expected inputs:
+// row - row buffer allocated with border in mind (have memory for both image and border pixels)
+// length - size of the buffer with left and right borders included
+void fillBorderReplicateRow(uint8_t* row, int length, int chan, int borderSize)
+{
+    auto leftBorder  = reinterpret_cast<T*>(row);
+    auto rightBorder = leftBorder + (length - borderSize) * chan;
+    for (int b = 0; b < borderSize; b++)
+    {
+        for (int c = 0; c < chan; c++)
+        {
+            leftBorder [b*chan + c] = leftBorder [borderSize*chan + c];
+            rightBorder[b*chan + c] = rightBorder[-chan + c];
+        }
+    }
+}
+
+template<typename T>
+void fillBorderReflectRow(uint8_t* row, int length, int chan, int borderSize)
+{
+    auto leftBorder  = reinterpret_cast<T*>(row);
+    auto rightBorder = leftBorder + (length - borderSize) * chan;
+    for (int b = 0; b < borderSize; b++)
+    {
+        for (int c = 0; c < chan; c++)
+        {
+            leftBorder [b*chan + c] = leftBorder [(2*borderSize - b)*chan + c];
+            rightBorder[b*chan + c] = rightBorder[(-b - 2)*chan + c];
+        }
+    }
+}
+
+template<typename T>
+void fillConstBorderRow(uint8_t* row, int length, int chan, int borderSize, cv::gapi::own::Scalar borderValue)
+{
+    GAPI_DbgAssert(chan > 0 && chan <= 4);
+
+    auto leftBorder  = reinterpret_cast<T*>(row);
+    auto rightBorder = leftBorder + (length - borderSize) * chan;
+    for (int b = 0; b < borderSize; b++)
+    {
+        for (int c = 0; c < chan; c++)
+        {
+            leftBorder [b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
+            rightBorder[b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
+        }
+    }
+}
+
+// Fills const border pixels in the whole mat
+void fillBorderConstant(int borderSize, cv::gapi::own::Scalar borderValue, cv::Mat& mat)
+{
+    // cv::Scalar can contain maximum 4 chan
+    GAPI_Assert(mat.channels() > 0 && mat.channels() <= 4);
+
+    auto getFillBorderRowFunc = [&](int type) {
+        switch(type)
+        {
+        case CV_8U:  return &fillConstBorderRow< uint8_t>; break;
+        case CV_16S: return &fillConstBorderRow< int16_t>; break;
+        case CV_16U: return &fillConstBorderRow<uint16_t>; break;
+        case CV_32F: return &fillConstBorderRow< float  >; break;
+        default: CV_Assert(false); return &fillConstBorderRow<uint8_t>;
+        }
+    };
+
+    auto fillBorderRow = getFillBorderRowFunc(mat.depth());
+    for (int y = 0; y < mat.rows; y++)
+    {
+        fillBorderRow(mat.ptr(y), mat.cols, mat.channels(), borderSize, borderValue);
+    }
+}
+} // anonymous namespace
+
+fluid::BorderHandler::BorderHandler(int border_size)
+{
+    CV_Assert(border_size > 0);
+    m_border_size = border_size;
+}
+
+template <int BorderType>
+fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)
+    : BorderHandler(border_size)
+{
+    auto getFillBorderRowFunc = [&](int border, int dataType) {
+        if (border == cv::BORDER_REPLICATE)
+        {
+            switch(dataType)
+            {
+            case CV_8U:  return &fillBorderReplicateRow< uint8_t>; break;
+            case CV_16S: return &fillBorderReplicateRow< int16_t>; break;
+            case CV_16U: return &fillBorderReplicateRow<uint16_t>; break;
+            case CV_32F: return &fillBorderReplicateRow< float  >; break;
+            default: CV_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>;
+            }
+        }
+        else if (border == cv::BORDER_REFLECT_101)
+        {
+            switch(dataType)
+            {
+            case CV_8U:  return &fillBorderReflectRow< uint8_t>; break;
+            case CV_16S: return &fillBorderReflectRow< int16_t>; break;
+            case CV_16U: return &fillBorderReflectRow<uint16_t>; break;
+            case CV_32F: return &fillBorderReflectRow< float  >; break;
+            default: CV_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>;
+            }
+        }
+        else
+        {
+            CV_Assert(!"Unsupported border type");
+            return &fillBorderReflectRow<uint8_t>;
+        }
+    };
+
+    m_fill_border_row = getFillBorderRowFunc(BorderType, data_type);
+}
+
+namespace {
+template <int BorderType> int getBorderIdx(int log_idx, int desc_height);
+
+template<> int getBorderIdx<cv::BORDER_REPLICATE>(int log_idx, int desc_height)
+{
+    return log_idx < 0 ? 0 : desc_height - 1;
+}
+
+template<> int getBorderIdx<cv::BORDER_REFLECT_101>(int log_idx, int desc_height)
+{
+    return log_idx < 0 ? -log_idx : 2*(desc_height - 1) - log_idx;
+}
+} // namespace
+
+template <int BorderType>
+const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const BufferStorageWithBorder& data, int desc_height) const
+{
+    auto idx = getBorderIdx<BorderType>(log_idx, desc_height);
+    return data.ptr(idx);
+}
+
+fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width)
+    : BorderHandler(border_size), m_border_value(border_value)
+{
+    m_const_border.create(1, desc_width + 2*m_border_size, data_type);
+    m_const_border = cv::gapi::own::to_ocv(border_value);
+}
+
+const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const
+{
+    return m_const_border.ptr(0, m_border_size);
+}
+
+void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data) const
+{
+    cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());
+}
+
+template <int BorderType>
+void fluid::BorderHandlerT<BorderType>::updateBorderPixels(BufferStorageWithBorder &data, int startLine, int nLines) const
+{
+    auto& mat   = data.data();
+    auto length = mat.cols;
+    auto chan   = mat.channels();
+
+    for (int l = startLine; l < startLine + nLines; l++)
+    {
+        auto row = mat.ptr(data.physIdx(l));
+        m_fill_border_row(row, length, chan, m_border_size);
+    }
+}
+
+std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const
+{
+    return m_const_border.total() * m_const_border.elemSize();
+}
+
+// Fluid BufferStorage implementation //////////////////////////////////////////
+void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype, int border_size, Border border)
+{
+    auto width = (desc_width + 2*border_size);
+    m_data.create(capacity, width, dtype);
+
+    switch(border.type)
+    {
+    case cv::BORDER_CONSTANT:
+        m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value, dtype, desc_width)); break;
+    case cv::BORDER_REPLICATE:
+        m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;
+    case cv::BORDER_REFLECT_101:
+        m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REFLECT_101>(border_size, dtype)); break;
+    default:
+        CV_Assert(false);
+    }
+
+    m_borderHandler->fillCompileTimeBorder(*this);
+}
+
+void fluid::BufferStorageWithoutBorder::create(int capacity, int desc_width, int dtype)
+{
+    auto width = desc_width;
+    m_data.create(capacity, width, dtype);
+
+    m_is_virtual = true;
+}
+
+const uint8_t* fluid::BufferStorageWithBorder::inLineB(int log_idx, int desc_height) const
+{
+    if (log_idx < 0 || log_idx >= desc_height)
+    {
+        return m_borderHandler->inLineB(log_idx, *this, desc_height);
+    }
+    else
+    {
+        return ptr(log_idx);
+    }
+}
+
+const uint8_t* fluid::BufferStorageWithoutBorder::inLineB(int log_idx, int /*desc_height*/) const
+{
+    return ptr(log_idx);
+}
+
+static void copyWithoutBorder(const cv::Mat& src, int src_border_size, cv::Mat& dst, int dst_border_size, int startSrcLine, int startDstLine, int lpi)
+{
+    // FIXME use cv::gapi::own::Rect when implement cv::gapi::own::Mat
+    auto subSrc = src(cv::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi});
+    auto subDst = dst(cv::Rect{dst_border_size, startDstLine, dst.cols - 2*dst_border_size, lpi});
+
+    subSrc.copyTo(subDst);
+}
+
+void fluid::BufferStorageWithoutBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
+{
+    for (int l = startLine; l < startLine + nLines; l++)
+    {
+        copyWithoutBorder(m_data, 0, dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
+    }
+}
+
+void fluid::BufferStorageWithBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
+{
+    // Copy required lpi lines line by line (to avoid wrap if invoked for multiple lines)
+    for (int l = startLine; l < startLine + nLines; l++)
+    {
+        copyWithoutBorder(m_data, borderSize(), dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
+    }
+}
+
+// FIXME? remember parent and remove src parameter?
+void fluid::BufferStorageWithBorder::updateBeforeRead(int startLine, int nLines, const BufferStorage& src)
+{
+    // TODO:
+    // Cover with tests!!
+    // (Ensure that there are no redundant copies done
+    // and only required (not fetched before) lines are copied)
+
+    GAPI_DbgAssert(startLine >= 0);
+
+    src.copyTo(*this, startLine, nLines);
+    m_borderHandler->updateBorderPixels(*this, startLine, nLines);
+}
+
+void fluid::BufferStorageWithoutBorder::updateBeforeRead(int /*startLine*/, int /*lpi*/, const BufferStorage& /*src*/)
+{
+    /* nothing */
+}
+
+void fluid::BufferStorageWithBorder::updateAfterWrite(int startLine, int nLines)
+{
+    // FIXME?
+    // Actually startLine + nLines can be > logical height so
+    // redundant end lines which will never be read
+    // can be filled in the ring buffer
+    m_borderHandler->updateBorderPixels(*this, startLine, nLines);
+}
+
+void fluid::BufferStorageWithoutBorder::updateAfterWrite(int /*startLine*/, int /*lpi*/)
+{
+    /* nothing */
+}
+
+size_t fluid::BufferStorageWithBorder::size() const
+{
+    return m_data.total()*m_data.elemSize() + m_borderHandler->size();
+}
+
+size_t fluid::BufferStorageWithoutBorder::size() const
+{
+    return m_data.total()*m_data.elemSize();
+}
+
+namespace fluid {
+namespace {
+std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
+                                                    int border_size, fluid::BorderOpt border);
+std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
+                                                    int border_size, fluid::BorderOpt border)
+{
+    if (border)
+    {
+        std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);
+        storage->create(capacity, desc_width, type, border_size, border.value());
+        return std::move(storage);
+    }
+
+    std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
+    storage->create(capacity, desc_width, type);
+    return std::move(storage);
+}
+
+std::unique_ptr<BufferStorage> createStorage(const cv::Mat& data, cv::gapi::own::Rect roi);
+std::unique_ptr<BufferStorage> createStorage(const cv::Mat& data, cv::gapi::own::Rect roi)
+{
+    std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
+    storage->attach(data, roi);
+    return std::move(storage);
+}
+} // namespace
+} // namespace fluid
+
+// Fluid View implementation ///////////////////////////////////////////////////
+
+void fluid::View::Priv::reset(int linesForFirstIteration)
+{
+    GAPI_DbgAssert(m_p);
+
+    m_lines_next_iter = linesForFirstIteration;
+    m_read_caret = m_p->priv().readStart();
+}
+
+void fluid::View::Priv::readDone(int linesRead, int linesForNextIteration)
+{
+    CV_DbgAssert(m_p);
+    m_read_caret += linesRead;
+    m_read_caret %= m_p->meta().size.height;
+    m_lines_next_iter = linesForNextIteration;
+}
+
+bool fluid::View::Priv::ready() const
+{
+    auto lastWrittenLine = m_p->priv().writeStart() + m_p->linesReady();
+    // + bottom border
+    if (lastWrittenLine == m_p->meta().size.height) lastWrittenLine += m_border_size;
+    // + top border
+    lastWrittenLine += m_border_size;
+
+    auto lastRequiredLine = m_read_caret + m_lines_next_iter;
+
+    return lastWrittenLine >= lastRequiredLine;
+}
+
+fluid::ViewPrivWithoutOwnBorder::ViewPrivWithoutOwnBorder(const Buffer *parent, int borderSize)
+{
+    CV_Assert(parent);
+    m_p           = parent;
+    m_border_size = borderSize;
+}
+
+const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const
+{
+    GAPI_DbgAssert(m_p);
+
+    const auto &p_priv = m_p->priv();
+
+    CV_Assert(   index >= -m_border_size
+              && index <  -m_border_size + m_lines_next_iter);
+
+    const int log_idx = m_read_caret + index;
+
+    return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);
+}
+
+fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int lineConsumption, int borderSize, Border border)
+{
+    GAPI_Assert(parent);
+    m_p           = parent;
+    m_border_size = borderSize;
+
+    auto desc = m_p->meta();
+    int  type = CV_MAKETYPE(desc.depth, desc.chan);
+    m_own_storage.create(lineConsumption, desc.size.width, type, borderSize, border);
+}
+
+void fluid::ViewPrivWithOwnBorder::prepareToRead()
+{
+    int startLine = 0;
+    int nLines = 0;
+
+    if (m_read_caret == m_p->priv().readStart())
+    {
+        // Need to fetch full window on the first iteration
+        startLine = (m_read_caret > m_border_size) ? m_read_caret - m_border_size : 0;
+        nLines = m_lines_next_iter;
+    }
+    else
+    {
+        startLine = m_read_caret + m_border_size;
+        nLines = m_lines_next_iter - 2*m_border_size;
+    }
+
+    m_own_storage.updateBeforeRead(startLine, nLines, m_p->priv().storage());
+}
+
+std::size_t fluid::ViewPrivWithOwnBorder::size() const
+{
+    GAPI_DbgAssert(m_p);
+    return m_own_storage.size();
+}
+
+const uint8_t* fluid::ViewPrivWithOwnBorder::InLineB(int index) const
+{
+    GAPI_DbgAssert(m_p);
+
+    GAPI_Assert( index >= -m_border_size
+              && index <  -m_border_size + m_lines_next_iter);
+
+    const int log_idx = m_read_caret + index;
+
+    return m_own_storage.inLineB(log_idx, m_p->meta().size.height);
+}
+
+const uint8_t* fluid::View::InLineB(int index) const
+{
+    return m_priv->InLineB(index);
+}
+
+fluid::View::operator bool() const
+{
+    return m_priv != nullptr && m_priv->m_p != nullptr;
+}
+
+int fluid::View::length() const
+{
+    return m_priv->m_p->length();
+}
+
+bool fluid::View::ready() const
+{
+    return m_priv->ready();
+}
+
+int fluid::View::y() const
+{
+    return m_priv->m_read_caret - m_priv->m_border_size;
+}
+
+cv::GMatDesc fluid::View::meta() const
+{
+    // FIXME: cover with test!
+    return m_priv->m_p->meta();
+}
+
+fluid::View::Priv& fluid::View::priv()
+{
+    return *m_priv;
+}
+
+const fluid::View::Priv& fluid::View::priv() const
+{
+    return *m_priv;
+}
+
+// Fluid Buffer implementation /////////////////////////////////////////////////
+
+fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
+    : m_readStart(read_start)
+    , m_roi(roi)
+{}
+
+void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
+                               int line_consumption,
+                               int border_size,
+                               int skew,
+                               int wlpi,
+                               int readStartPos,
+                               cv::gapi::own::Rect roi)
+{
+    GAPI_Assert(m_line_consumption == -1);
+    GAPI_Assert(line_consumption > 0);
+
+    m_line_consumption = line_consumption;
+    m_border_size      = border_size;
+    m_skew             = skew;
+    m_writer_lpi       = wlpi;
+    m_desc             = desc;
+    m_readStart        = readStartPos;
+    m_roi              = roi;
+}
+
+void fluid::Buffer::Priv::allocate(BorderOpt border)
+{
+    GAPI_Assert(!m_storage);
+
+    // Init physical buffer
+
+    // FIXME? combine with skew?
+    auto maxRead    = m_line_consumption + m_skew;
+    auto maxWritten = m_writer_lpi;
+
+    auto max = std::max(maxRead, maxWritten);
+    auto min = std::min(maxRead, maxWritten);
+
+    // FIXME:
+    // Fix the deadlock (completely)!!!
+    auto data_height = static_cast<int>(std::ceil((double)max / min) * min);
+
+    m_storage = createStorage(data_height,
+                              m_desc.size.width,
+                              CV_MAKETYPE(m_desc.depth, m_desc.chan),
+                              m_border_size,
+                              border);
+
+    // Finally, initialize carets
+    m_write_caret = 0;
+}
+
+void fluid::Buffer::Priv::bindTo(const cv::Mat &data, bool is_input)
+{
+    // FIXME: move all these fields into a separate structure
+    GAPI_Assert(m_skew == 0);
+    GAPI_Assert(m_desc == cv::descr_of(data));
+    if ( is_input) CV_Assert(m_writer_lpi  == 1);
+
+    m_storage = createStorage(data, m_roi);
+
+    m_is_input    = is_input;
+    m_write_caret = is_input ? writeEnd(): writeStart();
+    // NB: views remain the same!
+}
+
+bool fluid::Buffer::Priv::full() const
+{
+    int slowest_y = writeEnd();
+    if (!m_views.empty())
+    {
+        // reset with maximum possible value and then find minimum
+        slowest_y = m_desc.size.height;
+        for (const auto &v : m_views) slowest_y = std::min(slowest_y, v.y());
+    }
+
+    return m_write_caret + lpi() - slowest_y > m_storage->rows();
+}
+
+void fluid::Buffer::Priv::writeDone()
+{
+    // There are possible optimizations which can be done to fill a border values
+    // in compile time of the graph (for example border is const),
+    // so there is no need to update border values after each write.
+    // If such optimizations weren't applied, fill border for lines
+    // which have been just written
+    m_storage->updateAfterWrite(m_write_caret, m_writer_lpi);
+
+    // Final write may produce less LPI, so
+    // write caret may exceed logical buffer size
+    m_write_caret += m_writer_lpi;
+    // FIXME: add consistency check!
+}
+
+void fluid::Buffer::Priv::reset()
+{
+    m_write_caret = m_is_input ? writeEnd() : writeStart();
+}
+
+int fluid::Buffer::Priv::size() const
+{
+    std::size_t view_sz = 0;
+    for (const auto &v : m_views) view_sz += v.priv().size();
+
+    auto total = view_sz;
+    if (m_storage) total += m_storage->size();
+
+    // FIXME: Change API to return size_t!!!
+    return static_cast<int>(total);
+}
+
+int fluid::Buffer::Priv::linesReady() const
+{
+    if (m_is_input)
+    {
+        return m_storage->rows();
+    }
+    else
+    {
+        const int writes = std::min(m_write_caret - writeStart(), outputLines());
+        return writes;
+    }
+}
+
+uint8_t* fluid::Buffer::Priv::OutLineB(int index)
+{
+    GAPI_Assert(index >= 0 && index < m_writer_lpi);
+
+    return m_storage->ptr(m_write_caret + index);
+}
+
+int fluid::Buffer::Priv::lpi() const
+{
+    // FIXME:
+    // m_write_caret can be greater than m_writeRoi.y + m_writeRoi.height, so return value can be negative !!!
+    return std::min(writeEnd() - m_write_caret, m_writer_lpi);
+}
+
+fluid::Buffer::Buffer()
+    : m_priv(new Priv())
+{
+}
+
+fluid::Buffer::Buffer(const cv::GMatDesc &desc)
+    : m_priv(new Priv())
+{
+    int lineConsumption = 1;
+    int border = 0, skew = 0, wlpi = 1, readStart = 0;
+    cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
+    m_priv->init(desc, lineConsumption, border, skew, wlpi, readStart, roi);
+    m_priv->allocate({});
+}
+
+fluid::Buffer::Buffer(const cv::GMatDesc &desc,
+                      int max_line_consumption,
+                      int border_size,
+                      int skew,
+                      int wlpi,
+                      BorderOpt border)
+    : m_priv(new Priv())
+{
+    int readStart = 0;
+    cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
+    m_priv->init(desc, max_line_consumption, border_size, skew, wlpi, readStart, roi);
+    m_priv->allocate(border);
+}
+
+fluid::Buffer::Buffer(const cv::Mat &data, bool is_input)
+    : m_priv(new Priv())
+{
+    int lineConsumption = 1;
+    int border = 0, skew = 0, wlpi = 1, readStart = 0;
+    cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};
+    m_priv->init(descr_of(data), lineConsumption, border, skew, wlpi, readStart, roi);
+    m_priv->bindTo(data, is_input);
+}
+
+uint8_t* fluid::Buffer::Buffer::OutLineB(int index)
+{
+    return m_priv->OutLineB(index);
+}
+
+int fluid::Buffer::linesReady() const
+{
+    return m_priv->linesReady();
+}
+
+int fluid::Buffer::length() const
+{
+    return meta().size.width;
+}
+
+int fluid::Buffer::lpi() const
+{
+    return m_priv->lpi();
+}
+
+cv::GMatDesc fluid::Buffer::meta() const
+{
+    return m_priv->meta();
+}
+
+fluid::View::View(Priv* p)
+    : m_priv(p)
+{ /* nothing */ }
+
+fluid::View fluid::Buffer::mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage)
+{
+    // FIXME: logic outside of Priv (because View takes pointer to Buffer)
+    auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, lineConsumption, borderSize, border.value()))
+                           : View(new ViewPrivWithoutOwnBorder(this, borderSize));
+    m_priv->addView(view);
+    return view;
+}
+
+void fluid::debugBufferPriv(const fluid::Buffer& buffer, std::ostream &os)
+{
+    // FIXME Use cv::gapi::own Size and Rect with operator<<, when merged ADE-285
+    const auto& p = buffer.priv();
+    os << "Fluid buffer " << std::hex << &buffer << std::dec
+       << " " << p.m_desc.size.width << " x " << p.m_desc.size.height << "]"
+       << " readStart:" << p.m_readStart
+       << " roi:" << "[" << p.m_roi.width << " x " << p.m_roi.height << " from (" << p.m_roi.x << ", " << p.m_roi.y << ")]"
+       <<" (phys " << "[" << p.storage().cols() << " x " <<  p.storage().rows() << "]" << ") :"
+       << "  w: " << p.m_write_caret
+       << ", r: [";
+    for (const auto &v : p.m_views) { os << &v.priv() << ":" << v.y() << " "; }
+    os << "], avail: " << buffer.linesReady()
+       << std::endl;
+}
+
+void fluid::Buffer::debug(std::ostream &os) const
+{
+    debugBufferPriv(*this, os);
+}
+
+fluid::Buffer::Priv& fluid::Buffer::priv()
+{
+    return *m_priv;
+}
+
+const fluid::Buffer::Priv& fluid::Buffer::priv() const
+{
+    return *m_priv;
+}
+
+int fluid::Buffer::y() const
+{
+    return m_priv->y();
+}
+
+} // namespace cv::gapi
+} // namespace cv
diff --git a/modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp b/modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp
new file mode 100644 (file)
index 0000000..f8b03d1
--- /dev/null
@@ -0,0 +1,298 @@
+// 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_FLUID_BUFFER_PRIV_HPP
+#define OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP
+
+#include <vector>
+
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+#include "opencv2/gapi/own/convert.hpp" // cv::gapi::own::to_ocv
+
+namespace cv {
+namespace gapi {
+namespace fluid {
+
+class BufferStorageWithBorder;
+
+class BorderHandler
+{
+protected:
+    int m_border_size;
+
+public:
+    BorderHandler(int border_size);
+    virtual ~BorderHandler() = default;
+    virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const = 0;
+
+    // Fills border pixels after buffer allocation (if possible (for const border))
+    virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const { /* nothing */ }
+
+    // Fills required border lines
+    virtual void updateBorderPixels(BufferStorageWithBorder& /*data*/, int /*startLine*/, int /*lpi*/) const { /* nothing */ }
+
+    inline int borderSize() const { return m_border_size; }
+    virtual std::size_t size() const { return 0; }
+};
+
+template<int BorderType>
+class BorderHandlerT : public BorderHandler
+{
+    std::function<void(uint8_t*,int,int,int)> m_fill_border_row;
+public:
+    BorderHandlerT(int border_size, int data_type);
+    virtual void updateBorderPixels(BufferStorageWithBorder& data, int startLine, int lpi) const override;
+    virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override;
+};
+
+template<>
+class BorderHandlerT<cv::BORDER_CONSTANT> : public BorderHandler
+{
+    cv::gapi::own::Scalar m_border_value;
+    cv::Mat m_const_border;
+
+public:
+    BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width);
+    virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override;
+    virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const override;
+    virtual std::size_t size() const override;
+};
+
+class BufferStorage
+{
+protected:
+    cv::Mat m_data;
+
+public:
+    virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const = 0;
+
+    virtual ~BufferStorage() = default;
+
+    virtual const uint8_t* ptr(int idx) const = 0;
+    virtual       uint8_t* ptr(int idx) = 0;
+
+    inline bool empty() const { return m_data.empty(); }
+
+    inline const cv::Mat& data() const { return m_data; }
+    inline       cv::Mat& data()       { return m_data; }
+
+    inline int rows() const { return m_data.rows; }
+    inline int cols() const { return m_data.cols; }
+    inline int type() const { return m_data.type(); }
+
+    virtual const uint8_t* inLineB(int log_idx, int desc_height) const = 0;
+
+    // FIXME? remember parent and remove src parameter?
+    virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage& src) = 0;
+    virtual void updateAfterWrite(int startLine, int nLines) = 0;
+
+    virtual int physIdx(int logIdx) const = 0;
+
+    virtual size_t size() const = 0;
+};
+
+class BufferStorageWithoutBorder final : public BufferStorage
+{
+    bool m_is_virtual = true;
+    cv::gapi::own::Rect m_roi;
+
+public:
+    virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const override;
+
+    inline virtual const uint8_t* ptr(int idx) const override
+    {
+        GAPI_DbgAssert((m_is_virtual && m_roi == cv::gapi::own::Rect{}) || (!m_is_virtual && m_roi != cv::gapi::own::Rect{}));
+        return m_data.ptr(physIdx(idx), 0);
+    }
+    inline virtual uint8_t* ptr(int idx) override
+    {
+        GAPI_DbgAssert((m_is_virtual && m_roi == cv::gapi::own::Rect{}) || (!m_is_virtual && m_roi != cv::gapi::own::Rect{}));
+        return m_data.ptr(physIdx(idx), 0);
+    }
+
+    inline void attach(const cv::Mat& _data, const cv::gapi::own::Rect& _roi)
+    {
+        m_data = _data(cv::gapi::own::to_ocv(_roi));
+        m_roi = _roi;
+        m_is_virtual = false;
+    }
+
+    void create(int capacity, int desc_width, int type);
+
+    inline virtual const uint8_t* inLineB(int log_idx, int desc_height) const override;
+
+    virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage& src) override;
+    virtual void updateAfterWrite(int startLine, int nLines) override;
+
+    inline virtual int physIdx(int logIdx) const override { return (logIdx - m_roi.y) % m_data.rows; }
+
+    virtual size_t size() const override;
+};
+
+class BufferStorageWithBorder final: public BufferStorage
+{
+    std::unique_ptr<BorderHandler> m_borderHandler;
+
+public:
+    inline int borderSize() const { return m_borderHandler->borderSize(); }
+
+    virtual void copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const override;
+
+    inline virtual const uint8_t* ptr(int idx) const override
+    {
+        return m_data.ptr(physIdx(idx), borderSize());
+    }
+    inline virtual uint8_t* ptr(int idx) override
+    {
+        return m_data.ptr(physIdx(idx), borderSize());
+    }
+
+    void create(int capacity, int desc_width, int type, int border_size, Border border);
+
+    virtual const uint8_t* inLineB(int log_idx, int desc_height) const override;
+
+    virtual void updateBeforeRead(int startLine, int nLines, const BufferStorage &src) override;
+    virtual void updateAfterWrite(int startLine, int nLines) override;
+
+    inline virtual int physIdx(int logIdx) const override { return logIdx % m_data.rows; }
+
+    virtual size_t size() const override;
+};
+
+// FIXME: GAPI_EXPORTS is used here only to access internal methods
+// like readDone/writeDone in low-level tests
+class GAPI_EXPORTS View::Priv
+{
+    friend class View;
+protected:
+    const Buffer *m_p           = nullptr; // FIXME replace with weak_ptr
+    int           m_read_caret  = -1;
+    int           m_lines_next_iter = -1;
+    int m_border_size = -1;
+
+public:
+    virtual ~Priv() = default;
+    // API used by actors/backend
+
+    virtual void prepareToRead() = 0;
+
+    void readDone(int linesRead, int linesForNextIteration);
+    void reset(int linesForFirstIteration);
+
+    virtual std::size_t size() const = 0;
+
+    // Does the view have enough unread lines for next iteration
+    bool ready() const;
+
+    // API used (indirectly) by user code
+    virtual const uint8_t* InLineB(int index) const = 0;
+};
+
+class ViewPrivWithoutOwnBorder final : public View::Priv
+{
+public:
+    // API used by actors/backend
+    ViewPrivWithoutOwnBorder(const Buffer *p, int borderSize);
+
+    virtual void prepareToRead() override { /* nothing */ }
+
+    virtual std::size_t size() const override { return 0; }
+
+    // API used (indirectly) by user code
+    virtual const uint8_t* InLineB(int index) const override;
+};
+
+class ViewPrivWithOwnBorder final : public View::Priv
+{
+    BufferStorageWithBorder m_own_storage;
+
+public:
+    // API used by actors/backend
+    ViewPrivWithOwnBorder(const Buffer *p, int lineCapacity, int borderSize, Border border);
+
+    virtual void prepareToRead() override;
+    virtual std::size_t size() const override;
+
+    // API used (indirectly) by user code
+    virtual const uint8_t* InLineB(int index) const override;
+};
+
+void debugBufferPriv(const Buffer& buffer, std::ostream &os);
+
+// FIXME: GAPI_EXPORTS is used here only to access internal methods
+// like readDone/writeDone in low-level tests
+class GAPI_EXPORTS Buffer::Priv
+{
+    int m_line_consumption = -1;
+    int m_border_size      = -1;
+    int m_skew             = -1;
+    int m_writer_lpi       =  1;
+
+    cv::GMatDesc m_desc    = cv::GMatDesc{-1,-1,{-1,-1}};
+    bool m_is_input        = false;
+
+    int m_write_caret      = -1;
+
+    std::vector<View> m_views;
+
+    std::unique_ptr<BufferStorage> m_storage;
+
+    // Coordinate starting from which this buffer is assumed
+    // to be read (with border not being taken into account)
+    int m_readStart;
+    cv::gapi::own::Rect m_roi;
+
+    friend void debugBufferPriv(const Buffer& p, std::ostream &os);
+
+public:
+    Priv() = default;
+    Priv(int read_start, cv::gapi::own::Rect roi);
+
+    inline const BufferStorage& storage() const { return *m_storage.get(); }
+
+    // API used by actors/backend
+    void init(const cv::GMatDesc &desc,
+              int line_consumption,
+              int border_size,
+              int skew,
+              int wlpi,
+              int readStart,
+              cv::gapi::own::Rect roi);
+
+    void allocate(BorderOpt border);
+    void bindTo(const cv::Mat &data, bool is_input);
+
+    void addView(const View& view) { m_views.push_back(view); }
+
+    const GMatDesc meta() const { return m_desc; }
+
+    bool full() const;
+    void writeDone();
+    void reset();
+    int size() const;
+
+    int linesReady() const;
+
+    inline int y() const { return m_write_caret; }
+
+    inline int writer_lpi()     const { return m_writer_lpi; }
+
+    // API used (indirectly) by user code
+    uint8_t* OutLineB(int index = 0);
+    int lpi() const;
+
+    inline int readStart()   const { return m_readStart; }
+    inline int writeStart()  const { return m_roi.y; }
+    inline int writeEnd()    const { return m_roi.y + m_roi.height; }
+    inline int outputLines() const { return m_roi.height; }
+};
+
+} // namespace cv::gapi::fluid
+} // namespace cv::gapi
+} // namespace cv
+
+#endif // OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP
diff --git a/modules/gapi/src/backends/fluid/gfluidcore.cpp b/modules/gapi/src/backends/fluid/gfluidcore.cpp
new file mode 100644 (file)
index 0000000..f6d1e5b
--- /dev/null
@@ -0,0 +1,2121 @@
+// 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
+
+
+#include "opencv2/gapi/own/assert.hpp"
+#include "opencv2/core/traits.hpp"
+#include "opencv2/core/hal/intrin.hpp"
+
+#include "opencv2/gapi/core.hpp"
+
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+#include "gfluidbuffer_priv.hpp"
+#include "gfluidbackend.hpp"
+#include "gfluidutils.hpp"
+#include "gfluidcore.hpp"
+
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+
+namespace cv {
+namespace gapi {
+namespace fluid {
+
+//---------------------
+//
+// Arithmetic functions
+//
+//---------------------
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST absdiff(SRC1 x, SRC2 y)
+{
+    auto result = x > y? x - y: y - x;
+    return saturate<DST>(result, roundf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST addWeighted(SRC1 src1, SRC2 src2, float alpha, float beta, float gamma)
+{
+    float dst = src1*alpha + src2*beta + gamma;
+    return saturate<DST>(dst, roundf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST add(SRC1 x, SRC2 y)
+{
+    return saturate<DST>(x + y, roundf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST sub(SRC1 x, SRC2 y)
+{
+    return saturate<DST>(x - y, roundf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST subr(SRC1 x, SRC2 y)
+{
+    return saturate<DST>(y - x, roundf); // reverse: y - x
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST mul(SRC1 x, SRC2 y, float scale=1)
+{
+    auto result = scale * x * y;
+    return saturate<DST>(result, rintf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST div(SRC1 x, SRC2 y, float scale=1)
+{
+    // like OpenCV: returns 0, if y=0
+    auto result = y? scale * x / y: 0;
+    return saturate<DST>(result, rintf);
+}
+
+template<typename DST, typename SRC1, typename SRC2>
+static inline DST divr(SRC1 x, SRC2 y, float scale=1)
+{
+    auto result = x? scale * y / x: 0; // reverse: y / x
+    return saturate<DST>(result, rintf);
+}
+
+//---------------------------
+//
+// Fluid kernels: addWeighted
+//
+//---------------------------
+
+template<typename DST, typename SRC1, typename SRC2>
+static void run_addweighted(Buffer &dst, const View &src1, const View &src2,
+                            double alpha, double beta, double gamma)
+{
+    static_assert(std::is_same<SRC1, SRC2>::value, "wrong types");
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+    int length = width * chan;
+
+    // NB: assume in/out types are not 64-bits
+    auto _alpha = static_cast<float>( alpha );
+    auto _beta  = static_cast<float>( beta  );
+    auto _gamma = static_cast<float>( gamma );
+
+    for (int l=0; l < length; l++)
+        out[l] = addWeighted<DST>(in1[l], in2[l], _alpha, _beta, _gamma);
+}
+
+GAPI_FLUID_KERNEL(GFluidAddW, cv::gapi::core::GAddW, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, double alpha, const View &src2,
+                                      double beta, double gamma, int /*dtype*/,
+                        Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP               __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_(uchar , ushort, ushort, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_(uchar ,  short,  short, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_( short,  short,  short, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_(ushort, ushort, ushort, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_( float, uchar , uchar , run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_( float, ushort, ushort, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+        BINARY_( float,  short,  short, run_addweighted, dst, src1, src2, alpha, beta, gamma);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//--------------------------
+//
+// Fluid kernels: +, -, *, /
+//
+//--------------------------
+
+enum Arithm { ARITHM_ABSDIFF, ARITHM_ADD, ARITHM_SUBTRACT, ARITHM_MULTIPLY, ARITHM_DIVIDE };
+
+template<typename DST, typename SRC1, typename SRC2>
+static void run_arithm(Buffer &dst, const View &src1, const View &src2, Arithm arithm,
+                       double scale=1)
+{
+    static_assert(std::is_same<SRC1, SRC2>::value, "wrong types");
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+    int length = width * chan;
+
+    // NB: assume in/out types are not 64-bits
+    float _scale = static_cast<float>( scale );
+
+    switch (arithm)
+    {
+    case ARITHM_ABSDIFF:
+        for (int l=0; l < length; l++)
+            out[l] = absdiff<DST>(in1[l], in2[l]);
+        break;
+    case ARITHM_ADD:
+        for (int l=0; l < length; l++)
+            out[l] = add<DST>(in1[l], in2[l]);
+        break;
+    case ARITHM_SUBTRACT:
+        for (int l=0; l < length; l++)
+            out[l] = sub<DST>(in1[l], in2[l]);
+        break;
+    case ARITHM_MULTIPLY:
+        for (int l=0; l < length; l++)
+            out[l] = mul<DST>(in1[l], in2[l], _scale);
+        break;
+    case ARITHM_DIVIDE:
+        for (int l=0; l < length; l++)
+            out[l] = div<DST>(in1[l], in2[l], _scale);
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported arithmetic operation");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidAdd, cv::gapi::core::GAdd, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, int /*dtype*/, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_(uchar ,  short,  short, run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_(uchar ,  float,  float, run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_( short,  short,  short, run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_( float, uchar , uchar , run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_( float,  short,  short, run_arithm, dst, src1, src2, ARITHM_ADD);
+        BINARY_( float,  float,  float, run_arithm, dst, src1, src2, ARITHM_ADD);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidSub, cv::gapi::core::GSub, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, int /*dtype*/, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_(uchar ,  short,  short, run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_(uchar ,  float,  float, run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_( short,  short,  short, run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_( float, uchar , uchar , run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_( float,  short,  short, run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+        BINARY_( float,  float,  float, run_arithm, dst, src1, src2, ARITHM_SUBTRACT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMul, cv::gapi::core::GMul, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, double scale, int /*dtype*/, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_(uchar ,  short,  short, run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_(uchar ,  float,  float, run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_( short,  short,  short, run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_( float, uchar , uchar , run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_( float,  short,  short, run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+        BINARY_( float,  float,  float, run_arithm, dst, src1, src2, ARITHM_MULTIPLY, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidDiv, cv::gapi::core::GDiv, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, double scale, int /*dtype*/, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_(uchar ,  short,  short, run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_(uchar ,  float,  float, run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_( short,  short,  short, run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_( float, uchar , uchar , run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_( float,  short,  short, run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+        BINARY_( float,  float,  float, run_arithm, dst, src1, src2, ARITHM_DIVIDE, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidAbsDiff, cv::gapi::core::GAbsDiff, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_arithm, dst, src1, src2, ARITHM_ABSDIFF);
+        BINARY_(ushort, ushort, ushort, run_arithm, dst, src1, src2, ARITHM_ABSDIFF);
+        BINARY_( short,  short,  short, run_arithm, dst, src1, src2, ARITHM_ABSDIFF);
+        BINARY_( float,  float,  float, run_arithm, dst, src1, src2, ARITHM_ABSDIFF);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//--------------------------------------
+//
+// Fluid kernels: +, -, *, / with Scalar
+//
+//--------------------------------------
+
+static inline v_uint16x8  v_add_16u(const v_uint16x8 &x, const v_uint16x8 &y) { return x + y; }
+static inline v_uint16x8  v_sub_16u(const v_uint16x8 &x, const v_uint16x8 &y) { return x - y; }
+static inline v_uint16x8 v_subr_16u(const v_uint16x8 &x, const v_uint16x8 &y) { return y - x; }
+
+static inline v_float32x4  v_add_32f(const v_float32x4 &x, const v_float32x4 &y) { return x + y; }
+static inline v_float32x4  v_sub_32f(const v_float32x4 &x, const v_float32x4 &y) { return x - y; }
+static inline v_float32x4 v_subr_32f(const v_float32x4 &x, const v_float32x4 &y) { return y - x; }
+
+static inline int  s_add_8u(uchar x, uchar y) { return x + y; }
+static inline int  s_sub_8u(uchar x, uchar y) { return x - y; }
+static inline int s_subr_8u(uchar x, uchar y) { return y - x; }
+
+static inline float  s_add_32f(float x, float y) { return x + y; }
+static inline float  s_sub_32f(float x, float y) { return x - y; }
+static inline float s_subr_32f(float x, float y) { return y - x; }
+
+// manual SIMD if important case 8UC3
+static void run_arithm_s3(uchar out[], const uchar in[], int width, const uchar scalar[],
+                          v_uint16x8 (*v_op)(const v_uint16x8&, const v_uint16x8&),
+                          int (*s_op)(uchar, uchar))
+{
+    int w = 0;
+
+#if CV_SIMD128
+    for (; w <= width-16; w+=16)
+    {
+        v_uint8x16 x, y, z;
+        v_load_deinterleave(&in[3*w], x, y, z);
+
+        v_uint16x8 r0, r1;
+
+        v_expand(x, r0, r1);
+        r0 = v_op(r0, v_setall_u16(scalar[0])); // x + scalar[0]
+        r1 = v_op(r1, v_setall_u16(scalar[0]));
+        x = v_pack(r0, r1);
+
+        v_expand(y, r0, r1);
+        r0 = v_op(r0, v_setall_u16(scalar[1])); // y + scalar[1]
+        r1 = v_op(r1, v_setall_u16(scalar[1]));
+        y = v_pack(r0, r1);
+
+        v_expand(z, r0, r1);
+        r0 = v_op(r0, v_setall_u16(scalar[2])); // z + scalar[2]
+        r1 = v_op(r1, v_setall_u16(scalar[2]));
+        z = v_pack(r0, r1);
+
+        v_store_interleave(&out[3*w], x, y, z);
+    }
+#endif
+    UNUSED(v_op);
+    for (; w < width; w++)
+    {
+        out[3*w    ] = saturate<uchar>( s_op(in[3*w    ], scalar[0]) );
+        out[3*w + 1] = saturate<uchar>( s_op(in[3*w + 1], scalar[1]) );
+        out[3*w + 2] = saturate<uchar>( s_op(in[3*w + 2], scalar[2]) );
+    }
+}
+
+// manually SIMD if rounding 32F into 8U, single channel
+static void run_arithm_s1(uchar out[], const float in[], int width, const float scalar[],
+                          v_float32x4 (*v_op)(const v_float32x4&, const v_float32x4&),
+                          float (*s_op)(float, float))
+{
+    int w = 0;
+
+#if CV_SIMD128
+    for (; w <= width-16; w+=16)
+    {
+        v_float32x4 r0, r1, r2, r3;
+        r0 = v_load(&in[w     ]);
+        r1 = v_load(&in[w +  4]);
+        r2 = v_load(&in[w +  8]);
+        r3 = v_load(&in[w + 12]);
+
+        r0 = v_op(r0, v_setall_f32(scalar[0])); // r + scalar[0]
+        r1 = v_op(r1, v_setall_f32(scalar[0]));
+        r2 = v_op(r2, v_setall_f32(scalar[0]));
+        r3 = v_op(r3, v_setall_f32(scalar[0]));
+
+        v_int32x4 i0, i1, i2, i3;
+        i0 = v_round(r0);
+        i1 = v_round(r1);
+        i2 = v_round(r2);
+        i3 = v_round(r3);
+
+        v_uint16x8 us0, us1;
+        us0 = v_pack_u(i0, i1);
+        us1 = v_pack_u(i2, i3);
+
+        v_uint8x16 uc;
+        uc = v_pack(us0, us1);
+
+        v_store(&out[w], uc);
+    }
+#endif
+    UNUSED(v_op);
+    for (; w < width; w++)
+    {
+        out[w] = saturate<uchar>(s_op(in[w], scalar[0]), std::roundf);
+    }
+}
+
+static void run_arithm_s_add3(uchar out[], const uchar in[], int width, const uchar scalar[])
+{
+    run_arithm_s3(out, in, width, scalar, v_add_16u, s_add_8u);
+}
+
+static void run_arithm_s_sub3(uchar out[], const uchar in[], int width, const uchar scalar[])
+{
+    run_arithm_s3(out, in, width, scalar, v_sub_16u, s_sub_8u);
+}
+
+static void run_arithm_s_subr3(uchar out[], const uchar in[], int width, const uchar scalar[])
+{
+    run_arithm_s3(out, in, width, scalar, v_subr_16u, s_subr_8u); // reverse: subr
+}
+
+static void run_arithm_s_add1(uchar out[], const float in[], int width, const float scalar[])
+{
+    run_arithm_s1(out, in, width, scalar, v_add_32f, s_add_32f);
+}
+
+static void run_arithm_s_sub1(uchar out[], const float in[], int width, const float scalar[])
+{
+    run_arithm_s1(out, in, width, scalar, v_sub_32f, s_sub_32f);
+}
+
+static void run_arithm_s_subr1(uchar out[], const float in[], int width, const float scalar[])
+{
+    run_arithm_s1(out, in, width, scalar, v_subr_32f, s_subr_32f); // reverse: subr
+}
+
+// manually unroll the inner cycle by channels
+template<typename DST, typename SRC, typename SCALAR, typename FUNC>
+static void run_arithm_s(DST out[], const SRC in[], int width, int chan,
+                         const SCALAR scalar[4], FUNC func)
+{
+    if (chan == 4)
+    {
+        for (int w=0; w < width; w++)
+        {
+            out[4*w + 0] = func(in[4*w + 0], scalar[0]);
+            out[4*w + 1] = func(in[4*w + 1], scalar[1]);
+            out[4*w + 2] = func(in[4*w + 2], scalar[2]);
+            out[4*w + 3] = func(in[4*w + 3], scalar[3]);
+        }
+    }
+    else
+    if (chan == 3)
+    {
+        for (int w=0; w < width; w++)
+        {
+            out[3*w + 0] = func(in[3*w + 0], scalar[0]);
+            out[3*w + 1] = func(in[3*w + 1], scalar[1]);
+            out[3*w + 2] = func(in[3*w + 2], scalar[2]);
+        }
+    }
+    else
+    if (chan == 2)
+    {
+        for (int w=0; w < width; w++)
+        {
+            out[2*w + 0] = func(in[2*w + 0], scalar[0]);
+            out[2*w + 1] = func(in[2*w + 1], scalar[1]);
+        }
+    }
+    else
+    if (chan == 1)
+    {
+        for (int w=0; w < width; w++)
+        {
+            out[w] = func(in[w], scalar[0]);
+        }
+    }
+    else
+        CV_Error(cv::Error::StsBadArg, "unsupported number of channels");
+}
+
+template<typename DST, typename SRC>
+static void run_arithm_s(Buffer &dst, const View &src, const float scalar[4], Arithm arithm,
+                         float scale=1)
+{
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+
+    // What if we cast the scalar into the SRC type?
+    const SRC myscal[4] = { static_cast<SRC>(scalar[0]), static_cast<SRC>(scalar[1]),
+                            static_cast<SRC>(scalar[2]), static_cast<SRC>(scalar[3]) };
+    bool usemyscal = (myscal[0] == scalar[0]) && (myscal[1] == scalar[1]) &&
+                     (myscal[2] == scalar[2]) && (myscal[3] == scalar[3]);
+
+    switch (arithm)
+    {
+    case ARITHM_ABSDIFF:
+        for (int w=0; w < width; w++)
+            for (int c=0; c < chan; c++)
+                out[chan*w + c] = absdiff<DST>(in[chan*w + c], scalar[c]);
+        break;
+    case ARITHM_ADD:
+        if (usemyscal)
+        {
+            if (std::is_same<DST,uchar>::value &&
+                std::is_same<SRC,uchar>::value &&
+                chan == 3)
+                run_arithm_s_add3((uchar*)out, (const uchar*)in, width, (const uchar*)myscal);
+            else if (std::is_same<DST,uchar>::value &&
+                     std::is_same<SRC,float>::value &&
+                     chan == 1)
+                run_arithm_s_add1((uchar*)out, (const float*)in, width, (const float*)myscal);
+            else
+                run_arithm_s(out, in, width, chan, myscal, add<DST,SRC,SRC>);
+        }
+        else
+            run_arithm_s(out, in, width, chan, scalar, add<DST,SRC,float>);
+        break;
+    case ARITHM_SUBTRACT:
+        if (usemyscal)
+        {
+            if (std::is_same<DST,uchar>::value &&
+                std::is_same<SRC,uchar>::value &&
+                chan == 3)
+                run_arithm_s_sub3((uchar*)out, (const uchar*)in, width, (const uchar*)myscal);
+            else if (std::is_same<DST,uchar>::value &&
+                     std::is_same<SRC,float>::value &&
+                     chan == 1)
+                run_arithm_s_sub1((uchar*)out, (const float*)in, width, (const float*)myscal);
+            else
+                run_arithm_s(out, in, width, chan, myscal, sub<DST,SRC,SRC>);
+        }
+        else
+            run_arithm_s(out, in, width, chan, scalar, sub<DST,SRC,float>);
+        break;
+    // TODO: optimize miltiplication and division
+    case ARITHM_MULTIPLY:
+        for (int w=0; w < width; w++)
+            for (int c=0; c < chan; c++)
+                out[chan*w + c] = mul<DST>(in[chan*w + c], scalar[c], scale);
+        break;
+    case ARITHM_DIVIDE:
+        for (int w=0; w < width; w++)
+            for (int c=0; c < chan; c++)
+                out[chan*w + c] = div<DST>(in[chan*w + c], scalar[c], scale);
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported arithmetic operation");
+    }
+}
+
+template<typename DST, typename SRC>
+static void run_arithm_rs(Buffer &dst, const View &src, const float scalar[4], Arithm arithm,
+                          float scale=1)
+{
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+
+    // What if we cast the scalar into the SRC type?
+    const SRC myscal[4] = { static_cast<SRC>(scalar[0]), static_cast<SRC>(scalar[1]),
+                            static_cast<SRC>(scalar[2]), static_cast<SRC>(scalar[3]) };
+    bool usemyscal = (myscal[0] == scalar[0]) && (myscal[1] == scalar[1]) &&
+                     (myscal[2] == scalar[2]) && (myscal[3] == scalar[3]);
+
+    switch (arithm)
+    {
+    case ARITHM_SUBTRACT:
+        if (usemyscal)
+        {
+            if (std::is_same<DST,uchar>::value &&
+                std::is_same<SRC,uchar>::value &&
+                chan == 3)
+                run_arithm_s_subr3((uchar*)out, (const uchar*)in, width, (const uchar*)myscal);
+            else if (std::is_same<DST,uchar>::value &&
+                     std::is_same<SRC,float>::value &&
+                     chan == 1)
+                run_arithm_s_subr1((uchar*)out, (const float*)in, width, (const float*)myscal);
+            else
+                run_arithm_s(out, in, width, chan, myscal, subr<DST,SRC,SRC>);
+        }
+        else
+            run_arithm_s(out, in, width, chan, scalar, subr<DST,SRC,float>);
+        break;
+    // TODO: optimize division
+    case ARITHM_DIVIDE:
+        for (int w=0; w < width; w++)
+            for (int c=0; c < chan; c++)
+                out[chan*w + c] = div<DST>(scalar[c], in[chan*w + c], scale);
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported arithmetic operation");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidAbsDiffC, cv::gapi::core::GAbsDiffC, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &_scalar, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_ABSDIFF);
+        UNARY_(ushort, ushort, run_arithm_s, dst, src, scalar, ARITHM_ABSDIFF);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_ABSDIFF);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidAddC, cv::gapi::core::GAddC, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &_scalar, int /*dtype*/, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_(uchar ,  short, run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_(uchar ,  float, run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_( float, uchar , run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_( float,  short, run_arithm_s, dst, src, scalar, ARITHM_ADD);
+        UNARY_( float,  float, run_arithm_s, dst, src, scalar, ARITHM_ADD);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidSubC, cv::gapi::core::GSubC, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &_scalar, int /*dtype*/, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_(uchar ,  short, run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_(uchar ,  float, run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float, uchar , run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float,  short, run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float,  float, run_arithm_s, dst, src, scalar, ARITHM_SUBTRACT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidSubRC, cv::gapi::core::GSubRC, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::Scalar &_scalar, const View &src, int /*dtype*/, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_(uchar ,  short, run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_(uchar ,  float, run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( short,  short, run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float, uchar , run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float,  short, run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+        UNARY_( float,  float, run_arithm_rs, dst, src, scalar, ARITHM_SUBTRACT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMulC, cv::gapi::core::GMulC, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &_scalar, int /*dtype*/, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+        const float scale = 1.f;
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_(uchar ,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_(uchar ,  float, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float, uchar , run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float,  float, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMulCOld, cv::gapi::core::GMulCOld, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, double _scalar, int /*dtype*/, Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar),
+            static_cast<float>(_scalar),
+            static_cast<float>(_scalar),
+            static_cast<float>(_scalar)
+        };
+        const float scale = 1.f;
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_(uchar ,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_(uchar ,  float, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float, uchar , run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float,  short, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+        UNARY_( float,  float, run_arithm_s, dst, src, scalar, ARITHM_MULTIPLY, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidDivC, cv::gapi::core::GDivC, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &_scalar, double _scale, int /*dtype*/,
+                    Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+        const float scale = static_cast<float>(_scale);
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_(uchar ,  short, run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_(uchar ,  float, run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( short,  short, run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float, uchar , run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float,  short, run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float,  float, run_arithm_s, dst, src, scalar, ARITHM_DIVIDE, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidDivRC, cv::gapi::core::GDivRC, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::Scalar &_scalar, const View &src, double _scale, int /*dtype*/,
+                    Buffer &dst)
+    {
+        const float scalar[4] = {
+            static_cast<float>(_scalar[0]),
+            static_cast<float>(_scalar[1]),
+            static_cast<float>(_scalar[2]),
+            static_cast<float>(_scalar[3])
+        };
+        const float scale = static_cast<float>(_scale);
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_(uchar ,  short, run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_(uchar ,  float, run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( short,  short, run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float, uchar , run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float,  short, run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+        UNARY_( float,  float, run_arithm_rs, dst, src, scalar, ARITHM_DIVIDE, scale);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//----------------------------
+//
+// Fluid math kernels: bitwise
+//
+//----------------------------
+
+enum Bitwise { BW_AND, BW_OR, BW_XOR, BW_NOT };
+
+template<typename DST, typename SRC1, typename SRC2>
+static void run_bitwise2(Buffer &dst, const View &src1, const View &src2, Bitwise bitwise)
+{
+    static_assert(std::is_same<DST, SRC1>::value, "wrong types");
+    static_assert(std::is_same<DST, SRC2>::value, "wrong types");
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+    int length = width * chan;
+
+    switch (bitwise)
+    {
+    case BW_AND:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] & in2[l];
+        break;
+    case BW_OR:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] | in2[l];
+        break;
+    case BW_XOR:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] ^ in2[l];
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported bitwise operation");
+    }
+}
+
+template<typename DST, typename SRC>
+static void run_bitwise1(Buffer &dst, const View &src, Bitwise bitwise)
+{
+    static_assert(std::is_same<DST, SRC>::value, "wrong types");
+
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+    int length = width * chan;
+
+    switch (bitwise)
+    {
+    case BW_NOT:
+        for (int l=0; l < length; l++)
+            out[l] = ~in[l];
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported bitwise operation");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidAnd, cv::gapi::core::GAnd, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+
+        //      DST     SRC1    SRC2    OP            __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_bitwise2, dst, src1, src2, BW_AND);
+        BINARY_(ushort, ushort, ushort, run_bitwise2, dst, src1, src2, BW_AND);
+        BINARY_( short,  short,  short, run_bitwise2, dst, src1, src2, BW_AND);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidOr, cv::gapi::core::GOr, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+
+        //      DST     SRC1    SRC2    OP            __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_bitwise2, dst, src1, src2, BW_OR);
+        BINARY_(ushort, ushort, ushort, run_bitwise2, dst, src1, src2, BW_OR);
+        BINARY_( short,  short,  short, run_bitwise2, dst, src1, src2, BW_OR);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidXor, cv::gapi::core::GXor, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+
+        //      DST     SRC1    SRC2    OP            __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_bitwise2, dst, src1, src2, BW_XOR);
+        BINARY_(ushort, ushort, ushort, run_bitwise2, dst, src1, src2, BW_XOR);
+        BINARY_( short,  short,  short, run_bitwise2, dst, src1, src2, BW_XOR);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidNot, cv::gapi::core::GNot, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_bitwise1, dst, src, BW_NOT);
+        UNARY_(ushort, ushort, run_bitwise1, dst, src, BW_NOT);
+        UNARY_( short,  short, run_bitwise1, dst, src, BW_NOT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//-------------------
+//
+// Fluid kernels: LUT
+//
+//-------------------
+
+GAPI_FLUID_KERNEL(GFluidLUT, cv::gapi::core::GLUT, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Mat& lut, Buffer &dst)
+    {
+        GAPI_Assert(CV_8U == dst.meta().depth);
+        GAPI_Assert(CV_8U == src.meta().depth);
+
+        GAPI_DbgAssert(CV_8U == lut.type());
+        GAPI_DbgAssert(256 == lut.cols * lut.rows);
+        GAPI_DbgAssert(dst.length() == src.length());
+        GAPI_DbgAssert(dst.meta().chan == src.meta().chan);
+
+        const auto *in  = src.InLine<uchar>(0);
+              auto *out = dst.OutLine<uchar>();
+
+        int width  = dst.length();
+        int chan   = dst.meta().chan;
+        int length = width * chan;
+
+        for (int l=0; l < length; l++)
+            out[l] = lut.data[ in[l] ];
+    }
+};
+
+//-------------------------
+//
+// Fluid kernels: convertTo
+//
+//-------------------------
+
+template<typename DST, typename SRC>
+static void run_convertto(Buffer &dst, const View &src, double _alpha, double _beta)
+{
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width  = dst.length();
+    int chan   = dst.meta().chan;
+    int length = width * chan;
+
+    // NB: don't do this if SRC or DST is 64-bit
+    auto alpha = static_cast<float>( _alpha );
+    auto beta  = static_cast<float>( _beta  );
+
+    // compute faster if no alpha no beta
+    if (alpha == 1 && beta == 0)
+    {
+        // manual SIMD if need rounding
+        if (std::is_integral<DST>::value && std::is_floating_point<SRC>::value)
+        {
+            GAPI_Assert(( std::is_same<SRC,float>::value ));
+
+            int l = 0; // cycle index
+
+        #if CV_SIMD128
+            if (std::is_same<DST,uchar>::value)
+            {
+                for (; l <= length-16; l+=16)
+                {
+                    v_int32x4 i0, i1, i2, i3;
+                    i0 = v_round( v_load( (float*)& in[l     ] ) );
+                    i1 = v_round( v_load( (float*)& in[l +  4] ) );
+                    i2 = v_round( v_load( (float*)& in[l +  8] ) );
+                    i3 = v_round( v_load( (float*)& in[l + 12] ) );
+
+                    v_uint16x8 us0, us1;
+                    us0 = v_pack_u(i0, i1);
+                    us1 = v_pack_u(i2, i3);
+
+                    v_uint8x16 uc;
+                    uc = v_pack(us0, us1);
+                    v_store((uchar*)& out[l], uc);
+                }
+            }
+            if (std::is_same<DST,ushort>::value)
+            {
+                for (; l <= length-8; l+=8)
+                {
+                    v_int32x4 i0, i1;
+                    i0 = v_round( v_load( (float*)& in[l     ] ) );
+                    i1 = v_round( v_load( (float*)& in[l +  4] ) );
+
+                    v_uint16x8 us;
+                    us = v_pack_u(i0, i1);
+                    v_store((ushort*)& out[l], us);
+                }
+            }
+        #endif
+
+            // tail of SIMD cycle
+            for (; l < length; l++)
+            {
+                out[l] = saturate<DST>(in[l], rintf);
+            }
+        }
+        else if (std::is_integral<DST>::value) // here SRC is integral
+        {
+            for (int l=0; l < length; l++)
+            {
+                out[l] = saturate<DST>(in[l]);
+            }
+        }
+        else // DST is floating-point, SRC is any
+        {
+            for (int l=0; l < length; l++)
+            {
+                out[l] = static_cast<DST>(in[l]);
+            }
+        }
+    }
+    else // if alpha or beta is non-trivial
+    {
+        // TODO: optimize if alpha and beta and data are integral
+        for (int l=0; l < length; l++)
+        {
+            out[l] = saturate<DST>(in[l]*alpha + beta, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidConvertTo, cv::gapi::core::GConvertTo, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, int /*rtype*/, double alpha, double beta, Buffer &dst)
+    {
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_convertto, dst, src, alpha, beta);
+        UNARY_(uchar , ushort, run_convertto, dst, src, alpha, beta);
+        UNARY_(uchar ,  float, run_convertto, dst, src, alpha, beta);
+        UNARY_(ushort, uchar , run_convertto, dst, src, alpha, beta);
+        UNARY_(ushort, ushort, run_convertto, dst, src, alpha, beta);
+        UNARY_(ushort,  float, run_convertto, dst, src, alpha, beta);
+        UNARY_( float, uchar , run_convertto, dst, src, alpha, beta);
+        UNARY_( float, ushort, run_convertto, dst, src, alpha, beta);
+        UNARY_( float,  float, run_convertto, dst, src, alpha, beta);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//-----------------------------
+//
+// Fluid math kernels: min, max
+//
+//-----------------------------
+
+enum Minmax { MM_MIN, MM_MAX };
+
+template<typename DST, typename SRC1, typename SRC2>
+static void run_minmax(Buffer &dst, const View &src1, const View &src2, Minmax minmax)
+{
+    static_assert(std::is_same<DST, SRC1>::value, "wrong types");
+    static_assert(std::is_same<DST, SRC2>::value, "wrong types");
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    int length = width * chan;
+
+    switch (minmax)
+    {
+    case MM_MIN:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] < in2[l]? in1[l]: in2[l];
+        break;
+    case MM_MAX:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] > in2[l]? in1[l]: in2[l];
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported min/max operation");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidMin, cv::gapi::core::GMin, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_minmax, dst, src1, src2, MM_MIN);
+        BINARY_(ushort, ushort, ushort, run_minmax, dst, src1, src2, MM_MIN);
+        BINARY_( short,  short,  short, run_minmax, dst, src1, src2, MM_MIN);
+        BINARY_( float,  float,  float, run_minmax, dst, src1, src2, MM_MIN);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMax, cv::gapi::core::GMax, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    OP          __VA_ARGS__
+        BINARY_(uchar , uchar , uchar , run_minmax, dst, src1, src2, MM_MAX);
+        BINARY_(ushort, ushort, ushort, run_minmax, dst, src1, src2, MM_MAX);
+        BINARY_( short,  short,  short, run_minmax, dst, src1, src2, MM_MAX);
+        BINARY_( float,  float,  float, run_minmax, dst, src1, src2, MM_MAX);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//-----------------------
+//
+// Fluid kernels: compare
+//
+//-----------------------
+
+enum Compare { CMP_EQ, CMP_NE, CMP_GE, CMP_GT, CMP_LE, CMP_LT };
+
+template<typename DST, typename SRC1, typename SRC2>
+static void run_cmp(Buffer &dst, const View &src1, const View &src2, Compare compare)
+{
+    static_assert(std::is_same<SRC1, SRC2>::value, "wrong types");
+    static_assert(std::is_same<DST, uchar>::value, "wrong types");
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    int length = width * chan;
+
+    switch (compare)
+    {
+    case CMP_EQ:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] == in2[l]? 255: 0;
+        break;
+    case CMP_NE:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] != in2[l]? 255: 0;
+        break;
+    case CMP_GE:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] >= in2[l]? 255: 0;
+        break;
+    case CMP_LE:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] <= in2[l]? 255: 0;
+        break;
+    case CMP_GT:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] > in2[l]? 255: 0;
+        break;
+    case CMP_LT:
+        for (int l=0; l < length; l++)
+            out[l] = in1[l] < in2[l]? 255: 0;
+        break;
+    default:
+        CV_Error(cv::Error::StsBadArg, "unsupported compare operation");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidCmpEQ, cv::gapi::core::GCmpEQ, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_EQ);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_EQ);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_EQ);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpNE, cv::gapi::core::GCmpNE, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_NE);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_NE);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_NE);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpGE, cv::gapi::core::GCmpGE, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_GE);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_GE);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_GE);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpGT, cv::gapi::core::GCmpGT, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_GT);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_GT);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_GT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpLE, cv::gapi::core::GCmpLE, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_LE);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_LE);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_LE);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpLT, cv::gapi::core::GCmpLT, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, Buffer &dst)
+    {
+        //      DST    SRC1    SRC2    OP       __VA_ARGS__
+        BINARY_(uchar, uchar , uchar , run_cmp, dst, src1, src2, CMP_LT);
+        BINARY_(uchar,  short,  short, run_cmp, dst, src1, src2, CMP_LT);
+        BINARY_(uchar,  float,  float, run_cmp, dst, src1, src2, CMP_LT);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//---------------------
+//
+// Compare with GScalar
+//
+//---------------------
+
+template<typename DST, typename SRC, typename SCALAR=double>
+static void run_cmp(DST out[], const SRC in[], int length, Compare compare, SCALAR s)
+{
+    switch (compare)
+    {
+    case CMP_EQ:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] == s? 255: 0;
+        break;
+    case CMP_NE:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] != s? 255: 0;
+        break;
+    case CMP_GE:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] >= s? 255: 0;
+        break;
+    case CMP_LE:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] <= s? 255: 0;
+        break;
+    case CMP_GT:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > s? 255: 0;
+        break;
+    case CMP_LT:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] < s? 255: 0;
+        break;
+    default:
+        CV_Error(cv::Error::StsBadArg, "unsupported compare operation");
+    }
+}
+
+template<typename DST, typename SRC>
+static void run_cmp(Buffer &dst, const View &src, Compare compare, const cv::Scalar &scalar)
+{
+    static_assert(std::is_same<DST, uchar>::value, "wrong types");
+
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    int length = width * chan;
+
+    // compute faster if scalar rounds to SRC
+    double d =                   scalar[0]  ;
+    SRC    s = static_cast<SRC>( scalar[0] );
+
+    if (s == d)
+        run_cmp(out, in, length, compare, s);
+    else
+        run_cmp(out, in, length, compare, d);
+}
+
+GAPI_FLUID_KERNEL(GFluidCmpEQScalar, cv::gapi::core::GCmpEQScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_EQ, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_EQ, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_EQ, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpNEScalar, cv::gapi::core::GCmpNEScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_NE, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_NE, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_NE, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpGEScalar, cv::gapi::core::GCmpGEScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_GE, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_GE, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_GE, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpGTScalar, cv::gapi::core::GCmpGTScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_GT, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_GT, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_GT, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpLEScalar, cv::gapi::core::GCmpLEScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_LE, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_LE, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_LE, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCmpLTScalar, cv::gapi::core::GCmpLTScalar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &scalar, Buffer &dst)
+    {
+        //     DST    SRC     OP       __VA_ARGS__
+        UNARY_(uchar, uchar , run_cmp, dst, src, CMP_LT, scalar);
+        UNARY_(uchar,  short, run_cmp, dst, src, CMP_LT, scalar);
+        UNARY_(uchar,  float, run_cmp, dst, src, CMP_LT, scalar);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//-------------------------
+//
+// Fluid kernels: threshold
+//
+//-------------------------
+
+template<typename DST, typename SRC>
+static void run_threshold(Buffer &dst, const View &src, const cv::Scalar &thresh,
+                                                        const cv::Scalar &maxval,
+                                                                     int  type)
+{
+    static_assert(std::is_same<DST, SRC>::value, "wrong types");
+
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    int length = width * chan;
+
+    DST thresh_ = saturate<DST>(thresh[0], floord);
+    DST threshd = saturate<DST>(thresh[0], roundd);
+    DST maxvald = saturate<DST>(maxval[0], roundd);
+
+    switch (type)
+    {
+    case cv::THRESH_BINARY:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > thresh_? maxvald: 0;
+        break;
+    case cv::THRESH_BINARY_INV:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > thresh_? 0: maxvald;
+        break;
+    case cv::THRESH_TRUNC:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > thresh_? threshd: in[l];
+        break;
+    case cv::THRESH_TOZERO:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > thresh_? in[l]: 0;
+        break;
+    case cv::THRESH_TOZERO_INV:
+        for (int l=0; l < length; l++)
+            out[l] = in[l] > thresh_? 0: in[l];
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported threshold type");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidThreshold, cv::gapi::core::GThreshold, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &thresh,
+                                     const cv::Scalar &maxval,
+                                                  int  type,
+                        Buffer &dst)
+    {
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_threshold, dst, src, thresh, maxval, type);
+        UNARY_(ushort, ushort, run_threshold, dst, src, thresh, maxval, type);
+        UNARY_( short,  short, run_threshold, dst, src, thresh, maxval, type);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//------------------------
+//
+// Fluid kernels: in-range
+//
+//------------------------
+
+static void run_inrange3(uchar out[], const uchar in[], int width,
+                         const uchar lower[], const uchar upper[])
+{
+    int w = 0; // cycle index
+
+#if CV_SIMD128
+    for (; w <= width-16; w+=16)
+    {
+        v_uint8x16 i0, i1, i2;
+        v_load_deinterleave(&in[3*w], i0, i1, i2);
+
+        v_uint8x16 o;
+        o = (i0 >= v_setall_u8(lower[0])) & (i0 <= v_setall_u8(upper[0])) &
+            (i1 >= v_setall_u8(lower[1])) & (i1 <= v_setall_u8(upper[1])) &
+            (i2 >= v_setall_u8(lower[2])) & (i2 <= v_setall_u8(upper[2]));
+
+        v_store(&out[w], o);
+    }
+#endif
+
+    for (; w < width; w++)
+    {
+        out[w] = in[3*w  ] >= lower[0] && in[3*w  ] <= upper[0] &&
+                 in[3*w+1] >= lower[1] && in[3*w+1] <= upper[1] &&
+                 in[3*w+2] >= lower[2] && in[3*w+2] <= upper[2] ? 255: 0;
+    }
+}
+
+template<typename DST, typename SRC>
+static void run_inrange(Buffer &dst, const View &src, const cv::Scalar &upperb,
+                                                      const cv::Scalar &lowerb)
+{
+    static_assert(std::is_same<DST, uchar>::value, "wrong types");
+    static_assert(std::is_integral<SRC>::value,    "wrong types");
+
+    const auto *in  = src.InLine<SRC>(0);
+          auto *out = dst.OutLine<DST>();
+
+    int width = src.length();
+    int chan  = src.meta().chan;
+    GAPI_Assert(dst.meta().chan == 1);
+
+    // for integral input, in[i] >= lower equals in[i] >= ceil(lower)
+    // so we can optimize compare operations by rounding lower/upper
+    SRC lower[4], upper[4];
+    for (int c=0; c < chan; c++)
+    {
+        lower[c] = saturate<SRC>(lowerb[c],  ceild);
+        upper[c] = saturate<SRC>(upperb[c], floord);
+    }
+
+    // manually SIMD for important case if RGB/BGR
+    if (std::is_same<SRC,uchar>::value && chan==3)
+    {
+        run_inrange3((uchar*)out, (const uchar*)in, width,
+                     (const uchar*)lower, (const uchar*)upper);
+        return;
+    }
+
+    // TODO: please manually SIMD if multiple channels:
+    // modern compilers would perfectly vectorize this code if one channel,
+    // but may need help with de-interleaving channels if RGB/BGR image etc
+    switch (chan)
+    {
+    case 1:
+        for (int w=0; w < width; w++)
+            out[w] = in[w] >= lower[0] && in[w] <= upper[0]? 255: 0;
+        break;
+    case 2:
+        for (int w=0; w < width; w++)
+            out[w] = in[2*w  ] >= lower[0] && in[2*w  ] <= upper[0] &&
+                     in[2*w+1] >= lower[1] && in[2*w+1] <= upper[1] ? 255: 0;
+        break;
+    case 3:
+        for (int w=0; w < width; w++)
+            out[w] = in[3*w  ] >= lower[0] && in[3*w  ] <= upper[0] &&
+                     in[3*w+1] >= lower[1] && in[3*w+1] <= upper[1] &&
+                     in[3*w+2] >= lower[2] && in[3*w+2] <= upper[2] ? 255: 0;
+        break;
+    case 4:
+        for (int w=0; w < width; w++)
+            out[w] = in[4*w  ] >= lower[0] && in[4*w  ] <= upper[0] &&
+                     in[4*w+1] >= lower[1] && in[4*w+1] <= upper[1] &&
+                     in[4*w+2] >= lower[2] && in[4*w+2] <= upper[2] &&
+                     in[4*w+3] >= lower[3] && in[4*w+3] <= upper[3] ? 255: 0;
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported number of channels");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidInRange, cv::gapi::core::GInRange, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, const cv::Scalar &lowerb, const cv::Scalar& upperb,
+                        Buffer &dst)
+    {
+        //       DST     SRC    OP           __VA_ARGS__
+        INRANGE_(uchar, uchar , run_inrange, dst, src, upperb, lowerb);
+        INRANGE_(uchar, ushort, run_inrange, dst, src, upperb, lowerb);
+        INRANGE_(uchar,  short, run_inrange, dst, src, upperb, lowerb);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//----------------------
+//
+// Fluid kernels: select
+//
+//----------------------
+
+// manually vectored function for important case if RGB/BGR image
+static void run_select_row3(int width, uchar out[], uchar in1[], uchar in2[], uchar in3[])
+{
+    int w = 0; // cycle index
+
+#if CV_SIMD128
+    for (; w <= width-16; w+=16)
+    {
+        v_uint8x16 a1, b1, c1;
+        v_uint8x16 a2, b2, c2;
+        v_uint8x16 mask;
+        v_uint8x16 a, b, c;
+
+        v_load_deinterleave(&in1[3*w], a1, b1, c1);
+        v_load_deinterleave(&in2[3*w], a2, b2, c2);
+
+        mask = v_load(&in3[w]);
+        mask = mask != v_setzero_u8();
+
+        a = v_select(mask, a1, a2);
+        b = v_select(mask, b1, b2);
+        c = v_select(mask, c1, c2);
+
+        v_store_interleave(&out[3*w], a, b, c);
+    }
+#endif
+
+    for (; w < width; w++)
+    {
+        out[3*w    ] = in3[w]? in1[3*w    ]: in2[3*w    ];
+        out[3*w + 1] = in3[w]? in1[3*w + 1]: in2[3*w + 1];
+        out[3*w + 2] = in3[w]? in1[3*w + 2]: in2[3*w + 2];
+    }
+}
+
+// parameter chan is compile-time known constant, normally chan=1..4
+template<int chan, typename DST, typename SRC1, typename SRC2, typename SRC3>
+static void run_select_row(int width, DST out[], SRC1 in1[], SRC2 in2[], SRC3 in3[])
+{
+    if (std::is_same<DST,uchar>::value && chan==3)
+    {
+        // manually vectored function for important case if RGB/BGR image
+        run_select_row3(width, (uchar*)out, (uchar*)in1, (uchar*)in2, (uchar*)in3);
+        return;
+    }
+
+    // because `chan` is template parameter, its value is known at compilation time,
+    // so that modern compilers would efficiently vectorize this cycle if chan==1
+    // (if chan>1, compilers may need help with de-interleaving of the channels)
+    for (int w=0; w < width; w++)
+    {
+        for (int c=0; c < chan; c++)
+        {
+            out[w*chan + c] = in3[w]? in1[w*chan + c]: in2[w*chan + c];
+        }
+    }
+}
+
+template<typename DST, typename SRC1, typename SRC2, typename SRC3>
+static void run_select(Buffer &dst, const View &src1, const View &src2, const View &src3)
+{
+    static_assert(std::is_same<DST ,  SRC1>::value, "wrong types");
+    static_assert(std::is_same<DST ,  SRC2>::value, "wrong types");
+    static_assert(std::is_same<uchar, SRC3>::value, "wrong types");
+
+    auto *out = dst.OutLine<DST>();
+
+    const auto *in1 = src1.InLine<SRC1>(0);
+    const auto *in2 = src2.InLine<SRC2>(0);
+    const auto *in3 = src3.InLine<SRC3>(0);
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    switch (chan)
+    {
+    case 1: run_select_row<1>(width, out, in1, in2, in3); break;
+    case 2: run_select_row<2>(width, out, in1, in2, in3); break;
+    case 3: run_select_row<3>(width, out, in1, in2, in3); break;
+    case 4: run_select_row<4>(width, out, in1, in2, in3); break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported number of channels");
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidSelect, cv::gapi::core::GSelect, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, const View &src3, Buffer &dst)
+    {
+        //      DST     SRC1    SRC2    SRC3   OP          __VA_ARGS__
+        SELECT_(uchar , uchar , uchar , uchar, run_select, dst, src1, src2, src3);
+        SELECT_(ushort, ushort, ushort, uchar, run_select, dst, src1, src2, src3);
+        SELECT_( short,  short,  short, uchar, run_select, dst, src1, src2, src3);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+};
+
+//----------------------------------------------------
+//
+// Fluid kernels: split, merge, polat2cart, cart2polar
+//
+//----------------------------------------------------
+
+GAPI_FLUID_KERNEL(GFluidSplit3, cv::gapi::core::GSplit3, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3)
+    {
+        const auto *in   =  src.InLine<uchar>(0);
+              auto *out1 = dst1.OutLine<uchar>();
+              auto *out2 = dst2.OutLine<uchar>();
+              auto *out3 = dst3.OutLine<uchar>();
+
+        GAPI_Assert(3 == src.meta().chan);
+        int width = src.length();
+
+        int w = 0; // cycle counter
+
+    #if CV_SIMD128
+        for (; w <= width-16; w+=16)
+        {
+            v_uint8x16 a, b, c;
+            v_load_deinterleave(&in[3*w], a, b, c);
+            v_store(&out1[w], a);
+            v_store(&out2[w], b);
+            v_store(&out3[w], c);
+        }
+    #endif
+
+        for (; w < width; w++)
+        {
+            out1[w] = in[3*w    ];
+            out2[w] = in[3*w + 1];
+            out3[w] = in[3*w + 2];
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidSplit4, cv::gapi::core::GSplit4, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3, Buffer &dst4)
+    {
+        const auto *in   =  src.InLine<uchar>(0);
+              auto *out1 = dst1.OutLine<uchar>();
+              auto *out2 = dst2.OutLine<uchar>();
+              auto *out3 = dst3.OutLine<uchar>();
+              auto *out4 = dst4.OutLine<uchar>();
+
+        GAPI_Assert(4 == src.meta().chan);
+        int width = src.length();
+
+        int w = 0; // cycle counter
+
+    #if CV_SIMD128
+        for (; w <= width-16; w+=16)
+        {
+            v_uint8x16 a, b, c, d;
+            v_load_deinterleave(&in[4*w], a, b, c, d);
+            v_store(&out1[w], a);
+            v_store(&out2[w], b);
+            v_store(&out3[w], c);
+            v_store(&out4[w], d);
+        }
+    #endif
+
+        for (; w < width; w++)
+        {
+            out1[w] = in[4*w    ];
+            out2[w] = in[4*w + 1];
+            out3[w] = in[4*w + 2];
+            out4[w] = in[4*w + 3];
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMerge3, cv::gapi::core::GMerge3, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, const View &src3, Buffer &dst)
+    {
+        const auto *in1 = src1.InLine<uchar>(0);
+        const auto *in2 = src2.InLine<uchar>(0);
+        const auto *in3 = src3.InLine<uchar>(0);
+              auto *out = dst.OutLine<uchar>();
+
+        GAPI_Assert(3 == dst.meta().chan);
+        int width = dst.length();
+
+        int w = 0; // cycle counter
+
+    #if CV_SIMD128
+        for (; w <= width-16; w+=16)
+        {
+            v_uint8x16 a, b, c;
+            a = v_load(&in1[w]);
+            b = v_load(&in2[w]);
+            c = v_load(&in3[w]);
+            v_store_interleave(&out[3*w], a, b, c);
+        }
+    #endif
+
+        for (; w < width; w++)
+        {
+            out[3*w    ] = in1[w];
+            out[3*w + 1] = in2[w];
+            out[3*w + 2] = in3[w];
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidMerge4, cv::gapi::core::GMerge4, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, const View &src3, const View &src4,
+                    Buffer &dst)
+    {
+        const auto *in1 = src1.InLine<uchar>(0);
+        const auto *in2 = src2.InLine<uchar>(0);
+        const auto *in3 = src3.InLine<uchar>(0);
+        const auto *in4 = src4.InLine<uchar>(0);
+              auto *out = dst.OutLine<uchar>();
+
+        GAPI_Assert(4 == dst.meta().chan);
+        int width = dst.length();
+
+        int w = 0; // cycle counter
+
+    #if CV_SIMD128
+        for (; w <= width-16; w+=16)
+        {
+            v_uint8x16 a, b, c, d;
+            a = v_load(&in1[w]);
+            b = v_load(&in2[w]);
+            c = v_load(&in3[w]);
+            d = v_load(&in4[w]);
+            v_store_interleave(&out[4*w], a, b, c, d);
+        }
+    #endif
+
+        for (; w < width; w++)
+        {
+            out[4*w    ] = in1[w];
+            out[4*w + 1] = in2[w];
+            out[4*w + 2] = in3[w];
+            out[4*w + 3] = in4[w];
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidPolarToCart, cv::gapi::core::GPolarToCart, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, bool angleInDegrees,
+                    Buffer &dst1, Buffer &dst2)
+    {
+        GAPI_Assert(src1.meta().depth == CV_32F);
+        GAPI_Assert(src2.meta().depth == CV_32F);
+        GAPI_Assert(dst1.meta().depth == CV_32F);
+        GAPI_Assert(dst2.meta().depth == CV_32F);
+
+        const auto * in1 = src1.InLine<float>(0);
+        const auto * in2 = src2.InLine<float>(0);
+              auto *out1 = dst1.OutLine<float>();
+              auto *out2 = dst2.OutLine<float>();
+
+        int width = src1.length();
+        int chan  = src2.meta().chan;
+        int length = width * chan;
+
+        // SIMD: compiler vectoring!
+        for (int l=0; l < length; l++)
+        {
+            float angle = angleInDegrees?
+                          in2[l] * static_cast<float>(CV_PI / 180):
+                          in2[l];
+            float magnitude = in1[l];
+            float x = magnitude * std::cos(angle);
+            float y = magnitude * std::sin(angle);
+            out1[l] = x;
+            out2[l] = y;
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidCartToPolar, cv::gapi::core::GCartToPolar, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src1, const View &src2, bool angleInDegrees,
+                    Buffer &dst1, Buffer &dst2)
+    {
+        GAPI_Assert(src1.meta().depth == CV_32F);
+        GAPI_Assert(src2.meta().depth == CV_32F);
+        GAPI_Assert(dst1.meta().depth == CV_32F);
+        GAPI_Assert(dst2.meta().depth == CV_32F);
+
+        const auto * in1 = src1.InLine<float>(0);
+        const auto * in2 = src2.InLine<float>(0);
+              auto *out1 = dst1.OutLine<float>();
+              auto *out2 = dst2.OutLine<float>();
+
+        int width = src1.length();
+        int chan  = src2.meta().chan;
+        int length = width * chan;
+
+        // SIMD: compiler vectoring!
+        for (int l=0; l < length; l++)
+        {
+            float x = in1[l];
+            float y = in2[l];
+            float magnitude = std::hypot(y, x);
+            float angle_rad = std::atan2(y, x);
+            float angle = angleInDegrees?
+                          angle_rad * static_cast<float>(180 / CV_PI):
+                          angle_rad;
+            out1[l] = magnitude;
+            out2[l] = angle;
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidResize, cv::gapi::core::GResize, true)
+{
+    static const int Window = 1;
+    static const auto Kind = GFluidKernel::Kind::Resize;
+
+    constexpr static const int INTER_RESIZE_COEF_BITS = 11;
+    constexpr static const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
+    constexpr static const short ONE = INTER_RESIZE_COEF_SCALE;
+
+    struct ResizeUnit
+    {
+        short alpha0;
+        short alpha1;
+        int   s0;
+        int   s1;
+    };
+
+    static ResizeUnit map(double ratio, int start, int max, int outCoord)
+    {
+        float f = static_cast<float>((outCoord + 0.5f) * ratio - 0.5f);
+        int s = cvFloor(f);
+        f -= s;
+
+        ResizeUnit ru;
+
+        ru.s0 = std::max(s - start, 0);
+        ru.s1 = ((f == 0.0) || s + 1 >= max) ? s - start : s - start + 1;
+
+        ru.alpha0 = saturate_cast<short>((1.0f - f) * INTER_RESIZE_COEF_SCALE);
+        ru.alpha1 = saturate_cast<short>((f) * INTER_RESIZE_COEF_SCALE);
+
+        return ru;
+    }
+
+    static void initScratch(const cv::GMatDesc& in,
+                            cv::Size outSz, double /*fx*/, double /*fy*/, int /*interp*/,
+                            cv::gapi::fluid::Buffer &scratch)
+    {
+        CV_Assert(in.depth == CV_8U && in.chan == 3);
+
+        cv::Size scratch_size{static_cast<int>(outSz.width * sizeof(ResizeUnit)), 1};
+
+        cv::GMatDesc desc;
+        desc.chan  = 1;
+        desc.depth = CV_8UC1;
+        desc.size  = to_own(scratch_size);
+
+        cv::gapi::fluid::Buffer buffer(desc);
+        scratch = std::move(buffer);
+
+        ResizeUnit* mapX = scratch.OutLine<ResizeUnit>();
+        double hRatio = (double)in.size.width / outSz.width;
+
+        for (int x = 0, w = outSz.width; x < w; x++)
+        {
+            mapX[x] = map(hRatio, 0, in.size.width, x);
+        }
+    }
+
+    static void resetScratch(cv::gapi::fluid::Buffer& /*scratch*/)
+    {}
+
+    static void run(const cv::gapi::fluid::View& in, cv::Size /*sz*/, double /*fx*/, double /*fy*/, int /*interp*/,
+                    cv::gapi::fluid::Buffer& out, cv::gapi::fluid::Buffer &scratch)
+    {
+        double vRatio = (double)in.meta().size.height / out.meta().size.height;
+        auto mapY = map(vRatio, in.y(), in.meta().size.height, out.y());
+
+        auto beta0 = mapY.alpha0;
+        auto beta1 = mapY.alpha1;
+
+        const auto src0 = in.InLine <unsigned char>(mapY.s0);
+        const auto src1 = in.InLine <unsigned char>(mapY.s1);
+
+        auto dst = out.OutLine<unsigned char>();
+
+        ResizeUnit* mapX = scratch.OutLine<ResizeUnit>();
+
+        for (int x = 0; x < out.length(); x++)
+        {
+            short alpha0 = mapX[x].alpha0;
+            short alpha1 = mapX[x].alpha1;
+            int sx0 = mapX[x].s0;
+            int sx1 = mapX[x].s1;
+
+            int res00 = src0[3*sx0    ]*alpha0 + src0[3*(sx1)    ]*alpha1;
+            int res10 = src1[3*sx0    ]*alpha0 + src1[3*(sx1)    ]*alpha1;
+
+            int res01 = src0[3*sx0 + 1]*alpha0 + src0[3*(sx1) + 1]*alpha1;
+            int res11 = src1[3*sx0 + 1]*alpha0 + src1[3*(sx1) + 1]*alpha1;
+
+            int res02 = src0[3*sx0 + 2]*alpha0 + src0[3*(sx1) + 2]*alpha1;
+            int res12 = src1[3*sx0 + 2]*alpha0 + src1[3*(sx1) + 2]*alpha1;
+
+            dst[3*x    ] = uchar(( ((beta0 * (res00 >> 4)) >> 16) + ((beta1 * (res10 >> 4)) >> 16) + 2)>>2);
+            dst[3*x + 1] = uchar(( ((beta0 * (res01 >> 4)) >> 16) + ((beta1 * (res11 >> 4)) >> 16) + 2)>>2);
+            dst[3*x + 2] = uchar(( ((beta0 * (res02 >> 4)) >> 16) + ((beta1 * (res12 >> 4)) >> 16) + 2)>>2);
+        }
+    }
+};
+
+} // namespace fliud
+} // namespace gapi
+} // namespace cv
+
+cv::gapi::GKernelPackage cv::gapi::core::fluid::kernels()
+{
+    using namespace cv::gapi::fluid;
+
+    return cv::gapi::kernels
+     <       GFluidAdd
+            ,GFluidSub
+            ,GFluidMul
+            ,GFluidDiv
+            ,GFluidAbsDiff
+            ,GFluidAnd
+            ,GFluidOr
+            ,GFluidXor
+            ,GFluidMin
+            ,GFluidMax
+            ,GFluidCmpGT
+            ,GFluidCmpGE
+            ,GFluidCmpLE
+            ,GFluidCmpLT
+            ,GFluidCmpEQ
+            ,GFluidCmpNE
+            ,GFluidAddW
+            ,GFluidNot
+            ,GFluidLUT
+            ,GFluidConvertTo
+            ,GFluidSplit3
+            ,GFluidSplit4
+            ,GFluidMerge3
+            ,GFluidMerge4
+            ,GFluidSelect
+            ,GFluidPolarToCart
+            ,GFluidCartToPolar
+            ,GFluidAddC
+            ,GFluidSubC
+            ,GFluidSubRC
+            ,GFluidMulC
+            ,GFluidMulCOld
+            ,GFluidDivC
+            ,GFluidDivRC
+            ,GFluidAbsDiffC
+            ,GFluidCmpGTScalar
+            ,GFluidCmpGEScalar
+            ,GFluidCmpLEScalar
+            ,GFluidCmpLTScalar
+            ,GFluidCmpEQScalar
+            ,GFluidCmpNEScalar
+            ,GFluidThreshold
+            ,GFluidInRange
+            ,GFluidResize
+        #if 0
+            ,GFluidMean        -- not fluid
+            ,GFluidSum         -- not fluid
+            ,GFluidNormL1      -- not fluid
+            ,GFluidNormL2      -- not fluid
+            ,GFluidNormInf     -- not fluid
+            ,GFluidIntegral    -- not fluid
+            ,GFluidThresholdOT -- not fluid
+            ,GFluidResize      -- not fluid (?)
+            ,GFluidRemap       -- not fluid
+            ,GFluidFlip        -- not fluid
+            ,GFluidCrop        -- not fluid
+            ,GFluidConcatHor
+            ,GFluidConcatVert  -- not fluid
+        #endif
+        >();
+}
diff --git a/modules/gapi/src/backends/fluid/gfluidcore.hpp b/modules/gapi/src/backends/fluid/gfluidcore.hpp
new file mode 100644 (file)
index 0000000..eeae8d3
--- /dev/null
@@ -0,0 +1,19 @@
+// 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_GFLUIDCORE_HPP
+#define OPENCV_GAPI_GFLUIDCORE_HPP
+
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+namespace cv { namespace gapi { namespace core { namespace fluid {
+
+GAPI_EXPORTS GKernelPackage kernels();
+
+}}}}
+
+#endif // OPENCV_GAPI_GFLUIDCORE_HPP
diff --git a/modules/gapi/src/backends/fluid/gfluidimgproc.cpp b/modules/gapi/src/backends/fluid/gfluidimgproc.cpp
new file mode 100644 (file)
index 0000000..1dd291a
--- /dev/null
@@ -0,0 +1,1325 @@
+// 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
+
+
+#include "opencv2/gapi/own/assert.hpp"
+#include "opencv2/core/traits.hpp"
+#include "opencv2/imgproc/types_c.h"
+
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+
+#include "opencv2/gapi/own/types.hpp"
+
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+#include "gfluidbuffer_priv.hpp"
+#include "gfluidbackend.hpp"
+#include "gfluidimgproc.hpp"
+#include "gfluidutils.hpp"
+
+#include <cmath>
+#include <cstdlib>
+
+namespace cv {
+namespace gapi {
+namespace fluid {
+
+//----------------------------------
+//
+// Fluid kernels: RGB2Gray, BGR2Gray
+//
+//----------------------------------
+
+// Y' = 0.299*R' + 0.587*G' + 0.114*B'
+// U' = (B' - Y')*0.492
+// V' = (R' - Y')*0.877
+static const float coef_rgb2yuv_bt601[5] = {0.299f, 0.587f, 0.114f, 0.492f, 0.877f};
+
+// R' = Y' + 1.140*V'
+// G' = Y' - 0.394*U' - 0.581*V'
+// B' = Y' + 2.032*U'
+static const float coef_yuv2rgb_bt601[4] = {1.140f, -0.394f, -0.581f, 2.032f};
+
+static void run_rgb2gray(Buffer &dst, const View &src, float coef_r, float coef_g, float coef_b)
+{
+    GAPI_Assert(src.meta().depth == CV_8U);
+    GAPI_Assert(dst.meta().depth == CV_8U);
+    GAPI_Assert(src.meta().chan == 3);
+    GAPI_Assert(dst.meta().chan == 1);
+    GAPI_Assert(src.length() == dst.length());
+
+    const auto *in  = src.InLine<uchar>(0);
+          auto *out = dst.OutLine<uchar>();
+
+    int width = dst.length();
+
+    // TODO: Vectorize for SIMD
+    for (int w=0; w < width; w++)
+    {
+        uchar r = in[3*w    ];
+        uchar g = in[3*w + 1];
+        uchar b = in[3*w + 2];
+        float result = coef_r*r + coef_g*g + coef_b*b;
+        out[w] = saturate<uchar>(result, roundf);
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidRGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, float coef_r, float coef_g, float coef_b, Buffer &dst)
+    {
+        run_rgb2gray(dst, src, coef_r, coef_g, coef_b);
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidRGB2Gray, cv::gapi::imgproc::GRGB2Gray, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        float coef_r = coef_rgb2yuv_bt601[0];
+        float coef_g = coef_rgb2yuv_bt601[1];
+        float coef_b = coef_rgb2yuv_bt601[2];
+        run_rgb2gray(dst, src, coef_r, coef_g, coef_b);
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidBGR2Gray, cv::gapi::imgproc::GBGR2Gray, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        float coef_r = coef_rgb2yuv_bt601[0];
+        float coef_g = coef_rgb2yuv_bt601[1];
+        float coef_b = coef_rgb2yuv_bt601[2];
+        run_rgb2gray(dst, src, coef_b, coef_g, coef_r);
+    }
+};
+
+//--------------------------------------
+//
+// Fluid kernels: RGB-to-YUV, YUV-to-RGB
+//
+//--------------------------------------
+
+static void run_rgb2yuv(Buffer &dst, const View &src, const float coef[5])
+{
+    GAPI_Assert(src.meta().depth == CV_8U);
+    GAPI_Assert(dst.meta().depth == CV_8U);
+    GAPI_Assert(src.meta().chan == 3);
+    GAPI_Assert(dst.meta().chan == 3);
+    GAPI_Assert(src.length() == dst.length());
+
+    const auto *in  = src.InLine<uchar>(0);
+          auto *out = dst.OutLine<uchar>();
+
+    int width = dst.length();
+
+    // TODO: Vectorize for SIMD
+    for (int w=0; w < width; w++)
+    {
+        uchar r = in[3*w    ];
+        uchar g = in[3*w + 1];
+        uchar b = in[3*w + 2];
+        float y = coef[0]*r + coef[1]*g + coef[2]*b;
+        float u = coef[3]*(b - y) + 128;
+        float v = coef[4]*(r - y) + 128;
+        out[3*w    ] = saturate<uchar>(y, roundf);
+        out[3*w + 1] = saturate<uchar>(u, roundf);
+        out[3*w + 2] = saturate<uchar>(v, roundf);
+    }
+}
+
+static void run_yuv2rgb(Buffer &dst, const View &src, const float coef[4])
+{
+    GAPI_Assert(src.meta().depth == CV_8U);
+    GAPI_Assert(dst.meta().depth == CV_8U);
+    GAPI_Assert(src.meta().chan == 3);
+    GAPI_Assert(dst.meta().chan == 3);
+    GAPI_Assert(src.length() == dst.length());
+
+    const auto *in  = src.InLine<uchar>(0);
+          auto *out = dst.OutLine<uchar>();
+
+    int width = dst.length();
+
+    // TODO: Vectorize for SIMD
+    for (int w=0; w < width; w++)
+    {
+        uchar y = in[3*w    ];
+        int   u = in[3*w + 1] - 128;
+        int   v = in[3*w + 2] - 128;
+        float r = y             + coef[0]*v;
+        float g = y + coef[1]*u + coef[2]*v;
+        float b = y + coef[3]*u;
+        out[3*w    ] = saturate<uchar>(r, roundf);
+        out[3*w + 1] = saturate<uchar>(g, roundf);
+        out[3*w + 2] = saturate<uchar>(b, roundf);
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidRGB2YUV, cv::gapi::imgproc::GRGB2YUV, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        run_rgb2yuv(dst, src, coef_rgb2yuv_bt601);
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidYUV2RGB, cv::gapi::imgproc::GYUV2RGB, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        run_yuv2rgb(dst, src, coef_yuv2rgb_bt601);
+    }
+};
+
+//--------------------------------------
+//
+// Fluid kernels: RGB-to-Lab, BGR-to-LUV
+//
+//--------------------------------------
+
+enum LabLUV { LL_Lab, LL_LUV };
+
+// gamma-correction (inverse) for sRGB, 1/gamma=2.4 for inverse, like for Mac OS (?)
+static inline float f_gamma(float x)
+{
+    return x <= 0.04045f ? x*(1.f/12.92f) : std::pow((x + 0.055f)*(1/1.055f), 2.4f);
+}
+
+// saturate into interval [0, 1]
+static inline float clip01(float value)
+{
+    return value < 0? 0:
+           value > 1? 1:
+           value;
+}
+
+static inline void f_rgb2xyz(float  R, float  G, float  B,
+                             float& X, float& Y, float& Z)
+{
+    X = clip01(0.412453f*R + 0.357580f*G + 0.180423f*B);
+    Y = clip01(0.212671f*R + 0.715160f*G + 0.072169f*B);
+    Z = clip01(0.019334f*R + 0.119193f*G + 0.950227f*B);
+}
+
+static inline void f_xyz2lab(float  X, float  Y, float  Z,
+                             float& L, float& a, float& b)
+{
+    // CIE XYZ values of reference white point for D65 illuminant
+    static const float Xn = 0.950456f, Yn = 1.f, Zn = 1.088754f;
+
+    // Other coefficients below:
+    // 7.787f    = (29/3)^3/(29*4)
+    // 0.008856f = (6/29)^3
+    // 903.3     = (29/3)^3
+
+    float x = X/Xn, y = Y/Yn, z = Z/Zn;
+
+    auto f = [](float t){ return t>0.008856f? std::cbrt(t): (7.787f*t + 16.f/116.f); };
+
+    float fx = f(x), fy = f(y), fz = f(z);
+
+    L = y > 0.008856f ? (116.f*std::cbrt(y) - 16.f) : (903.3f * y);
+    a = 500.f * (fx - fy);
+    b = 200.f * (fy - fz);
+}
+
+static inline void f_xyz2luv(float  X, float  Y, float  Z,
+                             float& L, float& u, float& v)
+{
+    static const float un = 0.19793943f, vn = 0.46831096f;
+
+    float u1 = 4*X / (X + 15*Y + 3*Z);
+    float v1 = 9*Y / (X + 15*Y + 3*Z);
+
+    L = Y > 0.008856f ? (116.f*std::cbrt(Y) - 16.f) : (903.3f * Y);
+    u = 13*L * (u1 - un);
+    v = 13*L * (v1 - vn);
+}
+
+// compile-time parameters: output format (Lab/LUV),
+// and position of blue channel in BGR/RGB (0 or 2)
+template<LabLUV labluv, int blue=0>
+static void run_rgb2labluv(Buffer &dst, const View &src)
+{
+    GAPI_Assert(src.meta().depth == CV_8U);
+    GAPI_Assert(dst.meta().depth == CV_8U);
+    GAPI_Assert(src.meta().chan == 3);
+    GAPI_Assert(dst.meta().chan == 3);
+    GAPI_Assert(src.length() == dst.length());
+
+    const auto *in  = src.InLine<uchar>(0);
+          auto *out = dst.OutLine<uchar>();
+
+    int width = dst.length();
+
+    for (int w=0; w < width; w++)
+    {
+        float R, G, B;
+        B = in[3*w +    blue ] / 255.f;
+        G = in[3*w +    1    ] / 255.f;
+        R = in[3*w + (2^blue)] / 255.f;
+
+        B = f_gamma( B );
+        G = f_gamma( G );
+        R = f_gamma( R );
+
+        float X, Y, Z;
+        f_rgb2xyz(R, G, B, X, Y, Z);
+
+        // compile-time `if`
+        if (LL_Lab == labluv)
+        {
+            float L, a, b;
+            f_xyz2lab(X, Y, Z, L, a, b);
+
+            out[3*w    ] = saturate<uchar>(L * 255.f/100, roundf);
+            out[3*w + 1] = saturate<uchar>(a + 128, roundf);
+            out[3*w + 2] = saturate<uchar>(b + 128, roundf);
+        }
+        else if (LL_LUV == labluv)
+        {
+            float L, u, v;
+            f_xyz2luv(X, Y, Z, L, u, v);
+
+            out[3*w    ] = saturate<uchar>( L        * 255.f/100, roundf);
+            out[3*w + 1] = saturate<uchar>((u + 134) * 255.f/354, roundf);
+            out[3*w + 2] = saturate<uchar>((v + 140) * 255.f/262, roundf);
+        }
+        else
+            CV_Error(cv::Error::StsBadArg, "unsupported color conversion");;
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidRGB2Lab, cv::gapi::imgproc::GRGB2Lab, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        static const int blue = 2; // RGB: 0=red, 1=green, 2=blue
+        run_rgb2labluv<LL_Lab, blue>(dst, src);
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidBGR2LUV, cv::gapi::imgproc::GBGR2LUV, false)
+{
+    static const int Window = 1;
+
+    static void run(const View &src, Buffer &dst)
+    {
+        static const int blue = 0; // BGR: 0=blue, 1=green, 2=red
+        run_rgb2labluv<LL_LUV, blue>(dst, src);
+    }
+};
+
+//-------------------------------
+//
+// Fluid kernels: blur, boxFilter
+//
+//-------------------------------
+
+static const int maxKernelSize = 9;
+
+template<typename DST, typename SRC>
+static void run_boxfilter(Buffer &dst, const View &src, const cv::Size &kernelSize,
+                          const cv::Point& /* anchor */, bool normalize)
+{
+    GAPI_Assert(kernelSize.width <= maxKernelSize);
+    GAPI_Assert(kernelSize.width == kernelSize.height);
+
+    int kernel = kernelSize.width;
+    int border = (kernel - 1) / 2;
+
+    const SRC *in[ maxKernelSize ];
+          DST *out;
+
+    for (int i=0; i < kernel; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border);
+    }
+
+    out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    GAPI_DbgAssert(chan <= 4);
+
+    for (int w=0; w < width; w++)
+    {
+        float sum[4] = {0, 0, 0, 0};
+
+        for (int i=0; i < kernel; i++)
+        {
+            for (int j=0; j < kernel; j++)
+            {
+                for (int c=0; c < chan; c++)
+                    sum[c] += in[i][(w + j - border)*chan + c];
+            }
+        }
+
+        for (int c=0; c < chan; c++)
+        {
+            float result = normalize? sum[c]/(kernel * kernel) : sum[c];
+
+            out[w*chan + c] = saturate<DST>(result, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidBlur, cv::gapi::imgproc::GBlur, false)
+{
+    static const int Window = 3;
+
+    static void run(const View &src, const cv::Size& kernelSize, const cv::Point& anchor,
+                    int /* borderType */, const cv::Scalar& /* borderValue */, Buffer &dst)
+    {
+        // TODO: support sizes 3, 5, 7, 9, ...
+        GAPI_Assert(kernelSize.width  == 3 && kernelSize.height == 3);
+
+        // TODO: suport non-trivial anchor
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        static const bool normalize = true;
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_( short,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                            const cv::Size    & /* kernelSize */,
+                            const cv::Point   & /* anchor */,
+                                      int          borderType,
+                            const cv::Scalar  &    borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidBoxFilter, cv::gapi::imgproc::GBoxFilter, false)
+{
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                              int     /* ddepth */,
+                    const cv::Size  &    kernelSize,
+                    const cv::Point &   anchor,
+                              bool       normalize,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst)
+    {
+        // TODO: support sizes 3, 5, 7, 9, ...
+        GAPI_Assert(kernelSize.width  == 3 && kernelSize.height == 3);
+
+        // TODO: suport non-trivial anchor
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_( short,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_( float, uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_( float, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+        UNARY_( float,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ddepth */,
+                            const cv::Size    & /* kernelSize */,
+                            const cv::Point   & /* anchor */,
+                                      bool      /* normalize */,
+                                      int          borderType,
+                            const cv::Scalar  &    borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+//-------------------------
+//
+// Fluid kernels: sepFilter
+//
+//-------------------------
+
+template<typename T>
+static void getKernel(T k[], const cv::Mat& kernel)
+{
+    GAPI_Assert(kernel.channels() == 1);
+
+    int depth = CV_MAT_DEPTH(kernel.type());
+    int cols = kernel.cols;
+    int rows = kernel.rows;
+
+    switch ( depth )
+    {
+    case CV_8U:
+        for (int h=0; h < rows; h++)
+        for (int w=0; w < cols; w++)
+            k[h*cols + w] = static_cast<T>( kernel.at<uchar>(h, w) );
+        break;
+    case CV_16U:
+        for (int h=0; h < rows; h++)
+        for (int w=0; w < cols; w++)
+            k[h*cols + w] = static_cast<T>( kernel.at<ushort>(h, w) );
+        break;
+    case CV_16S:
+        for (int h=0; h < rows; h++)
+        for (int w=0; w < cols; w++)
+            k[h*cols + w] = static_cast<T>( kernel.at<short>(h, w) );
+        break;
+    case CV_32F:
+        for (int h=0; h < rows; h++)
+        for (int w=0; w < cols; w++)
+            k[h*cols + w] = static_cast<T>( kernel.at<float>(h, w) );
+        break;
+    default: CV_Error(cv::Error::StsBadArg, "unsupported kernel type");
+    }
+}
+
+template<typename DST, typename SRC>
+static void run_sepfilter(Buffer& dst, const View& src,
+                          const float kx[], int kxLen,
+                          const float ky[], int kyLen,
+                          const cv::Point& /* anchor */,
+                          float delta=0)
+{
+    static const int maxLines = 9;
+    GAPI_Assert(kyLen <= maxLines);
+
+    const SRC *in[ maxLines ];
+          DST *out;
+
+    int border = (kyLen - 1) / 2;
+    for (int i=0; i < kyLen; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border);
+    }
+
+    out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    for (int w=0; w < width; w++)
+    {
+        // TODO: make this cycle innermost
+        for (int c=0; c < chan; c++)
+        {
+            float sum=0;
+
+            for (int i=0; i < kyLen; i++)
+            {
+                float sumi=0;
+
+                for (int j=0; j < kxLen; j++)
+                {
+                    sumi += in[i][(w + j - border)*chan + c] * kx[j];
+                }
+
+                sum += sumi * ky[i];
+            }
+
+            float result = sum + delta;
+
+            out[w*chan + c] = saturate<DST>(result, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidSepFilter, cv::gapi::imgproc::GSepFilter, true)
+{
+    static const int Window = 3;
+
+    static void run(const     View&      src,
+                              int     /* ddepth */,
+                    const cv::Mat&       kernX,
+                    const cv::Mat&       kernY,
+                    const cv::Point&     anchor,
+                    const cv::Scalar&    delta_,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        // TODO: support non-trivial anchors
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        // TODO: support kernel heights 3, 5, 7, 9, ...
+        GAPI_Assert((kernY.rows == 1 || kernY.cols == 1)  && (kernY.cols * kernY.rows == 3));
+        GAPI_Assert((kernX.rows == 1 || kernX.cols == 1));
+
+        int kxLen = kernX.rows * kernX.cols;
+        int kyLen = kernY.rows * kernY.cols;
+
+        float *kx = scratch.OutLine<float>();
+        float *ky = kx + kxLen;
+
+        float delta = static_cast<float>(delta_[0]);
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);
+        UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);
+        UNARY_( short,  short, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);
+        UNARY_( float,  float, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const GMatDesc& /* in */,
+                                  int       /* ddepth */,
+                            const Mat     &    kernX,
+                            const Mat     &    kernY,
+                            const Point   & /* anchor */,
+                            const Scalar  & /* delta */,
+                                  int       /* borderType */,
+                            const Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        int kxLen = kernX.rows * kernX.cols;
+        int kyLen = kernY.rows * kernY.cols;
+
+        cv::gapi::own::Size bufsize(kxLen + kyLen, 1);
+        GMatDesc bufdesc = {CV_32F, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: move to resetScratch stage ?
+        float *kx = scratch.OutLine<float>();
+        float *ky = kx + kxLen;
+        getKernel(kx, kernX);
+        getKernel(ky, kernY);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ddepth */,
+                            const cv::Mat&      /* kernX */,
+                            const cv::Mat&      /* kernY */,
+                            const cv::Point&    /* anchor */,
+                            const cv::Scalar&   /* delta */,
+                                      int          borderType,
+                            const cv::Scalar&      borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+//----------------------------
+//
+// Fluid kernels: gaussianBlur
+//
+//----------------------------
+
+GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
+{
+    // TODO: support kernel height 3, 5, 7, 9, ...
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                    const cv::Size  &    ksize,
+                              double  /* sigmaX */,
+                              double  /* sigmaY */,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        GAPI_Assert(ksize.height == 3);
+
+        int kxsize = ksize.width;
+        int kysize = ksize.height;
+
+        auto *kx = scratch.OutLine<float>(); // cached kernX data
+        auto *ky = kx + kxsize;              // cached kernY data
+
+        auto  anchor = cv::Point(-1, -1);
+        float delta = 0.f;
+
+        //     DST     SRC     OP             __VA_ARGS__
+        UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);
+        UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);
+        UNARY_( short,  short, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const GMatDesc& /* in */,
+                            const cv::Size &   ksize,
+                                  double       sigmaX,
+                                  double       sigmaY,
+                                  int       /* borderType */,
+                            const cv::Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        int kxsize = ksize.width;
+        int kysize = ksize.height;
+
+        cv::gapi::own::Size bufsize(kxsize + kysize, 1);
+        GMatDesc bufdesc = {CV_32F, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: fill buffer at resetScratch stage?
+
+        if (sigmaX == 0)
+            sigmaX = 0.3 * ((kxsize - 1)/2. - 1) + 0.8;
+
+        if (sigmaY == 0)
+            sigmaY = sigmaX;
+
+        Mat kernX = getGaussianKernel(kxsize, sigmaX, CV_32F);
+
+        Mat kernY = kernX;
+        if (sigmaY != sigmaX)
+            kernY = getGaussianKernel(kysize, sigmaY, CV_32F);
+
+        auto *kx = scratch.OutLine<float>();
+        auto *ky = kx + kxsize;
+
+        getKernel(kx, kernX);
+        getKernel(ky, kernY);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                            const cv::Size    & /* ksize */,
+                                      double    /* sigmaX */,
+                                      double    /* sigmaY */,
+                                      int          borderType,
+                            const cv::Scalar  &    borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+//---------------------
+//
+// Fluid kernels: Sobel
+//
+//---------------------
+
+template<typename DST, typename SRC>
+static void run_sobel(Buffer& dst,
+                const View  & src,
+                      float   kx[],
+                      float   ky[],
+                      int     ksize,
+                      float   scale=1,
+                      float   delta=0)
+{
+    static const int kmax = 11;
+    GAPI_Assert(ksize <= kmax);
+
+    const SRC *in[ kmax ];
+          DST *out;
+
+    int border = (ksize - 1) / 2;
+    for (int i=0; i < ksize; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border);
+    }
+
+    out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    for (int w=0; w < width; w++)
+    {
+        // TODO: make this cycle innermost
+        for (int c=0; c < chan; c++)
+        {
+            float sum=0;
+
+            for (int i=0; i < ksize; i++)
+            {
+                float sumi=0;
+
+                for (int j=0; j < ksize; j++)
+                {
+                    sumi += in[i][(w + j - border)*chan + c] * kx[j];
+                }
+
+                sum += sumi * ky[i];
+            }
+
+            float result = sum*scale + delta;
+
+            out[w*chan + c] = saturate<DST>(result, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true)
+{
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                              int     /* ddepth */,
+                              int     /* dx */,
+                              int     /* dy */,
+                              int        ksize,
+                              double    _scale,
+                              double    _delta,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        // TODO: support kernel height 3, 5, 7, 9, ...
+        GAPI_Assert(ksize == 3 || ksize == CV_SCHARR);
+
+        if (ksize == CV_SCHARR)
+            ksize = 3;
+
+        auto *kx = scratch.OutLine<float>();
+        auto *ky = kx + ksize;
+
+        auto scale = static_cast<float>(_scale);
+        auto delta = static_cast<float>(_delta);
+
+        //     DST     SRC     OP         __VA_ARGS__
+        UNARY_(uchar , uchar , run_sobel, dst, src, kx, ky, ksize, scale, delta);
+        UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksize, scale, delta);
+        UNARY_( short,  short, run_sobel, dst, src, kx, ky, ksize, scale, delta);
+        UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksize, scale, delta);
+        UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksize, scale, delta);
+        UNARY_( float,  short, run_sobel, dst, src, kx, ky, ksize, scale, delta);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const GMatDesc& /* in */,
+                                  int       /* ddepth */,
+                                  int          dx,
+                                  int          dy,
+                                  int          ksize,
+                                  double    /* scale */,
+                                  double    /* delta */,
+                                  int       /* borderType */,
+                            const Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        cv::gapi::own::Size bufsize(ksize + ksize, 1);
+        GMatDesc bufdesc = {CV_32F, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: move to resetScratch stage ?
+        auto *kx = scratch.OutLine<float>();
+        auto *ky = kx + ksize;
+        Mat kxmat(1, ksize, CV_32FC1, kx);
+        Mat kymat(ksize, 1, CV_32FC1, ky);
+        getDerivKernels(kxmat, kymat, dx, dy, ksize);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ddepth */,
+                                      int       /* dx */,
+                                      int       /* dy */,
+                                      int       /* ksize */,
+                                      double    /* scale */,
+                                      double    /* delta */,
+                                      int          borderType,
+                            const cv::Scalar  &    borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+//------------------------
+//
+// Fluid kernels: filter2D
+//
+//------------------------
+
+template<typename DST, typename SRC>
+static void run_filter2d(Buffer& dst, const View& src,
+                         const float k[], int k_rows, int k_cols,
+                         const cv::Point& /* anchor */,
+                         float delta=0)
+{
+    static const int maxLines = 9;
+    GAPI_Assert(k_rows <= maxLines);
+
+    const SRC *in[ maxLines ];
+          DST *out;
+
+    int border_x = (k_cols - 1) / 2;
+    int border_y = (k_rows - 1) / 2;
+
+    for (int i=0; i < k_rows; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border_y);
+    }
+
+    out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    for (int w=0; w < width; w++)
+    {
+        // TODO: make this cycle innermost
+        for (int c=0; c < chan; c++)
+        {
+            float sum = 0;
+
+            for (int i=0; i < k_rows; i++)
+            for (int j=0; j < k_cols; j++)
+            {
+                sum += in[i][(w + j - border_x)*chan + c] * k[k_cols*i + j];
+            }
+
+            float result = sum + delta;
+
+            out[w*chan + c] = saturate<DST>(result, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidFilter2D, cv::gapi::imgproc::GFilter2D, true)
+{
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                              int     /* ddepth */,
+                    const cv::Mat   &    kernel,
+                    const cv::Point &    anchor,
+                    const cv::Scalar&    delta_,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        // TODO: support non-trivial anchors
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        // TODO: support kernel heights 3, 5, 7, 9, ...
+        GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
+
+        float delta = static_cast<float>(delta_[0]);
+
+        int k_rows = kernel.rows;
+        int k_cols = kernel.cols;
+        const float *k = scratch.OutLine<float>(); // copy of kernel.data
+
+        //     DST     SRC     OP            __VA_ARGS__
+        UNARY_(uchar , uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_(ushort, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_( short,  short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_( float, uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_( float, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_( float,  short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+        UNARY_( float,  float, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const cv::GMatDesc& /* in */,
+                                      int       /* ddepth */,
+                            const cv::Mat     &    kernel,
+                            const cv::Point   & /* anchor */,
+                            const cv::Scalar  & /* delta */,
+                                      int       /* borderType */,
+                            const cv::Scalar  & /* borderValue */,
+                                      Buffer  &    scratch)
+    {
+        cv::gapi::own::Size bufsize(kernel.rows * kernel.cols, 1);
+        GMatDesc bufdesc = {CV_32F, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: move to resetScratch stage ?
+        float *data = scratch.OutLine<float>();
+        getKernel(data, kernel);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ddepth */,
+                            const cv::Mat&      /* kernel */,
+                            const cv::Point&    /* anchor */,
+                            const cv::Scalar&   /* delta */,
+                                      int          borderType,
+                            const cv::Scalar&      borderValue)
+    {
+        return { borderType, borderValue};
+    }
+};
+
+//-----------------------------
+//
+// Fluid kernels: erode, dilate
+//
+//-----------------------------
+
+enum Morphology { M_ERODE, M_DILATE };
+
+template<typename DST, typename SRC>
+static void run_morphology(          Buffer&    dst,
+                           const     View  &    src,
+                           const     uchar      k[],
+                                     int        k_rows,
+                                     int        k_cols,
+                           const cv::Point & /* anchor */,
+                                     Morphology morphology)
+{
+    static const int maxLines = 9;
+    GAPI_Assert(k_rows <= maxLines);
+
+    const SRC *in[ maxLines ];
+          DST *out;
+
+    int border_x = (k_cols - 1) / 2;
+    int border_y = (k_rows - 1) / 2;
+
+    for (int i=0; i < k_rows; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border_y);
+    }
+
+    out = dst.OutLine<DST>();
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    for (int w=0; w < width; w++)
+    {
+        // TODO: make this cycle innermost
+        for (int c=0; c < chan; c++)
+        {
+            SRC result=0;
+            if (M_ERODE == morphology)
+            {
+                result = std::numeric_limits<SRC>::max();
+            }
+            else if (M_DILATE == morphology)
+            {
+                result = std::numeric_limits<SRC>::min();
+            }
+            else
+                CV_Error(cv::Error::StsBadArg, "unsupported morphology operation");
+
+            for (int i=0; i < k_rows; i++)
+            for (int j=0; j < k_cols; j++)
+            {
+                if ( k[k_cols*i + j] )
+                {
+                    if (M_ERODE == morphology)
+                    {
+                        result = std::min(result, in[i][(w + j - border_x)*chan + c]);
+                    }
+                    else if (M_DILATE == morphology)
+                    {
+                        result = std::max(result, in[i][(w + j - border_x)*chan + c]);
+                    }
+                    else
+                        CV_Error(cv::Error::StsBadArg, "unsupported morphology operation");
+                }
+            }
+
+            out[w*chan + c] = saturate<DST>(result, rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidErode, cv::gapi::imgproc::GErode, true)
+{
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                    const cv::Mat   &    kernel,
+                    const cv::Point &    anchor,
+                              int        iterations,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        // TODO: support non-trivial anchors
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        // TODO: support kernel heights 3, 5, 7, 9, ...
+        GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
+
+        // TODO: support iterations > 1
+        GAPI_Assert(iterations == 1);
+
+        int k_rows = kernel.rows;
+        int k_cols = kernel.cols;
+
+        auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
+
+        //     DST     SRC     OP              __VA_ARGS__
+        UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);
+        UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);
+        UNARY_( short,  short, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const GMatDesc& /* in */,
+                            const Mat     &    kernel,
+                            const Point   & /* anchor */,
+                              int           /* iterations */,
+                                  int       /* borderType */,
+                            const cv::Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        int k_rows = kernel.rows;
+        int k_cols = kernel.cols;
+
+        cv::gapi::own::Size bufsize(k_rows * k_cols, 1);
+        GMatDesc bufdesc = {CV_8U, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: move to resetScratch stage ?
+        auto *k = scratch.OutLine<uchar>();
+        getKernel(k, kernel);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                            const cv::Mat   &   /* kernel */,
+                            const cv::Point &   /* anchor */,
+                                      int       /* iterations */,
+                                      int          borderType,
+                            const cv::Scalar&      borderValue)
+    {
+    #if 1
+        // TODO: saturate borderValue to image type in general case (not only maximal border)
+        GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);
+        return { borderType, cv::gapi::own::Scalar::all(INT_MAX) };
+    #else
+        return { borderType, borderValue };
+    #endif
+    }
+};
+
+GAPI_FLUID_KERNEL(GFluidDilate, cv::gapi::imgproc::GDilate, true)
+{
+    static const int Window = 3;
+
+    static void run(const     View  &    src,
+                    const cv::Mat   &    kernel,
+                    const cv::Point &    anchor,
+                              int        iterations,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    dst,
+                              Buffer&    scratch)
+    {
+        // TODO: support non-trivial anchors
+        GAPI_Assert(anchor.x == -1 && anchor.y == -1);
+
+        // TODO: support kernel heights 3, 5, 7, 9, ...
+        GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
+
+        // TODO: support iterations > 1
+        GAPI_Assert(iterations == 1);
+
+        int k_rows = kernel.rows;
+        int k_cols = kernel.cols;
+
+        auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
+
+        //     DST     SRC     OP              __VA_ARGS__
+        UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);
+        UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);
+        UNARY_( short,  short, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static void initScratch(const GMatDesc& /* in */,
+                            const Mat     &    kernel,
+                            const Point   & /* anchor */,
+                              int           /* iterations */,
+                                  int       /* borderType */,
+                            const cv::Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        int k_rows = kernel.rows;
+        int k_cols = kernel.cols;
+
+        cv::gapi::own::Size bufsize(k_rows * k_cols, 1);
+        GMatDesc bufdesc = {CV_8U, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        // FIXME: move to resetScratch stage ?
+        auto *k = scratch.OutLine<uchar>();
+        getKernel(k, kernel);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                            const cv::Mat   &   /* kernel */,
+                            const cv::Point &   /* anchor */,
+                                      int       /* iterations */,
+                                      int       borderType,
+                            const cv::Scalar&   borderValue)
+    {
+    #if 1
+        // TODO: fix borderValue for Dilate in general case (not only minimal border)
+        GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);
+        return { borderType, cv::gapi::own::Scalar::all(INT_MIN) };
+    #else
+        return { borderType, borderValue };
+    #endif
+    }
+};
+
+//--------------------------
+//
+// Fluid kernels: medianBlur
+//
+//--------------------------
+
+template<typename DST, typename SRC>
+static void run_medianblur(      Buffer& dst,
+                           const View  & src,
+                                 int     ksize)
+{
+    static const int kmax = 9;
+    GAPI_Assert(ksize <= kmax);
+
+    const SRC *in[ kmax ];
+          DST *out;
+
+    int border = (ksize - 1) / 2;
+
+    for (int i=0; i < ksize; i++)
+    {
+        in[i] = src.InLine<SRC>(i - border);
+    }
+
+    out = dst.OutLine<DST>(0);
+
+    int width = dst.length();
+    int chan  = dst.meta().chan;
+
+    for (int w=0; w < width; w++)
+    {
+        // TODO: make this cycle innermost
+        for (int c=0; c < chan; c++)
+        {
+            SRC neighbours[kmax * kmax];
+
+            for (int i=0; i < ksize; i++)
+            for (int j=0; j < ksize; j++)
+            {
+                neighbours[i*ksize + j] = in[i][(w + j - border)*chan + c];
+            }
+
+            int length = ksize * ksize;
+            std::nth_element(neighbours, neighbours + length/2, neighbours + length);
+
+            out[w*chan + c] = saturate<DST>(neighbours[length/2], rintf);
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(GFluidMedianBlur, cv::gapi::imgproc::GMedianBlur, false)
+{
+    static const int Window = 3;
+
+    static void run(const View  & src,
+                          int     ksize,
+                          Buffer& dst)
+    {
+        // TODO: support kernel sizes: 3, 5, 7, 9, ...
+        GAPI_Assert(ksize == 3);
+
+        //     DST     SRC     OP              __VA_ARGS__
+        UNARY_(uchar , uchar , run_medianblur, dst, src, ksize);
+        UNARY_(ushort, ushort, run_medianblur, dst, src, ksize);
+        UNARY_( short,  short, run_medianblur, dst, src, ksize);
+
+        CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ksize */)
+    {
+        int  borderType  = cv::BORDER_REPLICATE;
+        auto borderValue = cv::Scalar();
+        return { borderType, borderValue };
+    }
+};
+
+} // namespace fliud
+} // namespace gapi
+} // namespace cv
+
+cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()
+{
+    using namespace cv::gapi::fluid;
+
+    return cv::gapi::kernels
+    <   GFluidBGR2Gray
+      , GFluidRGB2Gray
+      , GFluidRGB2GrayCustom
+      , GFluidRGB2YUV
+      , GFluidYUV2RGB
+      , GFluidRGB2Lab
+      , GFluidBGR2LUV
+      , GFluidBlur
+      , GFluidSepFilter
+      , GFluidBoxFilter
+      , GFluidFilter2D
+      , GFluidErode
+      , GFluidDilate
+      , GFluidMedianBlur
+      , GFluidGaussBlur
+      , GFluidSobel
+    #if 0
+      , GFluidCanny        -- not fluid (?)
+      , GFluidEqualizeHist -- not fluid
+    #endif
+    >();
+}
diff --git a/modules/gapi/src/backends/fluid/gfluidimgproc.hpp b/modules/gapi/src/backends/fluid/gfluidimgproc.hpp
new file mode 100644 (file)
index 0000000..810f736
--- /dev/null
@@ -0,0 +1,19 @@
+// 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_GFLUIDIMGPROC_HPP
+#define OPENCV_GAPI_GFLUIDIMGPROC_HPP
+
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+namespace cv { namespace gapi { namespace imgproc { namespace fluid {
+
+GAPI_EXPORTS GKernelPackage kernels();
+
+}}}}
+
+#endif // OPENCV_GAPI_GFLUIDIMGPROC_HPP
diff --git a/modules/gapi/src/backends/fluid/gfluidutils.hpp b/modules/gapi/src/backends/fluid/gfluidutils.hpp
new file mode 100644 (file)
index 0000000..2749525
--- /dev/null
@@ -0,0 +1,154 @@
+// 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 GFLUIDUTILS_HPP
+#define GFLUIDUTILS_HPP
+
+#include <limits>
+#include <type_traits>
+#include <opencv2/gapi/util/compiler_hints.hpp> //UNUSED
+
+namespace cv {
+namespace gapi {
+namespace fluid {
+
+//-----------------------------
+//
+// Numeric cast with saturation
+//
+//-----------------------------
+
+template<typename DST, typename SRC>
+static inline DST saturate(SRC x)
+{
+    // only integral types please!
+    GAPI_DbgAssert(std::is_integral<DST>::value &&
+                   std::is_integral<SRC>::value);
+
+    if (std::is_same<DST, SRC>::value)
+        return static_cast<DST>(x);
+
+    if (sizeof(DST) > sizeof(SRC))
+        return static_cast<DST>(x);
+
+    // compiler must recognize this saturation,
+    // so compile saturate<s16>(a + b) with adds
+    // instruction (e.g.: _mm_adds_epi16 if x86)
+    return x < std::numeric_limits<DST>::min()?
+               std::numeric_limits<DST>::min():
+           x > std::numeric_limits<DST>::max()?
+               std::numeric_limits<DST>::max():
+           static_cast<DST>(x);
+}
+
+// Note, that OpenCV rounds differently:
+// - like std::round() for add, subtract
+// - like std::rint() for multiply, divide
+template<typename DST, typename SRC, typename R>
+static inline DST saturate(SRC x, R round)
+{
+    if (std::is_floating_point<DST>::value)
+    {
+        return static_cast<DST>(x);
+    }
+    else if (std::is_integral<SRC>::value)
+    {
+        GAPI_DbgAssert(std::is_integral<DST>::value &&
+                       std::is_integral<SRC>::value);
+        return saturate<DST>(x);
+    }
+    else
+    {
+        GAPI_DbgAssert(std::is_integral<DST>::value &&
+                 std::is_floating_point<SRC>::value);
+#ifdef _WIN32
+// Suppress warning about convering x to floating-point
+// Note that x is already floating-point at this point
+#pragma warning(disable: 4244)
+#endif
+        int ix = static_cast<int>(round(x));
+#ifdef _WIN32
+#pragma warning(default: 4244)
+#endif
+        return saturate<DST>(ix);
+    }
+}
+
+// explicit suffix 'd' for double type
+static inline double  ceild(double x) { return std::ceil(x); }
+static inline double floord(double x) { return std::floor(x); }
+static inline double roundd(double x) { return std::round(x); }
+static inline double  rintd(double x) { return std::rint(x); }
+
+//--------------------------------
+//
+// Macros for mappig of data types
+//
+//--------------------------------
+
+#define UNARY_(DST, SRC, OP, ...)                         \
+    if (cv::DataType<DST>::depth == dst.meta().depth &&   \
+        cv::DataType<SRC>::depth == src.meta().depth)     \
+    {                                                     \
+        GAPI_DbgAssert(dst.length() == src.length());       \
+        GAPI_DbgAssert(dst.meta().chan == src.meta().chan); \
+                                                          \
+        OP<DST, SRC>(__VA_ARGS__);                        \
+        return;                                           \
+    }
+
+// especial unary operation: dst is always 8UC1 image
+#define INRANGE_(DST, SRC, OP, ...)                       \
+    if (cv::DataType<DST>::depth == dst.meta().depth &&   \
+        cv::DataType<SRC>::depth == src.meta().depth)     \
+    {                                                     \
+        GAPI_DbgAssert(dst.length() == src.length());       \
+        GAPI_DbgAssert(dst.meta().chan == 1);               \
+                                                          \
+        OP<DST, SRC>(__VA_ARGS__);                        \
+        return;                                           \
+    }
+
+#define BINARY_(DST, SRC1, SRC2, OP, ...)                  \
+    if (cv::DataType<DST>::depth == dst.meta().depth &&    \
+        cv::DataType<SRC1>::depth == src1.meta().depth &&  \
+        cv::DataType<SRC2>::depth == src2.meta().depth)    \
+    {                                                      \
+        GAPI_DbgAssert(dst.length() == src1.length());       \
+        GAPI_DbgAssert(dst.length() == src2.length());       \
+                                                           \
+        GAPI_DbgAssert(dst.meta().chan == src1.meta().chan); \
+        GAPI_DbgAssert(dst.meta().chan == src2.meta().chan); \
+                                                           \
+        OP<DST, SRC1, SRC2>(__VA_ARGS__);                  \
+        return;                                            \
+    }
+
+// especial ternary operation: src3 has only one channel
+#define SELECT_(DST, SRC1, SRC2, SRC3, OP, ...)            \
+    if (cv::DataType<DST>::depth == dst.meta().depth &&    \
+        cv::DataType<SRC1>::depth == src1.meta().depth &&  \
+        cv::DataType<SRC2>::depth == src2.meta().depth &&  \
+        cv::DataType<SRC3>::depth == src3.meta().depth)    \
+    {                                                      \
+        GAPI_DbgAssert(dst.length() == src1.length());       \
+        GAPI_DbgAssert(dst.length() == src2.length());       \
+        GAPI_DbgAssert(dst.length() == src3.length());       \
+                                                           \
+        GAPI_DbgAssert(dst.meta().chan == src1.meta().chan); \
+        GAPI_DbgAssert(dst.meta().chan == src2.meta().chan); \
+        GAPI_DbgAssert(              1 == src3.meta().chan); \
+                                                           \
+        OP<DST, SRC1, SRC2, SRC3>(__VA_ARGS__);            \
+        return;                                            \
+    }
+
+} // namespace fluid
+} // namespace gapi
+} // namespace cv
+
+#endif // GFLUIDUTILS_HPP
diff --git a/modules/gapi/src/compiler/README.md b/modules/gapi/src/compiler/README.md
new file mode 100644 (file)
index 0000000..995aa39
--- /dev/null
@@ -0,0 +1 @@
+This directory contains G-API graph compiler logic.
\ No newline at end of file
diff --git a/modules/gapi/src/compiler/gcompiled.cpp b/modules/gapi/src/compiler/gcompiled.cpp
new file mode 100644 (file)
index 0000000..49f5ed8
--- /dev/null
@@ -0,0 +1,131 @@
+// 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
+
+
+#include <ade/graph.hpp>
+
+#include "opencv2/gapi/gproto.hpp" // descr_of
+#include "opencv2/gapi/gcompiled.hpp"
+
+#include "compiler/gcompiled_priv.hpp"
+#include "backends/common/gbackend.hpp"
+
+// GCompiled private implementation ////////////////////////////////////////////
+void cv::GCompiled::Priv::setup(const GMetaArgs &_metaArgs,
+                                const GMetaArgs &_outMetas,
+                                std::unique_ptr<cv::gimpl::GExecutor> &&_pE)
+{
+    m_metas    = _metaArgs;
+    m_outMetas = _outMetas;
+    m_exec     = std::move(_pE);
+}
+
+bool cv::GCompiled::Priv::isEmpty() const
+{
+    return !m_exec;
+}
+
+void cv::GCompiled::Priv::run(cv::gimpl::GRuntimeArgs &&args)
+{
+    // Strip away types since ADE knows nothing about that
+    // args will be taken by specific GBackendExecutables
+    checkArgs(args);
+    m_exec->run(std::move(args));
+}
+
+const cv::GMetaArgs& cv::GCompiled::Priv::metas() const
+{
+    return m_metas;
+}
+
+const cv::GMetaArgs& cv::GCompiled::Priv::outMetas() const
+{
+    return m_outMetas;
+}
+
+void cv::GCompiled::Priv::checkArgs(const cv::gimpl::GRuntimeArgs &args) const
+{
+    const auto runtime_metas = descr_of(args.inObjs);
+    if (runtime_metas != m_metas)
+    {
+      util::throw_error(std::logic_error("This object was compiled "
+                                         "for different metadata!"));
+        // FIXME: Add details on what is actually wrong
+    }
+}
+
+const cv::gimpl::GModel::Graph& cv::GCompiled::Priv::model() const
+{
+    GAPI_Assert(nullptr != m_exec);
+    return m_exec->model();
+}
+
+// GCompiled public implementation /////////////////////////////////////////////
+cv::GCompiled::GCompiled()
+    : m_priv(new Priv())
+{
+}
+
+cv::GCompiled::operator bool() const
+{
+    return !m_priv->isEmpty();
+}
+
+void cv::GCompiled::operator() (GRunArgs &&ins, GRunArgsP &&outs)
+{
+    // FIXME: Check that <outs> matches the protocol
+    m_priv->run(cv::gimpl::GRuntimeArgs{std::move(ins),std::move(outs)});
+}
+
+void cv::GCompiled::operator ()(cv::Mat in, cv::Mat &out)
+{
+    (*this)(cv::gin(in), cv::gout(out));
+}
+
+void cv::GCompiled::operator() (cv::Mat in, cv::Scalar &out)
+{
+    (*this)(cv::gin(in), cv::gout(out));
+}
+
+void cv::GCompiled::operator() (cv::Mat in1, cv::Mat in2, cv::Mat &out)
+{
+    (*this)(cv::gin(in1, in2), cv::gout(out));
+}
+
+void cv::GCompiled::operator() (cv::Mat in1, cv::Mat in2, cv::Scalar &out)
+{
+    (*this)(cv::gin(in1, in2), cv::gout(out));
+}
+
+void cv::GCompiled::operator ()(const std::vector<cv::Mat> &ins,
+                                const std::vector<cv::Mat> &outs)
+{
+    GRunArgs call_ins;
+    GRunArgsP call_outs;
+
+    // Make a temporary copy of vector outs - cv::Mats are copies anyway
+    auto tmp = outs;
+    for (const cv::Mat &m : ins) { call_ins.emplace_back(m);   }
+    for (      cv::Mat &m : tmp) { call_outs.emplace_back(&m); }
+
+    (*this)(std::move(call_ins), std::move(call_outs));
+}
+
+const cv::GMetaArgs& cv::GCompiled::metas() const
+{
+    return m_priv->metas();
+}
+
+const cv::GMetaArgs& cv::GCompiled::outMetas() const
+{
+    return m_priv->outMetas();
+}
+
+
+cv::GCompiled::Priv& cv::GCompiled::priv()
+{
+    return *m_priv;
+}
diff --git a/modules/gapi/src/compiler/gcompiled_priv.hpp b/modules/gapi/src/compiler/gcompiled_priv.hpp
new file mode 100644 (file)
index 0000000..1bb3bbf
--- /dev/null
@@ -0,0 +1,58 @@
+// 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_GCOMPILED_PRIV_HPP
+#define OPENCV_GAPI_GCOMPILED_PRIV_HPP
+
+#include <memory> // unique_ptr
+
+#include "opencv2/gapi/util/optional.hpp"
+#include "compiler/gmodel.hpp"
+#include "executor/gexecutor.hpp"
+
+// NB: BTW, GCompiled is the only "public API" class which
+// private part (implementaion) is hosted in the "compiler/" module.
+//
+// This file is here just to keep ADE hidden from the top-level APIs.
+//
+// As the thing becomes more complex, appropriate API and implementation
+// part will be placed to api/ and compiler/ modules respectively.
+
+namespace cv {
+
+namespace gimpl
+{
+    struct GRuntimeArgs;
+};
+
+// FIXME: GAPI_EXPORTS is here only due to tests and Windows linker issues
+class GAPI_EXPORTS GCompiled::Priv
+{
+    // NB: For now, a GCompiled keeps the original ade::Graph alive.
+    // If we want to go autonomous, we might to do something with this.
+    GMetaArgs  m_metas;    // passed by user
+    GMetaArgs  m_outMetas; // inferred by compiler
+    std::unique_ptr<cv::gimpl::GExecutor> m_exec;
+
+    void checkArgs(const cv::gimpl::GRuntimeArgs &args) const;
+
+public:
+    void setup(const GMetaArgs &metaArgs,
+               const GMetaArgs &outMetas,
+               std::unique_ptr<cv::gimpl::GExecutor> &&pE);
+    bool isEmpty() const;
+
+    void run(cv::gimpl::GRuntimeArgs &&args);
+    const GMetaArgs& metas() const;
+    const GMetaArgs& outMetas() const;
+
+    const cv::gimpl::GModel::Graph& model() const;
+};
+
+}
+
+#endif // OPENCV_GAPI_GCOMPILED_PRIV_HPP
diff --git a/modules/gapi/src/compiler/gcompiler.cpp b/modules/gapi/src/compiler/gcompiler.cpp
new file mode 100644 (file)
index 0000000..e1c0d3a
--- /dev/null
@@ -0,0 +1,273 @@
+// 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
+
+
+#include <vector>
+#include <stack>
+#include <unordered_map>
+
+#include <ade/util/algorithm.hpp>      // any_of
+#include <ade/util/zip_range.hpp>      // zip_range, indexed
+
+#include <ade/graph.hpp>
+#include <ade/passes/check_cycles.hpp>
+
+#include "api/gcomputation_priv.hpp"
+#include "api/gnode_priv.hpp"   // FIXME: why it is here?
+#include "api/gproto_priv.hpp"  // FIXME: why it is here?
+#include "api/gcall_priv.hpp"   // FIXME: why it is here?
+#include "api/gapi_priv.hpp"    // FIXME: why it is here?
+#include "api/gbackend_priv.hpp" // Backend basic API (newInstance, etc)
+
+#include "compiler/gmodel.hpp"
+#include "compiler/gmodelbuilder.hpp"
+#include "compiler/gcompiler.hpp"
+#include "compiler/gcompiled_priv.hpp"
+#include "compiler/passes/passes.hpp"
+
+#include "executor/gexecutor.hpp"
+#include "backends/common/gbackend.hpp"
+
+// <FIXME:>
+#include "opencv2/gapi/cpu/core.hpp"    // Also directly refer to Core
+#include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations
+// </FIXME:>
+
+#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
+
+#include "opencv2/core/cvdef.h"
+#include "logger.hpp"
+
+namespace
+{
+    cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args)
+    {
+        static auto ocv_pkg = combine(cv::gapi::core::cpu::kernels(),
+                                      cv::gapi::imgproc::cpu::kernels(),
+                                      cv::unite_policy::KEEP);
+        auto user_pkg = cv::gimpl::getCompileArg<cv::gapi::GKernelPackage>(args);
+        return combine(ocv_pkg, user_pkg.value_or(cv::gapi::GKernelPackage{}), cv::unite_policy::REPLACE);
+    }
+
+    cv::util::optional<std::string> getGraphDumpDirectory(cv::GCompileArgs& args)
+    {
+        auto dump_info = cv::gimpl::getCompileArg<cv::graph_dump_path>(args);
+        if (!dump_info.has_value())
+        {
+            const char* path = std::getenv("GRAPH_DUMP_PATH");
+            return path
+                ? cv::util::make_optional(std::string(path))
+                : cv::util::optional<std::string>();
+        }
+        else
+        {
+            return cv::util::make_optional(dump_info.value().m_dump_path);
+        }
+    }
+} // anonymous namespace
+
+
+// GCompiler implementation ////////////////////////////////////////////////////
+
+cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c,
+                                GMetaArgs              &&metas,
+                                GCompileArgs           &&args)
+    : m_c(c), m_metas(std::move(metas)), m_args(std::move(args))
+{
+    using namespace std::placeholders;
+    m_all_kernels       = getKernelPackage(m_args);
+    auto lookup_order   = getCompileArg<gapi::GLookupOrder>(m_args).value_or(gapi::GLookupOrder());
+    auto dump_path      = getGraphDumpDirectory(m_args);
+
+    m_e.addPassStage("init");
+    m_e.addPass("init", "check_cycles",  ade::passes::CheckCycles());
+    m_e.addPass("init", "expand_kernels",  std::bind(passes::expandKernels, _1,
+                                                     m_all_kernels)); // NB: package is copied
+    m_e.addPass("init", "topo_sort",     ade::passes::TopologicalSort());
+    m_e.addPass("init", "init_islands",  passes::initIslands);
+    m_e.addPass("init", "check_islands", passes::checkIslands);
+    // TODO:
+    // - Check basic graph validity (i.e., all inputs are connected)
+    // - Complex dependencies (i.e. parent-child) unrolling
+    // - etc, etc, etc
+
+    // Remove GCompoundBackend to avoid calling setupBackend() with it in the list
+    m_all_kernels.remove(cv::gapi::compound::backend());
+    m_e.addPass("init", "resolve_kernels", std::bind(passes::resolveKernels, _1,
+                                                     std::ref(m_all_kernels), // NB: and not copied here
+                                                     lookup_order));
+
+    m_e.addPass("init", "check_islands_content", passes::checkIslandsContent);
+    m_e.addPassStage("meta");
+    m_e.addPass("meta", "initialize",   std::bind(passes::initMeta, _1, std::ref(m_metas)));
+    m_e.addPass("meta", "propagate",    passes::inferMeta);
+    m_e.addPass("meta", "finalize",     passes::storeResultingMeta);
+    // moved to another stage, FIXME: two dumps?
+    //    m_e.addPass("meta", "dump_dot",     passes::dumpDotStdout);
+
+    // Special stage for backend-specific transformations
+    // FIXME: document passes hierarchy and order for backend developers
+    m_e.addPassStage("transform");
+
+    m_e.addPassStage("exec");
+    m_e.addPass("exec", "fuse_islands",     passes::fuseIslands);
+    m_e.addPass("exec", "sync_islands",     passes::syncIslandTags);
+
+    if (dump_path.has_value())
+    {
+        m_e.addPass("exec", "dump_dot", std::bind(passes::dumpGraph, _1,
+                                                  dump_path.value()));
+    }
+
+    // Process backends at the last moment (after all G-API passes are added).
+    ade::ExecutionEngineSetupContext ectx(m_e);
+    auto backends = m_all_kernels.backends();
+    for (auto &b : backends)
+    {
+        b.priv().addBackendPasses(ectx);
+    }
+}
+
+void cv::gimpl::GCompiler::validateInputMeta()
+{
+    if (m_metas.size() != m_c.priv().m_ins.size())
+    {
+        util::throw_error(std::logic_error
+                    ("COMPILE: GComputation interface / metadata mismatch! "
+                     "(expected " + std::to_string(m_c.priv().m_ins.size()) + ", "
+                     "got " + std::to_string(m_metas.size()) + " meta arguments)"));
+    }
+
+    const auto meta_matches = [](const GMetaArg &meta, const GProtoArg &proto) {
+        switch (proto.index())
+        {
+        // FIXME: Auto-generate methods like this from traits:
+        case GProtoArg::index_of<cv::GMat>():
+            return util::holds_alternative<cv::GMatDesc>(meta);
+
+        case GProtoArg::index_of<cv::GScalar>():
+            return util::holds_alternative<cv::GScalarDesc>(meta);
+
+        case GProtoArg::index_of<cv::detail::GArrayU>():
+            return util::holds_alternative<cv::GArrayDesc>(meta);
+
+        default:
+            GAPI_Assert(false);
+        }
+        return false; // should never happen
+    };
+
+    for (const auto &meta_arg_idx : ade::util::indexed(ade::util::zip(m_metas, m_c.priv().m_ins)))
+    {
+        const auto &meta  = std::get<0>(ade::util::value(meta_arg_idx));
+        const auto &proto = std::get<1>(ade::util::value(meta_arg_idx));
+
+        if (!meta_matches(meta, proto))
+        {
+            const auto index  = ade::util::index(meta_arg_idx);
+            util::throw_error(std::logic_error
+                        ("GComputation object type / metadata descriptor mismatch "
+                         "(argument " + std::to_string(index) + ")"));
+            // FIXME: report what we've got and what we've expected
+        }
+    }
+    // All checks are ok
+}
+
+void cv::gimpl::GCompiler::validateOutProtoArgs()
+{
+    for (const auto &out_pos : ade::util::indexed(m_c.priv().m_outs))
+    {
+        const auto &node = proto::origin_of(ade::util::value(out_pos)).node;
+        if (node.shape() != cv::GNode::NodeShape::CALL)
+        {
+            auto pos = ade::util::index(out_pos);
+            util::throw_error(std::logic_error
+                        ("Computation output " + std::to_string(pos) +
+                         " is not a result of any operation"));
+        }
+    }
+}
+
+cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::generateGraph()
+{
+    validateInputMeta();
+    validateOutProtoArgs();
+
+    // Generate ADE graph from expression-based computation
+    std::unique_ptr<ade::Graph> pG(new ade::Graph);
+    ade::Graph& g = *pG;
+
+    GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    cv::gimpl::GModelBuilder builder(g);
+    auto proto_slots = builder.put(m_c.priv().m_ins, m_c.priv().m_outs);
+    GAPI_LOG_INFO(NULL, "Generated graph: " << g.nodes().size() << " nodes" << std::endl);
+
+    // Store Computation's protocol in metadata
+    Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+    gm.metadata().set(p);
+
+    return pG;
+}
+
+void cv::gimpl::GCompiler::runPasses(ade::Graph &g)
+{
+    m_e.runPasses(g);
+    GAPI_LOG_INFO(NULL, "All compiler passes are successful");
+}
+
+void cv::gimpl::GCompiler::compileIslands(ade::Graph &g)
+{
+    GModel::Graph gm(g);
+    std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model);
+    GIslandModel::Graph gim(*gptr);
+
+    // Run topological sort on GIslandModel first
+    auto pass_ctx = ade::passes::PassContext{*gptr};
+    ade::passes::TopologicalSort{}(pass_ctx);
+
+    // Now compile islands
+    GIslandModel::compileIslands(gim, g, m_args);
+}
+
+cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg)
+{
+    // This is the final compilation step. Here:
+    // - An instance of GExecutor is created. Depening on the platform,
+    //   build configuration, etc, a GExecutor may be:
+    //   - a naive single-thread graph interpreter;
+    //   - a std::thread-based thing
+    //   - a TBB-based thing, etc.
+    // - All this stuff is wrapped into a GCompiled object and returned
+    //   to user.
+
+    // Note: this happens in the last pass ("compile_islands"):
+    // - Each GIsland of GIslandModel instantiates its own,
+    //   backend-specific executable object
+    //   - Every backend gets a subgraph to execute, and builds
+    //     an execution plan for it (backend-specific execution)
+    // ...before call to produceCompiled();
+
+    const auto &outMetas = GModel::ConstGraph(*pg).metadata()
+        .get<OutputMeta>().outMeta;
+    std::unique_ptr<GExecutor> pE(new GExecutor(std::move(pg)));
+    // FIXME: select which executor will be actually used,
+    // make GExecutor abstract.
+
+    GCompiled compiled;
+    compiled.priv().setup(m_metas, outMetas, std::move(pE));
+    return compiled;
+}
+
+cv::GCompiled cv::gimpl::GCompiler::compile()
+{
+    std::unique_ptr<ade::Graph> pG = generateGraph();
+    runPasses(*pG);
+    compileIslands(*pG);
+    return produceCompiled(std::move(pG));
+}
diff --git a/modules/gapi/src/compiler/gcompiler.hpp b/modules/gapi/src/compiler/gcompiler.hpp
new file mode 100644 (file)
index 0000000..b369c14
--- /dev/null
@@ -0,0 +1,51 @@
+// 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_GCOMPILER_HPP
+#define OPENCV_GAPI_GCOMPILER_HPP
+
+
+#include "opencv2/gapi/gcommon.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/gcomputation.hpp"
+
+#include <ade/execution_engine/execution_engine.hpp>
+
+namespace cv { namespace gimpl {
+
+// FIXME: exported for internal tests only!
+class GAPI_EXPORTS GCompiler
+{
+    const GComputation&      m_c;
+    const GMetaArgs          m_metas;
+    GCompileArgs             m_args;
+    ade::ExecutionEngine     m_e;
+
+    cv::gapi::GKernelPackage m_all_kernels;
+
+    void validateInputMeta();
+    void validateOutProtoArgs();
+
+public:
+    explicit GCompiler(const GComputation &c,
+                             GMetaArgs    &&metas,
+                             GCompileArgs &&args);
+
+    // The method which does everything...
+    GCompiled compile();
+
+    // But is actually composed of this:
+    using GPtr = std::unique_ptr<ade::Graph>;
+    GPtr       generateGraph();               // Unroll GComputation into a GModel
+    void       runPasses(ade::Graph &g);      // Apply all G-API passes on a GModel
+    void       compileIslands(ade::Graph &g); // Instantiate GIslandExecutables in GIslandModel
+    GCompiled  produceCompiled(GPtr &&pg);    // Produce GCompiled from processed GModel
+};
+
+}}
+
+#endif // OPENCV_GAPI_GCOMPILER_HPP
diff --git a/modules/gapi/src/compiler/gislandmodel.cpp b/modules/gapi/src/compiler/gislandmodel.cpp
new file mode 100644 (file)
index 0000000..f517099
--- /dev/null
@@ -0,0 +1,287 @@
+// 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
+
+
+#include <sstream>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <ade/util/checked_cast.hpp>
+
+#include "api/gbackend_priv.hpp" // GBackend::Priv().compile()
+#include "compiler/gmodel.hpp"
+#include "compiler/gislandmodel.hpp"
+#include "logger.hpp"    // GAPI_LOG
+
+namespace cv { namespace gimpl {
+
+GIsland::GIsland(const gapi::GBackend &bknd,
+                 ade::NodeHandle op,
+                 util::optional<std::string> &&user_tag)
+    : m_backend(bknd)
+    , m_user_tag(std::move(user_tag))
+{
+    m_all.insert(op);
+    m_in_ops.insert(op);
+    m_out_ops.insert(op);
+}
+
+// _ because of gcc4.8 wanings on ARM
+GIsland::GIsland(const gapi::GBackend &_bknd,
+                 node_set &&_all,
+                 node_set &&_in_ops,
+                 node_set &&_out_ops,
+                 util::optional<std::string> &&_user_tag)
+    : m_backend(_bknd)
+    , m_all(std::move(_all))
+    , m_in_ops(std::move(_in_ops))
+    , m_out_ops(std::move(_out_ops))
+    , m_user_tag(std::move(_user_tag))
+{
+}
+
+const GIsland::node_set& GIsland::contents() const
+{
+    return m_all;
+}
+
+const GIsland::node_set& GIsland::in_ops() const
+{
+    return m_in_ops;
+}
+
+const GIsland::node_set& GIsland::out_ops() const
+{
+    return m_out_ops;
+}
+
+gapi::GBackend GIsland::backend() const
+{
+    return m_backend;
+}
+
+bool GIsland::is_user_specified() const
+{
+    return m_user_tag.has_value();
+}
+
+void GIsland::debug() const
+{
+    std::stringstream stream;
+    stream << name() << " {{\n  input ops: ";
+    for (const auto& nh : m_in_ops) stream << nh << "; ";
+    stream << "\n  output ops: ";
+    for (const auto& nh : m_out_ops) stream << nh << "; ";
+    stream << "\n  contents: ";
+    for (const auto& nh : m_all) stream << nh << "; ";
+    stream << "\n}}" << std::endl;
+    GAPI_LOG_INFO(NULL, stream.str());
+}
+
+GIsland::node_set GIsland::consumers(const ade::Graph &g,
+                                     const ade::NodeHandle &slot_nh) const
+{
+    GIslandModel::ConstGraph gim(g);
+    auto data_nh = gim.metadata(slot_nh).get<DataSlot>().original_data_node;
+    GIsland::node_set result;
+    for (const auto& in_op : m_in_ops)
+    {
+        auto it = std::find(in_op->inNodes().begin(),
+                            in_op->inNodes().end(),
+                            data_nh);
+        if (it != in_op->inNodes().end())
+            result.insert(in_op);
+    }
+    return result;
+}
+
+ade::NodeHandle GIsland::producer(const ade::Graph &g,
+                                  const ade::NodeHandle &slot_nh) const
+{
+    GIslandModel::ConstGraph gim(g);
+    auto data_nh = gim.metadata(slot_nh).get<DataSlot>().original_data_node;
+    for (const auto& out_op : m_out_ops)
+    {
+        auto it = std::find(out_op->outNodes().begin(),
+                            out_op->outNodes().end(),
+                            data_nh);
+        if (it != out_op->outNodes().end())
+            return out_op;
+    }
+    // Consistency: A GIsland requested for producer() of slot_nh should
+    // always had the appropriate GModel node handle in its m_out_ops vector.
+    GAPI_Assert(false);
+    return ade::NodeHandle();
+}
+
+std::string GIsland::name() const
+{
+    if (is_user_specified())
+        return m_user_tag.value();
+
+    std::stringstream ss;
+    ss << "island_#" << std::hex << static_cast<const void*>(this);
+    return ss.str();
+}
+
+void GIslandModel::generateInitial(GIslandModel::Graph &g,
+                                   const ade::Graph &src_graph)
+{
+    const GModel::ConstGraph src_g(src_graph);
+
+    // Initially GIslandModel is a 1:1 projection from GModel:
+    // 1) Every GModel::OP becomes a separate GIslandModel::FusedIsland;
+    // 2) Every GModel::DATA becomes GIslandModel::DataSlot;
+    // 3) Single-operation FusedIslands are connected with DataSlots in the
+    //    same way as OPs and DATA (edges with the same metadata)
+
+    using node_set = std::unordered_set
+        < ade::NodeHandle
+        , ade::HandleHasher<ade::Node>
+        >;
+    using node_map = std::unordered_map
+        < ade::NodeHandle
+        , ade::NodeHandle
+        , ade::HandleHasher<ade::Node>
+        >;
+
+    node_set all_operations;
+    node_map data_to_slot;
+
+    // First, list all operations and build create DataSlots in <g>
+    for (auto src_nh : src_g.nodes())
+    {
+        switch (src_g.metadata(src_nh).get<NodeType>().t)
+        {
+        case NodeType::OP:   all_operations.insert(src_nh);                break;
+        case NodeType::DATA: data_to_slot[src_nh] = mkSlotNode(g, src_nh); break;
+        default: GAPI_Assert(false); break;
+        }
+    } // for (src_g.nodes)
+
+    // Now put single-op islands and connect it with DataSlots
+    for (auto src_op_nh : all_operations)
+    {
+        auto nh = mkIslandNode(g, src_g.metadata(src_op_nh).get<Op>().backend, src_op_nh, src_graph);
+        for (auto in_edge : src_op_nh->inEdges())
+        {
+            auto src_data_nh = in_edge->srcNode();
+            auto isl_slot_nh = data_to_slot.at(src_data_nh);
+            g.link(isl_slot_nh, nh); // no other data stored yet
+        }
+        for (auto out_edge : src_op_nh->outEdges())
+        {
+            auto dst_data_nh = out_edge->dstNode();
+            auto isl_slot_nh = data_to_slot.at(dst_data_nh);
+            g.link(nh, isl_slot_nh);
+        }
+    } // for(all_operations)
+}
+
+ade::NodeHandle GIslandModel::mkSlotNode(Graph &g, const ade::NodeHandle &data_nh)
+{
+    auto nh = g.createNode();
+    g.metadata(nh).set(DataSlot{data_nh});
+    g.metadata(nh).set(NodeKind{NodeKind::SLOT});
+    return nh;
+}
+
+ade::NodeHandle GIslandModel::mkIslandNode(Graph &g, const gapi::GBackend& bknd, const ade::NodeHandle &op_nh, const ade::Graph &orig_g)
+{
+    const GModel::ConstGraph src_g(orig_g);
+    util::optional<std::string> user_tag;
+    if (src_g.metadata(op_nh).contains<Island>())
+    {
+        user_tag = util::make_optional(src_g.metadata(op_nh).get<Island>().island);
+    }
+
+    auto nh = g.createNode();
+    std::shared_ptr<GIsland> island(new GIsland(bknd, op_nh, std::move(user_tag)));
+    g.metadata(nh).set(FusedIsland{std::move(island)});
+    g.metadata(nh).set(NodeKind{NodeKind::ISLAND});
+    return nh;
+}
+
+ade::NodeHandle GIslandModel::mkIslandNode(Graph &g, std::shared_ptr<GIsland>&& isl)
+{
+    ade::NodeHandle nh = g.createNode();
+    g.metadata(nh).set(cv::gimpl::NodeKind{cv::gimpl::NodeKind::ISLAND});
+    g.metadata(nh).set<cv::gimpl::FusedIsland>({std::move(isl)});
+    return nh;
+}
+
+void GIslandModel::syncIslandTags(Graph &g, ade::Graph &orig_g)
+{
+    GModel::Graph gm(orig_g);
+    for (auto nh : g.nodes())
+    {
+        if (NodeKind::ISLAND == g.metadata(nh).get<NodeKind>().k)
+        {
+            auto island = g.metadata(nh).get<FusedIsland>().object;
+            auto isl_tag = island->name();
+            for (const auto& orig_nh_inside : island->contents())
+            {
+                gm.metadata(orig_nh_inside).set(Island{isl_tag});
+            }
+        }
+    }
+}
+
+void GIslandModel::compileIslands(Graph &g, const ade::Graph &orig_g, const GCompileArgs &args)
+{
+    GModel::ConstGraph gm(orig_g);
+
+    auto original_sorted = gm.metadata().get<ade::passes::TopologicalSortData>();
+    for (auto nh : g.nodes())
+    {
+        if (NodeKind::ISLAND == g.metadata(nh).get<NodeKind>().k)
+        {
+            auto island_obj = g.metadata(nh).get<FusedIsland>().object;
+            auto island_ops = island_obj->contents();
+
+            std::vector<ade::NodeHandle> topo_sorted_list;
+            ade::util::copy_if(original_sorted.nodes(),
+                               std::back_inserter(topo_sorted_list),
+                               [&](ade::NodeHandle sorted_nh) {
+                                   return ade::util::contains(island_ops, sorted_nh);
+                               });
+
+            auto island_exe = island_obj->backend().priv()
+                .compile(orig_g, args, topo_sorted_list);
+            GAPI_Assert(nullptr != island_exe);
+            g.metadata(nh).set(IslandExec{std::move(island_exe)});
+        }
+    }
+}
+
+ade::NodeHandle GIslandModel::producerOf(const ConstGraph &g, ade::NodeHandle &data_nh)
+{
+    for (auto nh : g.nodes())
+    {
+        // find a data slot...
+        if (NodeKind::SLOT == g.metadata(nh).get<NodeKind>().k)
+        {
+            // which is associated with the given data object...
+            if (data_nh == g.metadata(nh).get<DataSlot>().original_data_node)
+            {
+                // which probably has a produrer...
+                if (0u != nh->inNodes().size())
+                {
+                    // ...then the answer is that producer
+                    return nh->inNodes().front();
+                }
+                else return ade::NodeHandle(); // input data object?
+                                               // return empty to break the cycle
+            }
+        }
+    }
+    // No appropriate data slot found - probably, the object has been
+    // optimized out during fusion
+    return ade::NodeHandle();
+}
+
+} // namespace cv
+} // namespace gimpl
diff --git a/modules/gapi/src/compiler/gislandmodel.hpp b/modules/gapi/src/compiler/gislandmodel.hpp
new file mode 100644 (file)
index 0000000..a0e44c9
--- /dev/null
@@ -0,0 +1,184 @@
+// 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_GISLANDMODEL_HPP
+#define OPENCV_GAPI_GISLANDMODEL_HPP
+
+#include <unordered_set>
+#include <memory>        // shared_ptr
+
+#include <ade/graph.hpp>
+#include <ade/typed_graph.hpp>
+#include <ade/passes/topological_sort.hpp>
+
+#include "opencv2/gapi/util/optional.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+
+#include "compiler/gobjref.hpp"
+
+namespace cv { namespace gimpl {
+
+
+// FIXME: GAPI_EXPORTS only because of tests!
+class GAPI_EXPORTS GIsland
+{
+public:
+    using node_set = std::unordered_set
+         < ade::NodeHandle
+         , ade::HandleHasher<ade::Node>
+         >;
+
+    // Initial constructor (constructs a single-op Island)
+    GIsland(const gapi::GBackend &bknd,
+            ade::NodeHandle op,
+            util::optional<std::string>&& user_tag);
+
+    // Merged constructor
+    GIsland(const gapi::GBackend &bknd,
+            node_set &&all,
+            node_set &&in_ops,
+            node_set &&out_ops,
+            util::optional<std::string>&& user_tag);
+
+    const node_set& contents() const;
+    const node_set& in_ops() const;
+    const node_set& out_ops() const;
+
+    std::string name() const;
+    gapi::GBackend backend() const;
+
+    /**
+     * Returns all GModel operation node handles which are _reading_
+     * from a GModel data object associated (wrapped in) the given
+     * Slot object.
+     *
+     * @param g an ade::Graph with GIslandModel information inside
+     * @param slot_nh Slot object node handle of interest
+     * @return a set of GModel operation node handles
+     */
+    node_set consumers(const ade::Graph &g,
+                       const ade::NodeHandle &slot_nh) const;
+
+    /**
+     * Returns a GModel operation node handle which is _writing_
+     * to a GModel data object associated (wrapped in) the given
+     * Slot object.
+     *
+     * @param g an ade::Graph with GIslandModel information inside
+     * @param slot_nh Slot object node handle of interest
+     * @return a node handle of original GModel
+     */
+    ade::NodeHandle producer(const ade::Graph &g,
+                             const ade::NodeHandle &slot_nh) const;
+
+    void debug() const;
+    bool is_user_specified() const;
+
+protected:
+    gapi::GBackend m_backend; // backend which handles this Island execution
+
+    node_set m_all;     // everything (data + operations) within an island
+    node_set m_in_ops;  // operations island begins with
+    node_set m_out_ops; // operations island ends with
+
+    // has island name IF specified by user. Empty for internal (inferred) islands
+    util::optional<std::string> m_user_tag;
+};
+
+
+
+// GIslandExecutable - a backend-specific thing which executes
+// contents of an Island
+// * Is instantiated by the last step of the Islands fusion procedure;
+// * Is orchestrated by a GExecutor instance.
+//
+class GIslandExecutable
+{
+public:
+    using InObj  = std::pair<RcDesc, cv::GRunArg>;
+    using OutObj = std::pair<RcDesc, cv::GRunArgP>;
+
+    // FIXME: now run() requires full input vector to be available.
+    // actually, parts of subgraph may execute even if there's no all data
+    // slots in place.
+    // TODO: Add partial execution capabilities
+    virtual void run(std::vector<InObj>  &&input_objs,
+                     std::vector<OutObj> &&output_objs) = 0;
+
+    virtual ~GIslandExecutable() = default;
+};
+
+
+
+// Couldn't reuse NodeType here - FIXME unify (move meta to a shared place)
+struct NodeKind
+{
+    static const char *name() { return "NodeKind"; }
+    enum { ISLAND, SLOT} k;
+};
+
+// FIXME: Rename to Island (as soon as current GModel::Island is renamed
+// to IslandTag).
+struct FusedIsland
+{
+    static const char *name() { return "FusedIsland"; }
+    std::shared_ptr<GIsland> object;
+};
+
+struct DataSlot
+{
+    static const char *name() { return "DataSlot"; }
+    ade::NodeHandle original_data_node; // direct link to GModel
+};
+
+struct IslandExec
+{
+    static const char *name() { return "IslandExecutable"; }
+    std::shared_ptr<GIslandExecutable> object;
+};
+
+namespace GIslandModel
+{
+    using Graph = ade::TypedGraph
+        < NodeKind
+        , FusedIsland
+        , DataSlot
+        , IslandExec
+        , ade::passes::TopologicalSortData
+        >;
+
+    // FIXME: derive from TypedGraph
+    using ConstGraph = ade::ConstTypedGraph
+        < NodeKind
+        , FusedIsland
+        , DataSlot
+        , IslandExec
+        , ade::passes::TopologicalSortData
+        >;
+
+    // Top-level function
+    void generateInitial(Graph &g, const ade::Graph &src_g);
+    // "Building blocks"
+    ade::NodeHandle mkSlotNode(Graph &g, const ade::NodeHandle &data_nh);
+    ade::NodeHandle mkIslandNode(Graph &g, const gapi::GBackend &bknd, const ade::NodeHandle &op_nh, const ade::Graph &orig_g);
+    ade::NodeHandle mkIslandNode(Graph &g, std::shared_ptr<GIsland>&& isl);
+
+    // GIslandModel API
+    void syncIslandTags(Graph &g, ade::Graph &orig_g);
+    void compileIslands(Graph &g, const ade::Graph &orig_g, const GCompileArgs &args);
+
+    // Debug routines
+    // producerOf - returns an Island handle which produces given data object
+    //     from the original model (! don't mix with DataSlot)
+    // FIXME: GAPI_EXPORTS because of tests only!
+    ade::NodeHandle GAPI_EXPORTS producerOf(const ConstGraph &g, ade::NodeHandle &data_nh);
+
+} // namespace GIslandModel
+
+}} // namespace cv::gimpl
+
+#endif // OPENCV_GAPI_GISLANDMODEL_HPP
diff --git a/modules/gapi/src/compiler/gmodel.cpp b/modules/gapi/src/compiler/gmodel.cpp
new file mode 100644 (file)
index 0000000..cda1cf1
--- /dev/null
@@ -0,0 +1,245 @@
+// 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
+
+
+#include <string>
+#include <sstream> // used in GModel::log
+
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+#include <ade/util/checked_cast.hpp>
+
+#include "opencv2/gapi/gproto.hpp"
+#include "api/gnode_priv.hpp"
+#include "compiler/gobjref.hpp"
+#include "compiler/gmodel.hpp"
+
+namespace cv { namespace gimpl {
+
+ade::NodeHandle GModel::mkOpNode(GModel::Graph &g, const GKernel &k, const std::vector<GArg> &args, const std::string &island)
+{
+    ade::NodeHandle op_h = g.createNode();
+    g.metadata(op_h).set(NodeType{NodeType::OP});
+    //These extra empty {} are to please GCC (-Wmissing-field-initializers)
+    g.metadata(op_h).set(Op{k, args, {}, {}, {}});
+    if (!island.empty())
+        g.metadata(op_h).set(Island{island});
+    return op_h;
+}
+
+ade::NodeHandle GModel::mkDataNode(GModel::Graph &g, const GOrigin& origin)
+{
+    ade::NodeHandle op_h = g.createNode();
+    const auto id = g.metadata().get<DataObjectCounter>().GetNewId(origin.shape);
+    g.metadata(op_h).set(NodeType{NodeType::DATA});
+
+    GMetaArg meta;
+    Data::Storage storage = Data::Storage::INTERNAL; // By default, all objects are marked INTERNAL
+
+    if (origin.node.shape() == GNode::NodeShape::CONST_BOUNDED)
+    {
+        auto value = value_of(origin);
+        meta       = descr_of(value);
+        storage    = Data::Storage::CONST;
+        g.metadata(op_h).set(ConstValue{value});
+    }
+    g.metadata(op_h).set(Data{origin.shape, id, meta, origin.ctor, storage});
+    return op_h;
+}
+
+void GModel::linkIn(Graph &g, ade::NodeHandle opH, ade::NodeHandle objH, std::size_t in_port)
+{
+    // Check if input is already connected
+    for (const auto& in_e : opH->inEdges())
+    {
+        GAPI_Assert(g.metadata(in_e).get<Input>().port != in_port);
+    }
+
+    auto &op = g.metadata(opH).get<Op>();
+    auto &gm = g.metadata(objH).get<Data>();
+
+     // FIXME: check validity using kernel prototype
+    GAPI_Assert(in_port < op.args.size());
+
+    ade::EdgeHandle eh = g.link(objH, opH);
+    g.metadata(eh).set(Input{in_port});
+
+    // Replace an API object with a REF (G* -> GOBJREF)
+    op.args[in_port] = cv::GArg(RcDesc{gm.rc, gm.shape, {}});
+}
+
+void GModel::linkOut(Graph &g, ade::NodeHandle opH, ade::NodeHandle objH, std::size_t out_port)
+{
+    // FIXME: check validity using kernel prototype
+
+    // Check if output is already connected
+    for (const auto& out_e : opH->outEdges())
+    {
+        GAPI_Assert(g.metadata(out_e).get<Output>().port != out_port);
+    }
+
+    auto &op = g.metadata(opH).get<Op>();
+    auto &gm = g.metadata(objH).get<Data>();
+
+    GAPI_Assert(objH->inNodes().size() == 0u);
+
+    ade::EdgeHandle eh = g.link(opH, objH);
+    g.metadata(eh).set(Output{out_port});
+
+    // TODO: outs must be allocated according to kernel protocol!
+    const auto storage_with_port = ade::util::checked_cast<std::size_t>(out_port+1);
+    const auto min_out_size = std::max(op.outs.size(), storage_with_port);
+    op.outs.resize(min_out_size, RcDesc{-1,GShape::GMAT,{}}); // FIXME: Invalid shape instead?
+    op.outs[out_port] = RcDesc{gm.rc, gm.shape, {}};
+}
+
+std::vector<ade::NodeHandle> GModel::orderedInputs(Graph &g, ade::NodeHandle nh)
+{
+    std::vector<ade::NodeHandle> sorted_in_nhs(nh->inEdges().size());
+    for (const auto& in_eh : nh->inEdges())
+    {
+        const auto port = g.metadata(in_eh).get<cv::gimpl::Input>().port;
+        GAPI_Assert(port < sorted_in_nhs.size());
+        sorted_in_nhs[port] = in_eh->srcNode();
+    }
+    return sorted_in_nhs;
+}
+
+std::vector<ade::NodeHandle> GModel::orderedOutputs(Graph &g, ade::NodeHandle nh)
+{
+    std::vector<ade::NodeHandle> sorted_out_nhs(nh->outEdges().size());
+    for (const auto& out_eh : nh->outEdges())
+    {
+        const auto port = g.metadata(out_eh).get<cv::gimpl::Output>().port;
+        GAPI_Assert(port < sorted_out_nhs.size());
+        sorted_out_nhs[port] = out_eh->dstNode();
+    }
+    return sorted_out_nhs;
+}
+
+void GModel::init(Graph& g)
+{
+    g.metadata().set(DataObjectCounter());
+}
+
+void GModel::log(Graph &g, ade::NodeHandle nh, std::string &&msg, ade::NodeHandle updater)
+{
+    std::string s = std::move(msg);
+    if (updater != nullptr)
+    {
+        std::stringstream fmt;
+        fmt << " (via " << updater << ")";
+        s += fmt.str();
+    }
+
+    if (g.metadata(nh).contains<Journal>())
+    {
+        g.metadata(nh).get<Journal>().messages.push_back(s);
+    }
+    else
+    {
+        g.metadata(nh).set(Journal{{s}});
+    }
+}
+
+// FIXME:
+// Unify with GModel::log(.. ade::NodeHandle ..)
+void GModel::log(Graph &g, ade::EdgeHandle eh, std::string &&msg, ade::NodeHandle updater)
+{
+    std::string s = std::move(msg);
+    if (updater != nullptr)
+    {
+        std::stringstream fmt;
+        fmt << " (via " << updater << ")";
+        s += fmt.str();
+    }
+
+    if (g.metadata(eh).contains<Journal>())
+    {
+        g.metadata(eh).get<Journal>().messages.push_back(s);
+    }
+    else
+    {
+        g.metadata(eh).set(Journal{{s}});
+    }
+}
+
+ade::NodeHandle GModel::detail::dataNodeOf(const ConstGraph &g, const GOrigin &origin)
+{
+    // FIXME: Does it still work with graph transformations, e.g. redirectWriter()??
+    return g.metadata().get<Layout>().object_nodes.at(origin);
+}
+
+void GModel::redirectReaders(Graph &g, ade::NodeHandle from, ade::NodeHandle to)
+{
+    std::vector<ade::EdgeHandle> ehh(from->outEdges().begin(), from->outEdges().end());
+    for (auto e : ehh)
+    {
+        auto dst = e->dstNode();
+        auto input = g.metadata(e).get<Input>();
+        g.erase(e);
+        linkIn(g, dst, to, input.port);
+    }
+}
+
+void GModel::redirectWriter(Graph &g, ade::NodeHandle from, ade::NodeHandle to)
+{
+    GAPI_Assert(from->inEdges().size() == 1);
+    auto e = from->inEdges().front();
+    auto op = e->srcNode();
+    auto output = g.metadata(e).get<Output>();
+    g.erase(e);
+    linkOut(g, op, to, output.port);
+}
+
+GMetaArgs GModel::collectInputMeta(GModel::ConstGraph cg, ade::NodeHandle node)
+{
+    GAPI_Assert(cg.metadata(node).get<NodeType>().t == NodeType::OP);
+    GMetaArgs in_meta_args(cg.metadata(node).get<Op>().args.size());
+
+    for (const auto &e : node->inEdges())
+    {
+        const auto& in_data = cg.metadata(e->srcNode()).get<Data>();
+        in_meta_args[cg.metadata(e).get<Input>().port] = in_data.meta;
+    }
+
+    return in_meta_args;
+}
+
+
+ade::EdgeHandle GModel::getInEdgeByPort(const GModel::ConstGraph& cg,
+                                        const ade::NodeHandle&    nh,
+                                              std::size_t         in_port)
+{
+    auto inEdges = nh->inEdges();
+    const auto& edge = ade::util::find_if(inEdges, [&](ade::EdgeHandle eh) {
+        return cg.metadata(eh).get<Input>().port == in_port;
+    });
+    GAPI_Assert(edge != inEdges.end());
+    return *edge;
+}
+
+GMetaArgs GModel::collectOutputMeta(GModel::ConstGraph cg, ade::NodeHandle node)
+{
+    GAPI_Assert(cg.metadata(node).get<NodeType>().t == NodeType::OP);
+    GMetaArgs out_meta_args(cg.metadata(node).get<Op>().outs.size());
+
+    for (const auto &e : node->outEdges())
+    {
+        const auto& out_data = cg.metadata(e->dstNode()).get<Data>();
+        out_meta_args[cg.metadata(e).get<Output>().port] = out_data.meta;
+    }
+
+    return out_meta_args;
+}
+
+bool GModel::isActive(const GModel::Graph &cg, const cv::gapi::GBackend &backend)
+{
+    return ade::util::contains(cg.metadata().get<ActiveBackends>().backends,
+                               backend);
+}
+
+}} // cv::gimpl
diff --git a/modules/gapi/src/compiler/gmodel.hpp b/modules/gapi/src/compiler/gmodel.hpp
new file mode 100644 (file)
index 0000000..003519b
--- /dev/null
@@ -0,0 +1,251 @@
+// 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_GMODEL_HPP
+#define OPENCV_GAPI_GMODEL_HPP
+
+#include <memory>           // shared_ptr
+#include <unordered_map>
+#include <functional>       // std::function
+
+#include <ade/graph.hpp>
+#include <ade/typed_graph.hpp>
+#include <ade/passes/topological_sort.hpp>
+
+// /!\ ATTENTION:
+//
+// No API includes like GMat, GNode, GCall here!
+// This part of the system is API-unaware by its design.
+//
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "api/gapi_priv.hpp"   // GShape
+#include "api/gproto_priv.hpp" // origin_of
+#include "backends/common/gbackend.hpp"
+
+#include "compiler/gobjref.hpp"
+#include "compiler/gislandmodel.hpp"
+
+namespace cv { namespace gimpl {
+
+// TODO: Document all metadata types
+
+struct NodeType
+{
+    static const char *name() { return "NodeType"; }
+    enum { OP, DATA } t;
+};
+
+struct Input
+{
+    static const char *name() { return "Input"; }
+    std::size_t port;
+};
+
+struct Output
+{
+    static const char *name() { return "Output"; }
+    std::size_t port;
+};
+
+struct Op
+{
+    static const char *name() { return "Op"; }
+    cv::GKernel         k;
+    std::vector<GArg>   args; // TODO: Introduce a new type for internal args?
+    std::vector<RcDesc> outs; // TODO: Introduce a new type for resource references
+
+    cv::gapi::GBackend  backend;
+    util::any           opaque;
+};
+
+struct Data
+{
+    static const char *name() { return "Data"; }
+
+    // FIXME: This is a _pure_ duplication of RcDesc now! (except storage)
+    GShape   shape; // FIXME: Probably to be replaced by GMetaArg?
+    int      rc;
+    GMetaArg meta;
+    HostCtor ctor;  // T-specific helper to deal with unknown types in our code
+    // FIXME: Why rc+shape+meta is not represented as RcDesc here?
+
+    enum class Storage
+    {
+        INTERNAL,   // data object is not listed in GComputation protocol
+        INPUT,      // data object is listed in GComputation protocol as Input
+        OUTPUT,     // data object is listed in GComputation protocol as Output
+        CONST,      // data object is constant
+    };
+    Storage storage;
+};
+
+struct ConstValue
+{
+    static const char *name() { return "ConstValue"; }
+    GRunArg arg;
+};
+
+// This metadata is valid for both DATA and OP kinds of nodes
+// FIXME: Rename to IslandTag
+struct Island
+{
+    static const char *name() { return "Island"; }
+    std::string island; // can be set by user, otherwise is set by fusion
+};
+
+struct Protocol
+{
+    static const char *name() { return "Protocol"; }
+    // TODO: Replace the whole thing with a "Protocol" object
+    std::vector<RcDesc> inputs;
+    std::vector<RcDesc> outputs;
+
+    std::vector<ade::NodeHandle> in_nhs;
+    std::vector<ade::NodeHandle> out_nhs;
+};
+
+struct OutputMeta
+{
+    static const char *name() { return "OutputMeta"; }
+    GMetaArgs outMeta;
+};
+
+struct Journal
+{
+    static const char *name() { return "Journal"; }
+    std::vector<std::string> messages;
+};
+
+// The mapping between user-side GMat/GScalar/... objects
+// and its  appropriate nodes. Can be stored in graph optionally
+// (NOT used by any compiler or backends, introspection purposes
+// only)
+struct Layout
+{
+    static const char *name() { return "Layout"; }
+    GOriginMap<ade::NodeHandle> object_nodes;
+};
+
+// Unique data object counter (per-type)
+class DataObjectCounter
+{
+public:
+    static const char* name() { return "DataObjectCounter"; }
+    int GetNewId(GShape shape) { return m_next_data_id[shape]++; }
+private:
+    std::unordered_map<cv::GShape, int> m_next_data_id;
+};
+
+// A projected graph of Islands (generated from graph of Operations)
+struct IslandModel
+{
+    static const char* name() { return "IslandModel"; }
+    std::shared_ptr<ade::Graph> model;
+};
+
+// List of backends selected for current graph execution
+struct ActiveBackends
+{
+    static const char *name() { return "ActiveBackends"; }
+    std::unordered_set<cv::gapi::GBackend> backends;
+};
+
+namespace GModel
+{
+    using Graph = ade::TypedGraph
+        < NodeType
+        , Input
+        , Output
+        , Op
+        , Data
+        , ConstValue
+        , Island
+        , Protocol
+        , OutputMeta
+        , Journal
+        , ade::passes::TopologicalSortData
+        , DataObjectCounter
+        , Layout
+        , IslandModel
+        , ActiveBackends
+        >;
+
+    // FIXME: How to define it based on GModel???
+    using ConstGraph = ade::ConstTypedGraph
+        < NodeType
+        , Input
+        , Output
+        , Op
+        , Data
+        , ConstValue
+        , Island
+        , Protocol
+        , OutputMeta
+        , Journal
+        , ade::passes::TopologicalSortData
+        , DataObjectCounter
+        , Layout
+        , IslandModel
+        , ActiveBackends
+        >;
+
+    // User should initialize graph before using it
+    // GAPI_EXPORTS for tests
+    GAPI_EXPORTS void init (Graph& g);
+
+    ade::NodeHandle mkOpNode(Graph &g, const GKernel &k, const std::vector<GArg>& args, const std::string &island);
+
+    // FIXME: change it to take GMeta instead of GShape?
+    ade::NodeHandle mkDataNode(Graph &g, const GOrigin& origin);
+
+    // Adds a string message to a node. Any node can be subject of log, messages then
+    // appear in the dumped .dot file.x
+    void log(Graph &g, ade::NodeHandle op, std::string &&message, ade::NodeHandle updater = ade::NodeHandle());
+    void log(Graph &g, ade::EdgeHandle op, std::string &&message, ade::NodeHandle updater = ade::NodeHandle());
+
+    void linkIn   (Graph &g, ade::NodeHandle op,     ade::NodeHandle obj, std::size_t in_port);
+    void linkOut  (Graph &g, ade::NodeHandle op,     ade::NodeHandle obj, std::size_t out_port);
+
+    // FIXME: Align this GModel API properly, it is a mess now
+    namespace detail
+    {
+        // FIXME: GAPI_EXPORTS only because of tests!!!
+        GAPI_EXPORTS ade::NodeHandle dataNodeOf(const ConstGraph& g, const GOrigin &origin);
+    }
+    template<typename T> inline ade::NodeHandle dataNodeOf(const ConstGraph& g, T &&t)
+    {
+        return detail::dataNodeOf(g, cv::gimpl::proto::origin_of(GProtoArg{t}));
+    }
+
+    void linkIn   (Graph &g, ade::NodeHandle op,     ade::NodeHandle obj, std::size_t in_port);
+    void linkOut  (Graph &g, ade::NodeHandle op,     ade::NodeHandle obj, std::size_t out_port);
+
+    void redirectReaders(Graph &g, ade::NodeHandle from, ade::NodeHandle to);
+    void redirectWriter (Graph &g, ade::NodeHandle from, ade::NodeHandle to);
+
+    std::vector<ade::NodeHandle> orderedInputs (Graph &g, ade::NodeHandle nh);
+    std::vector<ade::NodeHandle> orderedOutputs(Graph &g, ade::NodeHandle nh);
+
+    // Returns input meta array for given op node
+    // Array is sparse, as metadata for non-gapi input objects is empty
+    // TODO:
+    // Cover with tests!!
+    GMetaArgs collectInputMeta(GModel::ConstGraph cg, ade::NodeHandle node);
+    GMetaArgs collectOutputMeta(GModel::ConstGraph cg, ade::NodeHandle node);
+
+    ade::EdgeHandle getInEdgeByPort(const GModel::ConstGraph& cg, const ade::NodeHandle& nh, std::size_t in_port);
+
+    // Returns true if the given backend participates in the execution
+    bool isActive(const GModel::Graph &cg, const cv::gapi::GBackend &backend);
+} // namespace GModel
+
+
+}} // namespace cv::gimpl
+
+#endif // OPENCV_GAPI_GMODEL_HPP
diff --git a/modules/gapi/src/compiler/gmodelbuilder.cpp b/modules/gapi/src/compiler/gmodelbuilder.cpp
new file mode 100644 (file)
index 0000000..90a73bd
--- /dev/null
@@ -0,0 +1,303 @@
+// 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
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//    FIXME: "I personally hate this file"
+//                                        - Dmitry
+//
+////////////////////////////////////////////////////////////////////////////////
+#include <utility>              // tuple
+#include <stack>                // stack
+#include <vector>               // vector
+#include <unordered_set>        // unordered_set
+#include <type_traits>          // is_same
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+
+#include "api/gapi_priv.hpp"    // GOrigin
+#include "api/gproto_priv.hpp"  // descriptor_of and other GProtoArg-related
+#include "api/gcall_priv.hpp"
+#include "api/gnode_priv.hpp"
+
+#include "compiler/gmodelbuilder.hpp"
+
+namespace {
+
+
+// TODO: move to helpers and cover with internal tests?
+template<typename T> struct GVisited
+{
+    typedef std::unordered_set<T> VTs;
+
+    bool visited(const T& t) const { return m_visited.find(t) != m_visited.end(); }
+    void visit  (const T& t)       { m_visited.insert(t); }
+    const VTs& visited()     const { return m_visited; }
+
+private:
+    VTs m_visited;
+};
+
+template<typename T, typename U = T> struct GVisitedTracker: protected GVisited<T>
+{
+    typedef std::vector<U> TUs;
+
+    void  visit(const T& t, const U& u) { GVisited<T>::visit(t); m_tracked.push_back(u); }
+    const TUs& tracked() const          { return m_tracked; }
+    using GVisited<T>::visited;
+
+private:
+    TUs m_tracked;
+};
+
+} // namespace
+
+
+cv::gimpl::Unrolled cv::gimpl::unrollExpr(const GProtoArgs &ins,
+                                          const GProtoArgs &outs)
+{
+    // FIXME: Who's gonna check if ins/outs are not EMPTY?
+    // FIXME: operator== for GObjects? (test if the same object or not)
+    using GObjId = const cv::GOrigin*;
+
+    GVisitedTracker<const GNode::Priv*, cv::GNode> ops;
+    GVisited<GObjId> reached_sources;
+    cv::GOriginSet   origins;
+
+    // Cache input argument objects for a faster look-up
+    // While the only reliable way to identify a Data object is Origin
+    // (multiple data objects may refer to the same Origin as result of
+    // multuple yield() calls), input objects can be uniquely identified
+    // by its `priv` address. Here we rely on this to verify if the expression
+    // we unroll actually matches the protocol specified to us by user.
+    std::unordered_set<GObjId> in_objs_p;
+    for (const auto& in_obj : ins)
+    {
+        // Objects are guarnateed to remain alive while this method
+        // is working, so it is safe to keep pointers here and below
+        in_objs_p.insert(&proto::origin_of(in_obj));
+    }
+
+    // Recursive expression traversal
+    std::stack<cv::GProtoArg> data_objs(std::deque<cv::GProtoArg>(outs.begin(), outs.end()));
+    while (!data_objs.empty())
+    {
+        const auto  obj   = data_objs.top();
+        const auto &obj_p = proto::origin_of(obj);
+        data_objs.pop();
+
+        const auto &origin = obj_p;
+        origins.insert(origin); // TODO: Put Object description here later on
+
+        // If this Object is listed in the protocol, don't dive deeper (even
+        // if it is in fact a result of operation). Our computation is
+        // bounded by this data slot, so terminate this recursion path early.
+        if (in_objs_p.find(&obj_p) != in_objs_p.end())
+        {
+            reached_sources.visit(&obj_p);
+            continue;
+        }
+
+        const cv::GNode &node = origin.node;
+        switch (node.shape())
+        {
+        case cv::GNode::NodeShape::EMPTY:
+            // TODO: Own exception type?
+            util::throw_error(std::logic_error("Empty node reached!"));
+            break;
+
+        case cv::GNode::NodeShape::PARAM:
+        case cv::GNode::NodeShape::CONST_BOUNDED:
+            // No preceding operation to this data object - so the data object is either a GComputation
+            // parameter or a constant (compile-time) value
+            // Record it to check if protocol matches expression tree later
+            if (!reached_sources.visited(&obj_p))
+                reached_sources.visit(&obj_p);
+            break;
+
+        case cv::GNode::NodeShape::CALL:
+            if (!ops.visited(&node.priv()))
+            {
+                // This operation hasn't been visited yet - mark it so,
+                // then add its operands to stack to continue recursion.
+                ops.visit(&node.priv(), node);
+
+                const cv::GCall         call   = origin.node.call();
+                const cv::GCall::Priv&  call_p = call.priv();
+
+                // Put the outputs object description of the node
+                // so that they are not lost if they are not consumed by other operations
+                for (const auto &it : ade::util::indexed(call_p.m_k.outShapes))
+                {
+                    std::size_t port  = ade::util::index(it);
+                    GShape shape      = ade::util::value(it);
+
+                    GOrigin org { shape, node, port};
+                    origins.insert(org);
+                }
+
+                for (const auto &arg : call_p.m_args)
+                {
+                    if (proto::is_dynamic(arg))
+                    {
+                        data_objs.push(proto::rewrap(arg)); // Dive deeper
+                    }
+                }
+            }
+            break;
+
+        default:
+            // Unsupported node shape
+            GAPI_Assert(false);
+            break;
+        }
+    }
+
+    // Check if protocol mentions data_objs which weren't reached during traversal
+    const auto missing_reached_sources = [&reached_sources](GObjId p) {
+        return reached_sources.visited().find(p) == reached_sources.visited().end();
+    };
+    if (ade::util::any_of(in_objs_p, missing_reached_sources))
+    {
+        // TODO: Own exception type or a return code?
+      util::throw_error(std::logic_error("Data object listed in Protocol "
+                                     "wasn\'t reached during unroll"));
+    }
+
+    // Check if there endpoint (parameter) data_objs which are not listed in protocol
+    const auto missing_in_proto = [&in_objs_p](GObjId p) {
+        return p->node.shape() != cv::GNode::NodeShape::CONST_BOUNDED &&
+               in_objs_p.find(p) == in_objs_p.end();
+    };
+    if (ade::util::any_of(reached_sources.visited(), missing_in_proto))
+    {
+        // TODO: Own exception type or a return code?
+      util::throw_error(std::logic_error("Data object reached during unroll "
+                                     "wasn\'t found in Protocol"));
+    }
+
+    return cv::gimpl::Unrolled{ops.tracked(), origins};
+}
+
+
+cv::gimpl::GModelBuilder::GModelBuilder(ade::Graph &g)
+    : m_g(g)
+{
+}
+
+cv::gimpl::GModelBuilder::ProtoSlots
+cv::gimpl::GModelBuilder::put(const GProtoArgs &ins, const GProtoArgs &outs)
+{
+    const auto unrolled = cv::gimpl::unrollExpr(ins, outs);
+
+    // First, put all operations and its arguments into graph.
+    for (const auto &op_expr_node : unrolled.all_ops)
+    {
+        GAPI_Assert(op_expr_node.shape() == GNode::NodeShape::CALL);
+        const GCall&        call    = op_expr_node.call();
+        const GCall::Priv&  call_p  = call.priv();
+        ade::NodeHandle     call_h  = put_OpNode(op_expr_node);
+
+        for (const auto &it : ade::util::indexed(call_p.m_args))
+        {
+            const auto  in_port = ade::util::index(it);
+            const auto& in_arg  = ade::util::value(it);
+
+            if (proto::is_dynamic(in_arg))
+            {
+                ade::NodeHandle data_h = put_DataNode(proto::origin_of(in_arg));
+                cv::gimpl::GModel::linkIn(m_g, call_h, data_h, in_port);
+            }
+        }
+    }
+
+    // Then iterate via all "origins", instantiate (if not yet) Data graph nodes
+    // and connect these nodes with their producers in graph
+    for (const auto &origin : unrolled.all_data)
+    {
+        const cv::GNode& prod = origin.node;
+        GAPI_Assert(prod.shape() != cv::GNode::NodeShape::EMPTY);
+
+        ade::NodeHandle data_h = put_DataNode(origin);
+        if (prod.shape() == cv::GNode::NodeShape::CALL)
+        {
+            ade::NodeHandle call_h = put_OpNode(prod);
+            cv::gimpl::GModel::linkOut(m_g, call_h, data_h, origin.port);
+        }
+    }
+
+    // Mark graph data nodes as INPUTs and OUTPUTs respectively (according to the protocol)
+    for (const auto &arg : ins)
+    {
+        ade::NodeHandle nh = put_DataNode(proto::origin_of(arg));
+        m_g.metadata(nh).get<Data>().storage = Data::Storage::INPUT;
+    }
+    for (const auto &arg : outs)
+    {
+        ade::NodeHandle nh = put_DataNode(proto::origin_of(arg));
+        m_g.metadata(nh).get<Data>().storage = Data::Storage::OUTPUT;
+    }
+
+    // And, finally, store data object layout in meta
+    m_g.metadata().set(Layout{m_graph_data});
+
+    // After graph is generated, specify which data objects are actually
+    // computation entry/exit points.
+    using NodeDescr = std::pair<std::vector<RcDesc>,
+                                std::vector<ade::NodeHandle> >;
+
+    const auto get_proto_slots = [&](const GProtoArgs &proto) -> NodeDescr
+    {
+        NodeDescr slots;
+
+        slots.first.reserve(proto.size());
+        slots.second.reserve(proto.size());
+
+        for (const auto &arg : proto)
+        {
+            ade::NodeHandle nh = put_DataNode(proto::origin_of(arg));
+            const auto &desc = m_g.metadata(nh).get<Data>();
+            //These extra empty {} are to please GCC (-Wmissing-field-initializers)
+            slots.first.push_back(RcDesc{desc.rc, desc.shape, {}});
+            slots.second.push_back(nh);
+        }
+        return slots;
+    };
+
+    auto in_slots  = get_proto_slots(ins);
+    auto out_slots = get_proto_slots(outs);
+    return ProtoSlots{in_slots.first,  out_slots.first,
+                      in_slots.second, out_slots.second};
+}
+
+ade::NodeHandle cv::gimpl::GModelBuilder::put_OpNode(const cv::GNode &node)
+{
+    const auto& node_p = node.priv();
+    const auto  it     = m_graph_ops.find(&node_p);
+    if (it == m_graph_ops.end())
+    {
+        GAPI_Assert(node.shape() == GNode::NodeShape::CALL);
+        const auto &call_p = node.call().priv();
+        auto nh = cv::gimpl::GModel::mkOpNode(m_g, call_p.m_k, call_p.m_args, node_p.m_island);
+        m_graph_ops[&node_p] = nh;
+        return nh;
+    }
+    else return it->second;
+}
+
+// FIXME: rename to get_DataNode (and same for Op)
+ade::NodeHandle cv::gimpl::GModelBuilder::put_DataNode(const GOrigin &origin)
+{
+    const auto it = m_graph_data.find(origin);
+    if (it == m_graph_data.end())
+    {
+        auto nh = cv::gimpl::GModel::mkDataNode(m_g, origin);
+        m_graph_data[origin] = nh;
+        return nh;
+    }
+    else return it->second;
+}
diff --git a/modules/gapi/src/compiler/gmodelbuilder.hpp b/modules/gapi/src/compiler/gmodelbuilder.hpp
new file mode 100644 (file)
index 0000000..ce12c7e
--- /dev/null
@@ -0,0 +1,77 @@
+// 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_GMODEL_BUILDER_HPP
+#define OPENCV_GAPI_GMODEL_BUILDER_HPP
+
+#include <map>
+#include <unordered_map>
+
+#include "opencv2/gapi/gproto.hpp"
+#include "opencv2/gapi/gcall.hpp"
+
+#include "api/gapi_priv.hpp"
+#include "api/gnode.hpp"
+#include "compiler/gmodel.hpp"
+
+namespace cv { namespace gimpl {
+
+struct Unrolled
+{
+    std::vector<cv::GNode> all_ops;
+    GOriginSet             all_data;
+
+    // NB.: Right now, as G-API operates with GMats only and that
+    // GMats have no type or dimensions (when a computation is built),
+    // track only origins (data links) with no any additional meta.
+};
+
+// FIXME: GAPI_EXPORTS only because of tests!!!
+GAPI_EXPORTS Unrolled unrollExpr(const GProtoArgs &ins, const GProtoArgs &outs);
+
+// This class generates an ADE graph with G-API specific metadata
+// to represent user-specified computation in terms of graph model
+//
+// Resulting graph is built according to the following rules:
+// - Every operation is a node
+// - Every dynamic object (GMat) is a node
+// - Edges between nodes represent producer/consumer relationships
+//   between operations and data objects.
+// FIXME: GAPI_EXPORTS only because of tests!!!
+class GAPI_EXPORTS GModelBuilder
+{
+    GModel::Graph m_g;
+
+    // Mappings of G-API user framework entities to ADE node handles
+    std::unordered_map<const cv::GNode::Priv*, ade::NodeHandle> m_graph_ops;
+    GOriginMap<ade::NodeHandle> m_graph_data;
+
+    // Internal methods for mapping APIs into ADE during put()
+    ade::NodeHandle put_OpNode(const cv::GNode &node);
+    ade::NodeHandle put_DataNode(const cv::GOrigin &origin);
+
+public:
+    explicit GModelBuilder(ade::Graph &g);
+
+    // TODO: replace GMat with a generic type
+    // TODO: Cover with tests! (as the rest of internal stuff)
+    // FIXME: Calling this method multiple times is currently UB
+    // TODO: add a semantic link between "ints" returned and in-model data IDs.
+    typedef std::tuple<std::vector<RcDesc>,
+                       std::vector<RcDesc>,
+                       std::vector<ade::NodeHandle>,
+                       std::vector<ade::NodeHandle> > ProtoSlots;
+
+    ProtoSlots put(const GProtoArgs &ins, const GProtoArgs &outs);
+
+protected:
+    ade::NodeHandle opNode(cv::GMat gmat);
+};
+
+}}
+
+#endif // OPENCV_GAPI_GMODEL_BUILDER_HPP
diff --git a/modules/gapi/src/compiler/gobjref.hpp b/modules/gapi/src/compiler/gobjref.hpp
new file mode 100644 (file)
index 0000000..be365c9
--- /dev/null
@@ -0,0 +1,50 @@
+// 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_GMATREF_HPP
+#define OPENCV_GAPI_GMATREF_HPP
+
+#include "opencv2/gapi/util/variant.hpp"
+#include "opencv2/gapi/garg.hpp"
+
+#include "api/gapi_priv.hpp" // GShape, HostCtor
+
+namespace cv
+{
+
+namespace gimpl
+{
+    struct RcDesc
+    {
+        int      id;      // id is unique but local to shape
+        GShape   shape;   // pair <id,shape> IS the unique ID
+        HostCtor ctor;    // FIXME: is it really used here? Or in <Data>?
+
+        bool operator==(const RcDesc &rhs) const
+        {
+            // FIXME: ctor is not checked (should be?)
+            return id == rhs.id && shape == rhs.shape;
+        }
+
+        bool operator< (const RcDesc &rhs) const
+        {
+            return (id == rhs.id) ? shape < rhs.shape : id < rhs.id;
+        }
+    };
+} // gimpl
+
+namespace detail
+{
+    template<> struct GTypeTraits<cv::gimpl::RcDesc>
+    {
+        static constexpr const ArgKind kind = ArgKind::GOBJREF;
+    };
+}
+
+} // cv
+
+#endif // OPENCV_GAPI_GMATREF_HPP
diff --git a/modules/gapi/src/compiler/passes/dump_dot.cpp b/modules/gapi/src/compiler/passes/dump_dot.cpp
new file mode 100644 (file)
index 0000000..333afa5
--- /dev/null
@@ -0,0 +1,221 @@
+// 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
+
+
+#include <iostream>                              // cout
+#include <sstream>                               // stringstream
+#include <fstream>                               // ofstream
+
+#include <ade/passes/check_cycles.hpp>
+
+#include "opencv2/gapi/gproto.hpp"
+#include "compiler/gmodel.hpp"
+#include "compiler/gislandmodel.hpp"
+#include "compiler/passes/passes.hpp"
+
+namespace cv { namespace gimpl { namespace passes {
+
+// TODO: FIXME: Ideally all this low-level stuff with accessing ADE APIs directly
+// should be incapsulated somewhere into GModel, so here we'd operate not
+// with raw nodes and edges, but with Operations and Data it produce/consume.
+void dumpDot(const ade::Graph &g, std::ostream& os)
+{
+    GModel::ConstGraph gr(g);
+
+    const std::unordered_map<cv::GShape, std::string> data_labels = {
+        {cv::GShape::GMAT,    "GMat"},
+        {cv::GShape::GSCALAR, "GScalar"},
+        {cv::GShape::GARRAY,  "GArray"},
+    };
+
+    auto format_op_label  = [&gr](ade::NodeHandle nh) -> std::string {
+        std::stringstream ss;
+        const cv::GKernel k = gr.metadata(nh).get<Op>().k;
+        ss << k.name << "_" << nh;
+        return ss.str();
+    };
+
+    auto format_op  = [&format_op_label](ade::NodeHandle nh) -> std::string {
+        return "\"" + format_op_label(nh) + "\"";
+    };
+
+    auto format_obj = [&gr, &data_labels](ade::NodeHandle nh) -> std::string {
+        std::stringstream ss;
+        const auto &data = gr.metadata(nh).get<Data>();
+        ss << data_labels.at(data.shape) << "_" << data.rc;
+        return ss.str();
+    };
+
+    auto format_log = [&gr](ade::NodeHandle nh, const std::string &obj_name) {
+        std::stringstream ss;
+        const auto &msgs = gr.metadata(nh).get<Journal>().messages;
+        ss << "xlabel=\"";
+        if (!obj_name.empty()) { ss << "*** " << obj_name << " ***:\n"; };
+        for (const auto &msg : msgs) { ss << msg << "\n"; }
+        ss << "\"";
+        return ss.str();
+    };
+
+    // FIXME:
+    // Unify with format_log
+    auto format_log_e = [&gr](ade::EdgeHandle nh) {
+        std::stringstream ss;
+        const auto &msgs = gr.metadata(nh).get<Journal>().messages;
+        for (const auto &msg : msgs) { ss << "\n" << msg; }
+        return ss.str();
+    };
+
+    auto sorted = gr.metadata().get<ade::passes::TopologicalSortData>();
+
+    os << "digraph GAPI_Computation {\n";
+
+    // Prior to dumping the graph itself, list Data and Op nodes individually
+    // and put type information in labels.
+    // Also prepare list of nodes in islands, if any
+    std::map<std::string, std::vector<std::string> > islands;
+    for (auto &nh : sorted.nodes())
+    {
+        const auto node_type = gr.metadata(nh).get<NodeType>().t;
+        if (NodeType::DATA == node_type)
+        {
+            const auto obj_data = gr.metadata(nh).get<Data>();
+            const auto obj_name = format_obj(nh);
+
+            os << obj_name << " [label=\"" << obj_name << "\n" << obj_data.meta << "\"";
+            if (gr.metadata(nh).contains<Journal>()) { os << ", " << format_log(nh, obj_name); }
+            os << " ]\n";
+
+            if (gr.metadata(nh).contains<Island>())
+                islands[gr.metadata(nh).get<Island>().island].push_back(obj_name);
+        }
+        else if (NodeType::OP == gr.metadata(nh).get<NodeType>().t)
+        {
+            const auto obj_name       = format_op(nh);
+            const auto obj_name_label = format_op_label(nh);
+
+            os << obj_name << " [label=\"" << obj_name_label << "\"";
+            if (gr.metadata(nh).contains<Journal>()) { os << ", " << format_log(nh, obj_name_label); }
+            os << " ]\n";
+
+            if (gr.metadata(nh).contains<Island>())
+                islands[gr.metadata(nh).get<Island>().island].push_back(obj_name);
+        }
+    }
+
+    // Then, dump Islands (only nodes, operations and data, without links)
+    for (const auto &isl : islands)
+    {
+        os << "subgraph \"cluster " + isl.first << "\" {\n";
+        for(auto isl_node : isl.second) os << isl_node << ";\n";
+        os << "label=\"" << isl.first << "\";";
+        os << "}\n";
+    }
+
+    // Now dump the graph
+    for (auto &nh : sorted.nodes())
+    {
+        // FIXME: Alan Kay probably hates me.
+        switch (gr.metadata(nh).get<NodeType>().t)
+        {
+        case NodeType::DATA:
+        {
+            const auto obj_name = format_obj(nh);
+            for (const auto &eh : nh->outEdges())
+            {
+                os << obj_name << " -> " << format_op(eh->dstNode())
+                   << " [ label = \"in_port: "
+                   << gr.metadata(eh).get<Input>().port;
+                   if (gr.metadata(eh).contains<Journal>()) { os << format_log_e(eh); }
+                   os << "\" ] \n";
+            }
+        }
+        break;
+        case NodeType::OP:
+        {
+            for (const auto &eh : nh->outEdges())
+            {
+                os << format_op(nh) << " -> " << format_obj(eh->dstNode())
+                   << " [ label = \"out_port: "
+                   << gr.metadata(eh).get<Output>().port
+                   << " \" ]; \n";
+            }
+        }
+        break;
+        default: GAPI_Assert(false);
+        }
+    }
+
+    // And finally dump a GIslandModel (not connected with GModel directly,
+    // but projected in the same .dot file side-by-side)
+    auto pIG = gr.metadata().get<IslandModel>().model;
+    GIslandModel::Graph gim(*pIG);
+    for (auto nh : gim.nodes())
+    {
+        switch (gim.metadata(nh).get<NodeKind>().k)
+        {
+        case NodeKind::ISLAND:
+            {
+                const auto island   = gim.metadata(nh).get<FusedIsland>().object;
+                const auto isl_name = "\"" + island->name() + "\"";
+                for (auto out_nh : nh->outNodes())
+                {
+                    os << isl_name << " -> \"slot:"
+                       << format_obj(gim.metadata(out_nh).get<DataSlot>()
+                                     .original_data_node)
+                       << "\"\n";
+                }
+            }
+            break;
+
+        case NodeKind::SLOT:
+            {
+                const auto obj_name = format_obj(gim.metadata(nh).get<DataSlot>()
+                                                 .original_data_node);
+                for (auto cons_nh : nh->outNodes())
+                {
+                    os << "\"slot:" << obj_name << "\" -> \""
+                       << gim.metadata(cons_nh).get<FusedIsland>().object->name()
+                       << "\"\n";
+                }
+            }
+            break;
+
+        default:
+            GAPI_Assert(false);
+            break;
+        }
+    }
+
+    os << "}" << std::endl;
+}
+
+void dumpDot(ade::passes::PassContext &ctx, std::ostream& os)
+{
+    dumpDot(ctx.graph, os);
+}
+
+void dumpDotStdout(ade::passes::PassContext &ctx)
+{
+    dumpDot(ctx, std::cout);
+}
+
+void dumpDotToFile(ade::passes::PassContext &ctx, const std::string& dump_path)
+{
+    std::ofstream dump_file(dump_path);
+
+    if (dump_file.is_open())
+    {
+        dumpDot(ctx, dump_file);
+        dump_file << std::endl;
+    }
+}
+
+void dumpGraph(ade::passes::PassContext &ctx, const std::string& dump_path)
+{
+    dump_path.empty() ? dumpDotStdout(ctx) : dumpDotToFile(ctx, dump_path);
+}
+
+}}} // cv::gimpl::passes
diff --git a/modules/gapi/src/compiler/passes/exec.cpp b/modules/gapi/src/compiler/passes/exec.cpp
new file mode 100644 (file)
index 0000000..137bbc1
--- /dev/null
@@ -0,0 +1,640 @@
+// 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
+
+
+#include <string>
+#include <list> // list
+#include <iomanip>  // setw, etc
+#include <fstream> // ofstream
+#include <memory>
+#include <functional>
+
+#include <ade/util/algorithm.hpp>   // contains
+#include <ade/util/chain_range.hpp> // chain
+
+#include "opencv2/gapi/util/optional.hpp"  // util::optional
+#include "opencv2/core/cvdef.h"
+#include "logger.hpp"    // GAPI_LOG
+
+#include "compiler/gmodel.hpp"
+#include "compiler/gislandmodel.hpp"
+#include "compiler/passes/passes.hpp"
+#include "compiler/passes/helpers.hpp"
+#include "compiler/transactions.hpp"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// N.B.
+// Merge is a binary operation (LHS `Merge` RHS) where LHS may be arbitrary
+//
+// After every merge, the procedure starts from the beginning (in the topological
+// order), thus trying to merge next "unmerged" island to the latest merged one.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Uncomment to dump more info on merge process
+// FIXME: make it user-configurable run-time option
+// #define DEBUG_MERGE
+
+namespace cv
+{
+namespace gimpl
+{
+namespace
+{
+    bool fusionIsTrivial(const ade::Graph &src_graph)
+    {
+        // Fusion is considered trivial if there only one
+        // active backend and no user-defined islands
+        // FIXME:
+        // Also check the cases backend can't handle
+        // (e.x. GScalar connecting two fluid ops should split the graph)
+        const GModel::ConstGraph g(src_graph);
+        const auto& active_backends = g.metadata().get<ActiveBackends>().backends;
+        return active_backends.size() == 1 &&
+                ade::util::all_of(g.nodes(), [&](ade::NodeHandle nh) {
+            return !g.metadata(nh).contains<Island>();
+        });
+    }
+
+    void fuseTrivial(GIslandModel::Graph &g, const ade::Graph &src_graph)
+    {
+        const GModel::ConstGraph src_g(src_graph);
+
+        const auto& backend = *src_g.metadata().get<ActiveBackends>().backends.cbegin();
+        const auto& proto = src_g.metadata().get<Protocol>();
+        GIsland::node_set all, in_ops, out_ops;
+
+        all.insert(src_g.nodes().begin(), src_g.nodes().end());
+
+        for (const auto nh : proto.in_nhs)
+        {
+            all.erase(nh);
+            in_ops.insert(nh->outNodes().begin(), nh->outNodes().end());
+        }
+        for (const auto nh : proto.out_nhs)
+        {
+            all.erase(nh);
+            out_ops.insert(nh->inNodes().begin(), nh->inNodes().end());
+        }
+
+        auto isl = std::make_shared<GIsland>(backend,
+                                             std::move(all),
+                                             std::move(in_ops),
+                                             std::move(out_ops),
+                                             util::optional<std::string>{});
+
+        auto ih = GIslandModel::mkIslandNode(g, std::move(isl));
+
+        for (const auto nh : proto.in_nhs)
+        {
+            auto slot = GIslandModel::mkSlotNode(g, nh);
+            g.link(slot, ih);
+        }
+        for (const auto nh : proto.out_nhs)
+        {
+            auto slot = GIslandModel::mkSlotNode(g, nh);
+            g.link(ih, slot);
+        }
+    }
+
+    struct MergeContext
+    {
+        using CycleCausers = std::pair< std::shared_ptr<GIsland>,
+                                        std::shared_ptr<GIsland> >;
+
+        struct CycleHasher final
+        {
+            std::size_t operator()(const CycleCausers& p) const
+            {
+                std::size_t a = std::hash<GIsland*>()(p.first.get());
+                std::size_t b = std::hash<GIsland*>()(p.second.get());
+                return a ^ (b << 1);
+            }
+        };
+
+        // Set of Islands pairs which cause a cycle if merged.
+        // Every new merge produces a new Island, and if Islands were
+        // merged (and thus dropped from GIslandModel), the objects may
+        // still be alive as included into this set.
+        std::unordered_set<CycleCausers, CycleHasher> cycle_causers;
+    };
+
+    bool canMerge(const GIslandModel::Graph &g,
+                  const ade::NodeHandle a_nh,
+                  const ade::NodeHandle /*slot_nh*/,
+                  const ade::NodeHandle b_nh,
+                  const MergeContext &ctx = MergeContext())
+    {
+        auto a_ptr = g.metadata(a_nh).get<FusedIsland>().object;
+        auto b_ptr = g.metadata(b_nh).get<FusedIsland>().object;
+        GAPI_Assert(a_ptr.get());
+        GAPI_Assert(b_ptr.get());
+
+        // Islands with different affinity can't be merged
+        if (a_ptr->backend() != b_ptr->backend())
+            return false;
+
+        // Islands which cause a cycle can't be merged as well
+        // (since the flag is set, the procedure already tried to
+        // merge these islands in the past)
+        if (ade::util::contains(ctx.cycle_causers, std::make_pair(a_ptr, b_ptr))||
+            ade::util::contains(ctx.cycle_causers, std::make_pair(b_ptr, a_ptr)))
+            return false;
+
+        // There may be user-defined islands. Initially user-defined
+        // islands also are built from single operations and then merged
+        // by this procedure, but there is some exceptions.
+        // User-specified island can't be merged to an internal island
+        if (   ( a_ptr->is_user_specified() && !b_ptr->is_user_specified())
+            || (!a_ptr->is_user_specified() &&  b_ptr->is_user_specified()))
+        {
+            return false;
+        }
+        else if (a_ptr->is_user_specified() && b_ptr->is_user_specified())
+        {
+            // These islads are _different_ user-specified Islands
+            // FIXME: today it may only differ by name
+            if (a_ptr->name() != b_ptr->name())
+                return false;
+        }
+
+        // FIXME: add a backend-specified merge checker
+        return true;
+    }
+
+    inline bool isProducedBy(const ade::NodeHandle &slot,
+                             const ade::NodeHandle &island)
+    {
+        // A data slot may have only 0 or 1 producer
+        if (slot->inNodes().size() == 0)
+            return false;
+
+        return slot->inNodes().front() == island;
+    }
+
+    inline bool isConsumedBy(const ade::NodeHandle &slot,
+                             const ade::NodeHandle &island)
+    {
+        auto it = std::find_if(slot->outNodes().begin(),
+                               slot->outNodes().end(),
+                               [&](const ade::NodeHandle &nh) {
+                                   return nh == island;
+                               });
+        return it != slot->outNodes().end();
+    }
+
+    /**
+     * Find a candidate Island for merge for the given Island nh.
+     *
+     * @param g Island Model where merge occurs
+     * @param nh GIsland node, either LHS or RHS of probable merge
+     * @param ctx Merge context, may contain some cached stuff to avoid
+     *      double/triple/etc checking
+     * @return Tuple of Island handle, Data slot handle (which connects them),
+     *      and a position of found handle with respect to nh (IN/OUT)
+     */
+    std::tuple<ade::NodeHandle, ade::NodeHandle, Direction>
+    findCandidate(const GIslandModel::Graph &g,
+                  ade::NodeHandle nh,
+                  const MergeContext &ctx = MergeContext())
+    {
+        using namespace std::placeholders;
+
+        // Find a first matching candidate GIsland for merge
+        // among inputs
+        for (const auto& input_data_nh : nh->inNodes())
+        {
+            if (input_data_nh->inNodes().size() != 0)
+            {
+                // Data node must have a single producer only
+                GAPI_DbgAssert(input_data_nh->inNodes().size() == 1);
+                auto input_data_prod_nh = input_data_nh->inNodes().front();
+                if (canMerge(g, input_data_prod_nh, input_data_nh, nh, ctx))
+                    return std::make_tuple(input_data_prod_nh,
+                                           input_data_nh,
+                                           Direction::In);
+            }
+        } // for(inNodes)
+
+        // Ok, now try to find it among the outputs
+        for (const auto& output_data_nh : nh->outNodes())
+        {
+            auto mergeTest = [&](ade::NodeHandle cons_nh) -> bool {
+                return canMerge(g, nh, output_data_nh, cons_nh, ctx);
+            };
+            auto cand_it = std::find_if(output_data_nh->outNodes().begin(),
+                                        output_data_nh->outNodes().end(),
+                                        mergeTest);
+            if (cand_it != output_data_nh->outNodes().end())
+                return std::make_tuple(*cand_it,
+                                       output_data_nh,
+                                       Direction::Out);
+        } // for(outNodes)
+        // Empty handle, no good candidates
+        return std::make_tuple(ade::NodeHandle(),
+                               ade::NodeHandle(),
+                               Direction::Invalid);
+    }
+
+    // A cancellable merge of two GIslands, "a" and "b", connected via "slot"
+    class MergeAction
+    {
+        ade::Graph &m_g;
+        const ade::Graph &m_orig_g;
+        GIslandModel::Graph m_gim;
+        ade::NodeHandle m_prod;
+        ade::NodeHandle m_slot;
+        ade::NodeHandle m_cons;
+
+        Change::List m_changes;
+
+        struct MergeObjects
+        {
+            using NS = GIsland::node_set;
+            NS all;      // same as in GIsland
+            NS in_ops;   // same as in GIsland
+            NS out_ops;  // same as in GIsland
+            NS opt_interim_slots;    // can be dropped (optimized out)
+            NS non_opt_interim_slots;// can't be dropped (extern. link)
+        };
+        MergeObjects identify() const;
+
+    public:
+        MergeAction(ade::Graph &g,
+                    const ade::Graph &orig_g,
+                    ade::NodeHandle prod,
+                    ade::NodeHandle slot,
+                    ade::NodeHandle cons)
+            : m_g(g)
+            , m_orig_g(orig_g)
+            , m_gim(GIslandModel::Graph(m_g))
+            , m_prod(prod)
+            , m_slot(slot)
+            , m_cons(cons)
+        {
+        }
+
+        void tryMerge(); // Try to merge islands Prod and Cons
+        void rollback(); // Roll-back changes if merge has been done but broke the model
+        void commit();   // Fix changes in the model after successful merge
+    };
+
+    // Merge proceduce is a replacement of two Islands, Prod and Cons,
+    // connected via !Slot!, with a new Island, which contain all Prod
+    // nodes + all Cons nodes, and reconnected in the graph properly:
+    //
+    // Merge(Prod, !Slot!, Cons)
+    //
+    //                                  [Slot 2]
+    //                                    :
+    //                                    v
+    //     ... [Slot 0] -> Prod -> !Slot! -> Cons -> [Slot 3] -> ...
+    //     ... [Slot 1] -'           :           '-> [Slot 4] -> ...
+    //                               V
+    //                              ...
+    // results into:
+    //
+    //     ... [Slot 0] -> Merged  -> [Slot 3] ...
+    //     ... [Slot 1] :         :-> [Slot 4] ...
+    //     ... [Slot 2] '         '-> !Slot! ...
+    //
+    // The rules are the following:
+    // 1) All Prod input slots become Merged input slots;
+    //    - Example: Slot 0 Slot 1
+    // 2) Any Cons input slots which come from Islands different to Prod
+    //    also become Merged input slots;
+    //    - Example: Slot 2
+    // 3) All Cons output slots become Merged output slots;
+    //    - Example: Slot 3, Slot 4
+    // 4) All Prod output slots which are not consumed by Cons
+    //    also become Merged output slots;
+    //    - (not shown on the example)
+    // 5) If the !Slot! which connects Prod and Cons is consumed
+    //    exclusively by Cons, it is optimized out (dropped) from the model;
+    // 6) If the !Slot! is used by consumers other by Cons, it
+    //    becomes an extra output of Merged
+    // 7) !Slot! may be not the only connection between Prod and Cons,
+    //    but as a result of merge operation, all such connections
+    //    should be handles as described for !Slot!
+
+    MergeAction::MergeObjects MergeAction::identify() const
+    {
+        auto lhs = m_gim.metadata(m_prod).get<FusedIsland>().object;
+        auto rhs = m_gim.metadata(m_cons).get<FusedIsland>().object;
+
+        GIsland::node_set interim_slots;
+
+        GIsland::node_set merged_all(lhs->contents());
+        merged_all.insert(rhs->contents().begin(), rhs->contents().end());
+
+        GIsland::node_set merged_in_ops(lhs->in_ops());     // (1)
+        for (auto cons_in_slot_nh : m_cons->inNodes())      // (2)
+        {
+            if (isProducedBy(cons_in_slot_nh, m_prod))
+            {
+                interim_slots.insert(cons_in_slot_nh);
+                // at this point, interim_slots are not sync with merged_all
+                // (merged_all will be updated with interim_slots which
+                // will be optimized out).
+            }
+            else
+            {
+                const auto extra_in_ops = rhs->consumers(m_g, cons_in_slot_nh);
+                merged_in_ops.insert(extra_in_ops.begin(), extra_in_ops.end());
+            }
+        }
+
+        GIsland::node_set merged_out_ops(rhs->out_ops());   // (3)
+        for (auto prod_out_slot_nh : m_prod->outNodes())    // (4)
+        {
+            if (!isConsumedBy(prod_out_slot_nh, m_cons))
+            {
+                merged_out_ops.insert(lhs->producer(m_g, prod_out_slot_nh));
+            }
+        }
+
+        // (5,6,7)
+        GIsland::node_set opt_interim_slots;
+        GIsland::node_set non_opt_interim_slots;
+
+        auto is_non_opt = [&](const ade::NodeHandle &slot_nh) {
+            // If a data object associated with this slot is a part
+            // of GComputation _output_ protocol, it can't be optimzied out
+            const auto data_nh = m_gim.metadata(slot_nh).get<DataSlot>().original_data_node;
+            const auto &data = GModel::ConstGraph(m_orig_g).metadata(data_nh).get<Data>();
+            if (data.storage == Data::Storage::OUTPUT)
+                return true;
+
+            // Otherwise, a non-optimizeable data slot is the one consumed
+            // by some other island than "cons"
+            const auto it = std::find_if(slot_nh->outNodes().begin(),
+                                         slot_nh->outNodes().end(),
+                                         [&](ade::NodeHandle &&nh)
+                                         {return nh != m_cons;});
+            return it != slot_nh->outNodes().end();
+        };
+        for (auto slot_nh : interim_slots)
+        {
+            // Put all intermediate data nodes (which are BOTH optimized
+            // and not-optimized out) to Island contents.
+            merged_all.insert(m_gim.metadata(slot_nh)
+                              .get<DataSlot>().original_data_node);
+
+            GIsland::node_set &dst = is_non_opt(slot_nh)
+                ? non_opt_interim_slots // there are consumers other than m_cons
+                : opt_interim_slots;    // there's no consumers other than m_cons
+            dst.insert(slot_nh);
+        }
+
+        // (4+6).
+        // BTW, (4) could be "All Prod output slots read NOT ONLY by Cons"
+        for (auto non_opt_slot_nh : non_opt_interim_slots)
+        {
+            merged_out_ops.insert(lhs->producer(m_g, non_opt_slot_nh));
+        }
+        return MergeObjects{
+            merged_all, merged_in_ops, merged_out_ops,
+            opt_interim_slots, non_opt_interim_slots
+        };
+    }
+
+    // FIXME(DM): Probably this procedure will be refactored dramatically one day...
+    void MergeAction::tryMerge()
+    {
+        // _: Understand the contents and I/O connections of a new merged Island
+        MergeObjects mo = identify();
+        auto lhs_obj = m_gim.metadata(m_prod).get<FusedIsland>().object;
+        auto rhs_obj = m_gim.metadata(m_cons).get<FusedIsland>().object;
+        GAPI_Assert(   ( lhs_obj->is_user_specified() &&  rhs_obj->is_user_specified())
+                    || (!lhs_obj->is_user_specified() && !rhs_obj->is_user_specified()));
+        cv::util::optional<std::string> maybe_user_tag;
+        if (lhs_obj->is_user_specified() && rhs_obj->is_user_specified())
+        {
+            GAPI_Assert(lhs_obj->name() == rhs_obj->name());
+            maybe_user_tag = cv::util::make_optional(lhs_obj->name());
+        }
+
+        // A: Create a new Island and add it to the graph
+        auto backend = m_gim.metadata(m_prod).get<FusedIsland>()
+            .object->backend();
+        auto merged = std::make_shared<GIsland>(backend,
+                                                           std::move(mo.all),
+                                                           std::move(mo.in_ops),
+                                                           std::move(mo.out_ops),
+                                                           std::move(maybe_user_tag));
+        // FIXME: move this debugging to some user-controllable log-level
+#ifdef DEBUG_MERGE
+        merged->debug();
+#endif
+        auto new_nh = GIslandModel::mkIslandNode(m_gim, std::move(merged));
+        m_changes.enqueue<Change::NodeCreated>(new_nh);
+
+        // B: Disconnect all Prod's input Slots from Prod,
+        //    connect it to Merged
+        std::vector<ade::EdgeHandle> input_edges(m_prod->inEdges().begin(),
+                                                 m_prod->inEdges().end());
+        for (auto in_edge : input_edges)
+        {
+            m_changes.enqueue<Change::NewLink>(m_g, in_edge->srcNode(), new_nh);
+            m_changes.enqueue<Change::DropLink>(m_g, m_prod, in_edge);
+        }
+
+        // C: Disconnect all Cons' output Slots from Cons,
+        //    connect it to Merged
+        std::vector<ade::EdgeHandle> output_edges(m_cons->outEdges().begin(),
+                                                  m_cons->outEdges().end());
+        for (auto out_edge : output_edges)
+        {
+            m_changes.enqueue<Change::NewLink>(m_g, new_nh, out_edge->dstNode());
+            m_changes.enqueue<Change::DropLink>(m_g, m_cons, out_edge);
+        }
+
+        // D: Process the intermediate slots (betweed Prod n Cons).
+        // D/1 - Those which are optimized out are just removed from the model
+        for (auto opt_slot_nh : mo.opt_interim_slots)
+        {
+            GAPI_Assert(1      == opt_slot_nh->inNodes().size() );
+            GAPI_Assert(m_prod == opt_slot_nh->inNodes().front());
+
+            std::vector<ade::EdgeHandle> edges_to_drop;
+            ade::util::copy(ade::util::chain(opt_slot_nh->inEdges(),
+                                             opt_slot_nh->outEdges()),
+                            std::back_inserter(edges_to_drop));
+            for (auto eh : edges_to_drop)
+            {
+                m_changes.enqueue<Change::DropLink>(m_g, opt_slot_nh, eh);
+            }
+            m_changes.enqueue<Change::DropNode>(opt_slot_nh);
+        }
+        // D/2 - Those which are used externally are connected to new nh
+        //       as outputs.
+        for (auto non_opt_slot_nh : mo.non_opt_interim_slots)
+        {
+            GAPI_Assert(1      == non_opt_slot_nh->inNodes().size() );
+            GAPI_Assert(m_prod == non_opt_slot_nh->inNodes().front());
+            m_changes.enqueue<Change::DropLink>(m_g, non_opt_slot_nh,
+                                                non_opt_slot_nh->inEdges().front());
+
+            std::vector<ade::EdgeHandle> edges_to_probably_drop
+                (non_opt_slot_nh->outEdges().begin(),
+                 non_opt_slot_nh->outEdges().end());;
+            for (auto eh : edges_to_probably_drop)
+            {
+                if (eh->dstNode() == m_cons)
+                {
+                    // drop only edges to m_cons, as there's other consumers
+                    m_changes.enqueue<Change::DropLink>(m_g, non_opt_slot_nh, eh);
+                }
+            }
+            m_changes.enqueue<Change::NewLink>(m_g, new_nh, non_opt_slot_nh);
+        }
+
+        // E. All Prod's output edges which are directly related to Merge (e.g.
+        //    connected to Cons) were processed on step (D).
+        //    Relink the remaining output links
+        std::vector<ade::EdgeHandle>  prod_extra_out_edges
+            (m_prod->outEdges().begin(),
+             m_prod->outEdges().end());
+        for (auto extra_out : prod_extra_out_edges)
+        {
+            m_changes.enqueue<Change::NewLink>(m_g, new_nh, extra_out->dstNode());
+            m_changes.enqueue<Change::DropLink>(m_g, m_prod, extra_out);
+        }
+
+        // F. All Cons' input edges which are directly related to merge (e.g.
+        //    connected to Prod) were processed on step (D) as well,
+        //    remaining should become Merged island's input edges
+        std::vector<ade::EdgeHandle> cons_extra_in_edges
+            (m_cons->inEdges().begin(),
+             m_cons->inEdges().end());
+        for (auto extra_in : cons_extra_in_edges)
+        {
+            m_changes.enqueue<Change::NewLink>(m_g, extra_in->srcNode(), new_nh);
+            m_changes.enqueue<Change::DropLink>(m_g, m_cons, extra_in);
+        }
+
+        // G. Finally, drop the original Island nodes. DropNode() does
+        //    the sanity check for us (both nodes should have 0 edges).
+        m_changes.enqueue<Change::DropNode>(m_prod);
+        m_changes.enqueue<Change::DropNode>(m_cons);
+    }
+
+    void MergeAction::rollback()
+    {
+        m_changes.rollback(m_g);
+    }
+    void MergeAction::commit()
+    {
+        m_changes.commit(m_g);
+    }
+
+#ifdef DEBUG_MERGE
+    void merge_debug(const ade::Graph &g, int iteration)
+    {
+        std::stringstream filename;
+        filename << "fusion_" << static_cast<const void*>(&g)
+                 << "_" << std::setw(4) << std::setfill('0') << iteration
+                 << ".dot";
+        std::ofstream ofs(filename.str());
+        passes::dumpDot(g, ofs);
+    }
+#endif
+
+    void fuseGeneral(ade::Graph& im, const ade::Graph& g)
+    {
+        GIslandModel::Graph gim(im);
+        MergeContext mc;
+
+        bool there_was_a_merge = false;
+        std::size_t iteration = 0u;
+        do
+        {
+            there_was_a_merge = false;
+
+            // FIXME: move this debugging to some user-controllable log level
+    #ifdef DEBUG_MERGE
+            GAPI_LOG_INFO(NULL, "Before next merge attempt " << iteration << "...");
+            merge_debug(g, iteration);
+    #endif
+            iteration++;
+            auto sorted = pass_helpers::topoSort(im);
+            for (auto nh : sorted)
+            {
+                if (NodeKind::ISLAND == gim.metadata(nh).get<NodeKind>().k)
+                {
+                    ade::NodeHandle cand_nh;
+                    ade::NodeHandle cand_slot;
+                    Direction dir = Direction::Invalid;
+                    std::tie(cand_nh, cand_slot, dir) = findCandidate(gim, nh, mc);
+                    if (cand_nh != nullptr && dir != Direction::Invalid)
+                    {
+                        auto lhs_nh = (dir == Direction::In  ? cand_nh : nh);
+                        auto rhs_nh = (dir == Direction::Out ? cand_nh : nh);
+
+                        auto l_obj = gim.metadata(lhs_nh).get<FusedIsland>().object;
+                        auto r_obj = gim.metadata(rhs_nh).get<FusedIsland>().object;
+                        GAPI_LOG_INFO(NULL, r_obj->name() << " can be merged into " << l_obj->name());
+                        // Try to do a merge. If merge was succesfull, check if the
+                        // graph have cycles (cycles are prohibited at this point).
+                        // If there are cycles, roll-back the merge and mark a pair of
+                        // these Islands with a special tag - "cycle-causing".
+                        MergeAction action(im, g, lhs_nh, cand_slot, rhs_nh);
+                        action.tryMerge();
+                        if (pass_helpers::hasCycles(im))
+                        {
+                            GAPI_LOG_INFO(NULL,
+                                          "merge(" << l_obj->name() << "," << r_obj->name() <<
+                                          ") caused cycle, rolling back...");
+                            action.rollback();
+                            // don't try to merge these two islands next time (findCandidate will use that)
+                            mc.cycle_causers.insert({l_obj, r_obj});
+                        }
+                        else
+                        {
+                            GAPI_LOG_INFO(NULL,
+                                          "merge(" << l_obj->name() << "," << r_obj->name() <<
+                                          ") was successful!");
+                            action.commit();
+    #ifdef DEBUG_MERGE
+                            GIslandModel::syncIslandTags(gim, g);
+    #endif
+                            there_was_a_merge = true;
+                            break; // start do{}while from the beginning
+                        }
+                    } // if(can merge)
+                } // if(ISLAND)
+            } // for(all nodes)
+        }
+        while (there_was_a_merge);
+    }
+}  // anonymous namespace
+
+void passes::fuseIslands(ade::passes::PassContext &ctx)
+{
+    std::shared_ptr<ade::Graph> gptr(new ade::Graph);
+    GIslandModel::Graph gim(*gptr);
+
+    if (fusionIsTrivial(ctx.graph))
+    {
+        fuseTrivial(gim, ctx.graph);
+    }
+    else
+    {
+        GIslandModel::generateInitial(gim, ctx.graph);
+        fuseGeneral(*gptr.get(), ctx.graph);
+    }
+    GModel::Graph(ctx.graph).metadata().set(IslandModel{std::move(gptr)});
+}
+
+void passes::syncIslandTags(ade::passes::PassContext &ctx)
+{
+    GModel::Graph gm(ctx.graph);
+    std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model);
+    GIslandModel::Graph gim(*gptr);
+    GIslandModel::syncIslandTags(gim, ctx.graph);
+}
+}} // namespace cv::gimpl
diff --git a/modules/gapi/src/compiler/passes/helpers.cpp b/modules/gapi/src/compiler/passes/helpers.cpp
new file mode 100644 (file)
index 0000000..e77a72b
--- /dev/null
@@ -0,0 +1,120 @@
+// 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
+
+
+#include <algorithm>     // copy
+#include <unordered_map>
+#include <unordered_set>
+
+#include <ade/util/filter_range.hpp>
+
+#include "opencv2/gapi/own/assert.hpp" // GAPI_Assert
+#include "compiler/passes/helpers.hpp"
+
+namespace {
+namespace Cycles
+{
+    // FIXME: This code is taken directly from ADE.
+    // export a bool(ade::Graph) function with pass instead
+    enum class TraverseState
+    {
+        visiting,
+        visited,
+    };
+    using state_t = std::unordered_map<ade::Node*, TraverseState>;
+
+    bool inline checkCycle(state_t& state, const ade::NodeHandle& node)
+    {
+        GAPI_Assert(nullptr != node);
+        state[node.get()] = TraverseState::visiting;
+        for (auto adj: node->outNodes())
+        {
+            auto it = state.find(adj.get());
+            if (state.end() == it) // not visited
+            {
+                // FIXME: use std::stack instead on-stack recursion
+                if (checkCycle(state, adj))
+                {
+                    return true; // detected! (deeper frame)
+                }
+            }
+            else if (TraverseState::visiting == it->second)
+            {
+                return true; // detected! (this frame)
+            }
+        }
+        state[node.get()] = TraverseState::visited;
+        return false; // not detected
+    }
+
+    bool inline hasCycles(const ade::Graph &graph)
+    {
+        state_t state;
+        bool detected = false;
+        for (auto node: graph.nodes())
+        {
+            if (state.end() == state.find(node.get()))
+            {
+                // not yet visited during recursion
+                detected |= checkCycle(state, node);
+                if (detected) break;
+            }
+        }
+        return detected;
+    }
+} // namespace Cycles
+
+namespace TopoSort
+{
+    using sorted_t = std::vector<ade::NodeHandle>;
+    using visited_t = std::unordered_set<ade::Node*>;
+
+    struct NonEmpty final
+    {
+        bool operator()(const ade::NodeHandle& node) const
+        {
+            return nullptr != node;
+        }
+    };
+
+    void inline visit(sorted_t& sorted, visited_t& visited, const ade::NodeHandle& node)
+    {
+        if (visited.end() == visited.find(node.get()))
+        {
+            for (auto adj: node->inNodes())
+            {
+                visit(sorted, visited, adj);
+            }
+            sorted.push_back(node);
+            visited.insert(node.get());
+        }
+    }
+
+    sorted_t inline topoSort(const ade::Graph &g)
+    {
+        sorted_t sorted;
+        visited_t visited;
+        for (auto node: g.nodes())
+        {
+            visit(sorted, visited, node);
+        }
+
+        auto r = ade::util::filter<NonEmpty>(ade::util::toRange(sorted));
+        return sorted_t(r.begin(), r.end());
+    }
+} // namespace TopoSort
+
+} // anonymous namespace
+
+bool cv::gimpl::pass_helpers::hasCycles(const ade::Graph &g)
+{
+    return Cycles::hasCycles(g);
+}
+
+std::vector<ade::NodeHandle> cv::gimpl::pass_helpers::topoSort(const ade::Graph &g)
+{
+    return TopoSort::topoSort(g);
+}
diff --git a/modules/gapi/src/compiler/passes/helpers.hpp b/modules/gapi/src/compiler/passes/helpers.hpp
new file mode 100644 (file)
index 0000000..3aa18e6
--- /dev/null
@@ -0,0 +1,31 @@
+// 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_COMPILER_PASSES_HELPERS_HPP
+#define OPENCV_GAPI_COMPILER_PASSES_HELPERS_HPP
+
+// FIXME: DROP THIS and REUSE ADE utilities
+// (which serve as passes already but are not exposed as standalone functions)
+
+#include <vector>
+
+#include <ade/passes/pass_base.hpp>
+#include <ade/node.hpp> // FIXME: Forward declarations instead?
+#include <ade/graph.hpp>
+
+namespace cv {
+namespace gimpl {
+namespace pass_helpers {
+
+bool hasCycles(const ade::Graph &graph);
+std::vector<ade::NodeHandle> topoSort(const ade::Graph &graph);
+
+} // namespace pass_helpers
+} // namespace gimpl
+} // name
+
+#endif // OPENCV_GAPI_COMPILER_PASSES_HELPERS_HPP
diff --git a/modules/gapi/src/compiler/passes/islands.cpp b/modules/gapi/src/compiler/passes/islands.cpp
new file mode 100644 (file)
index 0000000..8773f2c
--- /dev/null
@@ -0,0 +1,231 @@
+// 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
+
+
+#include <sstream>
+#include <stack>
+#include <ade/util/chain_range.hpp>
+#include <ade/graph.hpp>
+
+#include "compiler/gmodel.hpp"
+#include "compiler/passes/passes.hpp"
+
+namespace
+{
+    bool is_within_same_island(const cv::gimpl::GModel::Graph &gr,
+                               const ade::NodeHandle          &dataNode,
+                               const std::string              &island)
+    {
+        // A data node is within the same island as it's reader node
+        // if and only if data object's producer island (if there's a producer)
+        // is the same as the specified one.
+        //
+        // An object may have only a single producer, but multiple consumers,
+        // and these consumers may be assigned to different Islands.
+        // Since "initIslands" traversal direction is op-to-args, i.e. reverse,
+        // a single Data object may be visited twice during Islands initialization.
+        //
+        // In this case, Data object is part of Island A if and only if:
+        // - Data object's producer is part of Island A,
+        // - AND any of Data obejct's consumers is part of Island A.
+        //
+        //   Op["island0"] --> Data[ ? ] --> Op["island0"]
+        //                       :
+        //                       '---------> Op["island1"]
+        //
+        // In the above example, Data object is assigned to "island0" as
+        // it is surrounded by operations assigned to "island0"
+
+        using namespace cv::gimpl;
+
+        if (   gr.metadata(dataNode).contains<Island>()
+            && gr.metadata(dataNode).get<Island>().island != island)
+            return false;
+
+        if (dataNode->inNodes().empty())
+            return false;
+
+        GAPI_Assert(dataNode->inNodes().size() == 1u);
+        const auto prod_h = dataNode->inNodes().front();
+
+        // FIXME: ADE should have something like get_or<> or get<>(default)
+        GAPI_Assert(gr.metadata(prod_h).get<NodeType>().t == NodeType::OP);
+        return     (   gr.metadata(prod_h).contains<Island>()
+                    && gr.metadata(prod_h).get<Island>().island == island)
+                    && (ade::util::any_of(dataNode->outNodes(), [&](ade::NodeHandle cons_h)
+                    {
+                        return (   gr.metadata(cons_h).contains<Island>()
+                                && gr.metadata(cons_h).get<Island>().island == island);
+                    }));
+    }
+} // anonymous namespace
+
+// Initially only Operations have Island tag. This pass adds Island tag
+// to all data objects within an Island.
+// A data object is considered within an Island if and only if
+// its reader and writer are assigned to this Island (see above).
+void cv::gimpl::passes::initIslands(ade::passes::PassContext &ctx)
+{
+    GModel::Graph gr(ctx.graph);
+    for (const auto &nh : gr.nodes())
+    {
+        if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)
+        {
+            if (gr.metadata(nh).contains<Island>())
+            {
+                const auto island = gr.metadata(nh).get<Island>().island;
+
+                // It is enough to check only input nodes
+                for (const auto &in_data_node : nh->inNodes())
+                {
+                    if (is_within_same_island(gr, in_data_node, island))
+                    {
+                        gr.metadata(in_data_node).set(Island{island});
+                    }
+                } // for(in_data_node)
+            } // if (contains<Island>)
+        } // if (OP)
+    } // for (nodes)
+}
+
+// There should be no multiple (disconnected) islands with the same name.
+// This may occur if user assigns the same islands name to multiple ranges
+// in the graph.
+// FIXME: How it could be avoided on an earlier stage?
+void cv::gimpl::passes::checkIslands(ade::passes::PassContext &ctx)
+{
+    GModel::ConstGraph gr(ctx.graph);
+
+    // The algorithm is teh following:
+    //
+    // 1. Put all Tagged nodes (both Operations and Data) into a set
+    // 2. Initialize Visited set as (empty)
+    // 3. Initialize Traversal stack as (empty)
+    // 4. Initialize Islands map (String -> Integer) as (empty)
+    // 5. For every Tagged node from a set
+    //    a. Skip if it is Visited
+    //    b. For every input/output node:
+    //       * if it is tagged with the same island:
+    //         - add it to Traversal stack
+    //         - remove from Tagged nodes if it is t
+    //    c. While (stack is not empty):
+    //       - Take a node from Stack
+    //       - Repeat (b)
+    //    d. Increment Islands map [this island] by 1
+    //
+    //
+    // If whatever Island has counter is more than 1, it is a disjoint
+    // one (i.e. there's two islands with the same name).
+
+    using node_set = std::unordered_set
+         < ade::NodeHandle
+         , ade::HandleHasher<ade::Node>
+         >;
+    node_set tagged_nodes;
+    node_set visited_tagged_nodes;
+    std::unordered_map<std::string, int> island_counters;
+
+    for (const auto &nh : gr.nodes())
+    {
+        if (gr.metadata(nh).contains<Island>())
+        {
+            tagged_nodes.insert(nh);
+            island_counters[gr.metadata(nh).get<Island>().island] = 0;
+        }
+    }
+
+    // Make a copy to allow list modifications during traversal
+    for (const auto &tagged_nh : tagged_nodes)
+    {
+        if (visited_tagged_nodes.end() != ade::util::find(visited_tagged_nodes, tagged_nh))
+            continue;
+
+        // Run the recursive traversal process as described in 5/a-d.
+        // This process is like a flood-fill traversal for island.
+        // If there's to distint successful flood-fills happened for the same island
+        // name, there are two islands with this name.
+        std::stack<ade::NodeHandle> stack;
+        stack.push(tagged_nh);
+
+        while (!stack.empty())
+        {
+            const auto this_nh = stack.top();
+            stack.pop();
+
+            // Since _this_ node is visited, it is a part of processed island
+            // so mark it as visited to skip in other recursive processes
+            visited_tagged_nodes.insert(this_nh);
+
+            GAPI_DbgAssert(gr.metadata(this_nh).contains<Island>());
+            GAPI_DbgAssert(   gr.metadata(this_nh  ).get<Island>().island
+                         == gr.metadata(tagged_nh).get<Island>().island);
+            const auto &this_island = gr.metadata(this_nh).get<Island>().island;
+
+            for (const auto neighbor_nh : ade::util::chain(this_nh->inNodes(), this_nh->outNodes()))
+            {
+                if (   gr.metadata(neighbor_nh).contains<Island>()
+                    && gr.metadata(neighbor_nh).get<Island>().island == this_island
+                    && !visited_tagged_nodes.count(neighbor_nh))
+                {
+                    stack.push(neighbor_nh);
+                }
+            } // for (neighbor)
+        } // while (stack)
+
+        // Flood-fill is over, now increment island counter for this island
+        island_counters[gr.metadata(tagged_nh).get<Island>().island]++;
+    } // for(tagged)
+
+    bool check_failed = false;
+    std::stringstream ss;
+    for (const auto &ic : island_counters)
+    {
+        GAPI_Assert(ic.second > 0);
+        if (ic.second > 1)
+        {
+            check_failed = true;
+            ss << "\"" << ic.first << "\"(" << ic.second << ") ";
+        }
+    }
+    if (check_failed)
+    {
+        util::throw_error
+            (std::logic_error("There are multiple distinct islands "
+                              "with the same name: [" + ss.str() + "], "
+                              "please check your cv::gapi::island() parameters!"));
+    }
+}
+
+void cv::gimpl::passes::checkIslandsContent(ade::passes::PassContext &ctx)
+{
+    GModel::ConstGraph gr(ctx.graph);
+    std::unordered_map<std::string, cv::gapi::GBackend> backends_of_islands;
+    for (const auto& nh : gr.nodes())
+    {
+        if (NodeType::OP == gr.metadata(nh).get<NodeType>().t &&
+            gr.metadata(nh).contains<Island>())
+        {
+            const auto island      = gr.metadata(nh).get<Island>().island;
+            auto island_backend_it = backends_of_islands.find(island);
+            const auto& op         = gr.metadata(nh).get<Op>();
+
+            if (island_backend_it != backends_of_islands.end())
+            {
+                // Check that backend of the operation coincides with the backend of the island
+                // Backend of the island is determined by the backend of the first operation from this island
+                if (island_backend_it->second != op.backend)
+                {
+                    util::throw_error(std::logic_error(island + " contains kernels " + op.k.name +
+                                                       " with different backend"));
+                }
+            }
+            else
+            {
+                backends_of_islands.emplace(island, op.backend);
+            }
+        }
+    }
+}
diff --git a/modules/gapi/src/compiler/passes/kernels.cpp b/modules/gapi/src/compiler/passes/kernels.cpp
new file mode 100644 (file)
index 0000000..ad8555b
--- /dev/null
@@ -0,0 +1,155 @@
+// 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
+
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+#include <ade/graph.hpp>
+#include <ade/passes/check_cycles.hpp>
+
+#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
+
+#include "compiler/gmodel.hpp"
+#include "compiler/passes/passes.hpp"
+
+#include "api/gbackend_priv.hpp"
+#include "backends/common/gbackend.hpp"
+#include "compiler/gmodelbuilder.hpp"
+#include "logger.hpp"    // GAPI_LOG
+
+namespace
+{
+    struct ImplInfo
+    {
+        cv::GKernelImpl impl;
+        cv::GArgs       in_args;
+    };
+
+    // Generaly the algorithm is following
+    //
+    // 1. Get GCompoundKernel implementation
+    // 2. Create GCompoundContext
+    // 3. Run GCompoundKernel with GCompoundContext
+    // 4. Build subgraph from imputs/outputs GCompoundKernel
+    // 5. Replace compound node to subgraph
+
+    void expand(ade::Graph& g, ade::NodeHandle nh, const ImplInfo& impl_info)
+    {
+        cv::gimpl::GModel::Graph gr(g);
+        auto compound_impl = cv::util::any_cast<cv::detail::GCompoundKernel>(impl_info.impl.opaque);
+
+        // GCompoundContext instantiates its own objects
+        // in accordance with the RcDescs from in_args
+        cv::detail::GCompoundContext context(impl_info.in_args);
+        compound_impl.apply(context);
+
+        cv::GProtoArgs ins, outs;
+        ins.reserve(context.m_args.size());
+        outs.reserve(context.m_results.size());
+
+        // Inputs can be non-dynamic types.
+        // Such inputs are not used when building a graph
+        for (const auto& arg : context.m_args)
+        {
+            if (cv::gimpl::proto::is_dynamic(arg))
+            {
+                ins.emplace_back(cv::gimpl::proto::rewrap(arg));
+            }
+        }
+
+        ade::util::transform(context.m_results, std::back_inserter(outs), &cv::gimpl::proto::rewrap);
+
+        cv::gimpl::GModelBuilder builder(g);
+
+        // Build the subgraph graph which will need to replace the compound node
+        const auto& proto_slots = builder.put(ins, outs);
+
+        const auto& in_nhs  = std::get<2>(proto_slots);
+        const auto& out_nhs = std::get<3>(proto_slots);
+
+        auto sorted_in_nhs  = cv::gimpl::GModel::orderedInputs(gr, nh);
+        auto sorted_out_nhs = cv::gimpl::GModel::orderedOutputs(gr, nh);
+
+        // Reconnect expanded kernels from graph data objects
+        // to subgraph data objects, then drop that graph data objects
+        for (const auto& it : ade::util::zip(in_nhs, sorted_in_nhs))
+        {
+            const auto& subgr_in_nh = std::get<0>(it);
+            const auto& comp_in_nh  = std::get<1>(it);
+
+            cv::gimpl::GModel::redirectReaders(gr, subgr_in_nh, comp_in_nh);
+            gr.erase(subgr_in_nh);
+        }
+
+        gr.erase(nh);
+
+        for (const auto& it : ade::util::zip(out_nhs, sorted_out_nhs))
+        {
+            const auto& subgr_out_nh = std::get<0>(it);
+            const auto& comp_out_nh  = std::get<1>(it);
+
+            cv::gimpl::GModel::redirectWriter(gr, subgr_out_nh, comp_out_nh);
+            gr.erase(subgr_out_nh);
+        }
+    }
+}
+// This pass, given the kernel package, selects a kernel implementation
+// for every operation in the graph
+void cv::gimpl::passes::resolveKernels(ade::passes::PassContext   &ctx,
+                                       const gapi::GKernelPackage &kernels,
+                                       const gapi::GLookupOrder   &order)
+{
+    std::unordered_set<cv::gapi::GBackend> active_backends;
+
+    GModel::Graph gr(ctx.graph);
+    for (const auto &nh : gr.nodes())
+    {
+        if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)
+        {
+            auto &op = gr.metadata(nh).get<Op>();
+            cv::gapi::GBackend selected_backend;
+            cv::GKernelImpl    selected_impl;
+            std::tie(selected_backend, selected_impl)
+                = kernels.lookup(op.k.name, order);
+
+            selected_backend.priv().unpackKernel(ctx.graph, nh, selected_impl);
+            op.backend = selected_backend;
+            active_backends.insert(selected_backend);
+        }
+    }
+    gr.metadata().set(ActiveBackends{active_backends});
+}
+
+void cv::gimpl::passes::expandKernels(ade::passes::PassContext &ctx, const gapi::GKernelPackage &kernels)
+{
+    GModel::Graph gr(ctx.graph);
+
+    // Repeat the loop while there are compound kernels.
+    // Restart procedure after every successfull unrolling
+    bool has_compound_kernel = true;
+    while (has_compound_kernel)
+    {
+        has_compound_kernel = false;
+        for (const auto& nh : gr.nodes())
+        {
+            if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)
+            {
+                const auto& op = gr.metadata(nh).get<Op>();
+
+                cv::gapi::GBackend selected_backend;
+                cv::GKernelImpl    selected_impl;
+                std::tie(selected_backend, selected_impl) = kernels.lookup(op.k.name);
+
+                if (selected_backend == cv::gapi::compound::backend())
+                {
+                    has_compound_kernel = true;
+                    expand(ctx.graph, nh, ImplInfo{selected_impl, op.args});
+                    break;
+                }
+            }
+        }
+    }
+    GAPI_LOG_INFO(NULL, "Final graph: " << ctx.graph.nodes().size() << " nodes" << std::endl);
+}
diff --git a/modules/gapi/src/compiler/passes/meta.cpp b/modules/gapi/src/compiler/passes/meta.cpp
new file mode 100644 (file)
index 0000000..0be6cac
--- /dev/null
@@ -0,0 +1,123 @@
+// 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
+
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+#include <ade/graph.hpp>
+#include <ade/passes/check_cycles.hpp>
+
+#include "compiler/gmodel.hpp"
+#include "compiler/passes/passes.hpp"
+#include "logger.hpp"    // GAPI_LOG
+
+
+// Iterate over all nodes and initialize meta of objects taken from the
+// outside (i.e., computation input/output arguments)
+void cv::gimpl::passes::initMeta(ade::passes::PassContext &ctx, const GMetaArgs &metas)
+{
+    GModel::Graph gr(ctx.graph);
+
+    const auto &proto = gr.metadata().get<Protocol>();
+
+    for (const auto& it : ade::util::indexed(proto.in_nhs))
+    {
+        auto& data = gr.metadata(ade::util::value(it)).get<Data>();
+        data.meta = metas.at(ade::util::index(it));
+    }
+}
+
+// Iterate over all operations in the topological order, trigger kernels
+// validate() function, update output objects metadata.
+void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx)
+{
+    // FIXME: ADE pass dependency on topo_sort?
+    // FIXME: ADE pass dependency on initMeta?
+    GModel::Graph gr(ctx.graph);
+
+    const auto sorted = gr.metadata().get<ade::passes::TopologicalSortData>() ;
+    for (const auto &nh : sorted.nodes())
+    {
+        if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)
+        {
+            const auto& op = gr.metadata(nh).get<Op>();
+            GAPI_Assert(op.k.outMeta != nullptr);
+
+            // Prepare operation's input metadata vector
+            // Note that it's size is usually different from nh.inEdges.size(),
+            // and its element count is equal to operation's arguments count.
+            GMetaArgs input_meta_args(op.args.size());
+
+            // Iterate through input edges, update input_meta_args's slots
+            // appropriately. Not all of them will be updated due to (see above).
+            GAPI_Assert(nh->inEdges().size() > 0);
+            for (const auto &in_eh : nh->inEdges())
+            {
+                const auto& input_port = gr.metadata(in_eh).get<Input>().port;
+                const auto& input_nh   = in_eh->srcNode();
+                GAPI_Assert(gr.metadata(input_nh).get<NodeType>().t == NodeType::DATA);
+
+                const auto& input_meta = gr.metadata(input_nh).get<Data>().meta;
+                if (util::holds_alternative<util::monostate>(input_meta))
+                {
+                    // No meta in an input argument - a fatal error
+                    // (note graph is traversed here in topoligcal order)
+                  util::throw_error(std::logic_error("Fatal: input object's metadata "
+                                                 "not found!"));
+                    // FIXME: Add more details!!!
+                }
+                input_meta_args.at(input_port) = input_meta;
+            }
+            // Now ask kernel for it's output meta.
+            // Resulting out_args may have a larger size than op.outs, since some
+            // outputs could stay unused (unconnected)
+            const GMetaArgs out_metas = op.k.outMeta(input_meta_args, op.args);
+
+            // Walk through operation's outputs, update meta of output objects
+            // appropriately
+            GAPI_Assert(nh->outEdges().size() > 0);
+            for (const auto &out_eh : nh->outEdges())
+            {
+                const auto &output_port = gr.metadata(out_eh).get<Output>().port;
+                const auto &output_nh   = out_eh->dstNode();
+                GAPI_Assert(gr.metadata(output_nh).get<NodeType>().t == NodeType::DATA);
+
+                auto       &output_meta = gr.metadata(output_nh).get<Data>().meta;
+                if (!util::holds_alternative<util::monostate>(output_meta))
+                {
+                    GAPI_LOG_INFO(NULL,
+                                  "!!! Output object has an initialized meta - "
+                                  "how it is possible today?" << std::endl; );
+                    if (output_meta != out_metas.at(output_port))
+                    {
+                      util::throw_error(std::logic_error("Fatal: meta mismatch"));
+                        // FIXME: New exception type?
+                        // FIXME: More details!
+                    }
+                }
+                // Store meta in graph
+                output_meta = out_metas.at(output_port);
+            }
+        } // if(OP)
+    } // for(sorted)
+}
+
+// After all metadata in graph is infered, store a vector of inferred metas
+// for computation output values.
+void cv::gimpl::passes::storeResultingMeta(ade::passes::PassContext &ctx)
+{
+    GModel::Graph gr(ctx.graph);
+
+    const auto &proto = gr.metadata().get<Protocol>();
+    GMetaArgs output_metas(proto.out_nhs.size());
+
+    for (const auto& it : ade::util::indexed(proto.out_nhs))
+    {
+        auto& data = gr.metadata(ade::util::value(it)).get<Data>();
+        output_metas[ade::util::index(it)] = data.meta;
+    }
+
+    gr.metadata().set(OutputMeta{output_metas});
+}
diff --git a/modules/gapi/src/compiler/passes/passes.hpp b/modules/gapi/src/compiler/passes/passes.hpp
new file mode 100644 (file)
index 0000000..d73653b
--- /dev/null
@@ -0,0 +1,58 @@
+// 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_COMPILER_PASSES_HPP
+#define OPENCV_GAPI_COMPILER_PASSES_HPP
+
+#include <ostream>
+#include <ade/passes/pass_base.hpp>
+
+#include "opencv2/gapi/garg.hpp"
+#include "opencv2/gapi/gcommon.hpp"
+
+// Forward declarations - external
+namespace ade {
+    class Graph;
+
+    namespace passes {
+        struct PassContext;
+    }
+}
+
+namespace cv {
+
+namespace gimpl { namespace passes {
+
+void dumpDot(const ade::Graph &g, std::ostream& os);
+void dumpDot(ade::passes::PassContext &ctx, std::ostream& os);
+void dumpDotStdout(ade::passes::PassContext &ctx);
+void dumpGraph(ade::passes::PassContext     &ctx, const std::string& dump_path);
+void dumpDotToFile(ade::passes::PassContext &ctx, const std::string& dump_path);
+
+void initIslands(ade::passes::PassContext &ctx);
+void checkIslands(ade::passes::PassContext &ctx);
+void checkIslandsContent(ade::passes::PassContext &ctx);
+
+void initMeta(ade::passes::PassContext &ctx, const GMetaArgs &metas);
+void inferMeta(ade::passes::PassContext &ctx);
+void storeResultingMeta(ade::passes::PassContext &ctx);
+
+void expandKernels(ade::passes::PassContext &ctx,
+                   const gapi::GKernelPackage& kernels);
+
+void resolveKernels(ade::passes::PassContext       &ctx,
+                    const gapi::GKernelPackage &kernels,
+                    const gapi::GLookupOrder   &order);
+
+void fuseIslands(ade::passes::PassContext &ctx);
+void syncIslandTags(ade::passes::PassContext &ctx);
+
+}} // namespace gimpl::passes
+
+} // namespace cv
+
+#endif // OPENCV_GAPI_COMPILER_PASSES_HPP
diff --git a/modules/gapi/src/compiler/transactions.hpp b/modules/gapi/src/compiler/transactions.hpp
new file mode 100644 (file)
index 0000000..6b1800f
--- /dev/null
@@ -0,0 +1,147 @@
+// 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_COMPILER_TRANSACTIONS_HPP
+#define OPENCV_GAPI_COMPILER_TRANSACTIONS_HPP
+
+#include <algorithm> // find_if
+#include <functional>
+#include <list>
+
+#include <ade/graph.hpp>
+
+#include "opencv2/core/base.hpp"
+
+enum class Direction: int {Invalid, In, Out};
+
+////////////////////////////////////////////////////////////////////////////
+////
+// TODO: Probably it can be moved to ADE
+
+namespace Change
+{
+    struct Base
+    {
+        virtual void commit  (ade::Graph & ) {};
+        virtual void rollback(ade::Graph & ) {};
+        virtual ~Base() = default;
+    };
+
+    class NodeCreated final: public Base
+    {
+        ade::NodeHandle m_node;
+    public:
+        explicit NodeCreated(const ade::NodeHandle &nh) : m_node(nh) {}
+        virtual void rollback(ade::Graph &g) override { g.erase(m_node); }
+    };
+
+    // NB: Drops all metadata stored in the EdgeHandle,
+    // which is not restored even in the rollback
+
+    // FIXME: either add a way for users to preserve meta manually
+    // or extend ADE to manipulate with meta such way
+    class DropLink final: public Base
+    {
+        ade::NodeHandle m_node;
+        Direction       m_dir;
+
+        ade::NodeHandle m_sibling;
+
+    public:
+        DropLink(ade::Graph &g,
+                 const ade::NodeHandle &node,
+                 const ade::EdgeHandle &edge)
+            : m_node(node), m_dir(node == edge->srcNode()
+                                  ? Direction::Out
+                                  : Direction::In)
+        {
+            m_sibling = (m_dir == Direction::In
+                         ? edge->srcNode()
+                         : edge->dstNode());
+            g.erase(edge);
+        }
+
+        virtual void rollback(ade::Graph &g) override
+        {
+            switch(m_dir)
+            {
+            case Direction::In:  g.link(m_sibling, m_node); break;
+            case Direction::Out: g.link(m_node, m_sibling); break;
+            default: CV_Assert(false);
+            }
+        }
+    };
+
+    class NewLink final: public Base
+    {
+        ade::EdgeHandle m_edge;
+
+    public:
+        NewLink(ade::Graph &g,
+                  const ade::NodeHandle &prod,
+                  const ade::NodeHandle &cons)
+            : m_edge(g.link(prod, cons))
+        {
+        }
+
+        virtual void rollback(ade::Graph &g) override
+        {
+            g.erase(m_edge);
+        }
+    };
+
+    class DropNode final: public Base
+    {
+        ade::NodeHandle m_node;
+
+    public:
+        explicit DropNode(const ade::NodeHandle &nh)
+            : m_node(nh)
+        {
+            // According to the semantic, node should be disconnected
+            // manually before it is dropped
+            CV_Assert(m_node->inEdges().size()  == 0);
+            CV_Assert(m_node->outEdges().size() == 0);
+        }
+
+        virtual void commit(ade::Graph &g) override
+        {
+            g.erase(m_node);
+        }
+    };
+
+    class List
+    {
+        std::list< std::unique_ptr<Base> > m_changes;
+
+    public:
+        template<typename T, typename ...Args>
+        void enqueue(Args&&... args)
+        {
+            std::unique_ptr<Base> p(new T(args...));
+            m_changes.push_back(std::move(p));
+        }
+
+        void commit(ade::Graph &g)
+        {
+            // Commit changes in the forward order
+            for (auto& ch : m_changes) ch->commit(g);
+        }
+
+        void rollback(ade::Graph &g)
+        {
+            // Rollback changes in the reverse order
+            for (auto it = m_changes.rbegin(); it != m_changes.rend(); ++it)
+            {
+                (*it)->rollback(g);
+            }
+        }
+    };
+} // namespace Change
+////////////////////////////////////////////////////////////////////////////
+
+#endif // OPENCV_GAPI_COMPILER_TRANSACTIONS_HPP
diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp
new file mode 100644 (file)
index 0000000..4709f9d
--- /dev/null
@@ -0,0 +1,211 @@
+// 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
+
+
+#include <iostream>
+
+#include <ade/util/zip_range.hpp>
+
+#include "opencv2/core/types.hpp"
+#include "executor/gexecutor.hpp"
+
+cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
+    : m_orig_graph(std::move(g_model))
+    , m_island_graph(GModel::Graph(*m_orig_graph).metadata()
+                     .get<IslandModel>().model)
+    , m_gm(*m_orig_graph)
+    , m_gim(*m_island_graph)
+{
+    // NB: Right now GIslandModel is acyclic, so for a naive execution,
+    // simple unrolling to a list of triggers is enough
+
+    // Naive execution model is similar to current CPU (OpenCV) plugin
+    // execution model:
+    // 1. Allocate all internal resources first (NB - CPU plugin doesn't do it)
+    // 2. Put input/output GComputation arguments to the storage
+    // 3. For every Island, prepare vectors of input/output parameter descs
+    // 4. Iterate over a list of operations (sorted in the topological order)
+    // 5. For every operation, form a list of input/output data objects
+    // 6. Run GIslandExecutable
+    // 7. writeBack
+
+    m_ops.reserve(m_gim.nodes().size());
+    auto sorted = m_gim.metadata().get<ade::passes::TopologicalSortData>();
+    for (auto nh : sorted.nodes())
+    {
+        switch (m_gim.metadata(nh).get<NodeKind>().k)
+        {
+        case NodeKind::ISLAND:
+            {
+                std::vector<RcDesc> input_rcs;
+                std::vector<RcDesc> output_rcs;
+                input_rcs.reserve(nh->inNodes().size());
+                output_rcs.reserve(nh->outNodes().size());
+
+                auto xtract = [&](ade::NodeHandle slot_nh, std::vector<RcDesc> &vec) {
+                    const auto orig_data_nh
+                        = m_gim.metadata(slot_nh).get<DataSlot>().original_data_node;
+                    const auto &orig_data_info
+                        = m_gm.metadata(orig_data_nh).get<Data>();
+                    vec.emplace_back(RcDesc{ orig_data_info.rc
+                                           , orig_data_info.shape
+                                           , orig_data_info.ctor});
+                };
+                // (3)
+                for (auto in_slot_nh  : nh->inNodes())  xtract(in_slot_nh,  input_rcs);
+                for (auto out_slot_nh : nh->outNodes()) xtract(out_slot_nh, output_rcs);
+                m_ops.emplace_back(OpDesc{ std::move(input_rcs)
+                                         , std::move(output_rcs)
+                                         , m_gim.metadata(nh).get<IslandExec>().object});
+            }
+            break;
+
+        case NodeKind::SLOT:
+            {
+                const auto orig_data_nh
+                    = m_gim.metadata(nh).get<DataSlot>().original_data_node;
+                // (1)
+                initResource(orig_data_nh);
+                m_slots.emplace_back(DataDesc{nh, orig_data_nh});
+            }
+            break;
+
+        default:
+            GAPI_Assert(false);
+            break;
+        } // switch(kind)
+    } // for(gim nodes)
+}
+
+void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh)
+{
+    const Data &d = m_gm.metadata(orig_nh).get<Data>();
+
+    if (   d.storage != Data::Storage::INTERNAL
+        && d.storage != Data::Storage::CONST)
+        return;
+
+    // INTERNALS+CONST only! no need to allocate/reset output objects
+    // to as it is bound externally (e.g. already in the m_res)
+
+    switch (d.shape)
+    {
+    case GShape::GMAT:
+        {
+            const auto desc = util::get<cv::GMatDesc>(d.meta);
+            const auto type = CV_MAKETYPE(desc.depth, desc.chan);
+            m_res.slot<cv::gapi::own::Mat>()[d.rc].create(desc.size, type);
+        }
+        break;
+
+    case GShape::GSCALAR:
+        if (d.storage == Data::Storage::CONST)
+        {
+            auto rc = RcDesc{d.rc, d.shape, d.ctor};
+            magazine::bindInArg(m_res, rc, m_gm.metadata(orig_nh).get<ConstValue>().arg);
+        }
+        break;
+
+    case GShape::GARRAY:
+        // Constructed on Reset, do nothing here
+        break;
+
+    default:
+        GAPI_Assert(false);
+    }
+}
+
+void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
+{
+    // (2)
+    const auto proto = m_gm.metadata().get<Protocol>();
+
+    // Basic check if input/output arguments are correct
+    // FIXME: Move to GCompiled (do once for all GExecutors)
+    if (proto.inputs.size() != args.inObjs.size()) // TODO: Also check types
+    {
+        util::throw_error(std::logic_error
+                          ("Computation's input protocol doesn\'t "
+                           "match actual arguments!"));
+    }
+    if (proto.outputs.size() != args.outObjs.size()) // TODO: Also check types
+    {
+        util::throw_error(std::logic_error
+                          ("Computation's output protocol doesn\'t "
+                           "match actual arguments!"));
+    }
+
+    namespace util = ade::util;
+
+    //ensure that output Mat parameters are correctly allocated
+    for (auto index : util::iota(proto.out_nhs.size()) )     //FIXME: avoid copy of NodeHandle and GRunRsltComp ?
+    {
+        auto& nh = proto.out_nhs.at(index);
+        const Data &d = m_gm.metadata(nh).get<Data>();
+        if (d.shape == GShape::GMAT)
+        {
+            using cv::util::get;
+            const auto desc = get<cv::GMatDesc>(d.meta);
+            const auto type = CV_MAKETYPE(desc.depth, desc.chan);
+            auto& out_mat   = *get<cv::Mat*>(args.outObjs.at(index));
+            out_mat.create(cv::gapi::own::to_ocv(desc.size), type);
+        }
+    }
+    // Update storage with user-passed objects
+    for (auto it : ade::util::zip(ade::util::toRange(proto.inputs),
+                                  ade::util::toRange(args.inObjs)))
+    {
+        magazine::bindInArg(m_res, std::get<0>(it), std::get<1>(it));
+    }
+    for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
+                                  ade::util::toRange(args.outObjs)))
+    {
+        magazine::bindOutArg(m_res, std::get<0>(it), std::get<1>(it));
+    }
+
+    // Reset internal data
+    for (auto &sd : m_slots)
+    {
+        const auto& data = m_gm.metadata(sd.data_nh).get<Data>();
+        magazine::resetInternalData(m_res, data);
+    }
+
+    // Run the script
+    for (auto &op : m_ops)
+    {
+        // (5)
+        using InObj  = GIslandExecutable::InObj;
+        using OutObj = GIslandExecutable::OutObj;
+        std::vector<InObj>  in_objs;
+        std::vector<OutObj> out_objs;
+        in_objs.reserve (op.in_objects.size());
+        out_objs.reserve(op.out_objects.size());
+
+        for (const auto &rc : op.in_objects)
+        {
+            in_objs.emplace_back(InObj{rc, magazine::getArg(m_res, rc)});
+        }
+        for (const auto &rc : op.out_objects)
+        {
+            out_objs.emplace_back(OutObj{rc, magazine::getObjPtr(m_res, rc)});
+        }
+
+        // (6)
+        op.isl_exec->run(std::move(in_objs), std::move(out_objs));
+    }
+
+    // (7)
+    for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
+                                  ade::util::toRange(args.outObjs)))
+    {
+        magazine::writeBack(m_res, std::get<0>(it), std::get<1>(it));
+    }
+}
+
+const cv::gimpl::GModel::Graph& cv::gimpl::GExecutor::model() const
+{
+    return m_gm;
+}
diff --git a/modules/gapi/src/executor/gexecutor.hpp b/modules/gapi/src/executor/gexecutor.hpp
new file mode 100644 (file)
index 0000000..47c5582
--- /dev/null
@@ -0,0 +1,94 @@
+// 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_GEXECUTOR_HPP
+#define OPENCV_GAPI_GEXECUTOR_HPP
+
+#include <memory> // unique_ptr, shared_ptr
+
+#include <utility> // tuple, required by magazine
+#include <unordered_map> // required by magazine
+
+#include <ade/graph.hpp>
+
+#include "backends/common/gbackend.hpp"
+
+namespace cv {
+namespace gimpl {
+
+// Graph-level executor interface.
+//
+// This class specifies API for a "super-executor" which orchestrates
+// the overall Island graph execution.
+//
+// Every Island (subgraph) execution is delegated to a particular
+// backend and is done opaquely to the GExecutor.
+//
+// Inputs to a GExecutor instance are:
+// - GIslandModel - a high-level graph model which may be seen as a
+//   "procedure" to execute.
+//   - GModel - a low-level graph of operations (from which a GIslandModel
+//     is projected)
+// - GComputation runtime arguments - vectors of input/output objects
+//
+// Every GExecutor is responsible for
+// a. Maintaining non-island (intermediate) data objects within graph
+// b. Providing GIslandExecutables with input/output data according to
+//    their protocols
+// c. Triggering execution of GIslandExecutables when task/data dependencies
+//    are met.
+//
+// By default G-API stores all data on host, and cross-Island
+// exchange happens via host buffers (and CV data objects).
+//
+// Today's exchange data objects are:
+// - cv::Mat               - for image buffers
+// - cv::Scalar            - for single values (with up to four components inside)
+// - cv::detail::VectorRef - an untyped wrapper over std::vector<T>
+//
+
+class GExecutor
+{
+protected:
+    std::unique_ptr<ade::Graph> m_orig_graph;
+    std::shared_ptr<ade::Graph> m_island_graph;
+
+    cv::gimpl::GModel::Graph       m_gm;  // FIXME: make const?
+    cv::gimpl::GIslandModel::Graph m_gim; // FIXME: make const?
+
+    // FIXME: Naive executor details are here for now
+    // but then it should be moved to another place
+    struct OpDesc
+    {
+        std::vector<RcDesc> in_objects;
+        std::vector<RcDesc> out_objects;
+        std::shared_ptr<GIslandExecutable> isl_exec;
+    };
+    std::vector<OpDesc> m_ops;
+
+    struct DataDesc
+    {
+        ade::NodeHandle slot_nh;
+        ade::NodeHandle data_nh;
+    };
+    std::vector<DataDesc> m_slots;
+
+    Mag m_res;
+
+    void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc?
+
+public:
+    explicit GExecutor(std::unique_ptr<ade::Graph> &&g_model);
+    void run(cv::gimpl::GRuntimeArgs &&args);
+
+    const GModel::Graph& model() const; // FIXME: make it ConstGraph?
+};
+
+} // namespace gimpl
+} // namespace cv
+
+#endif // OPENCV_GAPI_GEXECUTOR_HPP
diff --git a/modules/gapi/src/logger.hpp b/modules/gapi/src/logger.hpp
new file mode 100644 (file)
index 0000000..047f6e4
--- /dev/null
@@ -0,0 +1,24 @@
+// 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_LOGGER_HPP__
+#define __OPENCV_GAPI_LOGGER_HPP__
+
+#if 1
+#include "opencv2/core/utils/logger.hpp"
+
+#define GAPI_LOG_INFO(tag, ...)    CV_LOG_INFO(tag, __VA_ARGS__)
+#define GAPI_LOG_WARNING(tag, ...) CV_LOG_WARNING(tag, __VA_ARGS__)
+
+#else
+#define GAPI_LOG_INFO(tag, ...)
+#define GAPI_LOG_WARNING(tag, ...)
+
+#endif
+
+
+#endif // __OPENCV_GAPI_LOGGER_HPP__
diff --git a/modules/gapi/src/precomp.hpp b/modules/gapi/src/precomp.hpp
new file mode 100644 (file)
index 0000000..ad15bc4
--- /dev/null
@@ -0,0 +1,19 @@
+// 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_PRECOMP_HPP__
+#define __OPENCV_GAPI_PRECOMP_HPP__
+
+#include "opencv2/core.hpp"
+#include "opencv2/imgproc.hpp"
+
+#include "opencv2/gapi.hpp"
+#include "opencv2/gapi/gkernel.hpp"
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+
+#endif // __OPENCV_GAPI_PRECOMP_HPP__
diff --git a/modules/gapi/test/common/gapi_compoundkernel_tests.cpp b/modules/gapi/test/common/gapi_compoundkernel_tests.cpp
new file mode 100644 (file)
index 0000000..1f5de7a
--- /dev/null
@@ -0,0 +1,500 @@
+// 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
+
+
+// FIXME: move out from Common
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/cpu/core.hpp"
+
+#include <ade/util/algorithm.hpp>
+
+namespace opencv_test
+{
+namespace
+{
+    G_TYPED_KERNEL(GCompoundDoubleAddC, <GMat(GMat, GScalar)>, "org.opencv.test.compound_double_addC")
+    {
+        static GMatDesc outMeta(GMatDesc in, GScalarDesc) { return in; }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundDoubleAddCImpl, GCompoundDoubleAddC)
+    {
+        static GMat expand(cv::GMat in, cv::GScalar s)
+        {
+            return cv::gapi::addC(cv::gapi::addC(in, s), s);
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundAddC, <GMat(GMat, GScalar)>, "org.opencv.test.compound_addC")
+    {
+        static GMatDesc outMeta(GMatDesc in, GScalarDesc) { return in; }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundAddCImpl, GCompoundAddC)
+    {
+        static GMat expand(cv::GMat in, cv::GScalar s)
+        {
+            return cv::gapi::addC(in, s);
+        }
+    };
+
+    using GMat3 = std::tuple<GMat,GMat,GMat>;
+    using GMat2 = std::tuple<GMat,GMat>;
+
+    G_TYPED_KERNEL_M(GCompoundMergeWithSplit, <GMat3(GMat, GMat, GMat)>, "org.opencv.test.compound_merge_split")
+    {
+        static std::tuple<GMatDesc,GMatDesc,GMatDesc> outMeta(GMatDesc a, GMatDesc b, GMatDesc c)
+        {
+            return std::make_tuple(a, b, c);
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundMergeWithSplitImpl, GCompoundMergeWithSplit)
+    {
+        static GMat3 expand(cv::GMat a, cv::GMat b, cv::GMat c)
+        {
+            return cv::gapi::split3(cv::gapi::merge3(a, b, c));
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundAddWithAddC, <GMat(GMat, GMat, GScalar)>, "org.opencv.test.compound_add_with_addc")
+    {
+        static GMatDesc outMeta(GMatDesc in, GMatDesc, GScalarDesc)
+        {
+            return in;
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundAddWithAddCImpl, GCompoundAddWithAddC)
+    {
+        static GMat expand(cv::GMat in1, cv::GMat in2, cv::GScalar s)
+        {
+            return cv::gapi::addC(cv::gapi::add(in1, in2), s);
+        }
+    };
+
+    G_TYPED_KERNEL_M(GCompoundSplitWithAdd, <GMat2(GMat)>, "org.opencv.test.compound_split_with_add")
+    {
+        static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc in)
+        {
+            const auto out_depth = in.depth;
+            const auto out_desc  = in.withType(out_depth, 1);
+            return std::make_tuple(out_desc, out_desc);
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundSplitWithAddImpl, GCompoundSplitWithAdd)
+    {
+        static GMat2 expand(cv::GMat in)
+        {
+            cv::GMat a, b, c;
+            std::tie(a, b, c) = cv::gapi::split3(in);
+            return std::make_tuple(cv::gapi::add(a, b), c);
+        }
+    };
+
+    G_TYPED_KERNEL_M(GCompoundParallelAddC, <GMat2(GMat, GScalar)>, "org.opencv.test.compound_parallel_addc")
+    {
+        static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc in, GScalarDesc)
+        {
+            return std::make_tuple(in, in);
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundParallelAddCImpl, GCompoundParallelAddC)
+    {
+        static GMat2 expand(cv::GMat in, cv::GScalar s)
+        {
+            return std::make_tuple(cv::gapi::addC(in, s), cv::gapi::addC(in, s));
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundAddImpl, cv::gapi::core::GAdd)
+    {
+        static GMat expand(cv::GMat in1, cv::GMat in2, int)
+        {
+            return cv::gapi::sub(cv::gapi::sub(in1, in2), in2);
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundAddWithAddCWithDoubleAddC, <GMat(GMat, GMat, GScalar)>, "org.opencv.test.compound_add_with_addC_with_double_addC")
+    {
+        static GMatDesc outMeta(GMatDesc in, GMatDesc, GScalarDesc)
+        {
+            return in;
+        }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundAddWithAddCWithDoubleAddCImpl, GCompoundAddWithAddCWithDoubleAddC)
+    {
+        static GMat expand(cv::GMat in1, cv::GMat in2, cv::GScalar s)
+        {
+            return GCompoundDoubleAddC::on(GCompoundAddWithAddC::on(in1, in2, s), s);
+        }
+    };
+
+    using GDoubleArray = cv::GArray<double>;
+    G_TYPED_KERNEL(GNegateArray, <GDoubleArray(GDoubleArray)>, "org.opencv.test.negate_array")
+    {
+        static GArrayDesc outMeta(const GArrayDesc&) { return empty_array_desc(); }
+    };
+
+    GAPI_OCV_KERNEL(GNegateArrayImpl, GNegateArray)
+    {
+        static void run(const std::vector<double>& in, std::vector<double>& out)
+        {
+            ade::util::transform(in, std::back_inserter(out), std::negate<double>());
+        }
+    };
+
+    G_TYPED_KERNEL(GMaxInArray, <GScalar(GDoubleArray)>, "org.opencv.test.max_in_array")
+    {
+        static GScalarDesc outMeta(const GArrayDesc&) { return empty_scalar_desc(); }
+    };
+
+    GAPI_OCV_KERNEL(GMaxInArrayImpl, GMaxInArray)
+    {
+        static void run(const std::vector<double>& in, cv::Scalar& out)
+        {
+            out = *std::max_element(in.begin(), in.end());
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundMaxInArray, <GScalar(GDoubleArray)>, "org.opencv.test.compound_max_in_array")
+    {
+        static GScalarDesc outMeta(const GArrayDesc&) { return empty_scalar_desc(); }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundMaxInArrayImpl, GCompoundMaxInArray)
+    {
+        static GScalar expand(GDoubleArray in)
+        {
+            return GMaxInArray::on(in);
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundNegateArray, <GDoubleArray(GDoubleArray)>, "org.opencv.test.compound_negate_array")
+    {
+        static GArrayDesc outMeta(const GArrayDesc&) { return empty_array_desc(); }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundNegateArrayImpl, GCompoundNegateArray)
+    {
+        static GDoubleArray expand(GDoubleArray in)
+        {
+            return GNegateArray::on(in);
+        }
+    };
+
+    G_TYPED_KERNEL(SetDiagKernel, <GMat(GMat, GDoubleArray)>, "org.opencv.test.empty_kernel")
+    {
+        static GMatDesc outMeta(GMatDesc in, GArrayDesc) { return in; }
+    };
+
+    void setDiag(cv::Mat& in, const std::vector<double>& diag)
+    {
+        GAPI_Assert(in.rows == static_cast<int>(diag.size()));
+        GAPI_Assert(in.cols == static_cast<int>(diag.size()));
+        for (int i = 0; i < in.rows; ++i)
+        {
+            in.at<uchar>(i, i) = static_cast<uchar>(diag[i]);
+        }
+    }
+
+    GAPI_OCV_KERNEL(SetDiagKernelImpl, SetDiagKernel)
+    {
+        static void run(const cv::Mat& in, const std::vector<double>& v, cv::Mat& out)
+        {
+            in.copyTo(out);
+            setDiag(out, v);
+        }
+    };
+
+    G_TYPED_KERNEL(GCompoundGMatGArrayGMat, <GMat(GMat, GDoubleArray, GMat)>, "org.opencv.test.compound_gmat_garray_gmat")
+    {
+        static GMatDesc outMeta(GMatDesc in, GArrayDesc, GMatDesc) { return in; }
+    };
+
+    GAPI_COMPOUND_KERNEL(GCompoundGMatGArrayGMatImpl, GCompoundGMatGArrayGMat)
+    {
+        static GMat expand(GMat a, GDoubleArray b, GMat c)
+        {
+            return SetDiagKernel::on(cv::gapi::add(a, c), b);
+        }
+    };
+
+} // namespace
+
+// FIXME avoid cv::combine that use custom and default kernels together
+TEST(GCompoundKernel, ReplaceDefaultKernel)
+{
+    cv::GMat in1, in2;
+    auto out = cv::gapi::add(in1, in2);
+    const auto custom_pkg = cv::gapi::kernels<GCompoundAddImpl>();
+    const auto full_pkg   = cv::gapi::combine(cv::gapi::core::cpu::kernels(), custom_pkg, cv::unite_policy::REPLACE);
+    cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out));
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+            in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+            out_mat(3, 3, CV_8UC1),
+            ref_mat(3, 3, CV_8UC1);
+
+    comp.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 - in_mat2 - in_mat2;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, DoubleAddC)
+{
+    cv::GMat in1, in2;
+    cv::GScalar s;
+    auto add_res   = cv::gapi::add(in1, in2);
+    auto super     = GCompoundDoubleAddC::on(add_res, s);
+    auto out       = cv::gapi::addC(super, s);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundDoubleAddCImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out));
+
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+        in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+        out_mat(3, 3, CV_8UC1),
+        ref_mat(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat1, in_mat2, scalar), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 + in_mat2 + scalar + scalar + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, AddC)
+{
+    cv::GMat in1, in2;
+    cv::GScalar s;
+    auto add_res   = cv::gapi::add(in1, in2);
+    auto super     = GCompoundAddC::on(add_res, s);
+    auto out       = cv::gapi::addC(super, s);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundAddCImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out));
+
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+        in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+        out_mat(3, 3, CV_8UC1),
+        ref_mat(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat1, in_mat2, scalar), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 + in_mat2 + scalar + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, MergeWithSplit)
+{
+    cv::GMat in, a1, b1, c1,
+        a2, b2, c2;
+
+    std::tie(a1, b1, c1) = cv::gapi::split3(in);
+    std::tie(a2, b2, c2) = GCompoundMergeWithSplit::on(a1, b1, c1);
+    auto out = cv::gapi::merge3(a2, b2, c2);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundMergeWithSplitImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC3), out_mat, ref_mat;
+    comp.apply(cv::gin(in_mat), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, AddWithAddC)
+{
+    cv::GMat in1, in2;
+    cv::GScalar s;
+    auto out = GCompoundAddWithAddC::on(in1, in2, s);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundAddWithAddCImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out));
+
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+        in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+        out_mat(3, 3, CV_8UC1),
+        ref_mat(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat1, in_mat2, scalar), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 + in_mat2 + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, SplitWithAdd)
+{
+    cv::GMat in, out1, out2;
+    std::tie(out1, out2) = GCompoundSplitWithAdd::on(in);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundSplitWithAddImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2));
+
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC3),
+        out_mat1(3, 3, CV_8UC1),
+        out_mat2(3, 3, CV_8UC1),
+        ref_mat1(3, 3, CV_8UC1),
+        ref_mat2(3, 3, CV_8UC1);
+
+    comp.apply(cv::gin(in_mat), cv::gout(out_mat1, out_mat2), cv::compile_args(full_pkg));
+
+    std::vector<cv::Mat> channels(3);
+    cv::split(in_mat, channels);
+
+    ref_mat1 = channels[0] + channels[1];
+    ref_mat2 = channels[2];
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat1 != ref_mat1));
+    EXPECT_EQ(0, cv::countNonZero(out_mat2 != ref_mat2));
+}
+
+TEST(GCompoundKernel, ParallelAddC)
+{
+    cv::GMat in1, out1, out2;
+    cv::GScalar in2;
+    std::tie(out1, out2) = GCompoundParallelAddC::on(in1, in2);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundParallelAddCImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out1, out2));
+
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1),
+        out_mat1(3, 3, CV_8UC1),
+        out_mat2(3, 3, CV_8UC1),
+        ref_mat1(3, 3, CV_8UC1),
+        ref_mat2(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat, scalar), cv::gout(out_mat1, out_mat2), cv::compile_args(full_pkg));
+
+    ref_mat1 = in_mat + scalar;
+    ref_mat2 = in_mat + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat1 != ref_mat1));
+    EXPECT_EQ(0, cv::countNonZero(out_mat2 != ref_mat2));
+}
+
+TEST(GCompoundKernel, GCompundKernelAndDefaultUseOneData)
+{
+    cv::GMat in1, in2;
+    cv::GScalar s;
+    auto out = cv::gapi::add(GCompoundAddWithAddC::on(in1, in2, s), cv::gapi::addC(in2, s));
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundAddWithAddCImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out));
+
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+        in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+        out_mat(3, 3, CV_8UC1),
+        ref_mat(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat1, in_mat2, scalar), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 + in_mat2 + scalar + in_mat2 + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, CompoundExpandedToCompound)
+{
+    cv::GMat in1, in2;
+    cv::GScalar s;
+    auto out = GCompoundAddWithAddCWithDoubleAddC::on(in1, in2, s);
+
+    const auto custom_pkg = cv::gapi::kernels<GCompoundAddWithAddCWithDoubleAddCImpl,
+                                              GCompoundAddWithAddCImpl,
+                                              GCompoundDoubleAddCImpl>();
+
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out));
+
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+            in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+            out_mat(3, 3, CV_8UC1),
+            ref_mat(3, 3, CV_8UC1);
+
+    cv::Scalar scalar = 2;
+
+    comp.apply(cv::gin(in_mat1, in_mat2, scalar), cv::gout(out_mat), cv::compile_args(full_pkg));
+    ref_mat = in_mat1 + in_mat2 + scalar + scalar + scalar;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GCompoundKernel, MaxInArray)
+{
+    GDoubleArray in;
+    auto out = GCompoundMaxInArray::on(in);
+    const auto custom_pkg = cv::gapi::kernels<GCompoundMaxInArrayImpl, GMaxInArrayImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+    std::vector<double> v = { 1, 5, -2, 3, 10, 2};
+    cv::Scalar out_scl;
+    cv::Scalar ref_scl(*std::max_element(v.begin(), v.end()));
+
+    comp.apply(cv::gin(v), cv::gout(out_scl), cv::compile_args(full_pkg));
+
+    EXPECT_EQ(out_scl, ref_scl);
+}
+
+TEST(GCompoundKernel, NegateArray)
+{
+    GDoubleArray in;
+    GDoubleArray out = GCompoundNegateArray::on(in);
+    const auto custom_pkg = cv::gapi::kernels<GCompoundNegateArrayImpl, GNegateArrayImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+    std::vector<double> in_v = {1, 5, -2, -10, 3};
+    std::vector<double> out_v;
+    std::vector<double> ref_v;
+    ade::util::transform(in_v, std::back_inserter(ref_v), std::negate<double>());
+
+    comp.apply(cv::gin(in_v), cv::gout(out_v), cv::compile_args(full_pkg));
+
+    EXPECT_EQ(out_v, ref_v);
+}
+
+TEST(GCompoundKernel, RightGArrayHandle)
+{
+    cv::GMat in[2];
+    GDoubleArray a;
+    cv::GMat out = GCompoundGMatGArrayGMat::on(in[0], a, in[1]);
+    const auto custom_pkg = cv::gapi::kernels<GCompoundGMatGArrayGMatImpl, SetDiagKernelImpl>();
+    const auto full_pkg   = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP);
+    cv::GComputation comp(cv::GIn(in[0], a, in[1]), cv::GOut(out));
+    std::vector<double> in_v(3, 1.0);
+    cv::Mat in_mat1 = cv::Mat::eye(cv::Size(3, 3), CV_8UC1),
+            in_mat2 = cv::Mat::eye(cv::Size(3, 3), CV_8UC1),
+            out_mat;
+    cv::Mat ref_mat= in_mat1 + in_mat2;
+    setDiag(ref_mat, in_v);
+
+    comp.apply(cv::gin(in_mat1, in_v, in_mat2), cv::gout(out_mat), cv::compile_args(full_pkg));
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+
+}
+} // opencv_test
diff --git a/modules/gapi/test/common/gapi_core_tests.cpp b/modules/gapi/test/common/gapi_core_tests.cpp
new file mode 100644 (file)
index 0000000..1b3e7f7
--- /dev/null
@@ -0,0 +1,8 @@
+// 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
+
+
+#include "gapi_core_tests_inl.hpp"
diff --git a/modules/gapi/test/common/gapi_core_tests.hpp b/modules/gapi/test/common/gapi_core_tests.hpp
new file mode 100644 (file)
index 0000000..b0b15a5
--- /dev/null
@@ -0,0 +1,151 @@
+// 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_CORE_TESTS_HPP
+#define OPENCV_GAPI_CORE_TESTS_HPP
+
+#include <iostream>
+
+#include "gapi_tests_common.hpp"
+
+namespace opencv_test
+{
+enum mathOp
+{
+    ADD = 0,
+    SUB = 1,
+    MUL = 2,
+    DIV = 3
+};
+
+enum bitwiseOp
+{
+    AND = 0,
+    OR = 1,
+    XOR = 2,
+    NOT = 3
+};
+
+namespace
+{
+const char *MathOperations[] = {"ADD", "SUB", "MUL", "DIV"};
+const char *BitwiseOperations[] = {"And", "Or", "Xor"};
+const char *CompareOperations[] = {"CMP_EQ", "CMP_GT", "CMP_GE", "CMP_LT", "CMP_LE", "CMP_NE"};
+//corresponds to OpenCV
+const char *NormOperations[] = {"", "NORM_INF", "NORM_L1", "","NORM_L2"};
+}
+
+
+struct PrintMathOpCoreParams
+{
+    template <class TestParams>
+    std::string operator()(const ::testing::TestParamInfo<TestParams>& info) const
+    {
+        std::stringstream ss;
+        cv::Size sz = std::get<4>(info.param);
+        ss<<MathOperations[std::get<0>(info.param)]
+                    <<"_"<<std::get<1>(info.param)
+                    <<"_"<<std::get<2>(info.param)
+                    <<"_"<<(int)std::get<3>(info.param)
+                    <<"_"<<sz.width
+                    <<"x"<<sz.height
+                    <<"_"<<(std::get<5>(info.param)+1)
+                    <<"_"<<std::get<6>(info.param)
+                    <<"_"<<std::get<7>(info.param);
+        return ss.str();
+   }
+};
+
+struct PrintCmpCoreParams
+{
+    template <class TestParams>
+    std::string operator()(const ::testing::TestParamInfo<TestParams>& info) const
+    {
+        std::stringstream ss;
+        cv::Size sz = std::get<3>(info.param);
+        ss<<CompareOperations[std::get<0>(info.param)]
+                    <<"_"<<std::get<1>(info.param)
+                    <<"_"<<std::get<2>(info.param)
+                    <<"_"<<sz.width
+                    <<"x"<<sz.height
+                    <<"_"<<std::get<4>(info.param);
+        return ss.str();
+   }
+};
+
+struct PrintBWCoreParams
+{
+    template <class TestParams>
+    std::string operator()(const ::testing::TestParamInfo<TestParams>& info) const
+    {
+        std::stringstream ss;
+        cv::Size sz = std::get<2>(info.param);
+        ss<<BitwiseOperations[std::get<0>(info.param)]
+                    <<"_"<<std::get<1>(info.param)
+                    <<"_"<<sz.width
+                    <<"x"<<sz.height
+                    <<"_"<<std::get<3>(info.param);
+        return ss.str();
+   }
+};
+
+struct PrintNormCoreParams
+{
+    template <class TestParams>
+    std::string operator()(const ::testing::TestParamInfo<TestParams>& info) const
+    {
+        std::stringstream ss;
+        cv::Size sz = std::get<2>(info.param);
+        ss<<NormOperations[std::get<0>(info.param)]
+                    <<"_"<<std::get<1>(info.param)
+                    <<"_"<<sz.width
+                    <<"x"<<sz.height;
+        return ss.str();
+   }
+};
+
+struct MathOpTest        : public TestParams<std::tuple<mathOp,bool,int,double,cv::Size,int,bool,bool,cv::GCompileArgs>>{};
+struct MulDoubleTest     : public TestParams<std::tuple<int,cv::Size,int,bool,cv::GCompileArgs>>{};
+struct DivTest           : public TestParams<std::tuple<int,cv::Size,int,bool, cv::GCompileArgs>>{};
+struct DivCTest          : public TestParams<std::tuple<int,cv::Size,int,bool, cv::GCompileArgs>>{};
+struct MeanTest          : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct MaskTest          : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct Polar2CartTest    : public TestParams<std::tuple<cv::Size,bool, cv::GCompileArgs>> {};
+struct Cart2PolarTest    : public TestParams<std::tuple<cv::Size,bool, cv::GCompileArgs>> {};
+struct CmpTest           : public TestParams<std::tuple<CmpTypes,bool,int,cv::Size,bool, cv::GCompileArgs>>{};
+struct BitwiseTest       : public TestParams<std::tuple<bitwiseOp,int,cv::Size,bool, cv::GCompileArgs>>{};
+struct NotTest           : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct SelectTest        : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct MinTest           : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>>{};
+struct MaxTest           : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>>{};
+struct AbsDiffTest       : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>>{};
+struct AbsDiffCTest      : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct SumTest           : public TestParams<std::tuple<int, cv::Size,bool, cv::GCompileArgs>> {};
+struct AddWeightedTest   : public TestParams<std::tuple<int,cv::Size,int,bool, cv::GCompileArgs>>{};
+struct NormTest          : public TestParams<std::tuple<NormTypes,int,cv::Size, cv::GCompileArgs>>{};
+struct IntegralTest      : public TestWithParam<std::tuple<int,cv::Size, cv::GCompileArgs>> {};
+struct ThresholdTest     : public TestParams<std::tuple<int,cv::Size,int,bool, cv::GCompileArgs>> {};
+struct ThresholdOTTest   : public TestParams<std::tuple<int,cv::Size,int,bool, cv::GCompileArgs>> {};
+struct InRangeTest       : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct Split3Test        : public TestParams<std::tuple<cv::Size, cv::GCompileArgs>> {};
+struct Split4Test        : public TestParams<std::tuple<cv::Size, cv::GCompileArgs>> {};
+struct ResizeTest        : public TestWithParam<std::tuple<int, int, cv::Size, cv::Size, double, cv::GCompileArgs>> {};
+struct ResizeTestFxFy    : public TestWithParam<std::tuple<int, int, cv::Size, double, double, double, cv::GCompileArgs>> {};
+struct Merge3Test        : public TestParams<std::tuple<cv::Size, cv::GCompileArgs>> {};
+struct Merge4Test        : public TestParams<std::tuple<cv::Size, cv::GCompileArgs>> {};
+struct RemapTest         : public TestParams<std::tuple<int,cv::Size,bool, cv::GCompileArgs>> {};
+struct FlipTest          : public TestParams<std::tuple<int, int, cv::Size,bool, cv::GCompileArgs>> {};
+struct CropTest          : public TestParams<std::tuple<int,cv::Rect,cv::Size,bool, cv::GCompileArgs>> {};
+struct ConcatHorTest     : public TestWithParam<std::tuple<int, cv::Size, cv::GCompileArgs>> {};
+struct ConcatVertTest    : public TestWithParam<std::tuple<int, cv::Size, cv::GCompileArgs>> {};
+struct ConcatVertVecTest : public TestWithParam<std::tuple<int, cv::Size, cv::GCompileArgs>> {};
+struct ConcatHorVecTest  : public TestWithParam<std::tuple<int, cv::Size, cv::GCompileArgs>> {};
+struct LUTTest           : public TestParams<std::tuple<int, int, cv::Size,bool, cv::GCompileArgs>> {};
+struct ConvertToTest     : public TestParams<std::tuple<int, int, cv::Size, cv::GCompileArgs>> {};
+} // opencv_test
+
+#endif //OPENCV_GAPI_CORE_TESTS_HPP
diff --git a/modules/gapi/test/common/gapi_core_tests_inl.hpp b/modules/gapi/test/common/gapi_core_tests_inl.hpp
new file mode 100644 (file)
index 0000000..fb9b336
--- /dev/null
@@ -0,0 +1,1420 @@
+// 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_CORE_TESTS_INL_HPP
+#define OPENCV_GAPI_CORE_TESTS_INL_HPP
+
+#include "opencv2/gapi/core.hpp"
+#include "gapi_core_tests.hpp"
+
+namespace opencv_test
+{
+
+TEST_P(MathOpTest, MatricesAccuracyTest )
+{
+    mathOp opType = ADD;
+    int type = 0, dtype = 0;
+    cv::Size sz;
+    double scale = 1; // mul, div
+    bool testWithScalar = false, initOutMatr = false, doReverseOp = false;
+    cv::GCompileArgs compile_args;
+    std::tie(opType, testWithScalar, type, scale, sz, dtype, initOutMatr, doReverseOp, compile_args) = GetParam();
+    initMatsRandU(type, sz, dtype, initOutMatr);
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+    cv::GMat in1, in2, out;
+    if( testWithScalar )
+    {
+        cv::GScalar sc1;
+        switch(opType)
+        {
+        case (ADD):
+        {
+            out = cv::gapi::addC(in1, sc1, dtype);
+            cv::add(in_mat1, sc, out_mat_ocv, cv::noArray(), dtype);
+            break;
+        }
+        case (SUB):
+        {
+            if( doReverseOp )
+            {
+                out = cv::gapi::subRC(sc1, in1, dtype);
+                cv::subtract(sc, in_mat1, out_mat_ocv, cv::noArray(), dtype);
+            }
+            else
+            {
+                out = cv::gapi::subC(in1, sc1, dtype);
+                cv::subtract(in_mat1, sc, out_mat_ocv, cv::noArray(), dtype);
+            }
+            break;
+        }
+        case (DIV):
+        {
+            if( doReverseOp )
+            {
+                out = cv::gapi::divRC(sc1, in1, scale, dtype);
+                cv::divide(sc, in_mat1, out_mat_ocv, scale, dtype);
+                break;
+            }
+            else
+            {
+                out = cv::gapi::divC(in1, sc1, scale, dtype);
+                cv::divide(in_mat1, sc, out_mat_ocv, scale, dtype);
+                break;
+            }
+        }
+        case (MUL):
+        {
+            // FIXME: add `scale` parameter to mulC
+            out = cv::gapi::mulC(in1, sc1, /* scale, */ dtype);
+            cv::multiply(in_mat1, sc, out_mat_ocv, 1., dtype);
+            break;
+        }
+        default:
+        {
+            FAIL() << "no such math operation type for scalar and matrix!";
+        }
+        }
+        cv::GComputation c(GIn(in1, sc1), GOut(out));
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args));
+    }
+    else
+    {
+        switch(opType)
+        {
+        case (ADD):
+        {
+            out = cv::gapi::add(in1, in2, dtype);
+            cv::add(in_mat1, in_mat2, out_mat_ocv, cv::noArray(), dtype);
+            break;
+        }
+        case (SUB):
+        {
+            out = cv::gapi::sub(in1, in2, dtype);
+            cv::subtract(in_mat1, in_mat2, out_mat_ocv, cv::noArray(), dtype);
+            break;
+        }
+        case (DIV):
+        {
+            out = cv::gapi::div(in1, in2, scale, dtype);
+            cv::divide(in_mat1, in_mat2, out_mat_ocv, scale, dtype);
+            break;
+        }
+        case (MUL):
+        {
+            out = cv::gapi::mul(in1, in2, scale, dtype);
+            cv::multiply(in_mat1, in_mat2, out_mat_ocv, scale, dtype);
+            break;
+        }
+        default:
+        {
+            FAIL() << "no such math operation type for matrix and matrix!";
+        }}
+        cv::GComputation c(GIn(in1, in2), GOut(out));
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: make threshold vs bit-exact criteria be driven by testing parameter
+    #if 1
+        if (CV_MAT_DEPTH(out_mat_ocv.type()) != CV_32F &&
+            CV_MAT_DEPTH(out_mat_ocv.type()) != CV_64F)
+        {
+            // integral: allow 1% of differences, and no diffs by >1 unit
+            EXPECT_LE(countNonZeroPixels(cv::abs(out_mat_gapi - out_mat_ocv) > 0),
+                                                           0.01*out_mat_ocv.total());
+            EXPECT_LE(countNonZeroPixels(cv::abs(out_mat_gapi - out_mat_ocv) > 1), 0);
+        }
+        else
+        {
+            // floating-point: expect 6 decimal digits - best we expect of F32
+            EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) >
+                                                    1e-6*cv::abs(out_mat_ocv)));
+        }
+    #else
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(MulDoubleTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    int dtype = std::get<2>(param);
+    cv::Size sz_in = std::get<1>(param);
+    bool initOut = std::get<3>(param);
+
+    auto& rng = cv::theRNG();
+    double d = rng.uniform(0.0, 10.0);
+    auto compile_args = std::get<4>(param);
+    initMatrixRandU(type, sz_in, dtype, initOut);
+
+    // G-API code ////////////////////////////////////////////////////////////
+    cv::GMat in1, out;
+    out = cv::gapi::mulC(in1, d, dtype);
+    cv::GComputation c(in1, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+
+    // OpenCV code ///////////////////////////////////////////////////////////
+    cv::multiply(in_mat1, d, out_mat_ocv, 1, dtype);
+
+    // Comparison ////////////////////////////////////////////////////////////
+#if 1
+    if (CV_MAT_DEPTH(out_mat_ocv.type()) != CV_32F &&
+        CV_MAT_DEPTH(out_mat_ocv.type()) != CV_64F)
+    {
+        // integral: allow 1% of differences, and no diffs by >1 unit
+        EXPECT_LE(countNonZeroPixels(cv::abs(out_mat_gapi - out_mat_ocv) > 0),
+                                                    0.01*out_mat_ocv.total());
+        EXPECT_LE(countNonZeroPixels(cv::abs(out_mat_gapi - out_mat_ocv) > 1), 0);
+    }
+    else
+    {
+        // floating-point: expect 6 decimal digits - best we expect of F32
+        EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) >
+            1e-6*cv::abs(out_mat_ocv)));
+    }
+#else
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+#endif
+    EXPECT_EQ(out_mat_gapi.size(), sz_in);
+}
+
+TEST_P(DivTest, DivByZeroTest)
+{
+    int type = 0, dtype = 0;
+    cv::Size sz_in;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz_in, dtype, initOut, compile_args) = GetParam();
+
+    initMatrixRandU(type, sz_in, dtype, initOut);
+    in_mat2 = cv::Mat(sz_in, type);
+    in_mat2.setTo(cv::Scalar::all(0));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::div(in1, in2, 1.0, dtype);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::divide(in_mat1, in_mat2, out_mat_ocv, 1.0, dtype);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(DivCTest, DivByZeroTest)
+{
+    int type = 0, dtype = 0;
+    cv::Size sz_in;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz_in, dtype, initOut, compile_args) = GetParam();
+
+    initMatrixRandU(type, sz_in, dtype, initOut);
+    sc = cv::Scalar::all(0);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1;
+    cv::GScalar sc1;
+    auto out = cv::gapi::divC(in1, sc1, dtype);
+    cv::GComputation c(GIn(in1, sc1), GOut(out));
+
+    c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::divide(in_mat1, sc, out_mat_ocv, dtype);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        cv::Mat zeros = cv::Mat::zeros(sz_in, type);
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != zeros));
+    }
+}
+
+TEST_P(MeanTest, AccuracyTest)
+{
+    int type = 0;
+    bool initOut = false;
+    cv::Size sz_in;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz_in, initOut, compile_args) = GetParam();
+    initMatrixRandU(type, sz_in, initOut);
+    cv::Scalar out_norm;
+    cv::Scalar out_norm_ocv;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::mean(in);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+    c.apply(cv::gin(in_mat1), cv::gout(out_norm), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        out_norm_ocv = cv::mean(in_mat1);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(out_norm[0], out_norm_ocv[0]);
+    }
+}
+
+TEST_P(MaskTest, AccuracyTest)
+{
+    int type = 0;
+    bool initOut = false;
+    cv::Size sz_in;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz_in, initOut, compile_args) = GetParam();
+    initMatrixRandU(type, sz_in, type, initOut);
+
+    in_mat2 = cv::Mat(sz_in, CV_8UC1);
+    cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+    in_mat2 = in_mat2 > 128;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in, m;
+    auto out = cv::gapi::mask(in, m);
+
+    cv::GComputation c(cv::GIn(in, m), cv::GOut(out));
+    c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        out_mat_ocv = cv::Mat::zeros(in_mat1.size(), in_mat1.type());
+        in_mat1.copyTo(out_mat_ocv, in_mat2);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+    }
+}
+
+TEST_P(Polar2CartTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<0>(param);
+    auto compile_args = std::get<2>(param);
+    initMatsRandU(CV_32FC1, sz_in, CV_32FC1, std::get<1>(param));
+
+    cv::Mat out_mat2;
+    cv::Mat out_mat_ocv2;
+    if(std::get<1>(param) == true)
+    {
+        out_mat2 = cv::Mat(sz_in, CV_32FC1);
+        out_mat_ocv2 = cv::Mat(sz_in, CV_32FC1);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2, out1, out2;
+    std::tie(out1, out2) = cv::gapi::polarToCart(in1, in2);
+
+    cv::GComputation c(GIn(in1, in2), GOut(out1, out2));
+    c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::polarToCart(in_mat1, in_mat2, out_mat_ocv, out_mat_ocv2);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // Note that we cannot rely on bit-exact sin/cos functions used for this
+        // transform, so we need a threshold for verifying results vs reference.
+        //
+        // Relative threshold like 1e-6 is very restrictive, nearly best we can
+        // expect of single-precision elementary functions implementation.
+        //
+        // However, good idea is making such threshold configurable: parameter
+        // of this test - which a specific test istantiation could setup.
+        //
+        // Note that test instantiation for the OpenCV back-end could even let
+        // the threshold equal to zero, as CV back-end calls the same kernel.
+        //
+        // TODO: Make threshold a configurable parameter of this test (ADE-221)
+
+        cv::Mat &outx = out_mat_gapi,
+                &outy = out_mat2;
+        cv::Mat &refx = out_mat_ocv,
+                &refy = out_mat_ocv2;
+        cv::Mat difx = cv::abs(refx - outx),
+                dify = cv::abs(refy - outy);
+        cv::Mat absx = cv::abs(refx),
+                absy = cv::abs(refy);
+
+        EXPECT_EQ(0, cv::countNonZero(difx > 1e-6*absx));
+        EXPECT_EQ(0, cv::countNonZero(dify > 1e-6*absy));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(Cart2PolarTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<0>(param);
+    auto compile_args = std::get<2>(param);
+    initMatsRandU(CV_32FC1, sz_in, CV_32FC1, std::get<1>(param));
+
+    cv::Mat out_mat2(sz_in, CV_32FC1);
+    cv::Mat out_mat_ocv2(sz_in, CV_32FC1);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2, out1, out2;
+    std::tie(out1, out2) = cv::gapi::cartToPolar(in1, in2);
+
+    cv::GComputation c(GIn(in1, in2), GOut(out1, out2));
+    c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cartToPolar(in_mat1, in_mat2, out_mat_ocv, out_mat_ocv2);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // Note that we cannot rely on bit-exact sin/cos functions used for this
+        // transform, so we need a threshold for verifying results vs reference.
+        //
+        // Relative threshold like 1e-6 is very restrictive, nearly best we can
+        // expect of single-precision elementary functions implementation.
+        //
+        // However, good idea is making such threshold configurable: parameter
+        // of this test - which a specific test istantiation could setup.
+        //
+        // Note that test instantiation for the OpenCV back-end could even let
+        // the threshold equal to zero, as CV back-end calls the same kernel.
+        //
+        // TODO: Make threshold a configurable parameter of this test (ADE-221)
+
+        cv::Mat &outm = out_mat_gapi,
+                &outa = out_mat2;
+        cv::Mat &refm = out_mat_ocv,
+                &refa = out_mat_ocv2;
+        cv::Mat difm = cv::abs(refm - outm),
+                difa = cv::abs(refa - outa);
+        cv::Mat absm = cv::abs(refm),
+                absa = cv::abs(refa);
+
+        // FIXME: Angle result looks inaccurate at OpenCV
+        //        (expected relative accuracy like 1e-6)
+        EXPECT_EQ(0, cv::countNonZero(difm > 1e-6*absm));
+        EXPECT_EQ(0, cv::countNonZero(difa > 1e-3*absa));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(CmpTest, AccuracyTest)
+{
+    CmpTypes opType = CMP_EQ;
+    int type = 0;
+    cv::Size sz;
+    bool testWithScalar = false, initOutMatr = false;
+    cv::GCompileArgs compile_args;
+    std::tie(opType, testWithScalar, type, sz, initOutMatr, compile_args) = GetParam();
+    initMatsRandU(type, sz, CV_8U, initOutMatr);
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+    cv::GMat in1, out;
+    if( testWithScalar )
+    {
+        cv::GScalar in2;
+        switch(opType)
+        {
+        case CMP_EQ: out = cv::gapi::cmpEQ(in1, in2); break;
+        case CMP_GT: out = cv::gapi::cmpGT(in1, in2); break;
+        case CMP_GE: out = cv::gapi::cmpGE(in1, in2); break;
+        case CMP_LT: out = cv::gapi::cmpLT(in1, in2); break;
+        case CMP_LE: out = cv::gapi::cmpLE(in1, in2); break;
+        case CMP_NE: out = cv::gapi::cmpNE(in1, in2); break;
+        default: FAIL() << "no such compare operation type for matrix and scalar!";
+        }
+
+        cv::compare(in_mat1, sc, out_mat_ocv, opType);
+
+        cv::GComputation c(GIn(in1, in2), GOut(out));
+        c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args));
+    }
+    else
+    {
+        cv::GMat in2;
+        switch(opType)
+        {
+        case CMP_EQ: out = cv::gapi::cmpEQ(in1, in2); break;
+        case CMP_GT: out = cv::gapi::cmpGT(in1, in2); break;
+        case CMP_GE: out = cv::gapi::cmpGE(in1, in2); break;
+        case CMP_LT: out = cv::gapi::cmpLT(in1, in2); break;
+        case CMP_LE: out = cv::gapi::cmpLE(in1, in2); break;
+        case CMP_NE: out = cv::gapi::cmpNE(in1, in2); break;
+        default: FAIL() << "no such compare operation type for two matrices!";
+        }
+
+        cv::compare(in_mat1, in_mat2, out_mat_ocv, opType);
+
+        cv::GComputation c(GIn(in1, in2), GOut(out));
+        c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(BitwiseTest, AccuracyTest)
+{
+    bitwiseOp opType = AND;
+    int type = 0;
+    cv::Size sz;
+    bool initOutMatr = false;
+    cv::GCompileArgs compile_args;
+    std::tie(opType, type, sz, initOutMatr, compile_args) = GetParam();
+    initMatsRandU(type, sz, type, initOutMatr);
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+    cv::GMat in1, in2, out;
+    switch(opType)
+    {
+        case AND:
+        {
+            out = cv::gapi::bitwise_and(in1, in2);
+            cv::bitwise_and(in_mat1, in_mat2, out_mat_ocv);
+            break;
+        }
+        case OR:
+        {
+            out = cv::gapi::bitwise_or(in1, in2);
+            cv::bitwise_or(in_mat1, in_mat2, out_mat_ocv);
+            break;
+        }
+        case XOR:
+        {
+            out = cv::gapi::bitwise_xor(in1, in2);
+            cv::bitwise_xor(in_mat1, in_mat2, out_mat_ocv);
+            break;
+        }
+        default:
+        {
+            FAIL() << "no such bitwise operation type!";
+        }
+    }
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(NotTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatrixRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::bitwise_not(in);
+    cv::GComputation c(in, out);
+
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::bitwise_not(in_mat1, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(SelectTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatsRandU(type, sz_in, type, std::get<2>(param));
+    cv::Mat in_mask(sz_in, CV_8UC1);
+    cv::randu(in_mask, cv::Scalar::all(0), cv::Scalar::all(255));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2, in3;
+    auto out = cv::gapi::select(in1, in2, in3);
+    cv::GComputation c(GIn(in1, in2, in3), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        in_mat2.copyTo(out_mat_ocv);
+        in_mat1.copyTo(out_mat_ocv, in_mask);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(MinTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::min(in1, in2);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::min(in_mat1, in_mat2, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(MaxTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::max(in1, in2);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::max(in_mat1, in_mat2, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(AbsDiffTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::absDiff(in1, in2);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::absdiff(in_mat1, in_mat2, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(AbsDiffCTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1;
+    cv::GScalar sc1;
+    auto out = cv::gapi::absDiffC(in1, sc1);
+    cv::GComputation c(cv::GIn(in1, sc1), cv::GOut(out));
+
+    c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::absdiff(in_mat1, sc, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(SumTest, AccuracyTest)
+{
+    auto param = GetParam();
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatrixRandU(std::get<0>(param), sz_in, std::get<2>(param));
+
+    cv::Scalar out_sum;
+    cv::Scalar out_sum_ocv;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::sum(in);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+    c.apply(cv::gin(in_mat1), cv::gout(out_sum), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        out_sum_ocv = cv::sum(in_mat1);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(out_sum[0], out_sum_ocv[0]);
+    }
+}
+
+TEST_P(AddWeightedTest, AccuracyTest)
+{
+    int type = 0, dtype = 0;
+    cv::Size sz_in;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz_in, dtype, initOut, compile_args) = GetParam();
+
+    auto& rng = cv::theRNG();
+    double alpha = rng.uniform(0.0, 1.0);
+    double beta = rng.uniform(0.0, 1.0);
+    double gamma = rng.uniform(0.0, 1.0);
+    initMatsRandU(type, sz_in, dtype, initOut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::addWeighted(in1, alpha, in2, beta, gamma, dtype);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::addWeighted(in_mat1, alpha, in_mat2, beta, gamma, out_mat_ocv, dtype);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // Note, that we cannot expect bitwise results for add-weighted:
+        //
+        //    tmp = src1*alpha + src2*beta + gamma;
+        //    dst = saturate<DST>( round(tmp) );
+        //
+        // Because tmp is floating-point, dst depends on compiler optimizations
+        //
+        // However, we must expect good accuracy of tmp, and rounding correctly
+
+        cv::Mat failures;
+
+        if (out_mat_ocv.type() == CV_32FC1)
+        {
+            // result: float - may vary in 7th decimal digit
+            failures = abs(out_mat_gapi - out_mat_ocv) > abs(out_mat_ocv) * 1e-6;
+        }
+        else
+        {
+            // result: integral - rounding may vary if fractional part of tmp
+            //                    is nearly 0.5
+
+            cv::Mat inexact, incorrect, diff, tmp;
+
+            inexact = out_mat_gapi != out_mat_ocv;
+
+            // even if rounded differently, check if still rounded correctly
+            cv::addWeighted(in_mat1, alpha, in_mat2, beta, gamma, tmp, CV_32F);
+            cv::subtract(out_mat_gapi, tmp, diff, cv::noArray(), CV_32F);
+            incorrect = abs(diff) >= 0.5000005f; // relative to 6 digits
+
+            failures = inexact & incorrect;
+        }
+
+        EXPECT_EQ(0, cv::countNonZero(failures));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(NormTest, AccuracyTest)
+{
+    NormTypes opType = NORM_INF;
+    int type = 0;
+    cv::Size sz;
+    cv::GCompileArgs compile_args;
+    std::tie(opType, type, sz, compile_args) = GetParam();
+    initMatrixRandU(type, sz, type, false);
+
+    cv::Scalar out_norm;
+    cv::Scalar out_norm_ocv;
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+    cv::GMat in1;
+    cv::GScalar out;
+    switch(opType)
+    {
+        case NORM_L1: out = cv::gapi::normL1(in1); break;
+        case NORM_L2: out = cv::gapi::normL2(in1); break;
+        case NORM_INF: out = cv::gapi::normInf(in1); break;
+        default: FAIL() << "no such norm operation type!";
+    }
+    out_norm_ocv = cv::norm(in_mat1, opType);
+    cv::GComputation c(GIn(in1), GOut(out));
+    c.apply(gin(in_mat1), gout(out_norm), std::move(compile_args));
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(out_norm[0], out_norm_ocv[0]);
+    }
+}
+
+TEST_P(IntegralTest, AccuracyTest)
+{
+    int type = std::get<0>(GetParam());
+    cv::Size sz_in = std::get<1>(GetParam());
+    auto compile_args = std::get<2>(GetParam());
+
+    int type_out = (type == CV_8U) ? CV_32SC1 : CV_64FC1;
+    cv::Mat in_mat1(sz_in, type);
+
+    cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+
+    cv::Size sz_out = cv::Size(sz_in.width + 1, sz_in.height + 1);
+    cv::Mat out_mat1(sz_out, type_out);
+    cv::Mat out_mat_ocv1(sz_out, type_out);
+
+    cv::Mat out_mat2(sz_out, CV_64FC1);
+    cv::Mat out_mat_ocv2(sz_out, CV_64FC1);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, out1, out2;
+    std::tie(out1, out2)  = cv::gapi::integral(in1, type_out, CV_64FC1);
+    cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2));
+
+    c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::integral(in_mat1, out_mat_ocv1, out_mat_ocv2);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv1 != out_mat1));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+    }
+}
+
+TEST_P(ThresholdTest, AccuracyTestBinary)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_in = std::get<1>(param);
+    int tt = std::get<2>(param);
+
+    auto compile_args = std::get<4>(param);
+    auto& rng = cv::theRNG();
+    cv::Scalar thr = cv::Scalar(rng(50),rng(50),rng(50),rng(50));
+    cv::Scalar maxval = cv::Scalar(50 + rng(50),50 + rng(50),50 + rng(50),50 + rng(50));
+    initMatrixRandU(type, sz_in, type, std::get<3>(param));
+    cv::Scalar out_scalar;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, out;
+    cv::GScalar th1, mv1;
+    out = cv::gapi::threshold(in1, th1, mv1, tt);
+    cv::GComputation c(GIn(in1, th1, mv1), GOut(out));
+
+    c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::threshold(in_mat1, out_mat_ocv, thr.val[0], maxval.val[0], tt);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(ThresholdOTTest, AccuracyTestOtsu)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_in = std::get<1>(param);
+    int tt = std::get<2>(param);
+
+    auto compile_args = std::get<4>(param);
+    auto& rng = cv::theRNG();
+    cv::Scalar maxval = cv::Scalar(50 + rng(50),50 + rng(50),50 + rng(50),50 + rng(50));
+    initMatrixRandU(type, sz_in, type, std::get<3>(param));
+    cv::Scalar out_gapi_scalar;
+    double ocv_res;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, out;
+    cv::GScalar mv1, scout;
+    std::tie<cv::GMat, cv::GScalar>(out, scout) = cv::gapi::threshold(in1, mv1, tt);
+    cv::GComputation c(cv::GIn(in1, mv1), cv::GOut(out, scout));
+
+    c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        ocv_res = cv::threshold(in_mat1, out_mat_ocv, maxval.val[0], maxval.val[0], tt);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+        EXPECT_EQ(ocv_res, out_gapi_scalar.val[0]);
+    }
+}
+
+TEST_P(InRangeTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_in = std::get<1>(param);
+
+    auto compile_args = std::get<3>(param);
+    auto& rng = cv::theRNG();
+    cv::Scalar thrLow = cv::Scalar(rng(100),rng(100),rng(100),rng(100));
+    cv::Scalar thrUp = cv::Scalar(100 + rng(100),100 + rng(100),100 + rng(100),100 + rng(100));
+    initMatrixRandU(type, sz_in, type, std::get<2>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1;
+    cv::GScalar th1, mv1;
+    auto out = cv::gapi::inRange(in1, th1, mv1);
+    cv::GComputation c(GIn(in1, th1, mv1), GOut(out));
+
+    c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::inRange(in_mat1, thrLow, thrUp, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(Split3Test, AccuracyTest)
+{
+    cv::Size sz_in = std::get<0>(GetParam());
+    auto compile_args = std::get<1>(GetParam());
+    initMatrixRandU(CV_8UC3, sz_in, CV_8UC1);
+
+    cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1);
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, out1, out2, out3;
+    std::tie(out1, out2, out3)  = cv::gapi::split3(in1);
+    cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3));
+
+    c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        std::vector<cv::Mat> out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3};
+        cv::split(in_mat1, out_mats_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv  != out_mat_gapi));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv3 != out_mat3));
+    }
+}
+
+TEST_P(Split4Test, AccuracyTest)
+{
+    cv::Size sz_in = std::get<0>(GetParam());
+    auto compile_args = std::get<1>(GetParam());
+    initMatrixRandU(CV_8UC4, sz_in, CV_8UC1);
+    cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat4 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv4 = cv::Mat(sz_in, CV_8UC1);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, out1, out2, out3, out4;
+    std::tie(out1, out2, out3, out4)  = cv::gapi::split4(in1);
+    cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3, out4));
+
+    c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        std::vector<cv::Mat> out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3, out_mat_ocv4};
+        cv::split(in_mat1, out_mats_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv  != out_mat_gapi));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv3 != out_mat3));
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv4 != out_mat4));
+    }
+}
+
+static void ResizeAccuracyTest(int type, int interp, cv::Size sz_in, cv::Size sz_out, double fx, double fy, double tolerance, cv::GCompileArgs&& compile_args)
+{
+    cv::Mat in_mat1 (sz_in, type );
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+
+    auto out_mat_sz = sz_out.area() == 0 ? cv::Size(saturate_cast<int>(sz_in.width *fx),
+                                                    saturate_cast<int>(sz_in.height*fy))
+                                         : sz_out;
+    cv::Mat out_mat(out_mat_sz, type);
+    cv::Mat out_mat_ocv(out_mat_sz, type);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::resize(in, sz_out, fx, fy, interp);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::resize(in_mat1, out_mat_ocv, sz_out, fx, fy, interp);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        cv::Mat absDiff;
+        cv::absdiff(out_mat, out_mat_ocv, absDiff);
+        EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+    }
+}
+
+TEST_P(ResizeTest, AccuracyTest)
+{
+    int type = 0, interp = 0;
+    cv::Size sz_in, sz_out;
+    double tolerance = 0.0;
+    cv::GCompileArgs compile_args;
+    std::tie(type, interp, sz_in, sz_out, tolerance, compile_args) = GetParam();
+    ResizeAccuracyTest(type, interp, sz_in, sz_out, 0.0, 0.0, tolerance, std::move(compile_args));
+}
+
+TEST_P(ResizeTestFxFy, AccuracyTest)
+{
+    int type = 0, interp = 0;
+    cv::Size sz_in;
+    double fx = 0.0, fy = 0.0, tolerance = 0.0;
+    cv::GCompileArgs compile_args;
+    std::tie(type, interp, sz_in, fx, fy, tolerance, compile_args) = GetParam();
+    ResizeAccuracyTest(type, interp, sz_in, cv::Size{0, 0}, fx, fy, tolerance, std::move(compile_args));
+}
+
+TEST_P(Merge3Test, AccuracyTest)
+{
+    cv::Size sz_in = std::get<0>(GetParam());
+    initMatsRandU(CV_8UC1, sz_in, CV_8UC3);
+    auto compile_args = std::get<1>(GetParam());
+    cv::Mat in_mat3(sz_in,  CV_8UC1);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat3, mean, stddev);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2, in3;
+    auto out = cv::gapi::merge3(in1, in2, in3);
+
+    cv::GComputation c(cv::GIn(in1, in2, in3), cv::GOut(out));
+    c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        std::vector<cv::Mat> in_mats_ocv = {in_mat1, in_mat2, in_mat3};
+        cv::merge(in_mats_ocv, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+    }
+}
+
+TEST_P(Merge4Test, AccuracyTest)
+{
+    cv::Size sz_in = std::get<0>(GetParam());
+    initMatsRandU(CV_8UC1, sz_in, CV_8UC4);
+    auto compile_args = std::get<1>(GetParam());
+    cv::Mat in_mat3(sz_in,  CV_8UC1);
+    cv::Mat in_mat4(sz_in,  CV_8UC1);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat3, mean, stddev);
+    cv::randn(in_mat4, mean, stddev);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2, in3, in4;
+    auto out = cv::gapi::merge4(in1, in2, in3, in4);
+
+    cv::GComputation c(cv::GIn(in1, in2, in3, in4), cv::GOut(out));
+    c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        std::vector<cv::Mat> in_mats_ocv = {in_mat1, in_mat2, in_mat3, in_mat4};
+        cv::merge(in_mats_ocv, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+    }
+}
+
+TEST_P(RemapTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_in = std::get<1>(param);
+    auto compile_args = std::get<3>(param);
+    initMatrixRandU(type, sz_in, type, std::get<2>(param));
+    cv::Mat in_map1(sz_in,  CV_16SC2);
+    cv::Mat in_map2 = cv::Mat();
+    cv::randu(in_map1, cv::Scalar::all(0), cv::Scalar::all(255));
+    cv::Scalar bv = cv::Scalar();
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1;
+    auto out = cv::gapi::remap(in1, in_map1, in_map2, cv::INTER_NEAREST,  cv::BORDER_REPLICATE, bv);
+    cv::GComputation c(in1, out);
+
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::remap(in_mat1, out_mat_ocv, in_map1, in_map2, cv::INTER_NEAREST, cv::BORDER_REPLICATE, bv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(FlipTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    int flipCode =  std::get<1>(param);
+    cv::Size sz_in = std::get<2>(param);
+    initMatrixRandU(type, sz_in, type, false);
+    auto compile_args = std::get<4>(GetParam());
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::flip(in, flipCode);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::flip(in_mat1, out_mat_ocv, flipCode);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(CropTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Rect rect_to = std::get<1>(param);
+    cv::Size sz_in = std::get<2>(param);
+    auto compile_args = std::get<4>(param);
+
+    initMatrixRandU(type, sz_in, type, false);
+    cv::Size sz_out = cv::Size(rect_to.width, rect_to.height);
+    if( std::get<3>(param) == true )
+    {
+        out_mat_gapi = cv::Mat(sz_out, type);
+        out_mat_ocv = cv::Mat(sz_out, type);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::crop(in, rect_to);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Mat(in_mat1, rect_to).copyTo(out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_out);
+    }
+}
+
+TEST_P(ConcatHorTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_out = std::get<1>(param);
+    auto compile_args = std::get<2>(param);
+
+    int wpart = sz_out.width / 4;
+    cv::Size sz_in1 = cv::Size(wpart, sz_out.height);
+    cv::Size sz_in2 = cv::Size(sz_out.width - wpart, sz_out.height);
+
+    cv::Mat in_mat1 (sz_in1, type );
+    cv::Mat in_mat2 (sz_in2, type);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+    cv::randn(in_mat2, mean, stddev);
+
+    cv::Mat out_mat(sz_out, type);
+    cv::Mat out_mat_ocv(sz_out, type);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::concatHor(in1, in2);
+
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::hconcat(in_mat1, in_mat2, out_mat_ocv );
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat));
+    }
+}
+
+TEST_P(ConcatVertTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_out = std::get<1>(param);
+    auto compile_args = std::get<2>(param);
+
+    int hpart = sz_out.height * 2/3;
+    cv::Size sz_in1 = cv::Size(sz_out.width, hpart);
+    cv::Size sz_in2 = cv::Size(sz_out.width, sz_out.height - hpart);
+
+    cv::Mat in_mat1 (sz_in1, type);
+    cv::Mat in_mat2 (sz_in2, type);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+    cv::randn(in_mat2, mean, stddev);
+
+    cv::Mat out_mat(sz_out, type);
+    cv::Mat out_mat_ocv(sz_out, type);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in1, in2;
+    auto out = cv::gapi::concatVert(in1, in2);
+
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::vconcat(in_mat1, in_mat2, out_mat_ocv );
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat));
+    }
+}
+
+TEST_P(ConcatVertVecTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_out = std::get<1>(param);
+    auto compile_args = std::get<2>(param);
+
+    int hpart1 = sz_out.height * 2/5;
+    int hpart2 = sz_out.height / 5;
+    cv::Size sz_in1 = cv::Size(sz_out.width, hpart1);
+    cv::Size sz_in2 = cv::Size(sz_out.width, hpart2);
+    cv::Size sz_in3 = cv::Size(sz_out.width, sz_out.height - hpart1 - hpart2);
+
+    cv::Mat in_mat1 (sz_in1, type);
+    cv::Mat in_mat2 (sz_in2, type);
+    cv::Mat in_mat3 (sz_in3, type);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+    cv::randn(in_mat2, mean, stddev);
+    cv::randn(in_mat3, mean, stddev);
+
+    cv::Mat out_mat(sz_out, type);
+    cv::Mat out_mat_ocv(sz_out, type);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    std::vector <cv::GMat> mats(3);
+    auto out = cv::gapi::concatVert(mats);
+
+    std::vector <cv::Mat> cvmats = {in_mat1, in_mat2, in_mat3};
+
+    cv::GComputation c({mats[0], mats[1], mats[2]}, {out});
+    c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::vconcat(cvmats, out_mat_ocv );
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat));
+    }
+}
+
+TEST_P(ConcatHorVecTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type = std::get<0>(param);
+    cv::Size sz_out = std::get<1>(param);
+    auto compile_args = std::get<2>(param);
+
+    int wpart1 = sz_out.width / 3;
+    int wpart2 = sz_out.width / 4;
+    cv::Size sz_in1 = cv::Size(wpart1, sz_out.height);
+    cv::Size sz_in2 = cv::Size(wpart2, sz_out.height);
+    cv::Size sz_in3 = cv::Size(sz_out.width - wpart1 - wpart2, sz_out.height);
+
+    cv::Mat in_mat1 (sz_in1, type);
+    cv::Mat in_mat2 (sz_in2, type);
+    cv::Mat in_mat3 (sz_in3, type);
+    cv::Scalar mean = cv::Scalar::all(127);
+    cv::Scalar stddev = cv::Scalar::all(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+    cv::randn(in_mat2, mean, stddev);
+    cv::randn(in_mat3, mean, stddev);
+
+    cv::Mat out_mat(sz_out, type);
+    cv::Mat out_mat_ocv(sz_out, type);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    std::vector <cv::GMat> mats(3);
+    auto out = cv::gapi::concatHor(mats);
+
+    std::vector <cv::Mat> cvmats = {in_mat1, in_mat2, in_mat3};
+
+    cv::GComputation c({mats[0], mats[1], mats[2]}, {out});
+    c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::hconcat(cvmats, out_mat_ocv );
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat));
+    }
+}
+
+TEST_P(LUTTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type_mat = std::get<0>(param);
+    int type_lut = std::get<1>(param);
+    int type_out = CV_MAKETYPE(CV_MAT_DEPTH(type_lut), CV_MAT_CN(type_mat));
+    cv::Size sz_in = std::get<2>(param);
+    auto compile_args = std::get<4>(GetParam());
+
+    initMatrixRandU(type_mat, sz_in, type_out);
+    cv::Size sz_lut = cv::Size(1, 256);
+    cv::Mat in_lut (sz_lut, type_lut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::LUT(in, in_lut);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::LUT(in_mat1, in_lut, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+TEST_P(ConvertToTest, AccuracyTest)
+{
+    auto param = GetParam();
+    int type_mat = std::get<0>(param);
+    int depth_to = std::get<1>(param);
+    cv::Size sz_in = std::get<2>(param);
+    int type_out = CV_MAKETYPE(depth_to, CV_MAT_CN(type_mat));
+    initMatrixRandU(type_mat, sz_in, type_out);
+    auto compile_args = std::get<3>(GetParam());
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::convertTo(in, depth_to);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        in_mat1.convertTo(out_mat_ocv, depth_to);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+
+} // opencv_test
+
+#endif //OPENCV_GAPI_CORE_TESTS_INL_HPP
diff --git a/modules/gapi/test/common/gapi_imgproc_tests.cpp b/modules/gapi/test/common/gapi_imgproc_tests.cpp
new file mode 100644 (file)
index 0000000..ea84191
--- /dev/null
@@ -0,0 +1,8 @@
+// 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
+
+
+#include "gapi_imgproc_tests_inl.hpp"
diff --git a/modules/gapi/test/common/gapi_imgproc_tests.hpp b/modules/gapi/test/common/gapi_imgproc_tests.hpp
new file mode 100644 (file)
index 0000000..517c125
--- /dev/null
@@ -0,0 +1,41 @@
+// 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_IMGPROC_TESTS_HPP
+#define OPENCV_GAPI_IMGPROC_TESTS_HPP
+
+#include <iostream>
+
+#include "gapi_tests_common.hpp"
+
+namespace opencv_test
+{
+struct Filter2DTest : public TestParams <std::tuple<MatType,int,cv::Size,int,int,bool,cv::GCompileArgs>> {};
+struct BoxFilterTest : public TestParams <std::tuple<MatType,int,cv::Size,int,int,double,bool,cv::GCompileArgs>> {};
+struct SepFilterTest : public TestParams <std::tuple<MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {};
+struct BlurTest : public TestParams <std::tuple<MatType,int,cv::Size,int,double,bool,cv::GCompileArgs>> {};
+struct GaussianBlurTest : public TestParams <std::tuple<MatType,int,cv::Size,bool,cv::GCompileArgs>> {};
+struct MedianBlurTest : public TestParams <std::tuple<MatType,int,cv::Size,bool,cv::GCompileArgs>> {};
+struct ErodeTest : public TestParams <std::tuple<MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {};
+struct Erode3x3Test : public TestParams <std::tuple<MatType,cv::Size,bool,int,cv::GCompileArgs>> {};
+struct DilateTest : public TestParams <std::tuple<MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {};
+struct Dilate3x3Test : public TestParams <std::tuple<MatType,cv::Size,bool,int,cv::GCompileArgs>> {};
+struct SobelTest : public TestParams <std::tuple<MatType,int,cv::Size,int,int,int,bool,cv::GCompileArgs>> {};
+struct EqHistTest : public TestParams <std::tuple<cv::Size,bool, cv::GCompileArgs>> {};
+struct CannyTest : public TestParams <std::tuple<MatType,cv::Size,double,double,int,bool,bool,cv::GCompileArgs>> {};
+struct RGB2GrayTest : public  TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct BGR2GrayTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct RGB2YUVTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct YUV2RGBTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct RGB2LabTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct BGR2LUVTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct LUV2BGRTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct BGR2YUVTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+struct YUV2BGRTest : public TestParams<std::tuple<cv::Size,bool,cv::GCompileArgs>> {};
+} // opencv_test
+
+#endif //OPENCV_GAPI_IMGPROC_TESTS_HPP
diff --git a/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp b/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp
new file mode 100644 (file)
index 0000000..9462ee3
--- /dev/null
@@ -0,0 +1,751 @@
+// 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_IMGPROC_TESTS_INL_HPP
+#define OPENCV_GAPI_IMGPROC_TESTS_INL_HPP
+
+#include "opencv2/gapi/imgproc.hpp"
+#include "gapi_imgproc_tests.hpp"
+
+namespace opencv_test
+{
+TEST_P(Filter2DTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0, borderType = 0, dtype = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, borderType, dtype, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, dtype, initOut);
+
+    cv::Point anchor = {-1, -1};
+    double delta = 0;
+
+    cv::Mat kernel = cv::Mat(kernSize, kernSize, CV_32FC1 );
+    cv::Scalar kernMean = cv::Scalar(1.0);
+    cv::Scalar kernStddev = cv::Scalar(2.0/3);
+    randn(kernel, kernMean, kernStddev);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::filter2D(in, dtype, kernel, anchor, delta, borderType);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::filter2D(in_mat1, out_mat_ocv, dtype, kernel, anchor, delta, borderType);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // TODO: Control this choice with test's especial parameter
+    #if 1
+        // Allow some rounding error
+        if (CV_MAT_DEPTH(out_mat_gapi.type()) == CV_32F)
+        {
+            // 6 decimal digits is nearly best accuracy we can expect of FP32 arithmetic here
+            EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) > 1e-6*cv::abs(out_mat_ocv)));
+        }
+        else
+        {
+            // allow wrong rounding if result fractional part is nearly 0.5,
+            // assume there would be not more than 0.01% of such cases
+            EXPECT_LE(cv::countNonZero(out_mat_gapi != out_mat_ocv), 1e-4*out_mat_ocv.total());
+        }
+    #else
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(BoxFilterTest, AccuracyTest)
+{
+    MatType type = 0;
+    int filterSize = 0, borderType = 0, dtype = 0;
+    cv::Size sz;
+    double tolerance = 0.0;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, filterSize, sz, borderType, dtype, tolerance, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, dtype, initOut);
+
+    cv::Point anchor = {-1, -1};
+    bool normalize = true;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::boxFilter(in, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::boxFilter(in_mat1, out_mat_ocv, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // TODO: Control this choice with test's especial parameter
+    #if 1
+        // Allow some rounding error
+        if (CV_MAT_DEPTH(out_mat_gapi.type()) == CV_32F)
+        {
+            // 6 decimal digits is nearly best accuracy we can expect of FP32 arithmetic here
+            EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) > 1e-6*cv::abs(out_mat_ocv)));
+        }
+        else
+        {
+            // allow wrong rounding if result fractional part is nearly 0.5,
+            // assume there would be not more than 0.01% of such cases
+            EXPECT_LE(cv::countNonZero(out_mat_gapi != out_mat_ocv), 1e-4*out_mat_ocv.total());
+        }
+    #else
+        cv::Mat absDiff;
+        cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+        EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(SepFilterTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0, dtype = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, dtype, initOut, compile_args) = GetParam();
+
+    cv::Mat kernelX(kernSize, 1, CV_32F);
+    cv::Mat kernelY(kernSize, 1, CV_32F);
+    randu(kernelX, -1, 1);
+    randu(kernelY, -1, 1);
+    initMatsRandN(type, sz, dtype, initOut);
+
+    cv::Point anchor = cv::Point(-1, -1);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::sepFilter(in, dtype, kernelX, kernelY, anchor, cv::Scalar() );
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::sepFilter2D(in_mat1, out_mat_ocv, dtype, kernelX, kernelY );
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // TODO: Control this choice with test's especial parameter
+    #if 1
+        // Expect some rounding error
+        EXPECT_LE(cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) > 1e-5 * cv::abs(out_mat_ocv)),
+                                                                         0.01 * out_mat_ocv.total());
+    #else
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(BlurTest, AccuracyTest)
+{
+    MatType type = 0;
+    int filterSize = 0, borderType = 0;
+    cv::Size sz;
+    double tolerance = 0.0;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, filterSize, sz, borderType, tolerance, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Point anchor = {-1, -1};
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::blur(in, cv::Size(filterSize, filterSize), anchor, borderType);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::blur(in_mat1, out_mat_ocv, cv::Size(filterSize, filterSize), anchor, borderType);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        cv::Mat absDiff;
+        cv::absdiff(out_mat_gapi, out_mat_ocv, absDiff);
+        EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(GaussianBlurTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Size kSize = cv::Size(kernSize, kernSize);
+    double sigmaX = rand();
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::gaussianBlur(in, kSize, sigmaX);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::GaussianBlur(in_mat1, out_mat_ocv, kSize, sigmaX);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        // TODO: Control this choice with test's especial parameter
+    #if 1
+        // Expect some rounding error
+        if (CV_MAT_DEPTH(out_mat_gapi.type()) == CV_32F ||
+            CV_MAT_DEPTH(out_mat_gapi.type()) == CV_64F)
+        {
+            // Note that 1e-6 is nearly best accuracy we can expect of FP32 arithetic
+            EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) >
+                                                    1e-6*cv::abs(out_mat_ocv)));
+        }
+        else if (CV_MAT_DEPTH(out_mat_gapi.type()) == CV_8U)
+        {
+            // OpenCV uses 16-bits fixed-point for 8U data, so may produce wrong results
+            EXPECT_LE(cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) > 1),
+                                                          0.05*out_mat_ocv.total());
+            EXPECT_LE(cv::countNonZero(cv::abs(out_mat_gapi - out_mat_ocv) > 2), 0);
+        }
+        else
+        {
+            EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        }
+    #else
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(MedianBlurTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::medianBlur(in, kernSize);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::medianBlur(in_mat1, out_mat_ocv, kernSize);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(ErodeTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0, kernType = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, kernType, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::erode(in, kernel);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::erode(in_mat1, out_mat_ocv, kernel);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(Erode3x3Test, AccuracyTest)
+{
+    MatType type = 0;
+    int numIters = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz, initOut, numIters, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3,3));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::erode3x3(in, numIters);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::erode(in_mat1, out_mat_ocv, kernel, cv::Point(-1, -1), numIters);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(DilateTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0, kernType = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, kernType, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::dilate(in, kernel);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::dilate(in_mat1, out_mat_ocv, kernel);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(Dilate3x3Test, AccuracyTest)
+{
+    MatType type = 0;
+    int numIters = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz, initOut, numIters, compile_args) = GetParam();
+    initMatsRandN(type, sz, type, initOut);
+
+    cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3,3));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::dilate3x3(in, numIters);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::dilate(in_mat1, out_mat_ocv, kernel, cv::Point(-1,-1), numIters);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+
+TEST_P(SobelTest, AccuracyTest)
+{
+    MatType type = 0;
+    int kernSize = 0, dtype = 0, dx = 0, dy = 0;
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, kernSize, sz, dtype, dx, dy, initOut, compile_args) = GetParam();
+    initMatsRandN(type, sz, dtype, initOut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::sobel(in, dtype, dx, dy, kernSize );
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Sobel(in_mat1, out_mat_ocv, dtype, dx, dy, kernSize);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(EqHistTest, AccuracyTest)
+{
+    cv::Size sz;
+    bool initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(sz, initOut, compile_args) = GetParam();
+    initMatsRandN(CV_8UC1, sz, CV_8UC1, initOut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::equalizeHist(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::equalizeHist(in_mat1, out_mat_ocv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(GetParam()));
+    }
+}
+
+TEST_P(CannyTest, AccuracyTest)
+{
+    MatType type;
+    int apSize = 0;
+    double thrLow = 0.0, thrUp = 0.0;
+    cv::Size sz;
+    bool l2gr = false, initOut = false;
+    cv::GCompileArgs compile_args;
+    std::tie(type, sz, thrLow, thrUp, apSize, l2gr, initOut, compile_args) = GetParam();
+
+    initMatsRandN(type, sz, CV_8UC1, initOut);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::Canny(in, thrLow, thrUp, apSize, l2gr);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Canny(in_mat1, out_mat_ocv, thrLow, thrUp, apSize, l2gr);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(RGB2GrayTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC1, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2Gray(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2GRAY);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding if result's fractional part is nearly 0.5
+        // - assume not more than 0.1% of pixels may deviate this way
+        // - deviation must not exceed 1 unit anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.001*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0);
+    #else
+        // insist of bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(BGR2GrayTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC1, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::BGR2Gray(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2GRAY);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding if result's fractional part is nearly 0.5
+        // - assume not more than 0.1% of pixels may deviate this way
+        // - deviation must not exceed 1 unit anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.001*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0);
+    #else
+        // insist of bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(RGB2YUVTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2YUV(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2YUV);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding if result's fractional part is nearly 0.5
+        // - assume not more than 15% of pixels may deviate this way
+        // - deviation must not exceed 1 unit anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.15*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0);
+    #else
+        // insist of bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(YUV2RGBTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::YUV2RGB(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2RGB);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding if result's fractional part is nearly 0.5
+        // - assume not more than 1% of pixels may deviate this way
+        // - deviation must not exceed 1 unit anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.01*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0);
+    #else
+        // insist of bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(RGB2LabTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::RGB2Lab(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2Lab);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding, if result's fractional part is nearly 0.5
+        // - assume not more than 25% of pixels may deviate this way
+        // - not more than 1% of pixels may deviate by 1 unit
+        // - deviation must not exceed 2 units anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.25*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0.01*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 2), 1e-5*3*out_mat_ocv.total());
+    #else
+        // insist on bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(BGR2LUVTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::BGR2LUV(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2Luv);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding, if result's fractional part is nearly 0.5
+        // - assume not more than 25% of pixels may deviate this way
+        // - not more than 1% of pixels may deviate by 2+ units
+        // - not more than 0.01% pixels may deviate by 5+ units
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.25   * 3 * out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0.01   * 3 * out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 5), 0.0001 * 3 * out_mat_ocv.total());
+    #else
+        // insist on bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(LUV2BGRTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::LUV2BGR(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_Luv2BGR);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(BGR2YUVTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::BGR2YUV(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2YUV);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+
+TEST_P(YUV2BGRTest, AccuracyTest)
+{
+    auto param = GetParam();
+    auto compile_args = std::get<2>(param);
+    initMatsRandN(CV_8UC3, std::get<0>(param), CV_8UC3, std::get<1>(param));
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::YUV2BGR(in);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2BGR);
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+    // TODO: control this choice with especial parameter of this test
+    #if 1
+        // allow faithful rounding, if result's fractional part is nearly 0.5
+        // - assume not more than 25% of pixels may deviate this way
+        // - not more than 1% of pixels may deviate by 1 unit
+        // - deviation must not exceed 2 units anyway
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 0), 0.25*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 1), 0.01*3*out_mat_ocv.total());
+        EXPECT_LE(cv::countNonZero(out_mat_gapi - out_mat_ocv > 2), 1e-5*3*out_mat_ocv.total());
+    #else
+        // insist on bit-exact results
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+    #endif
+        EXPECT_EQ(out_mat_gapi.size(), std::get<0>(param));
+    }
+}
+} // opencv_test
+
+#endif //OPENCV_GAPI_IMGPROC_TESTS_INL_HPP
diff --git a/modules/gapi/test/common/gapi_operators_tests.cpp b/modules/gapi/test/common/gapi_operators_tests.cpp
new file mode 100644 (file)
index 0000000..3fcf3f5
--- /dev/null
@@ -0,0 +1,8 @@
+// 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
+
+
+#include "gapi_operators_tests_inl.hpp"
diff --git a/modules/gapi/test/common/gapi_operators_tests.hpp b/modules/gapi/test/common/gapi_operators_tests.hpp
new file mode 100644 (file)
index 0000000..37bed92
--- /dev/null
@@ -0,0 +1,192 @@
+// 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_OPERATOR_TESTS_COMMON_HPP
+#define OPENCV_GAPI_OPERATOR_TESTS_COMMON_HPP
+
+#include "gapi_tests_common.hpp"
+
+namespace opencv_test
+{
+
+struct g_api_ocv_pair_mat_scalar {
+    using g_api_function_t = std::function<cv::GMat(cv::GMat,cv::GScalar)>;
+    using ocv_function_t   = std::function<void(cv::Mat const&, cv::Scalar, cv::Mat&)>;
+
+    std::string      name;
+    g_api_function_t g_api_function;
+    ocv_function_t   ocv_function;
+
+
+    g_api_ocv_pair_mat_scalar(std::string const& n, g_api_function_t const& g, ocv_function_t const& o)
+    : name(n), g_api_function(g), ocv_function(o) {}
+
+    g_api_ocv_pair_mat_scalar() = default;
+
+    friend std::ostream& operator<<(std::ostream& o, const g_api_ocv_pair_mat_scalar& p)
+    {
+        return o<<p.name;
+    }
+};
+
+struct g_api_ocv_pair_mat_mat {
+    using g_api_function_t = std::function<cv::GMat(cv::GMat,cv::GMat)>;
+    using ocv_function_t   = std::function<void(cv::Mat const&, cv::Mat const&, cv::Mat&)>;
+
+    std::string      name;
+    g_api_function_t g_api_function;
+    ocv_function_t   ocv_function;
+
+
+    g_api_ocv_pair_mat_mat(std::string const& n, g_api_function_t const& g, ocv_function_t const& o)
+    : name(n), g_api_function(g), ocv_function(o) {}
+
+    g_api_ocv_pair_mat_mat() = default;
+
+    friend std::ostream& operator<<(std::ostream& o, const g_api_ocv_pair_mat_mat& p)
+    {
+        return o<<p.name;
+    }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// FIXME: Please refactor this test to a template test (T,U) with enum (OP)
+//
+////////////////////////////////////////////////////////////////////////////////
+namespace
+{
+
+
+//declare test cases for matrix and scalar operators
+g_api_ocv_pair_mat_scalar opPlus =  {std::string{"operator+"},
+                                    [](cv::GMat in,cv::GScalar c){return in+c;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::add(in, c, out);}};
+g_api_ocv_pair_mat_scalar opPlusR = {std::string{"rev_operator+"},
+                                    [](cv::GMat in,cv::GScalar c){return c+in;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::add(c, in, out);}};
+g_api_ocv_pair_mat_scalar opMinus = {std::string{"operator-"},
+                                    [](cv::GMat in,cv::GScalar c){return in-c;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::subtract(in, c, out);}};
+g_api_ocv_pair_mat_scalar opMinusR = {std::string{"rev_operator-"},
+                                    [](cv::GMat in,cv::GScalar c){return c-in;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::subtract(c, in, out);}};
+g_api_ocv_pair_mat_scalar opMul =   {std::string{"operator*"},
+                                    [](cv::GMat in,cv::GScalar c){return in*c;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::multiply(in, c, out);}};
+g_api_ocv_pair_mat_scalar opMulR =  {std::string{"rev_operator*"},
+                                    [](cv::GMat in,cv::GScalar c){return c*in;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::multiply(c, in, out);}};
+g_api_ocv_pair_mat_scalar opDiv =   {std::string{"operator/"},
+                                    [](cv::GMat in,cv::GScalar c){return in/c;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::divide(in, c, out);}};
+g_api_ocv_pair_mat_scalar opDivR =  {std::string{"rev_operator/"},
+                                    [](cv::GMat in,cv::GScalar c){return c/in;},
+                                    [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::divide(c, in, out);}};
+
+g_api_ocv_pair_mat_scalar opGT = {std::string{"operator>"},
+                                            [](cv::GMat in,cv::GScalar c){return in>c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_GT);}};
+g_api_ocv_pair_mat_scalar opLT = {std::string{"operator<"},
+                                            [](cv::GMat in,cv::GScalar c){return in<c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_LT);}};
+g_api_ocv_pair_mat_scalar opGE = {std::string{"operator>="},
+                                            [](cv::GMat in,cv::GScalar c){return in>=c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_GE);}};
+g_api_ocv_pair_mat_scalar opLE = {std::string{"operator<="},
+                                            [](cv::GMat in,cv::GScalar c){return in<=c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_LE);}};
+g_api_ocv_pair_mat_scalar opEQ = {std::string{"operator=="},
+                                            [](cv::GMat in,cv::GScalar c){return in==c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_EQ);}};
+g_api_ocv_pair_mat_scalar opNE = {std::string{"operator!="},
+                                            [](cv::GMat in,cv::GScalar c){return in!=c;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(in, c, out,cv::CMP_NE);}};
+g_api_ocv_pair_mat_scalar opGTR = {std::string{"rev_operator>"},
+                                            [](cv::GMat in,cv::GScalar c){return c>in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_GT);}};
+g_api_ocv_pair_mat_scalar opLTR = {std::string{"rev_operator<"},
+                                            [](cv::GMat in,cv::GScalar c){return c<in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_LT);}};
+g_api_ocv_pair_mat_scalar opGER = {std::string{"rev_operator>="},
+                                            [](cv::GMat in,cv::GScalar c){return c>=in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_GE);}};
+g_api_ocv_pair_mat_scalar opLER = {std::string{"rev_operator<="},
+                                            [](cv::GMat in,cv::GScalar c){return c<=in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_LE);}};
+g_api_ocv_pair_mat_scalar opEQR = {std::string{"rev_operator=="},
+                                            [](cv::GMat in,cv::GScalar c){return c==in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_EQ);}};
+g_api_ocv_pair_mat_scalar opNER = {std::string{"rev_operator!="},
+                                            [](cv::GMat in,cv::GScalar c){return c!=in;},
+                                            [](const cv::Mat& in, cv::Scalar c, cv::Mat& out){cv::compare(c, in, out,cv::CMP_NE);}};
+
+g_api_ocv_pair_mat_scalar opAND = {std::string{"operator&"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in1&in2;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_and(in1, in2, out);}};
+g_api_ocv_pair_mat_scalar opOR = {std::string{"operator|"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in1|in2;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_or(in1, in2, out);}};
+g_api_ocv_pair_mat_scalar opXOR = {std::string{"operator^"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in1^in2;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_xor(in1, in2, out);}};
+g_api_ocv_pair_mat_scalar opANDR = {std::string{"rev_operator&"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in2&in1;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_and(in2, in1, out);}};
+g_api_ocv_pair_mat_scalar opORR = {std::string{"rev_operator|"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in2|in1;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_or(in2, in1, out);}};
+g_api_ocv_pair_mat_scalar opXORR = {std::string{"rev_operator^"},
+                                        [](cv::GMat in1,cv::GScalar in2){return in2^in1;},
+                                        [](const cv::Mat& in1, const cv::Scalar& in2, cv::Mat& out){cv::bitwise_xor(in2, in1, out);}};
+
+// declare test cases for matrix and matrix operators
+g_api_ocv_pair_mat_mat opPlusM =  {std::string{"operator+"},
+                                            [](cv::GMat in1,cv::GMat in2){return in1+in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::add(in1, in2, out);}};
+g_api_ocv_pair_mat_mat opMinusM = {std::string{"operator-"},
+                                            [](cv::GMat in,cv::GMat c){return in-c;},
+                                            [](const cv::Mat& in, const cv::Mat& c, cv::Mat& out){cv::subtract(in, c, out);}};
+g_api_ocv_pair_mat_mat opDivM = {std::string{"operator/"},
+                                            [](cv::GMat in,cv::GMat c){return in/c;},
+                                            [](const cv::Mat& in, const cv::Mat& c, cv::Mat& out){cv::divide(in, c, out);}};
+g_api_ocv_pair_mat_mat opGreater =  {std::string{"operator>"},
+                                            [](cv::GMat in1,cv::GMat in2){return in1>in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_GT);}};
+g_api_ocv_pair_mat_mat opGreaterEq = {std::string{"operator>="},
+                                            [](cv::GMat in1,cv::GMat in2){return in1>=in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_GE);}};
+g_api_ocv_pair_mat_mat opLess = {std::string{"operator<"},
+                                            [](cv::GMat in1,cv::GMat in2){return in1<in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_LT);}};
+g_api_ocv_pair_mat_mat opLessEq = {std::string{"operator<="},
+                                            [](cv::GMat in1,cv::GMat in2){return in1<=in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_LE);}};
+g_api_ocv_pair_mat_mat opEq = {std::string{"operator=="},
+                                            [](cv::GMat in1,cv::GMat in2){return in1==in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_EQ);}};
+g_api_ocv_pair_mat_mat opNotEq = {std::string{"operator!="},
+                                            [](cv::GMat in1,cv::GMat in2){return in1!=in2;},
+                                            [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::compare(in1, in2, out, cv::CMP_NE);}};
+
+g_api_ocv_pair_mat_mat opAnd = {std::string{"operator&"},
+                                        [](cv::GMat in1,cv::GMat in2){return in1&in2;},
+                                        [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::bitwise_and(in1, in2, out);}};
+g_api_ocv_pair_mat_mat opOr = {std::string{"operator|"},
+                                        [](cv::GMat in1,cv::GMat in2){return in1|in2;},
+                                        [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::bitwise_or(in1, in2, out);}};
+g_api_ocv_pair_mat_mat opXor = {std::string{"operator^"},
+                                        [](cv::GMat in1,cv::GMat in2){return in1^in2;},
+                                        [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::bitwise_xor(in1, in2, out);}};
+
+} // anonymous namespace
+struct MathOperatorMatScalarTest : public TestParams<std::tuple<g_api_ocv_pair_mat_scalar,int,cv::Size,int,bool,cv::GCompileArgs>>{};
+struct MathOperatorMatMatTest : public TestParams<std::tuple<g_api_ocv_pair_mat_mat,int,cv::Size,int,bool,cv::GCompileArgs>>{};
+struct NotOperatorTest : public TestParams<std::tuple<int,cv::Size,bool,cv::GCompileArgs>> {};
+} // opencv_test
+
+#endif // OPENCV_GAPI_OPERATOR_TESTS_COMMON_HPP
diff --git a/modules/gapi/test/common/gapi_operators_tests_inl.hpp b/modules/gapi/test/common/gapi_operators_tests_inl.hpp
new file mode 100644 (file)
index 0000000..5ac0e36
--- /dev/null
@@ -0,0 +1,102 @@
+// 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_OPERATOR_TESTS_INL_COMMON_HPP
+#define OPENCV_GAPI_OPERATOR_TESTS_INL_COMMON_HPP
+
+#include "gapi_operators_tests.hpp"
+
+namespace opencv_test
+{
+TEST_P(MathOperatorMatScalarTest, OperatorAccuracyTest )
+{
+    g_api_ocv_pair_mat_scalar op;
+    int type = 0, dtype = 0;
+    cv::Size sz;
+    bool initOutMatr = false;
+    cv::GCompileArgs compile_args;
+    std::tie(op, type, sz, dtype, initOutMatr, compile_args) = GetParam();
+    initMatsRandU(type, sz, dtype, initOutMatr);
+
+    auto fun_gapi = op.g_api_function;
+    auto fun_ocv = op.ocv_function ;
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+
+    cv::GMat in1;
+    cv::GScalar in2;
+    auto out = fun_gapi(in1, in2);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args));
+
+    fun_ocv(in_mat1, sc, out_mat_ocv);
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(MathOperatorMatMatTest, OperatorAccuracyTest )
+{
+    g_api_ocv_pair_mat_mat op;
+    int type = 0, dtype = 0;
+    cv::Size sz;
+    bool initOutMatr = false;
+    cv::GCompileArgs compile_args;
+    std::tie(op, type, sz, dtype, initOutMatr, compile_args) = GetParam();
+    initMatsRandU(type, sz, dtype, initOutMatr);
+
+    auto fun_gapi = op.g_api_function;
+    auto fun_ocv = op.ocv_function ;
+
+    // G-API code & corresponding OpenCV code ////////////////////////////////
+
+    cv::GMat in1;
+    cv::GMat in2;
+    auto out = fun_gapi(in1, in2);
+    cv::GComputation c(GIn(in1, in2), GOut(out));
+
+    c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args));
+
+    fun_ocv(in_mat1, in_mat2, out_mat_ocv);
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+        EXPECT_EQ(out_mat_gapi.size(), sz);
+    }
+}
+
+TEST_P(NotOperatorTest, OperatorAccuracyTest)
+{
+    cv::Size sz_in = std::get<1>(GetParam());
+    initMatrixRandU(std::get<0>(GetParam()), sz_in, std::get<0>(GetParam()), std::get<2>(GetParam()));
+    cv::GCompileArgs compile_args;
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = ~in;
+    cv::GComputation c(in, out);
+
+    c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        out_mat_ocv =~in_mat1;
+    }
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
+        EXPECT_EQ(out_mat_gapi.size(), sz_in);
+    }
+}
+} // opencv_test
+
+#endif // OPENCV_GAPI_OPERATOR_TESTS_INL_COMMON_HPP
diff --git a/modules/gapi/test/common/gapi_tests_common.hpp b/modules/gapi/test/common/gapi_tests_common.hpp
new file mode 100644 (file)
index 0000000..0c9e375
--- /dev/null
@@ -0,0 +1,108 @@
+// 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
+
+
+#include <iostream>
+
+#include "opencv2/ts.hpp"
+#include "opencv2/gapi.hpp"
+
+namespace
+{
+    inline std::ostream& operator<<(std::ostream& o, const cv::GCompileArg& arg)
+    {
+        return o << (arg.tag.empty() ? "empty" : arg.tag);
+    }
+}
+
+namespace opencv_test
+{
+
+class TestFunctional
+{
+public:
+    cv::Mat in_mat1;
+    cv::Mat in_mat2;
+    cv::Mat out_mat_gapi;
+    cv::Mat out_mat_ocv;
+
+    cv::Scalar sc;
+
+    void initMatsRandU(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true)
+    {
+        in_mat1 = cv::Mat(sz_in, type);
+        in_mat2 = cv::Mat(sz_in, type);
+
+        auto& rng = cv::theRNG();
+        sc = cv::Scalar(rng(100),rng(100),rng(100),rng(100));
+        cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+        cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+
+        if (createOutputMatrices && dtype != -1)
+        {
+            out_mat_gapi = cv::Mat (sz_in, dtype);
+            out_mat_ocv = cv::Mat (sz_in, dtype);
+        }
+    }
+
+    void initMatrixRandU(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true)
+    {
+        in_mat1 = cv::Mat(sz_in, type);
+
+        auto& rng = cv::theRNG();
+        sc = cv::Scalar(rng(100),rng(100),rng(100),rng(100));
+
+        cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+
+        if (createOutputMatrices && dtype != -1)
+        {
+            out_mat_gapi = cv::Mat (sz_in, dtype);
+            out_mat_ocv = cv::Mat (sz_in, dtype);
+        }
+    }
+
+    void initMatsRandN(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true)
+    {
+        in_mat1  = cv::Mat(sz_in, type);
+        cv::randn(in_mat1, cv::Scalar::all(127), cv::Scalar::all(40.f));
+
+        if (createOutputMatrices  && dtype != -1)
+        {
+            out_mat_gapi = cv::Mat(sz_in, dtype);
+            out_mat_ocv = cv::Mat(sz_in, dtype);
+        }
+    }
+
+    static cv::Mat nonZeroPixels(const cv::Mat& mat)
+    {
+        int channels = mat.channels();
+        std::vector<cv::Mat> split(channels);
+        cv::split(mat, split);
+        cv::Mat result;
+        for (int c=0; c < channels; c++)
+        {
+            if (c == 0)
+                result = split[c] != 0;
+            else
+                result = result | (split[c] != 0);
+        }
+        return result;
+    }
+
+    static int countNonZeroPixels(const cv::Mat& mat)
+    {
+        return cv::countNonZero( nonZeroPixels(mat) );
+    }
+
+};
+
+template<class T>
+class TestParams: public TestFunctional, public TestWithParam<T>{};
+
+template<class T>
+class TestPerfParams: public TestFunctional, public perf::TestBaseWithParam<T>{};
+
+}
diff --git a/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp b/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp
new file mode 100644 (file)
index 0000000..41d885f
--- /dev/null
@@ -0,0 +1,386 @@
+// 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
+
+
+#include "../test_precomp.hpp"
+#include "../common/gapi_core_tests.hpp"
+#include "opencv2/gapi/cpu/core.hpp"
+
+#define CORE_CPU cv::gapi::core::cpu::kernels()
+
+namespace opencv_test
+{
+
+// FIXME: Wut? See MulTestCPU/MathOpTest below (duplicate?)
+INSTANTIATE_TEST_CASE_P(AddTestCPU, MathOpTest,
+                        Combine(Values(ADD, MUL),
+                                testing::Bool(),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(1.0),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(false),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(MulTestCPU, MathOpTest,
+                        Combine(Values(MUL),
+                                testing::Bool(),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(1.0, 0.5, 2.0),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(false),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(SubTestCPU, MathOpTest,
+                        Combine(Values(SUB),
+                                testing::Bool(),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values (1.0),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(DivTestCPU, MathOpTest,
+                        Combine(Values(DIV),
+                                testing::Bool(),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values (1.0, 0.5, 2.0),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(MulTestCPU, MulDoubleTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(DivTestCPU, DivTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(DivCTestCPU, DivCTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(MeanTestCPU, MeanTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(MaskTestCPU, MaskTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SelectTestCPU, SelectTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Polar2CartCPU, Polar2CartTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Cart2PolarCPU, Cart2PolarTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(CompareTestCPU, CmpTest,
+                        Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE),
+                                testing::Bool(),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintCmpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(BitwiseTestCPU, BitwiseTest,
+                        Combine(Values(AND, OR, XOR),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintBWCoreParams());
+
+INSTANTIATE_TEST_CASE_P(BitwiseNotTestCPU, NotTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+ /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(MinTestCPU, MinTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(MaxTestCPU, MaxTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SumTestCPU, SumTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffTestCPU, AbsDiffTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffCTestCPU, AbsDiffCTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+// FIXME: Comparison introduced by YL doesn't work with C3
+INSTANTIATE_TEST_CASE_P(AddWeightedTestCPU, AddWeightedTest,
+                        Combine(Values( CV_8UC1/*, CV_8UC3*/, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values( -1, CV_8U, CV_16U, CV_32F ),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(NormTestCPU, NormTest,
+                        Combine(Values(NORM_INF, NORM_L1, NORM_L2),
+                                Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))),
+                        opencv_test::PrintNormCoreParams());
+
+INSTANTIATE_TEST_CASE_P(IntegralTestCPU, IntegralTest,
+                        Combine(Values( CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdOTTest,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+
+INSTANTIATE_TEST_CASE_P(InRangeTestCPU, InRangeTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Split3TestCPU, Split3Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Split4TestCPU, Split4Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::Size(64,64),
+                                       cv::Size(30,30)),
+                                Values(0.0),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTestFxFy,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(0.5, 0.1),
+                                Values(0.5, 0.1),
+                                Values(0.0),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Merge3TestCPU, Merge3Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Merge4TestCPU, Merge4Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(RemapTestCPU, RemapTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(FlipTestCPU, FlipTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(0,1,-1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(CropTestCPU, CropTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(LUTTestCPU, LUTTest,
+                        Combine(Values(CV_8UC1, CV_8UC3),
+                                Values(CV_8UC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(LUTTestCustomCPU, LUTTest,
+                        Combine(Values(CV_8UC3),
+                                Values(CV_8UC3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ConvertToCPU, ConvertToTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(CV_8U, CV_16U, CV_16S, CV_32F),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ConcatHorTestCPU, ConcatHorTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ConcatVertTestCPU, ConcatVertTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ConcatVertVecTestCPU, ConcatVertVecTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ConcatHorVecTestCPU, ConcatHorVecTest,
+                        Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_CPU))));
+}
diff --git a/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp b/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp
new file mode 100644 (file)
index 0000000..83bc6c1
--- /dev/null
@@ -0,0 +1,486 @@
+// 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
+
+
+#include "../test_precomp.hpp"
+#include "../common/gapi_core_tests.hpp"
+#include "backends/fluid/gfluidcore.hpp"
+
+namespace opencv_test
+{
+
+#define CORE_FLUID cv::gapi::core::fluid::kernels()
+
+// FIXME: Windows accuracy problems after recent update!
+INSTANTIATE_TEST_CASE_P(MathOpTestFluid, MathOpTest,
+                        Combine(Values(ADD, SUB, DIV, MUL),
+                                testing::Bool(),
+                                Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(1.0),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+                                testing::Bool(),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(MulSTestFluid, MulDoubleTest,
+                        Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1), // FIXME: extend with more types
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(DivCTestFluid, DivCTest,
+                        Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(CV_8U, CV_32F),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffTestFluid, AbsDiffTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffCTestFluid, AbsDiffCTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(BitwiseTestFluid, BitwiseTest,
+                        Combine(Values(AND, OR, XOR),
+                                Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))),
+                        opencv_test::PrintBWCoreParams());
+
+INSTANTIATE_TEST_CASE_P(BitwiseNotTestFluid, NotTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(MinTestFluid, MinTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(MaxTestFluid, MaxTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(CompareTestFluid, CmpTest,
+                        Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE),
+                                testing::Bool(),
+                                Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))),
+                        opencv_test::PrintCmpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(AddWeightedTestFluid, AddWeightedTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(LUTTestFluid, LUTTest,
+                        Combine(Values(CV_8UC1, CV_8UC3),
+                                Values(CV_8UC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(ConvertToFluid, ConvertToTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_32FC1),
+                                Values(CV_8U, CV_16U, CV_32F),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Split3TestFluid, Split3Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Split4TestFluid, Split4Test,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Merge3TestFluid, Merge3Test,
+                        Combine(Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Merge4TestFluid, Merge4Test,
+                        Combine(Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(SelectTestFluid, SelectTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Polar2CartFluid, Polar2CartTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(Cart2PolarFluid, Cart2PolarTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(ThresholdTestFluid, ThresholdTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV,
+                                       cv::THRESH_TRUNC,
+                                       cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(InRangeTestFluid, InRangeTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1920, 1080),
+                                       cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                testing::Bool(),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(ResizeTestFluid, ResizeTest,
+                        Combine(Values(CV_8UC3/*CV_8UC1, CV_16UC1, CV_16SC1*/),
+                                Values(/*cv::INTER_NEAREST,*/ cv::INTER_LINEAR/*, cv::INTER_AREA*/),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128),
+                                       cv::Size(64, 64),
+                                       cv::Size(30, 30)),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128),
+                                       cv::Size(64, 64),
+                                       cv::Size(30, 30)),
+                                Values(0.0),
+                                Values(cv::compile_args(CORE_FLUID))));
+
+//----------------------------------------------------------------------
+// FIXME: Clean-up test configurations which are enabled already
+#if 0
+INSTANTIATE_TEST_CASE_P(MathOpTestCPU, MathOpTest,
+                        Combine(Values(ADD, DIV, MUL),
+                                testing::Bool(),
+                                Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(false)),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(SubTestCPU, MathOpTest,
+                        Combine(Values(SUB),
+                                testing::Bool(),
+                                Values(CV_8UC1, CV_16SC1 , CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+/*init output matrices or not*/ testing::Bool(),
+                                testing::Bool()),
+                        opencv_test::PrintMathOpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(MulSTestCPU, MulSTest,
+                        Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(DivCTestCPU, DivCTest,
+                        Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(CV_8U, CV_32F),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(MeanTestCPU, MeanTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(SelectTestCPU, SelectTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(Polar2CartCPU, Polar2CartTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(Cart2PolarCPU, Cart2PolarTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(CompareTestCPU, CmpTest,
+                        Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE),
+                                testing::Bool(),
+                                Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()),
+                        opencv_test::PrintCmpCoreParams());
+
+INSTANTIATE_TEST_CASE_P(BitwiseTestCPU, BitwiseTest,
+                        Combine(Values(AND, OR, XOR),
+                                Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()),
+                        opencv_test::PrintBWCoreParams());
+
+INSTANTIATE_TEST_CASE_P(BitwiseNotTestCPU, NotTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+ /*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(MinTestCPU, MinTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(MaxTestCPU, MaxTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(SumTestCPU, SumTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffTestCPU, AbsDiffTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(AbsDiffCTestCPU, AbsDiffCTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(AddWeightedTestCPU, AddWeightedTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(NormTestCPU, NormTest,
+                        Combine(Values(NORM_INF, NORM_L1, NORM_L2),
+                                Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))),
+                        opencv_test::PrintNormCoreParams());
+
+INSTANTIATE_TEST_CASE_P(IntegralTestCPU, IntegralTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdOTTest,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE),
+/*init output matrices or not*/ testing::Bool()));
+
+
+INSTANTIATE_TEST_CASE_P(InRangeTestCPU, InRangeTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(Split3TestCPU, Split3Test,
+                        (Values(cv::Size(1280, 720),
+                                cv::Size(640, 480),
+                                cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(Split4TestCPU, Split4Test,
+                        (Values(cv::Size(1280, 720),
+                                cv::Size(640, 480),
+                                cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(Merge3TestCPU, Merge3Test,
+                        (Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(Merge4TestCPU, Merge4Test,
+                        (Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(RemapTestCPU, RemapTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(FlipTestCPU, FlipTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(0,1,-1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(CropTestCPU, CropTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(LUTTestCPU, LUTTest,
+                        Combine(Values(CV_8UC1, CV_8UC3),
+                                Values(CV_8UC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(LUTTestCustomCPU, LUTTest,
+                        Combine(Values(CV_8UC3),
+                                Values(CV_8UC3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+/*init output matrices or not*/ testing::Bool()));
+
+INSTANTIATE_TEST_CASE_P(ConvertToCPU, ConvertToTest,
+                        Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_32FC1),
+                                Values(CV_8U, CV_16U, CV_32F),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+
+INSTANTIATE_TEST_CASE_P(ConcatHorTestCPU, ConcatHorTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+INSTANTIATE_TEST_CASE_P(ConcatVertTestCPU, ConcatVertTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128))));
+
+//----------------------------------------------------------------------
+#endif // 0
+
+}
diff --git a/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp b/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp
new file mode 100644 (file)
index 0000000..dc6a560
--- /dev/null
@@ -0,0 +1,204 @@
+// 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
+
+
+#include "../test_precomp.hpp"
+
+#include "../common/gapi_imgproc_tests.hpp"
+#include "opencv2/gapi/cpu/imgproc.hpp"
+
+#define IMGPROC_CPU cv::gapi::imgproc::cpu::kernels()
+
+namespace opencv_test
+{
+
+INSTANTIATE_TEST_CASE_P(Filter2DTestCPU, Filter2DTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 4, 5, 7),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(-1, CV_32F),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(BoxFilterTestCPU, BoxFilterTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3,5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(-1, CV_32F),
+                                Values(0.0),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SepFilterTestCPU_8U, SepFilterTest,
+                        Combine(Values(CV_8UC1, CV_8UC3),
+                                Values(3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_16S, CV_32F),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SepFilterTestCPU_other, SepFilterTest,
+                        Combine(Values(CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_32F),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(BlurTestCPU, BlurTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3,5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(0.0),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(gaussBlurTestCPU, GaussianBlurTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(MedianBlurTestCPU, MedianBlurTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(ErodeTestCPU, ErodeTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::MorphShapes::MORPH_RECT,
+                                       cv::MorphShapes::MORPH_CROSS,
+                                       cv::MorphShapes::MORPH_ELLIPSE),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Erode3x3TestCPU, Erode3x3Test,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(1,2,4),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(DilateTestCPU, DilateTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::MorphShapes::MORPH_RECT,
+                                       cv::MorphShapes::MORPH_CROSS,
+                                       cv::MorphShapes::MORPH_ELLIPSE),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(Dilate3x3TestCPU, Dilate3x3Test,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(1,2,4),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SobelTestCPU, SobelTest,
+                        Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_32F),
+                                Values(0, 1),
+                                Values(1, 2),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(EqHistTestCPU, EqHistTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(CannyTestCPU, CannyTest,
+                        Combine(Values(CV_8UC1, CV_8UC3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(3.0, 120.0),
+                                Values(125.0, 240.0),
+                                Values(3, 5),
+                                testing::Bool(),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(RGB2GrayTestCPU, RGB2GrayTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(BGR2GrayTestCPU, BGR2GrayTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(RGB2YUVTestCPU, RGB2YUVTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(YUV2RGBTestCPU, YUV2RGBTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(RGB2LabTestCPU, RGB2LabTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(BGR2LUVTestCPU, BGR2LUVTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(LUV2BGRTestCPU, LUV2BGRTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(BGR2YUVTestCPU, BGR2YUVTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(YUV2BGRTestCPU, YUV2BGRTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+/*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+} // opencv_test
diff --git a/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp b/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp
new file mode 100644 (file)
index 0000000..387967f
--- /dev/null
@@ -0,0 +1,145 @@
+// 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
+
+
+#include "../test_precomp.hpp"
+#include "../common/gapi_imgproc_tests.hpp"
+#include "backends/fluid/gfluidimgproc.hpp"
+
+#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels()
+
+namespace opencv_test
+{
+
+INSTANTIATE_TEST_CASE_P(RGB2GrayTestFluid, RGB2GrayTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(BGR2GrayTestFluid, BGR2GrayTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(RGB2YUVTestFluid, RGB2YUVTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(YUV2RGBTestFluid, YUV2RGBTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(RGB2LabTestFluid, RGB2LabTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+// FIXME: Not supported by Fluid yet (no kernel implemented)
+INSTANTIATE_TEST_CASE_P(BGR2LUVTestFluid, BGR2LUVTest,
+                        Combine(Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(blurTestFluid, BlurTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(0.0),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(gaussBlurTestFluid, GaussianBlurTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(medianBlurTestFluid, MedianBlurTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(erodeTestFluid, ErodeTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::MorphShapes::MORPH_RECT,
+                                       cv::MorphShapes::MORPH_CROSS,
+                                       cv::MorphShapes::MORPH_ELLIPSE),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(dilateTestFluid, DilateTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::MorphShapes::MORPH_RECT,
+                                       cv::MorphShapes::MORPH_CROSS,
+                                       cv::MorphShapes::MORPH_ELLIPSE),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(SobelTestFluid, SobelTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_32F),
+                                Values(0, 1),
+                                Values(1, 2),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(boxFilterTestFluid, BoxFilterTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(-1, CV_32F),
+                                Values(0.0),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+// FIXME: Tests are failing on Fluid backend (accuracy issue?)
+INSTANTIATE_TEST_CASE_P(sepFilterTestFluid, SepFilterTest,
+                        Combine(Values(CV_32FC1),
+                                Values(3), // add kernel size=5 when implementation is ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_32F),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(filter2DTestFluid, Filter2DTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(3), // add kernel size=4,5,7 when implementation ready
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                Values(cv::BORDER_DEFAULT),
+                                Values(-1, CV_32F),
+                                Values(true, false),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+} // opencv_test
diff --git a/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp b/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp
new file mode 100644 (file)
index 0000000..9224678
--- /dev/null
@@ -0,0 +1,67 @@
+// 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
+
+
+#include "../test_precomp.hpp"
+#include "../common/gapi_operators_tests.hpp"
+#include "opencv2/gapi/cpu/core.hpp"
+
+#define CORE_CPU cv::gapi::core::cpu::kernels()
+
+namespace opencv_test
+{
+    // FIXME: CPU test runs are disabled since Fluid is an exclusive plugin now!
+    INSTANTIATE_TEST_CASE_P(MathOperatorTestCPU, MathOperatorMatMatTest,
+                        Combine(Values( opPlusM, opMinusM, opDivM,
+                                        opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq),
+                                Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                Values(cv::Size(1280, 720),
+                                   cv::Size(640, 480),
+                                   cv::Size(128, 128)),
+                                Values(-1, CV_8U, CV_32F),
+    /*init output matrices or not*/ testing::Bool(),
+                                Values(cv::compile_args(CORE_CPU))));
+
+    INSTANTIATE_TEST_CASE_P(MathOperatorTestCPU, MathOperatorMatScalarTest,
+                            Combine(Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, opDiv, opDivR,
+                                            opGT, opLT, opGE, opLE, opEQ, opNE,
+                                            opGTR, opLTR, opGER, opLER, opEQR, opNER),
+                                    Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                    Values(cv::Size(1280, 720),
+                                           cv::Size(640, 480),
+                                           cv::Size(128, 128)),
+                                    Values(-1, CV_8U, CV_32F),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_CPU))));
+
+    INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestCPU, MathOperatorMatMatTest,
+                            Combine(Values( opAnd, opOr, opXor ),
+                                    Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                    Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                    Values(-1),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_CPU))));
+
+    INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestCPU, MathOperatorMatScalarTest,
+                            Combine(Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ),
+                                    Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                    Values(cv::Size(1280, 720),
+                                           cv::Size(640, 480),
+                                           cv::Size(128, 128)),
+                                    Values(-1),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_CPU))));
+
+    INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestCPU, NotOperatorTest,
+                            Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                    Values(cv::Size(1280, 720),
+                                           cv::Size(640, 480),
+                                           cv::Size(128, 128)),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_CPU))));
+}
diff --git a/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp b/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp
new file mode 100644 (file)
index 0000000..7ad834b
--- /dev/null
@@ -0,0 +1,67 @@
+// 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
+
+
+#include "../common/gapi_operators_tests.hpp"
+#include "opencv2/gapi/cpu/core.hpp"
+
+#define CORE_FLUID cv::gapi::core::cpu::kernels()
+
+namespace opencv_test
+{
+    INSTANTIATE_TEST_CASE_P(MathOperatorTestFluid, MathOperatorMatMatTest,
+                            Combine(Values( opPlusM, opMinusM, opDivM,
+                                            opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq),
+                                    Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                    Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                    Values(-1, CV_8U, CV_32F),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_FLUID))));
+
+    //FIXME: Some Mat/Scalar Fluid kernels are not there yet!
+    INSTANTIATE_TEST_CASE_P(DISABLED_MathOperatorTestFluid, MathOperatorMatScalarTest,
+                            Combine(Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, opDiv, opDivR,
+                                            opGT, opLT, opGE, opLE, opEQ, opNE,
+                                            opGTR, opLTR, opGER, opLER, opEQR, opNER),
+                                    Values(CV_8UC1, CV_16SC1, CV_32FC1),
+                                    Values(cv::Size(1280, 720),
+                                           cv::Size(640, 480),
+                                           cv::Size(128, 128)),
+                                    Values(-1, CV_8U, CV_32F),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_FLUID))));
+
+    INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestFluid, MathOperatorMatMatTest,
+                            Combine(Values( opAnd, opOr, opXor ),
+                                    Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                    Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+                                    Values(-1),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_FLUID))));
+
+    //FIXME: Some Mat/Scalar Fluid kernels are not there yet!
+    INSTANTIATE_TEST_CASE_P(DISABLED_BitwiseOperatorTestFluid, MathOperatorMatScalarTest,
+                            Combine(Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ),
+                                    Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                    Values(cv::Size(1280, 720),
+                                           cv::Size(640, 480),
+                                           cv::Size(128, 128)),
+                                    Values(-1),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_FLUID))));
+
+    INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestFluid, NotOperatorTest,
+                        Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480),
+                                       cv::Size(128, 128)),
+    /*init output matrices or not*/ testing::Bool(),
+                                    Values(cv::compile_args(CORE_FLUID))));
+}
diff --git a/modules/gapi/test/gapi_array_tests.cpp b/modules/gapi/test/gapi_array_tests.cpp
new file mode 100644 (file)
index 0000000..e576562
--- /dev/null
@@ -0,0 +1,166 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include <vector>
+#include <ade/util/algorithm.hpp>
+
+namespace opencv_test
+{
+
+namespace ThisTest
+{
+using GPointArray = cv::GArray<cv::Point>;
+G_TYPED_KERNEL(GeneratePoints, <GPointArray(GMat)>, "test.array.out_const")
+{
+    static GArrayDesc outMeta(const GMatDesc&) { return empty_array_desc(); }
+};
+G_TYPED_KERNEL(FindCorners,    <GPointArray(GMat)>, "test.array.out")
+{
+    static GArrayDesc outMeta(const GMatDesc&) { return empty_array_desc(); }
+};
+G_TYPED_KERNEL(CountCorners,   <GScalar(GPointArray)>,  "test.array.in")
+{
+    static GScalarDesc outMeta(const GArrayDesc &) { return empty_scalar_desc(); }
+};
+} // namespace ThisTest
+
+namespace
+{
+GAPI_OCV_KERNEL(OCVGeneratePoints, ThisTest::GeneratePoints)
+{
+    static void run(cv::Mat, std::vector<cv::Point> &out)
+    {
+        for (int i = 0; i < 10; i++)
+            out.emplace_back(i, i);
+    }
+};
+
+GAPI_OCV_KERNEL(OCVFindCorners, ThisTest::FindCorners)
+{
+    static void run(cv::Mat in, std::vector<cv::Point> &out)
+    {
+        cv::goodFeaturesToTrack(in, out, 1024, 0.01, 3);
+    }
+};
+
+GAPI_OCV_KERNEL(OCVCountCorners, ThisTest::CountCorners)
+{
+    static void run(const std::vector<cv::Point> &in, cv::Scalar &out)
+    {
+        out[0] = static_cast<double>(in.size());
+    }
+};
+
+cv::Mat cross(int w, int h)
+{
+    cv::Mat mat = cv::Mat::eye(h, w, CV_8UC1)*255;
+    cv::Mat yee;
+    cv::flip(mat, yee, 0); // X-axis
+    mat |= yee;            // make an "X" matrix;
+    return mat;
+}
+} // (anonymous namespace)
+
+TEST(GArray, TestReturnValue)
+{
+    // FIXME: Make .apply() able to take compile arguments
+    cv::GComputationT<ThisTest::GPointArray(cv::GMat)> c(ThisTest::FindCorners::on);
+    auto cc = c.compile(cv::GMatDesc{CV_8U,1,{32,32}},
+                        cv::compile_args(cv::gapi::kernels<OCVFindCorners>()));
+
+    // Prepare input matrix
+    cv::Mat input = cross(32, 32);
+
+    std::vector<cv::Point> points;
+    cc(input, points);
+
+    // OCV goodFeaturesToTrack should find 5 points here (with these settings)
+    EXPECT_EQ(5u, points.size());
+    EXPECT_TRUE(ade::util::find(points, cv::Point(16,16)) != points.end());
+    EXPECT_TRUE(ade::util::find(points, cv::Point(30,30)) != points.end());
+    EXPECT_TRUE(ade::util::find(points, cv::Point( 1,30)) != points.end());
+    EXPECT_TRUE(ade::util::find(points, cv::Point(30, 1)) != points.end());
+    EXPECT_TRUE(ade::util::find(points, cv::Point( 1, 1)) != points.end());
+}
+
+TEST(GArray, TestInputArg)
+{
+    cv::GComputationT<cv::GScalar(ThisTest::GPointArray)> c(ThisTest::CountCorners::on);
+    auto cc = c.compile(cv::empty_array_desc(),
+                        cv::compile_args(cv::gapi::kernels<OCVCountCorners>()));
+
+    const std::vector<cv::Point> arr = {cv::Point(1,1), cv::Point(2,2)};
+    cv::Scalar out;
+    cc(arr, out);
+    EXPECT_EQ(2, out[0]);
+}
+
+TEST(GArray, TestPipeline)
+{
+    cv::GComputationT<cv::GScalar(cv::GMat)> c([](cv::GMat in)
+    {
+        return ThisTest::CountCorners::on(ThisTest::FindCorners::on(in));
+    });
+    auto cc = c.compile(cv::GMatDesc{CV_8U,1,{32,32}},
+                        cv::compile_args(cv::gapi::kernels<OCVFindCorners, OCVCountCorners>()));
+
+    cv::Mat input = cross(32, 32);
+    cv::Scalar out;
+    cc(input, out);
+    EXPECT_EQ(5, out[0]);
+}
+
+TEST(GArray, NoAggregationBetweenRuns)
+{
+    cv::GComputationT<cv::GScalar(cv::GMat)> c([](cv::GMat in)
+    {
+        return ThisTest::CountCorners::on(ThisTest::GeneratePoints::on(in));
+    });
+    auto cc = c.compile(cv::GMatDesc{CV_8U,1,{32,32}},
+                        cv::compile_args(cv::gapi::kernels<OCVGeneratePoints, OCVCountCorners>()));
+
+    cv::Mat input = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Scalar out;
+
+    cc(input, out);
+    EXPECT_EQ(10, out[0]);
+
+    // Last kernel in the graph counts number of elements in array, returned by the previous kernel
+    // (in this test, this variable is constant).
+    // After 10 executions, this number MUST remain the same - 1st kernel is adding new values on every
+    // run, but it is graph's responsibility to reset internal object state.
+    cv::Scalar out2;
+    for (int i = 0; i < 10; i++)
+    {
+        cc(input, out2);
+    }
+    EXPECT_EQ(10, out2[0]);
+}
+
+TEST(GArray, TestIntermediateOutput)
+{
+    using Result = std::tuple<ThisTest::GPointArray, cv::GScalar>;
+    cv::GComputationT<Result(cv::GMat)> c([](cv::GMat in)
+    {
+        auto corners = ThisTest::GeneratePoints::on(in);
+        return std::make_tuple(corners, ThisTest::CountCorners::on(corners));
+    });
+
+    cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
+    std::vector<cv::Point> out_points;
+    cv::Scalar out_count;
+
+    auto cc = c.compile(cv::descr_of(in_mat),
+                        cv::compile_args(cv::gapi::kernels<OCVGeneratePoints, OCVCountCorners>()));
+    cc(in_mat, out_points, out_count);
+
+    EXPECT_EQ(10u, out_points.size());
+    EXPECT_EQ(10,  out_count[0]);
+}
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_basic_hetero_tests.cpp b/modules/gapi/test/gapi_basic_hetero_tests.cpp
new file mode 100644 (file)
index 0000000..0bf67b1
--- /dev/null
@@ -0,0 +1,124 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "gapi_mock_kernels.hpp"
+
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    GAPI_OCV_KERNEL(OCVFoo, I::Foo)
+    {
+        static void run(const cv::Mat &in, cv::Mat &out)
+        {
+            out = in + 2;
+        }
+    };
+
+    GAPI_OCV_KERNEL(OCVBar, I::Bar)
+    {
+        static void run(const cv::Mat &a, const cv::Mat &b, cv::Mat &out)
+        {
+            out = 4*(a + b);
+        }
+    };
+
+    GAPI_FLUID_KERNEL(FFoo, I::Foo, false)
+    {
+        static const int Window = 1;
+
+        static void run(const cv::gapi::fluid::View   &in,
+                              cv::gapi::fluid::Buffer &out)
+        {
+            const uint8_t* in_ptr = in.InLine<uint8_t>(0);
+            uint8_t *out_ptr = out.OutLine<uint8_t>();
+            for (int i = 0; i < in.length(); i++)
+            {
+                out_ptr[i] = in_ptr[i] + 3;
+            }
+        }
+    };
+
+    GAPI_FLUID_KERNEL(FBar, I::Bar, false)
+    {
+        static const int Window = 1;
+
+        static void run(const cv::gapi::fluid::View   &in1,
+                        const cv::gapi::fluid::View   &in2,
+                              cv::gapi::fluid::Buffer &out)
+        {
+            const uint8_t* in1_ptr = in1.InLine<uint8_t>(0);
+            const uint8_t* in2_ptr = in2.InLine<uint8_t>(0);
+            uint8_t *out_ptr = out.OutLine<uint8_t>();
+            for (int i = 0; i < in1.length(); i++)
+            {
+                out_ptr[i] = 3*(in1_ptr[i] + in2_ptr[i]);
+            }
+        }
+    };
+} // anonymous namespace
+
+struct GAPIHeteroTest: public ::testing::Test
+{
+    cv::GComputation m_comp;
+    cv::gapi::GKernelPackage m_ocv_kernels;
+    cv::gapi::GKernelPackage m_fluid_kernels;
+    cv::gapi::GKernelPackage m_hetero_kernels;
+
+    cv::Mat m_in_mat;
+    cv::Mat m_out_mat;
+
+    GAPIHeteroTest();
+};
+
+GAPIHeteroTest::GAPIHeteroTest()
+    : m_comp([](){
+            cv::GMat in;
+            cv::GMat out = I::Bar::on(I::Foo::on(in),
+                                      I::Foo::on(in));
+            return cv::GComputation(in, out);
+        })
+    , m_ocv_kernels(cv::gapi::kernels<OCVFoo, OCVBar>())
+    , m_fluid_kernels(cv::gapi::kernels<FFoo, FBar>())
+    , m_hetero_kernels(cv::gapi::kernels<OCVFoo, FBar>())
+    , m_in_mat(cv::Mat::eye(cv::Size(64, 64), CV_8UC1))
+{
+}
+
+TEST_F(GAPIHeteroTest, TestOCV)
+{
+    EXPECT_TRUE(cv::gapi::cpu::backend() == m_ocv_kernels.lookup<I::Foo>());
+    EXPECT_TRUE(cv::gapi::cpu::backend() == m_ocv_kernels.lookup<I::Bar>());
+
+    cv::Mat ref = 4*(m_in_mat+2 + m_in_mat+2);
+    EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_ocv_kernels)));
+    EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat));
+}
+
+TEST_F(GAPIHeteroTest, TestFluid)
+{
+    EXPECT_TRUE(cv::gapi::fluid::backend() == m_fluid_kernels.lookup<I::Foo>());
+    EXPECT_TRUE(cv::gapi::fluid::backend() == m_fluid_kernels.lookup<I::Bar>());
+
+    cv::Mat ref = 3*(m_in_mat+3 + m_in_mat+3);
+    EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_fluid_kernels)));
+    EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat));
+}
+
+TEST_F(GAPIHeteroTest, TestBoth_ExpectFailure)
+{
+    EXPECT_TRUE(cv::gapi::cpu::backend()   == m_hetero_kernels.lookup<I::Foo>());
+    EXPECT_TRUE(cv::gapi::fluid::backend() == m_hetero_kernels.lookup<I::Bar>());
+    EXPECT_ANY_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_hetero_kernels)));
+}
+
+
+}  // namespace opencv_test
diff --git a/modules/gapi/test/gapi_desc_tests.cpp b/modules/gapi/test/gapi_desc_tests.cpp
new file mode 100644 (file)
index 0000000..711211d
--- /dev/null
@@ -0,0 +1,202 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    G_TYPED_KERNEL(KTest, <cv::GScalar(cv::GScalar)>, "org.opencv.test.scalar_kernel") {
+        static cv::GScalarDesc outMeta(cv::GScalarDesc in) { return in; }
+    };
+    GAPI_OCV_KERNEL(GOCVScalarTest, KTest)
+    {
+        static void run(const cv::Scalar &in, cv::Scalar &out) { out = in+cv::Scalar(1); }
+    };
+}
+
+TEST(GAPI_MetaDesc, MatDesc)
+{
+    cv::Mat m1(240, 320, CV_8U);
+    const auto desc1 = cv::descr_of(m1);
+    EXPECT_EQ(CV_8U, desc1.depth);
+    EXPECT_EQ(1,     desc1.chan);
+    EXPECT_EQ(320,   desc1.size.width);
+    EXPECT_EQ(240,   desc1.size.height);
+
+    cv::Mat m2(480, 640, CV_8UC3);
+    const auto desc2 = cv::descr_of(m2);
+    EXPECT_EQ(CV_8U, desc2.depth);
+    EXPECT_EQ(3,       desc2.chan);
+    EXPECT_EQ(640,     desc2.size.width);
+    EXPECT_EQ(480,     desc2.size.height);
+}
+
+TEST(GAPI_MetaDesc, Compare_Equal_MatDesc)
+{
+    const auto desc1 = cv::GMatDesc{CV_8U, 1, {64, 64}};
+    const auto desc2 = cv::GMatDesc{CV_8U, 1, {64, 64}};
+
+    EXPECT_TRUE(desc1 == desc2);
+}
+
+TEST(GAPI_MetaDesc, Compare_Not_Equal_MatDesc)
+{
+    const auto desc1 = cv::GMatDesc{CV_8U,  1, {64, 64}};
+    const auto desc2 = cv::GMatDesc{CV_32F, 1, {64, 64}};
+
+    EXPECT_TRUE(desc1 != desc2);
+}
+
+TEST(GAPI_MetaDesc, Compile_MatchMetaNumber_1)
+{
+    cv::GMat in;
+    cv::GComputation cc(in, in+in);
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    const auto desc2 = cv::GMatDesc{CV_32F,1,{128,128}};
+
+    EXPECT_NO_THROW(cc.compile(desc1));
+    EXPECT_NO_THROW(cc.compile(desc2));
+
+    // FIXME: custom exception type?
+    // It is worth checking if compilation fails with different number
+    // of meta parameters
+    EXPECT_THROW(cc.compile(desc1, desc1),        std::logic_error);
+    EXPECT_THROW(cc.compile(desc1, desc2, desc2), std::logic_error);
+}
+
+TEST(GAPI_MetaDesc, Compile_MatchMetaNumber_2)
+{
+    cv::GMat a, b;
+    cv::GComputation cc(cv::GIn(a, b), cv::GOut(a+b));
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    EXPECT_NO_THROW(cc.compile(desc1, desc1));
+
+    const auto desc2 = cv::GMatDesc{CV_32F,1,{128,128}};
+    EXPECT_NO_THROW(cc.compile(desc2, desc2));
+
+    // FIXME: custom exception type?
+    EXPECT_THROW(cc.compile(desc1),               std::logic_error);
+    EXPECT_THROW(cc.compile(desc2),               std::logic_error);
+    EXPECT_THROW(cc.compile(desc2, desc2, desc2), std::logic_error);
+}
+
+TEST(GAPI_MetaDesc, Compile_MatchMetaType_Mat)
+{
+    cv::GMat in;
+    cv::GComputation cc(in, in+in);
+
+    EXPECT_NO_THROW(cc.compile(cv::GMatDesc{CV_8U,1,{64,64}}));
+
+    // FIXME: custom exception type?
+    EXPECT_THROW(cc.compile(cv::empty_scalar_desc()), std::logic_error);
+}
+
+TEST(GAPI_MetaDesc, Compile_MatchMetaType_Scalar)
+{
+    cv::GScalar in;
+    cv::GComputation cc(cv::GIn(in), cv::GOut(KTest::on(in)));
+
+    const auto desc1 = cv::descr_of(cv::Scalar(128));
+    const auto desc2 = cv::GMatDesc{CV_8U,1,{64,64}};
+    const auto pkg   = cv::gapi::kernels<GOCVScalarTest>();
+    EXPECT_NO_THROW(cc.compile(desc1, cv::compile_args(pkg)));
+
+    // FIXME: custom exception type?
+    EXPECT_THROW(cc.compile(desc2, cv::compile_args(pkg)), std::logic_error);
+}
+
+TEST(GAPI_MetaDesc, Compile_MatchMetaType_Mixed)
+{
+    cv::GMat a;
+    cv::GScalar v;
+    cv::GComputation cc(cv::GIn(a, v), cv::GOut(cv::gapi::addC(a, v)));
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    const auto desc2 = cv::descr_of(cv::Scalar(4));
+
+    EXPECT_NO_THROW(cc.compile(desc1, desc2));
+
+    // FIXME: custom exception type(s)?
+    EXPECT_THROW(cc.compile(desc1),               std::logic_error);
+    EXPECT_THROW(cc.compile(desc2),               std::logic_error);
+    EXPECT_THROW(cc.compile(desc2, desc1),        std::logic_error);
+    EXPECT_THROW(cc.compile(desc1, desc1, desc1), std::logic_error);
+    EXPECT_THROW(cc.compile(desc1, desc2, desc1), std::logic_error);
+}
+
+TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaNumber_1)
+{
+    cv::GComputationT<cv::GMat(cv::GMat)> cc([](cv::GMat in)
+    {
+        return in+in;
+    });
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    const auto desc2 = cv::GMatDesc{CV_32F,1,{128,128}};
+
+    EXPECT_NO_THROW(cc.compile(desc1));
+    EXPECT_NO_THROW(cc.compile(desc2));
+}
+
+TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaNumber_2)
+{
+    cv::GComputationT<cv::GMat(cv::GMat,cv::GMat)> cc([](cv::GMat a, cv::GMat b)
+    {
+        return a + b;
+    });
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    EXPECT_NO_THROW(cc.compile(desc1, desc1));
+
+    const auto desc2 = cv::GMatDesc{CV_32F,1,{128,128}};
+    EXPECT_NO_THROW(cc.compile(desc2, desc2));
+}
+
+TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaType_Mat)
+{
+    cv::GComputationT<cv::GMat(cv::GMat)> cc([](cv::GMat in)
+    {
+        return in+in;
+    });
+
+    EXPECT_NO_THROW(cc.compile(cv::GMatDesc{CV_8U,1,{64,64}}));
+}
+
+TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaType_Scalar)
+{
+    cv::GComputationT<cv::GScalar(cv::GScalar)> cc([](cv::GScalar in)
+    {
+        return KTest::on(in);
+    });
+
+    const auto desc1 = cv::descr_of(cv::Scalar(128));
+    const auto pkg = cv::gapi::kernels<GOCVScalarTest>();
+    //     EXPECT_NO_THROW(cc.compile(desc1, cv::compile_args(pkg)));
+    cc.compile(desc1, cv::compile_args(pkg));
+}
+
+TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaType_Mixed)
+{
+    cv::GComputationT<cv::GMat(cv::GMat,cv::GScalar)> cc([](cv::GMat a, cv::GScalar v)
+    {
+        return cv::gapi::addC(a, v);
+    });
+
+    const auto desc1 = cv::GMatDesc{CV_8U,1,{64,64}};
+    const auto desc2 = cv::descr_of(cv::Scalar(4));
+
+    EXPECT_NO_THROW(cc.compile(desc1, desc2));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_fluid_resize_test.cpp b/modules/gapi/test/gapi_fluid_resize_test.cpp
new file mode 100644 (file)
index 0000000..de6d4ea
--- /dev/null
@@ -0,0 +1,649 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "gapi_fluid_test_kernels.hpp"
+
+namespace opencv_test
+{
+
+using namespace cv::gapi_test_kernels;
+
+G_TYPED_KERNEL(TCopy, <GMat(GMat)>, "test.fluid.copy")
+{
+    static GMatDesc outMeta(const cv::GMatDesc &in) {
+        return in;
+    }
+};
+
+GAPI_FLUID_KERNEL(FCopy, TCopy, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::gapi::fluid::View   &in,
+                          cv::gapi::fluid::Buffer &out)
+    {
+        const uint8_t* in_row  = in .InLine <uint8_t>(0);
+        uint8_t* out_row = out.OutLine<uint8_t>();
+
+        for (int i = 0, w = in.length(); i < w; i++)
+        {
+            //std::cout << std::setw(4) << int(in_row[i]);
+            out_row[i] = in_row[i];
+        }
+        //std::cout << std::endl;
+    }
+};
+
+GAPI_FLUID_KERNEL(FResizeNN, cv::gapi::core::GResize, false)
+{
+    static const int Window = 1;
+    static const auto Kind = GFluidKernel::Kind::Resize;
+
+    static void run(const cv::gapi::fluid::View& in, cv::Size /*sz*/, double /*fx*/, double /*fy*/, int /*interp*/,
+                    cv::gapi::fluid::Buffer& out)
+
+    {
+        double vRatio = (double)in.meta().size.height / out.meta().size.height;
+        auto y = out.y();
+        auto inY = in.y();
+
+        auto sy = static_cast<int>(y * vRatio);
+        int idx = sy - inY;
+
+        const auto src = in.InLine <unsigned char>(idx);
+        auto dst = out.OutLine<unsigned char>();
+
+        double horRatio = (double)in.length() / out.length();
+
+        for (int x = 0; x < out.length(); x++)
+        {
+            auto inX = static_cast<int>(x * horRatio);
+            dst[x] = src[inX];
+        }
+    }
+};
+
+namespace
+{
+namespace func
+{
+template <class Mapper>
+void initScratch(const cv::GMatDesc& in, cv::Size outSz, cv::gapi::fluid::Buffer &scratch)
+{
+    CV_Assert(in.depth == CV_8U && in.chan == 1);
+
+    cv::Size scratch_size{static_cast<int>(outSz.width * sizeof(typename Mapper::Unit)), 1};
+
+    cv::GMatDesc desc;
+    desc.chan  = 1;
+    desc.depth = CV_8UC1;
+    desc.size  = scratch_size;
+
+    cv::gapi::fluid::Buffer buffer(desc);
+    scratch = std::move(buffer);
+
+    auto mapX = scratch.OutLine<typename Mapper::Unit>();
+    double hRatio = (double)in.size.width / outSz.width;
+
+    for (int x = 0, w = outSz.width; x < w; x++)
+    {
+        mapX[x] = Mapper::map(hRatio, 0, in.size.width, x);
+    }
+}
+
+template <class Mapper>
+inline void calcRow(const cv::gapi::fluid::View& in, cv::gapi::fluid::Buffer& out, cv::gapi::fluid::Buffer &scratch)
+{
+    double vRatio = (double)in.meta().size.height / out.meta().size.height;
+    auto mapY = Mapper::map(vRatio, in.y(), in.meta().size.height, out.y());
+
+    const auto src0 = in.InLine <unsigned char>(mapY.s0);
+    const auto src1 = in.InLine <unsigned char>(mapY.s1);
+
+    auto dst = out.OutLine<unsigned char>();
+    auto mapX = scratch.OutLine<typename Mapper::Unit>();
+
+    for (int x = 0; x < out.length(); x++)
+    {
+        auto alpha0 = mapX[x].alpha0;
+        auto alpha1 = mapX[x].alpha1;
+        auto sx0 = mapX[x].s0;
+        auto sx1 = mapX[x].s1;
+
+        int res0 = src0[sx0]*alpha0 + src0[sx1]*alpha1;
+        int res1 = src1[sx0]*alpha0 + src1[sx1]*alpha1;
+
+        dst[x] = uchar(( ((mapY.alpha0 * (res0 >> 4)) >> 16) + ((mapY.alpha1 * (res1 >> 4)) >> 16) + 2)>>2);
+    }
+}
+} // namespace func
+
+constexpr static const int INTER_RESIZE_COEF_BITS = 11;
+constexpr static const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
+
+namespace linear
+{
+struct Mapper
+{
+    struct Unit
+    {
+        short alpha0;
+        short alpha1;
+        int   s0;
+        int   s1;
+    };
+
+    static inline Unit map(double ratio, int start, int max, int outCoord)
+    {
+        auto f = static_cast<float>((outCoord + 0.5f) * ratio - 0.5f);
+        int s = cvFloor(f);
+        f -= s;
+
+        Unit u;
+
+        u.s0 = std::max(s - start, 0);
+        u.s1 = ((f == 0.0) || s + 1 >= max) ? s - start : s - start + 1;
+
+        u.alpha0 = saturate_cast<short>((1.0f - f) * INTER_RESIZE_COEF_SCALE);
+        u.alpha1 = saturate_cast<short>((f) * INTER_RESIZE_COEF_SCALE);
+
+        return u;
+    }
+};
+
+} // namespace linear
+
+namespace areaUpscale
+{
+struct Mapper
+{
+    struct Unit
+    {
+        short alpha0;
+        short alpha1;
+        int   s0;
+        int   s1;
+    };
+
+    static inline Unit map(double ratio, int start, int max, int outCoord)
+    {
+        int s = cvFloor(outCoord*ratio);
+        float f = (float)((outCoord+1) - (s+1)/ratio);
+        f = f <= 0 ? 0.f : f - cvFloor(f);
+
+        Unit u;
+
+        u.s0 = std::max(s - start, 0);
+        u.s1 = ((f == 0.0) || s + 1 >= max) ? s - start : s - start + 1;
+
+        u.alpha0 = saturate_cast<short>((1.0f - f) * INTER_RESIZE_COEF_SCALE);
+        u.alpha1 = saturate_cast<short>((f) * INTER_RESIZE_COEF_SCALE);
+
+        return u;
+    }
+};
+} // namespace areaUpscale
+} // anonymous namespace
+
+GAPI_FLUID_KERNEL(FResizeLinear, cv::gapi::core::GResize, true)
+{
+    static const int Window = 1;
+    static const auto Kind = GFluidKernel::Kind::Resize;
+
+    static void initScratch(const cv::GMatDesc& in,
+                            cv::Size outSz, double /*fx*/, double /*fy*/, int /*interp*/,
+                            cv::gapi::fluid::Buffer &scratch)
+    {
+        func::initScratch<linear::Mapper>(in, outSz, scratch);
+    }
+
+    static void resetScratch(cv::gapi::fluid::Buffer& /*scratch*/)
+    {}
+
+    static void run(const cv::gapi::fluid::View& in, cv::Size /*sz*/, double /*fx*/, double /*fy*/, int /*interp*/,
+                    cv::gapi::fluid::Buffer& out, cv::gapi::fluid::Buffer &scratch)
+
+    {
+        func::calcRow<linear::Mapper>(in, out, scratch);
+    }
+};
+
+namespace
+{
+// FIXME
+// Move to some common place (to reuse/align with ResizeAgent)
+auto startInCoord = [](int outCoord, double ratio) {
+    return static_cast<int>(outCoord * ratio + 1e-3);
+};
+auto endInCoord = [](int outCoord, double ratio) {
+    return static_cast<int>(std::ceil((outCoord + 1) * ratio - 1e-3));
+};
+} // namespace
+
+GAPI_FLUID_KERNEL(FResizeArea, cv::gapi::core::GResize, false)
+{
+    static const int Window = 1;
+    static const auto Kind = GFluidKernel::Kind::Resize;
+
+    static void run(const cv::gapi::fluid::View& in, cv::Size /*sz*/, double /*fx*/, double /*fy*/, int /*interp*/,
+                    cv::gapi::fluid::Buffer& out)
+
+    {
+        auto y = out.y();
+        double vRatio = (double)in.meta().size.height / out.meta().size.height;
+
+        int startY = startInCoord(y, vRatio);
+        int endY   = endInCoord  (y, vRatio);
+
+        auto dst = out.OutLine<unsigned char>();
+
+        double hRatio = (double)in.length() / out.length();
+
+        for (int x = 0; x < out.length(); x++)
+        {
+            float res = 0.0;
+
+            int startX = startInCoord(x, hRatio);
+            int endX   = endInCoord  (x, hRatio);
+
+            for (int inY = startY; inY < endY; inY++)
+            {
+                double startCoordY = inY / vRatio;
+                double endCoordY = startCoordY + 1/vRatio;
+
+                if (startCoordY < y) startCoordY = y;
+                if (endCoordY > y + 1) endCoordY = y + 1;
+
+                float fracY = static_cast<float>((inY == startY || inY == endY - 1) ? endCoordY - startCoordY : 1/vRatio);
+
+                const auto src = in.InLine <unsigned char>(inY - startY);
+
+                float rowSum = 0.0f;
+
+                for (int inX = startX; inX < endX; inX++)
+                {
+                    double startCoordX = inX / hRatio;
+                    double endCoordX = startCoordX + 1/hRatio;
+
+                    if (startCoordX < x) startCoordX = x;
+                    if (endCoordX > x + 1) endCoordX = x + 1;
+
+                    float fracX = static_cast<float>((inX == startX || inX == endX - 1) ? endCoordX - startCoordX : 1/hRatio);
+
+                    rowSum += src[inX] * fracX;
+                }
+                res += rowSum * fracY;
+            }
+            dst[x] = static_cast<unsigned char>(std::rint(res));
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(FResizeAreaUpscale, cv::gapi::core::GResize, true)
+{
+    static const int Window = 1;
+    static const auto Kind = GFluidKernel::Kind::Resize;
+
+    static void initScratch(const cv::GMatDesc& in,
+                            cv::Size outSz, double /*fx*/, double /*fy*/, int /*interp*/,
+                            cv::gapi::fluid::Buffer &scratch)
+    {
+        func::initScratch<areaUpscale::Mapper>(in, outSz, scratch);
+    }
+
+    static void resetScratch(cv::gapi::fluid::Buffer& /*scratch*/)
+    {}
+
+    static void run(const cv::gapi::fluid::View& in, cv::Size /*sz*/, double /*fx*/, double /*fy*/, int /*interp*/,
+                    cv::gapi::fluid::Buffer& out, cv::gapi::fluid::Buffer &scratch)
+    {
+        func::calcRow<areaUpscale::Mapper>(in, out, scratch);
+    }
+};
+
+static auto fluidResizeTestPackage = [](int interpolation, cv::Size szIn, cv::Size szOut)
+{
+    bool upscale = szIn.width < szOut.width || szIn.height < szOut.height;
+
+    cv::gapi::GKernelPackage pkg;
+    switch (interpolation)
+    {
+    case cv::INTER_NEAREST: pkg = cv::gapi::kernels<FCopy, FResizeNN    >(); break;
+    case cv::INTER_LINEAR:  pkg = cv::gapi::kernels<FCopy, FResizeLinear>(); break;
+    case cv::INTER_AREA:    pkg = upscale ? cv::gapi::kernels<FCopy, FResizeAreaUpscale>()
+                                          : cv::gapi::kernels<FCopy, FResizeArea>(); break;
+    default: CV_Assert(false);
+    }
+    return cv::gapi::combine(pkg, fluidTestPackage, cv::unite_policy::KEEP);
+};
+
+struct ResizeTestFluid : public TestWithParam<std::tuple<int, int, cv::Size, std::tuple<cv::Size, cv::Rect>, double>> {};
+TEST_P(ResizeTestFluid, SanityTest)
+{
+    int type = 0, interp = 0;
+    cv::Size sz_in, sz_out;
+    double tolerance = 0.0;
+    cv::Rect outRoi;
+    std::tuple<cv::Size, cv::Rect> outSizeAndRoi;
+    std::tie(type, interp, sz_in, outSizeAndRoi, tolerance) = GetParam();
+    std::tie(sz_out, outRoi) = outSizeAndRoi;
+    if (outRoi == cv::Rect{}) outRoi = {0,0,sz_out.width,sz_out.height};
+    if (outRoi.width == 0) outRoi.width = sz_out.width;
+    double fx = 0, fy = 0;
+
+    cv::Mat in_mat1 (sz_in, type );
+    cv::Scalar mean = cv::Scalar(127);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+
+    cv::Mat out_mat = cv::Mat::zeros(sz_out, type);
+    cv::Mat out_mat_ocv = cv::Mat::zeros(sz_out, type);
+
+    cv::GMat in;
+    auto mid = TBlur3x3::on(in, cv::BORDER_REPLICATE, {});
+    auto out = cv::gapi::resize(mid, sz_out, fx, fy, interp);
+
+    cv::GComputation c(in, out);
+    c.apply(in_mat1, out_mat, cv::compile_args(GFluidOutputRois{{outRoi}}, fluidResizeTestPackage(interp, sz_in, sz_out)));
+
+    cv::Mat mid_mat;
+    cv::blur(in_mat1, mid_mat, {3,3}, {-1,-1},  cv::BORDER_REPLICATE);
+    cv::resize(mid_mat, out_mat_ocv, sz_out, fx, fy, interp);
+
+    cv::Mat absDiff;
+    cv::absdiff(out_mat(outRoi), out_mat_ocv(outRoi), absDiff);
+    EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
+}
+
+INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR),
+                                Values(cv::Size(8, 7),
+                                       cv::Size(8, 8),
+                                       cv::Size(8, 64),
+                                       cv::Size(8, 25),
+                                       cv::Size(16, 8),
+                                       cv::Size(16, 7)),
+                                Values(std::make_tuple(cv::Size(5, 4), cv::Rect{}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 0, 0, 2}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 1, 0, 2}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 2, 0, 2}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 0, 0, 3}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 2, 0, 2}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 4, 0, 3}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 0, 0, 3}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 1, 0, 2}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 3, 0, 1})),
+                                Values(0.0)));
+
+INSTANTIATE_TEST_CASE_P(ResizeAreaTestCPU, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_AREA),
+                                Values(cv::Size(8, 7),
+                                       cv::Size(8, 8),
+                                       cv::Size(8, 64),
+                                       cv::Size(8, 25),
+                                       cv::Size(16, 8),
+                                       cv::Size(16, 7)),
+                                Values(std::make_tuple(cv::Size(5, 4), cv::Rect{}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 0, 0, 2}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 1, 0, 2}),
+                                       std::make_tuple(cv::Size(5, 4), cv::Rect{0, 2, 0, 2}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 0, 0, 3}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 2, 0, 2}),
+                                       std::make_tuple(cv::Size(7, 7), cv::Rect{0, 4, 0, 3}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 0, 0, 3}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 1, 0, 2}),
+                                       std::make_tuple(cv::Size(8, 4), cv::Rect{0, 3, 0, 1})),
+                                // Actually this tolerance only for cases where OpenCV
+                                // uses ResizeAreaFast
+                                Values(1.0)));
+
+INSTANTIATE_TEST_CASE_P(ResizeUpscaleTestCPU, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                Values(cv::Size(1, 5),
+                                       cv::Size(3, 5),
+                                       cv::Size(7, 5),
+                                       cv::Size(1, 7),
+                                       cv::Size(3, 7),
+                                       cv::Size(7, 7)),
+                                Values(std::make_tuple(cv::Size(8, 8), cv::Rect{0,0,8,2}),
+                                       std::make_tuple(cv::Size(8, 8), cv::Rect{0,2,8,2}),
+                                       std::make_tuple(cv::Size(8, 8), cv::Rect{0,4,8,2}),
+                                       std::make_tuple(cv::Size(8, 8), cv::Rect{0,6,8,2}),
+                                       std::make_tuple(cv::Size(8, 8), cv::Rect{0,0,8,8}),
+                                       std::make_tuple(cv::Size(16, 8), cv::Rect{}),
+                                       std::make_tuple(cv::Size(16, 64), cv::Rect{0, 0,16,16}),
+                                       std::make_tuple(cv::Size(16, 64), cv::Rect{0,16,16,16}),
+                                       std::make_tuple(cv::Size(16, 64), cv::Rect{0,32,16,16}),
+                                       std::make_tuple(cv::Size(16, 64), cv::Rect{0,48,16,16}),
+                                       std::make_tuple(cv::Size(16, 64), cv::Rect{0, 0,16,64}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0, 0,16, 7}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0, 7,16, 6}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0,13,16, 6}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0,19,16, 6}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0, 0,16, 7}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0, 7,16, 7}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0,14,16, 7}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0,21,16, 4}),
+                                       std::make_tuple(cv::Size(16, 25), cv::Rect{0, 0,16,25}),
+                                       std::make_tuple(cv::Size(16, 7), cv::Rect{}),
+                                       std::make_tuple(cv::Size(16, 8), cv::Rect{})),
+                                Values(0.0)));
+
+INSTANTIATE_TEST_CASE_P(ResizeUpscaleOneDimDownscaleAnother, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                Values(cv::Size(6, 6),
+                                       cv::Size(8, 7),
+                                       cv::Size(8, 8),
+                                       cv::Size(8, 10),
+                                       cv::Size(10, 8),
+                                       cv::Size(10, 7)),
+                                Values(std::make_tuple(cv::Size(11, 5), cv::Rect{}),
+                                       std::make_tuple(cv::Size(11, 5), cv::Rect{0, 0, 0, 2}),
+                                       std::make_tuple(cv::Size(11, 5), cv::Rect{0, 2, 0, 2}),
+                                       std::make_tuple(cv::Size(11, 5), cv::Rect{0, 4, 0, 1}),
+                                       std::make_tuple(cv::Size(12, 2), cv::Rect{}),
+                                       std::make_tuple(cv::Size(12, 2), cv::Rect{0, 0, 0, 1}),
+                                       std::make_tuple(cv::Size(12, 2), cv::Rect{0, 1, 0, 1}),
+                                       std::make_tuple(cv::Size(23, 3), cv::Rect{}),
+                                       std::make_tuple(cv::Size(23, 3), cv::Rect{0, 0, 0, 1}),
+                                       std::make_tuple(cv::Size(23, 3), cv::Rect{0, 1, 0, 1}),
+                                       std::make_tuple(cv::Size(23, 3), cv::Rect{0, 2, 0, 1}),
+                                       std::make_tuple(cv::Size(3, 24), cv::Rect{}),
+                                       std::make_tuple(cv::Size(3, 24), cv::Rect{0,  0, 0, 6}),
+                                       std::make_tuple(cv::Size(3, 24), cv::Rect{0,  6, 0, 6}),
+                                       std::make_tuple(cv::Size(3, 24), cv::Rect{0, 12, 0, 6}),
+                                       std::make_tuple(cv::Size(3, 24), cv::Rect{0, 18, 0, 6}),
+                                       std::make_tuple(cv::Size(5, 11), cv::Rect{}),
+                                       std::make_tuple(cv::Size(5, 11), cv::Rect{0, 0, 0, 3}),
+                                       std::make_tuple(cv::Size(5, 11), cv::Rect{0, 3, 0, 3}),
+                                       std::make_tuple(cv::Size(5, 11), cv::Rect{0, 6, 0, 3}),
+                                       std::make_tuple(cv::Size(5, 11), cv::Rect{0, 9, 0, 2})),
+                                Values(0.0)));
+
+INSTANTIATE_TEST_CASE_P(Resize400_384TestCPU, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
+                                Values(cv::Size(128, 400)),
+                                Values(std::make_tuple(cv::Size(128, 384), cv::Rect{})),
+                                Values(0.0)));
+
+INSTANTIATE_TEST_CASE_P(Resize220_400TestCPU, ResizeTestFluid,
+                        Combine(Values(CV_8UC1),
+                                Values(cv::INTER_LINEAR),
+                                Values(cv::Size(220, 220)),
+                                Values(std::make_tuple(cv::Size(400, 400), cv::Rect{})),
+                                Values(0.0)));
+
+static auto cvBlur = [](const cv::Mat& in, cv::Mat& out, int kernelSize)
+{
+    if (kernelSize == 1)
+    {
+        out = in;
+    }
+    else
+    {
+        cv::blur(in, out, {kernelSize, kernelSize});
+    }
+};
+
+using SizesWithRois = std::tuple<cv::Size, cv::Rect, cv::Size, cv::Rect>;
+struct ResizeAndAnotherReaderTest : public TestWithParam<std::tuple<int, int, bool, SizesWithRois>>{};
+TEST_P(ResizeAndAnotherReaderTest, SanityTest)
+{
+    bool readFromInput = false;
+    int interp = -1, kernelSize = -1;
+    SizesWithRois sizesWithRois;
+    std::tie(interp, kernelSize, readFromInput, sizesWithRois) = GetParam();
+
+    cv::Size sz,  resizedSz;
+    cv::Rect roi, resizedRoi;
+    std::tie(sz, roi, resizedSz, resizedRoi) = sizesWithRois;
+
+    cv::Mat in_mat(sz, CV_8UC1);
+    cv::Scalar mean = cv::Scalar(127);
+    cv::Scalar stddev = cv::Scalar(40.f);
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Mat gapi_resize_out = cv::Mat::zeros(resizedSz, CV_8UC1);
+    cv::Mat gapi_blur_out = cv::Mat::zeros(sz, CV_8UC1);
+
+    auto blur = kernelSize == 1 ? &TBlur1x1::on : kernelSize == 3 ? &TBlur3x3::on : &TBlur5x5::on;
+
+    cv::GMat in, resize_out, blur_out;
+
+    if (readFromInput)
+    {
+        resize_out = gapi::resize(in, resizedSz, 0, 0, interp);
+        blur_out   = blur(in, cv::BORDER_DEFAULT, {});
+    }
+    else
+    {
+        auto mid   = TCopy::on(in);
+        resize_out = gapi::resize(mid, resizedSz, 0, 0, interp);
+        blur_out   = blur(mid, cv::BORDER_DEFAULT, {});
+    }
+
+    cv::GComputation c(GIn(in), GOut(resize_out, blur_out));
+    c.apply(gin(in_mat), gout(gapi_resize_out, gapi_blur_out), cv::compile_args(GFluidOutputRois{{resizedRoi, roi}},
+                                                                                fluidResizeTestPackage(interp, sz, resizedSz)));
+
+    cv::Mat ocv_resize_out = cv::Mat::zeros(resizedSz, CV_8UC1);
+    cv::resize(in_mat, ocv_resize_out, resizedSz, 0, 0, interp);
+    cv::Mat ocv_blur_out = cv::Mat::zeros(sz, CV_8UC1);
+    cvBlur(in_mat, ocv_blur_out, kernelSize);
+
+    EXPECT_EQ(0, cv::countNonZero(gapi_resize_out(resizedRoi) != ocv_resize_out(resizedRoi)));
+    EXPECT_EQ(0, cv::countNonZero(gapi_blur_out(roi) != ocv_blur_out(roi)));
+}
+
+INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeAndAnotherReaderTest,
+                        Combine(Values(cv::INTER_NEAREST, cv::INTER_LINEAR),
+                                Values(1, 3, 5),
+                                testing::Bool(), // Read from input directly or place a copy node at start
+                                Values(std::make_tuple(cv::Size{8,8}, cv::Rect{0,0,8,8},
+                                                       cv::Size{4,4}, cv::Rect{0,0,4,4}),
+                                       std::make_tuple(cv::Size{8,8}, cv::Rect{0,0,8,2},
+                                                       cv::Size{4,4}, cv::Rect{0,0,4,1}),
+                                       std::make_tuple(cv::Size{8,8}, cv::Rect{0,2,8,4},
+                                                       cv::Size{4,4}, cv::Rect{0,1,4,2}),
+                                       std::make_tuple(cv::Size{8,8}, cv::Rect{0,4,8,4},
+                                                       cv::Size{4,4}, cv::Rect{0,2,4,2}),
+                                       std::make_tuple(cv::Size{64,64}, cv::Rect{0, 0,64,64},
+                                                       cv::Size{49,49}, cv::Rect{0, 0,49,49}),
+                                       std::make_tuple(cv::Size{64,64}, cv::Rect{0, 0,64,15},
+                                                       cv::Size{49,49}, cv::Rect{0, 0,49,11}),
+                                       std::make_tuple(cv::Size{64,64}, cv::Rect{0,11,64,23},
+                                                       cv::Size{49,49}, cv::Rect{0, 9,49,17}),
+                                       std::make_tuple(cv::Size{64,64}, cv::Rect{0,50,64,14},
+                                                       cv::Size{49,49}, cv::Rect{0,39,49,10}))));
+
+struct BlursAfterResizeTest : public TestWithParam<std::tuple<int, int, int, bool, std::tuple<cv::Size, cv::Size, cv::Rect>>>{};
+TEST_P(BlursAfterResizeTest, SanityTest)
+{
+    bool readFromInput = false;
+    int interp = -1, kernelSize1 = -1, kernelSize2 = -1;
+    std::tuple<cv::Size, cv::Size, cv::Rect> sizesWithRoi;
+    std::tie(interp, kernelSize1, kernelSize2, readFromInput, sizesWithRoi) = GetParam();
+
+    cv::Size inSz,  outSz;
+    cv::Rect outRoi;
+    std::tie(inSz, outSz, outRoi) = sizesWithRoi;
+
+    cv::Mat in_mat(inSz, CV_8UC1);
+    cv::Scalar mean = cv::Scalar(127);
+    cv::Scalar stddev = cv::Scalar(40.f);
+    cv::randn(in_mat, mean, stddev);
+    cv::Mat gapi_out1 = cv::Mat::zeros(outSz, CV_8UC1);
+    cv::Mat gapi_out2 = cv::Mat::zeros(outSz, CV_8UC1);
+
+    auto blur1 = kernelSize1 == 1 ? &TBlur1x1::on : kernelSize1 == 3 ? &TBlur3x3::on : &TBlur5x5::on;
+    auto blur2 = kernelSize2 == 1 ? &TBlur1x1::on : kernelSize2 == 3 ? &TBlur3x3::on : &TBlur5x5::on;
+
+    cv::GMat in, out1, out2;
+    if (readFromInput)
+    {
+        auto resized = gapi::resize(in, outSz, 0, 0, interp);
+        out1 = blur1(resized, cv::BORDER_DEFAULT, {});
+        out2 = blur2(resized, cv::BORDER_DEFAULT, {});
+    }
+    else
+    {
+        auto mid = TCopy::on(in);
+        auto resized = gapi::resize(mid, outSz, 0, 0, interp);
+        out1 = blur1(resized, cv::BORDER_DEFAULT, {});
+        out2 = blur2(resized, cv::BORDER_DEFAULT, {});
+    }
+
+    cv::GComputation c(GIn(in), GOut(out1, out2));
+    c.apply(gin(in_mat), gout(gapi_out1, gapi_out2), cv::compile_args(GFluidOutputRois{{outRoi, outRoi}},
+                                                                      fluidResizeTestPackage(interp, inSz, outSz)));
+
+    cv::Mat ocv_out1 = cv::Mat::zeros(outSz, CV_8UC1);
+    cv::Mat ocv_out2 = cv::Mat::zeros(outSz, CV_8UC1);
+    cv::Mat resized = cv::Mat::zeros(outSz, CV_8UC1);
+    cv::resize(in_mat, resized, outSz, 0, 0, interp);
+    cvBlur(resized, ocv_out1, kernelSize1);
+    cvBlur(resized, ocv_out2, kernelSize2);
+
+    EXPECT_EQ(0, cv::countNonZero(gapi_out1(outRoi) != ocv_out1(outRoi)));
+    EXPECT_EQ(0, cv::countNonZero(gapi_out2(outRoi) != ocv_out2(outRoi)));
+}
+
+INSTANTIATE_TEST_CASE_P(ResizeTestCPU, BlursAfterResizeTest,
+                        Combine(Values(cv::INTER_NEAREST, cv::INTER_LINEAR),
+                                Values(1, 3, 5),
+                                Values(1, 3, 5),
+                                testing::Bool(), // Read from input directly or place a copy node at start
+                                Values(std::make_tuple(cv::Size{8,8},
+                                                       cv::Size{4,4}, cv::Rect{0,0,4,4}),
+                                       std::make_tuple(cv::Size{8,8},
+                                                       cv::Size{4,4}, cv::Rect{0,0,4,1}),
+                                       std::make_tuple(cv::Size{8,8},
+                                                       cv::Size{4,4}, cv::Rect{0,1,4,2}),
+                                       std::make_tuple(cv::Size{8,8},
+                                                       cv::Size{4,4}, cv::Rect{0,2,4,2}),
+                                       std::make_tuple(cv::Size{64,64},
+                                                       cv::Size{49,49}, cv::Rect{0, 0,49,49}),
+                                       std::make_tuple(cv::Size{64,64},
+                                                       cv::Size{49,49}, cv::Rect{0, 0,49,11}),
+                                       std::make_tuple(cv::Size{64,64},
+                                                       cv::Size{49,49}, cv::Rect{0, 9,49,17}),
+                                       std::make_tuple(cv::Size{64,64},
+                                                       cv::Size{49,49}, cv::Rect{0,39,49,10}))));
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_fluid_roi_test.cpp b/modules/gapi/test/gapi_fluid_roi_test.cpp
new file mode 100644 (file)
index 0000000..ee8674e
--- /dev/null
@@ -0,0 +1,197 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "gapi_fluid_test_kernels.hpp"
+
+namespace opencv_test
+{
+
+using namespace cv::gapi_test_kernels;
+
+struct PartialComputation : public TestWithParam <std::tuple<cv::Rect>> {};
+TEST_P(PartialComputation, Test)
+{
+    cv::Rect roi;
+    std::tie(roi) = GetParam();
+
+    int borderType = BORDER_REPLICATE;
+    int kernelSize = 3;
+    cv::Point anchor = {-1, -1};
+
+    cv::GMat in;
+    cv::GMat out = TBlur3x3::on(in, borderType, {});
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+    const auto sz = cv::Size(8, 10);
+    cv::Mat in_mat(sz, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Mat out_mat_gapi = cv::Mat::zeros(sz, CV_8UC1);
+    cv::Mat out_mat_ocv = cv::Mat::zeros(sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = c.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage, GFluidOutputRois{{to_own(roi)}}));
+    cc(cv::gin(in_mat), cv::gout(out_mat_gapi));
+
+    // Check with OpenCV
+    if (roi == cv::Rect{}) roi = cv::Rect{0,0,sz.width,sz.height};
+    cv::blur(in_mat(roi), out_mat_ocv(roi), {kernelSize, kernelSize}, anchor, borderType);
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, PartialComputation,
+                        Values(cv::Rect{},        cv::Rect{0,0,8,6}, cv::Rect{0,1,8,3},
+                               cv::Rect{0,2,8,3}, cv::Rect{0,3,8,5}, cv::Rect{0,4,8,6}));
+
+struct PartialComputationAddC : public TestWithParam <std::tuple<cv::Rect>> {};
+TEST_P(PartialComputationAddC, Test)
+{
+    cv::Rect roi;
+    std::tie(roi) = GetParam();
+
+    cv::GMat in;
+    cv::GMat out = TAddCSimple::on(in, 1);
+    cv::GComputation c(cv::GIn(in), cv::GOut(out));
+
+    const auto sz = cv::Size(8, 10);
+    cv::Mat in_mat(sz, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Mat out_mat_gapi = cv::Mat::zeros(sz, CV_8UC1);
+    cv::Mat out_mat_ocv = cv::Mat::zeros(sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = c.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage, GFluidOutputRois{{to_own(roi)}}));
+    cc(cv::gin(in_mat), cv::gout(out_mat_gapi));
+
+    // Check with OpenCV
+    if (roi == cv::Rect{}) roi = cv::Rect{0,0,sz.width,sz.height};
+    out_mat_ocv(roi) = in_mat(roi) + 1;
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+}
+
+INSTANTIATE_TEST_CASE_P(FluidRoi, PartialComputationAddC,
+                        Values(cv::Rect{},        cv::Rect{0,0,8,6}, cv::Rect{0,1,8,3},
+                               cv::Rect{0,2,8,3}, cv::Rect{0,3,8,5}, cv::Rect{0,4,8,6}));
+
+struct SequenceOfBlursRoiTest : public TestWithParam <std::tuple<int, cv::Rect>> {};
+TEST_P(SequenceOfBlursRoiTest, Test)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int borderType = 0;
+    cv::Rect roi;
+    std::tie(borderType, roi) = GetParam();
+    cv::Mat in_mat(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Point anchor = {-1, -1};
+    cv::Scalar borderValue(0);
+
+    GMat in;
+    auto mid = TBlur3x3::on(in,  borderType, borderValue);
+    auto out = TBlur5x5::on(mid, borderType, borderValue);
+
+    Mat out_mat_gapi = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out));
+    auto cc = c.compile(descr_of(in_mat), cv::compile_args(fluidTestPackage, GFluidOutputRois{{to_own(roi)}}));
+    cc(gin(in_mat), gout(out_mat_gapi));
+
+    cv::Mat mid_mat_ocv = Mat::zeros(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv = Mat::zeros(sz_in, CV_8UC1);
+
+    cv::blur(in_mat, mid_mat_ocv, {3,3}, anchor, borderType);
+
+    if (roi == cv::Rect{})
+    {
+        roi = cv::Rect{0, 0, sz_in.width, sz_in.height};
+    }
+
+    cv::blur(mid_mat_ocv(roi), out_mat_ocv(roi), {5,5}, anchor, borderType);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv != out_mat_gapi));
+}
+
+INSTANTIATE_TEST_CASE_P(FluidRoi, SequenceOfBlursRoiTest,
+                        Combine(Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101),
+                                Values(cv::Rect{0,0,320,240}, cv::Rect{0,64,320,128}, cv::Rect{0,128,320,112})));
+
+struct TwoBlursRoiTest : public TestWithParam <std::tuple<int, int, int, int, int, int, bool, cv::Rect>> {};
+TEST_P(TwoBlursRoiTest, Test)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int kernelSize1 = 0, kernelSize2 = 0;
+    int borderType1 = -1, borderType2 = -1;
+    cv::Scalar borderValue1{}, borderValue2{};
+    bool readFromInput = false;
+    cv::Rect outRoi;
+    std::tie(kernelSize1, borderType1, borderValue1, kernelSize2, borderType2, borderValue2, readFromInput, outRoi) = GetParam();
+    cv::Mat in_mat(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Point anchor = {-1, -1};
+
+    auto blur1 = kernelSize1 == 3 ? &TBlur3x3::on : TBlur5x5::on;
+    auto blur2 = kernelSize2 == 3 ? &TBlur3x3::on : TBlur5x5::on;
+
+    GMat in, out1, out2;
+    if (readFromInput)
+    {
+        out1 = blur1(in, borderType1, borderValue1);
+        out2 = blur2(in, borderType2, borderValue2);
+    }
+    else
+    {
+        auto mid = TAddCSimple::on(in, 0);
+        out1 = blur1(mid, borderType1, borderValue1);
+        out2 = blur2(mid, borderType2, borderValue2);
+    }
+
+    Mat out_mat_gapi1 = Mat::zeros(sz_in, CV_8UC1);
+    Mat out_mat_gapi2 = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out1, out2));
+    auto cc = c.compile(descr_of(in_mat), cv::compile_args(fluidTestPackage, GFluidOutputRois{{outRoi, outRoi}}));
+    cc(gin(in_mat), gout(out_mat_gapi1, out_mat_gapi2));
+
+    cv::Mat out_mat_ocv1 = Mat::zeros(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv2 = Mat::zeros(sz_in, CV_8UC1);
+
+    cv::blur(in_mat(outRoi), out_mat_ocv1(outRoi), {kernelSize1, kernelSize1}, anchor, borderType1);
+    cv::blur(in_mat(outRoi), out_mat_ocv2(outRoi), {kernelSize2, kernelSize2}, anchor, borderType2);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv1 != out_mat_gapi1));
+    EXPECT_EQ(0, countNonZero(out_mat_ocv2 != out_mat_gapi2));
+}
+
+INSTANTIATE_TEST_CASE_P(FluidRoi, TwoBlursRoiTest,
+                        Combine(Values(3, 5),
+                                Values(cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT_101),
+                                Values(0),
+                                Values(3, 5),
+                                Values(cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT_101),
+                                Values(0),
+                                testing::Bool(), // Read from input directly or place a copy node at start
+                                Values(cv::Rect{0,0,320,240}, cv::Rect{0,64,320,128}, cv::Rect{0,128,320,112})));
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_fluid_test.cpp b/modules/gapi/test/gapi_fluid_test.cpp
new file mode 100644 (file)
index 0000000..0996a8f
--- /dev/null
@@ -0,0 +1,710 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "opencv2/gapi/core.hpp"
+
+#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+ // FIXME: move these tests with priv() to internal suite
+#include "backends/fluid/gfluidbuffer_priv.hpp"
+
+#include "gapi_fluid_test_kernels.hpp"
+#include "logger.hpp"
+
+namespace opencv_test
+{
+
+using namespace cv::gapi_test_kernels;
+
+namespace
+{
+    void WriteFunction(uint8_t* row, int nr, int w) {
+        for (int i = 0; i < w; i++)
+            row[i] = static_cast<uint8_t>(nr+i);
+    };
+    void ReadFunction1x1(const uint8_t* row, int w) {
+        for (int i = 0; i < w; i++)
+            std::cout << std::setw(4) << static_cast<int>(row[i]) << " ";
+        std::cout << "\n";
+    };
+    void ReadFunction3x3(const uint8_t* rows[3], int w) {
+        for (int i = 0; i < 3; i++) {
+            for (int j = -1; j < w+1; j++) {
+                std::cout << std::setw(4) << static_cast<int>(rows[i][j]) << " ";
+            }
+            std::cout << "\n";
+        }
+        std::cout << "\n";
+    };
+}
+
+TEST(FluidBuffer, InputTest)
+{
+    const cv::Size buffer_size = {8,8};
+    cv::Mat in_mat = cv::Mat::eye(buffer_size, CV_8U);
+
+    cv::gapi::fluid::Buffer buffer(in_mat, true);
+    cv::gapi::fluid::View  view = buffer.mkView(1, 0, {}, false);
+    view.priv().reset(1);
+    int this_y = 0;
+
+    while (this_y < buffer_size.height)
+    {
+        const uint8_t* rrow = view.InLine<uint8_t>(0);
+        ReadFunction1x1(rrow, buffer_size.width);
+        view.priv().readDone(1,1);
+
+        cv::Mat from_buffer(1, buffer_size.width, CV_8U, const_cast<uint8_t*>(rrow));
+        EXPECT_EQ(0, cv::countNonZero(in_mat.row(this_y) != from_buffer));
+
+        this_y++;
+    }
+}
+
+TEST(FluidBuffer, CircularTest)
+{
+    const cv::Size buffer_size = {8,16};
+
+    cv::gapi::fluid::Buffer buffer(cv::GMatDesc{CV_8U,1,buffer_size}, 3, 1, 0, 1,
+        util::make_optional(cv::gapi::fluid::Border{cv::BORDER_CONSTANT, cv::gapi::own::Scalar(255)}));
+    cv::gapi::fluid::View view = buffer.mkView(3, 1, {}, false);
+    view.priv().reset(3);
+    buffer.debug(std::cout);
+
+    const auto whole_line_is = [](const uint8_t *line, int len, int value)
+    {
+        return std::all_of(line, line+len, [&](const uint8_t v){return v == value;});
+    };
+
+    // Store all read/written data in separate Mats to compare with
+    cv::Mat written_data(buffer_size, CV_8U);
+
+    // Simulate write/read process
+    int num_reads = 0, num_writes = 0;
+    while (num_reads < buffer_size.height)
+    {
+        if (num_writes < buffer_size.height)
+        {
+            uint8_t* wrow = buffer.OutLine<uint8_t>();
+            WriteFunction(wrow, num_writes, buffer_size.width);
+            buffer.priv().writeDone();
+
+            cv::Mat(1, buffer_size.width, CV_8U, wrow)
+                .copyTo(written_data.row(num_writes));
+            num_writes++;
+        }
+        buffer.debug(std::cout);
+
+        if (view.ready())
+        {
+            view.priv().prepareToRead();
+            const uint8_t* rrow[3] = {
+                view.InLine<uint8_t>(-1),
+                view.InLine<uint8_t>( 0),
+                view.InLine<uint8_t>( 1),
+            };
+            ReadFunction3x3(rrow, buffer_size.width);
+            view.priv().readDone(1,3);
+            buffer.debug(std::cout);
+
+            // Check borders right here
+            EXPECT_EQ(255u, rrow[0][-1]);
+            EXPECT_EQ(255u, rrow[0][buffer_size.width]);
+            if (num_reads == 0)
+            {
+                EXPECT_TRUE(whole_line_is(rrow[0]-1, buffer_size.width+2, 255u));
+            }
+            if (num_reads == buffer_size.height-1)
+            {
+                EXPECT_TRUE(whole_line_is(rrow[2]-1, buffer_size.width+2, 255u));
+            }
+
+            // Check window (without borders)
+            if (num_reads > 0 && num_reads < buffer_size.height-1)
+            {
+                // +1 everywhere since num_writes was just incremented above
+                cv::Mat written_lastLine2 = written_data.row(num_writes - (2+1));
+                cv::Mat written_lastLine1 = written_data.row(num_writes - (1+1));
+                cv::Mat written_lastLine0 = written_data.row(num_writes - (0+1));
+
+                cv::Mat read_prevLine(1, buffer_size.width, CV_8U, const_cast<uint8_t*>(rrow[0]));
+                cv::Mat read_thisLine(1, buffer_size.width, CV_8U, const_cast<uint8_t*>(rrow[1]));
+                cv::Mat read_nextLine(1, buffer_size.width, CV_8U, const_cast<uint8_t*>(rrow[2]));
+
+                EXPECT_EQ(0, cv::countNonZero(written_lastLine2 != read_prevLine));
+                EXPECT_EQ(0, cv::countNonZero(written_lastLine1 != read_thisLine));
+                EXPECT_EQ(0, cv::countNonZero(written_lastLine0 != read_nextLine));
+            }
+            num_reads++;
+        }
+    }
+}
+
+TEST(FluidBuffer, OutputTest)
+{
+    const cv::Size buffer_size = {8,16};
+    cv::Mat out_mat = cv::Mat(buffer_size, CV_8U);
+
+    cv::gapi::fluid::Buffer buffer(out_mat, false);
+    int num_writes = 0;
+    while (num_writes < buffer_size.height)
+    {
+        uint8_t* wrow = buffer.OutLine<uint8_t>();
+        WriteFunction(wrow, num_writes, buffer_size.width);
+        buffer.priv().writeDone();
+        num_writes++;
+    }
+
+    GAPI_LOG_INFO(NULL, "\n" << out_mat);
+
+    // Validity check
+    for (int r = 0; r < buffer_size.height; r++)
+    {
+        for (int c = 0; c < buffer_size.width; c++)
+        {
+            EXPECT_EQ(r+c, out_mat.at<uint8_t>(r, c));
+        }
+    }
+}
+
+TEST(Fluid, AddC_WithScalar)
+{
+    cv::GMat in;
+    cv::GScalar s;
+
+    cv::GComputation c(cv::GIn(in, s), cv::GOut(TAddScalar::on(in, s)));
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1), out_mat(3, 3, CV_8UC1), ref_mat;
+    cv::Scalar in_s(100);
+
+    auto cc = c.compile(cv::descr_of(in_mat), cv::descr_of(in_s), cv::compile_args(fluidTestPackage));
+
+    cc(cv::gin(in_mat, in_s), cv::gout(out_mat));
+    ref_mat = in_mat + in_s;
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(Fluid, Scalar_In_Middle_Graph)
+{
+    cv::GMat in;
+    cv::GScalar s;
+
+    cv::GComputation c(cv::GIn(in, s), cv::GOut(TAddScalar::on(TAddCSimple::on(in, 5), s)));
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1), out_mat(3, 3, CV_8UC1), ref_mat;
+    cv::Scalar in_s(100);
+
+    auto cc = c.compile(cv::descr_of(in_mat), cv::descr_of(in_s), cv::compile_args(fluidTestPackage));
+
+    cc(cv::gin(in_mat, in_s), cv::gout(out_mat));
+    ref_mat = (in_mat + 5) + in_s;
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(Fluid, Add_Scalar_To_Mat)
+{
+    cv::GMat in;
+    cv::GScalar s;
+
+    cv::GComputation c(cv::GIn(s, in), cv::GOut(TAddScalarToMat::on(s, in)));
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1), out_mat(3, 3, CV_8UC1), ref_mat;
+    cv::Scalar in_s(100);
+
+    auto cc = c.compile(cv::descr_of(in_s), cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+
+    cc(cv::gin(in_s, in_mat), cv::gout(out_mat));
+    ref_mat = in_mat + in_s;
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(Fluid, Sum_2_Mats_And_Scalar)
+{
+    cv::GMat a, b;
+    cv::GScalar s;
+
+    cv::GComputation c(cv::GIn(a, s, b), cv::GOut(TSum2MatsAndScalar::on(a, s, b)));
+    cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1),
+            in_mat2 = cv::Mat::eye(3, 3, CV_8UC1),
+            out_mat(3, 3, CV_8UC1),
+            ref_mat;
+    cv::Scalar in_s(100);
+
+    auto cc = c.compile(cv::descr_of(in_mat1), cv::descr_of(in_s), cv::descr_of(in_mat2), cv::compile_args(fluidTestPackage));
+
+    cc(cv::gin(in_mat1, in_s, in_mat2), cv::gout(out_mat));
+    ref_mat = in_mat1 + in_mat2 + in_s;
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(Fluid, Split3)
+{
+    cv::GMat bgr;
+    cv::GMat r,g,b;
+    std::tie(b,g,r) = cv::gapi::split3(bgr);
+    auto rr = TAddSimple::on(r, TId::on(b));
+    auto rrr = TAddSimple::on(TId::on(rr), g);
+    cv::GComputation c(bgr, TId::on(rrr));
+
+    cv::Size sz(5120, 5120);
+    cv::Mat eye_1 = cv::Mat::eye(sz, CV_8UC1);
+    std::vector<cv::Mat> eyes = {eye_1, eye_1, eye_1};
+    cv::Mat in_mat;
+    cv::merge(eyes, in_mat);
+    cv::Mat out_mat(sz, CV_8UC1);
+
+    // G-API
+    auto cc = c.compile(cv::descr_of(in_mat),
+                        cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat);
+
+    // OCV
+    std::vector<cv::Mat> chans;
+    cv::split(in_mat, chans);
+
+    // Compare
+    EXPECT_EQ(0, cv::countNonZero(out_mat != (chans[2]*3)));
+}
+
+TEST(Fluid, ScratchTest)
+{
+    cv::GMat in;
+    cv::GMat out = TPlusRow0::on(TPlusRow0::on(in));
+    cv::GComputation c(in, out);
+
+    cv::Size sz(8, 8);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat(sz, CV_8UC1);
+
+    // OpenCV (reference)
+    cv::Mat ref;
+    {
+        cv::Mat first_row = cv::Mat::zeros(1, sz.width, CV_8U);
+        cv::Mat remaining = cv::repeat(in_mat.row(0), sz.height-1, 1);
+        cv::Mat operand;
+        cv::vconcat(first_row, 2*remaining, operand);
+        ref = in_mat + operand;
+    }
+    GAPI_LOG_INFO(NULL, "\n" << ref);
+
+    // G-API
+    auto cc = c.compile(cv::descr_of(in_mat),
+                        cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat);
+    GAPI_LOG_INFO(NULL, "\n" << out_mat);
+    EXPECT_EQ(0, cv::countNonZero(ref != out_mat));
+
+    cc(in_mat, out_mat);
+    GAPI_LOG_INFO(NULL, "\n" << out_mat);
+    EXPECT_EQ(0, cv::countNonZero(ref != out_mat));
+}
+
+TEST(Fluid, MultipleOutRowsTest)
+{
+    cv::GMat in;
+    cv::GMat out = TAddCSimple::on(TAddCSimple::on(in, 1), 2);
+    cv::GComputation c(in, out);
+
+    cv::Size sz(4, 4);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat(sz, CV_8UC1);
+
+    auto cc = c.compile(cv::descr_of(in_mat),
+                        cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat);
+
+    std::cout << out_mat << std::endl;
+
+    cv::Mat ocv_ref = in_mat + 1 + 2;
+    EXPECT_EQ(0, cv::countNonZero(ocv_ref != out_mat));
+}
+
+
+TEST(Fluid, LPIWindow)
+{
+    cv::GMat in;
+    cv::GMat r,g,b;
+    std::tie(r,g,b) = cv::gapi::split3(in);
+    cv::GMat rr = TId7x7::on(r);
+    cv::GMat tmp = TAddSimple::on(rr, g);
+    cv::GMat out = TAddSimple::on(tmp, b);
+
+    cv::GComputation c(in, out);
+
+    cv::Size sz(8, 8);
+
+    cv::Mat eye_1 = cv::Mat::eye(sz, CV_8UC1);
+    std::vector<cv::Mat> eyes = {eye_1, eye_1, eye_1};
+    cv::Mat in_mat;
+    cv::merge(eyes, in_mat);
+
+    cv::Mat out_mat(sz, CV_8U);
+    auto cc = c.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat);
+
+    //std::cout << out_mat << std::endl;
+
+    // OpenCV reference
+    cv::Mat ocv_ref = eyes[0]+eyes[1]+eyes[2];
+
+    EXPECT_EQ(0, cv::countNonZero(ocv_ref != out_mat));
+}
+
+TEST(Fluid, MultipleReaders_SameLatency)
+{
+    //  in -> AddC -> a -> AddC -> b -> Add -> out
+    //                '--> AddC -> c -'
+    //
+    // b and c have the same skew
+
+    cv::GMat in;
+    cv::GMat a = TAddCSimple::on(in, 1); // FIXME - align naming (G, non-G)
+    cv::GMat b = TAddCSimple::on(a,  2);
+    cv::GMat c = TAddCSimple::on(a,  3);
+    cv::GMat out = TAddSimple::on(b, c);
+    cv::GComputation comp(in, out);
+
+    const auto sz = cv::Size(32, 32);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat_gapi(sz, CV_8UC1);
+    cv::Mat out_mat_ocv (sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat_gapi);
+
+    // Check with OpenCV
+    cv::Mat tmp = in_mat + 1;
+    out_mat_ocv = (tmp+2) + (tmp+3);
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+}
+
+TEST(Fluid, MultipleReaders_DifferentLatency)
+{
+    //  in1 -> AddC -> a -> AddC -------------> b -> Add -> out
+    //                 '--------------> Add --> c -'
+    //                 '--> Id7x7-> d -'
+    //
+    // b and c have different skew (due to latency introduced by Id7x7)
+    // a is ready by multiple views with different latency.
+
+    cv::GMat in;
+    cv::GMat a   = TAddCSimple::on(in, 1); // FIXME - align naming (G, non-G)
+    cv::GMat b   = TAddCSimple::on(a,  2);
+    cv::GMat d   = TId7x7::on(a);
+    cv::GMat c   = TAddSimple::on(a, d);
+    cv::GMat out = TAddSimple::on(b, c);
+    cv::GComputation comp(in, out);
+
+    const auto sz = cv::Size(32, 32);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat_gapi(sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(in_mat, out_mat_gapi);
+
+    // Check with OpenCV
+    cv::Mat ocv_a = in_mat + 1;
+    cv::Mat ocv_b = ocv_a + 2;
+    cv::Mat ocv_d = ocv_a;
+    cv::Mat ocv_c = ocv_a + ocv_d;
+    cv::Mat out_mat_ocv = ocv_b + ocv_c;
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+}
+
+TEST(Fluid, DISABLED_MultipleOutputs)
+{
+    // in -> AddC -> a -> AddC ------------------> out1
+    //               `--> Id7x7  --> b --> AddC -> out2
+
+    cv::GMat in;
+    cv::GMat a    = TAddCSimple::on(in, 1);
+    cv::GMat b    = TId7x7::on(a);
+    cv::GMat out1 = TAddCSimple::on(a, 2);
+    cv::GMat out2 = TAddCSimple::on(b, 7);
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2));
+
+    const auto sz = cv::Size(32, 32);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat_gapi1(sz, CV_8UC1), out_mat_gapi2(sz, CV_8UC1);
+    cv::Mat out_mat_ocv1(sz, CV_8UC1), out_mat_ocv2(sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(cv::gin(in_mat), cv::gout(out_mat_gapi1, out_mat_gapi2));
+
+    // Check with OpenCV
+    out_mat_ocv1 = in_mat + 1 + 2;
+    out_mat_ocv2 = in_mat + 1 + 7;
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi1 != out_mat_ocv1));
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi2 != out_mat_ocv2));
+}
+
+TEST(Fluid, EmptyOutputMatTest)
+{
+    cv::GMat in;
+    cv::GMat out = TAddCSimple::on(in, 2);
+    cv::GComputation c(in, out);
+
+    cv::Mat in_mat = cv::Mat::eye(cv::Size(32, 24), CV_8UC1);
+    cv::Mat out_mat;
+
+    auto cc = c.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+
+    cc(in_mat,    out_mat);
+    EXPECT_EQ(CV_8UC1, out_mat.type());
+    EXPECT_EQ(32, out_mat.cols);
+    EXPECT_EQ(24, out_mat.rows);
+    EXPECT_TRUE(out_mat.ptr() != nullptr);
+}
+
+struct LPISequenceTest : public TestWithParam<int>{};
+TEST_P(LPISequenceTest, DISABLED_LPISequenceTest)
+{
+    // in -> AddC -> a -> Blur (2lpi) -> out
+
+    int kernelSize = GetParam();
+    cv::GMat in;
+    cv::GMat a = TAddCSimple::on(in, 1);
+    auto blur = kernelSize == 3 ? &TBlur3x3_2lpi::on : &TBlur5x5_2lpi::on;
+    cv::GMat out = blur(a, cv::BORDER_CONSTANT, cv::Scalar(0));
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+    const auto sz = cv::Size(8, 10);
+    cv::Mat in_mat = cv::Mat::eye(sz, CV_8UC1);
+    cv::Mat out_mat_gapi(sz, CV_8UC1);
+    cv::Mat out_mat_ocv(sz, CV_8UC1);
+
+    // Run G-API
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(cv::gin(in_mat), cv::gout(out_mat_gapi));
+
+    // Check with OpenCV
+    cv::blur(in_mat + 1, out_mat_ocv, {kernelSize,kernelSize}, {-1,-1}, cv::BORDER_CONSTANT);
+    EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, LPISequenceTest,
+                        Values(3, 5));
+
+struct InputImageBorderTest : public TestWithParam <std::tuple<int, int>> {};
+TEST_P(InputImageBorderTest, InputImageBorderTest)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int ks         = 0;
+    int borderType = 0;
+    std::tie(ks, borderType) = GetParam();
+    cv::Mat in_mat1(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+
+    cv::Size kernelSize = {ks, ks};
+    cv::Point anchor = {-1, -1};
+    cv::Scalar borderValue(0);
+
+    auto gblur = ks == 3 ? &TBlur3x3::on : &TBlur5x5::on;
+
+    GMat in;
+    auto out = gblur(in, borderType, borderValue);
+
+    Mat out_mat_gapi = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out));
+    auto cc = c.compile(descr_of(in_mat1), cv::compile_args(fluidTestPackage));
+    cc(gin(in_mat1), gout(out_mat_gapi));
+
+    cv::Mat out_mat_ocv = Mat::zeros(sz_in, CV_8UC1);
+    cv::blur(in_mat1, out_mat_ocv, kernelSize, anchor, borderType);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv != out_mat_gapi));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, InputImageBorderTest,
+                        Combine(Values(3, 5),
+                                Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101)));
+
+struct SequenceOfBlursTest : public TestWithParam <std::tuple<int>> {};
+TEST_P(SequenceOfBlursTest, Test)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int borderType = 0;;
+    std::tie(borderType) = GetParam();
+    cv::Mat in_mat(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Point anchor = {-1, -1};
+    cv::Scalar borderValue(0);
+
+    GMat in;
+    auto mid = TBlur3x3::on(in,  borderType, borderValue);
+    auto out = TBlur5x5::on(mid, borderType, borderValue);
+
+    Mat out_mat_gapi = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out));
+    auto cc = c.compile(descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(gin(in_mat), gout(out_mat_gapi));
+
+    cv::Mat mid_mat_ocv = Mat::zeros(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv = Mat::zeros(sz_in, CV_8UC1);
+    cv::blur(in_mat, mid_mat_ocv, {3,3}, anchor, borderType);
+    cv::blur(mid_mat_ocv, out_mat_ocv, {5,5}, anchor, borderType);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv != out_mat_gapi));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, SequenceOfBlursTest,
+                               Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101));
+
+struct TwoBlursTest : public TestWithParam <std::tuple<int, int, int, int, int, int, bool>> {};
+TEST_P(TwoBlursTest, Test)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int kernelSize1 = 0, kernelSize2 = 0;
+    int borderType1 = -1, borderType2 = -1;
+    cv::Scalar borderValue1{}, borderValue2{};
+    bool readFromInput = false;
+    std::tie(kernelSize1, borderType1, borderValue1, kernelSize2, borderType2, borderValue2, readFromInput) = GetParam();
+    cv::Mat in_mat(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Point anchor = {-1, -1};
+
+    auto blur1 = kernelSize1 == 3 ? &TBlur3x3::on : TBlur5x5::on;
+    auto blur2 = kernelSize2 == 3 ? &TBlur3x3::on : TBlur5x5::on;
+
+    GMat in, out1, out2;
+    if (readFromInput)
+    {
+        out1 = blur1(in, borderType1, borderValue1);
+        out2 = blur2(in, borderType2, borderValue2);
+    }
+    else
+    {
+        auto mid = TAddCSimple::on(in, 0);
+        out1 = blur1(mid, borderType1, borderValue1);
+        out2 = blur2(mid, borderType2, borderValue2);
+    }
+
+    Mat out_mat_gapi1 = Mat::zeros(sz_in, CV_8UC1);
+    Mat out_mat_gapi2 = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out1, out2));
+    auto cc = c.compile(descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(gin(in_mat), gout(out_mat_gapi1, out_mat_gapi2));
+
+    cv::Mat out_mat_ocv1 = Mat::zeros(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv2 = Mat::zeros(sz_in, CV_8UC1);
+    cv::blur(in_mat, out_mat_ocv1, {kernelSize1, kernelSize1}, anchor, borderType1);
+    cv::blur(in_mat, out_mat_ocv2, {kernelSize2, kernelSize2}, anchor, borderType2);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv1 != out_mat_gapi1));
+    EXPECT_EQ(0, countNonZero(out_mat_ocv2 != out_mat_gapi2));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, TwoBlursTest,
+                               Combine(Values(3, 5),
+                                       Values(cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT_101),
+                                       Values(0),
+                                       Values(3, 5),
+                                       Values(cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT_101),
+                                       Values(0),
+                                       testing::Bool())); // Read from input directly or place a copy node at start
+
+struct TwoReadersTest : public TestWithParam <std::tuple<int, int, int, bool>> {};
+TEST_P(TwoReadersTest, Test)
+{
+    cv::Size sz_in = { 320, 240 };
+
+    int kernelSize = 0;
+    int borderType = -1;
+    cv::Scalar borderValue;
+    bool readFromInput = false;
+    std::tie(kernelSize, borderType, borderValue, readFromInput) = GetParam();
+    cv::Mat in_mat(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat, mean, stddev);
+
+    cv::Point anchor = {-1, -1};
+
+    auto blur = kernelSize == 3 ? &TBlur3x3::on : TBlur5x5::on;
+
+    GMat in, out1, out2;
+    if (readFromInput)
+    {
+        out1 = TAddCSimple::on(in, 0);
+        out2 = blur(in, borderType, borderValue);
+    }
+    else
+    {
+        auto mid = TAddCSimple::on(in, 0);
+        out1 = TAddCSimple::on(mid, 0);
+        out2 = blur(mid, borderType, borderValue);
+    }
+
+    Mat out_mat_gapi1 = Mat::zeros(sz_in, CV_8UC1);
+    Mat out_mat_gapi2 = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in), GOut(out1, out2));
+    auto cc = c.compile(descr_of(in_mat), cv::compile_args(fluidTestPackage));
+    cc(gin(in_mat), gout(out_mat_gapi1, out_mat_gapi2));
+
+    cv::Mat out_mat_ocv1 = Mat::zeros(sz_in, CV_8UC1);
+    cv::Mat out_mat_ocv2 = Mat::zeros(sz_in, CV_8UC1);
+    out_mat_ocv1 = in_mat;
+    cv::blur(in_mat, out_mat_ocv2, {kernelSize, kernelSize}, anchor, borderType);
+
+    EXPECT_EQ(0, countNonZero(out_mat_ocv1 != out_mat_gapi1));
+    EXPECT_EQ(0, countNonZero(out_mat_ocv2 != out_mat_gapi2));
+}
+
+INSTANTIATE_TEST_CASE_P(Fluid, TwoReadersTest,
+                               Combine(Values(3, 5),
+                                       Values(cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT_101),
+                                       Values(0),
+                                       testing::Bool())); // Read from input directly or place a copy node at start
+
+TEST(FluidTwoIslands, SanityTest)
+{
+    cv::Size sz_in{8,8};
+
+    GMat in1, in2;
+    auto out1 = TAddScalar::on(in1, {0});
+    auto out2 = TAddScalar::on(in2, {0});
+
+    cv::Mat in_mat1(sz_in, CV_8UC1);
+    cv::Mat in_mat2(sz_in, CV_8UC1);
+    cv::Scalar mean   = cv::Scalar(127.0f);
+    cv::Scalar stddev = cv::Scalar(40.f);
+
+    cv::randn(in_mat1, mean, stddev);
+    cv::randn(in_mat2, mean, stddev);
+
+    Mat out_mat1 = Mat::zeros(sz_in, CV_8UC1);
+    Mat out_mat2 = Mat::zeros(sz_in, CV_8UC1);
+
+    GComputation c(GIn(in1, in2), GOut(out1, out2));
+    EXPECT_NO_THROW(c.apply(gin(in_mat1, in_mat2), gout(out_mat1, out_mat2), cv::compile_args(fluidTestPackage)));
+    EXPECT_EQ(0, countNonZero(in_mat1 != out_mat1));
+    EXPECT_EQ(0, countNonZero(in_mat2 != out_mat2));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_fluid_test_kernels.cpp b/modules/gapi/test/gapi_fluid_test_kernels.cpp
new file mode 100644 (file)
index 0000000..058d9f3
--- /dev/null
@@ -0,0 +1,434 @@
+// 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
+
+#include <iomanip>
+#include "gapi_fluid_test_kernels.hpp"
+#include <opencv2/gapi/core.hpp>
+
+namespace cv
+{
+namespace gapi_test_kernels
+{
+
+GAPI_FLUID_KERNEL(FAddSimple, TAddSimple, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::gapi::fluid::View   &a,
+                    const cv::gapi::fluid::View   &b,
+                          cv::gapi::fluid::Buffer &o)
+    {
+        // std::cout << "AddSimple {{{\n";
+        // std::cout << "  a - "; a.debug(std::cout);
+        // std::cout << "  b - "; b.debug(std::cout);
+        // std::cout << "  o - "; o.debug(std::cout);
+
+        const uint8_t* in1 = a.InLine<uint8_t>(0);
+        const uint8_t* in2 = b.InLine<uint8_t>(0);
+              uint8_t* out = o.OutLine<uint8_t>();
+
+        // std::cout << "a: ";
+        // for (int i = 0, w = a.length(); i < w; i++)
+        // {
+        //     std::cout << std::setw(4) << int(in1[i]);
+        // }
+        // std::cout << "\n";
+
+        // std::cout << "b: ";
+        // for (int i = 0, w = a.length(); i < w; i++)
+        // {
+        //     std::cout << std::setw(4) << int(in2[i]);
+        // }
+        // std::cout << "\n";
+
+        for (int i = 0, w = a.length(); i < w; i++)
+        {
+            out[i] = in1[i] + in2[i];
+        }
+
+        // std::cout << "}}} " << std::endl;;
+    }
+};
+
+GAPI_FLUID_KERNEL(FAddCSimple, TAddCSimple, false)
+{
+    static const int Window = 1;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View   &in,
+                    const int                      cval,
+                          cv::gapi::fluid::Buffer &out)
+    {
+        for (int l = 0, lpi = out.lpi(); l < lpi; l++)
+        {
+            const uint8_t* in_row  = in .InLine <uint8_t>(l);
+                  uint8_t* out_row = out.OutLine<uint8_t>(l);
+            //std::cout << "l=" << l << ": ";
+            for (int i = 0, w = in.length(); i < w; i++)
+            {
+                //std::cout << std::setw(4) << int(in_row[i]);
+                out_row[i] = static_cast<uint8_t>(in_row[i] + cval);
+            }
+            //std::cout << std::endl;
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(FAddScalar, TAddScalar, false)
+{
+    static const int Window = 1;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View   &in,
+                    const cv::Scalar              &cval,
+                          cv::gapi::fluid::Buffer &out)
+    {
+        for (int l = 0, lpi = out.lpi(); l < lpi; l++)
+        {
+            const uint8_t* in_row  = in .InLine <uint8_t>(l);
+                  uint8_t* out_row = out.OutLine<uint8_t>(l);
+            std::cout << "l=" << l << ": ";
+            for (int i = 0, w = in.length(); i < w; i++)
+            {
+                std::cout << std::setw(4) << int(in_row[i]);
+                out_row[i] = static_cast<uint8_t>(in_row[i] + cval[0]);
+            }
+            std::cout << std::endl;
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(FAddScalarToMat, TAddScalarToMat, false)
+{
+    static const int Window = 1;
+    static const int LPI    = 2;
+
+    static void run(const cv::Scalar              &cval,
+                    const cv::gapi::fluid::View   &in,
+                          cv::gapi::fluid::Buffer &out)
+    {
+        for (int l = 0, lpi = out.lpi(); l < lpi; l++)
+        {
+            const uint8_t* in_row  = in .InLine <uint8_t>(l);
+                  uint8_t* out_row = out.OutLine<uint8_t>(l);
+            std::cout << "l=" << l << ": ";
+            for (int i = 0, w = in.length(); i < w; i++)
+            {
+                std::cout << std::setw(4) << int(in_row[i]);
+                out_row[i] = static_cast<uint8_t>(in_row[i] + cval[0]);
+            }
+            std::cout << std::endl;
+        }
+    }
+};
+
+template<int kernelSize, int lpi = 1>
+static void runBlur(const cv::gapi::fluid::View& src, cv::gapi::fluid::Buffer& dst)
+{
+    const auto borderSize = (kernelSize - 1) / 2;
+    const unsigned char* ins[kernelSize];
+
+    for (int l = 0; l < lpi; l++)
+    {
+        for (int i = 0; i < kernelSize; i++)
+        {
+            ins[i] = src.InLine<unsigned char>(i - borderSize + l);
+        }
+
+        auto out = dst.OutLine<unsigned char>(l);
+        const auto width = dst.length();
+
+        for (int w = 0; w < width; w++)
+        {
+            float res = 0.0f;
+            for (int i = 0; i < kernelSize; i++)
+            {
+                for (int j = -borderSize; j < borderSize + 1; j++)
+                {
+                    res += ins[i][w+j];
+                }
+            }
+            out[w] = static_cast<unsigned char>(std::rint(res / (kernelSize * kernelSize)));
+        }
+    }
+}
+
+GAPI_FLUID_KERNEL(FBlur1x1, TBlur1x1, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
+                    cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
+    {
+        runBlur<Window>(src, dst);
+    }
+};
+
+GAPI_FLUID_KERNEL(FBlur3x3, TBlur3x3, false)
+{
+    static const int Window = 3;
+
+    static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
+                    cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
+    {
+        runBlur<Window>(src, dst);
+    }
+
+    static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
+    {
+        return { borderType, to_own(borderValue)};
+    }
+};
+
+GAPI_FLUID_KERNEL(FBlur5x5, TBlur5x5, false)
+{
+    static const int Window = 5;
+
+    static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
+                    cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
+    {
+        runBlur<Window>(src, dst);
+    }
+
+    static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
+    {
+        return { borderType, to_own(borderValue)};
+    }
+};
+
+GAPI_FLUID_KERNEL(FBlur3x3_2lpi, TBlur3x3_2lpi, false)
+{
+    static const int Window = 3;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
+                    cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
+    {
+        runBlur<Window, LPI>(src, dst);
+    }
+
+    static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
+    {
+        return { borderType, to_own(borderValue)};
+    }
+};
+
+GAPI_FLUID_KERNEL(FBlur5x5_2lpi, TBlur5x5_2lpi, false)
+{
+    static const int Window = 5;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
+                    cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
+    {
+        runBlur<Window, LPI>(src, dst);
+    }
+
+    static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
+    {
+        return { borderType, to_own(borderValue )};
+    }
+};
+
+GAPI_FLUID_KERNEL(FIdentity, TId, false)
+{
+    static const int Window = 3;
+
+    static void run(const cv::gapi::fluid::View   &a,
+                          cv::gapi::fluid::Buffer &o)
+    {
+        const uint8_t* in[3] = {
+            a.InLine<uint8_t>(-1),
+            a.InLine<uint8_t>( 0),
+            a.InLine<uint8_t>(+1)
+        };
+        uint8_t* out = o.OutLine<uint8_t>();
+
+        // ReadFunction3x3(in, a.length());
+        for (int i = 0, w = a.length(); i < w; i++)
+        {
+            out[i] = in[1][i];
+        }
+    }
+
+    static gapi::fluid::Border getBorder(const cv::GMatDesc &)
+    {
+        return { cv::BORDER_REPLICATE, cv::gapi::own::Scalar{} };
+    }
+};
+
+GAPI_FLUID_KERNEL(FId7x7, TId7x7, false)
+{
+    static const int Window = 7;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View   &a,
+                          cv::gapi::fluid::Buffer &o)
+    {
+        for (int l = 0, lpi = o.lpi(); l < lpi; l++)
+        {
+            const uint8_t* in[Window] = {
+                a.InLine<uint8_t>(-3 + l),
+                a.InLine<uint8_t>(-2 + l),
+                a.InLine<uint8_t>(-1 + l),
+                a.InLine<uint8_t>( 0 + l),
+                a.InLine<uint8_t>(+1 + l),
+                a.InLine<uint8_t>(+2 + l),
+                a.InLine<uint8_t>(+3 + l),
+            };
+            uint8_t* out = o.OutLine<uint8_t>(l);
+
+            // std::cout << "Id7x7 " << l << " of " << lpi << " {{{\n";
+            // std::cout << "  a - "; a.debug(std::cout);
+            // std::cout << "  o - "; o.debug(std::cout);
+            // std::cout << "}}} " << std::endl;;
+
+            // // std::cout << "Id7x7 at " << a.y() << "/L" << l <<  " {{{" << std::endl;
+            // for (int j = 0; j < Window; j++)
+            // {
+            //     // std::cout << std::setw(2) << j-(Window-1)/2 << ": ";
+            //     for (int i = 0, w = a.length(); i < w; i++)
+            //         std::cout << std::setw(4) << int(in[j][i]);
+            //     std::cout << std::endl;
+            // }
+            // std::cout << "}}}" << std::endl;
+
+            for (int i = 0, w = a.length(); i < w; i++)
+                out[i] = in[(Window-1)/2][i];
+        }
+    }
+
+    static cv::gapi::fluid::Border getBorder(const cv::GMatDesc&/* src*/)
+    {
+        return { cv::BORDER_REPLICATE, cv::gapi::own::Scalar{} };
+    }
+};
+
+GAPI_FLUID_KERNEL(FPlusRow0, TPlusRow0, true)
+{
+    static const int Window = 1;
+
+    static void initScratch(const cv::GMatDesc            &in,
+                                  cv::gapi::fluid::Buffer &scratch)
+    {
+        cv::Size scratch_size{in.size.width, 1};
+        cv::gapi::fluid::Buffer buffer(in.withSize(scratch_size));
+        scratch = std::move(buffer);
+    }
+
+    static void resetScratch(cv::gapi::fluid::Buffer &scratch)
+    {
+        // FIXME: only 1 line can be used!
+        uint8_t* out_row = scratch.OutLine<uint8_t>();
+        for (int i = 0, w = scratch.length(); i < w; i++)
+        {
+            out_row[i] = 0;
+        }
+    }
+
+    static void run(const cv::gapi::fluid::View   &in,
+                          cv::gapi::fluid::Buffer &out,
+                          cv::gapi::fluid::Buffer &scratch)
+    {
+        const uint8_t* in_row  = in     .InLine <uint8_t>(0);
+              uint8_t* out_row = out    .OutLine<uint8_t>();
+              uint8_t* tmp_row = scratch.OutLine<uint8_t>();
+
+        if (in.y() == 0)
+        {
+            // Copy 1st row to scratch buffer
+            for (int i = 0, w = in.length(); i < w; i++)
+            {
+                out_row[i] = in_row[i];
+                tmp_row[i] = in_row[i];
+            }
+        }
+        else
+        {
+            // Output is 1st row + in
+            for (int i = 0, w = in.length(); i < w; i++)
+            {
+                out_row[i] = in_row[i] + tmp_row[i];
+            }
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(FTestSplit3, cv::gapi::core::GSplit3, false)
+{
+    static const int Window = 1;
+
+    static void run(const cv::gapi::fluid::View   &in,
+                          cv::gapi::fluid::Buffer &o1,
+                          cv::gapi::fluid::Buffer &o2,
+                          cv::gapi::fluid::Buffer &o3)
+    {
+        // std::cout << "Split3  {{{\n";
+        // std::cout << "  a - "; in.debug(std::cout);
+        // std::cout << "  1 - "; o1.debug(std::cout);
+        // std::cout << "  2 - "; o2.debug(std::cout);
+        // std::cout << "  3 - "; o3.debug(std::cout);
+        // std::cout << "}}} " << std::endl;;
+
+        const uint8_t* in_rgb = in.InLine<uint8_t>(0);
+              uint8_t* out_r  = o1.OutLine<uint8_t>();
+              uint8_t* out_g  = o2.OutLine<uint8_t>();
+              uint8_t* out_b  = o3.OutLine<uint8_t>();
+
+        for (int i = 0, w = in.length(); i < w; i++)
+        {
+            out_r[i] = in_rgb[3*i];
+            out_g[i] = in_rgb[3*i+1];
+            out_b[i] = in_rgb[3*i+2];
+        }
+    }
+};
+
+GAPI_FLUID_KERNEL(FSum2MatsAndScalar, TSum2MatsAndScalar, false)
+{
+    static const int Window = 1;
+    static const int LPI    = 2;
+
+    static void run(const cv::gapi::fluid::View   &a,
+                    const cv::Scalar              &cval,
+                    const cv::gapi::fluid::View   &b,
+                          cv::gapi::fluid::Buffer &out)
+    {
+        for (int l = 0, lpi = out.lpi(); l < lpi; l++)
+        {
+            const uint8_t* in_row1  = a .InLine <uint8_t>(l);
+            const uint8_t* in_row2  = b .InLine <uint8_t>(l);
+                  uint8_t* out_row = out.OutLine<uint8_t>(l);
+            std::cout << "l=" << l << ": ";
+            for (int i = 0, w = a.length(); i < w; i++)
+            {
+                std::cout << std::setw(4) << int(in_row1[i]);
+                std::cout << std::setw(4) << int(in_row2[i]);
+                out_row[i] = static_cast<uint8_t>(in_row1[i] + in_row2[i] + cval[0]);
+            }
+            std::cout << std::endl;
+        }
+    }
+};
+
+cv::gapi::GKernelPackage fluidTestPackage = cv::gapi::kernels
+        <FAddSimple
+        ,FAddCSimple
+        ,FAddScalar
+        ,FAddScalarToMat
+        ,FBlur1x1
+        ,FBlur3x3
+        ,FBlur5x5
+        ,FBlur3x3_2lpi
+        ,FBlur5x5_2lpi
+        ,FIdentity
+        ,FId7x7
+        ,FPlusRow0
+        ,FSum2MatsAndScalar
+        ,FTestSplit3
+        >();
+} // namespace gapi_test_kernels
+} // namespace cv
diff --git a/modules/gapi/test/gapi_fluid_test_kernels.hpp b/modules/gapi/test/gapi_fluid_test_kernels.hpp
new file mode 100644 (file)
index 0000000..f5d83ed
--- /dev/null
@@ -0,0 +1,105 @@
+// 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 GAPI_FLUID_TEST_KERNELS_HPP
+#define GAPI_FLUID_TEST_KERNELS_HPP
+
+#include "opencv2/gapi/fluid/gfluidkernel.hpp"
+
+namespace cv
+{
+namespace gapi_test_kernels
+{
+
+G_TYPED_KERNEL(TAddSimple, <GMat(GMat, GMat)>, "test.fluid.add_simple") {
+    static cv::GMatDesc outMeta(cv::GMatDesc a, cv::GMatDesc) {
+        return a;
+    }
+};
+
+G_TYPED_KERNEL(TAddCSimple, <GMat(GMat,int)>, "test.fluid.addc_simple")
+{
+    static GMatDesc outMeta(const cv::GMatDesc &in, int) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TAddScalar, <GMat(GMat,GScalar)>, "test.fluid.addc_scalar")
+{
+    static GMatDesc outMeta(const cv::GMatDesc &in, const cv::GScalarDesc&) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TAddScalarToMat, <GMat(GScalar,GMat)>, "test.fluid.add_scalar_to_mat")
+{
+    static GMatDesc outMeta(const cv::GScalarDesc&, const cv::GMatDesc &in) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TBlur1x1, <GMat(GMat,int,Scalar)>, "org.opencv.imgproc.filters.blur1x1"){
+    static GMatDesc outMeta(GMatDesc in, int, Scalar) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TBlur3x3, <GMat(GMat,int,Scalar)>, "org.opencv.imgproc.filters.blur3x3"){
+    static GMatDesc outMeta(GMatDesc in, int, Scalar) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TBlur5x5, <GMat(GMat,int,Scalar)>, "org.opencv.imgproc.filters.blur5x5"){
+    static GMatDesc outMeta(GMatDesc in, int, Scalar) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TBlur3x3_2lpi, <GMat(GMat,int,Scalar)>, "org.opencv.imgproc.filters.blur3x3_2lpi"){
+    static GMatDesc outMeta(GMatDesc in, int, Scalar) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TBlur5x5_2lpi, <GMat(GMat,int,Scalar)>, "org.opencv.imgproc.filters.blur5x5_2lpi"){
+    static GMatDesc outMeta(GMatDesc in, int, Scalar) {
+        return in;
+    }
+};
+
+G_TYPED_KERNEL(TId, <GMat(GMat)>, "test.fluid.identity") {
+    static cv::GMatDesc outMeta(cv::GMatDesc a) {
+        return a;
+    }
+};
+
+G_TYPED_KERNEL(TId7x7, <GMat(GMat)>, "test.fluid.identity7x7") {
+    static cv::GMatDesc outMeta(cv::GMatDesc a) {
+        return a;
+    }
+};
+
+G_TYPED_KERNEL(TPlusRow0, <GMat(GMat)>, "test.fluid.plus_row0") {
+    static cv::GMatDesc outMeta(cv::GMatDesc a) {
+        return a;
+    }
+};
+
+G_TYPED_KERNEL(TSum2MatsAndScalar, <GMat(GMat,GScalar,GMat)>, "test.fluid.sum_2_mats_and_scalar")
+{
+    static GMatDesc outMeta(const cv::GMatDesc &in, const cv::GScalarDesc&, const cv::GMatDesc&) {
+        return in;
+    }
+};
+
+extern cv::gapi::GKernelPackage fluidTestPackage;
+
+} // namespace gapi_test_kernels
+} // namespace cv
+
+#endif // GAPI_FLUID_TEST_KERNELS_HPP
diff --git a/modules/gapi/test/gapi_gcompiled_tests.cpp b/modules/gapi/test/gapi_gcompiled_tests.cpp
new file mode 100644 (file)
index 0000000..e482e2e
--- /dev/null
@@ -0,0 +1,173 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    static cv::GMat DemoCC(cv::GMat in, cv::GScalar scale)
+    {
+        return cv::gapi::medianBlur(in + in*scale, 3);
+    }
+
+    struct GCompiledValidateMetaTyped: public ::testing::Test
+    {
+        cv::GComputationT<cv::GMat(cv::GMat,cv::GScalar)> m_cc;
+
+        GCompiledValidateMetaTyped() : m_cc(DemoCC)
+        {
+        }
+    };
+
+    struct GCompiledValidateMetaUntyped: public ::testing::Test
+    {
+        cv::GMat in;
+        cv::GScalar scale;
+        cv::GComputation m_ucc;
+
+        GCompiledValidateMetaUntyped() : m_ucc(cv::GIn(in, scale),
+                                               cv::GOut(DemoCC(in, scale)))
+        {
+        }
+    };
+} // anonymous namespace
+
+TEST_F(GCompiledValidateMetaTyped, ValidMeta)
+{
+    cv::Mat in = cv::Mat::eye(cv::Size(128, 32), CV_8UC1);
+    cv::Scalar sc(127);
+
+    auto f = m_cc.compile(cv::descr_of(in),
+                          cv::descr_of(sc));
+
+    // Correct operation when meta is exactly the same
+    cv::Mat out;
+    EXPECT_NO_THROW(f(in, sc, out));
+
+    // Correct operation on next invocation with same meta
+    // taken from different input objects
+    cv::Mat in2 = cv::Mat::zeros(cv::Size(128, 32), CV_8UC1);
+    cv::Scalar sc2(64);
+    cv::Mat out2;
+    EXPECT_NO_THROW(f(in2, sc2, out2));
+}
+
+TEST_F(GCompiledValidateMetaTyped, InvalidMeta)
+{
+    auto f = m_cc.compile(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(64,32)},
+                          cv::empty_scalar_desc());
+
+    cv::Scalar sc(33);
+    cv::Mat out;
+
+    // 3 channels intead 1
+    cv::Mat in1 = cv::Mat::eye(cv::Size(64,32), CV_8UC3);
+    EXPECT_THROW(f(in1, sc, out), std::logic_error);
+
+    // 32f intead 8u
+    cv::Mat in2 = cv::Mat::eye(cv::Size(64,32), CV_32F);
+    EXPECT_THROW(f(in2, sc, out), std::logic_error);
+
+    // 32x32 instead of 64x32
+    cv::Mat in3 = cv::Mat::eye(cv::Size(32,32), CV_8UC1);
+    EXPECT_THROW(f(in3, sc, out), std::logic_error);
+
+    // All is wrong
+    cv::Mat in4 = cv::Mat::eye(cv::Size(128,64), CV_32FC3);
+    EXPECT_THROW(f(in4, sc, out), std::logic_error);
+}
+
+TEST_F(GCompiledValidateMetaUntyped, ValidMeta)
+{
+    cv::Mat in1 = cv::Mat::eye(cv::Size(128, 32), CV_8UC1);
+    cv::Scalar sc(127);
+
+    auto f = m_ucc.compile(cv::descr_of(in1),
+                           cv::descr_of(sc));
+
+    // Correct operation when meta is exactly the same
+    cv::Mat out1;
+    EXPECT_NO_THROW(f(cv::gin(in1, sc), cv::gout(out1)));
+
+    // Correct operation on next invocation with same meta
+    // taken from different input objects
+    cv::Mat in2 = cv::Mat::zeros(cv::Size(128, 32), CV_8UC1);
+    cv::Scalar sc2(64);
+    cv::Mat out2;
+    EXPECT_NO_THROW(f(cv::gin(in2, sc2), cv::gout(out2)));
+}
+
+TEST_F(GCompiledValidateMetaUntyped, InvalidMetaValues)
+{
+    auto f = m_ucc.compile(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(64,32)},
+                           cv::empty_scalar_desc());
+
+    cv::Scalar sc(33);
+    cv::Mat out;
+
+    // 3 channels intead 1
+    cv::Mat in1 = cv::Mat::eye(cv::Size(64,32), CV_8UC3);
+    EXPECT_THROW(f(cv::gin(in1, sc), cv::gout(out)), std::logic_error);
+
+    // 32f intead 8u
+    cv::Mat in2 = cv::Mat::eye(cv::Size(64,32), CV_32F);
+    EXPECT_THROW(f(cv::gin(in2, sc), cv::gout(out)), std::logic_error);
+
+    // 32x32 instead of 64x32
+    cv::Mat in3 = cv::Mat::eye(cv::Size(32,32), CV_8UC1);
+    EXPECT_THROW(f(cv::gin(in3, sc), cv::gout(out)), std::logic_error);
+
+    // All is wrong
+    cv::Mat in4 = cv::Mat::eye(cv::Size(128,64), CV_32FC3);
+    EXPECT_THROW(f(cv::gin(in4, sc), cv::gout(out)), std::logic_error);
+}
+
+TEST_F(GCompiledValidateMetaUntyped, InvalidMetaShape)
+{
+    auto f = m_ucc.compile(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(64,32)},
+                           cv::empty_scalar_desc());
+
+    cv::Mat in1 = cv::Mat::eye(cv::Size(64,32), CV_8UC1);
+    cv::Scalar sc(33);
+    cv::Mat out1;
+
+    // call as f(Mat,Mat) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(in1, in1), cv::gout(out1)), std::logic_error);
+
+    // call as f(Scalar,Mat) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(sc, in1), cv::gout(out1)), std::logic_error);
+
+    // call as f(Scalar,Scalar) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(sc, sc), cv::gout(out1)), std::logic_error);
+}
+
+TEST_F(GCompiledValidateMetaUntyped, InvalidMetaNumber)
+{
+    auto f = m_ucc.compile(cv::GMatDesc{CV_8U,1,cv::Size(64,32)},
+                           cv::empty_scalar_desc());
+
+    cv::Mat in1 = cv::Mat::eye(cv::Size(64,32), CV_8UC1);
+    cv::Scalar sc(33);
+    cv::Mat out1, out2;
+
+    // call as f(Mat,Scalar,Scalar) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(in1, sc, sc), cv::gout(out1)), std::logic_error);
+
+    // call as f(Scalar,Mat,Scalar) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(sc, in1, sc), cv::gout(out1)), std::logic_error);
+
+    // call as f(Scalar) while f(Mat,Scalar) is expected
+    EXPECT_THROW(f(cv::gin(sc), cv::gout(out1)), std::logic_error);
+
+    // call as f(Mat,Scalar,[out1],[out2]) while f(Mat,Scalar,[out]) is expected
+    EXPECT_THROW(f(cv::gin(in1, sc), cv::gout(out1, out2)), std::logic_error);
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_gcomputation_tests.cpp b/modules/gapi/test/gapi_gcomputation_tests.cpp
new file mode 100644 (file)
index 0000000..070cea6
--- /dev/null
@@ -0,0 +1,68 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+namespace opencv_test
+{
+
+  namespace
+  {
+      G_TYPED_KERNEL(CustomResize, <cv::GMat(cv::GMat, cv::Size, double, double, int)>, "org.opencv.customk.resize")
+      {
+          static cv::GMatDesc outMeta(cv::GMatDesc in, cv::Size sz, double fx, double fy, int) {
+              if (sz.width != 0 && sz.height != 0)
+              {
+                  return in.withSize(to_own(sz));
+              }
+              else
+              {
+                  GAPI_Assert(fx != 0. && fy != 0.);
+                  return in.withSize
+                    (cv::gapi::own::Size(static_cast<int>(std::round(in.size.width  * fx)),
+                                         static_cast<int>(std::round(in.size.height * fy))));
+              }
+          }
+      };
+
+      GAPI_OCV_KERNEL(CustomResizeImpl, CustomResize)
+      {
+          static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp, cv::Mat &out)
+          {
+              cv::resize(in, out, sz, fx, fy, interp);
+          }
+      };
+
+      struct GComputationApplyTest: public ::testing::Test
+      {
+          cv::GMat in;
+          cv::Mat  in_mat;
+          cv::Mat  out_mat;
+          cv::GComputation m_c;
+
+          GComputationApplyTest() : in_mat(300, 300, CV_8UC1),
+                                    m_c(cv::GIn(in), cv::GOut(CustomResize::on(in, cv::Size(100, 100),
+                                                                               0.0, 0.0, cv::INTER_LINEAR)))
+          {
+          }
+      };
+  }
+
+  TEST_F(GComputationApplyTest, ThrowDontPassCustomKernel)
+  {
+      EXPECT_THROW(m_c.apply(in_mat, out_mat), std::logic_error);
+  }
+
+  TEST_F(GComputationApplyTest, NoThrowPassCustomKernel)
+  {
+      const auto pkg = cv::gapi::kernels<CustomResizeImpl>();
+
+      ASSERT_NO_THROW(m_c.apply(in_mat, out_mat, cv::compile_args(pkg)));
+  }
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_kernel_tests.cpp b/modules/gapi/test/gapi_kernel_tests.cpp
new file mode 100644 (file)
index 0000000..509c862
--- /dev/null
@@ -0,0 +1,202 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+#include "gapi_mock_kernels.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    G_TYPED_KERNEL(GClone, <GMat(GMat)>, "org.opencv.test.clone")
+    {
+        static GMatDesc outMeta(GMatDesc in) { return in;  }
+
+    };
+
+    GAPI_OCV_KERNEL(GCloneImpl, GClone)
+    {
+        static void run(const cv::Mat& in, cv::Mat &out)
+        {
+            out = in.clone();
+        }
+    };
+}
+
+TEST(KernelPackage, Create)
+{
+    namespace J = Jupiter;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    EXPECT_EQ(3u, pkg.size());
+}
+
+TEST(KernelPackage, Includes)
+{
+    namespace J = Jupiter;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    EXPECT_TRUE (pkg.includes<J::Foo>());
+    EXPECT_TRUE (pkg.includes<J::Bar>());
+    EXPECT_TRUE (pkg.includes<J::Baz>());
+    EXPECT_FALSE(pkg.includes<J::Qux>());
+}
+
+TEST(KernelPackage, Include)
+{
+    namespace J = Jupiter;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    EXPECT_FALSE(pkg.includes<J::Qux>());
+
+    pkg.include<J::Qux>();
+    EXPECT_TRUE(pkg.includes<J::Qux>());
+}
+
+TEST(KernelPackage, CreateHetero)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz, S::Qux>();
+    EXPECT_EQ(4u, pkg.size());
+}
+
+TEST(KernelPackage, IncludesHetero)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz, S::Qux>();
+    EXPECT_TRUE (pkg.includes<J::Foo>());
+    EXPECT_TRUE (pkg.includes<J::Bar>());
+    EXPECT_TRUE (pkg.includes<J::Baz>());
+    EXPECT_FALSE(pkg.includes<J::Qux>());
+    EXPECT_TRUE (pkg.includes<S::Qux>());
+}
+
+TEST(KernelPackage, IncludeHetero)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    EXPECT_FALSE(pkg.includes<J::Qux>());
+    EXPECT_FALSE(pkg.includes<S::Qux>());
+
+    pkg.include<S::Qux>();
+    EXPECT_FALSE(pkg.includes<J::Qux>());
+    EXPECT_TRUE (pkg.includes<S::Qux>());
+}
+
+TEST(KernelPackage, Unite_REPLACE_Full)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto j_pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    auto s_pkg = cv::gapi::kernels<S::Foo, S::Bar, S::Baz>();
+    auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE);
+
+    EXPECT_EQ(3u, u_pkg.size());
+    EXPECT_FALSE(u_pkg.includes<J::Foo>());
+    EXPECT_FALSE(u_pkg.includes<J::Bar>());
+    EXPECT_FALSE(u_pkg.includes<J::Baz>());
+    EXPECT_TRUE (u_pkg.includes<S::Foo>());
+    EXPECT_TRUE (u_pkg.includes<S::Bar>());
+    EXPECT_TRUE (u_pkg.includes<S::Baz>());
+}
+
+TEST(KernelPackage, Unite_REPLACE_Partial)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto j_pkg = cv::gapi::kernels<J::Foo, J::Bar>();
+    auto s_pkg = cv::gapi::kernels<S::Bar>();
+    auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE);
+
+    EXPECT_EQ(2u, u_pkg.size());
+    EXPECT_TRUE (u_pkg.includes<J::Foo>());
+    EXPECT_FALSE(u_pkg.includes<J::Bar>());
+    EXPECT_TRUE (u_pkg.includes<S::Bar>());
+}
+
+TEST(KernelPackage, Unite_REPLACE_Append)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto j_pkg = cv::gapi::kernels<J::Foo, J::Bar>();
+    auto s_pkg = cv::gapi::kernels<S::Qux>();
+    auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE);
+
+    EXPECT_EQ(3u, u_pkg.size());
+    EXPECT_TRUE(u_pkg.includes<J::Foo>());
+    EXPECT_TRUE(u_pkg.includes<J::Bar>());
+    EXPECT_TRUE(u_pkg.includes<S::Qux>());
+}
+
+TEST(KernelPackage, Unite_KEEP_AllDups)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto j_pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+    auto s_pkg = cv::gapi::kernels<S::Foo, S::Bar, S::Baz>();
+    auto u_pkg = cv::gapi::combine(j_pkg ,s_pkg, cv::unite_policy::KEEP);
+
+    EXPECT_EQ(6u, u_pkg.size());
+    EXPECT_TRUE(u_pkg.includes<J::Foo>());
+    EXPECT_TRUE(u_pkg.includes<J::Bar>());
+    EXPECT_TRUE(u_pkg.includes<J::Baz>());
+    EXPECT_TRUE(u_pkg.includes<S::Foo>());
+    EXPECT_TRUE(u_pkg.includes<S::Bar>());
+    EXPECT_TRUE(u_pkg.includes<S::Baz>());
+}
+
+TEST(KernelPackage, Unite_KEEP_Append_NoDups)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    auto j_pkg = cv::gapi::kernels<J::Foo, J::Bar>();
+    auto s_pkg = cv::gapi::kernels<S::Qux>();
+    auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::KEEP);
+
+    EXPECT_EQ(3u, u_pkg.size());
+    EXPECT_TRUE(u_pkg.includes<J::Foo>());
+    EXPECT_TRUE(u_pkg.includes<J::Bar>());
+    EXPECT_TRUE(u_pkg.includes<S::Qux>());
+}
+
+TEST(KernelPackage, TestWithEmptyLHS)
+{
+    namespace J = Jupiter;
+    auto lhs = cv::gapi::kernels<>();
+    auto rhs = cv::gapi::kernels<J::Foo>();
+    auto pkg = cv::gapi::combine(lhs, rhs, cv::unite_policy::KEEP);
+
+    EXPECT_EQ(1u, pkg.size());
+    EXPECT_TRUE(pkg.includes<J::Foo>());
+}
+
+TEST(KernelPackage, TestWithEmptyRHS)
+{
+    namespace J = Jupiter;
+    auto lhs = cv::gapi::kernels<J::Foo>();
+    auto rhs = cv::gapi::kernels<>();
+    auto pkg = cv::gapi::combine(lhs, rhs, cv::unite_policy::KEEP);
+
+    EXPECT_EQ(1u, pkg.size());
+    EXPECT_TRUE(pkg.includes<J::Foo>());
+}
+
+TEST(KernelPackage, Can_Use_Custom_Kernel)
+{
+    cv::GMat in[2];
+    auto out = GClone::on(cv::gapi::add(in[0], in[1]));
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
+
+    auto pkg = cv::gapi::kernels<GCloneImpl>();
+
+    EXPECT_NO_THROW(cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)).
+                        compile({in_meta, in_meta}, cv::compile_args(pkg)));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_mock_kernels.hpp b/modules/gapi/test/gapi_mock_kernels.hpp
new file mode 100644 (file)
index 0000000..cd876ef
--- /dev/null
@@ -0,0 +1,123 @@
+// 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
+
+
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+
+#include "api/gbackend_priv.hpp" // directly instantiate GBackend::Priv
+
+namespace opencv_test
+{
+namespace {
+    // FIXME: Currently every Kernel implementation in this test file has
+    // its own backend() method and it is incorrect! API classes should
+    // provide it out of the box.
+
+namespace I
+{
+    G_TYPED_KERNEL(Foo, <cv::GMat(cv::GMat)>, "test.kernels.foo")
+    {
+        static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; }
+    };
+
+    G_TYPED_KERNEL(Bar, <cv::GMat(cv::GMat,cv::GMat)>, "test.kernels.bar")
+    {
+        static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) { return in; }
+    };
+
+    G_TYPED_KERNEL(Baz, <cv::GScalar(cv::GMat)>, "test.kernels.baz")
+    {
+        static cv::GScalarDesc outMeta(const cv::GMatDesc &) { return cv::empty_scalar_desc(); }
+    };
+
+    G_TYPED_KERNEL(Qux, <cv::GMat(cv::GMat, cv::GScalar)>, "test.kernels.qux")
+    {
+        static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GScalarDesc &) { return in; }
+    };
+
+    G_TYPED_KERNEL(Quux, <cv::GMat(cv::GScalar, cv::GMat)>, "test.kernels.quux")
+    {
+        static cv::GMatDesc outMeta(const cv::GScalarDesc &, const cv::GMatDesc& in) { return in; }
+    };
+}
+
+// Kernel implementations for imaginary Jupiter device
+namespace Jupiter
+{
+    namespace detail
+    {
+        static cv::gapi::GBackend backend(std::make_shared<cv::gapi::GBackend::Priv>());
+    }
+
+    inline cv::gapi::GBackend backend() { return detail::backend; }
+
+    GAPI_OCV_KERNEL(Foo, I::Foo)
+    {
+        static void run(const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Bar, I::Bar)
+    {
+        static void run(const cv::Mat &, const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Baz, I::Baz)
+    {
+        static void run(const cv::Mat &, cv::Scalar &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Qux, I::Qux)
+    {
+        static void run(const cv::Mat &, const cv::Scalar&, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+
+    GAPI_OCV_KERNEL(Quux, I::Quux)
+    {
+        static void run(const cv::Scalar&, const cv::Mat&, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+} // namespace Jupiter
+
+// Kernel implementations for imaginary Saturn device
+namespace Saturn
+{
+    namespace detail
+    {
+        static cv::gapi::GBackend backend(std::make_shared<cv::gapi::GBackend::Priv>());
+    }
+
+    inline cv::gapi::GBackend backend() { return detail::backend; }
+
+    GAPI_OCV_KERNEL(Foo, I::Foo)
+    {
+        static void run(const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Bar, I::Bar)
+    {
+        static void run(const cv::Mat &, const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Baz, I::Baz)
+    {
+        static void run(const cv::Mat &, cv::Scalar &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+    GAPI_OCV_KERNEL(Qux, I::Qux)
+    {
+        static void run(const cv::Mat &, const cv::Scalar&, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+
+    GAPI_OCV_KERNEL(Quux, I::Quux)
+    {
+        static void run(const cv::Scalar&, const cv::Mat&, cv::Mat &) { /*Do nothing*/ }
+        static cv::gapi::GBackend backend() { return detail::backend; } // FIXME: Must be removed
+    };
+} // namespace Saturn
+} // anonymous namespace
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_sample_pipelines.cpp b/modules/gapi/test/gapi_sample_pipelines.cpp
new file mode 100644 (file)
index 0000000..815aa0d
--- /dev/null
@@ -0,0 +1,301 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include <stdexcept>
+#include <ade/util/iota_range.hpp>
+#include "logger.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    G_TYPED_KERNEL(GInvalidResize, <GMat(GMat,Size,double,double,int)>, "org.opencv.test.invalid_resize")
+    {
+         static GMatDesc outMeta(GMatDesc in, Size, double, double, int) { return in; }
+    };
+
+    GAPI_OCV_KERNEL(GOCVInvalidResize, GInvalidResize)
+    {
+        static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp, cv::Mat &out)
+        {
+            cv::resize(in, out, sz, fx, fy, interp);
+        }
+    };
+
+    G_TYPED_KERNEL(GReallocatingCopy, <GMat(GMat)>, "org.opencv.test.reallocating_copy")
+    {
+         static GMatDesc outMeta(GMatDesc in) { return in; }
+    };
+
+    GAPI_OCV_KERNEL(GOCVReallocatingCopy, GReallocatingCopy)
+    {
+        static void run(const cv::Mat& in, cv::Mat &out)
+        {
+            out = in.clone();
+        }
+    };
+}
+
+TEST(GAPI_Pipeline, OverloadUnary_MatMat)
+{
+    cv::GMat in;
+    cv::GComputation comp(in, cv::gapi::bitwise_not(in));
+
+    cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Mat ref_mat = ~in_mat;
+
+    cv::Mat out_mat;
+    comp.apply(in_mat, out_mat);
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+
+    out_mat = cv::Mat();
+    auto cc = comp.compile(cv::descr_of(in_mat));
+    cc(in_mat, out_mat);
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GAPI_Pipeline, OverloadUnary_MatScalar)
+{
+    cv::GMat in;
+    cv::GComputation comp(in, cv::gapi::sum(in));
+
+    cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Scalar ref_scl = cv::sum(in_mat);
+
+    cv::Scalar out_scl;
+    comp.apply(in_mat, out_scl);
+    EXPECT_EQ(out_scl, ref_scl);
+
+    out_scl = cv::Scalar();
+    auto cc = comp.compile(cv::descr_of(in_mat));
+    cc(in_mat, out_scl);
+    EXPECT_EQ(out_scl, ref_scl);
+}
+
+TEST(GAPI_Pipeline, OverloadBinary_Mat)
+{
+    cv::GMat a, b;
+    cv::GComputation comp(a, b, cv::gapi::add(a, b));
+
+    cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Mat ref_mat = (in_mat+in_mat);
+
+    cv::Mat out_mat;
+    comp.apply(in_mat, in_mat, out_mat);
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+
+    out_mat = cv::Mat();
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::descr_of(in_mat));
+    cc(in_mat, in_mat, out_mat);
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GAPI_Pipeline, OverloadBinary_Scalar)
+{
+    cv::GMat a, b;
+    cv::GComputation comp(a, b, cv::gapi::sum(a + b));
+
+    cv::Mat in_mat = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Scalar ref_scl = cv::sum(in_mat+in_mat);
+
+    cv::Scalar out_scl;
+    comp.apply(in_mat, in_mat, out_scl);
+    EXPECT_EQ(out_scl, ref_scl);
+
+    out_scl = cv::Scalar();
+    auto cc = comp.compile(cv::descr_of(in_mat), cv::descr_of(in_mat));
+    cc(in_mat, in_mat, out_scl);
+    EXPECT_EQ(out_scl, ref_scl);
+}
+
+TEST(GAPI_Pipeline, Sharpen)
+{
+    const cv::Size sz_in (1280, 720);
+    const cv::Size sz_out( 640, 480);
+    cv::Mat in_mat (sz_in,  CV_8UC3);
+    in_mat = cv::Scalar(128, 33, 53);
+
+    cv::Mat out_mat(sz_out, CV_8UC3);
+    cv::Mat out_mat_y;
+    cv::Mat out_mat_ocv(sz_out, CV_8UC3);
+
+    float sharpen_coeffs[] = {
+         0.0f, -1.f,  0.0f,
+        -1.0f,  5.f, -1.0f,
+         0.0f, -1.f,  0.0f
+    };
+    cv::Mat sharpen_kernel(3, 3, CV_32F, sharpen_coeffs);
+
+    // G-API code //////////////////////////////////////////////////////////////
+
+    cv::GMat in;
+    auto vga     = cv::gapi::resize(in, sz_out);
+    auto yuv     = cv::gapi::RGB2YUV(vga);
+    auto yuv_p   = cv::gapi::split3(yuv);
+    auto y_sharp = cv::gapi::filter2D(std::get<0>(yuv_p), -1, sharpen_kernel);
+    auto yuv_new = cv::gapi::merge3(y_sharp, std::get<1>(yuv_p), std::get<2>(yuv_p));
+    auto out     = cv::gapi::YUV2RGB(yuv_new);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(y_sharp, out));
+    c.apply(cv::gin(in_mat), cv::gout(out_mat_y, out_mat));
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Mat smaller;
+        cv::resize(in_mat, smaller, sz_out);
+
+        cv::Mat yuv_mat;
+        cv::cvtColor(smaller, yuv_mat, cv::COLOR_RGB2YUV);
+        std::vector<cv::Mat> yuv_planar(3);
+        cv::split(yuv_mat, yuv_planar);
+        cv::filter2D(yuv_planar[0], yuv_planar[0], -1, sharpen_kernel);
+        cv::merge(yuv_planar, yuv_mat);
+        cv::cvtColor(yuv_mat, out_mat_ocv, cv::COLOR_YUV2RGB);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        cv::Mat diff = out_mat_ocv != out_mat;
+        std::vector<cv::Mat> diffBGR(3);
+        cv::split(diff, diffBGR);
+        EXPECT_EQ(0, cv::countNonZero(diffBGR[0]));
+        EXPECT_EQ(0, cv::countNonZero(diffBGR[1]));
+        EXPECT_EQ(0, cv::countNonZero(diffBGR[2]));
+    }
+
+    // Metadata check /////////////////////////////////////////////////////////
+    {
+        auto cc    = c.compile(cv::descr_of(in_mat));
+        auto metas = cc.outMetas();
+        ASSERT_EQ(2u, metas.size());
+
+        auto out_y_meta = cv::util::get<cv::GMatDesc>(metas[0]);
+        auto out_meta   = cv::util::get<cv::GMatDesc>(metas[1]);
+
+        // Y-output
+        EXPECT_EQ(CV_8U,   out_y_meta.depth);
+        EXPECT_EQ(1,       out_y_meta.chan);
+        EXPECT_EQ(640,     out_y_meta.size.width);
+        EXPECT_EQ(480,     out_y_meta.size.height);
+
+        // Final output
+        EXPECT_EQ(CV_8U,   out_meta.depth);
+        EXPECT_EQ(3,       out_meta.chan);
+        EXPECT_EQ(640,     out_meta.size.width);
+        EXPECT_EQ(480,     out_meta.size.height);
+    }
+}
+
+TEST(GAPI_Pipeline, CustomRGB2YUV)
+{
+    const cv::Size sz(1280, 720);
+
+    // BEWARE:
+    //
+    //    std::vector<cv::Mat> out_mats_cv(3, cv::Mat(sz, CV_8U))
+    //
+    // creates a vector of 3 elements pointing to the same Mat!
+    // FIXME: Make a G-API check for that
+    const int INS = 3;
+    std::vector<cv::Mat> in_mats(INS);
+    for (auto i : ade::util::iota(INS))
+    {
+        in_mats[i].create(sz, CV_8U);
+        cv::randu(in_mats[i], cv::Scalar::all(0), cv::Scalar::all(255));
+    }
+
+    const int OUTS = 3;
+    std::vector<cv::Mat> out_mats_cv(OUTS);
+    std::vector<cv::Mat> out_mats_gapi(OUTS);
+    for (auto i : ade::util::iota(OUTS))
+    {
+        out_mats_cv  [i].create(sz, CV_8U);
+        out_mats_gapi[i].create(sz, CV_8U);
+    }
+
+    // G-API code //////////////////////////////////////////////////////////////
+    {
+        cv::GMat r, g, b;
+        cv::GMat y = 0.299f*r + 0.587f*g + 0.114f*b;
+        cv::GMat u = 0.492f*(b - y);
+        cv::GMat v = 0.877f*(r - y);
+
+        cv::GComputation customCvt({r, g, b}, {y, u, v});
+        customCvt.apply(in_mats, out_mats_gapi);
+    }
+
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        cv::Mat r = in_mats[0], g = in_mats[1], b = in_mats[2];
+        cv::Mat y = 0.299f*r + 0.587f*g + 0.114f*b;
+        cv::Mat u = 0.492f*(b - y);
+        cv::Mat v = 0.877f*(r - y);
+
+        out_mats_cv[0] = y;
+        out_mats_cv[1] = u;
+        out_mats_cv[2] = v;
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        const auto diff = [](cv::Mat m1, cv::Mat m2, int t) {
+            return cv::abs(m1-m2) > t;
+        };
+
+        // FIXME: Not bit-accurate even now!
+        cv::Mat
+            diff_y = diff(out_mats_cv[0], out_mats_gapi[0], 2),
+            diff_u = diff(out_mats_cv[1], out_mats_gapi[1], 2),
+            diff_v = diff(out_mats_cv[2], out_mats_gapi[2], 2);
+
+        EXPECT_EQ(0, cv::countNonZero(diff_y));
+        EXPECT_EQ(0, cv::countNonZero(diff_u));
+        EXPECT_EQ(0, cv::countNonZero(diff_v));
+    }
+}
+
+TEST(GAPI_Pipeline, PipelineWithInvalidKernel)
+{
+    cv::GMat in, out;
+    cv::Mat in_mat(500, 500, CV_8UC1), out_mat;
+    out = GInvalidResize::on(in, cv::Size(300, 300), 0.0, 0.0, cv::INTER_LINEAR);
+
+    const auto pkg = cv::gapi::kernels<GOCVInvalidResize>();
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+    EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
+}
+
+TEST(GAPI_Pipeline, InvalidOutputComputation)
+{
+    cv::GMat in1, out1, out2, out3;
+
+    std::tie(out1, out2, out2) = cv::gapi::split3(in1);
+    cv::GComputation c({in1}, {out1, out2, out3});
+    cv::Mat in_mat;
+    cv::Mat out_mat1, out_mat2, out_mat3, out_mat4;
+    std::vector<cv::Mat> u_outs = {out_mat1, out_mat2, out_mat3, out_mat4};
+    std::vector<cv::Mat> u_ins = {in_mat};
+
+    EXPECT_THROW(c.apply(u_ins, u_outs), std::logic_error);
+}
+
+TEST(GAPI_Pipeline, PipelineAllocatingKernel)
+{
+    cv::GMat in, out;
+    cv::Mat in_mat(500, 500, CV_8UC1), out_mat;
+    out = GReallocatingCopy::on(in);
+
+    const auto pkg = cv::gapi::kernels<GOCVReallocatingCopy>();
+    cv::GComputation comp(cv::GIn(in), cv::GOut(out));
+
+    EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
+}
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_scalar_tests.cpp b/modules/gapi/test/gapi_scalar_tests.cpp
new file mode 100644 (file)
index 0000000..4c2a4f4
--- /dev/null
@@ -0,0 +1,116 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include <iostream>
+
+namespace opencv_test
+{
+
+TEST(GAPI_Scalar, Argument)
+{
+    cv::Size sz(2, 2);
+    cv::Mat in_mat(sz, CV_8U);
+
+    cv::GComputationT<cv::GMat (cv::GMat, cv::GScalar)> mulS([](cv::GMat in, cv::GScalar c)
+    {
+        return in*c;
+    });
+
+    cv::Mat out_mat(sz, CV_8U);
+    mulS.apply(in_mat, cv::Scalar(2), out_mat);
+
+    cv::Mat reference = in_mat*2;
+    EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat - reference)));
+}
+
+TEST(GAPI_Scalar, ReturnValue)
+{
+    const cv::Size sz(2, 2);
+    cv::Mat in_mat(sz, CV_8U, cv::Scalar(1));
+
+    cv::GComputationT<cv::GScalar (cv::GMat)> sum_of_sum([](cv::GMat in)
+    {
+        return cv::gapi::sum(in + in);
+    });
+
+    cv::Scalar out;
+    sum_of_sum.apply(in_mat, out);
+
+    EXPECT_EQ(8, out[0]);
+}
+
+TEST(GAPI_Scalar, TmpScalar)
+{
+    const cv::Size sz(2, 2);
+    cv::Mat in_mat(sz, CV_8U, cv::Scalar(1));
+
+    cv::GComputationT<cv::GMat (cv::GMat)> mul_by_sum([](cv::GMat in)
+    {
+        return in * cv::gapi::sum(in);
+    });
+
+    cv::Mat out_mat(sz, CV_8U);
+    mul_by_sum.apply(in_mat, out_mat);
+
+    cv::Mat reference = cv::Mat(sz, CV_8U, cv::Scalar(4));
+    EXPECT_EQ(0, cv::countNonZero(cv::abs(out_mat - reference)));
+}
+
+TEST(GAPI_ScalarWithValue, Simple_Arithmetic_Pipeline)
+{
+    GMat in;
+    GMat out = (in + 1) * 2;
+    cv::GComputation comp(in, out);
+
+    cv::Mat in_mat  = cv::Mat::eye(3, 3, CV_8UC1);
+    cv::Mat ref_mat, out_mat;
+
+    ref_mat = (in_mat + 1) * 2;
+    comp.apply(in_mat, out_mat);
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GAPI_ScalarWithValue, GScalar_Initilization)
+{
+    cv::Scalar sc(2);
+    cv::GMat in;
+    cv::GScalar s(sc);
+    cv::GComputation comp(in, cv::gapi::mulC(in, s));
+
+    cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1);
+    cv::Mat ref_mat, out_mat;
+    cv::multiply(in_mat, sc, ref_mat, 1, CV_8UC1);
+    comp.apply(cv::gin(in_mat), cv::gout(out_mat));
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+TEST(GAPI_ScalarWithValue, Constant_GScalar_In_Middle_Graph)
+{
+    cv::Scalar  sc(5);
+    cv::GMat    in1;
+    cv::GScalar in2;
+    cv::GScalar s(sc);
+
+    auto add_out = cv::gapi::addC(in1, in2);
+    cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(cv::gapi::mulC(add_out, s)));
+
+    cv::Mat    in_mat = cv::Mat::eye(3, 3, CV_8UC1);
+    cv::Scalar in_scalar(3);
+
+    cv::Mat ref_mat, out_mat, add_mat;
+    cv::add(in_mat, in_scalar, add_mat);
+    cv::multiply(add_mat, sc, ref_mat, 1, CV_8UC1);
+    comp.apply(cv::gin(in_mat, in_scalar), cv::gout(out_mat));
+
+    EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/gapi_smoke_test.cpp b/modules/gapi/test/gapi_smoke_test.cpp
new file mode 100644 (file)
index 0000000..9ac47f6
--- /dev/null
@@ -0,0 +1,97 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test
+{
+
+TEST(GAPI, Mat_Create_NoLink)
+{
+    cv::Mat m1;
+    cv::Mat m2 = m1;
+    m2.create(32, 32, CV_8U);
+
+    EXPECT_NE(m1.rows, m2.rows);
+    EXPECT_NE(m1.cols, m2.cols);
+    EXPECT_NE(m1.data, m2.data);
+}
+
+TEST(GAPI, Mat_Recreate)
+{
+    cv::Mat m1 = cv::Mat::zeros(480, 640, CV_8U);
+    m1.at<uchar>(0, 0) = 128;
+    cv::Mat m2 = m1;
+
+    EXPECT_EQ(m1.rows, m2.rows);
+    EXPECT_EQ(m1.cols, m2.cols);
+    EXPECT_EQ(m1.data, m2.data);
+    EXPECT_EQ(m1.at<uchar>(0, 0), m2.at<uchar>(0, 0));
+
+    // Calling "create" with the same meta is NOOP - both m1 and m2 are the same
+    m1.create(480, 640, CV_8U);
+    EXPECT_EQ(m1.rows, m2.rows);
+    EXPECT_EQ(m1.cols, m2.cols);
+    EXPECT_EQ(m1.data, m2.data);
+    EXPECT_EQ(m1.at<uchar>(0, 0), m2.at<uchar>(0, 0));
+
+    // Calling "create" on m2 with different meta doesn't update original m1
+    // Now m1 and m2 are distinct
+    m2.create(720, 1280, CV_8U);
+    m2.at<uchar>(0, 0) = 64; // Initialize 0,0 element since m2 is a new buffer
+    EXPECT_NE(m1.rows, m2.rows);
+    EXPECT_NE(m1.cols, m2.cols);
+    EXPECT_NE(m1.data, m2.data);
+    EXPECT_NE(m1.at<uchar>(0, 0), m2.at<uchar>(0, 0));
+
+    // What if a Mat is created from handle?
+    uchar data[] = {
+        32, 0, 0,
+         0, 0, 0,
+         0, 0, 0
+    };
+    cv::Mat m3(3, 3, CV_8U, data);
+    cv::Mat m4 = m3;
+    EXPECT_EQ(m3.rows, m4.rows);
+    EXPECT_EQ(m3.cols, m4.cols);
+    EXPECT_EQ(m3.data, m4.data);
+    EXPECT_EQ(data, m3.data);
+    EXPECT_EQ(data, m4.data);
+    EXPECT_EQ(m3.at<uchar>(0, 0), m4.at<uchar>(0, 0));
+
+    // cv::Mat::create must be NOOP if we don't change the meta,
+    // even if the origianl mat is created from handle.
+    m4.create(3, 3, CV_8U);
+    EXPECT_EQ(m3.rows, m4.rows);
+    EXPECT_EQ(m3.cols, m4.cols);
+    EXPECT_EQ(m3.data, m4.data);
+    EXPECT_EQ(data, m3.data);
+    EXPECT_EQ(data, m4.data);
+    EXPECT_EQ(m3.at<uchar>(0, 0), m4.at<uchar>(0, 0));
+}
+
+TEST(GAPI, EmptyOutMat)
+{
+    cv::Mat in_mat = cv::Mat(480, 640, CV_8U, cv::Scalar(64));
+
+    cv::GComputation cc([]()
+    {
+        cv::GMat in;
+        cv::GMat out = in + in;
+        return cv::GComputation(in, out);
+    });
+
+    cv::Mat out;
+    cc.apply(in_mat, out);
+
+    EXPECT_EQ(640, out.cols);
+    EXPECT_EQ(480, out.rows);
+    EXPECT_EQ(CV_8U, out.type());
+    EXPECT_EQ(0, cv::countNonZero(out - (in_mat+in_mat)));
+}
+
+}
diff --git a/modules/gapi/test/gapi_typed_tests.cpp b/modules/gapi/test/gapi_typed_tests.cpp
new file mode 100644 (file)
index 0000000..1716b55
--- /dev/null
@@ -0,0 +1,185 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test
+{
+
+namespace
+{
+    cv::Mat diff(cv::Mat m1, cv::Mat m2, int t)
+    {
+        return cv::abs(m1-m2) > t;
+    }
+
+    int non_zero3(cv::Mat m3c)
+    {
+        std::vector<cv::Mat> mm(3);
+        cv::split(m3c, mm);
+        return (  cv::countNonZero(mm[0])
+                + cv::countNonZero(mm[1])
+                + cv::countNonZero(mm[2]));
+    }
+}
+
+TEST(GAPI_Typed, UnaryOp)
+{
+    // Initialization //////////////////////////////////////////////////////////
+    const cv::Size sz(32, 32);
+    cv::Mat
+        in_mat         (sz, CV_8UC3),
+        out_mat_untyped(sz, CV_8UC3),
+        out_mat_typed1 (sz, CV_8UC3),
+        out_mat_typed2 (sz, CV_8UC3),
+        out_mat_cv     (sz, CV_8UC3);
+    cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255));
+
+    // Untyped G-API ///////////////////////////////////////////////////////////
+    cv::GComputation cvtU([]()
+    {
+        cv::GMat in;
+        cv::GMat out = cv::gapi::RGB2YUV(in);
+        return cv::GComputation(in, out);
+    });
+    cvtU.apply(in_mat, out_mat_untyped);
+
+    // Typed G-API /////////////////////////////////////////////////////////////
+    cv::GComputationT<cv::GMat (cv::GMat)> cvtT(cv::gapi::RGB2YUV);
+    auto cvtTComp = cvtT.compile(cv::descr_of(in_mat));
+
+    cvtT.apply(in_mat, out_mat_typed1);
+    cvtTComp(in_mat, out_mat_typed2);
+
+    // Plain OpenCV ////////////////////////////////////////////////////////////
+    cv::cvtColor(in_mat, out_mat_cv, cv::COLOR_RGB2YUV);
+
+    // Comparison //////////////////////////////////////////////////////////////
+    // FIXME: There must be OpenCV comparison test functions already available!
+    cv::Mat
+        diff_u  = diff(out_mat_cv, out_mat_untyped, 0),
+        diff_t  = diff(out_mat_cv, out_mat_typed1,  0),
+        diff_tc = diff(out_mat_cv, out_mat_typed2,  0);
+
+    EXPECT_EQ(0, non_zero3(diff_u));
+    EXPECT_EQ(0, non_zero3(diff_t));
+    EXPECT_EQ(0, non_zero3(diff_tc));
+}
+
+TEST(GAPI_Typed, BinaryOp)
+{
+    // Initialization //////////////////////////////////////////////////////////
+    const cv::Size sz(32, 32);
+    cv::Mat
+        in_mat1        (sz, CV_8UC1),
+        in_mat2        (sz, CV_8UC1),
+        out_mat_untyped(sz, CV_8UC1),
+        out_mat_typed1 (sz, CV_8UC1),
+        out_mat_typed2 (sz, CV_8UC1),
+        out_mat_cv     (sz, CV_8UC1);
+    cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
+    cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
+
+    // Untyped G-API ///////////////////////////////////////////////////////////
+    cv::GComputation cvtU([]()
+    {
+        cv::GMat in1, in2;
+        cv::GMat out = cv::gapi::add(in1, in2);
+        return cv::GComputation({in1, in2}, {out});
+    });
+    std::vector<cv::Mat> u_ins  = {in_mat1, in_mat2};
+    std::vector<cv::Mat> u_outs = {out_mat_untyped};
+    cvtU.apply(u_ins, u_outs);
+
+    // Typed G-API /////////////////////////////////////////////////////////////
+    cv::GComputationT<cv::GMat (cv::GMat, cv::GMat)> cvtT([](cv::GMat m1, cv::GMat m2)
+    {
+        return m1+m2;
+    });
+    auto cvtTC =  cvtT.compile(cv::descr_of(in_mat1),
+                               cv::descr_of(in_mat2));
+
+    cvtT.apply(in_mat1, in_mat2, out_mat_typed1);
+    cvtTC(in_mat1, in_mat2, out_mat_typed2);
+
+    // Plain OpenCV ////////////////////////////////////////////////////////////
+    cv::add(in_mat1, in_mat2, out_mat_cv);
+
+    // Comparison //////////////////////////////////////////////////////////////
+    // FIXME: There must be OpenCV comparison test functions already available!
+    cv::Mat
+        diff_u  = diff(out_mat_cv, out_mat_untyped, 0),
+        diff_t  = diff(out_mat_cv, out_mat_typed1,  0),
+        diff_tc = diff(out_mat_cv, out_mat_typed2,  0);
+
+    EXPECT_EQ(0, cv::countNonZero(diff_u));
+    EXPECT_EQ(0, cv::countNonZero(diff_t));
+    EXPECT_EQ(0, cv::countNonZero(diff_tc));
+}
+
+
+TEST(GAPI_Typed, MultipleOuts)
+{
+    // Initialization //////////////////////////////////////////////////////////
+    const cv::Size sz(32, 32);
+    cv::Mat
+        in_mat        (sz, CV_8UC1),
+        out_mat_unt1  (sz, CV_8UC1),
+        out_mat_unt2  (sz, CV_8UC1),
+        out_mat_typed1(sz, CV_8UC1),
+        out_mat_typed2(sz, CV_8UC1),
+        out_mat_comp1 (sz, CV_8UC1),
+        out_mat_comp2 (sz, CV_8UC1),
+        out_mat_cv1   (sz, CV_8UC1),
+        out_mat_cv2   (sz, CV_8UC1);
+    cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255));
+
+    // Untyped G-API ///////////////////////////////////////////////////////////
+    cv::GComputation cvtU([]()
+    {
+        cv::GMat in;
+        cv::GMat out1 = in * 2.f;
+        cv::GMat out2 = in * 4.f;
+        return cv::GComputation({in}, {out1, out2});
+    });
+    std::vector<cv::Mat> u_ins  = {in_mat};
+    std::vector<cv::Mat> u_outs = {out_mat_unt1, out_mat_unt2};
+    cvtU.apply(u_ins, u_outs);
+
+    // Typed G-API /////////////////////////////////////////////////////////////
+    cv::GComputationT<std::tuple<cv::GMat, cv::GMat> (cv::GMat)> cvtT([](cv::GMat in)
+    {
+        return std::make_tuple(in*2.f, in*4.f);
+    });
+    auto cvtTC =  cvtT.compile(cv::descr_of(in_mat));
+
+    cvtT.apply(in_mat, out_mat_typed1, out_mat_typed2);
+    cvtTC(in_mat, out_mat_comp1, out_mat_comp2);
+
+    // Plain OpenCV ////////////////////////////////////////////////////////////
+    out_mat_cv1 = in_mat * 2.f;
+    out_mat_cv2 = in_mat * 4.f;
+
+    // Comparison //////////////////////////////////////////////////////////////
+    // FIXME: There must be OpenCV comparison test functions already available!
+    cv::Mat
+        diff_u1 = diff(out_mat_cv1, out_mat_unt1,   0),
+        diff_u2 = diff(out_mat_cv2, out_mat_unt2,   0),
+        diff_t1 = diff(out_mat_cv1, out_mat_typed1, 0),
+        diff_t2 = diff(out_mat_cv2, out_mat_typed2, 0),
+        diff_c1 = diff(out_mat_cv1, out_mat_comp1,  0),
+        diff_c2 = diff(out_mat_cv2, out_mat_comp2,  0);
+
+    EXPECT_EQ(0, cv::countNonZero(diff_u1));
+    EXPECT_EQ(0, cv::countNonZero(diff_u2));
+    EXPECT_EQ(0, cv::countNonZero(diff_t1));
+    EXPECT_EQ(0, cv::countNonZero(diff_t2));
+    EXPECT_EQ(0, cv::countNonZero(diff_c1));
+    EXPECT_EQ(0, cv::countNonZero(diff_c2));
+}
+
+} // opencv_test
diff --git a/modules/gapi/test/gapi_util_tests.cpp b/modules/gapi/test/gapi_util_tests.cpp
new file mode 100644 (file)
index 0000000..574c0ab
--- /dev/null
@@ -0,0 +1,43 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include <type_traits>
+
+#include "opencv2/gapi/util/util.hpp"
+
+namespace opencv_test
+{
+
+TEST(GAPIUtil, AllSatisfy)
+{
+    static_assert(true == cv::detail::all_satisfy<std::is_integral, long, int, char>::value,
+                  "[long, int, char] are all integral types");
+    static_assert(true == cv::detail::all_satisfy<std::is_integral, char>::value,
+                  "char is an integral type");
+
+    static_assert(false == cv::detail::all_satisfy<std::is_integral, float, int, char>::value,
+                  "[float, int, char] are NOT all integral types");
+    static_assert(false == cv::detail::all_satisfy<std::is_integral, int, char, float>::value,
+                  "[int, char, float] are NOT all integral types");
+    static_assert(false == cv::detail::all_satisfy<std::is_integral, float>::value,
+                  "float is not an integral types");
+}
+
+TEST(GAPIUtil, AllButLast)
+{
+    using test1 = cv::detail::all_but_last<long, int, float>::type;
+    static_assert(true == cv::detail::all_satisfy<std::is_integral, test1>::value,
+                  "[long, int] are all integral types (float skipped)");
+
+    using test2 = cv::detail::all_but_last<int, float, char>::type;
+    static_assert(false == cv::detail::all_satisfy<std::is_integral, test2>::value,
+                  "[int, float] are NOT all integral types");
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_backend_tests.cpp b/modules/gapi/test/internal/gapi_int_backend_tests.cpp
new file mode 100644 (file)
index 0000000..67b6273
--- /dev/null
@@ -0,0 +1,86 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "gapi_mock_kernels.hpp"
+
+#include "compiler/gmodel.hpp"
+#include "compiler/gcompiler.hpp"
+
+namespace opencv_test {
+
+namespace {
+
+struct MockMeta
+{
+    static const char* name() { return "MockMeta"; }
+};
+
+class GMockBackendImpl final: public cv::gapi::GBackend::Priv
+{
+    virtual void unpackKernel(ade::Graph            &,
+                              const ade::NodeHandle &,
+                              const cv::GKernelImpl &) override
+    {
+        // Do nothing here
+    }
+
+    virtual EPtr compile(const ade::Graph &,
+                         const cv::GCompileArgs &,
+                         const std::vector<ade::NodeHandle> &) const override
+    {
+        // Do nothing here as well
+        return {};
+    }
+
+    virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override
+    {
+        ectx.addPass("transform", "set_mock_meta", [](ade::passes::PassContext &ctx) {
+                ade::TypedGraph<MockMeta> me(ctx.graph);
+                for (const auto &nh : me.nodes())
+                {
+                    me.metadata(nh).set(MockMeta{});
+                }
+            });
+    }
+};
+
+static cv::gapi::GBackend mock_backend(std::make_shared<GMockBackendImpl>());
+
+GAPI_OCV_KERNEL(MockFoo, I::Foo)
+{
+    static void run(const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
+    static cv::gapi::GBackend backend() { return mock_backend; } // FIXME: Must be removed
+};
+
+} // anonymous namespace
+
+TEST(GBackend, CustomPassesExecuted)
+{
+    cv::GMat in;
+    cv::GMat out = I::Foo::on(in);
+    cv::GComputation c(in, out);
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<MockFoo>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(c, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    // Inspect the graph and verify the metadata written by Mock backend
+    ade::TypedGraph<MockMeta> me(*graph);
+    EXPECT_LT(0u, static_cast<std::size_t>(me.nodes().size()));
+    for (const auto &nh : me.nodes())
+    {
+        EXPECT_TRUE(me.metadata(nh).contains<MockMeta>());
+    }
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_executor_tests.cpp b/modules/gapi/test/internal/gapi_int_executor_tests.cpp
new file mode 100644 (file)
index 0000000..20aad89
--- /dev/null
@@ -0,0 +1,83 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test
+{
+
+// FIXME: avoid code duplication
+// The below graph and config is taken from ComplexIslands test suite
+TEST(GExecutor, SmokeTest)
+{
+    cv::GMat    in[2];
+    cv::GMat    tmp[4];
+    cv::GScalar scl;
+    cv::GMat    out[2];
+
+    tmp[0] = cv::gapi::bitwise_not(cv::gapi::bitwise_not(in[0]));
+    tmp[1] = cv::gapi::boxFilter(in[1], -1, cv::Size(3,3));
+    tmp[2] = tmp[0] + tmp[1]; // FIXME: handle tmp[2] = tmp[0]+tmp[2] typo
+    scl    = cv::gapi::sum(tmp[1]);
+    tmp[3] = cv::gapi::medianBlur(tmp[1], 3);
+    out[0] = tmp[2] + scl;
+    out[1] = cv::gapi::boxFilter(tmp[3], -1, cv::Size(3,3));
+
+    //       isl0                                         #internal1
+    //       ...........................                  .........
+    // (in1) -> NotNot ->(tmp0) --> Add ---------> (tmp2) --> AddC -------> (out1)
+    //       :.....................^...:                  :..^....:
+    //                             :                         :
+    //                             :                         :
+    //      #internal0             :                         :
+    //        .....................:.........                :
+    // (in2) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //        :..........:..................:                  isl1
+    //                   :           ..............................
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out2)
+    //                               :............................:
+
+    cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]),  cv::GOut(tmp[2]));
+    cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
+
+    cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Mat in_mat2 = cv::Mat::eye(32, 32, CV_8UC1);
+    cv::Mat out_gapi[2];
+
+    // Run G-API:
+    cv::GComputation(cv::GIn(in[0],   in[1]),    cv::GOut(out[0],      out[1]))
+              .apply(cv::gin(in_mat1, in_mat2),  cv::gout(out_gapi[0], out_gapi[1]));
+
+    // Run OpenCV
+    cv::Mat out_ocv[2];
+    {
+        cv::Mat    ocv_tmp0;
+        cv::Mat    ocv_tmp1;
+        cv::Mat    ocv_tmp2;
+        cv::Mat    ocv_tmp3;
+        cv::Scalar ocv_scl;
+
+        ocv_tmp0 = in_mat1; // skip !(!)
+        cv::boxFilter(in_mat2, ocv_tmp1, -1, cv::Size(3,3));
+        ocv_tmp2 = ocv_tmp0 + ocv_tmp1;
+        ocv_scl  = cv::sum(ocv_tmp1);
+        cv::medianBlur(ocv_tmp1, ocv_tmp3, 3);
+        out_ocv[0] = ocv_tmp2 + ocv_scl;
+        cv::boxFilter(ocv_tmp3, out_ocv[1], -1, cv::Size(3,3));
+    }
+
+    EXPECT_EQ(0, cv::countNonZero(out_gapi[0] != out_ocv[0]));
+    EXPECT_EQ(0, cv::countNonZero(out_gapi[1] != out_ocv[1]));
+
+    // FIXME: check that GIslandModel has more than 1 island (e.g. fusion
+    // with breakdown worked)
+}
+
+// FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
+// between executed islands
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_garg_test.cpp b/modules/gapi/test/internal/gapi_int_garg_test.cpp
new file mode 100644 (file)
index 0000000..67696db
--- /dev/null
@@ -0,0 +1,100 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test {
+// Tests on T/Kind matching ////////////////////////////////////////////////////
+// {{
+
+template<class T, cv::detail::ArgKind Exp>
+struct Expected
+{
+    using type = T;
+    static const constexpr cv::detail::ArgKind kind = Exp;
+};
+
+template<typename T>
+struct GArgKind: public ::testing::Test
+{
+    using Type = typename T::type;
+    const cv::detail::ArgKind Kind = T::kind;
+};
+
+// The reason here is to _manually_ list types and their kinds
+// (and NOT reuse cv::detail::ArgKind::Traits<>, since it is a subject of testing)
+using GArg_Test_Types = ::testing::Types
+   <
+  // G-API types
+     Expected<cv::GMat,                 cv::detail::ArgKind::GMAT>
+   , Expected<cv::GScalar,              cv::detail::ArgKind::GSCALAR>
+   , Expected<cv::GArray<int>,          cv::detail::ArgKind::GARRAY>
+   , Expected<cv::GArray<float>,        cv::detail::ArgKind::GARRAY>
+   , Expected<cv::GArray<cv::Point>,    cv::detail::ArgKind::GARRAY>
+   , Expected<cv::GArray<cv::Rect>,     cv::detail::ArgKind::GARRAY>
+
+ // Built-in types
+   , Expected<int,                      cv::detail::ArgKind::OPAQUE>
+   , Expected<float,                    cv::detail::ArgKind::OPAQUE>
+   , Expected<int*,                     cv::detail::ArgKind::OPAQUE>
+   , Expected<cv::Point,                cv::detail::ArgKind::OPAQUE>
+   , Expected<std::string,              cv::detail::ArgKind::OPAQUE>
+   , Expected<cv::Mat,                  cv::detail::ArgKind::OPAQUE>
+   , Expected<std::vector<int>,         cv::detail::ArgKind::OPAQUE>
+   , Expected<std::vector<cv::Point>,   cv::detail::ArgKind::OPAQUE>
+   >;
+
+TYPED_TEST_CASE(GArgKind, GArg_Test_Types);
+
+TYPED_TEST(GArgKind, LocalVar)
+{
+    typename TestFixture::Type val{};
+    cv::GArg arg(val);
+    EXPECT_EQ(TestFixture::Kind, arg.kind);
+}
+
+TYPED_TEST(GArgKind, ConstLocalVar)
+{
+    const typename TestFixture::Type val{};
+    cv::GArg arg(val);
+    EXPECT_EQ(TestFixture::Kind, arg.kind);
+}
+
+TYPED_TEST(GArgKind, RValue)
+{
+    cv::GArg arg = cv::GArg(typename TestFixture::Type());
+    EXPECT_EQ(TestFixture::Kind, arg.kind);
+}
+
+// }}
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(GArg, HasWrap)
+{
+    static_assert(!cv::detail::has_custom_wrap<cv::GMat>::value,
+                  "GMat has no custom marshalling logic");
+    static_assert(!cv::detail::has_custom_wrap<cv::GScalar>::value,
+                  "GScalar has no custom marshalling logic");
+
+    static_assert(cv::detail::has_custom_wrap<cv::GArray<int> >::value,
+                  "GArray<int> has custom marshalling logic");
+    static_assert(cv::detail::has_custom_wrap<cv::GArray<std::string> >::value,
+                  "GArray<int> has custom marshalling logic");
+}
+
+TEST(GArg, GArrayU)
+{
+    // Placing a GArray<T> into GArg automatically strips it to GArrayU
+    cv::GArg arg1 = cv::GArg(cv::GArray<int>());
+    EXPECT_NO_THROW(arg1.get<cv::detail::GArrayU>());
+
+    cv::GArg arg2 = cv::GArg(cv::GArray<cv::Point>());
+    EXPECT_NO_THROW(arg2.get<cv::detail::GArrayU>());
+}
+
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp b/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp
new file mode 100644 (file)
index 0000000..6dbf777
--- /dev/null
@@ -0,0 +1,136 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "api/gcomputation_priv.hpp"
+
+namespace opencv_test
+{
+
+TEST(GMetaArg, Traits_Is_Positive)
+{
+    using namespace cv::detail;
+
+    static_assert(is_meta_descr<cv::GScalarDesc>::value,
+                  "GScalarDesc is a meta description type");
+
+    static_assert(is_meta_descr<cv::GMatDesc>::value,
+                  "GMatDesc is a meta description type");
+}
+
+TEST(GMetaArg, Traits_Is_Negative)
+{
+    using namespace cv::detail;
+
+    static_assert(!is_meta_descr<cv::GCompileArgs>::value,
+                  "GCompileArgs is NOT a meta description type");
+
+    static_assert(!is_meta_descr<int>::value,
+                  "int is NOT a meta description type");
+
+    static_assert(!is_meta_descr<std::string>::value,
+                  "str::string is NOT a meta description type");
+}
+
+TEST(GMetaArg, Traits_Are_EntireList_Positive)
+{
+    using namespace cv::detail;
+
+    static_assert(are_meta_descrs<cv::GScalarDesc>::value,
+                  "GScalarDesc is a meta description type");
+
+    static_assert(are_meta_descrs<cv::GMatDesc>::value,
+                  "GMatDesc is a meta description type");
+
+    static_assert(are_meta_descrs<cv::GMatDesc, cv::GScalarDesc>::value,
+                  "Both GMatDesc and GScalarDesc are meta types");
+}
+
+TEST(GMetaArg, Traits_Are_EntireList_Negative)
+{
+    using namespace cv::detail;
+
+    static_assert(!are_meta_descrs<cv::GCompileArgs>::value,
+                  "GCompileArgs is NOT among meta types");
+
+    static_assert(!are_meta_descrs<int, std::string>::value,
+                  "Both int and std::string is NOT among meta types");
+
+    static_assert(!are_meta_descrs<cv::GMatDesc, cv::GScalarDesc, int>::value,
+                  "List of type is not valid for meta as there\'s int");
+
+    static_assert(!are_meta_descrs<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs>::value,
+                  "List of type is not valid for meta as there\'s GCompileArgs");
+}
+
+TEST(GMetaArg, Traits_Are_ButLast_Positive)
+{
+    using namespace cv::detail;
+
+    static_assert(are_meta_descrs_but_last<cv::GScalarDesc, int>::value,
+                  "List is valid (int is ommitted)");
+
+    static_assert(are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs>::value,
+                  "List is valid (GCompileArgs are omitted)");
+}
+
+TEST(GMetaArg, Traits_Are_ButLast_Negative)
+{
+    using namespace cv::detail;
+
+    static_assert(!are_meta_descrs_but_last<int, std::string>::value,
+                  "Both int is NOT among meta types (std::string is omitted)");
+
+    static_assert(!are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, int, int>::value,
+                  "List of type is not valid for meta as there\'s two ints");
+
+    static_assert(!are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs, float>::value,
+                  "List of type is not valid for meta as there\'s GCompileArgs");
+}
+
+TEST(GMetaArg, Can_Get_Metas_From_Input_Run_Args)
+{
+    cv::Mat m(3, 3, CV_8UC3);
+    cv::Scalar s;
+    std::vector<int> v;
+
+    GMatDesc m_desc;
+    GMetaArgs meta_args = descr_of(cv::gin(m, s, v));
+
+    EXPECT_EQ(meta_args.size(), 3u);
+    EXPECT_NO_THROW(m_desc = util::get<cv::GMatDesc>(meta_args[0]));
+    EXPECT_NO_THROW(util::get<cv::GScalarDesc>(meta_args[1]));
+    EXPECT_NO_THROW(util::get<cv::GArrayDesc>(meta_args[2]));
+
+    EXPECT_EQ(CV_8U, m_desc.depth);
+    EXPECT_EQ(3, m_desc.chan);
+    EXPECT_EQ(cv::gapi::own::Size(3, 3), m_desc.size);
+}
+
+TEST(GMetaArg, Can_Get_Metas_From_Output_Run_Args)
+{
+    cv::Mat m(3, 3, CV_8UC3);
+    cv::Scalar s;
+    std::vector<int> v;
+
+    GMatDesc m_desc;
+    GRunArgsP out_run_args = cv::gout(m, s, v);
+    GMetaArg m_meta = descr_of(out_run_args[0]);
+    GMetaArg s_meta = descr_of(out_run_args[1]);
+    GMetaArg v_meta = descr_of(out_run_args[2]);
+
+    EXPECT_NO_THROW(m_desc = util::get<cv::GMatDesc>(m_meta));
+    EXPECT_NO_THROW(util::get<cv::GScalarDesc>(s_meta));
+    EXPECT_NO_THROW(util::get<cv::GArrayDesc>(v_meta));
+
+    EXPECT_EQ(CV_8U, m_desc.depth);
+    EXPECT_EQ(3, m_desc.chan);
+    EXPECT_EQ(cv::Size(3, 3), m_desc.size);
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp b/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp
new file mode 100644 (file)
index 0000000..a815e0d
--- /dev/null
@@ -0,0 +1,364 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include <ade/util/zip_range.hpp>   // util::indexed
+
+#include "opencv2/gapi/gkernel.hpp"
+#include "compiler/gmodelbuilder.hpp"
+#include "compiler/gmodel.hpp" // RcDesc, GModel::init
+
+namespace opencv_test
+{
+
+namespace test
+{
+
+namespace
+{
+    cv::GMat unaryOp(cv::GMat m)
+    {
+        return cv::GCall(cv::GKernel{"gapi.test.unaryop", nullptr, { GShape::GMAT } }).pass(m).yield(0);
+    }
+
+    cv::GMat binaryOp(cv::GMat m1, cv::GMat m2)
+    {
+        return cv::GCall(cv::GKernel{"gapi.test.binaryOp", nullptr, { GShape::GMAT } }).pass(m1, m2).yield(0);
+    }
+
+    std::vector<ade::NodeHandle> collectOperations(const cv::gimpl::GModel::Graph& gr)
+    {
+        std::vector<ade::NodeHandle> ops;
+        for (const auto& nh : gr.nodes())
+        {
+            if (gr.metadata(nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP)
+                ops.push_back(nh);
+        }
+        return ops;
+    }
+
+    ade::NodeHandle inputOf(cv::gimpl::GModel::Graph& gm, ade::NodeHandle nh, std::size_t port)
+    {
+        for (const auto& eh : nh->inEdges())
+        {
+            if (gm.metadata(eh).get<cv::gimpl::Input>().port == port)
+            {
+                return eh->srcNode();
+            }
+        }
+        util::throw_error(std::logic_error("port " + std::to_string(port) + " not found"));
+    }
+}
+}// namespace opencv_test::test
+
+TEST(GModelBuilder, Unroll_TestUnary)
+{
+    cv::GMat in;
+    cv::GMat out = test::unaryOp(in);
+
+    auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args);
+
+    EXPECT_EQ(1u, unrolled.all_ops.size());  // There is one operation
+    EXPECT_EQ(2u, unrolled.all_data.size()); // And two data objects (in, out)
+
+    // TODO check what the operation is, and so on, and so on
+}
+
+TEST(GModelBuilder, Unroll_TestUnaryOfUnary)
+{
+    cv::GMat in;
+    cv::GMat out = test::unaryOp(test::unaryOp(in));
+
+    auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args);
+
+    EXPECT_EQ(2u, unrolled.all_ops.size());  // There're two operations
+    EXPECT_EQ(3u, unrolled.all_data.size()); // And three data objects (in, out)
+
+    // TODO check what the operation is, and so on, and so on
+}
+
+TEST(GModelBuilder, Unroll_Not_All_Protocol_Inputs_Are_Reached)
+{
+    cv::GMat in1, in2;                                      // in1 -> unaryOp() -> u_op1 -> unaryOp() -> out
+    auto u_op1 = test::unaryOp(in1);                        // in2 -> unaryOp() -> u_op2
+    auto u_op2 = test::unaryOp(in2);
+    auto out   = test::unaryOp(u_op1);
+
+    EXPECT_THROW(cv::gimpl::unrollExpr(cv::GIn(in1, in2).m_args, cv::GOut(out).m_args), std::logic_error);
+}
+
+TEST(GModelBuilder, Unroll_Parallel_Path)
+{
+    cv::GMat in1, in2;                                      // in1 -> unaryOp() -> out1
+    auto out1 = test::unaryOp(in1);                         // in2 -> unaryOp() -> out2
+    auto out2 = test::unaryOp(in2);
+
+    auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in1, in2).m_args, cv::GOut(out1, out2).m_args);
+
+    EXPECT_EQ(unrolled.all_ops.size(),  2u);
+    EXPECT_EQ(unrolled.all_data.size(), 4u);
+}
+
+TEST(GModelBuilder, Unroll_WithBranch)
+{
+    // in -> unaryOp() -> tmp -->unaryOp() -> out1
+    //                     `---->unaryOp() -> out2
+
+    GMat in;
+    auto tmp = test::unaryOp(in);
+    auto out1 = test::unaryOp(tmp);
+    auto out2 = test::unaryOp(tmp);
+
+    auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out1, out2).m_args);
+
+    EXPECT_EQ(unrolled.all_ops.size(),  3u);
+    EXPECT_EQ(unrolled.all_data.size(), 4u);
+}
+
+TEST(GModelBuilder, Build_Unary)
+{
+    cv::GMat in;
+    cv::GMat out = test::unaryOp(in);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out).m_args);
+
+    EXPECT_EQ(3u, static_cast<std::size_t>(g.nodes().size()));    // Generated graph should have three nodes
+
+    // TODO: Check what the nodes are
+}
+
+TEST(GModelBuilder, Constant_GScalar)
+{
+    // in -> addC()-----(GMat)---->mulC()-----(GMat)---->unaryOp()----out
+    //         ^                     ^
+    //         |                     |
+    // 3-------`           c_s-------'
+
+    cv::GMat in;
+    cv::GScalar c_s = 5;
+    auto out = test::unaryOp((in + 3) * c_s);    // 3 converted to GScalar
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out).m_args);
+    cv::gimpl::Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+
+    auto in_nh   = p.in_nhs.front();
+    auto addC_nh = in_nh->outNodes().front();
+    auto mulC_nh = addC_nh->outNodes().front()->outNodes().front();
+
+    ASSERT_TRUE(gm.metadata(addC_nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP);
+    ASSERT_TRUE(gm.metadata(mulC_nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP);
+
+    auto s_3 = test::inputOf(gm, addC_nh, 1);
+    auto s_5 = test::inputOf(gm, mulC_nh, 1);
+
+    EXPECT_EQ(9u, static_cast<std::size_t>(g.nodes().size()));          // 6 data nodes (1 -input, 1 output, 2 constant, 2 temp) and 3 op nodes
+    EXPECT_EQ(2u, static_cast<std::size_t>(addC_nh->inNodes().size())); // in and 3
+    EXPECT_EQ(2u, static_cast<std::size_t>(mulC_nh->inNodes().size())); // addC output and c_s
+    EXPECT_EQ(3, (util::get<cv::gapi::own::Scalar>(gm.metadata(s_3).get<cv::gimpl::ConstValue>().arg))[0]);
+    EXPECT_EQ(5, (util::get<cv::gapi::own::Scalar>(gm.metadata(s_5).get<cv::gimpl::ConstValue>().arg))[0]);
+}
+
+TEST(GModelBuilder, Check_Multiple_Outputs)
+{
+    //            ------------------------------> r
+    //            '
+    //            '                    -----------> i_out1
+    //            '                    '
+    // in ----> split3() ---> g ---> integral()
+    //            '                    '
+    //            '                    -----------> i_out2
+    //            '
+    //            '---------> b ---> unaryOp() ---> u_out
+
+    cv::GMat in, r, g, b, i_out1, i_out2, u_out;
+    std::tie(r, g, b) = cv::gapi::split3(in);
+    std::tie(i_out1, i_out2) = cv::gapi::integral(g, 1, 1);
+    u_out = test::unaryOp(b);
+
+    ade::Graph gr;
+    cv::gimpl::GModel::Graph gm(gr);
+    cv::gimpl::GModel::init(gm);
+    auto proto_slots = cv::gimpl::GModelBuilder(gr).put(cv::GIn(in).m_args, cv::GOut(r, i_out1, i_out2, u_out).m_args);
+    cv::gimpl::Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+
+    EXPECT_EQ(4u, static_cast<std::size_t>(p.out_nhs.size()));
+    EXPECT_EQ(0u, gm.metadata(p.out_nhs[0]->inEdges().front()).get<cv::gimpl::Output>().port);
+    EXPECT_EQ(0u, gm.metadata(p.out_nhs[1]->inEdges().front()).get<cv::gimpl::Output>().port);
+    EXPECT_EQ(1u, gm.metadata(p.out_nhs[2]->inEdges().front()).get<cv::gimpl::Output>().port);
+    EXPECT_EQ(0u, gm.metadata(p.out_nhs[3]->inEdges().front()).get<cv::gimpl::Output>().port);
+    for (const auto& it : ade::util::indexed(p.out_nhs))
+    {
+        const auto& out_nh = ade::util::value(it);
+
+        EXPECT_EQ(cv::gimpl::NodeType::DATA, gm.metadata(out_nh).get<cv::gimpl::NodeType>().t);
+        EXPECT_EQ(GShape::GMAT, gm.metadata(out_nh).get<cv::gimpl::Data>().shape);
+    }
+}
+
+TEST(GModelBuilder, Unused_Outputs)
+{
+    cv::GMat in;
+    auto yuv_p = cv::gapi::split3(in);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(std::get<0>(yuv_p)).m_args);
+
+    EXPECT_EQ(5u, static_cast<std::size_t>(g.nodes().size()));    // 1 input, 1 operation, 3 outputs
+}
+
+TEST(GModelBuilder, Work_With_One_Channel_From_Split3)
+{
+    cv::GMat in, y, u, v;
+    std::tie(y, u, v) = cv::gapi::split3(in);
+    auto y_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(y_blur).m_args);
+
+    EXPECT_EQ(7u, static_cast<std::size_t>(g.nodes().size())); // 1 input, 2 operation, 3 nodes from split3, 1 output
+}
+
+TEST(GModelBuilder, Add_Nodes_To_Unused_Nodes)
+{
+    cv::GMat in, y, u, v;
+    std::tie(y, u, v) = cv::gapi::split3(in);
+    auto y_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
+    // unused nodes
+    auto u_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
+    auto v_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(y_blur).m_args);
+
+    EXPECT_EQ(7u, static_cast<std::size_t>(g.nodes().size())); // 1 input, 2 operation, 3 nodes from split3, 1 output
+}
+
+TEST(GModelBuilder, Unlisted_Inputs)
+{
+    // in1 -> binaryOp() -> out
+    //         ^
+    //         |
+    // in2 ----'
+
+    cv::GMat in1, in2;
+    auto out = test::binaryOp(in1, in2);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    // add required 2 inputs but pass 1
+    EXPECT_THROW(cv::gimpl::GModelBuilder(g).put(cv::GIn(in1).m_args, cv::GOut(out).m_args), std::logic_error);
+}
+
+TEST(GModelBuilder, Unroll_No_Link_Between_In_And_Out)
+{
+    // in    -> unaryOp() -> u_op
+    // other -> unaryOp() -> out
+
+    cv::GMat in, other;
+    auto u_op = test::unaryOp(in);
+    auto out  = test::unaryOp(other);
+
+    EXPECT_THROW(cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args), std::logic_error);
+}
+
+
+TEST(GModel_builder, Check_Binary_Op)
+{
+    // in1 -> binaryOp() -> out
+    //          ^
+    //          |
+    // in2 -----'
+
+    cv::GMat in1, in2;
+    auto out = test::binaryOp(in1, in2);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in1, in2).m_args, cv::GOut(out).m_args);
+
+    cv::gimpl::Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+    auto ops = test::collectOperations(g);
+
+    EXPECT_EQ(1u, ops.size());
+    EXPECT_EQ("gapi.test.binaryOp", gm.metadata(ops.front()).get<cv::gimpl::Op>().k.name);
+    EXPECT_EQ(2u, static_cast<std::size_t>(ops.front()->inEdges().size()));
+    EXPECT_EQ(1u, static_cast<std::size_t>(ops.front()->outEdges().size()));
+    EXPECT_EQ(1u, static_cast<std::size_t>(ops.front()->outNodes().size()));
+}
+
+TEST(GModelBuilder, Add_Operation_With_Two_Out_One_Time)
+{
+    // in -> integral() --> out_b1 -> unaryOp() -> out1
+    //            |
+    //            '-------> out_b2 -> unaryOp() -> out2
+
+    cv::GMat in, out_b1, out_b2;
+    std::tie(out_b1, out_b2) = cv::gapi::integral(in, 1, 1);
+    auto out1 = test::unaryOp(out_b1);
+    auto out2 = test::unaryOp(out_b1);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out1, out2).m_args);
+
+    auto ops = test::collectOperations(gm);
+
+    cv::gimpl::Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+    auto integral_nh = p.in_nhs.front()->outNodes().front();
+
+    EXPECT_EQ(3u, ops.size());
+    EXPECT_EQ("org.opencv.core.matrixop.integral", gm.metadata(integral_nh).get<cv::gimpl::Op>().k.name);
+    EXPECT_EQ(1u, static_cast<std::size_t>(integral_nh->inEdges().size()));
+    EXPECT_EQ(2u, static_cast<std::size_t>(integral_nh->outEdges().size()));
+    EXPECT_EQ(2u, static_cast<std::size_t>(integral_nh->outNodes().size()));
+}
+TEST(GModelBuilder, Add_Operation_With_One_Out_One_Time)
+{
+    // in1 -> binaryOp() -> b_out -> unaryOp() -> out1
+    //            ^           |
+    //            |           |
+    // in2 -------            '----> unaryOp() -> out2
+
+    cv::GMat in1, in2;
+    auto b_out = test::binaryOp(in1, in2);
+    auto out1 = test::unaryOp(b_out);
+    auto out2 = test::unaryOp(b_out);
+
+    ade::Graph g;
+    cv::gimpl::GModel::Graph gm(g);
+    cv::gimpl::GModel::init(gm);
+    auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in1, in2).m_args, cv::GOut(out1, out2).m_args);
+    cv::gimpl::Protocol p;
+    std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
+    cv::gimpl::GModel::Graph gr(g);
+    auto binaryOp_nh = p.in_nhs.front()->outNodes().front();
+
+    EXPECT_EQ(2u, static_cast<std::size_t>(binaryOp_nh->inEdges().size()));
+    EXPECT_EQ(1u, static_cast<std::size_t>(binaryOp_nh->outEdges().size()));
+    EXPECT_EQ(8u, static_cast<std::size_t>(g.nodes().size()));
+}
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp b/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp
new file mode 100644 (file)
index 0000000..91e55be
--- /dev/null
@@ -0,0 +1,527 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "compiler/transactions.hpp"
+
+#include "gapi_mock_kernels.hpp"
+
+#include "compiler/gmodel.hpp"
+#include "compiler/gislandmodel.hpp"
+#include "compiler/gcompiler.hpp"
+
+namespace opencv_test
+{
+
+TEST(IslandFusion, TwoOps_OneIsland)
+{
+    namespace J = Jupiter; // see mock_kernels.cpp
+
+    // Define a computation:
+    //
+    //    (in) -> J::Foo1 -> (tmp0) -> J::Foo2 -> (out)
+    //          :                               :
+    //          :          "island0"            :
+    //          :<----------------------------->:
+
+    cv::GMat in;
+    cv::GMat tmp0 = I::Foo::on(in);
+    cv::GMat out  = I::Foo::on(tmp0);
+    cv::GComputation cc(in, out);
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<J::Foo>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    // Inspect the graph and verify the islands configuration
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+
+    auto in_nh  = cv::gimpl::GModel::dataNodeOf(gm, in);
+    auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
+    auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    // in/out mats shouldn't be assigned to any Island
+    EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
+
+    // Since tmp is surrounded by two J kernels, tmp should be assigned
+    // to island J
+    EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
+}
+
+TEST(IslandFusion, TwoOps_TwoIslands)
+{
+    namespace J = Jupiter; // see mock_kernels.cpp
+    namespace S = Saturn;  // see mock_kernels.cpp
+
+    // Define a computation:
+    //
+    //    (in) -> J::Foo --> (tmp0) -> S::Bar --> (out)
+    //          :          :        ->          :
+    //          :          :         :          :
+    //          :<-------->:         :<-------->:
+
+    cv::GMat in;
+    cv::GMat tmp0 = I::Foo::on(in);
+    cv::GMat out  = I::Bar::on(tmp0, tmp0);
+    cv::GComputation cc(in, out);
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<J::Foo, S::Bar>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    // Inspect the graph and verify the islands configuration
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+
+    auto in_nh  = cv::gimpl::GModel::dataNodeOf(gm, in);
+    auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
+    auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    // in/tmp/out mats shouldn't be assigned to any Island
+    EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
+
+    auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    // There should be two islands in the GIslandModel
+    const auto is_island = [&](ade::NodeHandle nh) {
+        return (cv::gimpl::NodeKind::ISLAND
+                == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
+    };
+    const std::size_t num_isl = std::count_if(gim.nodes().begin(),
+                                              gim.nodes().end(),
+                                              is_island);
+    EXPECT_EQ(2u, num_isl);
+
+    auto isl_foo_nh  = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
+    auto isl_bar_nh  = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
+    ASSERT_NE(nullptr, isl_foo_nh);
+    ASSERT_NE(nullptr, isl_bar_nh);
+
+    // Islands should be different
+    auto isl_foo_obj = gim.metadata(isl_foo_nh).get<cv::gimpl::FusedIsland>().object;
+    auto isl_bar_obj = gim.metadata(isl_bar_nh).get<cv::gimpl::FusedIsland>().object;
+    EXPECT_FALSE(isl_foo_obj == isl_bar_obj);
+}
+
+TEST(IslandFusion, ConsumerHasTwoInputs)
+{
+    namespace J = Jupiter; // see mock_kernels.cpp
+
+    // Define a computation:     island
+    //            ............................
+    //    (in0) ->:J::Foo -> (tmp) -> S::Bar :--> (out)
+    //            :....................^.....:
+    //                                 |
+    //    (in1) -----------------------`
+    //
+
+    // Check that island is build correctly, when consumer has two inputs
+
+    GMat in[2];
+    GMat tmp = I::Foo::on(in[0]);
+    GMat out = I::Bar::on(tmp, in[1]);
+
+    cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
+
+    // Prepare compilation parameters manually
+    cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
+                              GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+
+    auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
+    auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
+    EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
+
+    auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    const auto is_island = [&](ade::NodeHandle nh) {
+        return (cv::gimpl::NodeKind::ISLAND
+                == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
+    };
+    const std::size_t num_isl = std::count_if(gim.nodes().begin(),
+                                              gim.nodes().end(),
+                                              is_island);
+    EXPECT_EQ(1u, num_isl);
+
+    auto isl_nh  = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
+    auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
+
+    EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
+
+    EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->inNodes().size()));
+    EXPECT_EQ(1u, static_cast<std::size_t>(isl_nh->outNodes().size()));
+}
+
+TEST(IslandFusion, DataNodeUsedDifferentBackend)
+{
+    // Define a computation:
+    //
+    //           internal isl            isl0
+    //             ...........................
+    //    (in1) -> :J::Foo--> (tmp) -> J::Foo: --> (out0)
+    //             :............|............:
+    //                          |     ........
+    //                          `---->:S::Baz: --> (out1)
+    //                                :......:
+
+    // Check that the node was not dropped out of the island
+    // because it is used by the kernel from another backend
+
+    namespace J = Jupiter;
+    namespace S = Saturn;
+
+    cv::GMat in, tmp, out0;
+    cv::GScalar out1;
+    tmp  = I::Foo::on(in);
+    out0 = I::Foo::on(tmp);
+    out1 = I::Baz::on(tmp);
+
+    cv::GComputation cc(cv::GIn(in), cv::GOut(out0, out1));
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<J::Foo, S::Baz>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    // Inspect the graph and verify the islands configuration
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+
+    auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in);
+    auto tmp_nh  = cv::gimpl::GModel::dataNodeOf(gm, tmp);
+    auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
+    auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
+
+    EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
+
+    auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    auto isl_nh  = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
+    auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
+
+    EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
+
+    EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->outNodes().size()));
+    EXPECT_EQ(7u, static_cast<std::size_t>(gm.nodes().size()));
+    EXPECT_EQ(6u, static_cast<std::size_t>(gim.nodes().size()));
+}
+
+TEST(IslandFusion, LoopBetweenDifferentBackends)
+{
+    // Define a computation:
+    //
+    //
+    //            .............................
+    //    (in) -> :J::Baz -> (tmp0) -> J::Quux: -> (out0)
+    //      |     :............|..........^....
+    //      |     ........     |          |         ........
+    //      `---->:S::Foo:     `----------|-------->:S::Qux:-> (out1)
+    //            :....|.:                |         :....^.:
+    //                 |                  |              |
+    //                 `-------------- (tmp1) -----------`
+
+    // Kernels S::Foo and S::Qux cannot merge, because there will be a cycle between islands
+
+    namespace J = Jupiter;
+    namespace S = Saturn;
+
+    cv::GScalar tmp0;
+    cv::GMat in, tmp1, out0, out1;
+
+    tmp0 = I::Baz::on(in);
+    tmp1 = I::Foo::on(in);
+    out1 = I::Qux::on(tmp1, tmp0);
+    out0 = I::Quux::on(tmp0, tmp1);
+
+    cv::GComputation cc(cv::GIn(in), cv::GOut(out1, out0));
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<J::Baz, J::Quux, S::Foo, S::Qux>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+    auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in);
+    auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
+    auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
+    auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
+    auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
+
+    EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
+    // The node does not belong to the island so as not to form a cycle
+    EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
+
+    EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
+
+    // There should be three islands in the GIslandModel
+    const auto is_island = [&](ade::NodeHandle nh) {
+        return (cv::gimpl::NodeKind::ISLAND
+                == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
+    };
+    const std::size_t num_isl = std::count_if(gim.nodes().begin(),
+                                              gim.nodes().end(),
+                                              is_island);
+    EXPECT_EQ(3u, num_isl);
+}
+
+TEST(IslandsFusion, PartionOverlapUserIsland)
+{
+    // Define a computation:
+    //
+    //           internal isl            isl0
+    //             ........            ........
+    //    (in0) -> :J::Foo:--> (tmp) ->:S::Bar: --> (out)
+    //             :......:            :......:
+    //                                    ^
+    //                                    |
+    //    (in1) --------------------------`
+
+    // Check that internal islands does't overlap user island
+
+    namespace J = Jupiter;
+    namespace S = Saturn;
+
+    GMat in[2];
+    GMat tmp = I::Foo::on(in[0]);
+    GMat out = I::Bar::on(tmp, in[1]);
+
+    cv::gapi::island("isl0", cv::GIn(tmp, in[1]), cv::GOut(out));
+    cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
+
+    // Prepare compilation parameters manually
+    cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
+                              GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+    auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
+    auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    auto foo_nh  = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
+    auto foo_obj = gim.metadata(foo_nh).get<cv::gimpl::FusedIsland>().object;
+
+    auto bar_nh  = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
+    auto bar_obj = gim.metadata(bar_nh).get<cv::gimpl::FusedIsland>().object;
+
+    EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(foo_obj->is_user_specified());
+    EXPECT_TRUE(bar_obj->is_user_specified());
+}
+
+TEST(IslandsFusion, DISABLED_IslandContainsDifferentBackends)
+{
+    // Define a computation:
+    //
+    //                       isl0
+    //             ............................
+    //    (in0) -> :J::Foo:--> (tmp) -> S::Bar: --> (out)
+    //             :..........................:
+    //                                    ^
+    //                                    |
+    //    (in1) --------------------------`
+
+    // Try create island contains different backends
+
+    namespace J = Jupiter;
+    namespace S = Saturn;
+
+    GMat in[2];
+    GMat tmp = I::Foo::on(in[0]);
+    GMat out = I::Bar::on(tmp, in[1]);
+
+    cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(out));
+    cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
+
+    // Prepare compilation parameters manually
+    cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
+                              GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
+    const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    EXPECT_ANY_THROW(compiler.runPasses(*graph));
+}
+
+TEST(IslandFusion, WithLoop)
+{
+    namespace J = Jupiter; // see mock_kernels.cpp
+
+    // Define a computation:
+    //
+    //    (in) -> J::Foo --> (tmp0) -> J::Foo --> (tmp1) -> J::Qux -> (out)
+    //                            :                        ^
+    //                            '--> J::Baz --> (scl0) --'
+    //
+    // The whole thing should be merged to a single island
+    // There's a cycle warning if Foo/Foo/Qux are merged first
+    // Then this island both produces data for Baz and consumes data
+    // from Baz. This is a cycle and it should be avoided by the merging code.
+    //
+    cv::GMat    in;
+    cv::GMat    tmp0 = I::Foo::on(in);
+    cv::GMat    tmp1 = I::Foo::on(tmp0);
+    cv::GScalar scl0 = I::Baz::on(tmp0);
+    cv::GMat    out  = I::Qux::on(tmp1, scl0);
+    cv::GComputation cc(in, out);
+
+    // Prepare compilation parameters manually
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
+    const auto pkg     = cv::gapi::kernels<J::Foo, J::Baz, J::Qux>();
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    // Inspect the graph and verify the islands configuration
+    cv::gimpl::GModel::ConstGraph gm(*graph);
+
+    auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in);
+    auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
+    auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
+    auto scl0_nh = cv::gimpl::GModel::dataNodeOf(gm, scl0);
+    auto out_nh  = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    // in/out mats shouldn't be assigned to any Island
+    EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
+
+    // tmp0/tmp1/scl should be assigned to island
+    EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
+    EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
+    EXPECT_TRUE(gm.metadata(scl0_nh).contains<cv::gimpl::Island>());
+
+    // Check that there's a single island object and it contains all
+    // that data object handles
+
+    cv::gimpl::GModel::ConstGraph cg(*graph);
+    auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    const auto is_island = [&](ade::NodeHandle nh) {
+        return (cv::gimpl::NodeKind::ISLAND
+                == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
+    };
+    const std::size_t num_isl = std::count_if(gim.nodes().begin(),
+                                              gim.nodes().end(),
+                                              is_island);
+    EXPECT_EQ(1u, num_isl);
+
+    auto isl_nh  = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
+    auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
+    EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp0_nh));
+    EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp1_nh));
+    EXPECT_TRUE(ade::util::contains(isl_obj->contents(), scl0_nh));
+}
+
+TEST(IslandFusion, Regression_ShouldFuseAll)
+{
+    // Initially the merge procedure didn't work as expected and
+    // stopped fusion even if it could be continued (e.g. full
+    // GModel graph could be fused into a single GIsland node).
+    // Example of this is custom RGB 2 YUV pipeline as shown below:
+
+    cv::GMat r, g, b;
+    cv::GMat y = 0.299f*r + 0.587f*g + 0.114f*b;
+    cv::GMat u = 0.492f*(b - y);
+    cv::GMat v = 0.877f*(r - y);
+
+    cv::GComputation customCvt({r, g, b}, {y, u, v});
+
+    const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
+
+    // Directly instantiate G-API graph compiler and run partial compilation
+    cv::gimpl::GCompiler compiler(customCvt, {in_meta,in_meta,in_meta}, cv::compile_args());
+    cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
+    compiler.runPasses(*graph);
+
+    cv::gimpl::GModel::ConstGraph cg(*graph);
+    auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
+    cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
+
+    std::vector<ade::NodeHandle> data_nhs;
+    std::vector<ade::NodeHandle> isl_nhs;
+    for (auto &&nh : gim.nodes())
+    {
+        if (gim.metadata(nh).contains<cv::gimpl::FusedIsland>())
+            isl_nhs.push_back(std::move(nh));
+        else if (gim.metadata(nh).contains<cv::gimpl::DataSlot>())
+            data_nhs.push_back(std::move(nh));
+        else FAIL() << "GIslandModel node with unexpected metadata type";
+    }
+
+    EXPECT_EQ(6u, data_nhs.size()); // 3 input nodes + 3 output nodes
+    EXPECT_EQ(1u, isl_nhs.size());  // 1 island
+}
+
+// FIXME: add more tests on mixed (hetero) graphs
+// ADE-222, ADE-223
+
+// FIXME: add test on combination of user-specified island
+// which should be heterogeneous (based on kernel availability)
+// but as we don't support this, compilation should fail
+
+// FIXME: add tests on automatic inferred islands which are
+// connected via 1) gmat 2) gscalar 3) garray,
+// check the case with executor
+// check the case when this 1/2/3 interim object is also gcomputation output
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_island_tests.cpp b/modules/gapi/test/internal/gapi_int_island_tests.cpp
new file mode 100644 (file)
index 0000000..09f1880
--- /dev/null
@@ -0,0 +1,653 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "compiler/gmodel.hpp"
+#include "compiler/gcompiled_priv.hpp"
+
+namespace opencv_test
+{
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests on a plain graph
+//
+// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
+//
+namespace
+{
+    struct PlainIslandsFixture
+    {
+        cv::GMat in;
+        cv::GMat tmp[3];
+        cv::GMat out;
+
+        PlainIslandsFixture()
+        {
+            tmp[0] = cv::gapi::boxFilter(in,     -1, cv::Size(3,3));
+            tmp[1] = cv::gapi::boxFilter(tmp[0], -1, cv::Size(3,3));
+            tmp[2] = cv::gapi::boxFilter(tmp[1], -1, cv::Size(3,3));
+            out    = cv::gapi::boxFilter(tmp[2], -1, cv::Size(3,3));
+        }
+    };
+
+    struct Islands: public ::testing::Test, public PlainIslandsFixture {};
+
+    using GIntArray = GArray<int>;
+
+    G_TYPED_KERNEL(CreateMatWithDiag, <GMat(GIntArray)>, "test.array.create_mat_with_diag")
+    {
+        static GMatDesc outMeta(const GArrayDesc&) { return cv::GMatDesc{CV_32S, 1,{3, 3}}; }
+    };
+
+    GAPI_OCV_KERNEL(CreateMatWithDiagImpl, CreateMatWithDiag)
+    {
+        static void run(const std::vector<int> &in, cv::Mat& out)
+        {
+            auto size = static_cast<int>(in.size());
+            out = Mat::zeros(size, size, CV_32SC1);
+            for(int i = 0; i < out.rows; i++)
+            {
+                auto* row = out.ptr<int>(i);
+                row[i] = in[i];
+            }
+        }
+    };
+
+    G_TYPED_KERNEL(Mat2Array, <GIntArray(GMat)>, "test.array.mat2array")
+    {
+        static GArrayDesc outMeta(const GMatDesc&) { return empty_array_desc(); }
+    };
+
+    GAPI_OCV_KERNEL(Mat2ArrayImpl, Mat2Array)
+    {
+        static void run(const cv::Mat& in, std::vector<int> &out)
+        {
+            GAPI_Assert(in.depth() == CV_32S && in.isContinuous());
+            out.reserve(in.cols * in.rows);
+            out.assign((int*)in.datastart, (int*)in.dataend);
+        }
+    };
+}
+
+TEST_F(Islands, SmokeTest)
+{
+    // (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
+    //                         :        "test"             :
+    //                         :<------------------------->:
+    cv::gapi::island("test", cv::GIn(tmp[0]), cv::GOut(tmp[2]));
+    auto cc = cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}});
+
+    const auto &gm = cc.priv().model();
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+
+    // tmp1 and tmp3 is not a part of any island
+    EXPECT_FALSE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
+
+    // tmp2 is part of "test" island
+    EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
+    EXPECT_EQ("test", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+}
+
+TEST_F(Islands, TwoIslands)
+{
+    // (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
+    //       :  "test1"                     :  : "test2"                          :
+    //       :<---------------------------->:  :<--------------------------------->
+    EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(in),     cv::GOut(tmp[1])));
+    EXPECT_NO_THROW(cv::gapi::island("test2", cv::GIn(tmp[1]), cv::GOut(out)));
+
+    auto cc = cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}});
+    const auto &gm = cc.priv().model();
+    const auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in);
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto out_nh  = cv::gimpl::GModel::dataNodeOf(gm, out);
+
+    // Only tmp0 and tmp2 should be listed in islands.
+    EXPECT_TRUE (gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
+    EXPECT_TRUE (gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in_nh)  .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
+
+    EXPECT_EQ("test1", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("test2", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island);
+}
+
+// FIXME: Disabled since currently merge procedure merges two into one
+// succesfully
+TEST_F(Islands, DISABLED_Two_Islands_With_Same_Name_Should_Fail)
+{
+    // (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
+    //       :  "test1"                     :  : "test1"                          :
+    //       :<---------------------------->:  :<--------------------------------->
+
+    EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(in),     cv::GOut(tmp[1])));
+    EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(tmp[1]), cv::GOut(out)));
+
+    EXPECT_ANY_THROW(cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}}));
+}
+
+
+// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
+//       :          "test1":            :              :
+//       :<----------------:----------->:              :
+//                         :                           :
+//                         :        "test2"            :
+//                         :<------------------------->:
+TEST_F(Islands, OverlappingIslands1)
+{
+    EXPECT_NO_THROW (cv::gapi::island("test1", cv::GIn(in),     cv::GOut(tmp[1])));
+    EXPECT_ANY_THROW(cv::gapi::island("test2", cv::GIn(tmp[0]), cv::GOut(tmp[2])));
+}
+
+TEST_F(Islands, OverlappingIslands2)
+{
+    EXPECT_NO_THROW (cv::gapi::island("test2", cv::GIn(tmp[0]), cv::GOut(tmp[2])));
+    EXPECT_ANY_THROW(cv::gapi::island("test1", cv::GIn(in),     cv::GOut(tmp[1])));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests on a complex graph
+//
+// (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+//                             ^                         ^
+// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+//                   :
+//                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+//
+namespace
+{
+    struct ComplexIslandsFixture
+    {
+        cv::GMat    in[2];
+        cv::GMat    tmp[4];
+        cv::GScalar scl;
+        cv::GMat    out[2];
+
+        ComplexIslandsFixture()
+        {
+            tmp[0] = cv::gapi::bitwise_not(in[0]);
+            tmp[1] = cv::gapi::boxFilter(in[1], -1, cv::Size(3,3));
+            tmp[2] = tmp[0] + tmp[1]; // FIXME: handle tmp[2] = tmp[0]+tmp[2] typo
+            scl    = cv::gapi::sum(tmp[1]);
+            tmp[3] = cv::gapi::medianBlur(tmp[1], 3);
+            out[0] = tmp[2] + scl;
+            out[1] = cv::gapi::boxFilter(tmp[3], -1, cv::Size(3,3));
+        }
+    };
+
+    struct ComplexIslands: public ::testing::Test, public ComplexIslandsFixture {};
+} // namespace
+
+TEST_F(ComplexIslands, SmokeTest)
+{
+    //       isl0                                          #internal1
+    //       ...........................                   ........
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //       :............ ........^...:                   :.^....:
+    //                   ...       :                         :
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //                   :                                     isl1
+    //                   :           ..............................
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //                               :............................:
+
+    cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]),  cv::GOut(tmp[2]));
+    cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                 cv::GMatDesc{CV_8U,1,{640,480}});
+    const auto &gm = cc.priv().model();
+    const auto in0_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    const auto in1_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
+    const auto scl_nh  = cv::gimpl::GModel::dataNodeOf(gm, scl);
+    const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
+    const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
+
+    // tmp0, tmp3 are in islands, others are not
+    EXPECT_TRUE(gm.metadata(tmp0_nh) .contains<cv::gimpl::Island>()); // isl0
+    EXPECT_TRUE(gm.metadata(tmp3_nh) .contains<cv::gimpl::Island>()); // isl1
+    EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>()); // (input is never fused)
+    EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>()); // (input is never fused)
+    EXPECT_TRUE (gm.metadata(tmp1_nh).contains<cv::gimpl::Island>()); // <internal island>
+    EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>()); // #not fused as cycle-causing#
+    EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>()); // #not fused as cycle-causing#
+    EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>()); // (output is never fused)
+    EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>()); // (output is never fused)
+
+    EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
+
+    EXPECT_NE("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+    EXPECT_NE("isl1", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+
+    // FIXME: Add a test with same graph for Fusion and check GIslandModel
+}
+
+TEST_F(ComplexIslands, DistinictIslandsWithSameName)
+{
+    //       isl0
+    //       ...........................
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //       :............ ........^...:                     ^
+    //                   ...       :                         :
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //                   :                                     isl0
+    //                   :           ..............................
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //                               :............................:
+
+    cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]),  cv::GOut(tmp[2]));
+    cv::gapi::island("isl0", cv::GIn(tmp[1]), cv::GOut(out[1]));
+
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]));
+
+    EXPECT_ANY_THROW(cc.compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                                cv::GMatDesc{CV_8U,1,{640,480}}));
+}
+
+TEST_F(ComplexIslands, FullGraph)
+{
+    cv::gapi::island("isl0",   cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]));
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                 cv::GMatDesc{CV_8U,1,{640,480}});
+    const auto &gm = cc.priv().model();
+    std::vector<ade::NodeHandle> handles_inside = {
+        cv::gimpl::GModel::dataNodeOf(gm, tmp[0]),
+        cv::gimpl::GModel::dataNodeOf(gm, tmp[1]),
+        cv::gimpl::GModel::dataNodeOf(gm, tmp[2]),
+        cv::gimpl::GModel::dataNodeOf(gm, tmp[3]),
+        cv::gimpl::GModel::dataNodeOf(gm, scl),
+    };
+    std::vector<ade::NodeHandle> handles_outside = {
+        cv::gimpl::GModel::dataNodeOf(gm, in[0]),
+        cv::gimpl::GModel::dataNodeOf(gm, in[1]),
+        cv::gimpl::GModel::dataNodeOf(gm, out[0]),
+        cv::gimpl::GModel::dataNodeOf(gm, out[1]),
+    };
+
+    for (auto nh_inside : handles_inside)
+    {
+        EXPECT_EQ("isl0", gm.metadata(nh_inside).get<cv::gimpl::Island>().island);
+    }
+    for (auto nh_outside : handles_outside)
+    {
+        EXPECT_FALSE(gm.metadata(nh_outside).contains<cv::gimpl::Island>());
+    }
+}
+
+TEST_F(ComplexIslands, ViaScalar)
+{
+    //
+    //        .........................................#internal0.
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //        :....................^.........................^...:
+    //                             :                         :
+    //        .....................:.........(isl0).         :
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //        :..........:.........................:
+    //                   :
+    //                   :            ..................#internal1.
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //                                :...........................:
+
+    cv::gapi::island("isl0",   cv::GIn(in[1]), cv::GOut(scl));
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                 cv::GMatDesc{CV_8U,1,{640,480}});
+    const auto &gm = cc.priv().model();
+
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
+
+    EXPECT_NE("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island); // <internal>
+    EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island); // isl0
+    EXPECT_NE("isl0", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island); // <internal>
+    EXPECT_NE("isl0", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island); // <internal>
+
+    std::vector<ade::NodeHandle> handles_outside = {
+        cv::gimpl::GModel::dataNodeOf(gm, in[0]),
+        cv::gimpl::GModel::dataNodeOf(gm, in[1]),
+        cv::gimpl::GModel::dataNodeOf(gm, scl),
+        cv::gimpl::GModel::dataNodeOf(gm, out[0]),
+        cv::gimpl::GModel::dataNodeOf(gm, out[1]),
+    };
+    for (auto nh_outside : handles_outside)
+    {
+        EXPECT_FALSE(gm.metadata(nh_outside).contains<cv::gimpl::Island>());
+    }
+}
+
+TEST_F(ComplexIslands, BorderDataIsland)
+{
+    //       .................................(isl0)..
+    //       :                                       :
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //       :                     ^                 :       ^
+    //       :                     :                 :       :
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //       :...........:...........................:
+    //                :  :  :
+    //                :  :  :.........................................(isl1)..
+    //                :  `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //                :                                                      :
+    //                :......................................................:
+
+    cv::gapi::island("isl0", cv::GIn(in[0],  in[1]), cv::GOut(tmp[2], scl));
+    cv::gapi::island("isl1", cv::GIn(tmp[1]),        cv::GOut(out[1]));
+
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                 cv::GMatDesc{CV_8U,1,{640,480}});
+    const auto &gm = cc.priv().model();
+    const auto in0_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    const auto in1_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
+    const auto scl_nh  = cv::gimpl::GModel::dataNodeOf(gm, scl);
+    const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
+    const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
+
+    // Check handles inside isl0
+    EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+    // ^^^ Important - tmp1 is assigned to isl0, not isl1
+
+    // Check handles inside isl1
+    EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
+
+    // Check outside handles
+    EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
+}
+
+
+TEST_F(ComplexIslands, IncompleteSpec)
+{
+    //       isl0
+    //       ...........................
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //       :...........xxx.......^...:                     ^
+    //                             :                         :
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //                   :
+    //                   :
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //
+
+    // tmp1 is missing in the below spec
+    EXPECT_ANY_THROW(cv::gapi::island("isl0", cv::GIn(in[0]),  cv::GOut(tmp[2])));
+
+    // empty range
+    EXPECT_ANY_THROW(cv::gapi::island("isl1", cv::GIn(tmp[2]),  cv::GOut(tmp[2])));
+}
+
+TEST_F(ComplexIslands, InputOperationFromDifferentIslands)
+{
+    //       isl1
+    //       ...........................                   ........
+    // (in0)--> Not  -> (tmp0) --> Add :--------> (tmp2)-->: AddC : -------> (out0)
+    //       :......................^..:                   :  ^   :
+    //       isl0                   :                      :  :   :
+    //       .......................:.......................  :   :
+    // (in1) :-> Blur -> (tmp1) ----'--> Sum ----> (scl0) -----   :
+    //       :....................................................:
+    //       isl0        :
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+    //
+
+    cv::gapi::island("isl0", cv::GIn(in[1], tmp[2]), cv::GOut(out[0]));
+    cv::gapi::island("isl1", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                cv::GMatDesc{CV_8U,1,{640,480}});
+
+    const auto &gm = cc.priv().model();
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+
+    EXPECT_EQ("isl1", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+    EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
+}
+
+TEST_F(ComplexIslands, NoWayBetweenNodes)
+{
+    // (in0) -> Not  -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
+    //                             ^                         ^
+    // (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
+    //                   :
+    //                   `------------> Median -> (tmp3) --> Blur -------> (out1)
+
+    EXPECT_ANY_THROW(cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(tmp[0])));
+}
+
+TEST_F(ComplexIslands, IslandsContainUnusedPart)
+{
+    // Unused part of the graph
+    // x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
+    // x                                                                               x
+    // x(in0) -> Not  -> (tmp0) --> Add ---------> (tmp2)---> AddC ---------> (out0)   x
+    // x                             ^                         ^                       x
+    // x x x x x x x x x x x x x x x | x x                     |                       x
+    //                               |   x                     |                       x
+    //          ......               |   x                     |                       x
+    // (in1) -> :Blur:----------> (tmp1) x-----> Sum ------> (scl0)                    x
+    //          ......    :              x x x x x x x x x x x x x x x x x x x x x x x x
+    //          isl0
+    //                    :
+    //                    `------------> Median -> (tmp3) --> Blur -------> (out1)
+
+    cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(scl));
+    auto cc = cv::GComputation(cv::GIn(in[1]), cv::GOut(out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}});
+
+    const auto &gm = cc.priv().model();
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+
+    //The output 0 is not specified in the graph
+    //means that there will not be a node scl, so that  tmp1 will not assign to the island
+    // FIXME Check that blur assigned to island using the function producerOf
+    // After merge islands fusion
+    EXPECT_FALSE(gm.metadata(tmp1_nh) .contains<cv::gimpl::Island>());
+}
+
+TEST_F(ComplexIslands, FullGraphInTwoIslands)
+{
+    //       isl0
+    //          ..................................................
+    // (in0) -> :Not -> (tmp0) --> Add ---------> (tmp2) --> AddC: -------> (out0)
+    //          ...................^....                     ^   :
+    //          ...............    |   :                     :   :
+    // (in1) -> :Blur-> (tmp1):----'-->:Sum ----> (scl0) ----'   :
+    //          ........ |    :        ...........................
+    //          isl1   : |    :............................................
+    //                 : `------------> Median -> (tmp3) --> Blur ------->:(out1)
+    //                 ....................................................
+
+    cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(out[0]));
+    cv::gapi::island("isl1", cv::GIn(in[1]), cv::GOut(out[1]));
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                cv::GMatDesc{CV_8U,1,{640,480}});
+
+    const auto &gm = cc.priv().model();
+    const auto in0_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    const auto in1_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
+    const auto scl_nh  = cv::gimpl::GModel::dataNodeOf(gm, scl);
+    const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
+    const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
+
+    // Check handles inside isl0
+    EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl0", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl0", gm.metadata(scl_nh).get<cv::gimpl::Island>().island);
+
+    // Check handles inside isl1
+    EXPECT_EQ("isl1", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
+    EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
+
+    // Check outside handles
+    EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
+}
+
+TEST_F(ComplexIslands, OnlyOperationsAssignedToIslands)
+{
+    cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(tmp[1]));
+    cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(scl));
+    cv::gapi::island("isl2", cv::GIn(scl, tmp[2]), cv::GOut(out[0]));
+    cv::gapi::island("isl3", cv::GIn(in[0]), cv::GOut(tmp[0]));
+    cv::gapi::island("isl4", cv::GIn(tmp[0], tmp[1]), cv::GOut(tmp[2]));
+    cv::gapi::island("isl5", cv::GIn(tmp[1]), cv::GOut(tmp[3]));
+    cv::gapi::island("isl6", cv::GIn(tmp[3]), cv::GOut(out[1]));
+
+    auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
+        .compile(cv::GMatDesc{CV_8U,1,{640,480}},
+                cv::GMatDesc{CV_8U,1,{640,480}});
+
+    const auto &gm = cc.priv().model();
+    //FIXME: Check that operation handles are really assigned to isl0..isl6
+    const auto in0_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
+    const auto in1_nh  = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
+    const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
+    const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
+    const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
+    const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
+    const auto scl_nh  = cv::gimpl::GModel::dataNodeOf(gm, scl);
+    const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
+    const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
+
+    EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp0_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp1_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp2_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp3_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
+}
+
+namespace
+{
+    struct IslandStructureWithGArray
+    {
+        GIntArray in, out;
+        GMat tmp;
+
+        IslandStructureWithGArray()
+        {
+            tmp = CreateMatWithDiag::on(in);
+            out = Mat2Array::on(tmp);
+        }
+    };
+
+    struct IslandsWithGArray: public ::testing::Test, public IslandStructureWithGArray {};
+} // namespace
+
+TEST_F(IslandsWithGArray, IslandWithGArrayAsInput)
+{
+    cv::gapi::island("isl0", cv::GIn(in), cv::GOut(tmp));
+
+    const auto pkg = cv::gapi::kernels<CreateMatWithDiagImpl, Mat2ArrayImpl>();
+    auto cc = cv::GComputation(cv::GIn(in), GOut(out)).compile(cv::empty_array_desc(), cv::compile_args(pkg));
+    const auto &gm = cc.priv().model();
+
+    const auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in.strip());
+    const auto out_nh  = cv::gimpl::GModel::dataNodeOf(gm, out.strip());
+    const auto tmp_nh  = cv::gimpl::GModel::dataNodeOf(gm, tmp);
+    GAPI_Assert(tmp_nh->inNodes().size() == 1);
+    const auto create_diag_mat_nh = tmp_nh->inNodes().front();
+
+    EXPECT_EQ("isl0", gm.metadata(create_diag_mat_nh).get<cv::gimpl::Island>().island);
+    EXPECT_FALSE(gm.metadata(in_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp_nh) .contains<cv::gimpl::Island>());
+}
+
+TEST_F(IslandsWithGArray, IslandWithGArrayAsOutput)
+{
+    cv::gapi::island("isl0", cv::GIn(tmp), cv::GOut(out));
+
+    const auto pkg = cv::gapi::kernels<CreateMatWithDiagImpl, Mat2ArrayImpl>();
+    auto cc = cv::GComputation(cv::GIn(in), GOut(out)).compile(cv::empty_array_desc(), cv::compile_args(pkg));
+    const auto &gm = cc.priv().model();
+
+    const auto in_nh   = cv::gimpl::GModel::dataNodeOf(gm, in.strip());
+    const auto out_nh  = cv::gimpl::GModel::dataNodeOf(gm, out.strip());
+    const auto tmp_nh  = cv::gimpl::GModel::dataNodeOf(gm, tmp);
+    GAPI_Assert(tmp_nh->inNodes().size() == 1);
+    const auto mat2array_nh = out_nh->inNodes().front();
+
+    EXPECT_EQ("isl0", gm.metadata(mat2array_nh).get<cv::gimpl::Island>().island);
+    EXPECT_FALSE(gm.metadata(in_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
+    EXPECT_FALSE(gm.metadata(tmp_nh) .contains<cv::gimpl::Island>());
+}
+////////////////////////////////////////////////////////////////////////////////
+// Wrong input tests on island name
+//
+namespace
+{
+    struct CheckName : public TestWithParam<std::tuple<bool, const char*> >,
+                       public PlainIslandsFixture
+    {
+        void assignIsland(const std::string &s)
+        {
+            cv::gapi::island(s, cv::GIn(tmp[0]), cv::GOut(tmp[2]));
+        };
+    };
+    TEST_P(CheckName, Test)
+    {
+        bool correct = false;
+        const char *name = "";
+        std::tie(correct, name) = GetParam();
+        if (correct) EXPECT_NO_THROW(assignIsland(name));
+        else EXPECT_ANY_THROW(assignIsland(name));
+    }
+} // namespace
+INSTANTIATE_TEST_CASE_P(IslandName, CheckName,
+                        Values(std::make_tuple(true,  "name"),
+                               std::make_tuple(true,  " name "),
+                               std::make_tuple(true,  " n a m e "),
+                               std::make_tuple(true,  " 123 $$ %%"),
+                               std::make_tuple(true,  ".: -"),
+                               std::make_tuple(false, ""),
+                               std::make_tuple(false, " "),
+                               std::make_tuple(false, " \t "),
+                               std::make_tuple(false, "  \t \t   ")));
+
+// FIXME: add <internal> test on unrollExpr() use for islands
+
+} // opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_recompilation_test.cpp b/modules/gapi/test/internal/gapi_int_recompilation_test.cpp
new file mode 100644 (file)
index 0000000..de8d46e
--- /dev/null
@@ -0,0 +1,71 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "api/gcomputation_priv.hpp"
+
+namespace opencv_test
+{
+
+TEST(GComputationCompile, NoRecompileWithSameMeta)
+{
+    cv::GMat in;
+    cv::GComputation cc(in, in+in);
+
+    cv::Mat in_mat1 = cv::Mat::eye  (32, 32, CV_8UC1);
+    cv::Mat in_mat2 = cv::Mat::zeros(32, 32, CV_8UC1);
+    cv::Mat out_mat;
+
+    cc.apply(in_mat1, out_mat);
+    auto comp1 = cc.priv().m_lastCompiled;
+
+    cc.apply(in_mat2, out_mat);
+    auto comp2 = cc.priv().m_lastCompiled;
+
+    // Both compiled objects are actually the same unique executable
+    EXPECT_EQ(&comp1.priv(), &comp2.priv());
+}
+
+TEST(GComputationCompile, NoRecompileWithWrongMeta)
+{
+    cv::GMat in;
+    cv::GComputation cc(in, in+in);
+
+    cv::Mat in_mat1 = cv::Mat::eye  (32, 32, CV_8UC1);
+    cv::Mat in_mat2 = cv::Mat::zeros(32, 32, CV_8UC1);
+    cv::Mat out_mat;
+
+    cc.apply(in_mat1, out_mat);
+    auto comp1 = cc.priv().m_lastCompiled;
+
+    EXPECT_THROW(cc.apply(cv::gin(cv::Scalar(128)), cv::gout(out_mat)), std::logic_error);
+    auto comp2 = cc.priv().m_lastCompiled;
+
+    // Both compiled objects are actually the same unique executable
+    EXPECT_EQ(&comp1.priv(), &comp2.priv());
+}
+
+TEST(GComputationCompile, RecompileWithDifferentMeta)
+{
+    cv::GMat in;
+    cv::GComputation cc(in, in+in);
+
+    cv::Mat in_mat1 = cv::Mat::eye  (32, 32, CV_8UC1);
+    cv::Mat in_mat2 = cv::Mat::zeros(64, 64, CV_32F);
+    cv::Mat out_mat;
+
+    cc.apply(in_mat1, out_mat);
+    auto comp1 = cc.priv().m_lastCompiled;
+
+    cc.apply(in_mat2, out_mat);
+    auto comp2 = cc.priv().m_lastCompiled;
+
+    // Both compiled objects are different
+    EXPECT_NE(&comp1.priv(), &comp2.priv());
+}
+
+} // opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp b/modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp
new file mode 100644 (file)
index 0000000..d4b16f6
--- /dev/null
@@ -0,0 +1,119 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+#include "gapi_mock_kernels.hpp"
+
+namespace opencv_test
+{
+
+TEST(Lookup, CreateOrder)
+{
+    const auto order = cv::gapi::lookup_order({Jupiter::backend(),
+                                               Saturn::backend()});
+    EXPECT_EQ(2u, order.size());
+    EXPECT_EQ(Jupiter::backend(), order[0]);
+    EXPECT_EQ(Saturn ::backend(), order[1]);
+}
+
+TEST(Lookup, NoOrder)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz,
+                                       S::Foo, S::Bar, S::Baz>();
+
+    EXPECT_NO_THROW (pkg.lookup<I::Foo>());
+    EXPECT_NO_THROW (pkg.lookup<I::Bar>());
+    EXPECT_NO_THROW (pkg.lookup<I::Baz>());
+    EXPECT_ANY_THROW(pkg.lookup<I::Qux>());
+}
+
+TEST(Lookup, Only_Jupiter)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz,
+                                       S::Foo, S::Bar, S::Baz>();
+
+    auto order = cv::gapi::lookup_order({J::backend()});
+
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Foo>(order));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Bar>(order));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Baz>(order));
+    EXPECT_ANY_THROW(pkg.lookup<I::Qux>(order));
+}
+
+TEST(Lookup, Only_Saturn)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz,
+                                       S::Foo, S::Bar, S::Baz>();
+
+    auto order = cv::gapi::lookup_order({S::backend()});
+
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Foo>(order));
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Bar>(order));
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Baz>(order));
+    EXPECT_ANY_THROW(pkg.lookup<I::Qux>(order));
+}
+
+TEST(Lookup, With_Order)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz,
+                                       S::Foo, S::Bar, S::Baz>();
+
+    auto prefer_j = cv::gapi::lookup_order({J::backend(), S::backend()});
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Foo>(prefer_j));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Bar>(prefer_j));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Baz>(prefer_j));
+    EXPECT_ANY_THROW(pkg.lookup<I::Qux>(prefer_j));
+
+    auto prefer_s = cv::gapi::lookup_order({S::backend(), J::backend()});
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Foo>(prefer_s));
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Bar>(prefer_s));
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Baz>(prefer_s));
+    EXPECT_ANY_THROW(pkg.lookup<I::Qux>(prefer_s));
+}
+
+TEST(Lookup, NoOverlap)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, S::Baz, S::Qux>();
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Foo>());
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Bar>());
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Baz>());
+    EXPECT_EQ(S::backend(), pkg.lookup<I::Qux>());
+}
+
+TEST(Lookup, ExtraBackend)
+{
+    namespace J = Jupiter;
+    namespace S = Saturn;
+    const auto pkg = cv::gapi::kernels<J::Foo, J::Bar, J::Baz>();
+
+    // Even if pkg doesn't contain S kernels while S is preferable,
+    // it should work.
+    const auto prefer_sj = cv::gapi::lookup_order({S::backend(), J::backend()});
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Foo>(prefer_sj));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Bar>(prefer_sj));
+    EXPECT_EQ(J::backend(), pkg.lookup<I::Baz>(prefer_sj));
+
+    // If search scope is limited to S only, neither J nor S  kernels
+    // shouldn't be found
+    const auto only_s = cv::gapi::lookup_order({S::backend()});
+    EXPECT_ANY_THROW(pkg.lookup<I::Foo>(only_s));
+    EXPECT_ANY_THROW(pkg.lookup<I::Bar>(only_s));
+    EXPECT_ANY_THROW(pkg.lookup<I::Baz>(only_s));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_int_vectorref_test.cpp b/modules/gapi/test/internal/gapi_int_vectorref_test.cpp
new file mode 100644 (file)
index 0000000..1b14e06
--- /dev/null
@@ -0,0 +1,207 @@
+// 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
+
+
+#include "test_precomp.hpp"
+
+namespace opencv_test
+{
+
+typedef ::testing::Types<int, cv::Point, cv::Rect> VectorRef_Test_Types;
+
+template<typename T> struct VectorRefT: public ::testing::Test { using Type = T; };
+
+TYPED_TEST_CASE(VectorRefT, VectorRef_Test_Types);
+
+TYPED_TEST(VectorRefT, Reset_Valid)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRefT<T> ref;       // vector ref created empty
+    EXPECT_NO_THROW(ref.reset());        // 1st reset is OK (initializes)
+    EXPECT_NO_THROW(ref.reset());        // 2nd reset is also OK (resets)
+}
+
+TYPED_TEST(VectorRefT, Reset_Invalid)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec(42);              // create a std::vector of 42 elements
+    cv::detail::VectorRefT<T> ref(vec);  // RO_EXT (since reference is const)
+    EXPECT_ANY_THROW(ref.reset());       // data-bound vector ref can't be reset
+}
+
+TYPED_TEST(VectorRefT, ReadRef_External)
+{
+    using T = typename TestFixture::Type;
+    const std::vector<T> vec(42);        // create a std::vector of 42 elements
+    cv::detail::VectorRefT<T> ref(vec);  // RO_EXT (since reference is const)
+    auto &vref = ref.rref();
+    EXPECT_EQ(vec.data(), vref.data());
+    EXPECT_EQ(vec.size(), vref.size());
+}
+
+TYPED_TEST(VectorRefT, ReadRef_Internal)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRefT<T> ref;
+    ref.reset();                         // RW_OWN (reset on empty ref)
+    auto &vref = ref.rref();             // read access is valid for RW_OWN
+    EXPECT_EQ(0u, vref.size());          // by default vector is empty
+}
+
+TYPED_TEST(VectorRefT, WriteRef_External)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec(42);               // create a std::vector of 42 elements
+    cv::detail::VectorRefT<T> ref(vec);   // RW_EXT (since reference is not const)
+    auto &vref = ref.wref();              // write access is valid with RW_EXT
+    EXPECT_EQ(vec.data(), vref.data());
+    EXPECT_EQ(vec.size(), vref.size());
+}
+
+TYPED_TEST(VectorRefT, WriteRef_Internal)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRefT<T> ref;
+    ref.reset();                          // RW_OWN (reset on empty ref)
+    auto &vref = ref.wref();              // write access is valid for RW_OWN
+    EXPECT_EQ(0u, vref.size());           // empty vector by default
+}
+
+TYPED_TEST(VectorRefT, WriteToRO)
+{
+    using T = typename TestFixture::Type;
+    const std::vector<T> vec(42);        // create a std::vector of 42 elements
+    cv::detail::VectorRefT<T> ref(vec);  // RO_EXT (since reference is const)
+    EXPECT_ANY_THROW(ref.wref());
+}
+
+TYPED_TEST(VectorRefT, ReadAfterWrite)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec;                        // Initial data holder (empty vector)
+    cv::detail::VectorRefT<T> writer(vec);     // RW_EXT
+
+    const auto& ro_ref = vec;
+    cv::detail::VectorRefT<T> reader(ro_ref);  // RO_EXT
+
+    EXPECT_EQ(0u, writer.wref().size()); // Check the initial state
+    EXPECT_EQ(0u, reader.rref().size());
+
+    writer.wref().emplace_back();        // Check that write is successfull
+    EXPECT_EQ(1u, writer.wref().size());
+
+    EXPECT_EQ(1u, vec.size());           // Check that changes are reflected to the original container
+    EXPECT_EQ(1u, reader.rref().size()); // Check that changes are reflected to reader's view
+
+    EXPECT_EQ(T(), vec.at(0));           // Check the value (must be default-initialized)
+    EXPECT_EQ(T(), reader.rref().at(0));
+    EXPECT_EQ(T(), writer.wref().at(0));
+}
+
+template<typename T> struct VectorRefU: public ::testing::Test { using Type = T; };
+
+TYPED_TEST_CASE(VectorRefU, VectorRef_Test_Types);
+
+template<class T> struct custom_struct { T a; T b; };
+
+TYPED_TEST(VectorRefU, Reset_Valid)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRef ref;           // vector ref created empty
+    EXPECT_NO_THROW(ref.reset<T>());     // 1st reset is OK (initializes)
+    EXPECT_NO_THROW(ref.reset<T>());     // 2nd reset is also OK (resets)
+
+    EXPECT_ANY_THROW(ref.reset<custom_struct<T> >()); // type change is not allowed
+}
+
+TYPED_TEST(VectorRefU, Reset_Invalid)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec(42);              // create a std::vector of 42 elements
+    cv::detail::VectorRef ref(vec);      // RO_EXT (since reference is const)
+    EXPECT_ANY_THROW(ref.reset<T>());    // data-bound vector ref can't be reset
+}
+
+TYPED_TEST(VectorRefU, ReadRef_External)
+{
+    using T = typename TestFixture::Type;
+    const std::vector<T> vec(42);        // create a std::vector of 42 elements
+    cv::detail::VectorRef ref(vec);      // RO_EXT (since reference is const)
+    auto &vref = ref.rref<T>();
+    EXPECT_EQ(vec.data(), vref.data());
+    EXPECT_EQ(vec.size(), vref.size());
+}
+
+TYPED_TEST(VectorRefU, ReadRef_Internal)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRef ref;
+    ref.reset<T>();                      // RW_OWN (reset on empty ref)
+    auto &vref = ref.rref<T>();          // read access is valid for RW_OWN
+    EXPECT_EQ(0u, vref.size());          // by default vector is empty
+}
+
+TYPED_TEST(VectorRefU, WriteRef_External)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec(42);             // create a std::vector of 42 elements
+    cv::detail::VectorRef ref(vec);     // RW_EXT (since reference is not const)
+    auto &vref = ref.wref<T>();         // write access is valid with RW_EXT
+    EXPECT_EQ(vec.data(), vref.data());
+    EXPECT_EQ(vec.size(), vref.size());
+}
+
+TYPED_TEST(VectorRefU, WriteRef_Internal)
+{
+    using T = typename TestFixture::Type;
+    cv::detail::VectorRef ref;
+    ref.reset<T>();                     // RW_OWN (reset on empty ref)
+    auto &vref = ref.wref<T>();         // write access is valid for RW_OWN
+    EXPECT_EQ(0u, vref.size());         // empty vector by default
+}
+
+TYPED_TEST(VectorRefU, WriteToRO)
+{
+    using T = typename TestFixture::Type;
+    const std::vector<T> vec(42);       // create a std::vector of 42 elements
+    cv::detail::VectorRef ref(vec);     // RO_EXT (since reference is const)
+    EXPECT_ANY_THROW(ref.wref<T>());
+}
+
+TYPED_TEST(VectorRefU, ReadAfterWrite)
+{
+    using T = typename TestFixture::Type;
+    std::vector<T> vec;                     // Initial data holder (empty vector)
+    cv::detail::VectorRef writer(vec);      // RW_EXT
+
+    const auto& ro_ref = vec;
+    cv::detail::VectorRef reader(ro_ref);   // RO_EXT
+
+    EXPECT_EQ(0u, writer.wref<T>().size()); // Check the initial state
+    EXPECT_EQ(0u, reader.rref<T>().size());
+
+    writer.wref<T>().emplace_back();        // Check that write is successfull
+    EXPECT_EQ(1u, writer.wref<T>().size());
+
+    EXPECT_EQ(1u, vec.size());              // Check that changes are reflected to the original container
+    EXPECT_EQ(1u, reader.rref<T>().size()); // Check that changes are reflected to reader's view
+
+    EXPECT_EQ(T(), vec.at(0));              // Check the value (must be default-initialized)
+    EXPECT_EQ(T(), reader.rref<T>().at(0));
+    EXPECT_EQ(T(), writer.wref<T>().at(0));
+}
+
+TEST(VectorRefU, TypeCheck)
+{
+    cv::detail::VectorRef ref;
+    ref.reset<int>(); // RW_OWN
+
+    EXPECT_ANY_THROW(ref.reset<char>());
+    EXPECT_ANY_THROW(ref.rref<char>());
+    EXPECT_ANY_THROW(ref.wref<char>());
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/internal/gapi_transactions_test.cpp b/modules/gapi/test/internal/gapi_transactions_test.cpp
new file mode 100644 (file)
index 0000000..f550340
--- /dev/null
@@ -0,0 +1,222 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include <ade/graph.hpp>
+#include "compiler/transactions.hpp"
+
+namespace opencv_test
+{
+namespace
+{
+
+bool contains(const ade::Graph& graph, const ade::NodeHandle& node)
+{
+    auto nodes = graph.nodes();
+    return nodes.end() != std::find(nodes.begin(), nodes.end(), node);
+}
+
+bool connected(const ade::NodeHandle& src_node, const ade::NodeHandle& dst_node)
+{
+    auto nodes = src_node->outNodes();
+    return nodes.end() != std::find(nodes.begin(), nodes.end(), dst_node);
+}
+
+struct SimpleGraph
+{
+    //       ehs[0]      ehs[1]     ehs[2]     ehs[3]
+    // nhs[0] -- > nhs[1] --> nhs[2] --> nhs[3] --> nhs[4]
+
+    enum { node_nums = 5 };
+    ade::Graph        graph;
+    ade::NodeHandle   fused_nh;                     /* For check that fusion  node is connected to the
+                                                               inputs of the prod and the outputs of the cons */
+    std::array<ade::NodeHandle, node_nums>     nhs;
+    std::array<ade::EdgeHandle, node_nums - 1> ehs;
+    Change::List changes;
+
+    SimpleGraph()
+    {
+        nhs[0] = graph.createNode();
+        for (int i = 1; i < node_nums; ++i)
+        {
+            nhs[i    ] = graph.createNode();
+            ehs[i - 1] = graph.link(nhs[i - 1], nhs[i]);
+        }
+    }
+
+    void fuse()
+    {
+        // nhs[0] --> fused_nh --> nhs[4]
+
+        fused_nh = graph.createNode();
+        changes.enqueue<Change::NodeCreated>(fused_nh);
+        changes.enqueue<Change::NewLink> (graph, nhs[0],    fused_nh);
+        changes.enqueue<Change::DropLink>(graph, nhs[1],    ehs[0]);
+        changes.enqueue<Change::NewLink> (graph, fused_nh, nhs[4]);
+        changes.enqueue<Change::DropLink>(graph, nhs[3],    ehs[3]);
+        changes.enqueue<Change::DropLink>(graph, nhs[1],    ehs[1]);
+        changes.enqueue<Change::DropLink>(graph, nhs[2],    ehs[2]);
+        changes.enqueue<Change::DropNode>(nhs[1]);
+        changes.enqueue<Change::DropNode>(nhs[2]);
+        changes.enqueue<Change::DropNode>(nhs[3]);
+    }
+
+    void commit()   { changes.commit(graph);   }
+    void rollback() { changes.rollback(graph); }
+
+};
+
+struct Transactions: public ::testing::Test, public SimpleGraph {};
+
+} // anonymous namespace
+
+TEST_F(Transactions, NodeCreated_Create)
+{
+    auto new_nh = graph.createNode();
+    Change::NodeCreated node_created(new_nh);
+
+    EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_TRUE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, NodeCreated_RollBack)
+{
+    auto new_nh = graph.createNode();
+    Change::NodeCreated node_created(new_nh);
+
+    node_created.rollback(graph);
+
+    EXPECT_EQ(5u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_FALSE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, NodeCreated_Commit)
+{
+    auto new_nh = graph.createNode();
+    Change::NodeCreated node_created(new_nh);
+
+    node_created.commit(graph);
+
+    EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_TRUE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, DropLink_Create)
+{
+    Change::DropLink drop_link(graph, nhs[0], ehs[0]);
+
+    EXPECT_FALSE(connected(nhs[0], nhs[1]));
+}
+
+TEST_F(Transactions, DropLink_RollBack)
+{
+    Change::DropLink drop_link(graph, nhs[0], ehs[0]);
+
+    drop_link.rollback(graph);
+
+    EXPECT_TRUE(connected(nhs[0], nhs[1]));
+}
+
+TEST_F(Transactions, DropLink_Commit)
+{
+    Change::DropLink drop_link(graph, nhs[0], ehs[0]);
+
+    drop_link.commit(graph);
+
+    EXPECT_FALSE(connected(nhs[0], nhs[1]));
+}
+
+TEST_F(Transactions, NewLink_Create)
+{
+    auto new_nh = graph.createNode();
+    Change::NewLink new_link(graph, new_nh, nhs[0]);
+
+    EXPECT_TRUE(connected(new_nh, nhs[0]));
+}
+
+TEST_F(Transactions, NewLink_RollBack)
+{
+    auto new_nh = graph.createNode();
+    Change::NewLink new_link(graph, new_nh, nhs[0]);
+
+    new_link.rollback(graph);
+
+    EXPECT_FALSE(connected(new_nh, nhs[0]));
+}
+
+TEST_F(Transactions, NewLink_Commit)
+{
+    auto new_nh = graph.createNode();
+    Change::NewLink new_link(graph, new_nh, nhs[0]);
+
+    new_link.commit(graph);
+
+    EXPECT_TRUE(connected(new_nh, nhs[0]));
+}
+
+TEST_F(Transactions, DropNode_Create)
+{
+    auto new_nh = graph.createNode();
+    Change::DropNode drop_node(new_nh);
+
+    EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_TRUE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, DropNode_RollBack)
+{
+    auto new_nh = graph.createNode();
+    Change::DropNode drop_node(new_nh);
+
+    drop_node.rollback(graph);
+
+    EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_TRUE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, DropNode_Commit)
+{
+    auto new_nh = graph.createNode();
+    Change::DropNode drop_node(new_nh);
+
+    drop_node.commit(graph);
+
+    EXPECT_EQ(5u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_FALSE(contains(graph, new_nh));
+}
+
+TEST_F(Transactions, Fusion_Commit)
+{
+    namespace C = Change;
+
+    fuse();
+    commit();
+
+    EXPECT_EQ(3u, static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_TRUE(connected(nhs[0]   , fused_nh));
+    EXPECT_TRUE(connected(fused_nh, nhs[4]));
+}
+
+TEST_F(Transactions, Fusion_RollBack)
+{
+    namespace C = Change;
+
+    fuse();
+    rollback();
+
+    EXPECT_EQ(static_cast<std::size_t>(node_nums),
+              static_cast<std::size_t>(graph.nodes().size()));
+    EXPECT_FALSE(contains(graph, fused_nh));
+
+    for (int i = 0; i < static_cast<int>(node_nums) - 1; ++i)
+    {
+        EXPECT_TRUE(connected(nhs[i], nhs[i + 1]));
+    }
+}
+
+} // opencv_test
diff --git a/modules/gapi/test/own/gapi_types_tests.cpp b/modules/gapi/test/own/gapi_types_tests.cpp
new file mode 100644 (file)
index 0000000..c254357
--- /dev/null
@@ -0,0 +1,159 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/own/types.hpp"
+
+namespace opencv_test
+{
+
+TEST(Point, CreateEmpty)
+{
+    cv::gapi::own::Point p;
+
+    EXPECT_EQ(0, p.x);
+    EXPECT_EQ(0, p.y);
+}
+
+TEST(Point, CreateWithParams)
+{
+    cv::gapi::own::Point p = {1, 2};
+
+    EXPECT_EQ(1, p.x);
+    EXPECT_EQ(2, p.y);
+}
+
+TEST(Rect, CreateEmpty)
+{
+    cv::gapi::own::Rect r;
+
+    EXPECT_EQ(0, r.x);
+    EXPECT_EQ(0, r.y);
+    EXPECT_EQ(0, r.width);
+    EXPECT_EQ(0, r.height);
+}
+
+TEST(Rect, CreateWithParams)
+{
+    cv::gapi::own::Rect r(1, 2, 3, 4);
+
+    EXPECT_EQ(1, r.x);
+    EXPECT_EQ(2, r.y);
+    EXPECT_EQ(3, r.width);
+    EXPECT_EQ(4, r.height);
+}
+
+TEST(Rect, CompareEqual)
+{
+    cv::gapi::own::Rect r1(1, 2, 3, 4);
+
+    cv::gapi::own::Rect r2(1, 2, 3, 4);
+
+    EXPECT_TRUE(r1 == r2);
+}
+
+TEST(Rect, CompareDefaultEqual)
+{
+    cv::gapi::own::Rect r1;
+
+    cv::gapi::own::Rect r2;
+
+    EXPECT_TRUE(r1 == r2);
+}
+
+TEST(Rect, CompareNotEqual)
+{
+    cv::gapi::own::Rect r1(1, 2, 3, 4);
+
+    cv::gapi::own::Rect r2;
+
+    EXPECT_TRUE(r1 != r2);
+}
+
+TEST(Rect, Intersection)
+{
+    cv::gapi::own::Rect r1(2, 2, 3, 3);
+    cv::gapi::own::Rect r2(3, 1, 3, 3);
+
+    cv::gapi::own::Rect intersect = r1 & r2;
+
+    EXPECT_EQ(3, intersect.x);
+    EXPECT_EQ(2, intersect.y);
+    EXPECT_EQ(2, intersect.width);
+    EXPECT_EQ(2, intersect.height);
+}
+
+TEST(Rect, AssignIntersection)
+{
+    cv::gapi::own::Rect r1(2, 2, 3, 3);
+    cv::gapi::own::Rect r2(3, 1, 3, 3);
+
+    r1 &= r2;
+
+    EXPECT_EQ(3, r1.x);
+    EXPECT_EQ(2, r1.y);
+    EXPECT_EQ(2, r1.width);
+    EXPECT_EQ(2, r1.height);
+}
+
+TEST(Size, CreateEmpty)
+{
+    cv::gapi::own::Size s;
+
+    EXPECT_EQ(0, s.width);
+    EXPECT_EQ(0, s.height);
+}
+
+TEST(Size, CreateWithParams)
+{
+    cv::gapi::own::Size s(640, 480);
+
+    EXPECT_EQ(640, s.width);
+    EXPECT_EQ(480, s.height);
+}
+
+TEST(Size, AdditionAssignment)
+{
+    cv::gapi::own::Size s1(1, 2);
+    cv::gapi::own::Size s2(2, 3);
+
+    s1 += s2;
+
+    EXPECT_EQ(3, s1.width);
+    EXPECT_EQ(5, s1.height);
+}
+
+TEST(Size, CompareEqual)
+{
+    cv::gapi::own::Size s1(1, 2);
+
+    cv::gapi::own::Size s2(1, 2);
+
+    EXPECT_TRUE(s1 == s2);
+    EXPECT_FALSE(s1 != s2);
+}
+
+TEST(Size, CompareDefaultEqual)
+{
+    cv::gapi::own::Size s1;
+    cv::gapi::own::Size s2;
+
+    EXPECT_TRUE(s1 == s2);
+    EXPECT_FALSE(s1 != s2);
+}
+
+TEST(Size, CompareNotEqual)
+{
+    cv::gapi::own::Size s1(1, 2);
+
+    cv::gapi::own::Size s2(3, 4);
+
+    EXPECT_FALSE(s1 == s2);
+    EXPECT_TRUE(s1 != s2);
+}
+
+} // opencv_test
diff --git a/modules/gapi/test/own/mat_tests.cpp b/modules/gapi/test/own/mat_tests.cpp
new file mode 100644 (file)
index 0000000..ffefe0c
--- /dev/null
@@ -0,0 +1,164 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/own/mat.hpp"
+#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+
+namespace opencv_test
+{
+using Mat = cv::gapi::own::Mat;
+
+TEST(OwnMat, DefaultConstruction)
+{
+    Mat m;
+    ASSERT_EQ(m.data, nullptr);
+    ASSERT_EQ(m.cols, 0);
+    ASSERT_EQ(m.rows, 0);
+    ASSERT_EQ(m.cols, 0);
+    ASSERT_EQ(m.type(), 0);
+    ASSERT_EQ(m.depth(), 0);
+}
+
+TEST(OwnMat, Create)
+{
+    auto size = cv::gapi::own::Size{32,16};
+    Mat m;
+    m.create(size, CV_8UC1);
+
+    ASSERT_NE(m.data, nullptr);
+    ASSERT_EQ((cv::gapi::own::Size{m.cols, m.rows}), size);
+
+    ASSERT_EQ(m.type(), CV_8UC1);
+    ASSERT_EQ(m.depth(), CV_8U);
+    ASSERT_EQ(m.channels(), 1);
+    ASSERT_EQ(m.step,   sizeof(uint8_t) * m.cols);
+}
+
+struct NonEmptyMat {
+    cv::gapi::own::Size size{32,16};
+    Mat m;
+    NonEmptyMat() {
+        m.create(size, CV_8UC1);
+    }
+};
+
+struct OwnMatSharedSemantics : NonEmptyMat, ::testing::Test {};
+
+
+namespace {
+    auto state_of = [](Mat const& mat) {
+        return std::make_tuple(
+                mat.data,
+                cv::Size{mat.cols, mat.rows},
+                mat.type(),
+                mat.depth(),
+                mat.channels()
+        );
+    };
+
+    void ensure_mats_are_same(Mat const& copy, Mat const& m){
+        EXPECT_NE(copy.data, nullptr);
+        EXPECT_EQ(state_of(copy), state_of(m));
+    }
+}
+TEST_F(OwnMatSharedSemantics, CopyConstruction)
+{
+    Mat copy(m);
+    ensure_mats_are_same(copy, m);
+}
+
+TEST_F(OwnMatSharedSemantics, CopyAssignment)
+{
+    Mat copy;
+    copy = m;
+    ensure_mats_are_same(copy, m);
+}
+
+struct OwnMatMoveSemantics : NonEmptyMat, ::testing::Test {
+    Mat& moved_from = m;
+    decltype(state_of(moved_from)) initial_state = state_of(moved_from);
+
+    void ensure_state_moved_to(Mat const& moved_to)
+    {
+        EXPECT_EQ(state_of(moved_to),     initial_state);
+        EXPECT_EQ(state_of(moved_from),   state_of(Mat{}));
+    }
+};
+
+TEST_F(OwnMatMoveSemantics, MoveConstruction)
+{
+    Mat moved_to(std::move(moved_from));
+
+    ensure_state_moved_to(moved_to);
+}
+
+TEST_F(OwnMatMoveSemantics, MoveAssignment)
+{
+    Mat moved_to(std::move(moved_from));
+    ensure_state_moved_to(moved_to);
+}
+
+struct OwnMatNonOwningView : NonEmptyMat, ::testing::Test {
+    decltype(state_of(m)) initial_state = state_of(m);
+
+    void TearDown() override {
+        EXPECT_EQ(state_of(m), initial_state)<<"State of the source matrix changed?";
+        //ASAN should complain here if memory is freed here (e.g. by bug in non owning logic of own::Mat)
+        volatile uchar dummy =  m.data[0];
+        cv::util::suppress_unused_warning(dummy);
+    }
+
+};
+
+TEST_F(OwnMatNonOwningView, Construction)
+{
+    Mat non_owning_view(m.rows, m.cols, m.type(), static_cast<void*>(m.data));
+
+    ensure_mats_are_same(non_owning_view, m);
+}
+
+TEST_F(OwnMatNonOwningView, CopyConstruction)
+{
+    Mat non_owning_view{m.rows, m.cols, m.type(), static_cast<void*>(m.data)};
+
+    Mat non_owning_view_copy = non_owning_view;
+    ensure_mats_are_same(non_owning_view_copy, m);
+}
+
+TEST_F(OwnMatNonOwningView, Assignment)
+{
+    Mat non_owning_view{m.rows, m.cols, m.type(), static_cast<void*>(m.data)};
+    Mat non_owning_view_copy;
+
+    non_owning_view_copy = non_owning_view;
+    ensure_mats_are_same(non_owning_view_copy, m);
+}
+
+TEST(OwnMatConversion, WithStep)
+{
+    constexpr int width  = 8;
+    constexpr int height = 8;
+    constexpr int stepInPixels = 16;
+
+    std::array<int, height * stepInPixels> data;
+    for (size_t i = 0; i < data.size(); i++)
+    {
+        data[i] = static_cast<int>(i);
+    }
+    cv::Mat cvMat(cv::Size{width, height}, CV_32S, data.data(), stepInPixels * sizeof(int));
+
+    auto ownMat = to_own(cvMat);
+    auto cvMatFromOwn = cv::gapi::own::to_ocv(ownMat);
+
+    EXPECT_EQ(0, cv::countNonZero(cvMat != cvMatFromOwn))
+    << cvMat << std::endl
+    << (cvMat != cvMatFromOwn);
+}
+
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/own/scalar_tests.cpp b/modules/gapi/test/own/scalar_tests.cpp
new file mode 100644 (file)
index 0000000..a9c5c01
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/own/scalar.hpp"
+
+namespace opencv_test
+{
+
+TEST(Scalar, CreateEmpty)
+{
+    cv::gapi::own::Scalar s;
+
+    for (int i = 0; i < 4; ++i)
+    {
+        EXPECT_EQ(s[i], 0.0);
+    }
+}
+
+TEST(Scalar, CreateFromVal)
+{
+    cv::gapi::own::Scalar s(5.0);
+
+    EXPECT_EQ(s[0], 5.0);
+    EXPECT_EQ(s[1], 0.0);
+    EXPECT_EQ(s[2], 0.0);
+    EXPECT_EQ(s[3], 0.0);
+}
+
+TEST(Scalar, CreateFromVals)
+{
+    cv::gapi::own::Scalar s(5.3, 3.3, 4.1, -2.0);
+
+    EXPECT_EQ(s[0], 5.3);
+    EXPECT_EQ(s[1], 3.3);
+    EXPECT_EQ(s[2], 4.1);
+    EXPECT_EQ(s[3], -2.0);
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/test_main.cpp b/modules/gapi/test/test_main.cpp
new file mode 100644 (file)
index 0000000..fa5862f
--- /dev/null
@@ -0,0 +1,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
+
+
+// FIXME: OpenCV license header
+
+#include "test_precomp.hpp"
+
+CV_TEST_MAIN("gapi")
diff --git a/modules/gapi/test/test_precomp.hpp b/modules/gapi/test/test_precomp.hpp
new file mode 100644 (file)
index 0000000..899ebdd
--- /dev/null
@@ -0,0 +1,24 @@
+// 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
+
+
+// FIXME: OpenCV header
+
+#ifndef __OPENCV_GAPI_TEST_PRECOMP_HPP__
+#define __OPENCV_GAPI_TEST_PRECOMP_HPP__
+
+#include <cstdint>
+#include <vector>
+
+#include "opencv2/ts.hpp"
+#include "opencv2/gapi.hpp"
+#include "opencv2/gapi/imgproc.hpp"
+#include "opencv2/gapi/core.hpp"
+#include "opencv2/gapi/cpu/gcpukernel.hpp"
+#include "opencv2/gapi/gcompoundkernel.hpp"
+#include "opencv2/gapi/operators.hpp"
+
+#endif // __OPENCV_GAPI_TEST_PRECOMP_HPP__
diff --git a/modules/gapi/test/util/any_tests.cpp b/modules/gapi/test/util/any_tests.cpp
new file mode 100644 (file)
index 0000000..60bbcc1
--- /dev/null
@@ -0,0 +1,121 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/util/any.hpp"
+
+namespace opencv_test
+{
+
+TEST(Any, basic)
+{
+   using namespace util;
+   any a(8);
+   auto casted_pointer =  any_cast<int>(&a);
+   ASSERT_NE(nullptr, casted_pointer);
+   ASSERT_EQ(*casted_pointer, 8);
+
+   *casted_pointer = 7;
+   ASSERT_EQ(any_cast<int>(a), 7);
+}
+
+TEST(Any, any_cast_ref_throws_on_empty)
+{
+   using namespace util;
+   any a;
+
+   ASSERT_THROW(util::any_cast<int>(a), bad_any_cast);
+}
+
+TEST(Any, copy)
+{
+   using namespace util;
+   any a(8);
+
+   ASSERT_EQ(any_cast<int>(a), 8);
+
+   any b (a);
+
+   ASSERT_NE(nullptr, any_cast<int>(&b));
+   ASSERT_EQ(8      , any_cast<int>(b));
+   ASSERT_EQ(8      , any_cast<int>(a));
+}
+
+TEST(Any, copy_empty)
+{
+   using namespace util;
+   any a;
+
+   ASSERT_EQ(nullptr, any_cast<int>(&a));
+
+   any b (a);
+
+   ASSERT_EQ(nullptr, any_cast<int>(&a));
+   ASSERT_EQ(nullptr, any_cast<int>(&b));
+}
+
+TEST(Any, move)
+{
+   using namespace util;
+   any a(8);
+
+   ASSERT_EQ(any_cast<int>(a), 8);
+
+   any b (std::move(a));
+
+   ASSERT_NE(nullptr,  any_cast<int>(&b));
+   ASSERT_EQ(8      ,  any_cast<int>(b));
+   ASSERT_EQ(nullptr,  any_cast<int>(&a));
+}
+
+TEST(Any, swap)
+{
+   using namespace util;
+   any a(8);
+   any b(7);
+
+   ASSERT_EQ(7, any_cast<int>(b));
+   ASSERT_EQ(8, any_cast<int>(a));
+
+   swap(a,b);
+
+   ASSERT_EQ(8, any_cast<int>(b));
+   ASSERT_EQ(7, any_cast<int>(a));
+}
+
+TEST(Any, move_assign)
+{
+   using namespace util;
+   any a(8);
+   any b;
+
+   ASSERT_EQ(any_cast<int>(a), 8);
+
+   b = (std::move(a));
+
+   ASSERT_NE(nullptr,  any_cast<int>(&b));
+   ASSERT_EQ(8      ,  any_cast<int>(b));
+   ASSERT_EQ(nullptr,  any_cast<int>(&a));
+}
+
+TEST(Any, copy_assign)
+{
+   using namespace util;
+   any a(8);
+   any b;
+
+   ASSERT_EQ(any_cast<int>(a), 8);
+   ASSERT_EQ(nullptr,  any_cast<int>(&b));
+
+   b = a;
+
+   ASSERT_NE(nullptr, any_cast<int>(&b));
+   ASSERT_EQ(8      , any_cast<int>(b));
+   ASSERT_EQ(8      , any_cast<int>(a));
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/util/optional_tests.cpp b/modules/gapi/test/util/optional_tests.cpp
new file mode 100644 (file)
index 0000000..b7fabd5
--- /dev/null
@@ -0,0 +1,175 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/util/optional.hpp"
+#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
+
+namespace opencv_test
+{
+
+TEST(Optional, EmptyCtor)
+{
+    util::optional<int> o;
+    EXPECT_FALSE(o.has_value());
+    EXPECT_FALSE(static_cast<bool>(o));
+}
+
+TEST(Optional, ValueCTor)
+{
+    util::optional<int> o(42);
+    EXPECT_TRUE(o.has_value());
+    EXPECT_TRUE(static_cast<bool>(o));
+}
+
+TEST(Optional, MoveCtr)
+{
+    util::optional<std::string> os1(std::string("text"));
+    EXPECT_TRUE(os1.has_value());
+
+    util::optional<std::string> os2(std::move(os1));
+    EXPECT_FALSE(os1.has_value());
+    EXPECT_TRUE(os2.has_value());
+    EXPECT_EQ("text", os2.value());
+}
+
+TEST(Optional, EmptyThrows)
+{
+    struct foo { int bar; };
+    util::optional<foo> om;
+    const util::optional<foo> oc;
+
+    int dummy;
+
+    EXPECT_THROW(dummy = om->bar,    util::bad_optional_access);
+    EXPECT_THROW(dummy = oc->bar,    util::bad_optional_access);
+    cv::util::suppress_unused_warning(dummy);
+    EXPECT_THROW(*om,        util::bad_optional_access);
+    EXPECT_THROW(*oc,        util::bad_optional_access);
+    EXPECT_THROW(om.value(), util::bad_optional_access);
+    EXPECT_THROW(oc.value(), util::bad_optional_access);
+}
+
+TEST(Optional, ValueNoThrow)
+{
+    struct foo { int bar; };
+    util::optional<foo> om(foo{42});
+    const util::optional<foo> oc(foo{42});
+
+    int dummy;
+    EXPECT_NO_THROW(dummy = om->bar);
+    EXPECT_NO_THROW(dummy = oc->bar);
+    cv::util::suppress_unused_warning(dummy);
+    EXPECT_NO_THROW(*om);
+    EXPECT_NO_THROW(*oc);
+    EXPECT_NO_THROW(om.value());
+    EXPECT_NO_THROW(oc.value());
+}
+
+TEST(Optional, Value)
+{
+    util::optional<int> oi(42);
+
+    struct foo { int bar; };
+    util::optional<foo> of(foo{42});
+
+    EXPECT_EQ(42, oi.value());
+    EXPECT_EQ(42, *oi);
+
+    EXPECT_EQ(42, of.value().bar);
+    EXPECT_EQ(42, of->bar);
+}
+
+TEST(Optional, Mutable)
+{
+    util::optional<int> oi(42);
+    *oi = 43;
+    EXPECT_EQ(43, *oi);
+
+    struct foo { int bar; int baz; };
+    util::optional<foo> of(foo{11,22});
+
+    (*of).bar = 42;
+    EXPECT_EQ(42, of->bar);
+    EXPECT_EQ(22, of->baz);
+
+    of->baz = 33;
+    EXPECT_EQ(42, of->bar);
+    EXPECT_EQ(33, of->baz);
+}
+
+TEST(Optional, MoveAssign)
+{
+    util::optional<int> e, i(42);
+
+    EXPECT_FALSE(e.has_value());
+    EXPECT_TRUE(i.has_value());
+    EXPECT_EQ(42, *i);
+
+    e = std::move(i);
+    EXPECT_TRUE(e.has_value());
+    EXPECT_FALSE(i.has_value());
+    EXPECT_EQ(42, *e);
+}
+
+TEST(Optional, CopyAssign)
+{
+    util::optional<int> e;
+    const util::optional<int> i(42);
+
+    EXPECT_FALSE(e.has_value());
+    EXPECT_TRUE(i.has_value());
+    EXPECT_EQ(42, *i);
+
+    e = i;
+    EXPECT_TRUE(e.has_value());
+    EXPECT_TRUE(i.has_value());
+    EXPECT_EQ(42, *e);
+    EXPECT_EQ(42, *i);
+}
+
+TEST(Optional, ValueOr)
+{
+    util::optional<int> e;
+    EXPECT_FALSE(e.has_value());
+    EXPECT_EQ(42, e.value_or(42));
+    EXPECT_EQ(42, e.value_or(42.1));
+}
+
+TEST(Optional, Swap)
+{
+    util::optional<int> e, i(42);
+
+    EXPECT_FALSE(e.has_value());
+    EXPECT_TRUE(i.has_value());
+    EXPECT_EQ(42, *i);
+
+    e.swap(i);
+
+    EXPECT_TRUE(e.has_value());
+    EXPECT_FALSE(i.has_value());
+    EXPECT_EQ(42, *e);
+}
+
+TEST(Optional, Reset)
+{
+    util::optional<int> i(42);
+    EXPECT_TRUE(i.has_value());
+
+    i.reset();
+    EXPECT_FALSE(i.has_value());
+}
+
+TEST(Optional, MakeOptional)
+{
+    std::string s("text");
+    auto os = util::make_optional(s);
+    EXPECT_TRUE(os.has_value());
+    EXPECT_EQ(s, os.value());
+}
+
+} // namespace opencv_test
diff --git a/modules/gapi/test/util/variant_tests.cpp b/modules/gapi/test/util/variant_tests.cpp
new file mode 100644 (file)
index 0000000..a95b6aa
--- /dev/null
@@ -0,0 +1,386 @@
+// 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
+
+
+#include "test_precomp.hpp"
+#include "opencv2/gapi/util/variant.hpp"
+#include <cstddef> //std::max_align_t
+
+namespace opencv_test
+{
+
+namespace
+{
+    typedef util::variant<int, std::string> TestVar;
+    typedef util::variant<int, float>       TestVar2;
+}
+
+TEST(Variant, EmptyCTor)
+{
+    util::variant<int> vi;
+    EXPECT_EQ(0,  util::get<int>(vi));
+
+    util::variant<int, std::string> vis;
+    EXPECT_EQ(0,  util::get<int>(vis));
+
+    util::variant<std::string> vs;
+    EXPECT_EQ("", util::get<std::string>(vs));
+
+    util::variant<std::string, int> vsi;
+    EXPECT_EQ("", util::get<std::string>(vsi));
+}
+
+TEST(Variant, ValueMoveCTor)
+{
+    util::variant<int> vi(42);
+    EXPECT_EQ(0u,     vi.index());
+    EXPECT_EQ(42,     util::get<int>(vi));
+
+    util::variant<int, std::string> vis(2017);
+    EXPECT_EQ(0u,     vis.index());
+    EXPECT_EQ(2017,   util::get<int>(vis));
+
+    util::variant<int, std::string> vis2(std::string("2017"));
+    EXPECT_EQ(1u,     vis2.index());
+    EXPECT_EQ("2017", util::get<std::string>(vis2));
+
+    util::variant<std::string> vs(std::string("2017"));
+    EXPECT_EQ(0u,     vs.index());
+    EXPECT_EQ("2017", util::get<std::string>(vs));
+
+    util::variant<std::string, int> vsi(std::string("2017"));
+    EXPECT_EQ(0u,     vsi.index());
+    EXPECT_EQ("2017", util::get<std::string>(vsi));
+
+    util::variant<std::string, int> vsi2(42);
+    EXPECT_EQ(1u,     vsi2.index());
+    EXPECT_EQ(42,     util::get<int>(vsi2));
+}
+
+TEST(Variant, ValueCopyCTor)
+{
+    const int i42         = 42;
+    const int i17         = 2017;
+    const std::string s17 = "2017";
+
+    util::variant<int> vi(i42);
+    EXPECT_EQ(0u,     vi.index());
+    EXPECT_EQ(i42,    util::get<int>(vi));
+
+    util::variant<int, std::string> vis(i17);
+    EXPECT_EQ(0u,     vis.index());
+    EXPECT_EQ(i17,    util::get<int>(vis));
+
+    util::variant<int, std::string> vis2(s17);
+    EXPECT_EQ(1u,     vis2.index());
+    EXPECT_EQ(s17,    util::get<std::string>(vis2));
+
+    util::variant<std::string> vs(s17);
+    EXPECT_EQ(0u,     vs.index());
+    EXPECT_EQ(s17,    util::get<std::string>(vs));
+
+    util::variant<std::string, int> vsi(s17);
+    EXPECT_EQ(0u,     vsi.index());
+    EXPECT_EQ(s17, util::get<std::string>(vsi));
+
+    util::variant<std::string, int> vsi2(i42);
+    EXPECT_EQ(1u,     vsi2.index());
+    EXPECT_EQ(i42,    util::get<int>(vsi2));
+}
+
+TEST(Variant, CopyMoveCTor)
+{
+    const TestVar tvconst(std::string("42"));
+
+    TestVar tv = tvconst;
+    EXPECT_EQ( 1u,  tv.index());
+    EXPECT_EQ("42", util::get<std::string>(tv));
+
+    TestVar tv2(TestVar(40+2));
+    EXPECT_EQ( 0u,  tv2.index());
+    EXPECT_EQ( 42,  util::get<int>(tv2));
+}
+
+TEST(Variant, Assign_Basic)
+{
+    TestVar vis;
+    EXPECT_EQ(0u, vis.index());
+    EXPECT_EQ(0,  util::get<int>(vis));
+
+    vis = 42;
+    EXPECT_EQ(0u, vis.index());
+    EXPECT_EQ(42, util::get<int>(vis));
+}
+
+TEST(Variant, Assign_ValueUpdate_SameType)
+{
+    TestVar vis(42);
+
+    EXPECT_EQ(0u, vis.index());
+    EXPECT_EQ(42, util::get<int>(vis));
+
+    vis = 43;
+    EXPECT_EQ(0u, vis.index());
+    EXPECT_EQ(43, util::get<int>(vis));
+}
+
+TEST(Variant, Assign_ValueUpdate_DiffType)
+{
+    TestVar vis(42);
+
+    EXPECT_EQ(0u, vis.index());
+    EXPECT_EQ(42, util::get<int>(vis));
+
+    vis = std::string("42");
+    EXPECT_EQ(1u, vis.index());
+    EXPECT_EQ("42", util::get<std::string>(vis));
+}
+
+TEST(Variant, Assign_ValueUpdate_Const)
+{
+    TestVar va(42);
+    const TestVar vb(43);
+
+    EXPECT_EQ(0u, va.index());
+    EXPECT_EQ(42, util::get<int>(va));
+
+    EXPECT_EQ(0u, vb.index());
+    EXPECT_EQ(43, util::get<int>(vb));
+
+    va = vb;
+
+    EXPECT_EQ(0u, va.index());
+    EXPECT_EQ(43, util::get<int>(va));
+}
+
+TEST(Variant, Assign_ValueUpdate_Const_DiffType)
+{
+    TestVar va(42);
+    const TestVar vb(std::string("42"));
+
+    EXPECT_EQ(0u, va.index());
+    EXPECT_EQ(42, util::get<int>(va));
+
+    EXPECT_EQ(1u, vb.index());
+    EXPECT_EQ("42", util::get<std::string>(vb));
+
+    va = vb;
+
+    EXPECT_EQ(1u,   va.index());
+    EXPECT_EQ("42", util::get<std::string>(va));
+}
+
+TEST(Variant, Assign_Move)
+{
+    TestVar va(42);
+    TestVar vb(std::string("42"));
+    TestVar vc(43);
+
+    EXPECT_EQ(0u, va.index());
+    EXPECT_EQ(42, util::get<int>(va));
+
+    EXPECT_EQ(1u, vb.index());
+    EXPECT_EQ("42", util::get<std::string>(vb));
+
+    EXPECT_EQ(0u, vc.index());
+    EXPECT_EQ(43, util::get<int>(vc));
+
+    va = std::move(vb);
+    EXPECT_EQ(1u, va.index());
+    EXPECT_EQ("42", util::get<std::string>(va));
+
+    va = std::move(vc);
+    EXPECT_EQ(0u, va.index());
+    EXPECT_EQ(43, util::get<int>(va));
+}
+
+TEST(Variant, Swap_SameIndex)
+{
+    TestVar tv1(42);
+    TestVar tv2(43);
+
+    EXPECT_EQ(0u, tv1.index());
+    EXPECT_EQ(42, util::get<int>(tv1));
+
+    EXPECT_EQ(0u, tv2.index());
+    EXPECT_EQ(43, util::get<int>(tv2));
+
+    tv1.swap(tv2);
+
+    EXPECT_EQ(0u, tv1.index());
+    EXPECT_EQ(43, util::get<int>(tv1));
+
+    EXPECT_EQ(0u, tv2.index());
+    EXPECT_EQ(42, util::get<int>(tv2));
+}
+
+TEST(Variant, Swap_DiffIndex)
+{
+    TestVar2 tv1(42);
+    TestVar2 tv2(3.14f);
+
+    EXPECT_EQ(0u, tv1.index());
+    EXPECT_EQ(42, util::get<int>(tv1));
+
+    EXPECT_EQ(1u, tv2.index());
+    EXPECT_EQ(3.14f, util::get<float>(tv2));
+
+    tv1.swap(tv2);
+
+    EXPECT_EQ(0u, tv2.index());
+    EXPECT_EQ(42, util::get<int>(tv2));
+
+    EXPECT_EQ(1u, tv1.index());
+    EXPECT_EQ(3.14f, util::get<float>(tv1));
+}
+
+TEST(Variant, Get)
+{
+    const TestVar cv(42);
+
+    // Test const& get()
+    EXPECT_EQ(42, util::get<int>(cv));
+    EXPECT_THROW(util::get<std::string>(cv), util::bad_variant_access);
+
+    // Test &get
+    TestVar cv2(std::string("42"));
+    EXPECT_EQ("42", util::get<std::string>(cv2));
+    EXPECT_THROW(util::get<int>(cv2), util::bad_variant_access);
+}
+
+TEST(Variant, GetWrite)
+{
+    util::variant<int, std::string> v(42);
+    EXPECT_EQ(42, util::get<int>(v));
+
+    util::get<int>(v) = 43;
+    EXPECT_EQ(43, util::get<int>(v));
+}
+
+TEST(Variant, NoDefaultCtor)
+{
+    struct MyType
+    {
+        int m_a;
+        MyType() = delete;
+    };
+
+    // This code MUST compile
+    util::variant<int, MyType> var;
+    SUCCEED() << "Code compiled";
+
+    // At the same time, util::variant<MyType, ...> MUST NOT.
+}
+
+TEST(Variant, MonoState)
+{
+    struct MyType
+    {
+        int m_a;
+        explicit MyType(int a) : m_a(a) {}
+        MyType() = delete;
+    };
+
+    util::variant<util::monostate, MyType> var;
+    EXPECT_EQ(0u, var.index());
+
+    var = MyType{42};
+    EXPECT_EQ(1u, var.index());
+    EXPECT_EQ(42, util::get<MyType>(var).m_a);
+}
+
+
+TEST(Variant, Eq)
+{
+    TestVar v1(42), v2(std::string("42"));
+    TestVar v3(v1), v4(v2);
+
+    EXPECT_TRUE(v1 == v3);
+    EXPECT_TRUE(v2 == v4);
+    EXPECT_TRUE(v1 != v2);
+    EXPECT_TRUE(v3 != v4);
+
+    EXPECT_FALSE(v1 == v2);
+    EXPECT_FALSE(v3 == v4);
+    EXPECT_FALSE(v1 != v3);
+    EXPECT_FALSE(v2 != v4);
+}
+
+TEST(Variant, Eq_Monostate)
+{
+    using TestVar3 = util::variant<util::monostate, int>;
+    TestVar3 v1;
+    TestVar3 v2(42);
+
+    EXPECT_NE(v1, v2);
+
+    v2 = util::monostate{};
+    EXPECT_EQ(v1, v2);
+}
+
+TEST(Variant, VectorOfVariants)
+{
+    std::vector<TestVar> vv1(1024);
+    std::vector<TestVar> vv2(1024);
+
+    EXPECT_TRUE(vv1 == vv2);
+
+    std::vector<TestVar> vv3(2048, TestVar(std::string("42")));
+
+    // Just test chat the below code compiles:
+    // 1: internal copy of variants from one vector to another,
+    //    with probable reallocation of 1st vector to host all elements
+    std::copy(vv1.begin(), vv1.end(), std::back_inserter(vv2));
+    EXPECT_EQ(2048u, vv2.size());
+
+    // 2: truncation of vector, with probable destruction of its tail memory
+    vv2.resize(1024);
+    EXPECT_EQ(1024u, vv2.size());
+
+    // 3. vector assignment, with overwriting underlying variants
+    vv2 = vv3;
+    EXPECT_EQ(2048u, vv2.size());
+    EXPECT_TRUE(vv2 == vv3);
+}
+
+TEST(Variant, HoldsAlternative)
+{
+    TestVar v(42);
+    EXPECT_TRUE (util::holds_alternative<int>        (v));
+    EXPECT_FALSE(util::holds_alternative<std::string>(v));
+
+    v = std::string("42");
+    EXPECT_FALSE(util::holds_alternative<int>        (v));
+    EXPECT_TRUE (util::holds_alternative<std::string>(v));
+}
+
+TEST(Variant, Sizeof)
+{
+    //variant has to store index of the contained type as well as the type itself
+    EXPECT_EQ(2 * sizeof(size_t), (sizeof(util::variant<int, char>)));
+#if !defined(__GNUG__) || __GNUG__ >= 5
+    // GCC versions prior to 5.0 have limited C++11 support, e.g.
+    // no std::max_align_t defined
+    EXPECT_EQ((sizeof(std::max_align_t) + std::max(sizeof(size_t), alignof(std::max_align_t))), (sizeof(util::variant<std::max_align_t, char>)));
+#endif
+}
+
+TEST(Variant, EXT_IndexOf)
+{
+    struct MyType{};
+    class MyClass{};
+
+    using V = util::variant<util::monostate, int, double, char, float, MyType, MyClass>;
+    static_assert(0u == V::index_of<util::monostate>(), "Index is incorrect");
+    static_assert(1u == V::index_of<int    >(), "Index is incorrect");
+    static_assert(2u == V::index_of<double >(), "Index is incorrect");
+    static_assert(3u == V::index_of<char   >(), "Index is incorrect");
+    static_assert(4u == V::index_of<float  >(), "Index is incorrect");
+    static_assert(5u == V::index_of<MyType >(), "Index is incorrect");
+    static_assert(6u == V::index_of<MyClass>(), "Index is incorrect");
+}
+
+} // namespace opencv_test