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>
diff --git a/tests/validation/CL/HarrisCorners.cpp b/tests/validation/CL/HarrisCorners.cpp
new file mode 100644
index 0000000..4188cb5
--- /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 0000000..3babfee
--- /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 0000000..042e857
--- /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 0000000..eab5cec
--- /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 0000000..2086abf
--- /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 e276888..af3ed90 100644
--- a/tests/validation/CPP/Utils.cpp
+++ b/tests/validation/CPP/Utils.cpp
@@ -31,36 +31,6 @@
 {
 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 2d879c1..91d1afe 100644
--- a/tests/validation/CPP/Utils.h
+++ b/tests/validation/CPP/Utils.h
@@ -41,8 +41,31 @@
 {
 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 d3bcbbd..23ad62a 100644
--- a/tests/validation/Helpers.cpp
+++ b/tests/validation/Helpers.cpp
@@ -29,6 +29,48 @@
 {
 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 @@
 
     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 30c6724..eecf976 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 @@
  * @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 @@
  */
 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 0000000..6d66549
--- /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 aa70e6e..ebca193 100644
--- a/tests/validation/Validation.cpp
+++ b/tests/validation/Validation.cpp
@@ -297,11 +297,24 @@
 {
     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 < min_num; ++i)
+    for(int i = 0; i < num_elements; ++i)
     {
-        ARM_COMPUTE_EXPECT_EQUAL(classified_labels[i], expected_labels[i], framework::LogLevel::ERRORS);
+        if(classified_labels[i] != expected_labels[i])
+        {
+            ++num_mismatches;
+            ARM_COMPUTE_EXPECT_EQUAL(classified_labels[i], expected_labels[i], framework::LogLevel::DEBUG);
+        }
+    }
+
+    if(num_elements > 0)
+    {
+        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 b6e7b8e..5e5dab0 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 @@
  * - 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 @@
     }
 }
 
+/** 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 0000000..50677f6
--- /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 */