Skip to content

Commit

Permalink
Merge pull request opencv#13723 from smirnov-alexey:gapi_add_sobelxy
Browse files Browse the repository at this point in the history
* Add Sobel kernel which returns both dx and dy

* Splice dx and dy and extend add_border function

Also change some tests parameters

* Add borderValue parameter in test

* Introduces fluid kernel for sobelxy

Adds tests (basic and performance) on new backend

* Introduces BufHelper struct for some arithmetic
  • Loading branch information
smirnov-alexey authored and alalek committed Feb 8, 2019
1 parent 766fd20 commit b1cc114
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 19 deletions.
59 changes: 59 additions & 0 deletions modules/gapi/include/opencv2/gapi/imgproc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
namespace cv { namespace gapi {

namespace imgproc {
using GMat2 = std::tuple<GMat,GMat>;
using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this?

G_TYPED_KERNEL(GFilter2D, <GMat(GMat,int,Mat,Point,Scalar,int,Scalar)>,"org.opencv.imgproc.filters.filter2D") {
Expand Down Expand Up @@ -83,6 +84,12 @@ namespace imgproc {
}
};

G_TYPED_KERNEL_M(GSobelXY, <GMat2(GMat,int,int,int,double,double,int,Scalar)>, "org.opencv.imgproc.filters.sobelxy") {
static std::tuple<GMatDesc, GMatDesc> outMeta(GMatDesc in, int ddepth, int, int, double, double, int, Scalar) {
return std::make_tuple(in.withDepth(ddepth), in.withDepth(ddepth));
}
};

G_TYPED_KERNEL(GEqHist, <GMat(GMat)>, "org.opencv.imgproc.equalizeHist"){
static GMatDesc outMeta(GMatDesc in) {
return in.withType(CV_8U, 1);
Expand Down Expand Up @@ -487,6 +494,58 @@ GAPI_EXPORTS GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize =
int borderType = BORDER_DEFAULT,
const Scalar& borderValue = Scalar(0));

/** @brief Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator.
In all cases except one, the \f$\texttt{ksize} \times \texttt{ksize}\f$ separable kernel is used to
calculate the derivative. When \f$\texttt{ksize = 1}\f$, the \f$3 \times 1\f$ or \f$1 \times 3\f$
kernel is used (that is, no Gaussian smoothing is done). `ksize = 1` can only be used for the first
or the second x- or y- derivatives.
There is also the special value `ksize = FILTER_SCHARR (-1)` that corresponds to the \f$3\times3\f$ Scharr
filter that may give more accurate results than the \f$3\times3\f$ Sobel. The Scharr aperture is
\f[\vecthreethree{-3}{0}{3}{-10}{0}{10}{-3}{0}{3}\f]
for the x-derivative, or transposed for the y-derivative.
The function calculates an image derivative by convolving the image with the appropriate kernel:
\f[\texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}}\f]
The Sobel operators combine Gaussian smoothing and differentiation, so the result is more or less
resistant to the noise. Most often, the function is called with ( xorder = 1, yorder = 0, ksize = 3)
or ( xorder = 0, yorder = 1, ksize = 3) to calculate the first x- or y- image derivative. The first
case corresponds to a kernel of:
\f[\vecthreethree{-1}{0}{1}{-2}{0}{2}{-1}{0}{1}\f]
The second case corresponds to a kernel of:
\f[\vecthreethree{-1}{-2}{-1}{0}{0}{0}{1}{2}{1}\f]
@note First returned matrix correspons to dx derivative while the second one to dy.
@note Rounding to nearest even is procedeed if hardware supports it, if not - to nearest.
@note Function textual ID is "org.opencv.imgproc.filters.sobelxy"
@param src input image.
@param ddepth output image depth, see @ref filter_depths "combinations"; in the case of
8-bit input images it will result in truncated derivatives.
@param order order of the derivatives.
@param ksize size of the extended Sobel kernel; it must be odd.
@param scale optional scale factor for the computed derivative values; by default, no scaling is
applied (see cv::getDerivKernels for details).
@param delta optional delta value that is added to the results prior to storing them in dst.
@param borderType pixel extrapolation method, see cv::BorderTypes
@param borderValue border value in case of constant border type
@sa filter2D, gaussianBlur, cartToPolar
*/
GAPI_EXPORTS std::tuple<GMat, GMat> SobelXY(const GMat& src, int ddepth, int order, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT,
const Scalar& borderValue = Scalar(0));

/** @brief Finds edges in an image using the Canny algorithm.
The function finds edges in the input image and marks them in the output map edges using the
Expand Down
1 change: 1 addition & 0 deletions modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Erode3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType, cv::Siz
class DilatePerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int, cv::GCompileArgs>> {};
class Dilate3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,int, cv::GCompileArgs>> {};
class SobelPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int,int, cv::GCompileArgs>> {};
class SobelXYPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int, cv::GCompileArgs>> {};
class CannyPerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,double,double,int,bool, cv::GCompileArgs>> {};
class EqHistPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {};
class RGB2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {};
Expand Down
46 changes: 46 additions & 0 deletions modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,52 @@ PERF_TEST_P_(SobelPerfTest, TestPerformance)

//------------------------------------------------------------------------------

PERF_TEST_P_(SobelXYPerfTest, TestPerformance)
{
compare_f cmpF;
MatType type = 0;
int kernSize = 0, dtype = 0, order = 0;
cv::Size sz;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, kernSize, sz, dtype, order, compile_args) = GetParam();

cv::Mat out_mat_ocv2 = cv::Mat(sz, dtype);
cv::Mat out_mat_gapi2 = cv::Mat(sz, dtype);

initMatsRandN(type, sz, dtype, false);

// OpenCV code /////////////////////////////////////////////////////////////
{
cv::Sobel(in_mat1, out_mat_ocv, dtype, order, 0, kernSize);
cv::Sobel(in_mat1, out_mat_ocv2, dtype, 0, order, kernSize);
}

// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::SobelXY(in, dtype, order, kernSize);
cv::GComputation c(cv::GIn(in), cv::GOut(std::get<0>(out), std::get<1>(out)));

// Warm-up graph engine:
c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat_gapi2), std::move(compile_args));

TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat_gapi2));
}

// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
EXPECT_TRUE(cmpF(out_mat_gapi2, out_mat_ocv2));
EXPECT_EQ(out_mat_gapi.size(), sz);
EXPECT_EQ(out_mat_gapi2.size(), sz);
}

SANITY_CHECK_NOTHING();
}

//------------------------------------------------------------------------------

PERF_TEST_P_(CannyPerfTest, TestPerformance)
{
compare_f cmpF;
Expand Down
36 changes: 27 additions & 9 deletions modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ INSTANTIATE_TEST_CASE_P(SepFilterPerfTestFluid_other, SepFilterPerfTest,
INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add 4, 5, 7 when kernel is ready
Values(3), // TODO: add 4, 5, 7 when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT),
Values(-1, CV_32F),
Expand All @@ -41,7 +41,7 @@ INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest,
INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT),
Values(-1, CV_32F),
Expand All @@ -50,29 +50,29 @@ INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest,
INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestFluid, GaussianBlurPerfTest,
Combine(Values(ToleranceFilter(1e-3f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestFluid, MedianBlurPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(ErodePerfTestFluid, ErodePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::MorphShapes::MORPH_RECT,
cv::MorphShapes::MORPH_CROSS,
Expand All @@ -90,7 +90,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Erode3x3PerfTestFluid, Erode3x3PerfTest,
INSTANTIATE_TEST_CASE_P(DilatePerfTestFluid, DilatePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(3), // add size=5, when kernel is ready
Values(3), // TODO: add size=5, when kernel is ready
Values(szVGA, sz720p, sz1080p),
Values(cv::MorphShapes::MORPH_RECT,
cv::MorphShapes::MORPH_CROSS,
Expand All @@ -108,7 +108,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Dilate3x3PerfTestFluid, Dilate3x3PerfTest,
INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
Values(3), // add 5x5 once supported
Values(3), // TODO: add 5x5 once supported
Values(szVGA, sz720p, sz1080p),
Values(-1, CV_16S, CV_32F),
Values(0, 1),
Expand All @@ -118,13 +118,31 @@ INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest,
INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid32F, SobelPerfTest,
Combine(Values(ToleranceFilter(1e-3f, 0.0).to_compare_f()),
Values(CV_32FC1),
Values(3), // add 5x5 once supported
Values(3), // TODO: add 5x5 once supported
Values(szVGA, sz720p, sz1080p),
Values(CV_32F),
Values(0, 1),
Values(1, 2),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(SobelXYPerfTestFluid, SobelXYPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
Values(3), // TODO: add 5x5 once supported
Values(szVGA, sz720p, sz1080p),
Values(-1, CV_16S, CV_32F),
Values(1, 2),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(SobelXYPerfTestFluid32F, SobelXYPerfTest,
Combine(Values(ToleranceFilter(1e-3f, 0.0).to_compare_f()),
Values(CV_32FC1),
Values(3), // TODO: add 5x5 once supported
Values(szVGA, sz720p, sz1080p),
Values(CV_32F),
Values(1, 2),
Values(cv::compile_args(IMGPROC_FLUID))));

INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestFluid, RGB2GrayPerfTest,
Combine(Values(ToleranceColor(1e-3).to_compare_f()),
Values(szVGA, sz720p, sz1080p),
Expand Down
7 changes: 7 additions & 0 deletions modules/gapi/src/api/kernels_imgproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize,
return imgproc::GSobel::on(src, ddepth, dx, dy, ksize, scale, delta, borderType, bordVal);
}

std::tuple<GMat, GMat> SobelXY(const GMat& src, int ddepth, int order, int ksize,
double scale, double delta,
int borderType, const Scalar& bordVal)
{
return imgproc::GSobelXY::on(src, ddepth, order, ksize, scale, delta, borderType, bordVal);
}

GMat equalizeHist(const GMat& src)
{
return imgproc::GEqHist::on(src);
Expand Down
37 changes: 27 additions & 10 deletions modules/gapi/src/backends/cpu/gcpuimgproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
#include "opencv2/gapi/cpu/imgproc.hpp"
#include "backends/cpu/gcpuimgproc.hpp"

namespace {
cv::Mat add_border(const cv::Mat& in, const int ksize, const int borderType, const cv::Scalar& bordVal){
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int add = (ksize - 1) / 2;
cv::copyMakeBorder(in, temp_in, add, add, add, add, borderType, bordVal);
return temp_in(cv::Rect(add, add, in.cols, in.rows));
}
return in;
}
}

GAPI_OCV_KERNEL(GCPUSepFilter, cv::gapi::imgproc::GSepFilter)
{
static void run(const cv::Mat& in, int ddepth, const cv::Mat& kernX, const cv::Mat& kernY, const cv::Point& anchor, const cv::Scalar& delta,
Expand Down Expand Up @@ -133,16 +146,19 @@ GAPI_OCV_KERNEL(GCPUSobel, cv::gapi::imgproc::GSobel)
static void run(const cv::Mat& in, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType,
const cv::Scalar& bordVal, cv::Mat &out)
{
if( borderType == cv::BORDER_CONSTANT )
{
cv::Mat temp_in;
int add = (ksize - 1) / 2;
cv::copyMakeBorder(in, temp_in, add, add, add, add, borderType, bordVal );
cv::Rect rect = cv::Rect(add, add, in.cols, in.rows);
cv::Sobel(temp_in(rect), out, ddepth, dx, dy, ksize, scale, delta, borderType);
}
else
cv::Sobel(in, out, ddepth, dx, dy, ksize, scale, delta, borderType);
cv::Mat temp_in = add_border(in, ksize, borderType, bordVal);
cv::Sobel(temp_in, out, ddepth, dx, dy, ksize, scale, delta, borderType);
}
};

GAPI_OCV_KERNEL(GCPUSobelXY, cv::gapi::imgproc::GSobelXY)
{
static void run(const cv::Mat& in, int ddepth, int order, int ksize, double scale, double delta, int borderType,
const cv::Scalar& bordVal, cv::Mat &out_dx, cv::Mat &out_dy)
{
cv::Mat temp_in = add_border(in, ksize, borderType, bordVal);
cv::Sobel(temp_in, out_dx, ddepth, order, 0, ksize, scale, delta, borderType);
cv::Sobel(temp_in, out_dy, ddepth, 0, order, ksize, scale, delta, borderType);
}
};

Expand Down Expand Up @@ -256,6 +272,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels()
, GCPUErode
, GCPUDilate
, GCPUSobel
, GCPUSobelXY
, GCPUCanny
, GCPUEqualizeHist
, GCPURGB2YUV
Expand Down
Loading

0 comments on commit b1cc114

Please sign in to comment.