Commit 85fad150 authored by Dmitry Matveev's avatar Dmitry Matveev Committed by Alexander Alekhin

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
parent 1d10d566
# 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)
......
/**
* @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
/**
* @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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment