diff options
Diffstat (limited to 'tests/validation')
-rw-r--r-- | tests/validation/CL/CMakeLists.txt | 4 | ||||
-rw-r--r-- | tests/validation/CL/Sobel3x3.cpp | 216 | ||||
-rw-r--r-- | tests/validation/CL/Sobel5x5.cpp | 215 | ||||
-rw-r--r-- | tests/validation/NEON/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/validation/NEON/Sobel3x3.cpp | 215 | ||||
-rw-r--r-- | tests/validation/NEON/Sobel5x5.cpp | 215 | ||||
-rw-r--r-- | tests/validation/Reference.cpp | 30 | ||||
-rw-r--r-- | tests/validation/Reference.h | 18 | ||||
-rw-r--r-- | tests/validation/ReferenceCPP.cpp | 20 | ||||
-rw-r--r-- | tests/validation/ReferenceCPP.h | 20 | ||||
-rw-r--r-- | tests/validation/TensorOperations.h | 104 |
11 files changed, 1047 insertions, 12 deletions
diff --git a/tests/validation/CL/CMakeLists.txt b/tests/validation/CL/CMakeLists.txt index 4144ec9975..9b67d1df45 100644 --- a/tests/validation/CL/CMakeLists.txt +++ b/tests/validation/CL/CMakeLists.txt @@ -29,7 +29,9 @@ set(arm_compute_test_validation_OPENCL_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.h ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseAnd.cpp -` ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Sobel3x3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Sobel5x5.cpp ) add_library(arm_compute_test_validation_OPENCL OBJECT diff --git a/tests/validation/CL/Sobel3x3.cpp b/tests/validation/CL/Sobel3x3.cpp new file mode 100644 index 0000000000..382e296180 --- /dev/null +++ b/tests/validation/CL/Sobel3x3.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "PaddingCalculator.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLSubTensor.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLSobel3x3.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute CL Sobel 3x3 function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode BorderMode used by the input tensor + * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT + * + * @return Computed output tensor. + */ +std::pair<CLTensor, CLTensor> compute_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst_x = create_tensor(shape, DataType::S16); + CLTensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + // Create sobel image configure function + CLSobel3x3 sobel_3x3; + sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value); + + // Allocate tensors + src.allocator()->allocate(); + dst_x.allocator()->allocate(); + dst_y.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst_x.info()->is_resizable()); + BOOST_TEST(!dst_y.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(CLAccessor(src), 0); + + // Compute function + sobel_3x3.run(); + + return std::make_pair(std::move(dst_x), std::move(dst_y)); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) +BOOST_AUTO_TEST_SUITE(Sobel3x3) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst_x = create_tensor(shape, DataType::S16); + CLTensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst_x.info()->is_resizable()); + BOOST_TEST(dst_y.info()->is_resizable()); + + // Create sobel 3x3 configure function + CLSobel3x3 sobel_3x3; + sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(shape); + ValidRegion dst_valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + validate(src.info()->valid_region(), src_valid_region); + validate(dst_x.info()->valid_region(), dst_valid_region); + validate(dst_y.info()->valid_region(), dst_valid_region); + + // Validate padding + PaddingCalculator calculator(shape.x(), 8); + + calculator.set_border_mode(border_mode); + calculator.set_border_size(1); + + const PaddingSize dst_padding = calculator.required_padding(); + + calculator.set_accessed_elements(16); + calculator.set_access_offset(-1); + + const PaddingSize src_padding = calculator.required_padding(); + + validate(src.info()->padding(), src_padding); + validate(dst_x.info()->padding(), dst_padding); + validate(dst_y.info()->padding(), dst_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<CLTensor, CLTensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + // Validate output + validate(CLAccessor(dst.first), ref_dst.first, valid_region); + validate(CLAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<CLTensor, CLTensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + // Validate output + validate(CLAccessor(dst.first), ref_dst.first, valid_region); + validate(CLAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/CL/Sobel5x5.cpp b/tests/validation/CL/Sobel5x5.cpp new file mode 100644 index 0000000000..b653808701 --- /dev/null +++ b/tests/validation/CL/Sobel5x5.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "PaddingCalculator.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLSubTensor.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLSobel5x5.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute CL Sobel 5x5 function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode BorderMode used by the input tensor + * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT + * + * @return Computed output tensor. + */ +std::pair<CLTensor, CLTensor> compute_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst_x = create_tensor(shape, DataType::S16); + CLTensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + // Create sobel image configure function + CLSobel5x5 sobel_5x5; + sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value); + + // Allocate tensors + src.allocator()->allocate(); + dst_x.allocator()->allocate(); + dst_y.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst_x.info()->is_resizable()); + BOOST_TEST(!dst_y.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(CLAccessor(src), 0); + + // Compute function + sobel_5x5.run(); + + return std::make_pair(std::move(dst_x), std::move(dst_y)); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) +BOOST_AUTO_TEST_SUITE(Sobel5x5) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst_x = create_tensor(shape, DataType::S16); + CLTensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst_x.info()->is_resizable()); + BOOST_TEST(dst_y.info()->is_resizable()); + + // Create sobel 5x5 configure function + CLSobel5x5 sobel_5x5; + sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(shape); + ValidRegion dst_valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + validate(src.info()->valid_region(), src_valid_region); + validate(dst_x.info()->valid_region(), dst_valid_region); + validate(dst_y.info()->valid_region(), dst_valid_region); + + // Validate padding + PaddingCalculator calculator(shape.x(), 8); + calculator.set_border_mode(border_mode); + calculator.set_border_size(2); + + const PaddingSize dst_padding = calculator.required_padding(); + + calculator.set_accessed_elements(16); + calculator.set_access_offset(-2); + + const PaddingSize src_padding = calculator.required_padding(); + + validate(src.info()->padding(), src_padding); + validate(dst_x.info()->padding(), dst_padding); + validate(dst_y.info()->padding(), dst_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<CLTensor, CLTensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + // Validate output + validate(CLAccessor(dst.first), ref_dst.first, valid_region); + validate(CLAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<CLTensor, CLTensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + // Validate output + validate(CLAccessor(dst.first), ref_dst.first, valid_region); + validate(CLAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/CMakeLists.txt b/tests/validation/NEON/CMakeLists.txt index a197f8cf0e..86bcce7e93 100644 --- a/tests/validation/NEON/CMakeLists.txt +++ b/tests/validation/NEON/CMakeLists.txt @@ -43,6 +43,8 @@ set(arm_compute_test_validation_NEON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/PixelWiseMultiplication.cpp ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MeanStdDev.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Sobel3x3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Sobel5x5.cpp ) add_library(arm_compute_test_validation_NEON OBJECT diff --git a/tests/validation/NEON/Sobel3x3.cpp b/tests/validation/NEON/Sobel3x3.cpp new file mode 100644 index 0000000000..8324614f0d --- /dev/null +++ b/tests/validation/NEON/Sobel3x3.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NESobel3x3.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "PaddingCalculator.h" +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon Sobel 3x3 function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode BorderMode used by the input tensor + * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT + * + * @return Computed output tensor. + */ +std::pair<Tensor, Tensor> compute_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst_x = create_tensor(shape, DataType::S16); + Tensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + // Create sobel image configure function + NESobel3x3 sobel_3x3; + sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value); + + // Allocate tensors + src.allocator()->allocate(); + dst_x.allocator()->allocate(); + dst_y.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst_x.info()->is_resizable()); + BOOST_TEST(!dst_y.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + sobel_3x3.run(); + + return std::make_pair(std::move(dst_x), std::move(dst_y)); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Sobel3x3) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst_x = create_tensor(shape, DataType::S16); + Tensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst_x.info()->is_resizable()); + BOOST_TEST(dst_y.info()->is_resizable()); + + // Create sobel 3x3 configure function + NESobel3x3 sobel_3x3; + sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(shape); + ValidRegion dst_valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + validate(src.info()->valid_region(), src_valid_region); + validate(dst_x.info()->valid_region(), dst_valid_region); + validate(dst_y.info()->valid_region(), dst_valid_region); + + // Validate padding + PaddingCalculator calculator(shape.x(), 8); + + calculator.set_border_mode(border_mode); + calculator.set_border_size(1); + + const PaddingSize dst_padding = calculator.required_padding(); + + calculator.set_accessed_elements(16); + calculator.set_access_offset(-1); + + const PaddingSize src_padding = calculator.required_padding(); + + validate(src.info()->padding(), src_padding); + validate(dst_x.info()->padding(), dst_padding); + validate(dst_y.info()->padding(), dst_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<Tensor, Tensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + // Validate output + validate(NEAccessor(dst.first), ref_dst.first, valid_region); + validate(NEAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<Tensor, Tensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + } + + // Validate output + validate(NEAccessor(dst.first), ref_dst.first, valid_region); + validate(NEAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Sobel5x5.cpp b/tests/validation/NEON/Sobel5x5.cpp new file mode 100644 index 0000000000..bb2d7baf79 --- /dev/null +++ b/tests/validation/NEON/Sobel5x5.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "PaddingCalculator.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NESobel5x5.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon Sobel 5x5 function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode BorderMode used by the input tensor + * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT + * + * @return Computed output tensor. + */ +std::pair<Tensor, Tensor> compute_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst_x = create_tensor(shape, DataType::S16); + Tensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + // Create sobel image configure function + NESobel5x5 sobel_5x5; + sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value); + + // Allocate tensors + src.allocator()->allocate(); + dst_x.allocator()->allocate(); + dst_y.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst_x.info()->is_resizable()); + BOOST_TEST(!dst_y.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + sobel_5x5.run(); + + return std::make_pair(std::move(dst_x), std::move(dst_y)); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Sobel5x5) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst_x = create_tensor(shape, DataType::S16); + Tensor dst_y = create_tensor(shape, DataType::S16); + + src.info()->set_format(Format::U8); + dst_x.info()->set_format(Format::S16); + dst_y.info()->set_format(Format::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst_x.info()->is_resizable()); + BOOST_TEST(dst_y.info()->is_resizable()); + + // Create sobel 5x5 configure function + NESobel5x5 sobel_5x5; + sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(shape); + ValidRegion dst_valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + validate(src.info()->valid_region(), src_valid_region); + validate(dst_x.info()->valid_region(), dst_valid_region); + validate(dst_y.info()->valid_region(), dst_valid_region); + + // Validate padding + PaddingCalculator calculator(shape.x(), 16); + + calculator.set_border_mode(border_mode); + calculator.set_border_size(2); + + const PaddingSize dst_padding = calculator.required_padding(); + + calculator.set_processed_elements(8); + calculator.set_access_offset(-2); + + const PaddingSize src_padding = calculator.required_padding(); + + validate(src.info()->padding(), src_padding); + validate(dst_x.info()->padding(), dst_padding); + validate(dst_y.info()->padding(), dst_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<Tensor, Tensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + // Validate output + validate(NEAccessor(dst.first), ref_dst.first, valid_region); + validate(NEAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode) +{ + uint8_t constant_border_value = 0; + + // Generate a random constant value if border_mode is constant + if(border_mode == BorderMode::CONSTANT) + { + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<uint8_t> distribution(0, 255); + constant_border_value = distribution(gen); + } + + // Compute function + std::pair<Tensor, Tensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value); + + // Compute reference + std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value); + + // Calculate valid region + ValidRegion valid_region = shape_to_valid_region(shape); + if(border_mode == BorderMode::UNDEFINED) + { + valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2)); + } + + // Validate output + validate(NEAccessor(dst.first), ref_dst.first, valid_region); + validate(NEAccessor(dst.second), ref_dst.second, valid_region); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/Reference.cpp b/tests/validation/Reference.cpp index 5f49caead0..be3f28b052 100644 --- a/tests/validation/Reference.cpp +++ b/tests/validation/Reference.cpp @@ -39,6 +39,36 @@ namespace test { namespace validation { +std::pair<RawTensor, RawTensor> Reference::compute_reference_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create reference + RawTensor ref_src = library->get(shape, Format::U8); + RawTensor ref_dst_x = library->get(shape, Format::S16); + RawTensor ref_dst_y = library->get(shape, Format::S16); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::sobel_3x3(ref_src, ref_dst_x, ref_dst_y, border_mode, constant_border_value); + + return std::make_pair(ref_dst_x, ref_dst_y); +} +std::pair<RawTensor, RawTensor> Reference::compute_reference_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value) +{ + // Create reference + RawTensor ref_src = library->get(shape, Format::U8); + RawTensor ref_dst_x = library->get(shape, Format::S16); + RawTensor ref_dst_y = library->get(shape, Format::S16); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::sobel_5x5(ref_src, ref_dst_x, ref_dst_y, border_mode, constant_border_value); + + return std::make_pair(ref_dst_x, ref_dst_y); +} std::pair<float, float> Reference::compute_reference_mean_and_standard_deviation(const TensorShape &shape) { // Create reference diff --git a/tests/validation/Reference.h b/tests/validation/Reference.h index 06a38c41f2..4cfd3c24c8 100644 --- a/tests/validation/Reference.h +++ b/tests/validation/Reference.h @@ -37,6 +37,24 @@ namespace validation class Reference { public: + /** Compute reference sobel 3x3. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode Border mode to use for input tensor + * @param[in] constant_border_value Constant value to use if @p border_mode is constant + * + * @return Computed raw tensors along x and y axis. + */ + static std::pair<RawTensor, RawTensor> compute_reference_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value); + /** Compute reference sobel 5x5. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] border_mode Border mode to use for input tensor + * @param[in] constant_border_value Constant value to use if @p border_mode is constant + * + * @return Computed raw tensors along x and y axis. + */ + static std::pair<RawTensor, RawTensor> compute_reference_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value); /** Compute reference mean and standard deviation. * * @param[in] shape Shape of the input tensors. diff --git a/tests/validation/ReferenceCPP.cpp b/tests/validation/ReferenceCPP.cpp index 5c429ea864..ca6bb6cec4 100644 --- a/tests/validation/ReferenceCPP.cpp +++ b/tests/validation/ReferenceCPP.cpp @@ -48,6 +48,26 @@ namespace test { namespace validation { +// Sobel 3x3 +void ReferenceCPP::sobel_3x3(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst_x.data_type() != DataType::S16 || dst_y.data_type() != DataType::S16); + Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<int16_t> dx(dst_x.shape(), dst_x.data_type(), dst_x.fixed_point_position(), reinterpret_cast<int16_t *>(dst_x.data())); + Tensor<int16_t> dy(dst_y.shape(), dst_y.data_type(), dst_y.fixed_point_position(), reinterpret_cast<int16_t *>(dst_y.data())); + tensor_operations::sobel_3x3(s, dx, dy, border_mode, constant_border_value); +} + +// Sobel 5x5 +void ReferenceCPP::sobel_5x5(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst_x.data_type() != DataType::S16 || dst_y.data_type() != DataType::S16); + Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<int16_t> dx(dst_x.shape(), dst_x.data_type(), dst_x.fixed_point_position(), reinterpret_cast<int16_t *>(dst_x.data())); + Tensor<int16_t> dy(dst_y.shape(), dst_y.data_type(), dst_y.fixed_point_position(), reinterpret_cast<int16_t *>(dst_y.data())); + tensor_operations::sobel_5x5(s, dx, dy, border_mode, constant_border_value); +} + // Absolute difference void ReferenceCPP::absolute_difference(const RawTensor &src1, const RawTensor &src2, RawTensor &dst) { diff --git a/tests/validation/ReferenceCPP.h b/tests/validation/ReferenceCPP.h index 4173b8bc1d..b5e3fa0058 100644 --- a/tests/validation/ReferenceCPP.h +++ b/tests/validation/ReferenceCPP.h @@ -42,6 +42,26 @@ namespace validation class ReferenceCPP final : public Reference { public: + /** Function to compute reference sobel 3x3. + * + * @param[in] src Input tensor. + * @param[in] dst_x Result tensor along x axis + * @param[in] dst_y Result tensor along y axis + * @param[in] border_mode Border mode to use for input tensor + * @param[in] constant_border_value Constant value to use if @p border_mode is constant + * + */ + static void sobel_3x3(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value); + /** Function to compute reference sobel 5x5. + * + * @param[in] src Input tensor. + * @param[in] dst_x Result tensor along x axis + * @param[in] dst_y Result tensor along y axis + * @param[in] border_mode Border mode to use for input tensor + * @param[in] constant_border_value Constant value to use if @p border_mode is constant + * + */ + static void sobel_5x5(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value); /** Function to compute the mean and standard deviation of a tensor. * * @param[in] src Input tensor. diff --git a/tests/validation/TensorOperations.h b/tests/validation/TensorOperations.h index 56cc657daa..fce257540b 100644 --- a/tests/validation/TensorOperations.h +++ b/tests/validation/TensorOperations.h @@ -34,10 +34,12 @@ #include "arm_compute/core/FixedPoint.h" #include "arm_compute/core/Types.h" #include "tests/validation/FixedPoint.h" +#include "tests/validation/ValidationUserConfiguration.h" #include <algorithm> #include <array> #include <cmath> +#include <random> namespace arm_compute { @@ -199,39 +201,119 @@ void vector_matrix_multiply(const int8_t *in, const int8_t *weights, const int8_ } } +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> +T tensor_elem_at(const Tensor<T> &in, Coordinates &coord, BorderMode border_mode, T constant_border_value) +{ + const int x = coord.x(); + const int y = coord.y(); + const int width = static_cast<int>(in.shape().x()); + const int height = static_cast<int>(in.shape().y()); + + // If on border + if(x < 0 || y < 0 || x >= width || y >= height) + { + if(border_mode == BorderMode::CONSTANT) + { + return constant_border_value; + } + else if(border_mode == BorderMode::REPLICATE) + { + coord.set(0, std::max(0, std::min(x, width - 1))); + coord.set(1, std::max(0, std::min(y, height - 1))); + return in[coord2index(in.shape(), coord)]; + } + else + { + // Return a random value if on border and border_mode == UNDEFINED + std::mt19937 gen(user_config.seed.get()); + std::uniform_int_distribution<T> distribution(0, 255); + return distribution(gen); + } + } + else + { + return in[coord2index(in.shape(), coord)]; + } +} + /** Apply 2D spatial filter on a single element of @p in at coordinates @p coord * * - filter sizes have to be odd number - * - Valid region assumed * - Row major order of filter assumed * - TO_ZERO rounding policy assumed * - SATURATE convert policy assumed * */ template <typename T1, typename T2, typename T3> -void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale) +void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale, BorderMode border_mode, + T1 constant_border_value = 0) { - using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; - intermediate_type val = 0; - int x = coord.x(); - int y = coord.y(); - for(size_t j = y - filter_shape[1] / 2; j <= y + filter_shape[1] / 2; ++j) + double val = 0; + const int x = coord.x(); + const int y = coord.y(); + for(int j = y - static_cast<int>(filter_shape[1] / 2); j <= y + static_cast<int>(filter_shape[1] / 2); ++j) { - for(size_t i = x - filter_shape[0] / 2; i <= x + filter_shape[0] / 2; ++i) + for(int i = x - static_cast<int>(filter_shape[0] / 2); i <= x + static_cast<int>(filter_shape[0] / 2); ++i) { coord.set(0, i); coord.set(1, j); - val += static_cast<intermediate_type>(*filter_itr) * static_cast<intermediate_type>(in[coord2index(in.shape(), coord)]); + double pixel_to_multiply = tensor_elem_at(in, coord, border_mode, constant_border_value); + val += static_cast<double>(*filter_itr) * pixel_to_multiply; ++filter_itr; } } coord.set(0, x); coord.set(1, y); - double rounded_val = cpp11::trunc(val * static_cast<double>(scale)); + const double rounded_val = cpp11::trunc(val * static_cast<double>(scale)); out[coord2index(in.shape(), coord)] = saturate_cast<T3>(rounded_val); } } // namespace +// Sobel 3x3 +template <typename T1, typename T2> +void sobel_3x3(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value) +{ + const std::array<int8_t, 9> sobel_x{ { -1, 0, 1, -2, 0, 2, -1, 0, 1 } }; + const std::array<int8_t, 9> sobel_y{ { -1, -2, -1, 0, 0, 0, 1, 2, 1 } }; + + for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx) + { + const Coordinates id = index2coord(in.shape(), element_idx); + + apply_2d_spatial_filter(id, in, out_x, TensorShape(3U, 3U), sobel_x.data(), 1.f, border_mode, constant_border_value); + apply_2d_spatial_filter(id, in, out_y, TensorShape(3U, 3U), sobel_y.data(), 1.f, border_mode, constant_border_value); + } +} + +// Sobel 5x5 +template <typename T1, typename T2> +void sobel_5x5(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value) +{ + const std::array<int8_t, 25> sobel_x{ { + -1, -2, 0, 2, 1, + -4, -8, 0, 8, 4, + -6, -12, 0, 12, 6, + -4, -8, 0, 8, 4, + -1, -2, 0, 2, 1 + } }; + + const std::array<int8_t, 25> sobel_y{ { + -1, -4, -6, -4, -1, + -2, -8, -12, -8, -2, + 0, 0, 0, 0, 0, + 2, 8, 12, 8, 2, + 1, 4, 6, 4, 1 + } }; + + for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx) + { + const Coordinates id = index2coord(in.shape(), element_idx); + + apply_2d_spatial_filter(id, in, out_x, TensorShape(5U, 5U), sobel_x.data(), 1.f, border_mode, constant_border_value); + apply_2d_spatial_filter(id, in, out_y, TensorShape(5U, 5U), sobel_y.data(), 1.f, border_mode, constant_border_value); + } +} + // Mean Standard Deviation template <typename T1> void mean_and_standard_deviation(const Tensor<T1> &in, float &mean, float &std_dev) @@ -438,7 +520,7 @@ void box3x3(const Tensor<T> &in, Tensor<T> &out) const Coordinates id = index2coord(in.shape(), element_idx); if(is_in_valid_region(valid_region, id)) { - apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale); + apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale, BorderMode::UNDEFINED); } } } |