Merge pull request #13723 from smirnov-alexey:gapi_add_sobelxy
authorAlexey Smirnov <alexey.smirnov@intel.com>
Fri, 8 Feb 2019 13:59:56 +0000 (16:59 +0300)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Fri, 8 Feb 2019 13:59:56 +0000 (16:59 +0300)
* 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

modules/gapi/include/opencv2/gapi/imgproc.hpp
modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp
modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp
modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp
modules/gapi/src/api/kernels_imgproc.cpp
modules/gapi/src/backends/cpu/gcpuimgproc.cpp
modules/gapi/src/backends/fluid/gfluidimgproc.cpp
modules/gapi/test/common/gapi_imgproc_tests.hpp
modules/gapi/test/common/gapi_imgproc_tests_inl.hpp
modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp
modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp

index b5a2b82..31dee7d 100644 (file)
@@ -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") {
@@ -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);
@@ -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
index 750c069..154f7d3 100644 (file)
@@ -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 >> {};
index 89ebf04..3f364d6 100644 (file)
@@ -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;
index 12554bf..635f2d0 100644 (file)
@@ -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),
@@ -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),
@@ -50,7 +50,7 @@ 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))));
@@ -58,21 +58,21 @@ INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest,
 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,
@@ -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,
@@ -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),
@@ -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),
index 7c4b522..7861de9 100644 (file)
@@ -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);
index d14584b..ad57ff3 100644 (file)
 #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,
@@ -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);
     }
 };
 
@@ -256,6 +272,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels()
         , GCPUErode
         , GCPUDilate
         , GCPUSobel
+        , GCPUSobelXY
         , GCPUCanny
         , GCPUEqualizeHist
         , GCPURGB2YUV
index de8beb1..e8cd6bd 100644 (file)
@@ -1022,6 +1022,168 @@ GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true)
     }
 };
 
