1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include <cassert>
16 #include <cmath>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <iostream>
20 #include <limits>
21
22 #include "tensorflow/lite/c/builtin_op_data.h"
23 #include "tensorflow/lite/c/c_api_internal.h"
24 #include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h"
25 #include "tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h"
26 #include "tensorflow/lite/kernels/internal/reference/reference_ops.h"
27 #include "tensorflow/lite/kernels/internal/tensor.h"
28 #include "tensorflow/lite/kernels/kernel_util.h"
29 #include "tensorflow/lite/kernels/op_macros.h"
30 #include "tensorflow/lite/kernels/padding.h"
31
32 namespace tflite {
33 namespace ops {
34 namespace builtin {
35 namespace pooling {
36
37 // This file has two implementation of each pooling op.
38 enum KernelType {
39 kReference,
40 kGenericOptimized,
41 };
42
43 enum PoolType {
44 kAverage,
45 kMax,
46 kL2,
47 };
48
49 struct OpData {
50 TfLitePaddingValues padding;
51 };
52
Init(TfLiteContext * context,const char * buffer,size_t length)53 void* Init(TfLiteContext* context, const char* buffer, size_t length) {
54 // This is a builtin op, so we don't use the contents in 'buffer', if any.
55 // Instead, we allocate a new object to carry information from Prepare() to
56 // Eval().
57 return new OpData;
58 }
59
Free(TfLiteContext * context,void * buffer)60 void Free(TfLiteContext* context, void* buffer) {
61 delete reinterpret_cast<OpData*>(buffer);
62 }
63
64 template <PoolType pool_type>
GenericPrepare(TfLiteContext * context,TfLiteNode * node)65 TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
66 auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
67 OpData* data = reinterpret_cast<OpData*>(node->user_data);
68
69 TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
70 TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
71 TfLiteTensor* output = GetOutput(context, node, 0);
72 const TfLiteTensor* input = GetInput(context, node, 0);
73 TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
74 TF_LITE_ENSURE_EQ(context, input->type, output->type);
75
76 int batches = input->dims->data[0];
77 int height = input->dims->data[1];
78 int width = input->dims->data[2];
79 int channels_out = input->dims->data[3];
80
81 // Matching GetWindowedOutputSize in TensorFlow.
82 auto padding = params->padding;
83 auto compute_out_size = [padding](int image_size, int filter_size,
84 int stride) -> int {
85 return padding == kTfLitePaddingSame
86 ? (image_size + stride - 1) / stride
87 : padding == kTfLitePaddingValid
88 ? (image_size - filter_size + stride) / stride
89 : 0;
90 };
91
92 int out_width =
93 compute_out_size(width, params->filter_width, params->stride_width);
94 int out_height =
95 compute_out_size(height, params->filter_height, params->stride_height);
96
97 data->padding.height = ComputePadding(params->stride_height, 1, height,
98 params->filter_height, out_height);
99 data->padding.width = ComputePadding(params->stride_width, 1, width,
100 params->filter_width, out_width);
101
102 if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) {
103 if (pool_type == kAverage || pool_type == kMax) {
104 TF_LITE_ENSURE_EQ(context, input->params.scale, output->params.scale);
105 TF_LITE_ENSURE_EQ(context, input->params.zero_point,
106 output->params.zero_point);
107 }
108 if (pool_type == kL2) {
109 // We currently don't have a quantized implementation of L2Pool
110 TF_LITE_ENSURE_EQ(context, input->type, kTfLiteFloat32);
111 }
112 }
113
114 TfLiteIntArray* output_size = TfLiteIntArrayCreate(4);
115 output_size->data[0] = batches;
116 output_size->data[1] = out_height;
117 output_size->data[2] = out_width;
118 output_size->data[3] = channels_out;
119 return context->ResizeTensor(context, output, output_size);
120 }
121
122 template <KernelType kernel_type>
AverageEvalFloat(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)123 void AverageEvalFloat(TfLiteContext* context, TfLiteNode* node,
124 TfLitePoolParams* params, OpData* data,
125 const TfLiteTensor* input, TfLiteTensor* output) {
126 float activation_min, activation_max;
127 CalculateActivationRange(params->activation, &activation_min,
128 &activation_max);
129 #define TF_LITE_AVERAGE_POOL(type) \
130 tflite::PoolParams op_params; \
131 op_params.stride_height = params->stride_height; \
132 op_params.stride_width = params->stride_width; \
133 op_params.filter_height = params->filter_height; \
134 op_params.filter_width = params->filter_width; \
135 op_params.padding_values.height = data->padding.height; \
136 op_params.padding_values.width = data->padding.width; \
137 op_params.float_activation_min = activation_min; \
138 op_params.float_activation_max = activation_max; \
139 type::AveragePool(op_params, GetTensorShape(input), \
140 GetTensorData<float>(input), GetTensorShape(output), \
141 GetTensorData<float>(output))
142 if (kernel_type == kReference) {
143 TF_LITE_AVERAGE_POOL(reference_ops);
144 } else {
145 TF_LITE_AVERAGE_POOL(optimized_ops);
146 }
147 #undef TF_LITE_AVERAGE_POOL
148 }
149
150 template <KernelType kernel_type>
AverageEvalQuantizedUint8(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)151 void AverageEvalQuantizedUint8(TfLiteContext* context, TfLiteNode* node,
152 TfLitePoolParams* params, OpData* data,
153 const TfLiteTensor* input,
154 TfLiteTensor* output) {
155 int32_t activation_min;
156 int32_t activation_max;
157 CalculateActivationRangeUint8(params->activation, output, &activation_min,
158 &activation_max);
159 #define TF_LITE_AVERAGE_POOL(type) \
160 tflite::PoolParams op_params; \
161 op_params.stride_height = params->stride_height; \
162 op_params.stride_width = params->stride_width; \
163 op_params.filter_height = params->filter_height; \
164 op_params.filter_width = params->filter_width; \
165 op_params.padding_values.height = data->padding.height; \
166 op_params.padding_values.width = data->padding.width; \
167 op_params.quantized_activation_min = activation_min; \
168 op_params.quantized_activation_max = activation_max; \
169 type::AveragePool(op_params, GetTensorShape(input), \
170 GetTensorData<uint8_t>(input), GetTensorShape(output), \
171 GetTensorData<uint8_t>(output))
172 if (kernel_type == kReference) {
173 TF_LITE_AVERAGE_POOL(reference_ops);
174 } else {
175 TF_LITE_AVERAGE_POOL(optimized_ops);
176 }
177 #undef TF_LITE_AVERAGE_POOL
178 }
179
AverageEvalQuantizedInt8(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)180 void AverageEvalQuantizedInt8(TfLiteContext* context, TfLiteNode* node,
181 TfLitePoolParams* params, OpData* data,
182 const TfLiteTensor* input, TfLiteTensor* output) {
183 int32_t activation_min;
184 int32_t activation_max;
185 CalculateActivationRangeInt8(params->activation, output, &activation_min,
186 &activation_max);
187 tflite::PoolParams op_params;
188 op_params.stride_height = params->stride_height;
189 op_params.stride_width = params->stride_width;
190 op_params.filter_height = params->filter_height;
191 op_params.filter_width = params->filter_width;
192 op_params.padding_values.height = data->padding.height;
193 op_params.padding_values.width = data->padding.width;
194 op_params.quantized_activation_min = activation_min;
195 op_params.quantized_activation_max = activation_max;
196 reference_integer_ops::AveragePool(
197 op_params, GetTensorShape(input), GetTensorData<int8_t>(input),
198 GetTensorShape(output), GetTensorData<int8_t>(output));
199 }
200
201 template <KernelType kernel_type>
MaxEvalFloat(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)202 void MaxEvalFloat(TfLiteContext* context, TfLiteNode* node,
203 TfLitePoolParams* params, OpData* data,
204 const TfLiteTensor* input, TfLiteTensor* output) {
205 float activation_min, activation_max;
206 CalculateActivationRange(params->activation, &activation_min,
207 &activation_max);
208 #define TF_LITE_MAX_POOL(type) \
209 tflite::PoolParams op_params; \
210 op_params.stride_height = params->stride_height; \
211 op_params.stride_width = params->stride_width; \
212 op_params.filter_height = params->filter_height; \
213 op_params.filter_width = params->filter_width; \
214 op_params.padding_values.height = data->padding.height; \
215 op_params.padding_values.width = data->padding.width; \
216 op_params.float_activation_min = activation_min; \
217 op_params.float_activation_max = activation_max; \
218 type::MaxPool(op_params, GetTensorShape(input), GetTensorData<float>(input), \
219 GetTensorShape(output), GetTensorData<float>(output))
220 if (kernel_type == kReference) {
221 TF_LITE_MAX_POOL(reference_ops);
222 } else {
223 TF_LITE_MAX_POOL(optimized_ops);
224 }
225 #undef TF_LITE_MAX_POOL
226 }
227
228 template <KernelType kernel_type>
MaxEvalQuantizedUInt8(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)229 void MaxEvalQuantizedUInt8(TfLiteContext* context, TfLiteNode* node,
230 TfLitePoolParams* params, OpData* data,
231 const TfLiteTensor* input, TfLiteTensor* output) {
232 int32_t activation_min;
233 int32_t activation_max;
234 CalculateActivationRangeUint8(params->activation, output, &activation_min,
235 &activation_max);
236 #define TF_LITE_MAX_POOL(type) \
237 tflite::PoolParams op_params; \
238 op_params.stride_height = params->stride_height; \
239 op_params.stride_width = params->stride_width; \
240 op_params.filter_height = params->filter_height; \
241 op_params.filter_width = params->filter_width; \
242 op_params.padding_values.height = data->padding.height; \
243 op_params.padding_values.width = data->padding.width; \
244 op_params.quantized_activation_min = activation_min; \
245 op_params.quantized_activation_max = activation_max; \
246 type::MaxPool(op_params, GetTensorShape(input), \
247 GetTensorData<uint8_t>(input), GetTensorShape(output), \
248 GetTensorData<uint8_t>(output))
249 if (kernel_type == kReference) {
250 TF_LITE_MAX_POOL(reference_ops);
251 } else {
252 TF_LITE_MAX_POOL(optimized_ops);
253 }
254 #undef TF_LITE_MAX_POOL
255 }
256
257 template <KernelType kernel_type>
MaxEvalQuantizedInt8(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)258 void MaxEvalQuantizedInt8(TfLiteContext* context, TfLiteNode* node,
259 TfLitePoolParams* params, OpData* data,
260 const TfLiteTensor* input, TfLiteTensor* output) {
261 int32_t activation_min;
262 int32_t activation_max;
263 CalculateActivationRangeInt8(params->activation, output, &activation_min,
264 &activation_max);
265 #define TF_LITE_MAX_POOL(type) \
266 tflite::PoolParams op_params; \
267 op_params.stride_height = params->stride_height; \
268 op_params.stride_width = params->stride_width; \
269 op_params.filter_height = params->filter_height; \
270 op_params.filter_width = params->filter_width; \
271 op_params.padding_values.height = data->padding.height; \
272 op_params.padding_values.width = data->padding.width; \
273 op_params.quantized_activation_min = activation_min; \
274 op_params.quantized_activation_max = activation_max; \
275 type::MaxPool(op_params, GetTensorShape(input), \
276 GetTensorData<int8_t>(input), GetTensorShape(output), \
277 GetTensorData<int8_t>(output))
278 TF_LITE_MAX_POOL(reference_integer_ops);
279 #undef TF_LITE_MAX_POOL
280 }
281
282 template <KernelType kernel_type>
L2EvalFloat(TfLiteContext * context,TfLiteNode * node,TfLitePoolParams * params,OpData * data,const TfLiteTensor * input,TfLiteTensor * output)283 void L2EvalFloat(TfLiteContext* context, TfLiteNode* node,
284 TfLitePoolParams* params, OpData* data,
285 const TfLiteTensor* input, TfLiteTensor* output) {
286 float activation_min, activation_max;
287 CalculateActivationRange(params->activation, &activation_min,
288 &activation_max);
289 #define TF_LITE_L2_POOL(type) \
290 tflite::PoolParams op_params; \
291 op_params.stride_height = params->stride_height; \
292 op_params.stride_width = params->stride_width; \
293 op_params.filter_height = params->filter_height; \
294 op_params.filter_width = params->filter_width; \
295 op_params.padding_values.height = data->padding.height; \
296 op_params.padding_values.width = data->padding.width; \
297 op_params.float_activation_min = activation_min; \
298 op_params.float_activation_max = activation_max; \
299 type::L2Pool(op_params, GetTensorShape(input), GetTensorData<float>(input), \
300 GetTensorShape(output), GetTensorData<float>(output))
301 if (kernel_type == kReference) {
302 TF_LITE_L2_POOL(reference_ops);
303 } else {
304 TF_LITE_L2_POOL(optimized_ops);
305 }
306 #undef TF_LITE_L2_POOL
307 }
308
309 #undef TF_LITE_KERNEL_TYPE_DISPATCH
310
311 template <KernelType kernel_type>
AverageEval(TfLiteContext * context,TfLiteNode * node)312 TfLiteStatus AverageEval(TfLiteContext* context, TfLiteNode* node) {
313 auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
314 OpData* data = reinterpret_cast<OpData*>(node->user_data);
315
316 TfLiteTensor* output = GetOutput(context, node, 0);
317 const TfLiteTensor* input = GetInput(context, node, 0);
318 switch (input->type) { // Already know in/out types are same.
319 case kTfLiteFloat32:
320 AverageEvalFloat<kernel_type>(context, node, params, data, input, output);
321 break;
322 case kTfLiteUInt8:
323 AverageEvalQuantizedUint8<kernel_type>(context, node, params, data, input,
324 output);
325 break;
326 case kTfLiteInt8:
327 AverageEvalQuantizedInt8(context, node, params, data, input, output);
328 break;
329 default:
330 context->ReportError(context, "Type %d not currently supported.",
331 input->type);
332 return kTfLiteError;
333 }
334 return kTfLiteOk;
335 }
336
337 template <KernelType kernel_type>
MaxEval(TfLiteContext * context,TfLiteNode * node)338 TfLiteStatus MaxEval(TfLiteContext* context, TfLiteNode* node) {
339 auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
340 OpData* data = reinterpret_cast<OpData*>(node->user_data);
341
342 TfLiteTensor* output = GetOutput(context, node, 0);
343 const TfLiteTensor* input = GetInput(context, node, 0);
344 switch (input->type) { // Already know in/out types are same.
345 case kTfLiteFloat32:
346 MaxEvalFloat<kernel_type>(context, node, params, data, input, output);
347 break;
348 case kTfLiteUInt8:
349 MaxEvalQuantizedUInt8<kernel_type>(context, node, params, data, input,
350 output);
351 break;
352 case kTfLiteInt8:
353 MaxEvalQuantizedInt8<kernel_type>(context, node, params, data, input,
354 output);
355 break;
356 default:
357 context->ReportError(context, "Type %d not currently supported.",
358 input->type);
359 return kTfLiteError;
360 }
361 return kTfLiteOk;
362 }
363
364 template <KernelType kernel_type>
L2Eval(TfLiteContext * context,TfLiteNode * node)365 TfLiteStatus L2Eval(TfLiteContext* context, TfLiteNode* node) {
366 auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
367 OpData* data = reinterpret_cast<OpData*>(node->user_data);
368
369 TfLiteTensor* output = GetOutput(context, node, 0);
370 const TfLiteTensor* input = GetInput(context, node, 0);
371 switch (input->type) { // Already know in/out types are same.
372 case kTfLiteFloat32:
373 L2EvalFloat<kernel_type>(context, node, params, data, input, output);
374 break;
375 case kTfLiteUInt8:
376 // We don't have a quantized implementation, so just fall through to the
377 // 'default' case.
378 default:
379 context->ReportError(context, "Type %d not currently supported.",
380 input->type);
381 return kTfLiteError;
382 }
383 return kTfLiteOk;
384 }
385
386 } // namespace pooling
387
Register_AVERAGE_POOL_REF()388 TfLiteRegistration* Register_AVERAGE_POOL_REF() {
389 static TfLiteRegistration r = {pooling::Init, pooling::Free,
390 pooling::GenericPrepare<pooling::kAverage>,
391 pooling::AverageEval<pooling::kReference>};
392 return &r;
393 }
394
Register_MAX_POOL_REF()395 TfLiteRegistration* Register_MAX_POOL_REF() {
396 static TfLiteRegistration r = {pooling::Init, pooling::Free,
397 pooling::GenericPrepare<pooling::kMax>,
398 pooling::MaxEval<pooling::kReference>};
399 return &r;
400 }
401
Register_L2_POOL_REF()402 TfLiteRegistration* Register_L2_POOL_REF() {
403 static TfLiteRegistration r = {pooling::Init, pooling::Free,
404 pooling::GenericPrepare<pooling::kL2>,
405 pooling::L2Eval<pooling::kReference>};
406 return &r;
407 }
408
Register_AVERAGE_POOL_GENERIC_OPT()409 TfLiteRegistration* Register_AVERAGE_POOL_GENERIC_OPT() {
410 static TfLiteRegistration r = {
411 pooling::Init, pooling::Free, pooling::GenericPrepare<pooling::kAverage>,
412 pooling::AverageEval<pooling::kGenericOptimized>};
413 return &r;
414 }
415
Register_MAX_POOL_GENERIC_OPT()416 TfLiteRegistration* Register_MAX_POOL_GENERIC_OPT() {
417 static TfLiteRegistration r = {pooling::Init, pooling::Free,
418 pooling::GenericPrepare<pooling::kMax>,
419 pooling::MaxEval<pooling::kGenericOptimized>};
420 return &r;
421 }
422
Register_L2_POOL_GENERIC_OPT()423 TfLiteRegistration* Register_L2_POOL_GENERIC_OPT() {
424 static TfLiteRegistration r = {pooling::Init, pooling::Free,
425 pooling::GenericPrepare<pooling::kL2>,
426 pooling::L2Eval<pooling::kGenericOptimized>};
427 return &r;
428 }
429
Register_AVERAGE_POOL_2D()430 TfLiteRegistration* Register_AVERAGE_POOL_2D() {
431 return Register_AVERAGE_POOL_GENERIC_OPT();
432 }
433
Register_MAX_POOL_2D()434 TfLiteRegistration* Register_MAX_POOL_2D() {
435 return Register_MAX_POOL_GENERIC_OPT();
436 }
437
Register_L2_POOL_2D()438 TfLiteRegistration* Register_L2_POOL_2D() {
439 return Register_L2_POOL_GENERIC_OPT();
440 }
441
442 } // namespace builtin
443 } // namespace ops
444 } // namespace tflite
445