aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/validation/CL/CMakeLists.txt4
-rw-r--r--tests/validation/CL/Sobel3x3.cpp216
-rw-r--r--tests/validation/CL/Sobel5x5.cpp215
-rw-r--r--tests/validation/NEON/CMakeLists.txt2
-rw-r--r--tests/validation/NEON/Sobel3x3.cpp215
-rw-r--r--tests/validation/NEON/Sobel5x5.cpp215
-rw-r--r--tests/validation/Reference.cpp30
-rw-r--r--tests/validation/Reference.h18
-rw-r--r--tests/validation/ReferenceCPP.cpp20
-rw-r--r--tests/validation/ReferenceCPP.h20
-rw-r--r--tests/validation/TensorOperations.h104
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);
}
}
}