From a0a0e29f635de08092c2325f8f049ffb286aabaf Mon Sep 17 00:00:00 2001 From: Pablo Tello Date: Fri, 21 Dec 2018 16:47:23 +0000 Subject: COMPMID-1766: Implemented CPP Non Max Suppression Change-Id: I1dcd5fb3d9ad6c6c750415bf8074698b800dfbc1 Reviewed-on: https://review.mlplatform.org/494 Tested-by: Arm Jenkins Reviewed-by: Giuseppe Rossini Reviewed-by: Georgios Pinitas --- tests/AssetsLibrary.h | 37 ++++++ tests/datasets/ShapeDatasets.h | 31 +++++ tests/validation/CPP/NonMaximalSuppression.cpp | 144 +++++++++++++++++++++ .../validation/fixtures/NonMaxSuppressionFixture.h | 124 ++++++++++++++++++ tests/validation/reference/NonMaxSuppression.cpp | 126 ++++++++++++++++++ tests/validation/reference/NonMaxSuppression.h | 44 +++++++ 6 files changed, 506 insertions(+) create mode 100644 tests/validation/CPP/NonMaximalSuppression.cpp create mode 100644 tests/validation/fixtures/NonMaxSuppressionFixture.h create mode 100644 tests/validation/reference/NonMaxSuppression.cpp create mode 100644 tests/validation/reference/NonMaxSuppression.h (limited to 'tests') diff --git a/tests/AssetsLibrary.h b/tests/AssetsLibrary.h index d09e22762d..366c1450ba 100644 --- a/tests/AssetsLibrary.h +++ b/tests/AssetsLibrary.h @@ -207,6 +207,9 @@ public: template void fill(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const; + template + void fill_boxes(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const; + /** Fills the specified @p raw tensor with random values drawn from @p * distribution. * @@ -481,6 +484,40 @@ void AssetsLibrary::fill_borders_with_garbage(T &&tensor, D &&distribution, std: }); } +template +void AssetsLibrary::fill_boxes(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const +{ + using ResultType = typename std::remove_reference::type::result_type; + std::mt19937 gen(_seed + seed_offset); + TensorShape shape(tensor.shape()); + const int num_boxes = tensor.num_elements() / 4; + // Iterate over all elements + std::uniform_real_distribution<> size_dist(0.f, 1.f); + for(int element_idx = 0; element_idx < num_boxes * 4; element_idx += 4) + { + const ResultType delta = size_dist(gen); + const ResultType epsilon = size_dist(gen); + const ResultType left = distribution(gen); + const ResultType top = distribution(gen); + const ResultType right = left + delta; + const ResultType bottom = top + epsilon; + const std::tuple box(left, top, right, bottom); + Coordinates x1 = index2coord(shape, element_idx); + Coordinates y1 = index2coord(shape, element_idx + 1); + Coordinates x2 = index2coord(shape, element_idx + 2); + Coordinates y2 = index2coord(shape, element_idx + 3); + ResultType &target_value_x1 = reinterpret_cast(tensor(x1))[0]; + ResultType &target_value_y1 = reinterpret_cast(tensor(y1))[0]; + ResultType &target_value_x2 = reinterpret_cast(tensor(x2))[0]; + ResultType &target_value_y2 = reinterpret_cast(tensor(y2))[0]; + store_value_with_data_type(&target_value_x1, std::get<0>(box), tensor.data_type()); + store_value_with_data_type(&target_value_y1, std::get<1>(box), tensor.data_type()); + store_value_with_data_type(&target_value_x2, std::get<2>(box), tensor.data_type()); + store_value_with_data_type(&target_value_y2, std::get<3>(box), tensor.data_type()); + } + fill_borders_with_garbage(tensor, distribution, seed_offset); +} + template void AssetsLibrary::fill(T &&tensor, D &&distribution, std::random_device::result_type seed_offset) const { diff --git a/tests/datasets/ShapeDatasets.h b/tests/datasets/ShapeDatasets.h index d7ffc127ef..bd29fe649a 100644 --- a/tests/datasets/ShapeDatasets.h +++ b/tests/datasets/ShapeDatasets.h @@ -949,6 +949,37 @@ public: { } }; + +/** Data set containing small 2D tensor shapes. */ +class Small2DNonMaxSuppressionShapes final : public ShapeDataset +{ +public: + Small2DNonMaxSuppressionShapes() + : ShapeDataset("Shape", + { + TensorShape{ 4U, 7U }, + TensorShape{ 4U, 13U }, + TensorShape{ 4U, 64U } + }) + { + } +}; + +/** Data set containing large 2D tensor shapes. */ +class Large2DNonMaxSuppressionShapes final : public ShapeDataset +{ +public: + Large2DNonMaxSuppressionShapes() + : ShapeDataset("Shape", + { + TensorShape{ 4U, 207U }, + TensorShape{ 4U, 113U }, + TensorShape{ 4U, 264U } + }) + { + } +}; + } // namespace datasets } // namespace test } // namespace arm_compute diff --git a/tests/validation/CPP/NonMaximalSuppression.cpp b/tests/validation/CPP/NonMaximalSuppression.cpp new file mode 100644 index 0000000000..6cd7b52066 --- /dev/null +++ b/tests/validation/CPP/NonMaximalSuppression.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019 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/CPP/functions/CPPDetectionOutputLayer.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" +#include "tests/NEON/Accessor.h" +#include "tests/PaddingCalculator.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/NonMaxSuppressionFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +const auto max_output_boxes_dataset = framework::dataset::make("MaxOutputBoxes", 1, 10); +const auto score_threshold_dataset = framework::dataset::make("ScoreThreshold", { 0.1f, 0.5f, 0.f, 1.f }); +const auto nms_threshold_dataset = framework::dataset::make("NMSThreshold", { 0.1f, 0.5f, 0.f, 1.f }); +const auto NMSParametersSmall = datasets::Small2DNonMaxSuppressionShapes() * max_output_boxes_dataset * score_threshold_dataset * nms_threshold_dataset; +const auto NMSParametersBig = datasets::Large2DNonMaxSuppressionShapes() * max_output_boxes_dataset * score_threshold_dataset * nms_threshold_dataset; + +} // namespace + +TEST_SUITE(CPP) +TEST_SUITE(NMS) + +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip(zip( + framework::dataset::make("BoundingBox",{ + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + TensorInfo(TensorShape(1U, 4U, 2U), 1, DataType::F32), // invalid shape + TensorInfo(TensorShape(4U, 2U), 1, DataType::S32), // invalid data type + TensorInfo(TensorShape(4U, 3U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 66U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + TensorInfo(TensorShape(4U, 100U), 1, DataType::F32), + }), + framework::dataset::make("Scores", { + TensorInfo(TensorShape(100U), 1, DataType::F32), + TensorInfo(TensorShape(37U, 2U, 13U, 27U), 1, DataType::F32), // invalid shape + TensorInfo(TensorShape(4U), 1, DataType::F32), + TensorInfo(TensorShape(3U), 1, DataType::U8), // invalid data type + TensorInfo(TensorShape(66U), 1, DataType::F32), // invalid data type + TensorInfo(TensorShape(100U), 1, DataType::F32), + TensorInfo(TensorShape(100U), 1, DataType::F32), + TensorInfo(TensorShape(100U), 1, DataType::F32), + TensorInfo(TensorShape(100U), 1, DataType::F32), + TensorInfo(TensorShape(100U), 1, DataType::F32), + })), + framework::dataset::make("Indices", { + TensorInfo(TensorShape(100U), 1, DataType::S32), + TensorInfo(TensorShape(100U), 1, DataType::S32), + TensorInfo(TensorShape(4U), 1, DataType::S32), + TensorInfo(TensorShape(3U), 1, DataType::S32), + TensorInfo(TensorShape(200U), 1, DataType::S32), // indices bigger than max bbs, OK because max_output is 66 + TensorInfo(TensorShape(100U), 1, DataType::F32), // invalid data type + TensorInfo(TensorShape(100U), 1, DataType::S32), + TensorInfo(TensorShape(100U), 1, DataType::S32), + TensorInfo(TensorShape(100U), 1, DataType::S32), + TensorInfo(TensorShape(100U), 1, DataType::S32), + + })), + framework::dataset::make("max_output", { + 10U, 2U,4U, 3U,66U, 1U, + 0U, /* invalid, must be greater than 0 */ + 10000U, /* OK, clamped to indices' size */ + 100U, + 10U, + })), + framework::dataset::make("score_threshold", { + 0.1f, 0.4f, 0.2f,0.8f,0.3f, 0.01f, 0.5f, 0.45f, + -1.f, /* invalid value, must be in [0,1] */ + 0.5f, + })), + framework::dataset::make("nms_threshold", { + 0.3f, 0.7f, 0.1f,0.13f,0.2f, 0.97f, 0.76f, 0.87f, 0.1f, + 10.f, /* invalid value, must be in [0,1]*/ + })), + framework::dataset::make("Expected", { + true, false, false, false, true, false, false,true, false, false + })), + + bbox_info, scores_info, indices_info, max_out, score_threshold, nms_threshold, expected) +{ + ARM_COMPUTE_EXPECT(bool(CPPNonMaximumSuppression::validate(&bbox_info.clone()->set_is_resizable(false), + &scores_info.clone()->set_is_resizable(false), + &indices_info.clone()->set_is_resizable(false), + max_out,score_threshold,nms_threshold)) == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + +using CPPNonMaxSuppressionFixture = NMSValidationFixture; + +FIXTURE_DATA_TEST_CASE(RunSmall, CPPNonMaxSuppressionFixture, framework::DatasetMode::PRECOMMIT, NMSParametersSmall) +{ + // Validate output + validate(Accessor(_target), _reference); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CPPNonMaxSuppressionFixture, framework::DatasetMode::NIGHTLY, NMSParametersBig) +{ + // Validate output + validate(Accessor(_target), _reference); +} + +TEST_SUITE_END() // CPP +TEST_SUITE_END() // NMS +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/fixtures/NonMaxSuppressionFixture.h b/tests/validation/fixtures/NonMaxSuppressionFixture.h new file mode 100644 index 0000000000..9299ed62a4 --- /dev/null +++ b/tests/validation/fixtures/NonMaxSuppressionFixture.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019 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_MAX_SUPPRESSION_FIXTURE +#define ARM_COMPUTE_TEST_NON_MAX_SUPPRESSION_FIXTURE + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "tests/AssetsLibrary.h" +#include "tests/Globals.h" +#include "tests/IAccessor.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Fixture.h" +#include "tests/validation/reference/NonMaxSuppression.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +template + +class NMSValidationFixture : public framework::Fixture +{ +public: + template + void setup(TensorShape input_shape, unsigned int max_output_size, float score_threshold, float nms_threshold) + { + ARM_COMPUTE_ERROR_ON(max_output_size == 0); + ARM_COMPUTE_ERROR_ON(input_shape.num_dimensions() != 2); + const TensorShape output_shape(max_output_size); + const TensorShape scores_shape(input_shape[1]); + _target = compute_target(input_shape, scores_shape, output_shape, max_output_size, score_threshold, nms_threshold); + _reference = compute_reference(input_shape, scores_shape, output_shape, max_output_size, score_threshold, nms_threshold); + } + +protected: + template + void fill(U &&tensor, int i, int lo, int hi) + { + std::uniform_real_distribution<> distribution(lo, hi); + library->fill_boxes(tensor, distribution, i); + } + + TensorType compute_target(const TensorShape input_shape, const TensorShape scores_shape, const TensorShape output_shape, + unsigned int max_output_size, float score_threshold, float nms_threshold) + { + // Create tensors + TensorType bboxes = create_tensor(input_shape, DataType::F32); + TensorType scores = create_tensor(scores_shape, DataType::F32); + TensorType indices = create_tensor(output_shape, DataType::S32); + + // Create and configure function + FunctionType nms_func; + nms_func.configure(&bboxes, &scores, &indices, max_output_size, score_threshold, nms_threshold); + + ARM_COMPUTE_EXPECT(bboxes.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(indices.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(scores.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Allocate tensors + bboxes.allocator()->allocate(); + indices.allocator()->allocate(); + scores.allocator()->allocate(); + + ARM_COMPUTE_EXPECT(!bboxes.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!indices.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!scores.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Fill tensors + fill(AccessorType(bboxes), 0, 0.f, 1.f); + fill(AccessorType(scores), 1, 0.f, 1.f); + + // Compute function + nms_func.run(); + return indices; + } + + SimpleTensor compute_reference(const TensorShape input_shape, const TensorShape scores_shape, const TensorShape output_shape, + unsigned int max_output_size, float score_threshold, float nms_threshold) + { + // Create reference + SimpleTensor bboxes{ input_shape, DataType::F32 }; + SimpleTensor scores{ scores_shape, DataType::F32 }; + SimpleTensor indices{ output_shape, DataType::S32 }; + + // Fill reference + fill(bboxes, 0, 0.f, 1.f); + fill(scores, 1, 0.f, 1.f); + + return reference::non_max_suppression(bboxes, scores, indices, max_output_size, score_threshold, nms_threshold); + } + + TensorType _target{}; + SimpleTensor _reference{}; +}; + +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_NON_MAX_SUPPRESSION_FIXTURE */ diff --git a/tests/validation/reference/NonMaxSuppression.cpp b/tests/validation/reference/NonMaxSuppression.cpp new file mode 100644 index 0000000000..013a26f645 --- /dev/null +++ b/tests/validation/reference/NonMaxSuppression.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 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 "Permute.h" + +#include "arm_compute/core/Types.h" +#include "tests/validation/Helpers.h" +#include + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +namespace +{ +inline float get_elem_by_coordinate(const SimpleTensor &tensor, Coordinates coord) +{ + return *static_cast(tensor(coord)); +} + +// Return intersection-over-union overlap between boxes i and j +inline bool iou_greater_than_threshold(const SimpleTensor &boxes, size_t i, size_t j, float iou_threshold) +{ + const float ymin_i = std::min(get_elem_by_coordinate(boxes, Coordinates(0, i)), get_elem_by_coordinate(boxes, Coordinates(2, i))); + const float xmin_i = std::min(get_elem_by_coordinate(boxes, Coordinates(1, i)), get_elem_by_coordinate(boxes, Coordinates(3, i))); + const float ymax_i = std::max(get_elem_by_coordinate(boxes, Coordinates(0, i)), get_elem_by_coordinate(boxes, Coordinates(2, i))); + const float xmax_i = std::max(get_elem_by_coordinate(boxes, Coordinates(1, i)), get_elem_by_coordinate(boxes, Coordinates(3, i))); + const float ymin_j = std::min(get_elem_by_coordinate(boxes, Coordinates(0, j)), get_elem_by_coordinate(boxes, Coordinates(2, j))); + const float xmin_j = std::min(get_elem_by_coordinate(boxes, Coordinates(1, j)), get_elem_by_coordinate(boxes, Coordinates(3, j))); + const float ymax_j = std::max(get_elem_by_coordinate(boxes, Coordinates(0, j)), get_elem_by_coordinate(boxes, Coordinates(2, j))); + const float xmax_j = std::max(get_elem_by_coordinate(boxes, Coordinates(1, j)), get_elem_by_coordinate(boxes, Coordinates(3, j))); + const float area_i = (ymax_i - ymin_i) * (xmax_i - xmin_i); + const float area_j = (ymax_j - ymin_j) * (xmax_j - xmin_j); + if(area_i <= 0 || area_j <= 0) + { + return false; + } + const float intersection_ymin = std::max(ymin_i, ymin_j); + const float intersection_xmin = std::max(xmin_i, xmin_j); + const float intersection_ymax = std::min(ymax_i, ymax_j); + const float intersection_xmax = std::min(xmax_i, xmax_j); + const float intersection_area = std::max(intersection_ymax - intersection_ymin, 0.0) * std::max(intersection_xmax - intersection_xmin, 0.0); + const float iou = intersection_area / (area_i + area_j - intersection_area); + return iou > iou_threshold; +} + +} // namespace + +SimpleTensor non_max_suppression(const SimpleTensor &bboxes, const SimpleTensor &scores, SimpleTensor &indices, + unsigned int max_output_size, float score_threshold, float nms_threshold) +{ + const size_t num_boxes = bboxes.shape().y(); + const size_t output_size = std::min(static_cast(max_output_size), num_boxes); + std::vector scores_data(num_boxes); + std::copy_n(scores.data(), num_boxes, scores_data.begin()); + + using CandidateBox = std::pair; + auto cmp = [](const CandidateBox bb0, const CandidateBox bb1) + { + return bb0.second < bb1.second; + }; + + std::priority_queue, decltype(cmp)> candidate_priority_queue(cmp); + for(size_t i = 0; i < scores_data.size(); ++i) + { + if(scores_data[i] > score_threshold) + { + candidate_priority_queue.emplace(CandidateBox({ i, scores_data[i] })); + } + } + + std::vector selected; + std::vector selected_scores; + CandidateBox next_candidate; + + while(selected.size() < output_size && !candidate_priority_queue.empty()) + { + next_candidate = candidate_priority_queue.top(); + candidate_priority_queue.pop(); + bool should_select = true; + for(int j = selected.size() - 1; j >= 0; --j) + { + if(iou_greater_than_threshold(bboxes, next_candidate.first, selected[j], nms_threshold)) + { + should_select = false; + break; + } + } + if(should_select) + { + selected.push_back(next_candidate.first); + selected_scores.push_back(next_candidate.second); + } + } + std::copy_n(selected.begin(), selected.size(), indices.data()); + return indices; +} + +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/reference/NonMaxSuppression.h b/tests/validation/reference/NonMaxSuppression.h new file mode 100644 index 0000000000..0418412939 --- /dev/null +++ b/tests/validation/reference/NonMaxSuppression.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 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_MAX_SUPPRESION_H__ +#define __ARM_COMPUTE_TEST_NON_MAX_SUPPRESION_H__ + +#include "tests/SimpleTensor.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +SimpleTensor non_max_suppression(const SimpleTensor &bboxes, const SimpleTensor &scores, SimpleTensor &indices, + unsigned int max_output_size, float score_threshold, float nms_threshold); + +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* __ARM_COMPUTE_TEST_NON_MAX_SUPPRESION_H__ */ -- cgit v1.2.1