aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAbe Mbise <abe.mbise@arm.com>2018-02-09 14:13:02 +0000
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:47:18 +0000
commit562fe0fcedace39735ead089cfdc4e2b07e40aad (patch)
tree8460b26191fb1fde45296bb1e19fddb1d84d0d0f /tests
parent6a3daf1f566c0bb722207f125d2aaee6930e9947 (diff)
downloadComputeLibrary-562fe0fcedace39735ead089cfdc4e2b07e40aad.tar.gz
COMPMID-578: Faster common keypoint validation routine for FAST and Harris corners
Change-Id: I1f3038ccfa6be5e0a19bec11a89af5ed2247c42f Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/119936 Reviewed-by: Anthony Barbier <anthony.barbier@arm.com> Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com> Tested-by: Jenkins <bsgcomp@arm.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/AssetsLibrary.cpp40
-rw-r--r--tests/validation/CL/FastCorners.cpp15
-rw-r--r--tests/validation/CL/HarrisCorners.cpp2
-rw-r--r--tests/validation/FastValidation.h161
-rw-r--r--tests/validation/NEON/FastCorners.cpp15
-rw-r--r--tests/validation/NEON/HarrisCorners.cpp6
-rw-r--r--tests/validation/Validation.h189
-rw-r--r--tests/validation/fixtures/FastCornersFixture.h34
-rw-r--r--tests/validation/reference/FastCorners.cpp38
9 files changed, 197 insertions, 303 deletions
diff --git a/tests/AssetsLibrary.cpp b/tests/AssetsLibrary.cpp
index f5eac8a59a..1cbd3b4e3d 100644
--- a/tests/AssetsLibrary.cpp
+++ b/tests/AssetsLibrary.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2017-2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -48,19 +48,26 @@ namespace
template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void rgb_to_luminance(const RawTensor &src, RawTensor &dst)
{
- const size_t min_size = std::min(src.size(), dst.size());
+ // Ensure in/out tensors have same image dimensions (independent of element size and number of channels)
+ ARM_COMPUTE_ERROR_ON_MSG(src.num_elements() != dst.num_elements(), "Input and output images must have equal dimensions");
- for(size_t i = 0, j = 0; i < min_size; i += 3, ++j)
+ const size_t num_elements = dst.num_elements();
+
+ // Currently, input is always RGB888 (3 U8 channels per element). Output can be U8, U16/S16 or U32
+ // Note that src.data()[i] returns pointer to first channel of element[i], so RGB values have [0,1,2] offsets
+ for(size_t i = 0, j = 0; j < num_elements; i += 3, ++j)
{
- reinterpret_cast<T *>(dst.data())[j] = 0.2126f * src.data()[i + 0] + 0.7152f * src.data()[i + 1] + 0.0722f * src.data()[i + 2];
+ reinterpret_cast<T *>(dst.data())[j] = 0.2126f * src.data()[i] + 0.7152f * src.data()[i + 1] + 0.0722f * src.data()[i + 2];
}
}
void extract_r_from_rgb(const RawTensor &src, RawTensor &dst)
{
- const size_t min_size = std::min(src.size(), dst.size());
+ ARM_COMPUTE_ERROR_ON(src.size() != 3 * dst.size());
+
+ const size_t num_elements = dst.num_elements();
- for(size_t i = 0, j = 0; i < min_size; i += 3, ++j)
+ for(size_t i = 0, j = 0; j < num_elements; i += 3, ++j)
{
dst.data()[j] = src.data()[i];
}
@@ -68,9 +75,23 @@ void extract_r_from_rgb(const RawTensor &src, RawTensor &dst)
void extract_g_from_rgb(const RawTensor &src, RawTensor &dst)
{
- const size_t min_size = std::min(src.size(), dst.size());
+ ARM_COMPUTE_ERROR_ON(src.size() != 3 * dst.size());
+
+ const size_t num_elements = dst.num_elements();
+
+ for(size_t i = 1, j = 0; j < num_elements; i += 3, ++j)
+ {
+ dst.data()[j] = src.data()[i];
+ }
+}
+
+void extract_b_from_rgb(const RawTensor &src, RawTensor &dst)
+{
+ ARM_COMPUTE_ERROR_ON(src.size() != 3 * dst.size());
+
+ const size_t num_elements = dst.num_elements();
- for(size_t i = 1, j = 0; i < min_size; i += 3, ++j)
+ for(size_t i = 2, j = 0; j < num_elements; i += 3, ++j)
{
dst.data()[j] = src.data()[i];
}
@@ -321,7 +342,8 @@ const AssetsLibrary::Extractor &AssetsLibrary::get_extractor(Format format, Chan
static std::map<std::pair<Format, Channel>, Extractor> extractors =
{
{ std::make_pair(Format::RGB888, Channel::R), extract_r_from_rgb },
- { std::make_pair(Format::RGB888, Channel::G), extract_g_from_rgb }
+ { std::make_pair(Format::RGB888, Channel::G), extract_g_from_rgb },
+ { std::make_pair(Format::RGB888, Channel::B), extract_b_from_rgb }
};
const auto it = extractors.find(std::make_pair(format, channel));
diff --git a/tests/validation/CL/FastCorners.cpp b/tests/validation/CL/FastCorners.cpp
index b5086ef75d..93af59d845 100644
--- a/tests/validation/CL/FastCorners.cpp
+++ b/tests/validation/CL/FastCorners.cpp
@@ -28,11 +28,12 @@
#include "tests/CL/CLAccessor.h"
#include "tests/CL/CLArrayAccessor.h"
#include "tests/PaddingCalculator.h"
+#include "tests/datasets/ImageFileDatasets.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/FastValidation.h"
+#include "tests/validation/Validation.h"
#include "tests/validation/fixtures/FastCornersFixture.h"
namespace arm_compute
@@ -45,10 +46,6 @@ namespace
{
/* Radius of the Bresenham circle around the candidate point */
const unsigned int bresenham_radius = 3;
-/* Allowed percentage of keypoints missing for target */
-const float allowed_missing = 10.f;
-/* Allowed percentage of keypoints mismatching between target and reference */
-const float allowed_mismatching = 10.f;
/* Tolerance used to compare corner strengths */
const AbsoluteTolerance<float> tolerance(0.5f);
} // namespace
@@ -95,21 +92,21 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combi
template <typename T>
using CLFastCornersFixture = FastCornersValidationFixture<CLTensor, CLAccessor, CLKeyPointArray, CLFastCorners, T>;
-FIXTURE_DATA_TEST_CASE(RunSmall, CLFastCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small2DShapes(), framework::dataset::make("Format", Format::U8)),
+FIXTURE_DATA_TEST_CASE(RunSmall, CLFastCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::SmallImageFiles(), framework::dataset::make("Format", Format::U8)),
framework::dataset::make("SuppressNonMax", { false, true })),
framework::dataset::make("BorderMode", BorderMode::UNDEFINED)))
{
// Validate output
CLArrayAccessor<KeyPoint> array(_target);
- fast_validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance, allowed_missing, allowed_mismatching);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance);
}
-FIXTURE_DATA_TEST_CASE(RunLarge, CLFastCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::Large2DShapes(), framework::dataset::make("Format", Format::U8)),
+FIXTURE_DATA_TEST_CASE(RunLarge, CLFastCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::LargeImageFiles(), framework::dataset::make("Format", Format::U8)),
framework::dataset::make("SuppressNonMax", { false, true })),
framework::dataset::make("BorderMode", BorderMode::UNDEFINED)))
{
// Validate output
CLArrayAccessor<KeyPoint> array(_target);
- fast_validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance, allowed_missing, allowed_mismatching);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance);
}
TEST_SUITE_END()
diff --git a/tests/validation/CL/HarrisCorners.cpp b/tests/validation/CL/HarrisCorners.cpp
index 00b6910223..890367c166 100644
--- a/tests/validation/CL/HarrisCorners.cpp
+++ b/tests/validation/CL/HarrisCorners.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018 ARM Limited.
+ * Copyright (c) 2017-2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
diff --git a/tests/validation/FastValidation.h b/tests/validation/FastValidation.h
deleted file mode 100644
index 10757cfdc2..0000000000
--- a/tests/validation/FastValidation.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2017-2018 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_FAST_VALIDATION_H__
-#define __ARM_COMPUTE_TEST_FAST_VALIDATION_H__
-
-#include "Validation.h"
-
-namespace arm_compute
-{
-namespace test
-{
-namespace validation
-{
-/** Check which keypoints from [first1, last1) are missing in [first2, last2) */
-template <typename T, typename U, typename V>
-std::pair<int64_t, int64_t> fast_compare_keypoints(T first1, T last1, U first2, U last2, V tolerance, bool check_mismatches = true)
-{
- /* Keypoint (x,y) should have similar strength (within tolerance) and other properties in both reference and target */
- const auto compare_props_eq = [&](const KeyPoint & lhs, const KeyPoint & rhs)
- {
- return compare<V>(lhs.strength, rhs.strength, tolerance)
- && lhs.tracking_status == rhs.tracking_status
- && lhs.scale == rhs.scale
- && lhs.orientation == rhs.orientation
- && lhs.error == rhs.error;
- };
-
- /* Used to sort KeyPoints by coordinates (x, y) */
- const auto compare_coords_lt = [](const KeyPoint & lhs, const KeyPoint & rhs)
- {
- return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
- };
-
- std::sort(first1, last1, compare_coords_lt);
- std::sort(first2, last2, compare_coords_lt);
-
- if(check_mismatches)
- {
- std::cout << "ref count = " << std::distance(first1, last1) << " \ttarget count = " << std::distance(first2, last2) << std::endl;
- }
-
- int64_t num_missing = 0;
- int64_t num_mismatches = 0;
- bool rest_missing = false;
-
- while(first1 != last1)
- {
- if(first2 == last2)
- {
- // num_missing += std::distance(first1, last1);
- rest_missing = true;
- ARM_COMPUTE_TEST_INFO("All key points from (" << first1->x << "," << first1->y << ") onwards not found");
- break;
- }
-
- if(compare_coords_lt(*first1, *first2))
- {
- ++num_missing;
- ARM_COMPUTE_TEST_INFO("Key point not found");
- ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1++);
- }
- else
- {
- if(!compare_coords_lt(*first2, *first1)) // Equal coordinates
- {
- if(check_mismatches && !compare_props_eq(*first1, *first2)) // Check other properties
- {
- ++num_mismatches;
- ARM_COMPUTE_TEST_INFO("Mismatching keypoint");
- ARM_COMPUTE_TEST_INFO("keypoint1 [ref] = " << *first1);
- ARM_COMPUTE_TEST_INFO("keypoint2 [tgt] = " << *first2);
- }
- ++first1;
- }
- ++first2;
- }
- }
-
- if(rest_missing)
- {
- while(first1 != last1)
- {
- ++num_missing;
- ARM_COMPUTE_TEST_INFO("Key point not found");
- ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1++);
- }
- }
-
- return std::make_pair(num_missing, num_mismatches);
-}
-
-template <typename T, typename U, typename V>
-void fast_validate_keypoints(T target_first, T target_last, U reference_first, U reference_last, V tolerance,
- float allowed_missing_percentage, float allowed_mismatch_percentage)
-{
- const int64_t num_elements_target = std::distance(target_first, target_last);
- const int64_t num_elements_reference = std::distance(reference_first, reference_last);
-
- int64_t num_missing = 0;
- int64_t num_mismatches = 0;
-
- if(num_elements_reference > 0)
- {
- std::tie(num_missing, num_mismatches) = fast_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 << "%) in ref are missing from target");
- ARM_COMPUTE_EXPECT(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
-
- ARM_COMPUTE_TEST_INFO(num_mismatches << " keypoints (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
- ARM_COMPUTE_EXPECT(percent_mismatches <= allowed_mismatch_percentage, framework::LogLevel::ERRORS);
-
- std::cout << "Mismatched keypoints: " << num_mismatches << "/" << num_elements_reference << " = " << std::fixed << std::setprecision(2) << percent_mismatches
- << "% \tMax allowed: " << allowed_mismatch_percentage << "%" << std::endl;
- std::cout << "Missing (not in tgt): " << num_missing << "/" << num_elements_reference << " = " << std::fixed << std::setprecision(2) << percent_missing
- << "% \tMax allowed: " << allowed_missing_percentage << "%" << std::endl;
- }
-
- if(num_elements_target > 0)
- {
- // Note: no need to check for mismatches a second time (last argument is 'false')
- std::tie(num_missing, num_mismatches) = fast_compare_keypoints(target_first, target_last, reference_first, reference_last, tolerance, false);
-
- 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 << "%) in target are missing from ref");
- ARM_COMPUTE_EXPECT(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
-
- std::cout << "Missing (not in ref): " << num_missing << "/" << num_elements_target << " = " << std::fixed << std::setprecision(2) << percent_missing
- << "% \tMax allowed: " << allowed_missing_percentage << "%\n"
- << std::endl;
- }
-}
-
-} // namespace validation
-} // namespace test
-} // namespace arm_compute
-#endif /* __ARM_COMPUTE_TEST_FAST_VALIDATION_H__ */
diff --git a/tests/validation/NEON/FastCorners.cpp b/tests/validation/NEON/FastCorners.cpp
index 4d42aa5349..4416662fea 100644
--- a/tests/validation/NEON/FastCorners.cpp
+++ b/tests/validation/NEON/FastCorners.cpp
@@ -28,11 +28,12 @@
#include "tests/NEON/Accessor.h"
#include "tests/NEON/ArrayAccessor.h"
#include "tests/PaddingCalculator.h"
+#include "tests/datasets/ImageFileDatasets.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/FastValidation.h"
+#include "tests/validation/Validation.h"
#include "tests/validation/fixtures/FastCornersFixture.h"
namespace arm_compute
@@ -45,10 +46,6 @@ namespace
{
/* Radius of the Bresenham circle around the candidate point */
const unsigned int bresenham_radius = 3;
-/* Allowed percentage of keypoints missing for target */
-const float allowed_missing = 10.f;
-/* Allowed percentage of keypoints mismatching between target and reference */
-const float allowed_mismatching = 10.f;
/* Tolerance used to compare corner strengths */
const AbsoluteTolerance<float> tolerance(0.5f);
} // namespace
@@ -94,21 +91,21 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combi
template <typename T>
using NEFastCornersFixture = FastCornersValidationFixture<Tensor, Accessor, KeyPointArray, NEFastCorners, T>;
-FIXTURE_DATA_TEST_CASE(RunSmall, NEFastCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small2DShapes(), framework::dataset::make("Format", Format::U8)),
+FIXTURE_DATA_TEST_CASE(RunSmall, NEFastCornersFixture<uint8_t>, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::SmallImageFiles(), framework::dataset::make("Format", Format::U8)),
framework::dataset::make("SuppressNonMax", { false, true })),
framework::dataset::make("BorderMode", BorderMode::UNDEFINED)))
{
// Validate output
ArrayAccessor<KeyPoint> array(_target);
- fast_validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance, allowed_missing, allowed_mismatching);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance);
}
-FIXTURE_DATA_TEST_CASE(RunLarge, NEFastCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::Large2DShapes(), framework::dataset::make("Format", Format::U8)),
+FIXTURE_DATA_TEST_CASE(RunLarge, NEFastCornersFixture<uint8_t>, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::LargeImageFiles(), framework::dataset::make("Format", Format::U8)),
framework::dataset::make("SuppressNonMax", { false, true })),
framework::dataset::make("BorderMode", BorderMode::UNDEFINED)))
{
// Validate output
ArrayAccessor<KeyPoint> array(_target);
- fast_validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance, allowed_missing, allowed_mismatching);
+ validate_keypoints(array.buffer(), array.buffer() + array.num_values(), _reference.begin(), _reference.end(), tolerance);
}
TEST_SUITE_END()
diff --git a/tests/validation/NEON/HarrisCorners.cpp b/tests/validation/NEON/HarrisCorners.cpp
index b8d791774f..3474a96f8f 100644
--- a/tests/validation/NEON/HarrisCorners.cpp
+++ b/tests/validation/NEON/HarrisCorners.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018 ARM Limited.
+ * Copyright (c) 2017-2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -46,9 +46,9 @@ namespace validation
namespace
{
/* Allowed percentage of keypoints missing for target */
-float allowed_missing_percentage = 10.f;
+const float allowed_missing_percentage = 10.f;
/* Allowed percentage of keypoints mismatching between target and reference */
-float allowed_mismatch_percentage = 10.f;
+const float allowed_mismatch_percentage = 10.f;
const auto use_fp16 = framework::dataset::make("UseFP16",
{
diff --git a/tests/validation/Validation.h b/tests/validation/Validation.h
index b12d7de97a..651b7e5b3c 100644
--- a/tests/validation/Validation.h
+++ b/tests/validation/Validation.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018 ARM Limited.
+ * Copyright (c) 2017-2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -473,76 +473,6 @@ void validate_wrap(const IAccessor &tensor, const SimpleTensor<T> &reference, co
}
}
-/** 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("Key point not found" << *first1)
- ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1)
- }
- 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("Mismatching keypoint")
- ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1)
- ARM_COMPUTE_TEST_INFO("keypoint2 = " << *point)
- }
-
- ++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,
- float allowed_missing_percentage, float allowed_mismatch_percentage)
-{
- const int64_t num_elements_target = std::distance(target_first, target_last);
- const int64_t num_elements_reference = std::distance(reference_first, reference_last);
-
- 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(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
-
- ARM_COMPUTE_TEST_INFO(num_mismatches << " keypoints (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
- ARM_COMPUTE_EXPECT(percent_mismatches <= allowed_mismatch_percentage, 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(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
- }
-}
-
template <typename T, typename U>
void validate(const IAccessor &tensor, const SimpleTensor<T> &reference, const SimpleTensor<T> &valid_mask, U tolerance_value, float tolerance_number)
{
@@ -652,6 +582,123 @@ void validate_min_max_loc(const MinMaxLocationValues<T> &target, const MinMaxLoc
}
}
+/** 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, bool check_mismatches = true)
+{
+ /* Keypoint (x,y) should have similar strength (within tolerance) and other properties in both reference and target */
+ const auto compare_props_eq = [&](const KeyPoint & lhs, const KeyPoint & rhs)
+ {
+ return compare<V>(lhs.strength, rhs.strength, tolerance)
+ && lhs.tracking_status == rhs.tracking_status
+ && lhs.scale == rhs.scale
+ && lhs.orientation == rhs.orientation
+ && lhs.error == rhs.error;
+ };
+
+ /* Used to sort KeyPoints by coordinates (x, y) */
+ const auto compare_coords_lt = [](const KeyPoint & lhs, const KeyPoint & rhs)
+ {
+ return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
+ };
+
+ std::sort(first1, last1, compare_coords_lt);
+ std::sort(first2, last2, compare_coords_lt);
+
+ if(check_mismatches)
+ {
+ ARM_COMPUTE_TEST_INFO("Checking for mismatches: ref count = " << std::distance(first1, last1) << " \ttarget count = " << std::distance(first2, last2));
+ }
+
+ int64_t num_missing = 0;
+ int64_t num_mismatches = 0;
+ bool rest_missing = false;
+
+ while(first1 != last1)
+ {
+ if(first2 == last2)
+ {
+ rest_missing = true;
+ break;
+ }
+
+ if(compare_coords_lt(*first1, *first2))
+ {
+ ++num_missing;
+ ARM_COMPUTE_TEST_INFO("Key point not found");
+ ARM_COMPUTE_TEST_INFO("keypoint1 = " << *first1++);
+ }
+ else
+ {
+ if(!compare_coords_lt(*first2, *first1)) // Equal coordinates
+ {
+ if(check_mismatches && !compare_props_eq(*first1, *first2)) // Check other properties
+ {
+ ++num_mismatches;
+ ARM_COMPUTE_TEST_INFO("Mismatching keypoint");
+ ARM_COMPUTE_TEST_INFO("keypoint1 [ref] = " << *first1);
+ ARM_COMPUTE_TEST_INFO("keypoint2 [tgt] = " << *first2);
+ }
+ ++first1;
+ }
+ ++first2;
+ }
+ }
+
+ if(rest_missing)
+ {
+ while(first1 != last1)
+ {
+ ++num_missing;
+ ARM_COMPUTE_TEST_INFO("Key point not found");
+ ARM_COMPUTE_TEST_INFO("keypoint1 = " << *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, float allowed_missing_percentage, float allowed_mismatch_percentage)
+{
+ const int64_t num_elements_target = std::distance(target_first, target_last);
+ const int64_t num_elements_reference = std::distance(reference_first, reference_last);
+
+ 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 << "%) in ref are missing from target");
+ ARM_COMPUTE_TEST_INFO("Missing (not in tgt): " << num_missing << "/" << num_elements_reference << " = " << std::fixed << std::setprecision(2) << percent_missing
+ << "% \tMax allowed: " << allowed_missing_percentage << "%");
+ ARM_COMPUTE_EXPECT(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
+
+ ARM_COMPUTE_TEST_INFO(num_mismatches << " keypoints (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
+ ARM_COMPUTE_TEST_INFO("Mismatched keypoints: " << num_mismatches << "/" << num_elements_reference << " = " << std::fixed << std::setprecision(2) << percent_mismatches
+ << "% \tMax allowed: " << allowed_mismatch_percentage << "%");
+ ARM_COMPUTE_EXPECT(percent_mismatches <= allowed_mismatch_percentage, framework::LogLevel::ERRORS);
+ }
+
+ if(num_elements_target > 0)
+ {
+ // Note: no need to check for mismatches a second time (last argument is 'false')
+ std::tie(num_missing, num_mismatches) = compare_keypoints(target_first, target_last, reference_first, reference_last, tolerance, false);
+
+ 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 << "%) in target are missing from ref");
+ ARM_COMPUTE_TEST_INFO("Missing (not in ref): " << num_missing << "/" << num_elements_target << " = " << std::fixed << std::setprecision(2) << percent_missing
+ << "% \tMax allowed: " << allowed_missing_percentage << "%");
+ ARM_COMPUTE_EXPECT(percent_missing <= allowed_missing_percentage, framework::LogLevel::ERRORS);
+ }
+}
+
} // namespace validation
} // namespace test
} // namespace arm_compute
diff --git a/tests/validation/fixtures/FastCornersFixture.h b/tests/validation/fixtures/FastCornersFixture.h
index 0b827f74b5..6f2add6210 100644
--- a/tests/validation/fixtures/FastCornersFixture.h
+++ b/tests/validation/fixtures/FastCornersFixture.h
@@ -49,7 +49,7 @@ class FastCornersValidationFixture : public framework::Fixture
{
public:
template <typename...>
- void setup(TensorShape shape, Format format, bool suppress_nonmax, BorderMode border_mode)
+ void setup(std::string image, Format format, bool suppress_nonmax, BorderMode border_mode)
{
std::mt19937 gen(library->seed());
std::uniform_int_distribution<uint8_t> int_dist(0, 255);
@@ -58,15 +58,15 @@ public:
const uint8_t constant_border_value = int_dist(gen);
const float threshold = real_dist(gen);
- _target = compute_target(shape, format, threshold, suppress_nonmax, border_mode, constant_border_value);
- _reference = compute_reference(shape, format, threshold, suppress_nonmax, border_mode, constant_border_value);
+ _target = compute_target(image, format, threshold, suppress_nonmax, border_mode, constant_border_value);
+ _reference = compute_reference(image, format, threshold, suppress_nonmax, border_mode, constant_border_value);
}
protected:
template <typename U>
- void fill(U &&tensor)
+ void fill(U &&tensor, RawTensor raw)
{
- library->fill_tensor_uniform(tensor, 0);
+ library->fill(tensor, raw);
}
template <typename F, typename std::enable_if<std::is_same<F, CLFastCorners>::value, int>::type = 0>
@@ -79,19 +79,20 @@ protected:
void configure_target(F &func, TensorType &src, ArrayType &corners, unsigned int *num_corners, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value)
{
ARM_COMPUTE_UNUSED(num_corners);
- // ARM_COMPUTE_ERROR_ON(num_corners);
func.configure(&src, threshold, suppress_nonmax, &corners, border_mode, constant_border_value);
}
- ArrayType compute_target(const TensorShape &shape, Format format, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value)
+ ArrayType compute_target(const std::string &image, Format format, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value)
{
+ // Load the image (cached by the library if loaded before)
+ const RawTensor &raw = library->get(image, format);
+
// Create tensors
- TensorType src = create_tensor<TensorType>(shape, data_type_from_format(format));
- src.info()->set_format(format);
+ TensorType src = create_tensor<TensorType>(raw.shape(), format);
// Create array of keypoints
- ArrayType corners(shape.total_size());
- unsigned int num_corners = shape.total_size();
+ ArrayType corners(raw.shape().total_size());
+ unsigned int num_corners = raw.shape().total_size();
// Create and configure function
FunctionType fast_corners;
@@ -105,7 +106,7 @@ protected:
ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS);
// Fill tensors
- fill(AccessorType(src));
+ fill(AccessorType(src), raw);
// Compute function
fast_corners.run();
@@ -113,13 +114,16 @@ protected:
return corners;
}
- std::vector<KeyPoint> compute_reference(const TensorShape &shape, Format format, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value)
+ std::vector<KeyPoint> compute_reference(const std::string &image, Format format, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value)
{
+ // Load the image (cached by the library if loaded before)
+ const RawTensor &raw = library->get(image, format);
+
// Create reference
- SimpleTensor<T> src{ shape, format };
+ SimpleTensor<T> src{ raw.shape(), format };
// Fill reference
- fill(src);
+ fill(src, raw);
// Compute reference
return reference::fast_corners<T>(src, threshold, suppress_nonmax, border_mode, constant_border_value);
diff --git a/tests/validation/reference/FastCorners.cpp b/tests/validation/reference/FastCorners.cpp
index ae70e365c3..bcea3f10b1 100644
--- a/tests/validation/reference/FastCorners.cpp
+++ b/tests/validation/reference/FastCorners.cpp
@@ -79,9 +79,7 @@ const std::array<std::array<int, 2>, 16> circle_offsets =
/*
FAST-9 bit masks for consecutive points surrounding a corner candidate
-
- // Speed-up rejection of non-corners by checking pixels 1, 9, then 5, 13...
- const std::array<unsigned int, 16> fast9_order = { { 0, 8, 4, 12, 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15 } };
+ Rejection of non-corners is expedited by checking pixels 1, 9, then 5, 13...
*/
const std::array<uint16_t, 16> fast9_masks =
{
@@ -156,8 +154,8 @@ std::vector<KeyPoint> fast_corners(const SimpleTensor<T> &src, float input_thres
// Get intensity of pixel at given index on the Bresenham circle around a candidate point
const auto intensity_at = [&](const Coordinates & point, const unsigned int idx)
{
- const auto offs = circle_offsets[idx];
- Coordinates px{ point.x() + offs[0], point.y() + offs[1] };
+ const auto offset = circle_offsets[idx];
+ Coordinates px{ point.x() + offset[0], point.y() + offset[1] };
return tensor_elem_at(src, px, border_mode, constant_border_value);
};
@@ -166,13 +164,13 @@ std::vector<KeyPoint> fast_corners(const SimpleTensor<T> &src, float input_thres
// 1. Detect potential corners (the segment test)
std::vector<Coordinates> corner_candidates;
- SimpleTensor<float> scores(src.shape(), DataType::F32);
+ SimpleTensor<uint8_t> scores(src.shape(), DataType::U8);
ValidRegion valid_region = shape_to_valid_region(src.shape(), BorderMode::UNDEFINED == border_mode, BorderSize(bresenham_radius));
for(int i = 0; i < src.num_elements(); ++i)
{
Coordinates candidate = index2coord(src.shape(), i);
- scores[i] = 0.f;
+ scores[i] = 0;
if(!is_in_valid_region(valid_region, candidate))
{
continue;
@@ -181,30 +179,21 @@ std::vector<KeyPoint> fast_corners(const SimpleTensor<T> &src, float input_thres
if(is_a_corner(candidate, src, threshold, border_mode, constant_border_value, intensity_at))
{
corner_candidates.emplace_back(candidate);
- scores[i] = 1.f;
+ scores[i] = 1;
}
}
- // 2. Calculate corner scores if non-maxima suppression
- // The corner response Cp function is defined as the largest threshold t for which the pixel p remains a corner
+ // 2. Calculate corner scores if necessary
if(suppress_nonmax)
{
for(const auto &candidate : corner_candidates)
{
- const auto index = coord2index(scores.shape(), candidate);
-
-#ifdef CALC_CORNER_RESPONSE_BY_ITERATION
- auto response = threshold;
- while(is_a_corner(candidate, src, response, border_mode, constant_border_value, intensity_at))
- {
- response += 1;
- }
- scores[index] = response - 1;
-#else // CALC_CORNER_RESPONSE_BY_ITERATION
- uint8_t thresh_max = UINT8_MAX;
- uint8_t thresh_min = threshold;
- uint8_t response = (thresh_min + thresh_max) / 2;
+ const auto index = coord2index(scores.shape(), candidate);
+ uint8_t thresh_max = UINT8_MAX;
+ uint8_t thresh_min = threshold;
+ uint8_t response = (thresh_min + thresh_max) / 2;
+ // Corner score (response) is the largest threshold for which the pixel remains a corner
while(thresh_max - thresh_min > 1)
{
response = (thresh_min + thresh_max) / 2;
@@ -218,10 +207,9 @@ std::vector<KeyPoint> fast_corners(const SimpleTensor<T> &src, float input_thres
}
}
scores[index] = thresh_min;
-#endif // CALC_CORNER_RESPONSE_BY_ITERATION
}
- scores = non_maxima_suppression(scores, border_mode, static_cast<float>(constant_border_value));
+ scores = non_maxima_suppression(scores, border_mode, constant_border_value);
valid_region = shape_to_valid_region(scores.shape(), BorderMode::UNDEFINED == border_mode, BorderSize(bresenham_radius + 1));
}