From d25af6786c0a714f2b4f099d0338dab17a5dc7e1 Mon Sep 17 00:00:00 2001 From: Manuel Bottini Date: Wed, 10 Jul 2019 17:06:12 +0100 Subject: COMPMID-2456: NEDeconvolutionLayer.cpp, NHWC is not supported Support of NHWC for NEDeconvolutionLayer Bugfix for QASYMM8 in CPPUpsample when offset is different than 0 QASYMM8 tests added in NEUpsample with offset different than 0 Change-Id: I8283fa5e5e323fd4d5777136359ddb33025674bb Signed-off-by: Manuel Bottini Reviewed-on: https://review.mlplatform.org/c/1517 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins Reviewed-by: Pablo Marquez --- .../runtime/NEON/functions/NEDeconvolutionLayer.h | 7 + src/core/CPP/kernels/CPPUpsampleKernel.cpp | 10 +- .../NEON/functions/NEDeconvolutionLayer.cpp | 142 ++++++++++++++++----- tests/validation/NEON/DeconvolutionLayer.cpp | 10 +- tests/validation/NEON/Upsample.cpp | 16 ++- tests/validation/fixtures/UpsampleLayerFixture.h | 67 ++++++++-- tests/validation/reference/UpsampleLayer.cpp | 36 +++++- 7 files changed, 225 insertions(+), 63 deletions(-) diff --git a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h index 62977a7647..360bb23f22 100644 --- a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h +++ b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h @@ -118,8 +118,15 @@ private: NEConvolutionLayer _conv_f; CPPUpsample _upsample_f; CPPFlipWeightsKernel _flip_weights; + NEPermute _permute_input; + NEPermute _permute_weights; + NEPermute _permute_output; Tensor _scaled_output; Tensor _weights_flipped; + Tensor _permuted_input; + Tensor _permuted_weights; + Tensor _permuted_output; + bool _is_nchw; const ITensor *_original_weights; ITensor *_input; PadStrideInfo _info; diff --git a/src/core/CPP/kernels/CPPUpsampleKernel.cpp b/src/core/CPP/kernels/CPPUpsampleKernel.cpp index 6620ce2aeb..ad2d54a0f8 100644 --- a/src/core/CPP/kernels/CPPUpsampleKernel.cpp +++ b/src/core/CPP/kernels/CPPUpsampleKernel.cpp @@ -34,8 +34,8 @@ #include #include -using namespace arm_compute; - +namespace arm_compute +{ CPPUpsampleKernel::CPPUpsampleKernel() : _input(nullptr), _output(nullptr), _info() { @@ -82,7 +82,10 @@ void CPPUpsampleKernel::run(const Window &window, const ThreadInfo &info) const int end_x = width_scaled - _info.pad().first; const size_t element_size = _input->info()->element_size(); - std::fill_n(_output->buffer(), _output->info()->total_size(), 0); + //The fill value is normally 0, but for QASYMM8 the '0' corresponds to the offset + const uint8_t fill_value = _output->info()->data_type() == DataType::QASYMM8 ? utility::clamp(_output->info()->quantization_info().uniform().offset) : 0; + //Filling a value different than 0 works only for QASYMM8 datatype since we are filling 1byte values in a buffer of uint8_ts + std::fill_n(_output->buffer(), _output->info()->total_size(), fill_value); // Create window Window window_out(window); @@ -99,3 +102,4 @@ void CPPUpsampleKernel::run(const Window &window, const ThreadInfo &info) }, in, out); } +} // namespace arm_compute \ No newline at end of file diff --git a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp index 7bbacb139c..581f257581 100644 --- a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp +++ b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp @@ -38,8 +38,15 @@ NEDeconvolutionLayer::NEDeconvolutionLayer(std::shared_ptr memor _conv_f(), _upsample_f(), _flip_weights(), + _permute_input(), + _permute_weights(), + _permute_output(), _scaled_output(), _weights_flipped(), + _permuted_input(), + _permuted_weights(), + _permuted_output(), + _is_nchw(false), _original_weights(nullptr), _input(nullptr), _info(), @@ -52,18 +59,20 @@ Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output); ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32, DataType::F16, DataType::QASYMM8); ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(weights, input); - ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(0) != weights->dimension(1)); - ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(0) < 1); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_LAYOUT(weights, input); + const unsigned int width_idx = get_data_layout_dimension_index(weights->data_layout(), DataLayoutDimension::WIDTH); + const unsigned int height_idx = get_data_layout_dimension_index(weights->data_layout(), DataLayoutDimension::HEIGHT); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(width_idx) != weights->dimension(height_idx)); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(width_idx) < 1); ARM_COMPUTE_RETURN_ERROR_ON(!info.padding_is_symmetric()); const unsigned int stride_x = info.stride().first; const unsigned int stride_y = info.stride().second; - auto out_dims = deconvolution_output_dimensions(input->dimension(0), input->dimension(1), weights->dimension(0), weights->dimension(1), + auto out_dims = deconvolution_output_dimensions(input->dimension(width_idx), input->dimension(height_idx), weights->dimension(width_idx), weights->dimension(height_idx), info.pad().first, info.pad().second, stride_x, stride_y); ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, weights); - if(is_data_type_quantized_asymmetric(input->data_type())) { ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(bias, 1, DataType::S32); @@ -90,10 +99,10 @@ Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(scale_out_shape)); const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL); - for(size_t i = 2; i < Coordinates::num_max_dimensions; ++i) - { - ARM_COMPUTE_RETURN_ERROR_ON(input->dimension(i) != scale_out_info.dimension(i)); - } + const unsigned int batches_idx = get_data_layout_dimension_index(weights->data_layout(), DataLayoutDimension::BATCHES); + const unsigned int channel_idx = get_data_layout_dimension_index(weights->data_layout(), DataLayoutDimension::CHANNEL); + ARM_COMPUTE_RETURN_ERROR_ON(input->dimension(batches_idx) != scale_out_info.dimension(batches_idx)); + ARM_COMPUTE_RETURN_ERROR_ON(input->dimension(channel_idx) != scale_out_info.dimension(channel_idx)); ARM_COMPUTE_RETURN_ON_ERROR(NEConvolutionLayer::validate(&scale_out_info, weights, bias, output, conv_info, WeightsInfo())); @@ -103,21 +112,24 @@ Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf void NEDeconvolutionLayer::configure(ITensor *input, const ITensor *weights, const ITensor *bias, ITensor *output, const PadStrideInfo &info) { ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_ERROR_THROW_ON(NEDeconvolutionLayer::validate(input->info(), weights->info(), bias->info(), output->info(), info)); + + const DataLayout data_layout = input->info()->data_layout(); _input = input; _original_weights = weights; _info = info; _is_prepared = false; + _is_nchw = data_layout == DataLayout::NCHW; - const DataLayout data_layout = input->info()->data_layout(); - const unsigned int stride_x = info.stride().first; - const unsigned int stride_y = info.stride().second; + const unsigned int stride_x = info.stride().first; + const unsigned int stride_y = info.stride().second; - _weights_flipped.allocator()->init(weights->info()->clone()->set_data_layout(data_layout)); - _flip_weights.configure(weights, &_weights_flipped); - - auto out_dims = deconvolution_output_dimensions(input->info()->dimension(0), input->info()->dimension(1), weights->info()->dimension(0), weights->info()->dimension(1), - info.pad().first, info.pad().second, stride_x, stride_y); + const unsigned int width_idx = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); + const unsigned int height_idx = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); + auto out_dims = deconvolution_output_dimensions(input->info()->dimension(width_idx), input->info()->dimension(height_idx), weights->info()->dimension(width_idx), + weights->info()->dimension(height_idx), + info.pad().first, info.pad().second, stride_x, stride_y); const TensorShape output_shape = compute_deconvolution_output_shape(out_dims, *input->info(), *weights->info()); // Output auto initialization if not yet initialized @@ -128,20 +140,73 @@ void NEDeconvolutionLayer::configure(ITensor *input, const ITensor *weights, con _memory_group.manage(&_scaled_output); - // Find the upsampled dimensions and the padding needed for the convolution with stride 1 in order to match output shape - unsigned int padx = 0; - unsigned int pady = 0; - const TensorShape scale_out_shape = compute_deconvolution_upsampled_shape(*input->info(), *weights->info(), stride_x, stride_y, out_dims, padx, pady); - - TensorInfo scale_out_info(scale_out_shape, 1, input->info()->data_type(), input->info()->quantization_info()); - _scaled_output.allocator()->init(scale_out_info); - - const PadStrideInfo upsample_info(stride_x, stride_y, padx / 2, pady / 2); - _upsample_f.configure(input, &_scaled_output, upsample_info); - - // setup the function to convolve the upscaled output - const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL); - _conv_f.configure(&_scaled_output, &_weights_flipped, bias, output, conv_info); + if(!_is_nchw) + { + _memory_group.manage(&_permuted_input); + _memory_group.manage(&_permuted_weights); + _memory_group.manage(&_permuted_output); + + // Configure the function to transform the input tensor from NHWC -> NCHW + _permuted_input.info()->set_quantization_info(input->info()->quantization_info()); + _permute_input.configure(input, &_permuted_input, PermutationVector(1U, 2U, 0U)); + _permuted_input.info()->set_data_layout(DataLayout::NCHW); + + // Configure the function to transform the weights tensor from NHWC -> NCHW + _permuted_weights.info()->set_quantization_info(weights->info()->quantization_info()); + _permute_weights.configure(weights, &_permuted_weights, PermutationVector(1U, 2U, 0U)); + _permuted_weights.info()->set_data_layout(DataLayout::NCHW); + + // Find the upsampled dimensions and the padding needed for the convolution with stride 1 in order to match output shape + unsigned int padx = 0; + unsigned int pady = 0; + const TensorShape scale_out_shape = compute_deconvolution_upsampled_shape(*_permuted_input.info(), *_permuted_weights.info(), stride_x, stride_y, out_dims, padx, + pady); + + TensorInfo scale_out_info(scale_out_shape, 1, _permuted_input.info()->data_type(), _permuted_input.info()->quantization_info()); + scale_out_info.set_data_layout(DataLayout::NCHW); + _scaled_output.allocator()->init(scale_out_info); + + const PadStrideInfo upsample_info(stride_x, stride_y, padx / 2, pady / 2); + _upsample_f.configure(&_permuted_input, &_scaled_output, upsample_info); + + _weights_flipped.allocator()->init(*_permuted_weights.info()->clone()); + _weights_flipped.info()->set_quantization_info(weights->info()->quantization_info()); + _flip_weights.configure(&_permuted_weights, &_weights_flipped); + + // setup the function to convolve the upscaled output + const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL); + + _permuted_output.info()->set_quantization_info(output->info()->quantization_info()); + _conv_f.configure(&_scaled_output, &_weights_flipped, bias, &_permuted_output, conv_info); + _permuted_output.info()->set_data_layout(DataLayout::NCHW); + + // Configure the function to transform the convoluted output to NHWC + _permute_output.configure(&_permuted_output, output, PermutationVector(2U, 0U, 1U)); + + _permuted_input.allocator()->allocate(); + _permuted_weights.allocator()->allocate(); + _permuted_output.allocator()->allocate(); + } + else + { + // Find the upsampled dimensions and the padding needed for the convolution with stride 1 in order to match output shape + unsigned int padx = 0; + unsigned int pady = 0; + const TensorShape scale_out_shape = compute_deconvolution_upsampled_shape(*input->info(), *weights->info(), stride_x, stride_y, out_dims, padx, pady); + + TensorInfo scale_out_info(scale_out_shape, 1, input->info()->data_type(), input->info()->quantization_info()); + scale_out_info.set_data_layout(data_layout); + _scaled_output.allocator()->init(scale_out_info); + const PadStrideInfo upsample_info(stride_x, stride_y, padx / 2, pady / 2); + _upsample_f.configure(input, &_scaled_output, upsample_info); + + _weights_flipped.allocator()->init(weights->info()->clone()->set_data_layout(data_layout)); + _flip_weights.configure(weights, &_weights_flipped); + + // setup the function to convolve the upscaled output + const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL); + _conv_f.configure(&_scaled_output, &_weights_flipped, bias, output, conv_info); + } _scaled_output.allocator()->allocate(); } @@ -151,8 +216,20 @@ void NEDeconvolutionLayer::run() MemoryGroupResourceScope scope_mg(_memory_group); + // Permute input + if(!_is_nchw) + { + _permute_input.run(); + } + _upsample_f.run(); _conv_f.run(); + + // Permute output + if(!_is_nchw) + { + _permute_output.run(); + } } void NEDeconvolutionLayer::prepare() @@ -163,6 +240,11 @@ void NEDeconvolutionLayer::prepare() // Run weights flipping and mark original weights tensor as unused _weights_flipped.allocator()->allocate(); + // Permute weights + if(!_is_nchw) + { + _permute_weights.run(); + } NEScheduler::get().schedule(&_flip_weights, Window::DimZ); _original_weights->mark_as_unused(); diff --git a/tests/validation/NEON/DeconvolutionLayer.cpp b/tests/validation/NEON/DeconvolutionLayer.cpp index 4d0ce63089..34a1bf5a45 100644 --- a/tests/validation/NEON/DeconvolutionLayer.cpp +++ b/tests/validation/NEON/DeconvolutionLayer.cpp @@ -62,7 +62,7 @@ const auto data3x3_precommit = datasets::SmallDeconvolutionShapes() * framework: const auto data1x1 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 4) * framework::dataset::make("StrideY", 1, 4) * framework::dataset::make("PadX", 0, 1) * framework::dataset::make("PadY", 0, 1) * framework::dataset::make("NumKernels", { 3 }); -const auto data_layouts_dataset = framework::dataset::make("DataLayout", { DataLayout::NCHW }); +const auto data_layouts_dataset = framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC }); } // namespace TEST_SUITE(NEON) @@ -250,7 +250,7 @@ TEST_SUITE(W4x4) FIXTURE_DATA_TEST_CASE(Run, NEDeconvolutionLayerQuantizedFixture4x4, framework::DatasetMode::NIGHTLY, combine(combine(combine(data4x4, framework::dataset::make("DataType", DataType::QASYMM8)), data_layouts_dataset), - framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 0)))) + framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 10)))) { // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8, tolerance_num); @@ -261,7 +261,7 @@ TEST_SUITE(W3x3) FIXTURE_DATA_TEST_CASE(RunSmall, NEDeconvolutionLayerQuantizedFixture3x3, framework::DatasetMode::PRECOMMIT, combine(combine(combine(data3x3_precommit, framework::dataset::make("DataType", DataType::QASYMM8)), data_layouts_dataset), - framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 0)))) + framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 10)))) { // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8, tolerance_num); @@ -269,7 +269,7 @@ FIXTURE_DATA_TEST_CASE(RunSmall, NEDeconvolutionLayerQuantizedFixture3x3, framework::DatasetMode::NIGHTLY, combine(combine(combine(data3x3, framework::dataset::make("DataType", DataType::QASYMM8)), data_layouts_dataset), - framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 0)))) + framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 10)))) { // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8, tolerance_num); @@ -280,7 +280,7 @@ TEST_SUITE(W1x1) FIXTURE_DATA_TEST_CASE(Run, NEDeconvolutionLayerQuantizedFixture1x1, framework::DatasetMode::NIGHTLY, combine(combine(combine(data1x1, framework::dataset::make("DataType", DataType::QASYMM8)), data_layouts_dataset), - framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 0)))) + framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 10)))) { // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8, tolerance_num); diff --git a/tests/validation/NEON/Upsample.cpp b/tests/validation/NEON/Upsample.cpp index 39b69ee1e3..9ddfbe0f8e 100644 --- a/tests/validation/NEON/Upsample.cpp +++ b/tests/validation/NEON/Upsample.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 ARM Limited. + * Copyright (c) 2018-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -105,6 +105,9 @@ DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip( template using NEUpsampleLayerFixture = UpsampleLayerFixture; +template +using NEUpsampleLayerQuantizedFixture = UpsampleLayerQuantizedFixture; + TEST_SUITE(Float) TEST_SUITE(FP32) FIXTURE_DATA_TEST_CASE(RunSmall, NEUpsampleLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), @@ -136,11 +139,12 @@ TEST_SUITE_END() // Float TEST_SUITE(Quantized) TEST_SUITE(QASYMM8) -FIXTURE_DATA_TEST_CASE(RunSmall, NEUpsampleLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), - framework::dataset::make("DataType", DataType::QASYMM8)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("PadInfo", { Size2D(2, 2) })), - framework::dataset::make("UpsamplingPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR }))) +FIXTURE_DATA_TEST_CASE(RunSmall, NEUpsampleLayerQuantizedFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(combine(datasets::SmallShapes(), + framework::dataset::make("DataType", DataType::QASYMM8)), + framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), + framework::dataset::make("PadInfo", { Size2D(2, 2) })), + framework::dataset::make("UpsamplingPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR })), + framework::dataset::make("QuantizationInfo", QuantizationInfo(2.f / 255.f, 10)))) { // Validate output validate(Accessor(_target), _reference); diff --git a/tests/validation/fixtures/UpsampleLayerFixture.h b/tests/validation/fixtures/UpsampleLayerFixture.h index 40229e2866..0a72e44dad 100644 --- a/tests/validation/fixtures/UpsampleLayerFixture.h +++ b/tests/validation/fixtures/UpsampleLayerFixture.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 ARM Limited. + * Copyright (c) 2018-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -40,37 +40,51 @@ namespace test namespace validation { template -class UpsampleLayerFixture : public framework::Fixture +class UpsampleLayerFixtureBase : public framework::Fixture { public: template void setup(TensorShape input_shape, DataType data_type, DataLayout data_layout, - Size2D info, const InterpolationPolicy &policy) + Size2D info, const InterpolationPolicy &policy, QuantizationInfo quantization_info) { _data_type = data_type; - _target = compute_target(input_shape, info, policy, data_type, data_layout); - _reference = compute_reference(input_shape, info, policy, data_type); + _target = compute_target(input_shape, info, policy, data_type, data_layout, quantization_info); + _reference = compute_reference(input_shape, info, policy, data_type, quantization_info); } protected: template void fill(U &&tensor, int i) { - library->fill_tensor_uniform(tensor, i); + if(_data_type == DataType::QASYMM8) + { + const auto bounds = get_quantized_bounds(tensor.quantization_info(), -1.0f, 1.0f); + std::uniform_int_distribution distribution(bounds.first, bounds.second); + library->fill(tensor, distribution, i); + } + else + { + library->fill_tensor_uniform(tensor, i); + } } - TensorType compute_target(TensorShape input_shape, - const Size2D &info, const InterpolationPolicy &policy, DataType data_type, DataLayout data_layout) + TensorType compute_target(TensorShape input_shape, const Size2D &info, const InterpolationPolicy &policy, + DataType data_type, DataLayout data_layout, QuantizationInfo quantization_info) { + TensorShape output_shape(input_shape); + output_shape.set(0, info.x() * input_shape[0]); + output_shape.set(1, info.y() * input_shape[1]); + if(data_layout == DataLayout::NHWC) { permute(input_shape, PermutationVector(2U, 0U, 1U)); + permute(output_shape, PermutationVector(2U, 0U, 1U)); } // Create tensors - TensorType src = create_tensor(input_shape, data_type, 1, QuantizationInfo(), data_layout); - TensorType dst; + TensorType src = create_tensor(input_shape, data_type, 1, quantization_info, data_layout); + TensorType dst = create_tensor(output_shape, data_type, 1, quantization_info, data_layout); // Create and configure function FunctionType upsample; @@ -95,11 +109,11 @@ protected: return dst; } - SimpleTensor compute_reference(const TensorShape &input_shape, - const Size2D &info, const InterpolationPolicy &policy, DataType data_type) + SimpleTensor compute_reference(const TensorShape &input_shape, const Size2D &info, const InterpolationPolicy &policy, + DataType data_type, QuantizationInfo quantization_info) { // Create reference - SimpleTensor src{ input_shape, data_type }; + SimpleTensor src{ input_shape, data_type, 1, quantization_info }; // Fill reference fill(src, 0); @@ -111,6 +125,33 @@ protected: SimpleTensor _reference{}; DataType _data_type{}; }; + +template +class UpsampleLayerFixture : public UpsampleLayerFixtureBase +{ +public: + template + void setup(TensorShape input_shape, DataType data_type, DataLayout data_layout, + Size2D info, const InterpolationPolicy &policy) + { + UpsampleLayerFixtureBase::setup(input_shape, data_type, data_layout, + info, policy, QuantizationInfo()); + } +}; + +template +class UpsampleLayerQuantizedFixture : public UpsampleLayerFixtureBase +{ +public: + template + void setup(TensorShape input_shape, DataType data_type, DataLayout data_layout, + Size2D info, const InterpolationPolicy &policy, QuantizationInfo quantization_info) + { + UpsampleLayerFixtureBase::setup(input_shape, data_type, data_layout, + info, policy, quantization_info); + } +}; + } // namespace validation } // namespace test } // namespace arm_compute diff --git a/tests/validation/reference/UpsampleLayer.cpp b/tests/validation/reference/UpsampleLayer.cpp index 876f6d794a..8e36ee857e 100644 --- a/tests/validation/reference/UpsampleLayer.cpp +++ b/tests/validation/reference/UpsampleLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 ARM Limited. + * Copyright (c) 2018-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -33,9 +33,10 @@ namespace validation { namespace reference { +namespace +{ template -SimpleTensor upsample_layer(const SimpleTensor &src, - const Size2D &info, const InterpolationPolicy policy) +SimpleTensor upsample_function(const SimpleTensor &src, const Size2D &info, const InterpolationPolicy policy) { ARM_COMPUTE_ERROR_ON(policy != InterpolationPolicy::NEAREST_NEIGHBOR); ARM_COMPUTE_UNUSED(policy); @@ -72,16 +73,39 @@ SimpleTensor upsample_layer(const SimpleTensor &src, } } } - return out; } +} // namespace + +template +SimpleTensor upsample_layer(const SimpleTensor &src, const Size2D &info, const InterpolationPolicy policy) +{ + return upsample_function(src, info, policy); +} + +template <> +SimpleTensor upsample_layer(const SimpleTensor &src, const Size2D &info, const InterpolationPolicy policy) +{ + SimpleTensor dst(src.shape(), src.data_type(), 1, src.quantization_info()); + + if(is_data_type_quantized_asymmetric(src.data_type())) + { + SimpleTensor src_tmp = convert_from_asymmetric(src); + SimpleTensor dst_tmp = upsample_function(src_tmp, info, policy); + dst = convert_to_asymmetric(dst_tmp, src.quantization_info()); + } + else + { + dst = upsample_function(src, info, policy); + } + return dst; +} + template SimpleTensor upsample_layer(const SimpleTensor &src, const Size2D &info, const InterpolationPolicy policy); template SimpleTensor upsample_layer(const SimpleTensor &src, const Size2D &info, const InterpolationPolicy policy); -template SimpleTensor upsample_layer(const SimpleTensor &src, - const Size2D &info, const InterpolationPolicy policy); } // namespace reference } // namespace validation } // namespace test -- cgit v1.2.1