From 25f23680b211b6dd27c006cb9575e816e8f80bb5 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Mon, 27 Nov 2017 14:35:09 +0000 Subject: COMPMID-589: Port HOGDescriptor to new validation Change-Id: I2021612e61de1b82aaeb49249d06929c7fceb15f Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/115216 Reviewed-by: Pablo Tello Tested-by: Jenkins --- tests/SimpleTensor.h | 9 +- tests/Utils.h | 28 ++- tests/datasets/HOGDescriptorDataset.h | 137 ++++++++++++ tests/validation/CL/HOGDescriptor.cpp | 83 +++++++ tests/validation/NEON/HOGDescriptor.cpp | 84 +++++++ tests/validation/fixtures/HOGDescriptorFixture.h | 142 ++++++++++++ tests/validation/reference/HOGDescriptor.cpp | 273 +++++++++++++++++++++++ tests/validation/reference/HOGDescriptor.h | 43 ++++ 8 files changed, 794 insertions(+), 5 deletions(-) create mode 100644 tests/datasets/HOGDescriptorDataset.h create mode 100644 tests/validation/CL/HOGDescriptor.cpp create mode 100644 tests/validation/NEON/HOGDescriptor.cpp create mode 100644 tests/validation/fixtures/HOGDescriptorFixture.h create mode 100644 tests/validation/reference/HOGDescriptor.cpp create mode 100644 tests/validation/reference/HOGDescriptor.h (limited to 'tests') diff --git a/tests/SimpleTensor.h b/tests/SimpleTensor.h index 6091991e66..902f5b51b5 100644 --- a/tests/SimpleTensor.h +++ b/tests/SimpleTensor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017, 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -191,7 +191,8 @@ SimpleTensor::SimpleTensor(TensorShape shape, Format format, int fixed_point_ _fixed_point_position(fixed_point_position), _quantization_info() { - _buffer = support::cpp14::make_unique(num_elements() * num_channels()); + _num_channels = num_channels(); + _buffer = support::cpp14::make_unique(num_elements() * _num_channels); } template @@ -338,13 +339,13 @@ T *SimpleTensor::data() template const void *SimpleTensor::operator()(const Coordinates &coord) const { - return _buffer.get() + coord2index(_shape, coord); + return _buffer.get() + coord2index(_shape, coord) * _num_channels; } template void *SimpleTensor::operator()(const Coordinates &coord) { - return _buffer.get() + coord2index(_shape, coord); + return _buffer.get() + coord2index(_shape, coord) * _num_channels; } template diff --git a/tests/Utils.h b/tests/Utils.h index d5c0a36ba2..5814965a40 100644 --- a/tests/Utils.h +++ b/tests/Utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017, 2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -27,6 +27,8 @@ #include "arm_compute/core/Coordinates.h" #include "arm_compute/core/Error.h" #include "arm_compute/core/FixedPoint.h" +#include "arm_compute/core/HOGInfo.h" +#include "arm_compute/core/Size2D.h" #include "arm_compute/core/TensorInfo.h" #include "arm_compute/core/TensorShape.h" #include "arm_compute/core/Types.h" @@ -494,6 +496,30 @@ inline T create_tensor(const TensorShape &shape, Format format) return tensor; } +/** Create and initialize a HOG (Histogram of Oriented Gradients) of the given type. + * + * @param[in] cell_size Cell size in pixels + * @param[in] block_size Block size in pixels. Must be a multiple of cell_size. + * @param[in] detection_window_size Detection window size in pixels. Must be a multiple of block_size and block_stride. + * @param[in] block_stride Distance in pixels between 2 consecutive blocks along the x and y direction. Must be a multiple of cell size + * @param[in] num_bins Number of histogram bins for each cell + * @param[in] normalization_type (Optional) Normalization type to use for each block + * @param[in] l2_hyst_threshold (Optional) Threshold used for L2HYS_NORM normalization method + * @param[in] phase_type (Optional) Type of @ref PhaseType + * + * @return Initialized HOG of given type. + */ +template +inline T create_HOG(const Size2D &cell_size, const Size2D &block_size, const Size2D &detection_window_size, const Size2D &block_stride, size_t num_bins, + HOGNormType normalization_type = HOGNormType::L2HYS_NORM, float l2_hyst_threshold = 0.2f, PhaseType phase_type = PhaseType::UNSIGNED) +{ + T hog; + HOGInfo hog_info(cell_size, block_size, block_size, block_stride, num_bins, normalization_type, l2_hyst_threshold, phase_type); + hog.init(hog_info); + + return hog; +} + /** Create a vector of random ROIs. * * @param[in] shape The shape of the input tensor. diff --git a/tests/datasets/HOGDescriptorDataset.h b/tests/datasets/HOGDescriptorDataset.h new file mode 100644 index 0000000000..73c64946ec --- /dev/null +++ b/tests/datasets/HOGDescriptorDataset.h @@ -0,0 +1,137 @@ +/* + * 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_HOG_DESCRIPTOR_DATASET +#define ARM_COMPUTE_TEST_HOG_DESCRIPTOR_DATASET + +#include "utils/TypePrinter.h" + +namespace arm_compute +{ +namespace test +{ +namespace datasets +{ +class HOGDescriptorDataset +{ +public: + using type = std::tuple; + + struct iterator + { + iterator(std::vector::const_iterator image_it, + std::vector::const_iterator hog_info_it) + : _image_it{ std::move(image_it) }, + _hog_info_it{ std::move(hog_info_it) } + { + } + + std::string description() const + { + std::stringstream description; + description << "Image=" << *_image_it << ":"; + description << "HOGInfo=" << *_hog_info_it; + + return description.str(); + } + + HOGDescriptorDataset::type operator*() const + { + return std::make_tuple(*_image_it, *_hog_info_it); + } + + iterator &operator++() + { + ++_image_it; + ++_hog_info_it; + + return *this; + } + + private: + std::vector::const_iterator _image_it; + std::vector::const_iterator _hog_info_it; + }; + + iterator begin() const + { + return iterator(_image.begin(), _hog_info.begin()); + } + + int size() const + { + return std::min(_image.size(), _hog_info.size()); + } + + void add_config(std::string image, + Size2D cell_size, Size2D block_size, Size2D detection_window_size, Size2D block_stride, + size_t num_bins, HOGNormType normalization_type, float l2_hyst_threshold, PhaseType phase_type) + { + _image.emplace_back(std::move(image)); + _hog_info.emplace_back(HOGInfo(cell_size, block_size, detection_window_size, block_stride, num_bins, normalization_type, l2_hyst_threshold, phase_type)); + } + +protected: + HOGDescriptorDataset() = default; + HOGDescriptorDataset(HOGDescriptorDataset &&) = default; + +private: + std::vector _image{}; + std::vector _hog_info{}; +}; + +// *INDENT-OFF* +// clang-format off +class SmallHOGDescriptorDataset final : public HOGDescriptorDataset +{ +public: + SmallHOGDescriptorDataset() + { + // image cell_size block_size detection_size block_stride bin normalization_type thresh phase_type + add_config("800x600.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2HYS_NORM, 0.2f, PhaseType::SIGNED); + add_config("800x600.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2HYS_NORM, 0.2f, PhaseType::UNSIGNED); + } +}; + +class LargeHOGDescriptorDataset final : public HOGDescriptorDataset +{ +public: + LargeHOGDescriptorDataset() + { + // image cell_size block_size detection_size block_stride bin normalization_type thresh phase_type + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2HYS_NORM, 0.2f, PhaseType::SIGNED); + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2_NORM, 0.2f, PhaseType::SIGNED); + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L1_NORM, 0.2f, PhaseType::SIGNED); + + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2HYS_NORM, 0.2f, PhaseType::UNSIGNED); + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L2_NORM, 0.2f, PhaseType::UNSIGNED); + add_config("1920x1080.ppm", Size2D(8U, 8U), Size2D(16U, 16U), Size2D(64U, 128U), Size2D(8U, 8U), 9U, HOGNormType::L1_NORM, 0.2f, PhaseType::UNSIGNED); + } +}; +// clang-format on +// *INDENT-ON* + +} // namespace datasets +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_HOG_DESCRIPTOR_DATASET */ diff --git a/tests/validation/CL/HOGDescriptor.cpp b/tests/validation/CL/HOGDescriptor.cpp new file mode 100644 index 0000000000..aef265a657 --- /dev/null +++ b/tests/validation/CL/HOGDescriptor.cpp @@ -0,0 +1,83 @@ +/* + * 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. + */ +#include "arm_compute/runtime/CL/CLHOG.h" +#include "arm_compute/runtime/CL/functions/CLHOGDescriptor.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" +#include "tests/CL/CLAccessor.h" +#include "tests/PaddingCalculator.h" +#include "tests/datasets/BorderModeDataset.h" +#include "tests/datasets/HOGDescriptorDataset.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/HOGDescriptorFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +AbsoluteTolerance tolerance(1e-2f); +} // namespace + +TEST_SUITE(CL) +TEST_SUITE(HOGDescriptor) + +using CLHOGDescriptorFixture = HOGDescriptorValidationFixture; + +// *INDENT-OFF* +// clang-format off +FIXTURE_DATA_TEST_CASE(RunSmall, CLHOGDescriptorFixture, framework::DatasetMode::PRECOMMIT, + combine(combine( + datasets::SmallHOGDescriptorDataset(), + framework::dataset::make("Format", Format::U8)), + framework::dataset::make("BorderMode", {BorderMode::CONSTANT, BorderMode::REPLICATE}))) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLHOGDescriptorFixture, framework::DatasetMode::NIGHTLY, + combine(combine( + datasets::LargeHOGDescriptorDataset(), + framework::dataset::make("Format", Format::U8)), + framework::dataset::make("BorderMode", {BorderMode::CONSTANT, BorderMode::REPLICATE}))) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance); +} +// clang-format on +// *INDENT-ON* + +TEST_SUITE_END() +TEST_SUITE_END() +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/NEON/HOGDescriptor.cpp b/tests/validation/NEON/HOGDescriptor.cpp new file mode 100644 index 0000000000..5f31773aad --- /dev/null +++ b/tests/validation/NEON/HOGDescriptor.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ +#include "arm_compute/runtime/HOG.h" +#include "arm_compute/runtime/NEON/functions/NEHOGDescriptor.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/BorderModeDataset.h" +#include "tests/datasets/HOGDescriptorDataset.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/HOGDescriptorFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +AbsoluteTolerance tolerance(0.5f); +constexpr float tolerance_number = 0.01f; +} // namespace + +TEST_SUITE(NEON) +TEST_SUITE(HOGDescriptor) + +// *INDENT-OFF* +// clang-format off +using NEHOGDescriptorFixture = HOGDescriptorValidationFixture; + +FIXTURE_DATA_TEST_CASE(RunSmall, NEHOGDescriptorFixture, framework::DatasetMode::PRECOMMIT, + combine(combine( + datasets::SmallHOGDescriptorDataset(), + framework::dataset::make("Format", Format::U8)), + framework::dataset::make("BorderMode", {BorderMode::CONSTANT, BorderMode::REPLICATE}))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance, tolerance_number); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, NEHOGDescriptorFixture, framework::DatasetMode::NIGHTLY, + combine(combine( + datasets::LargeHOGDescriptorDataset(), + framework::dataset::make("Format", Format::U8)), + framework::dataset::make("BorderMode", {BorderMode::CONSTANT, BorderMode::REPLICATE}))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance, tolerance_number); +} +// clang-format on +// *INDENT-ON* + +TEST_SUITE_END() +TEST_SUITE_END() +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/fixtures/HOGDescriptorFixture.h b/tests/validation/fixtures/HOGDescriptorFixture.h new file mode 100644 index 0000000000..cabee63ae3 --- /dev/null +++ b/tests/validation/fixtures/HOGDescriptorFixture.h @@ -0,0 +1,142 @@ +/* + * 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_HOG_DESCRIPTOR_FIXTURE +#define ARM_COMPUTE_TEST_HOG_DESCRIPTOR_FIXTURE + +#include "arm_compute/core/HOGInfo.h" +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/core/Types.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/HOGDescriptor.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +template +class HOGDescriptorValidationFixture : public framework::Fixture +{ +public: + template + void setup(std::string image, HOGInfo hog_info, Format format, BorderMode border_mode) + { + // Only defined borders supported + ARM_COMPUTE_ERROR_ON(border_mode == BorderMode::UNDEFINED); + + // Generate a random constant value + std::mt19937 gen(library->seed()); + std::uniform_int_distribution int_dist(0, 255); + const T constant_border_value = int_dist(gen); + + _target = compute_target(image, format, border_mode, constant_border_value, hog_info); + _reference = compute_reference(image, format, border_mode, constant_border_value, hog_info); + } + +protected: + template + void fill(V &&tensor, const std::string image, Format format) + { + library->fill(tensor, image, format); + } + + template + void fill(V &&tensor, int i, D max) + { + library->fill_tensor_uniform(tensor, i, static_cast(0), max); + } + + TensorType compute_target(const std::string image, Format &format, BorderMode &border_mode, T constant_border_value, const HOGInfo &hog_info) + { + // Get image shape for src tensor + TensorShape shape = library->get_image_shape(image); + + // Create tensor info for HOG descriptor + TensorInfo tensor_info_hog_descriptor(hog_info, shape.x(), shape.y()); + + // Create HOG + HOGType hog = create_HOG(hog_info.cell_size(), + hog_info.block_size(), + hog_info.detection_window_size(), + hog_info.block_stride(), + hog_info.num_bins(), + hog_info.normalization_type(), + hog_info.l2_hyst_threshold(), + hog_info.phase_type()); + + // Create tensors + TensorType src = create_tensor(shape, data_type_from_format(format)); + TensorType dst = create_tensor(tensor_info_hog_descriptor.tensor_shape(), DataType::F32, tensor_info_hog_descriptor.num_channels()); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Create and configure function + FunctionType hog_descriptor; + hog_descriptor.configure(&src, &dst, &hog, border_mode, constant_border_value); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + const T max = std::numeric_limits::max(); + + // Fill tensors + fill(AccessorType(src), image, format); + fill(AccessorType(dst), 1, static_cast(max)); + + // Compute function + hog_descriptor.run(); + + return dst; + } + + SimpleTensor compute_reference(const std::string image, Format format, BorderMode border_mode, T constant_border_value, const HOGInfo &hog_info) + { + // Create reference + SimpleTensor src{ library->get_image_shape(image), data_type_from_format(format) }; + + // Fill reference + fill(src, image, format); + + return reference::hog_descriptor(src, border_mode, constant_border_value, hog_info); + } + + TensorType _target{}; + SimpleTensor _reference{}; +}; +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_HOG_DESCRIPTOR_FIXTURE */ diff --git a/tests/validation/reference/HOGDescriptor.cpp b/tests/validation/reference/HOGDescriptor.cpp new file mode 100644 index 0000000000..369ac74edc --- /dev/null +++ b/tests/validation/reference/HOGDescriptor.cpp @@ -0,0 +1,273 @@ +/* + * 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. + */ +#include "HOGDescriptor.h" + +#include "Derivative.h" +#include "Magnitude.h" +#include "Phase.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +namespace +{ +template +void hog_orientation_compute(const SimpleTensor &mag, const SimpleTensor &phase, std::vector &bins, const HOGInfo &hog_info) +{ + const size_t num_bins = hog_info.num_bins(); + const size_t cell_height = hog_info.cell_size().height; + const size_t cell_width = hog_info.cell_size().width; + + float phase_scale = (PhaseType::SIGNED == hog_info.phase_type() ? num_bins / 360.0f : num_bins / 180.0f); + phase_scale *= (PhaseType::SIGNED == hog_info.phase_type() ? 360.0f / 255.0f : 1.0f); + + int row_idx = 0; + for(size_t yc = 0; yc < cell_height; ++yc) + { + for(size_t xc = 0; xc < cell_height; xc++) + { + const float mag_value = mag[(row_idx + xc)]; + const float phase_value = phase[(row_idx + xc)] * phase_scale + 0.5f; + const float w1 = phase_value - floor(phase_value); + + // The quantised phase is the histogram index [0, num_bins - 1] + // Check limit of histogram index. If hidx == num_bins, hidx = 0 + const auto hidx = static_cast(phase_value) % num_bins; + + // Weighted vote between 2 bins + bins[hidx] += mag_value * (1.0f - w1); + bins[(hidx + 1) % num_bins] += mag_value * w1; + } + + row_idx += cell_width; + } +} + +template +void hog_block_normalization_compute(SimpleTensor &block, SimpleTensor &desc, const HOGInfo &hog_info, size_t block_idx) +{ + const int num_bins_per_block = desc.num_channels(); + const HOGNormType norm_type = hog_info.normalization_type(); + const Coordinates id = index2coord(desc.shape(), block_idx); + + float sum = 0.0f; + + // Calculate sum + for(int i = 0; i < num_bins_per_block; ++i) + { + const float val = block[i]; + sum += (norm_type == HOGNormType::L1_NORM) ? std::fabs(val) : val * val; + } + + // Calculate normalization scale + float scale = 1.0f / (std::sqrt(sum) + num_bins_per_block * 0.1f); + + if(norm_type == HOGNormType::L2HYS_NORM) + { + // Reset sum + sum = 0.0f; + for(int i = 0; i < num_bins_per_block; ++i) + { + float val = block[i] * scale; + + // Clip scaled input_value if over l2_hyst_threshold + val = fmin(val, hog_info.l2_hyst_threshold()); + sum += val * val; + block[i] = val; + } + + // We use the same constants of OpenCV + scale = 1.0f / (std::sqrt(sum) + 1e-3f); + } + + for(int i = 0; i < num_bins_per_block; ++i) + { + block[i] *= scale; + reinterpret_cast(desc(id))[i] = block[i]; + } +} +} // namespace + +template +void hog_orientation_binning(const SimpleTensor &mag, const SimpleTensor &phase, SimpleTensor &hog_space, const HOGInfo &hog_info) +{ + const size_t cell_width = hog_info.cell_size().width; + const size_t cell_height = hog_info.cell_size().height; + const size_t shape_width = hog_space.shape().x() * hog_info.cell_size().width; + const size_t shape_height = hog_space.shape().y() * hog_info.cell_size().height; + + SimpleTensor mag_cell(TensorShape(cell_width, cell_height), DataType::F32); + SimpleTensor phase_cell(TensorShape(cell_width, cell_height), DataType::F32); + + int cell_idx = 0; + int y_offset = 0; + int x_offset = 0; + + // Traverse shape + for(auto sy = cell_height - 1; sy < shape_height; sy += cell_height) + { + x_offset = 0; + for(auto sx = cell_width - 1; sx < shape_width; sx += cell_width) + { + int row_idx = 0; + int elem_idx = 0; + + // Traverse cell + for(auto y = 0u; y < cell_height; ++y) + { + for(auto x = 0u; x < cell_width; ++x) + { + int shape_idx = x + row_idx + x_offset + y_offset; + mag_cell[elem_idx] = mag[shape_idx]; + phase_cell[elem_idx] = phase[shape_idx]; + elem_idx++; + } + + row_idx += shape_width; + } + + // Partition magnitude values into bins based on phase values + std::vector bins(hog_info.num_bins()); + hog_orientation_compute(mag_cell, phase_cell, bins, hog_info); + + for(size_t i = 0; i < hog_info.num_bins(); ++i) + { + hog_space[cell_idx * hog_info.num_bins() + i] = bins[i]; + } + + x_offset += cell_width; + cell_idx++; + } + + y_offset += (cell_height * shape_width); + } +} + +template +void hog_block_normalization(SimpleTensor &desc, const SimpleTensor &hog_space, const HOGInfo &hog_info) +{ + const Size2D cells_per_block = hog_info.num_cells_per_block(); + const Size2D cells_per_block_stride = hog_info.num_cells_per_block_stride(); + + const size_t block_width = hog_info.block_size().width; + const size_t block_height = hog_info.block_size().height; + const size_t block_stride_width = hog_info.block_stride().width; + const size_t block_stride_height = hog_info.block_stride().height; + const size_t shape_width = hog_space.shape().x() * hog_info.cell_size().width; + const size_t shape_height = hog_space.shape().y() * hog_info.cell_size().height; + + const size_t num_bins = hog_info.num_bins(); + const size_t num_channels = cells_per_block.area() * num_bins; + + SimpleTensor block(TensorShape{ 1u, 1u }, DataType::F32, num_channels); + + int block_idx = 0; + int block_y_offset = 0; + + // Traverse shape + for(auto sy = block_width - 1; sy < shape_height; sy += block_stride_height) + { + int block_x_offset = 0; + for(auto sx = block_height - 1; sx < shape_width; sx += block_stride_width) + { + int cell_y_offset = 0; + int elem_idx = 0; + + // Traverse block + for(auto y = 0u; y < cells_per_block.height; ++y) + { + int cell_x_offset = 0; + for(auto x = 0u; x < cells_per_block.width; ++x) + { + for(auto bin = 0u; bin < num_bins; ++bin) + { + int idx = bin + cell_x_offset + cell_y_offset + block_x_offset + block_y_offset; + block[elem_idx] = hog_space[idx]; + elem_idx++; + } + + cell_x_offset += num_bins; + } + + cell_y_offset += hog_space.shape().x() * num_bins; + } + + // Normalize block and write to descriptor + hog_block_normalization_compute(block, desc, hog_info, block_idx); + + block_x_offset += cells_per_block_stride.width * num_bins; + block_idx++; + } + + block_y_offset += cells_per_block_stride.height * num_bins * hog_space.shape().x(); + } +} + +template +SimpleTensor hog_descriptor(const SimpleTensor &src, BorderMode border_mode, U constant_border_value, const HOGInfo &hog_info) +{ + SimpleTensor _mag; + SimpleTensor _phase; + + SimpleTensor grad_x; + SimpleTensor grad_y; + + // Create tensor info for HOG descriptor + TensorInfo desc_info(hog_info, src.shape().x(), src.shape().y()); + SimpleTensor desc(desc_info.tensor_shape(), DataType::F32, desc_info.num_channels()); + + // Create HOG space tensor (num_cells_x, num_cells_y) + TensorShape hog_space_shape(src.shape().x() / hog_info.cell_size().width, + src.shape().y() / hog_info.cell_size().height); + + // For each cell a histogram with a num_bins is created + TensorInfo info_hog_space(hog_space_shape, hog_info.num_bins(), DataType::F32); + SimpleTensor hog_space(info_hog_space.tensor_shape(), DataType::F32, info_hog_space.num_channels()); + + // Calculate derivative + std::tie(grad_x, grad_y) = derivative(src, border_mode, constant_border_value, GradientDimension::GRAD_XY); + + // Calculate magnitude and phase + _mag = magnitude(grad_x, grad_y, MagnitudeType::L2NORM); + _phase = phase(grad_x, grad_y, hog_info.phase_type()); + + // For each cell create histogram based on magnitude and phase + hog_orientation_binning(_mag, _phase, hog_space, hog_info); + + // Normalize histograms based on block size + hog_block_normalization(desc, hog_space, hog_info); + + return desc; +} + +template SimpleTensor hog_descriptor(const SimpleTensor &src, BorderMode border_mode, uint8_t constant_border_value, const HOGInfo &hog_info); +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/reference/HOGDescriptor.h b/tests/validation/reference/HOGDescriptor.h new file mode 100644 index 0000000000..e886445ec7 --- /dev/null +++ b/tests/validation/reference/HOGDescriptor.h @@ -0,0 +1,43 @@ +/* + * 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_HOG_DESCRIPTOR_H__ +#define __ARM_COMPUTE_TEST_HOG_DESCRIPTOR_H__ + +#include "tests/SimpleTensor.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +template +SimpleTensor hog_descriptor(const SimpleTensor &src, BorderMode border_mode, U constant_border_value, const HOGInfo &hog_info); +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* __ARM_COMPUTE_TEST_HOG_DESCRIPTOR_H__ */ -- cgit v1.2.1