diff --git a/hal/riscv-rvv/include/core.hpp b/hal/riscv-rvv/include/core.hpp index cfc0cc1046..da2cad1648 100644 --- a/hal/riscv-rvv/include/core.hpp +++ b/hal/riscv-rvv/include/core.hpp @@ -334,6 +334,14 @@ int transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t #undef cv_hal_transpose2d #define cv_hal_transpose2d cv::rvv_hal::core::transpose2d +/* ############ rotate90 ############ */ + +int rotate90(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 angle); + +#undef cv_hal_rotate90 +#define cv_hal_rotate90 cv::rvv_hal::core::rotate90 + #endif // CV_HAL_RVV_1P0_ENABLED }}} // cv::rvv_hal::core diff --git a/hal/riscv-rvv/src/core/flip.cpp b/hal/riscv-rvv/src/core/flip.cpp index 8a39e98bd1..b730cce191 100644 --- a/hal/riscv-rvv/src/core/flip.cpp +++ b/hal/riscv-rvv/src/core/flip.cpp @@ -7,16 +7,7 @@ #include "rvv_hal.hpp" -#if defined (__clang__) && __clang_major__ < 18 -#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ - __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(v, 0, v0); \ - v = __riscv_vset(v, 1, v1); \ - v = __riscv_vset(v, 2, v2); -#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) -#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) -#define __riscv_vcreate_v_u32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u32, 2, v0, v1, v2) -#define __riscv_vcreate_v_u64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u64, 2, v0, v1, v2) -#endif +#include "rvv_vcreate.hpp" namespace cv { namespace rvv_hal { namespace core { @@ -24,24 +15,50 @@ namespace cv { namespace rvv_hal { namespace core { namespace { +template +struct FlipC1 : RVV +{ + static inline typename RVV::VecType gather(typename RVV::VecType v, + typename RVV::VecType indices, size_t vl) + { + return __riscv_vrgather(v, indices, vl); + } +}; + +template <> +struct FlipC1 : RVV_U8M4 +{ + // e8,m4 data and e16,m8 indices have the same number of lanes. + // 8-bit indices wrap when more than 256 bytes are reversed at once. + static inline RVV_U16M8::VecType vid(size_t vl) + { + return RVV_U16M8::vid(vl); + } + + static inline VecType gather(VecType v, RVV_U16M8::VecType indices, size_t vl) + { + return __riscv_vrgatherei16(v, indices, vl); + } +}; + #define CV_HAL_RVV_FLIP_C1(name, _Tps, RVV) \ inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ for (int h = 0; h < src_height; h++) { \ const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ - _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - h) : (h + 1))); \ + _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - 1 - h) : h)) + src_width; \ int vl; \ for (int w = 0; w < src_width; w += vl) { \ vl = RVV::setvl(src_width - w); \ - RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ auto v = RVV::vload(src_row + w, vl); \ - RVV::vstore(dst_row - w - vl, __riscv_vrgather(v, indices, vl), vl); \ + RVV::vstore(dst_row - w - vl, RVV::gather(v, indices, vl), vl); \ } \ } \ } -CV_HAL_RVV_FLIP_C1(8UC1, uchar, RVV_U8M8) -CV_HAL_RVV_FLIP_C1(16UC1, ushort, RVV_U16M8) -CV_HAL_RVV_FLIP_C1(32UC1, unsigned, RVV_U32M8) -CV_HAL_RVV_FLIP_C1(64UC1, uint64_t, RVV_U64M8) +CV_HAL_RVV_FLIP_C1(8UC1, uchar, FlipC1) +CV_HAL_RVV_FLIP_C1(16UC1, ushort, FlipC1) +CV_HAL_RVV_FLIP_C1(32UC1, unsigned, FlipC1) +CV_HAL_RVV_FLIP_C1(64UC1, uint64_t, FlipC1) #define CV_HAL_RVV_FLIP_INPLACE_C1(name, _Tps, RVV) \ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int flip_mode) { \ @@ -50,36 +67,36 @@ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int h; \ for (h = 0; h < new_height; h++) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - h) : (h + 1))); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - 1 - h) : h)) + width; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ vl = RVV::setvl(new_width - w); \ - RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ auto v_left = RVV::vload(row_begin + w, vl); \ auto v_right = RVV::vload(row_end - w - vl, vl); \ - RVV::vstore(row_begin + w, __riscv_vrgather(v_right, indices, vl), vl); \ - RVV::vstore(row_end - w - vl, __riscv_vrgather(v_left, indices, vl), vl); \ + RVV::vstore(row_begin + w, RVV::gather(v_right, indices, vl), vl); \ + RVV::vstore(row_end - w - vl, RVV::gather(v_left, indices, vl), vl); \ } \ } \ if (flip_mode == -1 && new_height * 2 != height) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (h + 1)); \ + _Tps* row_end = (_Tps*)(data + step * h) + width; \ new_width /= 2; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ vl = RVV::setvl(new_width - w); \ - RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ auto v_left = RVV::vload(row_begin + w, vl); \ auto v_right = RVV::vload(row_end - w - vl, vl); \ - RVV::vstore(row_begin + w, __riscv_vrgather(v_right, indices, vl), vl); \ - RVV::vstore(row_end - w - vl, __riscv_vrgather(v_left, indices, vl), vl); \ + RVV::vstore(row_begin + w, RVV::gather(v_right, indices, vl), vl); \ + RVV::vstore(row_end - w - vl, RVV::gather(v_left, indices, vl), vl); \ } \ } \ } -CV_HAL_RVV_FLIP_INPLACE_C1(8UC1, uchar, RVV_U8M8) -CV_HAL_RVV_FLIP_INPLACE_C1(16UC1, ushort, RVV_U16M8) -CV_HAL_RVV_FLIP_INPLACE_C1(32UC1, unsigned, RVV_U32M8) -CV_HAL_RVV_FLIP_INPLACE_C1(64UC1, uint64_t, RVV_U64M8) +CV_HAL_RVV_FLIP_INPLACE_C1(8UC1, uchar, FlipC1) +CV_HAL_RVV_FLIP_INPLACE_C1(16UC1, ushort, FlipC1) +CV_HAL_RVV_FLIP_INPLACE_C1(32UC1, unsigned, FlipC1) +CV_HAL_RVV_FLIP_INPLACE_C1(64UC1, uint64_t, FlipC1) // Suppress warnings of "ignoring attributes applied to VecType after definition", // VecType is vuint8m2x3_t, vuint16m2x3_t, vuint32m2x3_t or vuint64m2x3_t @@ -113,7 +130,7 @@ CV_HAL_RVV_FLIP_C3_TYPES(64) inline void flip_##name(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int flip_mode) { \ for (int h = 0; h < src_height; h++) { \ const _Tps* src_row = (const _Tps*)(src_data + src_step * h); \ - _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - h) : (h + 1))); \ + _Tps* dst_row = (_Tps*)(dst_data + dst_step * (flip_mode < 0 ? (src_height - 1 - h) : h)) + 3 * src_width; \ int vl; \ for (int w = 0; w < src_width; w += vl) { \ vl = RVV::setvl(src_width - w); \ @@ -136,7 +153,7 @@ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int h; \ for (h = 0; h < new_height; h++) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - h) : (h + 1))); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - 1 - h) : h)) + 3 * width; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ vl = RVV::setvl(new_width - w); \ @@ -151,7 +168,7 @@ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, } \ if (flip_mode == -1 && new_height * 2 != height) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (h + 1)); \ + _Tps* row_end = (_Tps*)(data + step * h) + 3 * width; \ new_width /= 2; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ diff --git a/hal/riscv-rvv/src/core/rotate.cpp b/hal/riscv-rvv/src/core/rotate.cpp new file mode 100644 index 0000000000..a4f2847237 --- /dev/null +++ b/hal/riscv-rvv/src/core/rotate.cpp @@ -0,0 +1,115 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "rvv_hal.hpp" +#include "rvv_vcreate.hpp" + +namespace cv { namespace rvv_hal { namespace core { + +#if CV_HAL_RVV_1P0_ENABLED + +namespace { + +#define CV_HAL_RVV_ROTATE_8_ROWS(func, T, RVV, suffix, bits) \ +static void func(const uchar* src_data, size_t src_step, int src_width, int src_height, \ + uchar* dst_data, size_t dst_step, int angle) \ +{ \ + const size_t src_step_elems = src_step / sizeof(T); \ + int y = 0; \ + for (; y <= src_height - 8; y += 8) \ + { \ + const T* src = reinterpret_cast(src_data) + y * src_step_elems; \ + for (int x = 0; x < src_width; ) \ + { \ + const size_t vl = __riscv_vsetvl_e##bits##m1(src_width - x); \ + auto v0 = __riscv_vle##bits##_v_##suffix##m1(src + x, vl); \ + auto v1 = __riscv_vle##bits##_v_##suffix##m1(src + src_step_elems + x, vl); \ + auto v2 = __riscv_vle##bits##_v_##suffix##m1(src + 2 * src_step_elems + x, vl); \ + auto v3 = __riscv_vle##bits##_v_##suffix##m1(src + 3 * src_step_elems + x, vl); \ + auto v4 = __riscv_vle##bits##_v_##suffix##m1(src + 4 * src_step_elems + x, vl); \ + auto v5 = __riscv_vle##bits##_v_##suffix##m1(src + 5 * src_step_elems + x, vl); \ + auto v6 = __riscv_vle##bits##_v_##suffix##m1(src + 6 * src_step_elems + x, vl); \ + auto v7 = __riscv_vle##bits##_v_##suffix##m1(src + 7 * src_step_elems + x, vl); \ + vuint##bits##m1x8_t v; \ + T* dst; \ + ptrdiff_t stride; \ + if (angle == 90) \ + { \ + v = __riscv_vcreate_v_##suffix##m1x8(v7, v6, v5, v4, v3, v2, v1, v0); \ + dst = reinterpret_cast(dst_data + static_cast(x) * dst_step) \ + + src_height - y - 8; \ + stride = static_cast(dst_step); \ + } \ + else \ + { \ + v = __riscv_vcreate_v_##suffix##m1x8(v0, v1, v2, v3, v4, v5, v6, v7); \ + dst = reinterpret_cast(dst_data + static_cast(src_width - 1 - x) * dst_step) + y; \ + stride = -static_cast(dst_step); \ + } \ + __riscv_vssseg8e##bits(dst, stride, v, vl); \ + x += static_cast(vl); \ + } \ + } \ + for (; y < src_height; ++y) \ + { \ + const T* src = reinterpret_cast(src_data + static_cast(y) * src_step); \ + for (int x = 0; x < src_width; ) \ + { \ + const size_t vl = RVV::setvl(src_width - x); \ + auto v = RVV::vload(src + x, vl); \ + T* dst; \ + ptrdiff_t stride; \ + if (angle == 90) \ + { \ + dst = reinterpret_cast(dst_data + static_cast(x) * dst_step) \ + + src_height - 1 - y; \ + stride = static_cast(dst_step); \ + } \ + else \ + { \ + dst = reinterpret_cast(dst_data + static_cast(src_width - 1 - x) * dst_step) + y; \ + stride = -static_cast(dst_step); \ + } \ + RVV::vstore_stride(dst, stride, v, vl); \ + x += static_cast(vl); \ + } \ + } \ +} + +CV_HAL_RVV_ROTATE_8_ROWS(rotate90_8u, uint8_t, RVV_U8M8, u8, 8) +CV_HAL_RVV_ROTATE_8_ROWS(rotate90_16u, uint16_t, RVV_U16M8, u16, 16) +CV_HAL_RVV_ROTATE_8_ROWS(rotate90_32u, uint32_t, RVV_U32M8, u32, 32) +CV_HAL_RVV_ROTATE_8_ROWS(rotate90_64u, uint64_t, RVV_U64M8, u64, 64) + +#undef CV_HAL_RVV_ROTATE_8_ROWS + +} // namespace + +int rotate90(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 angle) +{ + if (src_data == dst_data || (angle != 90 && angle != 270)) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + const size_t element_size = CV_ELEM_SIZE(src_type); + if (src_step % element_size != 0 || dst_step % element_size != 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + using RotateFunc = void (*)(const uchar*, size_t, int, int, uchar*, size_t, int); + static RotateFunc rotate90Funcs[] = { + nullptr, rotate90_8u, rotate90_16u, nullptr, rotate90_32u, + nullptr, nullptr, nullptr, rotate90_64u + }; + RotateFunc func = element_size < sizeof(rotate90Funcs) / sizeof(rotate90Funcs[0]) + ? rotate90Funcs[element_size] : nullptr; + if (!func) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + func(src_data, src_step, src_width, src_height, dst_data, dst_step, angle); + return CV_HAL_ERROR_OK; +} + +#endif // CV_HAL_RVV_1P0_ENABLED + +}}} // cv::rvv_hal::core diff --git a/hal/riscv-rvv/src/core/rvv_vcreate.hpp b/hal/riscv-rvv/src/core/rvv_vcreate.hpp new file mode 100644 index 0000000000..38967c8f6a --- /dev/null +++ b/hal/riscv-rvv/src/core/rvv_vcreate.hpp @@ -0,0 +1,48 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Copyright (C) 2025, SpaceMIT Inc., all rights reserved. + +#ifndef OPENCV_HAL_RVV_VCREATE_HPP_INCLUDED +#define OPENCV_HAL_RVV_VCREATE_HPP_INCLUDED + +// Tuple creation intrinsics were added in Clang 18. +// These shims are shared by flip, transpose and rotate. +#if defined(__clang__) && __clang_major__ < 18 +#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); +#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) +#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) +#define __riscv_vcreate_v_u32m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u32, 2, v0, v1, v2) +#define __riscv_vcreate_v_u64m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u64, 2, v0, v1, v2) + +#define OPENCV_HAL_IMPL_RVV_VCREATE_x4(suffix, width, v0, v1, v2, v3) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); \ + v = __riscv_vset(v, 3, v3); + +#define OPENCV_HAL_IMPL_RVV_VCREATE_x8(suffix, width, v0, v1, v2, v3, v4, v5, v6, v7) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x8(v, 0, v0); \ + v = __riscv_vset(v, 1, v1); \ + v = __riscv_vset(v, 2, v2); \ + v = __riscv_vset(v, 3, v3); \ + v = __riscv_vset(v, 4, v4); \ + v = __riscv_vset(v, 5, v5); \ + v = __riscv_vset(v, 6, v6); \ + v = __riscv_vset(v, 7, v7); + +#define __riscv_vcreate_v_u8m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u8, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#define __riscv_vcreate_v_u16m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u16, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#define __riscv_vcreate_v_i32m1x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(i32, 1, v0, v1, v2, v3) +#define __riscv_vcreate_v_i64m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(i64, 1, v0, v1, v2, v3, v4, v5, v6, v7) + +#define __riscv_vcreate_v_u32m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u32, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#define __riscv_vcreate_v_u64m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u64, 1, v0, v1, v2, v3, v4, v5, v6, v7) +#endif + +#endif // OPENCV_HAL_RVV_VCREATE_HPP_INCLUDED diff --git a/hal/riscv-rvv/src/core/transpose.cpp b/hal/riscv-rvv/src/core/transpose.cpp index 4f7ccd63d2..7a0746a5b2 100644 --- a/hal/riscv-rvv/src/core/transpose.cpp +++ b/hal/riscv-rvv/src/core/transpose.cpp @@ -7,28 +7,7 @@ #include "rvv_hal.hpp" -#if defined (__clang__) && __clang_major__ < 18 -#define OPENCV_HAL_IMPL_RVV_VCREATE_x4(suffix, width, v0, v1, v2, v3) \ - __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(v, 0, v0); \ - v = __riscv_vset(v, 1, v1); \ - v = __riscv_vset(v, 2, v2); \ - v = __riscv_vset(v, 3, v3); - -#define OPENCV_HAL_IMPL_RVV_VCREATE_x8(suffix, width, v0, v1, v2, v3, v4, v5, v6, v7) \ - __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x8(v, 0, v0); \ - v = __riscv_vset(v, 1, v1); \ - v = __riscv_vset(v, 2, v2); \ - v = __riscv_vset(v, 3, v3); \ - v = __riscv_vset(v, 4, v4); \ - v = __riscv_vset(v, 5, v5); \ - v = __riscv_vset(v, 6, v6); \ - v = __riscv_vset(v, 7, v7); - -#define __riscv_vcreate_v_u8m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u8, 1, v0, v1, v2, v3, v4, v5, v6, v7) -#define __riscv_vcreate_v_u16m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(u16, 1, v0, v1, v2, v3, v4, v5, v6, v7) -#define __riscv_vcreate_v_i32m1x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(i32, 1, v0, v1, v2, v3) -#define __riscv_vcreate_v_i64m1x8(v0, v1, v2, v3, v4, v5, v6, v7) OPENCV_HAL_IMPL_RVV_VCREATE_x8(i64, 1, v0, v1, v2, v3, v4, v5, v6, v7) -#endif +#include "rvv_vcreate.hpp" namespace cv { namespace rvv_hal { namespace core { @@ -186,7 +165,8 @@ static void transpose2d_32sC2(const uchar *src_data, size_t src_step, uchar *dst using Transpose2dFunc = void (*)(const uchar*, size_t, uchar*, size_t, int, int); int transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width, int src_height, int element_size) { - if (src_data == dst_data) { + if (src_data == dst_data || element_size <= 0 || + src_step % element_size != 0 || dst_step % element_size != 0) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } diff --git a/modules/core/src/matrix_transform.cpp b/modules/core/src/matrix_transform.cpp index 309d8d7ec2..54a6b5560a 100644 --- a/modules/core/src/matrix_transform.cpp +++ b/modules/core/src/matrix_transform.cpp @@ -325,6 +325,18 @@ static void transpose_48bit_simd(const uchar* src, size_t sstep, uchar* dst, siz template static void transpose_( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size sz ) { + // Byte-channel matrices may have strides that are not aligned for T. + // Preserve byte strides and avoid unaligned typed accesses in this case. + if (!isAligned(reinterpret_cast(src) | + reinterpret_cast(dst) | sstep | dstep)) + { + for (int y = 0; y < sz.height; ++y) + for (int x = 0; x < sz.width; ++x) + std::memcpy(dst + static_cast(x) * dstep + y * sizeof(T), + src + static_cast(y) * sstep + x * sizeof(T), sizeof(T)); + return; + } + #if CV_SIMD128 switch (sizeof(T)) { diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index ffe6ead18e..d87ee2078c 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -1012,6 +1012,112 @@ struct RotateOp : public BaseElemWiseOp int rotatecode; }; +typedef testing::TestWithParam > RotatePaddedTest; + +TEST_P(RotatePaddedTest, accuracy) +{ + const int type = get<0>(GetParam()); + const Size size = get<1>(GetParam()); + const int rotateCode = get<2>(GetParam()); + RNG& rng = theRNG(); + + Mat storage(size.height + 2, size.width + 2, type); + Mat src = storage(Rect(1, 1, size.width, size.height)); + rng.fill(src, RNG::UNIFORM, Scalar::all(0), Scalar::all(255)); + const Size dstSize = rotateCode == ROTATE_180 ? size : Size(size.height, size.width); + Mat dstStorage(dstSize.height + 2, dstSize.width + 2, type, Scalar::all(0)); + Mat actual = dstStorage(Rect(1, 1, dstSize.width, dstSize.height)); + Mat expected; + reference::rotate(src, expected, rotateCode); + cv::rotate(src, actual, rotateCode); + EXPECT_EQ(0, cvtest::norm(expected, actual, NORM_INF)); + Mat expectedStorage(dstStorage.size(), type, Scalar::all(0)); + expected.copyTo(expectedStorage(Rect(1, 1, dstSize.width, dstSize.height))); + EXPECT_EQ(0, cvtest::norm(expectedStorage, dstStorage, NORM_INF)); +} + +INSTANTIATE_TEST_CASE_P(Core_Rotate, RotatePaddedTest, testing::Combine( + testing::Values(CV_8UC1, CV_16UC1, CV_32SC1, CV_64FC1), + testing::Values(Size(3550, 3), Size(513, 7), Size(1025, 9), Size(17, 19)), + testing::Values(ROTATE_90_CLOCKWISE, ROTATE_180, ROTATE_90_COUNTERCLOCKWISE))); + +typedef testing::TestWithParam > FlipWideTest; + +TEST_P(FlipWideTest, accuracy) +{ + const Size size = get<0>(GetParam()); + const int flipCode = get<1>(GetParam()); + const bool inPlace = get<2>(GetParam()); + const bool padded = get<3>(GetParam()); + const int border = padded ? 1 : 0; + const Rect roi(border, border, size.width, size.height); + Mat storage(size.height + 2 * border, size.width + 2 * border, CV_8UC1, Scalar::all(0)); + Mat src = storage(roi); + theRNG().fill(src, RNG::UNIFORM, 0, 256); + Mat expected; + reference::flip(src, expected, flipCode); + Mat dstStorage(storage.size(), CV_8UC1, Scalar::all(0)); + Mat actual = inPlace ? src : dstStorage(roi); + cv::flip(src, actual, flipCode); + EXPECT_EQ(0, cvtest::norm(expected, actual, NORM_INF)); + Mat expectedStorage(storage.size(), CV_8UC1, Scalar::all(0)); + expected.copyTo(expectedStorage(roi)); + EXPECT_EQ(0, cvtest::norm(expectedStorage, inPlace ? storage : dstStorage, NORM_INF)); +} + +INSTANTIATE_TEST_CASE_P(Core_Flip, FlipWideTest, testing::Combine( + testing::Values(Size(255, 1), Size(256, 2), Size(257, 3), Size(513, 3), + Size(1025, 4), Size(3550, 3)), + testing::Values(-1, 1), testing::Bool(), testing::Bool())); + +// Multi-channel byte matrices may have a row step not divisible by elemSize(). +typedef testing::TestWithParam > TransformByteStepTest; + +TEST_P(TransformByteStepTest, accuracy) +{ + const int type = get<0>(GetParam()); + const int operation = get<1>(GetParam()); + const int paddedSide = get<2>(GetParam()); + const Size size(17, 9); + const Size dstSize = operation == ROTATE_180 ? size : Size(size.height, size.width); + const size_t srcStep = size.width * CV_ELEM_SIZE(type) + (paddedSide != 1 ? 1 : 0); + const size_t dstStep = dstSize.width * CV_ELEM_SIZE(type) + (paddedSide != 0 ? 1 : 0); + vector srcBuffer(srcStep * size.height, 0); + vector dstBuffer(dstStep * dstSize.height, 0); + Mat src(size, type, srcBuffer.data(), srcStep); + Mat actual(dstSize, type, dstBuffer.data(), dstStep); + theRNG().fill(src, RNG::UNIFORM, 0, 256); + Mat expected(dstSize, type); + for (int y = 0; y < size.height; ++y) + for (int x = 0; x < size.width; ++x) + { + int dy = x, dx = y; + if (operation == ROTATE_90_CLOCKWISE) + dx = size.height - 1 - y; + else if (operation == ROTATE_180) + { + dy = size.height - 1 - y; + dx = size.width - 1 - x; + } + else if (operation == ROTATE_90_COUNTERCLOCKWISE) + dy = size.width - 1 - x; + std::memcpy(expected.ptr(dy) + dx * expected.elemSize(), + src.ptr(y) + x * src.elemSize(), src.elemSize()); + } + if (operation == -1) + cv::transpose(src, actual); + else + cv::rotate(src, actual, operation); + EXPECT_EQ(0, cvtest::norm(expected, actual, NORM_INF)); + for (int y = 0; y < dstSize.height; ++y) + for (size_t x = dstSize.width * actual.elemSize(); x < dstStep; ++x) + EXPECT_EQ(0, dstBuffer[y * dstStep + x]); +} + +INSTANTIATE_TEST_CASE_P(Core_Transform, TransformByteStepTest, testing::Combine( + testing::Values(CV_8UC2, CV_8UC4, CV_8UC(8)), + testing::Values(-1, 0, 1, 2), testing::Values(0, 1, 2))); + struct TransposeOp : public BaseElemWiseOp { TransposeOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}