2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
6 #include "Pooling2d.hpp"
8 #include <armnn/Exceptions.hpp>
9 #include <armnn/Types.hpp>
11 #include <boost/numeric/conversion/cast.hpp>
19 using PoolingAlgorithm = armnn::PoolingAlgorithm;
21 float DefaultInitializer(PoolingAlgorithm algorithm)
25 case PoolingAlgorithm::Max:
27 return std::numeric_limits<float>::lowest();
29 case PoolingAlgorithm::Average:
30 case PoolingAlgorithm::L2:
36 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
41 using Accumulator = std::function<void(float & accu, float value)>;
43 Accumulator GetAccumulator(PoolingAlgorithm algorithm)
47 case PoolingAlgorithm::Max:
49 return [](float & accu, float value) {
56 case PoolingAlgorithm::Average:
58 return [](float & accu, float value) {
63 case PoolingAlgorithm::L2:
65 return [](float & accu, float value) {
66 accu += (value*value);
72 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
77 using Executor = std::function<void(float & accumulated, float kernelSize)>;
79 Executor GetExecutor(PoolingAlgorithm algorithm)
83 case PoolingAlgorithm::Max:
85 return [](float & accumulated, float kernelSize) {};
88 case PoolingAlgorithm::Average:
90 return [](float & accumulated, float kernelSize) {
91 accumulated /= kernelSize;
95 case PoolingAlgorithm::L2:
97 return [](float & accumulated, float kernelSize) {
98 accumulated = sqrtf(accumulated / kernelSize);
104 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
109 bool OnPaddingOnly(int start, int end, int maxRange, int padding)
111 if (end <= 0 || start > (maxRange - padding))
122 bool ClampRange(int & start, int & end, int maxRange)
124 if (start < 0 || end > maxRange)
126 start = std::min(std::max(start, 0), maxRange);
127 end = std::min(std::max(end, 0), maxRange);
140 void Pooling2d(const float* in,
142 const TensorInfo& inputInfo,
143 const TensorInfo& outputInfo,
144 const Pooling2dDescriptor& params)
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);
161 float defaultInitializer = DefaultInitializer(params.m_PoolType);
163 Accumulator accumulate = GetAccumulator(params.m_PoolType);
164 Executor execute = GetExecutor(params.m_PoolType);
166 // Check supported padding methods outside the loop to simplify
168 if (params.m_PaddingMethod != PaddingMethod::Exclude &&
169 params.m_PaddingMethod != PaddingMethod::IgnoreValue)
171 throw armnn::InvalidArgumentException("Unsupported padding type");
174 for (int n = 0; n < batchSize; n++)
176 for (int c = 0; c < channels; c++)
178 for (int yOutput = 0; yOutput < heightOutput; yOutput++)
180 for (int xOutput = 0; xOutput < widthOutput; xOutput++)
182 int hstart = (yOutput * strideY) - padTop;
183 int wstart = (xOutput * strideX) - padLeft;
184 int hend = hstart + poolHeight;
185 int wend = wstart + poolWidth;
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.
189 hend = std::min(hend, heightInput + padRight);
190 wend = std::min(wend, widthInput + padBottom);
192 float result = defaultInitializer;
193 float poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
195 // special case: when the pooling kernel is over a padding region and the padding
196 // 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))
206 bool clamped = ClampRange(wstart, wend, widthInput);
207 clamped |= ClampRange(hstart, hend, heightInput);
209 if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
211 // when we exclude the padding, it means we calculate with a smaller
212 // kernel size, so I change the divisor here
213 poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
216 for (auto yInput = hstart; yInput < hend; yInput++)
218 for (auto xInput = wstart; xInput < wend; xInput++)
220 float inval = in[n * widthInput * heightInput * channels +
221 c * widthInput * heightInput +
222 yInput * widthInput +
225 accumulate(result, inval);
229 execute(result, poolAreaSize);
231 out[n * widthOutput * heightOutput * channels +
232 c * widthOutput * heightOutput +
233 yOutput * widthOutput +