aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGian Marco <gianmarco.iodice@arm.com>2017-11-07 14:38:22 +0000
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:35:24 +0000
commit37908d9e675a240f65e038796f44691c4c530229 (patch)
tree13dde3a22fbead0067e9abf7fd06d73631e83d70
parent93dcd83caacc01eef99c550bd50a8d5393e55f1c (diff)
downloadComputeLibrary-37908d9e675a240f65e038796f44691c4c530229.tar.gz
COMPMID-560 - Validation mismatches Gaussian Pyramid Half Scale
Change-Id: If09afa444c6b3e91117d1b1a529faa0778457cd3 Reviewed-on: http://mpd-gerrit.cambridge.arm.com/96099 Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com> Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
-rw-r--r--arm_compute/core/NEON/kernels/NEGaussianPyramidKernel.h17
-rw-r--r--arm_compute/runtime/NEON/functions/NEGaussianPyramid.h3
-rw-r--r--src/core/NEON/kernels/NEGaussianPyramidKernel.cpp37
-rw-r--r--src/runtime/NEON/functions/NEGaussianPyramid.cpp25
-rw-r--r--tests/Utils.h61
-rw-r--r--tests/datasets/ShapeDatasets.h16
-rw-r--r--tests/validation/CPP/GaussianPyramidHalf.cpp66
-rw-r--r--tests/validation/CPP/GaussianPyramidHalf.h43
-rw-r--r--tests/validation/CPP/Scale.cpp21
-rw-r--r--tests/validation/CPP/Scale.h2
-rw-r--r--tests/validation/NEON/GaussianPyramid.cpp112
-rw-r--r--tests/validation/fixtures/GaussianPyramidHalfFixture.h125
12 files changed, 476 insertions, 52 deletions
diff --git a/arm_compute/core/NEON/kernels/NEGaussianPyramidKernel.h b/arm_compute/core/NEON/kernels/NEGaussianPyramidKernel.h
index 31779b520c..d28501107d 100644
--- a/arm_compute/core/NEON/kernels/NEGaussianPyramidKernel.h
+++ b/arm_compute/core/NEON/kernels/NEGaussianPyramidKernel.h
@@ -49,19 +49,17 @@ public:
/** Initialise the kernel's source, destination and border mode.
*
- * @param[in] input Source tensor. Data type supported: U8.
- * @param[out] output Destination tensor. Data type supported: S16.
- * @param[in] border_undefined True if the border mode is undefined. False if it's replicate or constant.
+ * @param[in] input Source tensor. Data type supported: U8.
+ * @param[out] output Destination tensor. Data type supported: S16.
*/
- void configure(const ITensor *input, ITensor *output, bool border_undefined);
+ void configure(const ITensor *input, ITensor *output);
// Inherited methods overridden:
void run(const Window &window, const ThreadInfo &info) override;
BorderSize border_size() const override;
private:
- BorderSize _border_size;
- int _l2_load_offset;
+ int _l2_load_offset;
};
/** NEON kernel to perform a GaussianPyramid (vertical pass) */
@@ -83,11 +81,10 @@ public:
/** Initialise the kernel's source, destination and border mode.
*
- * @param[in] input Source tensor. Data type supported: S16.
- * @param[out] output Destination tensor. Data type supported: U8.
- * @param[in] border_undefined True if the border mode is undefined. False if it's replicate or constant.
+ * @param[in] input Source tensor. Data type supported: S16.
+ * @param[out] output Destination tensor. Data type supported: U8.
*/
- void configure(const ITensor *input, ITensor *output, bool border_undefined);
+ void configure(const ITensor *input, ITensor *output);
// Inherited methods overridden:
void run(const Window &window, const ThreadInfo &info) override;
diff --git a/arm_compute/runtime/NEON/functions/NEGaussianPyramid.h b/arm_compute/runtime/NEON/functions/NEGaussianPyramid.h
index b4ed56a0c3..dbe0ecdf66 100644
--- a/arm_compute/runtime/NEON/functions/NEGaussianPyramid.h
+++ b/arm_compute/runtime/NEON/functions/NEGaussianPyramid.h
@@ -91,7 +91,8 @@ public:
void run() override;
private:
- std::unique_ptr<NEFillBorderKernel[]> _border_handler;
+ std::unique_ptr<NEFillBorderKernel[]> _horizontal_border_handler;
+ std::unique_ptr<NEFillBorderKernel[]> _vertical_border_handler;
std::unique_ptr<NEGaussianPyramidHorKernel[]> _horizontal_reduction;
std::unique_ptr<NEGaussianPyramidVertKernel[]> _vertical_reduction;
};
diff --git a/src/core/NEON/kernels/NEGaussianPyramidKernel.cpp b/src/core/NEON/kernels/NEGaussianPyramidKernel.cpp
index d6cb1b6444..7a123e2f57 100644
--- a/src/core/NEON/kernels/NEGaussianPyramidKernel.cpp
+++ b/src/core/NEON/kernels/NEGaussianPyramidKernel.cpp
@@ -41,20 +41,19 @@
using namespace arm_compute;
NEGaussianPyramidHorKernel::NEGaussianPyramidHorKernel()
- : _border_size(0), _l2_load_offset(0)
+ : _l2_load_offset(0)
{
}
BorderSize NEGaussianPyramidHorKernel::border_size() const
{
- return _border_size;
+ return BorderSize(0, 2);
}
-void NEGaussianPyramidHorKernel::configure(const ITensor *input, ITensor *output, bool border_undefined)
+void NEGaussianPyramidHorKernel::configure(const ITensor *input, ITensor *output)
{
ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8);
ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::S16);
- ARM_COMPUTE_ERROR_ON(input->info()->dimension(0) != 2 * output->info()->dimension(0));
ARM_COMPUTE_ERROR_ON(input->info()->dimension(1) != output->info()->dimension(1));
for(size_t i = 2; i < Coordinates::num_max_dimensions; ++i)
@@ -62,17 +61,16 @@ void NEGaussianPyramidHorKernel::configure(const ITensor *input, ITensor *output
ARM_COMPUTE_ERROR_ON(input->info()->dimension(i) != output->info()->dimension(i));
}
- _input = input;
- _output = output;
- _border_size = BorderSize(border_undefined ? 0 : 2, 2);
+ _input = input;
+ _output = output;
// Configure kernel window
constexpr unsigned int num_elems_processed_per_iteration = 16;
constexpr unsigned int num_elems_read_per_iteration = 32;
constexpr unsigned int num_elems_written_per_iteration = 8;
- constexpr float scale_x = 0.5f;
+ const float scale_x = static_cast<float>(output->info()->dimension(0)) / input->info()->dimension(0);
- Window win = calculate_max_window_horizontal(*input->info(), Steps(num_elems_processed_per_iteration), border_undefined, border_size());
+ Window win = calculate_max_window_horizontal(*input->info(), Steps(num_elems_processed_per_iteration));
AccessWindowHorizontal output_access(output->info(), 0, num_elems_written_per_iteration, scale_x);
// Sub sampling selects odd pixels (1, 3, 5, ...) for images with even
@@ -97,15 +95,12 @@ void NEGaussianPyramidHorKernel::configure(const ITensor *input, ITensor *output
_l2_load_offset += 1;
}
+ // Replace input access with static window
update_window_and_padding(win,
AccessWindowHorizontal(input->info(), _l2_load_offset, num_elems_read_per_iteration),
output_access);
- ValidRegion valid_region = input->info()->valid_region();
- valid_region.anchor.set(0, std::ceil((valid_region.anchor[0] + (border_undefined ? border_size().left : 0)) / 2.f));
- valid_region.shape.set(0, (valid_region.shape[0] - (border_undefined ? border_size().right : 0)) / 2 - valid_region.anchor[0]);
-
- output_access.set_valid_region(win, valid_region);
+ output->info()->set_valid_region(ValidRegion(Coordinates(), output->info()->tensor_shape()));
INEKernel::configure(win);
}
@@ -163,13 +158,11 @@ BorderSize NEGaussianPyramidVertKernel::border_size() const
return BorderSize(2, 0);
}
-void NEGaussianPyramidVertKernel::configure(const ITensor *input, ITensor *output, bool border_undefined)
+void NEGaussianPyramidVertKernel::configure(const ITensor *input, ITensor *output)
{
ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::S16);
ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::U8);
-
ARM_COMPUTE_ERROR_ON(input->info()->dimension(0) != output->info()->dimension(0));
- ARM_COMPUTE_ERROR_ON(input->info()->dimension(1) != 2 * output->info()->dimension(1));
for(size_t i = 2; i < Coordinates::num_max_dimensions; ++i)
{
@@ -189,9 +182,9 @@ void NEGaussianPyramidVertKernel::configure(const ITensor *input, ITensor *outpu
constexpr unsigned int num_elems_read_per_iteration = 16;
constexpr unsigned int num_rows_read_per_iteration = 5;
- constexpr float scale_y = 0.5f;
+ const float scale_y = static_cast<float>(output->info()->dimension(1)) / input->info()->dimension(1);
- Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration, num_rows_processed_per_iteration), border_undefined, border_size());
+ Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration, num_rows_processed_per_iteration));
AccessWindowRectangle output_access(output->info(), 0, 0, num_elems_written_per_iteration, num_rows_written_per_iteration, 1.f, scale_y);
// Determine whether we need to load even or odd rows. See above for a
@@ -207,11 +200,7 @@ void NEGaussianPyramidVertKernel::configure(const ITensor *input, ITensor *outpu
AccessWindowRectangle(input->info(), 0, _t2_load_offset, num_elems_read_per_iteration, num_rows_read_per_iteration),
output_access);
- ValidRegion valid_region = input->info()->valid_region();
- valid_region.anchor.set(1, std::ceil((valid_region.anchor[1] + (border_undefined ? border_size().top : 0)) / 2.f));
- valid_region.shape.set(1, (valid_region.shape[1] - (border_undefined ? border_size().bottom : 0)) / 2 - valid_region.anchor[1]);
-
- output_access.set_valid_region(win, valid_region);
+ output->info()->set_valid_region(ValidRegion(Coordinates(), output->info()->tensor_shape()));
INEKernel::configure(win);
}
diff --git a/src/runtime/NEON/functions/NEGaussianPyramid.cpp b/src/runtime/NEON/functions/NEGaussianPyramid.cpp
index 84ea0ca058..8a85bba68b 100644
--- a/src/runtime/NEON/functions/NEGaussianPyramid.cpp
+++ b/src/runtime/NEON/functions/NEGaussianPyramid.cpp
@@ -47,7 +47,8 @@ NEGaussianPyramid::NEGaussianPyramid()
}
NEGaussianPyramidHalf::NEGaussianPyramidHalf() // NOLINT
- : _border_handler(),
+ : _horizontal_border_handler(),
+ _vertical_border_handler(),
_horizontal_reduction(),
_vertical_reduction()
{
@@ -62,6 +63,9 @@ void NEGaussianPyramidHalf::configure(const ITensor *input, IPyramid *pyramid, B
ARM_COMPUTE_ERROR_ON(input->info()->dimension(1) != pyramid->info()->height());
ARM_COMPUTE_ERROR_ON(SCALE_PYRAMID_HALF != pyramid->info()->scale());
+ // Constant value to use for vertical fill border when the border mode is CONSTANT
+ const uint16_t pixel_value_u16 = static_cast<uint16_t>(constant_border_value) * 2 + static_cast<uint16_t>(constant_border_value) * 8 + static_cast<uint16_t>(constant_border_value) * 6;
+
/* Get number of pyramid levels */
const size_t num_levels = pyramid->info()->num_levels();
@@ -70,9 +74,10 @@ void NEGaussianPyramidHalf::configure(const ITensor *input, IPyramid *pyramid, B
if(num_levels > 1)
{
- _border_handler = arm_compute::support::cpp14::make_unique<NEFillBorderKernel[]>(num_levels - 1);
- _horizontal_reduction = arm_compute::support::cpp14::make_unique<NEGaussianPyramidHorKernel[]>(num_levels - 1);
- _vertical_reduction = arm_compute::support::cpp14::make_unique<NEGaussianPyramidVertKernel[]>(num_levels - 1);
+ _horizontal_border_handler = arm_compute::support::cpp14::make_unique<NEFillBorderKernel[]>(num_levels - 1);
+ _vertical_border_handler = arm_compute::support::cpp14::make_unique<NEFillBorderKernel[]>(num_levels - 1);
+ _horizontal_reduction = arm_compute::support::cpp14::make_unique<NEGaussianPyramidHorKernel[]>(num_levels - 1);
+ _vertical_reduction = arm_compute::support::cpp14::make_unique<NEGaussianPyramidVertKernel[]>(num_levels - 1);
// Apply half scale to the X dimension of the tensor shape
TensorShape tensor_shape = pyramid->info()->tensor_shape();
@@ -84,13 +89,16 @@ void NEGaussianPyramidHalf::configure(const ITensor *input, IPyramid *pyramid, B
for(unsigned int i = 0; i < num_levels - 1; ++i)
{
/* Configure horizontal kernel */
- _horizontal_reduction[i].configure(_pyramid->get_pyramid_level(i), _tmp.get_pyramid_level(i), border_mode == BorderMode::UNDEFINED);
+ _horizontal_reduction[i].configure(_pyramid->get_pyramid_level(i), _tmp.get_pyramid_level(i));
/* Configure vertical kernel */
- _vertical_reduction[i].configure(_tmp.get_pyramid_level(i), _pyramid->get_pyramid_level(i + 1), border_mode == BorderMode::UNDEFINED);
+ _vertical_reduction[i].configure(_tmp.get_pyramid_level(i), _pyramid->get_pyramid_level(i + 1));
+
+ /* Configure border */
+ _horizontal_border_handler[i].configure(_pyramid->get_pyramid_level(i), _horizontal_reduction[i].border_size(), border_mode, PixelValue(constant_border_value));
/* Configure border */
- _border_handler[i].configure(_pyramid->get_pyramid_level(i), _horizontal_reduction[i].border_size(), border_mode, PixelValue(constant_border_value));
+ _vertical_border_handler[i].configure(_tmp.get_pyramid_level(i), _vertical_reduction[i].border_size(), border_mode, PixelValue(pixel_value_u16));
}
_tmp.allocate();
@@ -109,8 +117,9 @@ void NEGaussianPyramidHalf::run()
for(unsigned int i = 0; i < num_levels - 1; ++i)
{
- NEScheduler::get().schedule(_border_handler.get() + i, Window::DimZ);
+ NEScheduler::get().schedule(_horizontal_border_handler.get() + i, Window::DimZ);
NEScheduler::get().schedule(_horizontal_reduction.get() + i, Window::DimY);
+ NEScheduler::get().schedule(_vertical_border_handler.get() + i, Window::DimZ);
NEScheduler::get().schedule(_vertical_reduction.get() + i, Window::DimY);
}
}
diff --git a/tests/Utils.h b/tests/Utils.h
index 70def45ec7..df1d7a543a 100644
--- a/tests/Utils.h
+++ b/tests/Utils.h
@@ -216,6 +216,67 @@ inline ValidRegion shape_to_valid_region(TensorShape shape, bool border_undefine
return ValidRegion(std::move(anchor), std::move(shape));
}
+/** Create a valid region for Gaussian Pyramid Half based on tensor shape and valid region at level "i - 1" and border mode
+ *
+ * @note The border size is 2 in case of Gaussian Pyramid Half
+ *
+ * @param[in] shape Shape used at level "i - 1" of Gaussian Pyramid Half
+ * @param[in] valid_region Valid region used at level "i - 1" of Gaussian Pyramid Half
+ * @param[in] border_undefined (Optional) Boolean indicating if the border mode is undefined.
+ *
+ * return The valid region for the level "i" of Gaussian Pyramid Half
+ */
+inline ValidRegion shape_to_valid_region_gaussian_pyramid_half(TensorShape shape, ValidRegion valid_region, bool border_undefined = false)
+{
+ constexpr int border_size = 2;
+ Coordinates anchor;
+ anchor.set_num_dimensions(shape.num_dimensions());
+
+ // Compute tensor shape for level "i" of Gaussian Pyramid Half
+ // dst_width = (src_width + 1) * 0.5f
+ // dst_height = (src_height + 1) * 0.5f
+ TensorShape dst_shape = shape;
+ dst_shape.set(0, (shape[0] + 1) * 0.5f);
+ dst_shape.set(1, (shape[1] + 1) * 0.5f);
+
+ if(border_undefined)
+ {
+ ARM_COMPUTE_ERROR_ON(shape.num_dimensions() < 2);
+
+ // Compute the left and top invalid borders
+ float invalid_border_left = static_cast<float>(valid_region.anchor.x() + border_size) / 2.0f;
+ float invalid_border_top = static_cast<float>(valid_region.anchor.y() + border_size) / 2.0f;
+
+ // For the new anchor point we can have 2 cases:
+ // 1) If the width/height of the tensor shape is odd, we have to take the ceil value of (valid_region.anchor.x() + border_size) / 2.0f or (valid_region.anchor.y() + border_size / 2.0f
+ // 2) If the width/height of the tensor shape is even, we have to take the floor value of (valid_region.anchor.x() + border_size) / 2.0f or (valid_region.anchor.y() + border_size) / 2.0f
+ // In this manner we should be able to propagate correctly the valid region along all levels of the pyramid
+ invalid_border_left = (shape[0] % 2) ? std::ceil(invalid_border_left) : std::floor(invalid_border_left);
+ invalid_border_top = (shape[1] % 2) ? std::ceil(invalid_border_top) : std::floor(invalid_border_top);
+
+ // Set the anchor point
+ anchor.set(0, static_cast<int>(invalid_border_left));
+ anchor.set(1, static_cast<int>(invalid_border_top));
+
+ // Compute shape
+ // Calculate the right and bottom invalid borders at the previous level of the pyramid
+ const float prev_invalid_border_right = static_cast<float>(shape[0] - (valid_region.anchor.x() + valid_region.shape[0]));
+ const float prev_invalid_border_bottom = static_cast<float>(shape[1] - (valid_region.anchor.y() + valid_region.shape[1]));
+
+ // Calculate the right and bottom invalid borders at the current level of the pyramid
+ const float invalid_border_right = std::ceil((prev_invalid_border_right + static_cast<float>(border_size)) / 2.0f);
+ const float invalid_border_bottom = std::ceil((prev_invalid_border_bottom + static_cast<float>(border_size)) / 2.0f);
+
+ const int valid_shape_x = std::max(0, static_cast<int>(dst_shape.x()) - static_cast<int>(invalid_border_left) - static_cast<int>(invalid_border_right));
+ const int valid_shape_y = std::max(0, static_cast<int>(dst_shape.y()) - static_cast<int>(invalid_border_top) - static_cast<int>(invalid_border_bottom));
+
+ dst_shape.set(0, valid_shape_x);
+ dst_shape.set(1, valid_shape_y);
+ }
+
+ return ValidRegion(std::move(anchor), std::move(dst_shape));
+}
+
/** Write the value after casting the pointer according to @p data_type.
*
* @warning The type of the value must match the specified data type.
diff --git a/tests/datasets/ShapeDatasets.h b/tests/datasets/ShapeDatasets.h
index 45f5d1c9ff..3dc4566e18 100644
--- a/tests/datasets/ShapeDatasets.h
+++ b/tests/datasets/ShapeDatasets.h
@@ -135,6 +135,22 @@ public:
}
};
+/** Data set containing medium 2D tensor shapes. */
+class Medium2DShapes final : public ShapeDataset
+{
+public:
+ Medium2DShapes()
+ : ShapeDataset("Shape",
+ {
+ TensorShape{ 42U, 37U },
+ TensorShape{ 57U, 60U },
+ TensorShape{ 128U, 64U },
+ TensorShape{ 83U, 72U }
+ })
+ {
+ }
+};
+
/** Data set containing large tensor shapes. */
class LargeShapes final : public ShapeDataset
{
diff --git a/tests/validation/CPP/GaussianPyramidHalf.cpp b/tests/validation/CPP/GaussianPyramidHalf.cpp
new file mode 100644
index 0000000000..18d3daa288
--- /dev/null
+++ b/tests/validation/CPP/GaussianPyramidHalf.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "GaussianPyramidHalf.h"
+
+#include "arm_compute/core/Helpers.h"
+
+#include "Gaussian5x5.h"
+#include "Scale.h"
+#include "Utils.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+std::vector<SimpleTensor<T>> gaussian_pyramid_half(const SimpleTensor<T> &src, BorderMode border_mode, uint8_t constant_border_value, size_t num_levels)
+{
+ std::vector<SimpleTensor<T>> dst;
+
+ // Level0 is equal to src
+ dst.push_back(src);
+
+ for(size_t i = 1; i < num_levels; ++i)
+ {
+ // Gaussian Filter
+ const SimpleTensor<T> out_gaus5x5 = reference::gaussian5x5(dst[i - 1], border_mode, constant_border_value);
+
+ // Scale down by 2 with nearest interpolation
+ const SimpleTensor<T> out = reference::scale(out_gaus5x5, SCALE_PYRAMID_HALF, SCALE_PYRAMID_HALF, InterpolationPolicy::NEAREST_NEIGHBOR, border_mode, constant_border_value, true);
+
+ dst.push_back(out);
+ }
+
+ return dst;
+}
+
+template std::vector<SimpleTensor<uint8_t>> gaussian_pyramid_half(const SimpleTensor<uint8_t> &src, BorderMode border_mode, uint8_t constant_border_value, size_t num_levels);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/CPP/GaussianPyramidHalf.h b/tests/validation/CPP/GaussianPyramidHalf.h
new file mode 100644
index 0000000000..abd29e1700
--- /dev/null
+++ b/tests/validation/CPP/GaussianPyramidHalf.h
@@ -0,0 +1,43 @@
+/*
+ * 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_TEST_GAUSSIAN_PYRAMID_HALF_H__
+#define __ARM_COMPUTE_TEST_GAUSSIAN_PYRAMID_HALF_H__
+
+#include "tests/SimpleTensor.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace reference
+{
+template <typename T>
+std::vector<SimpleTensor<T>> gaussian_pyramid_half(const SimpleTensor<T> &src, BorderMode border_mode, uint8_t constant_border_value, size_t num_levels);
+} // namespace reference
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_TEST_GAUSSIAN_PYRAMID_HALF_H__ */ \ No newline at end of file
diff --git a/tests/validation/CPP/Scale.cpp b/tests/validation/CPP/Scale.cpp
index 0da7497277..c368fa277a 100644
--- a/tests/validation/CPP/Scale.cpp
+++ b/tests/validation/CPP/Scale.cpp
@@ -36,11 +36,13 @@ namespace validation
namespace reference
{
template <typename T>
-SimpleTensor<T> scale(const SimpleTensor<T> &in, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, T constant_border_value)
+SimpleTensor<T> scale(const SimpleTensor<T> &in, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, T constant_border_value, bool ceil_policy_scale)
{
- TensorShape shape_scaled(in.shape());
- shape_scaled.set(0, in.shape()[0] * scale_x);
- shape_scaled.set(1, in.shape()[1] * scale_y);
+ // Add 1 if ceil_policy_scale is true
+ const size_t round_value = ceil_policy_scale ? 1U : 0U;
+ TensorShape shape_scaled(in.shape());
+ shape_scaled.set(0, (in.shape()[0] + round_value) * scale_x);
+ shape_scaled.set(1, (in.shape()[1] + round_value) * scale_y);
SimpleTensor<T> out(shape_scaled, in.data_type());
// Compute the ratio between source width/height and destination width/height
@@ -149,10 +151,13 @@ SimpleTensor<T> scale(const SimpleTensor<T> &in, float scale_x, float scale_y, I
return out;
}
-template SimpleTensor<uint8_t> scale(const SimpleTensor<uint8_t> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, uint8_t constant_border_value);
-template SimpleTensor<int16_t> scale(const SimpleTensor<int16_t> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, int16_t constant_border_value);
-template SimpleTensor<half> scale(const SimpleTensor<half> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, half constant_border_value);
-template SimpleTensor<float> scale(const SimpleTensor<float> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, float constant_border_value);
+template SimpleTensor<uint8_t> scale(const SimpleTensor<uint8_t> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, uint8_t constant_border_value,
+ bool ceil_policy_scale);
+template SimpleTensor<int16_t> scale(const SimpleTensor<int16_t> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, int16_t constant_border_value,
+ bool ceil_policy_scale);
+template SimpleTensor<half> scale(const SimpleTensor<half> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, half constant_border_value, bool ceil_policy_scale);
+template SimpleTensor<float> scale(const SimpleTensor<float> &src, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, float constant_border_value,
+ bool ceil_policy_scale);
} // namespace reference
} // namespace validation
} // namespace test
diff --git a/tests/validation/CPP/Scale.h b/tests/validation/CPP/Scale.h
index 53183ae742..87af2fd204 100644
--- a/tests/validation/CPP/Scale.h
+++ b/tests/validation/CPP/Scale.h
@@ -35,7 +35,7 @@ namespace validation
namespace reference
{
template <typename T>
-SimpleTensor<T> scale(const SimpleTensor<T> &in, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, T constant_border_value = 0);
+SimpleTensor<T> scale(const SimpleTensor<T> &in, float scale_x, float scale_y, InterpolationPolicy policy, BorderMode border_mode, T constant_border_value = 0, bool ceil_policy_scale = false);
} // namespace reference
} // namespace validation
} // namespace test
diff --git a/tests/validation/NEON/GaussianPyramid.cpp b/tests/validation/NEON/GaussianPyramid.cpp
new file mode 100644
index 0000000000..fb4944f9c3
--- /dev/null
+++ b/tests/validation/NEON/GaussianPyramid.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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/Types.h"
+#include "arm_compute/runtime/NEON/functions/NEGaussianPyramid.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/runtime/TensorAllocator.h"
+#include "tests/NEON/Accessor.h"
+#include "tests/PaddingCalculator.h"
+#include "tests/datasets/BorderModeDataset.h"
+#include "tests/datasets/ShapeDatasets.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Macros.h"
+#include "tests/framework/datasets/Datasets.h"
+#include "tests/validation/CPP/Utils.h"
+#include "tests/validation/Validation.h"
+#include "tests/validation/fixtures/GaussianPyramidHalfFixture.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+namespace
+{
+constexpr AbsoluteTolerance<float> tolerance_fp32(1.0f); /**< Tolerance value for comparing reference's output against implementation's output */
+
+const auto small_gaussian_pyramid_levels = combine(datasets::Medium2DShapes(), datasets::BorderModes()) * framework::dataset::make("numlevels", 2, 3);
+const auto large_gaussian_pyramid_levels = combine(datasets::Large2DShapes(), datasets::BorderModes()) * framework::dataset::make("numlevels", 2, 5);
+
+template <typename T, typename U>
+inline void validate_gaussian_pyramid(const Pyramid &target, const std::vector<SimpleTensor<T>> &reference, BorderMode border_mode, U tolerance)
+{
+ ValidRegion prev_valid_region = shape_to_valid_region(reference[0].shape());
+
+ for(size_t i = 1; i < reference.size(); ++i)
+ {
+ const ValidRegion valid_region = shape_to_valid_region_gaussian_pyramid_half(reference[i - 1].shape(), prev_valid_region, (border_mode == BorderMode::UNDEFINED));
+
+ // Validate outputs
+ validate(Accessor(*(target.get_pyramid_level(i))), reference[i], valid_region, tolerance);
+
+ // Keep the valid region for the next level
+ prev_valid_region = valid_region;
+ }
+}
+} // namespace
+
+TEST_SUITE(NEON)
+TEST_SUITE(GaussianPyramid)
+TEST_SUITE(Half)
+
+DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, large_gaussian_pyramid_levels,
+ shape, border_mode, num_levels)
+{
+ Tensor src = create_tensor<Tensor>(shape, DataType::U8);
+
+ // Create pyramid
+ PyramidInfo pyramid_info(num_levels, SCALE_PYRAMID_HALF, shape, Format::U8);
+ Pyramid dst;
+ dst.init(pyramid_info);
+
+ NEGaussianPyramidHalf gaussian_pyramid_half;
+ gaussian_pyramid_half.configure(&src, &dst, border_mode, 0);
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+
+ for(size_t level = 0; level < pyramid_info.num_levels(); ++level)
+ {
+ ARM_COMPUTE_EXPECT(dst.get_pyramid_level(level)->info()->is_resizable(), framework::LogLevel::ERRORS);
+ }
+}
+
+template <typename T>
+using NEGaussianPyramidHalfFixture = GaussianPyramidHalfValidationFixture<Tensor, Accessor, NEGaussianPyramidHalf, T, Pyramid>;
+
+FIXTURE_DATA_TEST_CASE(RunSmallGaussianPyramidHalf, NEGaussianPyramidHalfFixture<uint8_t>, framework::DatasetMode::ALL, small_gaussian_pyramid_levels)
+{
+ validate_gaussian_pyramid(_target, _reference, _border_mode, tolerance_fp32);
+}
+
+FIXTURE_DATA_TEST_CASE(RunLargeGaussianPyramidHalf, NEGaussianPyramidHalfFixture<uint8_t>, framework::DatasetMode::NIGHTLY, large_gaussian_pyramid_levels)
+{
+ validate_gaussian_pyramid(_target, _reference, _border_mode, tolerance_fp32);
+}
+TEST_SUITE_END()
+TEST_SUITE_END()
+TEST_SUITE_END()
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
diff --git a/tests/validation/fixtures/GaussianPyramidHalfFixture.h b/tests/validation/fixtures/GaussianPyramidHalfFixture.h
new file mode 100644
index 0000000000..c3e3efb6e6
--- /dev/null
+++ b/tests/validation/fixtures/GaussianPyramidHalfFixture.h
@@ -0,0 +1,125 @@
+/*
+ * 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_TEST_GAUSSIAN_PYRAMID_HALF_FIXTURE
+#define ARM_COMPUTE_TEST_GAUSSIAN_PYRAMID_HALF_FIXTURE
+
+#include "arm_compute/core/IPyramid.h"
+#include "arm_compute/core/PyramidInfo.h"
+#include "arm_compute/core/TensorShape.h"
+#include "arm_compute/core/Types.h"
+#include "tests/AssetsLibrary.h"
+#include "tests/Globals.h"
+#include "tests/IAccessor.h"
+#include "tests/framework/Asserts.h"
+#include "tests/framework/Fixture.h"
+#include "tests/validation/CPP/GaussianPyramidHalf.h"
+
+namespace arm_compute
+{
+namespace test
+{
+namespace validation
+{
+template <typename TensorType, typename AccessorType, typename FunctionType, typename T, typename PyramidType>
+class GaussianPyramidHalfValidationFixture : public framework::Fixture
+{
+public:
+ template <typename...>
+ void setup(TensorShape shape, BorderMode border_mode, size_t num_levels)
+ {
+ std::mt19937 gen(library->seed());
+ std::uniform_int_distribution<uint8_t> distribution(0, 255);
+ const uint8_t constant_border_value = distribution(gen);
+
+ _border_mode = border_mode;
+
+ // Compute target and reference
+ compute_target(shape, border_mode, constant_border_value, num_levels);
+ compute_reference(shape, border_mode, constant_border_value, num_levels);
+ }
+
+protected:
+ template <typename U>
+ void fill(U &&tensor)
+ {
+ library->fill_tensor_uniform(tensor, 0);
+ }
+
+ void compute_target(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value, size_t num_levels)
+ {
+ // Create tensors
+ TensorType src = create_tensor<TensorType>(shape, DataType::U8);
+
+ PyramidInfo pyramid_info(num_levels, SCALE_PYRAMID_HALF, shape, Format::U8);
+ _target.init(pyramid_info);
+
+ // Create and configure function
+ FunctionType gaussian_pyramid;
+
+ gaussian_pyramid.configure(&src, &_target, border_mode, constant_border_value);
+
+ ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS);
+ for(size_t i = 0; i < pyramid_info.num_levels(); ++i)
+ {
+ ARM_COMPUTE_EXPECT(_target.get_pyramid_level(i)->info()->is_resizable(), framework::LogLevel::ERRORS);
+ }
+
+ // Allocate input tensor
+ src.allocator()->allocate();
+
+ // Allocate pyramid
+ _target.allocate();
+
+ ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS);
+ for(size_t i = 0; i < pyramid_info.num_levels(); ++i)
+ {
+ ARM_COMPUTE_EXPECT(!_target.get_pyramid_level(i)->info()->is_resizable(), framework::LogLevel::ERRORS);
+ }
+
+ // Fill tensors
+ fill(AccessorType(src));
+
+ // Compute function
+ gaussian_pyramid.run();
+ }
+
+ void compute_reference(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value, size_t num_levels)
+ {
+ // Create reference
+ SimpleTensor<T> src{ shape, DataType::U8 };
+
+ // Fill reference
+ fill(src);
+
+ _reference = reference::gaussian_pyramid_half<T>(src, border_mode, constant_border_value, num_levels);
+ }
+
+ PyramidType _target{};
+ std::vector<SimpleTensor<T>> _reference{};
+ BorderMode _border_mode{};
+};
+} // namespace validation
+} // namespace test
+} // namespace arm_compute
+#endif /* ARM_COMPUTE_TEST_GAUSSIAN_PYRAMID_HALF_FIXTURE */ \ No newline at end of file