blob: ea8f4ee7e759f1af87a7930c678ee6653e97a917 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// 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>
12
telsoa014fcda012018-03-09 14:13:49 +000013#include <boost/numeric/conversion/cast.hpp>
14
15#include <limits>
16#include <algorithm>
17#include <functional>
18
19namespace
20{
21 using PoolingAlgorithm = armnn::PoolingAlgorithm;
22
23 float DefaultInitializer(PoolingAlgorithm algorithm)
24 {
25 switch (algorithm)
26 {
27 case PoolingAlgorithm::Max:
28 {
29 return std::numeric_limits<float>::lowest();
30 }
31 case PoolingAlgorithm::Average:
32 case PoolingAlgorithm::L2:
33 {
34 return 0.0f;
35 }
36 default:
37 {
38 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
39 }
40 }
41 }
42
43 using Accumulator = std::function<void(float & accu, float value)>;
44
45 Accumulator GetAccumulator(PoolingAlgorithm algorithm)
46 {
47 switch (algorithm)
48 {
49 case PoolingAlgorithm::Max:
50 {
51 return [](float & accu, float value) {
52 if (value > accu) {
53 accu = value;
54 }
55 };
56 }
57
58 case PoolingAlgorithm::Average:
59 {
60 return [](float & accu, float value) {
61 accu += value;
62 };
63 }
64
65 case PoolingAlgorithm::L2:
66 {
67 return [](float & accu, float value) {
68 accu += (value*value);
69 };
70 }
71
72 default:
73 {
74 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
75 }
76 }
77 }
78
79 using Executor = std::function<void(float & accumulated, float kernelSize)>;
80
81 Executor GetExecutor(PoolingAlgorithm algorithm)
82 {
83 switch (algorithm)
84 {
85 case PoolingAlgorithm::Max:
86 {
87 return [](float & accumulated, float kernelSize) {};
88 }
89
90 case PoolingAlgorithm::Average:
91 {
92 return [](float & accumulated, float kernelSize) {
93 accumulated /= kernelSize;
94 };
95 }
96
97 case PoolingAlgorithm::L2:
98 {
99 return [](float & accumulated, float kernelSize) {
100 accumulated = sqrtf(accumulated / kernelSize);
101 };
102 }
103
104 default:
105 {
106 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
107 }
108 }
109 }
110
Finn Williams70f609b2019-11-06 16:54:53 +0000111 bool OnPaddingOnly(int start, int end, int maxRange)
telsoa014fcda012018-03-09 14:13:49 +0000112 {
Finn Williams70f609b2019-11-06 16:54:53 +0000113 if (end <= 0 || start > maxRange)
telsoa014fcda012018-03-09 14:13:49 +0000114 {
115 return true;
116 }
117 else
118 {
119 return false;
120 }
121 }
122
123
124 bool ClampRange(int & start, int & end, int maxRange)
125 {
126 if (start < 0 || end > maxRange)
127 {
128 start = std::min(std::max(start, 0), maxRange);
129 end = std::min(std::max(end, 0), maxRange);
130 return true;
131 }
132 else
133 {
134 return false;
135 }
136 }
137}
138
Matteo Martincigh21350152018-11-28 16:22:22 +0000139using namespace armnnUtils;
140
telsoa014fcda012018-03-09 14:13:49 +0000141namespace armnn
142{
Teresa Charlina3b20472019-06-06 11:12:32 +0100143void Pooling2d(Decoder<float>& rInputDecoder,
144 Encoder<float>& rOutputEncoder,
telsoa014fcda012018-03-09 14:13:49 +0000145 const TensorInfo& inputInfo,
146 const TensorInfo& outputInfo,
147 const Pooling2dDescriptor& params)
148{
Teresa Charlina3b20472019-06-06 11:12:32 +0100149 const DataLayoutIndexed dataLayout(params.m_DataLayout);
James Conroy45a9b772018-10-31 11:47:53 +0000150 auto channelsIndex = dataLayout.GetChannelsIndex();
151 auto heightIndex = dataLayout.GetHeightIndex();
152 auto widthIndex = dataLayout.GetWidthIndex();
James Conroy69482272018-10-19 10:41:35 +0100153
telsoa014fcda012018-03-09 14:13:49 +0000154 const int batchSize = boost::numeric_cast<int>(outputInfo.GetShape()[0]);
James Conroy69482272018-10-19 10:41:35 +0100155 const int channels = boost::numeric_cast<int>(outputInfo.GetShape()[channelsIndex]);
156 const int heightOutput = boost::numeric_cast<int>(outputInfo.GetShape()[heightIndex]);
157 const int widthOutput = boost::numeric_cast<int>(outputInfo.GetShape()[widthIndex]);
158 const int heightInput = boost::numeric_cast<int>(inputInfo.GetShape()[heightIndex]);
159 const int widthInput = boost::numeric_cast<int>(inputInfo.GetShape()[widthIndex]);
telsoa014fcda012018-03-09 14:13:49 +0000160 const int padLeft = boost::numeric_cast<int>(params.m_PadLeft);
161 const int padRight = boost::numeric_cast<int>(params.m_PadRight);
162 const int padTop = boost::numeric_cast<int>(params.m_PadTop);
163 const int padBottom = boost::numeric_cast<int>(params.m_PadBottom);
164 const int strideX = boost::numeric_cast<int>(params.m_StrideX);
165 const int strideY = boost::numeric_cast<int>(params.m_StrideY);
166 const int poolHeight = boost::numeric_cast<int>(params.m_PoolHeight);
167 const int poolWidth = boost::numeric_cast<int>(params.m_PoolWidth);
168
169 float defaultInitializer = DefaultInitializer(params.m_PoolType);
170
171 Accumulator accumulate = GetAccumulator(params.m_PoolType);
172 Executor execute = GetExecutor(params.m_PoolType);
173
Teresa Charlina3b20472019-06-06 11:12:32 +0100174 TensorShape outputShape = outputInfo.GetShape();
175 TensorShape inputShape = inputInfo.GetShape();
James Conroy45a9b772018-10-31 11:47:53 +0000176
telsoa014fcda012018-03-09 14:13:49 +0000177 // Check supported padding methods outside the loop to simplify
telsoa01c577f2c2018-08-31 09:22:23 +0100178 // the inner loop.
telsoa014fcda012018-03-09 14:13:49 +0000179 if (params.m_PaddingMethod != PaddingMethod::Exclude &&
180 params.m_PaddingMethod != PaddingMethod::IgnoreValue)
181 {
182 throw armnn::InvalidArgumentException("Unsupported padding type");
183 }
184
185 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
223 unsigned int outputIndex = dataLayout.GetIndex(outputShape,
224 boost::numeric_cast<unsigned int>(n),
225 boost::numeric_cast<unsigned int>(c),
226 boost::numeric_cast<unsigned int>(yOutput),
227 boost::numeric_cast<unsigned int>(xOutput));
228 rOutputEncoder[outputIndex];
229 rOutputEncoder.Set(result);
230 continue;
telsoa014fcda012018-03-09 14:13:49 +0000231 }
232
Finn Williams70f609b2019-11-06 16:54:53 +0000233 bool clamped = hclamped |= ClampRange(wstart, wend, widthInput);
telsoa014fcda012018-03-09 14:13:49 +0000234
235 if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
236 {
telsoa01c577f2c2018-08-31 09:22:23 +0100237 // When we exclude the padding, it means we calculate with a smaller
238 // kernel size, so I changed the divisor here.
telsoa014fcda012018-03-09 14:13:49 +0000239 poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
240 }
241
242 for (auto yInput = hstart; yInput < hend; yInput++)
243 {
244 for (auto xInput = wstart; xInput < wend; xInput++)
245 {
Teresa Charlina3b20472019-06-06 11:12:32 +0100246 unsigned int inputIndex = dataLayout.GetIndex(inputShape,
247 boost::numeric_cast<unsigned int>(n),
248 boost::numeric_cast<unsigned int>(c),
249 boost::numeric_cast<unsigned int>(yInput),
250 boost::numeric_cast<unsigned int>(xInput));
251
252 rInputDecoder[inputIndex];
253 float inval = rInputDecoder.Get();
telsoa014fcda012018-03-09 14:13:49 +0000254
255 accumulate(result, inval);
256 }
257 }
258
259 execute(result, poolAreaSize);
260
Teresa Charlina3b20472019-06-06 11:12:32 +0100261 unsigned int outputIndex = dataLayout.GetIndex(outputShape,
262 boost::numeric_cast<unsigned int>(n),
263 boost::numeric_cast<unsigned int>(c),
264 boost::numeric_cast<unsigned int>(yOutput),
265 boost::numeric_cast<unsigned int>(xOutput));
266
267 rOutputEncoder[outputIndex];
268 rOutputEncoder.Set(result);
telsoa014fcda012018-03-09 14:13:49 +0000269 }
270 }
271 }
272 }
273}
274
275} //namespace armnn