+//---------------------
+//
+// Fluid kernels: SobelXY
+//
+//---------------------
+
+GAPI_FLUID_KERNEL(GFluidSobelXY, cv::gapi::imgproc::GSobelXY, true)
+{
+    static const int Window = 3;
+
+    struct BufHelper
+    {
+        float *kx_dx, *ky_dx,
+              *kx_dy, *ky_dy;
+        float *buf_start;
+        int buf_width, buf_chan;
+
+        static int length(int ksz, int width, int chan)
+        {
+            return ksz + ksz + ksz + ksz    // kernels: kx_dx, ky_dx, kx_dy, ky_dy
+                   + 2 * ksz * width * chan;
+        }
+
+        BufHelper(int ksz, int width, int chan, Buffer& scratch)
+        {
+            kx_dx = scratch.OutLine<float>();
+            ky_dx = kx_dx + ksz;
+            kx_dy = ky_dx + ksz;
+            ky_dy = kx_dy + ksz;
+            buf_start = ky_dy + ksz;
+            buf_width = width;
+            buf_chan = chan;
+        }
+
+        float* operator [](int i) {
+            return buf_start + i *  buf_width * buf_chan;
+        }
+    };
+
+    static void run(const     View  &    in,
+                              int     /* ddepth */,
+                              int     /* order */,
+                              int        ksize,
+                              double    _scale,
+                              double    _delta,
+                              int     /* borderType */,
+                    const cv::Scalar& /* borderValue */,
+                              Buffer&    out_x,
+                              Buffer&    out_y,
+                              Buffer&    scratch)
+    {
+        // TODO: support kernel height 3, 5, 7, 9, ...
+        GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
+
+        int ksz = (ksize == FILTER_SCHARR)? 3: ksize;
+
+        GAPI_Assert(out_x.meta().size.width == out_y.meta().size.width);
+        GAPI_Assert(out_x.meta().chan == out_y.meta().chan);
+
+        int width = out_x.meta().size.width;
+        int chan  = out_x.meta().chan;
+
+        BufHelper buf_helper(ksz, width, chan, scratch);
+
+        auto *kx_dx = buf_helper.kx_dx;
+        auto *ky_dx = buf_helper.ky_dx;
+        auto *kx_dy = buf_helper.kx_dy;
+        auto *ky_dy = buf_helper.ky_dy;
+
+        // Scratch buffer layout:
+        // |kx_dx|ky_dx|kx_dy|ky_dy|3 lines for horizontal kernel|3 lines for vertical kernel|
+        float *buf[3];
+        buf[0] = buf_helper[0];
+        buf[1] = buf_helper[1];
+        buf[2] = buf_helper[2];
+
+        auto scale = static_cast<float>(_scale);
+        auto delta = static_cast<float>(_delta);
+
+        auto calc = [&](const View& src, Buffer& dst, float* kx, float* ky) {
+            //     DST     SRC     OP         __VA_ARGS__
+            UNARY_(uchar , uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( short, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( short, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( short,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( float,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+            UNARY_( float,  float, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
+
+            CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
+        };
+
+        // calculate x-derivative
+        calc(in, out_x, kx_dx, ky_dx);
+
+        // Move pointers to calculate dy(preventing buffer data corruption)
+        buf[0] = buf_helper[3];
+        buf[1] = buf_helper[4];
+        buf[2] = buf_helper[5];
+
+        // calculate y-derivative
+        calc(in, out_y, kx_dy, ky_dy);
+    }
+
+    static void initScratch(const GMatDesc&    in,
+                                  int       /* ddepth */,
+                                  int          order,
+                                  int          ksize,
+                                  double    /* scale */,
+                                  double    /* delta */,
+                                  int       /* borderType */,
+                            const Scalar  & /* borderValue */,
+                                  Buffer  &    scratch)
+    {
+        // TODO: support kernel height 3, 5, 7, 9, ...
+        GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
+        int ksz = (ksize == FILTER_SCHARR) ? 3 : ksize;
+
+        int width = in.size.width;
+        int chan  = in.chan;
+        int buflen = BufHelper::length(ksz, width, chan);
+
+        cv::gapi::own::Size bufsize(buflen, 1);
+        GMatDesc bufdesc = {CV_32F, 1, bufsize};
+        Buffer buffer(bufdesc);
+        scratch = std::move(buffer);
+
+        BufHelper buf_helper(ksz, width, chan, scratch);
+
+        auto *kx_dx = buf_helper.kx_dx;
+        auto *ky_dx = buf_helper.ky_dx;
+        auto *kx_dy = buf_helper.kx_dy;
+        auto *ky_dy = buf_helper.ky_dy;
+
+        Mat kxmatX(1, ksize, CV_32FC1, kx_dx);
+        Mat kymatX(ksize, 1, CV_32FC1, ky_dx);
+        getDerivKernels(kxmatX, kymatX, order, 0, ksize);
+
+        Mat kxmatY(1, ksize, CV_32FC1, kx_dy);
+        Mat kymatY(ksize, 1, CV_32FC1, ky_dy);
+        getDerivKernels(kxmatY, kymatY, 0, order, ksize);
+    }
+
+    static void resetScratch(Buffer& /* scratch */)
+    {
+    }
+
+    static Border getBorder(const cv::GMatDesc& /* src */,
+                                      int       /* ddepth */,
+                                      int       /* order */,
+                                      int       /* ksize */,
+                                      double    /* scale */,
+                                      double    /* delta */,
+                                      int          borderType,
+                            const cv::Scalar  &    borderValue)
+    {
+        return {borderType, borderValue};
+    }
+};
+
 //------------------------
 //
 // Fluid kernels: filter2D
@@ -1546,6 +1708,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()
       , GFluidMedianBlur
       , GFluidGaussBlur
       , GFluidSobel
+      , GFluidSobelXY
     #if 0
       , GFluidCanny        -- not fluid (?)
       , GFluidEqualizeHist -- not fluid
index c21b26b..82d41ef 100644 (file)
@@ -26,6 +26,7 @@ struct Erode3x3Test : public TestParams <std::tuple<compare_f,MatType,cv::Size,b
 struct DilateTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,bool,cv::GCompileArgs>> {};
 struct Dilate3x3Test : public TestParams <std::tuple<compare_f,MatType,cv::Size,bool,int,cv::GCompileArgs>> {};
 struct SobelTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,int,int,bool,cv::GCompileArgs>> {};
+struct SobelXYTest : public TestParams <std::tuple<compare_f,MatType,int,cv::Size,int,int,int,int,cv::GCompileArgs>> {};
 struct EqHistTest : public TestParams <std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {};
 struct CannyTest : public TestParams <std::tuple<compare_f,MatType,cv::Size,double,double,int,bool,bool,cv::GCompileArgs>> {};
 struct RGB2GrayTest : public  TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {};
index 0812191..0ca0387 100644 (file)
@@ -353,6 +353,46 @@ TEST_P(SobelTest, AccuracyTest)
     }
 }
 
+TEST_P(SobelXYTest, AccuracyTest)
+{
+    compare_f cmpF;
+    MatType type = 0;
+    int kernSize = 0, dtype = 0, order = 0, border_type = 0, border_val = 0;
+    cv::Size sz;
+    cv::GCompileArgs compile_args;
+    std::tie(cmpF, type, kernSize, sz, dtype, order, border_type, border_val, compile_args) = GetParam();
+    initMatsRandN(type, sz, dtype);
+    cv::Mat out_mat_ocv2 = cv::Mat(sz, dtype);
+    cv::Mat out_mat_gapi2 = cv::Mat(sz, dtype);
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::GMat in;
+    auto out = cv::gapi::SobelXY(in, dtype, order, kernSize, 1, 0, border_type, border_val);
+
+    cv::GComputation c(cv::GIn(in), cv::GOut(std::get<0>(out), std::get<1>(out)));
+    c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat_gapi2), std::move(compile_args));
+    // OpenCV code /////////////////////////////////////////////////////////////
+    {
+        // workaround for cv::Sobel
+        cv::Mat temp_in;
+        if(border_type == cv::BORDER_CONSTANT)
+        {
+            int n_pixels = (kernSize - 1) / 2;
+            cv::copyMakeBorder(in_mat1, temp_in, n_pixels, n_pixels, n_pixels, n_pixels, border_type, border_val);
+            in_mat1 = temp_in(cv::Rect(n_pixels, n_pixels, in_mat1.cols, in_mat1.rows));
+        }
+        cv::Sobel(in_mat1, out_mat_ocv, dtype, order, 0, kernSize, 1, 0, border_type);
+        cv::Sobel(in_mat1, out_mat_ocv2, dtype, 0, order, kernSize, 1, 0, border_type);
+    }
+    // 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);
+    }
+}
+
 TEST_P(EqHistTest, AccuracyTest)
 {
     compare_f cmpF;
index beda022..3dc9167 100644 (file)
@@ -153,6 +153,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestCPU32F, SobelTest,
 /*init output matrices or not*/ testing::Bool(),
                                 Values(cv::compile_args(IMGPROC_CPU))));
 
