From 7b7858df42fccefbe6eb086ad516d5c011becd07 Mon Sep 17 00:00:00 2001 From: Georgios Pinitas Date: Wed, 21 Jun 2017 16:44:24 +0100 Subject: COMPMID-359: Implement NEON ROIPoolingLayer Change-Id: Ibffa738d4016d7221968bd43a4e6e1dab85baee8 Reviewed-on: http://mpd-gerrit.cambridge.arm.com/78623 Reviewed-by: Moritz Pflanzer Reviewed-by: Gian Marco Iodice Tested-by: Kaizen --- arm_compute/core/IArray.h | 2 + arm_compute/core/NEON/NEKernels.h | 1 + .../core/NEON/kernels/NEROIPoolingLayerKernel.h | 71 ++++++++++ arm_compute/core/Types.h | 40 ++++++ arm_compute/runtime/NEON/NEFunctions.h | 1 + .../runtime/NEON/functions/NEROIPoolingLayer.h | 63 +++++++++ scripts/check_clang-tidy.py | 2 + src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp | 151 +++++++++++++++++++++ src/runtime/NEON/functions/NEROIPoolingLayer.cpp | 45 ++++++ tests/NEON/Helper.h | 13 ++ tests/ProgramOptions.cpp | 2 +- tests/TypePrinter.h | 19 +++ tests/validation/Helpers.cpp | 72 ++++++++++ tests/validation/Helpers.h | 14 ++ tests/validation/NEON/ROIPoolingLayer.cpp | 106 +++++++++++++++ tests/validation/Reference.cpp | 23 ++++ tests/validation/Reference.h | 8 ++ tests/validation/ReferenceCPP.cpp | 8 ++ tests/validation/ReferenceCPP.h | 10 +- tests/validation/TensorOperations.h | 72 ++++++++++ tests/validation/TensorVisitors.h | 23 ++++ 21 files changed, 744 insertions(+), 2 deletions(-) create mode 100644 arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h create mode 100644 arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h create mode 100644 src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp create mode 100644 src/runtime/NEON/functions/NEROIPoolingLayer.cpp create mode 100644 tests/validation/Helpers.cpp create mode 100644 tests/validation/NEON/ROIPoolingLayer.cpp diff --git a/arm_compute/core/IArray.h b/arm_compute/core/IArray.h index 2ed56100cf..8f84bb182a 100644 --- a/arm_compute/core/IArray.h +++ b/arm_compute/core/IArray.h @@ -34,6 +34,7 @@ class KeyPoint; class Coordinates2D; class DetectionWindow; class Size2D; +class ROI; /** Array of type T */ template @@ -137,6 +138,7 @@ private: }; using IKeyPointArray = IArray; using ICoordinates2DArray = IArray; +using IROIArray = IArray; using IDetectionWindowArray = IArray; using ISize2DArray = IArray; using IUInt8Array = IArray; diff --git a/arm_compute/core/NEON/NEKernels.h b/arm_compute/core/NEON/NEKernels.h index eaa50f123b..ee63be3c46 100644 --- a/arm_compute/core/NEON/NEKernels.h +++ b/arm_compute/core/NEON/NEKernels.h @@ -80,6 +80,7 @@ #include "arm_compute/core/NEON/kernels/NENormalizationLayerKernel.h" #include "arm_compute/core/NEON/kernels/NEPixelWiseMultiplicationKernel.h" #include "arm_compute/core/NEON/kernels/NEPoolingLayerKernel.h" +#include "arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h" #include "arm_compute/core/NEON/kernels/NERemapKernel.h" #include "arm_compute/core/NEON/kernels/NEScaleKernel.h" #include "arm_compute/core/NEON/kernels/NEScharr3x3Kernel.h" diff --git a/arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h b/arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h new file mode 100644 index 0000000000..3a2f761370 --- /dev/null +++ b/arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_NEROIPOOLINGLAYERKERNEL_H__ +#define __ARM_COMPUTE_NEROIPOOLINGLAYERKERNEL_H__ + +#include "arm_compute/core/NEON/INEKernel.h" + +#include "arm_compute/core/IArray.h" + +namespace arm_compute +{ +class ITensor; + +/** Interface for the ROI pooling layer kernel */ +class NEROIPoolingLayerKernel : public INEKernel +{ +public: + /** Default constructor */ + NEROIPoolingLayerKernel(); + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NEROIPoolingLayerKernel(const NEROIPoolingLayerKernel &) = delete; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NEROIPoolingLayerKernel &operator=(const NEROIPoolingLayerKernel &) = delete; + /** Allow instances of this class to be moved */ + NEROIPoolingLayerKernel(NEROIPoolingLayerKernel &&) = default; + /** Allow instances of this class to be moved */ + NEROIPoolingLayerKernel &operator=(NEROIPoolingLayerKernel &&) = default; + /** Default destructor */ + ~NEROIPoolingLayerKernel() = default; + + /** Set the input and output tensors. + * + * @param[in] input Source tensor. Data types supported: F32. + * @param[in] rois Array containing the regions of interest. + * @param[out] output Destination tensor. Data types supported: Same as @p input. + * @param[in] pool_info Contains pooling operation information described in @ref ROIPoolingLayerInfo. + */ + void configure(const ITensor *input, const IROIArray *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info); + + // Inherited methods overridden: + void run(const Window &window) override; + +private: + const ITensor *_input; + const IROIArray *_rois; + ITensor *_output; + ROIPoolingLayerInfo _pool_info; +}; +} +#endif /*__ARM_COMPUTE_NEROIPOOLINGLAYERKERNEL_H__ */ diff --git a/arm_compute/core/Types.h b/arm_compute/core/Types.h index b7a30a5634..2bd449c5c6 100644 --- a/arm_compute/core/Types.h +++ b/arm_compute/core/Types.h @@ -300,6 +300,13 @@ struct Coordinates3D uint32_t z; /**< Z coordinates */ }; +/** Region of interest */ +struct ROI +{ + Rectangle rect; /**< Rectangle specifying the region of interest */ + uint16_t batch_idx; /**< The batch index of the region of interest */ +}; + /** Available channels */ enum class Channel { @@ -454,6 +461,39 @@ private: PadStrideInfo _pad_stride_info; }; +/** ROI Pooling Layer Information class */ +class ROIPoolingLayerInfo +{ +public: + /** Default Constructor + * + * @param[in] pooled_width Pooled width of the layer. + * @param[in] pooled_height Pooled height of the layer. + * @param[in] spatial_scale Spatial scale to be applied to the ROI coordinates and dimensions. + */ + ROIPoolingLayerInfo(unsigned int pooled_width, unsigned int pooled_height, float spatial_scale) + : _pooled_width(pooled_width), _pooled_height(pooled_height), _spatial_scale(spatial_scale) + { + } + unsigned int pooled_width() const + { + return _pooled_width; + } + unsigned int pooled_height() const + { + return _pooled_height; + } + float spatial_scale() const + { + return _spatial_scale; + } + +private: + unsigned int _pooled_width; + unsigned int _pooled_height; + float _spatial_scale; +}; + /** Activation Layer Information class */ class ActivationLayerInfo { diff --git a/arm_compute/runtime/NEON/NEFunctions.h b/arm_compute/runtime/NEON/NEFunctions.h index daf76f3a87..753cbc179b 100644 --- a/arm_compute/runtime/NEON/NEFunctions.h +++ b/arm_compute/runtime/NEON/NEFunctions.h @@ -80,6 +80,7 @@ #include "arm_compute/runtime/NEON/functions/NEPhase.h" #include "arm_compute/runtime/NEON/functions/NEPixelWiseMultiplication.h" #include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h" +#include "arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h" #include "arm_compute/runtime/NEON/functions/NERemap.h" #include "arm_compute/runtime/NEON/functions/NEScale.h" #include "arm_compute/runtime/NEON/functions/NEScharr3x3.h" diff --git a/arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h b/arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h new file mode 100644 index 0000000000..04b5c35150 --- /dev/null +++ b/arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_NEROIPOOLINGLAYER_H__ +#define __ARM_COMPUTE_NEROIPOOLINGLAYER_H__ + +#include "arm_compute/runtime/IFunction.h" + +#include "arm_compute/core/IArray.h" +#include "arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h" + +namespace arm_compute +{ +class ITensor; + +/** Basic function to run @ref NEROIPoolingLayerKernel. + * + * This function calls the following OpenCL kernels: + * -# @ref NEROIPoolingLayerKernel + * + */ +class NEROIPoolingLayer : public IFunction +{ +public: + /** Constructor */ + NEROIPoolingLayer(); + /** Set the input and output tensors. + * + * @param[in] input Source tensor. Data types supported: F32. + * @param[in] rois Array containing the regions of interest. + * @param[out] output Destination tensor. Data types supported: Same as @p input. + * @param[in] pool_info Contains pooling operation information described in @ref ROIPoolingLayerInfo. + */ + void configure(const ITensor *input, const IROIArray *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info); + + // Inherited methods overridden: + void run() override; + +private: + NEROIPoolingLayerKernel _roi_kernel; +}; +} +#endif /* __ARM_COMPUTE_NEROIPOOLINGLAYER_H__ */ diff --git a/scripts/check_clang-tidy.py b/scripts/check_clang-tidy.py index fc73dca94e..3a308e59a6 100755 --- a/scripts/check_clang-tidy.py +++ b/scripts/check_clang-tidy.py @@ -37,6 +37,8 @@ if __name__ == "__main__": ("PMUCounter.cpp" in line and "consider replacing 'long long' with 'int64'" in line) or ("Validation.cpp" in line and "parameter 'classified_labels' is unused" in line) or ("Validation.cpp" in line and "parameter 'expected_labels' is unused" in line) or + ("Reference.cpp" in line and "parameter 'rois' is unused" in line) or + ("ReferenceCPP.cpp" in line and "parameter 'rois' is unused" in line) or "3rdparty" in line): continue diff --git a/src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp b/src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp new file mode 100644 index 0000000000..ec2b6a5079 --- /dev/null +++ b/src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h" + +#include "arm_compute/core/AccessWindowStatic.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/ITensor.h" +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/core/Validate.h" +#include "arm_compute/core/Window.h" +#include "support/ToolchainSupport.h" + +#include +#include + +using namespace arm_compute; + +NEROIPoolingLayerKernel::NEROIPoolingLayerKernel() + : _input(nullptr), _rois(nullptr), _output(nullptr), _pool_info(0, 0, 0.f) +{ +} + +void NEROIPoolingLayerKernel::configure(const ITensor *input, const IROIArray *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input, rois, output); + ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); + ARM_COMPUTE_ERROR_ON((pool_info.pooled_width() == 0) || (pool_info.pooled_height() == 0)); + ARM_COMPUTE_ERROR_ON(rois->num_values() == 0); + + // Output auto inizialitation if not yet initialized + TensorShape output_shape(pool_info.pooled_width(), pool_info.pooled_height(), rois->num_values()); + auto_init_if_empty(*output->info(), output_shape, 1, input->info()->data_type(), input->info()->fixed_point_position()); + + ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, output); + ARM_COMPUTE_ERROR_ON((output->info()->dimension(0) != pool_info.pooled_width()) || (output->info()->dimension(1) != pool_info.pooled_height())); + + // Set instance variables + _input = input; + _rois = rois; + _output = output; + _pool_info = pool_info; + + // Configure kernel window + Window window; + window.set(Window::DimX, Window::Dimension(0, rois->num_values())); + window.set(Window::DimY, Window::Dimension(0, 1)); + + AccessWindowStatic input_access(input->info(), + input->info()->valid_region().start(0), + input->info()->valid_region().start(1), + input->info()->valid_region().end(0), + input->info()->valid_region().end(1)); + AccessWindowStatic output_access(output->info(), 0, 0, pool_info.pooled_width(), pool_info.pooled_height()); + + update_window_and_padding(window, input_access, output_access); + output_access.set_valid_region(window, ValidRegion(Coordinates(), output->info()->tensor_shape())); + INEKernel::configure(window); +} + +void NEROIPoolingLayerKernel::run(const Window &window) +{ + ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); + ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window); + + const int roi_list_start = window.x().start(); + const int roi_list_end = window.x().end(); + const int width = _input->info()->dimension(Window::DimX); + const int height = _input->info()->dimension(Window::DimY); + const int fms = _input->info()->dimension(Window::DimZ); + const int pooled_w = _pool_info.pooled_width(); + const int pooled_h = _pool_info.pooled_height(); + const float spatial_scale = _pool_info.spatial_scale(); + + for(int roi_indx = roi_list_start; roi_indx < roi_list_end; ++roi_indx) + { + const ROI &curr_roi = _rois->at(roi_indx); + + // Scale ROI + const int roi_batch = curr_roi.batch_idx; + const int roi_anchor_x = support::cpp11::round(curr_roi.rect.x * spatial_scale); + const int roi_anchor_y = support::cpp11::round(curr_roi.rect.y * spatial_scale); + const int roi_width = std::max(support::cpp11::round(curr_roi.rect.width * spatial_scale), 1.f); + const int roi_height = std::max(support::cpp11::round(curr_roi.rect.height * spatial_scale), 1.f); + + // Determine pooling regions + const auto pool_region_size_x = static_cast(roi_width) / pooled_w; + const auto pool_region_size_y = static_cast(roi_height) / pooled_h; + + // Iterate through all feature maps + for(int fm = 0; fm < fms; ++fm) + { + // Iterate through all output pixels + for(int py = 0; py < pooled_h; ++py) + { + for(int px = 0; px < pooled_w; ++px) + { + auto region_start_x = static_cast(std::floor(px * pool_region_size_x)); + auto region_end_x = static_cast(std::ceil((px + 1) * pool_region_size_x)); + auto region_start_y = static_cast(std::floor(py * pool_region_size_y)); + auto region_end_y = static_cast(std::ceil((py + 1) * pool_region_size_y)); + + region_start_x = std::min(std::max(region_start_x + roi_anchor_x, 0), width); + region_end_x = std::min(std::max(region_end_x + roi_anchor_x, 0), width); + region_start_y = std::min(std::max(region_start_y + roi_anchor_y, 0), height); + region_end_y = std::min(std::max(region_end_y + roi_anchor_y, 0), height); + + // Iterate through the pooling region + if((region_end_x <= region_start_x) || (region_end_y <= region_start_y)) + { + *reinterpret_cast(_output->ptr_to_element(Coordinates(px, py, fm, roi_indx))) = 0; + } + else + { + float curr_max = -FLT_MAX; + for(int j = region_start_y; j < region_end_y; ++j) + { + for(int i = region_start_x; i < region_end_x; ++i) + { + const auto val = *reinterpret_cast(_input->ptr_to_element(Coordinates(i, j, fm, roi_batch))); + curr_max = std::max(val, curr_max); + } + } + *reinterpret_cast(_output->ptr_to_element(Coordinates(px, py, fm, roi_indx))) = curr_max; + } + } + } + } + } +} diff --git a/src/runtime/NEON/functions/NEROIPoolingLayer.cpp b/src/runtime/NEON/functions/NEROIPoolingLayer.cpp new file mode 100644 index 0000000000..1f1400cf42 --- /dev/null +++ b/src/runtime/NEON/functions/NEROIPoolingLayer.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h" +#include "arm_compute/runtime/NEON/NEScheduler.h" + +using namespace arm_compute; + +NEROIPoolingLayer::NEROIPoolingLayer() + : _roi_kernel() +{ +} + +void NEROIPoolingLayer::configure(const ITensor *input, const IROIArray *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info) +{ + _roi_kernel.configure(input, rois, output, pool_info); +} + +void NEROIPoolingLayer::run() +{ + NEScheduler::get().schedule(&_roi_kernel, Window::DimX); +} diff --git a/tests/NEON/Helper.h b/tests/NEON/Helper.h index c8f1c2e635..e64ac27ef3 100644 --- a/tests/NEON/Helper.h +++ b/tests/NEON/Helper.h @@ -27,6 +27,7 @@ #include "Globals.h" #include "TensorLibrary.h" +#include "arm_compute/runtime/Array.h" #include "arm_compute/runtime/Tensor.h" namespace arm_compute @@ -71,6 +72,18 @@ inline Tensor create_tensor(const std::string &name, DataType data_type, int fix return tensor; } + +template +Array create_array(const std::vector &v) +{ + const size_t v_size = v.size(); + Array array(v_size); + + array.resize(v_size); + std::copy(v.begin(), v.end(), array.buffer()); + + return array; +} } // namespace neon } // namespace test } // namespace arm_compute diff --git a/tests/ProgramOptions.cpp b/tests/ProgramOptions.cpp index 0ae92f64e7..b5a7bb14ad 100644 --- a/tests/ProgramOptions.cpp +++ b/tests/ProgramOptions.cpp @@ -38,7 +38,7 @@ namespace test ProgramOptions::ProgramOptions() { boost::program_options::options_description generic("Generic options"); - generic.add_options()("help", "Print help message")("seed", boost::program_options::value(), "Seed for the tensor library"); + generic.add_options()("help", "Print help message")("seed", boost::program_options::value()->default_value(std::random_device()()), "Seed for the tensor library"); _visible.add(generic); diff --git a/tests/TypePrinter.h b/tests/TypePrinter.h index d76754448e..f5915c474a 100644 --- a/tests/TypePrinter.h +++ b/tests/TypePrinter.h @@ -53,6 +53,18 @@ inline ::std::ostream &operator<<(::std::ostream &os, const Dimensions &dimen return os; } +/** Formatted output of the Rectangle type. */ +inline ::std::ostream &operator<<(::std::ostream &os, const Rectangle &rect) +{ + os << "("; + os << rect.height << ", " << rect.width; + os << ", "; + os << rect.x << ", " << rect.y; + os << ")"; + + return os; +} + /** Formatted output of the PadStridInfo type. */ inline ::std::ostream &operator<<(::std::ostream &os, const PadStrideInfo &pad_stride_info) { @@ -65,6 +77,13 @@ inline ::std::ostream &operator<<(::std::ostream &os, const PadStrideInfo &pad_s return os; } +/** Formatted output of the ROIPoolingInfo type. */ +inline ::std::ostream &operator<<(::std::ostream &os, const ROIPoolingLayerInfo &pool_info) +{ + os << pool_info.pooled_width() << ", " << pool_info.pooled_height() << ", " << pool_info.spatial_scale(); + return os; +} + /** Formatted output of the BorderMode type. */ inline ::std::ostream &operator<<(::std::ostream &os, const BorderMode &mode) { diff --git a/tests/validation/Helpers.cpp b/tests/validation/Helpers.cpp new file mode 100644 index 0000000000..747260ea99 --- /dev/null +++ b/tests/validation/Helpers.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "validation/Helpers.h" + +using namespace arm_compute::test; + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +std::vector generate_random_rois(const TensorShape &shape, const ROIPoolingLayerInfo &pool_info, unsigned int num_rois, std::random_device::result_type seed) +{ + ARM_COMPUTE_ERROR_ON((pool_info.pooled_width() < 4) || (pool_info.pooled_height() < 4)); + + std::vector rois; + std::mt19937 gen(seed); + const int pool_width = pool_info.pooled_width(); + const int pool_height = pool_info.pooled_height(); + const float roi_scale = pool_info.spatial_scale(); + + // Calculate distribution bounds + const auto scaled_width = static_cast((shape.x() / roi_scale) / pool_width); + const auto scaled_height = static_cast((shape.y() / roi_scale) / pool_height); + const auto min_width = static_cast(pool_width / roi_scale); + const auto min_height = static_cast(pool_height / roi_scale); + + // Create distributions + std::uniform_int_distribution dist_batch(0, shape[3] - 1); + std::uniform_int_distribution dist_x(0, scaled_width); + std::uniform_int_distribution dist_y(0, scaled_height); + std::uniform_int_distribution dist_w(min_width, std::max(min_width, (pool_width - 2) * scaled_width)); + std::uniform_int_distribution dist_h(min_height, std::max(min_height, (pool_height - 2) * scaled_height)); + + for(unsigned int r = 0; r < num_rois; ++r) + { + ROI roi{}; + roi.batch_idx = dist_batch(gen); + roi.rect.x = dist_x(gen); + roi.rect.y = dist_y(gen); + roi.rect.width = dist_w(gen); + roi.rect.height = dist_h(gen); + rois.push_back(roi); + } + + return rois; +} +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/Helpers.h b/tests/validation/Helpers.h index c0c5865e56..d92699d93e 100644 --- a/tests/validation/Helpers.h +++ b/tests/validation/Helpers.h @@ -27,8 +27,12 @@ #include "Types.h" #include "ValidationUserConfiguration.h" +#include "arm_compute/core/Types.h" + +#include #include #include +#include namespace arm_compute { @@ -168,6 +172,16 @@ inline void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPatt } } +/** Create a vector of random ROIs. + * + * @param[in] shape The shape of the input tensor. + * @param[in] pool_info The ROI pooling information. + * @param[in] num_rois The number of ROIs to be created. + * @param[in] seed The random seed to be used. + * + * @return A vector that contains the requested number of random ROIs + */ +std::vector generate_random_rois(const TensorShape &shape, const ROIPoolingLayerInfo &pool_info, unsigned int num_rois, std::random_device::result_type seed); } // namespace validation } // namespace test } // namespace arm_compute diff --git a/tests/validation/NEON/ROIPoolingLayer.cpp b/tests/validation/NEON/ROIPoolingLayer.cpp new file mode 100644 index 0000000000..20aaefd3de --- /dev/null +++ b/tests/validation/NEON/ROIPoolingLayer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h" +#include "validation/Datasets.h" +#include "validation/Helpers.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +Tensor compute_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector &rois, ROIPoolingLayerInfo pool_info) +{ + TensorShape shape_dst; + shape_dst.set(0, pool_info.pooled_width()); + shape_dst.set(1, pool_info.pooled_height()); + shape_dst.set(2, shape.z()); + shape_dst.set(3, rois.size()); + + // Create tensors + Tensor src = create_tensor(shape, dt); + Tensor dst = create_tensor(shape_dst, dt); + Array rois_array = create_array(rois); + + // Create and configure function + NEROIPoolingLayer roi_pool; + roi_pool.configure(&src, &rois_array, &dst, pool_info); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + std::uniform_real_distribution<> distribution(-1, 1); + library->fill(NEAccessor(src), distribution, 0); + + // Compute function + roi_pool.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ROIPoolingLayer) + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, CNNFloatDataTypes() * boost::unit_test::data::make({ 10, 20, 40 }) * boost::unit_test::data::make({ 7, 9 }) * boost::unit_test::data::make({ 1.f / 8.f, 1.f / 16.f }), + dt, num_rois, roi_pool_size, roi_scale) +{ + TensorShape shape(50U, 47U, 2U, 3U); + ROIPoolingLayerInfo pool_info(roi_pool_size, roi_pool_size, roi_scale); + + // Construct ROI vector + std::vector rois = generate_random_rois(shape, pool_info, num_rois, user_config.seed); + + // Compute function + Tensor dst = compute_roi_pooling_layer(shape, dt, rois, pool_info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_roi_pooling_layer(shape, dt, rois, pool_info); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/Reference.cpp b/tests/validation/Reference.cpp index 761bc20ea1..2388fb6fe3 100644 --- a/tests/validation/Reference.cpp +++ b/tests/validation/Reference.cpp @@ -30,6 +30,7 @@ #include "validation/Helpers.h" #include +#include using namespace arm_compute::test; @@ -620,6 +621,28 @@ RawTensor Reference::compute_reference_pooling_layer(const TensorShape &shape_in return ref_dst; } +RawTensor Reference::compute_reference_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector &rois, const ROIPoolingLayerInfo &pool_info) +{ + TensorShape shape_dst; + shape_dst.set(0, pool_info.pooled_width()); + shape_dst.set(1, pool_info.pooled_height()); + shape_dst.set(2, shape.z()); + shape_dst.set(3, rois.size()); + + // Create reference + RawTensor ref_src = library->get(shape, dt); + RawTensor ref_dst = library->get(shape_dst, dt); + + // Fill reference + std::uniform_real_distribution<> distribution(-1, 1); + library->fill(ref_src, distribution, 0.0); + + // Compute reference + ReferenceCPP::roi_pooling_layer(ref_src, ref_dst, rois, pool_info); + + return ref_dst; +} + RawTensor Reference::compute_reference_softmax_layer(const TensorShape &shape, DataType dt, int fixed_point_position) { // Create reference diff --git a/tests/validation/Reference.h b/tests/validation/Reference.h index 41d6a60edb..1794ae6f23 100644 --- a/tests/validation/Reference.h +++ b/tests/validation/Reference.h @@ -331,6 +331,14 @@ public: * @return Computed raw tensor. */ static RawTensor compute_reference_pooling_layer(const TensorShape &shape_in, const TensorShape &shape_out, DataType dt, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** Compute reference roi pooling layer. + * + * @param[in] shape Shape of the input tensor. + * @param[in] dt Data type of input and output tensors. + * @param[in] rois Region of interest vector. + * @param[in] pool_info ROI Pooling Layer information. + */ + static RawTensor compute_reference_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector &rois, const ROIPoolingLayerInfo &pool_info); /** Compute reference softmax layer. * * @param[in] shape Shape of the input and output tensors. diff --git a/tests/validation/ReferenceCPP.cpp b/tests/validation/ReferenceCPP.cpp index 7c50f508a0..c89b737598 100644 --- a/tests/validation/ReferenceCPP.cpp +++ b/tests/validation/ReferenceCPP.cpp @@ -318,6 +318,14 @@ void ReferenceCPP::pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLa boost::apply_visitor(tensor_visitors::pooling_layer_visitor(s, pool_info, fixed_point_position), d); } +// ROI Pooling Layer +void ReferenceCPP::roi_pooling_layer(const RawTensor &src, RawTensor &dst, const std::vector &rois, const ROIPoolingLayerInfo &pool_info) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::roi_pooling_layer_visitor(s, rois, pool_info), d); +} + // Softmax Layer void ReferenceCPP::softmax_layer(const RawTensor &src, RawTensor &dst) { diff --git a/tests/validation/ReferenceCPP.h b/tests/validation/ReferenceCPP.h index 8ee8f31d9b..9debdeeeba 100644 --- a/tests/validation/ReferenceCPP.h +++ b/tests/validation/ReferenceCPP.h @@ -276,7 +276,7 @@ public: * @param[in] norm_info Normalization Layer information. */ static void normalization_layer(const RawTensor &src, RawTensor &dst, NormalizationLayerInfo norm_info); - /** Pooling layer of @p src based on the information from @p norm_info. + /** Pooling layer of @p src based on the information from @p pool_info. * * @param[in] src Input tensor. * @param[out] dst Result tensor. @@ -284,6 +284,14 @@ public: * @param[in] fixed_point_position Fixed point position. (Optional) */ static void pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** ROI Pooling layer of @p src based on the information from @p pool_info and @p rois. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[in] rois Region of Interest points. + * @param[in] pool_info ROI Pooling Layer information. + */ + static void roi_pooling_layer(const RawTensor &src, RawTensor &dst, const std::vector &rois, const ROIPoolingLayerInfo &pool_info); /** Softmax Layer of @p src. * * @param[in] src Input tensor. diff --git a/tests/validation/TensorOperations.h b/tests/validation/TensorOperations.h index 4f7765613c..5499e58f35 100644 --- a/tests/validation/TensorOperations.h +++ b/tests/validation/TensorOperations.h @@ -1438,6 +1438,78 @@ void pooling_layer(const Tensor &in, Tensor &out, PoolingLayerInfo pool_in } } +// Pooling layer +template +void roi_pooling_layer(const Tensor &in, Tensor &out, const std::vector &rois, const ROIPoolingLayerInfo &pool_info) +{ + const int num_rois = rois.size(); + const int width_in = in.shape().x(); + const int height_in = in.shape().y(); + const int fms = in.shape().z(); + const int volume_in = width_in * height_in * fms; + const int pool_w = pool_info.pooled_width(); + const int pool_h = pool_info.pooled_height(); + const int volume_out = pool_w * pool_h * fms; + const float roi_scale = pool_info.spatial_scale(); + + // Iterate through all rois + for(int roi_idx = 0; roi_idx < num_rois; ++roi_idx) + { + // Get dimensions of current ROI + const ROI &roi = rois[roi_idx]; + + int batch_id = roi.batch_idx; + int roi_start_x = support::cpp11::round(roi.rect.x * roi_scale); + int roi_start_y = support::cpp11::round(roi.rect.y * roi_scale); + int roi_width = std::max(support::cpp11::round(roi.rect.width * roi_scale), 1.f); + int roi_height = std::max(support::cpp11::round(roi.rect.height * roi_scale), 1.f); + + // Determine pooling regions + float pool_region_size_x = static_cast(roi_width) / pool_w; + float pool_region_size_y = static_cast(roi_height) / pool_h; + + // Iterate through all channel + for(int fm = 0; fm < fms; ++fm) + { + // Calculate each output pixel + for(int py = 0; py < pool_h; ++py) + { + for(int px = 0; px < pool_w; ++px) + { + int region_start_x = static_cast(std::floor(px * pool_region_size_x)); + int region_end_x = static_cast(std::ceil((px + 1) * pool_region_size_x)); + int region_start_y = static_cast(std::floor(py * pool_region_size_y)); + int region_end_y = static_cast(std::ceil((py + 1) * pool_region_size_y)); + + region_start_x = std::min(std::max(region_start_x + roi_start_x, 0), width_in); + region_end_x = std::min(std::max(region_end_x + roi_start_x, 0), width_in); + region_start_y = std::min(std::max(region_start_y + roi_start_y, 0), height_in); + region_end_y = std::min(std::max(region_end_y + roi_start_y, 0), height_in); + + // Iterate through each pixel in the pooling region + if((region_end_x <= region_start_x) || (region_end_y <= region_start_y)) + { + out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = 0; + } + else + { + T curr_max = std::numeric_limits::lowest(); + for(int j = region_start_y; j < region_end_y; ++j) + { + for(int i = region_start_x; i < region_end_x; ++i) + { + const auto val = in[batch_id * volume_in + fm * width_in * height_in + j * width_in + i]; + curr_max = std::max(val, curr_max); + } + } + out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = curr_max; + } + } + } + } + } +} + // Softmax Layer template ::value, int>::type * = nullptr> void softmax_layer(const Tensor &in, Tensor &out) diff --git a/tests/validation/TensorVisitors.h b/tests/validation/TensorVisitors.h index a274140734..1b58a16e4c 100644 --- a/tests/validation/TensorVisitors.h +++ b/tests/validation/TensorVisitors.h @@ -316,6 +316,29 @@ private: PoolingLayerInfo _pool_info; int _fixed_point_position; }; + +// ROI Pooling layer +struct roi_pooling_layer_visitor : public boost::static_visitor<> +{ +public: + explicit roi_pooling_layer_visitor(const TensorVariant &in, const std::vector &rois, ROIPoolingLayerInfo pool_info) + : _in(in), _rois(rois), _pool_info(pool_info) + { + } + + template + void operator()(Tensor &out) const + { + const Tensor &in = boost::get>(_in); + tensor_operations::roi_pooling_layer(in, out, _rois, _pool_info); + } + +private: + const TensorVariant &_in; + const std::vector &_rois; + ROIPoolingLayerInfo _pool_info; +}; + // Softmax Layer visitor struct softmax_layer_visitor : public boost::static_visitor<> { -- cgit v1.2.1