aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoritz Pflanzer <moritz.pflanzer@arm.com>2017-09-24 12:09:41 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:35:24 +0000
commit6c6597c1e17c32c6ad861780eee454a7deecfb75 (patch)
tree5df015557262a83e5e84a5fa365544bb1aa66762
parentc26ecf8ca13205cab2ce43d9f971e1569808e5bc (diff)
downloadComputeLibrary-6c6597c1e17c32c6ad861780eee454a7deecfb75.tar.gz
COMPMID-500: Move HarrisCorners to new validation
Change-Id: I4e21ad98d029e360010c5927f04b716527700a00 Reviewed-on: http://mpd-gerrit.cambridge.arm.com/88888 Reviewed-by: Anthony Barbier <anthony.barbier@arm.com> Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
-rw-r--r--arm_compute/core/CL/ICLArray.h4
-rw-r--r--arm_compute/core/Window.inl2
-rw-r--r--arm_compute/runtime/CL/CLArray.h11
-rw-r--r--src/core/CL/ICLKernel.cpp5
-rw-r--r--src/core/Validate.cpp2
-rw-r--r--tests/framework/Asserts.h19
-rw-r--r--tests/validation/CL/HarrisCorners.cpp122
-rw-r--r--tests/validation/CPP/HarrisCornerDetector.cpp205
-rw-r--r--tests/validation/CPP/HarrisCornerDetector.h47
-rw-r--r--tests/validation/CPP/NonMaximaSuppression.cpp75
-rw-r--r--tests/validation/CPP/NonMaximaSuppression.h44
-rw-r--r--tests/validation/CPP/Utils.cpp30
-rw-r--r--tests/validation/CPP/Utils.h25
-rw-r--r--tests/validation/Helpers.cpp60
-rw-r--r--tests/validation/Helpers.h56
-rw-r--r--tests/validation/NEON/HarrisCorners.cpp123
-rw-r--r--tests/validation/Validation.cpp19
-rw-r--r--tests/validation/Validation.h87
-rw-r--r--tests/validation/fixtures/HarrisCornersFixture.h126
-rw-r--r--tests/validation_old/CL/HarrisCorners.cpp224
-rw-r--r--tests/validation_old/NEON/HarrisCorners.cpp229
-rw-r--r--utils/TypePrinter.h13
22 files changed, 991 insertions, 537 deletions
diff --git a/arm_compute/core/CL/ICLArray.h b/arm_compute/core/CL/ICLArray.h
index e12695f206..6c3dbcd170 100644
--- a/arm_compute/core/CL/ICLArray.h
+++ b/arm_compute/core/CL/ICLArray.h
@@ -43,7 +43,9 @@ public:
ICLArray(const ICLArray &) = delete;
ICLArray &operator=(const ICLArray &) = delete;
- virtual ~ICLArray() = default;
+ ICLArray(ICLArray &&) = default;
+ ICLArray &operator=(ICLArray &&) = default;
+ virtual ~ICLArray() = default;
/** Interface to be implemented by the child class to return a reference to the OpenCL buffer containing the array's data.
*
* @return A reference to an OpenCL buffer containing the array's data.
diff --git a/arm_compute/core/Window.inl b/arm_compute/core/Window.inl
index 6b02128797..e46a0ec8f7 100644
--- a/arm_compute/core/Window.inl
+++ b/arm_compute/core/Window.inl
@@ -114,7 +114,7 @@ inline void Window::validate() const
for(size_t i = 0; i < Coordinates::num_max_dimensions; ++i)
{
ARM_COMPUTE_ERROR_ON(_dims[i].step() == 0);
- ARM_COMPUTE_ERROR_ON(_dims[i].end() <= _dims[i].start());
+ ARM_COMPUTE_ERROR_ON(_dims[i].end() < _dims[i].start());
ARM_COMPUTE_ERROR_ON((_dims[i].end() - _dims[i].start()) % _dims[i].step());
}
}
diff --git a/arm_compute/runtime/CL/CLArray.h b/arm_compute/runtime/CL/CLArray.h
index 3dc7f19bc7..dda26e2e89 100644
--- a/arm_compute/runtime/CL/CLArray.h
+++ b/arm_compute/runtime/CL/CLArray.h
@@ -37,16 +37,23 @@ template <class T>
class CLArray : public ICLArray<T>
{
public:
+ /** Default constructor: empty array */
+ CLArray()
+ : ICLArray<T>(0), _buffer()
+ {
+ }
/** Prevent instances of this class from being copied (As this class contains pointers) */
CLArray(const CLArray &) = delete;
/** Prevent instances of this class from being copied (As this class contains pointers) */
- const CLArray &operator=(const CLArray &) = delete;
+ CLArray &operator=(const CLArray &) = delete;
+ CLArray(CLArray &&) = default;
+ CLArray &operator=(CLArray &&) = default;
/** Constructor: initializes an array which can contain up to max_num_points values
*
* @param[in] max_num_values Maximum number of values the array will be able to stored
*/
CLArray(size_t max_num_values)
- : ICLArray<T>(max_num_values), _buffer(cl::Buffer(CLScheduler::get().context(), CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_WRITE, max_num_values * sizeof(T)))
+ : ICLArray<T>(max_num_values), _buffer(CLScheduler::get().context(), CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_WRITE, max_num_values * sizeof(T))
{
}
/** Enqueue a map operation of the allocated buffer.
diff --git a/src/core/CL/ICLKernel.cpp b/src/core/CL/ICLKernel.cpp
index b0ac40adf7..9663fd200f 100644
--- a/src/core/CL/ICLKernel.cpp
+++ b/src/core/CL/ICLKernel.cpp
@@ -43,7 +43,10 @@ void arm_compute::enqueue(cl::CommandQueue &queue, ICLKernel &kernel, const Wind
return;
}
- ARM_COMPUTE_ERROR_ON((0 == (window.x().end() - window.x().start())) || (0 == (window.y().end() - window.y().start())));
+ if((window.x().end() - window.x().start()) == 0 || (window.y().end() - window.y().start()) == 0)
+ {
+ return;
+ }
cl::NDRange gws((window.x().end() - window.x().start()) / window.x().step(),
(window.y().end() - window.y().start()) / window.y().step(),
diff --git a/src/core/Validate.cpp b/src/core/Validate.cpp
index 6925133b57..084a325711 100644
--- a/src/core/Validate.cpp
+++ b/src/core/Validate.cpp
@@ -184,7 +184,7 @@ void arm_compute::error_on_unconfigured_kernel(const char *function, const char
ARM_COMPUTE_UNUSED(kernel);
ARM_COMPUTE_ERROR_ON_LOC(kernel == nullptr, function, file, line);
- ARM_COMPUTE_ERROR_ON_LOC_MSG((kernel->window().x().start() == kernel->window().x().end()) && (kernel->window().x().end() == 0),
+ ARM_COMPUTE_ERROR_ON_LOC_MSG((kernel->window().x().start() == kernel->window().x().end()) && (kernel->window().x().end() == 0) && (kernel->window().x().step() == 0),
function, file, line,
"This kernel hasn't been configured.");
}
diff --git a/tests/framework/Asserts.h b/tests/framework/Asserts.h
index 936dfcf9bc..9d6d4fad9a 100644
--- a/tests/framework/Asserts.h
+++ b/tests/framework/Asserts.h
@@ -135,6 +135,25 @@ ARM_COMPUTE_TEST_COMP_FACTORY(ASSERT, Assertion, !=, NOT_EQUAL, throw arm_comput
arm_compute::test::framework::Framework::get().clear_test_info(); \
} while(false)
+#define ARM_COMPUTE_ASSERT_FAIL(MSG) \
+ do \
+ { \
+ std::stringstream msg; \
+ msg << "Assertion '" << MSG << "' failed.\n"; \
+ arm_compute::test::framework::Framework::get().print_test_info(msg); \
+ throw arm_compute::test::framework::TestError(msg.str(), arm_compute::test::framework::LogLevel::ERRORS); \
+ arm_compute::test::framework::Framework::get().clear_test_info(); \
+ } while(false)
+
+#define ARM_COMPUTE_EXPECT_FAIL(MSG, LEVEL) \
+ do \
+ { \
+ std::stringstream msg; \
+ msg << "Expectation '" << MSG << "' failed.\n"; \
+ arm_compute::test::framework::Framework::get().print_test_info(msg); \
+ arm_compute::test::framework::Framework::get().log_failed_expectation(arm_compute::test::framework::TestError(msg.str(), LEVEL)); \
+ arm_compute::test::framework::Framework::get().clear_test_info(); \
+ } while(false)
} // namespace framework
} // namespace test
} // namespace arm_compute
diff --git a/tests/validation/CL/HarrisCorners.cpp b/tests/validation/CL/HarrisCorners.cpp
new file mode 100644
index 0000000000..4188cb54f0
--- /dev/null
+++ b/tests/validation/CL/HarrisCorners.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "arm_compute/core/Types.h"
+#include "arm_compute/runtime/CL/CLArray.h"
+#include "arm_compute/runtime/CL/CLTensor.h"
+#include "arm_compute/runtime/CL/CLTensorAllocator.h"
+#include "arm_compute/runtime/CL/functions/CLHarrisCorners.h"
+#include "tests/CL/CLAccessor.h"
+#include "tests/CL/CLArrayAccessor.h"
+#include "tests/PaddingCalculator.h"
+#include "tests/datasets/BorderModeDataset.h"
+#include "tests/datasets/ShapeDatasets.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Macros.h"
+#include "tests/framework/datasets/Datasets.h"
+#include "tests/validation/Validation.h"
+#include "tests/validation/fixtures/HarrisCornersFixture.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace
+{
+const auto use_fp16 = framework::dataset::make("UseFP16",
+{ false });
+
+const auto data = combine(framework::dataset::make("GradientSize", { 3, 5, 7 }), combine(framework::dataset::make("BlockSize", { 3, 5, 7 }), combine(datasets::BorderModes(), use_fp16)));
+} // namespace
+
+TEST_SUITE(CL)
+TEST_SUITE(HarrisCorners)
+
+DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(concat(datasets::Small2DShapes(), datasets::Large2DShapes()), data), framework::dataset::make("Format", Format::U8)), shape,
+ gradient_size, block_size, border_mode, use_fp16, format)
+{
+ ARM_COMPUTE_UNUSED(use_fp16);
+ ARM_COMPUTE_ERROR_ON(use_fp16);
+
+ std::mt19937 gen(library->seed());
+ std::uniform_real_distribution<float> real_dist(0.f, 0.01f);
+
+ const float threshold = real_dist(gen);
+ const float sensitivity = real_dist(gen);
+
+ constexpr float max_euclidean_distance = 30.f;
+ real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
+ const float min_dist = real_dist(gen);
+
+ // Generate a random constant value
+ std::uniform_int_distribution<uint8_t> int_dist(0, 255);
+ const uint8_t constant_border_value = int_dist(gen);
+
+ // Create tensors
+ CLTensor src = create_tensor<CLTensor>(shape, data_type_from_format(format));
+ src.info()->set_format(format);
+ CLKeyPointArray corners;
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Create harris corners configure function
+ CLHarrisCorners harris_corners;
+ harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient_size, block_size, &corners, border_mode, constant_border_value);
+
+ // Validate padding
+ PaddingCalculator calculator(shape.x(), 8);
+
+ calculator.set_border_mode(border_mode);
+ calculator.set_border_size(gradient_size / 2);
+ calculator.set_access_offset(-gradient_size / 2);
+ calculator.set_accessed_elements(16);
+
+ const PaddingSize padding = calculator.required_padding();
+
+ validate(src.info()->padding(), padding);
+}
+
+template <typename T>
+using CLHarrisCornersFixture = HarrisCornersValidationFixture<CLTensor, CLAccessor, CLKeyPointArray, CLHarrisCorners, T>;
+
+FIXTURE_DATA_TEST_CASE(RunSmall, CLHarrisCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::Small2DShapes(), data), framework::dataset::make("Format", Format::U8)))
+{
+ // Validate output
+ CLArrayAccessor<KeyPoint> array(_target);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), RelativeTolerance<float>(0.0001f));
+}
+
+FIXTURE_DATA_TEST_CASE(RunLarge, CLHarrisCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::Large2DShapes(), data), framework::dataset::make("Format", Format::U8)))
+{
+ // Validate output
+ CLArrayAccessor<KeyPoint> array(_target);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), RelativeTolerance<float>(0.0001f));
+}
+
+TEST_SUITE_END()
+TEST_SUITE_END()
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/CPP/HarrisCornerDetector.cpp b/tests/validation/CPP/HarrisCornerDetector.cpp
new file mode 100644
index 0000000000..3babfeed42
--- /dev/null
+++ b/tests/validation/CPP/HarrisCornerDetector.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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 "HarrisCornerDetector.h"
+
+#include "Utils.h"
+#include "tests/validation/CPP/NonMaximaSuppression.h"
+#include "tests/validation/CPP/Sobel.h"
+#include "tests/validation/Helpers.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+namespace
+{
+template <typename T>
+std::tuple<SimpleTensor<T>, SimpleTensor<T>, float> compute_sobel(const SimpleTensor<uint8_t> &src, int gradient_size, int block_size, BorderMode border_mode, uint8_t constant_border_value)
+{
+ SimpleTensor<T> grad_x;
+ SimpleTensor<T> grad_y;
+ float norm_factor = 0.f;
+
+ std::tie(grad_x, grad_y) = sobel<T>(src, gradient_size, border_mode, constant_border_value);
+
+ switch(gradient_size)
+ {
+ case 3:
+ norm_factor = 1.f / (4 * 255 * block_size);
+ break;
+ case 5:
+ norm_factor = 1.f / (16 * 255 * block_size);
+ break;
+ case 7:
+ norm_factor = 1.f / (64 * 255 * block_size);
+ break;
+ default:
+ ARM_COMPUTE_ERROR("Gradient size not supported.");
+ }
+
+ return std::make_tuple(grad_x, grad_y, norm_factor);
+}
+
+template <typename T, typename U>
+std::vector<KeyPoint> harris_corner_detector_impl(const SimpleTensor<U> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
+ U constant_border_value)
+{
+ ARM_COMPUTE_ERROR_ON(block_size != 3 && block_size != 5 && block_size != 7);
+
+ SimpleTensor<T> grad_x;
+ SimpleTensor<T> grad_y;
+ float norm_factor = 0.f;
+
+ // Sobel
+ std::tie(grad_x, grad_y, norm_factor) = compute_sobel<T>(src, gradient_size, block_size, border_mode, constant_border_value);
+
+ SimpleTensor<float> scores(src.shape(), DataType::F32);
+ ValidRegion scores_region = shape_to_valid_region(scores.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(gradient_size / 2 + block_size / 2));
+
+ // Calculate scores
+ for(int i = 0; i < scores.num_elements(); ++i)
+ {
+ Coordinates src_coord = index2coord(src.shape(), i);
+ Coordinates block_top_left{ src_coord.x() - block_size / 2, src_coord.y() - block_size / 2 };
+ Coordinates block_bottom_right{ src_coord.x() + block_size / 2, src_coord.y() + block_size / 2 };
+
+ if(!is_in_valid_region(scores_region, src_coord))
+ {
+ scores[i] = 0.f;
+ continue;
+ }
+
+ float Gx2 = 0.f;
+ float Gy2 = 0.f;
+ float Gxy = 0.f;
+
+ // Calculate Gx^2, Gy^2 and Gxy within the given window
+ for(int y = src_coord.y() - block_size / 2; y <= src_coord.y() + block_size / 2; ++y)
+ {
+ for(int x = src_coord.x() - block_size / 2; x <= src_coord.x() + block_size / 2; ++x)
+ {
+ Coordinates block_coord(x, y);
+
+ const float norm_x = tensor_elem_at(grad_x, block_coord, border_mode, static_cast<T>(constant_border_value)) * norm_factor;
+ const float norm_y = tensor_elem_at(grad_y, block_coord, border_mode, static_cast<T>(constant_border_value)) * norm_factor;
+
+ Gx2 += std::pow(norm_x, 2);
+ Gy2 += std::pow(norm_y, 2);
+ Gxy += norm_x * norm_y;
+ }
+ }
+
+ const float trace2 = std::pow(Gx2 + Gy2, 2);
+ const float det = Gx2 * Gy2 - std::pow(Gxy, 2);
+ const float response = det - sensitivity * trace2;
+
+ if(response > threshold)
+ {
+ scores[i] = response;
+ }
+ else
+ {
+ scores[i] = 0.f;
+ }
+ }
+
+ // Suppress non-maxima candidates
+ SimpleTensor<float> suppressed_scores = non_maxima_suppression(scores, border_mode != BorderMode::UNDEFINED ? BorderMode::CONSTANT : BorderMode::UNDEFINED, 0.f);
+ ValidRegion suppressed_scores_region = shape_to_valid_region(suppressed_scores.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(gradient_size / 2 + block_size / 2 + 1));
+
+ // Create vector of candidate corners
+ std::vector<KeyPoint> corner_candidates;
+
+ for(int i = 0; i < suppressed_scores.num_elements(); ++i)
+ {
+ Coordinates coord = index2coord(suppressed_scores.shape(), i);
+
+ if(is_in_valid_region(suppressed_scores_region, coord) && suppressed_scores[i] > 0.f)
+ {
+ KeyPoint corner;
+ corner.x = coord.x();
+ corner.y = coord.y();
+ corner.tracking_status = 1;
+ corner.strength = suppressed_scores[i];
+ corner.scale = 0.f;
+ corner.orientation = 0.f;
+ corner.error = 0.f;
+
+ corner_candidates.emplace_back(corner);
+ }
+ }
+
+ // Sort descending by strength
+ std::sort(corner_candidates.begin(), corner_candidates.end(), [](const KeyPoint & a, const KeyPoint & b)
+ {
+ return a.strength > b.strength;
+ });
+
+ std::vector<KeyPoint> corners;
+ corners.reserve(corner_candidates.size());
+
+ // Only add corner if there is no stronger within min_dist
+ for(const KeyPoint &point : corner_candidates)
+ {
+ const auto strongest = std::find_if(corners.begin(), corners.end(), [&](const KeyPoint & other)
+ {
+ return std::sqrt((std::pow(point.x - other.x, 2) + std::pow(point.y - other.y, 2))) < min_dist;
+ });
+
+ if(strongest == corners.end())
+ {
+ corners.emplace_back(point);
+ }
+ }
+
+ corners.shrink_to_fit();
+
+ return corners;
+}
+} // namespace
+
+template <typename T>
+std::vector<KeyPoint> harris_corner_detector(const SimpleTensor<T> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
+ T constant_border_value)
+{
+ if(gradient_size < 7)
+ {
+ return harris_corner_detector_impl<int16_t>(src, threshold, min_dist, sensitivity, gradient_size, block_size, border_mode, constant_border_value);
+ }
+ else
+ {
+ return harris_corner_detector_impl<int32_t>(src, threshold, min_dist, sensitivity, gradient_size, block_size, border_mode, constant_border_value);
+ }
+}
+
+template std::vector<KeyPoint> harris_corner_detector(const SimpleTensor<uint8_t> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
+ uint8_t constant_border_value);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/CPP/HarrisCornerDetector.h b/tests/validation/CPP/HarrisCornerDetector.h
new file mode 100644
index 0000000000..042e8570c2
--- /dev/null
+++ b/tests/validation/CPP/HarrisCornerDetector.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+#ifndef __ARM_COMPUTE_TEST_HARRIS_CORNER_DETECTOR_H__
+#define __ARM_COMPUTE_TEST_HARRIS_CORNER_DETECTOR_H__
+
+#include "arm_compute/core/Types.h"
+#include "tests/SimpleTensor.h"
+
+#include <vector>
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+std::vector<KeyPoint> harris_corner_detector(const SimpleTensor<T> &src, float threshold, float min_dist, float sensitivity, int gradient_size, int block_size, BorderMode border_mode,
+ T constant_border_value = 0);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_TEST_HARRIS_CORNER_DETECTOR_H__ */
diff --git a/tests/validation/CPP/NonMaximaSuppression.cpp b/tests/validation/CPP/NonMaximaSuppression.cpp
new file mode 100644
index 0000000000..eab5cecfc8
--- /dev/null
+++ b/tests/validation/CPP/NonMaximaSuppression.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "NonMaximaSuppression.h"
+
+#include "Utils.h"
+#include "tests/validation/Helpers.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+SimpleTensor<T> non_maxima_suppression(const SimpleTensor<T> &src, BorderMode border_mode, T constant_border_value)
+{
+ constexpr int block_size = 3;
+ SimpleTensor<T> dst(src.shape(), src.data_type(), src.num_channels());
+ ValidRegion valid_region = shape_to_valid_region(src.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(block_size / 2));
+
+ for(int i = 0; i < src.num_elements(); ++i)
+ {
+ Coordinates coord = index2coord(src.shape(), i);
+ int x = coord.x();
+ int y = coord.y();
+
+ if(!is_in_valid_region(valid_region, coord))
+ {
+ continue;
+ }
+
+ if(src[i] >= tensor_elem_at(src, Coordinates(x - 1, y - 1), border_mode, constant_border_value) && src[i] >= tensor_elem_at(src, Coordinates(x, y - 1), border_mode, constant_border_value)
+ && src[i] >= tensor_elem_at(src, Coordinates(x + 1, y - 1), border_mode, constant_border_value) && src[i] >= tensor_elem_at(src, Coordinates(x - 1, y), border_mode, constant_border_value)
+ && src[i] > tensor_elem_at(src, Coordinates(x + 1, y), border_mode, constant_border_value) && src[i] > tensor_elem_at(src, Coordinates(x - 1, y + 1), border_mode, constant_border_value)
+ && src[i] > tensor_elem_at(src, Coordinates(x, y + 1), border_mode, constant_border_value) && src[i] > tensor_elem_at(src, Coordinates(x + 1, y + 1), border_mode, constant_border_value))
+ {
+ dst[i] = src[i];
+ }
+ else
+ {
+ dst[i] = T(0);
+ }
+ }
+
+ return dst;
+}
+
+template SimpleTensor<float> non_maxima_suppression(const SimpleTensor<float> &src, BorderMode border_mode, float constant_border_value);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/CPP/NonMaximaSuppression.h b/tests/validation/CPP/NonMaximaSuppression.h
new file mode 100644
index 0000000000..2086abfe83
--- /dev/null
+++ b/tests/validation/CPP/NonMaximaSuppression.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#ifndef __ARM_COMPUTE_TEST_NON_MAXIMA_SUPPRESSION_H__
+#define __ARM_COMPUTE_TEST_NON_MAXIMA_SUPPRESSION_H__
+
+#include "arm_compute/core/Types.h"
+#include "tests/SimpleTensor.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+SimpleTensor<T> non_maxima_suppression(const SimpleTensor<T> &src, BorderMode border_mode, T constant_border_value = 0);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_TEST_NON_MAXIMA_SUPPRESSION_H__ */
diff --git a/tests/validation/CPP/Utils.cpp b/tests/validation/CPP/Utils.cpp
index e27688889d..af3ed9099f 100644
--- a/tests/validation/CPP/Utils.cpp
+++ b/tests/validation/CPP/Utils.cpp
@@ -31,36 +31,6 @@ namespace test
{
namespace validation
{
-// Return a tensor element at a specified coordinate with different border modes
-template <typename T>
-T tensor_elem_at(const SimpleTensor<T> &in, Coordinates coord, BorderMode border_mode, T constant_border_value)
-{
- const int x = coord.x();
- const int y = coord.y();
- const auto width = static_cast<int>(in.shape().x());
- const auto height = static_cast<int>(in.shape().y());
-
- // If coordinates beyond range of tensor's width or height
- if(x < 0 || y < 0 || x >= width || y >= height)
- {
- 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)));
- }
- else
- {
- return static_cast<T>(constant_border_value);
- }
- }
- return in[coord2index(in.shape(), coord)];
-}
-
-template uint8_t tensor_elem_at(const SimpleTensor<uint8_t> &in, Coordinates coord, BorderMode border_mode, uint8_t constant_border_value);
-template int16_t tensor_elem_at(const SimpleTensor<int16_t> &in, Coordinates coord, BorderMode border_mode, int16_t constant_border_value);
-template half tensor_elem_at(const SimpleTensor<half> &in, Coordinates coord, BorderMode border_mode, half constant_border_value);
-template float tensor_elem_at(const SimpleTensor<float> &in, Coordinates coord, BorderMode border_mode, float constant_border_value);
-
// Return the bilinear value at a specified coordinate with different border modes
template <typename T>
T bilinear_policy(const SimpleTensor<T> &in, Coordinates id, float xn, float yn, BorderMode border_mode, T constant_border_value)
diff --git a/tests/validation/CPP/Utils.h b/tests/validation/CPP/Utils.h
index 2d879c129b..91d1afe1d7 100644
--- a/tests/validation/CPP/Utils.h
+++ b/tests/validation/CPP/Utils.h
@@ -41,8 +41,31 @@ namespace test
{
namespace validation
{
+// Return a tensor element at a specified coordinate with different border modes
template <typename T>
-T tensor_elem_at(const SimpleTensor<T> &in, Coordinates coord, BorderMode border_mode, T constant_border_value);
+T tensor_elem_at(const SimpleTensor<T> &src, Coordinates coord, BorderMode border_mode, T constant_border_value)
+{
+ const int x = coord.x();
+ const int y = coord.y();
+ const int width = src.shape().x();
+ const int height = src.shape().y();
+
+ // If coordinates beyond range of tensor's width or height
+ if(x < 0 || y < 0 || x >= width || y >= height)
+ {
+ 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)));
+ }
+ else
+ {
+ return constant_border_value;
+ }
+ }
+
+ return src[coord2index(src.shape(), coord)];
+}
template <typename T>
T bilinear_policy(const SimpleTensor<T> &in, Coordinates id, float xn, float yn, BorderMode border_mode, T constant_border_value);
diff --git a/tests/validation/Helpers.cpp b/tests/validation/Helpers.cpp
index d3bcbbd9e4..23ad62a6c3 100644
--- a/tests/validation/Helpers.cpp
+++ b/tests/validation/Helpers.cpp
@@ -29,6 +29,48 @@ namespace test
{
namespace validation
{
+void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPattern pattern)
+{
+ unsigned int v = 0;
+ std::mt19937 gen(library->seed());
+ std::bernoulli_distribution dist(0.5);
+
+ for(int r = 0; r < rows; ++r)
+ {
+ for(int c = 0; c < cols; ++c, ++v)
+ {
+ uint8_t val = 0;
+
+ switch(pattern)
+ {
+ case MatrixPattern::BOX:
+ val = 255;
+ break;
+ case MatrixPattern::CROSS:
+ val = ((r == (rows / 2)) || (c == (cols / 2))) ? 255 : 0;
+ break;
+ case MatrixPattern::DISK:
+ val = (((r - rows / 2.0f + 0.5f) * (r - rows / 2.0f + 0.5f)) / ((rows / 2.0f) * (rows / 2.0f)) + ((c - cols / 2.0f + 0.5f) * (c - cols / 2.0f + 0.5f)) / ((cols / 2.0f) *
+ (cols / 2.0f))) <= 1.0f ? 255 : 0;
+ break;
+ case MatrixPattern::OTHER:
+ val = (dist(gen) ? 0 : 255);
+ break;
+ default:
+ return;
+ }
+
+ mask[v] = val;
+ }
+ }
+
+ if(pattern == MatrixPattern::OTHER)
+ {
+ std::uniform_int_distribution<uint8_t> distribution_u8(0, ((cols * rows) - 1));
+ mask[distribution_u8(gen)] = 255;
+ }
+}
+
TensorShape calculate_depth_concatenate_shape(const std::vector<TensorShape> &input_shapes)
{
ARM_COMPUTE_ERROR_ON(input_shapes.empty());
@@ -52,6 +94,24 @@ TensorShape calculate_depth_concatenate_shape(const std::vector<TensorShape> &in
return out_shape;
}
+
+HarrisCornersParameters harris_corners_parameters()
+{
+ HarrisCornersParameters params;
+
+ std::mt19937 gen(library->seed());
+ std::uniform_real_distribution<float> threshold_dist(0.f, 0.01f);
+ std::uniform_real_distribution<float> sensitivity(0.04f, 0.15f);
+ std::uniform_real_distribution<float> euclidean_distance(0.f, 30.f);
+ std::uniform_int_distribution<uint8_t> int_dist(0, 255);
+
+ params.threshold = threshold_dist(gen);
+ params.sensitivity = sensitivity(gen);
+ params.min_dist = euclidean_distance(gen);
+ params.constant_border_value = int_dist(gen);
+
+ return params;
+}
} // namespace validation
} // namespace test
} // namespace arm_compute
diff --git a/tests/validation/Helpers.h b/tests/validation/Helpers.h
index 30c67245a2..eecf976a13 100644
--- a/tests/validation/Helpers.h
+++ b/tests/validation/Helpers.h
@@ -26,7 +26,9 @@
#include "arm_compute/core/Types.h"
#include "arm_compute/core/Utils.h"
+#include "support/Half.h"
#include "tests/Globals.h"
+#include "tests/SimpleTensor.h"
#include <random>
#include <type_traits>
@@ -136,47 +138,7 @@ std::pair<T, T> get_activation_layer_test_bounds(ActivationLayerInfo::Activation
* @param[in] rows Rows (height) of mask
* @param[in] pattern Pattern to fill the mask according to
*/
-inline void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPattern pattern)
-{
- unsigned int v = 0;
- std::mt19937 gen(library->seed());
- std::bernoulli_distribution dist(0.5);
-
- for(int r = 0; r < rows; ++r)
- {
- for(int c = 0; c < cols; ++c, ++v)
- {
- uint8_t val = 0;
-
- switch(pattern)
- {
- case MatrixPattern::BOX:
- val = 255;
- break;
- case MatrixPattern::CROSS:
- val = ((r == (rows / 2)) || (c == (cols / 2))) ? 255 : 0;
- break;
- case MatrixPattern::DISK:
- val = (((r - rows / 2.0f + 0.5f) * (r - rows / 2.0f + 0.5f)) / ((rows / 2.0f) * (rows / 2.0f)) + ((c - cols / 2.0f + 0.5f) * (c - cols / 2.0f + 0.5f)) / ((cols / 2.0f) *
- (cols / 2.0f))) <= 1.0f ? 255 : 0;
- break;
- case MatrixPattern::OTHER:
- val = (dist(gen) ? 0 : 255);
- break;
- default:
- return;
- }
-
- mask[v] = val;
- }
- }
-
- if(pattern == MatrixPattern::OTHER)
- {
- std::uniform_int_distribution<uint8_t> distribution_u8(0, ((cols * rows) - 1));
- mask[distribution_u8(gen)] = 255;
- }
-}
+void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPattern pattern);
/** Calculate output tensor shape give a vector of input tensor to concatenate
*
@@ -186,6 +148,18 @@ inline void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPatt
*/
TensorShape calculate_depth_concatenate_shape(const std::vector<TensorShape> &input_shapes);
+/** Parameters of Harris Corners algorithm. */
+struct HarrisCornersParameters
+{
+ float threshold{ 0.f };
+ float sensitivity{ 0.f };
+ float min_dist{ 0.f };
+ uint8_t constant_border_value{ 0 };
+};
+
+/** Generate parameters for Harris Corners algorithm. */
+HarrisCornersParameters harris_corners_parameters();
+
/** Helper function to fill the Lut random by a ILutAccessor.
*
* @param[in,out] table Accessor at the Lut.
diff --git a/tests/validation/NEON/HarrisCorners.cpp b/tests/validation/NEON/HarrisCorners.cpp
new file mode 100644
index 0000000000..6d66549a8c
--- /dev/null
+++ b/tests/validation/NEON/HarrisCorners.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "arm_compute/core/Types.h"
+#include "arm_compute/runtime/NEON/functions/NEHarrisCorners.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/runtime/TensorAllocator.h"
+#include "tests/NEON/Accessor.h"
+#include "tests/NEON/ArrayAccessor.h"
+#include "tests/PaddingCalculator.h"
+#include "tests/datasets/BorderModeDataset.h"
+#include "tests/datasets/ShapeDatasets.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Macros.h"
+#include "tests/framework/datasets/Datasets.h"
+#include "tests/validation/Validation.h"
+#include "tests/validation/fixtures/HarrisCornersFixture.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace
+{
+const auto use_fp16 = framework::dataset::make("UseFP16",
+{
+#ifdef ARM_COMPUTE_ENABLE_FP16
+ true,
+#endif /* ARM_COMPUTE_ENABLE_FP16 */
+ false
+});
+
+const auto data = combine(framework::dataset::make("GradientSize", { 3, 5, 7 }), combine(framework::dataset::make("BlockSize", { 3, 5, 7 }), combine(datasets::BorderModes(), use_fp16)));
+} // namespace
+
+TEST_SUITE(NEON)
+TEST_SUITE(HarrisCorners)
+
+DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(concat(datasets::Small2DShapes(), datasets::Large2DShapes()), data), framework::dataset::make("Format", Format::U8)), shape,
+ gradient_size, block_size, border_mode, use_fp16, format)
+{
+ std::mt19937 gen(library->seed());
+ std::uniform_real_distribution<float> real_dist(0.f, 0.01f);
+
+ const float threshold = real_dist(gen);
+ const float sensitivity = real_dist(gen);
+
+ constexpr float max_euclidean_distance = 30.f;
+ real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
+ const float min_dist = real_dist(gen);
+
+ // Generate a random constant value
+ std::uniform_int_distribution<uint8_t> int_dist(0, 255);
+ const uint8_t constant_border_value = int_dist(gen);
+
+ // Create tensors
+ Tensor src = create_tensor<Tensor>(shape, data_type_from_format(format));
+ src.info()->set_format(format);
+ KeyPointArray corners;
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Create harris corners configure function
+ NEHarrisCorners harris_corners;
+ harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient_size, block_size, &corners, border_mode, constant_border_value, use_fp16);
+
+ // Validate padding
+ PaddingCalculator calculator(shape.x(), 8);
+
+ calculator.set_border_mode(border_mode);
+ calculator.set_border_size(gradient_size / 2);
+ calculator.set_access_offset(-gradient_size / 2);
+ calculator.set_accessed_elements(16);
+
+ const PaddingSize padding = calculator.required_padding();
+
+ validate(src.info()->padding(), padding);
+}
+
+template <typename T>
+using NEHarrisCornersFixture = HarrisCornersValidationFixture<Tensor, Accessor, KeyPointArray, NEHarrisCorners, T>;
+
+FIXTURE_DATA_TEST_CASE(RunSmall, NEHarrisCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::Small2DShapes(), data), framework::dataset::make("Format", Format::U8)))
+{
+ // Validate output
+ ArrayAccessor<KeyPoint> array(_target);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), RelativeTolerance<float>(0.0001f));
+}
+
+FIXTURE_DATA_TEST_CASE(RunLarge, NEHarrisCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::Large2DShapes(), data), framework::dataset::make("Format", Format::U8)))
+{
+ // Validate output
+ ArrayAccessor<KeyPoint> array(_target);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), RelativeTolerance<float>(0.0001f));
+}
+
+TEST_SUITE_END()
+TEST_SUITE_END()
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/Validation.cpp b/tests/validation/Validation.cpp
index aa70e6e794..ebca1936e7 100644
--- a/tests/validation/Validation.cpp
+++ b/tests/validation/Validation.cpp
@@ -297,11 +297,24 @@ void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned
{
ARM_COMPUTE_EXPECT_EQUAL(classified_labels.size(), expected_labels.size(), framework::LogLevel::ERRORS);
- const int min_num = std::min(classified_labels.size(), expected_labels.size());
+ int64_t num_mismatches = 0;
+ const int num_elements = std::min(classified_labels.size(), expected_labels.size());
+
+ for(int i = 0; i < num_elements; ++i)
+ {
+ if(classified_labels[i] != expected_labels[i])
+ {
+ ++num_mismatches;
+ ARM_COMPUTE_EXPECT_EQUAL(classified_labels[i], expected_labels[i], framework::LogLevel::DEBUG);
+ }
+ }
- for(int i = 0; i < min_num; ++i)
+ if(num_elements > 0)
{
- ARM_COMPUTE_EXPECT_EQUAL(classified_labels[i], expected_labels[i], framework::LogLevel::ERRORS);
+ const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
+
+ ARM_COMPUTE_TEST_INFO(num_mismatches << " values (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
+ ARM_COMPUTE_EXPECT_EQUAL(num_mismatches, 0, framework::LogLevel::ERRORS);
}
}
} // namespace validation
diff --git a/tests/validation/Validation.h b/tests/validation/Validation.h
index b6e7b8e82b..5e5dab0040 100644
--- a/tests/validation/Validation.h
+++ b/tests/validation/Validation.h
@@ -25,6 +25,7 @@
#define __ARM_COMPUTE_TEST_VALIDATION_H__
#include "arm_compute/core/FixedPoint.h"
+#include "arm_compute/core/IArray.h"
#include "arm_compute/core/Types.h"
#include "tests/IAccessor.h"
#include "tests/SimpleTensor.h"
@@ -212,7 +213,11 @@ void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned
* - All values should match
*/
template <typename T, typename U = AbsoluteTolerance<T>>
-void validate(T target, T reference, U tolerance = AbsoluteTolerance<T>());
+bool validate(T target, T reference, U tolerance = AbsoluteTolerance<T>());
+
+/** Validate key points. */
+template <typename T, typename U, typename V = AbsoluteTolerance<float>>
+void validate_keypoints(T target_first, T target_last, U reference_first, U reference_last, V tolerance = AbsoluteTolerance<float>());
template <typename T>
struct compare_base
@@ -358,13 +363,89 @@ void validate(const IAccessor &tensor, const SimpleTensor<T> &reference, const V
}
}
+/** Check which keypoints from [first1, last1) are missing in [first2, last2) */
+template <typename T, typename U, typename V>
+std::pair<int64_t, int64_t> compare_keypoints(T first1, T last1, U first2, U last2, V tolerance)
+{
+ int64_t num_missing = 0;
+ int64_t num_mismatches = 0;
+
+ while(first1 != last1)
+ {
+ const auto point = std::find_if(first2, last2, [&](KeyPoint point)
+ {
+ return point.x == first1->x && point.y == first1->y;
+ });
+
+ if(point == last2)
+ {
+ ++num_missing;
+ ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1)
+ ARM_COMPUTE_EXPECT_FAIL("Key point not found", framework::LogLevel::DEBUG);
+ }
+ else if(!validate(point->tracking_status, first1->tracking_status) || !validate(point->strength, first1->strength, tolerance) || !validate(point->scale, first1->scale)
+ || !validate(point->orientation, first1->orientation) || !validate(point->error, first1->error))
+ {
+ ++num_mismatches;
+ ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1)
+ ARM_COMPUTE_TEST_INFO("keypoint2 = " << *point)
+ ARM_COMPUTE_EXPECT_FAIL("Mismatching keypoint", framework::LogLevel::DEBUG);
+ }
+
+ ++first1;
+ }
+
+ return std::make_pair(num_missing, num_mismatches);
+}
+
+template <typename T, typename U, typename V>
+void validate_keypoints(T target_first, T target_last, U reference_first, U reference_last, V tolerance)
+{
+ const int64_t num_elements_target = std::distance(target_first, target_last);
+ const int64_t num_elements_reference = std::distance(reference_first, reference_last);
+
+ ARM_COMPUTE_EXPECT_EQUAL(num_elements_target, num_elements_reference, framework::LogLevel::ERRORS);
+
+ int64_t num_missing = 0;
+ int64_t num_mismatches = 0;
+
+ if(num_elements_reference > 0)
+ {
+ std::tie(num_missing, num_mismatches) = compare_keypoints(reference_first, reference_last, target_first, target_last, tolerance);
+
+ const float percent_missing = static_cast<float>(num_missing) / num_elements_reference * 100.f;
+ const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements_reference * 100.f;
+
+ ARM_COMPUTE_TEST_INFO(num_missing << " keypoints (" << std::fixed << std::setprecision(2) << percent_missing << "%) are missing in target");
+ ARM_COMPUTE_EXPECT_EQUAL(num_missing, 0, framework::LogLevel::ERRORS);
+
+ ARM_COMPUTE_TEST_INFO(num_mismatches << " keypoints (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
+ ARM_COMPUTE_EXPECT_EQUAL(num_mismatches, 0, framework::LogLevel::ERRORS);
+ }
+
+ if(num_elements_target > 0)
+ {
+ std::tie(num_missing, num_mismatches) = compare_keypoints(target_first, target_last, reference_first, reference_last, tolerance);
+
+ const float percent_missing = static_cast<float>(num_missing) / num_elements_target * 100.f;
+
+ ARM_COMPUTE_TEST_INFO(num_missing << " keypoints (" << std::fixed << std::setprecision(2) << percent_missing << "%) are not part of target");
+ ARM_COMPUTE_EXPECT_EQUAL(num_missing, 0, framework::LogLevel::ERRORS);
+ }
+}
+
template <typename T, typename U>
-void validate(T target, T reference, U tolerance)
+bool validate(T target, T reference, U tolerance)
{
ARM_COMPUTE_TEST_INFO("reference = " << std::setprecision(5) << framework::make_printable(reference));
ARM_COMPUTE_TEST_INFO("target = " << std::setprecision(5) << framework::make_printable(target));
ARM_COMPUTE_TEST_INFO("tolerance = " << std::setprecision(5) << framework::make_printable(static_cast<typename U::value_type>(tolerance)));
- ARM_COMPUTE_EXPECT((compare<U>(target, reference, tolerance)), framework::LogLevel::ERRORS);
+
+ const bool equal = compare<U>(target, reference, tolerance);
+
+ ARM_COMPUTE_EXPECT(equal, framework::LogLevel::ERRORS);
+
+ return equal;
}
template <typename T, typename U>
diff --git a/tests/validation/fixtures/HarrisCornersFixture.h b/tests/validation/fixtures/HarrisCornersFixture.h
new file mode 100644
index 0000000000..50677f6bf3
--- /dev/null
+++ b/tests/validation/fixtures/HarrisCornersFixture.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+#ifndef ARM_COMPUTE_TEST_HARRIS_CORNERS_FIXTURE
+#define ARM_COMPUTE_TEST_HARRIS_CORNERS_FIXTURE
+
+#include "arm_compute/core/TensorShape.h"
+#include "arm_compute/core/Types.h"
+#include "tests/AssetsLibrary.h"
+#include "tests/Globals.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Fixture.h"
+#include "tests/validation/CPP/HarrisCornerDetector.h"
+#include "tests/validation/Helpers.h"
+
+namespace arm_compute
+{
+class CLHarrisCorners;
+class NEHarrisCorners;
+
+namespace test
+{
+namespace validation
+{
+template <typename TensorType, typename AccessorType, typename ArrayType, typename FunctionType, typename T>
+class HarrisCornersValidationFixture : public framework::Fixture
+{
+public:
+ template <typename...>
+ void setup(TensorShape shape, int gradient_size, int block_size, BorderMode border_mode, bool use_fp16, Format format)
+ {
+ HarrisCornersParameters params = harris_corners_parameters();
+
+ _target = compute_target(shape, gradient_size, block_size, border_mode, use_fp16, format, params);
+ //TODO(COMPMID-543): Add use_fp16 to reference
+ _reference = compute_reference(shape, gradient_size, block_size, border_mode, format, params);
+ }
+
+protected:
+ template <typename U>
+ void fill(U &&tensor)
+ {
+ library->fill_tensor_uniform(tensor, 0);
+ }
+
+ template <typename F, typename std::enable_if<std::is_same<F, NEHarrisCorners>::value, int>::type = 0>
+ void configure_target(F &func, TensorType &src, ArrayType &corners, int gradient_size, int block_size, BorderMode border_mode, bool use_fp16, const HarrisCornersParameters &params)
+ {
+ func.configure(&src, params.threshold, params.min_dist, params.sensitivity, gradient_size, block_size, &corners, border_mode, params.constant_border_value, use_fp16);
+ }
+
+ template <typename F, typename std::enable_if<std::is_same<F, CLHarrisCorners>::value, int>::type = 0>
+ void configure_target(F &func, TensorType &src, ArrayType &corners, int gradient_size, int block_size, BorderMode border_mode, bool use_fp16, const HarrisCornersParameters &params)
+ {
+ ARM_COMPUTE_UNUSED(use_fp16);
+ ARM_COMPUTE_ERROR_ON(use_fp16);
+ func.configure(&src, params.threshold, params.min_dist, params.sensitivity, gradient_size, block_size, &corners, border_mode, params.constant_border_value);
+ }
+
+ ArrayType compute_target(const TensorShape &shape, int gradient_size, int block_size, BorderMode border_mode, bool use_fp16, Format format, const HarrisCornersParameters &params)
+ {
+ // Create tensors
+ TensorType src = create_tensor<TensorType>(shape, data_type_from_format(format));
+ src.info()->set_format(format);
+
+ // Create array of keypoints
+ ArrayType corners(shape.total_size());
+
+ // Create harris corners configure function
+ FunctionType harris_corners;
+ configure_target<FunctionType>(harris_corners, src, corners, gradient_size, block_size, border_mode, use_fp16, params);
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Allocate tensors
+ src.allocator()->allocate();
+
+ ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Fill tensors
+ fill(AccessorType(src));
+
+ // Compute function
+ harris_corners.run();
+
+ return corners;
+ }
+
+ std::vector<KeyPoint> compute_reference(const TensorShape &shape, int gradient_size, int block_size, BorderMode border_mode, Format format, const HarrisCornersParameters &params)
+ {
+ // Create reference
+ SimpleTensor<T> src{ shape, format };
+
+ // Fill reference
+ fill(src);
+
+ return reference::harris_corner_detector<T>(src, params.threshold, params.min_dist, params.sensitivity, gradient_size, block_size, border_mode, params.constant_border_value);
+ }
+
+ ArrayType _target{};
+ std::vector<KeyPoint> _reference{};
+};
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* ARM_COMPUTE_TEST_HARRIS_CORNERS_FIXTURE */
diff --git a/tests/validation_old/CL/HarrisCorners.cpp b/tests/validation_old/CL/HarrisCorners.cpp
deleted file mode 100644
index ff39918a43..0000000000
--- a/tests/validation_old/CL/HarrisCorners.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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 "Utils.h"
-#include "tests/AssetsLibrary.h"
-#include "tests/Globals.h"
-#include "tests/validation_old/Datasets.h"
-#include "tests/validation_old/Reference.h"
-#include "tests/validation_old/Validation.h"
-#include "tests/validation_old/ValidationUserConfiguration.h"
-#include "utils/TypePrinter.h"
-
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Types.h"
-#include "arm_compute/runtime/CL/CLArray.h"
-#include "arm_compute/runtime/CL/functions/CLHarrisCorners.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "arm_compute/runtime/TensorAllocator.h"
-
-#include "PaddingCalculator.h"
-#include "tests/validation_old/boost_wrapper.h"
-
-#include <random>
-#include <string>
-
-using namespace arm_compute;
-using namespace arm_compute::test;
-using namespace arm_compute::test::validation;
-
-namespace
-{
-/** Compute CL Harris corners function.
- *
- * @param[in] shape Shape of input tensor
- * @param[in] threshold Minimum threshold with which to eliminate Harris Corner scores (computed using the normalized Sobel kernel).
- * @param[in] min_dist Radial Euclidean distance for the euclidean distance stage
- * @param[in] sensitivity Sensitivity threshold k from the Harris-Stephens equation
- * @param[in] gradient_size The gradient window size to use on the input. The implementation supports 3, 5, and 7
- * @param[in] block_size The block window size used to compute the Harris Corner score. The implementation supports 3, 5, and 7.
- * @param[in] border_mode Border mode to use
- * @param[in] constant_border_value Constant value to use for borders if border_mode is set to CONSTANT.
- *
- * @return Computed corners' keypoints.
- */
-void compute_harris_corners(const TensorShape &shape, CLKeyPointArray &corners, float threshold, float min_dist, float sensitivity,
- int32_t gradient_size, int32_t block_size, BorderMode border_mode, uint8_t constant_border_value)
-{
- // Create tensors
- CLTensor src = create_tensor<CLTensor>(shape, DataType::U8);
- src.info()->set_format(Format::U8);
-
- // Create harris corners configure function
- CLHarrisCorners harris_corners;
- harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient_size, block_size, &corners, border_mode, constant_border_value);
-
- // Allocate tensors
- src.allocator()->allocate();
-
- BOOST_TEST(!src.info()->is_resizable());
-
- // Fill tensors
- library->fill_tensor_uniform(CLAccessor(src), 0);
-
- // Compute function
- harris_corners.run();
-}
-} // namespace
-
-#ifndef DOXYGEN_SKIP_THIS
-BOOST_AUTO_TEST_SUITE(CL)
-BOOST_AUTO_TEST_SUITE(HarrisCorners)
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
-BOOST_DATA_TEST_CASE(Configuration, (Small2DShapes() + Large2DShapes()) * BorderModes()
- * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }),
- shape, border_mode, gradient, block)
-{
- // Create tensors
- CLTensor src = create_tensor<CLTensor>(shape, DataType::U8);
- src.info()->set_format(Format::U8);
-
- CLKeyPointArray corners(shape.total_size());
-
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- float min_dist = real_dist(gen);
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- BOOST_TEST(src.info()->is_resizable());
-
- // Create harris corners configure function
- CLHarrisCorners harris_corners;
- harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient, block, &corners, border_mode, constant_border_value);
-
- // Validate valid region
- const ValidRegion valid_region = shape_to_valid_region(shape);
-
- validate(src.info()->valid_region(), valid_region);
-
- // Validate padding
- PaddingCalculator calculator(shape.x(), 8);
-
- calculator.set_border_mode(border_mode);
- calculator.set_border_size(gradient / 2);
- calculator.set_access_offset(-gradient / 2);
- calculator.set_accessed_elements(16);
-
- const PaddingSize padding = calculator.required_padding();
-
- validate(src.info()->padding(), padding);
-}
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
-BOOST_DATA_TEST_CASE(RunSmall, Small2DShapes() * BorderModes() * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }), shape, border_mode, gradient, block)
-{
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- const float min_dist = real_dist(gen);
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- // Create array of keypoints
- CLKeyPointArray dst(shape.total_size());
-
- // Compute function
- compute_harris_corners(shape, dst, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Compute reference
- KeyPointArray ref_dst = Reference::compute_reference_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Validate output
- dst.map();
- validate(dst, ref_dst, 1);
- dst.unmap();
-}
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
-BOOST_DATA_TEST_CASE(RunLarge, Large2DShapes() * BorderModes() * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }), shape, border_mode, gradient, block)
-{
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- const float min_dist = real_dist(gen);
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- // Create array of keypoints
- CLKeyPointArray dst(shape.total_size());
-
- // Compute function
- compute_harris_corners(shape, dst, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Compute reference
- KeyPointArray ref_dst = Reference::compute_reference_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Validate output
- dst.map();
- validate(dst, ref_dst);
- dst.unmap();
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-BOOST_AUTO_TEST_SUITE_END()
-#endif /* DOXYGEN_SKIP_THIS */
diff --git a/tests/validation_old/NEON/HarrisCorners.cpp b/tests/validation_old/NEON/HarrisCorners.cpp
deleted file mode 100644
index 172be87dc2..0000000000
--- a/tests/validation_old/NEON/HarrisCorners.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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 "NEON/Accessor.h"
-#include "NEON/Helper.h"
-#include "Utils.h"
-#include "tests/AssetsLibrary.h"
-#include "tests/Globals.h"
-#include "tests/validation_old/Datasets.h"
-#include "tests/validation_old/Reference.h"
-#include "tests/validation_old/Validation.h"
-#include "tests/validation_old/ValidationUserConfiguration.h"
-#include "utils/TypePrinter.h"
-
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Types.h"
-#include "arm_compute/runtime/NEON/functions/NEHarrisCorners.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "arm_compute/runtime/TensorAllocator.h"
-
-#include "PaddingCalculator.h"
-#include "tests/validation_old/boost_wrapper.h"
-
-#include <random>
-#include <string>
-
-using namespace arm_compute;
-using namespace arm_compute::test;
-using namespace arm_compute::test::validation;
-
-namespace
-{
-/** Compute Neon Harris corners function.
- *
- * @param[in] shape Shape of input tensor
- * @param[in] threshold Minimum threshold with which to eliminate Harris Corner scores (computed using the normalized Sobel kernel).
- * @param[in] min_dist Radial Euclidean distance for the euclidean distance stage
- * @param[in] sensitivity Sensitivity threshold k from the Harris-Stephens equation
- * @param[in] gradient_size The gradient window size to use on the input. The implementation supports 3, 5, and 7
- * @param[in] block_size The block window size used to compute the Harris Corner score. The implementation supports 3, 5, and 7.
- * @param[in] border_mode Border mode to use
- * @param[in] constant_border_value Constant value to use for borders if border_mode is set to CONSTANT.
- * @param[in] use_fp16 If true the FP16 kernels will be used. If false F32 kernels are used.
- *
- * @return Computed corners' keypoints.
- */
-KeyPointArray compute_harris_corners(const TensorShape &shape, float threshold, float min_dist, float sensitivity,
- int32_t gradient_size, int32_t block_size, BorderMode border_mode, uint8_t constant_border_value, bool use_fp16)
-{
- // Create tensors
- Tensor src = create_tensor<Tensor>(shape, DataType::U8);
- src.info()->set_format(Format::U8);
-
- // Create array of keypoints
- KeyPointArray corners(shape.total_size());
-
- // Create harris corners configure function
- NEHarrisCorners harris_corners;
- harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient_size, block_size, &corners, border_mode, constant_border_value, use_fp16);
-
- // Allocate tensors
- src.allocator()->allocate();
-
- BOOST_TEST(!src.info()->is_resizable());
-
- // Fill tensors
- library->fill_tensor_uniform(Accessor(src), 0);
-
- // Compute function
- harris_corners.run();
-
- return corners;
-}
-} // namespace
-
-#ifndef DOXYGEN_SKIP_THIS
-BOOST_AUTO_TEST_SUITE(NEON)
-BOOST_AUTO_TEST_SUITE(HarrisCorners)
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
-BOOST_DATA_TEST_CASE(Configuration, (Small2DShapes() + Large2DShapes()) * BorderModes()
- * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }),
- shape, border_mode, gradient, block)
-{
- // Create tensors
- Tensor src = create_tensor<Tensor>(shape, DataType::U8);
- src.info()->set_format(Format::U8);
-
- KeyPointArray corners;
-
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- const float min_dist = real_dist(gen);
-
- // 50% chance to use fp16
- bool use_fp16 = real_dist(gen) < max_euclidean_distance / 2 ? true : false;
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- BOOST_TEST(src.info()->is_resizable());
-
- // Create harris corners configure function
- NEHarrisCorners harris_corners;
- harris_corners.configure(&src, threshold, min_dist, sensitivity, gradient, block, &corners, border_mode, constant_border_value, use_fp16);
-
- // Validate valid region
- const ValidRegion valid_region = shape_to_valid_region(shape);
-
- validate(src.info()->valid_region(), valid_region);
-
- // Validate padding
- PaddingCalculator calculator(shape.x(), 8);
-
- calculator.set_border_mode(border_mode);
- calculator.set_border_size(gradient / 2);
- calculator.set_access_offset(-gradient / 2);
- calculator.set_accessed_elements(16);
-
- const PaddingSize padding = calculator.required_padding();
-
- validate(src.info()->padding(), padding);
-}
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
-BOOST_DATA_TEST_CASE(RunSmall, Small2DShapes() * BorderModes() * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }), shape, border_mode, gradient, block)
-{
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- const float min_dist = real_dist(gen);
-
- // 50% chance to use fp16
- bool use_fp16 = real_dist(gen) < max_euclidean_distance / 2 ? true : false;
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- // Compute function
- KeyPointArray dst = compute_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value, use_fp16);
-
- // Compute reference
- KeyPointArray ref_dst = Reference::compute_reference_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Validate output
- validate(dst, ref_dst);
-}
-
-BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
-BOOST_DATA_TEST_CASE(RunLarge, Large2DShapes() * BorderModes() * boost::unit_test::data::make({ 3, 5, 7 }) * boost::unit_test::data::make({ 3, 5, 7 }), shape, border_mode, gradient, block)
-{
- uint8_t constant_border_value = 0;
-
- std::mt19937 gen(user_config.seed.get());
- std::uniform_real_distribution<float> real_dist(0.01, std::numeric_limits<float>::min());
-
- const float threshold = real_dist(gen);
- const float sensitivity = real_dist(gen);
- const float max_euclidean_distance = 30.f;
-
- real_dist = std::uniform_real_distribution<float>(0.f, max_euclidean_distance);
- float min_dist = real_dist(gen);
-
- // 50% chance to use fp16
- bool use_fp16 = real_dist(gen) < max_euclidean_distance / 2 ? true : false;
-
- // Generate a random constant value if border_mode is constant
- if(border_mode == BorderMode::CONSTANT)
- {
- std::uniform_int_distribution<uint8_t> int_dist(0, 255);
- constant_border_value = int_dist(gen);
- }
-
- // Compute function
- KeyPointArray dst = compute_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value, use_fp16);
-
- // Compute reference
- KeyPointArray ref_dst = Reference::compute_reference_harris_corners(shape, threshold, min_dist, sensitivity, gradient, block, border_mode, constant_border_value);
-
- // Validate output
- validate(dst, ref_dst);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-BOOST_AUTO_TEST_SUITE_END()
-#endif /* DOXYGEN_SKIP_THIS */
diff --git a/utils/TypePrinter.h b/utils/TypePrinter.h
index 6e2d7e24f3..861794aab3 100644
--- a/utils/TypePrinter.h
+++ b/utils/TypePrinter.h
@@ -622,5 +622,18 @@ inline std::string to_string(const PoolingLayerInfo &info)
return str.str();
}
+/** Formatted output of the KeyPoint type. */
+inline ::std::ostream &operator<<(::std::ostream &os, const KeyPoint &point)
+{
+ os << "{x=" << point.x << ","
+ << "y=" << point.y << ","
+ << "strength=" << point.strength << ","
+ << "scale=" << point.scale << ","
+ << "orientation=" << point.orientation << ","
+ << "tracking_status=" << point.tracking_status << ","
+ << "error=" << point.error << "}";
+
+ return os;
+}
} // namespace arm_compute
#endif /* __ARM_COMPUTE_TEST_TYPE_PRINTER_H__ */