blob: 1ae2de0a22b438b8fd6b5060e239274781a1e42c [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
Matteo Martincighe011d202019-11-28 11:35:47 +00005
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "TfLiteParser.hpp"
7
Matthew Bentham39ef3e52020-01-20 10:09:09 +00008#include <armnn/Descriptors.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +01009#include <armnn/Exceptions.hpp>
Derek Lamberti08446972019-11-26 16:38:31 +000010#include <armnn/Logging.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011#include <armnn/TypesUtils.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010012#include <armnn/utility/Assert.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000013#include <armnn/utility/IgnoreUnused.hpp>
Derek Lambertif0176992020-04-28 13:37:49 +010014#include <armnn/utility/NumericCast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010015
16// armnnUtils:
Matteo Martincighe011d202019-11-28 11:35:47 +000017#include <armnnUtils/Permute.hpp>
18
Sadik Armagan479045b2018-10-01 11:51:37 +010019#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010020#include <VerificationHelpers.hpp>
21
22// The generated code based on the Tf Lite schema:
23#include <schema_generated.h>
24
Matteo Martincighe011d202019-11-28 11:35:47 +000025#include <flatbuffers/flexbuffers.h>
26
telsoa01c577f2c2018-08-31 09:22:23 +010027#include <boost/format.hpp>
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +010028#include <boost/numeric/conversion/cast.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000029#include <boost/filesystem.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010030
31#include <fstream>
32#include <algorithm>
33#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010034#include <numeric>
Derek Lambertic9e52792020-03-11 11:42:26 +000035#include <sstream>
36
37#define ARMNN_THROW_PARSE_EXCEPTION(msg) \
38 { \
39 throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
40 << ": " \
41 << CHECK_LOCATION().AsString()).str()); \
42 }
telsoa01c577f2c2018-08-31 09:22:23 +010043
44using namespace armnn;
45using armnn::CheckLocation;
46namespace armnnTfLiteParser
47{
48namespace
49{
jimfly01c25411c2018-11-14 17:47:22 +000050
telsoa01c577f2c2018-08-31 09:22:23 +010051const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
52
53void CheckSubgraph(const TfLiteParser::ModelPtr & model,
54 size_t subgraphIndex,
55 const CheckLocation & location)
56{
57 if (model.get() == nullptr)
58 {
59 throw ParseException(
60 boost::str(
61 boost::format("%1% was called with invalid (null) model. "
62 "Possible reason is that the model is not yet loaded and Unpack(ed). "
63 "subgraph:%2% at %3%") %
64 location.m_Function %
65 subgraphIndex %
66 location.FileLine()));
67 }
68 else if (subgraphIndex >= model->subgraphs.size())
69 {
70 throw ParseException(
71 boost::str(
72 boost::format("%1% was called with an invalid subgraph index. "
73 "subgraph:%2% at %3%") %
74 location.m_Function %
75 subgraphIndex %
76 location.FileLine()));
77 }
78}
79
80#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
81 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
82
83void CheckModel(const TfLiteParser::ModelPtr & model,
84 size_t subgraphIndex,
85 size_t operatorIndex,
86 const CheckLocation & location)
87{
88 if (model.get() == nullptr)
89 {
90 throw ParseException(
91 boost::str(
92 boost::format("%1% was called with invalid (null) model. "
93 "Possible reason is that the model is not yet loaded and Unpack(ed). "
94 "subgraph:%2% operator:%3% at %4%") %
95 location.m_Function %
96 subgraphIndex %
97 operatorIndex %
98 location.FileLine()));
99 }
100 else if (subgraphIndex >= model->subgraphs.size())
101 {
102 throw ParseException(
103 boost::str(
104 boost::format("%1% was called with an invalid subgraph index. "
105 "subgraph:%2% operator:%3% at %4%") %
106 location.m_Function %
107 subgraphIndex %
108 operatorIndex %
109 location.FileLine()));
110 }
111 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
112 operatorIndex != VIRTUAL_OPERATOR_ID)
113 {
114 throw ParseException(
115 boost::str(
116 boost::format("%1% was called with an invalid operator index. "
117 "subgraph:%2% operator:%3% at %4%") %
118 location.m_Function %
119 subgraphIndex %
120 operatorIndex %
121 location.FileLine()));
122 }
123}
124
125#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
126 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
127
128void CheckTensor(const TfLiteParser::ModelPtr & model,
129 size_t subgraphIndex,
130 size_t tensorIndex,
131 const CheckLocation & location)
132{
133 // not checking model, because I assume CHECK_MODEL already run
134 // and checked that. An assert would do.
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100135 ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
telsoa01c577f2c2018-08-31 09:22:23 +0100136
137 // also subgraph index should be checked by CHECK_MODEL so
138 // I only add an assert here
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100139 ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
telsoa01c577f2c2018-08-31 09:22:23 +0100140
141 // the tensor index is the only one to check here
142 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
143 {
144 throw ParseException(
145 boost::str(
146 boost::format("%1% was called with an invalid tensor index. "
147 "subgraph:%2% tensor:%3% at %4%") %
148 location.m_Function %
149 subgraphIndex %
150 tensorIndex %
151 location.FileLine()));
152 }
153}
154
155#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
156 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
157
158void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
159 const CheckLocation & location)
160{
161 if (rawPtr == nullptr)
162 {
163 throw ParseException(
164 boost::str(
165 boost::format("%1% was called with a null tensor pointer. "
166 "at %2%") %
167 location.m_Function %
168 location.FileLine()));
169
170 }
171}
172
173#define CHECK_TENSOR_PTR(TENSOR_PTR) \
174 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
175
176void CheckBuffer(const TfLiteParser::ModelPtr & model,
177 size_t bufferIndex,
178 const CheckLocation & location)
179{
180 if (model.get() == nullptr)
181 {
182 throw ParseException(
183 boost::str(
184 boost::format("%1% was called with invalid (null) model. "
185 "Possible reason is that the model is not yet loaded and Unpack(ed). "
186 "buffer:%2% at %3%") %
187 location.m_Function %
188 bufferIndex %
189 location.FileLine()));
190 }
191 else if (bufferIndex >= model->buffers.size())
192 {
193 throw ParseException(
194 boost::str(
195 boost::format("%1% was called with an invalid buffer index. "
196 "buffer index:%2% at %3%") %
197 location.m_Function %
198 bufferIndex %
199 location.FileLine()));
200 }
201 else if (model->buffers[bufferIndex].get() == nullptr)
202 {
203 throw ParseException(
204 boost::str(
205 boost::format("The buffer #%1% is null. %3%") %
206 bufferIndex %
207 location.AsString()));
208 }
209}
210
211#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
212 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
213
214void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
215 const armnn::TensorInfo & tensorInfo,
216 uint32_t bufferId,
217 const CheckLocation & location)
218{
219 if (bufferPtr == nullptr)
220 {
221 throw ParseException(
222 boost::str(
223 boost::format("BufferPtr is null for buffer:%1%. %2%") %
224 bufferId %
225 location.AsString()));
226 }
227 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
228 tensorInfo.GetNumBytes() > bufferPtr->data.size())
229 {
230 std::stringstream ss;
231 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
232 << "For tensor: " << tensorInfo.GetShape()
233 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
234 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
235 throw ParseException(ss.str());
236 }
237}
238
239#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
240 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
241
242bool IsActivationSupported(tflite::ActivationFunctionType activationType)
243{
244 switch(activationType)
245 {
246 case tflite::ActivationFunctionType_NONE:
247 case tflite::ActivationFunctionType_RELU:
248 case tflite::ActivationFunctionType_RELU6:
249 case tflite::ActivationFunctionType_TANH:
250 {
251 return true;
252 }
253 default:
254 {
255 return false;
256 }
257 }
258}
259
260#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
261 do { \
262 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
263 { \
264 throw ParseException( \
265 boost::str( \
266 boost::format("TfLite parser doesn't suppport fused activation: " \
267 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
268 OPTION->fused_activation_function % \
269 tflite::EnumNameActivationFunctionType(\
270 OPTION->fused_activation_function) % \
271 __func__ % \
272 SUBGRAPH_INDEX % \
273 OPERATOR_INDEX % \
274 CHECK_LOCATION().FileLine())); \
275 } \
276 } while(false)
277
278
279std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
280{
281 std::vector<unsigned int> result;
282 result.reserve(in.size());
283 for (auto & i : in)
284 {
285 result.push_back(CHECKED_NON_NEGATIVE(i));
286 }
287 return result;
288}
289
290void CalcPadding(uint32_t inputSize,
291 uint32_t filterSize,
292 uint32_t stride,
Pablo Tellof0bd6832019-04-26 17:58:13 +0100293 uint32_t dilation,
telsoa01c577f2c2018-08-31 09:22:23 +0100294 uint32_t& paddingFront,
295 uint32_t& paddingBack,
296 tflite::Padding padding)
297{
298 paddingFront = 0;
299 paddingBack = 0;
300 if (padding == tflite::Padding_SAME)
301 {
302 uint32_t outputSize = (inputSize + stride - 1) / stride;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100303 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
304 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
telsoa01c577f2c2018-08-31 09:22:23 +0100305 if (temp > inputSize)
306 {
307 paddingFront = (temp - inputSize) / 2;
308 paddingBack = (temp - inputSize) - paddingFront;
309 }
310 }
311}
312
Keith Davis0c2eeac2020-02-11 16:51:50 +0000313armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes,
314 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
telsoa01c577f2c2018-08-31 09:22:23 +0100315{
316 armnn::DataType type;
317 CHECK_TENSOR_PTR(tensorPtr);
318
319 switch (tensorPtr->type)
320 {
321 case tflite::TensorType_UINT8:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000322 type = armnn::DataType::QAsymmU8;
telsoa01c577f2c2018-08-31 09:22:23 +0100323 break;
324 case tflite::TensorType_FLOAT32:
325 type = armnn::DataType::Float32;
326 break;
Finn Williamsed66d142019-12-06 09:55:55 +0000327 case tflite::TensorType_INT8:
Keith Davis67e6c542020-02-19 10:08:33 +0000328 if (tensorPtr->quantization->zero_point.size() == 1)
Ryan OShea03181ff2020-02-07 17:22:22 +0000329 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000330 // Per-tensor
Ryan OShea03181ff2020-02-07 17:22:22 +0000331 type = armnn::DataType::QAsymmS8;
332 }
333 else
334 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000335 // Per-channel
Ryan OShea03181ff2020-02-07 17:22:22 +0000336 type = armnn::DataType::QSymmS8;
337 }
Finn Williamsed66d142019-12-06 09:55:55 +0000338 break;
339 case tflite::TensorType_INT16:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000340 type = armnn::DataType::QSymmS16;
Finn Williamsed66d142019-12-06 09:55:55 +0000341 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100342 case tflite::TensorType_INT32:
343 type = armnn::DataType::Signed32;
344 break;
345
346 default:
347 {
348 CheckLocation location = CHECK_LOCATION();
349 throw ParseException(
350 boost::str(
351 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
352 tensorPtr->type %
353 tflite::EnumNameTensorType(tensorPtr->type) %
354 tensorPtr->name %
355 location.AsString()));
356 }
357 }
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100358 std::vector<unsigned int> safeShape = shapes;
359 if (safeShape.size() == 0)
360 {
361 safeShape.push_back(1);
362 }
363
Keith Davisd305e1a2020-01-22 11:57:54 +0000364 float quantizationScale = 0.0f;
365 int32_t quantizationOffset = 0;
366
367 if (tensorPtr->quantization.get())
368 {
369 if (tensorPtr->quantization->scale.size() <= 1)
370 {
371 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
372 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
373
374 if (tensorPtr->quantization->scale.size() == 1)
375 {
376 quantizationScale = tensorPtr->quantization->scale[0];
377 }
378 if (tensorPtr->quantization->zero_point.size() == 1)
379 {
380 // NOTE: we lose precision here when converting from 64 bit to 32
Ryan OShea03181ff2020-02-07 17:22:22 +0000381 // but this is what we support at the moment in ArmNN
Keith Davisd305e1a2020-01-22 11:57:54 +0000382 quantizationOffset = boost::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
383 }
384
385 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
386 safeShape.data(),
387 type,
388 quantizationScale,
389 quantizationOffset);
390
391 return result;
392 }
393 else
394 {
395 std::vector<float> quantizationScales;
396 std::vector<int32_t> quantizationOffsets;
397
398 // Scale
399 std::copy(tensorPtr->quantization->scale.begin(),
400 tensorPtr->quantization->scale.end(),
401 std::back_inserter(quantizationScales));
402
Keith Davis0c2eeac2020-02-11 16:51:50 +0000403 // QSymmS8 Per-axis
Keith Davisd305e1a2020-01-22 11:57:54 +0000404 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
405 safeShape.data(),
406 type,
407 quantizationScales,
Keith Davis0c2eeac2020-02-11 16:51:50 +0000408 dimensionMappings[boost::numeric_cast<unsigned int>(
409 tensorPtr->quantization->quantized_dimension)]);
Keith Davisd305e1a2020-01-22 11:57:54 +0000410 return result;
411 }
412 }
413 else
414 {
415 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
416 safeShape.data(),
417 type,
418 quantizationScale,
419 quantizationOffset);
420 return result;
421 }
telsoa01c577f2c2018-08-31 09:22:23 +0100422}
423
Keith Davis0c2eeac2020-02-11 16:51:50 +0000424armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
425 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000426{
427 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000428 return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000429}
430
telsoa01c577f2c2018-08-31 09:22:23 +0100431template<typename T>
432std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
433CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
434 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000435 armnn::TensorInfo& tensorInfo,
436 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100437{
Jan Eilers8eb25602020-03-09 12:13:48 +0000438 IgnoreUnused(tensorPtr);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100439 ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
440 ARMNN_ASSERT_MSG(bufferPtr != nullptr,
telsoa01c577f2c2018-08-31 09:22:23 +0100441 boost::str(
442 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
443
444 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000445
446 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
447 {
448 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000449 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
450 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000451 }
452 else
453 {
454 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
455 }
456
telsoa01c577f2c2018-08-31 09:22:23 +0100457 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
458}
459
telsoa01c577f2c2018-08-31 09:22:23 +0100460armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
461{
462 // generate the binding id by shifting the tensor id by 8 bit
463 // and add the subgraph id, which allows 256 subgraphs
464 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
465}
466
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000467bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
468{
469 const unsigned int actualSize = actual.GetNumDimensions();
470 if (actualSize != expected.size())
471 {
472 return false;
473 }
474
475 for (unsigned int i = 0u; i < actualSize; i++)
476 {
477 if (expected[i] < 0 ||
478 actual[i] != static_cast<unsigned int>(expected[i]))
479 {
480 return false;
481 }
482 }
483
484 return true;
485}
486
telsoa01c577f2c2018-08-31 09:22:23 +0100487} // <anonymous>
488
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100489TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
490: m_Options(options)
491, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100492, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
493{
494 // register supported operators
Sadik Armagan66dedc72019-12-10 16:32:07 +0000495 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000496 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
497 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
498 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
499 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000500 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000501 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Finn Williamsed66d142019-12-06 09:55:55 +0000502 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
Derek Lambertif0176992020-04-28 13:37:49 +0100503 m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParser::ParseExp;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000504 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
505 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
506 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
507 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
508 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000509 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000510 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000511 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
512 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
513 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
514 m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000515 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
516 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
517 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
518 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
519 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000520 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000521 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
522 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000523 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
Derek Lambertif0176992020-04-28 13:37:49 +0100524 m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParser::ParseSplitV;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000525 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
526 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
527 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000528 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
529 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
530 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
531 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100532
533 // register supported custom operators
534 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100535}
536
537void TfLiteParser::ResetParser()
538{
539 m_Network = armnn::INetworkPtr(nullptr, nullptr);
540 m_Model = nullptr;
541 m_SubgraphConnections.clear();
542}
543
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200544void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
545 size_t operatorIndex,
546 IConnectableLayer *layer)
547{
548 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100549 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200550
Derek Lambertiff05cc52019-04-26 13:05:17 +0100551 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
552 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200553
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100554 ARMNN_ASSERT(operatorPtr->inputs.size() > 1);
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200555
556 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100557 TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200558 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100559 TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200560
561 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
562 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
563
564 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
565 {
566 uint32_t id = reshapedInputId;
567 reshapedInputId = inputId;
568 inputId = id;
569
570 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
571 inputTensorInfo = ToTensorInfo(tensorPtr);
572 }
573
574 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
575
576 std::vector<unsigned> reshapedDim;
577 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
578 {
579 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
580 }
581
582 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
583 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
584
585 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
586
587 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
588 armnn::ReshapeDescriptor desc;
589 desc.m_TargetShape = reshapedTensorInfo.GetShape();
590 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
591
592 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
593 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
594
595 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
596
597 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
598 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
599}
600
telsoa01c577f2c2018-08-31 09:22:23 +0100601INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
602{
603 ResetParser();
604 m_Model = LoadModelFromFile(graphFile);
605 return CreateNetworkFromModel();
606}
607
608INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
609{
610 ResetParser();
611 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
612 return CreateNetworkFromModel();
613}
614
615INetworkPtr TfLiteParser::CreateNetworkFromModel()
616{
617 m_Network = INetwork::Create();
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100618 ARMNN_ASSERT(m_Model.get() != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100619
620 bool failedToCreate = false;
621 std::stringstream errors;
622
623 if (m_Model->subgraphs.size() != 1)
624 {
625 throw ParseException(
626 boost::str(
627 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
628 m_Model->subgraphs.size() %
629 CHECK_LOCATION().AsString()));
630 }
631
632 size_t subgraphIndex = 0;
Derek Lambertiff05cc52019-04-26 13:05:17 +0100633 for (SubgraphPtr const & subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100634 {
635 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
636
637 size_t operatorIndex = 0;
638 for (OperatorPtr const & op : subgraph->operators)
639 {
640 try
641 {
telsoa01c577f2c2018-08-31 09:22:23 +0100642 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
643 auto builtinCode = opCodePtr->builtin_code;
644
645 if (builtinCode > tflite::BuiltinOperator_MAX)
646 {
647 throw ParseException(
648 boost::str(
649 boost::format("Operator code %1% is out of range 0-%2%. "
650 "subgraph:%3% operator idx:%4%. %5%") %
651 builtinCode %
652 tflite::BuiltinOperator_MAX %
653 subgraphIndex %
654 operatorIndex %
655 CHECK_LOCATION().AsString()));
656 }
657
658 // lookup and call the parser function
659 auto & parserFunction = m_ParserFunctions[builtinCode];
660 (this->*parserFunction)(subgraphIndex, operatorIndex);
661 }
662 catch (const ParseException& e)
663 {
664 failedToCreate = true;
665 std::stringstream errorString;
666
667 errorString << "Failed to parse operator #" << operatorIndex
668 << " within subgraph #" << subgraphIndex
669 << " error: " << e.what();
Derek Lamberti08446972019-11-26 16:38:31 +0000670 ARMNN_LOG(error) << errorString.str();
telsoa01c577f2c2018-08-31 09:22:23 +0100671
672 errors << errorString.str() << "\n";
673 }
674 ++operatorIndex;
675 }
676
677 SetupInputLayers(subgraphIndex);
678 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200679 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100680
681 ++subgraphIndex;
682 }
683
684 if (failedToCreate)
685 {
686 // we can skip everything and let the outer exception handler deal with the error
687 throw ParseException(errors.str());
688 }
689
690 // establish the connections from the layer outputs to the inputs of the subsequent layers
691 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
692 {
693 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
694 {
695 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
696 {
697 for (size_t inputSlotIdx = 0;
698 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
699 ++inputSlotIdx)
700 {
701 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
702 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
703 }
704 }
705 }
706 }
707
708 return std::move(m_Network);
709}
710
711void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
712 size_t tensorIndex,
713 armnn::IOutputSlot* slot)
714{
715 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100716 ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
717 ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100718
719 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
720
721 // assuming there is only one producer for that tensor
722 if (tensorSlots.outputSlot != nullptr)
723 {
724 throw ParseException(boost::str(
725 boost::format("Another layer has already registered itself as the producer of "
726 "subgraph:%1% tensor:%2% %3%") %
727 subgraphIndex %
728 tensorIndex %
729 CHECK_LOCATION().AsString()));
730 }
731
732 tensorSlots.outputSlot = slot;
733}
734
735void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
736 size_t tensorIndex,
737 armnn::IInputSlot* slot)
738{
739 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100740 ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
741 ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100742
743 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
744 tensorSlots.inputSlots.push_back(slot);
745}
746
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100747void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
748{
749 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
750
751 // NOTE: By default we presume the custom operator is not supported
752 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
753
754 // Identify custom code defined for custom operator
755 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
756 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
757
758 // Find parser function that correspondes to custom code (if any)
759 auto iterator = m_CustomParserFunctions.find(customCode);
760 if (iterator != m_CustomParserFunctions.end())
761 {
762 customParserFunction = iterator->second;
763 }
764
765 // Run parser function
766 (this->*customParserFunction)(subgraphIndex, operatorIndex);
767}
768
telsoa01c577f2c2018-08-31 09:22:23 +0100769void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
770{
771 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100772
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100773 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
774
775 auto opcodeIndex = operatorPtr->opcode_index;
776 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
777
778 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
779 {
780 // Do not add StandInLayer, throw ParseException instead
781 throw ParseException(
782 boost::str(
783 boost::format("Operator not supported. "
784 "subgraph:%1% operator:%2% "
785 "opcode_index:%3% opcode:%4% / %5% %6%") %
786 subgraphIndex %
787 operatorIndex %
788 opcodeIndex %
789 opcode %
790 tflite::EnumNameBuiltinOperator(opcode) %
791 CHECK_LOCATION().AsString()));
792 }
793
794 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
795 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
796
797 const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
798 const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
799
800 StandInDescriptor descriptor(numInputs, numOutputs);
801 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
802
803 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
804 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
805 for (unsigned int i = 0u; i < numOutputs; ++i)
806 {
807 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
808 }
809
810 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
811 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
812
813 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
814 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100815}
816
telsoa01c577f2c2018-08-31 09:22:23 +0100817void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
818{
819 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
820
821 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
822 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
823
824 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
825
826 Convolution2dDescriptor desc;
827 desc.m_BiasEnabled = false;
828 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
829 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000830 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100831 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
832 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000833
telsoa01c577f2c2018-08-31 09:22:23 +0100834 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
835 CHECK_VALID_SIZE(inputs.size(), 2, 3);
836
837 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
838 CHECK_VALID_SIZE(outputs.size(), 1);
839
840 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
841 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
842
843 // assuming input is NHWC
844 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
845 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
846
847 // assuming the filter is OHWI : Output, H, W, Input
848 // which is essentially the same as NHWC
849 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
850 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
851
Pablo Tellof0bd6832019-04-26 17:58:13 +0100852 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
853 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
854 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
855 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100856
Matteo Martincigh747ef822018-12-18 09:26:39 +0000857 auto filterTensorAndData = CreateConstTensor(inputs[1],
858 filterTensorInfo,
859 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100860 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100861
862 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
863
864 if (inputs.size() == 3)
865 {
866 desc.m_BiasEnabled = true;
867 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000868 auto biasTensorAndData = CreateConstTensor(inputs[2],
869 biasTensorInfo,
870 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100871 layer = m_Network->AddConvolution2dLayer(desc,
872 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100873 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100874 layerName.c_str());
875 }
876 else
877 {
878 layer = m_Network->AddConvolution2dLayer(desc,
879 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100880 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100881 layerName.c_str());
882 }
883
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100884 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100885
telsoa01c577f2c2018-08-31 09:22:23 +0100886 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000887 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100888
889 // register the input connection slots for the layer, connections are made after all layers have been created
890 // only the tensors for the inputs are relevant, exclude the const tensors
891 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000892 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100893
jimfly01c25411c2018-11-14 17:47:22 +0000894 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100895 // register the output connection slots for the layer, connections are made after all layers have been created
896 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
897 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
898}
899
900void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
901{
902 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
903
904 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
905 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
906
907 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
908
909 DepthwiseConvolution2dDescriptor desc;
910 desc.m_BiasEnabled = false;
911 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
912 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000913 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100914 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100915
916 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
917 CHECK_VALID_SIZE(inputs.size(), 2, 3);
918 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
919 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100920 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
921 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000922
Keith Davis0c2eeac2020-02-11 16:51:50 +0000923 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
924 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
925
telsoa01c577f2c2018-08-31 09:22:23 +0100926 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000927 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100928
Matteo Martincigh747ef822018-12-18 09:26:39 +0000929 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100930 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
931 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000932
933 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100934 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
935 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
936
Matteo Martincigh747ef822018-12-18 09:26:39 +0000937 // Reshape weights as [ H, W, I, M ]
938 filterTensorInfo.SetShape({ filterHeight,
939 filterWidth,
940 inputTensorInfo.GetShape()[3],
941 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
942
Pablo Tellof0bd6832019-04-26 17:58:13 +0100943 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
944 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
945 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
946 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100947
Matteo Martincigh747ef822018-12-18 09:26:39 +0000948 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100949 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100950 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
951
952 if (inputs.size() == 3)
953 {
954 desc.m_BiasEnabled = true;
955 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000956 auto biasTensorAndData = CreateConstTensor(inputs[2],
957 biasTensorInfo,
958 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100959 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
960 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100961 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100962 layerName.c_str());
963 }
964 else
965 {
966 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
967 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100968 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100969 layerName.c_str());
970 }
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100971 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100972
telsoa01c577f2c2018-08-31 09:22:23 +0100973 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000974 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100975
976 // register the input connection slots for the layer, connections are made after all layers have been created
977 // only the tensors for the inputs are relevant, exclude the const tensors
978 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000979 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100980
jimfly01c25411c2018-11-14 17:47:22 +0000981 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100982 // register the output connection slots for the layer, connections are made after all layers have been created
983 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
984 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
985}
986
Finn Williamsed66d142019-12-06 09:55:55 +0000987void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
988{
989 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
990
991 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
992 CHECK_VALID_SIZE(inputs.size(), 1);
993
994 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
995 CHECK_VALID_SIZE(outputs.size(), 1);
996
997 auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
998
999 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001000 ARMNN_ASSERT(layer != nullptr);
Finn Williamsed66d142019-12-06 09:55:55 +00001001
1002 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1003 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1004
1005 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1006 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1007
1008 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1009 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1010}
1011
Derek Lambertif0176992020-04-28 13:37:49 +01001012void TfLiteParser::ParseExp(size_t subgraphIndex, size_t operatorIndex)
1013{
1014 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1015
1016 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1017 CHECK_VALID_SIZE(inputs.size(), 1);
1018
1019 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1020 CHECK_VALID_SIZE(outputs.size(), 1);
1021
1022 auto layerName = boost::str(boost::format("Exp:%1%:%2%") % subgraphIndex % operatorIndex);
1023
1024 ElementwiseUnaryDescriptor desc;
1025 desc.m_Operation = UnaryOperation::Exp;
1026 IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerName.c_str());
1027 ARMNN_ASSERT(layer != nullptr);
1028
1029 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1030 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1031
1032 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1033 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1034
1035 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1036 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1037}
1038
Keith Davis4cd29a02019-09-09 14:49:20 +01001039void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1040{
1041 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1042
1043 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +01001044 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +01001045
1046 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1047 CHECK_VALID_SIZE(outputs.size(), 1);
1048
1049 armnn::IConnectableLayer* layer = nullptr;
1050 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
1051
Mike Kelly08759e22020-03-02 11:41:31 +00001052 TransposeDescriptor desc;
Keith Davis4cd29a02019-09-09 14:49:20 +01001053
josh minorba424d22019-11-13 10:55:17 -06001054 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +01001055 {
1056 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1057 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -06001058 auto numPermVecElements = permuteTensorInfo.GetNumElements();
1059 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +01001060 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
Mike Kelly08759e22020-03-02 11:41:31 +00001061 PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
Kevin May85d92602019-09-27 17:21:06 +01001062
Mike Kelly08759e22020-03-02 11:41:31 +00001063 desc = TransposeDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +01001064 }
1065
Mike Kelly08759e22020-03-02 11:41:31 +00001066 layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
Keith Davis4cd29a02019-09-09 14:49:20 +01001067
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001068 ARMNN_ASSERT(layer != nullptr);
Keith Davis4cd29a02019-09-09 14:49:20 +01001069
1070 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1071 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1072
1073 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1074 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1075
1076 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1077 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1078}
1079
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001080void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1081{
1082 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1083
1084 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1085 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1086
1087 TransposeConvolution2dDescriptor desc;
1088 desc.m_BiasEnabled = false;
1089 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1090 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1091 desc.m_DataLayout = armnn::DataLayout::NHWC;
1092
1093 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001094 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001095
1096 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1097 CHECK_VALID_SIZE(outputs.size(), 1);
1098
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001099 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001100 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1101
1102 // TfLite uses NHWC tensors
1103 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1104 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1105
1106 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1107 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1108
1109 CalcPadding(inputHeight,
1110 filterHeight,
1111 desc.m_StrideY,
1112 1, // DilationY
1113 desc.m_PadTop,
1114 desc.m_PadBottom,
1115 options->padding);
1116
1117 CalcPadding(inputWidth,
1118 filterWidth,
1119 desc.m_StrideX,
1120 1, // DilationX
1121 desc.m_PadLeft,
1122 desc.m_PadRight,
1123 options->padding);
1124
1125 auto filterTensorAndData = CreateConstTensor(inputs[1],
1126 filterTensorInfo,
1127 armnn::Optional<armnn::PermutationVector&>());
1128
1129 armnn::IConnectableLayer* layer = nullptr;
1130 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1131
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001132 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1133 filterTensorAndData.first,
1134 EmptyOptional(),
1135 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001136
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001137 ARMNN_ASSERT(layer != nullptr);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001138
1139 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1140 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1141
1142 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1143 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001144 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001145
1146 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1147 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1148}
1149
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001150void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1151{
1152 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1153}
1154
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001155void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1156{
1157 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1158
1159 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1160 CHECK_VALID_SIZE(inputs.size(), 3);
1161
1162 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1163 CHECK_VALID_SIZE(outputs.size(), 1);
1164
1165 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1166 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1167
1168 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1169 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1170
1171 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1172 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1173
1174 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1175 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1176
1177 size_t step = 2;
1178 std::vector<std::pair<unsigned int, unsigned int>> crops;
1179 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1180 {
1181 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1182 }
1183
1184 armnn::BatchToSpaceNdDescriptor desc;
1185 desc.m_BlockShape = blockShape;
1186 desc.m_Crops = crops;
1187 desc.m_DataLayout = armnn::DataLayout::NHWC;
1188
1189 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1190
1191 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1192 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1193
1194 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1195
1196 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1197 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1198
1199 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1200 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1201}
1202
Matthew Jackson28c94572019-07-18 10:47:03 +01001203void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1204{
1205 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1206
1207 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1208 CHECK_VALID_SIZE(inputs.size(), 1);
1209
1210 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1211 CHECK_VALID_SIZE(outputs.size(), 1);
1212
1213 L2NormalizationDescriptor desc;
1214 desc.m_DataLayout = armnn::DataLayout::NHWC;
1215 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1216 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1217
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001218 ARMNN_ASSERT(layer != nullptr);
Matthew Jackson28c94572019-07-18 10:47:03 +01001219
1220 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1221 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1222
1223 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1224 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1225
1226 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1227 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1228}
1229
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001230void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1231{
1232 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1233}
1234
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001235void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1236{
1237 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1238
1239 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1240 CHECK_VALID_SIZE(inputs.size(), 2);
1241
1242 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1243 CHECK_VALID_SIZE(outputs.size(), 1);
1244
1245 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1246 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1247
1248 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1249 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1250
1251 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1252 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1253
1254 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1255 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1256 {
1257 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1258 }
1259 else
1260 {
1261 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1262 }
1263
1264 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1265 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1266}
1267
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001268void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1269{
1270 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1271
1272 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1273 CHECK_VALID_SIZE(inputs.size(), 2);
1274
1275 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1276 CHECK_VALID_SIZE(outputs.size(), 1);
1277
1278 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1279 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1280
1281 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1282 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1283
1284 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1285 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1286
1287 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1288 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1289 {
1290 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1291 }
1292 else
1293 {
1294 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1295 }
1296
1297 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1298 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1299}
1300
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001301void TfLiteParser::ParsePool(size_t subgraphIndex,
1302 size_t operatorIndex,
1303 PoolingAlgorithm algorithm)
1304{
1305 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1306
1307 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1308 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1309
1310 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1311
1312 std::string layerName;
1313
1314 switch (algorithm)
1315 {
1316 case PoolingAlgorithm::Average:
1317 layerName =
1318 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1319 break;
1320 case PoolingAlgorithm::Max:
1321 layerName =
1322 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1323 break;
1324 default:
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001325 ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001326 }
1327
1328 Pooling2dDescriptor desc;
1329
1330 desc.m_PoolType = algorithm;
1331 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1332 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1333 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1334 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1335 desc.m_PaddingMethod = PaddingMethod::Exclude;
1336 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001337 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001338
1339 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1340 CHECK_VALID_SIZE(inputs.size(), 1);
1341 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1342
1343 // assuming input is NHWC
1344 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1345 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1346
Pablo Tellof0bd6832019-04-26 17:58:13 +01001347 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1348 desc.m_PadTop, desc.m_PadBottom, options->padding);
1349 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1350 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001351
1352 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1353 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001354
1355 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1356
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001357 ARMNN_ASSERT(layer != nullptr);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001358
jimfly01c25411c2018-11-14 17:47:22 +00001359 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1360 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001361
1362 // register the input connection slots for the layer, connections are made after all layers have been created
1363 // only the tensors for the inputs are relevant, exclude the const tensors
1364 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001365 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001366
jimfly01c25411c2018-11-14 17:47:22 +00001367 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001368 // register the output connection slots for the layer, connections are made after all layers have been created
1369 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1370 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1371}
1372
josh minorba424d22019-11-13 10:55:17 -06001373void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1374{
1375 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1376
1377 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1378 CHECK_VALID_SIZE(inputs.size(), 3);
1379 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1380 CHECK_VALID_SIZE(outputs.size(), 1);
1381
1382 SliceDescriptor desc;
1383
1384 // set begin tensor info for slice descriptor
1385 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1386 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1387
1388 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1389 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1390
1391 // set size tensor info for slice descriptor
1392 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1393 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1394
1395 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1396 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1397 desc = SliceDescriptor(begin, size);
1398
1399 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1400 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1401
1402 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1403 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1404
1405 // register the input connection slots for the layer, connections are made after all layers have been created
1406 // only the tensors for the inputs are relevant, exclude the const tensors
1407 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1408 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1409
1410 // register the output connection slots for the layer, connections are made after all layers have been created
1411 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1412 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1413}
1414
telsoa01c577f2c2018-08-31 09:22:23 +01001415void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1416{
1417 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1418 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1419 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1420
1421 SoftmaxDescriptor desc;
1422 desc.m_Beta = options->beta;
1423
1424 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1425 CHECK_VALID_SIZE(inputs.size(), 1);
1426 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1427 CHECK_VALID_SIZE(outputs.size(), 1);
1428
1429 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1430 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1431
1432 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1433 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1434
1435 // register the input connection slots for the layer, connections are made after all layers have been created
1436 // only the tensors for the inputs are relevant, exclude the const tensors
1437 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1438 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1439
1440 // register the output connection slots for the layer, connections are made after all layers have been created
1441 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1442 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1443}
1444
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001445void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1446{
1447 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1448
1449 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1450 CHECK_VALID_SIZE(inputs.size(), 3);
1451
1452 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1453 CHECK_VALID_SIZE(outputs.size(), 1);
1454
1455 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1456 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1457
1458 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1459 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1460
1461 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1462 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1463
1464 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1465 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1466
1467 size_t step = 2;
1468 std::vector<std::pair<unsigned int, unsigned int>> padList;
1469 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1470 {
1471 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1472 }
1473
1474 armnn::SpaceToBatchNdDescriptor desc;
1475 desc.m_BlockShape = blockShape;
1476 desc.m_PadList = padList;
1477 desc.m_DataLayout = armnn::DataLayout::NHWC;
1478
1479 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1480
1481 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1482 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1483
1484 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1485
1486 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1487 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1488
1489 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1490 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1491}
1492
telsoa01c577f2c2018-08-31 09:22:23 +01001493armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1494 const armnn::TensorInfo & inputTensorInfo)
1495{
1496 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1497 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1498 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1499
1500 if (inputTensorInfo.GetNumDimensions() > 4)
1501 {
1502 std::stringstream ss;
1503 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1504 << " shape:" << inputTensorInfo.GetShape() << " "
1505 << CHECK_LOCATION().AsString();
1506 throw ParseException(ss.str());
1507 }
1508
1509 if (squeezeDims.empty())
1510 {
1511 squeezeDims.assign(dimensionSequence,
1512 dimensionSequence+inputTensorInfo.GetNumDimensions());
1513 }
1514
1515 std::vector<uint32_t> outputDims;
1516 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1517 {
1518 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1519 auto currentDimension = inputTensorInfo.GetShape()[i];
1520 if (skipSqueeze || currentDimension != 1)
1521 {
1522 outputDims.push_back(currentDimension);
1523 }
1524 }
1525
1526 if (outputDims.size() > 4)
1527 {
1528 std::stringstream ss;
1529 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1530 << " shape:" << inputTensorInfo.GetShape() << " "
1531 << CHECK_LOCATION().AsString();
1532 throw ParseException(ss.str());
1533 }
1534
1535 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1536 outputDims.data());
1537
1538 // we need to preserve the tensor type and the quantization data as well
1539 TensorInfo outTensorInfo = inputTensorInfo;
1540 outTensorInfo.SetShape(outShape);
1541
1542 return outTensorInfo;
1543}
1544
1545void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1546{
1547 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1548
1549 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1550 CHECK_VALID_SIZE(inputs.size(), 1);
1551
1552 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1553 CHECK_VALID_SIZE(outputs.size(), 1);
1554
1555 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1556 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1557
1558 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1559 armnn::TensorInfo outputTensorInfo =
1560 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1561 inputTensorInfo);
1562
1563 ReshapeDescriptor reshapeDesc;
1564 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1565
1566 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1567 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1568 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1569
1570 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1571 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1572
1573 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1574 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1575}
1576
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001577void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1578{
1579 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1580
1581 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1582 CHECK_VALID_SIZE(inputs.size(), 4);
1583
1584 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1585 CHECK_VALID_SIZE(outputs.size(), 1);
1586
1587 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1588 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1589
1590 StridedSliceDescriptor desc;
1591 desc.m_BeginMask = options->begin_mask;
1592 desc.m_EllipsisMask = options->ellipsis_mask;
1593 desc.m_EndMask = options->end_mask;
1594 desc.m_NewAxisMask = options->new_axis_mask;
1595 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1596 desc.m_DataLayout = armnn::DataLayout::NHWC;
1597
1598 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1599 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1600
1601 std::vector<int> begin(beginTensorInfo.GetNumElements());
1602 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1603
1604 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1605 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1606
1607 std::vector<int> end(endTensorInfo.GetNumElements());
1608 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1609
1610 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1611 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1612
1613 std::vector<int> stride(strideTensorInfo.GetNumElements());
1614 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1615
1616 desc.m_Begin = begin;
1617 desc.m_End = end;
1618 desc.m_Stride = stride;
1619
1620 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1621 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1622
1623 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1624 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1625
1626 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1627 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1628
1629 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1630 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1631}
1632
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001633void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1634{
1635 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1636
1637 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1638 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1639
1640 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1641 CHECK_VALID_SIZE(inputs.size(), 2);
1642
1643 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1644 CHECK_VALID_SIZE(outputs.size(), 1);
1645
1646 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1647 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1648
1649 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1650 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1651
1652 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1653 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1654
1655 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1656 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1657 {
1658 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1659 }
1660 else
1661 {
1662 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1663 }
1664
1665 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1666
1667 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1668 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1669}
1670
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001671void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1672{
1673 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1674
1675 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1676 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1677
1678 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1679 CHECK_VALID_SIZE(inputs.size(), 2);
1680
1681 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1682 CHECK_VALID_SIZE(outputs.size(), 1);
1683
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001684 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1685 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1686
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001687 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1688 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1689
1690 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1691 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1692
1693 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001694 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1695 {
1696 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1697 }
1698 else
1699 {
1700 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1701 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001702
1703 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1704
1705 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1706 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1707}
1708
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001709void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1710{
1711 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1712
1713 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1714 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1715
1716 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1717 CHECK_VALID_SIZE(inputs.size(), 2);
1718
1719 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1720 CHECK_VALID_SIZE(outputs.size(), 1);
1721
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001722 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1723 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1724
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001725 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1726 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1727
1728 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1729 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1730
1731 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001732 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1733 {
1734 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1735 }
1736 else
1737 {
1738 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1739 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001740
1741 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1742
1743 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1744 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1745}
1746
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001747void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1748{
1749 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1750
1751 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1752
1753 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1754 CHECK_VALID_SIZE(outputs.size(), 1);
1755
1756 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1757 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1758
1759 armnn::MeanDescriptor desc;
1760 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1761 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1762 desc.m_Axis = axis;
1763
1764 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1765 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1766
1767 desc.m_KeepDims =
1768 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1769 true : false;
1770
1771 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1772 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1773
1774 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1775
1776 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1777 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1778
1779 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1780 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1781}
1782
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001783void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1784{
1785 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1786
1787 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1788
1789 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1790 CHECK_VALID_SIZE(outputs.size(), 1);
1791
1792 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1793 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1794
1795 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1796 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1797
1798 size_t step = 2;
1799 armnn::PadDescriptor desc;
1800 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1801 {
1802 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1803 }
1804
1805 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1806 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1807
1808 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1809 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1810
1811 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1812 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1813
1814 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1815 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1816}
1817
Sadik Armagan66dedc72019-12-10 16:32:07 +00001818void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
1819{
1820 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1821
1822 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1823 CHECK_VALID_SIZE(inputs.size(), 1);
1824
1825 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1826 CHECK_VALID_SIZE(outputs.size(), 1);
1827
1828 auto layerName = boost::str(boost::format("Quantize:%1%:%2%") % subgraphIndex % operatorIndex);
1829
1830 IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001831 ARMNN_ASSERT(layer != nullptr);
Sadik Armagan66dedc72019-12-10 16:32:07 +00001832
1833 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1834 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1835
1836 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1837 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1838
1839 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1840 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1841}
Finn Williamsc42c3842019-01-22 14:18:11 +00001842
Sadik Armagan58f39192018-09-17 14:14:39 +01001843void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1844{
Finn Williamsc42c3842019-01-22 14:18:11 +00001845 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001846}
1847
1848void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1849{
Finn Williamsc42c3842019-01-22 14:18:11 +00001850 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1851}
Sadik Armagan58f39192018-09-17 14:14:39 +01001852
Finn Williamsc42c3842019-01-22 14:18:11 +00001853void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1854{
1855 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1856}
1857
Nina Drozd99851762019-04-09 09:37:38 +01001858void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1859{
1860 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1861}
1862
Finn Williamsc42c3842019-01-22 14:18:11 +00001863
1864void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1865{
1866 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001867 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
Jan Eilers8eb25602020-03-09 12:13:48 +00001868 IgnoreUnused(operatorPtr);
Sadik Armagan58f39192018-09-17 14:14:39 +01001869
1870 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1871 CHECK_VALID_SIZE(inputs.size(), 1);
1872
1873 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1874 CHECK_VALID_SIZE(outputs.size(), 1);
1875
Finn Williamsc42c3842019-01-22 14:18:11 +00001876 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001877 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001878 activationDesc.m_Function = activationType;
1879
1880 switch (activationType)
1881 {
1882 case ActivationFunction::ReLu:
1883 {
1884 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1885 break;
1886 }
1887 case ActivationFunction::BoundedReLu:
1888 {
1889 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1890 activationDesc.m_A = 6.0f;
1891 activationDesc.m_B = 0.0f;
1892 break;
1893 }
1894 case ActivationFunction::Sigmoid:
1895 {
1896 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1897 break;
1898 }
Nina Drozd99851762019-04-09 09:37:38 +01001899 case ActivationFunction::TanH:
1900 {
1901 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1902 activationDesc.m_A = 1.0f;
1903 activationDesc.m_B = 1.0f;
1904 break;
1905 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001906 default:
1907 {
1908 throw ParseException(
1909 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1910 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1911 }
1912 }
1913
1914 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001915
1916 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1917 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1918
1919 // register the input connection slots for the layer, connections are made after all layers have been created
1920 // only the tensors for the inputs are relevant, exclude the const tensors
1921 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1922 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1923
1924 // register the output connection slots for the layer, connections are made after all layers have been created
1925 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1926 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1927}
Sadikb94967b2018-09-19 15:30:00 +01001928armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1929 const std::vector<int32_t> & targetDimsIn)
1930{
1931 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1932 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1933
1934 if (stretchDim != targetDimsIn.end())
1935 {
1936 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1937 {
1938 throw ParseException(
1939 boost::str(
1940 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1941 }
1942
1943 auto targetNumElements =
1944 boost::numeric_cast<unsigned int>(
1945 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1946
1947 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1948 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1949 }
1950
1951 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1952
1953 TensorInfo reshapeInfo = inputTensorInfo;
1954 reshapeInfo.SetShape(outputShape);
1955
1956 return reshapeInfo;
1957}
1958
1959void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1960{
1961 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1962
1963 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001964
1965 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1966 CHECK_VALID_SIZE(outputs.size(), 1);
1967
1968 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1969 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1970
1971 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001972 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
Derek Lambertic9e52792020-03-11 11:42:26 +00001973
1974 std::vector<int32_t> targetShape;
1975 if (inputs.size() > 1 && inputs[1] != nullptr)
1976 {
Derek Lambertic9e52792020-03-11 11:42:26 +00001977 if (inputs[1]->is_variable)
1978 {
1979 ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
1980 }
1981
1982 if (inputs[1]->shape.size() != 1)
1983 {
1984 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
1985 }
1986
1987 if (inputs[1]->type != tflite::TensorType_INT32)
1988 {
1989 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
1990 }
1991
1992 auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1993 auto vals = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
1994 for (int i=0; i < inputs[1]->shape[0]; i++)
1995 {
1996 targetShape.push_back(vals[i]);
1997 }
Derek Lambertif4a953f2020-03-17 14:25:57 +00001998
1999 if (options != nullptr &&
2000 options->new_shape.empty() == false &&
2001 options->new_shape != targetShape)
2002 {
2003 ARMNN_THROW_PARSE_EXCEPTION("Target shape defined in reshape parameters and as input tensor but "
2004 "the values do not match");
2005 }
Derek Lambertic9e52792020-03-11 11:42:26 +00002006 }
2007 else
2008 {
2009 if (options == nullptr)
2010 {
2011 ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2012 "At least one method required");
2013 }
2014
2015 targetShape = options->new_shape;
2016 }
2017
kevmay0171972a82018-12-17 14:28:03 +00002018 armnn::TensorInfo reshapeOutputTensorInfo =
Derek Lambertic9e52792020-03-11 11:42:26 +00002019 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
Sadikb94967b2018-09-19 15:30:00 +01002020
kevmay0171972a82018-12-17 14:28:03 +00002021 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00002022 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2023 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00002024 {
2025 std::stringstream ss;
2026 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00002027 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00002028 << " does not equal output shape "
2029 << actualOutputTensorInfo.GetShape()
2030 << ": "
2031 << CHECK_LOCATION().AsString();
2032 throw ParseException(ss.str());
2033 }
2034
Sadikb94967b2018-09-19 15:30:00 +01002035 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00002036 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01002037
2038 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
2039 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00002040 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01002041
2042 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2043 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2044
2045 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2046 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2047}
2048
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002049void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2050{
Sadik Armagana3b31f02019-12-05 09:08:53 +00002051 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2052}
2053
2054void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2055{
2056 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2057}
2058
2059void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2060{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002061 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2062
2063 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2064 CHECK_VALID_SIZE(inputs.size(), 2);
2065
2066 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2067 CHECK_VALID_SIZE(outputs.size(), 1);
2068
2069 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2070
2071 // Data for the parsed tensor args (size) must be stored locally.
2072 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2073
2074 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2075 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2076
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002077 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002078 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002079 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002080 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2081 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002082
Sadik Armagana3b31f02019-12-05 09:08:53 +00002083 auto layerName = str(boost::format("Resize:"));
2084
2085 switch (resizeMethod)
2086 {
2087 case ResizeMethod::Bilinear:
2088 {
2089 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
Sang-Hoon Park820eb142020-01-08 10:25:24 +00002090
2091 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2092 const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2093
2094 desc.m_BilinearAlignCorners = options->align_corners;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002095 break;
2096 }
2097 case ResizeMethod::NearestNeighbor:
2098 {
2099 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
2100 break;
2101 }
2102 default:
2103 {
2104 throw ParseException(
2105 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
2106 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
2107 }
2108 }
2109
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002110 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002111
2112 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2113 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2114
2115 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2116 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2117
2118 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2119 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2120}
2121
Sadik Armagan479045b2018-10-01 11:51:37 +01002122void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2123{
2124 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2125
2126 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2127 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2128
2129 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2130
2131 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2132 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2133 CHECK_VALID_SIZE(outputs.size(), 1);
2134
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002135 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2136 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01002137
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002138 const unsigned int concatDimInput = static_cast<unsigned int>(
2139 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01002140
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002141 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2142 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01002143
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002144 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01002145
2146 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2147 {
2148 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2149
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002150 // This set up concatDescriptor view origin
2151 armnnUtils::ProcessConcatInputTensorInfo(
2152 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01002153 }
2154
2155 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Jim Flynn906f9462019-05-10 13:55:21 +01002156 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Sadik Armagan479045b2018-10-01 11:51:37 +01002157
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002158 ARMNN_ASSERT(layer != nullptr);
Sadik Armagan479045b2018-10-01 11:51:37 +01002159
2160 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2161 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01002162
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002163 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01002164
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002165 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01002166
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002167 // add fused activation layer
2168 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01002169
Sadik Armagan479045b2018-10-01 11:51:37 +01002170 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2171 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2172}
2173
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002174void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2175{
2176 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2177
2178 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2179 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2180
2181 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2182
2183 FullyConnectedDescriptor desc;
2184 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002185 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002186
2187 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2188 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2189 CHECK_VALID_SIZE(outputs.size(), 1);
2190
2191 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2192
2193 // Fully Connected Layer accepts two dimensional weights input
2194 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2195 if (weightsDimension != 2)
2196 {
2197 throw ParseException(
2198 boost::str(
2199 boost::format(
2200 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2201 "Node %2%")
2202 % weightsDimension
2203 % CHECK_LOCATION().AsString()));
2204 }
2205
Matteo Martincigh747ef822018-12-18 09:26:39 +00002206 auto filterTensorAndData = CreateConstTensor(inputs[1],
2207 filterTensorInfo,
2208 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002209 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002210 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2211
2212 if (inputs.size() == 3)
2213 {
2214 desc.m_BiasEnabled = true;
2215 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002216 auto biasTensorAndData = CreateConstTensor(inputs[2],
2217 biasTensorInfo,
2218 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002219 layer = m_Network->AddFullyConnectedLayer(desc,
2220 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002221 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002222 layerName.c_str());
2223 }
2224 else
2225 {
2226 layer = m_Network->AddFullyConnectedLayer(desc,
2227 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002228 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002229 layerName.c_str());
2230 }
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002231 ARMNN_ASSERT(layer != nullptr);
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002232
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002233 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2234
2235 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2236
2237 if (inputTensorInfo.GetNumDimensions() > 2)
2238 {
2239 // Add reshape to flatten to 2D [batch_size, input_size],
2240 // where "input_size" corresponds to the number of inputs to the layer,
2241 // matching the second dimension of weights,
2242 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2243 std::vector<unsigned int> reshapedDimensions(2);
2244 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2245 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2246
2247 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2248 {
2249 throw ParseException(
2250 boost::str(
2251 boost::format(
2252 "Failed to deduce input tensor shape from filter size %1%")
2253 % reshapedDimensions[1]
2254 % CHECK_LOCATION().AsString()));
2255 }
2256
2257 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2258 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2259
2260 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2261 armnn::ReshapeDescriptor desc;
2262 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2263 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2264
2265 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2266 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2267
2268 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2269 }
2270 else
2271 {
2272 // register the input connection slot for the layer
2273 // only the tensors for the inputs are relevant, exclude the const tensors
2274 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2275 }
2276
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002277 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2278 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2279
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002280 // we need to add the activation layer and fortunately we don't need to care about the data layout
2281 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2282 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002283
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002284 // register the output connection slots for the layer, connections are made after all layers have been created
2285 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2286 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2287}
2288
keidav011b3e2ea2019-02-21 10:07:37 +00002289void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2290{
2291 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2292
2293 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2294
2295 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2296 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2297 CHECK_VALID_SIZE(outputs.size(), 4);
2298
2299 // Obtain custom options from flexbuffers
2300 auto custom_options = operatorPtr->custom_options;
2301 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2302
2303 // Obtain descriptor information from tf lite
2304 DetectionPostProcessDescriptor desc;
2305 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2306 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2307 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2308 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2309 desc.m_NumClasses = m["num_classes"].AsUInt32();
2310 desc.m_ScaleH = m["h_scale"].AsFloat();
2311 desc.m_ScaleW = m["w_scale"].AsFloat();
2312 desc.m_ScaleX = m["x_scale"].AsFloat();
2313 desc.m_ScaleY = m["y_scale"].AsFloat();
2314
keidav0107d58c72019-02-26 11:57:39 +00002315 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002316 {
keidav0107d58c72019-02-26 11:57:39 +00002317 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002318 }
2319 if (!(m["detections_per_class"].IsNull()))
2320 {
2321 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2322 }
2323
2324 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2325 {
2326 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2327 "must be positive and less than or equal to 1.");
2328 }
2329
2330 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2331 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2332 armnn::Optional<armnn::PermutationVector&>());
2333
2334 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2335 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2336 layerName.c_str());
2337
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002338 ARMNN_ASSERT(layer != nullptr);
keidav011b3e2ea2019-02-21 10:07:37 +00002339
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002340 // The model does not specify the output shapes.
2341 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2342 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2343 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2344 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2345 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2346 m_OverridenOutputShapes.push_back({ 1 });
2347
keidav011b3e2ea2019-02-21 10:07:37 +00002348 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2349 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002350 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002351 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2352 }
2353
2354 // Register the input connection slots for the layer, connections are made after all layers have been created
2355 // only the tensors for the inputs are relevant, exclude the const tensors
2356 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2357 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2358
2359 // Register the output connection slots for the layer, connections are made after all layers have been created
2360 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2361 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2362 outputTensorIndexes[1],
2363 outputTensorIndexes[2],
2364 outputTensorIndexes[3]});
2365}
2366
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002367/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2368void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2369{
2370 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2371
2372 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2373 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2374 CHECK_VALID_SIZE(outputs.size(), 1);
2375
2376 if (inputs.size() < 1)
2377 {
2378 throw ParseException("Pack must have at least one input.");
2379 }
2380
2381 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2382 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2383
2384 StackDescriptor desc;
2385 desc.m_Axis = static_cast<uint32_t>(options->axis);
2386 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2387
2388 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2389 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2390 desc.m_InputShape = inputTensorInfo.GetShape();
2391
2392 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2393 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2394
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002395 ARMNN_ASSERT(layer != nullptr);
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002396
2397 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2398 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2399
2400 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2401 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2402
2403 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2404 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2405}
2406
Nina Drozd200e3802019-04-15 09:47:39 +01002407void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2408{
2409 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2410
2411 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2412 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2413
2414 // This unpackAxis indicates the axis to unpack
2415 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2416
2417 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2418 CHECK_VALID_SIZE(inputs.size(), 1);
2419
2420 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002421
2422 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2423 {
2424 throw ParseException(
2425 boost::str(
2426 boost::format(
2427 "The unpack axis: %1% cannot be greater than or equal to "
2428 "the number of input dimension %2% %3%")
2429 % unpackAxis
2430 % inputTensorInfo.GetNumDimensions()
2431 % CHECK_LOCATION().AsString()));
2432 }
2433
Nina Drozd200e3802019-04-15 09:47:39 +01002434 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2435 // If num is not defined, automatically infer from the length of the dimension axis.
2436 if(unpackNum == 0)
2437 {
2438 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2439 }
2440
2441 // If unpack number cannot be inferred and is still zero, throw ParseException.
2442 if(unpackNum == 0)
2443 {
2444 throw ParseException("Number to unpack must greater than zero.");
2445 }
2446
2447 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2448 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2449
2450 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2451 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2452
2453 // Add current input shape to unpackDimSizes
2454 for (unsigned int i = 0; i < inputDimSize; ++i)
2455 {
2456 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2457 }
2458
2459 if (unpackDimSizes[unpackAxis] != unpackNum)
2460 {
2461 throw ParseException("Number to unpack must be the same as length of the dimension to "
2462 "unpack along.");
2463 }
2464
2465 unpackDimSizes[unpackAxis] /= unpackNum;
2466
2467 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2468 for (unsigned int j = 0; j < unpackNum; ++j)
2469 {
2470 // Set the size of the views.
2471 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2472 {
2473 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2474 }
2475 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2476 }
2477
2478 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2479 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2480
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002481 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2482 unpackDimSizes.data());
2483
Nina Drozd200e3802019-04-15 09:47:39 +01002484 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2485 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2486
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002487 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2488 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2489 {
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002490 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002491 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2492 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002493 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002494 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2495
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002496 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2497 outputTensorInfo.GetDataType(),
2498 outputTensorInfo.GetQuantizationScale(),
2499 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002500 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2501
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002502 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002503
2504 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2505 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2506 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2507 }
Nina Drozd200e3802019-04-15 09:47:39 +01002508}
2509
Nina Drozd0324f482019-04-08 10:52:10 +01002510void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2511{
2512 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2513
2514 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2515 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2516
2517 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2518
Nina Drozd200e3802019-04-15 09:47:39 +01002519 // If number of splits cannot be inferred and is zero, throw ParseException.
2520 if(numSplits == 0)
2521 {
2522 throw ParseException("Number to splits must greater than zero.");
2523 }
2524
Nina Drozd0324f482019-04-08 10:52:10 +01002525 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2526 CHECK_VALID_SIZE(inputs.size(), 2);
2527 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2528 CHECK_VALID_SIZE(outputs.size(), numSplits);
2529
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002530 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2531 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002532
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002533 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2534 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2535 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2536
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002537 ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002538 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002539
Nina Drozd0324f482019-04-08 10:52:10 +01002540 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002541 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002542 {
2543 throw ParseException(
2544 boost::str(
2545 boost::format(
2546 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002547 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002548 % inputTensorInfo.GetNumDimensions()
2549 % MaxNumOfTensorDimensions
2550 % CHECK_LOCATION().AsString()));
2551 }
2552
2553 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2554
2555 // Add current input shape to splitterDimSizes
2556 for (unsigned int i = 0; i < inputDimSize; ++i)
2557 {
2558 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2559 }
2560
2561 if (splitterDimSizes[splitDim] % numSplits != 0)
2562 {
2563 throw ParseException("Number of splits must evenly divide the dimension");
2564 }
2565 splitterDimSizes[splitDim] /= numSplits;
2566
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002567 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002568 for (unsigned int j = 0; j < numSplits; ++j)
2569 {
2570 // Set the size of the views.
2571 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2572 {
2573 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2574 }
2575 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2576 }
2577
2578 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2579 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2580
2581 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002582 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002583
Nina Drozd0324f482019-04-08 10:52:10 +01002584 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2585 {
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002586 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2587 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002588 }
2589
2590 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2591 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2592}
2593
Derek Lambertif0176992020-04-28 13:37:49 +01002594unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2595{
2596 int numDims = armnn::numeric_cast<int>(numDimsIn);
2597 int v = idx < 0 ? numDims + idx : idx;
2598 ARMNN_ASSERT(v >= 0);
2599 ARMNN_ASSERT(v < numDims);
2600
2601 return static_cast<unsigned int>(v);
2602}
2603
2604void TfLiteParser::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2605{
2606 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2607
2608 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2609
2610
2611 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2612 CHECK_VALID_SIZE(inputs.size(), 3);
2613
2614 auto& inputTensor = inputs[0];
2615 auto& splitsTensor = inputs[1];
2616 auto& axisTensor = inputs[2];
2617
2618 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
2619 armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
2620 armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
2621 ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2622
2623 // Inputs
2624 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2625 if (inputDimSize > MaxNumOfTensorDimensions)
2626 {
2627 throw ParseException(
2628 boost::str(
2629 boost::format(
2630 "The number of dimensions: %1% for input tensors of the "
2631 "split op cannot be greater than %2% %3%")
2632 % inputTensorInfo.GetNumDimensions()
2633 % MaxNumOfTensorDimensions
2634 % CHECK_LOCATION().AsString()));
2635 }
2636
2637 // Get split axis
2638 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
2639 std::vector<int> axisData(axisTensorInfo.GetNumElements());
2640 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2641 const unsigned int splitDim = ComputeWrappedIndex(axisData[0], inputTensorInfo.GetNumDimensions());
2642
2643
2644 // Set split sizes
2645 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2646 CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
2647 unsigned int numSplits = 0;
2648 std::vector<int> splitsData(0);
2649 if (options)
2650 {
2651 numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2652 splitsData.resize(numSplits);
2653
2654 if (inputTensorInfo.GetShape()[splitDim] % numSplits != 0)
2655 {
2656 throw ParseException("Number of splits must evenly divide the split axis");
2657 }
2658 unsigned int splitSize = inputTensorInfo.GetShape()[splitDim] / numSplits;
2659 for (auto& split : splitsData)
2660 {
2661 split = numeric_cast<int>(splitSize);
2662 }
2663 }
2664 else
2665 {
2666 numSplits = splitsInfo.GetShape()[0];
2667 splitsData.resize(numSplits);
2668
2669 BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
2670 ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
2671
2672 int numInferred = 0;
2673 int specifiedSizes = 0;
2674 unsigned int inferIdx = 0;
2675 unsigned int idx = 0;
2676 for (auto split : splitsData)
2677 {
2678 if (split < 0)
2679 {
2680 numInferred++;
2681 inferIdx = idx;
2682 }
2683 else
2684 {
2685 specifiedSizes += split;
2686 }
2687 idx++;
2688 }
2689
2690 if (numInferred > 0)
2691 {
2692 if (numInferred > 1)
2693 {
2694 throw ParseException("Cannot infer split size for more than one split");
2695 }
2696 splitsData[inferIdx] = numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - specifiedSizes;
2697 }
2698 }
2699
2700 if (numSplits <=0)
2701 {
2702 throw ParseException("SplitV has invalid number of splits");
2703 }
2704
2705 //Ouput size validation
2706 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2707 CHECK_VALID_SIZE(outputs.size(), numSplits);
2708
2709 // Setup Armnn descriptor
2710 SplitterDescriptor splitDesc(numSplits, inputDimSize);
2711 unsigned int accumSplit = 0;
2712 for (unsigned int j = 0; j < numSplits; ++j)
2713 {
2714 unsigned int splitSize = numeric_cast<unsigned int>(splitsData[j]);
2715
2716 // Set the size of the views.
2717 for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
2718 {
2719 unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
2720 if (dimIdx == splitDim)
2721 {
2722 dimSize = splitSize;
2723 }
2724 splitDesc.SetViewSize(j, dimIdx, dimSize);
2725 }
2726
2727 splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
2728 accumSplit += splitSize;
2729 }
2730
2731 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2732 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2733
2734 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2735 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2736
2737 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2738 {
2739 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2740 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2741 }
2742
2743 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2744 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2745}
2746
Sadik Armagan58f39192018-09-17 14:14:39 +01002747armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2748 unsigned int outputSlot,
2749 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002750{
2751 ActivationDescriptor activationDesc;
2752 std::string layerName = prevLayer->GetName();
2753
2754 switch(activationType)
2755 {
2756 case tflite::ActivationFunctionType_NONE:
2757 {
2758 // this is a no-op: return previous layer
2759 return prevLayer;
2760 }
2761 case tflite::ActivationFunctionType_RELU:
2762 {
2763 activationDesc.m_Function = ActivationFunction::ReLu;
2764 layerName += ":RELU";
2765 break;
2766 }
2767 case tflite::ActivationFunctionType_RELU6:
2768 {
2769 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2770 activationDesc.m_A = 6.0f;
2771 activationDesc.m_B = 0.0f;
2772 layerName += ":RELU6";
2773 break;
2774 }
2775 case tflite::ActivationFunctionType_TANH:
2776 {
2777 activationDesc.m_Function = ActivationFunction::TanH;
2778 activationDesc.m_A = 1.0f;
2779 activationDesc.m_B = 1.0f;
2780 layerName += ":TANH";
2781 break;
2782 }
2783
2784 // I only put these here as a reminder what others we could support
2785 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2786 case tflite::ActivationFunctionType_SIGN_BIT:
2787 default:
2788 {
2789 throw ParseException(
2790 boost::str(
2791 boost::format("TfLite parser doesn't suppport fused activation: "
2792 "%1%/%2% %3% ") %
2793 activationType %
2794 tflite::EnumNameActivationFunctionType(activationType) %
2795 CHECK_LOCATION().AsString()));
2796
2797 }
2798 }
2799
2800 IConnectableLayer* activationLayer =
2801 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2802
2803 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2804 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2805 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2806 return activationLayer;
2807}
2808
2809TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2810{
2811 if (fileName == nullptr)
2812 {
2813 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2814 CHECK_LOCATION().AsString()));
2815 }
2816 boost::system::error_code errorCode;
2817 boost::filesystem::path pathToFile(fileName);
2818 if (!boost::filesystem::exists(pathToFile, errorCode))
2819 {
Derek Lambertic9e52792020-03-11 11:42:26 +00002820 std::string locationString = CHECK_LOCATION().AsString();
2821 std::string msg = boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2822 fileName %
2823 errorCode %
2824 locationString);
2825 throw FileNotFoundException(msg);
telsoa01c577f2c2018-08-31 09:22:23 +01002826 }
2827 std::ifstream file(fileName, std::ios::binary);
2828 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2829 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2830 fileContent.size());
2831}
2832
2833TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2834{
2835 if (binaryContent == nullptr)
2836 {
2837 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2838 CHECK_LOCATION().AsString()));
2839 }
2840 flatbuffers::Verifier verifier(binaryContent, len);
2841 if (verifier.VerifyBuffer<tflite::Model>() == false)
2842 {
2843 throw ParseException(
2844 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2845 "flatbuffers format. size:%1% %2%") %
2846 len %
2847 CHECK_LOCATION().AsString()));
2848 }
2849 return tflite::UnPackModel(binaryContent);
2850}
2851
2852TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2853 size_t subgraphIndex,
2854 size_t operatorIndex)
2855{
2856 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2857
Derek Lambertiff05cc52019-04-26 13:05:17 +01002858 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2859 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002860
2861 size_t inputCount = operatorPtr->inputs.size();
2862 TensorRawPtrVector result(inputCount);
2863 for (size_t i=0; i<inputCount; ++i)
2864 {
2865 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002866 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002867 }
2868 return result;
2869}
2870
2871TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2872 size_t subgraphIndex,
2873 size_t operatorIndex)
2874{
2875 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2876
Derek Lambertiff05cc52019-04-26 13:05:17 +01002877 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2878 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002879
2880 size_t outputCount = operatorPtr->outputs.size();
2881 TensorRawPtrVector result(outputCount);
2882 for (size_t i=0; i<outputCount; ++i)
2883 {
2884 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2885 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002886 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002887 }
2888 return result;
2889}
2890
2891TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2892 size_t subgraphIndex)
2893{
2894 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002895 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002896
Derek Lambertiff05cc52019-04-26 13:05:17 +01002897 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002898 TensorIdRawPtrVector result(inputCount);
2899 for (size_t i=0; i<inputCount; ++i)
2900 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002901 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01002902 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002903 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002904 }
2905 return result;
2906}
2907
2908TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2909 size_t subgraphIndex)
2910{
2911 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002912 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002913
Derek Lambertiff05cc52019-04-26 13:05:17 +01002914 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002915 TensorIdRawPtrVector result(outputCount);
2916 for (size_t i=0; i<outputCount; ++i)
2917 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002918 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2919 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002920 }
2921 return result;
2922}
2923
2924std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2925 size_t subgraphIndex,
2926 size_t operatorIndex)
2927{
2928 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002929 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2930 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002931 return operatorPtr->inputs;
2932}
2933
2934std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2935 size_t subgraphIndex,
2936 size_t operatorIndex)
2937{
2938 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002939 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2940 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002941 return operatorPtr->outputs;
2942}
2943
2944void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2945 size_t operatorIndex,
2946 IConnectableLayer* layer,
2947 const std::vector<unsigned int>& tensorIndexes)
2948{
2949 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002950 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01002951 if (tensorIndexes.size() != layer->GetNumInputSlots())
2952 {
2953 throw ParseException(
2954 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2955 " for subgraph:%3% operator index:%4% %5%") %
2956 tensorIndexes.size() %
2957 layer->GetNumInputSlots() %
2958 subgraphIndex %
2959 operatorIndex %
2960 CHECK_LOCATION().AsString()));
2961 }
2962
2963 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2964 {
2965 unsigned int tensorIndex = tensorIndexes[slotIndex];
2966 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2967 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2968 }
2969}
2970
2971void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2972 size_t operatorIndex,
2973 IConnectableLayer* layer,
2974 const std::vector<unsigned int>& tensorIndexes)
2975{
2976 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002977 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01002978 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2979 {
2980 throw ParseException(
2981 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2982 " for subgraph:%3% operator index:%4% %5%") %
2983 tensorIndexes.size() %
2984 layer->GetNumOutputSlots() %
2985 subgraphIndex %
2986 operatorIndex %
2987 CHECK_LOCATION().AsString()));
2988 }
2989
2990 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2991 {
2992 unsigned int tensorIndex = tensorIndexes[slotIndex];
2993 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2994 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2995 }
2996}
2997
2998void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2999{
3000 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3001
3002 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3003 for (auto const & tensorIdAndPtr : inputs)
3004 {
3005 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3006 IConnectableLayer* layer =
3007 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3008
3009 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3010 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3011
3012 RegisterOutputSlots(subgraphIndex,
3013 VIRTUAL_OPERATOR_ID,
3014 layer,
3015 { static_cast<uint32_t>(tensorIdAndPtr.first) });
3016 }
3017}
3018
3019void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
3020{
3021 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3022
3023 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3024 for (auto const & tensorIdAndPtr : outputs)
3025 {
3026 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3027 IConnectableLayer* layer =
3028 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3029
3030 RegisterInputSlots(subgraphIndex,
3031 VIRTUAL_OPERATOR_ID,
3032 layer,
3033 { static_cast<uint32_t>(tensorIdAndPtr.first) });
3034 }
3035}
3036
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003037void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
3038{
3039 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3040
Derek Lambertiff05cc52019-04-26 13:05:17 +01003041 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003042 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3043 {
3044 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3045 {
3046 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3047 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3048 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01003049 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003050 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3051 auto tensorAndData = CreateConstTensor(tensorPtr,
3052 tensorInfo,
3053 armnn::Optional<armnn::PermutationVector&>());
3054
3055 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
3056 IConnectableLayer *layer =
3057 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
3058
3059 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3060 RegisterOutputSlots(subgraphIndex,
3061 VIRTUAL_OPERATOR_ID,
3062 layer,
3063 { tensorIndex });
3064
3065 }
3066 }
3067 }
3068}
3069
telsoa01c577f2c2018-08-31 09:22:23 +01003070// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3071TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
3072{
3073 CHECK_BUFFER(model, bufferIndex);
3074 return model->buffers[bufferIndex].get();
3075}
3076
Matteo Martincigh747ef822018-12-18 09:26:39 +00003077template<typename T>
3078std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3079TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
3080 TfLiteParser::TensorRawPtr tensorPtr,
3081 armnn::TensorInfo& tensorInfo,
3082 armnn::Optional<armnn::PermutationVector&> permutationVector)
3083{
3084 auto constData = CreateConstTensorImpl<T>(bufferPtr,
3085 tensorPtr,
3086 tensorInfo,
3087 permutationVector);
3088 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
3089 return std::make_pair(constData.first, std::move(storage));
3090}
3091
telsoa01c577f2c2018-08-31 09:22:23 +01003092std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3093TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00003094 armnn::TensorInfo& tensorInfo,
3095 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01003096{
3097 CHECK_TENSOR_PTR(tensorPtr);
3098 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3099 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3100
3101 switch (tensorInfo.GetDataType())
3102 {
3103 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003104 return CreateConstTensorAndStoreData<float>(bufferPtr,
3105 tensorPtr,
3106 tensorInfo,
3107 permutationVector);
Derek Lambertif90c56d2020-01-10 17:14:08 +00003108 case armnn::DataType::QAsymmU8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003109 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3110 tensorPtr,
3111 tensorInfo,
3112 permutationVector);
Keith Davisd305e1a2020-01-22 11:57:54 +00003113 case armnn::DataType::QSymmS8:
3114 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3115 tensorPtr,
3116 tensorInfo,
3117 permutationVector);
Keith Davis67e6c542020-02-19 10:08:33 +00003118 case armnn::DataType::QAsymmS8:
3119 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3120 tensorPtr,
3121 tensorInfo,
3122 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01003123 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003124 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3125 tensorPtr,
3126 tensorInfo,
3127 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01003128 default:
3129 {
3130 std::stringstream errString;
3131 errString << "Unexpected datatype when creating const tensor: "
3132 << armnn::GetDataTypeName(tensorInfo.GetDataType())
3133 << " shape:" << tensorInfo.GetShape()
3134 << CHECK_LOCATION().AsString();
3135 throw ParseException(errString.str());
3136 }
3137 }
3138}
3139
3140BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
3141 const std::string& name) const
3142{
3143 CHECK_SUBGRAPH(m_Model, subgraphId);
3144 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3145 for (auto const & input : inputs)
3146 {
3147 if (input.second->name == name)
3148 {
3149 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3150 return std::make_pair(bindingId, ToTensorInfo(input.second));
3151 }
3152 }
3153
3154 std::stringstream bindings;
3155 for (auto const & input : inputs)
3156 {
3157 bindings << "'" << input.second->name << "' ";
3158 }
3159
3160 throw ParseException(
3161 boost::str(
3162 boost::format("No input binding found for subgraph:%1% and name:%2%. "
3163 "Possible inputs are: [%3%] %4%") %
3164 subgraphId %
3165 name %
3166 bindings.str() %
3167 CHECK_LOCATION().AsString()));
3168}
3169
3170BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
3171 const std::string& name) const
3172{
3173 CHECK_SUBGRAPH(m_Model, subgraphId);
3174 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003175 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01003176 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003177 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01003178 if (output.second->name == name)
3179 {
3180 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003181 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3182 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3183 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01003184 }
3185 }
3186
3187 std::stringstream bindings;
3188 for (auto const & output : outputs)
3189 {
3190 bindings << "'" << output.second->name << "' ";
3191 }
3192
3193 throw ParseException(
3194 boost::str(
3195 boost::format("No output binding found for subgraph:%1% and name:%2%. "
3196 "Possible outputs are: [%3%] %4%") %
3197 subgraphId %
3198 name %
3199 bindings.str() %
3200 CHECK_LOCATION().AsString()));
3201}
3202
3203size_t TfLiteParser::GetSubgraphCount() const
3204{
3205 return m_Model->subgraphs.size();
3206}
3207
3208std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3209{
3210 CHECK_SUBGRAPH(m_Model, subgraphId);
3211 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3212 std::vector<std::string> result;
3213 result.reserve(inputs.size());
3214 for (auto const & input : inputs)
3215 {
3216 result.push_back(input.second->name);
3217 }
3218 return result;
3219}
3220
3221std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3222{
3223 CHECK_SUBGRAPH(m_Model, subgraphId);
3224 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3225 std::vector<std::string> result;
3226 result.reserve(outputs.size());
3227 for (auto const & output : outputs)
3228 {
3229 result.push_back(output.second->name);
3230 }
3231 return result;
3232}
3233
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003234ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003235{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003236 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01003237}
3238
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003239ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003240{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003241 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01003242}
3243
3244void ITfLiteParser::Destroy(ITfLiteParser* parser)
3245{
3246 delete parser;
3247}
3248
3249TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3250: m_FloatData(std::move(data))
3251, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003252, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003253, m_Int32Data(nullptr)
3254{
3255}
3256
3257TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3258: m_FloatData(nullptr)
3259, m_Uint8Data(std::move(data))
Keith Davisd305e1a2020-01-22 11:57:54 +00003260, m_Int8Data(nullptr)
3261, m_Int32Data(nullptr)
3262{
3263}
3264
3265TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3266: m_FloatData(nullptr)
3267, m_Uint8Data(nullptr)
3268, m_Int8Data(std::move(data))
telsoa01c577f2c2018-08-31 09:22:23 +01003269, m_Int32Data(nullptr)
3270{
3271}
3272
3273TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3274: m_FloatData(nullptr)
3275, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003276, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003277, m_Int32Data(std::move(data))
3278{
3279}
3280
3281} // armnnTfLiteParser