From bb123bd6f64444141161562aad06cb406762d47a Mon Sep 17 00:00:00 2001 From: Sang-Hoon Park Date: Fri, 3 Jan 2020 10:57:30 +0000 Subject: MLCE-139 add align_corners parameter to NEScale Change-Id: I497ceb54c5fd8af1af8c529f90fd5a00a45263c8 Signed-off-by: Sang-Hoon Park Reviewed-on: https://review.mlplatform.org/c/2538 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins Reviewed-by: Michele Di Giorgio Reviewed-by: Pablo Marquez --- src/core/NEON/kernels/NEScaleKernel.cpp | 52 ++++++++++++++++++-------- src/core/Utils.cpp | 14 ++++++- src/runtime/CL/functions/CLScale.cpp | 11 ++++-- src/runtime/GLES_COMPUTE/functions/GCScale.cpp | 6 ++- src/runtime/NEON/functions/NEScale.cpp | 23 +++++++----- 5 files changed, 74 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/core/NEON/kernels/NEScaleKernel.cpp b/src/core/NEON/kernels/NEScaleKernel.cpp index 1cea8c6714..66408ab94c 100644 --- a/src/core/NEON/kernels/NEScaleKernel.cpp +++ b/src/core/NEON/kernels/NEScaleKernel.cpp @@ -31,6 +31,7 @@ #include "arm_compute/core/ITensor.h" #include "arm_compute/core/NEON/wrapper/wrapper.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/Utility.h" @@ -45,7 +46,7 @@ namespace { Status validate_arguments(const ITensorInfo *input, const ITensorInfo *dx, const ITensorInfo *dy, const ITensorInfo *offsets, ITensorInfo *output, InterpolationPolicy policy, - BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding) + BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, bool align_corners) { ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input); ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8, DataType::S16, DataType::F16, DataType::F32, DataType::QASYMM8); @@ -56,9 +57,13 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *dx, const ARM_COMPUTE_RETURN_ERROR_ON(!use_padding && border_mode != BorderMode::CONSTANT); ARM_COMPUTE_UNUSED(constant_border_value); - const DataLayout data_layout = input->data_layout(); - ARM_COMPUTE_RETURN_ERROR_ON(output->dimension(get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH)) == 0); - ARM_COMPUTE_RETURN_ERROR_ON(output->dimension(get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT)) == 0); + const DataLayout data_layout = input->data_layout(); + const auto width_index = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); + const auto height_index = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); + const auto output_width = output->dimension(width_index); + const auto output_height = output->dimension(height_index); + ARM_COMPUTE_RETURN_ERROR_ON(output_width == 0); + ARM_COMPUTE_RETURN_ERROR_ON(output_height == 0); if(policy == InterpolationPolicy::NEAREST_NEIGHBOR) { @@ -70,6 +75,18 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *dx, const ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(offsets, 1, DataType::S32); ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(dx, 1, DataType::F32); ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(dy, 1, DataType::F32); + + if(align_corners) + { + // For bilinear method with aligned corners, the resize ratio will + // be calculated by (input_size - 1)/(output_size - 1). Belows are + // checking possible overflows. + const auto input_width = input->dimension(width_index); + const auto input_height = input->dimension(height_index); + + ARM_COMPUTE_RETURN_ERROR_ON(input_width == 0 || input_height == 0); + ARM_COMPUTE_RETURN_ERROR_ON((output_width - 1 == 0) || (output_height - 1 == 0)); + } } if(policy == InterpolationPolicy::AREA) @@ -327,7 +344,7 @@ inline void scale_bilinear_nhwc_core(const ITensor *input, const ITensor *offset NEScaleKernel::NEScaleKernel() : _func(nullptr), _offsets(nullptr), _dx(nullptr), _dy(nullptr), _input(nullptr), _output(nullptr), _policy(), _border_size(1), _border_mode(), _constant_border_value(PixelValue()), - _sampling_offset(0), _use_padding(true) + _sampling_offset(0), _use_padding(true), _align_corners(false) { } @@ -338,7 +355,7 @@ BorderSize NEScaleKernel::border_size() const void NEScaleKernel::configure(const ITensor *input, const ITensor *dx, const ITensor *dy, const ITensor *offsets, ITensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, - bool use_padding) + bool use_padding, bool align_corners) { ARM_COMPUTE_ERROR_ON_NULLPTR(input, output); // Perform validation step @@ -347,7 +364,7 @@ void NEScaleKernel::configure(const ITensor *input, const ITensor *dx, const ITe dy != nullptr ? dy->info() : nullptr, offsets != nullptr ? offsets->info() : nullptr, output->info(), - policy, border_mode, constant_border_value, sampling_policy, use_padding)); + policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners)); // Get data layout and width/height indices const DataLayout data_layout = input->info()->data_layout(); @@ -364,6 +381,9 @@ void NEScaleKernel::configure(const ITensor *input, const ITensor *dx, const ITe _border_mode = border_mode; _constant_border_value = constant_border_value; _use_padding = use_padding; + _align_corners = _policy == InterpolationPolicy::BILINEAR + && sampling_policy == SamplingPolicy::TOP_LEFT + && align_corners; if(sampling_policy == SamplingPolicy::CENTER) { @@ -371,8 +391,8 @@ void NEScaleKernel::configure(const ITensor *input, const ITensor *dx, const ITe } // Compute the ratio between source width/height and destination width/height - const auto wr = static_cast(input->info()->dimension(idx_width)) / static_cast(output->info()->dimension(idx_width)); - const auto hr = static_cast(input->info()->dimension(idx_height)) / static_cast(output->info()->dimension(idx_height)); + const auto wr = arm_compute::calculate_resize_ratio(input->info()->dimension(idx_width), output->info()->dimension(idx_width), _align_corners); + const auto hr = arm_compute::calculate_resize_ratio(input->info()->dimension(idx_height), output->info()->dimension(idx_height), _align_corners); // Add constant border only on top in case of NHWC layout if(data_layout == DataLayout::NHWC) @@ -425,7 +445,7 @@ void NEScaleKernel::scale_nearest_nchw(const Window &window) const size_t input_stride = _input->info()->strides_in_bytes()[1]; // Compute the ratio between source height and destination height - const auto hr = static_cast(_input->info()->dimension(1)) / static_cast(_output->info()->dimension(1)); + const auto hr = arm_compute::calculate_resize_ratio(_input->info()->dimension(1), _output->info()->dimension(1), _align_corners); // Don't increment in X and Y direction for the input tensor // A pointer to the start of this plane is needed as base for the precomputed offsets @@ -622,7 +642,7 @@ void NEScaleKernel::scale_bilinear_nchw(const Window &window) ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(_input, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); // Compute the ratio between source height and destination height - const auto hr = static_cast(_input->info()->dimension(1)) / static_cast(_output->info()->dimension(1)); + const auto hr = arm_compute::calculate_resize_ratio(_input->info()->dimension(1), _output->info()->dimension(1), _align_corners); // Don't increment in X and Y direction for the input tensor // A pointer to the start of this plane is needed as base for the precomputed offsets @@ -871,8 +891,8 @@ void NEScaleKernel::scale_area_nchw(const Window &window) Iterator in(_input, win_in); Iterator out(_output, window); - const auto wr = static_cast(_input->info()->dimension(0)) / static_cast(_output->info()->dimension(0)); - const auto hr = static_cast(_input->info()->dimension(1)) / static_cast(_output->info()->dimension(1)); + const auto wr = arm_compute::calculate_resize_ratio(_input->info()->dimension(0), _output->info()->dimension(0), _align_corners); + const auto hr = arm_compute::calculate_resize_ratio(_input->info()->dimension(1), _output->info()->dimension(1), _align_corners); const auto w = _input->info()->dimension(0); const auto h = _input->info()->dimension(1); const size_t in_stride = _input->info()->strides_in_bytes()[1]; @@ -919,7 +939,7 @@ void NEScaleKernel::scale_nhwc(const Window &window) const size_t input_stride_c = _input->info()->strides_in_bytes()[idx_channels]; // Compute the ratio between source height and destination height - const auto hr = static_cast(_input->info()->dimension(idx_height)) / static_cast(_output->info()->dimension(idx_height)); + const auto hr = arm_compute::calculate_resize_ratio(_input->info()->dimension(idx_height), _output->info()->dimension(idx_height), _align_corners); // Don't increment in width/height/channels for the input tensor // A pointer to the start of this plane is needed as base for the precomputed offsets @@ -994,7 +1014,7 @@ void NEScaleKernel::scale_nhwc(const Window &window) Status NEScaleKernel::validate(const ITensorInfo *input, const ITensorInfo *dx, const ITensorInfo *dy, const ITensorInfo *offsets, ITensorInfo *output, InterpolationPolicy policy, - BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding) + BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, bool align_corners) { BorderSize border_size(1); if(input->data_layout() == DataLayout::NHWC) @@ -1002,7 +1022,7 @@ Status NEScaleKernel::validate(const ITensorInfo *input, const ITensorInfo *dx, border_size = (border_mode == BorderMode::CONSTANT && policy == InterpolationPolicy::BILINEAR) ? BorderSize(1, 0, 0, 0) : BorderSize(0); } - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, dx, dy, offsets, output, policy, border_mode, constant_border_value, sampling_policy, use_padding)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, dx, dy, offsets, output, policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners)); ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(input->clone().get(), dx != nullptr ? dx->clone().get() : nullptr, dy != nullptr ? dy->clone().get() : nullptr, diff --git a/src/core/Utils.cpp b/src/core/Utils.cpp index 7e7dea5e34..30e5b66540 100644 --- a/src/core/Utils.cpp +++ b/src/core/Utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 ARM Limited. + * Copyright (c) 2016-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -459,6 +459,18 @@ QuantizationInfo arm_compute::get_softmax_output_quantization_info(DataType inpu return QuantizationInfo(1.f / 256, 0); } +float arm_compute::calculate_resize_ratio(size_t input_size, size_t output_size, bool align_corners) +{ + const size_t offset = align_corners ? 1 : 0; + const auto in = input_size - offset; + const auto out = output_size - offset; + + ARM_COMPUTE_ERROR_ON((input_size == 0 || output_size == 0) && offset == 1); + ARM_COMPUTE_ERROR_ON(out == 0); + + return static_cast(in) / static_cast(out); +} + #ifdef ARM_COMPUTE_ASSERTS_ENABLED void arm_compute::print_consecutive_elements(std::ostream &s, DataType dt, const uint8_t *ptr, unsigned int n, int stream_width, const std::string &element_delim) { diff --git a/src/runtime/CL/functions/CLScale.cpp b/src/runtime/CL/functions/CLScale.cpp index f204e644a6..39d992739c 100644 --- a/src/runtime/CL/functions/CLScale.cpp +++ b/src/runtime/CL/functions/CLScale.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 ARM Limited. + * Copyright (c) 2016-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -32,8 +32,10 @@ using namespace arm_compute; -void CLScale::configure(ICLTensor *input, ICLTensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy) +void CLScale::configure(ICLTensor *input, ICLTensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, + bool align_corners) { + ARM_COMPUTE_UNUSED(use_padding, align_corners); auto k = arm_compute::support::cpp14::make_unique(); k->set_target(CLScheduler::get().target()); k->configure(input, output, policy, border_mode, sampling_policy); @@ -51,8 +53,9 @@ void CLScale::configure(ICLTensor *input, ICLTensor *output, InterpolationPolicy _border_handler.configure(input, _kernel->border_size(), border_mode, constant_border_value); } -Status CLScale::validate(const ITensorInfo *input, const ITensorInfo *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy) +Status CLScale::validate(const ITensorInfo *input, const ITensorInfo *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, + bool use_padding, bool align_corners) { - ARM_COMPUTE_UNUSED(constant_border_value); + ARM_COMPUTE_UNUSED(constant_border_value, use_padding, align_corners); return CLScaleKernel::validate(input, output, policy, border_mode, sampling_policy); } diff --git a/src/runtime/GLES_COMPUTE/functions/GCScale.cpp b/src/runtime/GLES_COMPUTE/functions/GCScale.cpp index cfe65a30d4..3b56ea120d 100644 --- a/src/runtime/GLES_COMPUTE/functions/GCScale.cpp +++ b/src/runtime/GLES_COMPUTE/functions/GCScale.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017 ARM Limited. + * Copyright (c) 2016-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -31,8 +31,10 @@ using namespace arm_compute; -void GCScale::configure(IGCTensor *input, IGCTensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy) +void GCScale::configure(IGCTensor *input, IGCTensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, + bool align_corners) { + ARM_COMPUTE_UNUSED(use_padding, align_corners); auto k = arm_compute::support::cpp14::make_unique(); k->configure(input, output, policy, border_mode == BorderMode::UNDEFINED, sampling_policy); _kernel = std::move(k); diff --git a/src/runtime/NEON/functions/NEScale.cpp b/src/runtime/NEON/functions/NEScale.cpp index be643b3757..27c057592c 100644 --- a/src/runtime/NEON/functions/NEScale.cpp +++ b/src/runtime/NEON/functions/NEScale.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 ARM Limited. + * Copyright (c) 2016-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -98,16 +98,21 @@ NEScale::NEScale() // NOLINT _dy(), _scale_kernel(), _border_handler(), - _use_padding(true) + _use_padding(true), + _align_corners(false) { } -void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding) +void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, + bool align_corners) { ARM_COMPUTE_ERROR_ON_NULLPTR(input, output); ARM_COMPUTE_ERROR_THROW_ON(NEScale::validate(input->info(), output->info(), policy, border_mode, constant_border_value, sampling_policy, use_padding)); - _use_padding = use_padding; + _use_padding = use_padding; + _align_corners = policy == InterpolationPolicy::BILINEAR + && sampling_policy == SamplingPolicy::TOP_LEFT + && align_corners; // Get data layout and width/height indices const DataLayout data_layout = input->info()->data_layout(); @@ -118,8 +123,8 @@ void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy pol const TensorShape shape(output->info()->dimension(idx_width), output->info()->dimension(idx_height)); // Compute the ratio between source width/height and destination width/height - const auto wr = static_cast(input->info()->dimension(idx_width)) / static_cast(output->info()->dimension(idx_width)); - const auto hr = static_cast(input->info()->dimension(idx_height)) / static_cast(output->info()->dimension(idx_height)); + const auto wr = arm_compute::calculate_resize_ratio(input->info()->dimension(idx_width), output->info()->dimension(idx_width), _align_corners); + const auto hr = arm_compute::calculate_resize_ratio(input->info()->dimension(idx_height), output->info()->dimension(idx_height), _align_corners); // Get the element size of the input image const size_t input_element_size = input->info()->element_size(); @@ -155,7 +160,7 @@ void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy pol _dx.allocator()->init(tensor_info_dxdy); _dy.allocator()->init(tensor_info_dxdy); - _scale_kernel.configure(input, &_dx, &_dy, &_offsets, output, policy, border_mode, constant_border_value, sampling_policy, use_padding); + _scale_kernel.configure(input, &_dx, &_dy, &_offsets, output, policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners); // Allocate once the configure methods have been called _offsets.allocator()->allocate(); @@ -181,7 +186,7 @@ void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy pol } Status NEScale::validate(const ITensorInfo *input, const ITensorInfo *output, InterpolationPolicy policy, - BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding) + BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, bool align_corners) { ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output); ARM_COMPUTE_RETURN_ERROR_ON(sampling_policy != SamplingPolicy::CENTER && sampling_policy != SamplingPolicy::TOP_LEFT); @@ -218,7 +223,7 @@ Status NEScale::validate(const ITensorInfo *input, const ITensorInfo *output, In } ARM_COMPUTE_RETURN_ON_ERROR(NEScaleKernel::validate(input->clone().get(), dx, dy, offsets, output->clone().get(), - policy, border_mode, constant_border_value, sampling_policy, use_padding)); + policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners)); return Status{}; } -- cgit v1.2.1