diff options
author | Alexey Suhov <asuhov@users.noreply.github.com> | 2019-01-21 21:31:31 +0300 |
---|---|---|
committer | openvino-pushbot <44090433+openvino-pushbot@users.noreply.github.com> | 2019-01-21 21:31:31 +0300 |
commit | 9de27f16bc8b712a5b8c99d1d4b4a66c9144942d (patch) | |
tree | 01a383efe94d92b9870d513c2c5ea5d15b07010a /inference-engine/thirdparty/fluid | |
parent | fbc7a4a710c24def8ab199926a7da90a0394b87d (diff) | |
download | dldt-9de27f16bc8b712a5b8c99d1d4b4a66c9144942d.tar.gz dldt-9de27f16bc8b712a5b8c99d1d4b4a66c9144942d.tar.bz2 dldt-9de27f16bc8b712a5b8c99d1d4b4a66c9144942d.zip |
Publishing R5 content (#72)
* Publishing R5 content
* Updated ade revision
* updated readme
* add possibility to build CPU plugin with Intel MKL package
Diffstat (limited to 'inference-engine/thirdparty/fluid')
209 files changed, 43004 insertions, 0 deletions
diff --git a/inference-engine/thirdparty/fluid/README.md b/inference-engine/thirdparty/fluid/README.md new file mode 100644 index 000000000..3b31555e5 --- /dev/null +++ b/inference-engine/thirdparty/fluid/README.md @@ -0,0 +1,45 @@ +# OpenCV G-API (Fluid), standalone edition + +This subtree hosts sources of G-API - a new OpenCV module for +efficient image processing. G-API serves as a preprocessing vehicle +for Inference Engine. At the moment, only Fluid (CPU) backend is used. + +The sources are taken from OpenCV's [main repository](https://github.com/opencv). + +There are supplementary scripts which ease and verify the update +process. + +## Usage + +Updating to the latest `master`: + + ./update.sh + +Updating to a particular revision: + + ./update.sh COMMIT_HASH + +During update, this script checks if the source tree was modified +after the latest update. If it was, update fails -- we want to avoid +any diverge in the source so _no changes_ should be committed ever to +this copy of G-API. + +One can check manually if sources were diverged from its last "valid" +copy by running + + ./check.sh + +An error message and non-zero exit code indicate possible inconsitency +with this source copy. + +One updated, all changes will be automatically staged. + +## Files + +In addition to the source tree, the above two scripts maintain two +files: +- `revision.txt` -- the OpenCV's revision used to produce this source + copy. If the code was taken from `master`, a timestamp is stored + otherwise. +- `checksum.txt` -- latest valid copy's check sum. Don't update this + file manually. diff --git a/inference-engine/thirdparty/fluid/check.sh b/inference-engine/thirdparty/fluid/check.sh new file mode 100644 index 000000000..e222af611 --- /dev/null +++ b/inference-engine/thirdparty/fluid/check.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ ! -f modules ] && [ ! -f checksum.txt ]; then + exit 0 +fi + +THIS_HASH=$(./checksum.sh) +OLD_HASH=$(cat checksum.txt) + +if [ $THIS_HASH != $OLD_HASH ]; then + echo "Invalid checksum -- any changes were done to the source tree here?" + exit 1 +fi + +echo "Check done." diff --git a/inference-engine/thirdparty/fluid/checksum.sh b/inference-engine/thirdparty/fluid/checksum.sh new file mode 100644 index 000000000..3c8236177 --- /dev/null +++ b/inference-engine/thirdparty/fluid/checksum.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +find modules/ -type f -exec md5sum {} \; | sort -k 2 | md5sum | cut -d' ' -f 1 diff --git a/inference-engine/thirdparty/fluid/checksum.txt b/inference-engine/thirdparty/fluid/checksum.txt new file mode 100644 index 000000000..d912ec087 --- /dev/null +++ b/inference-engine/thirdparty/fluid/checksum.txt @@ -0,0 +1 @@ +5d28798fbe1b11d9c9d6fcd28c02f07e diff --git a/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt b/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt new file mode 100644 index 000000000..ec05b385c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt @@ -0,0 +1,112 @@ +# FIXME: Rework standalone build in more generic maner +# (Restructure directories, add common pass, etc) +if (NOT DEFINED OPENCV_INITIAL_PASS) + include("cmake/standalone.cmake") + return() +endif() + +# 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}/gpu/*.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/gfluidimgproc_func.dispatch.cpp + src/backends/fluid/gfluidcore.cpp + + # GPU Backend (currently built-in) + src/backends/gpu/ggpubackend.cpp + src/backends/gpu/ggpukernel.cpp + src/backends/gpu/ggpuimgproc.cpp + src/backends/gpu/ggpucore.cpp + + # Compound + src/backends/common/gcompoundbackend.cpp + src/backends/common/gcompoundkernel.cpp + ) + +ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2) + +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() +ocv_add_samples() diff --git a/inference-engine/thirdparty/fluid/modules/gapi/cmake/DownloadADE.cmake b/inference-engine/thirdparty/fluid/modules/gapi/cmake/DownloadADE.cmake new file mode 100644 index 000000000..8b6f1176f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/cmake/DownloadADE.cmake @@ -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.1d.zip") +set(ade_subdir "ade-0.1.1d") +set(ade_md5 "37479d90e3a5d47f132f512b22cbe206") +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/inference-engine/thirdparty/fluid/modules/gapi/cmake/init.cmake b/inference-engine/thirdparty/fluid/modules/gapi/cmake/init.cmake new file mode 100644 index 000000000..9f6ebeff4 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/cmake/init.cmake @@ -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/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake b/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake new file mode 100644 index 000000000..dd6e8cc00 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake @@ -0,0 +1,34 @@ +if (NOT TARGET ade ) + find_package(ade 0.1.0 REQUIRED) +endif() + +set(FLUID_TARGET fluid) +set(FLUID_ROOT "${CMAKE_CURRENT_LIST_DIR}/../") + +file(GLOB FLUID_includes "${FLUID_ROOT}/include/opencv2/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/g*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/util/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/own/*.hpp" + "${FLUID_ROOT}/include/opencv2/gapi/fluid/*.hpp") +file(GLOB FLUID_sources "${FLUID_ROOT}/src/api/g*.cpp" + "${FLUID_ROOT}/src/compiler/*.cpp" + "${FLUID_ROOT}/src/compiler/passes/*.cpp" + "${FLUID_ROOT}/src/executor/*.cpp" + "${FLUID_ROOT}/src/backends/fluid/*.cpp" + "${FLUID_ROOT}/src/backends/common/*.cpp") + +add_library(${FLUID_TARGET} STATIC ${FLUID_includes} ${FLUID_sources}) + +target_include_directories(${FLUID_TARGET} + PUBLIC $<BUILD_INTERFACE:${FLUID_ROOT}/include> + PRIVATE ${FLUID_ROOT}/src) + +target_compile_definitions(${FLUID_TARGET} PUBLIC -DGAPI_STANDALONE +# This preprocessor definition resolves symbol clash when +# standalone fluid meets gapi ocv module in one application + PUBLIC cv=fluidcv) + +set_target_properties(${FLUID_TARGET} PROPERTIES POSITION_INDEPENDENT_CODE True) +set_property(TARGET ${FLUID_TARGET} PROPERTY CXX_STANDARD 11) + +target_link_libraries(${FLUID_TARGET} PRIVATE ade) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/00-root.markdown b/inference-engine/thirdparty/fluid/modules/gapi/doc/00-root.markdown new file mode 100644 index 000000000..bbc90ab44 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/00-root.markdown @@ -0,0 +1,113 @@ +# Graph API {#gapi} + +# Introduction {#gapi_root_intro} + +OpenCV Graph API (or G-API) is a new OpenCV module targeted to make +regular image processing fast and portable. These two goals are +achieved by introducing a new graph-based model of execution. + +G-API is a special module in OpenCV -- in contrast with the majority +of other main modules, this one acts as a framework rather than some +specific CV algorithm. G-API provides means to define CV operations, +construct graphs (in form of expressions) using it, and finally +implement and run the operations for a particular backend. + +@note G-API is a new module and now is in active development. It's API +is volatile at the moment and there may be minor but +compatibility-breaking changes in the future. + +# Contents + +G-API documentation is organized into the following chapters: + +- @subpage gapi_purposes + + The motivation behind G-API and its goals. + +- @subpage gapi_hld + + General overview of G-API architecture and its major internal + components. + +- @subpage gapi_kernel_api + + Learn how to introduce new operations in G-API and implement it for + various backends. + +- @subpage gapi_impl + + Low-level implementation details of G-API, for those who want to + contribute. + +- API Reference: functions and classes + + - @subpage gapi_core + + Core G-API operations - arithmetic, boolean, and other matrix + operations; + + - @subpage gapi_imgproc + + Image processing functions: color space conversions, various + filters, etc. + +# API Example {#gapi_example} + +A very basic example of G-API pipeline is shown below: + +@include modules/gapi/samples/api_example.cpp + +<!-- TODO align this code with text using marks and itemized list --> + +G-API is a separate OpenCV module so its header files have to be +included explicitly. The first four lines of `main()` create and +initialize OpenCV's standard video capture object, which fetches +video frames from either an attached camera or a specified file. + +G-API pipelie is constructed next. In fact, it is a series of G-API +operation calls on cv::GMat data. The important aspect of G-API is +that this code block is just a declaration of actions, but not the +actions themselves. No processing happens at this point, G-API only +tracks which operations form pipeline and how it is connected. G-API +_Data objects_ (here it is cv::GMat) are used to connect operations +each other. `in` is an _empty_ cv::GMat signalling that it is a +beginning of computation. + +After G-API code is written, it is captured into a call graph with +instantiation of cv::GComputation object. This object takes +input/output data references (in this example, `in` and `out` +cv::GMat objects, respectively) as parameters and reconstructs the +call graph based on all the data flow between `in` and `out`. + +cv::GComputation is a thin object in sense that it just captures which +operations form up a computation. However, it can be used to execute +computations -- in the following processing loop, every captured frame (a +cv::Mat `input_frame`) is passed to cv::GComputation::apply(). + +![Example pipeline running on sample video 'vtest.avi'](pics/demo.jpg) + +cv::GComputation::apply() is a polimorphic method which accepts a +variadic number of arguments. Since this computation is defined on one +input, one output, a special overload of cv::GComputation::apply() is +used to pass input data and get output data. + +Internally, cv::GComputation::apply() compiles the captured graph for +the given input parameters and executes the compiled graph on data +immediately. + +There is a number important concepts can be outlines with this examle: +* Graph declaration and graph execution are distinct steps; +* Graph is built implicitly from a sequence of G-API expressions; +* G-API supports function-like calls -- e.g. cv::gapi::resize(), and + operators, e.g operator|() which is used to compute bitwise OR; +* G-API syntax aims to look pure: every operation call within a graph + yields a new result, thus forming a directed acyclic graph (DAG); +* Graph declaration is not bound to any data -- real data objects + (cv::Mat) come into picture after the graph is already declared. + +<!-- FIXME: The above operator|() link links to MatExpr not GAPI --> + +See [tutorials and porting examples](@ref tutorial_table_of_content_gapi) +to learn more on various G-API features and concepts. + +<!-- TODO Add chapter on declaration, compilation, execution --> diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/01-background.markdown b/inference-engine/thirdparty/fluid/modules/gapi/doc/01-background.markdown new file mode 100644 index 000000000..65983cd7a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/01-background.markdown @@ -0,0 +1,76 @@ +# Why Graph API? {#gapi_purposes} + +# Motivation behind G-API {#gapi_intro_why} + +G-API module brings graph-based model of execution to OpenCV. This +chapter briefly describes how this new model can help software +developers in two aspects: optimizing and porting image processing +algorithms. + +## Optimizing with Graph API {#gapi_intro_opt} + +Traditionally OpenCV provided a lot of stand-alone image processing +functions (see modules `core` and `imgproc`). Many of that functions +are well-optimized (e.g. vectorized for specific CPUs, parallel, etc) +but still the out-of-box optimization scope has been limited to a +single function only -- optimizing the whole algorithm built atop of that +functions was a responsibility of a programmer. + +OpenCV 3.0 introduced _Transparent API_ (or _T-API_) which allowed to +offload OpenCV function calls transparently to OpenCL devices and save +on Host/Device data transfers with cv::UMat -- and it was a great step +forward. However, T-API is a dynamic API -- user code still remains +unconstrained and OpenCL kernels are enqueued in arbitrary order, thus +eliminating further pipeline-level optimization potential. + +G-API brings implicit graph model to OpenCV 4.0. Graph model captures +all operations and its data dependencies in a pipeline and so provides +G-API framework with extra information to do pipeline-level +optimizations. + +The cornerstone of graph-based optimizations is _Tiling_. Tiling +allows to break the processing into smaller parts and reorganize +operations to enable data parallelism, improve data locality, and save +memory footprint. Data locality is an especially important aspect of +software optimization due to diffent costs of memory access on modern +computer architectures -- the more data is reused in the first level +cache, the more efficient pipeline is. + +Definitely the aforementioned techinques can be applied manually -- +but it requires extra skills and knowledge of the target platform and +the algorithm implementation changes irrevocably -- becoming more +specific, less flexible, and harder to extend and maintain. + +G-API takes this responsiblity and complexity from user and does the +majority of the work by itself, keeping the algorithm code clean from +device or optimization details. This approach has its own limitations, +though, as graph model is a _constrained_ model and not every +algorithm can be represented as a graph, so the G-API scope is limited +only to regular image processing -- various filters, arithmentic, +binary operations, and well-defined geometrical transformations. + +## Porting with Graph API {#gapi_intro_port} + +The essense of G-API is declaring a sequence of operations to run, and +then executing that sequence. G-API is a constrained API, so it puts a +number of limitations on which operations can form a pipeline and +which data these operations may exchange each other. + +This formalization in fact helps to make an algorithm portable. G-API +clearly separates operation _interfaces_ from its _implementations_. + +One operation (_kernel_) may have multiple implementations even for a +single device (e.g., OpenCV-based "reference" implementation and a +tiled optimized implementation, both running on CPU). Graphs (or +_Computations_ in G-API terms) are built only using operation +interfaces, not implementations -- thus the same graph can be executed +on different devices (and, of course, using different optimization +techniques) with little-to-no changes in the graph itself. + +G-API supports plugins (_Backends_) which aggreate logic and +intelligence on what is the best way to execute on a particular +platform. Once a pipeline is built with G-API, it can be parametrized +to use either of the backends (or a combination of it) and so a graph +can be ported easily to a new platform. + +@sa @ref gapi_hld diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/10-hld-overview.md b/inference-engine/thirdparty/fluid/modules/gapi/doc/10-hld-overview.md new file mode 100644 index 000000000..1dc5b505d --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/10-hld-overview.md @@ -0,0 +1,159 @@ +# High-level design overview {#gapi_hld} + +# G-API High-level design overview + +[TOC] + +G-API is a heterogeneous framework and provides single API to program +image processing pipelines with a number of supported backends. + +The key design idea is to keep pipeline code itself platform-neutral +while specifying which kernels to use and which devices to utilize +using extra parameters at graph compile (configuration) time. This +requirement has led to the following architecture: + +<!-- FIXME: Render from dot directly --> + +![G-API framework architecture](pics/gapi_scheme.png) + +There are three layers in this architecture: +* **API Layer** -- this is the top layer, which implements G-API + public interface, its building blocks and semantics. + When user constructs a pipeline with G-API, he interacts with this + layer directly, and the entities the user operates on (like cv::GMat + or cv::GComputation) are provided by this layer. +* **Graph Compiler Layer** -- this is the intermediate layer which + unrolls user computation into a graph and then applies a number of + transformations to it (e.g. optimizations). This layer is built atop + of [ADE Framework](@ref gapi_detail_ade). +* **Backends Layer** -- this is the lowest level layer, which lists a + number of _Backends_. In contrast with the above two layers, + backends are highly coupled with low-level platform details, with + every backend standing for every platform. A backend operates on a + processed graph (coming from the graph compiler) and executes this + graph optimally for a specific platform or device. + +# API layer {#gapi_api_layer} + +API layer is what user interacts with when defining and using a +pipeline (a Computation in G-API terms). API layer defines a set of +G-API _dynamic_ objects which can be used as inputs, outputs, and +intermediate data objects within a graph: +* cv::GMat +* cv::GScalar +* cv::GArray (template class) + +API layer specifies a list of Operations which are defined on these +data objects -- so called kernels. See G-API [core](@ref gapi_core) +and [imgproc](@ref gapi_imgproc) namespaces for details on which +operations G-API provides by default. + +G-API is not limited to these operations only -- users can define +their own kernels easily using a special macro G_TYPED_KERNEL(). + +API layer is also responsible for marshalling and storing operation +parameters on pipeline creation. In addition to the aforementioned +G-API dynamic objects, operations may also accept arbitrary +parameters (more on this [below](@ref gapi_detail_params)), so API +layer captures its values and stores internally upon the moment of +execution. + +Finally, cv::GComputation and cv::GCompiled are the remaining +important components of API layer. The former wraps a series of G-API +expressions into an object (graph), and the latter is a product of +graph _compilation_ (see [this chapter](@ref gapi_detail_compiler) for +details). + +# Graph compiler layer {#gapi_compiler} + +Every G-API computation is compiled before it executes. Compilation +process is triggered in two ways: +* _implicitly_, when cv::GComputation::apply() is used. In this case, + graph compilation is then immediately followed by execution. +* _explicitly_, when cv::GComputation::compile() is used. In this case, + a cv::GCompiled object is returned which then can be invoked as a + C++ functor. + +The first way is recommended for cases when input data format is not +known in advance -- e.g. when it comes from an arbitrary input file. +The second way is recommended for deployment (production) scenarios +where input data characteristics are usually predefined. + +Graph compilation process is built atop of ADE Framework. Initially, a +bipartite graph is generated from expressions captured by API layer. +This graph contains nodes of two types: _Data_ and _Operations_. Graph +always starts and ends with a Data node(s), with Operations nodes +in-between. Every Operation node has inputs and outputs, both are Data +nodes. + +After the initial graph is generated, it is actually processed by a +number of graph transformations, called _passes_. ADE Framework acts +as a compiler pass management engine, and passes are written +specifically for G-API. + +There are different passes which check graph validity, refine details +on operations and data, organize nodes into clusters ("Islands") based +on affinity or user-specified regioning[TBD], and more. Backends also +are able to inject backend-specific passes into the compilation +process, see more on this in the [dedicated chapter](@ref gapi_detail_meta). + +Result of graph compilation is a compiled object, represented by class +cv::GCompiled. A new cv::GCompiled object is always created regardless +if there was an explicit or implicit compilation request (see +above). Actual graph execution happens within cv::GCompiled and is +determined by backends which participated in the graph compilation. + +@sa cv::GComputation::apply(), cv::GComputation::compile(), cv::GCompiled + +# Backends layer {#gapi_backends} + +The above diagram lists two backends, _OpenCV_ and _Fluid_. _OpenCV_ +is so-called "reference backend", which implements G-API operations +using plain old OpenCV functions. This backend is useful for +prototyping on a familiar development system. _Fluid_ is a plugin for +cache-efficient execution on CPU -- it implements a different +execution policy and operates with its own, special kernels. Fluid +backend allows to achieve less memory footprint and better memory +locality when running on CPU. + +There may be more backends available, e.g. Halide, OpenCL, etc. -- +G-API provides an uniform internal API to develop backends so any +enthusiast or a company are free to scale G-API on a new platform or +accelerator. In terms of OpenCV infrastructure, every new backend is a +new distinct OpenCV module, which extends G-API when build as a part +of OpenCV. + +# Graph execution {#gapi_compiled} + +The way graph executed is defined by backends selected for +compilation. In fact, every backend builds its own execution script as +the final stage of graph compilation process, when an executable +(compiled) object is being generated. For example, in OpenCV backend, +this script is just a topologically-sorted sequence of OpenCV +functions to call; for Fluid backend, it is a similar thing -- a +topologically sorted list of _Agents_ processing lines of input on +every iteration. + +Graph execution is triggered in two ways: +* via cv::GComputation::apply(), with graph compiled in-place exactly + for the given input data; +* via cv::GCompiled::operator()(), when the graph has been precompiled. + +Both methods are polimorphic and take a variadic number of arguments, +with validity checks performed in runtime. If a number, shapes, and +formats of passed data objects differ from expected, a run-time +exception is thrown. G-API also provides _typed_ wrappers to move +these checks to the compile time -- see cv::GComputationT<>. + +G-API graph execution is declared stateless -- it means that a +compiled functor (cv::GCompiled) acts like a pure C++ function and +provides the same result for the same set of input arguments. + +Both execution methods take \f$N+M\f$ parameters, where \f$N\f$ is a +number of inputs, and \f$M\f$ is a number of outputs on which a +cv::GComputation is defined. Note that while G-API types (cv::GMat, +etc) are used in definition, the execution methods accept OpenCV's +traditional data types (like cv::Mat) which hold actual data -- see +table in [parameter marshalling](@#gapi_detail_params). + +@sa @ref gapi_impl, @ref gapi_kernel_api diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/20-kernel-api.markdown b/inference-engine/thirdparty/fluid/modules/gapi/doc/20-kernel-api.markdown new file mode 100644 index 000000000..93b852f6a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/20-kernel-api.markdown @@ -0,0 +1,170 @@ +# Kernel API {#gapi_kernel_api} + +[TOC] + +# G-API Kernel API + +The core idea behind G-API is portability -- a pipeline built with +G-API must be portable (or at least able to be portable). It means +that either it works out-of-the box when compiled for new platform, +_or_ G-API provides necessary tools to make it running there, with +little-to-no changes in the algorithm itself. + +This idea can be achieved by separating kernel interface from its +implementation. Once a pipeline is built using kernel interfaces, it +becomes implementation-neutral -- the implementation details +(i.e. which kernels to use) are passed on a separate stage (graph +compilation). + +Kernel-implementation hierarchy may look like: + +![Kernel API/implementation hierarchy example](pics/kernel_hierarchy.png) + +A pipeline itself then can be expressed only in terms of `A`, `B`, and +so on, and choosing which implementation to use in execution becomes +an external parameter. + +# Defining a kernel {#gapi_defining_kernel} + +G-API provides a macro to define a new kernel interface -- +G_TYPED_KERNEL(): + +@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_api + +This macro is a shortcut to a new type definition. It takes three +arguments to register a new type, and requires type body to be present +(see [below](@ref gapi_kernel_supp_info)). The macro arguments are: +1. Kernel interface name -- also serves as a name of new type defined + with this macro; +2. Kernel signature -- an `std::function<>`-like signature which defines + API of the kernel; +3. Kernel's unique name -- used to identify kernel when its type + informattion is stripped within the system. + +Kernel declaration may be seen as function declaration -- in both cases +a new entity must be used then according to the way it was defined. + +Kernel signature defines kernel's usage syntax -- which parameters +it takes during graph construction. Implementations can also use this +signature to derive it into backend-specific callback signatures (see +next chapter). + +Kernel may accept values of any type, and G-API _dynamic_ types are +handled in a special way. All other types are opaque to G-API and +passed to kernel in `outMeta()` or in execution callbacks as-is. + +Kernel's return value can _only_ be of G-API dynamic type -- cv::GMat, +cv::GScalar, or cv::GArray<T>. If an operation has more than one output, +it should be wrapped into an `std::tuple<>` (which can contain only +mentioned G-API types). Arbitrary-output-number operations are not +supported. + +Once a kernel is defined, it can be used in pipelines with special, +G-API-supplied method "::on()". This method has the same signature as +defined in kernel, so this code: + +@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_on + +is a perfectly legal construction. This example has some verbosity, +though, so usually a kernel declaration comes with a C++ function +wrapper ("factory method") which enables optional parameters, more +compact syntax, Doxygen comments, etc: + +@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap + +so now it can be used like: + +@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap_call + +# Extra information {#gapi_kernel_supp_info} + +In the current version, kernel declaration body (everything within the +curly braces) must contain a static function `outMeta()`. This function +establishes a functional dependency between operation's input and +output metadata. + +_Metadata_ is an information about data kernel operates on. Since +non-G-API types are opaque to G-API, G-API cares only about `G*` data +descriptors (i.e. dimensions and format of cv::GMat, etc). + +`outMeta()` is also an example of how kernel's signature can be +transformed into a derived callback -- note that in this example, +`outMeta()` signature exactly follows the kernel signature (defined +within the macro) but is different -- where kernel expects cv::GMat, +`outMeta()` takes and returns cv::GMatDesc (a G-API structure metadata +for cv::GMat). + +The point of `outMeta()` is to propagate metadata information within +computation from inputs to outputs and infer metadata of internal +(intermediate, temporary) data objects. This information is required +for further pipeline optimizations, memory allocation, and other +operations done by G-API framework during graph compilation. + +<!-- TODO add examples --> + +# Implementing a kernel {#gapi_kernel_implementing} + +Once a kernel is declared, its interface can be used to implement +versions of this kernel in different backends. This concept is +naturally projected from object-oriented programming +"Interface/Implementation" idiom: an interface can be implemented +multiple times, and different implementations of a kernel should be +substitutable with each other without breaking the algorithm +(pipeline) logic (Liskov Substitution Principle). + +Every backend defines its own way to implement a kernel interface. +This way is regular, though -- whatever plugin is, its kernel +implementation must be "derived" from a kernel interface type. + +Kernel implementation are then organized into _kernel +packages_. Kernel packages are passed to cv::GComputation::compile() +as compile arguments, with some hints to G-API on how to select proper +kernels (see more on this in "Heterogeneity"[TBD]). + +For example, the aforementioned `Filter2D` is implemented in +"reference" CPU (OpenCV) plugin this way (*NOTE* -- this is a +simplified form with improper border handling): + +@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_ocv + +Note how CPU (OpenCV) plugin has transformed the original kernel +signature: +- Input cv::GMat has been substituted with cv::Mat, holding actual input + data for the underlying OpenCV function call; +- Output cv::GMat has been transformed into extra output parameter, thus + `GCPUFilter2D::run()` takes one argument more than the original + kernel signature. + +The basic intuition for kernel developer here is _not to care_ where +that cv::Mat objects come from instead of the original cv::GMat -- and +just follow the signature conventions defined by the plugin. G-API +will call this method during execution and supply all the necessary +information (and forward the original opaque data as-is). + +# Compound kernels + +Sometimes kernel is a single thing only on API level. It is convenient +for users, but on a particular implementation side it would be better to +have multiple kernels (a subgraph) doing the thing instead. An example +is goodFeaturesToTrack() -- while in OpenCV backend it may remain a +single kernel, with Fluid it becomes compound -- Fluid can handle Harris +response calculation but can't do sparse non-maxima suppression and +point extraction to an STL vector: + +<!-- PIC --> + +A compound kernel _implementation_ can be defined using a generic +macro GAPI_COMPOUND_KERNEL(): + +@snippet modules/gapi/samples/kernel_api_snippets.cpp compound + +<!-- TODO: ADD on how Compound kernels may simplify dispatching --> +<!-- TODO: Add details on when expand() is called! --> + +It is important to distinguish a compound kernel from G-API high-order +function, i.e. a C++ function which looks like a kernel but in fact +generates a subgraph. The core difference is that a compound kernel is +an _implementation detail_ and a kernel implementation may be either +compound or not (depending on backend capabilities), while a +high-order function is a "macro" in terms of G-API and so cannot act as +an interface which then needs to be implemented by a backend. diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/30-implementation.markdown b/inference-engine/thirdparty/fluid/modules/gapi/doc/30-implementation.markdown new file mode 100644 index 000000000..b1ad3bae9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/30-implementation.markdown @@ -0,0 +1,27 @@ +# Implementation details {#gapi_impl} + +[TOC] + +# G-API Implementation details {#gapi_impl_header} + +## Api layer details {#gapi_detail_api} + +### Expression unrolling {#gapi_detail_expr} + +### Parameter marshalling {#gapi_detail_params} + +### Operations representation {#gapi_detail_operations} + +## Graph compiler details {#gapi_detail_compiler} + +### ADE basics {#gapi_detail_ade} + +### Graph model representation {#gapi_detail_gmodel} + +### G-API metadata and passes {#gapi_detail_meta} + +## Backends details {#gapi_detail_backends} + +### Backend scope of work {#gapi_backend_scope} + +### Graph transformation {#gapi_backend_pass} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/dot/kernel_hierarchy.dot b/inference-engine/thirdparty/fluid/modules/gapi/doc/dot/kernel_hierarchy.dot new file mode 100644 index 000000000..0eb92bc40 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/dot/kernel_hierarchy.dot @@ -0,0 +1,17 @@ +digraph { + rankdir=BT; + node [shape=record]; + + ki_a [label="{<f0> interface\nA}"]; + ki_b [label="{<f0> interface\nB}"]; + + {rank=same; ki_a ki_b}; + + "CPU::A" -> ki_a [dir="forward"]; + "OpenCL::A" -> ki_a [dir="forward"]; + "Halide::A" -> ki_a [dir="forward"]; + + "CPU::B" -> ki_b [dir="forward"]; + "OpenCL::B" -> ki_b [dir="forward"]; + "Halide::B" -> ki_b [dir="forward"]; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/demo.jpg b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/demo.jpg Binary files differnew file mode 100644 index 000000000..742d135f7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/demo.jpg diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/gapi_scheme.png b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/gapi_scheme.png Binary files differnew file mode 100644 index 000000000..24271e322 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/gapi_scheme.png diff --git a/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/kernel_hierarchy.png b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/kernel_hierarchy.png Binary files differnew file mode 100644 index 000000000..631f4a10d --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/doc/pics/kernel_hierarchy.png diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp new file mode 100644 index 000000000..a043a83fc --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp @@ -0,0 +1,33 @@ +// 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> + +/** \defgroup gapi G-API framework +@{ + @defgroup gapi_main_classes G-API Main Classes + @defgroup gapi_data_objects G-API Data Objects + @{ + @defgroup gapi_meta_args G-API Metadata Descriptors + @} + @defgroup gapi_std_backends G-API Standard backends + @defgroup gapi_compile_args G-API Graph Compilation Arguments +@} + */ + +#include "opencv2/gapi/gmat.hpp" +#include "opencv2/gapi/garray.hpp" +#include "opencv2/gapi/gcomputation.hpp" +#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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp new file mode 100644 index 000000000..9af3620fe --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp @@ -0,0 +1,1600 @@ +// 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(GPhase, <GMat(GMat, GMat, bool)>, "org.opencv.core.math.phase") { + static GMatDesc outMeta(const GMatDesc &inx, const GMatDesc &, bool) { + return inx; + } + }; + + 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); + } + }; + + G_TYPED_KERNEL(GSqrt, <GMat(GMat)>, "org.opencv.core.math.sqrt") { + static GMatDesc outMeta(GMatDesc in) { + return in; + } + }; +} + +//! @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 = 1.0, 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); + +/** @brief Calculates the rotation angle of 2D vectors. + +The function cv::phase calculates the rotation angle of each 2D vector that +is formed from the corresponding elements of x and y : +\f[\texttt{angle} (I) = \texttt{atan2} ( \texttt{y} (I), \texttt{x} (I))\f] + +The angle estimation accuracy is about 0.3 degrees. When x(I)=y(I)=0 , +the corresponding angle(I) is set to 0. +@param x input floating-point array of x-coordinates of 2D vectors. +@param y input array of y-coordinates of 2D vectors; it must have the +same size and the same type as x. +@param angleInDegrees when true, the function calculates the angle in +degrees, otherwise, they are measured in radians. +@return array of vector angles; it has the same size and same type as x. +*/ +GAPI_EXPORTS GMat phase(const GMat& x, const GMat &y, bool angleInDegrees = false); + +/** @brief Calculates a square root of array elements. + +The function cv::gapi::sqrt calculates a square root of each input array element. +In case of multi-channel arrays, each channel is processed +independently. The accuracy is approximately the same as of the built-in +std::sqrt . +@param src input floating-point array. +@return output array of the same size and type as src. +*/ +GAPI_EXPORTS GMat sqrt(const GMat &src); + +//! @} 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp new file mode 100644 index 000000000..ec76fe5d5 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp new file mode 100644 index 000000000..facaab6aa --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -0,0 +1,266 @@ +// 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 <functional> +#include <unordered_map> +#include <utility> +#include <vector> + +#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 +{ + /** + * \addtogroup gapi_std_backends + * @{ + * + * @brief G-API backends available in this OpenCV version + * + * G-API backends play a corner stone role in G-API execution + * stack. Every backend is hardware-oriented and thus can run its + * kernels efficiently on the target platform. + * + * Backends are usually "back boxes" for G-API users -- on the API + * side, all backends are represented as different objects of the + * same class cv::gapi::GBackend. User can manipulate with backends + * mainly by specifying which kernels to use or where to look up + * for kernels first. + * + * @sa @ref gapi_hld, cv::gapi::lookup_order() + */ + + /** + * @brief Get a reference to CPU (OpenCV) backend. + * + * This is the default backend in G-API at the moment, providing + * broader functional coverage but losing some graph model + * advantages. Provided mostly for reference and prototyping + * purposes. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ +} // namespace cpu +} // namespace gapi + +// 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp new file mode 100644 index 000000000..0b96db08a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/imgproc.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/core.hpp new file mode 100644 index 000000000..8c21f5760 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/core.hpp @@ -0,0 +1,20 @@ +// 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_CORE_HPP +#define OPENCV_GAPI_FLUID_CORE_HPP + +#include <opencv2/gapi/gkernel.hpp> // GKernelPackage +#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS + +namespace cv { namespace gapi { namespace core { namespace fluid { + +GAPI_EXPORTS GKernelPackage kernels(); + +}}}} + +#endif // OPENCV_GAPI_FLUID_CORE_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp new file mode 100644 index 000000000..8965ec75b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp @@ -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_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/own/mat.hpp> +#include <opencv2/gapi/gmat.hpp> + +#include "opencv2/gapi/util/optional.hpp" +#include "opencv2/gapi/own/scalar.hpp" +#include "opencv2/gapi/own/mat.hpp" + +namespace cv { +namespace gapi { +namespace fluid { + +struct Border +{ +#if !defined(GAPI_STANDALONE) + // This constructor is required to support existing kernels which are part of G-API + Border(int _type, cv::Scalar _val) : type(_type), value(to_own(_val)) {}; +#endif // !defined(GAPI_STANDALONE) + 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: + struct Cache + { + std::vector<const uint8_t*> m_linePtrs; + GMatDesc m_desc; + int m_border_size = 0; + + inline const uint8_t* linePtr(int index) const + { + return m_linePtrs[index + m_border_size]; + } + }; + + View() = default; + + const inline uint8_t* InLineB(int index) const // -(w-1)/2...0...+(w-1)/2 for Filters + { + return m_cache->linePtr(index); + } + + template<typename T> const inline T* InLine(int i) const + { + const uint8_t* ptr = this->InLineB(i); + return reinterpret_cast<const T*>(ptr); + } + + inline operator bool() const { return m_priv != nullptr; } + bool ready() const; + inline int length() const { return m_cache->m_desc.size.width; } + int y() const; + + inline const GMatDesc& meta() const { return m_cache->m_desc; } + + 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; + const Cache* m_cache; +}; + +class GAPI_EXPORTS Buffer +{ +public: + struct Cache + { + std::vector<uint8_t*> m_linePtrs; + GMatDesc m_desc; + }; + + // 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::gapi::own::Mat &data, bool is_input); + + inline uint8_t* OutLineB(int index = 0) + { + return m_cache->m_linePtrs[index]; + } + + template<typename T> inline 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; + inline int length() const { return m_cache->m_desc.size.width; } + int lpi() const; // LPI for WRITER + + inline const GMatDesc& meta() const { return m_cache->m_desc; } + + View mkView(int borderSize, 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; + const Cache* m_cache; +}; + +} // namespace cv::gapi::fluid +} // namespace cv::gapi +} // namespace cv + +#endif // OPENCV_GAPI_FLUID_BUFFER_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp new file mode 100644 index 000000000..c71c5aa2c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -0,0 +1,302 @@ +// 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 +{ + /** + * \addtogroup gapi_std_backends G-API Standard backends + * @{ + */ + /** + * @brief Get a reference to Fluid backend. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ +} // namespace flud +} // namespace gapi + + +class GAPI_EXPORTS GFluidKernel +{ +public: + 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].unsafe_get<cv::gapi::fluid::View>(); + } +}; + +template<> struct fluid_get_in<cv::GScalar> +{ + // FIXME: change to return by reference when moved to own::Scalar +#if !defined(GAPI_STANDALONE) + static const cv::Scalar get(const cv::GArgs &in_args, int idx) + { + return cv::gapi::own::to_ocv(in_args[idx].unsafe_get<cv::gapi::own::Scalar>()); + } +#else + static const cv::gapi::own::Scalar get(const cv::GArgs &in_args, int idx) + { + return in_args[idx].get<cv::gapi::own::Scalar>(); + } +#endif // !defined(GAPI_STANDALONE) +}; +template<class T> struct fluid_get_in +{ + static const T& get(const cv::GArgs &in_args, int idx) + { + return in_args[idx].unsafe_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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp new file mode 100644 index 000000000..dedfa9dbe --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/imgproc.hpp @@ -0,0 +1,20 @@ +// 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_IMGPROC_HPP +#define OPENCV_GAPI_FLUID_IMGPROC_HPP + +#include <opencv2/gapi/gkernel.hpp> // GKernelPackage +#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS + +namespace cv { namespace gapi { namespace imgproc { namespace fluid { + +GAPI_EXPORTS GKernelPackage kernels(); + +}}}} + +#endif // OPENCV_GAPI_FLUID_IMGPROC_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp new file mode 100644 index 000000000..f8a317006 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp @@ -0,0 +1,126 @@ +// 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> inline T& get() + { + return util::any_cast<typename std::remove_reference<T>::type>(value); + } + + template<typename T> inline const T& get() const + { + return util::any_cast<typename std::remove_reference<T>::type>(value); + } + + template<typename T> inline T& unsafe_get() + { + return util::unsafe_any_cast<typename std::remove_reference<T>::type>(value); + } + + template<typename T> inline const T& unsafe_get() const + { + return util::unsafe_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< +#if !defined(GAPI_STANDALONE) + cv::Mat, + cv::Scalar, + cv::UMat, +#endif // !defined(GAPI_STANDALONE) + cv::gapi::own::Mat, + cv::gapi::own::Scalar, + cv::detail::VectorRef + >; +using GRunArgs = std::vector<GRunArg>; + +using GRunArgP = util::variant< +#if !defined(GAPI_STANDALONE) + cv::Mat*, + cv::Scalar*, + cv::UMat*, +#endif // !defined(GAPI_STANDALONE) + cv::gapi::own::Mat*, + 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp new file mode 100644 index 000000000..87d00155b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp @@ -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_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; + +/** + * \addtogroup gapi_meta_args + * @{ + */ +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 {};} +static 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 + +/** \addtogroup gapi_data_objects + * @{ + */ + +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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp new file mode 100644 index 000000000..baf4f44e2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp new file mode 100644 index 000000000..6a3f51f77 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -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 + + +#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? +/** \addtogroup gapi_compile_args + * @{ + * + * @brief Compilation arguments: a set of data structures which can be + * passed to control compilation process + * + * G-API comes with a number of graph compilation options which can be + * passed to cv::GComputation::apply() or + * cv::GComputation::compile(). Known compilation options are listed + * in this page, while extra backends may introduce their own + * compilation options (G-API transparently accepts _everything_ which + * can be passed to cv::compile_args(), it depends on underlying + * backends if an option would be interpreted or not). + * + * For example, if an example computation is executed like this: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_decl_apply + * + * Extra parameter specifying which kernels to compile with can be + * passed like this: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp apply_with_param + */ + +/** + * @brief Represents an arbitrary compilation argument. + * + * Any value can be wrapped into cv::GCompileArg, but only known ones + * (to G-API or its backends) can be interpreted correctly. + * + * Normally objects of this class shouldn't be created manually, use + * cv::compile_args() function which automatically wraps everything + * passed in (a variadic template parameter pack) into a vector of + * cv::GCompileArg objects. + */ +struct GAPI_EXPORTS GCompileArg +{ +public: + 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>; + +/** + * Wraps a list of arguments (a parameter pack) into a vector of + * compilation arguments (cv::GCompileArg). + */ +template<typename... Ts> GCompileArgs compile_args(Ts&&... args) +{ + return GCompileArgs{ GCompileArg(args)... }; +} + +/** + * @brief Ask G-API to dump compiled graph in Graphviz format under + * the given file name. + * + * Specifies a graph dump path (path to .dot file to be generated). + * G-API will dump a .dot file under specified path during a + * compilation process if this flag is passed. + */ +struct graph_dump_path +{ + std::string m_dump_path; +}; +/** @} */ + +namespace detail +{ + 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp new file mode 100644 index 000000000..ad491b733 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp @@ -0,0 +1,217 @@ +// 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/opencv_includes.hpp" +#include "opencv2/gapi/own/assert.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). + +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @brief Represents a compiled computation (graph). Can only be used + * with image / data formats & resolutions it was compiled for, with + * some exceptions. + * + * This class represents a product of graph compilation (calling + * cv::GComputation::compile()). Objects of this class actually do + * data processing, and graph execution is incapsulated into objects + * of this class. Execution model itself depends on kernels and + * backends which were using during the compilation, see @ref + * gapi_compile_args for details. + * + * In a general case, GCompiled objects can be applied to data only in + * that formats/resolutions they were compiled for (see @ref + * gapi_meta_args). However, if the underlying backends allow, a + * compiled object can be _reshaped_ to handle data (images) of + * different resolution, though formats and types must remain the same. + * + * GCompiled is very similar to `std::function<>` in its semantics -- + * running it looks like a function call in the user code. + * + * At the moment, GCompiled objects are not reentrant -- generally, + * the objects are stateful since graph execution itself is a stateful + * process and this state is now maintained in GCompiled's own memory + * (not on the process stack). + * + * At the same time, two different GCompiled objects produced from the + * single cv::GComputation are completely independent and can be used + * concurrently. + */ +class GAPI_EXPORTS GCompiled +{ +public: + /// @private + class GAPI_EXPORTS Priv; + + /** + * @brief Constructs an empty object + */ + GCompiled(); + + /** + * @brief Run the compiled computation, a generic version. + * + * @param ins vector of inputs to process. + * @param outs vector of outputs to produce. + * + * Input/output vectors must have the same number of elements as + * defined in the cv::GComputation protocol (at the moment of its + * construction). Shapes of elements also must conform to protocol + * (e.g. cv::Mat needs to be passed where cv::GMat has been + * declared as input, and so on). Run-time exception is generated + * otherwise. + * + * Objects in output vector may remain empty (like cv::Mat) -- + * G-API will automatically initialize output objects to proper formats. + * + * @note Don't construct GRunArgs/GRunArgsP objects manually, use + * cv::gin()/cv::gout() wrappers instead. + */ + void operator() (GRunArgs &&ins, GRunArgsP &&outs); // Generic arg-to-arg +#if !defined(GAPI_STANDALONE) + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Mat for unary computation + * process. + */ + void operator() (cv::Mat in, cv::Mat &out); // Unary overload + + /** + * @brief Execute an unary computation + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Scalar for unary computation + * process. + */ + void operator() (cv::Mat in, cv::Scalar &out); // Unary overload (scalar) + + /** + * @brief Execute a binary computation + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Mat for binary computation + * process. + */ + void operator() (cv::Mat in1, cv::Mat in2, cv::Mat &out); // Binary overload + + /** + * @brief Execute an binary computation + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Scalar for binary computation + * process. + */ + void operator() (cv::Mat in1, cv::Mat in2, cv::Scalar &out); // Binary overload (scalar) + + /** + * @brief Execute a computation with arbitrary number of + * inputs/outputs. + * + * @overload + * @param ins vector of input cv::Mat objects to process by the + * computation. + * @param outs vector of output cv::Mat objects to produce by the + * computation. + * + * Numbers of elements in ins/outs vectos must match numbers of + * inputs/outputs which were used to define the source GComputation. + */ + void operator() (const std::vector<cv::Mat> &ins, // Compatibility overload + const std::vector<cv::Mat> &outs); +#endif // !defined(GAPI_STANDALONE) + /// @private + Priv& priv(); + + /** + * @brief Check if compiled object is valid (non-empty) + * + * @return true if the object is runnable (valid), false otherwise + */ + explicit operator bool () const; + + /** + * @brief Vector of metadata this graph was compiled for. + * + * @return Unless _reshape_ is not supported, return value is the + * same vector which was passed to cv::GComputation::compile() to + * produce this compiled object. Otherwise, it is the latest + * metadata vector passed to reshape() (if that call was + * successful). + */ + const GMetaArgs& metas() const; // Meta passed to compile() + + /** + * @brief Vector of metadata descriptions of graph outputs + * + * @return vector with formats/resolutions of graph's output + * objects, auto-inferred from input metadata vector by + * operations which form this computation. + * + * @note GCompiled objects produced from the same + * cv::GComputiation graph with different input metas may return + * different values in this vector. + */ + const GMetaArgs& outMetas() const; + + /** + * @brief Check if the underlying backends support reshape or not. + * + * @return true if supported, false otherwise. + */ + bool canReshape() const; + + /** + * @brief Reshape a compiled graph to support new image + * resolutions. + * + * Throws an exception if an error occurs. + * + * @param inMetas new metadata to reshape on. Vector size and + * metadata shapes must match the computation's protocol. + * @param args compilation arguments to use. + */ + // FIXME: Why it requires compile args? + void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); + +protected: + /// @private + std::shared_ptr<Priv> m_priv; +}; +/** @} */ + +} + +#endif // OPENCV_GAPI_GCOMPILED_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp new file mode 100644 index 000000000..c5ac8a7d2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp new file mode 100644 index 000000000..e89b9ae39 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp @@ -0,0 +1,456 @@ +// 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; +} + +/** + * \addtogroup gapi_main_classes + * @{ + */ +/** + * @brief GComputation class represents a captured computation + * graph. GComputation objects form boundaries for expression code + * user writes with G-API, allowing to compile and execute it. + * + * G-API computations are defined with input/output data + * objects. G-API will track automatically which operations connect + * specified outputs to the inputs, forming up a call graph to be + * executed. The below example expresses calculation of Sobel operator + * for edge detection (\f$G = \sqrt{G_x^2 + G_y^2}\f$): + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_def + * + * Full pipeline can be now captured with this object declaration: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_full + * + * Input/output data objects on which a call graph should be + * reconstructed are passed using special wrappers cv::GIn and + * cv::GOut. G-API will track automatically which operations form a + * path from inputs to outputs and build the execution graph appropriately. + * + * Note that cv::GComputation doesn't take ownership on data objects + * it is defined. Moreover, multiple GComputation objects may be + * defined on the same expressions, e.g. a smaller pipeline which + * expects that image gradients are already pre-calculated may be + * defined like this: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_sub + * + * The resulting graph would expect two inputs and produce one + * output. In this case, it doesn't matter if gx/gy data objects are + * results of cv::gapi::Sobel operators -- G-API will stop unrolling + * expressions and building the underlying graph one reaching this + * data objects. + * + * The way how GComputation is defined is important as its definition + * specifies graph _protocol_ -- the way how the graph should be + * used. Protocol is defined by number of inputs, number of outputs, + * and shapes of inputs and outputs. + * + * In the above example, sobelEdge expects one Mat on input and + * produces one Mat; while sobelEdgeSub expects two Mats on input and + * produces one Mat. GComputation's protocol defines how other + * computaion methods should be used -- cv::GComputation::compile() and + * cv::GComputation::apply(). For example, if a graph is defined on + * two GMat inputs, two cv::Mat objects have to be passed to apply() + * for execution. GComputation checks protocol correctness in runtime + * so passing a different number of objects in apply() or passing + * cv::Scalar instead of cv::Mat there would compile well as a C++ + * source but raise an exception in run-time. G-API also comes with a + * typed wrapper cv::GComputationT<> which introduces this type-checking in + * compile-time. + * + * cv::GComputation itself is a thin object which just captures what + * the graph is. The compiled graph (which actually process data) is + * represented by class GCompiled. Use compile() method to generate a + * compiled graph with given compile options. cv::GComputation can + * also be used to process data with implicit graph compilation + * on-the-fly, see apply() for details. + * + * GComputation is a reference-counted object -- once defined, all its + * copies will refer to the same instance. + * + * @sa GCompiled + */ +class GAPI_EXPORTS GComputation +{ +public: + class Priv; + typedef std::function<GComputation()> Generator; + + // Various constructors enable different ways to define a computation: ///// + // 1. Generic constructors + /** + * @brief Define a computation using a generator function. + * + * Graph can be defined in-place directly at the moment of its + * construction with a lambda: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp graph_gen + * + * This may be useful since all temporary objects (cv::GMats) and + * namespaces can be localized to scope of lambda, without + * contaminating the parent scope with probably unecessary objects + * and information. + * + * @param gen generator function which returns a cv::GComputation, + * see Generator. + */ + GComputation(const Generator& gen); // Generator + // overload + + /** + * @brief Generic GComputation constructor. + * + * Constructs a new graph with a given protocol, specified as a + * flow of operations connecting input/output objects. Throws if + * the passed boundaries are invalid, e.g. if there's no + * functional dependency (path) between given outputs and inputs. + * + * @param ins Input data vector. + * @param outs Output data vector. + * + * @note Don't construct GProtoInputArgs/GProtoOutputArgs objects + * directly, use cv::GIn()/cv::GOut() wrapper functions instead. + * + * @sa @ref gapi_data_objects + */ + GComputation(GProtoInputArgs &&ins, + GProtoOutputArgs &&outs); // Arg-to-arg overload + + // 2. Syntax sugar and compatibility overloads + /** + * @brief Defines an unary (one input -- one output) computation + * + * @overload + * @param in input GMat of the defined unary computation + * @param out output GMat of the defined unary computation + */ + GComputation(GMat in, GMat out); // Unary overload + + /** + * @brief Defines an unary (one input -- one output) computation + * + * @overload + * @param in input GMat of the defined unary computation + * @param out output GScalar of the defined unary computation + */ + GComputation(GMat in, GScalar out); // Unary overload (scalar) + + /** + * @brief Defines a binary (two inputs -- one output) computation + * + * @overload + * @param in1 first input GMat of the defined binary computation + * @param in2 second input GMat of the defined binary computation + * @param out output GMat of the defined binary computation + */ + GComputation(GMat in1, GMat in2, GMat out); // Binary overload + + /** + * @brief Defines a binary (two inputs -- one output) computation + * + * @overload + * @param in1 first input GMat of the defined binary computation + * @param in2 second input GMat of the defined binary computation + * @param out output GScalar of the defined binary computation + */ + GComputation(GMat in1, GMat in2, GScalar out); // Binary + // overload + // (scalar) + + /** + * @brief Defines a computation with arbitrary input/output number. + * + * @overload + * @param ins vector of inputs GMats for this computation + * @param outs vector of outputs GMats for this computation + * + * Use this overload for cases when number of computation + * inputs/outputs is not known in compile-time -- e.g. when graph + * is programmatically generated to build an image pyramid with + * the given number of levels, etc. + */ + GComputation(const std::vector<GMat> &ins, // Compatibility overload + const std::vector<GMat> &outs); + + // Various versions of apply(): //////////////////////////////////////////// + // 1. Generic apply() + /** + * @brief Compile graph on-the-fly and immediately execute it on + * the inputs data vectors. + * + * Number of input/output data objects must match GComputation's + * protocol, also types of host data objects (cv::Mat, cv::Scalar) + * must match the shapes of data objects from protocol (cv::GMat, + * cv::GScalar). If there's a mismatch, a run-time exception will + * be generated. + * + * Internally, a cv::GCompiled object is created for the given + * input format configuration, which then is executed on the input + * data immediately. cv::GComputation caches compiled objects + * produced within apply() -- if this method would be called next + * time with the same input parameters (image formats, image + * resolution, etc), the underlying compiled graph will be reused + * without recompilation. If new metadata doesn't match the cached + * one, the underlying compiled graph is regenerated. + * + * @note compile() always triggers a compilation process and + * produces a new GCompiled object regardless if a similar one has + * been cached via apply() or not. + * + * @param ins vector of input data to process. Don't create + * GRunArgs object manually, use cv::gin() wrapper instead. + * @param outs vector of output data to fill results in. cv::Mat + * objects may be empty in this vector, G-API will automatically + * initialize it with the required format & dimensions. Don't + * create GRunArgsP object manually, use cv::gout() wrapper instead. + * @param args a list of compilation arguments to pass to the + * underlying compilation process. Don't create GCompileArgs + * object manually, use cv::compile_args() wrapper instead. + * + * @sa @ref gapi_data_objects, @ref gapi_compile_args + */ + void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); // Arg-to-arg overload + + /// @private -- Exclude this function from OpenCV documentation + void apply(const std::vector<cv::gapi::own::Mat>& ins, // Compatibility overload + const std::vector<cv::gapi::own::Mat>& outs, + GCompileArgs &&args = {}); + + // 2. Syntax sugar and compatibility overloads +#if !defined(GAPI_STANDALONE) + /** + * @brief Execute an unary computation (with compilation on the fly) + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Mat for unary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in, cv::Mat &out, GCompileArgs &&args = {}); // Unary overload + + /** + * @brief Execute an unary computation (with compilation on the fly) + * + * @overload + * @param in input cv::Mat for unary computation + * @param out output cv::Scalar for unary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in, cv::Scalar &out, GCompileArgs &&args = {}); // Unary overload (scalar) + + /** + * @brief Execute a binary computation (with compilation on the fly) + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Mat for binary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in1, cv::Mat in2, cv::Mat &out, GCompileArgs &&args = {}); // Binary overload + + /** + * @brief Execute an binary computation (with compilation on the fly) + * + * @overload + * @param in1 first input cv::Mat for binary computation + * @param in2 second input cv::Mat for binary computation + * @param out output cv::Scalar for binary computation + * @param args compilation arguments for underlying compilation + * process. + */ + void apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompileArgs &&args = {}); // Binary overload (scalar) + + /** + * @brief Execute a computation with arbitrary number of + * inputs/outputs (with compilation on-the-fly). + * + * @overload + * @param ins vector of input cv::Mat objects to process by the + * computation. + * @param outs vector of output cv::Mat objects to produce by the + * computation. + * @param args compilation arguments for underlying compilation + * process. + * + * Numbers of elements in ins/outs vectos must match numbers of + * inputs/outputs which were used to define this GComputation. + */ + void apply(const std::vector<cv::Mat>& ins, // Compatibility overload + const std::vector<cv::Mat>& outs, + GCompileArgs &&args = {}); +#endif // !defined(GAPI_STANDALONE) + // Various versions of compile(): ////////////////////////////////////////// + // 1. Generic compile() - requires metas to be passed as vector + /** + * @brief Compile the computation for specific input format(s). + * + * This method triggers compilation process and produces a new + * GCompiled object which then can process data of the given + * format. Passing data with different format to the compiled + * computation will generate a run-time exception. + * + * @param in_metas vector of input metadata configuration. Grab + * metadata from real data objects (like cv::Mat or cv::Scalar) + * using cv::descr_of(), or create it on your own. + * @param args compilation arguments for this compilation + * process. Compilation arguments directly affect what kind of + * executable object would be produced, e.g. which kernels (and + * thus, devices) would be used to execute computation. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + * + * @sa @ref gapi_compile_args + */ + GCompiled compile(GMetaArgs &&in_metas, GCompileArgs &&args = {}); + + // 2. Syntax sugar - variadic list of metas, no extra compile args + // FIXME: SFINAE looks ugly in the generated documentation + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + */ + template<typename... Ts> + auto compile(const Ts&... metas) -> + typename std::enable_if<detail::are_meta_descrs<Ts...>::value, GCompiled>::type + { + return compile(GMetaArgs{GMetaArg(metas)...}, GCompileArgs()); + } + + // 3. Syntax sugar - variadic list of metas, extra compile args + // (seems optional parameters don't work well when there's an variadic template + // comes first) + // + // Ideally it should look like: + // + // template<typename... Ts> + // GCompiled compile(const Ts&... metas, GCompileArgs &&args) + // + // But not all compilers can hande this (and seems they shouldn't be able to). + // FIXME: SFINAE looks ugly in the generated documentation + /** + * @overload + * + * Takes a variadic parameter pack with metadata + * descriptors for which a compiled object needs to be produced, + * followed by GCompileArgs object representing compilation + * arguments for this process. + * + * @return GCompiled, an executable computation compiled + * specifically for the given input parameters. + */ + template<typename... Ts> + auto compile(const Ts&... meta_and_compile_args) -> + typename std::enable_if<detail::are_meta_descrs_but_last<Ts...>::value + && std::is_same<GCompileArgs, detail::last_type_t<Ts...> >::value, + GCompiled>::type + { + //FIXME: wrapping meta_and_compile_args into a tuple to unwrap them inside a helper function is the overkill + return compile(std::make_tuple(meta_and_compile_args...), + typename detail::MkSeq<sizeof...(Ts)-1>::type()); + } + + // Internal use only + /// @private + Priv& priv(); + /// @private + const Priv& priv() const; + +protected: + + // 4. Helper method for (3) + /// @private + template<typename... Ts, int... IIs> + GCompiled compile(const std::tuple<Ts...> &meta_and_compile_args, detail::Seq<IIs...>) + { + GMetaArgs meta_args = {GMetaArg(std::get<IIs>(meta_and_compile_args))...}; + GCompileArgs comp_args = std::get<sizeof...(Ts)-1>(meta_and_compile_args); + return compile(std::move(meta_args), std::move(comp_args)); + } + /// @private + std::shared_ptr<Priv> m_priv; +}; +/** @} */ + +namespace gapi +{ + // FIXME: all these standalone functions need to be added to some + // common documentation section + /** + * @brief Define an tagged island (subgraph) within a computation. + * + * Declare an Island tagged with `name` and defined from `ins` to `outs` + * (exclusively, as ins/outs are data objects, and regioning is done on + * operations level). + * Throws if any operation between `ins` and `outs` are already assigned + * to another island. + * + * Islands allow to partition graph into subgraphs, fine-tuning + * the way it is scheduled by the underlying executor. + * + * @param name name of the Island to create + * @param ins vector of input data objects where the subgraph + * begins + * @param outs vector of output data objects where the subgraph + * ends. + * + * The way how an island is defined is similar to how + * cv::GComputation is defined on input/output data objects. + * Same rules apply here as well -- if there's no functional + * dependency between inputs and outputs or there's not enough + * input data objects were specified to properly calculate all + * outputs, an exception is thrown. + * + * Use cv::GIn() / cv::GOut() to specify input/output vectors. + */ + void GAPI_EXPORTS island(const std::string &name, + GProtoInputArgs &&ins, + GProtoOutputArgs &&outs); +} // namespace gapi + +} // namespace cv +#endif // OPENCV_GAPI_GCOMPUTATION_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp new file mode 100644 index 000000000..adc7da3c7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -0,0 +1,563 @@ +// 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 <string> // string +#include <type_traits> // false_type, true_type +#include <unordered_map> // map (for GKernelPackage) +#include <utility> // tuple +#include <vector> // lookup order + +#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 { + /** \addtogroup gapi_compile_args + * @{ + */ + + // Lookup order is in fact a vector of Backends to traverse during look-up + /** + * @brief Priority list of backends to use during kernel + * resolution process. + * + * Priority is descending -- the first backend in the list has the + * top priority, and the last one has the lowest priority. + * + * If there's multiple implementations available for a kernel at + * the moment of graph compilation, a kernel (and thus a backend) + * will be selected according to this order (if the parameter is passed). + * + * Default order is not specified (and by default, only + * CPU(OpenCV) backend is involved in graph compilation). + */ + using GLookupOrder = std::vector<GBackend>; + /** + * @brief Create a backend lookup order -- priority list of + * backends to use during graph compilation process. + * + * @sa GLookupOrder, @ref gapi_std_backends + */ + inline GLookupOrder lookup_order(std::initializer_list<GBackend> &&list) + { + return GLookupOrder(std::move(list)); + } + + // FIXME: Hide implementation + /** + * @brief A container class for heterogeneous kernel + * implementation collections. + * + * GKernelPackage is a special container class which stores kernel + * _implementations_. Objects of this class are created and passed + * to cv::GComputation::compile() to specify which kernels to use + * in the compiled graph. GKernelPackage may contain kernels of + * different backends, e.g. be heterogeneous. + * + * The most easy way to create a kernel package is to use function + * cv::gapi::kernels(). This template functions takes kernel + * implementations in form of type list (variadic template) and + * generates a kernel package atop of that. + * + * Kernel packages can be also generated programatically, starting + * with an empty package (created with the default constructor) + * and then by populating it with kernels via call to + * GKernelPackage::include(). Note this method is also a template + * one since G-API kernel implementations are _types_, not objects. + * + * Finally, two kernel packages can be combined into a new one + * with function cv::gapi::combine(). There are different rules + * apply to this process, see also cv::gapi::unite_policy for + * details. + */ + class GAPI_EXPORTS GKernelPackage + { + /// @private + using S = std::unordered_map<std::string, GKernelImpl>; + + /// @private + using M = std::unordered_map<GBackend, S>; + + /// @private + M m_backend_kernels; + + protected: + /// @private + // Check if package contains ANY implementation of a kernel API + // by API textual id. + bool includesAPI(const std::string &id) const; + + /// @private + // Remove ALL implementations of the given API (identified by ID) + void removeAPI(const std::string &id); + + public: + /** + * @brief Returns total number of kernels in the package + * (accross all backends included) + * + * @return a number of kernels in the package + */ + std::size_t size() const; + + /** + * @brief Test if a particular kernel _implementation_ KImpl is + * included in this kernel package. + * + * @sa includesAPI() + * + * @return true if there is such kernel, false otherwise. + */ + template<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; + } + + /** + * @brief Remove all kernels associated with the given backend + * from the package. + * + * Does nothing if there's no kernels of this backend in the package. + * + * @param backend backend which kernels to remove + */ + void remove(const GBackend& backend); + + /** + * @brief Remove all kernels implementing the given API from + * the package. + * + * Does nothing if there's no kernels implementing the given interface. + */ + template<typename KAPI> + void remove() + { + removeAPI(KAPI::id()); + } + + // FIXME: Rename to includes() and distinguish API/impl case by + // statically? + /** + * Check if package contains ANY implementation of a kernel API + * by API type. + */ + template<typename KAPI> + bool includesAPI() const + { + return includesAPI(KAPI::id()); + } + + /** + * @brief Find a kernel (by its API), given the look-up order. + * + * If order is empty, returns first suitable implementation. + * Throws if nothing found. + * + * @return Backend which hosts matching kernel implementation. + * + * @sa cv::gapi::lookup_order + */ + template<typename KAPI> + GBackend lookup(const GLookupOrder &order = {}) const + { + return lookup(KAPI::id(), order).first; + } + + /// @private + std::pair<cv::gapi::GBackend, cv::GKernelImpl> + lookup(const std::string &id, const GLookupOrder &order = {}) const; + + // FIXME: No overwrites allowed? + /** + * @brief Put a new kernel implementation KImpl into package. + * + * @param up unite policy to use. If the package has already + * implementation for this kernel (probably from another + * backend), and cv::unite_policy::KEEP is passed, the + * existing implementation remains in package; on + * cv::unite_policy::REPLACE all other existing + * implementations are first dropped from the package. + */ + template<typename KImpl> + void include(const cv::unite_policy up = cv::unite_policy::KEEP) + { + auto backend = KImpl::backend(); + auto kernel_id = KImpl::API::id(); + auto kernel_impl = GKernelImpl{KImpl::kernel()}; + if (up == cv::unite_policy::REPLACE) removeAPI(kernel_id); + else GAPI_Assert(up == cv::unite_policy::KEEP); + + // Regardless of the policy, store new impl in its storage slot. + m_backend_kernels[backend][kernel_id] = std::move(kernel_impl); + } + + /** + * @brief Lists all backends which are included into package + * + * @return vector of backends + */ + std::vector<GBackend> backends() const; + + // TODO: Doxygen bug -- it wants me to place this comment + // here, not below. + /** + * @brief Create a new package based on `lhs` and `rhs`, + * with unity policy defined by `policy`. + * + * @param lhs "Left-hand-side" package in the process + * @param rhs "Right-hand-side" package in the process + * @param policy Unite policy which is used in case of conflicts + * -- when the same kernel API is implemented in both packages by + * different backends; cv::unite_policy::KEEP keeps both + * implementation in the resulting package, while + * cv::unite_policy::REPLACE gives precedence two kernels from + * "Right-hand-side". + * + * @return a new kernel package. + */ + friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs, + const GKernelPackage &rhs, + const cv::unite_policy policy); + }; + + /** + * @brief Create a kernel package object containing kernels + * specified in variadic template argument. + * + * In G-API, kernel implementations are _types_. Every backend has + * its own kernel API (like GAPI_OCV_KERNEL() and + * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for + * each kernel implementation. + * + * Use this function to pass kernel implementations (defined in + * either way) to the system. Example: + * + * @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet + * + * Note that kernels() itself is a function returning object, not + * a type, so having `()` at the end is important -- it must be a + * function call. + */ + template<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; + }; + + /** @} */ + + 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp new file mode 100644 index 000000000..0fa53427d --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -0,0 +1,149 @@ +// 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; + +/** \addtogroup gapi_data_objects + * @{ + * + * @brief Data-representing objects which can be used to build G-API + * expressions. + */ + +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; +}; + +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ +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; + } +#if !defined(GAPI_STANDALONE) + GMatDesc withSizeDelta(cv::Size delta) const + { + return withSizeDelta(to_own(delta)); + } + + GMatDesc withSize(cv::Size sz) const + { + return withSize(to_own(sz)); + } +#endif // !defined(GAPI_STANDALONE) + // 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; + } + + // 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}}; } + +#if !defined(GAPI_STANDALONE) +class Mat; +GAPI_EXPORTS GMatDesc descr_of(const cv::Mat &mat); +GAPI_EXPORTS GMatDesc descr_of(const cv::UMat &mat); +#endif // !defined(GAPI_STANDALONE) + +/** @} */ + +namespace gapi { namespace own { + class Mat; + GAPI_EXPORTS GMatDesc descr_of(const Mat &mat); +}}//gapi::own + +std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc); + +} // namespace cv + +#endif // OPENCV_GAPI_GMAT_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp new file mode 100644 index 000000000..473be342e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp new file mode 100644 index 000000000..8b53d9b64 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp new file mode 100644 index 000000000..98d49b5b8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp @@ -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_GPU_CORE_API_HPP +#define OPENCV_GAPI_GPU_CORE_API_HPP + +#include <opencv2/core/cvdef.h> // GAPI_EXPORTS +#include <opencv2/gapi/gkernel.hpp> // GKernelPackage + +namespace cv { +namespace gapi { +namespace core { +namespace gpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace gpu +} // namespace core +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_GPU_CORE_API_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp new file mode 100644 index 000000000..e5a6215e3 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp @@ -0,0 +1,244 @@ +// 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_GGPUKERNEL_HPP +#define OPENCV_GAPI_GGPUKERNEL_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> + +// FIXME: namespace scheme for backends? +namespace cv { + +namespace gimpl +{ + // Forward-declare an internal class + class GGPUExecutable; +} // namespace gimpl + +namespace gapi +{ +namespace gpu +{ + /** + * \addtogroup gapi_std_backends G-API Standard backends + * @{ + */ + /** + * @brief Get a reference to GPU backend. + * + * At the moment, the GPU backend is built atop of OpenCV + * "Transparent API" (T-API), see cv::UMat for details. + * + * @sa gapi_std_backends + */ + GAPI_EXPORTS cv::gapi::GBackend backend(); + /** @} */ +} // namespace gpu +} // namespace gapi + + +// Represents arguments which are passed to a wrapped GPU function +// FIXME: put into detail? +class GAPI_EXPORTS GGPUContext +{ +public: + // Generic accessor API + template<typename T> + const T& inArg(int input) { return m_args.at(input).get<T>(); } + + // Syntax sugar + const cv::UMat& inMat(int input); + cv::UMat& outMatR(int output); // FIXME: Avoid cv::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; + std::unordered_map<std::size_t, GRunArgP> m_results; + + + friend class gimpl::GGPUExecutable; +}; + +class GAPI_EXPORTS GGPUKernel +{ +public: + // This function is kernel's execution entry point (does the processing work) + using F = std::function<void(GGPUContext &)>; + + GGPUKernel(); + explicit GGPUKernel(const F& f); + + void apply(GGPUContext &ctx); + +protected: + F m_f; +}; + +// FIXME: This is an ugly ad-hoc imlpementation. TODO: refactor + +namespace detail +{ +template<class T> struct gpu_get_in; +template<> struct gpu_get_in<cv::GMat> +{ + static cv::UMat get(GGPUContext &ctx, int idx) { return ctx.inMat(idx); } +}; +template<> struct gpu_get_in<cv::GScalar> +{ + static cv::Scalar get(GGPUContext &ctx, int idx) { return to_ocv(ctx.inVal(idx)); } +}; +template<typename U> struct gpu_get_in<cv::GArray<U> > +{ + static const std::vector<U>& get(GGPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); } +}; +template<class T> struct gpu_get_in +{ + static T get(GGPUContext &ctx, int idx) { return ctx.inArg<T>(idx); } +}; + +struct tracked_cv_umat{ + //TODO Think if T - API could reallocate UMat to a proper size - how do we handle this ? + //tracked_cv_umat(cv::UMat& m) : r{(m)}, original_data{m.getMat(ACCESS_RW).data} {} + tracked_cv_umat(cv::UMat& m) : r{ (m) }, original_data{ nullptr } {} + cv::UMat r; + uchar* original_data; + + operator cv::UMat& (){ return r;} + void validate() const{ + //if (r.getMat(ACCESS_RW).data != original_data) + //{ + // util::throw_error + // (std::logic_error + // ("OpenCV kernel output parameter was reallocated. \n" + // "Incorrect meta data was provided ?")); + //} + + } +}; + +struct scalar_wrapper_gpu +{ + //FIXME reuse CPU (OpenCV) plugin code + scalar_wrapper_gpu(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_gpu(Outputs&... outs) +{ + struct + { + void operator()(tracked_cv_umat* bm) { bm->validate(); } + void operator()(scalar_wrapper_gpu* 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 gpu_get_out; +template<> struct gpu_get_out<cv::GMat> +{ + static tracked_cv_umat get(GGPUContext &ctx, int idx) + { + auto& r = ctx.outMatR(idx); + return{ r }; + } +}; +template<> struct gpu_get_out<cv::GScalar> +{ + static scalar_wrapper_gpu get(GGPUContext &ctx, int idx) + { + auto& s = ctx.outValR(idx); + return{ s }; + } +}; +template<typename U> struct gpu_get_out<cv::GArray<U> > +{ + static std::vector<U>& get(GGPUContext &ctx, int idx) { return ctx.outVecR<U>(idx); } +}; + +template<typename, typename, typename> +struct GPUCallHelper; + +// FIXME: probably can be simplified with std::apply or analogue. +template<typename Impl, typename... Ins, typename... Outs> +struct GPUCallHelper<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_gpu(outs...); + } + }; + + template<int... IIs, int... OIs> + static void call_impl(GGPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>) + { + //TODO: Make sure that OpenCV kernels do not reallocate memory for output parameters + //by comparing it's state (data ptr) before and after the call. + //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(gpu_get_in<Ins>::get(ctx, IIs))...>::call(gpu_get_in<Ins>::get(ctx, IIs)..., gpu_get_out<Outs>::get(ctx, OIs)...); + } + + static void call(GGPUContext &ctx) + { + call_impl(ctx, + typename detail::MkSeq<sizeof...(Ins)>::type(), + typename detail::MkSeq<sizeof...(Outs)>::type()); + } +}; + +} // namespace detail + +template<class Impl, class K> +class GGPUKernelImpl: public detail::GPUCallHelper<Impl, typename K::InArgs, typename K::OutArgs> +{ + using P = detail::GPUCallHelper<Impl, typename K::InArgs, typename K::OutArgs>; + +public: + using API = K; + + static cv::gapi::GBackend backend() { return cv::gapi::gpu::backend(); } + static cv::GGPUKernel kernel() { return GGPUKernel(&P::call); } +}; + +#define GAPI_GPU_KERNEL(Name, API) struct Name: public cv::GGPUKernelImpl<Name, API> + +} // namespace cv + +#endif // OPENCV_GAPI_GGPUKERNEL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp new file mode 100644 index 000000000..6071dda98 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp @@ -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_GPU_IMGPROC_API_HPP +#define OPENCV_GAPI_GPU_IMGPROC_API_HPP + +#include <opencv2/core/cvdef.h> // GAPI_EXPORTS +#include <opencv2/gapi/gkernel.hpp> // GKernelPackage + +namespace cv { +namespace gapi { +namespace imgproc { +namespace gpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace gpu +} // namespace imgproc +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_GPU_IMGPROC_API_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp new file mode 100644 index 000000000..dd1205b63 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp @@ -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 + + +#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" + +namespace cv +{ +// Forward declaration; GNode and GOrigin are an internal +// (user-inaccessible) classes. +class GNode; +struct GOrigin; + +/** \addtogroup gapi_data_objects + * @{ + */ + +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 +#if !defined(GAPI_STANDALONE) + explicit GScalar(const cv::Scalar& s); // Constant value constructor from cv::Scalar +#endif // !defined(GAPI_STANDALONE) + 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; +}; + +/** @} */ + +/** + * \addtogroup gapi_meta_args + * @{ + */ +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(); } + +#if !defined(GAPI_STANDALONE) +GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar &scalar); +#endif // !defined(GAPI_STANDALONE) +/** @} */ + +GAPI_EXPORTS GScalarDesc descr_of(const cv::gapi::own::Scalar &scalar); + +std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &desc); + +} // namespace cv + +#endif // OPENCV_GAPI_GSCALAR_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtype_traits.hpp new file mode 100644 index 000000000..d05e02e0e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtype_traits.hpp @@ -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 + + +#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(>ypeTraits<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; +#if !defined(GAPI_STANDALONE) + template<> struct GTypeOf<cv::Mat> { using type = cv::GMat; }; + template<> struct GTypeOf<cv::Scalar> { using type = cv::GScalar; }; +#endif // !defined(GAPI_STANDALONE) + template<> struct GTypeOf<cv::gapi::own::Mat> { using type = cv::GMat; }; + 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp new file mode 100644 index 000000000..a966f263f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp @@ -0,0 +1,187 @@ +// 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 +#if !defined(GAPI_STANDALONE) + +#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 // !defined(GAPI_STANDALONE) +#endif // OPENCV_GAPI_GTYPED_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp new file mode 100644 index 000000000..aeed9fa6c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -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 = FILTER_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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/opencv_includes.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/opencv_includes.hpp new file mode 100644 index 000000000..5acf28023 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/opencv_includes.hpp @@ -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_OPENCV_INCLUDES_HPP +#define OPENCV_GAPI_OPENCV_INCLUDES_HPP + +#if !defined(GAPI_STANDALONE) +# include <opencv2/core/mat.hpp> +# include <opencv2/core/cvdef.h> +# include <opencv2/core/types.hpp> +# include <opencv2/core/base.hpp> +#else // Without OpenCV +# include <opencv2/gapi/own/cvdefs.hpp> +#endif // !defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_OPENCV_INCLUDES_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp new file mode 100644 index 000000000..27a1d8012 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp new file mode 100644 index 000000000..8d3feff01 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp @@ -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 !defined(GAPI_STANDALONE) +#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 NDEBUG +# define GAPI_DbgAssert(expr) +#else +# define GAPI_DbgAssert(expr) GAPI_Assert(expr) +#endif + +#endif // OPENCV_GAPI_OWN_ASSERT_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp new file mode 100644 index 000000000..8c1feb408 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp @@ -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_OWN_CONVERT_HPP +#define OPENCV_GAPI_OWN_CONVERT_HPP + +#if !defined(GAPI_STANDALONE) + +#include <opencv2/gapi/opencv_includes.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 // !defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_OWN_CONVERT_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp new file mode 100644 index 000000000..e11053692 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp @@ -0,0 +1,146 @@ +// 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_CV_DEFS_HPP +#define OPENCV_GAPI_CV_DEFS_HPP + +#if defined(GAPI_STANDALONE) + +// Simulate OpenCV definitions taken from various +// OpenCV interface headers if G-API is built in a +// standalone mode. + +// interface.h: + +typedef unsigned char uchar; +typedef char schar; + +typedef unsigned short ushort; + +#define CV_CN_MAX 512 +#define CV_CN_SHIFT 3 +#define CV_DEPTH_MAX (1 << CV_CN_SHIFT) + + +#define CV_8U 0 +#define CV_8S 1 +#define CV_16U 2 +#define CV_16S 3 +#define CV_32S 4 +#define CV_32F 5 +#define CV_64F 6 +#define CV_USRTYPE1 7 + +#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1) +#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK) + +#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT)) +#define CV_MAKE_TYPE CV_MAKETYPE + +#define CV_8UC1 CV_MAKETYPE(CV_8U,1) +#define CV_8UC2 CV_MAKETYPE(CV_8U,2) +#define CV_8UC3 CV_MAKETYPE(CV_8U,3) +#define CV_8UC4 CV_MAKETYPE(CV_8U,4) +#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n)) + +#define CV_8SC1 CV_MAKETYPE(CV_8S,1) +#define CV_8SC2 CV_MAKETYPE(CV_8S,2) +#define CV_8SC3 CV_MAKETYPE(CV_8S,3) +#define CV_8SC4 CV_MAKETYPE(CV_8S,4) +#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n)) + +#define CV_16UC1 CV_MAKETYPE(CV_16U,1) +#define CV_16UC2 CV_MAKETYPE(CV_16U,2) +#define CV_16UC3 CV_MAKETYPE(CV_16U,3) +#define CV_16UC4 CV_MAKETYPE(CV_16U,4) +#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n)) + +#define CV_16SC1 CV_MAKETYPE(CV_16S,1) +#define CV_16SC2 CV_MAKETYPE(CV_16S,2) +#define CV_16SC3 CV_MAKETYPE(CV_16S,3) +#define CV_16SC4 CV_MAKETYPE(CV_16S,4) +#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n)) + +#define CV_32SC1 CV_MAKETYPE(CV_32S,1) +#define CV_32SC2 CV_MAKETYPE(CV_32S,2) +#define CV_32SC3 CV_MAKETYPE(CV_32S,3) +#define CV_32SC4 CV_MAKETYPE(CV_32S,4) +#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n)) + +#define CV_32FC1 CV_MAKETYPE(CV_32F,1) +#define CV_32FC2 CV_MAKETYPE(CV_32F,2) +#define CV_32FC3 CV_MAKETYPE(CV_32F,3) +#define CV_32FC4 CV_MAKETYPE(CV_32F,4) +#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n)) + +#define CV_64FC1 CV_MAKETYPE(CV_64F,1) +#define CV_64FC2 CV_MAKETYPE(CV_64F,2) +#define CV_64FC3 CV_MAKETYPE(CV_64F,3) +#define CV_64FC4 CV_MAKETYPE(CV_64F,4) +#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n)) + +// cvdef.h: + +#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT) +#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1) +#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1) +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) +#define CV_MAT_CONT_FLAG_SHIFT 14 +#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT) +#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG) +#define CV_IS_CONT_MAT CV_IS_MAT_CONT +#define CV_SUBMAT_FLAG_SHIFT 15 +#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT) +#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG) + +///** Size of each channel item, +// 0x8442211 = 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */ +//#define CV_ELEM_SIZE1(type) \ +// ((((sizeof(size_t)<<28)|0x8442211) >> CV_MAT_DEPTH(type)*4) & 15) + +#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK) + +/** 0x3a50 = 11 10 10 01 01 00 00 ~ array of log2(sizeof(arr_type_elem)) */ +#define CV_ELEM_SIZE(type) \ + (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3)) + +// base.h: +namespace cv +{ +enum BorderTypes { + BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i` + BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh` + BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb` + BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg` + BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba` + BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno` + + BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101 + BORDER_ISOLATED = 16 //!< do not look outside of ROI +}; +// imgproc.hpp: +enum InterpolationFlags{ + INTER_NEAREST = 0, + INTER_LINEAR = 1, + INTER_CUBIC = 2, + INTER_AREA = 3, + INTER_LANCZOS4 = 4, + INTER_LINEAR_EXACT = 5, + INTER_MAX = 7, +}; +} // namespace cv + +static inline int cvFloor( double value ) +{ + int i = (int)value; + return i - (i > value); +} + +#endif // defined(GAPI_STANDALONE) + +#endif // OPENCV_GAPI_CV_DEFS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp new file mode 100644 index 000000000..0d955d0e4 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp new file mode 100644 index 000000000..73f3afcbc --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp @@ -0,0 +1,291 @@ +// 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_MAT_HPP +#define OPENCV_GAPI_OWN_MAT_HPP + +#include "opencv2/gapi/opencv_includes.hpp" +#include "opencv2/gapi/own/types.hpp" +#include "opencv2/gapi/own/scalar.hpp" +#include "opencv2/gapi/own/saturate.hpp" +#include "opencv2/gapi/own/assert.hpp" + +#include <memory> //std::shared_ptr +#include <cstring> //std::memcpy +#include "opencv2/gapi/util/throw.hpp" + +namespace cv { namespace gapi { namespace own { + namespace detail { + template <typename T, unsigned char channels> + void assign_row(void* ptr, int cols, Scalar const& s) + { + auto p = static_cast<T*>(ptr); + for (int c = 0; c < cols; c++) + { + for (int ch = 0; ch < channels; ch++) + { + p[c * channels + ch] = saturate<T>(s[ch], roundd); + } + } + } + + 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, const Rect& roi ) + : Mat(src) + { + rows = roi.height; + cols = roi.width; + data = ptr(roi.y, roi.x); + } + + Mat(Mat const& src) = default; + Mat(Mat&& src) = default; + + Mat& operator=(Mat const& src) = default; + Mat& operator=(Mat&& src) = default; + + /** @brief Sets all or some of the array elements to the specified value. + @param s Assigned scalar converted to the actual array type. + */ + Mat& operator = (const Scalar& s) + { + constexpr unsigned max_channels = 4; //Scalar can't fit more than 4 + const auto channels = static_cast<unsigned int>(this->channels()); + GAPI_Assert(channels <= max_channels); + + using func_p_t = void (*)(void*, int, Scalar const&); + using detail::assign_row; + #define TABLE_ENTRY(type) {assign_row<type, 1>, assign_row<type, 2>, assign_row<type, 3>, assign_row<type, 4>} + static constexpr func_p_t func_tbl[][max_channels] = { + TABLE_ENTRY(uchar), + TABLE_ENTRY(schar), + TABLE_ENTRY(ushort), + TABLE_ENTRY(short), + TABLE_ENTRY(int), + TABLE_ENTRY(float), + TABLE_ENTRY(double) + }; + #undef TABLE_ENTRY + + static_assert(CV_8U == 0 && CV_8S == 1 && CV_16U == 2 && CV_16S == 3 + && CV_32S == 4 && CV_32F == 5 && CV_64F == 6, + "OCV type ids used as indexes to array, thus exact numbers are important!" + ); + + const auto depth = static_cast<unsigned int>(this->depth()); + GAPI_Assert(depth < sizeof(func_tbl)/sizeof(func_tbl[0])); + + for (int r = 0; r < rows; ++r) + { + auto* f = func_tbl[depth][channels -1]; + (*f)(static_cast<void *>(ptr(r)), cols, s ); + } + return *this; + } + + /** @brief Returns the matrix element size in bytes. + + The method returns the matrix element size in bytes. For example, if the matrix type is CV_16SC3 , + the method returns 3\*sizeof(short) or 6. + */ + size_t elemSize() const + { + return CV_ELEM_SIZE(type()); + } + /** @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);} + + /** + @param _rows New number of rows. + @param _cols New number of columns. + @param _type New matrix type. + */ + void create(int _rows, int _cols, int _type) + { + create({_cols, _rows}, _type); + } + /** @overload + @param _size Alternative new matrix size specification: Size(cols, rows) + @param _type New matrix type. + */ + void create(Size _size, int _type) + { + if (_size != 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); + } + } + + /** @brief Copies the matrix to another one. + + The method copies the matrix data to another matrix. Before copying the data, the method invokes : + @code + m.create(this->size(), this->type()); + @endcode + so that the destination matrix is reallocated if needed. While m.copyTo(m); works flawlessly, the + function does not handle the case of a partial overlap between the source and the destination + matrices. + */ + void copyTo(Mat& dst) const + { + dst.create(rows, cols, type()); + for (int r = 0; r < rows; ++r) + { + std::copy_n(ptr(r), detail::default_step(type(),cols), dst.ptr(r)); + } + } + + /** @brief Returns true if the array has no elements. + + The method returns true if Mat::total() is 0 or if Mat::data is NULL. Because of pop_back() and + resize() methods `M.total() == 0` does not imply that `M.data == NULL`. + */ + bool empty() const; + + /** @brief Returns the total number of array elements. + + The method returns the number of array elements (a number of pixels if the array represents an + image). + */ + size_t total() const + { + return static_cast<size_t>(rows * cols); + } + + + /** @overload + @param roi Extracted submatrix specified as a rectangle. + */ + Mat operator()( const Rect& roi ) const + { + return Mat{*this, roi}; + } + + + /** @brief Returns a pointer to the specified matrix row. + + The methods return `uchar*` or typed pointer to the specified matrix row. See the sample in + Mat::isContinuous to know how to use these methods. + @param row Index along the dimension 0 + @param col Index along the dimension 1 + */ + uchar* ptr(int row, int col = 0) + { + return const_cast<uchar*>(const_cast<const Mat*>(this)->ptr(row,col)); + } + /** @overload */ + const uchar* ptr(int row, int col = 0) const + { + return data + step * row + CV_ELEM_SIZE(type()) * col; + } + + + 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 /* OPENCV_GAPI_OWN_MAT_HPP */ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/saturate.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/saturate.hpp new file mode 100644 index 000000000..207dcde25 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/saturate.hpp @@ -0,0 +1,90 @@ +// 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_SATURATE_HPP +#define OPENCV_GAPI_OWN_SATURATE_HPP + +#include <cmath> + +#include <limits> +#include <type_traits> + +#include <opencv2/gapi/own/assert.hpp> + +namespace cv { namespace gapi { namespace own { +//----------------------------- +// +// 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 +inline double ceild(double x) { return std::ceil(x); } +inline double floord(double x) { return std::floor(x); } +inline double roundd(double x) { return std::round(x); } +inline double rintd(double x) { return std::rint(x); } + +} //namespace own +} //namespace gapi +} //namespace cv +#endif /* OPENCV_GAPI_OWN_SATURATE_HPP */ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/scalar.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/scalar.hpp new file mode 100644 index 000000000..bda91c83b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/scalar.hpp @@ -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_GAPI_OWN_SCALAR_HPP +#define OPENCV_GAPI_GAPI_OWN_SCALAR_HPP + +#include <opencv2/gapi/own/exports.hpp> + +namespace cv +{ +namespace gapi +{ +namespace own +{ + +class GAPI_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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/types.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/types.hpp new file mode 100644 index 000000000..20445ee0f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/types.hpp @@ -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_TYPES_HPP +#define OPENCV_GAPI_TYPES_HPP + +#include <algorithm> // std::max, std::min +#include <ostream> + +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 !defined(GAPI_STANDALONE) + 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 // !defined(GAPI_STANDALONE) + + 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 !defined(GAPI_STANDALONE) + 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 // !defined(GAPI_STANDALONE) + + 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp new file mode 100644 index 000000000..3146cb6fd --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp @@ -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_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); + + template<class value_t> + friend value_t& unsafe_any_cast(any& operand); + + template<class value_t> + friend const value_t& unsafe_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()); + } + + template<class value_t> + inline value_t& unsafe_any_cast(any& operand) + { +#ifdef DEBUG + return any_cast<value_t>(operand); +#else + return static_cast<any::holder_impl<typename std::decay<value_t>::type> *>(operand.hldr.get())->v; +#endif + } + + template<class value_t> + inline const value_t& unsafe_any_cast(const any& operand) + { +#ifdef DEBUG + return any_cast<value_t>(operand); +#else + return static_cast<any::holder_impl<typename std::decay<value_t>::type> *>(operand.hldr.get())->v; +#endif + } + +} // 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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp new file mode 100644 index 000000000..575655e8f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/compiler_hints.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp new file mode 100644 index 000000000..54126d627 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/throw.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/throw.hpp new file mode 100644 index 000000000..689bf583c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/throw.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp new file mode 100644 index 000000000..d0378e0e5 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp new file mode 100644 index 000000000..cb0270a73 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp @@ -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 {{(©_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/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.cpp new file mode 100644 index 000000000..2df4d8890 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.cpp @@ -0,0 +1,9 @@ +// 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 "gapi_core_perf_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp new file mode 100644 index 000000000..8af7b1abf --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp @@ -0,0 +1,76 @@ +// 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_PERF_TESTS_HPP +#define OPENCV_GAPI_CORE_PERF_TESTS_HPP + + +#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, cv::GCompileArgs>> {}; + class AddCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class SubPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class SubCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class SubRCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class MulPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class MulDoublePerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class MulCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class DivPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {}; + class DivCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class DivRCPerfTest : public TestPerfParams<tuple<compare_f,cv::Size, MatType, int, cv::GCompileArgs>> {}; + class MaskPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class MeanPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class Polar2CartPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; + class Cart2PolarPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; + class CmpPerfTest : public TestPerfParams<tuple<CmpTypes, cv::Size, MatType, cv::GCompileArgs>> {}; + class CmpWithScalarPerfTest : public TestPerfParams<tuple<CmpTypes, cv::Size, MatType, cv::GCompileArgs>> {}; + class BitwisePerfTest : public TestPerfParams<tuple<bitwiseOp, cv::Size, MatType, cv::GCompileArgs>> {}; + class BitwiseNotPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class SelectPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class MinPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class MaxPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class AbsDiffPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class AbsDiffCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class SumPerfTest : public TestPerfParams<tuple<cv::Size, MatType, double, cv::GCompileArgs>> {}; + class AddWeightedPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, double, cv::GCompileArgs>> {}; + class NormPerfTest : public TestPerfParams<tuple<NormTypes, cv::Size, MatType, double, cv::GCompileArgs>> {}; + class IntegralPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class ThresholdPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class ThresholdOTPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class InRangePerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class Split3PerfTest : public TestPerfParams<tuple<cv::Size, cv::GCompileArgs>> {}; + class Split4PerfTest : public TestPerfParams<tuple<cv::Size, cv::GCompileArgs>> {}; + class Merge3PerfTest : public TestPerfParams<tuple<cv::Size, cv::GCompileArgs>> {}; + class Merge4PerfTest : public TestPerfParams<tuple<cv::Size, cv::GCompileArgs>> {}; + class RemapPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class FlipPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {}; + class CropPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::Rect, cv::GCompileArgs>> {}; + class ConcatHorPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class ConcatHorVecPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class ConcatVertPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class ConcatVertVecPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {}; + class LUTPerfTest : public TestPerfParams<tuple<MatType, MatType, cv::Size, cv::GCompileArgs>> {}; + class ConvertToPerfTest : public TestPerfParams<tuple<MatType, int, cv::Size, cv::GCompileArgs>> {}; + class ResizePerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, cv::Size, cv::GCompileArgs>> {}; + class ResizeFxFyPerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, double, double, cv::GCompileArgs>> {}; +} +#endif // OPENCV_GAPI_CORE_PERF_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests_inl.hpp new file mode 100644 index 000000000..f49e06161 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests_inl.hpp @@ -0,0 +1,1841 @@ +// 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_PERF_TESTS_INL_HPP +#define OPENCV_GAPI_CORE_PERF_TESTS_INL_HPP + +#include <iostream> + +#include "gapi_core_perf_tests.hpp" + +namespace opencv_test +{ +using namespace perf; + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(AddPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(AddCPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SubPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SubCPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SubRCPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MulPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(GetParam()); + + initMatsRandU(type, sz, dtype, false); + + // OpenCV code /////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv, 1.0, dtype); + + // G-API code //////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2, 1.0, dtype); + cv::GComputation c(GIn(in1, in2), GOut(out)); + + // Warm-up graph engine: + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MulDoublePerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MulCPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(DivPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + int dtype = get<3>(GetParam()); + cv::GCompileArgs compile_args = get<4>(GetParam()); + + // FIXIT Unstable input data for divide + 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(DivCPerfTest, TestPerformance) +{ + Size sz = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(GetParam()); + + // FIXIT Unstable input data for divide + 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(DivRCPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + int dtype = get<3>(GetParam()); + cv::GCompileArgs compile_args = get<4>(GetParam()); + + // FIXIT Unstable input data for divide + initMatsRandU(type, sz, dtype, false); + + // FIXIT Unstable input data for divide, don't process zeros + sc += Scalar::all(1); + in_mat1 += 1; + + // 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MaskPerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MeanPerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(out_norm), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(out_norm[0], out_norm_ocv[0]); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Polar2CartPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz_in = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi, out_mat2), std::move(compile_args)); + } + // Comparison //////////////////////////////////////////////////////////// + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_TRUE(cmpF(out_mat_ocv2, out_mat2)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Cart2PolarPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz_in = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi, out_mat2), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_TRUE(cmpF(out_mat_ocv2, out_mat2)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(CmpPerfTest, TestPerformance) +{ + CmpTypes opType = get<0>(GetParam()); + cv::Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + 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); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(CmpWithScalarPerfTest, TestPerformance) +{ + CmpTypes opType = get<0>(GetParam()); + cv::Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), 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); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BitwisePerfTest, TestPerformance) +{ + bitwiseOp opType = get<0>(GetParam()); + cv::Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BitwiseNotPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SelectPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MinPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MaxPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(AbsDiffPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(AbsDiffCPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SumPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + double tolerance = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(out_sum), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + { + EXPECT_LE(std::abs(out_sum[0] - out_sum_ocv[0]) / std::max(1.0, std::abs(out_sum_ocv[0])), tolerance) + << "OCV=" << out_sum_ocv[0] << " GAPI=" << out_sum[0]; + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(AddWeightedPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int dtype = get<2>(GetParam()); + double tolerance = get<3>(GetParam()); + cv::GCompileArgs compile_args = get<4>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check + if (0) + { + // 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) >= tolerance;// 0.5000005f; // relative to 6 digits + + failures = inexact & incorrect; + } + + EXPECT_EQ(0, cv::countNonZero(failures)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(NormPerfTest, TestPerformance) +{ + NormTypes opType = get<0>(GetParam()); + cv::Size sz = get<1>(GetParam()); + MatType type = get<2>(GetParam()); + double tolerance = get<3>(GetParam()); + cv::GCompileArgs compile_args = get<4>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1), gout(out_norm), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + { + EXPECT_LE(std::abs(out_norm[0] - out_norm_ocv[0]) / std::max(1.0, std::abs(out_norm_ocv[0])), tolerance) + << "OCV=" << out_norm_ocv[0] << " GAPI=" << out_norm[0]; + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(IntegralPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_ocv1 != out_mat1)); + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_ocv2 != out_mat2)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ThresholdPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int tt = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(GetParam()); + + cv::Scalar thr = initScalarRandU(50); + cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ThresholdOTPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int tt = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(GetParam()); + + cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: 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(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(InRangePerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(GetParam()); + + cv::Scalar thrLow = initScalarRandU(100); + cv::Scalar thrUp = initScalarRandU(100) + cv::Scalar(100, 100, 100, 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Split3PerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + cv::GCompileArgs compile_args = 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); + + // 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(0, cv::norm(out_mat_ocv2, out_mat2, NORM_INF)); + EXPECT_EQ(0, cv::norm(out_mat_ocv3, out_mat3, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Split4PerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + cv::GCompileArgs compile_args = 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); + + // 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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(0, cv::norm(out_mat_ocv2, out_mat2, NORM_INF)); + EXPECT_EQ(0, cv::norm(out_mat_ocv3, out_mat3, NORM_INF)); + EXPECT_EQ(0, cv::norm(out_mat_ocv4, out_mat4, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Merge3PerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + cv::GCompileArgs compile_args = get<1>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Merge4PerfTest, TestPerformance) +{ + Size sz_in = get<0>(GetParam()); + cv::GCompileArgs compile_args = get<1>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(RemapPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(FlipPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int flipCode = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(CropPerfTest, TestPerformance) +{ + cv::Size sz_in = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::Rect rect_to = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_out); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ConcatHorPerfTest, TestPerformance) +{ + cv::Size sz_out = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ConcatHorVecPerfTest, TestPerformance) +{ + cv::Size sz_out = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ConcatVertPerfTest, TestPerformance) +{ + cv::Size sz_out = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ConcatVertVecPerfTest, TestPerformance) +{ + cv::Size sz_out = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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), std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat_gapi), std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +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()); + cv::GCompileArgs compile_args = get<3>(GetParam()); + + initMatrixRandU(type_mat, sz_in, type_out); + cv::Size sz_lut = cv::Size(1, 256); + cv::Mat in_lut(sz_lut, type_lut); + cv::randu(in_lut, cv::Scalar::all(0), cv::Scalar::all(255)); + + // 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ConvertToPerfTest, TestPerformance) +{ + MatType type_mat = get<0>(GetParam()); + int depth_to = get<1>(GetParam()); + cv::Size sz_in = get<2>(GetParam()); + cv::GCompileArgs compile_args = get<3>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + // FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); + EXPECT_EQ(out_mat_gapi.size(), sz_in); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ResizePerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int interp = get<2>(GetParam()); + cv::Size sz_in = get<3>(GetParam()); + cv::Size sz_out = get<4>(GetParam()); + cv::GCompileArgs compile_args = 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); + 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ResizeFxFyPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + MatType type = get<1>(GetParam()); + int interp = get<2>(GetParam()); + cv::Size sz_in = get<3>(GetParam()); + double fx = get<4>(GetParam()); + double fy = get<5>(GetParam()); + cv::GCompileArgs compile_args = get<6>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison //////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +} +#endif // OPENCV_GAPI_CORE_PERF_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.cpp new file mode 100644 index 000000000..5a2ffb88a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.cpp @@ -0,0 +1,9 @@ +// 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 "gapi_imgproc_perf_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp new file mode 100644 index 000000000..750c0692c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp @@ -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 + + +#ifndef OPENCV_GAPI_IMGPROC_PERF_TESTS_HPP +#define OPENCV_GAPI_IMGPROC_PERF_TESTS_HPP + + + +#include "../../test/common/gapi_tests_common.hpp" +#include "opencv2/gapi/imgproc.hpp" + +namespace opencv_test +{ + + using namespace perf; + + //------------------------------------------------------------------------------ + +class SepFilterPerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, int, cv::GCompileArgs>> {}; +class Filter2DPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int, cv::GCompileArgs>> {}; +class BoxFilterPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int, cv::GCompileArgs>> {}; +class BlurPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int, cv::GCompileArgs>> {}; +class GaussianBlurPerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, cv::GCompileArgs>> {}; +class MedianBlurPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size, cv::GCompileArgs>> {}; +class ErodePerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int, cv::GCompileArgs>> {}; +class Erode3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType, cv::Size, int, cv::GCompileArgs>> {}; +class DilatePerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int, cv::GCompileArgs>> {}; +class Dilate3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,int, cv::GCompileArgs>> {}; +class SobelPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int,int, cv::GCompileArgs>> {}; +class CannyPerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,double,double,int,bool, cv::GCompileArgs>> {}; +class EqHistPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; +class RGB2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; +class BGR2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; +class RGB2YUVPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; +class YUV2RGBPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; +class RGB2LabPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; +class BGR2LUVPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; +class LUV2BGRPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; +class BGR2YUVPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; +class YUV2BGRPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {}; +} +#endif //OPENCV_GAPI_IMGPROC_PERF_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp new file mode 100644 index 000000000..5a13cfeeb --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp @@ -0,0 +1,909 @@ +// 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_PERF_TESTS_INL_HPP +#define OPENCV_GAPI_IMGPROC_PERF_TESTS_INL_HPP + + +#include <iostream> + +#include "gapi_imgproc_perf_tests.hpp" + +namespace opencv_test +{ + + using namespace perf; + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SepFilterPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, dtype = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, dtype, 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, 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Filter2DPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, borderType = 0, dtype = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, borderType, dtype, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BoxFilterPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int filterSize = 0, borderType = 0, dtype = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, filterSize, sz, borderType, dtype, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BlurPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int filterSize = 0, borderType = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, filterSize, sz, borderType, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(GaussianBlurPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(MedianBlurPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(ErodePerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, kernType = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, kernType, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Erode3x3PerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int numIters = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, sz, numIters, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(DilatePerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, kernType = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, kernType, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(Dilate3x3PerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int numIters = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, sz, numIters, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(SobelPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, dtype = 0, dx = 0, dy = 0; + cv::Size sz; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, kernSize, sz, dtype, dx, dy, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(CannyPerfTest, TestPerformance) +{ + compare_f cmpF; + MatType type; + int apSize = 0; + double thrLow = 0.0, thrUp = 0.0; + cv::Size sz; + bool l2gr = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, sz, thrLow, thrUp, apSize, l2gr, compile_args) = 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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(EqHistPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(RGB2GrayPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BGR2GrayPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(RGB2YUVPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(YUV2RGBPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(RGB2LabPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BGR2LUVPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(LUV2BGRPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } + + SANITY_CHECK_NOTHING(); + +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(BGR2YUVPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(YUV2BGRPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(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, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +} +#endif //OPENCV_GAPI_IMGPROC_PERF_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp new file mode 100644 index 000000000..6957401ad --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp @@ -0,0 +1,286 @@ +// 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 "../common/gapi_core_perf_tests.hpp" +#include "opencv2/gapi/cpu/core.hpp" + +#define CORE_CPU cv::gapi::core::cpu::kernels() + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(DivPerfTestCPU, DivPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(-1, CV_8U, CV_16U, CV_32F), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(DivRCPerfTestCPU, DivRCPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(-1, CV_8U, CV_16U, CV_32F), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(MaskPerfTestCPU, MaskPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_16UC1, CV_16SC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(MeanPerfTestCPU, MeanPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Polar2CartPerfTestCPU, Polar2CartPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Cart2PolarPerfTestCPU, Cart2PolarPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(BitwiseNotPerfTestCPU, BitwiseNotPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(SelectPerfTestCPU, SelectPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(MinPerfTestCPU, MinPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(MaxPerfTestCPU, MaxPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffPerfTestCPU, AbsDiffPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffCPerfTestCPU, AbsDiffCPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(SumPerfTestCPU, SumPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(0.0), + Values(cv::compile_args(CORE_CPU)))); + +// FIXME: Comparison introduced by YL doesn't work with C3 +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), + Values(0.5000005), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(0.0), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(IntegralPerfTestCPU, IntegralPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ThresholdPerfTestCPU, ThresholdOTPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1), + Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(InRangePerfTestCPU, InRangePerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Split3PerfTestCPU, Split3PerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Split4PerfTestCPU, Split4PerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Merge3PerfTestCPU, Merge3PerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(Merge4PerfTestCPU, Merge4PerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(RemapPerfTestCPU, RemapPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +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), + Values(cv::compile_args(CORE_CPU)))); + +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)), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatHorPerfTestCPU, ConcatHorPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatHorVecPerfTestCPU, ConcatHorVecPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatVertPerfTestCPU, ConcatVertPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatVertVecPerfTestCPU, ConcatVertVecPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(LUTPerfTestCPU, LUTPerfTest, + Combine(Values(CV_8UC1, CV_8UC3), + Values(CV_8UC1), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(LUTPerfTestCustomCPU, LUTPerfTest, + Combine(Values(CV_8UC3), + Values(CV_8UC3), + Values(szSmall128, szVGA, sz720p, sz1080p), + Values(cv::compile_args(CORE_CPU)))); + + +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), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ResizePerfTestCPU, ResizePerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ResizeFxFyPerfTestCPU, ResizeFxFyPerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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(cv::compile_args(CORE_CPU)))); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp new file mode 100644 index 000000000..ea3d753f2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp @@ -0,0 +1,188 @@ +// 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 "../common/gapi_imgproc_perf_tests.hpp" +#include "opencv2/gapi/cpu/imgproc.hpp" + + +#define IMGPROC_CPU cv::gapi::imgproc::cpu::kernels() + +namespace opencv_test +{ + +INSTANTIATE_TEST_CASE_P(SepFilterPerfTestCPU_8U, SepFilterPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3), + Values(3), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_16S, CV_32F), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(SepFilterPerfTestCPU_other, SepFilterPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_16UC1, CV_16SC1, CV_32FC1), + Values(3), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_32F), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(Filter2DPerfTestCPU, Filter2DPerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestCPU, BoxFilterPerfTest, + Combine(Values(AbsTolerance(0).to_compare_f()), + 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(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BlurPerfTestCPU, BlurPerfTest, + Combine(Values(AbsTolerance(0).to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::BORDER_DEFAULT), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestCPU, GaussianBlurPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestCPU, MedianBlurPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(ErodePerfTestCPU, ErodePerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(Erode3x3PerfTestCPU, Erode3x3PerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(szVGA, sz720p, sz1080p), + Values(1, 2, 4), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(DilatePerfTestCPU, DilatePerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(Dilate3x3PerfTestCPU, Dilate3x3PerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(szVGA, sz720p, sz1080p), + Values(1, 2, 4), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(SobelPerfTestCPU, SobelPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_16S, CV_32F), + Values(0, 1), + Values(1, 2), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(SobelPerfTestCPU32F, SobelPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(CV_32F), + Values(0, 1), + Values(1, 2), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(CannyPerfTestCPU, CannyPerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(EqHistPerfTestCPU, EqHistPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestCPU, RGB2GrayPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2GrayPerfTestCPU, BGR2GrayPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2YUVPerfTestCPU, RGB2YUVPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2RGBPerfTestCPU, YUV2RGBPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2LabPerfTestCPU, RGB2LabPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2LUVPerfTestCPU, BGR2LUVPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(LUV2BGRPerfTestCPU, LUV2BGRPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2YUVPerfTestCPU, BGR2YUVPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestCPU, YUV2BGRPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp new file mode 100644 index 000000000..a5d13e661 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp @@ -0,0 +1,76 @@ +// 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 "../common/gapi_imgproc_perf_tests.hpp" + +#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels() + +namespace opencv_test +{ + + INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), // add CV_32FC1 when ready + Values(3), // add 5x5 once supported + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_16S, CV_32F), + Values(0, 1), + Values(1, 2), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid32F, SobelPerfTest, + Combine(Values(ToleranceFilter(1e-3f, 0.0).to_compare_f()), + Values(CV_32FC1), + Values(3), // add 5x5 once supported + Values(szVGA, sz720p, sz1080p), + Values(CV_32F), + Values(0, 1), + Values(1, 2), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestFluid, RGB2GrayPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(BGR2GrayPerfTestFluid, BGR2GrayPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(RGB2YUVPerfTestFluid, RGB2YUVPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(YUV2RGBPerfTestFluid, YUV2RGBPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(BGR2YUVPerfTestFluid, BGR2YUVPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestFluid, YUV2BGRPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(BGR2LUVPerfTestFluid, BGR2LUVPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + + INSTANTIATE_TEST_CASE_P(RGB2LabPerfTestFluid, RGB2LabPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_core_perf_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_core_perf_tests_gpu.cpp new file mode 100644 index 000000000..652cbae6b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_core_perf_tests_gpu.cpp @@ -0,0 +1,291 @@ +// 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 "../common/gapi_core_perf_tests.hpp" +#include "opencv2/gapi/gpu/core.hpp" + +#define CORE_GPU cv::gapi::core::gpu::kernels() + +namespace opencv_test +{ + +INSTANTIATE_TEST_CASE_P(AddPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(AddCPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(SubPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(SubCPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(SubRCPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(MulPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(MulDoublePerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(MulCPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(DivPerfTestGPU, DivPerfTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values( -1, CV_8U, CV_16U, CV_32F ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(DivCPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(DivRCPerfTestGPU, DivRCPerfTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values( -1, CV_8U, CV_16U, CV_32F ), + Values(cv::compile_args(CORE_GPU)))); +//TODO: mask test doesn't work +#if 0 +INSTANTIATE_TEST_CASE_P(MaskPerfTestGPU, MaskPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_16UC1, CV_16SC1), + Values(cv::compile_args(CORE_GPU)))); +#endif + +INSTANTIATE_TEST_CASE_P(MeanPerfTestGPU, MeanPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Polar2CartPerfTestGPU, Polar2CartPerfTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Cart2PolarPerfTestGPU, Cart2PolarPerfTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-2, 2).to_compare_f()), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(CmpPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(CmpWithScalarPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(BitwisePerfTestGPU, BitwisePerfTest, + Combine(Values(AND, OR, XOR), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(BitwiseNotPerfTestGPU, BitwiseNotPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(SelectPerfTestGPU, SelectPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(MinPerfTestGPU, MinPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(MaxPerfTestGPU, MaxPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffPerfTestGPU, AbsDiffPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffCPerfTestGPU, AbsDiffCPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(SumPerfTestGPU, SumPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(4.0), //TODO: too relaxed? + Values(cv::compile_args(CORE_GPU)))); + +// FIXME: Comparison introduced by YL doesn't work with C3 +INSTANTIATE_TEST_CASE_P(AddWeightedPerfTestGPU, 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 ), + Values(0.50005), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(NormPerfTestGPU, 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 ), + Values(4.0), //TODO: too relaxed? + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(IntegralPerfTestGPU, IntegralPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ThresholdPerfTestGPU, 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), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ThresholdPerfTestGPU, ThresholdOTPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1 ), + Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(InRangePerfTestGPU, InRangePerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Split3PerfTestGPU, Split3PerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Split4PerfTestGPU, Split4PerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Merge3PerfTestGPU, Merge3PerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Merge4PerfTestGPU, Merge4PerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(RemapPerfTestGPU, RemapPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(FlipPerfTestGPU, FlipPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(0,1,-1), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(CropPerfTestGPU, 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)), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatHorPerfTestGPU, ConcatHorPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatVertPerfTestGPU, ConcatVertPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); + +//TODO: fix this backend to allow ConcatVertVec ConcatHorVec +#if 0 +INSTANTIATE_TEST_CASE_P(ConcatHorVecPerfTestGPU, ConcatHorVecPerfTest, + Combine(Values(szSmall128, szVGA, sz720p, sz1080p), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(cv::compile_args(CORE_GPU)))); + + +INSTANTIATE_TEST_CASE_P(ConcatVertVecPerfTestGPU, ConcatVertVecPerfTest, + Combine(Values( szSmall128, szVGA, sz720p, sz1080p ), + Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::compile_args(CORE_GPU)))); +#endif + +INSTANTIATE_TEST_CASE_P(LUTPerfTestGPU, LUTPerfTest, + Combine(Values(CV_8UC1, CV_8UC3), + Values(CV_8UC1), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(LUTPerfTestCustomGPU, LUTPerfTest, + Combine(Values(CV_8UC3), + Values(CV_8UC3), + Values( szSmall128, szVGA, sz720p, sz1080p ), + Values(cv::compile_args(CORE_GPU)))); + + +INSTANTIATE_TEST_CASE_P(ConvertToPerfTestGPU, 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 ), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ResizePerfTestGPU, ResizePerfTest, + Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ResizeFxFyPerfTestGPU, ResizeFxFyPerfTest, + Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(cv::compile_args(CORE_GPU)))); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_imgproc_perf_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_imgproc_perf_tests_gpu.cpp new file mode 100644 index 000000000..14ef60606 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/gpu/gapi_imgproc_perf_tests_gpu.cpp @@ -0,0 +1,180 @@ +// 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 "../common/gapi_imgproc_perf_tests.hpp" +#include "opencv2/gapi/gpu/imgproc.hpp" + +#define IMGPROC_GPU cv::gapi::imgproc::gpu::kernels() + +namespace opencv_test +{ + + +INSTANTIATE_TEST_CASE_P(SepFilterPerfTestGPU_8U, SepFilterPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + Values(CV_8UC1, CV_8UC3), + Values(3), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_16S, CV_32F), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(SepFilterPerfTestGPU_other, SepFilterPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + Values(CV_16UC1, CV_16SC1, CV_32FC1), + Values(3), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_32F), + Values(cv::compile_args(IMGPROC_GPU)))); + + + +INSTANTIATE_TEST_CASE_P(Filter2DPerfTestGPU, Filter2DPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestGPU, BoxFilterPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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(cv::compile_args(IMGPROC_GPU)))); //TODO: 8UC1 doesn't work + +INSTANTIATE_TEST_CASE_P(BlurPerfTestGPU, BlurPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::BORDER_DEFAULT), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestGPU, GaussianBlurPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), //TODO: too relaxed? + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestGPU, MedianBlurPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(ErodePerfTestGPU, ErodePerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(Erode3x3PerfTestGPU, Erode3x3PerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(szVGA, sz720p, sz1080p), + Values(1,2,4), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(DilatePerfTestGPU, DilatePerfTest, + Combine(Values(AbsExact().to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(Dilate3x3PerfTestGPU, Dilate3x3PerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(szVGA, sz720p, sz1080p), + Values(1,2,4), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(SobelPerfTestGPU, SobelPerfTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1/*, CV_32FC1*/), //TODO: CV_32FC1 fails accuracy + Values(3, 5), + Values(szVGA, sz720p, sz1080p), + Values(-1, CV_32F), + Values(0, 1), + Values(1, 2), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(CannyPerfTestGPU, CannyPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + 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), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(EqHistPerfTestGPU, EqHistPerfTest, + Combine(Values(AbsExact().to_compare_f()), // FIXIT unrealiable check + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestGPU, RGB2GrayPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2GrayPerfTestGPU, BGR2GrayPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2YUVPerfTestGPU, RGB2YUVPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2RGBPerfTestGPU, YUV2RGBPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2LabPerfTestGPU, RGB2LabPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2LUVPerfTestGPU, BGR2LUVPerfTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(LUV2BGRPerfTestGPU, LUV2BGRPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2YUVPerfTestGPU, BGR2YUVPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestGPU, YUV2BGRPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_GPU)))); + +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp new file mode 100644 index 000000000..48786b6a9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/internal/gapi_compiler_perf_tests.cpp @@ -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 "perf_precomp.hpp" +#include "../../test/common/gapi_tests_common.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/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_main.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_main.cpp new file mode 100644 index 000000000..8d6d77edc --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_main.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp new file mode 100644 index 000000000..abd7cbe66 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp @@ -0,0 +1,25 @@ +// 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/gpu/ggpukernel.hpp" +#include "opencv2/gapi/operators.hpp" + +#include "opencv2/gapi/fluid/core.hpp" +#include "opencv2/gapi/fluid/imgproc.hpp" + +#endif // __OPENCV_GAPI_PERF_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/samples/api_example.cpp b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_example.cpp new file mode 100644 index 000000000..a731000f4 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_example.cpp @@ -0,0 +1,34 @@ +#include <opencv2/videoio.hpp> +#include <opencv2/highgui.hpp> +#include <opencv2/gapi.hpp> +#include <opencv2/gapi/core.hpp> +#include <opencv2/gapi/imgproc.hpp> + +int main(int argc, char *argv[]) +{ + cv::VideoCapture cap; + if (argc > 1) cap.open(argv[1]); + else cap.open(0); + CV_Assert(cap.isOpened()); + + cv::GMat in; + cv::GMat vga = cv::gapi::resize(in, cv::Size(), 0.5, 0.5); + cv::GMat gray = cv::gapi::BGR2Gray(vga); + cv::GMat blurred = cv::gapi::blur(gray, cv::Size(5,5)); + cv::GMat edges = cv::gapi::Canny(blurred, 32, 128, 3); + cv::GMat b,g,r; + std::tie(b,g,r) = cv::gapi::split3(vga); + cv::GMat out = cv::gapi::merge3(b, g | edges, r); + cv::GComputation ac(in, out); + + cv::Mat input_frame; + cv::Mat output_frame; + CV_Assert(cap.read(input_frame)); + do + { + ac.apply(input_frame, output_frame); + cv::imshow("output", output_frame); + } while (cap.read(input_frame) && cv::waitKey(30) < 0); + + return 0; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp new file mode 100644 index 000000000..5e8859da1 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp @@ -0,0 +1,82 @@ +#include <opencv2/videoio.hpp> +#include <opencv2/highgui.hpp> +#include <opencv2/gapi.hpp> +#include <opencv2/gapi/core.hpp> +#include <opencv2/gapi/imgproc.hpp> + +#include <opencv2/gapi/cpu/gcpukernel.hpp> + +#include <opencv2/gapi/fluid/core.hpp> +#include <opencv2/gapi/fluid/imgproc.hpp> + +G_TYPED_KERNEL(IAdd, <cv::GMat(cv::GMat)>, "test.custom.add") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +G_TYPED_KERNEL(IFilter2D, <cv::GMat(cv::GMat)>, "test.custom.filter2d") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +G_TYPED_KERNEL(IRGB2YUV, <cv::GMat(cv::GMat)>, "test.custom.add") { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } +}; +GAPI_OCV_KERNEL(CustomAdd, IAdd) { static void run(cv::Mat, cv::Mat &) {} }; +GAPI_OCV_KERNEL(CustomFilter2D, IFilter2D) { static void run(cv::Mat, cv::Mat &) {} }; +GAPI_OCV_KERNEL(CustomRGB2YUV, IRGB2YUV) { static void run(cv::Mat, cv::Mat &) {} }; + +int main(int argc, char *argv[]) +{ + if (argc < 3) + return -1; + + cv::Mat input = cv::imread(argv[1]); + cv::Mat output; + + { + //! [graph_def] + cv::GMat in; + cv::GMat gx = cv::gapi::Sobel(in, CV_32F, 1, 0); + cv::GMat gy = cv::gapi::Sobel(in, CV_32F, 0, 1); + cv::GMat g = cv::gapi::sqrt(cv::gapi::mul(gx, gx) + cv::gapi::mul(gy, gy)); + cv::GMat out = cv::gapi::convertTo(g, CV_8U); + //! [graph_def] + + //! [graph_decl_apply] + //! [graph_cap_full] + cv::GComputation sobelEdge(cv::GIn(in), cv::GOut(out)); + //! [graph_cap_full] + sobelEdge.apply(input, output); + //! [graph_decl_apply] + + //! [apply_with_param] + cv::gapi::GKernelPackage kernels = cv::gapi::combine + (cv::gapi::core::fluid::kernels(), + cv::gapi::imgproc::fluid::kernels(), + cv::unite_policy::KEEP); + sobelEdge.apply(input, output, cv::compile_args(kernels)); + //! [apply_with_param] + + //! [graph_cap_sub] + cv::GComputation sobelEdgeSub(cv::GIn(gx, gy), cv::GOut(out)); + //! [graph_cap_sub] + } + //! [graph_gen] + cv::GComputation sobelEdgeGen([](){ + cv::GMat in; + cv::GMat gx = cv::gapi::Sobel(in, CV_32F, 1, 0); + cv::GMat gy = cv::gapi::Sobel(in, CV_32F, 0, 1); + cv::GMat g = cv::gapi::sqrt(cv::gapi::mul(gx, gx) + cv::gapi::mul(gy, gy)); + cv::GMat out = cv::gapi::convertTo(g, CV_8U); + return cv::GComputation(in, out); + }); + //! [graph_gen] + + cv::imwrite(argv[2], output); + + //! [kernels_snippet] + cv::gapi::GKernelPackage pkg = cv::gapi::kernels + < CustomAdd + , CustomFilter2D + , CustomRGB2YUV + >(); + //! [kernels_snippet] + return 0; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/samples/kernel_api_snippets.cpp b/inference-engine/thirdparty/fluid/modules/gapi/samples/kernel_api_snippets.cpp new file mode 100644 index 000000000..a30161d27 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/samples/kernel_api_snippets.cpp @@ -0,0 +1,157 @@ +// [filter2d_api] +#include <opencv2/gapi.hpp> + +G_TYPED_KERNEL(GFilter2D, + <cv::GMat(cv::GMat,int,cv::Mat,cv::Point,double,int,cv::Scalar)>, + "org.opencv.imgproc.filters.filter2D") +{ + static cv::GMatDesc // outMeta's return value type + outMeta(cv::GMatDesc in , // descriptor of input GMat + int ddepth , // depth parameter + cv::Mat /* coeffs */, // (unused) + cv::Point /* anchor */, // (unused) + double /* scale */, // (unused) + int /* border */, // (unused) + cv::Scalar /* bvalue */ ) // (unused) + { + return in.withDepth(ddepth); + } +}; +// [filter2d_api] + +cv::GMat filter2D(cv::GMat , + int , + cv::Mat , + cv::Point , + double , + int , + cv::Scalar); + +// [filter2d_wrap] +cv::GMat filter2D(cv::GMat in, + int ddepth, + cv::Mat k, + cv::Point anchor = cv::Point(-1,-1), + double scale = 0., + int border = cv::BORDER_DEFAULT, + cv::Scalar bval = cv::Scalar(0)) +{ + return GFilter2D::on(in, ddepth, k, anchor, scale, border, bval); +} +// [filter2d_wrap] + +// [compound] +#include <opencv2/gapi/gcompoundkernel.hpp> // GAPI_COMPOUND_KERNEL() + +using PointArray2f = cv::GArray<cv::Point2f>; + +G_TYPED_KERNEL(HarrisCorners, + <PointArray2f(cv::GMat,int,double,double,int,double)>, + "org.opencv.imgproc.harris_corner") +{ + static cv::GArrayDesc outMeta(const cv::GMatDesc &, + int, + double, + double, + int, + double) + { + // No special metadata for arrays in G-API (yet) + return cv::empty_array_desc(); + } +}; + +// Define Fluid-backend-local kernels which form GoodFeatures +G_TYPED_KERNEL(HarrisResponse, + <cv::GMat(cv::GMat,double,int,double)>, + "org.opencv.fluid.harris_response") +{ + static cv::GMatDesc outMeta(const cv::GMatDesc &in, + double, + int, + double) + { + return in.withType(CV_32F, 1); + } +}; + +G_TYPED_KERNEL(ArrayNMS, + <PointArray2f(cv::GMat,int,double)>, + "org.opencv.cpu.nms_array") +{ + static cv::GArrayDesc outMeta(const cv::GMatDesc &, + int, + double) + { + return cv::empty_array_desc(); + } +}; + +GAPI_COMPOUND_KERNEL(GFluidHarrisCorners, HarrisCorners) +{ + static PointArray2f + expand(cv::GMat in, + int maxCorners, + double quality, + double minDist, + int blockSize, + double k) + { + cv::GMat response = HarrisResponse::on(in, quality, blockSize, k); + return ArrayNMS::on(response, maxCorners, minDist); + } +}; + +// Then implement HarrisResponse as Fluid kernel and NMSresponse +// as a generic (OpenCV) kernel +// [compound] + +// [filter2d_ocv] +#include <opencv2/gapi/cpu/gcpukernel.hpp> // GAPI_OCV_KERNEL() +#include <opencv2/imgproc.hpp> // cv::filter2D() + +GAPI_OCV_KERNEL(GCPUFilter2D, GFilter2D) +{ + static void + run(const cv::Mat &in, // in - derived from GMat + const int ddepth, // opaque (passed as-is) + const cv::Mat &k, // opaque (passed as-is) + const cv::Point &anchor, // opaque (passed as-is) + const double delta, // opaque (passed as-is) + const int border, // opaque (passed as-is) + const cv::Scalar &, // opaque (passed as-is) + cv::Mat &out) // out - derived from GMat (retval) + { + cv::filter2D(in, out, ddepth, k, anchor, delta, border); + } +}; +// [filter2d_ocv] + +int main(int, char *[]) +{ + std::cout << "This sample is non-complete. It is used as code snippents in documentation." << std::endl; + +cv::Mat conv_kernel_mat; + +{ +// [filter2d_on] +cv::GMat in; +cv::GMat out = GFilter2D::on(/* GMat */ in, + /* int */ -1, + /* Mat */ conv_kernel_mat, + /* Point */ cv::Point(-1,-1), + /* double */ 0., + /* int */ cv::BORDER_DEFAULT, + /* Scalar */ cv::Scalar(0)); +// [filter2d_on] +} + +{ +// [filter2d_wrap_call] +cv::GMat in; +cv::GMat out = filter2D(in, -1, conv_kernel_mat); +// [filter2d_wrap_call] +} + +return 0; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/README.md b/inference-engine/thirdparty/fluid/modules/gapi/src/api/README.md new file mode 100644 index 000000000..970f730ec --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/README.md @@ -0,0 +1 @@ +This directory contains implementation of G-API frontend (public API classes).
\ No newline at end of file diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.cpp new file mode 100644 index 000000000..744db1671 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.cpp @@ -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 "precomp.hpp" +#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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.hpp new file mode 100644 index 000000000..edab0a08b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gapi_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp new file mode 100644 index 000000000..0fd19a7e6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp @@ -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 "precomp.hpp" +#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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp new file mode 100644 index 000000000..8144d21d4 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp @@ -0,0 +1,353 @@ +// 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 <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, bool is_umat) +{ + switch (rc.shape) + { + case GShape::GMAT: + { + switch (arg.index()) + { + case GRunArg::index_of<cv::gapi::own::Mat>() : + if (is_umat) + { +#if !defined(GAPI_STANDALONE) + auto& mag_umat = mag.template slot<cv::UMat>()[rc.id]; + mag_umat = to_ocv(util::get<cv::gapi::own::Mat>(arg)).getUMat(ACCESS_READ); +#else + util::throw_error(std::logic_error("UMat is not supported in stadnalone build")); +#endif // !defined(GAPI_STANDALONE) + } + else + { + auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id]; + mag_mat = util::get<cv::gapi::own::Mat>(arg); + } + break; +#if !defined(GAPI_STANDALONE) + case GRunArg::index_of<cv::Mat>() : + if (is_umat) + { + auto& mag_umat = mag.template slot<cv::UMat>()[rc.id]; + mag_umat = (util::get<cv::UMat>(arg)); + } + else + { + auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id]; + mag_mat = to_own(util::get<cv::Mat>(arg)); + } + break; +#endif // !defined(GAPI_STANDALONE) + 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; +#if !defined(GAPI_STANDALONE) + case GRunArg::index_of<cv::Scalar>() : mag_scalar = to_own(util::get<cv::Scalar>(arg)); break; +#endif // !defined(GAPI_STANDALONE) + 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, bool is_umat) +{ + switch (rc.shape) + { + case GShape::GMAT: + { + switch (arg.index()) + { + case GRunArgP::index_of<cv::gapi::own::Mat*>() : + if (is_umat) + { +#if !defined(GAPI_STANDALONE) + auto& mag_umat = mag.template slot<cv::UMat>()[rc.id]; + mag_umat = to_ocv(*(util::get<cv::gapi::own::Mat*>(arg))).getUMat(ACCESS_RW); +#else + util::throw_error(std::logic_error("UMat is not supported in standalone build")); +#endif // !defined(GAPI_STANDALONE) + } + else + { + auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id]; + mag_mat = *util::get<cv::gapi::own::Mat*>(arg); + } + break; +#if !defined(GAPI_STANDALONE) + case GRunArgP::index_of<cv::Mat*>() : + if (is_umat) + { + auto& mag_umat = mag.template slot<cv::UMat>()[rc.id]; + mag_umat = (*util::get<cv::UMat*>(arg)); + } + else + { + auto& mag_mat = mag.template slot<cv::gapi::own::Mat>()[rc.id]; + mag_mat = to_own(*util::get<cv::Mat*>(arg)); + } + break; +#endif // !defined(GAPI_STANDALONE) + 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; +#if !defined(GAPI_STANDALONE) + case GRunArgP::index_of<cv::Scalar*>() : mag_scalar = to_own(*util::get<cv::Scalar*>(arg)); break; +#endif // !defined(GAPI_STANDALONE) + 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, bool is_umat) +{ + switch (rc.shape) + { + case GShape::GMAT: + if (is_umat) + { +#if !defined(GAPI_STANDALONE) + return GRunArgP(&mag.template slot<cv::UMat>()[rc.id]); +#else + util::throw_error(std::logic_error("UMat is not supported in standalone build")); +#endif // !defined(GAPI_STANDALONE) + } + else + 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, bool is_umat) +{ + 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; +#if !defined(GAPI_STANDALONE) + case GRunArgP::index_of<cv::Mat*>() : out_arg_data = util::get<cv::Mat*>(g_arg)->data; break; + case GRunArgP::index_of<cv::UMat*>() : out_arg_data = (util::get<cv::UMat*>(g_arg))->getMat(ACCESS_RW).data; break; +#endif // !defined(GAPI_STANDALONE) + default: util::throw_error(std::logic_error("content type of the runtime argument does not match to resource description ?")); + } + if (is_umat) + { +#if !defined(GAPI_STANDALONE) + auto& in_mag = mag.template slot<cv::UMat>().at(rc.id); + GAPI_Assert((out_arg_data == (in_mag.getMat(ACCESS_RW).data)) && " data for output parameters was reallocated ?"); +#else + util::throw_error(std::logic_error("UMat is not supported in standalone build")); +#endif // !defined(GAPI_STANDALONE) + } + else + { + 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; +#if !defined(GAPI_STANDALONE) + 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; +#endif // !defined(GAPI_STANDALONE) + 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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp new file mode 100644 index 000000000..1c6e29715 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp new file mode 100644 index 000000000..2dd823daa --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp @@ -0,0 +1,65 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall_priv.hpp new file mode 100644 index 000000000..ffb122ec8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp new file mode 100644 index 000000000..ab761edf9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp @@ -0,0 +1,238 @@ +// 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 <algorithm> // remove_if +#include <cctype> // isspace (non-locale version) +#include <ade/util/algorithm.hpp> + +#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(); +} + +// FIXME: Introduce similar query/test method for GMetaArgs as a building block +// for functions like this? +static bool formats_are_same(const cv::GMetaArgs& metas1, const cv::GMetaArgs& metas2) +{ + return std::equal(metas1.cbegin(), metas1.cend(), metas2.cbegin(), + [](const cv::GMetaArg& meta1, const cv::GMetaArg& meta2) { + if (meta1.index() == meta2.index() && meta1.index() == cv::GMetaArg::index_of<cv::GMatDesc>()) + { + const auto& desc1 = cv::util::get<cv::GMatDesc>(meta1); + const auto& desc2 = cv::util::get<cv::GMatDesc>(meta2); + + // comparison by size is omitted + return (desc1.chan == desc2.chan && + desc1.depth == desc2.depth); + } + else + { + return meta1 == meta2; + } + }); +} + +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) + { + if (m_priv->m_lastCompiled && + m_priv->m_lastCompiled.canReshape() && + formats_are_same(m_priv->m_lastMetas, in_metas)) + { + m_priv->m_lastCompiled.reshape(in_metas, args); + } + else + { + // 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; + } + m_priv->m_lastCompiled(std::move(ins), std::move(outs)); +} + +void cv::GComputation::apply(const std::vector<cv::gapi::own::Mat> &ins, + const std::vector<cv::gapi::own::Mat> &outs, + GCompileArgs &&args) +{ + GRunArgs call_ins; + GRunArgsP call_outs; + + auto tmp = outs; + for (const cv::gapi::own::Mat &m : ins) { call_ins.emplace_back(m); } + for ( cv::gapi::own::Mat &m : tmp) { call_outs.emplace_back(&m); } + + apply(std::move(call_ins), std::move(call_outs), std::move(args)); +} + +#if !defined(GAPI_STANDALONE) +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)); +} +#endif // !defined(GAPI_STANDALONE) + +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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation_priv.hpp new file mode 100644 index 000000000..13d1b9afa --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp new file mode 100644 index 000000000..f8c851abf --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp @@ -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 + + +#include "precomp.hpp" +#include <iostream> // cerr +#include <functional> // hash +#include <numeric> // accumulate + +#include <ade/util/algorithm.hpp> + +#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()); +} + +void cv::gapi::GKernelPackage::removeAPI(const std::string &id) +{ + for (auto &bk : m_backend_kernels) + bk.second.erase(id); +} + +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 RHS package has a precedense, 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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp new file mode 100644 index 000000000..e8c528555 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp @@ -0,0 +1,78 @@ +// 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/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; +} + +#if !defined(GAPI_STANDALONE) +cv::GMatDesc cv::descr_of(const cv::Mat &mat) +{ + return GMatDesc{mat.depth(), mat.channels(), {mat.cols, mat.rows}}; +} +cv::GMatDesc cv::descr_of(const cv::UMat &mat) +{ + return GMatDesc{ mat.depth(), mat.channels(),{ mat.cols, mat.rows } }; +} +#endif + +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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.cpp new file mode 100644 index 000000000..efda5d542 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.cpp @@ -0,0 +1,89 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.hpp new file mode 100644 index 000000000..bd6c7901e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode_priv.hpp new file mode 100644 index 000000000..5425471f8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gnode_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp new file mode 100644 index 000000000..2482d628b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp @@ -0,0 +1,162 @@ +// 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 <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()) + { +#if !defined(GAPI_STANDALONE) + 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))); +#endif // !defined(GAPI_STANDALONE) + + case GRunArg::index_of<cv::gapi::own::Mat>(): + return cv::GMetaArg(descr_of(util::get<cv::gapi::own::Mat>(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()) + { +#if !defined(GAPI_STANDALONE) + case GRunArgP::index_of<cv::Mat*>(): return GMetaArg(descr_of(*util::get<cv::Mat*>(argp))); + case GRunArgP::index_of<cv::UMat*>(): return GMetaArg(descr_of(*util::get<cv::UMat*>(argp))); + case GRunArgP::index_of<cv::Scalar*>(): return GMetaArg(descr_of(*util::get<cv::Scalar*>(argp))); +#endif // !defined(GAPI_STANDALONE) + case GRunArgP::index_of<cv::gapi::own::Mat*>(): return GMetaArg(descr_of(*util::get<cv::gapi::own::Mat*>(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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto_priv.hpp new file mode 100644 index 000000000..2684924c7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto_priv.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp new file mode 100644 index 000000000..30f3dc944 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp @@ -0,0 +1,73 @@ +// 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/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(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(); +} + +#if !defined(GAPI_STANDALONE) +cv::GScalar::GScalar(const cv::Scalar& s) + : m_priv(new GOrigin(GShape::GSCALAR, cv::gimpl::ConstVal(to_own(s)))) +{ +} + +cv::GScalarDesc cv::descr_of(const cv::Scalar& s) +{ + return cv::descr_of(to_own(s)); +} +#endif // !defined(GAPI_STANDALONE) + +namespace cv { +std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &) +{ + os << "(scalar)"; + return os; +} +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp new file mode 100644 index 000000000..c9fe19ed6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp @@ -0,0 +1,359 @@ +// 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/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 phase(const GMat &x, const GMat &y, bool angleInDegrees) +{ + return core::GPhase::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); +} + +GMat sqrt(const GMat& src) +{ + return core::GSqrt::on(src); +} + +} //namespace gapi +} //namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp new file mode 100644 index 000000000..7c4b522e9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp @@ -0,0 +1,144 @@ +// 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/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/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp new file mode 100644 index 000000000..44fc4fa52 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp @@ -0,0 +1,213 @@ +// 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/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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/README.md b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/README.md new file mode 100644 index 000000000..3aeeb1ece --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/README.md @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gbackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gbackend.hpp new file mode 100644 index 000000000..613022cb9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gbackend.hpp @@ -0,0 +1,106 @@ +// 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 +#if !defined(GAPI_STANDALONE) +using Mag = magazine::Class<cv::gapi::own::Mat, cv::UMat, cv::gapi::own::Scalar, cv::detail::VectorRef>; +#else +using Mag = magazine::Class<cv::gapi::own::Mat, cv::gapi::own::Scalar, cv::detail::VectorRef>; +#endif + +namespace magazine +{ + void bindInArg (Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat = false); + void bindOutArg(Mag& mag, const RcDesc &rc, const GRunArgP &arg, bool is_umat = false); + + void resetInternalData(Mag& mag, const Data &d); + cv::GRunArg getArg (const Mag& mag, const RcDesc &ref); + cv::GRunArgP getObjPtr ( Mag& mag, const RcDesc &rc, bool is_umat = false); + void writeBack (const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat = false); +} // 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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp new file mode 100644 index 000000000..948898f28 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp @@ -0,0 +1,20 @@ +// 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/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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp new file mode 100644 index 000000000..89abcef59 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp @@ -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 + + +#include "precomp.hpp" + +#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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp new file mode 100644 index 000000000..5cc8bb0b7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -0,0 +1,229 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp new file mode 100644 index 000000000..6ce8c4883 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp @@ -0,0 +1,72 @@ +// 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 inline bool canReshape() const override { return false; } + virtual inline void reshape(ade::Graph&, const GCompileArgs&) override + { + // FIXME: CPU plugin is in fact reshapeable (as it was initially, + // even before outMeta() has been introduced), so this limitation + // should be dropped. + util::throw_error(std::logic_error("GCPUExecutable::reshape() should never be called")); + } + + virtual void run(std::vector<InObj> &&input_objs, + std::vector<OutObj> &&output_objs) override; +}; + +}} + +#endif // OPENCV_GAPI_GBACKEND_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp new file mode 100644 index 000000000..c42f863bf --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp @@ -0,0 +1,595 @@ +// 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(GCPUPhase, cv::gapi::core::GPhase) +{ + static void run(const cv::Mat &x, const cv::Mat &y, bool angleInDegrees, cv::Mat &out) + { + cv::phase(x, y, out, 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); + } +}; + +GAPI_OCV_KERNEL(GCPUSqrt, cv::gapi::core::GSqrt) +{ + static void run(const cv::Mat& in, cv::Mat &out) + { + cv::sqrt(in, out); + } +}; + +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 + , GCPUPhase + , 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 + , GCPUSqrt + >(); + return pkg; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.hpp new file mode 100644 index 000000000..77e9e82a0 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp new file mode 100644 index 000000000..d14584bfa --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.hpp new file mode 100644 index 000000000..172871a77 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp new file mode 100644 index 000000000..af13eed6c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp @@ -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 + + +#include "precomp.hpp" + +#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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp new file mode 100644 index 000000000..e6eaaae8c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -0,0 +1,1383 @@ +// 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 <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 "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 + { + using namespace cv::gimpl; + GModel::ConstGraph g(graph); + auto isl_graph = g.metadata().get<IslandModel>().model; + GIslandModel::Graph gim(*isl_graph); + + const auto num_islands = std::count_if + (gim.nodes().begin(), gim.nodes().end(), + [&](const ade::NodeHandle &nh) { + return gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND; + }); + + const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args); + if (num_islands > 1 && out_rois.has_value()) + cv::util::throw_error(std::logic_error("GFluidOutputRois feature supports only one-island graphs")); + + auto rois = out_rois.value_or(cv::GFluidOutputRois()); + return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, std::move(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 FluidMapper +{ + FluidMapper(double ratio, int lpi) : m_ratio(ratio), m_lpi(lpi) {} + virtual ~FluidMapper() = default; + virtual int firstWindow(int outCoord, int lpi) const = 0; + virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const = 0; + +protected: + double m_ratio = 0.0; + int m_lpi = 0; +}; + +struct FluidDownscaleMapper : public FluidMapper +{ + virtual int firstWindow(int outCoord, int lpi) const override; + virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const override; + using FluidMapper::FluidMapper; +}; + +struct FluidUpscaleMapper : public FluidMapper +{ + virtual int firstWindow(int outCoord, int lpi) const override; + virtual std::pair<int,int> linesReadAndNextWindow(int outCoord, int lpi) const override; + FluidUpscaleMapper(double ratio, int lpi, int inHeight) : FluidMapper(ratio, lpi), m_inHeight(inHeight) {} +private: + int m_inHeight = 0; +}; + +struct FluidFilterAgent : public FluidAgent +{ +private: + virtual int firstWindow() const override; + virtual std::pair<int,int> linesReadAndnextWindow() const override; + virtual void setRatio(double) override { /* nothing */ } +public: + using FluidAgent::FluidAgent; +}; + +struct FluidResizeAgent : public FluidAgent +{ +private: + virtual int firstWindow() const override; + virtual std::pair<int,int> linesReadAndnextWindow() const override; + virtual void setRatio(double ratio) override; + + std::unique_ptr<FluidMapper> m_mapper; +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) +{ + GAPI_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 maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi) +{ + switch (k.m_kind) + { + case cv::GFluidKernel::Kind::Filter: return k.m_window + lpi - 1; break; + case cv::GFluidKernel::Kind::Resize: + { + if (inH >= outH) + { + // FIXME: + // This is a suboptimal value, can be reduced + return calcResizeWindow(inH, outH) * lpi; + } + else + { + // FIXME: + // This is a suboptimal value, can be reduced + return (inH == 1) ? 1 : 2 + lpi - 1; + } + } break; + default: GAPI_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: GAPI_Assert(false); return 0; + } +} + +inline double inCoord(int outIdx, double ratio) +{ + return outIdx * ratio; +} + +inline int windowStart(int outIdx, double ratio) +{ + return static_cast<int>(inCoord(outIdx, ratio) + 1e-3); +} + +inline int windowEnd(int outIdx, double ratio) +{ + return static_cast<int>(std::ceil(inCoord(outIdx + 1, ratio) - 1e-3)); +} + +inline double inCoordUpscale(int outCoord, double ratio) +{ + // Calculate the projection of output pixel's center + return (outCoord + 0.5) * ratio - 0.5; +} + +inline int upscaleWindowStart(int outCoord, double ratio) +{ + int start = static_cast<int>(inCoordUpscale(outCoord, ratio)); + GAPI_DbgAssert(start >= 0); + return start; +} + +inline 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::FluidDownscaleMapper::firstWindow(int outCoord, int lpi) const +{ + return windowEnd(outCoord + lpi - 1, m_ratio) - windowStart(outCoord, m_ratio); +} + +std::pair<int,int> cv::gimpl::FluidDownscaleMapper::linesReadAndNextWindow(int outCoord, int lpi) const +{ + auto nextStartIdx = outCoord + 1 + m_lpi - 1; + auto nextEndIdx = nextStartIdx + lpi - 1; + + auto currStart = windowStart(outCoord, m_ratio); + auto nextStart = windowStart(nextStartIdx, m_ratio); + auto nextEnd = windowEnd(nextEndIdx, m_ratio); + + auto lines_read = nextStart - currStart; + auto next_window = nextEnd - nextStart; + + return std::make_pair(lines_read, next_window); +} + +int cv::gimpl::FluidUpscaleMapper::firstWindow(int outCoord, int lpi) const +{ + return upscaleWindowEnd(outCoord + lpi - 1, m_ratio, m_inHeight) - upscaleWindowStart(outCoord, m_ratio); +} + +std::pair<int,int> cv::gimpl::FluidUpscaleMapper::linesReadAndNextWindow(int outCoord, int lpi) const +{ + auto nextStartIdx = outCoord + 1 + m_lpi - 1; + auto nextEndIdx = nextStartIdx + lpi - 1; + + auto currStart = upscaleWindowStart(outCoord, m_ratio); + auto nextStart = upscaleWindowStart(nextStartIdx, m_ratio); + auto nextEnd = upscaleWindowEnd(nextEndIdx, m_ratio, m_inHeight); + + auto lines_read = nextStart - currStart; + auto next_window = nextEnd - nextStart; + + return std::make_pair(lines_read, next_window); +} + +int cv::gimpl::FluidFilterAgent::firstWindow() const +{ + return k.m_window + k.m_lpi - 1; +} + +std::pair<int,int> cv::gimpl::FluidFilterAgent::linesReadAndnextWindow() const +{ + int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines - k.m_lpi); + return std::make_pair(k.m_lpi, k.m_window - 1 + lpi); +} + +int cv::gimpl::FluidResizeAgent::firstWindow() const +{ + auto outIdx = out_buffers[0]->priv().y(); + auto lpi = std::min(m_outputLines - m_producedLines, k.m_lpi); + return m_mapper->firstWindow(outIdx, lpi); +} + +std::pair<int,int> cv::gimpl::FluidResizeAgent::linesReadAndnextWindow() const +{ + auto outIdx = out_buffers[0]->priv().y(); + auto lpi = std::min(m_outputLines - m_producedLines - k.m_lpi, k.m_lpi); + return m_mapper->linesReadAndNextWindow(outIdx, lpi); +} + +void cv::gimpl::FluidResizeAgent::setRatio(double ratio) +{ + if (ratio >= 1.0) + { + m_mapper.reset(new FluidDownscaleMapper(ratio, k.m_lpi)); + } + else + { + m_mapper.reset(new FluidUpscaleMapper(ratio, k.m_lpi, in_views[0].meta().size.height)); + } +} + +bool cv::gimpl::FluidAgent::canRead() const +{ + // An agent can work if every input buffer have enough data to start + for (const 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 + GAPI_DbgAssert(!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_DbgAssert(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) + { + auto pair = linesReadAndnextWindow(); + in_view.priv().readDone(pair.first, pair.second); + }; + } + + 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 ////////////////////////////////////////////// + +void cv::gimpl::GFluidExecutable::initBufferRois(std::vector<int>& readStarts, + std::vector<cv::gapi::own::Rect>& rois, + const std::vector<cv::gapi::own::Rect>& out_rois) +{ + GConstFluidModel fg(m_g); + auto proto = m_gm.metadata().get<Protocol>(); + std::stack<ade::NodeHandle> nodesToVisit; + + // FIXME? + // There is possible case when user pass the vector full of default Rect{}-s, + // Can be diagnosed and handled appropriately + if (proto.outputs.size() != out_rois.size()) + { + GAPI_Assert(out_rois.size() == 0); + // No inference required, buffers will obtain roi from meta + return; + } + + // 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); + auto id = m_id_map.at(d.rc); + readStarts[id] = 0; + + if (out_rois[idx] == gapi::own::Rect{}) + { + rois[id] = gapi::own::Rect{ 0, 0, desc.size.width, desc.size.height }; + } + else + { + // Only slices are supported at the moment + GAPI_Assert(out_rois[idx].x == 0); + GAPI_Assert(out_rois[idx].width == desc.size.width); + rois[id] = out_rois[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 && fg.metadata(inNode).contains<FluidData>()) + { + 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: GAPI_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; + // Continue traverse on internal (w.r.t Island) data nodes only. + if (fd.internal) 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()) +} + +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) +{ + 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; + + auto grab_mat_nh = [&](ade::NodeHandle nh) { + auto rc = m_gm.metadata(nh).get<Data>().rc; + if (m_id_map.count(rc) == 0) + { + m_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: m_agents.emplace_back(new FluidResizeAgent(m_g, nh)); break; + default: GAPI_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 int 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 int 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() > 0); + GAPI_Assert(m_id_map.size() == static_cast<size_t>(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(); + m_buffers.resize(m_num_int_buffers + num_scratch); + + // 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::indexed(ade::util::toRange(agent->in_buffer_ids))) + { + auto in_idx = ade::util::index(it); + auto buf_idx = ade::util::value(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.border_size, 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); + } + 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::indexed(ade::util::toRange(agent->out_buffer_ids))) + { + auto out_idx = ade::util::index(it); + auto buf_idx = m_id_map.at(ade::util::value(it)); + agent->out_buffers.at(out_idx) = &m_buffers.at(buf_idx); + } + } + + // After parameters are there, initialize scratch buffers + if (num_scratch) + { + GAPI_LOG_INFO(NULL, "Initializing " << num_scratch << " scratch buffer(s)" << std::endl); + std::size_t last_scratch_id = 0; + + for (auto i : m_scratch_users) + { + auto &agent = m_agents.at(i); + GAPI_Assert(agent->k.m_scratch); + const std::size_t new_scratch_idx = m_num_int_buffers + last_scratch_id; + agent->out_buffers.emplace_back(&m_buffers[new_scratch_idx]); + last_scratch_id++; + } + } + + makeReshape(outputRois); + + std::size_t total_size = 0; + for (const auto &i : ade::util::indexed(m_buffers)) + { + // Check that all internal and scratch buffers are allocated + const auto idx = ade::util::index(i); + const auto b = ade::util::value(i); + if (idx >= m_num_int_buffers || + fg.metadata(m_all_gmat_ids[idx]).get<FluidData>().internal == true) + { + 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"); +} + +namespace +{ + void resetFluidData(ade::Graph& graph) + { + using namespace cv::gimpl; + GModel::Graph g(graph); + GFluidModel fg(graph); + for (const auto node : g.nodes()) + { + if (g.metadata(node).get<NodeType>().t == NodeType::DATA) + { + auto& fd = fg.metadata(node).get<FluidData>(); + fd.latency = 0; + fd.skew = 0; + fd.max_consumption = 0; + } + } + } + + void initFluidUnits(ade::Graph& graph) + { + using namespace cv::gimpl; + GModel::Graph g(graph); + GFluidModel fg(graph); + + auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes(); + for (auto node : sorted) + { + if (fg.metadata(node).contains<FluidUnit>()) + { + 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); + } + } + + GAPI_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 line_consumption = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi); + 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)); + } + } + } + + // FIXME! + // Split into initLineConsumption and initBorderSizes, + // call only consumption related stuff during reshape + void initLineConsumption(ade::Graph& graph) + { + using namespace cv::gimpl; + GModel::Graph g(graph); + GFluidModel fg(graph); + + for (const auto &node : g.nodes()) + { + if (fg.metadata(node).contains<FluidUnit>()) + { + const auto &fu = fg.metadata(node).get<FluidUnit>(); + + for (const 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); + } + } + } + } + + void calcLatency(ade::Graph& graph) + { + using namespace cv::gimpl; + GModel::Graph g(graph); + GFluidModel fg(graph); + + auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes(); + for (const auto &node : sorted) + { + if (fg.metadata(node).contains<FluidUnit>()) + { + 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 (const 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 (const 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)); + } + } + } + } + + void calcSkew(ade::Graph& graph) + { + using namespace cv::gimpl; + GModel::Graph g(graph); + GFluidModel fg(graph); + + auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes(); + for (const auto &node : sorted) + { + if (fg.metadata(node).contains<FluidUnit>()) + { + int max_latency = 0; + for (const 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 (const 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); + } + } + } + } +} + +void cv::gimpl::GFluidExecutable::makeReshape(const std::vector<gapi::own::Rect> &out_rois) +{ + GConstFluidModel fg(m_g); + + // Calculate rois for each fluid buffer + std::vector<int> readStarts(m_num_int_buffers); + std::vector<cv::gapi::own::Rect> rois(m_num_int_buffers); + initBufferRois(readStarts, rois, out_rois); + + // NB: Allocate ALL buffer object at once, and avoid any further reallocations + // (since raw pointers-to-elements are taken) + for (const auto &it : m_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); + + m_buffers[id].priv().init(meta, fd.lpi_write, readStarts[id], rois[id]); + + // TODO: + // Introduce Storage::INTERNAL_GRAPH and Storage::INTERNAL_ISLAND? + if (fd.internal == true) + { + m_buffers[id].priv().allocate(fd.border, fd.border_size, fd.max_consumption, fd.skew); + std::stringstream stream; + m_buffers[id].debug(stream); + GAPI_LOG_INFO(NULL, stream.str()); + } + } + + // Allocate views, initialize agents + for (auto &agent : m_agents) + { + const auto &fu = fg.metadata(agent->op_handle).get<FluidUnit>(); + for (auto it : ade::util::indexed(ade::util::toRange(agent->in_buffer_ids))) + { + auto in_idx = ade::util::index(it); + auto buf_idx = ade::util::value(it); + + if (buf_idx >= 0) + { + agent->in_views[in_idx].priv().allocate(fu.line_consumption, fu.border); + } + } + + agent->setRatio(fu.ratio); + agent->m_outputLines = agent->out_buffers.front()->priv().outputLines(); + } + + // Initialize scratch buffers + if (m_scratch_users.size()) + { + for (auto i : m_scratch_users) + { + auto &agent = m_agents.at(i); + GAPI_Assert(agent->k.m_scratch); + + // Trigger Scratch buffer initialization method + agent->k.m_is(GModel::collectInputMeta(m_gm, agent->op_handle), agent->in_args, *agent->out_buffers.back()); + std::stringstream stream; + agent->out_buffers.back()->debug(stream); + GAPI_LOG_INFO(NULL, stream.str()); + } + } + + // FIXME: calculate the size (lpi * ..) + m_script.clear(); + m_script.reserve(10000); +} + +void cv::gimpl::GFluidExecutable::reshape(ade::Graph &g, const GCompileArgs &args) +{ + // FIXME: Probably this needs to be integrated into common pass re-run routine + // Backends may want to mark with passes to re-run on reshape and framework could + // do it system-wide (without need in every backend handling reshape() directly). + // This design needs to be analyzed for implementation. + resetFluidData(g); + initFluidUnits(g); + initLineConsumption(g); + calcLatency(g); + calcSkew(g); + const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args).value_or(cv::GFluidOutputRois()); + makeReshape(out_rois.rois); +} + +// 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(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(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_DbgAssert(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! + + if (m_script.empty()) + { + 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; + m_script.push_back(agent.get()); + } + if (!agent->done()) complete = false; + } + } + GAPI_Assert(work_done || complete); + } while (!complete); // FIXME: number of iterations can be calculated statically + } + else + { + for (auto &agent : m_script) + { + agent->doWork(); + } + } +} + +// 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", "init_fluid_data", [](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); + + GFluidModel fg(ctx.graph); + + const auto setFluidData = [&](ade::NodeHandle nh, bool internal) { + FluidData fd; + fd.internal = internal; + fg.metadata(nh).set(fd); + }; + + for (const auto& nh : gim.nodes()) + { + if (gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND) + { + const auto isl = gim.metadata(nh).get<FusedIsland>().object; + if (isl->backend() == cv::gapi::fluid::backend()) + { + // add FluidData to all data nodes inside island + for (const auto node : isl->contents()) + { + if (g.metadata(node).get<NodeType>().t == NodeType::DATA) + setFluidData(node, true); + } + + // add FluidData to slot if it's read/written by fluid + std::vector<ade::NodeHandle> io_handles; + for (const auto &in_op : isl->in_ops()) + { + ade::util::copy(in_op->inNodes(), std::back_inserter(io_handles)); + } + for (const auto &out_op : isl->out_ops()) + { + ade::util::copy(out_op->outNodes(), std::back_inserter(io_handles)); + } + for (const auto &io_node : io_handles) + { + if (!fg.metadata(io_node).contains<FluidData>()) + setFluidData(io_node, false); + } + } // if (fluid backend) + } // if (ISLAND) + } // for (gim.nodes()) + }); + // 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 (fg.metadata(node).contains<FluidUnit>()) + { + // 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; + + initFluidUnits(ctx.graph); + }); + 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; + + initLineConsumption(ctx.graph); + }); + 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; + + calcLatency(ctx.graph); + }); + 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; + + calcSkew(ctx.graph); + }); + + 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 (fg.metadata(node).contains<FluidData>()) + { + 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 (fd.internal == true) + { + // 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) { + return fg.metadata(nh).contains<FluidUnit>() && + fg.metadata(nh).get<FluidUnit>().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 (fg.metadata(node).contains<FluidData>()) + { + auto &fd = fg.metadata(node).get<FluidData>(); + for (auto out_edge : node->outEdges()) + { + const auto dstNode = out_edge->dstNode(); + if (fg.metadata(dstNode).contains<FluidUnit>()) + { + const auto &fu = fg.metadata(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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp new file mode 100644 index 000000000..ba8b9771f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp @@ -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_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 "FluidUnit"; } + 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; + bool internal = false; // is node internal to any fluid island + 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; + + // Execution methods + void reset(); + bool canWork() const; + bool canRead() const; + bool canWrite() const; + void doWork(); + bool done() const; + + void debug(std::ostream& os); + + // FIXME: + // refactor (implement a more solid replacement or + // drop this method completely) + virtual void setRatio(double ratio) = 0; + +private: + // FIXME!!! + // move to another class + virtual int firstWindow() const = 0; + virtual std::pair<int,int> linesReadAndnextWindow() 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; + + std::vector<FluidAgent*> m_script; + + 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::unordered_map<int, std::size_t> m_id_map; // GMat id -> buffer idx map + std::map<std::size_t, ade::NodeHandle> m_all_gmat_ids; + + 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); + + void initBufferRois(std::vector<int>& readStarts, std::vector<cv::gapi::own::Rect>& rois, const std::vector<gapi::own::Rect> &out_rois); + void makeReshape(const std::vector<cv::gapi::own::Rect>& out_rois); + +public: + GFluidExecutable(const ade::Graph &g, + const std::vector<ade::NodeHandle> &nodes, + const std::vector<cv::gapi::own::Rect> &outputRois); + + virtual inline bool canReshape() const override { return true; } + virtual void reshape(ade::Graph& g, const GCompileArgs& args) override; + + 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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp new file mode 100644 index 000000000..6672ea272 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp @@ -0,0 +1,760 @@ +// 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 <iomanip> // hex, dec (debug) + +#include "opencv2/gapi/own/convert.hpp" +#include "opencv2/gapi/own/types.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::gapi::own::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: GAPI_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) +{ + GAPI_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 depth) { + if (border == cv::BORDER_REPLICATE) + { + switch(depth) + { + 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: GAPI_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>; + } + } + else if (border == cv::BORDER_REFLECT_101) + { + switch(depth) + { + 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: GAPI_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>; + } + } + else + { + GAPI_Assert(!"Unsupported border type"); + return &fillBorderReflectRow<uint8_t>; + } + }; + + m_fill_border_row = getFillBorderRowFunc(BorderType, CV_MAT_DEPTH(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) + : BorderHandler(border_size), m_border_value(border_value) +{ /* nothing */ } + +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) +{ + m_const_border.create(1, data.cols(), data.data().type()); + m_const_border = m_border_value; + + 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::BufferStorage::updateInCache(View::Cache& cache, int start_log_idx, int nLines) const +{ + for (int i = 0; i < nLines; i++) + { + cache.m_linePtrs[i] = inLineB(start_log_idx + i, cache.m_desc.size.height); + } +} + +void fluid::BufferStorage::updateOutCache(Buffer::Cache& cache, int start_log_idx, int nLines) +{ + for (int i = 0; i < nLines; i++) + { + cache.m_linePtrs[i] = ptr(start_log_idx + i); + } +} + +void fluid::BufferStorageWithBorder::init(int dtype, int border_size, Border border) +{ + switch(border.type) + { + case cv::BORDER_CONSTANT: + m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value)); 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: + GAPI_Assert(false); + } +} + +void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype) +{ + auto borderSize = m_borderHandler->borderSize(); + auto width = (desc_width + 2*borderSize); + m_data.create(capacity, width, dtype); + + 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); + } +} + +static void copyWithoutBorder(const cv::gapi::own::Mat& src, int src_border_size, cv::gapi::own::Mat& dst, int dst_border_size, int startSrcLine, int startDstLine, int lpi) +{ + auto subSrc = src(cv::gapi::own::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi}); + auto subDst = dst(cv::gapi::own::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->init(type, border_size, border.value()); + storage->create(capacity, desc_width, type); + 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::gapi::own::Mat& data, cv::gapi::own::Rect roi); +std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::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) +{ + GAPI_DbgAssert(m_p); + m_read_caret += linesRead; + 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) +{ + GAPI_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(); + + GAPI_DbgAssert(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); +} + +void fluid::ViewPrivWithoutOwnBorder::allocate(int lineConsumption, BorderOpt) +{ + initCache(lineConsumption); +} + +void fluid::ViewPrivWithoutOwnBorder::prepareToRead() +{ + const auto &storage = m_p->priv().storage(); + + const int start_log_idx = m_read_caret - m_border_size; + storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter); +} + +fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int borderSize) +{ + GAPI_Assert(parent); + m_p = parent; + m_border_size = borderSize; +} + +void fluid::ViewPrivWithOwnBorder::allocate(int lineConsumption, BorderOpt border) +{ + initCache(lineConsumption); + + const auto& desc = m_cache.m_desc; + int type = CV_MAKETYPE(desc.depth, desc.chan); + m_own_storage.init(type, m_border_size, border.value()); + m_own_storage.create(lineConsumption, desc.size.width, type); +} + +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()); + + const int start_log_idx = m_read_caret - m_border_size; + m_own_storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter); +} + +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_DbgAssert(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); +} + +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; +} + +fluid::View::Priv& fluid::View::priv() +{ + return *m_priv; +} + +const fluid::View::Priv& fluid::View::priv() const +{ + return *m_priv; +} + +void fluid::View::Priv::initCache(int lineConsumption) +{ + m_cache.m_linePtrs.resize(lineConsumption); + m_cache.m_desc = m_p->priv().meta(); + m_cache.m_border_size = m_border_size; +} + +// 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 writer_lpi, + int readStartPos, + cv::gapi::own::Rect roi) +{ + m_writer_lpi = writer_lpi; + m_desc = desc; + m_readStart = readStartPos; + m_roi = roi == own::Rect{} ? own::Rect{ 0, 0, desc.size.width, desc.size.height } + : roi; + m_cache.m_linePtrs.resize(writer_lpi); + m_cache.m_desc = desc; +} + +void fluid::Buffer::Priv::allocate(BorderOpt border, + int border_size, + int line_consumption, + int skew) +{ + GAPI_Assert(line_consumption > 0); + + // Init physical buffer + + // FIXME? combine line_consumption with skew? + auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1; + + m_storage = createStorage(data_height, + m_desc.size.width, + CV_MAKETYPE(m_desc.depth, m_desc.chan), + border_size, + border); + + // Finally, initialize carets + m_write_caret = writeStart(); + + m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi); +} + +void fluid::Buffer::Priv::bindTo(const cv::gapi::own::Mat &data, bool is_input) +{ + // FIXME: move all these fields into a separate structure + GAPI_Assert(m_desc == descr_of(data)); + + // Currently m_writer_lpi is obtained from metadata which is shared between islands + // and this assert can trigger for slot which connects two fluid islands. + // m_writer_lpi is used only in write-related functions and doesn't affect + // buffer which is island's input so it's safe to skip this check. + // FIXME: + // Bring back this check when we move to 1 buffer <-> 1 metadata model + // if (is_input) GAPI_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! + + m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi); +} + +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! + + m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi); +} + +void fluid::Buffer::Priv::reset() +{ + m_write_caret = m_is_input ? writeEnd() : writeStart(); + m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi); +} + +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_DbgAssert(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()) + , m_cache(&m_priv->cache()) +{ +} + +fluid::Buffer::Buffer(const cv::GMatDesc &desc) + : m_priv(new Priv()) + , m_cache(&m_priv->cache()) +{ + 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, wlpi, readStart, roi); + m_priv->allocate({}, border, lineConsumption, skew); +} + +fluid::Buffer::Buffer(const cv::GMatDesc &desc, + int max_line_consumption, + int border_size, + int skew, + int wlpi, + BorderOpt border) + : m_priv(new Priv()) + , m_cache(&m_priv->cache()) +{ + int readStart = 0; + cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height}; + m_priv->init(desc, wlpi, readStart, roi); + m_priv->allocate(border, border_size, max_line_consumption, skew); +} + +fluid::Buffer::Buffer(const cv::gapi::own::Mat &data, bool is_input) + : m_priv(new Priv()) + , m_cache(&m_priv->cache()) +{ + int wlpi = 1, readStart = 0; + cv::gapi::own::Rect roi{0, 0, data.cols, data.rows}; + m_priv->init(descr_of(data), wlpi, readStart, roi); + m_priv->bindTo(data, is_input); +} + +int fluid::Buffer::linesReady() const +{ + return m_priv->linesReady(); +} + +int fluid::Buffer::lpi() const +{ + return m_priv->lpi(); +} + +fluid::View::View(Priv* p) + : m_priv(p), m_cache(&p->cache()) +{ /* nothing */ } + +fluid::View fluid::Buffer::mkView(int borderSize, bool ownStorage) +{ + // FIXME: logic outside of Priv (because View takes pointer to Buffer) + auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, borderSize)) + : 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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp new file mode 100644 index 000000000..1f3eadc11 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer_priv.hpp @@ -0,0 +1,308 @@ +// 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/exports.hpp" // GAPI_EXPORTS + +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)) + inline virtual void fillCompileTimeBorder(BufferStorageWithBorder &) { /* nothing */ } + + // Fills required border lines + inline virtual void updateBorderPixels(BufferStorageWithBorder& /*data*/, int /*startLine*/, int /*lpi*/) const { /* nothing */ } + + inline int borderSize() const { return m_border_size; } + inline 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::gapi::own::Mat m_const_border; + +public: + BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value); + virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override; + virtual void fillCompileTimeBorder(BufferStorageWithBorder &) override; + virtual std::size_t size() const override; +}; + +class BufferStorage +{ +protected: + cv::gapi::own::Mat m_data; + +public: + void updateInCache(View::Cache& cache, int start_log_idx, int nLines) const; + void updateOutCache(Buffer::Cache& cache, int start_log_idx, int nLines); + + 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::gapi::own::Mat& data() const { return m_data; } + inline cv::gapi::own::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 inline 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::gapi::own::Mat& _data, cv::gapi::own::Rect _roi) + { + m_data = _data(_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 { return ptr(log_idx); } + + 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 init(int depth, int border_size, Border border); + void create(int capacity, int desc_width, int dtype); + + 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: + View::Cache m_cache; + + 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 + + const View::Cache& cache() const { return m_cache; } + void initCache(int lineConsumption); + + virtual void allocate(int lineConsumption, BorderOpt border) = 0; + 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 allocate(int lineConsumption, BorderOpt) override; + virtual void prepareToRead() override; + + inline 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 borderSize); + + virtual void allocate(int lineConsumption, BorderOpt border) override; + 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 +{ + Buffer::Cache m_cache; + + 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 writer_lpi, + int readStart, + cv::gapi::own::Rect roi); + + void allocate(BorderOpt border, int border_size, int line_consumption, int skew); + void bindTo(const cv::gapi::own::Mat &data, bool is_input); + + inline void addView(const View& view) { m_views.push_back(view); } + + inline 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; } + + inline const Buffer::Cache& cache() const { return m_cache; } +}; + +} // namespace cv::gapi::fluid +} // namespace cv::gapi +} // namespace cv + +#endif // OPENCV_GAPI_FLUID_BUFFER_PRIV_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp new file mode 100644 index 000000000..16a63e217 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp @@ -0,0 +1,2193 @@ +// 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 + +#if !defined(GAPI_STANDALONE) + +#include "precomp.hpp" + +#include "opencv2/gapi/own/assert.hpp" +#include "opencv2/core/traits.hpp" +#include "opencv2/core/hal/hal.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 "opencv2/gapi/fluid/core.hpp" + +#include "gfluidbuffer_priv.hpp" +#include "gfluidbackend.hpp" +#include "gfluidutils.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"); + + 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); + + SRC lower[4], upper[4]; + for (int c=0; c < chan; c++) + { + if (std::is_integral<SRC>::value) + { + // for integral input, in[i] >= lower equals in[i] >= ceil(lower) + // so we can optimize compare operations by rounding lower/upper + lower[c] = saturate<SRC>(lowerb[c], ceild); + upper[c] = saturate<SRC>(upperb[c], floord); + } + else + { + // FIXME: now values used in comparison are floats (while they + // have double precision initially). Comparison float/float + // may differ from float/double (how it should work in this case) + // + // Example: threshold=1/3 (or 1/10) + lower[c] = static_cast<SRC>(lowerb[c]); + upper[c] = static_cast<SRC>(upperb[c]); + } + } + + // 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); + INRANGE_(uchar, float, 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(GFluidPhase, cv::gapi::core::GPhase, false) +{ + static const int Window = 1; + + static void run(const View &src_x, + const View &src_y, + bool angleInDegrees, + Buffer &dst) + { + const auto w = dst.length() * dst.meta().chan; + if (src_x.meta().depth == CV_32F && src_y.meta().depth == CV_32F) + { + hal::fastAtan32f(src_y.InLine<float>(0), + src_x.InLine<float>(0), + dst.OutLine<float>(), + w, + angleInDegrees); + } + else if (src_x.meta().depth == CV_64F && src_y.meta().depth == CV_64F) + { + hal::fastAtan64f(src_y.InLine<double>(0), + src_x.InLine<double>(0), + dst.OutLine<double>(), + w, + angleInDegrees); + } else GAPI_Assert(false && !"Phase supports 32F/64F input only!"); + } +}; + +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); + } + } +}; + +GAPI_FLUID_KERNEL(GFluidSqrt, cv::gapi::core::GSqrt, false) +{ + static const int Window = 1; + + static void run(const View &in, Buffer &out) + { + const auto w = out.length() * out.meta().chan; + if (in.meta().depth == CV_32F) + { + hal::sqrt32f(in.InLine<float>(0), + out.OutLine<float>(0), + w); + } + else if (in.meta().depth == CV_64F) + { + hal::sqrt64f(in.InLine<double>(0), + out.OutLine<double>(0), + w); + } else GAPI_Assert(false && !"Sqrt supports 32F/64F input only!"); + } +}; + +} // 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 + ,GFluidPhase + ,GFluidAddC + ,GFluidSubC + ,GFluidSubRC + ,GFluidMulC + ,GFluidMulCOld + ,GFluidDivC + ,GFluidDivRC + ,GFluidAbsDiffC + ,GFluidCmpGTScalar + ,GFluidCmpGEScalar + ,GFluidCmpLEScalar + ,GFluidCmpLTScalar + ,GFluidCmpEQScalar + ,GFluidCmpNEScalar + ,GFluidThreshold + ,GFluidInRange + ,GFluidResize + ,GFluidSqrt + #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 + >(); +} + +#endif // !defined(GAPI_STANDALONE) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp new file mode 100644 index 000000000..e2e4c4f75 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp @@ -0,0 +1,1338 @@ +// 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 + +#if !defined(GAPI_STANDALONE) + +#include "precomp.hpp" + +#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 "opencv2/gapi/fluid/imgproc.hpp" + +#include "gfluidbuffer_priv.hpp" +#include "gfluidbackend.hpp" +#include "gfluidutils.hpp" + +#include "gfluidimgproc_func.hpp" + +#include "opencv2/imgproc/hal/hal.hpp" +#include "opencv2/core/hal/intrin.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()); + + GAPI_Assert(coef_r < 1 && coef_g < 1 && coef_b < 1); + GAPI_Assert(std::abs(coef_r + coef_g + coef_b - 1) < 0.001); + + const auto *in = src.InLine<uchar>(0); + auto *out = dst.OutLine<uchar>(); + + int width = dst.length(); + + run_rgb2gray_impl(out, in, width, coef_r, coef_g, coef_b); +} + +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(); + + run_rgb2yuv_impl(out, in, width, coef); +} + +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(); + + run_yuv2rgb_impl(out, in, width, coef); +} + +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 }; + +#define LabLuv_reference 0 // 1=use reference code of RGB/BGR to LUV/Lab, 0=don't + +#if LabLuv_reference + +// 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); +} + +template<LabLUV labluv, int blue=0> +static void run_rgb2labluv_reference(uchar out[], const uchar in[], int width) +{ + 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");; + } +} + +#endif // LabLuv_reference + +// 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(); + +#if LabLuv_reference + run_rgb2labluv_reference<labluv, blue>(out, in, width); +#else + uchar *dst_data = out; + const uchar *src_data = in; + size_t src_step = width; + size_t dst_step = width; + int height = 1; + int depth = CV_8U; + int scn = 3; + bool swapBlue = (blue == 2); + bool isLab = (LL_Lab == labluv); + bool srgb = true; + cv::hal::cvtBGRtoLab(src_data, src_step, dst_data, dst_step, + width, height, depth, scn, swapBlue, isLab, srgb); +#endif +} + +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, + const float kx[], + const float ky[], + int ksize, + float scale, // default: 1 + float delta, // default: 0 + float *buf[]) +{ + 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; + + GAPI_DbgAssert(ksize == 3); +// float buf[3][width * chan]; + + int y = dst.y(); + int y0 = dst.priv().writeStart(); +// int y1 = dst.priv().writeEnd(); + + run_sobel_row(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0); +} + +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 == FILTER_SCHARR); + + int ksz = (ksize == FILTER_SCHARR)? 3: ksize; + + auto *kx = scratch.OutLine<float>(); + auto *ky = kx + ksz; + + int width = dst.meta().size.width; + int chan = dst.meta().chan; + + float *buf[3]; + buf[0] = ky + ksz; + buf[1] = buf[0] + width*chan; + buf[2] = buf[1] + width*chan; + + 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, ksz, scale, delta, buf); + UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( short, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( short, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( short, short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( float, short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + UNARY_( float, float, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf); + + 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) + { + // TODO: support kernel height 3, 5, 7, 9, ... + GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR); + int ksz = (ksize == FILTER_SCHARR) ? 3 : ksize; + + int width = in.size.width; + int chan = in.chan; + + int buflen = ksz + ksz // kernels: kx, ky + + ksz * width * chan; // working buffers + + cv::gapi::own::Size bufsize(buflen, 1); + GMatDesc bufdesc = {CV_32F, 1, bufsize}; + Buffer buffer(bufdesc); + scratch = std::move(buffer); + + auto *kx = scratch.OutLine<float>(); + auto *ky = kx + ksz; + + 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 + >(); +} + +#endif // !defined(GAPI_STANDALONE) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp new file mode 100644 index 000000000..9b217903e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp @@ -0,0 +1,93 @@ +// 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 + +#if !defined(GAPI_STANDALONE) + +#include "gfluidimgproc_func.hpp" +#include "gfluidimgproc_func.simd.hpp" +#include "backends/fluid/gfluidimgproc_func.simd_declarations.hpp" + +#include "gfluidutils.hpp" + +#include "opencv2/core/cvdef.h" +#include "opencv2/core/hal/intrin.hpp" + +#include <cmath> +#include <cstdlib> + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + +namespace cv { +namespace gapi { +namespace fluid { + +//---------------------------------- +// +// Fluid kernels: RGB2Gray, BGR2Gray +// +//---------------------------------- + +void run_rgb2gray_impl(uchar out[], const uchar in[], int width, + float coef_r, float coef_g, float coef_b) +{ + CV_CPU_DISPATCH(run_rgb2gray_impl, + (out, in, width, coef_r, coef_g, coef_b), + CV_CPU_DISPATCH_MODES_ALL); +} + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// +//-------------------------------------- + +void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef[5]) +{ + CV_CPU_DISPATCH(run_rgb2yuv_impl, (out, in, width, coef), CV_CPU_DISPATCH_MODES_ALL); +} + +void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]) +{ + CV_CPU_DISPATCH(run_yuv2rgb_impl, (out, in, width, coef), CV_CPU_DISPATCH_MODES_ALL); +} + +//--------------------- +// +// Fluid kernels: Sobel +// +//--------------------- + +#define RUN_SOBEL_ROW(DST, SRC) \ +void run_sobel_row(DST out[], const SRC *in[], int width, int chan, \ + const float kx[], const float ky[], int border, \ + float scale, float delta, float *buf[], \ + int y, int y0) \ +{ \ + CV_CPU_DISPATCH(run_sobel_row, \ + (out, in, width, chan, kx, ky, border, scale, delta, buf,y, y0), \ + CV_CPU_DISPATCH_MODES_ALL); \ +} + +RUN_SOBEL_ROW(uchar , uchar ) +RUN_SOBEL_ROW(ushort, ushort) +RUN_SOBEL_ROW( short, uchar ) +RUN_SOBEL_ROW( short, ushort) +RUN_SOBEL_ROW( short, short) +RUN_SOBEL_ROW( float, uchar ) +RUN_SOBEL_ROW( float, ushort) +RUN_SOBEL_ROW( float, short) +RUN_SOBEL_ROW( float, float) + +#undef RUN_SOBEL_ROW + +} // namespace fliud +} // namespace gapi +} // namespace cv + +#endif // !defined(GAPI_STANDALONE) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp new file mode 100644 index 000000000..1b6f1b8c0 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp @@ -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 + +#pragma once + +#if !defined(GAPI_STANDALONE) + +#include "opencv2/core.hpp" + +namespace cv { +namespace gapi { +namespace fluid { + +//---------------------------------- +// +// Fluid kernels: RGB2Gray, BGR2Gray +// +//---------------------------------- + +void run_rgb2gray_impl(uchar out[], const uchar in[], int width, + float coef_r, float coef_g, float coef_b); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// +//-------------------------------------- + +void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef[5]); + +void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]); + +//--------------------- +// +// Fluid kernels: Sobel +// +//--------------------- + +#define RUN_SOBEL_ROW(DST, SRC) \ +void run_sobel_row(DST out[], const SRC *in[], int width, int chan, \ + const float kx[], const float ky[], int border, \ + float scale, float delta, float *buf[], \ + int y, int y0); + +RUN_SOBEL_ROW(uchar , uchar ) +RUN_SOBEL_ROW(ushort, ushort) +RUN_SOBEL_ROW( short, uchar ) +RUN_SOBEL_ROW( short, ushort) +RUN_SOBEL_ROW( short, short) +RUN_SOBEL_ROW( float, uchar ) +RUN_SOBEL_ROW( float, ushort) +RUN_SOBEL_ROW( float, short) +RUN_SOBEL_ROW( float, float) + +#undef RUN_SOBEL_ROW + +} // namespace fluid +} // namespace gapi +} // namespace cv + +#endif // !defined(GAPI_STANDALONE) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp new file mode 100644 index 000000000..c87be085a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp @@ -0,0 +1,562 @@ +// 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 + +// NB: allow including this *.hpp several times! +// #pragma once -- don't: this file is NOT once! + +#if !defined(GAPI_STANDALONE) + +#include "opencv2/gapi/own/saturate.hpp" + +#include "opencv2/core.hpp" +#include "opencv2/core/hal/intrin.hpp" + +#include <cstdint> + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + +using cv::gapi::own::saturate; + +namespace cv { +namespace gapi { +namespace fluid { + +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +//---------------------------------- +// +// Fluid kernels: RGB2Gray, BGR2Gray +// +//---------------------------------- + +void run_rgb2gray_impl(uchar out[], const uchar in[], int width, + float coef_r, float coef_g, float coef_b); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// +//-------------------------------------- + +void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef[5]); + +void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]); + +//--------------------- +// +// Fluid kernels: Sobel +// +//--------------------- + +#define RUN_SOBEL_ROW(DST, SRC) \ +void run_sobel_row(DST out[], const SRC *in[], int width, int chan, \ + const float kx[], const float ky[], int border, \ + float scale, float delta, float *buf[], \ + int y, int y0); + +RUN_SOBEL_ROW(uchar , uchar ) +RUN_SOBEL_ROW(ushort, ushort) +RUN_SOBEL_ROW( short, uchar ) +RUN_SOBEL_ROW( short, ushort) +RUN_SOBEL_ROW( short, short) +RUN_SOBEL_ROW( float, uchar ) +RUN_SOBEL_ROW( float, ushort) +RUN_SOBEL_ROW( float, short) +RUN_SOBEL_ROW( float, float) + +#undef RUN_SOBEL_ROW + +//---------------------------------------------------------------------- + +#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +//---------------------------------- +// +// Fluid kernels: RGB2Gray, BGR2Gray +// +//---------------------------------- + +void run_rgb2gray_impl(uchar out[], const uchar in[], int width, + float coef_r, float coef_g, float coef_b) +{ + // assume: + // - coefficients are less than 1 + // - and their sum equals 1 + + constexpr int unity = 1 << 16; // Q0.0.16 inside ushort: + ushort rc = static_cast<ushort>(coef_r * unity + 0.5f); + ushort gc = static_cast<ushort>(coef_g * unity + 0.5f); + ushort bc = static_cast<ushort>(coef_b * unity + 0.5f); + + GAPI_Assert(rc + gc + bc <= unity); + GAPI_Assert(rc + gc + bc >= USHRT_MAX); + +#if CV_SIMD + constexpr int nlanes = v_uint8::nlanes; + if (width >= nlanes) + { + for (int w=0; w < width; ) + { + // process main part of pixels row + for ( ; w <= width - nlanes; w += nlanes) + { + v_uint8 r, g, b; + v_load_deinterleave(&in[3*w], r, g, b); + + v_uint16 r0, r1, g0, g1, b0, b1; + v_expand(r, r0, r1); + v_expand(g, g0, g1); + v_expand(b, b0, b1); + + v_uint16 y0, y1; + static const ushort half = 1 << 7; // Q0.8.8 + y0 = (v_mul_hi(r0 << 8, vx_setall_u16(rc)) + + v_mul_hi(g0 << 8, vx_setall_u16(gc)) + + v_mul_hi(b0 << 8, vx_setall_u16(bc)) + + vx_setall_u16(half)) >> 8; + y1 = (v_mul_hi(r1 << 8, vx_setall_u16(rc)) + + v_mul_hi(g1 << 8, vx_setall_u16(gc)) + + v_mul_hi(b1 << 8, vx_setall_u16(bc)) + + vx_setall_u16(half)) >> 8; + + v_uint8 y; + y = v_pack(y0, y1); + v_store(&out[w], y); + } + + // process tail (if any) + if (w < width) + { + GAPI_DbgAssert(width - nlanes >= 0); + w = width - nlanes; + } + } + + return; + } +#endif + + for (int w=0; w < width; w++) + { + uchar r = in[3*w ]; + uchar g = in[3*w + 1]; + uchar b = in[3*w + 2]; + + static const int half = 1 << 15; // Q0.0.16 + ushort y = (r*rc + b*bc + g*gc + half) >> 16; + out[w] = static_cast<uchar>(y); + } +} + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// +//-------------------------------------- + +void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef[5]) +{ + ushort c0 = static_cast<ushort>(coef[0]*(1 << 16) + 0.5f); // Q0.0.16 un-signed + ushort c1 = static_cast<ushort>(coef[1]*(1 << 16) + 0.5f); + ushort c2 = static_cast<ushort>(coef[2]*(1 << 16) + 0.5f); + short c3 = static_cast<short>(coef[3]*(1 << 12) + 0.5f); // Q1.0.12 signed + short c4 = static_cast<short>(coef[4]*(1 << 12) + 0.5f); + + int w = 0; + +#if CV_SIMD + static const int nlanes = v_uint8::nlanes; + for ( ; w <= width - nlanes; w += nlanes) + { + v_uint8 r, g, b; + v_load_deinterleave(&in[3*w], r, g, b); + + v_uint16 _r0, _r1, _g0, _g1, _b0, _b1; + v_expand(r, _r0, _r1); + v_expand(g, _g0, _g1); + v_expand(b, _b0, _b1); + + _r0 = _r0 << 7; // Q0.9.7 un-signed + _r1 = _r1 << 7; + _g0 = _g0 << 7; + _g1 = _g1 << 7; + _b0 = _b0 << 7; + _b1 = _b1 << 7; + + v_uint16 _y0, _y1; + _y0 = v_mul_hi(vx_setall_u16(c0), _r0) // Q0.9.7 + + v_mul_hi(vx_setall_u16(c1), _g0) + + v_mul_hi(vx_setall_u16(c2), _b0); + _y1 = v_mul_hi(vx_setall_u16(c0), _r1) + + v_mul_hi(vx_setall_u16(c1), _g1) + + v_mul_hi(vx_setall_u16(c2), _b1); + + v_int16 r0, r1, b0, b1, y0, y1; + r0 = v_reinterpret_as_s16(_r0); // Q1.8.7 signed + r1 = v_reinterpret_as_s16(_r1); + b0 = v_reinterpret_as_s16(_b0); + b1 = v_reinterpret_as_s16(_b1); + y0 = v_reinterpret_as_s16(_y0); + y1 = v_reinterpret_as_s16(_y1); + + v_int16 u0, u1, v0, v1; + u0 = v_mul_hi(vx_setall_s16(c3), b0 - y0); // Q1.12.3 + u1 = v_mul_hi(vx_setall_s16(c3), b1 - y1); + v0 = v_mul_hi(vx_setall_s16(c4), r0 - y0); + v1 = v_mul_hi(vx_setall_s16(c4), r1 - y1); + + v_uint8 y, u, v; + y = v_pack((_y0 + vx_setall_u16(1 << 6)) >> 7, + (_y1 + vx_setall_u16(1 << 6)) >> 7); + u = v_pack_u((u0 + vx_setall_s16(257 << 2)) >> 3, // 257 << 2 = 128.5 * (1 << 3) + (u1 + vx_setall_s16(257 << 2)) >> 3); + v = v_pack_u((v0 + vx_setall_s16(257 << 2)) >> 3, + (v1 + vx_setall_s16(257 << 2)) >> 3); + + v_store_interleave(&out[3*w], y, u, v); + } +#endif + + for ( ; w < width; w++) + { + short r = in[3*w ] << 7; // Q1.8.7 signed + short g = in[3*w + 1] << 7; + short b = in[3*w + 2] << 7; + short y = (c0*r + c1*g + c2*b) >> 16; // Q1.8.7 + short u = c3*(b - y) >> 16; // Q1.12.3 + short v = c4*(r - y) >> 16; + out[3*w ] = static_cast<uchar>((y + (1 << 6)) >> 7); + out[3*w + 1] = saturate<uchar>((u + (128 << 3) + (1 << 2)) >> 3); + out[3*w + 2] = saturate<uchar>((v + (128 << 3) + (1 << 2)) >> 3); + } +} + +void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]) +{ + short c0 = static_cast<short>(coef[0] * (1 << 12) + 0.5f); // Q1.3.12 + short c1 = static_cast<short>(coef[1] * (1 << 12) + 0.5f); + short c2 = static_cast<short>(coef[2] * (1 << 12) + 0.5f); + short c3 = static_cast<short>(coef[3] * (1 << 12) + 0.5f); + + int w = 0; + +#if CV_SIMD + static const int nlanes = v_uint8::nlanes; + for ( ; w <= width - nlanes; w += nlanes) + { + v_uint8 y, u, v; + v_load_deinterleave(&in[3*w], y, u, v); + + v_uint16 _y0, _y1, _u0, _u1, _v0, _v1; + v_expand(y, _y0, _y1); + v_expand(u, _u0, _u1); + v_expand(v, _v0, _v1); + + v_int16 y0, y1, u0, u1, v0, v1; + y0 = v_reinterpret_as_s16(_y0); + y1 = v_reinterpret_as_s16(_y1); + u0 = v_reinterpret_as_s16(_u0); + u1 = v_reinterpret_as_s16(_u1); + v0 = v_reinterpret_as_s16(_v0); + v1 = v_reinterpret_as_s16(_v1); + + y0 = y0 << 3; // Q1.12.3 + y1 = y1 << 3; + u0 = (u0 - vx_setall_s16(128)) << 7; // Q1.8.7 + u1 = (u1 - vx_setall_s16(128)) << 7; + v0 = (v0 - vx_setall_s16(128)) << 7; + v1 = (v1 - vx_setall_s16(128)) << 7; + + v_int16 r0, r1, g0, g1, b0, b1; + r0 = y0 + v_mul_hi(vx_setall_s16(c0), v0); // Q1.12.3 + r1 = y1 + v_mul_hi(vx_setall_s16(c0), v1); + g0 = y0 + v_mul_hi(vx_setall_s16(c1), u0) + + v_mul_hi(vx_setall_s16(c2), v0); + g1 = y1 + v_mul_hi(vx_setall_s16(c1), u1) + + v_mul_hi(vx_setall_s16(c2), v1); + b0 = y0 + v_mul_hi(vx_setall_s16(c3), u0); + b1 = y1 + v_mul_hi(vx_setall_s16(c3), u1); + + v_uint8 r, g, b; + r = v_pack_u((r0 + vx_setall_s16(1 << 2)) >> 3, + (r1 + vx_setall_s16(1 << 2)) >> 3); + g = v_pack_u((g0 + vx_setall_s16(1 << 2)) >> 3, + (g1 + vx_setall_s16(1 << 2)) >> 3); + b = v_pack_u((b0 + vx_setall_s16(1 << 2)) >> 3, + (b1 + vx_setall_s16(1 << 2)) >> 3); + + v_store_interleave(&out[3*w], r, g, b); + } +#endif + + for ( ; w < width; w++) + { + short y = in[3*w ] << 3; // Q1.12.3 + short u = (in[3*w + 1] - 128) << 7; // Q1.8.7 + short v = (in[3*w + 2] - 128) << 7; + short r = y + ( c0*v >> 16); // Q1.12.3 + short g = y + ((c1*u + c2*v) >> 16); + short b = y + ((c3*u ) >> 16); + out[3*w ] = saturate<uchar>((r + (1 << 2)) >> 3); + out[3*w + 1] = saturate<uchar>((g + (1 << 2)) >> 3); + out[3*w + 2] = saturate<uchar>((b + (1 << 2)) >> 3); + } +} + +//--------------------- +// +// Fluid kernels: Sobel +// +//--------------------- + +// Sobel 3x3: vertical pass +template<bool noscale, typename DST> +static void run_sobel3x3_vert(DST out[], int length, const float ky[], + float scale, float delta, const int r[], float *buf[]) +{ + float ky0 = ky[0], + ky1 = ky[1], + ky2 = ky[2]; + + int r0 = r[0], + r1 = r[1], + r2 = r[2]; + +#if CV_SIMD + // for floating-point output, + // manual vectoring may be not better than compiler's optimization +#define EXPLICIT_SIMD_32F 0 // 1=vectorize 32f case explicitly, 0=don't +#if EXPLICIT_SIMD_32F + if (std::is_same<DST, float>::value && length >= v_int16::nlanes) + { + constexpr static int nlanes = v_float32::nlanes; + + for (int l=0; l < length; ) + { + for (; l <= length - nlanes; l += nlanes) + { + v_float32 sum = vx_load(&buf[r0][l]) * vx_setall_f32(ky0); + sum = v_fma(vx_load(&buf[r1][l]), vx_setall_f32(ky1), sum); + sum = v_fma(vx_load(&buf[r2][l]), vx_setall_f32(ky2), sum); + + if (!noscale) + { + sum = v_fma(sum, vx_setall_f32(scale), vx_setall_f32(delta)); + } + + v_store(reinterpret_cast<float*>(&out[l]), sum); + } + + if (l < length) + { + // tail: recalculate last pixels + GAPI_DbgAssert(length >= nlanes); + l = length - nlanes; + } + } + + return; + } +#endif + + if ((std::is_same<DST, short>::value || std::is_same<DST, ushort>::value) + && length >= v_int16::nlanes) + { + constexpr static int nlanes = v_int16::nlanes; + + for (int l=0; l < length; ) + { + for (; l <= length - nlanes; l += nlanes) + { + v_float32 sum0 = vx_load(&buf[r0][l]) * vx_setall_f32(ky0); + sum0 = v_fma(vx_load(&buf[r1][l]), vx_setall_f32(ky1), sum0); + sum0 = v_fma(vx_load(&buf[r2][l]), vx_setall_f32(ky2), sum0); + + v_float32 sum1 = vx_load(&buf[r0][l + nlanes/2]) * vx_setall_f32(ky0); + sum1 = v_fma(vx_load(&buf[r1][l + nlanes/2]), vx_setall_f32(ky1), sum1); + sum1 = v_fma(vx_load(&buf[r2][l + nlanes/2]), vx_setall_f32(ky2), sum1); + + if (!noscale) + { + sum0 = v_fma(sum0, vx_setall_f32(scale), vx_setall_f32(delta)); + sum1 = v_fma(sum1, vx_setall_f32(scale), vx_setall_f32(delta)); + } + + v_int32 isum0 = v_round(sum0), + isum1 = v_round(sum1); + + if (std::is_same<DST, short>::value) + { + // signed short + v_int16 res = v_pack(isum0, isum1); + v_store(reinterpret_cast<short*>(&out[l]), res); + } else + { + // unsigned short + v_uint16 res = v_pack_u(isum0, isum1); + v_store(reinterpret_cast<ushort*>(&out[l]), res); + } + } + + if (l < length) + { + // tail: recalculate last pixels + GAPI_DbgAssert(length >= nlanes); + l = length - nlanes; + } + } + + return; + } + + if (std::is_same<DST, uchar>::value && length >= v_uint8::nlanes) + { + constexpr static int nlanes = v_uint8::nlanes; + + for (int l=0; l < length; ) + { + for (; l <= length - nlanes; l += nlanes) + { + v_float32 sum0 = vx_load(&buf[r0][l]) * vx_setall_f32(ky0); + sum0 = v_fma(vx_load(&buf[r1][l]), vx_setall_f32(ky1), sum0); + sum0 = v_fma(vx_load(&buf[r2][l]), vx_setall_f32(ky2), sum0); + + v_float32 sum1 = vx_load(&buf[r0][l + nlanes/4]) * vx_setall_f32(ky0); + sum1 = v_fma(vx_load(&buf[r1][l + nlanes/4]), vx_setall_f32(ky1), sum1); + sum1 = v_fma(vx_load(&buf[r2][l + nlanes/4]), vx_setall_f32(ky2), sum1); + + v_float32 sum2 = vx_load(&buf[r0][l + 2*nlanes/4]) * vx_setall_f32(ky0); + sum2 = v_fma(vx_load(&buf[r1][l + 2*nlanes/4]), vx_setall_f32(ky1), sum2); + sum2 = v_fma(vx_load(&buf[r2][l + 2*nlanes/4]), vx_setall_f32(ky2), sum2); + + v_float32 sum3 = vx_load(&buf[r0][l + 3*nlanes/4]) * vx_setall_f32(ky0); + sum3 = v_fma(vx_load(&buf[r1][l + 3*nlanes/4]), vx_setall_f32(ky1), sum3); + sum3 = v_fma(vx_load(&buf[r2][l + 3*nlanes/4]), vx_setall_f32(ky2), sum3); + + if (!noscale) + { + sum0 = v_fma(sum0, vx_setall_f32(scale), vx_setall_f32(delta)); + sum1 = v_fma(sum1, vx_setall_f32(scale), vx_setall_f32(delta)); + sum2 = v_fma(sum2, vx_setall_f32(scale), vx_setall_f32(delta)); + sum3 = v_fma(sum3, vx_setall_f32(scale), vx_setall_f32(delta)); + } + + v_int32 isum0 = v_round(sum0), + isum1 = v_round(sum1), + isum2 = v_round(sum2), + isum3 = v_round(sum3); + + v_int16 ires0 = v_pack(isum0, isum1), + ires1 = v_pack(isum2, isum3); + + v_uint8 res = v_pack_u(ires0, ires1); + v_store(reinterpret_cast<uchar*>(&out[l]), res); + } + + if (l < length) + { + // tail: recalculate last pixels + GAPI_DbgAssert(length >= nlanes); + l = length - nlanes; + } + } + + return; + } +#endif + + // reference code + for (int l=0; l < length; l++) + { + float sum = buf[r0][l]*ky0 + buf[r1][l]*ky1 + buf[r2][l]*ky2; + + if (!noscale) + { + sum = sum*scale + delta; + } + + out[l] = cv::gapi::own::saturate<DST>(sum, rintf); + } +} + +template<typename DST, typename SRC> +static void run_sobel_impl(DST out[], const SRC *in[], int width, int chan, + const float kx[], const float ky[], int border, + float scale, float delta, float *buf[], + int y, int y0) +{ + int r[3]; + r[0] = (y - y0) % 3; // buf[r[0]]: previous + r[1] = (y - y0 + 1) % 3; // this + r[2] = (y - y0 + 2) % 3; // next row + + int length = width * chan; + + // horizontal pass + + // full horizontal pass is needed only if very 1st row in ROI; + // for 2nd and further rows, it is enough to convolve only the + // "next" row - as we can reuse buffers from previous calls to + // this kernel (note that Fluid processes rows consequently) + int k0 = (y == y0)? 0: 2; + + for (int k = k0; k < 3; k++) + { + // previous, this , next pixel + const SRC *s[3] = {in[k] - border*chan , in[k], in[k] + border*chan}; + + // rely on compiler vectoring + for (int l=0; l < length; l++) + { + buf[r[k]][l] = s[0][l]*kx[0] + s[1][l]*kx[1] + s[2][l]*kx[2]; + } + } + + // vertical pass + if (scale == 1 && delta == 0) + { + constexpr static bool noscale = true; // omit scaling + run_sobel3x3_vert<noscale, DST>(out, length, ky, scale, delta, r, buf); + } else + { + constexpr static bool noscale = false; // do scaling + run_sobel3x3_vert<noscale, DST>(out, length, ky, scale, delta, r, buf); + } +} + +#define RUN_SOBEL_ROW(DST, SRC) \ +void run_sobel_row(DST out[], const SRC *in[], int width, int chan, \ + const float kx[], const float ky[], int border, \ + float scale, float delta, float *buf[], \ + int y, int y0) \ +{ \ + run_sobel_impl(out, in, width, chan, kx, ky, border, scale, delta, buf,y, y0); \ +} + +RUN_SOBEL_ROW(uchar , uchar ) +RUN_SOBEL_ROW(ushort, ushort) +RUN_SOBEL_ROW( short, uchar ) +RUN_SOBEL_ROW( short, ushort) +RUN_SOBEL_ROW( short, short) +RUN_SOBEL_ROW( float, uchar ) +RUN_SOBEL_ROW( float, ushort) +RUN_SOBEL_ROW( float, short) +RUN_SOBEL_ROW( float, float) + +#undef RUN_SOBEL_ROW + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // namespace fluid +} // namespace gapi +} // namespace cv + +#endif // !defined(GAPI_STANDALONE) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidutils.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidutils.hpp new file mode 100644 index 000000000..a38b2f132 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidutils.hpp @@ -0,0 +1,93 @@ +// 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 +#include <opencv2/gapi/own/saturate.hpp> + +namespace cv { +namespace gapi { +namespace fluid { + +using cv::gapi::own::saturate; +using cv::gapi::own::ceild; +using cv::gapi::own::floord; +using cv::gapi::own::roundd; +using cv::gapi::own::rintd; + +//-------------------------------- +// +// 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/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.cpp new file mode 100644 index 000000000..eda6a5fd7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.cpp @@ -0,0 +1,226 @@ +// 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 <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/gpu/ggpubackend.hpp" +#include "backends/gpu/ggpuimgproc.hpp" +#include "backends/gpu/ggpucore.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 GGPUModel = ade::TypedGraph + < cv::gimpl::Unit + , cv::gimpl::Protocol + >; + +// FIXME: Same issue with Typed and ConstTyped +using GConstGGPUModel = ade::ConstTypedGraph + < cv::gimpl::Unit + , cv::gimpl::Protocol + >; + +namespace +{ + class GGPUBackendImpl final: public cv::gapi::GBackend::Priv + { + virtual void unpackKernel(ade::Graph &graph, + const ade::NodeHandle &op_node, + const cv::GKernelImpl &impl) override + { + GGPUModel gm(graph); + auto gpu_impl = cv::util::any_cast<cv::GGPUKernel>(impl.opaque); + gm.metadata(op_node).set(cv::gimpl::Unit{gpu_impl}); + } + + virtual EPtr compile(const ade::Graph &graph, + const cv::GCompileArgs &, + const std::vector<ade::NodeHandle> &nodes) const override + { + return EPtr{new cv::gimpl::GGPUExecutable(graph, nodes)}; + } + }; +} + +cv::gapi::GBackend cv::gapi::gpu::backend() +{ + static cv::gapi::GBackend this_backend(std::make_shared<GGPUBackendImpl>()); + return this_backend; +} + +// GGPUExcecutable implementation ////////////////////////////////////////////// +cv::gimpl::GGPUExecutable::GGPUExecutable(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::UMat>()[desc.rc].create(mat_desc.size.width, mat_desc.size.height, type); + } + break; + } + default: util::throw_error(std::logic_error("Unsupported NodeType type")); + } + } +} + +// FIXME: Document what it does +cv::GArg cv::gimpl::GGPUExecutable::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::UMat>()[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::GGPUExecutable::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, true); + for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second, true); + + // 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. + GConstGGPUModel 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? + GGPUKernel k = gcm.metadata(op_info.nh).get<Unit>().k; + + // Initialize kernel's execution context: + // - Input parameters + GGPUContext 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(&GGPUExecutable::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, true); + } + + // Now trigger the executable unit + k.apply(context); + + 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, true); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.hpp new file mode 100644 index 000000000..1fb128d5f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpubackend.hpp @@ -0,0 +1,72 @@ +// 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_GGPUBACKEND_HPP +#define OPENCV_GAPI_GGPUBACKEND_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/gpu/ggpukernel.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 "GPUKernel"; } + GGPUKernel k; +}; + +class GGPUExecutable 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: + GGPUExecutable(const ade::Graph &graph, + const std::vector<ade::NodeHandle> &nodes); + + virtual inline bool canReshape() const override { return false; } + virtual inline void reshape(ade::Graph&, const GCompileArgs&) override + { + // FIXME: GPU plugin is in fact reshapeable (as it was initially, + // even before outMeta() has been introduced), so this limitation + // should be dropped. + util::throw_error(std::logic_error("GGPUExecutable::reshape() should never be called")); + } + + virtual void run(std::vector<InObj> &&input_objs, + std::vector<OutObj> &&output_objs) override; +}; + +}} + +#endif // OPENCV_GAPI_GGPUBACKEND_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.cpp new file mode 100644 index 000000000..a1ee6a113 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.cpp @@ -0,0 +1,582 @@ +// 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/gpu/core.hpp" +#include "backends/gpu/ggpucore.hpp" + +GAPI_GPU_KERNEL(GGPUAdd, cv::gapi::core::GAdd) +{ + static void run(const cv::UMat& a, const cv::UMat& b, int dtype, cv::UMat& out) + { + cv::add(a, b, out, cv::noArray(), dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUAddC, cv::gapi::core::GAddC) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, int dtype, cv::UMat& out) + { + cv::add(a, b, out, cv::noArray(), dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUSub, cv::gapi::core::GSub) +{ + static void run(const cv::UMat& a, const cv::UMat& b, int dtype, cv::UMat& out) + { + cv::subtract(a, b, out, cv::noArray(), dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUSubC, cv::gapi::core::GSubC) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, int dtype, cv::UMat& out) + { + cv::subtract(a, b, out, cv::noArray(), dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUSubRC, cv::gapi::core::GSubRC) +{ + static void run(const cv::Scalar& a, const cv::UMat& b, int dtype, cv::UMat& out) + { + cv::subtract(a, b, out, cv::noArray(), dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUMul, cv::gapi::core::GMul) +{ + static void run(const cv::UMat& a, const cv::UMat& b, double scale, int dtype, cv::UMat& out) + { + cv::multiply(a, b, out, scale, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUMulCOld, cv::gapi::core::GMulCOld) +{ + static void run(const cv::UMat& a, double b, int dtype, cv::UMat& out) + { + cv::multiply(a, b, out, 1, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUMulC, cv::gapi::core::GMulC) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, int dtype, cv::UMat& out) + { + cv::multiply(a, b, out, 1, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUDiv, cv::gapi::core::GDiv) +{ + static void run(const cv::UMat& a, const cv::UMat& b, double scale, int dtype, cv::UMat& out) + { + cv::divide(a, b, out, scale, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUDivC, cv::gapi::core::GDivC) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, double scale, int dtype, cv::UMat& out) + { + cv::divide(a, b, out, scale, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUDivRC, cv::gapi::core::GDivRC) +{ + static void run(const cv::Scalar& a, const cv::UMat& b, double scale, int dtype, cv::UMat& out) + { + cv::divide(a, b, out, scale, dtype); + } +}; + +GAPI_GPU_KERNEL(GGPUMask, cv::gapi::core::GMask) +{ + static void run(const cv::UMat& in, const cv::UMat& mask, cv::UMat& out) + { + out = cv::UMat::zeros(in.size(), in.type()); + in.copyTo(out, mask); + } +}; + + +GAPI_GPU_KERNEL(GGPUMean, cv::gapi::core::GMean) +{ + static void run(const cv::UMat& in, cv::Scalar& out) + { + out = cv::mean(in); + } +}; + +GAPI_GPU_KERNEL(GGPUPolarToCart, cv::gapi::core::GPolarToCart) +{ + static void run(const cv::UMat& magn, const cv::UMat& angle, bool angleInDegrees, cv::UMat& outx, cv::UMat& outy) + { + cv::polarToCart(magn, angle, outx, outy, angleInDegrees); + } +}; + +GAPI_GPU_KERNEL(GGPUCartToPolar, cv::gapi::core::GCartToPolar) +{ + static void run(const cv::UMat& x, const cv::UMat& y, bool angleInDegrees, cv::UMat& outmagn, cv::UMat& outangle) + { + cv::cartToPolar(x, y, outmagn, outangle, angleInDegrees); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpGT, cv::gapi::core::GCmpGT) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_GT); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpGE, cv::gapi::core::GCmpGE) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_GE); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpLE, cv::gapi::core::GCmpLE) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_LE); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpLT, cv::gapi::core::GCmpLT) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_LT); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpEQ, cv::gapi::core::GCmpEQ) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_EQ); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpNE, cv::gapi::core::GCmpNE) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_NE); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpGTScalar, cv::gapi::core::GCmpGTScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_GT); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpGEScalar, cv::gapi::core::GCmpGEScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_GE); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpLEScalar, cv::gapi::core::GCmpLEScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_LE); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpLTScalar, cv::gapi::core::GCmpLTScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_LT); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpEQScalar, cv::gapi::core::GCmpEQScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_EQ); + } +}; + +GAPI_GPU_KERNEL(GGPUCmpNEScalar, cv::gapi::core::GCmpNEScalar) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::compare(a, b, out, cv::CMP_NE); + } +}; + +GAPI_GPU_KERNEL(GGPUAnd, cv::gapi::core::GAnd) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::bitwise_and(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUAndS, cv::gapi::core::GAndS) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::bitwise_and(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUOr, cv::gapi::core::GOr) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::bitwise_or(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUOrS, cv::gapi::core::GOrS) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::bitwise_or(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUXor, cv::gapi::core::GXor) +{ + static void run(const cv::UMat& a, const cv::UMat& b, cv::UMat& out) + { + cv::bitwise_xor(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUXorS, cv::gapi::core::GXorS) +{ + static void run(const cv::UMat& a, const cv::Scalar& b, cv::UMat& out) + { + cv::bitwise_xor(a, b, out); + } +}; + +GAPI_GPU_KERNEL(GGPUNot, cv::gapi::core::GNot) +{ + static void run(const cv::UMat& a, cv::UMat& out) + { + cv::bitwise_not(a, out); + } +}; + +GAPI_GPU_KERNEL(GGPUSelect, cv::gapi::core::GSelect) +{ + static void run(const cv::UMat& src1, const cv::UMat& src2, const cv::UMat& mask, cv::UMat& out) + { + src2.copyTo(out); + src1.copyTo(out, mask); + } +}; + +////TODO: doesn't compiled with UMat +//GAPI_GPU_KERNEL(GGPUMin, cv::gapi::core::GMin) +//{ +// static void run(const cv::UMat& in1, const cv::UMat& in2, cv::UMat& out) +// { +// out = cv::min(in1, in2); +// } +//}; +// +////TODO: doesn't compiled with UMat +//GAPI_GPU_KERNEL(GGPUMax, cv::gapi::core::GMax) +//{ +// static void run(const cv::UMat& in1, const cv::UMat& in2, cv::UMat& out) +// { +// out = cv::max(in1, in2); +// } +//}; + + +GAPI_GPU_KERNEL(GGPUAbsDiff, cv::gapi::core::GAbsDiff) +{ + static void run(const cv::UMat& in1, const cv::UMat& in2, cv::UMat& out) + { + cv::absdiff(in1, in2, out); + } +}; + +GAPI_GPU_KERNEL(GGPUAbsDiffC, cv::gapi::core::GAbsDiffC) +{ + static void run(const cv::UMat& in1, const cv::Scalar& in2, cv::UMat& out) + { + cv::absdiff(in1, in2, out); + } +}; + +GAPI_GPU_KERNEL(GGPUSum, cv::gapi::core::GSum) +{ + static void run(const cv::UMat& in, cv::Scalar& out) + { + out = cv::sum(in); + } +}; + +GAPI_GPU_KERNEL(GGPUAddW, cv::gapi::core::GAddW) +{ + static void run(const cv::UMat& in1, double alpha, const cv::UMat& in2, double beta, double gamma, int dtype, cv::UMat& out) + { + cv::addWeighted(in1, alpha, in2, beta, gamma, out, dtype); + } +}; + + +GAPI_GPU_KERNEL(GGPUNormL1, cv::gapi::core::GNormL1) +{ + static void run(const cv::UMat& in, cv::Scalar& out) + { + out = cv::norm(in, cv::NORM_L1); + } +}; + +GAPI_GPU_KERNEL(GGPUNormL2, cv::gapi::core::GNormL2) +{ + static void run(const cv::UMat& in, cv::Scalar& out) + { + out = cv::norm(in, cv::NORM_L2); + } +}; + +GAPI_GPU_KERNEL(GGPUNormInf, cv::gapi::core::GNormInf) +{ + static void run(const cv::UMat& in, cv::Scalar& out) + { + out = cv::norm(in, cv::NORM_INF); + } +}; + +GAPI_GPU_KERNEL(GGPUIntegral, cv::gapi::core::GIntegral) +{ + static void run(const cv::UMat& in, int sdepth, int sqdepth, cv::UMat& out, cv::UMat& outSq) + { + cv::integral(in, out, outSq, sdepth, sqdepth); + } +}; + +GAPI_GPU_KERNEL(GGPUThreshold, cv::gapi::core::GThreshold) +{ + static void run(const cv::UMat& in, const cv::Scalar& a, const cv::Scalar& b, int type, cv::UMat& out) + { + cv::threshold(in, out, a.val[0], b.val[0], type); + } +}; + +GAPI_GPU_KERNEL(GGPUThresholdOT, cv::gapi::core::GThresholdOT) +{ + static void run(const cv::UMat& in, const cv::Scalar& b, int type, cv::UMat& out, cv::Scalar& outScalar) + { + outScalar = cv::threshold(in, out, b.val[0], b.val[0], type); + } +}; + + +GAPI_GPU_KERNEL(GGPUInRange, cv::gapi::core::GInRange) +{ + static void run(const cv::UMat& in, const cv::Scalar& low, const cv::Scalar& up, cv::UMat& out) + { + cv::inRange(in, low, up, out); + } +}; + +GAPI_GPU_KERNEL(GGPUSplit3, cv::gapi::core::GSplit3) +{ + static void run(const cv::UMat& in, cv::UMat &m1, cv::UMat &m2, cv::UMat &m3) + { + std::vector<cv::UMat> 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_GPU_KERNEL(GGPUSplit4, cv::gapi::core::GSplit4) +{ + static void run(const cv::UMat& in, cv::UMat &m1, cv::UMat &m2, cv::UMat &m3, cv::UMat &m4) + { + std::vector<cv::UMat> 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_GPU_KERNEL(GGPUMerge3, cv::gapi::core::GMerge3) +{ + static void run(const cv::UMat& in1, const cv::UMat& in2, const cv::UMat& in3, cv::UMat &out) + { + std::vector<cv::UMat> inMats = {in1, in2, in3}; + cv::merge(inMats, out); + } +}; + +GAPI_GPU_KERNEL(GGPUMerge4, cv::gapi::core::GMerge4) +{ + static void run(const cv::UMat& in1, const cv::UMat& in2, const cv::UMat& in3, const cv::UMat& in4, cv::UMat &out) + { + std::vector<cv::UMat> inMats = {in1, in2, in3, in4}; + cv::merge(inMats, out); + } +}; + +GAPI_GPU_KERNEL(GGPUResize, cv::gapi::core::GResize) +{ + static void run(const cv::UMat& in, cv::Size sz, double fx, double fy, int interp, cv::UMat &out) + { + cv::resize(in, out, sz, fx, fy, interp); + } +}; + +GAPI_GPU_KERNEL(GGPURemap, cv::gapi::core::GRemap) +{ + static void run(const cv::UMat& in, const cv::Mat& x, const cv::Mat& y, int a, int b, cv::Scalar s, cv::UMat& out) + { + cv::remap(in, out, x, y, a, b, s); + } +}; + +GAPI_GPU_KERNEL(GGPUFlip, cv::gapi::core::GFlip) +{ + static void run(const cv::UMat& in, int code, cv::UMat& out) + { + cv::flip(in, out, code); + } +}; + +GAPI_GPU_KERNEL(GGPUCrop, cv::gapi::core::GCrop) +{ + static void run(const cv::UMat& in, cv::Rect rect, cv::UMat& out) + { + cv::UMat(in, rect).copyTo(out); + } +}; + +GAPI_GPU_KERNEL(GGPUConcatHor, cv::gapi::core::GConcatHor) +{ + static void run(const cv::UMat& in1, const cv::UMat& in2, cv::UMat& out) + { + cv::hconcat(in1, in2, out); + } +}; + +GAPI_GPU_KERNEL(GGPUConcatVert, cv::gapi::core::GConcatVert) +{ + static void run(const cv::UMat& in1, const cv::UMat& in2, cv::UMat& out) + { + cv::vconcat(in1, in2, out); + } +}; + +GAPI_GPU_KERNEL(GGPULUT, cv::gapi::core::GLUT) +{ + static void run(const cv::UMat& in, const cv::Mat& lut, cv::UMat& out) + { + cv::LUT(in, lut, out); + } +}; + +GAPI_GPU_KERNEL(GGPUConvertTo, cv::gapi::core::GConvertTo) +{ + static void run(const cv::UMat& in, int rtype, double alpha, double beta, cv::UMat& out) + { + in.convertTo(out, rtype, alpha, beta); + } +}; + +cv::gapi::GKernelPackage cv::gapi::core::gpu::kernels() +{ + static auto pkg = cv::gapi::kernels + < GGPUAdd + , GGPUAddC + , GGPUSub + , GGPUSubC + , GGPUSubRC + , GGPUMul + , GGPUMulC + , GGPUMulCOld + , GGPUDiv + , GGPUDivC + , GGPUDivRC + , GGPUMean + , GGPUMask + , GGPUPolarToCart + , GGPUCartToPolar + , GGPUCmpGT + , GGPUCmpGE + , GGPUCmpLE + , GGPUCmpLT + , GGPUCmpEQ + , GGPUCmpNE + , GGPUCmpGTScalar + , GGPUCmpGEScalar + , GGPUCmpLEScalar + , GGPUCmpLTScalar + , GGPUCmpEQScalar + , GGPUCmpNEScalar + , GGPUAnd + , GGPUAndS + , GGPUOr + , GGPUOrS + , GGPUXor + , GGPUXorS + , GGPUNot + , GGPUSelect + //, GGPUMin + //, GGPUMax + , GGPUAbsDiff + , GGPUAbsDiffC + , GGPUSum + , GGPUAddW + , GGPUNormL1 + , GGPUNormL2 + , GGPUNormInf + , GGPUIntegral + , GGPUThreshold + , GGPUThresholdOT + , GGPUInRange + , GGPUSplit3 + , GGPUSplit4 + , GGPUResize + , GGPUMerge3 + , GGPUMerge4 + , GGPURemap + , GGPUFlip + , GGPUCrop + , GGPUConcatHor + , GGPUConcatVert + , GGPULUT + , GGPUConvertTo + >(); + return pkg; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.hpp new file mode 100644 index 000000000..47cbfa6bd --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpucore.hpp @@ -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_GGPUCORE_HPP +#define OPENCV_GAPI_GGPUCORE_HPP + +#include <map> +#include <string> + +#include "opencv2/gapi/gpu/ggpukernel.hpp" + +namespace cv { namespace gimpl { + +// NB: This is what a "Kernel Package" from the original Wiki doc should be. +void loadGPUCore(std::map<std::string, cv::GGPUKernel> &kmap); + +} +} + +#endif // OPENCV_GAPI_GGPUCORE_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.cpp new file mode 100644 index 000000000..9b7aca1a2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.cpp @@ -0,0 +1,277 @@ +// 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/gpu/imgproc.hpp" +#include "backends/gpu/ggpuimgproc.hpp" + + +GAPI_GPU_KERNEL(GGPUSepFilter, cv::gapi::imgproc::GSepFilter) +{ + static void run(const cv::UMat& 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::UMat &out) + { + if( border == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUBoxFilter, cv::gapi::imgproc::GBoxFilter) +{ + static void run(const cv::UMat& in, int ddepth, const cv::Size& ksize, const cv::Point& anchor, bool normalize, int borderType, const cv::Scalar& bordVal, cv::UMat &out) + { + if( borderType == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUBlur, cv::gapi::imgproc::GBlur) +{ + static void run(const cv::UMat& in, const cv::Size& ksize, const cv::Point& anchor, int borderType, const cv::Scalar& bordVal, cv::UMat &out) + { + if( borderType == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUFilter2D, cv::gapi::imgproc::GFilter2D) +{ + static void run(const cv::UMat& in, int ddepth, const cv::Mat& k, const cv::Point& anchor, const cv::Scalar& delta, int border, + const cv::Scalar& bordVal, cv::UMat &out) + { + if( border == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUGaussBlur, cv::gapi::imgproc::GGaussBlur) +{ + static void run(const cv::UMat& in, const cv::Size& ksize, double sigmaX, double sigmaY, int borderType, const cv::Scalar& bordVal, cv::UMat &out) + { + if( borderType == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUMedianBlur, cv::gapi::imgproc::GMedianBlur) +{ + static void run(const cv::UMat& in, int ksize, cv::UMat &out) + { + cv::medianBlur(in, out, ksize); + } +}; + +GAPI_GPU_KERNEL(GGPUErode, cv::gapi::imgproc::GErode) +{ + static void run(const cv::UMat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::UMat &out) + { + cv::erode(in, out, kernel, anchor, iterations, borderType, borderValue); + } +}; + +GAPI_GPU_KERNEL(GGPUDilate, cv::gapi::imgproc::GDilate) +{ + static void run(const cv::UMat& in, const cv::Mat& kernel, const cv::Point& anchor, int iterations, int borderType, const cv::Scalar& borderValue, cv::UMat &out) + { + cv::dilate(in, out, kernel, anchor, iterations, borderType, borderValue); + } +}; + +GAPI_GPU_KERNEL(GGPUSobel, cv::gapi::imgproc::GSobel) +{ + static void run(const cv::UMat& in, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType, + const cv::Scalar& bordVal, cv::UMat &out) + { + if( borderType == cv::BORDER_CONSTANT ) + { + cv::UMat 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_GPU_KERNEL(GGPUEqualizeHist, cv::gapi::imgproc::GEqHist) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::equalizeHist(in, out); + } +}; + +GAPI_GPU_KERNEL(GGPUCanny, cv::gapi::imgproc::GCanny) +{ + static void run(const cv::UMat& in, double thr1, double thr2, int apSize, bool l2gradient, cv::UMat &out) + { + cv::Canny(in, out, thr1, thr2, apSize, l2gradient); + } +}; + +GAPI_GPU_KERNEL(GGPURGB2YUV, cv::gapi::imgproc::GRGB2YUV) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_RGB2YUV); + } +}; + +GAPI_GPU_KERNEL(GGPUYUV2RGB, cv::gapi::imgproc::GYUV2RGB) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_YUV2RGB); + } +}; + +GAPI_GPU_KERNEL(GGPURGB2Lab, cv::gapi::imgproc::GRGB2Lab) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_RGB2Lab); + } +}; + +GAPI_GPU_KERNEL(GGPUBGR2LUV, cv::gapi::imgproc::GBGR2LUV) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_BGR2Luv); + } +}; + +GAPI_GPU_KERNEL(GGPUBGR2YUV, cv::gapi::imgproc::GBGR2YUV) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_BGR2YUV); + } +}; + +GAPI_GPU_KERNEL(GGPULUV2BGR, cv::gapi::imgproc::GLUV2BGR) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_Luv2BGR); + } +}; + +GAPI_GPU_KERNEL(GGPUYUV2BGR, cv::gapi::imgproc::GYUV2BGR) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_YUV2BGR); + } +}; + +GAPI_GPU_KERNEL(GGPURGB2Gray, cv::gapi::imgproc::GRGB2Gray) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_RGB2GRAY); + } +}; + +GAPI_GPU_KERNEL(GGPUBGR2Gray, cv::gapi::imgproc::GBGR2Gray) +{ + static void run(const cv::UMat& in, cv::UMat &out) + { + cv::cvtColor(in, out, cv::COLOR_BGR2GRAY); + } +}; + +GAPI_GPU_KERNEL(GGPURGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom) +{ + //TODO: avoid copy + static void run(const cv::UMat& in, float rY, float bY, float gY, cv::UMat &out) + { + cv::Mat planes[3]; + cv::split(in.getMat(cv::ACCESS_READ), planes); + cv::Mat tmp_out = (planes[0]*rY + planes[1]*bY + planes[2]*gY); + tmp_out.copyTo(out); + } +}; + + +cv::gapi::GKernelPackage cv::gapi::imgproc::gpu::kernels() +{ + static auto pkg = cv::gapi::kernels + < GGPUFilter2D + , GGPUSepFilter + , GGPUBoxFilter + , GGPUBlur + , GGPUGaussBlur + , GGPUMedianBlur + , GGPUErode + , GGPUDilate + , GGPUSobel + , GGPUCanny + , GGPUEqualizeHist + , GGPURGB2YUV + , GGPUYUV2RGB + , GGPURGB2Lab + , GGPUBGR2LUV + , GGPUBGR2YUV + , GGPUYUV2BGR + , GGPULUV2BGR + , GGPUBGR2Gray + , GGPURGB2Gray + , GGPURGB2GrayCustom + >(); + return pkg; +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.hpp new file mode 100644 index 000000000..cd2e324e6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpuimgproc.hpp @@ -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_GGPUIMGPROC_HPP +#define OPENCV_GAPI_GGPUIMGPROC_HPP + +#include <map> +#include <string> + +#include "opencv2/gapi/gpu/ggpukernel.hpp" + +namespace cv { namespace gimpl { + +// NB: This is what a "Kernel Package" from the origianl Wiki doc should be. +void loadGPUImgProc(std::map<std::string, cv::GGPUKernel> &kmap); + +}} + +#endif // OPENCV_GAPI_GGPUIMGPROC_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpukernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpukernel.cpp new file mode 100644 index 000000000..87e2aa97e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/gpu/ggpukernel.cpp @@ -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/gpu/ggpukernel.hpp" + +const cv::UMat& cv::GGPUContext::inMat(int input) +{ + return (inArg<cv::UMat>(input)); +} + +cv::UMat& cv::GGPUContext::outMatR(int output) +{ + return (*(util::get<cv::UMat*>(m_results.at(output)))); +} + +const cv::gapi::own::Scalar& cv::GGPUContext::inVal(int input) +{ + return inArg<cv::gapi::own::Scalar>(input); +} + +cv::gapi::own::Scalar& cv::GGPUContext::outValR(int output) +{ + return *util::get<cv::gapi::own::Scalar*>(m_results.at(output)); +} + +cv::detail::VectorRef& cv::GGPUContext::outVecRef(int output) +{ + return util::get<cv::detail::VectorRef>(m_results.at(output)); +} + +cv::GGPUKernel::GGPUKernel() +{ +} + +cv::GGPUKernel::GGPUKernel(const GGPUKernel::F &f) + : m_f(f) +{ +} + +void cv::GGPUKernel::apply(GGPUContext &ctx) +{ + CV_Assert(m_f); + m_f(ctx); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/README.md b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/README.md new file mode 100644 index 000000000..995aa3977 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/README.md @@ -0,0 +1 @@ +This directory contains G-API graph compiler logic.
\ No newline at end of file diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp new file mode 100644 index 000000000..876575d94 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp @@ -0,0 +1,157 @@ +// 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 <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 + } +} + +bool cv::GCompiled::Priv::canReshape() const +{ + GAPI_Assert(m_exec); + return m_exec->canReshape(); +} + +void cv::GCompiled::Priv::reshape(const GMetaArgs& inMetas, const GCompileArgs& args) +{ + GAPI_Assert(m_exec); + m_exec->reshape(inMetas, args); + m_metas = inMetas; +} + +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)}); +} + +#if !defined(GAPI_STANDALONE) +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)); +} +#endif // !defined(GAPI_STANDALONE) + +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; +} + +bool cv::GCompiled::canReshape() const +{ + return m_priv->canReshape(); +} + +void cv::GCompiled::reshape(const GMetaArgs& inMetas, const GCompileArgs& args) +{ + m_priv->reshape(inMetas, args); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled_priv.hpp new file mode 100644 index 000000000..e616b2bb7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled_priv.hpp @@ -0,0 +1,61 @@ +// 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; + + bool canReshape() const; + void reshape(const GMetaArgs& inMetas, const GCompileArgs &args); + + 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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp new file mode 100644 index 000000000..32ce8e38f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp @@ -0,0 +1,281 @@ +// 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 <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:> +#if !defined(GAPI_STANDALONE) +#include "opencv2/gapi/cpu/core.hpp" // Also directly refer to Core +#include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations +#endif // !defined(GAPI_STANDALONE) +// </FIXME:> + +#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend() + +#include "logger.hpp" + +namespace +{ + cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args) + { + static auto ocv_pkg = +#if !defined(GAPI_STANDALONE) + combine(cv::gapi::core::cpu::kernels(), + cv::gapi::imgproc::cpu::kernels(), + cv::unite_policy::KEEP); +#else + cv::gapi::GKernelPackage(); +#endif // !defined(GAPI_STANDALONE) + 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", std::bind(passes::inferMeta, _1, false)); + 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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp new file mode 100644 index 000000000..b369c14d1 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.cpp new file mode 100644 index 000000000..8e20302a3 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.cpp @@ -0,0 +1,289 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp new file mode 100644 index 000000000..03b42ff38 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp @@ -0,0 +1,187 @@ +// 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 bool canReshape() const = 0; + virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp new file mode 100644 index 000000000..4b2455219 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp @@ -0,0 +1,247 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp new file mode 100644 index 000000000..003519b82 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.cpp new file mode 100644 index 000000000..c9b2fbbdf --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.cpp @@ -0,0 +1,305 @@ +// 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 "precomp.hpp" + +#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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp new file mode 100644 index 000000000..ce12c7e11 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gobjref.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gobjref.hpp new file mode 100644 index 000000000..be365c90e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gobjref.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp new file mode 100644 index 000000000..8741089ba --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp @@ -0,0 +1,223 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp new file mode 100644 index 000000000..7119e3411 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp @@ -0,0 +1,641 @@ +// 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 <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 "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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp new file mode 100644 index 000000000..60bf36afd --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp @@ -0,0 +1,122 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.hpp new file mode 100644 index 000000000..3aa18e627 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/islands.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/islands.cpp new file mode 100644 index 000000000..942f738bd --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/islands.cpp @@ -0,0 +1,233 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp new file mode 100644 index 000000000..2703149e7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp @@ -0,0 +1,157 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp new file mode 100644 index 000000000..528d84ce8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp @@ -0,0 +1,125 @@ +// 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 <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, bool meta_is_initialized) +{ + // 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 auto& 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 (!meta_is_initialized && !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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp new file mode 100644 index 000000000..14f6acdc0 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp @@ -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, bool meta_is_initialized); +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/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/transactions.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/transactions.hpp new file mode 100644 index 000000000..54af8a6e6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/transactions.hpp @@ -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/gapi/own/assert.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: GAPI_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 + GAPI_Assert(m_node->inEdges().size() == 0); + GAPI_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/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp new file mode 100644 index 000000000..f117c0633 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp @@ -0,0 +1,244 @@ +// 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 <iostream> + +#include <ade/util/zip_range.hpp> + +#include "opencv2/gapi/opencv_includes.hpp" +#include "executor/gexecutor.hpp" +#include "compiler/passes/passes.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 + + 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); + +#if !defined(GAPI_STANDALONE) + // Building as part of OpenCV - follow OpenCV behavior + // if output buffer is not enough to hold the result, reallocate it + auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index)); + out_mat.create(cv::gapi::own::to_ocv(desc.size), type); +#else + // Building standalone - output buffer should always exist, + // and _exact_ match our inferred metadata + auto& out_mat = *get<cv::gapi::own::Mat*>(args.outObjs.at(index)); + GAPI_Assert( out_mat.type() == type + && out_mat.data != nullptr + && out_mat.rows == desc.size.height + && out_mat.cols == desc.size.width) +#endif // !defined(GAPI_STANDALONE) + } + } + // 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; +} + +bool cv::gimpl::GExecutor::canReshape() const +{ + // FIXME: Introduce proper reshaping support on GExecutor level + // for all cases! + return (m_ops.size() == 1) && m_ops[0].isl_exec->canReshape(); +} + +void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args) +{ + GAPI_Assert(canReshape()); + auto& g = *m_orig_graph.get(); + ade::passes::PassContext ctx{g}; + passes::initMeta(ctx, inMetas); + passes::inferMeta(ctx, true); + m_ops[0].isl_exec->reshape(g, args); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.hpp new file mode 100644 index 000000000..e4128ba77 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.hpp @@ -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 + + +#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); + + bool canReshape() const; + void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); + + const GModel::Graph& model() const; // FIXME: make it ConstGraph? +}; + +} // namespace gimpl +} // namespace cv + +#endif // OPENCV_GAPI_GEXECUTOR_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/logger.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/logger.hpp new file mode 100644 index 000000000..ff4c7591b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/logger.hpp @@ -0,0 +1,22 @@ +// 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 !defined(GAPI_STANDALONE) +# include "opencv2/core/cvdef.h" +# 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 // !defined(GAPI_STANDALONE) + + +#endif // __OPENCV_GAPI_LOGGER_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp new file mode 100644 index 000000000..eebe9d896 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp @@ -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_PRECOMP_HPP__ +#define __OPENCV_GAPI_PRECOMP_HPP__ + +#if !defined(GAPI_STANDALONE) +# include "opencv2/core.hpp" +# include "opencv2/imgproc.hpp" +# include "opencv2/gapi/core.hpp" +# include "opencv2/gapi/imgproc.hpp" +#endif // !defined(GAPI_STANDALONE) + +#include "opencv2/gapi.hpp" +#include "opencv2/gapi/gkernel.hpp" + +#endif // __OPENCV_GAPI_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp new file mode 100644 index 000000000..1f5de7a92 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.cpp new file mode 100644 index 000000000..eb7761248 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.cpp @@ -0,0 +1,9 @@ +// 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_core_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp new file mode 100644 index 000000000..77a82dfd2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp @@ -0,0 +1,153 @@ +// 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,double,cv::GCompileArgs>> {}; +struct AddWeightedTest : public TestParams<std::tuple<int,cv::Size,int,bool,double,cv::GCompileArgs>>{}; +struct NormTest : public TestParams<std::tuple<NormTypes,int,cv::Size, double, 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<compare_f, int, int, cv::Size, cv::Size, cv::GCompileArgs>> {}; +struct ResizeTestFxFy : public TestWithParam<std::tuple<compare_f, int, int, cv::Size, 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>> {}; +struct PhaseTest : public TestParams<std::tuple<int, cv::Size, bool, cv::GCompileArgs>> {}; +struct SqrtTest : public TestParams<std::tuple<int, cv::Size, cv::GCompileArgs>> {}; +} // opencv_test + +#endif //OPENCV_GAPI_CORE_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp new file mode 100644 index 000000000..d33b5cc63 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp @@ -0,0 +1,1479 @@ +// 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 ) + { + in_mat1.setTo(1, in_mat1 == 0); // avoid zeros in divide input data + out = cv::gapi::divRC(sc1, in1, scale, dtype); + cv::divide(sc, in_mat1, out_mat_ocv, scale, dtype); + break; + } + else + { + sc += Scalar(1, 1, 1, 1); // avoid zeros in divide input data + 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): + { + in_mat2.setTo(1, in_mat2 == 0); // avoid zeros in divide input data + 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, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pull/12826 +{ + 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, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pull/12826 +{ + 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 tolerance = std::get<3>(param); + auto compile_args = std::get<4>(param); + //initMatrixRandU(std::get<0>(param), sz_in, std::get<2>(param)); + initMatsRandN(std::get<0>(param), sz_in, std::get<2>(param)); //TODO: workaround trying to fix SumTest failures + + + 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_LE(std::abs(out_sum[0] - out_sum_ocv[0]) / std::max(1.0, std::abs(out_sum_ocv[0])), tolerance) + << "OCV=" << out_sum_ocv[0] << " GAPI=" << out_sum[0]; + } +} + +TEST_P(AddWeightedTest, AccuracyTest) +{ + int type = 0, dtype = 0; + cv::Size sz_in; + bool initOut = false; + cv::GCompileArgs compile_args; + double tolerance = 0.0; + std::tie(type, sz_in, dtype, initOut, tolerance, 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) >= tolerance;// 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; + double tolerance = 0.0; + cv::GCompileArgs compile_args; + std::tie(opType, type, sz, tolerance, 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_LE(std::abs(out_norm[0] - out_norm_ocv[0]) / std::max(1.0, std::abs(out_norm_ocv[0])), tolerance) + << "OCV=" << out_norm_ocv[0] << " GAPI=" << out_norm[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); + cv::Scalar thr = initScalarRandU(50); + cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 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 ////////////////////////////////////////////////////////////// + { + ASSERT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_L1)); + } +} + +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); + cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 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); + cv::Scalar thrLow = initScalarRandU(100); + cv::Scalar thrUp = initScalarRandU(100) + cv::Scalar(100, 100, 100, 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(compare_f cmpF, int type, int interp, cv::Size sz_in, cv::Size sz_out, double fx, double fy, 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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat, out_mat_ocv)); + } +} + +TEST_P(ResizeTest, AccuracyTest) +{ + compare_f cmpF; + int type = 0, interp = 0; + cv::Size sz_in, sz_out; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, interp, sz_in, sz_out, compile_args) = GetParam(); + ResizeAccuracyTest(cmpF, type, interp, sz_in, sz_out, 0.0, 0.0, std::move(compile_args)); +} + +TEST_P(ResizeTestFxFy, AccuracyTest) +{ + compare_f cmpF; + int type = 0, interp = 0; + cv::Size sz_in; + double fx = 0.0, fy = 0.0; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, interp, sz_in, fx, fy, compile_args) = GetParam(); + ResizeAccuracyTest(cmpF, type, interp, sz_in, cv::Size{0, 0}, fx, fy, 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); + cv::randu(in_lut, cv::Scalar::all(0), cv::Scalar::all(255)); + + // 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); + } +} + +TEST_P(PhaseTest, AccuracyTest) +{ + int img_type = -1; + cv::Size img_size; + bool angle_in_degrees = false; + cv::GCompileArgs compile_args; + std::tie(img_type, img_size, angle_in_degrees, compile_args) = GetParam(); + initMatsRandU(img_type, img_size, img_type); + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in_x, in_y; + auto out = cv::gapi::phase(in_x, in_y, angle_in_degrees); + + cv::GComputation c(in_x, in_y, out); + c.apply(in_mat1, in_mat2, out_mat_gapi, std::move(compile_args)); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::phase(in_mat1, in_mat2, out_mat_ocv, angle_in_degrees); + + // Comparison ////////////////////////////////////////////////////////////// + // FIXME: use a comparison functor instead (after enabling OpenCL) + { + EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); + } +} + +TEST_P(SqrtTest, AccuracyTest) +{ + int img_type = -1; + cv::Size img_size; + cv::GCompileArgs compile_args; + std::tie(img_type, img_size, compile_args) = GetParam(); + initMatrixRandU(img_type, img_size, img_type); + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in; + auto out = cv::gapi::sqrt(in); + + cv::GComputation c(in, out); + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::sqrt(in_mat1, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // FIXME: use a comparison functor instead (after enabling OpenCL) + { + EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); + } +} + + +} // opencv_test + +#endif //OPENCV_GAPI_CORE_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.cpp new file mode 100644 index 000000000..b7c027908 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.cpp @@ -0,0 +1,9 @@ +// 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_imgproc_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp new file mode 100644 index 000000000..c21b26b68 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp @@ -0,0 +1,42 @@ +// 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<compare_f, MatType,int,cv::Size,int,int,bool,cv::GCompileArgs>> {}; +struct BoxFilterTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,int,bool,cv::GCompileArgs>> {}; +struct SepFilterTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {}; +struct BlurTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {}; +struct GaussianBlurTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,bool,cv::GCompileArgs>> {}; +struct MedianBlurTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,bool,cv::GCompileArgs>> {}; +struct ErodeTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {}; +struct Erode3x3Test : public TestParams <std::tuple<compare_f,MatType,cv::Size,bool,int,cv::GCompileArgs>> {}; +struct DilateTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {}; +struct Dilate3x3Test : public TestParams <std::tuple<compare_f,MatType,cv::Size,bool,int,cv::GCompileArgs>> {}; +struct SobelTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,int,int,bool,cv::GCompileArgs>> {}; +struct EqHistTest : public TestParams <std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct CannyTest : public TestParams <std::tuple<compare_f,MatType,cv::Size,double,double,int,bool,bool,cv::GCompileArgs>> {}; +struct RGB2GrayTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct BGR2GrayTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct RGB2YUVTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct YUV2RGBTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct RGB2LabTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct BGR2LUVTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct LUV2BGRTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct BGR2YUVTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +struct YUV2BGRTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; +} // opencv_test + +#endif //OPENCV_GAPI_IMGPROC_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp new file mode 100644 index 000000000..3de428922 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp @@ -0,0 +1,630 @@ +// 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) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, borderType = 0, dtype = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(BoxFilterTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int filterSize = 0, borderType = 0, dtype = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, filterSize, sz, borderType, dtype, 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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(SepFilterTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, dtype = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(BlurTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int filterSize = 0, borderType = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, type, filterSize, sz, borderType, 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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(GaussianBlurTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF,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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(MedianBlurTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(ErodeTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, kernType = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(Erode3x3Test, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int numIters = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(DilateTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, kernType = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(Dilate3x3Test, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int numIters = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + + +TEST_P(SobelTest, AccuracyTest) +{ + compare_f cmpF; + MatType type = 0; + int kernSize = 0, dtype = 0, dx = 0, dy = 0; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(EqHistTest, AccuracyTest) +{ + compare_f cmpF; + cv::Size sz; + bool initOut = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(GetParam())); + } +} + +TEST_P(CannyTest, AccuracyTest) +{ + compare_f cmpF; + 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(cmpF, 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_TRUE(cmpF(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<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC1, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(BGR2GrayTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC1, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(RGB2YUVTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(YUV2RGBTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(RGB2LabTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(BGR2LUVTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(LUV2BGRTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(BGR2YUVTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} + +TEST_P(YUV2BGRTest, AccuracyTest) +{ + auto param = GetParam(); + auto compile_args = std::get<3>(param); + compare_f cmpF = std::get<0>(param); + initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(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 ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + } +} +} // opencv_test + +#endif //OPENCV_GAPI_IMGPROC_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.cpp new file mode 100644 index 000000000..1f6f0ce20 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.cpp @@ -0,0 +1,9 @@ +// 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_operators_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp new file mode 100644 index 000000000..9f53d3685 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp @@ -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 in2){return in-in2;}, + [](const cv::Mat& in, const cv::Mat& in2, cv::Mat& out){cv::subtract(in, in2, out);}}; +g_api_ocv_pair_mat_mat opDivM = {std::string{"operator/"}, + [](cv::GMat in,cv::GMat in2){return in/in2;}, + [](const cv::Mat& in, const cv::Mat& in2, cv::Mat& out){cv::divide(in, in2, 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<compare_f, g_api_ocv_pair_mat_scalar,int,cv::Size,int,bool,cv::GCompileArgs>>{}; +struct MathOperatorMatMatTest : public TestParams<std::tuple<compare_f, 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/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp new file mode 100644 index 000000000..7ec702ae9 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp @@ -0,0 +1,104 @@ +// 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 ) +{ + compare_f cmpF; + g_api_ocv_pair_mat_scalar op; + int type = 0, dtype = 0; + cv::Size sz; + bool initOutMatr = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(MathOperatorMatMatTest, OperatorAccuracyTest ) +{ + compare_f cmpF; + g_api_ocv_pair_mat_mat op; + int type = 0, dtype = 0; + cv::Size sz; + bool initOutMatr = false; + cv::GCompileArgs compile_args; + std::tie(cmpF, 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_TRUE(cmpF(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/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp new file mode 100644 index 000000000..be0fc3c7e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp @@ -0,0 +1,296 @@ +// 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; + + cv::Scalar initScalarRandU(unsigned upper) + { + auto& rng = cv::theRNG(); + double s1 = rng(upper); + double s2 = rng(upper); + double s3 = rng(upper); + double s4 = rng(upper); + return cv::Scalar(s1, s2, s3, s4); + } + + 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); + + sc = initScalarRandU(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); + + sc = initScalarRandU(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>{}; + +using compare_f = std::function<bool(const cv::Mat &a, const cv::Mat &b)>; + +template<typename T> +struct Wrappable +{ + compare_f to_compare_f() + { + T t = *static_cast<T*const>(this); + return [t](const cv::Mat &a, const cv::Mat &b) + { + return t(a, b); + }; + } +}; + +class AbsExact : public Wrappable<AbsExact> +{ +public: + AbsExact() {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + if (cv::norm(in1, in2, NORM_INF) != 0) + { + std::cout << "AbsExact error: G-API output and reference output matrixes are not bitexact equal." << std::endl; + return false; + } + else + { + return true; + } + } +private: +}; + +class AbsTolerance : public Wrappable<AbsTolerance> +{ +public: + AbsTolerance(double tol) : _tol(tol) {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + if (cv::norm(in1, in2, NORM_INF) > _tol) + { + std::cout << "AbsTolerance error: Number of different pixels in " << std::endl; + std::cout << "G-API output and reference output matrixes exceeds " << _tol << " pixels threshold." << std::endl; + return false; + } + else + { + return true; + } + } +private: + double _tol; +}; + +class Tolerance_FloatRel_IntAbs : public Wrappable<Tolerance_FloatRel_IntAbs> +{ +public: + Tolerance_FloatRel_IntAbs(double tol, double tol8u) : _tol(tol), _tol8u(tol8u) {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + int depth = CV_MAT_DEPTH(in1.type()); + { + double err = depth >= CV_32F ? cv::norm(in1, in2, NORM_L1 | NORM_RELATIVE) + : cv::norm(in1, in2, NORM_INF); + double tolerance = depth >= CV_32F ? _tol : _tol8u; + if (err > tolerance) + { + std::cout << "Tolerance_FloatRel_IntAbs error: err=" << err + << " tolerance=" << tolerance + << " depth=" << cv::typeToString(depth) << std::endl; + return false; + } + else + { + return true; + } + } + } +private: + double _tol; + double _tol8u; +}; + + +class AbsSimilarPoints : public Wrappable<AbsSimilarPoints> +{ +public: + AbsSimilarPoints(double tol, double percent) : _tol(tol), _percent(percent) {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + Mat diff; + cv::absdiff(in1, in2, diff); + Mat err_mask = diff > _tol; + int err_points = cv::countNonZero(err_mask.reshape(1)); + double max_err_points = _percent * std::max((size_t)1000, in1.total()); + if (err_points > max_err_points) + { + std::cout << "AbsSimilarPoints error: err_points=" << err_points + << " max_err_points=" << max_err_points << " (total=" << in1.total() << ")" + << " diff_tolerance=" << _tol << std::endl; + return false; + } + else + { + return true; + } + } +private: + double _tol; + double _percent; +}; + + +class ToleranceFilter : public Wrappable<ToleranceFilter> +{ +public: + ToleranceFilter(double tol, double tol8u, double inf_tol = 2.0) : _tol(tol), _tol8u(tol8u), _inf_tol(inf_tol) {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + int depth = CV_MAT_DEPTH(in1.type()); + { + double err_Inf = cv::norm(in1, in2, NORM_INF); + if (err_Inf > _inf_tol) + { + std::cout << "ToleranceFilter error: err_Inf=" << err_Inf << " tolerance=" << _inf_tol << std::endl; + return false; + } + double err = cv::norm(in1, in2, NORM_L2 | NORM_RELATIVE); + double tolerance = depth >= CV_32F ? _tol : _tol8u; + if (err > tolerance) + { + std::cout << "ToleranceFilter error: err=" << err << " tolerance=" << tolerance + << " depth=" << cv::depthToString(depth) + << std::endl; + return false; + } + } + return true; + } +private: + double _tol; + double _tol8u; + double _inf_tol; +}; + +class ToleranceColor : public Wrappable<ToleranceColor> +{ +public: + ToleranceColor(double tol, double inf_tol = 2.0) : _tol(tol), _inf_tol(inf_tol) {} + bool operator() (const cv::Mat& in1, const cv::Mat& in2) const + { + { + double err_Inf = cv::norm(in1, in2, NORM_INF); + if (err_Inf > _inf_tol) + { + std::cout << "ToleranceColor error: err_Inf=" << err_Inf << " tolerance=" << _inf_tol << std::endl;; + return false; + } + double err = cv::norm(in1, in2, NORM_L1 | NORM_RELATIVE); + if (err > _tol) + { + std::cout << "ToleranceColor error: err=" << err << " tolerance=" << _tol << std::endl;; + return false; + } + } + return true; + } +private: + double _tol; + double _inf_tol; +}; +} // namespace opencv_test + +namespace +{ + inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_f&) + { + return os << "compare_f"; + } +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp new file mode 100644 index 000000000..11e78bd99 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp @@ -0,0 +1,405 @@ +// 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(PhaseCPU, PhaseTest, + Combine(Values(CV_32F, CV_32FC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + testing::Bool(), + Values(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(SqrtCPU, SqrtTest, + Combine(Values(CV_32F, CV_32FC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + 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(1e-5), + 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(0.5000005), + 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(1e-5), + 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, 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(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(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(cv::compile_args(CORE_CPU)))); + +INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTestFxFy, + Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(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/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp new file mode 100644 index 000000000..c65052b36 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp @@ -0,0 +1,506 @@ +// 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" + +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(0.5000005), + 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(PhaseFluid, PhaseTest, + Combine(Values(CV_32F, CV_32FC3), + 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(SqrtFluid, SqrtTest, + Combine(Values(CV_32F, CV_32FC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + 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, 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( + ResizeTestFluid, ResizeTest, + Combine(Values(AbsExact().to_compare_f()), + 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(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()) + Values(0.0), + ); + +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))), + Values(0.0), + 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/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp new file mode 100644 index 000000000..beda02240 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp @@ -0,0 +1,238 @@ +// 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(AbsExact().to_compare_f()), + 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(AbsTolerance(0).to_compare_f()), + 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), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(SepFilterTestCPU_8U, SepFilterTest, + Combine(Values(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsTolerance(0.0).to_compare_f()), + 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), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(gaussBlurTestCPU, GaussianBlurTest, + Combine(Values(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), + Values(3, 5), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(-1, CV_16S, 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(SobelTestCPU32F, SobelTest, + Combine(Values(AbsExact().to_compare_f()), + Values(CV_32FC1), + Values(3, 5), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(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(AbsExact().to_compare_f()), + 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(AbsSimilarPoints(0, 0.05).to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp new file mode 100644 index 000000000..5dca2092a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp @@ -0,0 +1,168 @@ +// 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" + +#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels() + +namespace opencv_test +{ + +INSTANTIATE_TEST_CASE_P(RGB2GrayTestFluid, RGB2GrayTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + 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(ToleranceColor(1e-3).to_compare_f()), + 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(ToleranceColor(1e-3).to_compare_f()), + 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(ToleranceColor(1e-3).to_compare_f()), + 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(AbsSimilarPoints(1, 0.05).to_compare_f()), + 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(ToleranceColor(5e-3, 6).to_compare_f()), + 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(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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(true, false), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(gaussBlurTestFluid, GaussianBlurTest, + Combine(Values(ToleranceFilter(1e-3f, 0.01).to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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_16S, CV_32F), + Values(0, 1), + Values(1, 2), + Values(true, false), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(SobelTestFluid32F, SobelTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + Values(CV_32FC1), + Values(3), // add kernel size=5 when implementation is ready + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_32F), + Values(0, 1), + Values(1, 2), + Values(true, false), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(boxFilterTestFluid32, BoxFilterTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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(true, false), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(sepFilterTestFluid, SepFilterTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp new file mode 100644 index 000000000..435c798c6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp @@ -0,0 +1,73 @@ +// 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp new file mode 100644 index 000000000..4179fa53b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp @@ -0,0 +1,72 @@ +// 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" + +#define CORE_FLUID cv::gapi::core::fluid::kernels() + +namespace opencv_test +{ + +INSTANTIATE_TEST_CASE_P(MathOperatorTestFluid, MathOperatorMatMatTest, + Combine(Values(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: 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(AbsExact().to_compare_f()), + 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(AbsExact().to_compare_f()), + 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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_array_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_array_tests.cpp new file mode 100644 index 000000000..e5765624c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_array_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp new file mode 100644 index 000000000..62069d865 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp @@ -0,0 +1,312 @@ +// 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); + } + }; + + void FluidFooRow(const uint8_t* in, uint8_t* out, int length) + { + for (int i = 0; i < length; i++) + { + out[i] = in[i] + 3; + } + } + + void FluidBarRow(const uint8_t* in1, const uint8_t* in2, uint8_t* out, int length) + { + for (int i = 0; i < length; i++) + { + out[i] = 3*(in1[i] + in2[i]); + } + } + + 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) + { + FluidFooRow(in.InLineB(0), out.OutLineB(), in.length()); + } + }; + + 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) + { + FluidBarRow(in1.InLineB(0), in2.InLineB(0), out.OutLineB(), in1.length()); + } + }; + + G_TYPED_KERNEL(FluidFooI, <cv::GMat(cv::GMat)>, "test.kernels.fluid_foo") + { + static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; } + }; + + G_TYPED_KERNEL(FluidBarI, <cv::GMat(cv::GMat,cv::GMat)>, "test.kernels.fluid_bar") + { + static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) { return in; } + }; + + GAPI_FLUID_KERNEL(FluidFoo, FluidFooI, false) + { + static const int Window = 1; + + static void run(const cv::gapi::fluid::View &in, + cv::gapi::fluid::Buffer &out) + { + FluidFooRow(in.InLineB(0), out.OutLineB(), in.length()); + } + }; + + GAPI_FLUID_KERNEL(FluidBar, FluidBarI, 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) + { + FluidBarRow(in1.InLineB(0), in2.InLineB(0), out.OutLineB(), in1.length()); + } + }; + + GAPI_FLUID_KERNEL(FluidFoo2lpi, FluidFooI, false) + { + static const int Window = 1; + static const int LPI = 2; + + static void run(const cv::gapi::fluid::View &in, + cv::gapi::fluid::Buffer &out) + { + for (int l = 0; l < out.lpi(); l++) + { + FluidFooRow(in.InLineB(l), out.OutLineB(l), in.length()); + } + } + }; + + cv::Mat ocvFoo(const cv::Mat &in) + { + cv::Mat out; + OCVFoo::run(in, out); + return out; + } + cv::Mat ocvBar(const cv::Mat &in1, const cv::Mat &in2) + { + cv::Mat out; + OCVBar::run(in1, in2, out); + return out; + } + cv::Mat fluidFoo(const cv::Mat &in) + { + cv::Mat out(in.rows, in.cols, in.type()); + for (int y = 0; y < in.rows; y++) + { + FluidFooRow(in.ptr(y), out.ptr(y), in.cols); + } + return out; + } + cv::Mat fluidBar(const cv::Mat &in1, const cv::Mat &in2) + { + cv::Mat out(in1.rows, in1.cols, in1.type()); + for (int y = 0; y < in1.rows; y++) + { + FluidBarRow(in1.ptr(y), in2.ptr(y), out.ptr(y), in1.cols); + } + return out; + } +} // 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 = ocvBar(ocvFoo(m_in_mat), ocvFoo(m_in_mat)); + 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 = fluidBar(fluidFoo(m_in_mat), fluidFoo(m_in_mat)); + 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) +{ + EXPECT_TRUE(cv::gapi::cpu::backend() == m_hetero_kernels.lookup<I::Foo>()); + EXPECT_TRUE(cv::gapi::fluid::backend() == m_hetero_kernels.lookup<I::Bar>()); + + cv::Mat ref = fluidBar(ocvFoo(m_in_mat), ocvFoo(m_in_mat)); + EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_hetero_kernels))); + EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat)); +} + +struct GAPIBigHeteroTest : public ::testing::TestWithParam<std::array<int, 9>> +{ + cv::GComputation m_comp; + cv::gapi::GKernelPackage m_kernels; + + cv::Mat m_in_mat; + cv::Mat m_out_mat1; + cv::Mat m_out_mat2; + + cv::Mat m_ref_mat1; + cv::Mat m_ref_mat2; + + GAPIBigHeteroTest(); +}; + +// Foo7 +// .-> Foo2 -> Foo3 -< +// Foo0 -> Foo1 Bar -> Foo6 +// `-> Foo4 -> Foo5 -` + +GAPIBigHeteroTest::GAPIBigHeteroTest() + : m_comp([&](){ + auto flags = GetParam(); + std::array<std::function<cv::GMat(cv::GMat)>, 8> foos; + + for (int i = 0; i < 8; i++) + { + foos[i] = flags[i] ? &I::Foo::on : &FluidFooI::on; + } + auto bar = flags[8] ? &I::Bar::on : &FluidBarI::on; + + cv::GMat in; + auto foo1Out = foos[1](foos[0](in)); + auto foo3Out = foos[3](foos[2](foo1Out)); + auto foo6Out = foos[6](bar(foo3Out, + foos[5](foos[4](foo1Out)))); + auto foo7Out = foos[7](foo3Out); + + return cv::GComputation(GIn(in), GOut(foo6Out, foo7Out)); + }) + , m_kernels(cv::gapi::kernels<OCVFoo, OCVBar, FluidFoo, FluidBar>()) + , m_in_mat(cv::Mat::eye(cv::Size(64, 64), CV_8UC1)) +{ + auto flags = GetParam(); + std::array<std::function<cv::Mat(cv::Mat)>, 8> foos; + + for (int i = 0; i < 8; i++) + { + foos[i] = flags[i] ? ocvFoo : fluidFoo; + } + auto bar = flags[8] ? ocvBar : fluidBar; + + cv::Mat foo1OutMat = foos[1](foos[0](m_in_mat)); + cv::Mat foo3OutMat = foos[3](foos[2](foo1OutMat)); + + m_ref_mat1 = foos[6](bar(foo3OutMat, + foos[5](foos[4](foo1OutMat)))); + + m_ref_mat2 = foos[7](foo3OutMat); +} + +TEST_P(GAPIBigHeteroTest, Test) +{ + EXPECT_NO_THROW(m_comp.apply(gin(m_in_mat), gout(m_out_mat1, m_out_mat2), cv::compile_args(m_kernels))); + EXPECT_EQ(0, cv::countNonZero(m_ref_mat1 != m_out_mat1)); + EXPECT_EQ(0, cv::countNonZero(m_ref_mat2 != m_out_mat2)); +} + +static auto configurations = []() +{ + // Fill all possible configurations + // from 000000000 to 111111111 + std::array<std::array<int, 9>, 512> arr; + for (auto n = 0; n < 512; n++) + { + for (auto i = 0; i < 9; i++) + { + arr[n][i] = (n >> (8 - i)) & 1; + } + } + return arr; +}(); + +INSTANTIATE_TEST_CASE_P(GAPIBigHeteroTest, GAPIBigHeteroTest, + ::testing::ValuesIn(configurations)); + +TEST(GAPIHeteroTestLPI, Test) +{ + cv::GMat in; + auto mid = FluidFooI::on(in); + auto out = FluidFooI::on(mid); + cv::gapi::island("isl0", GIn(in), GOut(mid)); + cv::gapi::island("isl1", GIn(mid), GOut(out)); + cv::GComputation c(in, out); + + cv::Mat in_mat = cv::Mat::eye(cv::Size(64, 64), CV_8UC1); + cv::Mat out_mat; + EXPECT_NO_THROW(c.apply(in_mat, out_mat, cv::compile_args(cv::gapi::kernels<FluidFoo2lpi>()))); + cv::Mat ref = fluidFoo(fluidFoo(in_mat)); + EXPECT_EQ(0, cv::countNonZero(ref != out_mat)); +} + +} // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp new file mode 100644 index 000000000..711211da2 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp new file mode 100644 index 000000000..bc0b991e6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp @@ -0,0 +1,720 @@ +// 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(FResizeNN1Lpi, 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 length = out.length(); + double vRatio = (double)in.meta().size.height / out.meta().size.height; + double hRatio = (double)in.length() / length; + auto y = out.y(); + auto inY = in.y(); + + for (int l = 0; l < out.lpi(); l++) + { + auto sy = static_cast<int>((y+l) * vRatio); + int idx = sy - inY; + + const auto src = in.InLine <unsigned char>(idx); + auto dst = out.OutLine<unsigned char>(l); + + for (int x = 0; x < length; x++) + { + auto inX = static_cast<int>(x * hRatio); + 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 mapX = scratch.OutLine<typename Mapper::Unit>(); + auto inY = in.y(); + auto inH = in.meta().size.height; + auto outY = out.y(); + auto length = out.length(); + + for (int l = 0; l < out.lpi(); l++) + { + auto mapY = Mapper::map(vRatio, inY, inH, outY + l); + + const auto src0 = in.InLine <unsigned char>(mapY.s0); + const auto src1 = in.InLine <unsigned char>(mapY.s1); + + auto dst = out.OutLine<unsigned char>(l); + + for (int x = 0; x < 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(FResizeLinear1Lpi, 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(FResizeArea1Lpi, 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 firstOutLineIdx = out.y(); + auto firstViewLineIdx = in.y(); + auto length = out.length(); + double vRatio = (double)in.meta().size.height / out.meta().size.height; + double hRatio = (double)in.length() / length; + + for (int l = 0; l < out.lpi(); l++) + { + int outY = firstOutLineIdx + l; + int startY = startInCoord(outY, vRatio); + int endY = endInCoord (outY, vRatio); + + auto dst = out.OutLine<unsigned char>(l); + + for (int x = 0; x < 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 < outY) startCoordY = outY; + if (endCoordY > outY + 1) endCoordY = outY + 1; + + float fracY = static_cast<float>((inY == startY || inY == endY - 1) ? endCoordY - startCoordY : 1/vRatio); + + const auto src = in.InLine <unsigned char>(inY - firstViewLineIdx); + + 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(FResizeAreaUpscale1Lpi, 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); + } +}; + +#define ADD_RESIZE_KERNEL_WITH_LPI(interp, lpi, scratch) \ +struct Resize##interp##lpi##LpiHelper : public FResize##interp##1Lpi { static const int LPI = lpi; }; \ +struct FResize##interp##lpi##Lpi : public cv::GFluidKernelImpl<Resize##interp##lpi##LpiHelper, cv::gapi::core::GResize, scratch>{}; + +ADD_RESIZE_KERNEL_WITH_LPI(NN, 2, false) +ADD_RESIZE_KERNEL_WITH_LPI(NN, 3, false) +ADD_RESIZE_KERNEL_WITH_LPI(NN, 4, false) + +ADD_RESIZE_KERNEL_WITH_LPI(Linear, 2, true) +ADD_RESIZE_KERNEL_WITH_LPI(Linear, 3, true) +ADD_RESIZE_KERNEL_WITH_LPI(Linear, 4, true) + +ADD_RESIZE_KERNEL_WITH_LPI(Area, 2, false) +ADD_RESIZE_KERNEL_WITH_LPI(Area, 3, false) +ADD_RESIZE_KERNEL_WITH_LPI(Area, 4, false) + +ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 2, true) +ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 3, true) +ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 4, true) +#undef ADD_RESIZE_KERNEL_WITH_LPI + +static auto fluidResizeTestPackage = [](int interpolation, cv::Size szIn, cv::Size szOut, int lpi = 1) +{ + using namespace cv; + using namespace cv::gapi; + bool upscale = szIn.width < szOut.width || szIn.height < szOut.height; + +#define RESIZE_CASE(interp, lpi) \ + case lpi: pkg = kernels<FCopy, FResize##interp##lpi##Lpi>(); break; + +#define RESIZE_SWITCH(interp) \ + switch(lpi) \ + { \ + RESIZE_CASE(interp, 1) \ + RESIZE_CASE(interp, 2) \ + RESIZE_CASE(interp, 3) \ + RESIZE_CASE(interp, 4) \ + default: CV_Assert(false); \ + } + + GKernelPackage pkg; + switch (interpolation) + { + case INTER_NEAREST: RESIZE_SWITCH(NN); break; + case INTER_LINEAR: RESIZE_SWITCH(Linear); break; + case INTER_AREA: + { + if (upscale) + { + RESIZE_SWITCH(AreaUpscale) + } + else + { + RESIZE_SWITCH(Area); + } + }break; + default: CV_Assert(false); + } + return combine(pkg, fluidTestPackage, unite_policy::KEEP); + +#undef RESIZE_SWITCH +#undef RESIZE_CASE +}; + +struct ResizeTestFluid : public TestWithParam<std::tuple<int, int, cv::Size, std::tuple<cv::Size, cv::Rect>, int, double>> {}; +TEST_P(ResizeTestFluid, SanityTest) +{ + int type = 0, interp = 0; + cv::Size sz_in, sz_out; + int lpi = 0; + double tolerance = 0.0; + cv::Rect outRoi; + std::tuple<cv::Size, cv::Rect> outSizeAndRoi; + std::tie(type, interp, sz_in, outSizeAndRoi, lpi, 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, lpi))); + + 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(1, 2, 3, 4), // lpi + 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})), + Values(1, 2, 3, 4), // lpi + // 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(1, 2, 3, 4), // lpi + 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(1, 2, 3, 4), // lpi + 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(1, 2, 3, 4), // lpi + 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(1, 2, 3, 4), // lpi + 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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_roi_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_roi_test.cpp new file mode 100644 index 000000000..ee8674ede --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_roi_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp new file mode 100644 index 000000000..5b3501175 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp @@ -0,0 +1,713 @@ +// 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(to_own(in_mat), true); + cv::gapi::fluid::View view = buffer.mkView(0, false); + view.priv().allocate(1, {}); + view.priv().reset(1); + int this_y = 0; + + while (this_y < buffer_size.height) + { + view.priv().prepareToRead(); + 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(1, {}); + view.priv().reset(3); + view.priv().allocate(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(to_own(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, 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, 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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp new file mode 100644 index 000000000..6bd06fe27 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp @@ -0,0 +1,436 @@ +// 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 <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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp new file mode 100644 index 000000000..f5d83edf5 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcompiled_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcompiled_tests.cpp new file mode 100644 index 000000000..e482e2e36 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcompiled_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp new file mode 100644 index 000000000..070cea692 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp new file mode 100644 index 000000000..aeb47628e --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp @@ -0,0 +1,284 @@ +// 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, IncludesAPI) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, S::Bar>(); + EXPECT_TRUE (pkg.includesAPI<I::Foo>()); + EXPECT_TRUE (pkg.includesAPI<I::Bar>()); + EXPECT_FALSE(pkg.includesAPI<I::Baz>()); + EXPECT_FALSE(pkg.includesAPI<I::Qux>()); +} + +TEST(KernelPackage, IncludesAPI_Overlapping) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, J::Bar, S::Foo, S::Bar>(); + EXPECT_TRUE (pkg.includesAPI<I::Foo>()); + EXPECT_TRUE (pkg.includesAPI<I::Bar>()); + EXPECT_FALSE(pkg.includesAPI<I::Baz>()); + EXPECT_FALSE(pkg.includesAPI<I::Qux>()); +} + +TEST(KernelPackage, Include_Add) +{ + 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, Include_KEEP) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, J::Bar>(); + EXPECT_FALSE(pkg.includes<S::Foo>()); + EXPECT_FALSE(pkg.includes<S::Bar>()); + + pkg.include<S::Bar>(); // default (KEEP) + EXPECT_TRUE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Bar>()); + + pkg.include<S::Foo>(cv::unite_policy::KEEP); // explicit (KEEP) + EXPECT_TRUE(pkg.includes<J::Foo>()); + EXPECT_TRUE(pkg.includes<S::Foo>()); +} + +TEST(KernelPackage, Include_REPLACE) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, J::Bar>(); + EXPECT_FALSE(pkg.includes<S::Bar>()); + + pkg.include<S::Bar>(cv::unite_policy::REPLACE); + EXPECT_FALSE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Bar>()); +} + +TEST(KernelPackage, RemoveBackend) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, J::Bar, S::Foo>(); + EXPECT_TRUE(pkg.includes<J::Foo>()); + EXPECT_TRUE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Foo>()); + + pkg.remove(J::backend()); + EXPECT_FALSE(pkg.includes<J::Foo>()); + EXPECT_FALSE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Foo>()); +}; + +TEST(KernelPackage, RemoveAPI) +{ + namespace J = Jupiter; + namespace S = Saturn; + auto pkg = cv::gapi::kernels<J::Foo, J::Bar, S::Foo, S::Bar>(); + EXPECT_TRUE(pkg.includes<J::Foo>()); + EXPECT_TRUE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Foo>()); + + pkg.remove<I::Foo>(); + EXPECT_TRUE(pkg.includes<J::Bar>()); + EXPECT_TRUE(pkg.includes<S::Bar>()); + EXPECT_FALSE(pkg.includes<J::Foo>()); + EXPECT_FALSE(pkg.includes<S::Foo>()); +}; + +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, Combine_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, Combine_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, Combine_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, Combine_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, Combine_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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp new file mode 100644 index 000000000..cd876efdb --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp new file mode 100644 index 000000000..815aa0d87 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_scalar_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_scalar_tests.cpp new file mode 100644 index 000000000..7b4baa01d --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_scalar_tests.cpp @@ -0,0 +1,117 @@ +// 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::randn(in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f)); + + 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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_smoke_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_smoke_test.cpp new file mode 100644 index 000000000..9ac47f6d7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_smoke_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_typed_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_typed_tests.cpp new file mode 100644 index 000000000..1716b5505 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_typed_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp new file mode 100644 index 000000000..574c0ab54 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp new file mode 100644 index 000000000..6c331c033 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp @@ -0,0 +1,395 @@ +// 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/gpu/core.hpp" + +#define CORE_GPU cv::gapi::core::gpu::kernels() + +namespace opencv_test +{ + +// FIXME: Wut? See MulTestGPU/MathOpTest below (duplicate?) +INSTANTIATE_TEST_CASE_P(AddTestGPU, 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_GPU))), + opencv_test::PrintMathOpCoreParams()); + +INSTANTIATE_TEST_CASE_P(MulTestGPU, 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_GPU))), + opencv_test::PrintMathOpCoreParams()); + +INSTANTIATE_TEST_CASE_P(SubTestGPU, 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_GPU))), + opencv_test::PrintMathOpCoreParams()); + +INSTANTIATE_TEST_CASE_P(DivTestGPU, 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_GPU))), + opencv_test::PrintMathOpCoreParams()); + +INSTANTIATE_TEST_CASE_P(MulTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(DivTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(DivCTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(MeanTestGPU, 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_GPU)))); + +//TODO: mask test doesn't work +#if 0 +INSTANTIATE_TEST_CASE_P(MaskTestGPU, 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_GPU)))); +#endif + +INSTANTIATE_TEST_CASE_P(SelectTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(Polar2CartGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(Cart2PolarGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(CompareTestGPU, 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_GPU))), + opencv_test::PrintCmpCoreParams()); + +INSTANTIATE_TEST_CASE_P(BitwiseTestGPU, 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_GPU))), + opencv_test::PrintBWCoreParams()); + +INSTANTIATE_TEST_CASE_P(BitwiseNotTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(MinTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(MaxTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(SumTestGPU, 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(1e-3), //TODO: too relaxed? + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(AbsDiffCTestGPU, 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_GPU)))); + +// FIXME: Comparison introduced by YL doesn't work with C3 +INSTANTIATE_TEST_CASE_P(AddWeightedTestGPU, 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(0.50005), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(NormTestGPU, 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(1e-3), //TODO: too relaxed? + Values(cv::compile_args(CORE_GPU))), + opencv_test::PrintNormCoreParams()); + +INSTANTIATE_TEST_CASE_P(IntegralTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ThresholdTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ThresholdTestGPU, 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_GPU)))); + + +INSTANTIATE_TEST_CASE_P(InRangeTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(Split3TestGPU, Split3Test, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Split4TestGPU, Split4Test, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ResizeTestGPU, ResizeTest, + Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(ResizeTestGPU, ResizeTestFxFy, + Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), + 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(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Merge3TestGPU, Merge3Test, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(Merge4TestGPU, Merge4Test, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(cv::compile_args(CORE_GPU)))); + +INSTANTIATE_TEST_CASE_P(RemapTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(FlipTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(CropTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(LUTTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(LUTTestCustomGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConvertToGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatHorTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatVertTestGPU, 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_GPU)))); + +//TODO: fix this backend to allow ConcatVertVec ConcatHorVec +#if 0 +INSTANTIATE_TEST_CASE_P(ConcatVertVecTestGPU, 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ConcatHorVecTestGPU, 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_GPU)))); +#endif +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp new file mode 100644 index 000000000..65d452c34 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp @@ -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 "../test_precomp.hpp" + +#include "../common/gapi_imgproc_tests.hpp" +#include "opencv2/gapi/gpu/imgproc.hpp" + +#define IMGPROC_GPU cv::gapi::imgproc::gpu::kernels() + +namespace opencv_test +{ + + +INSTANTIATE_TEST_CASE_P(Filter2DTestGPU, Filter2DTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(BoxFilterTestGPU, BoxFilterTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + 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), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); //TODO: 8UC1 doesn't work + +INSTANTIATE_TEST_CASE_P(SepFilterTestGPU_8U, SepFilterTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(SepFilterTestGPU_other, SepFilterTest, + Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(BlurTestGPU, BlurTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), + 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), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(gaussBlurTestGPU, GaussianBlurTest, + Combine(Values(ToleranceFilter(1e-5f, 0.01).to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Values(3), // FIXIT 5 + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(MedianBlurTestGPU, MedianBlurTest, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(ErodeTestGPU, ErodeTest, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(Erode3x3TestGPU, Erode3x3Test, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(DilateTestGPU, DilateTest, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(Dilate3x3TestGPU, Dilate3x3Test, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(SobelTestGPU, SobelTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), + Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1/*, CV_32FC1*/), //TODO: CV_32FC1 fails accuracy + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(EqHistTestGPU, EqHistTest, + Combine(Values(AbsExact().to_compare_f()), // FIXIT Non reliable check + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(CannyTestGPU, CannyTest, + Combine(Values(AbsSimilarPoints(0, 0.05).to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2GrayTestGPU, RGB2GrayTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2GrayTestGPU, BGR2GrayTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2YUVTestGPU, RGB2YUVTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2RGBTestGPU, YUV2RGBTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2LabTestGPU, RGB2LabTest, + Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2LUVTestGPU, BGR2LUVTest, + Combine(Values(ToleranceColor(5e-3, 6).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(LUV2BGRTestGPU, LUV2BGRTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(BGR2YUVTestGPU, BGR2YUVTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + +INSTANTIATE_TEST_CASE_P(YUV2BGRTestGPU, YUV2BGRTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), +/*init output matrices or not*/ testing::Bool(), + Values(cv::compile_args(IMGPROC_GPU)))); + + +} // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp new file mode 100644 index 000000000..5a116bd35 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp @@ -0,0 +1,72 @@ +// 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/gpu/core.hpp" + +#define CORE_GPU cv::gapi::core::gpu::kernels() + +namespace opencv_test +{ + + +INSTANTIATE_TEST_CASE_P(MathOperatorTestGPU, MathOperatorMatMatTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(MathOperatorTestGPU, MathOperatorMatScalarTest, + Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestGPU, MathOperatorMatMatTest, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestGPU, MathOperatorMatScalarTest, + Combine(Values(AbsExact().to_compare_f()), + 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_GPU)))); + +INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestGPU, 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_GPU)))); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_backend_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_backend_tests.cpp new file mode 100644 index 000000000..67b627313 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_backend_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_executor_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_executor_tests.cpp new file mode 100644 index 000000000..20aad89b6 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_executor_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_garg_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_garg_test.cpp new file mode 100644 index 000000000..67696dbb0 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_garg_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp new file mode 100644 index 000000000..6dbf7778f --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp new file mode 100644 index 000000000..a815e0d22 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp new file mode 100644 index 000000000..91e55bed7 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_tests.cpp new file mode 100644 index 000000000..09f188032 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_island_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp new file mode 100644 index 000000000..252af9c1a --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp @@ -0,0 +1,233 @@ +// 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" + +#include "opencv2/gapi/fluid/gfluidkernel.hpp" +#include "opencv2/gapi/fluid/core.hpp" +#include "opencv2/gapi/fluid/imgproc.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()); +} + +TEST(GComputationCompile, FluidReshapeWithDifferentDims) +{ + 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_8UC1); + cv::Mat out_mat; + + cc.apply(in_mat1, out_mat, cv::compile_args(cv::gapi::core::fluid::kernels())); + 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, FluidReshapeResizeDownScale) +{ + cv::Size szOut(4, 4); + cv::GMat in; + cv::GComputation cc(in, cv::gapi::resize(in, szOut)); + + cv::Mat in_mat1( 8, 8, CV_8UC3); + cv::Mat in_mat2(16, 16, CV_8UC3); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::Mat out_mat1, out_mat2; + + cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels())); + auto comp1 = cc.priv().m_lastCompiled; + + cc.apply(in_mat2, out_mat2); + auto comp2 = cc.priv().m_lastCompiled; + + // Both compiled objects are actually the same unique executable + EXPECT_EQ(&comp1.priv(), &comp2.priv()); + + cv::Mat cv_out_mat1, cv_out_mat2; + cv::resize(in_mat1, cv_out_mat1, szOut); + cv::resize(in_mat2, cv_out_mat2, szOut); + + EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1)); + EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2)); +} + +TEST(GComputationCompile, FluidReshapeSwitchToUpscaleFromDownscale) +{ + cv::Size szOut(4, 4); + cv::GMat in; + cv::GComputation cc(in, cv::gapi::resize(in, szOut)); + + cv::Mat in_mat1( 8, 8, CV_8UC3); + cv::Mat in_mat2( 2, 2, CV_8UC3); + cv::Mat in_mat3(16, 16, CV_8UC3); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat3, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::Mat out_mat1, out_mat2, out_mat3; + + cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels())); + auto comp1 = cc.priv().m_lastCompiled; + + cc.apply(in_mat2, out_mat2); + auto comp2 = cc.priv().m_lastCompiled; + + cc.apply(in_mat3, out_mat3); + auto comp3 = cc.priv().m_lastCompiled; + + EXPECT_EQ(&comp1.priv(), &comp2.priv()); + EXPECT_EQ(&comp1.priv(), &comp3.priv()); + + cv::Mat cv_out_mat1, cv_out_mat2, cv_out_mat3; + cv::resize(in_mat1, cv_out_mat1, szOut); + cv::resize(in_mat2, cv_out_mat2, szOut); + cv::resize(in_mat3, cv_out_mat3, szOut); + + EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1)); + EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2)); + EXPECT_EQ(0, cv::countNonZero(out_mat3 != cv_out_mat3)); +} + +TEST(GComputationCompile, ReshapeBlur) +{ + cv::Size kernelSize{3, 3}; + cv::GMat in; + cv::GComputation cc(in, cv::gapi::blur(in, kernelSize)); + + cv::Mat in_mat1( 8, 8, CV_8UC1); + cv::Mat in_mat2(16, 16, 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)); + cv::Mat out_mat1, out_mat2; + + cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::imgproc::fluid::kernels())); + auto comp1 = cc.priv().m_lastCompiled; + + cc.apply(in_mat2, out_mat2); + auto comp2 = cc.priv().m_lastCompiled; + + // Both compiled objects are actually the same unique executable + EXPECT_EQ(&comp1.priv(), &comp2.priv()); + + cv::Mat cv_out_mat1, cv_out_mat2; + cv::blur(in_mat1, cv_out_mat1, kernelSize); + cv::blur(in_mat2, cv_out_mat2, kernelSize); + + EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1)); + EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2)); +} + +TEST(GComputationCompile, ReshapeRois) +{ + cv::Size kernelSize{3, 3}; + cv::Size szOut(8, 8); + cv::GMat in; + auto blurred = cv::gapi::blur(in, kernelSize); + cv::GComputation cc(in, cv::gapi::resize(blurred, szOut)); + + cv::Mat first_in_mat(8, 8, CV_8UC3); + cv::randn(first_in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f)); + cv::Mat first_out_mat; + auto fluidKernels = cv::gapi::combine(gapi::imgproc::fluid::kernels(), + gapi::core::fluid::kernels(), + cv::unite_policy::REPLACE); + cc.apply(first_in_mat, first_out_mat, cv::compile_args(fluidKernels)); + auto first_comp = cc.priv().m_lastCompiled; + + constexpr int niter = 4; + for (int i = 0; i < niter; i++) + { + int width = 4 + 2*i; + int height = width; + cv::Mat in_mat(width, height, CV_8UC3); + cv::randn(in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f)); + cv::Mat out_mat = cv::Mat::zeros(szOut, CV_8UC3); + + int x = 0; + int y = szOut.height * i / niter; + int roiW = szOut.width; + int roiH = szOut.height / niter; + cv::Rect roi{x, y, roiW, roiH}; + + cc.apply(in_mat, out_mat, cv::compile_args(cv::GFluidOutputRois{{to_own(roi)}})); + auto comp = cc.priv().m_lastCompiled; + + EXPECT_EQ(&first_comp.priv(), &comp.priv()); + + cv::Mat blur_mat, cv_out_mat; + cv::blur(in_mat, blur_mat, kernelSize); + cv::resize(blur_mat, cv_out_mat, szOut); + + EXPECT_EQ(0, cv::countNonZero(out_mat(roi) != cv_out_mat(roi))); + } +} + +} // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp new file mode 100644 index 000000000..d4b16f627 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_resolve_kernel_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_vectorref_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_vectorref_test.cpp new file mode 100644 index 000000000..1b14e0670 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_vectorref_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_transactions_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_transactions_test.cpp new file mode 100644 index 000000000..f550340e8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_transactions_test.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp new file mode 100644 index 000000000..c25435707 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp new file mode 100644 index 000000000..ba2cd2df4 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp @@ -0,0 +1,387 @@ +// 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.total(), static_cast<size_t>(size.height*size.width)); + ASSERT_EQ(m.type(), CV_8UC1); + ASSERT_EQ(m.depth(), CV_8U); + ASSERT_EQ(m.channels(), 1); + ASSERT_EQ(m.elemSize(), sizeof(uint8_t)); + ASSERT_EQ(m.step, sizeof(uint8_t) * m.cols); +} + +TEST(OwnMat, CreateOverload) +{ + auto size = cv::gapi::own::Size{32,16}; + Mat m; + m.create(size.height,size.width, CV_8UC1); + + ASSERT_NE(m.data, nullptr); + ASSERT_EQ((cv::Size{m.cols, m.rows}), size); + + ASSERT_EQ(m.total(), static_cast<size_t>(size.height*size.width)); + ASSERT_EQ(m.type(), CV_8UC1); + ASSERT_EQ(m.depth(), CV_8U); + ASSERT_EQ(m.channels(), 1); + ASSERT_EQ(m.elemSize(), sizeof(uint8_t)); + ASSERT_EQ(m.step, sizeof(uint8_t) * m.cols); +} +TEST(OwnMat, Create3chan) +{ + auto size = cv::Size{32,16}; + Mat m; + m.create(size, CV_8UC3); + + ASSERT_NE(m.data, nullptr); + ASSERT_EQ((cv::Size{m.cols, m.rows}), size); + + ASSERT_EQ(m.type(), CV_8UC3); + ASSERT_EQ(m.depth(), CV_8U); + ASSERT_EQ(m.channels(), 3); + ASSERT_EQ(m.elemSize(), 3 * sizeof(uint8_t)); + ASSERT_EQ(m.step, 3* 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); +} + +TEST(OwnMat, PtrWithStep) +{ + 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); + } + Mat mat(height, width, CV_32S, data.data(), stepInPixels * sizeof(int)); + + EXPECT_EQ(& data[0], reinterpret_cast<int*>(mat.ptr(0))); + EXPECT_EQ(& data[1], reinterpret_cast<int*>(mat.ptr(0, 1))); + EXPECT_EQ(& data[stepInPixels], reinterpret_cast<int*>(mat.ptr(1))); + EXPECT_EQ(& data[stepInPixels +1], reinterpret_cast<int*>(mat.ptr(1,1))); + + auto const& cmat = mat; + + EXPECT_EQ(& data[0], reinterpret_cast<const int*>(cmat.ptr(0))); + EXPECT_EQ(& data[1], reinterpret_cast<const int*>(cmat.ptr(0, 1))); + EXPECT_EQ(& data[stepInPixels], reinterpret_cast<const int*>(cmat.ptr(1))); + EXPECT_EQ(& data[stepInPixels +1], reinterpret_cast<const int*>(cmat.ptr(1,1))); +} + +TEST(OwnMat, CopyToWithStep) +{ + 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); + } + Mat mat(height, width, CV_32S, data.data(), stepInPixels * sizeof(int)); + + Mat dst; + mat.copyTo(dst); + + EXPECT_NE(mat.data, dst.data); + EXPECT_EQ(0, cv::countNonZero(to_ocv(mat) != to_ocv(dst))) + << to_ocv(mat) << std::endl + << (to_ocv(mat) != to_ocv(dst)); +} + +TEST(OwnMat, ScalarAssign32SC1) +{ + 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); + } + Mat mat(height, width, CV_32S, data.data(), stepInPixels * sizeof(data[0])); + + mat = cv::gapi::own::Scalar{-1}; + + std::array<int, height * stepInPixels> expected; + + for (size_t row = 0; row < height; row++) + { + for (size_t col = 0; col < stepInPixels; col++) + { + auto index = row*stepInPixels + col; + expected[index] = col < width ? -1 : static_cast<int>(index); + } + } + + auto cmp_result_mat = (cv::Mat{height, stepInPixels, CV_32S, data.data()} != cv::Mat{height, stepInPixels, CV_32S, expected.data()}); + EXPECT_EQ(0, cv::countNonZero(cmp_result_mat)) + << cmp_result_mat << std::endl; +} + +TEST(OwnMat, ScalarAssign8UC1) +{ + constexpr int width = 8; + constexpr int height = 8; + constexpr int stepInPixels = 16; + + std::array<uchar, height * stepInPixels> data; + for (size_t i = 0; i < data.size(); i++) + { + data[i] = static_cast<uchar>(i); + } + Mat mat(height, width, CV_8U, data.data(), stepInPixels * sizeof(data[0])); + + mat = cv::gapi::own::Scalar{-1}; + + std::array<uchar, height * stepInPixels> expected; + + for (size_t row = 0; row < height; row++) + { + for (size_t col = 0; col < stepInPixels; col++) + { + auto index = row*stepInPixels + col; + expected[index] = col < width ? cv::saturate_cast<uchar>(-1) : static_cast<uchar>(index); + } + } + + auto cmp_result_mat = (cv::Mat{height, stepInPixels, CV_8U, data.data()} != cv::Mat{height, stepInPixels, CV_8U, expected.data()}); + EXPECT_EQ(0, cv::countNonZero(cmp_result_mat)) + << cmp_result_mat << std::endl; +} + +TEST(OwnMat, ScalarAssign8UC3) +{ + constexpr auto cv_type = CV_8SC3; + constexpr int channels = 3; + constexpr int width = 8; + constexpr int height = 8; + constexpr int stepInPixels = 16; + + std::array<schar, height * stepInPixels * channels> data; + for (size_t i = 0; i < data.size(); i+= channels) + { + data[i + 0] = static_cast<schar>(10 * i + 0); + data[i + 1] = static_cast<schar>(10 * i + 1); + data[i + 2] = static_cast<schar>(10 * i + 2); + } + + Mat mat(height, width, cv_type, data.data(), channels * stepInPixels * sizeof(data[0])); + + mat = cv::gapi::own::Scalar{-10, -11, -12}; + + std::array<schar, data.size()> expected; + + for (size_t row = 0; row < height; row++) + { + for (size_t col = 0; col < stepInPixels; col++) + { + int index = static_cast<int>(channels * (row*stepInPixels + col)); + expected[index + 0] = static_cast<schar>(col < width ? -10 : 10 * index + 0); + expected[index + 1] = static_cast<schar>(col < width ? -11 : 10 * index + 1); + expected[index + 2] = static_cast<schar>(col < width ? -12 : 10 * index + 2); + } + } + + auto cmp_result_mat = (cv::Mat{height, stepInPixels, cv_type, data.data()} != cv::Mat{height, stepInPixels, cv_type, expected.data()}); + EXPECT_EQ(0, cv::countNonZero(cmp_result_mat)) + << cmp_result_mat << std::endl + << "data : " << std::endl + << cv::Mat{height, stepInPixels, cv_type, data.data()} << std::endl + << "expected : " << std::endl + << cv::Mat{height, stepInPixels, cv_type, expected.data()} << std::endl; +} + +TEST(OwnMat, ROIView) +{ + constexpr int width = 8; + constexpr int height = 8; + constexpr int stepInPixels = 16; + + std::array<uchar, height * stepInPixels> data; + for (size_t i = 0; i < data.size(); i++) + { + data[i] = static_cast<uchar>(i); + } + + +// std::cout<<cv::Mat{height, stepInPixels, CV_8U, data.data()}<<std::endl; + + std::array<uchar, 4 * 4> expected; + + for (size_t row = 0; row < 4; row++) + { + for (size_t col = 0; col < 4; col++) + { + expected[row*4 +col] = static_cast<uchar>(stepInPixels * (2 + row) + 2 + col); + } + } + + Mat mat(height, width, CV_8U, data.data(), stepInPixels * sizeof(data[0])); + Mat roi_view (mat, cv::gapi::own::Rect{2,2,4,4}); + +// std::cout<<cv::Mat{4, 4, CV_8U, expected.data()}<<std::endl; +// + auto expected_cv_mat = cv::Mat{4, 4, CV_8U, expected.data()}; + + auto cmp_result_mat = (to_ocv(roi_view) != expected_cv_mat); + EXPECT_EQ(0, cv::countNonZero(cmp_result_mat)) + << cmp_result_mat << std::endl + << to_ocv(roi_view) << std::endl + << expected_cv_mat << std::endl; +} +} // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp new file mode 100644 index 000000000..a9c5c0123 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/test_main.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/test_main.cpp new file mode 100644 index 000000000..fa5862fa1 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/test_main.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp new file mode 100644 index 000000000..bcab803ba --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp @@ -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 + + +// 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/gpu/ggpukernel.hpp" +#include "opencv2/gapi/gcompoundkernel.hpp" +#include "opencv2/gapi/operators.hpp" +#include "opencv2/gapi/fluid/imgproc.hpp" +#include "opencv2/gapi/fluid/core.hpp" + +#endif // __OPENCV_GAPI_TEST_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp new file mode 100644 index 000000000..60bbcc13b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp new file mode 100644 index 000000000..b7fabd530 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp @@ -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/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp new file mode 100644 index 000000000..a95b6aa80 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp @@ -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 diff --git a/inference-engine/thirdparty/fluid/revision.txt b/inference-engine/thirdparty/fluid/revision.txt new file mode 100644 index 000000000..e088afdce --- /dev/null +++ b/inference-engine/thirdparty/fluid/revision.txt @@ -0,0 +1 @@ +a3df05d93b188d4e86e23ffd1e988dbec0fc9211 diff --git a/inference-engine/thirdparty/fluid/update.sh b/inference-engine/thirdparty/fluid/update.sh new file mode 100644 index 000000000..5f4c0536c --- /dev/null +++ b/inference-engine/thirdparty/fluid/update.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +REVISION="" +TARGET_DIR=$(pwd) + +case "$#" in + "0") echo "Using latest master..." + REVISION="master" + ;; + "1") REVISION=$1 + echo "Using revision ${REVISION}..." + ;; + *) echo "Usage: ${0} [REVISION] + + Update Fluid to OpenCV source tree at the given REVISION. + If no revision specified, the most recent 'master' commit is used. +" + exit 1 ;; +esac + +# Before doing anything, check if this snapshot was not modified +./check.sh +if [ $? -ne 0 ]; then + echo "Consistency check failed, please reset this subtree to its initial state first!" + exit 1 +fi + +# Download the stuff... +URL="https://github.com/opencv/opencv/archive/${REVISION}.zip" +wget -c ${URL} +if [ $? -ne 0 ]; then + echo "Failed to download ${URL}!" + exit 1 +fi + +unzip -qq ${REVISION}.zip + +# Remove current files +if [ -f modules ]; then + find modules -type f | xargs git rm + find modules -type f | xargs rm + rm -vd modules +fi + +# Put a new copy. Extend this section if needed +# BOM thing might help here, probably +pushd "opencv-${REVISION}" +cp -rv --parent modules/gapi ${TARGET_DIR} +popd +# Note "-f" is used to add files like doc/ which are omitted +# now by IE's current .gitignore - it breaks checksum otherwise. +git add -f modules/gapi + +# Clean-up files +rm -rf "opencv-${REVISION}" +rm "${REVISION}.zip" + +# Calculate and store checksum +./checksum.sh > checksum.txt +git add checksum.txt + +# Store revision +if [ ${REVISION} == "master" ]; then + REVISION="${REVISION} / $(date +%F)" +fi +echo ${REVISION} > revision.txt +git add revision.txt + +# Display status +git status + +# Fin +echo "Done!" |