diff --git a/adapters/opencv/intrinsiccv_hal.cpp b/adapters/opencv/intrinsiccv_hal.cpp index 3076ea6d534b5d9bd1588e02e8e2c297c45fb2a9..a509b335391572df5b0c010529328b83692ab259 100644 --- a/adapters/opencv/intrinsiccv_hal.cpp +++ b/adapters/opencv/intrinsiccv_hal.cpp @@ -670,6 +670,15 @@ int convertTo(const uchar *src_data, size_t src_step, int src_depth, uchar *dst_data, size_t dst_step, int dst_depth, int width, int height, double scale, double shift) { if (src_depth != dst_depth) { + // type conversion + if (scale == 1.0 && shift == 0.0) { + // uint8 to float32 + if (src_depth == CV_8U && dst_depth == CV_32F) { + return convert_error(intrinsiccv_type_conversion_u8_f32( + reinterpret_cast(src_data), src_step, + reinterpret_cast(dst_data), dst_step, width, height)); + } + } return CV_HAL_ERROR_NOT_IMPLEMENTED; } diff --git a/intrinsiccv/include/intrinsiccv/intrinsiccv.h b/intrinsiccv/include/intrinsiccv/intrinsiccv.h index c58397274cc4d0bdf683a22de17250ab78ef97cd..6f97ebeaa9e35cb3ce541fb713202209d6c0b380 100644 --- a/intrinsiccv/include/intrinsiccv/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv/intrinsiccv.h @@ -666,6 +666,35 @@ INTRINSICCV_API_DECLARATION(intrinsiccv_yuv_sp_to_bgra_u8, const uint8_t *src_y, size_t dst_stride, size_t width, size_t height, bool is_nv21); +/// Converts the elements in `src` from an integer type to a floating-point +/// type, then stores the result in `dst`. +/// +/// Each resulting element is saturated, i.e. it is the smallest/largest +/// number of the type of the element if the result would underflow/overflow. +/// Source and destination data length is `width` * `height`. Number of elements +/// is limited to @ref INTRINSICCV_MAX_IMAGE_PIXELS. +/// +/// @param src Pointer to the source data. Must be non-null. +/// @param src_stride Distance in bytes from the start of one row to the +/// start of the next row for the source data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes from the start of one row to the +/// start of the next row for the destination data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param width Number of pixels in a row. +/// @param height Number of rows in the data. +/// +INTRINSICCV_API_DECLARATION(intrinsiccv_type_conversion_u8_f32, + const uint8_t *src, size_t src_stride, float *dst, + size_t dst_stride, size_t width, size_t height); +/// @copydoc intrinsiccv_type_conversion_u8_f32 +INTRINSICCV_API_DECLARATION(intrinsiccv_type_conversion_s8_f32, + const int8_t *src, size_t src_stride, float *dst, + size_t dst_stride, size_t width, size_t height); + /// Performs a comparison of each element's value in `src` with respect to a /// caller defined threshold. The strictly larger elements are set to /// `value` and the rest to 0. diff --git a/intrinsiccv/include/intrinsiccv/sve2.h b/intrinsiccv/include/intrinsiccv/sve2.h index cd326d0c79b906ed8e510b173e81fd0fd66ec5d2..df7b4f3e958dbe46d26d0ec54369f8c6133bab4f 100644 --- a/intrinsiccv/include/intrinsiccv/sve2.h +++ b/intrinsiccv/include/intrinsiccv/sve2.h @@ -491,7 +491,7 @@ class RemainingPathAdapter : public OperationBase { } }; // end of class RemainingPathAdapter -// Shorthand for applying a generic unrolled NEON operation. +// Shorthand for applying a generic unrolled SVE2 operation. template void apply_operation_by_rows(OperationType &operation, ArgTypes &&...args) INTRINSICCV_STREAMING_COMPATIBLE { diff --git a/intrinsiccv/src/conversions/int8_to_float_api.cpp b/intrinsiccv/src/conversions/int8_to_float_api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7b06e5bed689efc3a948d704f3647065fd8eed6 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_api.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "intrinsiccv/dispatch.h" +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/types.h" + +namespace intrinsiccv { + +namespace neon { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace neon + +namespace sve2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace sve2 + +namespace sme2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace sme2 + +#define INTRINSICCV_DEFINE_C_API(name, type) \ + INTRINSICCV_MULTIVERSION_C_API( \ + name, intrinsiccv::neon::type_conversion_int_to_float, \ + INTRINSICCV_SVE2_IMPL_IF( \ + intrinsiccv::sve2::type_conversion_int_to_float), \ + intrinsiccv::sme2::type_conversion_int_to_float) + +INTRINSICCV_DEFINE_C_API(intrinsiccv_type_conversion_u8_f32, uint8_t); +INTRINSICCV_DEFINE_C_API(intrinsiccv_type_conversion_s8_f32, int8_t); +} // namespace intrinsiccv diff --git a/intrinsiccv/src/conversions/int8_to_float_neon.cpp b/intrinsiccv/src/conversions/int8_to_float_neon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c164737b21d08ea6900bdcd05ae63757597c089 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_neon.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/neon.h" + +namespace intrinsiccv::neon { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T*, size_t, float*, + size_t, size_t, size_t) { + return INTRINSICCV_ERROR_NOT_IMPLEMENTED; +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::neon diff --git a/intrinsiccv/src/conversions/int8_to_float_sc.h b/intrinsiccv/src/conversions/int8_to_float_sc.h new file mode 100644 index 0000000000000000000000000000000000000000..e01b8dff7de5278a7d192e74a4d6dc61aadda7de --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sc.h @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef INTRINSICCV_FLOAT_TO_INT_SC_H +#define INTRINSICCV_FLOAT_TO_INT_SC_H + +#include + +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/sve2.h" + +namespace INTRINSICCV_TARGET_NAMESPACE { + +template +class int_to_float_operation final { + public: + using SrcVecTraits = INTRINSICCV_TARGET_NAMESPACE::VecTraits< + std::conditional_t, int8_t, uint8_t>>; + using SrcVectorType = typename SrcVecTraits::VectorType; + using DstVecTraits = INTRINSICCV_TARGET_NAMESPACE::VecTraits; + using DstVectorType = typename DstVecTraits::VectorType; + + using VecTraits = DstVecTraits; + + void process_row(size_t width, Columns src, + Columns dst) { + LoopUnroll{width, VecTraits::num_lanes()} + .unroll_twice([&](size_t step) INTRINSICCV_STREAMING_COMPATIBLE { + svbool_t pg = VecTraits::svptrue(); + DstVectorType dst_vector1 = vector_path(pg, &src[0]); + DstVectorType dst_vector2 = vector_path( + pg, &src.at(ptrdiff_t(VecTraits::num_lanes()))[0]); + svst1(pg, &dst[0], dst_vector1); + svst1_vnum(pg, &dst[0], 1, dst_vector2); + src += ptrdiff_t(step); + dst += ptrdiff_t(step); + }) + .remaining([&](size_t length, size_t) INTRINSICCV_STREAMING_COMPATIBLE { + size_t index = 0; + svbool_t pg = VecTraits::svwhilelt(index, length); + while (svptest_first(VecTraits::svptrue(), pg)) { + DstVectorType dst_vector = + vector_path(pg, &src[ptrdiff_t(index)]); + svst1(pg, &dst[ptrdiff_t(index)], dst_vector); + // Update loop counter and calculate the next governing predicate. + index += VecTraits::num_lanes(); + pg = VecTraits::svwhilelt(index, length); + } + }); + } + + private: + template , int> = 0> + DstVectorType vector_path(svbool_t& pg, + const T* src) INTRINSICCV_STREAMING_COMPATIBLE { + svuint32_t src_vector = svld1ub_u32(pg, src); + return svcvt_f32_u32_x(pg, src_vector); + } + + template , int> = 0> + DstVectorType vector_path(svbool_t& pg, + const T* src) INTRINSICCV_STREAMING_COMPATIBLE { + svint32_t src_vector = svld1sb_s32(pg, src); + return svcvt_f32_s32_x(pg, src_vector); + } +}; // end of class int_to_float_operation + +template +static intrinsiccv_error_t type_conversion_int_to_float_sc( + const T* src, size_t src_stride, float* dst, size_t dst_stride, + size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTER_AND_STRIDE(src, src_stride); + CHECK_POINTER_AND_STRIDE(dst, dst_stride); + CHECK_IMAGE_SIZE(width, height); + + int_to_float_operation operation; + Rectangle rect{width, height}; + Rows src_rows{src, src_stride}; + Rows dst_rows{dst, dst_stride}; + zip_rows(operation, rect, src_rows, dst_rows); + + return INTRINSICCV_OK; +} + +} // namespace INTRINSICCV_TARGET_NAMESPACE + +#endif // INTRINSICCV_INT8_TO_FLOAT_SC_H diff --git a/intrinsiccv/src/conversions/int8_to_float_sme2.cpp b/intrinsiccv/src/conversions/int8_to_float_sme2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a5f3a758186992fee49e65b37d1e37b9b51e8cd1 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sme2.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "int8_to_float_sc.h" + +namespace intrinsiccv::sme2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height) { + return type_conversion_int_to_float_sc(src, src_stride, dst, dst_stride, + width, height); +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::sme2 diff --git a/intrinsiccv/src/conversions/int8_to_float_sve2.cpp b/intrinsiccv/src/conversions/int8_to_float_sve2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b19ce007775868cb3fc432cccb183c3f3aea31a9 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sve2.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "int8_to_float_sc.h" + +namespace intrinsiccv::sve2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height) { + return type_conversion_int_to_float_sc(src, src_stride, dst, dst_stride, + width, height); +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::sve2 diff --git a/scripts/ci.sh b/scripts/ci.sh index 3eda8c4f964cea8bd70c2e9c1f3f51dd947aee40..60f76f2c270611b770af0186176a3a9b3bf82fcb 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -49,16 +49,17 @@ ninja -C build/gcc # Run tests LONG_VECTOR_TESTS="GRAY2.*:RGB*" +EXCLUDE_TYPE_CONVERSION_TESTS="-TypeConversion*" TESTRESULT=0 qemu-aarch64 build/test/framework/intrinsiccv-framework-test --gtest_output=xml:build/test-results/ || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}": --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme=off \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sve128/ --vector-length=16 || TESTRESULT=1 qemu-aarch64 -cpu max,sve2048=on,sve-default-vector-length=256,sme=off \ build/test/api/intrinsiccv-api-test --gtest_filter="${LONG_VECTOR_TESTS}" --gtest_output=xml:build/test-results/clang-sve2048/ --vector-length=256 || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme512=on \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sme/ --vector-length=64 || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}" --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 scripts/prefix_testsuite_names.py build/test-results/clang-neon/intrinsiccv-api-test.xml "clang-neon." scripts/prefix_testsuite_names.py build/test-results/clang-sve128/intrinsiccv-api-test.xml "clang-sve128." @@ -77,7 +78,7 @@ if [[ $(dpkg --print-architecture) = arm64 ]]; then -DINTRINSICCV_ENABLE_SME2=OFF \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -Wno-pass-failed" ninja -C build/sanitize intrinsiccv-api-test - build/sanitize/test/api/intrinsiccv-api-test + build/sanitize/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}" fi # Build benchmarks, just to prevent bitrot. diff --git a/test/api/test_int8_to_float.cpp b/test/api/test_int8_to_float.cpp new file mode 100644 index 0000000000000000000000000000000000000000..13d66cca5322d6cdccb5de85cda1460c338334d9 --- /dev/null +++ b/test/api/test_int8_to_float.cpp @@ -0,0 +1,301 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "framework/array.h" +#include "framework/generator.h" +#include "framework/operation.h" +#include "framework/utils.h" +#include "intrinsiccv/intrinsiccv.h" +#include "test_config.h" + +#define INTRINSICCV_TYPE_CONVERSION(itype, input_type_name, otype, \ + output_type_name) \ + INTRINSICCV_DIFF_IO_API( \ + type_conversion, \ + intrinsiccv_type_conversion_##input_type_name##_##output_type_name, \ + itype, otype) + +INTRINSICCV_TYPE_CONVERSION(uint8_t, u8, float, f32); +INTRINSICCV_TYPE_CONVERSION(int8_t, s8, float, f32); + +template +class TypeConversionTest final { + protected: + static constexpr InputType min() { + return std::numeric_limits::min(); + } + static constexpr InputType max() { + return std::numeric_limits::max(); + } + + struct Elements { + size_t width{}; + size_t height{}; + + std::vector> source_rows; + std::vector> expected_rows; + + Elements(size_t _width, size_t _height, + std::vector>&& _source_rows, + std::vector>&& _expected_rows) + : width(_width), + height(_height), + source_rows(std::move(_source_rows)), + expected_rows(std::move(_expected_rows)) {} + }; + + const Elements test_case_custom = { + // clang-format off + 4, 6, + {{ + { min(), min(), max() - 1, max() }, + { min(), min(), min(), min() }, + { min(), min(), min(), max() - 1 }, + { max() - 1, max(), 113, 114 }, + { 112, 113, 114, 115 }, + { 12, 12, 12, 12 } + }}, + {{ + { min(), min(), max() - 1.0, max() }, + { min(), min(), min(), min() }, + { min(), min(), min(), max() - 1.0 }, + { max() - 1.0, max(), 113.0, 114.0 }, + { 112.0, 113.0, 114.0, 115.0 }, + { 12.0, 12.0, 12.0, 12.0 } + }} + // clang-format on + }; + + public: + void test_custom() { + const size_t& width = test_case_custom.width; + const size_t& height = test_case_custom.height; + + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual(width, height, 1, 1); + + for (size_t i = 0; i < height; i++) { + source.set(i, 0, test_case_custom.source_rows[i]); + expected.set(i, 0, test_case_custom.expected_rows[i]); + } + + ASSERT_EQ(INTRINSICCV_OK, (type_conversion())( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height)); + + EXPECT_EQ_ARRAY2D(expected, actual); + } + + void test_fill(const size_t width, const size_t height) { + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual(width, height, 1, 1); + + source.fill(13); + expected.fill(13.0); + + actual.fill(0); + + ASSERT_EQ(INTRINSICCV_OK, (type_conversion()( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height))); + + EXPECT_EQ_ARRAY2D(expected, actual); + } +}; + +template +class TypeConversionTestLinear final { + public: + // minimum_size set by caller to trigger the 'big' path. + void test_scalar(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() - 1; + test_linear(width, minimum_size); + } + + void test_vector(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() * 2; + test_linear(width, minimum_size); + } + + protected: + static constexpr InputType min() { + return std::numeric_limits::min(); + } + static constexpr InputType max() { + return std::numeric_limits::max(); + } + + void calculate_expected(const test::Array2D& source, + test::Array2D& expected) { + for (size_t hindex = 0; hindex < source.height(); ++hindex) { + for (size_t vindex = 0; vindex < source.width(); ++vindex) { + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + *expected.at(hindex, vindex) = *source.at(hindex, vindex); + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + } + } + } + + private: + class GenerateLinearSeries : public test::Generator { + public: + explicit GenerateLinearSeries(InputType start_from) + : counter_{start_from} {} + + std::optional next() override { return counter_++; } + + private: + InputType counter_; + }; // end of class GenerateLinearSeries + + void test_linear(size_t width, size_t minimum_size) { + size_t image_size = + std::max(minimum_size, static_cast(max() - min())); + size_t height = image_size / width + 1; + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual = + test::Array2D(width, height, 1, 1); + + GenerateLinearSeries generator(min()); + + source.fill(generator); + + calculate_expected(source, expected); + + ASSERT_EQ(INTRINSICCV_OK, (type_conversion()( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height))); + + EXPECT_EQ_ARRAY2D(expected, actual); + } +}; + +template +class TypeConversion : public testing::Test {}; + +using ElementTypes = + ::testing::Types, std::pair>; + +// Tests intrinsiccv_type_conversion API. +TYPED_TEST_SUITE(TypeConversion, ElementTypes); + +TYPED_TEST(TypeConversion, NullPointer) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + InputType src[1] = {}; + OutputType dst[1]; + test::test_null_args(type_conversion(), src, + sizeof(InputType), dst, sizeof(OutputType), 1, 1); +} + +TYPED_TEST(TypeConversion, OversizeImage) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + InputType src[1] = {}; + OutputType dst[1]; + EXPECT_EQ(INTRINSICCV_ERROR_RANGE, + (type_conversion()( + src, sizeof(InputType), dst, sizeof(OutputType), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1))); + EXPECT_EQ(INTRINSICCV_ERROR_RANGE, + (type_conversion()( + src, sizeof(InputType), dst, sizeof(OutputType), 1, + INTRINSICCV_MAX_IMAGE_PIXELS + 1))); + EXPECT_EQ( + INTRINSICCV_ERROR_RANGE, + (type_conversion()( + src, sizeof(TypeParam), dst, sizeof(OutputType), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1))); +} + +TYPED_TEST(TypeConversion, Custom) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_custom(); +} + +TYPED_TEST(TypeConversion, Scalar) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_scalar(); +} +TYPED_TEST(TypeConversion, Vector) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_vector(); +} + +TYPED_TEST(TypeConversion, Scalar1Tbx) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_scalar(2500); + TypeConversionTestLinear{}.test_scalar(2500); +} +TYPED_TEST(TypeConversion, Vector1Tbx) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_vector(2500); + TypeConversionTestLinear{}.test_vector(2500); +} + +TYPED_TEST(TypeConversion, CustomFits128VectorSize) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 1); +} +TYPED_TEST(TypeConversion, CustomFits128VectorSize2x) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 2); +} +TYPED_TEST(TypeConversion, CustomFits128VectorSize3x) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 3); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 4); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize2x) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 8); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize3x) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(6, 8); +} +TYPED_TEST(TypeConversion, Custom128OneRemaining) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(1, 17); +} +TYPED_TEST(TypeConversion, Custom128AllButOneRemaining) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(5, 3); +} +TYPED_TEST(TypeConversion, CustomAboutHalfRemaining) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(19, 2); +} +TYPED_TEST(TypeConversion, CustomEmpty) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(0, 0); +} +TYPED_TEST(TypeConversion, CustomOne) { + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(1, 1); +} diff --git a/test/framework/array.h b/test/framework/array.h index f854c98191ef4a40bb93c7444d990a6de0de074c..338a3bd2c7dd36144ece54b57c4819aec26e5dd4 100644 --- a/test/framework/array.h +++ b/test/framework/array.h @@ -124,6 +124,22 @@ class Array2D : public TwoDimensional { } } + // Sets values in a row starting at a given column from a const vector. + void set(size_t row, size_t column, const std::vector &values) { + ASSERT_EQ(valid(), true) << "Array is invalid."; + ASSERT_GE(width() - column, values.size()); + + ElementType *ptr = at(row, column); + if (!ptr) { + return; + } + + size_t index = 0; + for (ElementType value : values) { + ptr[index++] = value; + } + } + // Sets values in a row starting at a given column. void set(size_t row, size_t column, std::initializer_list values) { diff --git a/test/framework/utils.h b/test/framework/utils.h index 35e41e62ed92044d9d3694508472b1c8e8f6eeba..83943d0d05cf1c12a8473e7fd7d3cd77f29e6eb5 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -24,6 +24,14 @@ return impl; \ } +#define INTRINSICCV_DIFF_IO_API(name, impl, itype, otype) \ + template , bool> = true, \ + std::enable_if_t, bool> = true> \ + static decltype(auto) name() { \ + return impl; \ + } + // Generates a fatal failure with a generic message, and returns with a given // value. #define TEST_FAIL_WITH(return_value, message) \