+INSTANTIATE_TEST_CASE_P(SobelXYTestCPU, SobelXYTest,
+                        Combine(Values(AbsExact().to_compare_f()),
+                                Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_16S, CV_32F),
+                                Values(1, 2),
+                                Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT),
+                                Values(0, 1, 255),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
+INSTANTIATE_TEST_CASE_P(SobelXYTestCPU32F, SobelXYTest,
+                        Combine(Values(AbsExact().to_compare_f()),
+                                Values(CV_32FC1),
+                                Values(3, 5),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(CV_32F),
+                                Values(1, 2),
+                                Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT),
+                                Values(0, 1, 255),
+                                Values(cv::compile_args(IMGPROC_CPU))));
+
 INSTANTIATE_TEST_CASE_P(EqHistTestCPU, EqHistTest,
                         Combine(Values(AbsExact().to_compare_f()),
                                 Values(cv::Size(1280, 720),
index 5dca209..f053565 100644 (file)
@@ -132,6 +132,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestFluid32F, SobelTest,
                                 Values(true, false),
                                 Values(cv::compile_args(IMGPROC_FLUID))));
 
+INSTANTIATE_TEST_CASE_P(SobelXYTestFluid, SobelXYTest,
+                        Combine(Values(AbsExact().to_compare_f()),
+                                Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
+                                Values(3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(-1, CV_16S, CV_32F),
+                                Values(1, 2),
+                                Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101),
+                                Values(0, 1, 255),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
+INSTANTIATE_TEST_CASE_P(SobelXYTestFluid32F, SobelXYTest,
+                        Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
+                                Values(CV_32FC1),
+                                Values(3),
+                                Values(cv::Size(1280, 720),
+                                       cv::Size(640, 480)),
+                                Values(CV_32F),
+                                Values(1, 2),
+                                Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101),
+                                Values(0, 1, 255),
+                                Values(cv::compile_args(IMGPROC_FLUID))));
+
 INSTANTIATE_TEST_CASE_P(boxFilterTestFluid32, BoxFilterTest,
                         Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
                                 Values(CV_8UC1, CV_16UC1, CV_16SC1),