aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormorgolock <pablo.tello@arm.com>2020-04-09 14:17:48 +0100
committerPablo Marquez <pablo.tello@arm.com>2020-06-15 14:04:49 +0000
commit37722d9a81627520fa347eb65199dbfeb84b26bd (patch)
tree3cb811c83e933337e685606625fcd44690b570d7
parent4a61653202afb018f4f259d3c144a735d73f0a20 (diff)
downloadComputeLibrary-37722d9a81627520fa347eb65199dbfeb84b26bd.tar.gz
COMPMID-2449: Implement NEUnPoolLayer
Change-Id: I5677c87bba97dd395a3e13dbce34a3dd2c437033 Signed-off-by: morgolock <pablo.tello@arm.com> Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3289 Tested-by: Arm Jenkins <bsgcomp@arm.com> Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com> Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
-rw-r--r--Android.bp2
-rw-r--r--arm_compute/core/NEON/NEKernels.h1
-rw-r--r--arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h97
-rw-r--r--arm_compute/core/utils/misc/ShapeCalculator.h31
-rw-r--r--arm_compute/runtime/NEON/NEFunctions.h1
-rw-r--r--arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h77
-rw-r--r--src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp146
-rw-r--r--src/core/NEON/kernels/NEPoolingLayerKernel.cpp68
-rw-r--r--src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp54
-rw-r--r--src/runtime/NEON/functions/NEPoolingLayer.cpp4
-rw-r--r--tests/validation/NEON/MaxUnpoolingLayer.cpp82
-rw-r--r--tests/validation/fixtures/MaxUnpoolingLayerFixture.h159
-rw-r--r--tests/validation/fixtures/PoolingLayerFixture.h1
-rw-r--r--tests/validation/reference/MaxUnpoolingLayer.cpp106
-rw-r--r--tests/validation/reference/MaxUnpoolingLayer.h46
-rw-r--r--tests/validation/reference/PoolingLayer.cpp78
16 files changed, 892 insertions, 61 deletions
diff --git a/Android.bp b/Android.bp
index 59fb270d0d..b484a1e909 100644
--- a/Android.bp
+++ b/Android.bp
@@ -309,6 +309,7 @@ cc_library_static {
"src/core/NEON/kernels/NELKTrackerKernel.cpp",
"src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp",
"src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp",
+ "src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp",
"src/core/NEON/kernels/NEMeanStdDevKernel.cpp",
"src/core/NEON/kernels/NEMeanStdDevNormalizationKernel.cpp",
"src/core/NEON/kernels/NEMedian3x3Kernel.cpp",
@@ -685,6 +686,7 @@ cc_library_static {
"src/runtime/NEON/functions/NELaplacianReconstruct.cpp",
"src/runtime/NEON/functions/NELocallyConnectedLayer.cpp",
"src/runtime/NEON/functions/NEMagnitude.cpp",
+ "src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp",
"src/runtime/NEON/functions/NEMeanStdDev.cpp",
"src/runtime/NEON/functions/NEMeanStdDevNormalizationLayer.cpp",
"src/runtime/NEON/functions/NEMedian3x3.cpp",
diff --git a/arm_compute/core/NEON/NEKernels.h b/arm_compute/core/NEON/NEKernels.h
index 38701f434a..dfe0ccaafc 100644
--- a/arm_compute/core/NEON/NEKernels.h
+++ b/arm_compute/core/NEON/NEKernels.h
@@ -106,6 +106,7 @@
#include "arm_compute/core/NEON/kernels/NELKTrackerKernel.h"
#include "arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h"
#include "arm_compute/core/NEON/kernels/NEMagnitudePhaseKernel.h"
+#include "arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h"
#include "arm_compute/core/NEON/kernels/NEMeanStdDevKernel.h"
#include "arm_compute/core/NEON/kernels/NEMeanStdDevNormalizationKernel.h"
#include "arm_compute/core/NEON/kernels/NEMedian3x3Kernel.h"
diff --git a/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h b/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h
new file mode 100644
index 0000000000..269317b6c1
--- /dev/null
+++ b/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020 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_NEMAXUNPOOLINGLAYERKERNEL_H
+#define ARM_COMPUTE_NEMAXUNPOOLINGLAYERKERNEL_H
+
+#include "arm_compute/core/NEON/INEKernel.h"
+
+namespace arm_compute
+{
+class ITensor;
+
+/** Interface for the pooling layer kernel */
+class NEMaxUnpoolingLayerKernel : public INEKernel
+{
+public:
+ const char *name() const override
+ {
+ return "NEMaxUnpoolingLayerKernel";
+ }
+ /** Default constructor */
+ NEMaxUnpoolingLayerKernel();
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ NEMaxUnpoolingLayerKernel(const NEMaxUnpoolingLayerKernel &) = delete;
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ NEMaxUnpoolingLayerKernel &operator=(const NEMaxUnpoolingLayerKernel &) = delete;
+ /** Allow instances of this class to be moved */
+ NEMaxUnpoolingLayerKernel(NEMaxUnpoolingLayerKernel &&) = default;
+ /** Allow instances of this class to be moved */
+ NEMaxUnpoolingLayerKernel &operator=(NEMaxUnpoolingLayerKernel &&) = default;
+ /** Default destructor */
+ ~NEMaxUnpoolingLayerKernel() = default;
+ /** Set the input and output tensors.
+ *
+ * @note Output shape must be equal to the shape of the original input to pool.
+ *
+ * @param[in] input Source tensor. Data types supported: F16/F32.
+ * @param[out] indices The indices of the maximal values. Data type supported: U32.
+ * @param[out] output Destination tensor. Data types supported: Same as @p input.
+ * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo.
+ */
+ void configure(const ITensor *input, const ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info);
+ /** Static function to check if given info will lead to a valid configuration of @ref NEMaxUnpoolingLayerKernel
+ *
+ * @param[in] input Source tensor. Data types supported: F16/F32.
+ * @param[in] output Destination tensor. Data types supported: Same as @p input.
+ * @param[in] indices The indices of the maximal values. Data type supported: U32.
+ * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo.
+ *
+ * @return a status
+ */
+ static Status validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info);
+
+ // Inherited methods overridden:
+ void run(const Window &window, const ThreadInfo &info) override;
+
+private:
+ /** Function to perform 2x2 pooling and compute the pooling indices. The indices can be used for max unpool.
+ *
+ * @param[in] window_input Input region on which to execute the kernel.
+ */
+ template <typename T>
+ void unpooling2(const Window &window_input);
+
+ using UnpoolingFunction = void (NEMaxUnpoolingLayerKernel::*)(const Window &window);
+
+private:
+ UnpoolingFunction _func;
+ const ITensor *_input;
+ ITensor *_output;
+ const ITensor *_indices;
+ PoolingLayerInfo _pool_info;
+ DataLayout _data_layout;
+ unsigned int _num_elems_processed_per_iteration;
+};
+} // namespace arm_compute
+#endif /*ARM_COMPUTE_NEMAXUNPOOLINGLAYERKERNEL_H */
diff --git a/arm_compute/core/utils/misc/ShapeCalculator.h b/arm_compute/core/utils/misc/ShapeCalculator.h
index dfccec8b37..bc85c6986f 100644
--- a/arm_compute/core/utils/misc/ShapeCalculator.h
+++ b/arm_compute/core/utils/misc/ShapeCalculator.h
@@ -809,6 +809,37 @@ inline TensorShape compute_pool_shape(const ITensorInfo &input, PoolingLayerInfo
return output_shape;
}
+/** Calculate the output unpool shape of a tensor
+ *
+ * @param[in] input Input tensor info
+ * @param[in] pool_info Pooling layer info
+ *
+ * @return the calculated shape
+ */
+inline TensorShape compute_unpool_shape(const ITensorInfo &input, PoolingLayerInfo pool_info)
+{
+ const unsigned int idx_width = get_data_layout_dimension_index(input.data_layout(), DataLayoutDimension::WIDTH);
+ const unsigned int idx_height = get_data_layout_dimension_index(input.data_layout(), DataLayoutDimension::HEIGHT);
+ const TensorShape input_shape = input.tensor_shape();
+ ARM_COMPUTE_ERROR_ON(input_shape[idx_height] <= 1 || input_shape[idx_width] <= 1);
+ const PadStrideInfo pad_stride_info = pool_info.pad_stride_info;
+ const unsigned int stride_x = pad_stride_info.stride().first;
+ const unsigned int stride_y = pad_stride_info.stride().second;
+
+ const int pad_left = pad_stride_info.pad_left();
+ const int pad_top = pad_stride_info.pad_top();
+ const int pad_right = pad_stride_info.pad_right();
+ const int pad_bottom = pad_stride_info.pad_bottom();
+
+ TensorShape output_shape = input_shape;
+ const unsigned int out_width = (input_shape[idx_width] - 1) * stride_x - pad_left - pad_right + pool_info.pool_size.width;
+ const unsigned int out_height = (input_shape[idx_height] - 1) * stride_y - pad_top - pad_bottom + pool_info.pool_size.height;
+
+ output_shape.set(idx_width, out_width);
+ output_shape.set(idx_height, out_height);
+ return output_shape;
+}
+
/** Calculate the output roi align shape of a tensor
*
* @param[in] input Input tensor info
diff --git a/arm_compute/runtime/NEON/NEFunctions.h b/arm_compute/runtime/NEON/NEFunctions.h
index de364fa9af..19137b8830 100644
--- a/arm_compute/runtime/NEON/NEFunctions.h
+++ b/arm_compute/runtime/NEON/NEFunctions.h
@@ -105,6 +105,7 @@
#include "arm_compute/runtime/NEON/functions/NELaplacianReconstruct.h"
#include "arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h"
#include "arm_compute/runtime/NEON/functions/NEMagnitude.h"
+#include "arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h"
#include "arm_compute/runtime/NEON/functions/NEMeanStdDev.h"
#include "arm_compute/runtime/NEON/functions/NEMeanStdDevNormalizationLayer.h"
#include "arm_compute/runtime/NEON/functions/NEMedian3x3.h"
diff --git a/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h b/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h
new file mode 100644
index 0000000000..f93e4caf93
--- /dev/null
+++ b/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 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_NEMAXUNPOOLINGLAYER_H
+#define ARM_COMPUTE_NEMAXUNPOOLINGLAYER_H
+
+#include "arm_compute/runtime/IFunction.h"
+
+#include "arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h"
+#include "arm_compute/core/NEON/kernels/NEMemsetKernel.h"
+
+namespace arm_compute
+{
+class ITensor;
+
+/** Function to perform MaxUnpooling. This function calls the following NEON kernels:
+ *
+ * -# @ref NEMemsetKernel
+ * -# @ref NEMaxUnpoolingLayerKernel
+ */
+class NEMaxUnpoolingLayer : public IFunction
+{
+public:
+ /** Constructor */
+ NEMaxUnpoolingLayer();
+ /** Set the input and output tensors.
+ *
+ * @note F16 is supported for pool sizes 2 and 3 only
+ *
+ * @param[in, out] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32.
+ * @param[out] output Destination tensor. Data types supported: Same as @p input.
+ * @param[out] indices The indices of the maximal values. Data type supported: U32.
+ * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo.
+ */
+ void configure(ITensor *input, ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info);
+ /** Static function to check if given info will lead to a valid configuration of @ref NEMaxUnpoolingLayer
+ *
+ * @note F16 is supported for pool sizes 2 and 3 only
+ *
+ * @param[in] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32.
+ * @param[in] indices The indices of the maximal values. Data type supported: U32.
+ * @param[in] output Destination tensor. Data types supported: Same as @p input.
+ * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo.
+ *
+ * @return a status
+ */
+ static Status validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info);
+
+ // Inherited methods overridden:
+ void run() override;
+
+private:
+ NEMemsetKernel _memset_kernel;
+ NEMaxUnpoolingLayerKernel _unpooling_layer_kernel;
+};
+}
+#endif /* ARM_COMPUTE_NEMAXUNPOOLINGLAYER_H */
diff --git a/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp b/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp
new file mode 100644
index 0000000000..1967c553bd
--- /dev/null
+++ b/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020 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/NEMaxUnpoolingLayerKernel.h"
+
+#include "arm_compute/core/AccessWindowStatic.h"
+#include "arm_compute/core/CPP/Validate.h"
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/ITensor.h"
+#include "arm_compute/core/NEON/NEAsymm.h"
+#include "arm_compute/core/NEON/NEFixedPoint.h"
+#include "arm_compute/core/NEON/NEMath.h"
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/core/Validate.h"
+#include "arm_compute/core/Window.h"
+#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+
+#include "support/ToolchainSupport.h"
+
+namespace arm_compute
+{
+using namespace misc::shape_calculator;
+
+namespace
+{
+Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, const PoolingLayerInfo &pool_info, const ITensorInfo *indices)
+{
+ ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output, indices);
+ int pool_stride_x = 0;
+ int pool_stride_y = 0;
+ PoolingType pool_type = pool_info.pool_type;
+ const PadStrideInfo pad_stride_info = pool_info.pad_stride_info;
+ std::tie(pool_stride_x, pool_stride_y) = pad_stride_info.stride();
+ const int pool_size_x = pool_info.pool_size.width;
+ const int pool_size_y = pool_info.pool_size.height;
+ const Size2D pool_size(pool_size_x, pool_size_y);
+ ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input);
+ ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(indices, 1, DataType::U32);
+ ARM_COMPUTE_RETURN_ERROR_ON_MSG(pool_type != PoolingType::MAX, "Pooling indices only supported for MAX pooling method");
+ ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32);
+ ARM_COMPUTE_RETURN_ERROR_ON_MSG((pool_size != Size2D(2, 2)), "Pooling indices only supported for pool size 2x2");
+ if(output->total_size() != 0)
+ {
+ ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
+ ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_LAYOUT(input, output);
+ }
+
+ return Status{};
+}
+} // namespace
+
+NEMaxUnpoolingLayerKernel::NEMaxUnpoolingLayerKernel()
+ : _func(nullptr), _input(nullptr), _output(nullptr), _indices(nullptr), _pool_info(), _data_layout(DataLayout::UNKNOWN), _num_elems_processed_per_iteration(0)
+{
+}
+
+void NEMaxUnpoolingLayerKernel::configure(const ITensor *input, const ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info)
+{
+ ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
+ const Size2D pool_size(pool_info.pool_size.width, pool_info.pool_size.height);
+ ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info(), pool_info, indices->info()));
+ _input = input;
+ _output = output;
+ _indices = indices;
+ _pool_info = pool_info;
+ _data_layout = input->info()->data_layout();
+ switch(input->info()->data_type())
+ {
+ case DataType::F32:
+ _func = &NEMaxUnpoolingLayerKernel::unpooling2<float>;
+ break;
+ case DataType::QASYMM8:
+ _func = &NEMaxUnpoolingLayerKernel::unpooling2<uint8_t>;
+ break;
+ case DataType::QASYMM8_SIGNED:
+ _func = &NEMaxUnpoolingLayerKernel::unpooling2<int8_t>;
+ break;
+#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC
+ case DataType::F16:
+ _func = &NEMaxUnpoolingLayerKernel::unpooling2<float16_t>;
+ break;
+#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */
+ default:
+ break;
+ }
+ const TensorShape output_shape = compute_unpool_shape(*input->info(), pool_info);
+ auto_init_if_empty(*output->info(), input->info()->clone()->set_tensor_shape(output_shape));
+ _num_elems_processed_per_iteration = 1;
+ auto window = calculate_max_window(*input->info(), Steps(_num_elems_processed_per_iteration));
+ INEKernel::configure(window);
+}
+template <typename T>
+void NEMaxUnpoolingLayerKernel::unpooling2(const Window &window)
+{
+ Iterator input(_input, window);
+ Iterator indices(_indices, window);
+ auto out_ptr = reinterpret_cast<T *>(_output->buffer());
+ const int out_stride_w = static_cast<int>(_output->info()->strides_in_bytes()[3]);
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ auto vindices = reinterpret_cast<uint32_t *>(indices.ptr());
+ auto vinput = reinterpret_cast<T *>(input.ptr());
+ out_ptr[id[3] * out_stride_w / sizeof(T) + *vindices] = *vinput;
+ },
+ input, indices);
+}
+
+Status NEMaxUnpoolingLayerKernel::validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info)
+{
+ ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, indices, output);
+ ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output, pool_info, indices));
+ return Status{};
+}
+
+void NEMaxUnpoolingLayerKernel::run(const Window &window, const ThreadInfo &info)
+{
+ ARM_COMPUTE_UNUSED(info);
+ ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
+ ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
+ ARM_COMPUTE_ERROR_ON(_func == nullptr);
+ // Run function
+ (this->*_func)(window);
+}
+} // namespace arm_compute
diff --git a/src/core/NEON/kernels/NEPoolingLayerKernel.cpp b/src/core/NEON/kernels/NEPoolingLayerKernel.cpp
index 6d61f51f31..d6b17534d3 100644
--- a/src/core/NEON/kernels/NEPoolingLayerKernel.cpp
+++ b/src/core/NEON/kernels/NEPoolingLayerKernel.cpp
@@ -36,7 +36,6 @@
#include "arm_compute/core/Validate.h"
#include "arm_compute/core/Window.h"
#include "arm_compute/core/utils/misc/ShapeCalculator.h"
-
#include "support/ToolchainSupport.h"
#include "arm_compute/core/NEON/wrapper/wrapper.h"
@@ -137,6 +136,7 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, c
ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input);
if(indices)
{
+ ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32, DataType::F16);
ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(indices, 1, DataType::U32);
ARM_COMPUTE_RETURN_ERROR_ON_MSG(pool_type != PoolingType::MAX, "Pooling indices only supported for MAX pooling method");
}
@@ -156,7 +156,6 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, c
if(indices)
{
ARM_COMPUTE_RETURN_ERROR_ON_MSG((pool_size != Size2D(2, 2)), "Pooling indices only supported for pool size 2x2");
-
ARM_COMPUTE_RETURN_ERROR_ON((indices->dimension(get_data_layout_dimension_index(indices->data_layout(), DataLayoutDimension::WIDTH)) != pooled_w)
|| (indices->dimension(get_data_layout_dimension_index(indices->data_layout(), DataLayoutDimension::HEIGHT)) != pooled_h));
}
@@ -1489,6 +1488,39 @@ void NEPoolingLayerKernel::poolingMxN_f32_nchw(const Window &window_input, const
input, output);
}
+inline uint32_t offset_no_padding(uint32_t padded_offset, const Coordinates &id, const ITensorInfo &info, int pool_stride_x, int pool_stride_y)
+{
+ const int pad_left = info.padding().left;
+ const int pad_right = info.padding().right;
+ const int pad_top = info.padding().top;
+ const int pad_bottom = info.padding().bottom;
+ const int in_stride_y = static_cast<int>(info.strides_in_bytes().y());
+ const int in_stride_w = static_cast<int>(info.strides_in_bytes()[3]);
+ const int pad_horiz = pad_left + pad_right;
+ const int pad_vert = pad_top + pad_bottom;
+
+ if(info.data_layout() == DataLayout::NCHW)
+ {
+ const uint32_t offset_base = padded_offset
+ - sizeof(float) * pad_horiz * id.y() * pool_stride_y /* subtract padding elems per row */
+ - pad_top * sizeof(float) /* top padding */
+ - sizeof(float) * pad_horiz * info.tensor_shape()[1] * id.z() - pad_vert * in_stride_y * id.z() /* for each Z plane there are height*pad_right padding elems */
+ - in_stride_w * id[3];
+
+ return offset_base;
+ }
+ else
+ {
+ const uint32_t offset_base = padded_offset
+ - sizeof(float) * pad_horiz * id.y() * pool_stride_x // subtract padding elems per row
+ - pad_top * sizeof(float) // top padding
+ - sizeof(float) * pad_horiz * info.tensor_shape()[1] * id.z() * pool_stride_y // for each Z plane there are width*pad_right padding elems
+ - in_stride_w * id[3];
+
+ return offset_base;
+ }
+}
+
void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &window_input, const Window &window)
{
Iterator input(_input, window_input);
@@ -1502,11 +1534,10 @@ void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &windo
std::tie(pool_stride_x, pool_stride_y) = _pool_info.pad_stride_info.stride();
const uint8_t *const input_top_ptr = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_left), -static_cast<int>(pool_pad_top)));
const uint8_t *const input_bottom_ptr = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_left), -static_cast<int>(pool_pad_top) + 1));
-
- const Strides &input_strides = _input->info()->strides_in_bytes();
- const auto in_stridew = input_strides[1];
-
- execute_window_loop(window, [&](const Coordinates &)
+ const int pad_left = _input->info()->padding().left;
+ const int pad_right = _input->info()->padding().right;
+ const int in_stride_y = static_cast<int>(_input->info()->strides_in_bytes().y());
+ execute_window_loop(window, [&](const Coordinates & id)
{
const auto input_offset_top = input_top_ptr + input.offset();
const auto input_offset_bottom = input_bottom_ptr + input.offset();
@@ -1521,8 +1552,9 @@ void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &windo
final_res = vget_lane_f32(res, 0);
// Store result
*(reinterpret_cast<float *>(output.ptr())) = final_res;
- const uint32_t offset_top = (uint32_t)(input.offset() / sizeof(float));
- const uint32_t offset_bottom = (uint32_t)offset_top + (in_stridew / sizeof(float));
+ const uint32_t offset_base = offset_no_padding(input.offset(), id, *_input->info(), pool_stride_x, pool_stride_y);
+ const uint32_t offset_top = (uint32_t)(offset_base / sizeof(float));
+ const uint32_t offset_bottom = offset_top + in_stride_y / sizeof(float) - pad_right - pad_left;
const uint32x2_t voffset_top = { offset_top, offset_top + 1u };
const uint32x2_t voffset_bottom = { offset_bottom, offset_bottom + 1u };
const uint32x2_t tmp_indices = vbsl_u32(vcgt_f32(top_data, bottom_data), voffset_top, voffset_bottom);
@@ -1867,10 +1899,8 @@ void NEPoolingLayerKernel::pooling2_f32_nhwc_maxpool_indices(const Window &windo
float32x4_t vres;
const int pad_right = _input->info()->padding().right;
- const int pad_top = _input->info()->padding().top;
const int in_stride_y = static_cast<int>(_input->info()->strides_in_bytes().y());
const int in_stride_z = static_cast<int>(_input->info()->strides_in_bytes().z());
- const int in_stride_w = static_cast<int>(_input->info()->strides_in_bytes()[3]);
execute_window_loop(window, [&](const Coordinates & id)
{
@@ -1904,17 +1934,11 @@ void NEPoolingLayerKernel::pooling2_f32_nhwc_maxpool_indices(const Window &windo
// Store result
vst1q_f32(reinterpret_cast<float *>(output.ptr()), vres);
- const uint32_t offset_base = input.offset()
- - sizeof(float) * pad_right * id.y() * pool_stride_x /* subtract padding elems per row */
- - pad_top * sizeof(float) /* top padding */
- - sizeof(float) * pad_right * _input->info()->tensor_shape()[1] * id.z() * pool_stride_y /* for each Z plane there are width*pad_right padding elems */
- - in_stride_w * id[3] + _input->info()->tensor_shape()[0] * sizeof(float) * id[3];
-
- const uint32_t offset_x0 = (uint32_t)offset_base / sizeof(float);
- const uint32_t offset_x1 = (uint32_t)offset_x0 + in_stride_y / sizeof(float) - pad_right;
- const uint32_t offset_x2 = (uint32_t)offset_x0 + in_stride_z / sizeof(float) - pad_right * _input->info()->tensor_shape()[1];
- const uint32_t offset_x3 = (uint32_t)offset_x2 + in_stride_y / sizeof(float) - pad_right;
-
+ const uint32_t offset_base = offset_no_padding(input.offset(), id, *_input->info(), pool_stride_x, pool_stride_y);
+ const uint32_t offset_x0 = (uint32_t)offset_base / sizeof(float);
+ const uint32_t offset_x1 = (uint32_t)offset_x0 + in_stride_y / sizeof(float) - pad_right;
+ const uint32_t offset_x2 = (uint32_t)offset_x0 + in_stride_z / sizeof(float) - pad_right * _input->info()->tensor_shape()[1];
+ const uint32_t offset_x3 = (uint32_t)offset_x2 + in_stride_y / sizeof(float) - pad_right;
const uint32x4_t voffset_x0 = { offset_x0, offset_x0 + 1, offset_x0 + 2, offset_x0 + 3 };
const uint32x4_t voffset_x1 = { offset_x1, offset_x1 + 1, offset_x1 + 2, offset_x1 + 3 };
const uint32x4_t voffset_x2 = { offset_x2, offset_x2 + 1, offset_x2 + 2, offset_x2 + 3 };
diff --git a/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp b/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp
new file mode 100644
index 0000000000..e606370b0a
--- /dev/null
+++ b/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 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/NEMaxUnpoolingLayer.h"
+
+#include "arm_compute/core/ITensor.h"
+#include "arm_compute/runtime/NEON/NEScheduler.h"
+
+namespace arm_compute
+{
+NEMaxUnpoolingLayer::NEMaxUnpoolingLayer()
+
+ : _memset_kernel(), _unpooling_layer_kernel()
+{
+}
+
+void NEMaxUnpoolingLayer::configure(ITensor *input, ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info)
+{
+ const PixelValue zero_value(0.f);
+ _memset_kernel.configure(output, zero_value);
+ _unpooling_layer_kernel.configure(input, indices, output, pool_info);
+}
+
+Status NEMaxUnpoolingLayer::validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info)
+{
+ return NEMaxUnpoolingLayerKernel::validate(input, indices, output, pool_info);
+}
+
+void NEMaxUnpoolingLayer::run()
+{
+ NEScheduler::get().schedule(&_memset_kernel, Window::DimY);
+ NEScheduler::get().schedule(&_unpooling_layer_kernel, Window::DimY);
+}
+} /* namespace arm_compute */
diff --git a/src/runtime/NEON/functions/NEPoolingLayer.cpp b/src/runtime/NEON/functions/NEPoolingLayer.cpp
index 12921cf40e..fb1b04ab6f 100644
--- a/src/runtime/NEON/functions/NEPoolingLayer.cpp
+++ b/src/runtime/NEON/functions/NEPoolingLayer.cpp
@@ -49,8 +49,8 @@ void NEPoolingLayer::configure(ITensor *input, ITensor *output, const PoolingLay
case DataLayout::NCHW:
{
// Configure border depending on operation required (quantize border in case of asymmetric data_type)
- BorderMode border_mode = (pool_info.pool_type == PoolingType::MAX) ? BorderMode::REPLICATE : BorderMode::CONSTANT;
- PixelValue zero_value(0.f);
+ BorderMode border_mode = (!indices && pool_info.pool_type == PoolingType::MAX) ? BorderMode::REPLICATE : BorderMode::CONSTANT;
+ PixelValue zero_value((indices) ? std::numeric_limits<int>::min() : 0.f);
if(is_data_type_quantized_asymmetric(input->info()->data_type()) && !pool_info.exclude_padding)
{
zero_value = PixelValue(0, input->info()->data_type(), input->info()->quantization_info());
diff --git a/tests/validation/NEON/MaxUnpoolingLayer.cpp b/tests/validation/NEON/MaxUnpoolingLayer.cpp
new file mode 100644
index 0000000000..949d569c89
--- /dev/null
+++ b/tests/validation/NEON/MaxUnpoolingLayer.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2020 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/Types.h"
+#include "arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h"
+#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/runtime/TensorAllocator.h"
+#include "tests/NEON/Accessor.h"
+#include "tests/datasets/ShapeDatasets.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Macros.h"
+#include "tests/framework/datasets/Datasets.h"
+#include "tests/validation/Validation.h"
+#include "tests/validation/fixtures/MaxUnpoolingLayerFixture.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+TEST_SUITE(NEON)
+TEST_SUITE(PoolingLayer)
+
+template <typename T>
+using NEMaxUnpoolingLayerFixture = MaxUnpoolingLayerValidationFixture<Tensor, Accessor, NEPoolingLayer, NEMaxUnpoolingLayer, T>;
+
+const auto PoolingLayerIndicesDatasetFPSmall = combine(combine(framework::dataset::make("PoolType", { PoolingType::MAX }), framework::dataset::make("PoolingSize", { Size2D(2, 2) })),
+ framework::dataset::make("PadStride", { PadStrideInfo(2, 2, 0, 0), PadStrideInfo(2, 1, 0, 0) }));
+
+TEST_SUITE(Float)
+TEST_SUITE(FP32)
+FIXTURE_DATA_TEST_CASE(MaxUnpooling, NEMaxUnpoolingLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), combine(PoolingLayerIndicesDatasetFPSmall,
+ framework::dataset::make("DataType", DataType::F32))),
+ framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })
+
+ ))
+{
+ // Validate output
+ validate(Accessor(_target), _reference);
+}
+TEST_SUITE_END() // FP32
+#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC
+TEST_SUITE(FP16)
+FIXTURE_DATA_TEST_CASE(MaxUnpooling, NEMaxUnpoolingLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), combine(PoolingLayerIndicesDatasetFPSmall,
+ framework::dataset::make("DataType", DataType::F16))),
+ framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })
+
+ ))
+{
+ // Validate output
+ validate(Accessor(_target), _reference);
+}
+TEST_SUITE_END() // FP16
+#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */
+TEST_SUITE_END() // Float
+TEST_SUITE_END() // PoolingLayer
+TEST_SUITE_END() // NEON
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/fixtures/MaxUnpoolingLayerFixture.h b/tests/validation/fixtures/MaxUnpoolingLayerFixture.h
new file mode 100644
index 0000000000..ee08f59e7e
--- /dev/null
+++ b/tests/validation/fixtures/MaxUnpoolingLayerFixture.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef ARM_COMPUTE_TEST_POOLING_LAYER_FIXTURE
+#define ARM_COMPUTE_TEST_POOLING_LAYER_FIXTURE
+
+#include "arm_compute/core/TensorShape.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "tests/AssetsLibrary.h"
+#include "tests/Globals.h"
+#include "tests/IAccessor.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Fixture.h"
+#include "tests/validation/reference/MaxUnpoolingLayer.h"
+#include "tests/validation/reference/PoolingLayer.h"
+#include <random>
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+template <typename TensorType, typename AccessorType, typename PoolingFunctionType, typename MaxUnpoolingFunctionType, typename T>
+class MaxUnpoolingLayerValidationGenericFixture : public framework::Fixture
+{
+public:
+ template <typename...>
+ void setup(TensorShape shape, PoolingLayerInfo pool_info, DataType data_type, DataLayout data_layout)
+ {
+ std::mt19937 gen(library->seed());
+ std::uniform_int_distribution<> offset_dis(0, 20);
+ const float scale = data_type == DataType::QASYMM8_SIGNED ? 1.f / 127.f : 1.f / 255.f;
+ const int scale_in = data_type == DataType::QASYMM8_SIGNED ? -offset_dis(gen) : offset_dis(gen);
+ const int scale_out = data_type == DataType::QASYMM8_SIGNED ? -offset_dis(gen) : offset_dis(gen);
+ const QuantizationInfo input_qinfo(scale, scale_in);
+ const QuantizationInfo output_qinfo(scale, scale_out);
+ _pool_info = pool_info;
+ _target = compute_target(shape, pool_info, data_type, data_layout, input_qinfo, output_qinfo);
+ _reference = compute_reference(shape, pool_info, data_type, input_qinfo, output_qinfo);
+ }
+
+protected:
+ template <typename U>
+ void fill(U &&tensor)
+ {
+ if(!is_data_type_quantized(tensor.data_type()))
+ {
+ std::uniform_real_distribution<> distribution(-1.f, 1.f);
+ library->fill(tensor, distribution, 0);
+ }
+ else // data type is quantized_asymmetric
+ {
+ library->fill_tensor_uniform(tensor, 0);
+ }
+ }
+
+ TensorType compute_target(TensorShape input_shape, PoolingLayerInfo pool_info,
+ DataType data_type, DataLayout data_layout,
+ QuantizationInfo input_qinfo, QuantizationInfo output_qinfo)
+ {
+ // Change shape in case of NHWC.
+ if(data_layout == DataLayout::NHWC)
+ {
+ permute(input_shape, PermutationVector(2U, 0U, 1U));
+ }
+
+ // Create tensors
+ TensorType src = create_tensor<TensorType>(input_shape, data_type, 1, input_qinfo, data_layout);
+ const TensorShape dst_shape = misc::shape_calculator::compute_pool_shape(*(src.info()), pool_info);
+ TensorType dst = create_tensor<TensorType>(dst_shape, data_type, 1, output_qinfo, data_layout);
+ TensorType unpooled = create_tensor<TensorType>(input_shape, data_type, 1, output_qinfo, data_layout);
+ TensorType indices = create_tensor<TensorType>(dst_shape, DataType::U32, 1, output_qinfo, data_layout);
+
+ // Create and configure function
+ PoolingFunctionType pool_layer;
+ pool_layer.configure(&src, &dst, pool_info, &indices);
+ // Create and configure function
+
+ MaxUnpoolingFunctionType unpool_layer;
+ unpool_layer.configure(&dst, &indices, &unpooled, pool_info);
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+ ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS);
+ ARM_COMPUTE_EXPECT(indices.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Allocate tensors
+ src.allocator()->allocate();
+ dst.allocator()->allocate();
+ indices.allocator()->allocate();
+ unpooled.allocator()->allocate();
+
+ ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS);
+ ARM_COMPUTE_EXPECT(!dst.info()->is_resizable(), framework::LogLevel::ERRORS);
+ ARM_COMPUTE_EXPECT(!indices.info()->is_resizable(), framework::LogLevel::ERRORS);
+ ARM_COMPUTE_EXPECT(!unpooled.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ // Fill tensors
+ fill(AccessorType(src));
+
+ // Compute function
+ pool_layer.run();
+ unpool_layer.run();
+ return unpooled;
+ }
+
+ SimpleTensor<T> compute_reference(TensorShape input_shape, PoolingLayerInfo info, DataType data_type,
+ QuantizationInfo input_qinfo, QuantizationInfo output_qinfo)
+ {
+ SimpleTensor<T> src(input_shape, data_type, 1, input_qinfo);
+ SimpleTensor<uint32_t> indices{};
+ // Fill reference
+ fill(src);
+ auto pooled_tensor = reference::pooling_layer<T>(src, info, output_qinfo, &indices);
+ return reference::max_unpooling_layer<T>(pooled_tensor, info, output_qinfo, indices, input_shape);
+ }
+
+ TensorType _target{};
+ SimpleTensor<T> _reference{};
+ PoolingLayerInfo _pool_info{};
+};
+
+template <typename TensorType, typename AccessorType, typename F1, typename F2, typename T>
+class MaxUnpoolingLayerValidationFixture : public MaxUnpoolingLayerValidationGenericFixture<TensorType, AccessorType, F1, F2, T>
+{
+public:
+ template <typename...>
+ void setup(TensorShape shape, PoolingType pool_type, Size2D pool_size, PadStrideInfo pad_stride_info, DataType data_type, DataLayout data_layout)
+ {
+ MaxUnpoolingLayerValidationGenericFixture<TensorType, AccessorType, F1, F2, T>::setup(shape, PoolingLayerInfo(pool_type, pool_size, data_layout, pad_stride_info, true),
+ data_type, data_layout);
+ }
+};
+
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* ARM_COMPUTE_TEST_POOLING_LAYER_FIXTURE */
diff --git a/tests/validation/fixtures/PoolingLayerFixture.h b/tests/validation/fixtures/PoolingLayerFixture.h
index eb40cea0c2..b9b5b3857b 100644
--- a/tests/validation/fixtures/PoolingLayerFixture.h
+++ b/tests/validation/fixtures/PoolingLayerFixture.h
@@ -86,7 +86,6 @@ protected:
{
permute(shape, PermutationVector(2U, 0U, 1U));
}
-
// Create tensors
TensorType src = create_tensor<TensorType>(shape, data_type, 1, input_qinfo, data_layout);
const TensorShape dst_shape = misc::shape_calculator::compute_pool_shape(*(src.info()), info);
diff --git a/tests/validation/reference/MaxUnpoolingLayer.cpp b/tests/validation/reference/MaxUnpoolingLayer.cpp
new file mode 100644
index 0000000000..d74a930856
--- /dev/null
+++ b/tests/validation/reference/MaxUnpoolingLayer.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020 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 "MaxUnpoolingLayer.h"
+
+#include "arm_compute/core/Types.h"
+#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+#include "tests/validation/Helpers.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+using namespace arm_compute::misc::shape_calculator;
+
+template <typename T>
+SimpleTensor<T> max_unpooling_layer_internal(const SimpleTensor<T> &src, const PoolingLayerInfo &info,
+ const QuantizationInfo &output_qinfo, SimpleTensor<uint32_t> &indices,
+ TensorShape output_shape, DataLayout data_layout)
+{
+ ARM_COMPUTE_UNUSED(info);
+ ARM_COMPUTE_UNUSED(output_qinfo);
+ ARM_COMPUTE_UNUSED(data_layout);
+ // Create reference
+ SimpleTensor<T> dst{ output_shape, src.data_type(), 1 };
+ ARM_COMPUTE_ERROR_ON(indices.shape().total_size() == 0);
+ std::fill_n(dst.data(), dst.num_elements(), 0);
+ const auto w_indices = static_cast<int>(indices.shape()[0]);
+ const auto h_indices = static_cast<int>(indices.shape()[1]);
+ const auto z_indices = static_cast<int>(indices.shape()[2]);
+ const auto b_indices = static_cast<int>(indices.shape()[3]);
+ const auto w_dst = static_cast<int>(dst.shape()[0]);
+ const auto h_dst = static_cast<int>(dst.shape()[1]);
+ const auto z_dst = static_cast<int>(dst.shape()[2]);
+ for(int b = 0; b < b_indices; ++b)
+ {
+ for(int r = 0; r < z_indices; ++r)
+ {
+ for(int h = 0; h < h_indices; ++h)
+ {
+ for(int w = 0; w < w_indices; ++w)
+ {
+ const uint32_t index_into_dst = indices[b * z_indices * h_indices * w_indices + r * h_indices * w_indices + h * w_indices + w];
+ const auto input_val = src[b * z_indices * h_indices * w_indices + r * h_indices * w_indices + h * w_indices + w];
+ auto *ptr = &dst[b * z_dst * h_dst * w_dst];
+ ptr[index_into_dst] = input_val;
+ }
+ }
+ }
+ }
+ return dst;
+}
+
+template <>
+SimpleTensor<uint8_t> max_unpooling_layer<uint8_t>(
+ const SimpleTensor<uint8_t> &src, const PoolingLayerInfo &info,
+ const QuantizationInfo &output_qinfo, SimpleTensor<uint32_t> &indices,
+ TensorShape output_shape, DataLayout data_layout)
+
+{
+ SimpleTensor<float> src_tmp = convert_from_asymmetric(src);
+ SimpleTensor<float> dst_tmp = max_unpooling_layer_internal<float>(src_tmp, info, output_qinfo, indices, output_shape, data_layout);
+ SimpleTensor<uint8_t> dst = convert_to_asymmetric<uint8_t>(dst_tmp, output_qinfo);
+ return dst;
+}
+
+template <typename T>
+SimpleTensor<T> max_unpooling_layer(const SimpleTensor<T> &src, const PoolingLayerInfo &info,
+ const QuantizationInfo &output_qinfo, SimpleTensor<uint32_t> &indices,
+ TensorShape output_shape, DataLayout data_layout)
+{
+ return max_unpooling_layer_internal<T>(src, info, output_qinfo, indices, output_shape, data_layout);
+}
+
+template SimpleTensor<float> max_unpooling_layer(const SimpleTensor<float> &src, const PoolingLayerInfo &info,
+ const QuantizationInfo &output_qinfo, SimpleTensor<uint32_t> &indices,
+ TensorShape output_shape, DataLayout data_layout);
+
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/reference/MaxUnpoolingLayer.h b/tests/validation/reference/MaxUnpoolingLayer.h
new file mode 100644
index 0000000000..b594265099
--- /dev/null
+++ b/tests/validation/reference/MaxUnpoolingLayer.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef ARM_COMPUTE_TEST_MAXUNPOOLING_LAYER_H
+#define ARM_COMPUTE_TEST_MAXUNPOOLING_LAYER_H
+
+#include "tests/SimpleTensor.h"
+#include "tests/validation/Helpers.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+SimpleTensor<T> max_unpooling_layer(const SimpleTensor<T> &src, const PoolingLayerInfo &info, const QuantizationInfo &output_qinfo, SimpleTensor<uint32_t> &indices,
+ TensorShape output_shape, DataLayout data_layout = DataLayout::NCHW);
+
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* ARM_COMPUTE_TEST_MAXUNPOOLING_LAYER_H */
diff --git a/tests/validation/reference/PoolingLayer.cpp b/tests/validation/reference/PoolingLayer.cpp
index 778e28d7c1..c110a67842 100644
--- a/tests/validation/reference/PoolingLayer.cpp
+++ b/tests/validation/reference/PoolingLayer.cpp
@@ -43,9 +43,10 @@ SimpleTensor<T> pooling_layer_internal(const SimpleTensor<T> &src, const Pooling
ARM_COMPUTE_ERROR_ON(info.is_global_pooling && (src.shape().x() != src.shape().y()));
// Create reference
SimpleTensor<T> dst{ compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info), src.data_type(), 1 };
+ auto pooled_shape = compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info);
if(indices)
{
- *indices = SimpleTensor<uint32_t> { compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info), DataType::U32, 1 };
+ *indices = SimpleTensor<uint32_t> { pooled_shape, DataType::U32, 1 };
}
const int pool_size_x = info.is_global_pooling ? src.shape().x() : info.pool_size.width;
const int pool_size_y = info.is_global_pooling ? src.shape().y() : info.pool_size.height;
@@ -58,56 +59,62 @@ SimpleTensor<T> pooling_layer_internal(const SimpleTensor<T> &src, const Pooling
int pad_bottom = info.pad_stride_info.pad_bottom();
bool exclude_padding = info.exclude_padding;
- const auto w_src = static_cast<int>(src.shape()[0]);
- const auto h_src = static_cast<int>(src.shape()[1]);
- const int upper_dims = src.shape().total_size() / (w_src * h_src);
+ const auto w_src = static_cast<int>(src.shape()[0]);
+ const auto h_src = static_cast<int>(src.shape()[1]);
+ const auto z_src = static_cast<int>(src.shape()[2]);
+ const auto b_src = static_cast<int>(src.shape()[3]);
+
+ const int upper_dims = src.shape().total_size() / (w_src * h_src);
+
+ const auto w_dst = static_cast<int>(dst.shape()[0]);
+ const auto h_dst = static_cast<int>(dst.shape()[1]);
+ const auto z_dst = static_cast<int>(dst.shape()[2]);
- const auto w_dst = static_cast<int>(dst.shape()[0]);
- const auto h_dst = static_cast<int>(dst.shape()[1]);
TensorShape shape_nhwc(src.shape());
permute(shape_nhwc, PermutationVector(2U, 0U, 1U));
-
if(type == PoolingType::MAX)
{
- for(int r = 0; r < upper_dims; ++r)
+ for(int b = 0; b < b_src; ++b)
{
- for(int h = 0; h < h_dst; ++h)
+ for(int r = 0; r < z_src; ++r)
{
- for(int w = 0; w < w_dst; ++w)
+ for(int h = 0; h < h_dst; ++h)
{
- int wstart = w * pool_stride_x - pad_left;
- int hstart = h * pool_stride_y - pad_top;
- int wend = std::min(wstart + pool_size_x, w_src);
- int hend = std::min(hstart + pool_size_y, h_src);
- wstart = std::max(wstart, 0);
- hstart = std::max(hstart, 0);
-
- auto max_val = std::numeric_limits<ACC_T>::lowest();
- int max_index{ 0 };
- for(int y = hstart; y < hend; ++y)
+ for(int w = 0; w < w_dst; ++w)
{
- for(int x = wstart; x < wend; ++x)
+ int wstart = w * pool_stride_x - pad_left;
+ int hstart = h * pool_stride_y - pad_top;
+ int wend = std::min(wstart + pool_size_x, w_src);
+ int hend = std::min(hstart + pool_size_y, h_src);
+ wstart = std::max(wstart, 0);
+ hstart = std::max(hstart, 0);
+ auto max_val = std::numeric_limits<ACC_T>::lowest();
+ int max_index{ 0 };
+ for(int y = hstart; y < hend; ++y)
{
- const auto val = static_cast<ACC_T>(src[r * h_src * w_src + y * w_src + x]);
- if(val > max_val)
+ for(int x = wstart; x < wend; ++x)
{
- max_val = val;
- if(data_layout == DataLayout::NCHW)
+ const auto val = static_cast<ACC_T>(src[b * z_src * h_src * w_src + r * h_src * w_src + y * w_src + x]);
+ if(val > max_val)
{
- max_index = coord2index(src.shape(), Coordinates(x, y, r));
- }
- else
- {
- max_index = coord2index(shape_nhwc, Coordinates(r, x, y));
+ max_val = val;
+ if(data_layout == DataLayout::NCHW)
+ {
+ max_index = coord2index(src.shape(), Coordinates(x, y, r, 0));
+ }
+ else
+ {
+ max_index = coord2index(shape_nhwc, Coordinates(r, x, y, 0));
+ }
}
}
}
- }
- dst[r * h_dst * w_dst + h * w_dst + w] = static_cast<T>(max_val);
- if(indices)
- {
- (*indices)[r * h_dst * w_dst + h * w_dst + w] = max_index;
+ dst[b * z_dst * h_dst * w_dst + r * h_dst * w_dst + h * w_dst + w] = static_cast<T>(max_val);
+ if(indices)
+ {
+ (*indices)[b * z_dst * h_dst * w_dst + r * h_dst * w_dst + h * w_dst + w] = max_index;
+ }
}
}
}
@@ -164,7 +171,6 @@ SimpleTensor<T> pooling_layer_internal(const SimpleTensor<T> &src, const Pooling
}
}
}
-
return dst;
}