blob: 7388fed147667040c1234d973d73f5510c30921b [file] [log] [blame]
Matthew Sloyan2e5d0b22021-10-21 14:05:31 +01001//
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
12namespace
13{
14
15// Convert a linear index into n-dimensional coordinates.
16// E.g. index = 2 returns [0, 0, 2].
17inline 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.
37inline 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
56namespace armnn
57{
58
59void 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 {
159 case PaddingMode::Reflect:
160 {
161 for(unsigned int i = 0; i < numInputDimensions; ++i)
162 {
163 coordOfInput.emplace_back(getReflectIndex(i));
164 }
165 break;
166 }
167 case PaddingMode::Symmetric:
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