diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 002916fc6e..16af2b969c 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -2508,6 +2508,7 @@ optional flag #WARP_INVERSE_MAP, that sets M as the inverse transformation ( \f$\texttt{dst}\rightarrow\texttt{src}\f$ ). @param borderMode pixel extrapolation method (#BORDER_CONSTANT or #BORDER_REPLICATE). @param borderValue value used in case of a constant border; by default, it equals 0. +@param hint Implementation modfication flags. See #AlgorithmHint @sa warpAffine, resize, remap, getRectSubPix, perspectiveTransform */ @@ -2515,7 +2516,8 @@ CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, - const Scalar& borderValue = Scalar()); + const Scalar& borderValue = Scalar(), + AlgorithmHint hint = cv::ALGO_HINT_DEFAULT); /** @brief Applies a generic geometrical transformation to an image. diff --git a/modules/imgproc/perf/perf_warp.cpp b/modules/imgproc/perf/perf_warp.cpp index 615525ba8a..35fd4027f7 100644 --- a/modules/imgproc/perf/perf_warp.cpp +++ b/modules/imgproc/perf/perf_warp.cpp @@ -13,7 +13,7 @@ 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 > TestWarpPerspective; typedef TestBaseWithParam< tuple > TestWarpPerspectiveNear_t; typedef TestBaseWithParam< tuple > TestRemap; @@ -67,22 +67,37 @@ PERF_TEST_P( TestWarpPerspective, WarpPerspective, Values( szVGA, sz720p, sz1080p ), InterType::all(), BorderMode::all(), - Values(1, 3, 4) + Values(CV_8UC3, CV_16UC3, CV_32FC3, CV_8UC1, CV_16UC1, CV_32FC1, CV_8UC4, CV_16UC4, CV_32FC4) ) ) { Size sz, szSrc(512, 512); - int borderMode, interType, channels; + int type, borderMode, interType; sz = get<0>(GetParam()); interType = get<1>(GetParam()); borderMode = get<2>(GetParam()); - channels = get<3>(GetParam()); - + type = get<3>(GetParam()); Scalar borderColor = Scalar::all(150); - Mat src(szSrc, CV_8UC(channels)), dst(sz, CV_8UC(channels)); - cvtest::fillGradient(src); - if(borderMode == BORDER_CONSTANT) cvtest::smoothBorder(src, borderColor, 1); + Mat src(szSrc, type), dst(sz, type); + switch (src.depth()) { + case CV_8U: { + cvtest::fillGradient(src); + if(borderMode == BORDER_CONSTANT) cvtest::smoothBorder(src, borderColor, 1); + break; + } + case CV_16U: { + cvtest::fillGradient(src); + if(borderMode == BORDER_CONSTANT) cvtest::smoothBorder(src, borderColor, 1); + break; + } + case CV_32F: { + cvtest::fillGradient(src); + if(borderMode == BORDER_CONSTANT) cvtest::smoothBorder(src, borderColor, 1); + break; + } + } + Mat rotMat = getRotationMatrix2D(Point2f(src.cols/2.f, src.rows/2.f), 30., 2.2); Mat warpMat(3, 3, CV_64FC1); for(int r=0; r<2; r++) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index dc28a8d927..c1dd68d749 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -2278,6 +2278,7 @@ static bool ocl_warpTransform_cols4(InputArray _src, OutputArray _dst, InputArra if ( !dev.isIntel() || !(type == CV_8UC1) || !(dtype == CV_8UC1) || !(_dst.cols() % 4 == 0) || + (op_type == OCL_OP_PERSPECTIVE && interpolation == INTER_LINEAR && (cn == 1 || cn == 3 || cn == 4)) || !(borderType == cv::BORDER_CONSTANT && (interpolation == cv::INTER_NEAREST || interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC))) return false; @@ -2370,7 +2371,9 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, const char * const kernelName = op_type == OCL_OP_AFFINE ? "warpAffine" : "warpPerspective"; int scalarcn = cn == 3 ? 4 : cn; - bool is32f = !dev.isAMD() && (interpolation == INTER_CUBIC || interpolation == INTER_LINEAR) && op_type == OCL_OP_AFFINE; + bool is32f = op_type == OCL_OP_AFFINE ? + /* Affine*/ !dev.isAMD() && (interpolation == INTER_CUBIC || interpolation == INTER_LINEAR) : + /* Perspective*/ interpolation == INTER_LINEAR; int wdepth = interpolation == INTER_NEAREST ? depth : std::max(is32f ? CV_32F : CV_32S, depth); int sctype = CV_MAKETYPE(wdepth, scalarcn); @@ -2385,8 +2388,7 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, ocl::typeToStr(CV_MAT_DEPTH(type)), ocl::typeToStr(sctype), cn, rowsPerWI); } - else - { + else { char cvt[2][50]; opts = format("-D INTER_%s -D T=%s -D T1=%s -D ST=%s -D WT=%s -D SRC_DEPTH=%d" " -D CONVERT_TO_WT=%s -D CONVERT_TO_T=%s%s -D CT=%s -D CN=%d -D ROWS_PER_WI=%d", @@ -3256,12 +3258,58 @@ private: namespace hal { -void warpPerspective(int src_type, +static void warpPerspective(int src_type, const uchar * src_data, size_t src_step, int src_width, int src_height, uchar * dst_data, size_t dst_step, int dst_width, int dst_height, - const double M[9], int interpolation, int borderType, const double borderValue[4]) + const double M[9], int interpolation, int borderType, const double borderValue[4], AlgorithmHint hint) { CALL_HAL(warpPerspective, cv_hal_warpPerspective, src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, M, interpolation, borderType, borderValue); + + if (interpolation == INTER_LINEAR) { + switch (src_type) { + case CV_8UC1: { + if (hint == cv::ALGO_HINT_APPROX) { + CV_CPU_DISPATCH(warpPerspectiveLinearApproxInvoker_8UC1, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } else { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_8UC1, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + } + case CV_8UC3: { + if (hint == cv::ALGO_HINT_APPROX) { + CV_CPU_DISPATCH(warpPerspectiveLinearApproxInvoker_8UC3, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } else { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_8UC3, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + } + case CV_8UC4: { + if (hint == cv::ALGO_HINT_APPROX) { + CV_CPU_DISPATCH(warpPerspectiveLinearApproxInvoker_8UC4, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } else { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_8UC4, (src_data, src_step, src_height, src_width, dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + } + case CV_16UC1: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_16UC1, ((const uint16_t*)src_data, src_step, src_height, src_width, (uint16_t*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + case CV_16UC3: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_16UC3, ((const uint16_t*)src_data, src_step, src_height, src_width, (uint16_t*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + case CV_16UC4: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_16UC4, ((const uint16_t*)src_data, src_step, src_height, src_width, (uint16_t*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + case CV_32FC1: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_32FC1, ((const float*)src_data, src_step, src_height, src_width, (float*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + case CV_32FC3: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_32FC3, ((const float*)src_data, src_step, src_height, src_width, (float*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + case CV_32FC4: { + CV_CPU_DISPATCH(warpPerspectiveLinearInvoker_32FC4, ((const float*)src_data, src_step, src_height, src_width, (float*)dst_data, dst_step, dst_height, dst_width, M, borderType, borderValue), CV_CPU_DISPATCH_MODES_ALL); + } + // no default + } + } + Mat src(Size(src_width, src_height), src_type, const_cast(src_data), src_step); Mat dst(Size(dst_width, dst_height), src_type, dst_data, dst_step); @@ -3342,10 +3390,14 @@ void warpPerspectiveBlockline(const double *M, short* xy, short* alpha, double X } // cv:: void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0, - Size dsize, int flags, int borderType, const Scalar& borderValue ) + Size dsize, int flags, int borderType, const Scalar& borderValue, + AlgorithmHint hint ) { CV_INSTRUMENT_REGION(); + if (hint == cv::ALGO_HINT_DEFAULT) + hint = cv::getDefaultAlgorithmHint(); + CV_Assert( _src.total() > 0 ); CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && @@ -3436,7 +3488,7 @@ void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0, invert(matM, matM); hal::warpPerspective(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, - matM.ptr(), interpolation, borderType, borderValue.val); + matM.ptr(), interpolation, borderType, borderValue.val, hint); } diff --git a/modules/imgproc/src/opencl/warp_perspective.cl b/modules/imgproc/src/opencl/warp_perspective.cl index 06bc2bd7fa..36a8cdc11f 100644 --- a/modules/imgproc/src/opencl/warp_perspective.cl +++ b/modules/imgproc/src/opencl/warp_perspective.cl @@ -122,16 +122,14 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s if (dx < dst_cols && dy < dst_rows) { - CT X0 = M[0] * dx + M[1] * dy + M[2]; - CT Y0 = M[3] * dx + M[4] * dy + M[5]; - CT W = M[6] * dx + M[7] * dy + M[8]; - W = W != 0.0f ? INTER_TAB_SIZE / W : 0.0f; - int X = rint(X0 * W), Y = rint(Y0 * W); + float W = M[6] * dx + M[7] * dy + M[8]; + float X0 = (M[0] * dx + M[1] * dy + M[2]) / W; + float Y0 = (M[3] * dx + M[4] * dy + M[5]) / W; - short sx = convert_short_sat(X >> INTER_BITS); - short sy = convert_short_sat(Y >> INTER_BITS); - short ay = (short)(Y & (INTER_TAB_SIZE - 1)); - short ax = (short)(X & (INTER_TAB_SIZE - 1)); + int sx = convert_short_rtn(X0); + int sy = convert_short_rtn(Y0); + float ay = Y0 - (CT)sy; + float ax = X0 - (CT)sx; WT v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? CONVERT_TO_WT(loadpix(srcptr + mad24(sy, src_step, src_offset + sx * pixsize))) : scalar; @@ -142,24 +140,12 @@ __kernel void warpPerspective(__global const uchar * srcptr, int src_step, int s WT v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? CONVERT_TO_WT(loadpix(srcptr + mad24(sy+1, src_step, src_offset + (sx+1) * pixsize))) : scalar; - float taby = 1.f/INTER_TAB_SIZE*ay; - float tabx = 1.f/INTER_TAB_SIZE*ax; - int dst_index = mad24(dy, dst_step, dst_offset + dx * pixsize); -#if SRC_DEPTH <= 4 - int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); - int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); - int itab2 = convert_short_sat_rte( taby*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); - int itab3 = convert_short_sat_rte( taby*tabx * INTER_REMAP_COEF_SCALE ); - - WT val = v0 * itab0 + v1 * itab1 + v2 * itab2 + v3 * itab3; - storepix(CONVERT_TO_T((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); -#else - float tabx2 = 1.0f - tabx, taby2 = 1.0f - taby; - WT val = v0 * tabx2 * taby2 + v1 * tabx * taby2 + v2 * tabx2 * taby + v3 * tabx * taby; - storepix(CONVERT_TO_T(val), dstptr + dst_index); -#endif + v0 = fma(v1 - v0, ax, v0); + v2 = fma(v3 - v2, ax, v2); + v0 = fma(v2 - v0, ay, v0); + storepix(CONVERT_TO_T(v0), dstptr + dst_index); } } diff --git a/modules/imgproc/src/warp_common.vector.hpp b/modules/imgproc/src/warp_common.vector.hpp index 0dcd044473..2470eb0a72 100644 --- a/modules/imgproc/src/warp_common.vector.hpp +++ b/modules/imgproc/src/warp_common.vector.hpp @@ -57,6 +57,59 @@ #define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(CN, DEPTH) \ CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_##DEPTH(CN) +// Shuffle (ARM NEON) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_LOAD() \ + uint8x8x4_t t00 = { \ + vld1_u8(src + addr[0]), \ + vld1_u8(src + addr[1]), \ + vld1_u8(src + addr[2]), \ + vld1_u8(src + addr[3]) \ + }; \ + uint8x8x4_t t01 = { \ + vld1_u8(src + addr[4]), \ + vld1_u8(src + addr[5]), \ + vld1_u8(src + addr[6]), \ + vld1_u8(src + addr[7]) \ + }; \ + uint8x8x4_t t10 = { \ + vld1_u8(src + addr[0] + srcstep), \ + vld1_u8(src + addr[1] + srcstep), \ + vld1_u8(src + addr[2] + srcstep), \ + vld1_u8(src + addr[3] + srcstep) \ + }; \ + uint8x8x4_t t11 = { \ + vld1_u8(src + addr[4] + srcstep), \ + vld1_u8(src + addr[5] + srcstep), \ + vld1_u8(src + addr[6] + srcstep), \ + vld1_u8(src + addr[7] + srcstep) \ + }; \ + uint32x2_t p00_, p01_, p10_, p11_; +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(coords, cn) \ + p00_ = vreinterpret_u32_u8(vtbl4_u8(t00, coords)); \ + p01_ = vreinterpret_u32_u8(vtbl4_u8(t01, coords)); \ + p10_ = vreinterpret_u32_u8(vtbl4_u8(t10, coords)); \ + p11_ = vreinterpret_u32_u8(vtbl4_u8(t11, coords)); \ + p00##cn = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); \ + p01##cn = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); \ + p10##cn = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); \ + p11##cn = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_C1() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_LOAD() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(grays, g) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_C3() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_LOAD() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(reds, r) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(greens, g) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(blues, b) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_C4() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_LOAD() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(reds, r) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(greens, g) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(blues, b) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_TRN(alphas, a) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(CN) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8_##CN() + // Shuffle (not all pixels within image) #define CV_WARP_LINEAR_VECTOR_SHUFFLE_STORE_CONSTANT_BORDER_8UC1() \ @@ -190,6 +243,25 @@ CV_WARP_LINEAR_VECTOR_FETCH_PIXEL_##CN(1, 1, uf*3); \ } +// Shuffle (not all pixels within image) (ARM NEON) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(cn, offset)\ + p00##cn = vld1_u8(pixbuf + offset); \ + p01##cn = vld1_u8(pixbuf + offset + 8); \ + p10##cn = vld1_u8(pixbuf + offset + 16); \ + p11##cn = vld1_u8(pixbuf + offset + 24); +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_C1() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(g, 0) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_C3() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(r, 0) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(g, 32) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(b, 64) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_C4() \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(r, 0) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(g, 32) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(b, 64) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_LOAD(a, 96) +#define CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(CN) \ + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8_##CN() // Load pixels for linear interpolation (uint8_t -> int16_t) #define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16(cn, i) \ @@ -211,6 +283,26 @@ #define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(CN) \ CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_##CN(); +// Load pixels for linear interpolation (uint8_t -> int16_t) (ARM NEON) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(cn) \ + v_int16 f00##cn = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00##cn))), \ + f01##cn = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01##cn))), \ + f10##cn = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10##cn))), \ + f11##cn = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11##cn))); +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(g) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(r) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(g) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(b) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(r) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(g) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(b) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8S16_NEON(a) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(CN) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON_##CN(); + // Load pixels for linear interpolation (uint16_t -> uint16_t) #define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16(cn, i) \ v_uint16 f00##cn = vx_load(pixbuf + uf * i), \ @@ -232,44 +324,44 @@ CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16_##CN(); // Load pixels for linear interpolation (int16_t -> float) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(cn) \ +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(cn) \ v_float32 f00##cn##l = v_cvt_f32(v_expand_low(f00##cn)), f00##cn##h = v_cvt_f32(v_expand_high(f00##cn)), \ f01##cn##l = v_cvt_f32(v_expand_low(f01##cn)), f01##cn##h = v_cvt_f32(v_expand_high(f01##cn)), \ f10##cn##l = v_cvt_f32(v_expand_low(f10##cn)), f10##cn##h = v_cvt_f32(v_expand_high(f10##cn)), \ f11##cn##l = v_cvt_f32(v_expand_low(f11##cn)), f11##cn##h = v_cvt_f32(v_expand_high(f11##cn)); -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32_C1() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(g) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32_C3() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(r) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(g) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(b) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32_C4() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(r) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(g) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(b) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_S16F32(a) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32(CN) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32_##CN() +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(g) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(b) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(b) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_S16F32(a) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(CN) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32_##CN() // Load pixels for linear interpolation (uint16_t -> float) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(cn) \ +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(cn) \ v_float32 f00##cn##l = v_cvt_f32(v_reinterpret_as_s32(v_expand_low(f00##cn))), f00##cn##h = v_cvt_f32(v_reinterpret_as_s32(v_expand_high(f00##cn))), \ f01##cn##l = v_cvt_f32(v_reinterpret_as_s32(v_expand_low(f01##cn))), f01##cn##h = v_cvt_f32(v_reinterpret_as_s32(v_expand_high(f01##cn))), \ f10##cn##l = v_cvt_f32(v_reinterpret_as_s32(v_expand_low(f10##cn))), f10##cn##h = v_cvt_f32(v_reinterpret_as_s32(v_expand_high(f10##cn))), \ f11##cn##l = v_cvt_f32(v_reinterpret_as_s32(v_expand_low(f11##cn))), f11##cn##h = v_cvt_f32(v_reinterpret_as_s32(v_expand_high(f11##cn))); -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32_C1() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(g) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32_C3() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(r) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(g) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(b) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32_C4() \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(r) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(g) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(b) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U16F32(a) -#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32(CN) \ - CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32_##CN() +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(g) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(b) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(b) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_CN_U16F32(a) +#define CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(CN) \ + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32_##CN() // Load pixels for linear interpolation (float -> float) #define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_F32(cn, i) \ @@ -291,8 +383,27 @@ #define CV_WARP_LINEAR_VECTOR_INTER_LOAD_F32(CN) \ CV_WARP_LINEAR_VECTOR_INTER_LOAD_F32_##CN() +// Load pixels for linear interpolation (uint8_t -> float16) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(cn) \ + v_float16 f00##cn = v_float16(vcvtq_f16_u16(vmovl_u8(p00##cn))), \ + f01##cn = v_float16(vcvtq_f16_u16(vmovl_u8(p01##cn))), \ + f10##cn = v_float16(vcvtq_f16_u16(vmovl_u8(p10##cn))), \ + f11##cn = v_float16(vcvtq_f16_u16(vmovl_u8(p11##cn))); +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(g) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(b) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(b) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_CN_U8F16(a) +#define CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(CN) \ + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16_##CN() -// Linear interpolation calculation +// Linear interpolation calculation (F32) #define CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F32(cn) \ f00##cn##l = v_fma(alphal, v_sub(f01##cn##l, f00##cn##l), f00##cn##l); f00##cn##h = v_fma(alphah, v_sub(f01##cn##h, f00##cn##h), f00##cn##h); \ f10##cn##l = v_fma(alphal, v_sub(f11##cn##l, f10##cn##l), f10##cn##l); f10##cn##h = v_fma(alphah, v_sub(f11##cn##h, f10##cn##h), f10##cn##h); @@ -328,6 +439,42 @@ CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F32_##CN() \ CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F32_##CN() +// Linear interpolation calculation (F16) +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(cn) \ + f00##cn = v_fma(alpha, v_sub(f01##cn, f00##cn), f00##cn); \ + f10##cn = v_fma(alpha, v_sub(f11##cn, f10##cn), f10##cn); +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(g) +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(b) +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(b) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16(a) + +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(cn) \ + f00##cn = v_fma(beta, v_sub(f10##cn, f00##cn), f00##cn); +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16_C1() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(g) +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16_C3() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(b) +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16_C4() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(r) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(g) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(b) \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16(a) + +#define CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(CN) \ + v_float16 alpha = v_cvt_f16(src_x0, src_x1), \ + beta = v_cvt_f16(src_y0, src_y1); \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_ALPHA_F16_##CN() \ + CV_WARP_LINEAR_VECTOR_INTER_CALC_BETA_F16_##CN() + // Store #define CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U8_C1() \ @@ -385,3 +532,26 @@ v_store_interleave(dstptr + x*4 + vlanes_32*4, f00rh, f00gh, f00bh, f00ah); #define CV_WARP_LINEAR_VECTOR_INTER_STORE_F32F32(CN) \ CV_WARP_LINEAR_VECTOR_INTER_STORE_F32F32_##CN() + +#define CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8_C1() \ + uint8x8_t result = { \ + vqmovun_s16(vcvtnq_s16_f16(f00g.val)), \ + }; \ + vst1_u8(dstptr + x, result); +#define CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8_C3() \ + uint8x8x3_t result = { \ + vqmovun_s16(vcvtnq_s16_f16(f00r.val)), \ + vqmovun_s16(vcvtnq_s16_f16(f00g.val)), \ + vqmovun_s16(vcvtnq_s16_f16(f00b.val)), \ + }; \ + vst3_u8(dstptr + x*3, result); +#define CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8_C4() \ + uint8x8x4_t result = { \ + vqmovun_s16(vcvtnq_s16_f16(f00r.val)), \ + vqmovun_s16(vcvtnq_s16_f16(f00g.val)), \ + vqmovun_s16(vcvtnq_s16_f16(f00b.val)), \ + vqmovun_s16(vcvtnq_s16_f16(f00a.val)), \ + }; \ + vst4_u8(dstptr + x*4, result); +#define CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(CN) \ + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8_##CN() diff --git a/modules/imgproc/src/warp_kernels.simd.hpp b/modules/imgproc/src/warp_kernels.simd.hpp index 1d9916491c..e3fea8059c 100644 --- a/modules/imgproc/src/warp_kernels.simd.hpp +++ b/modules/imgproc/src/warp_kernels.simd.hpp @@ -7,7 +7,33 @@ #include "warp_common.hpp" #include "opencv2/core/hal/intrin.hpp" -#define CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD() \ +#define CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1() \ + v_float32 dst_x0 = vx_load(start_indices.data()); \ + v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); \ + v_float32 M0 = vx_setall_f32(M[0]), \ + M3 = vx_setall_f32(M[3]); \ + v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), \ + M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); +#define CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1() \ + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1() \ + v_float32 M6 = vx_setall_f32(M[6]); \ + v_float32 M_w = vx_setall_f32(static_cast(y * M[7] + M[8])); + +#define CV_WARP_LINEAR_VECTOR_GET_ADDR_C1() \ + v_int32 addr_0 = v_fma(v_srcstep, src_iy0, src_ix0), \ + addr_1 = v_fma(v_srcstep, src_iy1, src_ix1); +#define CV_WARP_LINEAR_VECTOR_GET_ADDR_C3() \ + v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, three)), \ + addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, three)); +#define CV_WARP_LINEAR_VECTOR_GET_ADDR_C4() \ + v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, four)), \ + addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, four)); +#define CV_WARP_LINEAR_VECTOR_GET_ADDR(CN) \ + CV_WARP_LINEAR_VECTOR_GET_ADDR_##CN() \ + vx_store(addr, addr_0); \ + vx_store(addr + vlanes_32, addr_1); + +#define CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(CN) \ v_float32 src_x0 = v_fma(M0, dst_x0, M_x), \ src_y0 = v_fma(M3, dst_x0, M_y), \ src_x1 = v_fma(M0, dst_x1, M_x), \ @@ -26,7 +52,36 @@ src_x0 = v_sub(src_x0, v_cvt_f32(src_ix0)); \ src_y0 = v_sub(src_y0, v_cvt_f32(src_iy0)); \ src_x1 = v_sub(src_x1, v_cvt_f32(src_ix1)); \ - src_y1 = v_sub(src_y1, v_cvt_f32(src_iy1)); + src_y1 = v_sub(src_y1, v_cvt_f32(src_iy1)); \ + CV_WARP_LINEAR_VECTOR_GET_ADDR(CN); + +#define CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(CN) \ + v_float32 src_x0 = v_fma(M0, dst_x0, M_x), \ + src_y0 = v_fma(M3, dst_x0, M_y), \ + src_w0 = v_fma(M6, dst_x0, M_w), \ + src_x1 = v_fma(M0, dst_x1, M_x), \ + src_y1 = v_fma(M3, dst_x1, M_y), \ + src_w1 = v_fma(M6, dst_x1, M_w); \ + src_x0 = v_div(src_x0, src_w0); \ + src_y0 = v_div(src_y0, src_w0); \ + src_x1 = v_div(src_x1, src_w1); \ + src_y1 = v_div(src_y1, src_w1); \ + dst_x0 = v_add(dst_x0, delta); \ + dst_x1 = v_add(dst_x1, delta); \ + v_int32 src_ix0 = v_floor(src_x0), \ + src_iy0 = v_floor(src_y0), \ + src_ix1 = v_floor(src_x1), \ + src_iy1 = v_floor(src_y1); \ + v_uint32 mask_0 = v_lt(v_reinterpret_as_u32(src_ix0), inner_scols), \ + mask_1 = v_lt(v_reinterpret_as_u32(src_ix1), inner_scols); \ + mask_0 = v_and(mask_0, v_lt(v_reinterpret_as_u32(src_iy0), inner_srows)); \ + mask_1 = v_and(mask_1, v_lt(v_reinterpret_as_u32(src_iy1), inner_srows)); \ + v_uint16 inner_mask = v_pack(mask_0, mask_1); \ + src_x0 = v_sub(src_x0, v_cvt_f32(src_ix0)); \ + src_y0 = v_sub(src_y0, v_cvt_f32(src_iy0)); \ + src_x1 = v_sub(src_x1, v_cvt_f32(src_ix1)); \ + src_y1 = v_sub(src_y1, v_cvt_f32(src_iy1)); \ + CV_WARP_LINEAR_VECTOR_GET_ADDR(CN); namespace cv{ CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN @@ -58,7 +113,7 @@ void warpAffineLinearInvoker_32FC3(const float *src_data, size_t src_step, int s void warpAffineLinearInvoker_32FC4(const float *src_data, size_t src_step, int src_rows, int src_cols, float *dst_data, size_t dst_step, int dst_rows, int dst_cols, const double M[6], int border_type, const double border_value[4]); - +// Approximate branch that uses FP16 intrinsics if possible void warpAffineLinearApproxInvoker_8UC1(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, const double M[6], int border_type, const double border_value[4]); @@ -69,6 +124,44 @@ void warpAffineLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, const double M[6], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_8UC3(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_8UC4(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_16UC1(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_16UC3(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_16UC4(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_32FC1(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_32FC3(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearInvoker_32FC4(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +// Approximate branch that uses FP16 intrinsics if possible +void warpPerspectiveLinearApproxInvoker_8UC1(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearApproxInvoker_8UC3(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); +void warpPerspectiveLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double M[9], int border_type, const double border_value[4]); + #ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY namespace { @@ -157,7 +250,7 @@ void warpAffineLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int } v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - uint8x8_t gray = {0, 8, 16, 24, 1, 9, 17, 25}; + uint8x8_t grays = {0, 8, 16, 24, 1, 9, 17, 25}; #endif #endif @@ -166,22 +259,12 @@ void warpAffineLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, src_ix0), - addr_1 = v_fma(v_srcstep, src_iy1, src_ix1); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 uint8x8_t p00g, p01g, p10g, p11g; @@ -189,45 +272,7 @@ void warpAffineLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - uint8x8x4_t t00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t t01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t t10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t t11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(t00, gray)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(t01, gray)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(t10, gray)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(t11, gray)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C1); #else CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 8U); #endif @@ -235,22 +280,16 @@ void warpAffineLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 8U); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - p00g = vld1_u8(pixbuf); - p01g = vld1_u8(pixbuf + 8); - p10g = vld1_u8(pixbuf + 16); - p11g = vld1_u8(pixbuf + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C1); #endif } #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 - v_int16 f00g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00g))), - f01g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01g))), - f10g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10g))), - f11g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11g))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C1); #else CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C1); #endif - CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32(C1); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C1); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C1); @@ -350,22 +389,12 @@ void warpAffineLinearInvoker_8UC3(const uint8_t *src_data, size_t src_step, int int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, three)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, three)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 uint8x8_t p00r, p01r, p10r, p11r, @@ -375,65 +404,7 @@ void warpAffineLinearInvoker_8UC3(const uint8_t *src_data, size_t src_step, int if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - uint8x8x4_t p00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t p01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t p10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t p11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, reds)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, reds)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, reds)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, reds)); - - p00r = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01r = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10r = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11r = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, greens)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, greens)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, greens)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, greens)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, blues)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, blues)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, blues)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, blues)); - - p00b = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01b = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10b = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11b = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C3); #else CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 8U); #endif @@ -441,40 +412,16 @@ void warpAffineLinearInvoker_8UC3(const uint8_t *src_data, size_t src_step, int CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 8U); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - p00r = vld1_u8(pixbuf); - p01r = vld1_u8(pixbuf + 8); - p10r = vld1_u8(pixbuf + 16); - p11r = vld1_u8(pixbuf + 24); - - p00g = vld1_u8(pixbuf + 32); - p01g = vld1_u8(pixbuf + 32 + 8); - p10g = vld1_u8(pixbuf + 32 + 16); - p11g = vld1_u8(pixbuf + 32 + 24); - - p00b = vld1_u8(pixbuf + 64); - p01b = vld1_u8(pixbuf + 64 + 8); - p10b = vld1_u8(pixbuf + 64 + 16); - p11b = vld1_u8(pixbuf + 64 + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C3); #endif } #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 - v_int16 f00r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00r))), - f01r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01r))), - f10r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10r))), - f11r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11r))); - v_int16 f00g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00g))), - f01g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01g))), - f10g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10g))), - f11g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11g))); - v_int16 f00b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00b))), - f01b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01b))), - f10b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10b))), - f11b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11b))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C3); #else CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C3); #endif - CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32(C3); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C3); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C3); @@ -579,22 +526,12 @@ void warpAffineLinearInvoker_8UC4(const uint8_t *src_data, size_t src_step, int int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, four)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, four)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 uint8x8_t p00r, p01r, p10r, p11r, @@ -605,75 +542,7 @@ void warpAffineLinearInvoker_8UC4(const uint8_t *src_data, size_t src_step, int if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - uint8x8x4_t p00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t p01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t p10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t p11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, reds)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, reds)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, reds)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, reds)); - - p00r = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01r = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10r = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11r = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, greens)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, greens)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, greens)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, greens)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, blues)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, blues)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, blues)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, blues)); - - p00b = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01b = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10b = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11b = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, alphas)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, alphas)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, alphas)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, alphas)); - - p00a = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01a = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10a = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11a = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C4) #else CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 8U); #endif @@ -681,50 +550,17 @@ void warpAffineLinearInvoker_8UC4(const uint8_t *src_data, size_t src_step, int CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 8U); #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 - p00r = vld1_u8(pixbuf); - p01r = vld1_u8(pixbuf + 8); - p10r = vld1_u8(pixbuf + 16); - p11r = vld1_u8(pixbuf + 24); - - p00g = vld1_u8(pixbuf + 32); - p01g = vld1_u8(pixbuf + 32 + 8); - p10g = vld1_u8(pixbuf + 32 + 16); - p11g = vld1_u8(pixbuf + 32 + 24); - - p00b = vld1_u8(pixbuf + 64); - p01b = vld1_u8(pixbuf + 64 + 8); - p10b = vld1_u8(pixbuf + 64 + 16); - p11b = vld1_u8(pixbuf + 64 + 24); - - p00a = vld1_u8(pixbuf + 96); - p01a = vld1_u8(pixbuf + 96 + 8); - p10a = vld1_u8(pixbuf + 96 + 16); - p11a = vld1_u8(pixbuf + 96 + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C4); #endif } #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 - v_int16 f00r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00r))), - f01r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01r))), - f10r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10r))), - f11r = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11r))); - v_int16 f00g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00g))), - f01g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01g))), - f10g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10g))), - f11g = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11g))); - v_int16 f00b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00b))), - f01b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01b))), - f10b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10b))), - f11b = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11b))); - v_int16 f00a = v_reinterpret_as_s16(v_uint16(vmovl_u8(p00a))), - f01a = v_reinterpret_as_s16(v_uint16(vmovl_u8(p01a))), - f10a = v_reinterpret_as_s16(v_uint16(vmovl_u8(p10a))), - f11a = v_reinterpret_as_s16(v_uint16(vmovl_u8(p11a))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C4); #else CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C4); #endif - CV_WARP_LINEAR_VECTOR_INTER_LOAD_S16F32(C4); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C4); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C4); @@ -814,22 +650,12 @@ void warpAffineLinearInvoker_16UC1(const uint16_t *src_data, size_t src_step, in int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, src_ix0), - addr_1 = v_fma(v_srcstep, src_iy1, src_ix1); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 16U); @@ -839,7 +665,7 @@ void warpAffineLinearInvoker_16UC1(const uint16_t *src_data, size_t src_step, in CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C1); - CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32(C1); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C1); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C1); @@ -933,22 +759,12 @@ void warpAffineLinearInvoker_16UC3(const uint16_t *src_data, size_t src_step, in int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, three)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, three)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 16U); @@ -958,7 +774,7 @@ void warpAffineLinearInvoker_16UC3(const uint16_t *src_data, size_t src_step, in CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C3); - CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32(C3); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C3); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C3); @@ -1055,22 +871,12 @@ void warpAffineLinearInvoker_16UC4(const uint16_t *src_data, size_t src_step, in int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, four)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, four)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 16U); @@ -1080,7 +886,7 @@ void warpAffineLinearInvoker_16UC4(const uint16_t *src_data, size_t src_step, in CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C4); - CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16F32(C4); + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C4); CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C4); @@ -1171,22 +977,12 @@ void warpAffineLinearInvoker_32FC1(const float *src_data, size_t src_step, int s int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, src_ix0), - addr_1 = v_fma(v_srcstep, src_iy1, src_ix1); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 32F); @@ -1290,22 +1086,12 @@ void warpAffineLinearInvoker_32FC3(const float *src_data, size_t src_step, int s int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, three)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, three)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 32F); @@ -1413,22 +1199,12 @@ void warpAffineLinearInvoker_32FC4(const float *src_data, size_t src_step, int s int x = 0; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, four)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, four)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 32F); @@ -1519,97 +1295,34 @@ void warpAffineLinearApproxInvoker_8UC1(const uint8_t *src_data, size_t src_step bvalbuf[i] = bval[0]; } v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); - uint8x8_t gray = {0, 8, 16, 24, 1, 9, 17, 25}; + uint8x8_t grays = {0, 8, 16, 24, 1, 9, 17, 25}; for (int y = r.start; y < r.end; y++) { uint8_t* dstptr = dst + y*dststep; int x = 0; - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, src_ix0), - addr_1 = v_fma(v_srcstep, src_iy1, src_ix1); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); uint8x8_t p00g, p01g, p10g, p11g; if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image - uint8x8x4_t t00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t t01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t t10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t t11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(t00, gray)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(t01, gray)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(t10, gray)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(t11, gray)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C1); } else { CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 8U); - p00g = vld1_u8(pixbuf); - p01g = vld1_u8(pixbuf + 8); - p10g = vld1_u8(pixbuf + 16); - p11g = vld1_u8(pixbuf + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C1); } - v_float16 f00 = v_float16(vcvtq_f16_u16(vmovl_u8(p00g))); - v_float16 f01 = v_float16(vcvtq_f16_u16(vmovl_u8(p01g))); - v_float16 f10 = v_float16(vcvtq_f16_u16(vmovl_u8(p10g))); - v_float16 f11 = v_float16(vcvtq_f16_u16(vmovl_u8(p11g))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C1); - v_float16 alpha = v_cvt_f16(src_x0, src_x1), - beta = v_cvt_f16(src_y0, src_y1); + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C1); - f00 = v_fma(alpha, v_sub(f01, f00), f00); - f10 = v_fma(alpha, v_sub(f11, f10), f10); - f00 = v_fma(beta, v_sub(f10, f00), f00); - - uint8x8_t result = { - vqmovun_s16(vcvtnq_s16_f16(f00.val)), - }; - - vst1_u8(dstptr + x, result); + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C1); } for (; x < dstcols; x++) { @@ -1705,143 +1418,30 @@ void warpAffineLinearApproxInvoker_8UC3(const uint8_t *src_data, size_t src_step uint8_t* dstptr = dst + y*dststep; int x = 0; - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, three)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, three)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); uint8x8_t p00r, p01r, p10r, p11r, p00g, p01g, p10g, p11g, p00b, p01b, p10b, p11b; if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image - uint8x8x4_t p00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t p01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t p10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t p11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, reds)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, reds)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, reds)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, reds)); - - p00r = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01r = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10r = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11r = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, greens)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, greens)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, greens)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, greens)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, blues)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, blues)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, blues)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, blues)); - - p00b = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01b = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10b = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11b = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C3); } else { CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 8U); - p00r = vld1_u8(pixbuf); - p01r = vld1_u8(pixbuf + 8); - p10r = vld1_u8(pixbuf + 16); - p11r = vld1_u8(pixbuf + 24); - - p00g = vld1_u8(pixbuf + 32); - p01g = vld1_u8(pixbuf + 32 + 8); - p10g = vld1_u8(pixbuf + 32 + 16); - p11g = vld1_u8(pixbuf + 32 + 24); - - p00b = vld1_u8(pixbuf + 64); - p01b = vld1_u8(pixbuf + 64 + 8); - p10b = vld1_u8(pixbuf + 64 + 16); - p11b = vld1_u8(pixbuf + 64 + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C3); } - v_float16 f00r = v_float16(vcvtq_f16_u16(vmovl_u8(p00r))); - v_float16 f01r = v_float16(vcvtq_f16_u16(vmovl_u8(p01r))); - v_float16 f10r = v_float16(vcvtq_f16_u16(vmovl_u8(p10r))); - v_float16 f11r = v_float16(vcvtq_f16_u16(vmovl_u8(p11r))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C3); - v_float16 f00g = v_float16(vcvtq_f16_u16(vmovl_u8(p00g))); - v_float16 f01g = v_float16(vcvtq_f16_u16(vmovl_u8(p01g))); - v_float16 f10g = v_float16(vcvtq_f16_u16(vmovl_u8(p10g))); - v_float16 f11g = v_float16(vcvtq_f16_u16(vmovl_u8(p11g))); + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C3); - v_float16 f00b = v_float16(vcvtq_f16_u16(vmovl_u8(p00b))); - v_float16 f01b = v_float16(vcvtq_f16_u16(vmovl_u8(p01b))); - v_float16 f10b = v_float16(vcvtq_f16_u16(vmovl_u8(p10b))); - v_float16 f11b = v_float16(vcvtq_f16_u16(vmovl_u8(p11b))); - - v_float16 alpha = v_cvt_f16(src_x0, src_x1), - beta = v_cvt_f16(src_y0, src_y1); - - f00r = v_fma(alpha, v_sub(f01r, f00r), f00r); - f10r = v_fma(alpha, v_sub(f11r, f10r), f10r); - - f00g = v_fma(alpha, v_sub(f01g, f00g), f00g); - f10g = v_fma(alpha, v_sub(f11g, f10g), f10g); - - f00b = v_fma(alpha, v_sub(f01b, f00b), f00b); - f10b = v_fma(alpha, v_sub(f11b, f10b), f10b); - - f00r = v_fma(beta, v_sub(f10r, f00r), f00r); - f00g = v_fma(beta, v_sub(f10g, f00g), f00g); - f00b = v_fma(beta, v_sub(f10b, f00b), f00b); - - uint8x8x3_t result = { - vqmovun_s16(vcvtnq_s16_f16(f00r.val)), - vqmovun_s16(vcvtnq_s16_f16(f00g.val)), - vqmovun_s16(vcvtnq_s16_f16(f00b.val)), - }; - vst3_u8(dstptr + x*3, result); + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C3); } for (; x < dstcols; x++) { @@ -1940,22 +1540,12 @@ void warpAffineLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step uint8_t* dstptr = dst + y*dststep; int x = 0; - v_float32 dst_x0 = vx_load(start_indices.data()); - v_float32 dst_x1 = v_add(dst_x0, vx_setall_f32(float(vlanes_32))); - v_float32 M0 = vx_setall_f32(M[0]), - M3 = vx_setall_f32(M[3]); - v_float32 M_x = vx_setall_f32(static_cast(y * M[1] + M[2])), - M_y = vx_setall_f32(static_cast(y * M[4] + M[5])); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); - for (; x < dstcols - uf; x += uf) { + for (; x <= dstcols - uf; x += uf) { // [TODO] apply halide trick - CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD(); - - v_int32 addr_0 = v_fma(v_srcstep, src_iy0, v_mul(src_ix0, four)), - addr_1 = v_fma(v_srcstep, src_iy1, v_mul(src_ix1, four)); - vx_store(addr, addr_0); - vx_store(addr + vlanes_32, addr_1); + CV_WARPAFFINE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); uint8x8_t p00r, p01r, p10r, p11r, p00g, p01g, p10g, p11g, @@ -1963,146 +1553,18 @@ void warpAffineLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step p00a, p01a, p10a, p11a; if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image - uint8x8x4_t p00 = { - vld1_u8(src + addr[0]), - vld1_u8(src + addr[1]), - vld1_u8(src + addr[2]), - vld1_u8(src + addr[3]) - }; - - uint8x8x4_t p01 = { - vld1_u8(src + addr[4]), - vld1_u8(src + addr[5]), - vld1_u8(src + addr[6]), - vld1_u8(src + addr[7]) - }; - - uint8x8x4_t p10 = { - vld1_u8(src + addr[0] + srcstep), - vld1_u8(src + addr[1] + srcstep), - vld1_u8(src + addr[2] + srcstep), - vld1_u8(src + addr[3] + srcstep) - }; - - uint8x8x4_t p11 = { - vld1_u8(src + addr[4] + srcstep), - vld1_u8(src + addr[5] + srcstep), - vld1_u8(src + addr[6] + srcstep), - vld1_u8(src + addr[7] + srcstep) - }; - - uint32x2_t p00_, p01_, p10_, p11_; - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, reds)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, reds)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, reds)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, reds)); - - p00r = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01r = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10r = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11r = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, greens)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, greens)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, greens)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, greens)); - - p00g = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01g = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10g = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11g = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, blues)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, blues)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, blues)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, blues)); - - p00b = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01b = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10b = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11b = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); - - p00_ = vreinterpret_u32_u8(vtbl4_u8(p00, alphas)); - p01_ = vreinterpret_u32_u8(vtbl4_u8(p01, alphas)); - p10_ = vreinterpret_u32_u8(vtbl4_u8(p10, alphas)); - p11_ = vreinterpret_u32_u8(vtbl4_u8(p11, alphas)); - - p00a = vreinterpret_u8_u32(vtrn1_u32(p00_, p01_)); - p01a = vreinterpret_u8_u32(vtrn2_u32(p00_, p01_)); - p10a = vreinterpret_u8_u32(vtrn1_u32(p10_, p11_)); - p11a = vreinterpret_u8_u32(vtrn2_u32(p10_, p11_)); + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C4); } else { CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 8U); - p00r = vld1_u8(pixbuf); - p01r = vld1_u8(pixbuf + 8); - p10r = vld1_u8(pixbuf + 16); - p11r = vld1_u8(pixbuf + 24); - - p00g = vld1_u8(pixbuf + 32); - p01g = vld1_u8(pixbuf + 32 + 8); - p10g = vld1_u8(pixbuf + 32 + 16); - p11g = vld1_u8(pixbuf + 32 + 24); - - p00b = vld1_u8(pixbuf + 64); - p01b = vld1_u8(pixbuf + 64 + 8); - p10b = vld1_u8(pixbuf + 64 + 16); - p11b = vld1_u8(pixbuf + 64 + 24); - - p00a = vld1_u8(pixbuf + 96); - p01a = vld1_u8(pixbuf + 96 + 8); - p10a = vld1_u8(pixbuf + 96 + 16); - p11a = vld1_u8(pixbuf + 96 + 24); + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C4); } - v_float16 f00r = v_float16(vcvtq_f16_u16(vmovl_u8(p00r))); - v_float16 f01r = v_float16(vcvtq_f16_u16(vmovl_u8(p01r))); - v_float16 f10r = v_float16(vcvtq_f16_u16(vmovl_u8(p10r))); - v_float16 f11r = v_float16(vcvtq_f16_u16(vmovl_u8(p11r))); + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C4); - v_float16 f00g = v_float16(vcvtq_f16_u16(vmovl_u8(p00g))); - v_float16 f01g = v_float16(vcvtq_f16_u16(vmovl_u8(p01g))); - v_float16 f10g = v_float16(vcvtq_f16_u16(vmovl_u8(p10g))); - v_float16 f11g = v_float16(vcvtq_f16_u16(vmovl_u8(p11g))); + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C4); - v_float16 f00b = v_float16(vcvtq_f16_u16(vmovl_u8(p00b))); - v_float16 f01b = v_float16(vcvtq_f16_u16(vmovl_u8(p01b))); - v_float16 f10b = v_float16(vcvtq_f16_u16(vmovl_u8(p10b))); - v_float16 f11b = v_float16(vcvtq_f16_u16(vmovl_u8(p11b))); - - v_float16 f00a = v_float16(vcvtq_f16_u16(vmovl_u8(p00a))); - v_float16 f01a = v_float16(vcvtq_f16_u16(vmovl_u8(p01a))); - v_float16 f10a = v_float16(vcvtq_f16_u16(vmovl_u8(p10a))); - v_float16 f11a = v_float16(vcvtq_f16_u16(vmovl_u8(p11a))); - - v_float16 alpha = v_cvt_f16(src_x0, src_x1), - beta = v_cvt_f16(src_y0, src_y1); - - f00r = v_fma(alpha, v_sub(f01r, f00r), f00r); - f10r = v_fma(alpha, v_sub(f11r, f10r), f10r); - - f00g = v_fma(alpha, v_sub(f01g, f00g), f00g); - f10g = v_fma(alpha, v_sub(f11g, f10g), f10g); - - f00b = v_fma(alpha, v_sub(f01b, f00b), f00b); - f10b = v_fma(alpha, v_sub(f11b, f10b), f10b); - - f00a = v_fma(alpha, v_sub(f01a, f00a), f00a); - f10a = v_fma(alpha, v_sub(f11a, f10a), f10a); - - f00r = v_fma(beta, v_sub(f10r, f00r), f00r); - f00g = v_fma(beta, v_sub(f10g, f00g), f00g); - f00b = v_fma(beta, v_sub(f10b, f00b), f00b); - f00a = v_fma(beta, v_sub(f10a, f00a), f00a); - - uint8x8x4_t result = { - vqmovun_s16(vcvtnq_s16_f16(f00r.val)), - vqmovun_s16(vcvtnq_s16_f16(f00g.val)), - vqmovun_s16(vcvtnq_s16_f16(f00b.val)), - vqmovun_s16(vcvtnq_s16_f16(f00a.val)), - }; - vst4_u8(dstptr + x*4, result); + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C4); } for (; x < dstcols; x++) { @@ -2130,6 +1592,1414 @@ void warpAffineLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step #endif } +void warpPerspectiveLinearInvoker_8UC1(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4]; + + uint8_t bvalbuf[max_uf]; + for (int i = 0; i < uf; i++) { + bvalbuf[i] = bval[0]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t grays = {0, 8, 16, 24, 1, 9, 17, 25}; + #endif +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t p00g, p01g, p10g, p11g; + #endif + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C1); + #else + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 8U); + #endif + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 8U); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C1); + #endif + } + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C1); + #else + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C1); + #endif + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U8(C1); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00g, p01g, p10g, p11g; + const uint8_t *srcptr = src + srcstep * iy + ix; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C1); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_SCALAR_STORE(C1, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_8UC3(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_16{VTraits::max_nlanes}; + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_16 = VTraits::vlanes(); + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), three = vx_setall_s32(3); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4*3]; + + uint8_t bvalbuf[max_uf*3]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*3] = bval[0]; + bvalbuf[i*3+1] = bval[1]; + bvalbuf[i*3+2] = bval[2]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + v_uint8 bval_v1 = vx_load_low(&bvalbuf[uf]); + v_uint8 bval_v2 = vx_load_low(&bvalbuf[uf*2]); + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t reds = {0, 8, 16, 24, 3, 11, 19, 27}, + greens = {1, 9, 17, 25, 4, 12, 20, 28}, + blues = {2, 10, 18, 26, 5, 13, 21, 29}; + #endif +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t p00r, p01r, p10r, p11r, + p00g, p01g, p10g, p11g, + p00b, p01b, p10b, p11b; + #endif + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C3); + #else + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 8U); + #endif + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 8U); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C3); + #endif + } + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C3); + #else + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C3); + #endif + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U8(C3); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p01r, p01g, p01b; + int p10r, p10g, p10b, p11r, p11g, p11b; + const uint8_t *srcptr = src + srcstep * iy + ix*3; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C3); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_SCALAR_STORE(C3, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_8UC4(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_16{VTraits::max_nlanes}; + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_16 = VTraits::vlanes(); + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), four = vx_setall_s32(4); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4*4]; + + uint8_t bvalbuf[max_uf*4]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*4] = bval[0]; + bvalbuf[i*4+1] = bval[1]; + bvalbuf[i*4+2] = bval[2]; + bvalbuf[i*4+3] = bval[3]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + v_uint8 bval_v1 = vx_load_low(&bvalbuf[uf]); + v_uint8 bval_v2 = vx_load_low(&bvalbuf[uf*2]); + v_uint8 bval_v3 = vx_load_low(&bvalbuf[uf*3]); + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t reds = {0, 8, 16, 24, 4, 12, 20, 28}, + greens = {1, 9, 17, 25, 5, 13, 21, 29}, + blues = {2, 10, 18, 26, 6, 14, 22, 30}, + alphas = {3, 11, 19, 27, 7, 15, 23, 31}; + #endif +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + uint8x8_t p00r, p01r, p10r, p11r, + p00g, p01g, p10g, p11g, + p00b, p01b, p10b, p11b, + p00a, p01a, p10a, p11a; + #endif + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C4); + #else + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 8U); + #endif + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 8U); + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C4); + #endif + } + + #if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 // In case neon fp16 intrinsics are not available; still requires A64 + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16_NEON(C4); + #else + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8S16(C4); + #endif + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_S16F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U8(C4); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p00a, p01r, p01g, p01b, p01a; + int p10r, p10g, p10b, p10a, p11r, p11g, p11b, p11a; + const uint8_t *srcptr = src + srcstep * iy + ix*4; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C4); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_SCALAR_STORE(C4, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_16UC1(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(uint16_t), dststep = dst_step/sizeof(uint16_t); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint16_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint16_t pixbuf[max_uf*4]; + + uint16_t bvalbuf[max_uf]; + for (int i = 0; i < uf; i++) { + bvalbuf[i] = bval[0]; + } + v_uint16 bval_v0 = vx_load(&bvalbuf[0]); +#endif + + for (int y = r.start; y < r.end; y++) { + uint16_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 16U); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 16U); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C1); + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U16(C1); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00g, p01g, p10g, p11g; + const uint16_t *srcptr = src + srcstep * iy + ix; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C1); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_SCALAR_STORE(C1, 16U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_16UC3(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(uint16_t), dststep = dst_step/sizeof(uint16_t); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint16_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), three = vx_setall_s32(3); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint16_t pixbuf[max_uf*4*3]; + + uint16_t bvalbuf[max_uf*3]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*3] = bval[0]; + bvalbuf[i*3+1] = bval[1]; + bvalbuf[i*3+2] = bval[2]; + } + v_uint16 bval_v0 = vx_load(&bvalbuf[0]); + v_uint16 bval_v1 = vx_load(&bvalbuf[uf]); + v_uint16 bval_v2 = vx_load(&bvalbuf[uf*2]); +#endif + + for (int y = r.start; y < r.end; y++) { + uint16_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 16U); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 16U); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C3); + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U16(C3); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p01r, p01g, p01b; + int p10r, p10g, p10b, p11r, p11g, p11b; + const uint16_t *srcptr = src + srcstep * iy + ix*3; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C3); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_SCALAR_STORE(C3, 16U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_16UC4(const uint16_t *src_data, size_t src_step, int src_rows, int src_cols, + uint16_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(uint16_t), dststep = dst_step/sizeof(uint16_t); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint16_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), four = vx_setall_s32(4); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint16_t pixbuf[max_uf*4*4]; + + uint16_t bvalbuf[max_uf*4]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*4] = bval[0]; + bvalbuf[i*4+1] = bval[1]; + bvalbuf[i*4+2] = bval[2]; + bvalbuf[i*4+3] = bval[3]; + } + v_uint16 bval_v0 = vx_load(&bvalbuf[0]); + v_uint16 bval_v1 = vx_load(&bvalbuf[uf]); + v_uint16 bval_v2 = vx_load(&bvalbuf[uf*2]); + v_uint16 bval_v3 = vx_load(&bvalbuf[uf*3]); +#endif + + for (int y = r.start; y < r.end; y++) { + uint16_t* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 16U); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 16U); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U16(C4); + + CV_WARP_LINEAR_VECTOR_INTER_CONVERT_U16F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32U16(C4); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p00a, p01r, p01g, p01b, p01a; + int p10r, p10g, p10b, p10a, p11r, p11g, p11b, p11a; + const uint16_t *srcptr = src + srcstep * iy + ix*4; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C4); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_SCALAR_STORE(C4, 16U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_32FC1(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(float), dststep = dst_step/sizeof(float); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + float bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + float pixbuf[max_uf*4]; + + float bvalbuf[max_uf]; + for (int i = 0; i < uf; i++) { + bvalbuf[i] = bval[0]; + } + v_float32 bval_v0_l = vx_load(&bvalbuf[0]); + v_float32 bval_v0_h = vx_load(&bvalbuf[vlanes_32]); +#endif + + for (int y = r.start; y < r.end; y++) { + float* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C1, 32F); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 32F); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32F32(C1); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + float p00g, p01g, p10g, p11g; + const float *srcptr = src + srcstep * iy + ix; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C1); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_SCALAR_STORE(C1, 32F); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_32FC3(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(float), dststep = dst_step/sizeof(float); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + float bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), three = vx_setall_s32(3); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + float pixbuf[max_uf*4*3]; + + float bvalbuf[max_uf*3]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*3] = bval[0]; + bvalbuf[i*3+1] = bval[1]; + bvalbuf[i*3+2] = bval[2]; + } + v_float32 bval_v0_l = vx_load(&bvalbuf[0]); + v_float32 bval_v0_h = vx_load(&bvalbuf[vlanes_32]); + v_float32 bval_v1_l = vx_load(&bvalbuf[uf]); + v_float32 bval_v1_h = vx_load(&bvalbuf[uf+vlanes_32]); + v_float32 bval_v2_l = vx_load(&bvalbuf[uf*2]); + v_float32 bval_v2_h = vx_load(&bvalbuf[uf*2+vlanes_32]); +#endif + + for (int y = r.start; y < r.end; y++) { + float* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C3, 32F); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 32F); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32F32(C3); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + float p00r, p00g, p00b, p01r, p01g, p01b; + float p10r, p10g, p10b, p11r, p11g, p11b; + const float *srcptr = src + srcstep * iy + ix*3; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C3); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_SCALAR_STORE(C3, 32F); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearInvoker_32FC4(const float *src_data, size_t src_step, int src_rows, int src_cols, + float *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step/sizeof(float), dststep = dst_step/sizeof(float); + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + float bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), four = vx_setall_s32(4); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + float pixbuf[max_uf*4*4]; + + float bvalbuf[max_uf*4]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*4] = bval[0]; + bvalbuf[i*4+1] = bval[1]; + bvalbuf[i*4+2] = bval[2]; + bvalbuf[i*4+3] = bval[3]; + } + v_float32 bval_v0_l = vx_load(&bvalbuf[0]); + v_float32 bval_v0_h = vx_load(&bvalbuf[vlanes_32]); + v_float32 bval_v1_l = vx_load(&bvalbuf[uf]); + v_float32 bval_v1_h = vx_load(&bvalbuf[uf+vlanes_32]); + v_float32 bval_v2_l = vx_load(&bvalbuf[uf*2]); + v_float32 bval_v2_h = vx_load(&bvalbuf[uf*2+vlanes_32]); + v_float32 bval_v3_l = vx_load(&bvalbuf[uf*3]); + v_float32 bval_v3_h = vx_load(&bvalbuf[uf*3+vlanes_32]); +#endif + + for (int y = r.start; y < r.end; y++) { + float* dstptr = dst + y*dststep; + int x = 0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN(C4, 32F); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 32F); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F32F32(C4); + } +#endif // (CV_SIMD || CV_SIMD_SCALABLE) + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + float p00r, p00g, p00b, p00a, p01r, p01g, p01b, p01a; + float p10r, p10g, p10b, p10a, p11r, p11g, p11b, p11a; + const float *srcptr = src + srcstep * iy + ix*4; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C4); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_SCALAR_STORE(C4, 32F); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +} + +void warpPerspectiveLinearApproxInvoker_8UC1(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { +#if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 && CV_SIMD128_FP16 + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4]; + + uint8_t bvalbuf[max_uf]; + for (int i = 0; i < uf; i++) { + bvalbuf[i] = bval[0]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + uint8x8_t grays = {0, 8, 16, 24, 1, 9, 17, 25}; + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C1); + + uint8x8_t p00g, p01g, p10g, p11g; + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C1); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C1, 8U); + + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C1); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C1); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C1); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C1); + } + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00g, p01g, p10g, p11g; + const uint8_t *srcptr = src + srcstep * iy + ix; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C1); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C1); + + CV_WARP_LINEAR_SCALAR_STORE(C1, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +#else + warpPerspectiveLinearInvoker_8UC1(src_data, src_step, src_rows, src_cols, + dst_data, dst_step, dst_rows, dst_cols, + dM, border_type, border_value); +#endif +} + +void warpPerspectiveLinearApproxInvoker_8UC3(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { +#if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 && CV_SIMD128_FP16 + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + + int vlanes_32 = VTraits::vlanes(); + + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), three = vx_setall_s32(3); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4*3]; + + uint8_t bvalbuf[max_uf*3]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*3] = bval[0]; + bvalbuf[i*3+1] = bval[1]; + bvalbuf[i*3+2] = bval[2]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + v_uint8 bval_v1 = vx_load_low(&bvalbuf[uf]); + v_uint8 bval_v2 = vx_load_low(&bvalbuf[uf*2]); + uint8x8_t reds = {0, 8, 16, 24, 3, 11, 19, 27}, + greens = {1, 9, 17, 25, 4, 12, 20, 28}, + blues = {2, 10, 18, 26, 5, 13, 21, 29}; + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C3); + + uint8x8_t p00r, p01r, p10r, p11r, + p00g, p01g, p10g, p11g, + p00b, p01b, p10b, p11b; + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C3); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C3, 8U); + + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C3); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C3); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C3); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C3); + } + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p01r, p01g, p01b; + int p10r, p10g, p10b, p11r, p11g, p11b; + const uint8_t *srcptr = src + srcstep * iy + ix*3; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C3); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C3); + + CV_WARP_LINEAR_SCALAR_STORE(C3, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +#else + warpPerspectiveLinearInvoker_8UC3(src_data, src_step, src_rows, src_cols, + dst_data, dst_step, dst_rows, dst_cols, + dM, border_type, border_value); +#endif +} + +void warpPerspectiveLinearApproxInvoker_8UC4(const uint8_t *src_data, size_t src_step, int src_rows, int src_cols, + uint8_t *dst_data, size_t dst_step, int dst_rows, int dst_cols, + const double dM[9], int border_type, const double border_value[4]) { +#if defined(CV_NEON_AARCH64) && CV_NEON_AARCH64 && CV_SIMD128_FP16 + auto worker = [&](const Range &r) { + CV_INSTRUMENT_REGION(); + + const auto *src = src_data; + auto *dst = dst_data; + size_t srcstep = src_step, dststep = dst_step; + int srccols = src_cols, srcrows = src_rows; + int dstcols = dst_cols; + float M[9]; + for (int i = 0; i < 9; i++) { + M[i] = static_cast(dM[i]); + } + uint8_t bval[] = { + saturate_cast(border_value[0]), + saturate_cast(border_value[1]), + saturate_cast(border_value[2]), + saturate_cast(border_value[3]), + }; + int border_type_x = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : border_type; + int border_type_y = border_type != BORDER_CONSTANT && + border_type != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : border_type; + + constexpr int max_vlanes_32{VTraits::max_nlanes}; + constexpr int max_uf{max_vlanes_32*2}; + int vlanes_32 = VTraits::vlanes(); + // unrolling_factor = lane_size / 16 = vlanes_32 * 32 / 16 = vlanes_32 * 2 + int uf = vlanes_32 * 2; + + std::array start_indices; + std::iota(start_indices.data(), start_indices.data() + max_vlanes_32, 0.f); + + v_uint32 inner_srows = vx_setall_u32((unsigned)srcrows - 2), + inner_scols = vx_setall_u32((unsigned)srccols - 1), + outer_srows = vx_setall_u32((unsigned)srcrows + 1), + outer_scols = vx_setall_u32((unsigned)srccols + 1); + v_float32 delta = vx_setall_f32(static_cast(uf)); + v_int32 one = vx_setall_s32(1), four = vx_setall_s32(4); + v_int32 v_srcstep = vx_setall_s32(int(srcstep)); + int32_t addr[max_uf], + src_ix[max_uf], + src_iy[max_uf]; + uint8_t pixbuf[max_uf*4*4]; + + uint8_t bvalbuf[max_uf*4]; + for (int i = 0; i < uf; i++) { + bvalbuf[i*4] = bval[0]; + bvalbuf[i*4+1] = bval[1]; + bvalbuf[i*4+2] = bval[2]; + bvalbuf[i*4+3] = bval[3]; + } + v_uint8 bval_v0 = vx_load_low(&bvalbuf[0]); + v_uint8 bval_v1 = vx_load_low(&bvalbuf[uf]); + v_uint8 bval_v2 = vx_load_low(&bvalbuf[uf*2]); + v_uint8 bval_v3 = vx_load_low(&bvalbuf[uf*3]); + uint8x8_t reds = {0, 8, 16, 24, 4, 12, 20, 28}, + greens = {1, 9, 17, 25, 5, 13, 21, 29}, + blues = {2, 10, 18, 26, 6, 14, 22, 30}, + alphas = {3, 11, 19, 27, 7, 15, 23, 31}; + + for (int y = r.start; y < r.end; y++) { + uint8_t* dstptr = dst + y*dststep; + int x = 0; + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD1(); + + for (; x <= dstcols - uf; x += uf) { + // [TODO] apply halide trick + + CV_WARPPERSPECTIVE_LINEAR_VECTOR_COMPUTE_MAPPED_COORD2(C4); + + uint8x8_t p00r, p01r, p10r, p11r, + p00g, p01g, p10g, p11g, + p00b, p01b, p10b, p11b, + p00a, p01a, p10a, p11a; + + if (v_reduce_min(inner_mask) != 0) { // all loaded pixels are completely inside the image + CV_WARP_LINEAR_VECTOR_SHUFFLE_ALLWITHIN_NEON_U8(C4); + } else { + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN(C4, 8U); + + CV_WARP_LINEAR_VECTOR_SHUFFLE_NOTALLWITHIN_NEON_U8(C4); + } + + CV_WARP_LINEAR_VECTOR_INTER_LOAD_U8F16(C4); + + CV_WARP_LINEAR_VECTOR_INTER_CALC_F16(C4); + + CV_WARP_LINEAR_VECTOR_INTER_STORE_F16U8(C4); + } + + for (; x < dstcols; x++) { + float w = x*M[6] + y*M[7] + M[8]; + float sx = (x*M[0] + y*M[1] + M[2]) / w; + float sy = (x*M[3] + y*M[4] + M[5]) / w; + int ix = cvFloor(sx), iy = cvFloor(sy); + sx -= ix; sy -= iy; + int p00r, p00g, p00b, p00a, p01r, p01g, p01b, p01a; + int p10r, p10g, p10b, p10a, p11r, p11g, p11b, p11a; + const uint8_t *srcptr = src + srcstep * iy + ix*4; + + CV_WARP_LINEAR_SCALAR_SHUFFLE(C4); + + CV_WARP_LINEAR_SCALAR_INTER_CALC_F32(C4); + + CV_WARP_LINEAR_SCALAR_STORE(C4, 8U); + } + } + }; + parallel_for_(Range(0, dst_rows), worker); +#else + warpPerspectiveLinearInvoker_8UC4(src_data, src_step, src_rows, src_cols, + dst_data, dst_step, dst_rows, dst_cols, + dM, border_type, border_value); +#endif +} + #endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index 73f017afae..1dbc58cf24 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -1042,16 +1042,16 @@ protected: virtual void run_func(); virtual void run_reference_func(); + template + void newLinear(int x, float sx, float sy, const T *srcptr_, T *dstptr, int srccols, int srcrows, size_t srcstep, + const T *bval, int borderType_x, int borderType_y); + Mat M; private: void warpAffine(const Mat&, Mat&); template void newWarpAffine(const Mat&, Mat&, const Mat&); - - template - void newLinear(int x, float sx, float sy, const T *srcptr_, T *dstptr, int srccols, int srcrows, size_t srcstep, - const T *bval, int borderType_x, int borderType_y); }; CV_WarpAffine_Test::CV_WarpAffine_Test() : @@ -1336,6 +1336,9 @@ protected: private: void warpPerspective(const Mat&, Mat&); + + template + void newWarpPerspective(const Mat&, Mat&, const Mat&); }; CV_WarpPerspective_Test::CV_WarpPerspective_Test() : @@ -1369,7 +1372,8 @@ void CV_WarpPerspective_Test::generate_test_data() void CV_WarpPerspective_Test::run_func() { - cv::warpPerspective(src, dst, M, dst.size(), interpolation, borderType, borderValue); + cv::warpPerspective(src, dst, M, dst.size(), interpolation, borderType, borderValue, cv::ALGO_HINT_APPROX); + // cv::warpPerspective(src, dst, M, dst.size(), interpolation, borderType, borderValue); } float CV_WarpPerspective_Test::get_success_error_level(int _interpolation, int _depth) const @@ -1384,6 +1388,54 @@ void CV_WarpPerspective_Test::run_reference_func() tmp.convertTo(reference_dst, reference_dst.depth()); } +template +void CV_WarpPerspective_Test::newWarpPerspective(const Mat &_src, Mat &_dst, const Mat &tM) +{ + int num_channels = _dst.channels(); + CV_CheckTrue(num_channels == 1 || num_channels == 3 || num_channels == 4, ""); + + auto *srcptr_ = _src.ptr(); + auto *dstptr_ = _dst.ptr(); + size_t srcstep = _src.step/sizeof(T), dststep = _dst.step/sizeof(T); + int srccols = _src.cols, srcrows = _src.rows; + int dstcols = _dst.cols, dstrows = _dst.rows; + + Mat tmp; + tM.convertTo(tmp, CV_32F); + auto *_M = tmp.ptr(); + + T bval[] = { + saturate_cast(borderValue[0]), + saturate_cast(borderValue[1]), + saturate_cast(borderValue[2]), + saturate_cast(borderValue[3]), + }; + + int borderType_x = borderType != BORDER_CONSTANT && + borderType != BORDER_TRANSPARENT && + srccols <= 1 ? BORDER_REPLICATE : borderType; + int borderType_y = borderType != BORDER_CONSTANT && + borderType != BORDER_TRANSPARENT && + srcrows <= 1 ? BORDER_REPLICATE : borderType; + + for (int y = 0; y < dstrows; y++) { + T* dstptr = dstptr_ + y*dststep; + for (int x = 0; x < dstcols; x++) { + float w = x*_M[6] + y*_M[7] + _M[8]; + float sx = (x*_M[0] + y*_M[1] + _M[2]) / w; + float sy = (x*_M[3] + y*_M[4] + _M[5]) / w; + + if (num_channels == 3) { + newLinear<3>(x, sx, sy, srcptr_, dstptr, srccols, srcrows, srcstep, bval, borderType_x, borderType_y); + } else if (num_channels == 4) { + newLinear<4>(x, sx, sy, srcptr_, dstptr, srccols, srcrows, srcstep, bval, borderType_x, borderType_y); + } else { + newLinear<1>(x, sx, sy, srcptr_, dstptr, srccols, srcrows, srcstep, bval, borderType_x, borderType_y); + } + } + } +} + void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) { Size ssize = _src.size(), dsize = _dst.size(); @@ -1410,6 +1462,17 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) if (inter == INTER_AREA) inter = INTER_LINEAR; + if (inter == INTER_LINEAR) { + int dst_depth = _dst.depth(), dst_channels = _dst.channels(); + if (dst_depth == CV_8U && (dst_channels == 1 || dst_channels == 3 || dst_channels == 4)) { + return newWarpPerspective(_src, _dst, M); + } else if (dst_depth == CV_16U && (dst_channels == 1 || dst_channels == 3 || dst_channels == 4)) { + return newWarpPerspective(_src, _dst, M); + } else if (dst_depth == CV_32F && (dst_channels == 1 || dst_channels == 3 || dst_channels == 4)) { + return newWarpPerspective(_src, _dst, M); + } + } + mapx.create(dsize, CV_16SC2); if (inter != INTER_NEAREST) mapy.create(dsize, CV_16SC1);