diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 947dfc3029..baa81cf0ba 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1382,12 +1382,14 @@ Sobel( src, dy, CV_16SC1, 0, 1, 3 ); @param dx output image with first-order derivative in x. @param dy output image with first-order derivative in y. @param ksize size of Sobel kernel. It must be 3. +@param borderType pixel extrapolation method, see cv::BorderTypes @sa Sobel */ CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, - OutputArray dy, int ksize = 3 ); + OutputArray dy, int ksize = 3, + int borderType = BORDER_DEFAULT ); /** @brief Calculates the first x- or y- image derivative using Scharr operator. diff --git a/modules/imgproc/perf/perf_spatialgradient.cpp b/modules/imgproc/perf/perf_spatialgradient.cpp index 87456146de..84d41e1dc2 100644 --- a/modules/imgproc/perf/perf_spatialgradient.cpp +++ b/modules/imgproc/perf/perf_spatialgradient.cpp @@ -7,18 +7,20 @@ using namespace testing; using std::tr1::make_tuple; using std::tr1::get; -typedef std::tr1::tuple Size_Ksize_t; -typedef perf::TestBaseWithParam Size_Ksize; +typedef std::tr1::tuple Size_Ksize_BorderType_t; +typedef perf::TestBaseWithParam Size_Ksize_BorderType; -PERF_TEST_P( Size_Ksize, spatialGradient, +PERF_TEST_P( Size_Ksize_BorderType, spatialGradient, Combine( SZ_ALL_HD, - Values( 3 ) + Values( 3 ), + Values( BORDER_DEFAULT ) ) ) { Size size = std::tr1::get<0>(GetParam()); int ksize = std::tr1::get<1>(GetParam()); + int borderType = std::tr1::get<2>(GetParam()); Mat src(size, CV_8UC1); Mat dx(size, CV_16SC1); @@ -26,7 +28,7 @@ PERF_TEST_P( Size_Ksize, spatialGradient, declare.in(src, WARMUP_RNG).out(dx, dy); - TEST_CYCLE() spatialGradient(src, dx, dy, ksize); + TEST_CYCLE() spatialGradient(src, dx, dy, ksize, borderType); SANITY_CHECK(dx); SANITY_CHECK(dy); diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index e87869a4ea..ac209af17c 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -46,128 +46,73 @@ namespace cv { -void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksize ) +void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, + int ksize, int borderType ) { // Prepare InputArray src Mat src = _src.getMat(); CV_Assert( !src.empty() ); - CV_Assert( src.isContinuous() ); CV_Assert( src.type() == CV_8UC1 ); + CV_Assert( borderType == BORDER_DEFAULT || borderType == BORDER_REPLICATE ); // Prepare OutputArrays dx, dy _dx.create( src.size(), CV_16SC1 ); _dy.create( src.size(), CV_16SC1 ); Mat dx = _dx.getMat(), dy = _dy.getMat(); - CV_Assert( dx.isContinuous() ); - CV_Assert( dy.isContinuous() ); // TODO: Allow for other kernel sizes CV_Assert(ksize == 3); - // Reference - //Sobel( src, dx, CV_16SC1, 1, 0, ksize ); - //Sobel( src, dy, CV_16SC1, 0, 1, ksize ); - // Get dimensions - int H = src.rows, - W = src.cols, - N = H * W; - - // Get raw pointers to input/output data - uchar* p_src = src.ptr(0); - short* p_dx = dx.ptr(0); - short* p_dy = dy.ptr(0); + const int H = src.rows, + W = src.cols; // Row, column indices int i, j; - /* NOTE: - * - * Sobel-x: -1 0 1 - * -2 0 2 - * -1 0 1 - * - * Sobel-y: -1 -2 -1 - * 0 0 0 - * 1 2 1 - */ - - // No-SSE - int idx; + // Store pointers to rows of input/output data + // Padded by two rows for border handling + uchar* P_src[H+2]; + short* P_dx [H+2]; + short* P_dy [H+2]; - p_dx[0] = 0; // Top-left corner - p_dy[0] = 0; - p_dx[W-1] = 0; // Top-right corner - p_dy[W-1] = 0; - p_dx[N-1] = 0; // Bottom-right corner - p_dy[N-1] = 0; - p_dx[N-W] = 0; // Bottom-left corner - p_dy[N-W] = 0; + int i_top = 0, // Case for H == 1 && W == 1 && BORDER_REPLICATE + i_bottom = H - 1, + j_offl = 0, // j offset from 0th pixel to reach -1st pixel + j_offr = 0; // j offset from W-1th pixel to reach Wth pixel - // Handle special case: column matrix - if ( W == 1 ) + if ( borderType == BORDER_DEFAULT ) // Equiv. to BORDER_REFLECT_101 { - for ( i = 1; i < H - 1; i++ ) + if ( H > 1 ) { - p_dx[i] = 0; - p_dy[i] = 4*(p_src[i + 1] - p_src[i - 1]); // Should be 2?! 4 makes tests pass + i_top = 1; + i_bottom = H - 2; } - return; - } - - // Handle special case: row matrix - if ( H == 1 ) - { - for ( j = 1; j < W - 1; j++ ) + if ( W > 1 ) { - p_dx[j] = 4*(p_src[j + 1] - p_src[j - 1]); // Should be 2?! 4 makes tests pass - p_dy[j] = 0; + j_offl = 1; + j_offr = -1; } - return; } - // Do top row - for ( j = 1; j < W - 1; j++ ) - { - idx = j; - p_dx[idx] = -(p_src[idx+W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx+W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = 0; - } - - // Do right column - idx = 2*W - 1; - for ( i = 1; i < H - 1; i++ ) - { - p_dx[idx] = 0; - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W-1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W-1]); - idx += W; - } + P_src[0] = src.ptr(i_top); // Mirrored top border + P_src[H+1] = src.ptr(i_bottom); // Mirrored bottom border - // Do bottom row - idx = N - W + 1; - for ( j = 1; j < W - 1; j++ ) + for ( i = 0; i < H; i++ ) { - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx-W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx-W+1]); - p_dy[idx] = 0; - idx++; + P_src[i+1] = src.ptr(i); + P_dx [i] = dx.ptr(i); + P_dy [i] = dy.ptr(i); } - // Do left column - idx = W; - for ( i = 1; i < H - 1; i++ ) - { - p_dx[idx] = 0; - p_dy[idx] = -(p_src[idx-W+1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W+1] + 2*p_src[idx+W] + p_src[idx+W+1]); - idx += W; - } + // Pointer to row vectors + uchar *p_src, *c_src, *n_src; // previous, current, next row + short *c_dx, *c_dy; - // Do Inner area + int j_start = 0; +/* #if CV_SIMD128 // Characters in variable names have the following meanings: // u: unsigned char @@ -260,16 +205,39 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi } } #else - for ( i = 1; i < H - 1; i++ ) - for ( j = 1; j < W - 1; j++ ) +*/ + + /* NOTE: + * + * Sobel-x: -1 0 1 + * -2 0 2 + * -1 0 1 + * + * Sobel-y: -1 -2 -1 + * 0 0 0 + * 1 2 1 + */ + int j_p, j_n; + for ( i = 0; i < H; i++ ) { - idx = i*W + j; - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; + c_dx = P_dx [i]; + c_dy = P_dy [i]; + + for ( j = j_start; j < W; j++ ) + { + j_p = j - 1; + j_n = j + 1; + if ( j_p < 0 ) j_p = j + j_offl; + if ( j_n >= W ) j_n = j + j_offr; + + c_dx[j] = -(p_src[j_p] + c_src[j_p] + c_src[j_p] + n_src[j_p]) + + (p_src[j_n] + c_src[j_n] + c_src[j_n] + n_src[j_n]); + c_dy[j] = -(p_src[j_p] + p_src[j] + p_src[j] + p_src[j_n]) + + (n_src[j_p] + n_src[j] + n_src[j] + n_src[j_n]); + } } -#endif +//#endif } diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 0c98ed35c5..5994b1b11b 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -587,19 +587,17 @@ void CV_SpatialGradientTest::get_test_array_types_and_sizes( int test_case_idx, // Outputs are only CV_16SC1 for now types[OUTPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][0] - = types[REF_OUTPUT][1] = CV_16SC1; + = types[REF_OUTPUT][1] = CV_16SC1; ksize = 3; + border = BORDER_DEFAULT; // TODO: Add BORDER_REPLICATE } void CV_SpatialGradientTest::run_func() { - Mat dx, dy; - spatialGradient( test_mat[INPUT][0].clone(), dx, dy, ksize ); - - test_mat[OUTPUT][0] = dx; - test_mat[OUTPUT][1] = dy; + spatialGradient( test_mat[INPUT][0], test_mat[OUTPUT][0], + test_mat[OUTPUT][1], ksize, border ); } void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) @@ -607,10 +605,12 @@ void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) int dx, dy; dx = 1; dy = 0; - Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], CV_16SC1, dx, dy, ksize ); + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], CV_16SC1, dx, dy, ksize, + 1, 0, border ); dx = 0; dy = 1; - Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][1], CV_16SC1, dx, dy, ksize ); + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][1], CV_16SC1, dx, dy, ksize, + 1, 0, border ); }