mirror of
https://github.com/opencv/opencv.git
synced 2025-06-11 20:09:23 +08:00
Merge pull request #13030 from dmatveev:tutorial
* G-API: First steps with tutorial * G-API Tutorial: First iteration * G-API port of anisotropic image segmentation tutorial; * Currently works via OpenCV only; * Some new kernels have been required. * G-API Tutorial: added chapters on execution code, inspection, and profiling * G-API Tutorial: make Fluid kernel headers public For some reason, these headers were not moved to the public headers subtree during the initial development. Somehow it even worked for the existing workloads. * G-API Tutorial: Fix a couple of issues found during the work * Introduced Phase & Sqrt kernels, OCV & Fluid versions * Extended GKernelPackage to allow kernel removal & policies on include() All the above stuff needs to be tested, tests will be added later * G-API Tutorial: added chapter on running Fluid backend * G-API Tutorial: fix a number of issues in the text * G-API Tutorial - some final updates - Fixed post-merge issues after Sobel kernel renaming; - Simplified G-API code a little bit; - Put a conclusion note in text. * G-API Tutorial - fix build issues in test/perf targets Public headers were refactored but tests suites were not updated in time * G-API Tutorial: Added tests & reference docs on new kernels * Phase * Sqrt * G-API Tutorial: added link to the tutorial from the main module doc * G-API Tutorial: Added tests on new GKernelPackage functionality * G-API Tutorial: Extended InRange tests to cover 32F * G-API Tutorial: Misc fixes * Avoid building examples when gapi module is not there * Added a volatile API disclaimer to G-API root documentation page * G-API Tutorial: Fix perf tests build issue This change came from master where Fluid kernels are still used incorrectly. * G-API Tutorial: Fixed channels support in Sqrt/Phase fluid kernels Extended tests to cover this case * G-API Tutorial: Fix text problems found on team review
This commit is contained in:
parent
1d10d56651
commit
85fad1504a
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/result.jpg
Normal file
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/result.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/segm.gif
Normal file
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/segm.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 140 KiB |
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/segm_fluid.gif
Normal file
BIN
doc/tutorials/gapi/anisotropic_segmentation/pics/segm_fluid.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 KiB |
@ -0,0 +1,411 @@
|
||||
# Porting anisotropic image segmentation on G-API {#tutorial_gapi_anisotropic_segmentation}
|
||||
|
||||
[TOC]
|
||||
|
||||
# Introduction {#gapi_anisotropic_intro}
|
||||
|
||||
In this tutorial you will learn:
|
||||
* How an existing algorithm can be transformed into a G-API
|
||||
computation (graph);
|
||||
* How to inspect and profile G-API graphs;
|
||||
* How to customize graph execution without changing its code.
|
||||
|
||||
This tutorial is based on @ref
|
||||
tutorial_anisotropic_image_segmentation_by_a_gst.
|
||||
|
||||
# Quick start: using OpenCV backend {#gapi_anisotropic_start}
|
||||
|
||||
Before we start, let's review the original algorithm implementation:
|
||||
|
||||
@include cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp
|
||||
|
||||
## Examining calcGST() {#gapi_anisotropic_calcgst}
|
||||
|
||||
The function calcGST() is clearly an image processing pipeline:
|
||||
* It is just a sequence of operations over a number of cv::Mat;
|
||||
* No logic (conditionals) and loops involved in the code;
|
||||
* All functions operate on 2D images (like cv::Sobel, cv::multiply,
|
||||
cv::boxFilter, cv::sqrt, etc).
|
||||
|
||||
Considering the above, calcGST() is a great candidate to start
|
||||
with. In the original code, its prototype is defined like this:
|
||||
|
||||
@snippet cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp calcGST_proto
|
||||
|
||||
With G-API, we can define it as follows:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp calcGST_proto
|
||||
|
||||
It is important to understand that the new G-API based version of
|
||||
calcGST() will just produce a compute graph, in contrast to its
|
||||
original version, which actually calculates the values. This is a
|
||||
principial difference -- G-API based functions like this are used to
|
||||
construct graphs, not to process the actual data.
|
||||
|
||||
Let's start implementing calcGST() with calculation of \f$J\f$
|
||||
matrix. This is how the original code looks like:
|
||||
|
||||
@snippet cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp calcJ_header
|
||||
|
||||
Here we need to declare output objects for every new operation (see
|
||||
img as a result for cv::Mat::convertTo, imgDiffX and others as results for
|
||||
cv::Sobel and cv::multiply).
|
||||
|
||||
The G-API analogue is listed below:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp calcGST_header
|
||||
|
||||
This snippet demonstrates the following syntactic difference between
|
||||
G-API and traditional OpenCV:
|
||||
* All standard G-API functions are by default placed in "cv::gapi"
|
||||
namespace;
|
||||
* G-API operations _return_ its results -- there's no need to pass
|
||||
extra "output" parameters to the functions.
|
||||
|
||||
Note -- this code is also using `auto` -- types of intermediate objects
|
||||
like `img`, `imgDiffX`, and so on are inferred automatically by the
|
||||
C++ compiler. In this example, the types are determined by G-API
|
||||
operation return values which all are cv::GMat.
|
||||
|
||||
G-API standard kernels are trying to follow OpenCV API conventions
|
||||
whenever possible -- so cv::gapi::sobel takes the same arguments as
|
||||
cv::Sobel, cv::gapi::mul follows cv::multiply, and so on (except
|
||||
having a return value).
|
||||
|
||||
The rest of calcGST() function can be implemented the same
|
||||
way trivially. Below is its full source code:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp calcGST
|
||||
|
||||
## Running G-API graph {#gapi_anisotropic_running}
|
||||
|
||||
After calcGST() is defined in G-API language, we can construct a graph
|
||||
based on it and finally run it -- pass input image and obtain
|
||||
result. Before we do it, let's have a look how original code looked
|
||||
like:
|
||||
|
||||
@snippet cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp main_extra
|
||||
|
||||
G-API-based functions like calcGST() can't be applied to input data
|
||||
directly, since it is a _construction_ code, not the _processing_ code.
|
||||
In order to _run_ computations, a special object of class
|
||||
cv::GComputation needs to be created. This object wraps our G-API code
|
||||
(which is a composition of G-API data and operations) into a callable
|
||||
object, similar to C++11
|
||||
[std::function<>](https://en.cppreference.com/w/cpp/utility/functional/function).
|
||||
|
||||
cv::GComputation class has a number of constructors which can be used
|
||||
to define a graph. Generally, user needs to pass graph boundaries
|
||||
-- _input_ and _output_ objects, on which a GComputation is
|
||||
defined. Then G-API analyzes the call flow from _outputs_ to _inputs_
|
||||
and reconstructs the graph with operations in-between the specified
|
||||
boundaries. This may sound complex, however in fact the code looks
|
||||
like this:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp main
|
||||
|
||||
Note that this code slightly changes from the original one: forming up
|
||||
the resulting image is also a part of the pipeline (done with
|
||||
cv::gapi::addWeighted). Normalization of orientation and coherency
|
||||
images is still done by traditional OpenCV (using cv::normalize) as
|
||||
G-API doesn't provide such kernel at the moment.
|
||||
|
||||
Result of this G-API pipeline bit-exact matches the original one
|
||||
(given the same input image):
|
||||
|
||||

|
||||
|
||||
## G-API initial version: full listing {#gapi_anisotropic_ocv}
|
||||
|
||||
Below is the full listing of the initial anisotropic image
|
||||
segmentation port on G-API:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi.cpp full_sample
|
||||
|
||||
# Inspecting the initial version {#gapi_anisotropic_inspect}
|
||||
|
||||
After we have got the initial working version of our algorithm working
|
||||
with G-API, we can use it to inspect and learn how G-API works. This
|
||||
chapter covers two aspects: understanding the graph structure, and
|
||||
memory profiling.
|
||||
|
||||
## Understanding the graph structure {#gapi_anisotropic_inspect_graph}
|
||||
|
||||
G-API stands for "Graph API", but did you mention any graphs in the
|
||||
above example? It was one of the initial design goals -- G-API was
|
||||
designed with expressions in mind to make adoption and porting process
|
||||
more straightforward. People _usually_ don't think in terms of
|
||||
_Nodes_ and _Edges_ when writing ordinary code, and so G-API, while
|
||||
being a Graph API, doesn't force its users to do that.
|
||||
|
||||
However, a graph is still built implicitly when a cv::GComputation
|
||||
object is defined. It may be useful to inspect how the resulting graph
|
||||
looks like to check if it is generated correctly and if it really
|
||||
represents our alrogithm. It is also useful to learn the structure of
|
||||
the graph to see if it has any redundancies.
|
||||
|
||||
G-API allows to dump generated graphs to `.dot` files which then
|
||||
could be visualized with [Graphviz](https://www.graphviz.org/), a
|
||||
popular open graph visualization software.
|
||||
|
||||
<!-- TODO THIS VARIABLE NEEDS TO BE FIXED TO DUMP DIR ASAP! -->
|
||||
|
||||
In order to dump our graph to a `.dot` file, set `GRAPH_DUMP_PATH` to a
|
||||
file name before running the application, e.g.:
|
||||
|
||||
$ GRAPH_DUMP_PATH=segm.dot ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
|
||||
|
||||
Now this file can be visalized with a `dot` command like this:
|
||||
|
||||
$ dot segm.dot -Tpng -o segm.png
|
||||
|
||||
or viewed instantly with `xdot` command (please refer to your
|
||||
distribution/operating system documentation on how to install these
|
||||
packages).
|
||||
|
||||

|
||||
|
||||
The above diagram demonstrates a number of interesting aspects of
|
||||
G-API's internal algorithm representation:
|
||||
1. G-API underlying graph is a bipartite graph: it consists of
|
||||
_Operation_ and _Data_ nodes such that a _Data_ node can only be
|
||||
connected to an _Operation_ node, _Operation_ node can only be
|
||||
connected to a _Data_ node, and nodes of a single kind are never
|
||||
connected directly.
|
||||
2. Graph is directed - every edge in the graph has a direction.
|
||||
3. Graph "begins" and "ends" with a _Data_ kind of nodes.
|
||||
4. A _Data_ node can have only a single writer and multiple readers.
|
||||
5. An _Operation_ node may have multiple inputs, though every input
|
||||
must have an unique _port number_ (among inputs).
|
||||
6. An _Operation_ node may have multiple outputs, and every output
|
||||
must have an unique _port number_ (among outputs).
|
||||
|
||||
## Measuring memory footprint {#gapi_anisotropic_memory_ocv}
|
||||
|
||||
Let's measure and compare memory footprint of the algorithm in its two
|
||||
versions: G-API-based and OpenCV-based. At the moment, G-API version
|
||||
is also OpenCV-based since it fallbacks to OpenCV functions inside.
|
||||
|
||||
On GNU/Linux, application memory footprint can be profiled with
|
||||
[Valgrind](http://valgrind.org/). On Debian/Ubuntu systems it can be
|
||||
installed like this (assuming you have administrator priveleges):
|
||||
|
||||
$ sudo apt-get install valgrind massif-visualizer
|
||||
|
||||
Once installed, we can collect memory profiles easily for our two
|
||||
algorithm versions:
|
||||
|
||||
$ valgrind --tool=massif --massif-out-file=ocv.out ./bin/example_tutorial_anisotropic_image_segmentation
|
||||
==6101== Massif, a heap profiler
|
||||
==6101== Copyright (C) 2003-2015, and GNU GPL'd, by Nicholas Nethercote
|
||||
==6101== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
|
||||
==6101== Command: ./bin/example_tutorial_anisotropic_image_segmentation
|
||||
==6101==
|
||||
==6101==
|
||||
$ valgrind --tool=massif --massif-out-file=gapi.out ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
|
||||
==6117== Massif, a heap profiler
|
||||
==6117== Copyright (C) 2003-2015, and GNU GPL'd, by Nicholas Nethercote
|
||||
==6117== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
|
||||
==6117== Command: ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
|
||||
==6117==
|
||||
==6117==
|
||||
|
||||
Once done, we can inspect the collected profiles with
|
||||
[Massif Visualizer](@https://github.com/KDE/massif-visualizer)
|
||||
(installed in the above step).
|
||||
|
||||
Below is the visualized memory profile of the original OpenCV version
|
||||
of the algorithm:
|
||||
|
||||

|
||||
|
||||
We see that memory is allocated as the application
|
||||
executes, reaching its peak in the calcGST() function; then the
|
||||
footprint drops as calcGST() completes its execution and all temporary
|
||||
buffers are freed. Massif reports us peak memory consumption of 7.6 MiB.
|
||||
|
||||
Now let's have a look on the profile of G-API version:
|
||||
|
||||

|
||||
|
||||
Once G-API computation is created and its execution starts, G-API
|
||||
allocates all required memory at once and then the memory profile
|
||||
remains flat until the termination of the program. Massif reports us
|
||||
peak memory consumption of 10.6 MiB.
|
||||
|
||||
A reader may ask a right question at this point -- is G-API that bad?
|
||||
What is the reason in using it than?
|
||||
|
||||
Hopefully, it is not. The reason why we see here an increased memory
|
||||
consumption is because the default naive OpenCV-based backend is used to
|
||||
execute this graph. This backend serves mostly for quick prototyping
|
||||
and debugging algorithms before offload/further optimization.
|
||||
|
||||
This backend doesn't utilize any complex memory mamagement strategies yet
|
||||
since it is not its point at the moment. In the following chapter,
|
||||
we'll learn about Fluid backend and see how the same G-API code can
|
||||
run in a completely different model (and the footprint shrinked to a
|
||||
number of kilobytes).
|
||||
|
||||
# Backends and kernels {#gapi_anisotropic_backends}
|
||||
|
||||
This chapter covers how a G-API computation can be executed in a
|
||||
special way -- e.g. offloaded to another device, or scheduled with a
|
||||
special intelligence. G-API is designed to make its graphs portable --
|
||||
it means that once a graph is defined in G-API terms, no changes
|
||||
should be required in it if we want to run it on CPU or on GPU or on
|
||||
both devices at once. [G-API High-level overview](@ref gapi_hld) and
|
||||
[G-API Kernel API](@ref gapi_kernel_api) shed more light on technical
|
||||
details which make it possible. In this chapter, we will utilize G-API
|
||||
Fluid backend to make our graph cache-efficient on CPU.
|
||||
|
||||
G-API defines _backend_ as the lower-level entity which knows how to
|
||||
run kernels. Backends may have (and, in fact, do have) different
|
||||
_Kernel APIs_ which are used to program and integrate kernels for that
|
||||
backends. In this context, _kernel_ is an implementaion of an
|
||||
_operation_, which is defined on the top API level (see
|
||||
G_TYPED_KERNEL() macro).
|
||||
|
||||
Backend is a thing which is aware of device & platform specifics, and
|
||||
which executes its kernels with keeping that specifics in mind. For
|
||||
example, there may be [Halide](http://halide-lang.org/) backend which
|
||||
allows to write (implement) G-API operations in Halide language and
|
||||
then generate functional Halide code for portions of G-API graph which
|
||||
map well there.
|
||||
|
||||
## Running a graph with a Fluid backend {#gapi_anisotropic_fluid}
|
||||
|
||||
OpenCV 4.0 is bundled with two G-API backends -- the default "OpenCV"
|
||||
which we just used, and a special "Fluid" backend.
|
||||
|
||||
Fluid backend reorganizes the execution to save memory and to achieve
|
||||
near-perfect cache locality, implementing so-called "streaming" model
|
||||
of execution.
|
||||
|
||||
In order to start using Fluid kernels, we need first to include
|
||||
appropriate header files (which are not included by default):
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp fluid_includes
|
||||
|
||||
Once these headers are included, we can form up a new _kernel package_
|
||||
and specify it to G-API:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp kernel_pkg
|
||||
|
||||
In G-API, kernels (or operation implementations) are objects. Kernels are
|
||||
organized into collections, or _kernel packages_, represented by class
|
||||
cv::gapi::GKernelPackage. The main purpose of a kernel package is to
|
||||
capture which kernels we would like to use in our graph, and pass it
|
||||
as a _graph compilation option_:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp kernel_pkg_use
|
||||
|
||||
Traditional OpenCV is logically divided into modules, whith every
|
||||
module providing a set of functions. In G-API, there are also
|
||||
"modules" which are represented as kernel packages provided by a
|
||||
particular backend. In this example, we pass Fluid kernel packages to
|
||||
G-API to utilize appropriate Fluid functions in our graph.
|
||||
|
||||
Kernel packages are combinable -- in the above example, we take "Core"
|
||||
and "ImgProc" Fluid kernel packages and combine it into a single
|
||||
one. See documentation reference on cv::gapi::combine and
|
||||
cv::unite_policy on package combination options.
|
||||
|
||||
If no kernel packages are specified in options, G-API is using
|
||||
_default_ package which consists of default OpenCV implementations and
|
||||
thus G-API graphs are executed via OpenCV functions by default. OpenCV
|
||||
backend provides broader functional coverage than any other
|
||||
backend. If a kernel package is specified, like in this example, then
|
||||
it is being combined with the _default_ one with
|
||||
cv::unite_policy::REPLACE. It means that user-specified
|
||||
implementations will replace default implementations in case of
|
||||
conflict.
|
||||
|
||||
Kernel packages may contain a mix of kernels, in particular, multiple
|
||||
implementations of the same kernel. For example, a single kernel
|
||||
package may contain both OpenCV and Fluid implementations of kernel
|
||||
"Filter2D". In this case, the implementation selection preference can
|
||||
be specified with a special compilation parameter cv::gapi::lookup_order.
|
||||
|
||||
<!-- FIXME Document this process better as a part of regular -->
|
||||
<!-- documentation, not a tutorial kind of thing -->
|
||||
|
||||
## Troubleshooting and customization {#gapi_anisotropic_trouble}
|
||||
|
||||
After the above modifications, (in OpenCV 4.0) the app should crash
|
||||
with a message like this:
|
||||
|
||||
```
|
||||
$ ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi_fluid
|
||||
terminate called after throwing an instance of 'std::logic_error'
|
||||
what(): .../modules/gapi/src/backends/fluid/gfluidimgproc.cpp:436: Assertion kernelSize.width == 3 && kernelSize.height == 3 in function run failed
|
||||
|
||||
Aborted (core dumped)
|
||||
```
|
||||
|
||||
Fluid backend has a number of limitations in OpenCV 4.0 (see this
|
||||
[wiki page](https://github.com/opencv/opencv/wiki/Graph-API) for a
|
||||
more up-to-date status). In particular, the Box filter used in this
|
||||
sample supports only static 3x3 kernel size.
|
||||
|
||||
We can overcome this problem easily by avoiding G-API using Fluid
|
||||
version of Box filter kernel in this sample. It can be done by
|
||||
removing the appropriate kernel from the kernel package we've just
|
||||
created:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp kernel_hotfix
|
||||
|
||||
Now this kernel package doesn't have _any_ implementation of Box
|
||||
filter kernel interface (specified as a template parameter). As
|
||||
described above, G-API will fall-back to OpenCV to run this kernel
|
||||
now. The resulting code with this change now looks like:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/porting_anisotropic_image_segmentation/porting_anisotropic_image_segmentation_gapi_fluid.cpp kernel_pkg_proper
|
||||
|
||||
Let's examine the memory profile for this sample after we switched to
|
||||
Fluid backend. Now it looks like this:
|
||||
|
||||

|
||||
|
||||
Now the tool reports 3.8MiB -- and we just changed a few lines in our
|
||||
code, without modifying the graph itself! It is a ~2.8X improvement of
|
||||
the previous G-API result, and 2X improvement of the original OpenCV
|
||||
version.
|
||||
|
||||
Let's also examine how the internal representation of the graph now
|
||||
looks like. Dumping the graph into `.dot` would result into a
|
||||
visualization like this:
|
||||
|
||||

|
||||
|
||||
This graph doesn't differ structually from its previous version (in
|
||||
terms of operations and data objects), though a changed layout (on the
|
||||
left side of the dump) is easily noticeable.
|
||||
|
||||
The visualization reflects how G-API deals with mixed graphs, also
|
||||
called _heterogeneous_ graphs. The majority of operations in this
|
||||
graph are implemented with Fluid backend, but Box filters are executed
|
||||
by the OpenCV backend. One can easily see that the graph is partioned
|
||||
(with rectangles). G-API groups connected operations based on their
|
||||
affinity, forming _subgraphs_ (or _islands_ in G-API terminology), and
|
||||
our top-level graph becomes a composition of multiple smaller
|
||||
subgraphs. Every backend determines how its subgraph (island) is
|
||||
executed, so Fluid backend optimizes out memory where possible, and
|
||||
six intermediate buffers accessed by OpenCV Box filters are allocated
|
||||
fully and can't be optimized out.
|
||||
|
||||
<!-- TODO: add a chapter on custom kernels -->
|
||||
<!-- TODO: make a full-fluid pipeline -->
|
||||
<!-- TODO: talk about parallelism when it is available -->
|
||||
|
||||
# Conclusion {#gapi_tutor_conclusion}
|
||||
|
||||
This tutorial demonstrates what G-API is and what its key design
|
||||
concepts are, how an algorithm can be ported to G-API, and
|
||||
how to utilize graph model benefits after that.
|
||||
|
||||
In OpenCV 4.0, G-API is still in its inception stage -- it is more a
|
||||
foundation for all future work, though ready for use even now.
|
||||
|
||||
Further, this tutorial will be extended with new chapters on custom
|
||||
kernels programming, parallelism, and more.
|
17
doc/tutorials/gapi/table_of_content_gapi.markdown
Normal file
17
doc/tutorials/gapi/table_of_content_gapi.markdown
Normal file
@ -0,0 +1,17 @@
|
||||
# Graph API (gapi module) {#tutorial_table_of_content_gapi}
|
||||
|
||||
In this section you will learn about graph-based image processing and
|
||||
how G-API module can be used for that.
|
||||
|
||||
- @subpage tutorial_gapi_anisotropic_segmentation
|
||||
|
||||
*Languages:* C++
|
||||
|
||||
*Compatibility:* \> OpenCV 4.0
|
||||
|
||||
*Author:* Dmitry Matveev
|
||||
|
||||
This is an end-to-end tutorial where an existing sample algorithm
|
||||
is ported on G-API, covering the basic intuition behind this
|
||||
transition process, and examining benefits which a graph model
|
||||
brings there.
|
@ -67,6 +67,10 @@ As always, we would be happy to hear your comments and receive your contribution
|
||||
Use the powerful
|
||||
machine learning classes for statistical classification, regression and clustering of data.
|
||||
|
||||
- @subpage tutorial_table_of_content_gapi
|
||||
|
||||
Learn how to use Graph API (G-API) and port algorithms from "traditional" OpenCV to a graph model.
|
||||
|
||||
- @subpage tutorial_table_of_content_photo
|
||||
|
||||
Use OpenCV for
|
||||
|
@ -12,6 +12,10 @@ 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:
|
||||
@ -103,7 +107,7 @@ There is a number important concepts can be outlines with this examle:
|
||||
|
||||
<!-- FIXME: The above operator|() link links to MatExpr not GAPI -->
|
||||
|
||||
See Tutorial[TBD] and Porting examples[TBD] to learn more on various
|
||||
G-API features and concepts.
|
||||
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 -->
|
||||
|
@ -144,6 +144,12 @@ namespace core {
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
@ -447,6 +453,12 @@ namespace core {
|
||||
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
|
||||
@ -738,6 +750,35 @@ in radians (which is by default), or in degrees.
|
||||
*/
|
||||
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
|
||||
|
@ -5,10 +5,11 @@
|
||||
// Copyright (C) 2018 Intel Corporation
|
||||
|
||||
|
||||
#ifndef OPENCV_GAPI_GFLUIDCORE_HPP
|
||||
#define OPENCV_GAPI_GFLUIDCORE_HPP
|
||||
#ifndef OPENCV_GAPI_FLUID_CORE_HPP
|
||||
#define OPENCV_GAPI_FLUID_CORE_HPP
|
||||
|
||||
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
|
||||
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
|
||||
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
|
||||
|
||||
namespace cv { namespace gapi { namespace core { namespace fluid {
|
||||
|
||||
@ -16,4 +17,4 @@ GAPI_EXPORTS GKernelPackage kernels();
|
||||
|
||||
}}}}
|
||||
|
||||
#endif // OPENCV_GAPI_GFLUIDCORE_HPP
|
||||
#endif // OPENCV_GAPI_FLUID_CORE_HPP
|
@ -5,10 +5,11 @@
|
||||
// Copyright (C) 2018 Intel Corporation
|
||||
|
||||
|
||||
#ifndef OPENCV_GAPI_GFLUIDIMGPROC_HPP
|
||||
#define OPENCV_GAPI_GFLUIDIMGPROC_HPP
|
||||
#ifndef OPENCV_GAPI_FLUID_IMGPROC_HPP
|
||||
#define OPENCV_GAPI_FLUID_IMGPROC_HPP
|
||||
|
||||
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
|
||||
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
|
||||
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
|
||||
|
||||
namespace cv { namespace gapi { namespace imgproc { namespace fluid {
|
||||
|
||||
@ -16,4 +17,4 @@ GAPI_EXPORTS GKernelPackage kernels();
|
||||
|
||||
}}}}
|
||||
|
||||
#endif // OPENCV_GAPI_GFLUIDIMGPROC_HPP
|
||||
#endif // OPENCV_GAPI_FLUID_IMGPROC_HPP
|
@ -313,6 +313,9 @@ namespace gapi {
|
||||
// by API textual id.
|
||||
bool includesAPI(const std::string &id) const;
|
||||
|
||||
// Remove ALL implementations of the given API (identified by ID)
|
||||
void removeAPI(const std::string &id);
|
||||
|
||||
public:
|
||||
// Return total number of kernels (accross all backends)
|
||||
std::size_t size() const;
|
||||
@ -331,8 +334,16 @@ namespace gapi {
|
||||
// Removes all the kernels related to the given backend
|
||||
void remove(const GBackend& backend);
|
||||
|
||||
template<typename KAPI>
|
||||
void remove()
|
||||
{
|
||||
removeAPI(KAPI::id());
|
||||
}
|
||||
|
||||
// Check if package contains ANY implementation of a kernel API
|
||||
// by API type.
|
||||
// FIXME: Rename to includes() and distinguish API/impl case by
|
||||
// statically?
|
||||
template<typename KAPI>
|
||||
bool includesAPI() const
|
||||
{
|
||||
@ -354,11 +365,16 @@ namespace gapi {
|
||||
|
||||
// Put a new kernel implementation into package
|
||||
// FIXME: No overwrites allowed?
|
||||
template<typename KImpl> void include()
|
||||
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);
|
||||
}
|
||||
|
||||
@ -366,8 +382,8 @@ namespace gapi {
|
||||
std::vector<GBackend> backends() const;
|
||||
|
||||
friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &,
|
||||
const GKernelPackage &,
|
||||
const cv::unite_policy);
|
||||
const GKernelPackage &,
|
||||
const cv::unite_policy);
|
||||
};
|
||||
|
||||
template<typename... KK> GKernelPackage kernels()
|
||||
@ -389,8 +405,8 @@ namespace gapi {
|
||||
// Return a new package based on `lhs` and `rhs`,
|
||||
// with unity policy defined by `policy`.
|
||||
GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs,
|
||||
const GKernelPackage &rhs,
|
||||
const cv::unite_policy policy);
|
||||
const GKernelPackage &rhs,
|
||||
const cv::unite_policy policy);
|
||||
} // namespace gapi
|
||||
|
||||
namespace detail
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "../common/gapi_imgproc_perf_tests.hpp"
|
||||
#include "../../src/backends/fluid/gfluidimgproc.hpp"
|
||||
|
||||
|
||||
#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels()
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
#include "../../test/common/gapi_tests_common.hpp"
|
||||
#include "../../src/backends/fluid/gfluidcore.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
@ -19,4 +19,7 @@
|
||||
#include "opencv2/gapi/gpu/ggpukernel.hpp"
|
||||
#include "opencv2/gapi/operators.hpp"
|
||||
|
||||
#endif
|
||||
#include "opencv2/gapi/fluid/core.hpp"
|
||||
#include "opencv2/gapi/fluid/imgproc.hpp"
|
||||
|
||||
#endif // __OPENCV_GAPI_PERF_PRECOMP_HPP__
|
||||
|
@ -34,6 +34,12 @@ bool cv::gapi::GKernelPackage::includesAPI(const std::string &id) const
|
||||
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(),
|
||||
@ -53,7 +59,7 @@ cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs,
|
||||
{
|
||||
// REPLACE policy: if there is a collision, prefer RHS
|
||||
// to LHS
|
||||
// since OTHER package has a prefernece, start with its copy
|
||||
// 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
|
||||
|
@ -104,6 +104,11 @@ std::tuple<GMat, GMat> cartToPolar(const GMat& x, const GMat& y,
|
||||
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);
|
||||
@ -345,5 +350,10 @@ 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
|
||||
|
@ -132,6 +132,14 @@ GAPI_OCV_KERNEL(GCPUCartToPolar, cv::gapi::core::GCartToPolar)
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
@ -509,6 +517,14 @@ GAPI_OCV_KERNEL(GCPUConvertTo, cv::gapi::core::GConvertTo)
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
@ -527,6 +543,7 @@ cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels()
|
||||
, GCPUMask
|
||||
, GCPUPolarToCart
|
||||
, GCPUCartToPolar
|
||||
, GCPUPhase
|
||||
, GCPUCmpGT
|
||||
, GCPUCmpGE
|
||||
, GCPUCmpLE
|
||||
@ -572,6 +589,7 @@ cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels()
|
||||
, GCPUConcatVert
|
||||
, GCPULUT
|
||||
, GCPUConvertTo
|
||||
, GCPUSqrt
|
||||
>();
|
||||
return pkg;
|
||||
}
|
||||
|
@ -32,8 +32,6 @@
|
||||
|
||||
#include "backends/fluid/gfluidbuffer_priv.hpp"
|
||||
#include "backends/fluid/gfluidbackend.hpp"
|
||||
#include "backends/fluid/gfluidimgproc.hpp"
|
||||
#include "backends/fluid/gfluidcore.hpp"
|
||||
|
||||
#include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
|
||||
|
||||
|
@ -10,17 +10,18 @@
|
||||
|
||||
#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 "gfluidcore.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
@ -1543,7 +1544,6 @@ static void run_inrange(Buffer &dst, const View &src, const cv::Scalar &upperb,
|
||||
const cv::Scalar &lowerb)
|
||||
{
|
||||
static_assert(std::is_same<DST, uchar>::value, "wrong types");
|
||||
static_assert(std::is_integral<SRC>::value, "wrong types");
|
||||
|
||||
const auto *in = src.InLine<SRC>(0);
|
||||
auto *out = dst.OutLine<DST>();
|
||||
@ -1552,13 +1552,26 @@ static void run_inrange(Buffer &dst, const View &src, const cv::Scalar &upperb,
|
||||
int chan = src.meta().chan;
|
||||
GAPI_Assert(dst.meta().chan == 1);
|
||||
|
||||
// for integral input, in[i] >= lower equals in[i] >= ceil(lower)
|
||||
// so we can optimize compare operations by rounding lower/upper
|
||||
SRC lower[4], upper[4];
|
||||
for (int c=0; c < chan; c++)
|
||||
{
|
||||
lower[c] = saturate<SRC>(lowerb[c], ceild);
|
||||
upper[c] = saturate<SRC>(upperb[c], floord);
|
||||
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
|
||||
@ -1611,6 +1624,7 @@ GAPI_FLUID_KERNEL(GFluidInRange, cv::gapi::core::GInRange, false)
|
||||
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");
|
||||
}
|
||||
@ -1951,6 +1965,35 @@ GAPI_FLUID_KERNEL(GFluidCartToPolar, cv::gapi::core::GCartToPolar, false)
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
@ -2052,6 +2095,28 @@ GAPI_FLUID_KERNEL(GFluidResize, cv::gapi::core::GResize, true)
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
@ -2088,6 +2153,7 @@ cv::gapi::GKernelPackage cv::gapi::core::fluid::kernels()
|
||||
,GFluidSelect
|
||||
,GFluidPolarToCart
|
||||
,GFluidCartToPolar
|
||||
,GFluidPhase
|
||||
,GFluidAddC
|
||||
,GFluidSubC
|
||||
,GFluidSubRC
|
||||
@ -2105,6 +2171,7 @@ cv::gapi::GKernelPackage cv::gapi::core::fluid::kernels()
|
||||
,GFluidThreshold
|
||||
,GFluidInRange
|
||||
,GFluidResize
|
||||
,GFluidSqrt
|
||||
#if 0
|
||||
,GFluidMean -- not fluid
|
||||
,GFluidSum -- not fluid
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
#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 "gfluidimgproc.hpp"
|
||||
#include "gfluidutils.hpp"
|
||||
|
||||
#include "gfluidimgproc_func.hpp"
|
||||
|
@ -146,6 +146,8 @@ struct ConcatVertVecTest : public TestWithParam<std::tuple<int, cv::Size, cv::GC
|
||||
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
|
||||
|
@ -1422,6 +1422,58 @@ TEST_P(ConvertToTest, AccuracyTest)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -137,6 +137,21 @@ INSTANTIATE_TEST_CASE_P(Cart2PolarCPU, Cart2PolarTest,
|
||||
/*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(),
|
||||
@ -255,7 +270,7 @@ INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdOTTest,
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InRangeTestCPU, InRangeTest,
|
||||
Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1),
|
||||
Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1),
|
||||
Values(cv::Size(1280, 720),
|
||||
cv::Size(640, 480),
|
||||
cv::Size(128, 128)),
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "../common/gapi_core_tests.hpp"
|
||||
#include "backends/fluid/gfluidcore.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
@ -193,6 +192,21 @@ INSTANTIATE_TEST_CASE_P(Cart2PolarFluid, Cart2PolarTest,
|
||||
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),
|
||||
@ -206,7 +220,7 @@ INSTANTIATE_TEST_CASE_P(ThresholdTestFluid, ThresholdTest,
|
||||
Values(cv::compile_args(CORE_FLUID))));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InRangeTestFluid, InRangeTest,
|
||||
Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1),
|
||||
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),
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "../common/gapi_imgproc_tests.hpp"
|
||||
#include "backends/fluid/gfluidimgproc.hpp"
|
||||
|
||||
#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels()
|
||||
|
||||
|
@ -7,9 +7,8 @@
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "../common/gapi_operators_tests.hpp"
|
||||
#include "opencv2/gapi/cpu/core.hpp"
|
||||
|
||||
#define CORE_FLUID cv::gapi::core::cpu::kernels()
|
||||
#define CORE_FLUID cv::gapi::core::fluid::kernels()
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
@ -46,7 +46,29 @@ TEST(KernelPackage, Includes)
|
||||
EXPECT_FALSE(pkg.includes<J::Qux>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Include)
|
||||
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>();
|
||||
@ -56,6 +78,66 @@ TEST(KernelPackage, Include)
|
||||
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;
|
||||
@ -89,7 +171,7 @@ TEST(KernelPackage, IncludeHetero)
|
||||
EXPECT_TRUE (pkg.includes<S::Qux>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Unite_REPLACE_Full)
|
||||
TEST(KernelPackage, Combine_REPLACE_Full)
|
||||
{
|
||||
namespace J = Jupiter;
|
||||
namespace S = Saturn;
|
||||
@ -106,7 +188,7 @@ TEST(KernelPackage, Unite_REPLACE_Full)
|
||||
EXPECT_TRUE (u_pkg.includes<S::Baz>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Unite_REPLACE_Partial)
|
||||
TEST(KernelPackage, Combine_REPLACE_Partial)
|
||||
{
|
||||
namespace J = Jupiter;
|
||||
namespace S = Saturn;
|
||||
@ -120,7 +202,7 @@ TEST(KernelPackage, Unite_REPLACE_Partial)
|
||||
EXPECT_TRUE (u_pkg.includes<S::Bar>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Unite_REPLACE_Append)
|
||||
TEST(KernelPackage, Combine_REPLACE_Append)
|
||||
{
|
||||
namespace J = Jupiter;
|
||||
namespace S = Saturn;
|
||||
@ -134,7 +216,7 @@ TEST(KernelPackage, Unite_REPLACE_Append)
|
||||
EXPECT_TRUE(u_pkg.includes<S::Qux>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Unite_KEEP_AllDups)
|
||||
TEST(KernelPackage, Combine_KEEP_AllDups)
|
||||
{
|
||||
namespace J = Jupiter;
|
||||
namespace S = Saturn;
|
||||
@ -151,7 +233,7 @@ TEST(KernelPackage, Unite_KEEP_AllDups)
|
||||
EXPECT_TRUE(u_pkg.includes<S::Baz>());
|
||||
}
|
||||
|
||||
TEST(KernelPackage, Unite_KEEP_Append_NoDups)
|
||||
TEST(KernelPackage, Combine_KEEP_Append_NoDups)
|
||||
{
|
||||
namespace J = Jupiter;
|
||||
namespace S = Saturn;
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include "test_precomp.hpp"
|
||||
#include "api/gcomputation_priv.hpp"
|
||||
|
||||
#include <backends/fluid/gfluidcore.hpp>
|
||||
#include <backends/fluid/gfluidimgproc.hpp>
|
||||
#include "opencv2/gapi/fluid/gfluidkernel.hpp"
|
||||
#include "opencv2/gapi/fluid/core.hpp"
|
||||
#include "opencv2/gapi/fluid/imgproc.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
@ -21,5 +21,7 @@
|
||||
#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__
|
||||
|
@ -10,7 +10,9 @@
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
//! [calcGST_proto]
|
||||
void calcGST(const Mat& inputImg, Mat& imgCoherencyOut, Mat& imgOrientationOut, int w);
|
||||
//! [calcGST_proto]
|
||||
|
||||
int main()
|
||||
{
|
||||
@ -26,6 +28,7 @@ int main()
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [main_extra]
|
||||
//! [main]
|
||||
Mat imgCoherency, imgOrientation;
|
||||
calcGST(imgIn, imgCoherency, imgOrientation, W);
|
||||
@ -45,32 +48,36 @@ int main()
|
||||
|
||||
normalize(imgCoherency, imgCoherency, 0, 255, NORM_MINMAX);
|
||||
normalize(imgOrientation, imgOrientation, 0, 255, NORM_MINMAX);
|
||||
|
||||
imwrite("result.jpg", 0.5*(imgIn + imgBin));
|
||||
imwrite("Coherency.jpg", imgCoherency);
|
||||
imwrite("Orientation.jpg", imgOrientation);
|
||||
//! [main_extra]
|
||||
return 0;
|
||||
}
|
||||
//! [calcGST]
|
||||
//! [calcJ_header]
|
||||
void calcGST(const Mat& inputImg, Mat& imgCoherencyOut, Mat& imgOrientationOut, int w)
|
||||
{
|
||||
Mat img;
|
||||
inputImg.convertTo(img, CV_64F);
|
||||
inputImg.convertTo(img, CV_32F);
|
||||
|
||||
// GST components calculation (start)
|
||||
// J = (J11 J12; J12 J22) - GST
|
||||
Mat imgDiffX, imgDiffY, imgDiffXY;
|
||||
Sobel(img, imgDiffX, CV_64F, 1, 0, 3);
|
||||
Sobel(img, imgDiffY, CV_64F, 0, 1, 3);
|
||||
Sobel(img, imgDiffX, CV_32F, 1, 0, 3);
|
||||
Sobel(img, imgDiffY, CV_32F, 0, 1, 3);
|
||||
multiply(imgDiffX, imgDiffY, imgDiffXY);
|
||||
//! [calcJ_header]
|
||||
|
||||
Mat imgDiffXX, imgDiffYY;
|
||||
multiply(imgDiffX, imgDiffX, imgDiffXX);
|
||||
multiply(imgDiffY, imgDiffY, imgDiffYY);
|
||||
|
||||
Mat J11, J22, J12; // J11, J22 and J12 are GST components
|
||||
boxFilter(imgDiffXX, J11, CV_64F, Size(w, w));
|
||||
boxFilter(imgDiffYY, J22, CV_64F, Size(w, w));
|
||||
boxFilter(imgDiffXY, J12, CV_64F, Size(w, w));
|
||||
boxFilter(imgDiffXX, J11, CV_32F, Size(w, w));
|
||||
boxFilter(imgDiffYY, J22, CV_32F, Size(w, w));
|
||||
boxFilter(imgDiffXY, J12, CV_32F, Size(w, w));
|
||||
// GST components calculation (stop)
|
||||
|
||||
// eigenvalue calculation (start)
|
||||
|
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @brief You will learn how port an existing algorithm to G-API
|
||||
* @author Dmitry Matveev, dmitry.matveev@intel.com, based
|
||||
* on sample by Karpushin Vladislav, karpushin@ngs.ru
|
||||
*/
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#ifdef HAVE_OPENCV_GAPI
|
||||
|
||||
//! [full_sample]
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/gapi.hpp"
|
||||
#include "opencv2/gapi/core.hpp"
|
||||
#include "opencv2/gapi/imgproc.hpp"
|
||||
|
||||
//! [calcGST_proto]
|
||||
void calcGST(const cv::GMat& inputImg, cv::GMat& imgCoherencyOut, cv::GMat& imgOrientationOut, int w);
|
||||
//! [calcGST_proto]
|
||||
|
||||
int main()
|
||||
{
|
||||
int W = 52; // window size is WxW
|
||||
double C_Thr = 0.43; // threshold for coherency
|
||||
int LowThr = 35; // threshold1 for orientation, it ranges from 0 to 180
|
||||
int HighThr = 57; // threshold2 for orientation, it ranges from 0 to 180
|
||||
|
||||
cv::Mat imgIn = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
std::cout << "ERROR : Image cannot be loaded..!!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [main]
|
||||
// Calculate Gradient Structure Tensor and post-process it for output with G-API
|
||||
cv::GMat in;
|
||||
cv::GMat imgCoherency, imgOrientation;
|
||||
calcGST(in, imgCoherency, imgOrientation, W);
|
||||
|
||||
cv::GMat imgCoherencyBin = imgCoherency > C_Thr;
|
||||
cv::GMat imgOrientationBin = cv::gapi::inRange(imgOrientation, LowThr, HighThr);
|
||||
cv::GMat imgBin = imgCoherencyBin & imgOrientationBin;
|
||||
cv::GMat out = cv::gapi::addWeighted(in, 0.5, imgBin, 0.5, 0.0);
|
||||
|
||||
// Capture the graph into object segm
|
||||
cv::GComputation segm(cv::GIn(in), cv::GOut(out, imgCoherency, imgOrientation));
|
||||
|
||||
// Define cv::Mats for output data
|
||||
cv::Mat imgOut, imgOutCoherency, imgOutOrientation;
|
||||
|
||||
// Run the graph
|
||||
segm.apply(cv::gin(imgIn), cv::gout(imgOut, imgOutCoherency, imgOutOrientation));
|
||||
|
||||
// Normalize extra outputs (out of the graph)
|
||||
cv::normalize(imgOutCoherency, imgOutCoherency, 0, 255, cv::NORM_MINMAX);
|
||||
cv::normalize(imgOutOrientation, imgOutOrientation, 0, 255, cv::NORM_MINMAX);
|
||||
|
||||
cv::imwrite("result.jpg", imgOut);
|
||||
cv::imwrite("Coherency.jpg", imgOutCoherency);
|
||||
cv::imwrite("Orientation.jpg", imgOutOrientation);
|
||||
//! [main]
|
||||
|
||||
return 0;
|
||||
}
|
||||
//! [calcGST]
|
||||
//! [calcGST_header]
|
||||
void calcGST(const cv::GMat& inputImg, cv::GMat& imgCoherencyOut, cv::GMat& imgOrientationOut, int w)
|
||||
{
|
||||
auto img = cv::gapi::convertTo(inputImg, CV_32F);
|
||||
auto imgDiffX = cv::gapi::Sobel(img, CV_32F, 1, 0, 3);
|
||||
auto imgDiffY = cv::gapi::Sobel(img, CV_32F, 0, 1, 3);
|
||||
auto imgDiffXY = cv::gapi::mul(imgDiffX, imgDiffY);
|
||||
//! [calcGST_header]
|
||||
|
||||
auto imgDiffXX = cv::gapi::mul(imgDiffX, imgDiffX);
|
||||
auto imgDiffYY = cv::gapi::mul(imgDiffY, imgDiffY);
|
||||
|
||||
auto J11 = cv::gapi::boxFilter(imgDiffXX, CV_32F, cv::Size(w, w));
|
||||
auto J22 = cv::gapi::boxFilter(imgDiffYY, CV_32F, cv::Size(w, w));
|
||||
auto J12 = cv::gapi::boxFilter(imgDiffXY, CV_32F, cv::Size(w, w));
|
||||
|
||||
auto tmp1 = J11 + J22;
|
||||
auto tmp2 = J11 - J22;
|
||||
auto tmp22 = cv::gapi::mul(tmp2, tmp2);
|
||||
auto tmp3 = cv::gapi::mul(J12, J12);
|
||||
auto tmp4 = cv::gapi::sqrt(tmp22 + 4.0*tmp3);
|
||||
|
||||
auto lambda1 = tmp1 + tmp4;
|
||||
auto lambda2 = tmp1 - tmp4;
|
||||
|
||||
imgCoherencyOut = (lambda1 - lambda2) / (lambda1 + lambda2);
|
||||
imgOrientationOut = 0.5*cv::gapi::phase(J22 - J11, 2.0*J12, true);
|
||||
}
|
||||
//! [calcGST]
|
||||
|
||||
//! [full_sample]
|
||||
|
||||
#else
|
||||
#include <iostream>
|
||||
int main()
|
||||
{
|
||||
std::cerr << "This tutorial code requires G-API module to run" << std::endl;
|
||||
}
|
||||
#endif // HAVE_OPECV_GAPI
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @brief You will learn how port an existing algorithm to G-API
|
||||
* @author Dmitry Matveev, dmitry.matveev@intel.com, based
|
||||
* on sample by Karpushin Vladislav, karpushin@ngs.ru
|
||||
*/
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#ifdef HAVE_OPENCV_GAPI
|
||||
|
||||
//! [full_sample]
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/gapi.hpp"
|
||||
#include "opencv2/gapi/core.hpp"
|
||||
#include "opencv2/gapi/imgproc.hpp"
|
||||
//! [fluid_includes]
|
||||
#include "opencv2/gapi/fluid/core.hpp" // Fluid Core kernel library
|
||||
#include "opencv2/gapi/fluid/imgproc.hpp" // Fluid ImgProc kernel library
|
||||
//! [fluid_includes]
|
||||
#include "opencv2/gapi/fluid/gfluidkernel.hpp" // Fluid user kernel API
|
||||
|
||||
//! [calcGST_proto]
|
||||
void calcGST(const cv::GMat& inputImg, cv::GMat& imgCoherencyOut, cv::GMat& imgOrientationOut, int w);
|
||||
//! [calcGST_proto]
|
||||
|
||||
int main()
|
||||
{
|
||||
int W = 52; // window size is WxW
|
||||
double C_Thr = 0.43; // threshold for coherency
|
||||
int LowThr = 35; // threshold1 for orientation, it ranges from 0 to 180
|
||||
int HighThr = 57; // threshold2 for orientation, it ranges from 0 to 180
|
||||
|
||||
cv::Mat imgIn = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
std::cout << "ERROR : Image cannot be loaded..!!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [main]
|
||||
// Calculate Gradient Structure Tensor and post-process it for output with G-API
|
||||
cv::GMat in;
|
||||
cv::GMat imgCoherency, imgOrientation;
|
||||
calcGST(in, imgCoherency, imgOrientation, W);
|
||||
|
||||
auto imgCoherencyBin = imgCoherency > C_Thr;
|
||||
auto imgOrientationBin = cv::gapi::inRange(imgOrientation, LowThr, HighThr);
|
||||
auto imgBin = imgCoherencyBin & imgOrientationBin;
|
||||
cv::GMat out = cv::gapi::addWeighted(in, 0.5, imgBin, 0.5, 0.0);
|
||||
|
||||
// Capture the graph into object segm
|
||||
cv::GComputation segm(cv::GIn(in), cv::GOut(out, imgCoherency, imgOrientation));
|
||||
|
||||
// Define cv::Mats for output data
|
||||
cv::Mat imgOut, imgOutCoherency, imgOutOrientation;
|
||||
|
||||
//! [kernel_pkg_proper]
|
||||
//! [kernel_pkg]
|
||||
// Prepare the kernel package and run the graph
|
||||
cv::gapi::GKernelPackage fluid_kernels = cv::gapi::combine // Define a custom kernel package:
|
||||
(cv::gapi::core::fluid::kernels(), // ...with Fluid Core kernels
|
||||
cv::gapi::imgproc::fluid::kernels(), // ...and Fluid ImgProc kernels
|
||||
cv::unite_policy::KEEP);
|
||||
//! [kernel_pkg]
|
||||
//! [kernel_hotfix]
|
||||
fluid_kernels.remove<cv::gapi::imgproc::GBoxFilter>(); // Remove Fluid Box filter as unsuitable,
|
||||
// G-API will fall-back to OpenCV there.
|
||||
//! [kernel_hotfix]
|
||||
//! [kernel_pkg_use]
|
||||
segm.apply(cv::gin(imgIn), // Input data vector
|
||||
cv::gout(imgOut, imgOutCoherency, imgOutOrientation), // Output data vector
|
||||
cv::compile_args(fluid_kernels)); // Kernel package to use
|
||||
//! [kernel_pkg_use]
|
||||
//! [kernel_pkg_proper]
|
||||
|
||||
// Normalize extra outputs (out of the graph)
|
||||
cv::normalize(imgOutCoherency, imgOutCoherency, 0, 255, cv::NORM_MINMAX);
|
||||
cv::normalize(imgOutOrientation, imgOutOrientation, 0, 255, cv::NORM_MINMAX);
|
||||
|
||||
cv::imwrite("result.jpg", imgOut);
|
||||
cv::imwrite("Coherency.jpg", imgOutCoherency);
|
||||
cv::imwrite("Orientation.jpg", imgOutOrientation);
|
||||
//! [main]
|
||||
|
||||
return 0;
|
||||
}
|
||||
//! [calcGST]
|
||||
//! [calcGST_header]
|
||||
void calcGST(const cv::GMat& inputImg, cv::GMat& imgCoherencyOut, cv::GMat& imgOrientationOut, int w)
|
||||
{
|
||||
auto img = cv::gapi::convertTo(inputImg, CV_32F);
|
||||
auto imgDiffX = cv::gapi::Sobel(img, CV_32F, 1, 0, 3);
|
||||
auto imgDiffY = cv::gapi::Sobel(img, CV_32F, 0, 1, 3);
|
||||
auto imgDiffXY = cv::gapi::mul(imgDiffX, imgDiffY);
|
||||
//! [calcGST_header]
|
||||
|
||||
auto imgDiffXX = cv::gapi::mul(imgDiffX, imgDiffX);
|
||||
auto imgDiffYY = cv::gapi::mul(imgDiffY, imgDiffY);
|
||||
|
||||
auto J11 = cv::gapi::boxFilter(imgDiffXX, CV_32F, cv::Size(w, w));
|
||||
auto J22 = cv::gapi::boxFilter(imgDiffYY, CV_32F, cv::Size(w, w));
|
||||
auto J12 = cv::gapi::boxFilter(imgDiffXY, CV_32F, cv::Size(w, w));
|
||||
|
||||
auto tmp1 = J11 + J22;
|
||||
auto tmp2 = J11 - J22;
|
||||
auto tmp22 = cv::gapi::mul(tmp2, tmp2);
|
||||
auto tmp3 = cv::gapi::mul(J12, J12);
|
||||
auto tmp4 = cv::gapi::sqrt(tmp22 + 4.0*tmp3);
|
||||
|
||||
auto lambda1 = tmp1 + tmp4;
|
||||
auto lambda2 = tmp1 - tmp4;
|
||||
|
||||
imgCoherencyOut = (lambda1 - lambda2) / (lambda1 + lambda2);
|
||||
imgOrientationOut = 0.5*cv::gapi::phase(J22 - J11, 2.0*J12, true);
|
||||
}
|
||||
//! [calcGST]
|
||||
|
||||
//! [full_sample]
|
||||
|
||||
#else
|
||||
#include <iostream>
|
||||
int main()
|
||||
{
|
||||
std::cerr << "This tutorial code requires G-API module to run" << std::endl;
|
||||
}
|
||||
#endif // HAVE_OPECV_GAPI
|
Loading…
Reference in New Issue
Block a user