diff options
author | Matthew Sloyan <matthew.sloyan@arm.com> | 2021-10-21 14:05:31 +0100 |
---|---|---|
committer | Teresa Charlin <teresa.charlinreyes@arm.com> | 2021-10-27 20:54:21 +0100 |
commit | 2e5d0b2e2a212ceb803681b717cbaf821f5e0929 (patch) | |
tree | dd2364c8cd2845bd880191526a5eeb51eee7a2d4 /src/backends/reference/workloads | |
parent | 65b86d4a42f3a55322d4bd4d8dccf6cf22775a30 (diff) | |
download | armnn-2e5d0b2e2a212ceb803681b717cbaf821f5e0929.tar.gz |
IVGCVSW-6469 Add MirrorPad FrontEnd and Ref Support
* Added PaddingMode enum to PaddingDescriptor to enable Symmetric and
Reflect padding.
* Added Symmetric and Reflect Ref implementation.
* Added Serializer & Deserializer support.
* Added unit tests.
Signed-off-by: Matthew Sloyan <matthew.sloyan@arm.com>
Change-Id: I4bed907b31742b32ccefe5e8ca39a6f1e5bd9dee
Diffstat (limited to 'src/backends/reference/workloads')
-rw-r--r-- | src/backends/reference/workloads/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/backends/reference/workloads/MirrorPad.cpp | 199 | ||||
-rw-r--r-- | src/backends/reference/workloads/MirrorPad.hpp | 22 | ||||
-rw-r--r-- | src/backends/reference/workloads/RefPadWorkload.cpp | 19 |
4 files changed, 237 insertions, 5 deletions
diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt index 5727291be3..f212522895 100644 --- a/src/backends/reference/workloads/CMakeLists.txt +++ b/src/backends/reference/workloads/CMakeLists.txt @@ -52,6 +52,8 @@ list(APPEND armnnRefBackendWorkloads_sources Concatenate.hpp Concatenate.cpp Minimum.hpp + MirrorPad.cpp + MirrorPad.hpp Pad.cpp Pad.hpp Pooling2d.cpp diff --git a/src/backends/reference/workloads/MirrorPad.cpp b/src/backends/reference/workloads/MirrorPad.cpp new file mode 100644 index 0000000000..7388fed147 --- /dev/null +++ b/src/backends/reference/workloads/MirrorPad.cpp @@ -0,0 +1,199 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "MirrorPad.hpp" + +#include "BaseIterator.hpp" +#include "Decoders.hpp" +#include "Encoders.hpp" + +namespace +{ + +// Convert a linear index into n-dimensional coordinates. +// E.g. index = 2 returns [0, 0, 2]. +inline std::vector<unsigned int> IndexToCoord(const armnn::TensorShape& shape, unsigned int index) +{ + unsigned int numOfElements = shape.GetNumElements(); + + ARMNN_ASSERT_MSG(index <= numOfElements, "Index has to be in [0, num_elements]"); + ARMNN_ASSERT_MSG(numOfElements != 0, "Cannot create coordinate from empty shape"); + + std::vector<unsigned int> coord(shape.GetNumDimensions()); + for(unsigned int i = 0; i < shape.GetNumDimensions(); ++i) + { + numOfElements /= shape[i]; + coord[i] = index / numOfElements; + index %= numOfElements; + } + + return coord; +} + +// Returns the index of a given coordinate. +// E.g. [0, 0, 2] returns 2. +inline unsigned int CoordToIndex(const armnn::TensorShape& shape, const std::vector<unsigned int>& coord) +{ + ARMNN_ASSERT_MSG(shape.GetNumDimensions() != 0, "Cannot get index from empty shape"); + ARMNN_ASSERT_MSG(coord.size() != 0, "Cannot get index of empty coordinate"); + + unsigned int index = 0; + unsigned int dimSize = 1; + + for (unsigned int i = shape.GetNumDimensions(); i > 0; --i) + { + index += coord[i - 1] * dimSize; + dimSize *= shape[i - 1]; + } + + return index; +} + +} // anonymous namespace + +namespace armnn +{ + +void MirrorPad(const TensorInfo& inputInfo, + const TensorInfo& outputInfo, + const ITensorHandle* inputHandle, + ITensorHandle* outputHandle, + const PadQueueDescriptor& data) +{ + auto padList = data.m_Parameters.m_PadList; + PaddingMode paddingMode = data.m_Parameters.m_PaddingMode; + + TensorShape outputShape = outputInfo.GetShape(); + TensorShape inputShape = inputInfo.GetShape(); + + unsigned int numOutputElements = outputInfo.GetNumElements(); + unsigned int numInputDimensions = inputShape.GetNumDimensions(); + assert(numInputDimensions == outputShape.GetNumDimensions()); + + // If padding mode is Reflect then both paddings must be no greater than inputShape(i) - 1. + // If padding mode is Symmetric then both paddings must be no greater than inputShape(i). + const unsigned int isReflect = static_cast<unsigned int>(paddingMode == PaddingMode::Reflect); + for(unsigned int i = 0; i < padList.size(); ++i) + { + if(padList.at(i).first > (inputShape[i] - isReflect) || + padList.at(i).second > (inputShape[i] - isReflect)) + { + throw armnn::InvalidArgumentException("Paddings must be less (Reflect) or " + "equal (Symmetric) to the dimension size."); + } + } + + auto inputData = MakeDecoder<float>(inputInfo, inputHandle->Map()); + auto outData = MakeEncoder<float>(outputInfo, outputHandle->Map()); + + Decoder<float>& input = *inputData; + Encoder<float>& output = *outData; + + for(unsigned int idx = 0; idx < numOutputElements; ++idx) + { + // Get the coordinates of the current index in vector form. E.g inx 1 = [0, 0, 0, 1 ] + const std::vector<unsigned int> coord = IndexToCoord(outputShape, idx); + + std::vector<unsigned int> dimensions; + std::vector<unsigned int> coords; + + for(unsigned int i = 0; i < numInputDimensions; ++i) + { + dimensions.emplace_back(i); + coords.emplace_back(coord[i]); + } + + auto isInPadding = [&](unsigned int i) + { + return (coords[i] < padList[i].first || coords[i] > inputShape[i] + padList[i].first - 1); + }; + + auto getReflectIndex = [&](unsigned int i) -> unsigned int + { + if(isInPadding(i)) + { + if(coords[i] < padList[i].first) + { + return padList[i].first - coords[i]; + } + else + { + return 2 * inputShape[i] + padList[i].first - 2 - coords[i]; + } + } + return coords[i] - padList[i].first; + }; + + auto getSymmetricIndex = [&](unsigned int i) -> unsigned int + { + if(isInPadding(i)) + { + if(coords[i] < padList[i].first) + { + return padList[i].first - coords[i] - 1; + } + else + { + return 2 * inputShape[i] + padList[i].first - 1 - coords[i]; + } + } + return coords[i] - padList[i].first; + }; + + // Location of the value in the input tensor to use in the output. + std::vector<unsigned int> coordOfInput; + + // any_of works as a loop here to check if any of the dimensions are in the padding. + // If dimensions is in the padding area, then create the coordinates of the location in the + // input tensor to use in the output. + // E.g. + // Input tensor = [ 1, 2, 3 ], Rank = 1. + // Output tensor = [ 2, 1, 2, 3, 1 ] if Reflect or [ 1, 1, 2, 3, 3 ] if Symmetric with a padding of (1, 1). + // So it will either return [ 1 ] or [ 0 ] which is used to set the first value in the output tensor and so on. + if(std::any_of(dimensions.begin(), dimensions.end(), isInPadding)) + { + switch(paddingMode) + { + case PaddingMode::Reflect: + { + for(unsigned int i = 0; i < numInputDimensions; ++i) + { + coordOfInput.emplace_back(getReflectIndex(i)); + } + break; + } + case PaddingMode::Symmetric: + { + for(unsigned int i = 0; i < numInputDimensions; ++i) + { + coordOfInput.emplace_back(getSymmetricIndex(i)); + } + break; + } + default: + throw InvalidArgumentException("Padding mode not supported."); + break; + } + } + else + { + for(unsigned int i = 0; i < numInputDimensions; ++i) + { + coordOfInput.emplace_back(coord[i] - padList[i].first); + } + } + + // Set output value using the coordinate of the input value to use. + const unsigned int indexOfInput = CoordToIndex(inputShape, coordOfInput); + + input[indexOfInput]; + auto inputValue = input.Get(); + + output[idx]; + output.Set(inputValue); + } +} + +} //namespace armnn
\ No newline at end of file diff --git a/src/backends/reference/workloads/MirrorPad.hpp b/src/backends/reference/workloads/MirrorPad.hpp new file mode 100644 index 0000000000..3deaf1d5fd --- /dev/null +++ b/src/backends/reference/workloads/MirrorPad.hpp @@ -0,0 +1,22 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "armnn/Tensor.hpp" + +#include <backendsCommon/Workload.hpp> +#include <backendsCommon/WorkloadData.hpp> + +namespace armnn +{ + +void MirrorPad(const TensorInfo& inputInfo, + const TensorInfo& outputInfo, + const ITensorHandle* inputHandle, + ITensorHandle* outputHandle, + const PadQueueDescriptor& data); + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefPadWorkload.cpp b/src/backends/reference/workloads/RefPadWorkload.cpp index f15306d1af..fd0728c8cd 100644 --- a/src/backends/reference/workloads/RefPadWorkload.cpp +++ b/src/backends/reference/workloads/RefPadWorkload.cpp @@ -5,6 +5,7 @@ #include "RefPadWorkload.hpp" +#include "MirrorPad.hpp" #include "Pad.hpp" #include "Profiling.hpp" #include "RefWorkloadUtils.hpp" @@ -29,11 +30,19 @@ void RefPadWorkload::Execute(std::vector<ITensorHandle*> inputs, std::vector<ITe const TensorInfo& inputInfo = GetTensorInfo(inputs[0]); const TensorInfo& outputInfo = GetTensorInfo(outputs[0]); - armnn::Pad(inputInfo, - outputInfo, - inputs[0], - outputs[0], - m_Data); + PaddingMode paddingMode = m_Data.m_Parameters.m_PaddingMode; + if (paddingMode == PaddingMode::Constant) + { + armnn::Pad(inputInfo, outputInfo, inputs[0], outputs[0], m_Data); + } + else if(paddingMode == PaddingMode::Reflect || paddingMode == PaddingMode::Symmetric) + { + armnn::MirrorPad(inputInfo, outputInfo, inputs[0], outputs[0], m_Data); + } + else + { + throw InvalidArgumentException("Padding mode not supported."); + } } } //namespace armnn
\ No newline at end of file |