From 562fe0fcedace39735ead089cfdc4e2b07e40aad Mon Sep 17 00:00:00 2001 From: Abe Mbise Date: Fri, 9 Feb 2018 14:13:02 +0000 Subject: 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 Reviewed-by: Georgios Pinitas Tested-by: Jenkins --- tests/AssetsLibrary.cpp | 40 ++++-- tests/validation/CL/FastCorners.cpp | 15 +- tests/validation/CL/HarrisCorners.cpp | 2 +- tests/validation/FastValidation.h | 161 --------------------- tests/validation/NEON/FastCorners.cpp | 15 +- tests/validation/NEON/HarrisCorners.cpp | 6 +- tests/validation/Validation.h | 189 +++++++++++++++---------- tests/validation/fixtures/FastCornersFixture.h | 34 +++-- tests/validation/reference/FastCorners.cpp | 38 ++--- 9 files changed, 197 insertions(+), 303 deletions(-) delete mode 100644 tests/validation/FastValidation.h 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 ::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(dst.data())[j] = 0.2126f * src.data()[i + 0] + 0.7152f * src.data()[i + 1] + 0.0722f * src.data()[i + 2]; + reinterpret_cast(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, 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 tolerance(0.5f); } // namespace @@ -95,21 +92,21 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combi template using CLFastCornersFixture = FastCornersValidationFixture; -FIXTURE_DATA_TEST_CASE(RunSmall, CLFastCornersFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small2DShapes(), framework::dataset::make("Format", Format::U8)), +FIXTURE_DATA_TEST_CASE(RunSmall, CLFastCornersFixture, 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 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, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::Large2DShapes(), framework::dataset::make("Format", Format::U8)), +FIXTURE_DATA_TEST_CASE(RunLarge, CLFastCornersFixture, 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 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 -std::pair 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(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 -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(num_missing) / num_elements_reference * 100.f; - const float percent_mismatches = static_cast(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(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 tolerance(0.5f); } // namespace @@ -94,21 +91,21 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combi template using NEFastCornersFixture = FastCornersValidationFixture; -FIXTURE_DATA_TEST_CASE(RunSmall, NEFastCornersFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small2DShapes(), framework::dataset::make("Format", Format::U8)), +FIXTURE_DATA_TEST_CASE(RunSmall, NEFastCornersFixture, 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 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, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::Large2DShapes(), framework::dataset::make("Format", Format::U8)), +FIXTURE_DATA_TEST_CASE(RunLarge, NEFastCornersFixture, 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 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 &reference, co } } -/** Check which keypoints from [first1, last1) are missing in [first2, last2) */ -template -std::pair 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 -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(num_missing) / num_elements_reference * 100.f; - const float percent_mismatches = static_cast(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(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 void validate(const IAccessor &tensor, const SimpleTensor &reference, const SimpleTensor &valid_mask, U tolerance_value, float tolerance_number) { @@ -652,6 +582,123 @@ void validate_min_max_loc(const MinMaxLocationValues &target, const MinMaxLoc } } +/** Check which keypoints from [first1, last1) are missing in [first2, last2) */ +template +std::pair 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(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 +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(num_missing) / num_elements_reference * 100.f; + const float percent_mismatches = static_cast(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(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 - 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 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 - void fill(U &&tensor) + void fill(U &&tensor, RawTensor raw) { - library->fill_tensor_uniform(tensor, 0); + library->fill(tensor, raw); } template ::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(shape, data_type_from_format(format)); - src.info()->set_format(format); + TensorType src = create_tensor(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 compute_reference(const TensorShape &shape, Format format, float threshold, bool suppress_nonmax, BorderMode border_mode, uint8_t constant_border_value) + std::vector 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 src{ shape, format }; + SimpleTensor src{ raw.shape(), format }; // Fill reference - fill(src); + fill(src, raw); // Compute reference return reference::fast_corners(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, 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 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 fast9_masks = { @@ -156,8 +154,8 @@ std::vector fast_corners(const SimpleTensor &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 fast_corners(const SimpleTensor &src, float input_thres // 1. Detect potential corners (the segment test) std::vector corner_candidates; - SimpleTensor scores(src.shape(), DataType::F32); + SimpleTensor 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 fast_corners(const SimpleTensor &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 fast_corners(const SimpleTensor &src, float input_thres } } scores[index] = thresh_min; -#endif // CALC_CORNER_RESPONSE_BY_ITERATION } - scores = non_maxima_suppression(scores, border_mode, static_cast(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)); } -- cgit v1.2.1