Commit b1cc114b authored by Alexey Smirnov's avatar Alexey Smirnov Committed by Alexander Alekhin

Merge pull request #13723 from smirnov-alexey:gapi_add_sobelxy

* 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
parent 766fd20f
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
namespace cv { namespace gapi { namespace cv { namespace gapi {
namespace imgproc { namespace imgproc {
using GMat2 = std::tuple<GMat,GMat>;
using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this? 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") { G_TYPED_KERNEL(GFilter2D, <GMat(GMat,int,Mat,Point,Scalar,int,Scalar)>,"org.opencv.imgproc.filters.filter2D") {
...@@ -83,6 +84,12 @@ namespace imgproc { ...@@ -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"){ G_TYPED_KERNEL(GEqHist, <GMat(GMat)>, "org.opencv.imgproc.equalizeHist"){
static GMatDesc outMeta(GMatDesc in) { static GMatDesc outMeta(GMatDesc in) {
return in.withType(CV_8U, 1); 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 = ...@@ -487,6 +494,58 @@ GAPI_EXPORTS GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize =
int borderType = BORDER_DEFAULT, int borderType = BORDER_DEFAULT,
const Scalar& borderValue = Scalar(0)); 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. /** @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 The function finds edges in the input image and marks them in the output map edges using the
......
...@@ -31,6 +31,7 @@ class Erode3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType, cv::Siz ...@@ -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 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 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 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 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 EqHistPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {};
class RGB2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {}; class RGB2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs >> {};
......
...@@ -498,6 +498,52 @@ PERF_TEST_P_(SobelPerfTest, TestPerformance) ...@@ -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) PERF_TEST_P_(CannyPerfTest, TestPerformance)
{ {
compare_f cmpF; compare_f cmpF;
......
...@@ -32,7 +32,7 @@ INSTANTIATE_TEST_CASE_P(SepFilterPerfTestFluid_other, SepFilterPerfTest, ...@@ -32,7 +32,7 @@ INSTANTIATE_TEST_CASE_P(SepFilterPerfTestFluid_other, SepFilterPerfTest,
INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest, INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT), Values(cv::BORDER_DEFAULT),
Values(-1, CV_32F), Values(-1, CV_32F),
...@@ -41,7 +41,7 @@ INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest, ...@@ -41,7 +41,7 @@ INSTANTIATE_TEST_CASE_P(Filter2DPerfTestFluid, Filter2DPerfTest,
INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest, INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT), Values(cv::BORDER_DEFAULT),
Values(-1, CV_32F), Values(-1, CV_32F),
...@@ -50,7 +50,7 @@ INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest, ...@@ -50,7 +50,7 @@ INSTANTIATE_TEST_CASE_P(BoxFilterPerfTestFluid, BoxFilterPerfTest,
INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest, INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::BORDER_DEFAULT), Values(cv::BORDER_DEFAULT),
Values(cv::compile_args(IMGPROC_FLUID)))); Values(cv::compile_args(IMGPROC_FLUID))));
...@@ -58,21 +58,21 @@ INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest, ...@@ -58,21 +58,21 @@ INSTANTIATE_TEST_CASE_P(BlurPerfTestFluid, BlurPerfTest,
INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestFluid, GaussianBlurPerfTest, INSTANTIATE_TEST_CASE_P(GaussianBlurPerfTestFluid, GaussianBlurPerfTest,
Combine(Values(ToleranceFilter(1e-3f, 0.01).to_compare_f()), Combine(Values(ToleranceFilter(1e-3f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_FLUID)))); Values(cv::compile_args(IMGPROC_FLUID))));
INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestFluid, MedianBlurPerfTest, INSTANTIATE_TEST_CASE_P(MedianBlurPerfTestFluid, MedianBlurPerfTest,
Combine(Values(AbsExact().to_compare_f()), Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_FLUID)))); Values(cv::compile_args(IMGPROC_FLUID))));
INSTANTIATE_TEST_CASE_P(ErodePerfTestFluid, ErodePerfTest, INSTANTIATE_TEST_CASE_P(ErodePerfTestFluid, ErodePerfTest,
Combine(Values(AbsExact().to_compare_f()), Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::MorphShapes::MORPH_RECT, Values(cv::MorphShapes::MORPH_RECT,
cv::MorphShapes::MORPH_CROSS, cv::MorphShapes::MORPH_CROSS,
...@@ -90,7 +90,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Erode3x3PerfTestFluid, Erode3x3PerfTest, ...@@ -90,7 +90,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Erode3x3PerfTestFluid, Erode3x3PerfTest,
INSTANTIATE_TEST_CASE_P(DilatePerfTestFluid, DilatePerfTest, INSTANTIATE_TEST_CASE_P(DilatePerfTestFluid, DilatePerfTest,
Combine(Values(AbsExact().to_compare_f()), Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), 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(szVGA, sz720p, sz1080p),
Values(cv::MorphShapes::MORPH_RECT, Values(cv::MorphShapes::MORPH_RECT,
cv::MorphShapes::MORPH_CROSS, cv::MorphShapes::MORPH_CROSS,
...@@ -108,7 +108,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Dilate3x3PerfTestFluid, Dilate3x3PerfTest, ...@@ -108,7 +108,7 @@ INSTANTIATE_TEST_CASE_P(DISABLED_Dilate3x3PerfTestFluid, Dilate3x3PerfTest,
INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest, INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest,
Combine(Values(AbsExact().to_compare_f()), Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), 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(szVGA, sz720p, sz1080p),
Values(-1, CV_16S, CV_32F), Values(-1, CV_16S, CV_32F),
Values(0, 1), Values(0, 1),
...@@ -118,13 +118,31 @@ INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest, ...@@ -118,13 +118,31 @@ INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid, SobelPerfTest,
INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid32F, SobelPerfTest, INSTANTIATE_TEST_CASE_P(SobelPerfTestFluid32F, SobelPerfTest,
Combine(Values(ToleranceFilter(1e-3f, 0.0).to_compare_f()), Combine(Values(ToleranceFilter(1e-3f, 0.0).to_compare_f()),
Values(CV_32FC1), Values(CV_32FC1),
Values(3), // add 5x5 once supported Values(3), // TODO: add 5x5 once supported
Values(szVGA, sz720p, sz1080p), Values(szVGA, sz720p, sz1080p),
Values(CV_32F), Values(CV_32F),
Values(0, 1), Values(0, 1),
Values(1, 2), Values(1, 2),
Values(cv::compile_args(IMGPROC_FLUID)))); 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, INSTANTIATE_TEST_CASE_P(RGB2GrayPerfTestFluid, RGB2GrayPerfTest,
Combine(Values(ToleranceColor(1e-3).to_compare_f()), Combine(Values(ToleranceColor(1e-3).to_compare_f()),
Values(szVGA, sz720p, sz1080p), Values(szVGA, sz720p, sz1080p),
......
...@@ -80,6 +80,13 @@ GMat Sobel(const GMat& src, int ddepth, int dx, int dy, int ksize, ...@@ -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); 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) GMat equalizeHist(const GMat& src)
{ {
return imgproc::GEqHist::on(src); return imgproc::GEqHist::on(src);
......
...@@ -11,6 +11,19 @@ ...@@ -11,6 +11,19 @@
#include "opencv2/gapi/cpu/imgproc.hpp" #include "opencv2/gapi/cpu/imgproc.hpp"
#include "backends/cpu/gcpuimgproc.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) 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, 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) ...@@ -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, 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) const cv::Scalar& bordVal, cv::Mat &out)
{ {
if( borderType == cv::BORDER_CONSTANT ) cv::Mat temp_in = add_border(in, ksize, borderType, bordVal);
{ cv::Sobel(temp_in, out, ddepth, dx, dy, ksize, scale, delta, borderType);
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); GAPI_OCV_KERNEL(GCPUSobelXY, cv::gapi::imgproc::GSobelXY)
cv::Sobel(temp_in(rect), out, ddepth, dx, dy, ksize, scale, delta, borderType); {
} static void run(const cv::Mat& in, int ddepth, int order, int ksize, double scale, double delta, int borderType,
else const cv::Scalar& bordVal, cv::Mat &out_dx, cv::Mat &out_dy)
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_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() ...@@ -256,6 +272,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels()
, GCPUErode , GCPUErode
, GCPUDilate , GCPUDilate
, GCPUSobel , GCPUSobel
, GCPUSobelXY
, GCPUCanny , GCPUCanny
, GCPUEqualizeHist , GCPUEqualizeHist
, GCPURGB2YUV , GCPURGB2YUV
......
...@@ -1022,6 +1022,168 @@ GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true) ...@@ -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 // Fluid kernels: filter2D
...@@ -1546,6 +1708,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels() ...@@ -1546,6 +1708,7 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()
, GFluidMedianBlur , GFluidMedianBlur
, GFluidGaussBlur , GFluidGaussBlur
, GFluidSobel , GFluidSobel
, GFluidSobelXY
#if 0 #if 0
, GFluidCanny -- not fluid (?) , GFluidCanny -- not fluid (?)
, GFluidEqualizeHist -- not fluid , GFluidEqualizeHist -- not fluid
......
...@@ -26,6 +26,7 @@ struct Erode3x3Test : public TestParams <std::tuple<compare_f,MatType,cv::Size,b ...@@ -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 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 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 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 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 CannyTest : public TestParams <std::tuple<compare_f,MatType,cv::Size,double,double,int,bool,bool,cv::GCompileArgs>> {};
struct RGB2GrayTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {}; struct RGB2GrayTest : public TestParams<std::tuple<compare_f,cv::Size,bool,cv::GCompileArgs>> {};
......
...@@ -353,6 +353,46 @@ TEST_P(SobelTest, AccuracyTest) ...@@ -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) TEST_P(EqHistTest, AccuracyTest)
{ {
compare_f cmpF; compare_f cmpF;
......
...@@ -153,6 +153,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestCPU32F, SobelTest, ...@@ -153,6 +153,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestCPU32F, SobelTest,
/*init output matrices or not*/ testing::Bool(), /*init output matrices or not*/ testing::Bool(),
Values(cv::compile_args(IMGPROC_CPU)))); 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, INSTANTIATE_TEST_CASE_P(EqHistTestCPU, EqHistTest,
Combine(Values(AbsExact().to_compare_f()), Combine(Values(AbsExact().to_compare_f()),
Values(cv::Size(1280, 720), Values(cv::Size(1280, 720),
......
...@@ -132,6 +132,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestFluid32F, SobelTest, ...@@ -132,6 +132,30 @@ INSTANTIATE_TEST_CASE_P(SobelTestFluid32F, SobelTest,
Values(true, false), Values(true, false),
Values(cv::compile_args(IMGPROC_FLUID)))); 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, INSTANTIATE_TEST_CASE_P(boxFilterTestFluid32, BoxFilterTest,
Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()),
Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(CV_8UC1, CV_16UC1, CV_16SC1),
......
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