blob: 521c5db299f061471f7a16cea534104d0a8683a4 [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//
5#include "TfLiteParser.hpp"
6
7#include <armnn/ArmNN.hpp>
8#include <armnn/Exceptions.hpp>
9#include <armnn/TypesUtils.hpp>
10#include <boost/filesystem.hpp>
11
12// armnnUtils:
Sadik Armagan479045b2018-10-01 11:51:37 +010013#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010014#include <Permute.hpp>
15#include <VerificationHelpers.hpp>
16
17// The generated code based on the Tf Lite schema:
18#include <schema_generated.h>
19
20#include <boost/core/ignore_unused.hpp>
21#include <boost/assert.hpp>
22#include <boost/format.hpp>
23#include <boost/log/trivial.hpp>
24
25#include <fstream>
26#include <algorithm>
27#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010028#include <numeric>
keidav011b3e2ea2019-02-21 10:07:37 +000029#include <flatbuffers/flexbuffers.h>
telsoa01c577f2c2018-08-31 09:22:23 +010030
31using namespace armnn;
32using armnn::CheckLocation;
33namespace armnnTfLiteParser
34{
35namespace
36{
jimfly01c25411c2018-11-14 17:47:22 +000037
telsoa01c577f2c2018-08-31 09:22:23 +010038const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
39
40void CheckSubgraph(const TfLiteParser::ModelPtr & model,
41 size_t subgraphIndex,
42 const CheckLocation & location)
43{
44 if (model.get() == nullptr)
45 {
46 throw ParseException(
47 boost::str(
48 boost::format("%1% was called with invalid (null) model. "
49 "Possible reason is that the model is not yet loaded and Unpack(ed). "
50 "subgraph:%2% at %3%") %
51 location.m_Function %
52 subgraphIndex %
53 location.FileLine()));
54 }
55 else if (subgraphIndex >= model->subgraphs.size())
56 {
57 throw ParseException(
58 boost::str(
59 boost::format("%1% was called with an invalid subgraph index. "
60 "subgraph:%2% at %3%") %
61 location.m_Function %
62 subgraphIndex %
63 location.FileLine()));
64 }
65}
66
67#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
68 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
69
70void CheckModel(const TfLiteParser::ModelPtr & model,
71 size_t subgraphIndex,
72 size_t operatorIndex,
73 const CheckLocation & location)
74{
75 if (model.get() == nullptr)
76 {
77 throw ParseException(
78 boost::str(
79 boost::format("%1% was called with invalid (null) model. "
80 "Possible reason is that the model is not yet loaded and Unpack(ed). "
81 "subgraph:%2% operator:%3% at %4%") %
82 location.m_Function %
83 subgraphIndex %
84 operatorIndex %
85 location.FileLine()));
86 }
87 else if (subgraphIndex >= model->subgraphs.size())
88 {
89 throw ParseException(
90 boost::str(
91 boost::format("%1% was called with an invalid subgraph index. "
92 "subgraph:%2% operator:%3% at %4%") %
93 location.m_Function %
94 subgraphIndex %
95 operatorIndex %
96 location.FileLine()));
97 }
98 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
99 operatorIndex != VIRTUAL_OPERATOR_ID)
100 {
101 throw ParseException(
102 boost::str(
103 boost::format("%1% was called with an invalid operator index. "
104 "subgraph:%2% operator:%3% at %4%") %
105 location.m_Function %
106 subgraphIndex %
107 operatorIndex %
108 location.FileLine()));
109 }
110}
111
112#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
113 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
114
115void CheckTensor(const TfLiteParser::ModelPtr & model,
116 size_t subgraphIndex,
117 size_t tensorIndex,
118 const CheckLocation & location)
119{
120 // not checking model, because I assume CHECK_MODEL already run
121 // and checked that. An assert would do.
122 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
123
124 // also subgraph index should be checked by CHECK_MODEL so
125 // I only add an assert here
126 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
127
128 // the tensor index is the only one to check here
129 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
130 {
131 throw ParseException(
132 boost::str(
133 boost::format("%1% was called with an invalid tensor index. "
134 "subgraph:%2% tensor:%3% at %4%") %
135 location.m_Function %
136 subgraphIndex %
137 tensorIndex %
138 location.FileLine()));
139 }
140}
141
142#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
143 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
144
145void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
146 const CheckLocation & location)
147{
148 if (rawPtr == nullptr)
149 {
150 throw ParseException(
151 boost::str(
152 boost::format("%1% was called with a null tensor pointer. "
153 "at %2%") %
154 location.m_Function %
155 location.FileLine()));
156
157 }
158}
159
160#define CHECK_TENSOR_PTR(TENSOR_PTR) \
161 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
162
163void CheckBuffer(const TfLiteParser::ModelPtr & model,
164 size_t bufferIndex,
165 const CheckLocation & location)
166{
167 if (model.get() == nullptr)
168 {
169 throw ParseException(
170 boost::str(
171 boost::format("%1% was called with invalid (null) model. "
172 "Possible reason is that the model is not yet loaded and Unpack(ed). "
173 "buffer:%2% at %3%") %
174 location.m_Function %
175 bufferIndex %
176 location.FileLine()));
177 }
178 else if (bufferIndex >= model->buffers.size())
179 {
180 throw ParseException(
181 boost::str(
182 boost::format("%1% was called with an invalid buffer index. "
183 "buffer index:%2% at %3%") %
184 location.m_Function %
185 bufferIndex %
186 location.FileLine()));
187 }
188 else if (model->buffers[bufferIndex].get() == nullptr)
189 {
190 throw ParseException(
191 boost::str(
192 boost::format("The buffer #%1% is null. %3%") %
193 bufferIndex %
194 location.AsString()));
195 }
196}
197
198#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
199 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
200
201void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
202 const armnn::TensorInfo & tensorInfo,
203 uint32_t bufferId,
204 const CheckLocation & location)
205{
206 if (bufferPtr == nullptr)
207 {
208 throw ParseException(
209 boost::str(
210 boost::format("BufferPtr is null for buffer:%1%. %2%") %
211 bufferId %
212 location.AsString()));
213 }
214 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
215 tensorInfo.GetNumBytes() > bufferPtr->data.size())
216 {
217 std::stringstream ss;
218 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
219 << "For tensor: " << tensorInfo.GetShape()
220 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
221 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
222 throw ParseException(ss.str());
223 }
224}
225
226#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
227 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
228
229bool IsActivationSupported(tflite::ActivationFunctionType activationType)
230{
231 switch(activationType)
232 {
233 case tflite::ActivationFunctionType_NONE:
234 case tflite::ActivationFunctionType_RELU:
235 case tflite::ActivationFunctionType_RELU6:
236 case tflite::ActivationFunctionType_TANH:
237 {
238 return true;
239 }
240 default:
241 {
242 return false;
243 }
244 }
245}
246
247#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
248 do { \
249 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
250 { \
251 throw ParseException( \
252 boost::str( \
253 boost::format("TfLite parser doesn't suppport fused activation: " \
254 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
255 OPTION->fused_activation_function % \
256 tflite::EnumNameActivationFunctionType(\
257 OPTION->fused_activation_function) % \
258 __func__ % \
259 SUBGRAPH_INDEX % \
260 OPERATOR_INDEX % \
261 CHECK_LOCATION().FileLine())); \
262 } \
263 } while(false)
264
265
266std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
267{
268 std::vector<unsigned int> result;
269 result.reserve(in.size());
270 for (auto & i : in)
271 {
272 result.push_back(CHECKED_NON_NEGATIVE(i));
273 }
274 return result;
275}
276
277void CalcPadding(uint32_t inputSize,
278 uint32_t filterSize,
279 uint32_t stride,
280 uint32_t& paddingFront,
281 uint32_t& paddingBack,
282 tflite::Padding padding)
283{
284 paddingFront = 0;
285 paddingBack = 0;
286 if (padding == tflite::Padding_SAME)
287 {
288 uint32_t outputSize = (inputSize + stride - 1) / stride;
289 uint32_t temp = (outputSize - 1) * stride + filterSize;
290 if (temp > inputSize)
291 {
292 paddingFront = (temp - inputSize) / 2;
293 paddingBack = (temp - inputSize) - paddingFront;
294 }
295 }
296}
297
298armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
299{
300 armnn::DataType type;
301 CHECK_TENSOR_PTR(tensorPtr);
302
303 switch (tensorPtr->type)
304 {
305 case tflite::TensorType_UINT8:
306 type = armnn::DataType::QuantisedAsymm8;
307 break;
308 case tflite::TensorType_FLOAT32:
309 type = armnn::DataType::Float32;
310 break;
311 case tflite::TensorType_INT32:
312 type = armnn::DataType::Signed32;
313 break;
314
315 default:
316 {
317 CheckLocation location = CHECK_LOCATION();
318 throw ParseException(
319 boost::str(
320 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
321 tensorPtr->type %
322 tflite::EnumNameTensorType(tensorPtr->type) %
323 tensorPtr->name %
324 location.AsString()));
325 }
326 }
327
328 float quantizationScale = 0.0f;
329 int32_t quantizationOffset = 0;
330
331 if (tensorPtr->quantization.get())
332 {
333 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
334 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
335
336 if (tensorPtr->quantization->scale.size() == 1)
337 {
338 quantizationScale = tensorPtr->quantization->scale[0];
339 }
340 if (tensorPtr->quantization->zero_point.size() == 1)
341 {
342 // NOTE: we lose precision here when converting from 64 bit to 32
343 // but this is what we support at the monent in ArmNN
344 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
345 }
346 }
347
348 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
349
350 // two statements (on purpose) for easier debugging:
351 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
352 dimensions.data(),
353 type,
354 quantizationScale,
355 quantizationOffset);
356 return result;
357}
358
359template<typename T>
360std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
361CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
362 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000363 armnn::TensorInfo& tensorInfo,
364 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100365{
366 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
367 BOOST_ASSERT_MSG(bufferPtr != nullptr,
368 boost::str(
369 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
370
371 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000372
373 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
374 {
375 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000376 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
377 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000378 }
379 else
380 {
381 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
382 }
383
telsoa01c577f2c2018-08-31 09:22:23 +0100384 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
385}
386
telsoa01c577f2c2018-08-31 09:22:23 +0100387armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
388{
389 // generate the binding id by shifting the tensor id by 8 bit
390 // and add the subgraph id, which allows 256 subgraphs
391 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
392}
393
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000394bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
395{
396 const unsigned int actualSize = actual.GetNumDimensions();
397 if (actualSize != expected.size())
398 {
399 return false;
400 }
401
402 for (unsigned int i = 0u; i < actualSize; i++)
403 {
404 if (expected[i] < 0 ||
405 actual[i] != static_cast<unsigned int>(expected[i]))
406 {
407 return false;
408 }
409 }
410
411 return true;
412}
413
telsoa01c577f2c2018-08-31 09:22:23 +0100414} // <anonymous>
415
416TfLiteParser::TfLiteParser()
417: m_Network(nullptr, nullptr)
418, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
419{
420 // register supported operators
421 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Bruno Goncalvesdb947e22019-02-08 18:52:21 -0200422 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
Sadik Armagan479045b2018-10-01 11:51:37 +0100423 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100424 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
425 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
keidav011b3e2ea2019-02-21 10:07:37 +0000426 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseDetectionPostProcess;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100427 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Finn Williamsc42c3842019-01-22 14:18:11 +0000428 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100429 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -0200430 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -0200431 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
Sadik Armagan58f39192018-09-17 14:14:39 +0100432 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
433 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100434 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -0200435 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
Sadik Armagan479045b2018-10-01 11:51:37 +0100436 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
Bruno Goncalvesbaded142019-02-08 19:02:48 -0200437 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
Sadik Armagan479045b2018-10-01 11:51:37 +0100438 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Bruno Goncalvesbbeae262019-02-07 18:37:39 -0200439 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200440 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Bruno Goncalvesf803f782018-12-18 13:40:30 -0200441 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Bruno Goncalves2235cee2018-12-19 12:51:45 -0200442 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Bruno Goncalves6c2355b2018-12-19 12:52:01 -0200443 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
telsoa01c577f2c2018-08-31 09:22:23 +0100444}
445
446void TfLiteParser::ResetParser()
447{
448 m_Network = armnn::INetworkPtr(nullptr, nullptr);
449 m_Model = nullptr;
450 m_SubgraphConnections.clear();
451}
452
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200453void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
454 size_t operatorIndex,
455 IConnectableLayer *layer)
456{
457 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
458 BOOST_ASSERT(layer != nullptr);
459
460 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
461 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
462
463 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
464
465 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
466 TensorRawPtr tensorPtr = subGraphPtr->tensors[reshapedInputId].get();
467 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
468 TensorRawPtr tensorPtr1 = subGraphPtr->tensors[inputId].get();
469
470 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
471 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
472
473 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
474 {
475 uint32_t id = reshapedInputId;
476 reshapedInputId = inputId;
477 inputId = id;
478
479 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
480 inputTensorInfo = ToTensorInfo(tensorPtr);
481 }
482
483 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
484
485 std::vector<unsigned> reshapedDim;
486 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
487 {
488 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
489 }
490
491 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
492 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
493
494 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
495
496 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
497 armnn::ReshapeDescriptor desc;
498 desc.m_TargetShape = reshapedTensorInfo.GetShape();
499 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
500
501 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
502 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
503
504 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
505
506 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
507 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
508}
509
telsoa01c577f2c2018-08-31 09:22:23 +0100510INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
511{
512 ResetParser();
513 m_Model = LoadModelFromFile(graphFile);
514 return CreateNetworkFromModel();
515}
516
517INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
518{
519 ResetParser();
520 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
521 return CreateNetworkFromModel();
522}
523
524INetworkPtr TfLiteParser::CreateNetworkFromModel()
525{
526 m_Network = INetwork::Create();
527 BOOST_ASSERT(m_Model.get() != nullptr);
528
529 bool failedToCreate = false;
530 std::stringstream errors;
531
532 if (m_Model->subgraphs.size() != 1)
533 {
534 throw ParseException(
535 boost::str(
536 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
537 m_Model->subgraphs.size() %
538 CHECK_LOCATION().AsString()));
539 }
540
541 size_t subgraphIndex = 0;
542 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
543 {
544 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
545
546 size_t operatorIndex = 0;
547 for (OperatorPtr const & op : subgraph->operators)
548 {
549 try
550 {
telsoa01c577f2c2018-08-31 09:22:23 +0100551 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
552 auto builtinCode = opCodePtr->builtin_code;
553
554 if (builtinCode > tflite::BuiltinOperator_MAX)
555 {
556 throw ParseException(
557 boost::str(
558 boost::format("Operator code %1% is out of range 0-%2%. "
559 "subgraph:%3% operator idx:%4%. %5%") %
560 builtinCode %
561 tflite::BuiltinOperator_MAX %
562 subgraphIndex %
563 operatorIndex %
564 CHECK_LOCATION().AsString()));
565 }
566
567 // lookup and call the parser function
568 auto & parserFunction = m_ParserFunctions[builtinCode];
569 (this->*parserFunction)(subgraphIndex, operatorIndex);
570 }
571 catch (const ParseException& e)
572 {
573 failedToCreate = true;
574 std::stringstream errorString;
575
576 errorString << "Failed to parse operator #" << operatorIndex
577 << " within subgraph #" << subgraphIndex
578 << " error: " << e.what();
579 BOOST_LOG_TRIVIAL(error) << errorString.str();
580
581 errors << errorString.str() << "\n";
582 }
583 ++operatorIndex;
584 }
585
586 SetupInputLayers(subgraphIndex);
587 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200588 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100589
590 ++subgraphIndex;
591 }
592
593 if (failedToCreate)
594 {
595 // we can skip everything and let the outer exception handler deal with the error
596 throw ParseException(errors.str());
597 }
598
599 // establish the connections from the layer outputs to the inputs of the subsequent layers
600 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
601 {
602 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
603 {
604 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
605 {
606 for (size_t inputSlotIdx = 0;
607 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
608 ++inputSlotIdx)
609 {
610 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
611 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
612 }
613 }
614 }
615 }
616
617 return std::move(m_Network);
618}
619
620void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
621 size_t tensorIndex,
622 armnn::IOutputSlot* slot)
623{
624 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
625 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
626 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
627
628 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
629
630 // assuming there is only one producer for that tensor
631 if (tensorSlots.outputSlot != nullptr)
632 {
633 throw ParseException(boost::str(
634 boost::format("Another layer has already registered itself as the producer of "
635 "subgraph:%1% tensor:%2% %3%") %
636 subgraphIndex %
637 tensorIndex %
638 CHECK_LOCATION().AsString()));
639 }
640
641 tensorSlots.outputSlot = slot;
642}
643
644void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
645 size_t tensorIndex,
646 armnn::IInputSlot* slot)
647{
648 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
649 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
650 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
651
652 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
653 tensorSlots.inputSlots.push_back(slot);
654}
655
656void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
657{
658 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
659 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
660 //
661 auto opcodeIndex = operatorPtr->opcode_index;
662 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
663
664 throw ParseException(
665 boost::str(
666 boost::format("Operator not supported. "
667 "subgraph:%1% operator:%2% "
668 "opcode_index:%3% opcode:%4% / %5% %6%") %
669 subgraphIndex %
670 operatorIndex %
671 opcodeIndex %
672 opcode %
673 tflite::EnumNameBuiltinOperator(opcode) %
674 CHECK_LOCATION().AsString()));
675}
676
telsoa01c577f2c2018-08-31 09:22:23 +0100677void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
678{
679 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
680
681 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
682 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
683
684 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
685
686 Convolution2dDescriptor desc;
687 desc.m_BiasEnabled = false;
688 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
689 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000690 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100691
692 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
693 CHECK_VALID_SIZE(inputs.size(), 2, 3);
694
695 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
696 CHECK_VALID_SIZE(outputs.size(), 1);
697
698 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
699 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
700
701 // assuming input is NHWC
702 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
703 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
704
705 // assuming the filter is OHWI : Output, H, W, Input
706 // which is essentially the same as NHWC
707 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
708 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
709
710 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
711 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
712
Matteo Martincigh747ef822018-12-18 09:26:39 +0000713 auto filterTensorAndData = CreateConstTensor(inputs[1],
714 filterTensorInfo,
715 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100716 armnn::IConnectableLayer* layer;
717
718 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
719
720 if (inputs.size() == 3)
721 {
722 desc.m_BiasEnabled = true;
723 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000724 auto biasTensorAndData = CreateConstTensor(inputs[2],
725 biasTensorInfo,
726 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100727 layer = m_Network->AddConvolution2dLayer(desc,
728 filterTensorAndData.first,
729 biasTensorAndData.first,
730 layerName.c_str());
731 }
732 else
733 {
734 layer = m_Network->AddConvolution2dLayer(desc,
735 filterTensorAndData.first,
736 layerName.c_str());
737 }
738
739 BOOST_ASSERT(layer != nullptr);
740
telsoa01c577f2c2018-08-31 09:22:23 +0100741 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000742 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100743
744 // register the input connection slots for the layer, connections are made after all layers have been created
745 // only the tensors for the inputs are relevant, exclude the const tensors
746 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000747 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100748
jimfly01c25411c2018-11-14 17:47:22 +0000749 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100750 // register the output connection slots for the layer, connections are made after all layers have been created
751 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
752 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
753}
754
755void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
756{
757 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
758
759 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
760 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
761
762 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
763
764 DepthwiseConvolution2dDescriptor desc;
765 desc.m_BiasEnabled = false;
766 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
767 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000768 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100769 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
770 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
771
772 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
773 CHECK_VALID_SIZE(inputs.size(), 2, 3);
774 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
775 CHECK_VALID_SIZE(outputs.size(), 1);
776
777 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
778 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
779
Matteo Martincigh747ef822018-12-18 09:26:39 +0000780 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100781 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
782 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000783
784 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100785 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
786 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
787
Matteo Martincigh747ef822018-12-18 09:26:39 +0000788 // Reshape weights as [ H, W, I, M ]
789 filterTensorInfo.SetShape({ filterHeight,
790 filterWidth,
791 inputTensorInfo.GetShape()[3],
792 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
793
794 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
795 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
796
telsoa01c577f2c2018-08-31 09:22:23 +0100797 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
798 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
799
Matteo Martincigh747ef822018-12-18 09:26:39 +0000800 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100801 armnn::IConnectableLayer* layer;
802 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
803
804 if (inputs.size() == 3)
805 {
806 desc.m_BiasEnabled = true;
807 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000808 auto biasTensorAndData = CreateConstTensor(inputs[2],
809 biasTensorInfo,
810 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100811 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
812 filterTensorAndData.first,
813 biasTensorAndData.first,
814 layerName.c_str());
815 }
816 else
817 {
818 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
819 filterTensorAndData.first,
820 layerName.c_str());
821 }
822 BOOST_ASSERT(layer != nullptr);
823
telsoa01c577f2c2018-08-31 09:22:23 +0100824 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000825 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100826
827 // register the input connection slots for the layer, connections are made after all layers have been created
828 // only the tensors for the inputs are relevant, exclude the const tensors
829 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000830 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100831
jimfly01c25411c2018-11-14 17:47:22 +0000832 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100833 // register the output connection slots for the layer, connections are made after all layers have been created
834 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
835 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
836}
837
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100838void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
839{
840 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
841}
842
Bruno Goncalvesdb947e22019-02-08 18:52:21 -0200843void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
844{
845 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
846
847 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
848 CHECK_VALID_SIZE(inputs.size(), 3);
849
850 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
851 CHECK_VALID_SIZE(outputs.size(), 1);
852
853 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
854 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
855
856 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
857 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
858
859 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
860 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
861
862 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
863 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
864
865 size_t step = 2;
866 std::vector<std::pair<unsigned int, unsigned int>> crops;
867 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
868 {
869 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
870 }
871
872 armnn::BatchToSpaceNdDescriptor desc;
873 desc.m_BlockShape = blockShape;
874 desc.m_Crops = crops;
875 desc.m_DataLayout = armnn::DataLayout::NHWC;
876
877 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
878
879 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
880 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
881
882 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
883
884 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
885 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
886
887 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
888 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
889}
890
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100891void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
892{
893 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
894}
895
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -0200896void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
897{
898 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
899
900 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
901 CHECK_VALID_SIZE(inputs.size(), 2);
902
903 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
904 CHECK_VALID_SIZE(outputs.size(), 1);
905
906 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
907 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
908
909 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
910 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
911
912 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
913 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
914
915 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
916 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
917 {
918 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
919 }
920 else
921 {
922 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
923 }
924
925 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
926 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
927}
928
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -0200929void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
930{
931 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
932
933 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
934 CHECK_VALID_SIZE(inputs.size(), 2);
935
936 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
937 CHECK_VALID_SIZE(outputs.size(), 1);
938
939 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
940 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
941
942 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
943 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
944
945 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
946 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
947
948 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
949 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
950 {
951 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
952 }
953 else
954 {
955 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
956 }
957
958 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
959 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
960}
961
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100962void TfLiteParser::ParsePool(size_t subgraphIndex,
963 size_t operatorIndex,
964 PoolingAlgorithm algorithm)
965{
966 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
967
968 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
969 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
970
971 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
972
973 std::string layerName;
974
975 switch (algorithm)
976 {
977 case PoolingAlgorithm::Average:
978 layerName =
979 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
980 break;
981 case PoolingAlgorithm::Max:
982 layerName =
983 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
984 break;
985 default:
986 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
987 }
988
989 Pooling2dDescriptor desc;
990
991 desc.m_PoolType = algorithm;
992 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
993 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
994 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
995 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
996 desc.m_PaddingMethod = PaddingMethod::Exclude;
997 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000998 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100999
1000 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1001 CHECK_VALID_SIZE(inputs.size(), 1);
1002 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1003
1004 // assuming input is NHWC
1005 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1006 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1007
1008 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1009 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1010
1011 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1012 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001013
1014 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1015
1016 BOOST_ASSERT(layer != nullptr);
1017
jimfly01c25411c2018-11-14 17:47:22 +00001018 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1019 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001020
1021 // register the input connection slots for the layer, connections are made after all layers have been created
1022 // only the tensors for the inputs are relevant, exclude the const tensors
1023 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001024 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001025
jimfly01c25411c2018-11-14 17:47:22 +00001026 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001027 // register the output connection slots for the layer, connections are made after all layers have been created
1028 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1029 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1030}
1031
telsoa01c577f2c2018-08-31 09:22:23 +01001032void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1033{
1034 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1035 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1036 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1037
1038 SoftmaxDescriptor desc;
1039 desc.m_Beta = options->beta;
1040
1041 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1042 CHECK_VALID_SIZE(inputs.size(), 1);
1043 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1044 CHECK_VALID_SIZE(outputs.size(), 1);
1045
1046 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1047 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1048
1049 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1050 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1051
1052 // register the input connection slots for the layer, connections are made after all layers have been created
1053 // only the tensors for the inputs are relevant, exclude the const tensors
1054 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1055 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1056
1057 // register the output connection slots for the layer, connections are made after all layers have been created
1058 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1059 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1060}
1061
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001062void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1063{
1064 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1065
1066 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1067 CHECK_VALID_SIZE(inputs.size(), 3);
1068
1069 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1070 CHECK_VALID_SIZE(outputs.size(), 1);
1071
1072 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1073 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1074
1075 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1076 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1077
1078 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1079 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1080
1081 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1082 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1083
1084 size_t step = 2;
1085 std::vector<std::pair<unsigned int, unsigned int>> padList;
1086 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1087 {
1088 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1089 }
1090
1091 armnn::SpaceToBatchNdDescriptor desc;
1092 desc.m_BlockShape = blockShape;
1093 desc.m_PadList = padList;
1094 desc.m_DataLayout = armnn::DataLayout::NHWC;
1095
1096 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1097
1098 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1099 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1100
1101 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1102
1103 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1104 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1105
1106 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1107 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1108}
1109
telsoa01c577f2c2018-08-31 09:22:23 +01001110armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1111 const armnn::TensorInfo & inputTensorInfo)
1112{
1113 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1114 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1115 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1116
1117 if (inputTensorInfo.GetNumDimensions() > 4)
1118 {
1119 std::stringstream ss;
1120 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1121 << " shape:" << inputTensorInfo.GetShape() << " "
1122 << CHECK_LOCATION().AsString();
1123 throw ParseException(ss.str());
1124 }
1125
1126 if (squeezeDims.empty())
1127 {
1128 squeezeDims.assign(dimensionSequence,
1129 dimensionSequence+inputTensorInfo.GetNumDimensions());
1130 }
1131
1132 std::vector<uint32_t> outputDims;
1133 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1134 {
1135 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1136 auto currentDimension = inputTensorInfo.GetShape()[i];
1137 if (skipSqueeze || currentDimension != 1)
1138 {
1139 outputDims.push_back(currentDimension);
1140 }
1141 }
1142
1143 if (outputDims.size() > 4)
1144 {
1145 std::stringstream ss;
1146 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1147 << " shape:" << inputTensorInfo.GetShape() << " "
1148 << CHECK_LOCATION().AsString();
1149 throw ParseException(ss.str());
1150 }
1151
1152 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1153 outputDims.data());
1154
1155 // we need to preserve the tensor type and the quantization data as well
1156 TensorInfo outTensorInfo = inputTensorInfo;
1157 outTensorInfo.SetShape(outShape);
1158
1159 return outTensorInfo;
1160}
1161
1162void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1163{
1164 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1165
1166 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1167 CHECK_VALID_SIZE(inputs.size(), 1);
1168
1169 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1170 CHECK_VALID_SIZE(outputs.size(), 1);
1171
1172 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1173 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1174
1175 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1176 armnn::TensorInfo outputTensorInfo =
1177 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1178 inputTensorInfo);
1179
1180 ReshapeDescriptor reshapeDesc;
1181 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1182
1183 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1184 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1185 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1186
1187 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1188 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1189
1190 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1191 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1192}
1193
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001194void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1195{
1196 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1197
1198 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1199 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1200
1201 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1202 CHECK_VALID_SIZE(inputs.size(), 2);
1203
1204 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1205 CHECK_VALID_SIZE(outputs.size(), 1);
1206
1207 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1208 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1209
1210 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1211 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1212
1213 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1214 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1215
1216 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1217 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1218 {
1219 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1220 }
1221 else
1222 {
1223 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1224 }
1225
1226 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1227
1228 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1229 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1230}
1231
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001232void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1233{
1234 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1235
1236 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1237 const auto * options = operatorPtr->builtin_options.AsAddOptions();
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
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001245 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1246 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1247
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001248 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1249 IConnectableLayer* layer = m_Network->AddAdditionLayer(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));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001255 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 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001263
1264 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1265
1266 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1267 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1268}
1269
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001270void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1271{
1272 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1273
1274 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1275 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1276
1277 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1278 CHECK_VALID_SIZE(inputs.size(), 2);
1279
1280 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1281 CHECK_VALID_SIZE(outputs.size(), 1);
1282
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001283 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1284 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1285
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001286 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1287 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1288
1289 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1290 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1291
1292 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001293 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1294 {
1295 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1296 }
1297 else
1298 {
1299 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1300 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001301
1302 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1303
1304 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1305 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1306}
1307
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001308void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1309{
1310 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1311
1312 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1313
1314 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1315 CHECK_VALID_SIZE(outputs.size(), 1);
1316
1317 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1318 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1319
1320 armnn::MeanDescriptor desc;
1321 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1322 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1323 desc.m_Axis = axis;
1324
1325 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1326 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1327
1328 desc.m_KeepDims =
1329 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1330 true : false;
1331
1332 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1333 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1334
1335 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1336
1337 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1338 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1339
1340 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1341 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1342}
1343
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001344void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1345{
1346 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1347
1348 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1349
1350 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1351 CHECK_VALID_SIZE(outputs.size(), 1);
1352
1353 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1354 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1355
1356 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1357 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1358
1359 size_t step = 2;
1360 armnn::PadDescriptor desc;
1361 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1362 {
1363 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1364 }
1365
1366 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1367 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1368
1369 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1370 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1371
1372 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1373 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1374
1375 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1376 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1377}
1378
Finn Williamsc42c3842019-01-22 14:18:11 +00001379
Sadik Armagan58f39192018-09-17 14:14:39 +01001380void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1381{
Finn Williamsc42c3842019-01-22 14:18:11 +00001382 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001383}
1384
1385void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1386{
Finn Williamsc42c3842019-01-22 14:18:11 +00001387 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1388}
Sadik Armagan58f39192018-09-17 14:14:39 +01001389
Finn Williamsc42c3842019-01-22 14:18:11 +00001390void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1391{
1392 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1393}
1394
1395
1396void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1397{
1398 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001399 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1400 boost::ignore_unused(operatorPtr);
1401
1402 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1403 CHECK_VALID_SIZE(inputs.size(), 1);
1404
1405 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1406 CHECK_VALID_SIZE(outputs.size(), 1);
1407
Finn Williamsc42c3842019-01-22 14:18:11 +00001408 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001409 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001410 activationDesc.m_Function = activationType;
1411
1412 switch (activationType)
1413 {
1414 case ActivationFunction::ReLu:
1415 {
1416 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1417 break;
1418 }
1419 case ActivationFunction::BoundedReLu:
1420 {
1421 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1422 activationDesc.m_A = 6.0f;
1423 activationDesc.m_B = 0.0f;
1424 break;
1425 }
1426 case ActivationFunction::Sigmoid:
1427 {
1428 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1429 break;
1430 }
1431 default:
1432 {
1433 throw ParseException(
1434 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1435 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1436 }
1437 }
1438
1439 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001440
1441 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1442 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1443
1444 // register the input connection slots for the layer, connections are made after all layers have been created
1445 // only the tensors for the inputs are relevant, exclude the const tensors
1446 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1447 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1448
1449 // register the output connection slots for the layer, connections are made after all layers have been created
1450 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1451 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1452}
Sadikb94967b2018-09-19 15:30:00 +01001453armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1454 const std::vector<int32_t> & targetDimsIn)
1455{
1456 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1457 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1458
1459 if (stretchDim != targetDimsIn.end())
1460 {
1461 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1462 {
1463 throw ParseException(
1464 boost::str(
1465 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1466 }
1467
1468 auto targetNumElements =
1469 boost::numeric_cast<unsigned int>(
1470 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1471
1472 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1473 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1474 }
1475
1476 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1477
1478 TensorInfo reshapeInfo = inputTensorInfo;
1479 reshapeInfo.SetShape(outputShape);
1480
1481 return reshapeInfo;
1482}
1483
1484void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1485{
1486 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1487
1488 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001489
1490 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1491 CHECK_VALID_SIZE(outputs.size(), 1);
1492
1493 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1494 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1495
1496 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001497 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1498 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001499 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1500
kevmay0171972a82018-12-17 14:28:03 +00001501 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001502 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1503 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001504 {
1505 std::stringstream ss;
1506 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001507 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001508 << " does not equal output shape "
1509 << actualOutputTensorInfo.GetShape()
1510 << ": "
1511 << CHECK_LOCATION().AsString();
1512 throw ParseException(ss.str());
1513 }
1514
Sadikb94967b2018-09-19 15:30:00 +01001515 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001516 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001517
1518 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1519 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001520 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001521
1522 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1523 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1524
1525 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1526 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1527}
1528
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001529void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1530{
1531 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1532
1533 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1534 CHECK_VALID_SIZE(inputs.size(), 2);
1535
1536 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1537 CHECK_VALID_SIZE(outputs.size(), 1);
1538
1539 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1540
1541 // Data for the parsed tensor args (size) must be stored locally.
1542 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1543
1544 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1545 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1546
1547 ResizeBilinearDescriptor desc;
1548 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
1549 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1550 desc.m_DataLayout = armnn::DataLayout::NHWC;
1551
1552 auto layerName = boost::str(boost::format("ResizeBilinear:%1%:%2%") % subgraphIndex % operatorIndex);
1553 IConnectableLayer* layer = m_Network->AddResizeBilinearLayer(desc, layerName.c_str());
1554
1555 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1556 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1557
1558 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1559 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1560
1561 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1562 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1563}
1564
Sadik Armagan479045b2018-10-01 11:51:37 +01001565void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1566{
1567 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1568
1569 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1570 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1571
1572 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1573
1574 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1575 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1576 CHECK_VALID_SIZE(outputs.size(), 1);
1577
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001578 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1579 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001580
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001581 const unsigned int concatDimInput = static_cast<unsigned int>(
1582 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001583
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001584 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1585 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001586
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001587 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01001588
1589 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1590 {
1591 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1592
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001593 // This set up concatDescriptor view origin
1594 armnnUtils::ProcessConcatInputTensorInfo(
1595 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01001596 }
1597
1598 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1599 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1600
1601 BOOST_ASSERT(layer != nullptr);
1602
1603 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1604 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01001605
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001606 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01001607
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001608 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01001609
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001610 // add fused activation layer
1611 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01001612
Sadik Armagan479045b2018-10-01 11:51:37 +01001613 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1614 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1615}
1616
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001617void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1618{
1619 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1620
1621 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1622 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1623
1624 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1625
1626 FullyConnectedDescriptor desc;
1627 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001628 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001629
1630 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1631 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1632 CHECK_VALID_SIZE(outputs.size(), 1);
1633
1634 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1635
1636 // Fully Connected Layer accepts two dimensional weights input
1637 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1638 if (weightsDimension != 2)
1639 {
1640 throw ParseException(
1641 boost::str(
1642 boost::format(
1643 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1644 "Node %2%")
1645 % weightsDimension
1646 % CHECK_LOCATION().AsString()));
1647 }
1648
Matteo Martincigh747ef822018-12-18 09:26:39 +00001649 auto filterTensorAndData = CreateConstTensor(inputs[1],
1650 filterTensorInfo,
1651 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001652 armnn::IConnectableLayer* layer;
1653 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1654
1655 if (inputs.size() == 3)
1656 {
1657 desc.m_BiasEnabled = true;
1658 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001659 auto biasTensorAndData = CreateConstTensor(inputs[2],
1660 biasTensorInfo,
1661 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001662 layer = m_Network->AddFullyConnectedLayer(desc,
1663 filterTensorAndData.first,
1664 biasTensorAndData.first,
1665 layerName.c_str());
1666 }
1667 else
1668 {
1669 layer = m_Network->AddFullyConnectedLayer(desc,
1670 filterTensorAndData.first,
1671 layerName.c_str());
1672 }
1673 BOOST_ASSERT(layer != nullptr);
1674
1675 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1676 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1677
1678 // register the input connection slot for the layer
1679 // only the tensors for the inputs are relevant, exclude the const tensors
1680 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1681 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1682
1683 // we need to add the activation layer and fortunately we don't need to care about the data layout
1684 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1685 options->fused_activation_function);
1686 // register the output connection slots for the layer, connections are made after all layers have been created
1687 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1688 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1689}
1690
keidav011b3e2ea2019-02-21 10:07:37 +00001691void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
1692{
1693 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1694
1695 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1696
1697 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1698 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1699 CHECK_VALID_SIZE(outputs.size(), 4);
1700
1701 // Obtain custom options from flexbuffers
1702 auto custom_options = operatorPtr->custom_options;
1703 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
1704
1705 // Obtain descriptor information from tf lite
1706 DetectionPostProcessDescriptor desc;
1707 desc.m_MaxDetections = m["max_detections"].AsUInt32();
1708 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
1709 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
1710 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
1711 desc.m_NumClasses = m["num_classes"].AsUInt32();
1712 desc.m_ScaleH = m["h_scale"].AsFloat();
1713 desc.m_ScaleW = m["w_scale"].AsFloat();
1714 desc.m_ScaleX = m["x_scale"].AsFloat();
1715 desc.m_ScaleY = m["y_scale"].AsFloat();
1716
1717 if (!(m["use_regular_non_max_suppression"].IsNull()))
1718 {
1719 desc.m_UseRegularNms = m["use_regular_non_max_suppression"].AsBool();
1720 }
1721 if (!(m["detections_per_class"].IsNull()))
1722 {
1723 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
1724 }
1725
1726 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
1727 {
1728 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
1729 "must be positive and less than or equal to 1.");
1730 }
1731
1732 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
1733 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
1734 armnn::Optional<armnn::PermutationVector&>());
1735
1736 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
1737 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
1738 layerName.c_str());
1739
1740 BOOST_ASSERT(layer != nullptr);
1741
1742 // Register outputs
1743 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
1744 {
1745 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i]);
1746 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
1747 }
1748
1749 // Register the input connection slots for the layer, connections are made after all layers have been created
1750 // only the tensors for the inputs are relevant, exclude the const tensors
1751 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1752 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1753
1754 // Register the output connection slots for the layer, connections are made after all layers have been created
1755 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1756 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
1757 outputTensorIndexes[1],
1758 outputTensorIndexes[2],
1759 outputTensorIndexes[3]});
1760}
1761
Sadik Armagan58f39192018-09-17 14:14:39 +01001762armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1763 unsigned int outputSlot,
1764 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001765{
1766 ActivationDescriptor activationDesc;
1767 std::string layerName = prevLayer->GetName();
1768
1769 switch(activationType)
1770 {
1771 case tflite::ActivationFunctionType_NONE:
1772 {
1773 // this is a no-op: return previous layer
1774 return prevLayer;
1775 }
1776 case tflite::ActivationFunctionType_RELU:
1777 {
1778 activationDesc.m_Function = ActivationFunction::ReLu;
1779 layerName += ":RELU";
1780 break;
1781 }
1782 case tflite::ActivationFunctionType_RELU6:
1783 {
1784 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1785 activationDesc.m_A = 6.0f;
1786 activationDesc.m_B = 0.0f;
1787 layerName += ":RELU6";
1788 break;
1789 }
1790 case tflite::ActivationFunctionType_TANH:
1791 {
1792 activationDesc.m_Function = ActivationFunction::TanH;
1793 activationDesc.m_A = 1.0f;
1794 activationDesc.m_B = 1.0f;
1795 layerName += ":TANH";
1796 break;
1797 }
1798
1799 // I only put these here as a reminder what others we could support
1800 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1801 case tflite::ActivationFunctionType_SIGN_BIT:
1802 default:
1803 {
1804 throw ParseException(
1805 boost::str(
1806 boost::format("TfLite parser doesn't suppport fused activation: "
1807 "%1%/%2% %3% ") %
1808 activationType %
1809 tflite::EnumNameActivationFunctionType(activationType) %
1810 CHECK_LOCATION().AsString()));
1811
1812 }
1813 }
1814
1815 IConnectableLayer* activationLayer =
1816 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1817
1818 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1819 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1820 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1821 return activationLayer;
1822}
1823
1824TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1825{
1826 if (fileName == nullptr)
1827 {
1828 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1829 CHECK_LOCATION().AsString()));
1830 }
1831 boost::system::error_code errorCode;
1832 boost::filesystem::path pathToFile(fileName);
1833 if (!boost::filesystem::exists(pathToFile, errorCode))
1834 {
1835 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1836 fileName %
1837 errorCode %
1838 CHECK_LOCATION().AsString()));
1839 }
1840 std::ifstream file(fileName, std::ios::binary);
1841 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1842 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1843 fileContent.size());
1844}
1845
1846TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1847{
1848 if (binaryContent == nullptr)
1849 {
1850 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1851 CHECK_LOCATION().AsString()));
1852 }
1853 flatbuffers::Verifier verifier(binaryContent, len);
1854 if (verifier.VerifyBuffer<tflite::Model>() == false)
1855 {
1856 throw ParseException(
1857 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1858 "flatbuffers format. size:%1% %2%") %
1859 len %
1860 CHECK_LOCATION().AsString()));
1861 }
1862 return tflite::UnPackModel(binaryContent);
1863}
1864
1865TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1866 size_t subgraphIndex,
1867 size_t operatorIndex)
1868{
1869 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1870
1871 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1872 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1873
1874 size_t inputCount = operatorPtr->inputs.size();
1875 TensorRawPtrVector result(inputCount);
1876 for (size_t i=0; i<inputCount; ++i)
1877 {
1878 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1879 result[i] = subGraphPtr->tensors[inputId].get();
1880 }
1881 return result;
1882}
1883
1884TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1885 size_t subgraphIndex,
1886 size_t operatorIndex)
1887{
1888 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1889
1890 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1891 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1892
1893 size_t outputCount = operatorPtr->outputs.size();
1894 TensorRawPtrVector result(outputCount);
1895 for (size_t i=0; i<outputCount; ++i)
1896 {
1897 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1898 CHECK_TENSOR(model, subgraphIndex, outputId);
1899 result[i] = subGraphPtr->tensors[outputId].get();
1900 }
1901 return result;
1902}
1903
1904TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1905 size_t subgraphIndex)
1906{
1907 CHECK_SUBGRAPH(model, subgraphIndex);
1908 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1909
1910 size_t inputCount = subGraphPtr->inputs.size();
1911 TensorIdRawPtrVector result(inputCount);
1912 for (size_t i=0; i<inputCount; ++i)
1913 {
1914 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1915 CHECK_TENSOR(model, subgraphIndex, inputId);
1916 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1917 }
1918 return result;
1919}
1920
1921TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1922 size_t subgraphIndex)
1923{
1924 CHECK_SUBGRAPH(model, subgraphIndex);
1925 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1926
1927 size_t outputCount = subGraphPtr->outputs.size();
1928 TensorIdRawPtrVector result(outputCount);
1929 for (size_t i=0; i<outputCount; ++i)
1930 {
1931 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1932 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1933 }
1934 return result;
1935}
1936
1937std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1938 size_t subgraphIndex,
1939 size_t operatorIndex)
1940{
1941 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1942 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1943 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1944 return operatorPtr->inputs;
1945}
1946
1947std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1948 size_t subgraphIndex,
1949 size_t operatorIndex)
1950{
1951 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1952 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1953 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1954 return operatorPtr->outputs;
1955}
1956
1957void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1958 size_t operatorIndex,
1959 IConnectableLayer* layer,
1960 const std::vector<unsigned int>& tensorIndexes)
1961{
1962 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1963 BOOST_ASSERT(layer != nullptr);
1964 if (tensorIndexes.size() != layer->GetNumInputSlots())
1965 {
1966 throw ParseException(
1967 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1968 " for subgraph:%3% operator index:%4% %5%") %
1969 tensorIndexes.size() %
1970 layer->GetNumInputSlots() %
1971 subgraphIndex %
1972 operatorIndex %
1973 CHECK_LOCATION().AsString()));
1974 }
1975
1976 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1977 {
1978 unsigned int tensorIndex = tensorIndexes[slotIndex];
1979 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1980 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1981 }
1982}
1983
1984void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1985 size_t operatorIndex,
1986 IConnectableLayer* layer,
1987 const std::vector<unsigned int>& tensorIndexes)
1988{
1989 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1990 BOOST_ASSERT(layer != nullptr);
1991 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1992 {
1993 throw ParseException(
1994 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1995 " for subgraph:%3% operator index:%4% %5%") %
1996 tensorIndexes.size() %
1997 layer->GetNumOutputSlots() %
1998 subgraphIndex %
1999 operatorIndex %
2000 CHECK_LOCATION().AsString()));
2001 }
2002
2003 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2004 {
2005 unsigned int tensorIndex = tensorIndexes[slotIndex];
2006 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2007 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2008 }
2009}
2010
2011void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2012{
2013 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2014
2015 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2016 for (auto const & tensorIdAndPtr : inputs)
2017 {
2018 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2019 IConnectableLayer* layer =
2020 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2021
2022 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2023 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2024
2025 RegisterOutputSlots(subgraphIndex,
2026 VIRTUAL_OPERATOR_ID,
2027 layer,
2028 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2029 }
2030}
2031
2032void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2033{
2034 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2035
2036 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2037 for (auto const & tensorIdAndPtr : outputs)
2038 {
2039 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2040 IConnectableLayer* layer =
2041 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2042
2043 RegisterInputSlots(subgraphIndex,
2044 VIRTUAL_OPERATOR_ID,
2045 layer,
2046 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2047 }
2048}
2049
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002050void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2051{
2052 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2053
2054 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
2055 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2056 {
2057 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2058 {
2059 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2060 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2061 {
2062 TensorRawPtr tensorPtr = subGraphPtr->tensors[tensorIndex].get();
2063 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2064 auto tensorAndData = CreateConstTensor(tensorPtr,
2065 tensorInfo,
2066 armnn::Optional<armnn::PermutationVector&>());
2067
2068 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2069 IConnectableLayer *layer =
2070 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2071
2072 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2073 RegisterOutputSlots(subgraphIndex,
2074 VIRTUAL_OPERATOR_ID,
2075 layer,
2076 { tensorIndex });
2077
2078 }
2079 }
2080 }
2081}
2082
telsoa01c577f2c2018-08-31 09:22:23 +01002083// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2084TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2085{
2086 CHECK_BUFFER(model, bufferIndex);
2087 return model->buffers[bufferIndex].get();
2088}
2089
Matteo Martincigh747ef822018-12-18 09:26:39 +00002090template<typename T>
2091std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2092TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2093 TfLiteParser::TensorRawPtr tensorPtr,
2094 armnn::TensorInfo& tensorInfo,
2095 armnn::Optional<armnn::PermutationVector&> permutationVector)
2096{
2097 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2098 tensorPtr,
2099 tensorInfo,
2100 permutationVector);
2101 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2102 return std::make_pair(constData.first, std::move(storage));
2103}
2104
telsoa01c577f2c2018-08-31 09:22:23 +01002105std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2106TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002107 armnn::TensorInfo& tensorInfo,
2108 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002109{
2110 CHECK_TENSOR_PTR(tensorPtr);
2111 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2112 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2113
2114 switch (tensorInfo.GetDataType())
2115 {
2116 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002117 return CreateConstTensorAndStoreData<float>(bufferPtr,
2118 tensorPtr,
2119 tensorInfo,
2120 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002121 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002122 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2123 tensorPtr,
2124 tensorInfo,
2125 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002126 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002127 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2128 tensorPtr,
2129 tensorInfo,
2130 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002131 default:
2132 {
2133 std::stringstream errString;
2134 errString << "Unexpected datatype when creating const tensor: "
2135 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2136 << " shape:" << tensorInfo.GetShape()
2137 << CHECK_LOCATION().AsString();
2138 throw ParseException(errString.str());
2139 }
2140 }
2141}
2142
2143BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2144 const std::string& name) const
2145{
2146 CHECK_SUBGRAPH(m_Model, subgraphId);
2147 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2148 for (auto const & input : inputs)
2149 {
2150 if (input.second->name == name)
2151 {
2152 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2153 return std::make_pair(bindingId, ToTensorInfo(input.second));
2154 }
2155 }
2156
2157 std::stringstream bindings;
2158 for (auto const & input : inputs)
2159 {
2160 bindings << "'" << input.second->name << "' ";
2161 }
2162
2163 throw ParseException(
2164 boost::str(
2165 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2166 "Possible inputs are: [%3%] %4%") %
2167 subgraphId %
2168 name %
2169 bindings.str() %
2170 CHECK_LOCATION().AsString()));
2171}
2172
2173BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2174 const std::string& name) const
2175{
2176 CHECK_SUBGRAPH(m_Model, subgraphId);
2177 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2178 for (auto const & output : outputs)
2179 {
2180 if (output.second->name == name)
2181 {
2182 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
2183 return std::make_pair(bindingId, ToTensorInfo(output.second));
2184 }
2185 }
2186
2187 std::stringstream bindings;
2188 for (auto const & output : outputs)
2189 {
2190 bindings << "'" << output.second->name << "' ";
2191 }
2192
2193 throw ParseException(
2194 boost::str(
2195 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2196 "Possible outputs are: [%3%] %4%") %
2197 subgraphId %
2198 name %
2199 bindings.str() %
2200 CHECK_LOCATION().AsString()));
2201}
2202
2203size_t TfLiteParser::GetSubgraphCount() const
2204{
2205 return m_Model->subgraphs.size();
2206}
2207
2208std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2209{
2210 CHECK_SUBGRAPH(m_Model, subgraphId);
2211 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2212 std::vector<std::string> result;
2213 result.reserve(inputs.size());
2214 for (auto const & input : inputs)
2215 {
2216 result.push_back(input.second->name);
2217 }
2218 return result;
2219}
2220
2221std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2222{
2223 CHECK_SUBGRAPH(m_Model, subgraphId);
2224 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2225 std::vector<std::string> result;
2226 result.reserve(outputs.size());
2227 for (auto const & output : outputs)
2228 {
2229 result.push_back(output.second->name);
2230 }
2231 return result;
2232}
2233
2234ITfLiteParser* ITfLiteParser::CreateRaw()
2235{
2236 return new TfLiteParser();
2237}
2238
2239ITfLiteParserPtr ITfLiteParser::Create()
2240{
2241 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
2242}
2243
2244void ITfLiteParser::Destroy(ITfLiteParser* parser)
2245{
2246 delete parser;
2247}
2248
2249TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2250: m_FloatData(std::move(data))
2251, m_Uint8Data(nullptr)
2252, m_Int32Data(nullptr)
2253{
2254}
2255
2256TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2257: m_FloatData(nullptr)
2258, m_Uint8Data(std::move(data))
2259, m_Int32Data(nullptr)
2260{
2261}
2262
2263TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2264: m_FloatData(nullptr)
2265, m_Uint8Data(nullptr)
2266, m_Int32Data(std::move(data))
2267{
2268}
2269
2270} // armnnTfLiteParser