blob: be6ff387f3ef93f79600db1e11ab6a28ebf7f592 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
telsoa014fcda012018-03-09 14:13:49 +00002// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5
6#include "Pooling2d.hpp"
7
8#include <armnn/Exceptions.hpp>
9#include <armnn/Types.hpp>
10
Matteo Martincighe011d202019-11-28 11:35:47 +000011#include <armnnUtils/DataLayoutIndexed.hpp>
Matthew Sloyan171214c2020-09-09 09:07:37 +010012#include <armnn/utility/NumericCast.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000013
telsoa014fcda012018-03-09 14:13:49 +000014#include <boost/numeric/conversion/cast.hpp>
15
16#include <limits>
17#include <algorithm>
18#include <functional>
19
20namespace
21{
22 using PoolingAlgorithm = armnn::PoolingAlgorithm;
23
24 float DefaultInitializer(PoolingAlgorithm algorithm)
25 {
26 switch (algorithm)
27 {
28 case PoolingAlgorithm::Max:
29 {
30 return std::numeric_limits<float>::lowest();
31 }
32 case PoolingAlgorithm::Average:
33 case PoolingAlgorithm::L2:
34 {
35 return 0.0f;
36 }
37 default:
38 {
39 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
40 }
41 }
42 }
43
44 using Accumulator = std::function<void(float & accu, float value)>;
45
46 Accumulator GetAccumulator(PoolingAlgorithm algorithm)
47 {
48 switch (algorithm)
49 {
50 case PoolingAlgorithm::Max:
51 {
52 return [](float & accu, float value) {
53 if (value > accu) {
54 accu = value;
55 }
56 };
57 }
58
59 case PoolingAlgorithm::Average:
60 {
61 return [](float & accu, float value) {
62 accu += value;
63 };
64 }
65
66 case PoolingAlgorithm::L2:
67 {
68 return [](float & accu, float value) {
69 accu += (value*value);
70 };
71 }
72
73 default:
74 {
75 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
76 }
77 }
78 }
79
80 using Executor = std::function<void(float & accumulated, float kernelSize)>;
81
82 Executor GetExecutor(PoolingAlgorithm algorithm)
83 {
84 switch (algorithm)
85 {
86 case PoolingAlgorithm::Max:
87 {
Derek Lamberti901ea112019-12-10 22:07:09 +000088 return [](float & /*accumulated*/, float /*kernelSize*/) {};
telsoa014fcda012018-03-09 14:13:49 +000089 }
90
91 case PoolingAlgorithm::Average:
92 {
93 return [](float & accumulated, float kernelSize) {
94 accumulated /= kernelSize;
95 };
96 }
97
98 case PoolingAlgorithm::L2:
99 {
100 return [](float & accumulated, float kernelSize) {
101 accumulated = sqrtf(accumulated / kernelSize);
102 };
103 }
104
105 default:
106 {
107 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
108 }
109 }
110 }
111
Finn Williams70f609b2019-11-06 16:54:53 +0000112 bool OnPaddingOnly(int start, int end, int maxRange)
telsoa014fcda012018-03-09 14:13:49 +0000113 {
Finn Williams70f609b2019-11-06 16:54:53 +0000114 if (end <= 0 || start > maxRange)
telsoa014fcda012018-03-09 14:13:49 +0000115 {
116 return true;
117 }
118 else
119 {
120 return false;
121 }
122 }
123
124
125 bool ClampRange(int & start, int & end, int maxRange)
126 {
127 if (start < 0 || end > maxRange)
128 {
129 start = std::min(std::max(start, 0), maxRange);
130 end = std::min(std::max(end, 0), maxRange);
131 return true;
132 }
133 else
134 {
135 return false;
136 }
137 }
138}
139
Matteo Martincigh21350152018-11-28 16:22:22 +0000140using namespace armnnUtils;
141
telsoa014fcda012018-03-09 14:13:49 +0000142namespace armnn
143{
Teresa Charlina3b20472019-06-06 11:12:32 +0100144void Pooling2d(Decoder<float>& rInputDecoder,
145 Encoder<float>& rOutputEncoder,
telsoa014fcda012018-03-09 14:13:49 +0000146 const TensorInfo& inputInfo,
147 const TensorInfo& outputInfo,
148 const Pooling2dDescriptor& params)
149{
Teresa Charlina3b20472019-06-06 11:12:32 +0100150 const DataLayoutIndexed dataLayout(params.m_DataLayout);
James Conroy45a9b772018-10-31 11:47:53 +0000151 auto channelsIndex = dataLayout.GetChannelsIndex();
152 auto heightIndex = dataLayout.GetHeightIndex();
153 auto widthIndex = dataLayout.GetWidthIndex();
James Conroy69482272018-10-19 10:41:35 +0100154
Matthew Sloyan171214c2020-09-09 09:07:37 +0100155 const int batchSize = armnn::numeric_cast<int>(outputInfo.GetShape()[0]);
156 const int channels = armnn::numeric_cast<int>(outputInfo.GetShape()[channelsIndex]);
157 const int heightOutput = armnn::numeric_cast<int>(outputInfo.GetShape()[heightIndex]);
158 const int widthOutput = armnn::numeric_cast<int>(outputInfo.GetShape()[widthIndex]);
159 const int heightInput = armnn::numeric_cast<int>(inputInfo.GetShape()[heightIndex]);
160 const int widthInput = armnn::numeric_cast<int>(inputInfo.GetShape()[widthIndex]);
161 const int padLeft = armnn::numeric_cast<int>(params.m_PadLeft);
162 const int padRight = armnn::numeric_cast<int>(params.m_PadRight);
163 const int padTop = armnn::numeric_cast<int>(params.m_PadTop);
164 const int padBottom = armnn::numeric_cast<int>(params.m_PadBottom);
165 const int strideX = armnn::numeric_cast<int>(params.m_StrideX);
166 const int strideY = armnn::numeric_cast<int>(params.m_StrideY);
167 const int poolHeight = armnn::numeric_cast<int>(params.m_PoolHeight);
168 const int poolWidth = armnn::numeric_cast<int>(params.m_PoolWidth);
telsoa014fcda012018-03-09 14:13:49 +0000169
170 float defaultInitializer = DefaultInitializer(params.m_PoolType);
171
172 Accumulator accumulate = GetAccumulator(params.m_PoolType);
173 Executor execute = GetExecutor(params.m_PoolType);
174
175 // Check supported padding methods outside the loop to simplify
telsoa01c577f2c2018-08-31 09:22:23 +0100176 // the inner loop.
telsoa014fcda012018-03-09 14:13:49 +0000177 if (params.m_PaddingMethod != PaddingMethod::Exclude &&
178 params.m_PaddingMethod != PaddingMethod::IgnoreValue)
179 {
180 throw armnn::InvalidArgumentException("Unsupported padding type");
181 }
182
Finn Williamsea8ce702020-09-29 19:54:00 +0100183 const std::vector<float> decodedInputVec = rInputDecoder.DecodeTensor(inputInfo.GetShape());
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100184
telsoa014fcda012018-03-09 14:13:49 +0000185 for (int n = 0; n < batchSize; n++)
186 {
187 for (int c = 0; c < channels; c++)
188 {
189 for (int yOutput = 0; yOutput < heightOutput; yOutput++)
190 {
Finn Williams70f609b2019-11-06 16:54:53 +0000191 // Calculate values independent of the x axis
192 int hstart = (yOutput * strideY) - padTop;
193 int hend = hstart + poolHeight;
194 // Clamp the pooling region inside the valid input area (which includes the padding).
195 // This is necessary because the final pooling in a row may overlap beyond the padding.
196 hend = std::min(hend, heightInput + padBottom);
197
198 int height = hend - hstart;
199 bool hclamped = ClampRange(hstart, hend, heightInput);
200
telsoa014fcda012018-03-09 14:13:49 +0000201 for (int xOutput = 0; xOutput < widthOutput; xOutput++)
202 {
telsoa014fcda012018-03-09 14:13:49 +0000203 int wstart = (xOutput * strideX) - padLeft;
telsoa014fcda012018-03-09 14:13:49 +0000204 int wend = wstart + poolWidth;
205
206 // Clamp the pooling region inside the valid input area (which includes the padding).
207 // This is necessary because the final pooling in a row may overlap beyond the padding.
surmeh01bceff2f2018-03-29 16:29:27 +0100208 wend = std::min(wend, widthInput + padRight);
telsoa014fcda012018-03-09 14:13:49 +0000209
210 float result = defaultInitializer;
Finn Williams70f609b2019-11-06 16:54:53 +0000211 float poolAreaSize = boost::numeric_cast<float>(height * (wend - wstart));
telsoa014fcda012018-03-09 14:13:49 +0000212
telsoa01c577f2c2018-08-31 09:22:23 +0100213 // Special case: when the pooling kernel is over a padding region and the padding
telsoa014fcda012018-03-09 14:13:49 +0000214 // size is larger or equal to the kernel and the kernel only covers
215 // padding and no real values, then we initialize the result as zero
216 // by convention. This is because we need to choose a value here and
217 // all values we have are padding, which we ignore.
Finn Williams70f609b2019-11-06 16:54:53 +0000218 if (OnPaddingOnly(hstart, hend, heightInput) ||
219 OnPaddingOnly(wstart, wend, widthInput))
telsoa014fcda012018-03-09 14:13:49 +0000220 {
221 result = 0.0f;
Finn Williams70f609b2019-11-06 16:54:53 +0000222
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100223 int outputIndex;
224
225 if(dataLayout.GetDataLayout() == DataLayout::NHWC)
226 {
227 outputIndex = n * heightOutput * widthOutput * channels +
228 yOutput * widthOutput * channels +
229 xOutput * channels +
230 c;
231 }
232 else
233 {
234 outputIndex = n * heightOutput * widthOutput * channels +
235 c * heightOutput * widthOutput +
236 yOutput * widthOutput +
237 xOutput;
238 }
239
240 rOutputEncoder[static_cast<unsigned int>(outputIndex)];
Finn Williams70f609b2019-11-06 16:54:53 +0000241 rOutputEncoder.Set(result);
242 continue;
telsoa014fcda012018-03-09 14:13:49 +0000243 }
244
Finn Williams70f609b2019-11-06 16:54:53 +0000245 bool clamped = hclamped |= ClampRange(wstart, wend, widthInput);
telsoa014fcda012018-03-09 14:13:49 +0000246
247 if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
248 {
telsoa01c577f2c2018-08-31 09:22:23 +0100249 // When we exclude the padding, it means we calculate with a smaller
250 // kernel size, so I changed the divisor here.
telsoa014fcda012018-03-09 14:13:49 +0000251 poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
252 }
253
254 for (auto yInput = hstart; yInput < hend; yInput++)
255 {
256 for (auto xInput = wstart; xInput < wend; xInput++)
257 {
Teresa Charlina3b20472019-06-06 11:12:32 +0100258
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100259 int inputIndex;
260 if(dataLayout.GetDataLayout() == DataLayout::NHWC)
261 {
262 inputIndex = n * heightInput * widthInput * channels +
263 yInput * widthInput * channels +
264 xInput * channels +
265 c;
telsoa014fcda012018-03-09 14:13:49 +0000266
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100267 }
268 else
269 {
270 inputIndex = n * heightInput * widthInput * channels +
271 c * heightInput * widthInput +
272 yInput * widthInput +
273 xInput;
274 }
275
276 accumulate(result, decodedInputVec[static_cast<unsigned int>(inputIndex)]);
telsoa014fcda012018-03-09 14:13:49 +0000277 }
278 }
279
280 execute(result, poolAreaSize);
281
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100282 int outputIndex;
Teresa Charlina3b20472019-06-06 11:12:32 +0100283
Finn Williamsb9dcfe62020-09-17 15:58:31 +0100284 if(dataLayout.GetDataLayout() == DataLayout::NHWC)
285 {
286 outputIndex = n * heightOutput * widthOutput * channels +
287 yOutput * widthOutput * channels +
288 xOutput * channels +
289 c;
290 }
291 else
292 {
293 outputIndex = n * heightOutput * widthOutput * channels +
294 c * heightOutput * widthOutput +
295 yOutput * widthOutput +
296 xOutput;
297 }
298
299 rOutputEncoder[static_cast<unsigned int>(outputIndex)];
Teresa Charlina3b20472019-06-06 11:12:32 +0100300 rOutputEncoder.Set(result);
telsoa014fcda012018-03-09 14:13:49 +0000301 }
302 }
303 }
304 }
305}
306
307} //namespace armnn