diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 88dd2e7f56..66dafa24ce 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -275,7 +275,8 @@ enum InterpolationFlags{ - flag is __not__ set: \f$dst( \rho , \phi ) = src(x,y)\f$ - flag is set: \f$dst(x,y) = src( \rho , \phi )\f$ */ - WARP_INVERSE_MAP = 16 + WARP_INVERSE_MAP = 16, + WARP_RELATIVE_MAP = 32 }; /** \brief Specify the polar mapping mode @@ -2490,6 +2491,7 @@ CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst, The function remap transforms the source image using the specified map: \f[\texttt{dst} (x,y) = \texttt{src} (map_x(x,y),map_y(x,y))\f] +\f[\texttt{dst} (x,y) = \texttt{src} (x+map_x(x,y),y+map_y(x,y))\f] with WARP_RELATIVE_MAP where values of pixels with non-integer coordinates are computed using one of available interpolation methods. \f$map_x\f$ and \f$map_y\f$ can be encoded as separate floating-point maps @@ -2509,7 +2511,9 @@ representation to fixed-point for speed. @param map2 The second map of y values having the type CV_16UC1, CV_32FC1, or none (empty map if map1 is (x,y) points), respectively. @param interpolation Interpolation method (see #InterpolationFlags). The methods #INTER_AREA -and #INTER_LINEAR_EXACT are not supported by this function. +#INTER_LINEAR_EXACT and #INTER_NEAREST_EXACT are not supported by this function. +The extra flag WARP_RELATIVE_MAP that can be ORed to the interpolation method +(e.g. INTER_LINEAR | WARP_RELATIVE_MAP) @param borderMode Pixel extrapolation method (see #BorderTypes). When borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image that corresponds to the "outliers" in the source image are not modified by the function. diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index d3e55f576f..255ed0c37f 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -376,8 +376,9 @@ enum /** ... and other image warping flags */ enum { - CV_WARP_FILL_OUTLIERS =8, - CV_WARP_INVERSE_MAP =16 + CV_WARP_FILL_OUTLIERS = 8, + CV_WARP_INVERSE_MAP = 16, + CV_WARP_RELATIVE_MAP = 32 }; /** Shapes of a structuring element for morphological operations diff --git a/modules/imgproc/perf/perf_warp.cpp b/modules/imgproc/perf/perf_warp.cpp index 4e7de8d2fe..3716e663f9 100644 --- a/modules/imgproc/perf/perf_warp.cpp +++ b/modules/imgproc/perf/perf_warp.cpp @@ -9,14 +9,15 @@ enum{HALF_SIZE=0, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH}; CV_ENUM(BorderMode, BORDER_CONSTANT, BORDER_REPLICATE) CV_ENUM(InterType, INTER_NEAREST, INTER_LINEAR) +CV_ENUM(InterTypeExtended, INTER_NEAREST, INTER_LINEAR, WARP_RELATIVE_MAP) CV_ENUM(RemapMode, HALF_SIZE, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH) typedef TestBaseWithParam< tuple > TestWarpAffine; typedef TestBaseWithParam< tuple > TestWarpPerspective; typedef TestBaseWithParam< tuple > TestWarpPerspectiveNear_t; -typedef TestBaseWithParam< tuple > TestRemap; +typedef TestBaseWithParam< tuple > TestRemap; -void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode ); +void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode, bool relative = false ); PERF_TEST_P( TestWarpAffine, WarpAffine, Combine( @@ -204,7 +205,7 @@ PERF_TEST_P( TestRemap, remap, Combine( Values( CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1 ), Values( szVGA, sz1080p ), - InterType::all(), + InterTypeExtended::all(), BorderMode::all(), RemapMode::all() ) @@ -224,7 +225,7 @@ PERF_TEST_P( TestRemap, remap, declare.in(source, WARMUP_RNG); - update_map(source, map_x, map_y, remapMode); + update_map(source, map_x, map_y, remapMode, ((interpolationType & WARP_RELATIVE_MAP) != 0)); TEST_CYCLE() { @@ -234,7 +235,7 @@ PERF_TEST_P( TestRemap, remap, SANITY_CHECK_NOTHING(); } -void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode ) +void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode, bool relative ) { for( int j = 0; j < src.rows; j++ ) { @@ -267,6 +268,12 @@ void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode ) map_y.at(j,i) = static_cast(src.rows - j) ; break; } // end of switch + + if( relative ) + { + map_x.at(j,i) -= static_cast(i); + map_y.at(j,i) -= static_cast(j); + } } } } diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 39f983ae7b..567f890906 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -326,9 +326,9 @@ static inline int clip(int x, int a, int b) * General warping (affine, perspective, remap) * \****************************************************************************************/ -template +template static void remapNearest( const Mat& _src, Mat& _dst, const Mat& _xy, - int borderType, const Scalar& _borderValue ) + int borderType, const Scalar& _borderValue, const Point& _offset ) { Size ssize = _src.size(), dsize = _dst.size(); const int cn = _src.channels(); @@ -341,7 +341,7 @@ static void remapNearest( const Mat& _src, Mat& _dst, const Mat& _xy, unsigned width1 = ssize.width, height1 = ssize.height; - if( _dst.isContinuous() && _xy.isContinuous() ) + if( _dst.isContinuous() && _xy.isContinuous() && !isRelative ) { dsize.width *= dsize.height; dsize.height = 1; @@ -351,12 +351,13 @@ static void remapNearest( const Mat& _src, Mat& _dst, const Mat& _xy, { T* D = _dst.ptr(dy); const short* XY = _xy.ptr(dy); - + const int off_y = isRelative ? (_offset.y+dy) : 0; if( cn == 1 ) { for(int dx = 0; dx < dsize.width; dx++ ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + const int off_x = isRelative ? (_offset.x+dx) : 0; + int sx = XY[dx*2]+off_x, sy = XY[dx*2+1]+off_y; if( (unsigned)sx < width1 && (unsigned)sy < height1 ) D[dx] = S0[sy*sstep + sx]; else @@ -382,7 +383,8 @@ static void remapNearest( const Mat& _src, Mat& _dst, const Mat& _xy, { for(int dx = 0; dx < dsize.width; dx++, D += cn ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + const int off_x = isRelative ? (_offset.x+dx) : 0; + int sx = XY[dx*2]+off_x, sy = XY[dx*2+1]+off_y; const T *S; if( (unsigned)sx < width1 && (unsigned)sy < height1 ) { @@ -427,11 +429,11 @@ static void remapNearest( const Mat& _src, Mat& _dst, const Mat& _xy, } } - +template struct RemapNoVec { int operator()( const Mat&, void*, const short*, const ushort*, - const void*, int ) const { return 0; } + const void*, int, cv::Point& ) const { return 0; } }; #if CV_SIMD128 @@ -439,13 +441,13 @@ struct RemapNoVec typedef unsigned short CV_DECL_ALIGNED(1) unaligned_ushort; typedef int CV_DECL_ALIGNED(1) unaligned_int; +template struct RemapVec_8u { int operator()( const Mat& _src, void* _dst, const short* XY, - const ushort* FXY, const void* _wtab, int width ) const + const ushort* FXY, const void* _wtab, int width, const Point& _offset ) const { int cn = _src.channels(), x = 0, sstep = (int)_src.step; - if( (cn != 1 && cn != 3 && cn != 4) || sstep >= 0x8000 ) return 0; @@ -493,12 +495,25 @@ struct RemapVec_8u *(unaligned_ushort*)(base + offset[2]), *(unaligned_ushort*)(base + offset[3]), \ 0, 0, 0, 0) + const short _rel_offset_x = static_cast(_offset.x); + const short _rel_offset_y = static_cast(_offset.y); + v_int16x8 v_dxy0(_rel_offset_x, _rel_offset_y, _rel_offset_x, _rel_offset_y, _rel_offset_x, _rel_offset_y, _rel_offset_x, _rel_offset_y); + v_int16x8 v_dxy1 = v_dxy0; + v_dxy0 = v_add(v_dxy0, v_int16x8(0, 0, 1, 0, 2, 0, 3, 0)); + v_dxy1 = v_add(v_dxy1, v_int16x8(4, 0, 5, 0, 6, 0, 7, 0)); if( cn == 1 ) { for( ; x <= width - 8; x += 8 ) { v_int16x8 _xy0 = v_load(XY + x*2); v_int16x8 _xy1 = v_load(XY + x*2 + 8); + if (isRelative) + { + const short x_s16 = static_cast(x); + v_int16x8 v_dxy01(x_s16, 0, x_s16, 0, x_s16, 0, x_s16, 0); + _xy0 = v_add(_xy0, v_add(v_dxy01, v_dxy0)); + _xy1 = v_add(_xy1, v_add(v_dxy01, v_dxy1)); + } v_int32x4 v0, v1, v2, v3, a0, b0, c0, d0, a1, b1, c1, d1, a2, b2, c2, d2; v_int32x4 xy0 = v_dotprod( _xy0, xy2ofs ); @@ -545,6 +560,12 @@ struct RemapVec_8u { v_int16x8 u0, v0, u1, v1; v_int16x8 _xy0 = v_load(XY + x * 2); + if (isRelative) + { + const short x_s16 = static_cast(x); + v_int16x8 v_dxy01(x_s16, 0, x_s16, 0, x_s16, 0, x_s16, 0); + _xy0 = v_add(_xy0, v_add(v_dxy01, v_dxy0)); + } v_int32x4 xy0 = v_dotprod(_xy0, xy2ofs); v_store(iofs0, xy0); @@ -595,10 +616,17 @@ struct RemapVec_8u for( ; x <= width - 4; x += 4, D += 16 ) { v_int16x8 _xy0 = v_load(XY + x * 2); + if (isRelative) + { + const short x_s16 = static_cast(x); + v_int16x8 v_dxy01(x_s16, 0, x_s16, 0, x_s16, 0, x_s16, 0); + _xy0 = v_add(_xy0, v_add(v_dxy01, v_dxy0)); + } v_int16x8 u0, v0, u1, v1; v_int32x4 xy0 = v_dotprod( _xy0, xy2ofs ); v_store(iofs0, xy0); + int offset0 = FXY[x] * 16; int offset1 = FXY[x + 1] * 16; int offset2 = FXY[x + 2] * 16; @@ -640,15 +668,14 @@ struct RemapVec_8u #else -typedef RemapNoVec RemapVec_8u; +template using RemapVec_8u = RemapNoVec; #endif - -template +template static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _fxy, const void* _wtab, - int borderType, const Scalar& _borderValue ) + int borderType, const Scalar& _borderValue, const Point& _offset ) { typedef typename CastOp::rtype T; typedef typename CastOp::type1 WT; @@ -678,12 +705,12 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, const ushort* FXY = _fxy.ptr(dy); int X0 = 0; bool prevInlier = false; - + const int off_y = (isRelative ? (_offset.y+dy) : 0); for(int dx = 0; dx <= dsize.width; dx++ ) { bool curInlier = dx < dsize.width ? - (unsigned)XY[dx*2] < width1 && - (unsigned)XY[dx*2+1] < height1 : !prevInlier; + (unsigned)XY[dx*2]+(isRelative ? (_offset.x+dx) : 0) < width1 && + (unsigned)XY[dx*2+1]+off_y < height1 : !prevInlier; if( curInlier == prevInlier ) continue; @@ -694,7 +721,8 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, if( !curInlier ) { - int len = vecOp( _src, D, XY + dx*2, FXY + dx, wtab, X1 - dx ); + Point subOffset(_offset.x+dx, _offset.y+dy); + int len = vecOp( _src, D, XY + dx*2, FXY + dx, wtab, X1 - dx, subOffset ); D += len*cn; dx += len; @@ -702,7 +730,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, { for( ; dx < X1; dx++, D++ ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; const AT* w = wtab + FXY[dx]*4; const T* S = S0 + sy*sstep + sx; *D = castOp(WT(S[0]*w[0] + S[1]*w[1] + S[sstep]*w[2] + S[sstep+1]*w[3])); @@ -711,7 +739,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else if( cn == 2 ) for( ; dx < X1; dx++, D += 2 ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; const AT* w = wtab + FXY[dx]*4; const T* S = S0 + sy*sstep + sx*2; WT t0 = S[0]*w[0] + S[2]*w[1] + S[sstep]*w[2] + S[sstep+2]*w[3]; @@ -721,7 +749,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else if( cn == 3 ) for( ; dx < X1; dx++, D += 3 ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; const AT* w = wtab + FXY[dx]*4; const T* S = S0 + sy*sstep + sx*3; WT t0 = S[0]*w[0] + S[3]*w[1] + S[sstep]*w[2] + S[sstep+3]*w[3]; @@ -732,7 +760,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else if( cn == 4 ) for( ; dx < X1; dx++, D += 4 ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; const AT* w = wtab + FXY[dx]*4; const T* S = S0 + sy*sstep + sx*4; WT t0 = S[0]*w[0] + S[4]*w[1] + S[sstep]*w[2] + S[sstep+4]*w[3]; @@ -745,7 +773,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else for( ; dx < X1; dx++, D += cn ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; const AT* w = wtab + FXY[dx]*4; const T* S = S0 + sy*sstep + sx*cn; for(int k = 0; k < cn; k++ ) @@ -760,7 +788,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, if (borderType == BORDER_TRANSPARENT) { for (; dx < X1; dx++, D += cn) { if (dx >= dsize.width) continue; - const int sx = XY[dx * 2], sy = XY[dx * 2 + 1]; + const int sx = XY[dx * 2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx * 2 + 1]+off_y; // If the mapped point is still within bounds, it did not get computed // because it lacked 4 neighbors. Still, it can be computed with an // approximate formula. If it is outside, the point is left untouched. @@ -791,7 +819,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, if( cn == 1 ) for( ; dx < X1; dx++, D++ ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; if( borderType == BORDER_CONSTANT && (sx >= ssize.width || sx+1 < 0 || sy >= ssize.height || sy+1 < 0) ) @@ -831,7 +859,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else for( ; dx < X1; dx++, D += cn ) { - int sx = XY[dx*2], sy = XY[dx*2+1]; + int sx = XY[dx*2]+(isRelative ? (_offset.x+dx) : 0), sy = XY[dx*2+1]+off_y; if( borderType == BORDER_CONSTANT && (sx >= ssize.width || sx+1 < 0 || sy >= ssize.height || sy+1 < 0) ) @@ -876,10 +904,10 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, } -template +template static void remapBicubic( const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _fxy, const void* _wtab, - int borderType, const Scalar& _borderValue ) + int borderType, const Scalar& _borderValue, const Point& _offset ) { typedef typename CastOp::rtype T; typedef typename CastOp::type1 WT; @@ -898,7 +926,7 @@ static void remapBicubic( const Mat& _src, Mat& _dst, const Mat& _xy, unsigned width1 = std::max(ssize.width-3, 0), height1 = std::max(ssize.height-3, 0); - if( _dst.isContinuous() && _xy.isContinuous() && _fxy.isContinuous() ) + if( _dst.isContinuous() && _xy.isContinuous() && _fxy.isContinuous() && !isRelative ) { dsize.width *= dsize.height; dsize.height = 1; @@ -909,10 +937,11 @@ static void remapBicubic( const Mat& _src, Mat& _dst, const Mat& _xy, T* D = _dst.ptr(dy); const short* XY = _xy.ptr(dy); const ushort* FXY = _fxy.ptr(dy); - + const int off_y = isRelative ? (_offset.y+dy) : 0; for(int dx = 0; dx < dsize.width; dx++, D += cn ) { - int sx = XY[dx*2]-1, sy = XY[dx*2+1]-1; + const int off_x = isRelative ? (_offset.x+dx) : 0; + int sx = XY[dx*2]-1+off_x, sy = XY[dx*2+1]-1+off_y; const AT* w = wtab + FXY[dx]*16; if( (unsigned)sx < width1 && (unsigned)sy < height1 ) { @@ -980,10 +1009,10 @@ static void remapBicubic( const Mat& _src, Mat& _dst, const Mat& _xy, } -template +template static void remapLanczos4( const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _fxy, const void* _wtab, - int borderType, const Scalar& _borderValue ) + int borderType, const Scalar& _borderValue, const Point& _offset ) { typedef typename CastOp::rtype T; typedef typename CastOp::type1 WT; @@ -1002,7 +1031,7 @@ static void remapLanczos4( const Mat& _src, Mat& _dst, const Mat& _xy, unsigned width1 = std::max(ssize.width-7, 0), height1 = std::max(ssize.height-7, 0); - if( _dst.isContinuous() && _xy.isContinuous() && _fxy.isContinuous() ) + if( _dst.isContinuous() && _xy.isContinuous() && _fxy.isContinuous() && !isRelative ) { dsize.width *= dsize.height; dsize.height = 1; @@ -1013,10 +1042,11 @@ static void remapLanczos4( const Mat& _src, Mat& _dst, const Mat& _xy, T* D = _dst.ptr(dy); const short* XY = _xy.ptr(dy); const ushort* FXY = _fxy.ptr(dy); - + const int off_y = isRelative ? (_offset.y+dy) : 0; for(int dx = 0; dx < dsize.width; dx++, D += cn ) { - int sx = XY[dx*2]-3, sy = XY[dx*2+1]-3; + const int off_x = isRelative ? (_offset.x+dx) : 0; + int sx = XY[dx*2]-3+off_x, sy = XY[dx*2+1]-3+off_y; const AT* w = wtab + FXY[dx]*64; const T* S = S0 + sy*sstep + sx*cn; if( (unsigned)sx < width1 && (unsigned)sy < height1 ) @@ -1091,11 +1121,11 @@ static void remapLanczos4( const Mat& _src, Mat& _dst, const Mat& _xy, typedef void (*RemapNNFunc)(const Mat& _src, Mat& _dst, const Mat& _xy, - int borderType, const Scalar& _borderValue ); + int borderType, const Scalar& _borderValue, const Point& _offset); typedef void (*RemapFunc)(const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _fxy, const void* _wtab, - int borderType, const Scalar& _borderValue); + int borderType, const Scalar& _borderValue, const Point& _offset); class RemapInvoker : public ParallelLoopBody @@ -1186,7 +1216,7 @@ public: } } } - nnfunc( *src, dpart, bufxy, borderType, borderValue ); + nnfunc( *src, dpart, bufxy, borderType, borderValue, Point(x, y) ); continue; } @@ -1293,7 +1323,7 @@ public: } } } - ifunc(*src, dpart, bufxy, bufa, ctab, borderType, borderValue); + ifunc(*src, dpart, bufxy, bufa, ctab, borderType, borderValue, Point(x, y)); } } } @@ -1315,6 +1345,9 @@ private: static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, InputArray _map2, int interpolation, int borderType, const Scalar& borderValue) { + const bool hasRelativeFlag = ((interpolation & WARP_RELATIVE_MAP) != 0); + interpolation &= ~WARP_RELATIVE_MAP; + const ocl::Device & dev = ocl::Device::getDefault(); int cn = _src.channels(), type = _src.type(), depth = _src.depth(), rowsPerWI = dev.isIntel() ? 4 : 1; @@ -1354,9 +1387,10 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input static const char * const interMap[] = { "INTER_NEAREST", "INTER_LINEAR", "INTER_CUBIC", "INTER_LINEAR", "INTER_LANCZOS" }; static const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", "BORDER_REFLECT_101", "BORDER_TRANSPARENT" }; - String buildOptions = format("-D %s -D %s -D T=%s -D ROWS_PER_WI=%d", + String buildOptions = format("-D %s -D %s -D T=%s -D ROWS_PER_WI=%d -D WARP_RELATIVE=%d", interMap[interpolation], borderMap[borderType], - ocl::typeToStr(type), rowsPerWI); + ocl::typeToStr(type), rowsPerWI, + hasRelativeFlag ? 1 : 0); if (interpolation != INTER_NEAREST) { @@ -1687,38 +1721,73 @@ void cv::remap( InputArray _src, OutputArray _dst, { CV_INSTRUMENT_REGION(); - static RemapNNFunc nn_tab[] = + const bool hasRelativeFlag = ((interpolation & WARP_RELATIVE_MAP) != 0); + + static RemapNNFunc nn_tab[2][8] = { - remapNearest, remapNearest, remapNearest, remapNearest, - remapNearest, remapNearest, remapNearest, 0 + { + remapNearest, remapNearest, remapNearest, remapNearest, + remapNearest, remapNearest, remapNearest, 0 + }, + { + remapNearest, remapNearest, remapNearest, remapNearest, + remapNearest, remapNearest, remapNearest, 0 + } }; - static RemapFunc linear_tab[] = + static RemapFunc linear_tab[2][8] = { - remapBilinear, RemapVec_8u, short>, 0, - remapBilinear, RemapNoVec, float>, - remapBilinear, RemapNoVec, float>, 0, - remapBilinear, RemapNoVec, float>, - remapBilinear, RemapNoVec, float>, 0 + { + remapBilinear, RemapVec_8u, short, false>, 0, + remapBilinear, RemapNoVec, float, false>, + remapBilinear, RemapNoVec, float, false>, 0, + remapBilinear, RemapNoVec, float, false>, + remapBilinear, RemapNoVec, float, false>, 0 + }, + { + remapBilinear, RemapVec_8u, short, true>, 0, + remapBilinear, RemapNoVec, float, true>, + remapBilinear, RemapNoVec, float, true>, 0, + remapBilinear, RemapNoVec, float, true>, + remapBilinear, RemapNoVec, float, true>, 0 + } }; - static RemapFunc cubic_tab[] = + static RemapFunc cubic_tab[2][8] = { - remapBicubic, short, INTER_REMAP_COEF_SCALE>, 0, - remapBicubic, float, 1>, - remapBicubic, float, 1>, 0, - remapBicubic, float, 1>, - remapBicubic, float, 1>, 0 - }; + { + remapBicubic, short, INTER_REMAP_COEF_SCALE, false>, 0, + remapBicubic, float, 1, false>, + remapBicubic, float, 1, false>, 0, + remapBicubic, float, 1, false>, + remapBicubic, float, 1, false>, 0 + }, + { + remapBicubic, short, INTER_REMAP_COEF_SCALE, true>, 0, + remapBicubic, float, 1, true>, + remapBicubic, float, 1, true>, 0, + remapBicubic, float, 1, true>, + remapBicubic, float, 1, true>, 0 + } +}; - static RemapFunc lanczos4_tab[] = + static RemapFunc lanczos4_tab[2][8] = { - remapLanczos4, short, INTER_REMAP_COEF_SCALE>, 0, - remapLanczos4, float, 1>, - remapLanczos4, float, 1>, 0, - remapLanczos4, float, 1>, - remapLanczos4, float, 1>, 0 - }; + { + remapLanczos4, short, INTER_REMAP_COEF_SCALE, false>, 0, + remapLanczos4, float, 1, false>, + remapLanczos4, float, 1, false>, 0, + remapLanczos4, float, 1, false>, + remapLanczos4, float, 1, false>, 0 + }, + { + remapLanczos4, short, INTER_REMAP_COEF_SCALE, true>, 0, + remapLanczos4, float, 1, true>, + remapLanczos4, float, 1, true>, 0, + remapLanczos4, float, 1, true>, + remapLanczos4, float, 1, true>, 0 + } +}; CV_Assert( !_map1.empty() ); CV_Assert( _map2.empty() || (_map2.size() == _map1.size())); @@ -1738,7 +1807,8 @@ void cv::remap( InputArray _src, OutputArray _dst, ((map1.type() == CV_32FC2 && map2.empty() && map1.size == dst.size) || (map1.type() == CV_32FC1 && map2.type() == CV_32FC1 && map1.size == dst.size && map2.size == dst.size) || (map1.empty() && map2.type() == CV_32FC2 && map2.size == dst.size)) && - ((borderType & BORDER_ISOLATED) != 0 || !src.isSubmatrix()), + ((borderType & BORDER_ISOLATED) != 0 || !src.isSubmatrix()) && + !hasRelativeFlag, openvx_remap(src, dst, map1, map2, interpolation, borderValue)); CV_Assert( dst.cols < SHRT_MAX && dst.rows < SHRT_MAX && src.cols < SHRT_MAX && src.rows < SHRT_MAX ); @@ -1746,6 +1816,7 @@ void cv::remap( InputArray _src, OutputArray _dst, if( dst.data == src.data ) src = src.clone(); + interpolation &= ~WARP_RELATIVE_MAP; if( interpolation == INTER_AREA ) interpolation = INTER_LINEAR; @@ -1798,21 +1869,22 @@ void cv::remap( InputArray _src, OutputArray _dst, bool fixpt = depth == CV_8U; bool planar_input = false; + const int relativeOptionIndex = (hasRelativeFlag ? 1 : 0); if( interpolation == INTER_NEAREST ) { - nnfunc = nn_tab[depth]; + nnfunc = nn_tab[relativeOptionIndex][depth]; CV_Assert( nnfunc != 0 ); } else { if( interpolation == INTER_LINEAR ) - ifunc = linear_tab[depth]; + ifunc = linear_tab[relativeOptionIndex][depth]; else if( interpolation == INTER_CUBIC ){ - ifunc = cubic_tab[depth]; + ifunc = cubic_tab[relativeOptionIndex][depth]; CV_Assert( _src.channels() <= 4 ); } else if( interpolation == INTER_LANCZOS4 ){ - ifunc = lanczos4_tab[depth]; + ifunc = lanczos4_tab[relativeOptionIndex][depth]; CV_Assert( _src.channels() <= 4 ); } else diff --git a/modules/imgproc/src/opencl/remap.cl b/modules/imgproc/src/opencl/remap.cl index 8c8b933ab5..9aadde0215 100644 --- a/modules/imgproc/src/opencl/remap.cl +++ b/modules/imgproc/src/opencl/remap.cl @@ -168,6 +168,10 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src int gx = convert_int_sat_rte(map1[0]); int gy = convert_int_sat_rte(map2[0]); + #if WARP_RELATIVE + gx += x; + gy += y; + #endif if (NEED_EXTRAPOLATION(gx, gy)) { @@ -210,6 +214,11 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o __global T * dst = (__global T *)(dstptr + dst_index); int2 gxy = convert_int2_sat_rte(map[0]); + #if WARP_RELATIVE + gxy.x += x; + gxy.y += y; + #endif + int gx = gxy.x, gy = gxy.y; if (NEED_EXTRAPOLATION(gx, gy)) @@ -250,6 +259,11 @@ __kernel void remap_16SC2(__global const uchar * srcptr, int src_step, int src_o __global T * dst = (__global T *)(dstptr + dst_index); int2 gxy = convert_int2(map[0]); + #if WARP_RELATIVE + gxy.x += x; + gxy.y += y; + #endif + int gx = gxy.x, gy = gxy.y; if (NEED_EXTRAPOLATION(gx, gy)) @@ -296,6 +310,11 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int int dx = (map2Value & (INTER_TAB_SIZE - 1)) < (INTER_TAB_SIZE >> 1) ? 1 : 0; int dy = (map2Value >> INTER_BITS) < (INTER_TAB_SIZE >> 1) ? 1 : 0; int2 gxy = convert_int2(map1[0]) + (int2)(dx, dy); + #if WARP_RELATIVE + gxy.x += x; + gxy.y += y; + #endif + int gx = gxy.x, gy = gxy.y; if (NEED_EXTRAPOLATION(gx, gy)) @@ -349,6 +368,10 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int __global T * dst = (__global T *)(dstptr + dst_index); int2 map_dataA = convert_int2(map1[0]); + #if WARP_RELATIVE + map_dataA.x += x; + map_dataA.y += y; + #endif int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); @@ -414,8 +437,12 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src #if defined BORDER_CONSTANT float xf = map1[0], yf = map2[0]; - int sx = convert_int_sat_rtz(mad(xf, (float)INTER_TAB_SIZE, 0.5f)) >> INTER_BITS; - int sy = convert_int_sat_rtz(mad(yf, (float)INTER_TAB_SIZE, 0.5f)) >> INTER_BITS; + int sx = (convert_int_sat_rtz(mad(xf, (float)INTER_TAB_SIZE, 0.5f)) >> INTER_BITS); + int sy = (convert_int_sat_rtz(mad(yf, (float)INTER_TAB_SIZE, 0.5f)) >> INTER_BITS); + #if WARP_RELATIVE + sx += x; + sy += y; + #endif __constant float * coeffs_x = coeffs + ((convert_int_rte(xf * INTER_TAB_SIZE) & (INTER_TAB_SIZE - 1)) << 1); __constant float * coeffs_y = coeffs + ((convert_int_rte(yf * INTER_TAB_SIZE) & (INTER_TAB_SIZE - 1)) << 1); @@ -456,6 +483,10 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src storepix(CONVERT_TO_T(sum), dst); #else float2 map_data = (float2)(map1[0], map2[0]); + #if WARP_RELATIVE + map_data.x += x; + map_data.y += y; + #endif int2 map_dataA = convert_int2_sat_rtn(map_data); int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); @@ -520,6 +551,10 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o __global T * dst = (__global T *)(dstptr + dst_index); float2 map_data = map[0]; + #if WARP_RELATIVE + map_data.x += x; + map_data.y += y; + #endif int2 map_dataA = convert_int2_sat_rtn(map_data); int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); diff --git a/modules/imgproc/test/ocl/test_warp.cpp b/modules/imgproc/test/ocl/test_warp.cpp index b43c9b6732..852dc465ab 100644 --- a/modules/imgproc/test/ocl/test_warp.cpp +++ b/modules/imgproc/test/ocl/test_warp.cpp @@ -438,6 +438,101 @@ OCL_TEST_P(Remap_INTER_LINEAR, Mat) } } +///////////////////////////////////////////////////////////////////////////////////////////////// +// remap relative + +PARAM_TEST_CASE(RemapRelative, MatDepth, Channels, Interpolation, BorderType, bool) +{ + int srcType; + int interpolation; + int borderType; + bool useFixedPoint; + + Scalar val; + + TEST_DECLARE_INPUT_PARAMETER(map1); + TEST_DECLARE_INPUT_PARAMETER(map2); + TEST_DECLARE_OUTPUT_PARAMETER(dst); + + UMat uSrc; + UMat uMapRelativeX32F; + UMat uMapRelativeY32F; + UMat uMapAbsoluteX32F; + UMat uMapAbsoluteY32F; + UMat uMapRelativeX16S; + UMat uMapRelativeY16S; + UMat uMapAbsoluteX16S; + UMat uMapAbsoluteY16S; + + virtual void SetUp() + { + srcType = CV_MAKE_TYPE(GET_PARAM(0), GET_PARAM(1)); + interpolation = GET_PARAM(2); + borderType = GET_PARAM(3); + useFixedPoint = GET_PARAM(4); + + const int nChannels = CV_MAT_CN(srcType); + const cv::Size size(127, 61); + cv::Mat data64FC1(1, size.area()*nChannels, CV_64FC1); + data64FC1.forEach([&](double& pixel, const int* position) {pixel = static_cast(position[1]);}); + + cv::Mat src; + data64FC1.reshape(nChannels, size.height).convertTo(src, srcType); + + cv::Mat mapRelativeX32F(size, CV_32FC1); + mapRelativeX32F.setTo(cv::Scalar::all(-0.33)); + + cv::Mat mapRelativeY32F(size, CV_32FC1); + mapRelativeY32F.setTo(cv::Scalar::all(-0.33)); + + cv::Mat mapAbsoluteX32F = mapRelativeX32F.clone(); + mapAbsoluteX32F.forEach([&](float& pixel, const int* position) { + pixel += static_cast(position[1]); + }); + + cv::Mat mapAbsoluteY32F = mapRelativeY32F.clone(); + mapAbsoluteY32F.forEach([&](float& pixel, const int* position) { + pixel += static_cast(position[0]); + }); + + OCL_ON(src.copyTo(uSrc)); + OCL_ON(mapRelativeX32F.copyTo(uMapRelativeX32F)); + OCL_ON(mapRelativeY32F.copyTo(uMapRelativeY32F)); + OCL_ON(mapAbsoluteX32F.copyTo(uMapAbsoluteX32F)); + OCL_ON(mapAbsoluteY32F.copyTo(uMapAbsoluteY32F)); + + if (useFixedPoint) + { + const bool nninterpolation = (interpolation == cv::INTER_NEAREST) || (interpolation == cv::INTER_NEAREST_EXACT); + OCL_ON(cv::convertMaps(uMapAbsoluteX32F, uMapAbsoluteY32F, uMapAbsoluteX16S, uMapAbsoluteY16S, CV_16SC2, nninterpolation)); + OCL_ON(cv::convertMaps(uMapRelativeX32F, uMapRelativeY32F, uMapRelativeX16S, uMapRelativeY16S, CV_16SC2, nninterpolation)); + } + } +}; + +OCL_TEST_P(RemapRelative, Mat) +{ + cv::UMat uDstAbsolute; + cv::UMat uDstRelative; + if (useFixedPoint) + { + OCL_ON(cv::remap(uSrc, uDstAbsolute, uMapAbsoluteX16S, uMapAbsoluteY16S, interpolation, borderType)); + OCL_ON(cv::remap(uSrc, uDstRelative, uMapRelativeX16S, uMapRelativeY16S, interpolation | WARP_RELATIVE_MAP, borderType)); + } + else + { + OCL_ON(cv::remap(uSrc, uDstAbsolute, uMapAbsoluteX32F, uMapAbsoluteY32F, interpolation, borderType)); + OCL_ON(cv::remap(uSrc, uDstRelative, uMapRelativeX32F, uMapRelativeY32F, interpolation | WARP_RELATIVE_MAP, borderType)); + } + + cv::Mat dstAbsolute; + OCL_ON(uDstAbsolute.copyTo(dstAbsolute)); + cv::Mat dstRelative; + OCL_ON(uDstRelative.copyTo(dstRelative)); + + EXPECT_MAT_NEAR(dstAbsolute, dstRelative, dstAbsolute.depth() == CV_32F ? 1e-3 : 1.0); +} + ///////////////////////////////////////////////////////////////////////////////////// OCL_INSTANTIATE_TEST_CASE_P(ImgprocWarp, WarpAffine, Combine( @@ -515,6 +610,20 @@ OCL_INSTANTIATE_TEST_CASE_P(ImgprocWarp, Remap_INTER_NEAREST, Combine( (BorderType)BORDER_REFLECT_101), Bool())); +OCL_INSTANTIATE_TEST_CASE_P(ImgprocWarp, RemapRelative, Combine( + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(1, 3, 4), + Values((Interpolation)INTER_NEAREST, + (Interpolation)INTER_LINEAR, + (Interpolation)INTER_CUBIC, + (Interpolation)INTER_LANCZOS4), + Values((BorderType)BORDER_CONSTANT, + (BorderType)BORDER_REPLICATE, + (BorderType)BORDER_WRAP, + (BorderType)BORDER_REFLECT, + (BorderType)BORDER_REFLECT_101), + Bool())); + } } // namespace opencv_test::ocl #endif // HAVE_OPENCL diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 4ba2ab80b4..737128aa55 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1311,6 +1311,73 @@ TEST(Imgproc_resize_area, regression_quarter_round) check_resize_area(expected, actual, 0.5); } +typedef tuple RemapRelativeParam; +typedef testing::TestWithParam Imgproc_RemapRelative; + +TEST_P(Imgproc_RemapRelative, validity) +{ + int srcType = CV_MAKE_TYPE(get<0>(GetParam()), get<1>(GetParam())); + int interpolation = get<2>(GetParam()); + int borderType = get<3>(GetParam()); + bool useFixedPoint = get<4>(GetParam()); + + const int nChannels = CV_MAT_CN(srcType); + const cv::Size size(127, 61); + cv::Mat data64FC1(1, size.area()*nChannels, CV_64FC1); + data64FC1.forEach([&](double& pixel, const int* position) {pixel = static_cast(position[1]);}); + + cv::Mat src; + data64FC1.reshape(nChannels, size.height).convertTo(src, srcType); + + cv::Mat mapRelativeX32F(size, CV_32FC1); + mapRelativeX32F.setTo(cv::Scalar::all(-0.33)); + + cv::Mat mapRelativeY32F(size, CV_32FC1); + mapRelativeY32F.setTo(cv::Scalar::all(-0.33)); + + cv::Mat mapAbsoluteX32F = mapRelativeX32F.clone(); + mapAbsoluteX32F.forEach([&](float& pixel, const int* position) { + pixel += static_cast(position[1]); + }); + + cv::Mat mapAbsoluteY32F = mapRelativeY32F.clone(); + mapAbsoluteY32F.forEach([&](float& pixel, const int* position) { + pixel += static_cast(position[0]); + }); + + cv::Mat mapAbsoluteX16S; + cv::Mat mapAbsoluteY16S; + cv::Mat mapRelativeX16S; + cv::Mat mapRelativeY16S; + if (useFixedPoint) + { + const bool nninterpolation = (interpolation == cv::INTER_NEAREST) || (interpolation == cv::INTER_NEAREST_EXACT); + cv::convertMaps(mapAbsoluteX32F, mapAbsoluteY32F, mapAbsoluteX16S, mapAbsoluteY16S, CV_16SC2, nninterpolation); + cv::convertMaps(mapRelativeX32F, mapRelativeY32F, mapRelativeX16S, mapRelativeY16S, CV_16SC2, nninterpolation); + } + + cv::Mat dstAbsolute; + cv::Mat dstRelative; + if (useFixedPoint) + { + cv::remap(src, dstAbsolute, mapAbsoluteX16S, mapAbsoluteY16S, interpolation, borderType); + cv::remap(src, dstRelative, mapRelativeX16S, mapRelativeY16S, interpolation | WARP_RELATIVE_MAP, borderType); + } + else + { + cv::remap(src, dstAbsolute, mapAbsoluteX32F, mapAbsoluteY32F, interpolation, borderType); + cv::remap(src, dstRelative, mapRelativeX32F, mapRelativeY32F, interpolation | WARP_RELATIVE_MAP, borderType); + } + + EXPECT_EQ(cvtest::norm(dstAbsolute, dstRelative, NORM_INF), 0); +}; + +INSTANTIATE_TEST_CASE_P(ImgProc, Imgproc_RemapRelative, testing::Combine( + testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), + testing::Values(1, 3, 4), + testing::Values((int)INTER_NEAREST, (int)INTER_LINEAR, (int)INTER_CUBIC, (int)INTER_LANCZOS4), + testing::Values((int)BORDER_CONSTANT, (int)BORDER_REPLICATE, (int)BORDER_WRAP, (int)BORDER_REFLECT, (int)BORDER_REFLECT_101), + testing::Values(false, true))); //////////////////////////////////////////////////////////////////////////