ArmNN
 24.02
MirrorPad.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "MirrorPad.hpp"
7 
8 #include "BaseIterator.hpp"
9 #include "Decoders.hpp"
10 #include "Encoders.hpp"
11 
12 namespace
13 {
14 
15 // Convert a linear index into n-dimensional coordinates.
16 // E.g. index = 2 returns [0, 0, 2].
17 inline std::vector<unsigned int> IndexToCoord(const armnn::TensorShape& shape, unsigned int index)
18 {
19  unsigned int numOfElements = shape.GetNumElements();
20 
21  ARMNN_ASSERT_MSG(index <= numOfElements, "Index has to be in [0, num_elements]");
22  ARMNN_ASSERT_MSG(numOfElements != 0, "Cannot create coordinate from empty shape");
23 
24  std::vector<unsigned int> coord(shape.GetNumDimensions());
25  for(unsigned int i = 0; i < shape.GetNumDimensions(); ++i)
26  {
27  numOfElements /= shape[i];
28  coord[i] = index / numOfElements;
29  index %= numOfElements;
30  }
31 
32  return coord;
33 }
34 
35 // Returns the index of a given coordinate.
36 // E.g. [0, 0, 2] returns 2.
37 inline unsigned int CoordToIndex(const armnn::TensorShape& shape, const std::vector<unsigned int>& coord)
38 {
39  ARMNN_ASSERT_MSG(shape.GetNumDimensions() != 0, "Cannot get index from empty shape");
40  ARMNN_ASSERT_MSG(coord.size() != 0, "Cannot get index of empty coordinate");
41 
42  unsigned int index = 0;
43  unsigned int dimSize = 1;
44 
45  for (unsigned int i = shape.GetNumDimensions(); i > 0; --i)
46  {
47  index += coord[i - 1] * dimSize;
48  dimSize *= shape[i - 1];
49  }
50 
51  return index;
52 }
53 
54 } // anonymous namespace
55 
56 namespace armnn
57 {
58 
59 void MirrorPad(const TensorInfo& inputInfo,
60  const TensorInfo& outputInfo,
61  const ITensorHandle* inputHandle,
62  ITensorHandle* outputHandle,
63  const PadQueueDescriptor& data)
64 {
65  auto padList = data.m_Parameters.m_PadList;
66  PaddingMode paddingMode = data.m_Parameters.m_PaddingMode;
67 
68  TensorShape outputShape = outputInfo.GetShape();
69  TensorShape inputShape = inputInfo.GetShape();
70 
71  unsigned int numOutputElements = outputInfo.GetNumElements();
72  unsigned int numInputDimensions = inputShape.GetNumDimensions();
73  assert(numInputDimensions == outputShape.GetNumDimensions());
74 
75  // If padding mode is Reflect then both paddings must be no greater than inputShape(i) - 1.
76  // If padding mode is Symmetric then both paddings must be no greater than inputShape(i).
77  const unsigned int isReflect = static_cast<unsigned int>(paddingMode == PaddingMode::Reflect);
78  for(unsigned int i = 0; i < padList.size(); ++i)
79  {
80  if(padList.at(i).first > (inputShape[i] - isReflect) ||
81  padList.at(i).second > (inputShape[i] - isReflect))
82  {
83  throw armnn::InvalidArgumentException("Paddings must be less (Reflect) or "
84  "equal (Symmetric) to the dimension size.");
85  }
86  }
87 
88  auto inputData = MakeDecoder<float>(inputInfo, inputHandle->Map());
89  auto outData = MakeEncoder<float>(outputInfo, outputHandle->Map());
90 
91  Decoder<float>& input = *inputData;
92  Encoder<float>& output = *outData;
93 
94  for(unsigned int idx = 0; idx < numOutputElements; ++idx)
95  {
96  // Get the coordinates of the current index in vector form. E.g inx 1 = [0, 0, 0, 1 ]
97  const std::vector<unsigned int> coord = IndexToCoord(outputShape, idx);
98 
99  std::vector<unsigned int> dimensions;
100  std::vector<unsigned int> coords;
101 
102  for(unsigned int i = 0; i < numInputDimensions; ++i)
103  {
104  dimensions.emplace_back(i);
105  coords.emplace_back(coord[i]);
106  }
107 
108  auto isInPadding = [&](unsigned int i)
109  {
110  return (coords[i] < padList[i].first || coords[i] > inputShape[i] + padList[i].first - 1);
111  };
112 
113  auto getReflectIndex = [&](unsigned int i) -> unsigned int
114  {
115  if(isInPadding(i))
116  {
117  if(coords[i] < padList[i].first)
118  {
119  return padList[i].first - coords[i];
120  }
121  else
122  {
123  return 2 * inputShape[i] + padList[i].first - 2 - coords[i];
124  }
125  }
126  return coords[i] - padList[i].first;
127  };
128 
129  auto getSymmetricIndex = [&](unsigned int i) -> unsigned int
130  {
131  if(isInPadding(i))
132  {
133  if(coords[i] < padList[i].first)
134  {
135  return padList[i].first - coords[i] - 1;
136  }
137  else
138  {
139  return 2 * inputShape[i] + padList[i].first - 1 - coords[i];
140  }
141  }
142  return coords[i] - padList[i].first;
143  };
144 
145  // Location of the value in the input tensor to use in the output.
146  std::vector<unsigned int> coordOfInput;
147 
148  // any_of works as a loop here to check if any of the dimensions are in the padding.
149  // If dimensions is in the padding area, then create the coordinates of the location in the
150  // input tensor to use in the output.
151  // E.g.
152  // Input tensor = [ 1, 2, 3 ], Rank = 1.
153  // Output tensor = [ 2, 1, 2, 3, 1 ] if Reflect or [ 1, 1, 2, 3, 3 ] if Symmetric with a padding of (1, 1).
154  // So it will either return [ 1 ] or [ 0 ] which is used to set the first value in the output tensor and so on.
155  if(std::any_of(dimensions.begin(), dimensions.end(), isInPadding))
156  {
157  switch(paddingMode)
158  {
160  {
161  for(unsigned int i = 0; i < numInputDimensions; ++i)
162  {
163  coordOfInput.emplace_back(getReflectIndex(i));
164  }
165  break;
166  }
168  {
169  for(unsigned int i = 0; i < numInputDimensions; ++i)
170  {
171  coordOfInput.emplace_back(getSymmetricIndex(i));
172  }
173  break;
174  }
175  default:
176  throw InvalidArgumentException("Padding mode not supported.");
177  break;
178  }
179  }
180  else
181  {
182  for(unsigned int i = 0; i < numInputDimensions; ++i)
183  {
184  coordOfInput.emplace_back(coord[i] - padList[i].first);
185  }
186  }
187 
188  // Set output value using the coordinate of the input value to use.
189  const unsigned int indexOfInput = CoordToIndex(inputShape, coordOfInput);
190 
191  input[indexOfInput];
192  auto inputValue = input.Get();
193 
194  output[idx];
195  output.Set(inputValue);
196  }
197 }
198 
199 } //namespace armnn
armnn::PaddingMode::Symmetric
@ Symmetric
armnn::Decoder< float >
armnn::TensorInfo::GetNumElements
unsigned int GetNumElements() const
Definition: Tensor.hpp:198
armnn::Encoder::Set
virtual void Set(IType right)=0
BaseIterator.hpp
armnn::TensorInfo
Definition: Tensor.hpp:152
MirrorPad.hpp
armnn::ITensorHandle
Definition: ITensorHandle.hpp:16
armnn::PaddingMode
PaddingMode
The padding mode controls whether the padding should be filled with constant values (Constant),...
Definition: Types.hpp:200
ARMNN_ASSERT_MSG
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
armnn::TensorShape
Definition: Tensor.hpp:20
armnn::Encoder< float >
armnn::QueueDescriptorWithParameters::m_Parameters
LayerDescriptor m_Parameters
Definition: WorkloadData.hpp:66
armnn::TensorShape::GetNumDimensions
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
armnn::MirrorPad
void MirrorPad(const TensorInfo &inputInfo, const TensorInfo &outputInfo, const ITensorHandle *inputHandle, ITensorHandle *outputHandle, const PadQueueDescriptor &data)
Definition: MirrorPad.cpp:59
armnn::InvalidArgumentException
Definition: Exceptions.hpp:80
armnn::Decoder::Get
virtual IType Get() const =0
armnn::TensorInfo::GetShape
const TensorShape & GetShape() const
Definition: Tensor.hpp:193
armnn::PadQueueDescriptor
Definition: WorkloadData.hpp:294
armnn::PadDescriptor::m_PadList
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
Definition: Descriptors.hpp:1218
Decoders.hpp
armnn
Copyright (c) 2021 ARM Limited and Contributors.
Definition: 01_00_quick_start.dox:6
armnn::PadDescriptor::m_PaddingMode
PaddingMode m_PaddingMode
Specifies the Padding mode (Constant, Reflect or Symmetric)
Definition: Descriptors.hpp:1224
Encoders.hpp
armnn::PaddingMode::Reflect
@ Reflect
armnn::TensorShape::GetNumElements
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified.
Definition: Tensor.cpp:181
armnn::ITensorHandle::Map
virtual const void * Map(bool blocking=true) const =0
Map the tensor data for access.