blob: 5812a290e702b361ba57b454b21144e03e1569eb [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
11#include <boost/numeric/conversion/cast.hpp>
12
13#include <limits>
14#include <algorithm>
15#include <functional>
16
17namespace
18{
19 using PoolingAlgorithm = armnn::PoolingAlgorithm;
20
21 float DefaultInitializer(PoolingAlgorithm algorithm)
22 {
23 switch (algorithm)
24 {
25 case PoolingAlgorithm::Max:
26 {
27 return std::numeric_limits<float>::lowest();
28 }
29 case PoolingAlgorithm::Average:
30 case PoolingAlgorithm::L2:
31 {
32 return 0.0f;
33 }
34 default:
35 {
36 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
37 }
38 }
39 }
40
41 using Accumulator = std::function<void(float & accu, float value)>;
42
43 Accumulator GetAccumulator(PoolingAlgorithm algorithm)
44 {
45 switch (algorithm)
46 {
47 case PoolingAlgorithm::Max:
48 {
49 return [](float & accu, float value) {
50 if (value > accu) {
51 accu = value;
52 }
53 };
54 }
55
56 case PoolingAlgorithm::Average:
57 {
58 return [](float & accu, float value) {
59 accu += value;
60 };
61 }
62
63 case PoolingAlgorithm::L2:
64 {
65 return [](float & accu, float value) {
66 accu += (value*value);
67 };
68 }
69
70 default:
71 {
72 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
73 }
74 }
75 }
76
77 using Executor = std::function<void(float & accumulated, float kernelSize)>;
78
79 Executor GetExecutor(PoolingAlgorithm algorithm)
80 {
81 switch (algorithm)
82 {
83 case PoolingAlgorithm::Max:
84 {
85 return [](float & accumulated, float kernelSize) {};
86 }
87
88 case PoolingAlgorithm::Average:
89 {
90 return [](float & accumulated, float kernelSize) {
91 accumulated /= kernelSize;
92 };
93 }
94
95 case PoolingAlgorithm::L2:
96 {
97 return [](float & accumulated, float kernelSize) {
98 accumulated = sqrtf(accumulated / kernelSize);
99 };
100 }
101
102 default:
103 {
104 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
105 }
106 }
107 }
108
109 bool OnPaddingOnly(int start, int end, int maxRange, int padding)
110 {
111 if (end <= 0 || start > (maxRange - padding))
112 {
113 return true;
114 }
115 else
116 {
117 return false;
118 }
119 }
120
121
122 bool ClampRange(int & start, int & end, int maxRange)
123 {
124 if (start < 0 || end > maxRange)
125 {
126 start = std::min(std::max(start, 0), maxRange);
127 end = std::min(std::max(end, 0), maxRange);
128 return true;
129 }
130 else
131 {
132 return false;
133 }
134 }
135}
136
137namespace armnn
138{
139
140void Pooling2d(const float* in,
141 float* out,
142 const TensorInfo& inputInfo,
143 const TensorInfo& outputInfo,
144 const Pooling2dDescriptor& params)
145{
146 const int batchSize = boost::numeric_cast<int>(outputInfo.GetShape()[0]);
147 const int channels = boost::numeric_cast<int>(outputInfo.GetShape()[1]);
148 const int heightOutput = boost::numeric_cast<int>(outputInfo.GetShape()[2]);
149 const int widthOutput = boost::numeric_cast<int>(outputInfo.GetShape()[3]);
150 const int heightInput = boost::numeric_cast<int>(inputInfo.GetShape()[2]);
151 const int widthInput = boost::numeric_cast<int>(inputInfo.GetShape()[3]);
152 const int padLeft = boost::numeric_cast<int>(params.m_PadLeft);
153 const int padRight = boost::numeric_cast<int>(params.m_PadRight);
154 const int padTop = boost::numeric_cast<int>(params.m_PadTop);
155 const int padBottom = boost::numeric_cast<int>(params.m_PadBottom);
156 const int strideX = boost::numeric_cast<int>(params.m_StrideX);
157 const int strideY = boost::numeric_cast<int>(params.m_StrideY);
158 const int poolHeight = boost::numeric_cast<int>(params.m_PoolHeight);
159 const int poolWidth = boost::numeric_cast<int>(params.m_PoolWidth);
160
161 float defaultInitializer = DefaultInitializer(params.m_PoolType);
162
163 Accumulator accumulate = GetAccumulator(params.m_PoolType);
164 Executor execute = GetExecutor(params.m_PoolType);
165
166 // Check supported padding methods outside the loop to simplify
telsoa01c577f2c2018-08-31 09:22:23 +0100167 // the inner loop.
telsoa014fcda012018-03-09 14:13:49 +0000168 if (params.m_PaddingMethod != PaddingMethod::Exclude &&
169 params.m_PaddingMethod != PaddingMethod::IgnoreValue)
170 {
171 throw armnn::InvalidArgumentException("Unsupported padding type");
172 }
173
174 for (int n = 0; n < batchSize; n++)
175 {
176 for (int c = 0; c < channels; c++)
177 {
178 for (int yOutput = 0; yOutput < heightOutput; yOutput++)
179 {
180 for (int xOutput = 0; xOutput < widthOutput; xOutput++)
181 {
182 int hstart = (yOutput * strideY) - padTop;
183 int wstart = (xOutput * strideX) - padLeft;
184 int hend = hstart + poolHeight;
185 int wend = wstart + poolWidth;
186
187 // Clamp the pooling region inside the valid input area (which includes the padding).
188 // This is necessary because the final pooling in a row may overlap beyond the padding.
surmeh01bceff2f2018-03-29 16:29:27 +0100189 hend = std::min(hend, heightInput + padBottom);
190 wend = std::min(wend, widthInput + padRight);
telsoa014fcda012018-03-09 14:13:49 +0000191
192 float result = defaultInitializer;
193 float poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
194
telsoa01c577f2c2018-08-31 09:22:23 +0100195 // Special case: when the pooling kernel is over a padding region and the padding
telsoa014fcda012018-03-09 14:13:49 +0000196 // size is larger or equal to the kernel and the kernel only covers
197 // padding and no real values, then we initialize the result as zero
198 // by convention. This is because we need to choose a value here and
199 // all values we have are padding, which we ignore.
200 if (OnPaddingOnly(hstart, hend, heightInput, padBottom) ||
201 OnPaddingOnly(wstart, wend, widthInput, padRight))
202 {
203 result = 0.0f;
204 }
205
206 bool clamped = ClampRange(wstart, wend, widthInput);
207 clamped |= ClampRange(hstart, hend, heightInput);
208
209 if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
210 {
telsoa01c577f2c2018-08-31 09:22:23 +0100211 // When we exclude the padding, it means we calculate with a smaller
212 // kernel size, so I changed the divisor here.
telsoa014fcda012018-03-09 14:13:49 +0000213 poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
214 }
215
216 for (auto yInput = hstart; yInput < hend; yInput++)
217 {
218 for (auto xInput = wstart; xInput < wend; xInput++)
219 {
220 float inval = in[n * widthInput * heightInput * channels +
221 c * widthInput * heightInput +
222 yInput * widthInput +
223 xInput];
224
225 accumulate(result, inval);
226 }
227 }
228
229 execute(result, poolAreaSize);
230
231 out[n * widthOutput * heightOutput * channels +
232 c * widthOutput * heightOutput +
233 yOutput * widthOutput +
234 xOutput] = result;
235 }
236 }
237 }
238 }
239}
240
241} //namespace armnn