diff --git a/adapters/opencv/doc-opencv.md b/adapters/opencv/doc-opencv.md index b8deda1b701c51c7a32a76b591eb8bad4d92d295..a197c78f06373e0c1a4237354ef82d34b246d356 100644 --- a/adapters/opencv/doc-opencv.md +++ b/adapters/opencv/doc-opencv.md @@ -1,5 +1,5 @@ @@ -107,7 +107,7 @@ Notes on parameters: * margins are not supported. * `dx`,`dy` - either vertical `{dx,dy} == {0,1}` or horizontal `{dx,dy == 1,0` operation is supported. -### `canny` +### `canny` (Experimental) Apply Canny edge detection filter to a given image. Notes on parameters: diff --git a/adapters/opencv/intrinsiccv_hal.cpp b/adapters/opencv/intrinsiccv_hal.cpp index 401b20a7dfea04189addf59d63945929decc08a0..4e2f2af04a4472ad73c322618dd6b1cb10c23648 100644 --- a/adapters/opencv/intrinsiccv_hal.cpp +++ b/adapters/opencv/intrinsiccv_hal.cpp @@ -495,6 +495,7 @@ int sobel(const uchar *src_data, size_t src_step, uchar *dst_data, return CV_HAL_ERROR_NOT_IMPLEMENTED; } +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY int canny(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int width, int height, int cn, double lowThreshold, double highThreshold, int ksize, bool L2gradient) { @@ -520,6 +521,7 @@ int canny(const uchar *src_data, size_t src_step, uchar *dst_data, static_cast(width), static_cast(height), lowThreshold, highThreshold)); } +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY int transpose(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height, diff --git a/adapters/opencv/intrinsiccv_hal.h b/adapters/opencv/intrinsiccv_hal.h index c6cba2ff218bf32f98baa876111b3536f29e0f4f..1eab08d6e973c27a40186d6937c4ab598cc0d991 100644 --- a/adapters/opencv/intrinsiccv_hal.h +++ b/adapters/opencv/intrinsiccv_hal.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 @@ -70,9 +70,11 @@ int sobel(const uchar *src_data, size_t src_step, uchar *dst_data, int margin_bottom, int dx, int dy, int ksize, double scale, double delta, int border_type); +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY int canny(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int width, int height, int cn, double lowThreshold, double highThreshold, int ksize, bool L2gradient); +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY int transpose(const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, int src_width, int src_height, int element_size); @@ -224,6 +226,7 @@ static inline int intrinsiccv_sobel_with_fallback( #undef cv_hal_sobel #define cv_hal_sobel intrinsiccv_sobel_with_fallback +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY // canny static inline int intrinsiccv_canny_with_fallback( const uchar *src_data, size_t src_step, uchar *dst_data, size_t dst_step, @@ -235,6 +238,7 @@ static inline int intrinsiccv_canny_with_fallback( } #undef cv_hal_canny #define cv_hal_canny intrinsiccv_canny_with_fallback +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY #endif // OPENCV_IMGPROC_HAL_REPLACEMENT_HPP diff --git a/intrinsiccv/CMakeLists.txt b/intrinsiccv/CMakeLists.txt index de7a5473dc89e4e790c3c520b196267392acbb4e..3c5b6040f085059c75e4fd5dfcad2d10a17abe67 100644 --- a/intrinsiccv/CMakeLists.txt +++ b/intrinsiccv/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023-2024 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 @@ -11,7 +11,10 @@ include(CheckCXXCompilerFlag) option(INTRINSICCV_ENABLE_SVE2 "Explicitly enables or disables SVE2 code paths for all supported algorithms" OFF) option(INTRINSICCV_ENABLE_SVE2_SELECTIVELY "Explicitly enables or disables SVE2 code paths for selected algorithms" ON) option(INTRINSICCV_ENABLE_SME2 "Explicitly enables or disables SME2 code paths for all supported algorithms" ON) -option(INTRINSICCV_ASSUME_128BIT_SVE2 "If turned ON 128-bit SVE2 vector length is assumed" OFF) +option(INTRINSICCV_ASSUME_128BIT_SVE2 "Internal - If turned ON 128-bit SVE2 vector length is assumed" OFF) +option(INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE "Internal - If turned ON interleaving loads and stores are preferred instead of continuous loads and stores" OFF) +option(INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY "Internal - Enable experimental Canny algorithm" OFF) +option(INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV "Internal - If turned ON Canny algorithm creates bit exact result compared to OpenCV's original implementation" ON) if (INTRINSICCV_ENABLE_SVE2 AND INTRINSICCV_ENABLE_SVE2_SELECTIVELY) message(FATAL_ERROR "[IntrinsicCV] INTRINSICCV_ENABLE_SVE2 and INTRINSICCV_ENABLE_SVE2_SELECTIVELY cannot be set at the same time") @@ -45,6 +48,12 @@ if (INTRINSICCV_ENABLE_SME2) endif() endif() +if(INTRINSICCV_BUILD_SVE2 AND INTRINSICCV_ENABLE_SVE2) + set(INTRINSICCV_ALWAYS_ENABLE_SVE2 ON) +endif() + +configure_file("${CMAKE_CURRENT_LIST_DIR}/include/intrinsiccv/config.h.in" "include/intrinsiccv/config.h") + file(GLOB INTRINSICCV_API_SOURCES "${CMAKE_CURRENT_LIST_DIR}/src/*_api.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/**/*_api.cpp" @@ -67,6 +76,7 @@ file(GLOB INTRINSICCV_SME2_SOURCES set(INTRINSICCV_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include" + "${CMAKE_CURRENT_BINARY_DIR}/include" ) set(INTRINSICCV_WARNING_FLAGS @@ -99,14 +109,6 @@ set(INTRINSICCV_CXX_FLAGS ${INTRINSICCV_SDL_FLAGS} ) -if(INTRINSICCV_ENABLE_SVE2) - list(APPEND INTRINSICCV_CXX_FLAGS "-DINTRINSICCV_ALWAYS_ENABLE_SVE2=1") -endif() - -if (INTRINSICCV_ASSUME_128BIT_SVE2) - list(APPEND INTRINSICCV_CXX_FLAGS "-DINTRINSICCV_SVE2_128=1") -endif() - if (CMAKE_BUILD_TYPE EQUAL "DEBUG") list(APPEND INTRINSICCV_CXX_FLAGS "-O0" "-g") else() diff --git a/intrinsiccv/include/intrinsiccv/config.h b/intrinsiccv/include/intrinsiccv/config.h.in similarity index 66% rename from intrinsiccv/include/intrinsiccv/config.h rename to intrinsiccv/include/intrinsiccv/config.h.in index d4f9a8a27fa2b3ed5aeb18032bd7bbd95637bb7a..a842fdfff12ae45ccd445cd7a22e6a11b991584d 100644 --- a/intrinsiccv/include/intrinsiccv/config.h +++ b/intrinsiccv/include/intrinsiccv/config.h.in @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 @@ -7,16 +7,15 @@ // Main configuration switches. -// Set to '1' if interleaving loads and stores are preferred, otherwise it is -// set to '0'. -#ifndef INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE -#define INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE 0 -#endif +#cmakedefine01 INTRINSICCV_ALWAYS_ENABLE_SVE2 -// Set to '1' if 128-bit SVE2 VL is assumed, otherwise it is set to '0'. -#ifndef INTRINSICCV_SVE2_128 -#define INTRINSICCV_SVE2_128 0 -#endif +#cmakedefine01 INTRINSICCV_ASSUME_128BIT_SVE2 + +#cmakedefine01 INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE + +#cmakedefine01 INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY + +#cmakedefine01 INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV // Set to '1' if compiling NEON code paths, otherwise it is set to '0'. #ifndef INTRINSICCV_TARGET_NEON @@ -33,19 +32,6 @@ #define INTRINSICCV_TARGET_SME2 0 #endif -// Set to '1' to always enable SVE2 code paths, otherwise enable them -// selectively. -#ifndef INTRINSICCV_ALWAYS_ENABLE_SVE2 -#define INTRINSICCV_ALWAYS_ENABLE_SVE2 0 -#endif - -// OpenCV requires that diagonal non-maxima-suppressions are calculated as -// curr > prev && curr > next -// This is different from other directions where -// curr > prev && curr >= next -// is used. Furthermore, the elements in the diagonal directions are reversed. -#define INTRINSICCV_DIRECTIONAL_MASKING_CONFORM_OPENCV 1 - // Derived configuration switches and macros below. #if INTRINSICCV_TARGET_NEON @@ -57,8 +43,8 @@ #endif #if INTRINSICCV_TARGET_SME2 -#undef INTRINSICCV_SVE2_128 -#define INTRINSICCV_SVE2_128 0 +#undef INTRINSICCV_ASSUME_128BIT_SVE2 +#define INTRINSICCV_ASSUME_128BIT_SVE2 0 #define INTRINSICCV_TARGET_FN_ATTRS INTRINSICCV_ATTR_SECTION(".text.sme2") #define INTRINSICCV_LOCALLY_STREAMING __arm_locally_streaming #define INTRINSICCV_STREAMING_COMPATIBLE __arm_streaming_compatible diff --git a/intrinsiccv/include/intrinsiccv/intrinsiccv.h b/intrinsiccv/include/intrinsiccv/intrinsiccv.h index 19dc8573bef3fac8c6adce1988db276d9ff85da7..e352f29ef35c610612666fe5b40bff394556b614 100644 --- a/intrinsiccv/include/intrinsiccv/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv/intrinsiccv.h @@ -915,6 +915,7 @@ intrinsiccv_error_t intrinsiccv_sobel_3x3_horizontal_s16_u8( const uint8_t *src, size_t src_stride, int16_t *dst, size_t dst_stride, size_t width, size_t height, size_t channels); +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY /// Canny edge detector for uint8_t grayscale input. Output is also a uint8_t /// grayscale image. Width and height are the same for input and output. Number /// of pixels is limited to @ref INTRINSICCV_MAX_IMAGE_PIXELS. @@ -947,6 +948,7 @@ intrinsiccv_error_t intrinsiccv_canny_u8(const uint8_t *src, size_t src_stride, size_t width, size_t height, double low_threshold, double high_threshold); +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY /// Creates a filter context according to the parameters. /// diff --git a/intrinsiccv/src/analysis/canny_neon.cpp b/intrinsiccv/src/analysis/canny_neon.cpp index 94b99d696b9bef6f4f25007c1df10b884aa7edc7..a76bddecf81042483edfce14b20cfb2e14a5b4ad 100644 --- a/intrinsiccv/src/analysis/canny_neon.cpp +++ b/intrinsiccv/src/analysis/canny_neon.cpp @@ -6,6 +6,8 @@ #include "intrinsiccv/intrinsiccv.h" #include "intrinsiccv/neon.h" +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY + namespace intrinsiccv::neon { // Container to hold strong edges. @@ -236,9 +238,14 @@ T *remove_constant_pool_usage(T *ptr) { // bottom-left} and {left, right} neighbouring values as governed by the // associated directions. // -// If INTRINSICCV_DIRECTIONAL_MASKING_CONFORM_OPENCV is set to 1, the diagonal -// directions are swapped, and lanes in 'next_row_by_directions', where the -// direction is diagonal, are incremented by 1 saturating. +// If INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV is set to 1: +// - diagonal directions are swapped +// - diagonal non-maxima-suppressions are calculated as: +// curr > prev && curr > next +// This is different from other directions where it is calculated as: +// curr > prev && curr >= next +// (To achieve this lanes in 'next_row_by_directions', where the direction is +// diagonal, are incremented by 1 saturating.) static void directional_masking(const int16_t *prev_rows, const int16_t *curr_rows, const int16_t *next_rows, int16x8_t directions, @@ -252,7 +259,7 @@ static void directional_masking(const int16_t *prev_rows, static constexpr int8_t kIndices[4 * kNumLanesS8] = { /* Lane offsets holding 'lane number + VL' */ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -#if INTRINSICCV_DIRECTIONAL_MASKING_CONFORM_OPENCV +#if INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV /* Table lookup indices for previous row */ 32, 32, -2, 2, 0, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* Table lookup indices for next row */ @@ -312,7 +319,7 @@ static void directional_masking(const int16_t *prev_rows, // 2.1 tmp_indices_0 = vqtbl1q_s8(next_row_table, dir); -#if INTRINSICCV_DIRECTIONAL_MASKING_CONFORM_OPENCV +#if INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV int8x16_t opencv_tmp_indices_0 = tmp_indices_0; #endif // 2.2 @@ -348,7 +355,7 @@ static void directional_masking(const int16_t *prev_rows, curr_row_by_directions = vreinterpretq_s16_s8(curr_row.val[1]); next_row_by_directions = vreinterpretq_s16_s8(next_row_by_dir); -#if INTRINSICCV_DIRECTIONAL_MASKING_CONFORM_OPENCV +#if INTRINSICCV_CANNY_ALGORITHM_CONFORM_OPENCV // Reuse temporary indexing values from step 2.1 to saturating add one to // diagonal values in the next row. This works only because of the domain of // input values is restricted. @@ -539,3 +546,5 @@ extern "C" INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t intrinsiccv_canny_u8( } } // namespace intrinsiccv::neon + +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY diff --git a/intrinsiccv/src/conversions/rgb_to_rgb_sc.h b/intrinsiccv/src/conversions/rgb_to_rgb_sc.h index 864cbe57824bd6b42211d51a7201240c5957ecfc..145d6fb778f2cd769f3e5a6e1e03f7f649dab2be 100644 --- a/intrinsiccv/src/conversions/rgb_to_rgb_sc.h +++ b/intrinsiccv/src/conversions/rgb_to_rgb_sc.h @@ -13,7 +13,8 @@ namespace intrinsiccv::sve2 { template class RGBToBGR final : -#if !INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE && INTRINSICCV_SVE2_128 +#if !INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE && \ + INTRINSICCV_ASSUME_128BIT_SVE2 public UsesTailPath, #endif public UnrollTwice { @@ -22,7 +23,8 @@ class RGBToBGR final : using VecTraits = sve2::VecTraits; using VectorType = typename VecTraits::VectorType; -#if INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || !INTRINSICCV_SVE2_128 +#if INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || \ + !INTRINSICCV_ASSUME_128BIT_SVE2 void vector_path(ContextType ctx, const ScalarType *src, ScalarType *dst) INTRINSICCV_STREAMING_COMPATIBLE { auto pg = ctx.predicate(); @@ -32,7 +34,8 @@ class RGBToBGR final : svst3(pg, dst, dst_vect); } -#else // INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || !INTRINSICCV_SVE2_128 +#else // INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || + // !INTRINSICCV_ASSUME_128BIT_SVE2 explicit RGBToBGR(svuint8x4_t &indices) INTRINSICCV_STREAMING_COMPATIBLE : indices_{indices} { initialize_indices(); @@ -93,7 +96,8 @@ class RGBToBGR final : // Hold a reference because a sizeless types cannot be members. svuint8x4_t &indices_; -#endif // !INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || !INTRINSICCV_SVE2_128 +#endif // !INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || + // !INTRINSICCV_ASSUME_128BIT_SVE2 }; // end of class RGBToBGR template @@ -191,7 +195,8 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgb_to_bgr_u8_sc( Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; -#if INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || !INTRINSICCV_SVE2_128 +#if INTRINSICCV_PREFER_INTERLEAVING_LOAD_STORE || \ + !INTRINSICCV_ASSUME_128BIT_SVE2 RGBToBGR operation; #else svuint8x4_t table_indices; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91e782999c797ab4cb3a2e84e2b01aa365ee5bf7..b22697f90ed85b6c1bb4fa98276dd5f7e2d66cee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,10 +1,13 @@ -# SPDX-FileCopyrightText: 2023 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: 2023 - 2024 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.16) -set(INTRINSICCV_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../intrinsiccv/include) +set(INTRINSICCV_INCLUDE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../intrinsiccv/include + ${CMAKE_CURRENT_BINARY_DIR}/../intrinsiccv/include +) set(INTRINSICCV_TEST_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(INTRINSICCV_WARNING_FLAGS diff --git a/test/api/test_canny.cpp b/test/api/test_canny.cpp index 71b23c81d0fea0762c418f2d135d7c356179d42c..36f45577e0224d7ab3e386731118552f33e76a2f 100644 --- a/test/api/test_canny.cpp +++ b/test/api/test_canny.cpp @@ -7,6 +7,8 @@ #include "framework/utils.h" #include "intrinsiccv/intrinsiccv.h" +#if INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY + #define INTRINSICCV_CANNY(type, suffix) \ INTRINSICCV_API(canny, intrinsiccv_canny_##suffix, type) @@ -49,3 +51,5 @@ TYPED_TEST(CannyTest, ImageSize) { INTRINSICCV_MAX_IMAGE_PIXELS, INTRINSICCV_MAX_IMAGE_PIXELS, 0.0, 1.0)); } + +#endif // INTRINSICCV_EXPERIMENTAL_FEATURE_CANNY