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>
diff --git a/arm_compute/core/IArray.h b/arm_compute/core/IArray.h
index 2ed5610..8f84bb1 100644
--- a/arm_compute/core/IArray.h
+++ b/arm_compute/core/IArray.h
@@ -34,6 +34,7 @@
 class Coordinates2D;
 class DetectionWindow;
 class Size2D;
+class ROI;
 
 /** Array of type T */
 template <class T>
@@ -137,6 +138,7 @@
 };
 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 eaa50f1..ee63be3 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 0000000..3a2f761
--- /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 b7a30a5..2bd449c 100644
--- a/arm_compute/core/Types.h
+++ b/arm_compute/core/Types.h
@@ -300,6 +300,13 @@
     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 @@
     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 daf76f3..753cbc1 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 0000000..04b5c35
--- /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 fc73dca..3a308e5 100755
--- a/scripts/check_clang-tidy.py
+++ b/scripts/check_clang-tidy.py
@@ -37,6 +37,8 @@
                    ("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 0000000..ec2b6a5
--- /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 0000000..1f1400c
--- /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 c8f1c2e..e64ac27 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 @@
 
     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 0ae92f6..b5a7bb1 100644
--- a/tests/ProgramOptions.cpp
+++ b/tests/ProgramOptions.cpp
@@ -38,7 +38,7 @@
 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 d767544..f5915c4 100644
--- a/tests/TypePrinter.h
+++ b/tests/TypePrinter.h
@@ -53,6 +53,18 @@
     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 @@
     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 0000000..747260e
--- /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 c0c5865..d92699d 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 @@
     }
 }
 
+/** 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 0000000..20aaefd
--- /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 761bc20..2388fb6 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 @@
     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 41d6a60..1794ae6 100644
--- a/tests/validation/Reference.h
+++ b/tests/validation/Reference.h
@@ -331,6 +331,14 @@
       * @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 7c50f50..c89b737 100644
--- a/tests/validation/ReferenceCPP.cpp
+++ b/tests/validation/ReferenceCPP.cpp
@@ -318,6 +318,14 @@
     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 8ee8f31..9debdee 100644
--- a/tests/validation/ReferenceCPP.h
+++ b/tests/validation/ReferenceCPP.h
@@ -276,7 +276,7 @@
      * @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 @@
      * @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 4f77656..5499e58 100644
--- a/tests/validation/TensorOperations.h
+++ b/tests/validation/TensorOperations.h
@@ -1438,6 +1438,78 @@
     }
 }
 
+// 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 a274140..1b58a16 100644
--- a/tests/validation/TensorVisitors.h
+++ b/tests/validation/TensorVisitors.h
@@ -316,6 +316,29 @@
     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<>
 {