aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgios Pinitas <georgios.pinitas@arm.com>2017-06-21 16:44:24 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-09-17 14:15:39 +0100
commit7b7858df42fccefbe6eb086ad516d5c011becd07 (patch)
tree92d8671160cd440647b6ee8aa7f949cf9896f6bb
parentd0ae8b8ac8a371fd552c46d4b3be3db8628a5ade (diff)
downloadComputeLibrary-7b7858df42fccefbe6eb086ad516d5c011becd07.tar.gz
COMPMID-359: Implement NEON ROIPoolingLayer
Change-Id: Ibffa738d4016d7221968bd43a4e6e1dab85baee8 Reviewed-on: http://mpd-gerrit.cambridge.arm.com/78623 Reviewed-by: Moritz Pflanzer <moritz.pflanzer@arm.com> Reviewed-by: Gian Marco Iodice <gianmarco.iodice@arm.com> Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
-rw-r--r--arm_compute/core/IArray.h2
-rw-r--r--arm_compute/core/NEON/NEKernels.h1
-rw-r--r--arm_compute/core/NEON/kernels/NEROIPoolingLayerKernel.h71
-rw-r--r--arm_compute/core/Types.h40
-rw-r--r--arm_compute/runtime/NEON/NEFunctions.h1
-rw-r--r--arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h63
-rwxr-xr-xscripts/check_clang-tidy.py2
-rw-r--r--src/core/NEON/kernels/NEROIPoolingLayerKernel.cpp151
-rw-r--r--src/runtime/NEON/functions/NEROIPoolingLayer.cpp45
-rw-r--r--tests/NEON/Helper.h13
-rw-r--r--tests/ProgramOptions.cpp2
-rw-r--r--tests/TypePrinter.h19
-rw-r--r--tests/validation/Helpers.cpp72
-rw-r--r--tests/validation/Helpers.h14
-rw-r--r--tests/validation/NEON/ROIPoolingLayer.cpp106
-rw-r--r--tests/validation/Reference.cpp23
-rw-r--r--tests/validation/Reference.h8
-rw-r--r--tests/validation/ReferenceCPP.cpp8
-rw-r--r--tests/validation/ReferenceCPP.h10
-rw-r--r--tests/validation/TensorOperations.h72
-rw-r--r--tests/validation/TensorVisitors.h23
21 files changed, 744 insertions, 2 deletions
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 <class T>
@@ -137,6 +138,7 @@ private:
};
using IKeyPointArray = IArray<KeyPoint>;
using ICoordinates2DArray = IArray<Coordinates2D>;
+using IROIArray = IArray<ROI>;
using IDetectionWindowArray = IArray<DetectionWindow>;
using ISize2DArray = IArray<Size2D>;
using IUInt8Array = IArray<uint8_t>;
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 <cfloat>
+#include <cmath>
+
+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<float>(roi_width) / pooled_w;
+ const auto pool_region_size_y = static_cast<float>(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<int>(std::floor(px * pool_region_size_x));
+ auto region_end_x = static_cast<int>(std::ceil((px + 1) * pool_region_size_x));
+ auto region_start_y = static_cast<int>(std::floor(py * pool_region_size_y));
+ auto region_end_y = static_cast<int>(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<float *>(_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<const float *>(_input->ptr_to_element(Coordinates(i, j, fm, roi_batch)));
+ curr_max = std::max(val, curr_max);
+ }
+ }
+ *reinterpret_cast<float *>(_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 <typename T>
+Array<T> create_array(const std::vector<T> &v)
+{
+ const size_t v_size = v.size();
+ Array<T> 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<std::random_device::result_type>(), "Seed for the tensor library");
+ generic.add_options()("help", "Print help message")("seed", boost::program_options::value<std::random_device::result_type>()->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<T> &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<ROI> 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<ROI> 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<int>((shape.x() / roi_scale) / pool_width);
+ const auto scaled_height = static_cast<int>((shape.y() / roi_scale) / pool_height);
+ const auto min_width = static_cast<int>(pool_width / roi_scale);
+ const auto min_height = static_cast<int>(pool_height / roi_scale);
+
+ // Create distributions
+ std::uniform_int_distribution<int> dist_batch(0, shape[3] - 1);
+ std::uniform_int_distribution<int> dist_x(0, scaled_width);
+ std::uniform_int_distribution<int> dist_y(0, scaled_height);
+ std::uniform_int_distribution<int> dist_w(min_width, std::max(min_width, (pool_width - 2) * scaled_width));
+ std::uniform_int_distribution<int> 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 <random>
#include <type_traits>
#include <utility>
+#include <vector>
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<ROI> 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 <random>
+
+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<ROI> &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<ROI> 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<ROI> 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 <random>
+#include <vector>
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<ROI> &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<ROI> &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<ROI> &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<ROI> &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<T> &in, Tensor<T> &out, PoolingLayerInfo pool_in
}
}
+// Pooling layer
+template <typename T>
+void roi_pooling_layer(const Tensor<T> &in, Tensor<T> &out, const std::vector<ROI> &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<float>(roi_width) / pool_w;
+ float pool_region_size_y = static_cast<float>(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<int>(std::floor(px * pool_region_size_x));
+ int region_end_x = static_cast<int>(std::ceil((px + 1) * pool_region_size_x));
+ int region_start_y = static_cast<int>(std::floor(py * pool_region_size_y));
+ int region_end_y = static_cast<int>(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<T>::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 <typename T, typename std::enable_if<is_floating_point<T>::value, int>::type * = nullptr>
void softmax_layer(const Tensor<T> &in, Tensor<T> &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<ROI> &rois, ROIPoolingLayerInfo pool_info)
+ : _in(in), _rois(rois), _pool_info(pool_info)
+ {
+ }
+
+ template <typename T>
+ void operator()(Tensor<T> &out) const
+ {
+ const Tensor<T> &in = boost::get<Tensor<T>>(_in);
+ tensor_operations::roi_pooling_layer(in, out, _rois, _pool_info);
+ }
+
+private:
+ const TensorVariant &_in;
+ const std::vector<ROI> &_rois;
+ ROIPoolingLayerInfo _pool_info;
+};
+
// Softmax Layer visitor
struct softmax_layer_visitor : public boost::static_visitor<>
{