blob: 31aab029ab39d7bd27a8e296795ba14eaf6fc4f7 [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;
Sadik Armagan58f39192018-09-17 14:14:39 +0100430 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
431 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100432 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -0200433 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
Sadik Armagan479045b2018-10-01 11:51:37 +0100434 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
435 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Bruno Goncalvesbbeae262019-02-07 18:37:39 -0200436 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200437 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Bruno Goncalvesf803f782018-12-18 13:40:30 -0200438 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Bruno Goncalves2235cee2018-12-19 12:51:45 -0200439 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Bruno Goncalves6c2355b2018-12-19 12:52:01 -0200440 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
telsoa01c577f2c2018-08-31 09:22:23 +0100441}
442
443void TfLiteParser::ResetParser()
444{
445 m_Network = armnn::INetworkPtr(nullptr, nullptr);
446 m_Model = nullptr;
447 m_SubgraphConnections.clear();
448}
449
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200450void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
451 size_t operatorIndex,
452 IConnectableLayer *layer)
453{
454 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
455 BOOST_ASSERT(layer != nullptr);
456
457 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
458 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
459
460 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
461
462 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
463 TensorRawPtr tensorPtr = subGraphPtr->tensors[reshapedInputId].get();
464 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
465 TensorRawPtr tensorPtr1 = subGraphPtr->tensors[inputId].get();
466
467 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
468 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
469
470 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
471 {
472 uint32_t id = reshapedInputId;
473 reshapedInputId = inputId;
474 inputId = id;
475
476 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
477 inputTensorInfo = ToTensorInfo(tensorPtr);
478 }
479
480 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
481
482 std::vector<unsigned> reshapedDim;
483 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
484 {
485 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
486 }
487
488 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
489 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
490
491 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
492
493 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
494 armnn::ReshapeDescriptor desc;
495 desc.m_TargetShape = reshapedTensorInfo.GetShape();
496 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
497
498 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
499 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
500
501 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
502
503 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
504 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
505}
506
telsoa01c577f2c2018-08-31 09:22:23 +0100507INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
508{
509 ResetParser();
510 m_Model = LoadModelFromFile(graphFile);
511 return CreateNetworkFromModel();
512}
513
514INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
515{
516 ResetParser();
517 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
518 return CreateNetworkFromModel();
519}
520
521INetworkPtr TfLiteParser::CreateNetworkFromModel()
522{
523 m_Network = INetwork::Create();
524 BOOST_ASSERT(m_Model.get() != nullptr);
525
526 bool failedToCreate = false;
527 std::stringstream errors;
528
529 if (m_Model->subgraphs.size() != 1)
530 {
531 throw ParseException(
532 boost::str(
533 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
534 m_Model->subgraphs.size() %
535 CHECK_LOCATION().AsString()));
536 }
537
538 size_t subgraphIndex = 0;
539 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
540 {
541 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
542
543 size_t operatorIndex = 0;
544 for (OperatorPtr const & op : subgraph->operators)
545 {
546 try
547 {
telsoa01c577f2c2018-08-31 09:22:23 +0100548 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
549 auto builtinCode = opCodePtr->builtin_code;
550
551 if (builtinCode > tflite::BuiltinOperator_MAX)
552 {
553 throw ParseException(
554 boost::str(
555 boost::format("Operator code %1% is out of range 0-%2%. "
556 "subgraph:%3% operator idx:%4%. %5%") %
557 builtinCode %
558 tflite::BuiltinOperator_MAX %
559 subgraphIndex %
560 operatorIndex %
561 CHECK_LOCATION().AsString()));
562 }
563
564 // lookup and call the parser function
565 auto & parserFunction = m_ParserFunctions[builtinCode];
566 (this->*parserFunction)(subgraphIndex, operatorIndex);
567 }
568 catch (const ParseException& e)
569 {
570 failedToCreate = true;
571 std::stringstream errorString;
572
573 errorString << "Failed to parse operator #" << operatorIndex
574 << " within subgraph #" << subgraphIndex
575 << " error: " << e.what();
576 BOOST_LOG_TRIVIAL(error) << errorString.str();
577
578 errors << errorString.str() << "\n";
579 }
580 ++operatorIndex;
581 }
582
583 SetupInputLayers(subgraphIndex);
584 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200585 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100586
587 ++subgraphIndex;
588 }
589
590 if (failedToCreate)
591 {
592 // we can skip everything and let the outer exception handler deal with the error
593 throw ParseException(errors.str());
594 }
595
596 // establish the connections from the layer outputs to the inputs of the subsequent layers
597 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
598 {
599 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
600 {
601 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
602 {
603 for (size_t inputSlotIdx = 0;
604 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
605 ++inputSlotIdx)
606 {
607 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
608 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
609 }
610 }
611 }
612 }
613
614 return std::move(m_Network);
615}
616
617void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
618 size_t tensorIndex,
619 armnn::IOutputSlot* slot)
620{
621 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
622 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
623 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
624
625 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
626
627 // assuming there is only one producer for that tensor
628 if (tensorSlots.outputSlot != nullptr)
629 {
630 throw ParseException(boost::str(
631 boost::format("Another layer has already registered itself as the producer of "
632 "subgraph:%1% tensor:%2% %3%") %
633 subgraphIndex %
634 tensorIndex %
635 CHECK_LOCATION().AsString()));
636 }
637
638 tensorSlots.outputSlot = slot;
639}
640
641void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
642 size_t tensorIndex,
643 armnn::IInputSlot* slot)
644{
645 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
646 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
647 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
648
649 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
650 tensorSlots.inputSlots.push_back(slot);
651}
652
653void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
654{
655 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
656 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
657 //
658 auto opcodeIndex = operatorPtr->opcode_index;
659 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
660
661 throw ParseException(
662 boost::str(
663 boost::format("Operator not supported. "
664 "subgraph:%1% operator:%2% "
665 "opcode_index:%3% opcode:%4% / %5% %6%") %
666 subgraphIndex %
667 operatorIndex %
668 opcodeIndex %
669 opcode %
670 tflite::EnumNameBuiltinOperator(opcode) %
671 CHECK_LOCATION().AsString()));
672}
673
telsoa01c577f2c2018-08-31 09:22:23 +0100674void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
675{
676 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
677
678 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
679 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
680
681 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
682
683 Convolution2dDescriptor desc;
684 desc.m_BiasEnabled = false;
685 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
686 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000687 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100688
689 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
690 CHECK_VALID_SIZE(inputs.size(), 2, 3);
691
692 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
693 CHECK_VALID_SIZE(outputs.size(), 1);
694
695 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
696 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
697
698 // assuming input is NHWC
699 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
700 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
701
702 // assuming the filter is OHWI : Output, H, W, Input
703 // which is essentially the same as NHWC
704 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
705 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
706
707 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
708 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
709
Matteo Martincigh747ef822018-12-18 09:26:39 +0000710 auto filterTensorAndData = CreateConstTensor(inputs[1],
711 filterTensorInfo,
712 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100713 armnn::IConnectableLayer* layer;
714
715 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
716
717 if (inputs.size() == 3)
718 {
719 desc.m_BiasEnabled = true;
720 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000721 auto biasTensorAndData = CreateConstTensor(inputs[2],
722 biasTensorInfo,
723 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100724 layer = m_Network->AddConvolution2dLayer(desc,
725 filterTensorAndData.first,
726 biasTensorAndData.first,
727 layerName.c_str());
728 }
729 else
730 {
731 layer = m_Network->AddConvolution2dLayer(desc,
732 filterTensorAndData.first,
733 layerName.c_str());
734 }
735
736 BOOST_ASSERT(layer != nullptr);
737
telsoa01c577f2c2018-08-31 09:22:23 +0100738 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000739 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100740
741 // register the input connection slots for the layer, connections are made after all layers have been created
742 // only the tensors for the inputs are relevant, exclude the const tensors
743 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000744 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100745
jimfly01c25411c2018-11-14 17:47:22 +0000746 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100747 // register the output connection slots for the layer, connections are made after all layers have been created
748 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
749 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
750}
751
752void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
753{
754 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
755
756 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
757 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
758
759 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
760
761 DepthwiseConvolution2dDescriptor desc;
762 desc.m_BiasEnabled = false;
763 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
764 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000765 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100766 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
767 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
768
769 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
770 CHECK_VALID_SIZE(inputs.size(), 2, 3);
771 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
772 CHECK_VALID_SIZE(outputs.size(), 1);
773
774 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
775 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
776
Matteo Martincigh747ef822018-12-18 09:26:39 +0000777 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100778 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
779 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000780
781 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100782 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
783 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
784
Matteo Martincigh747ef822018-12-18 09:26:39 +0000785 // Reshape weights as [ H, W, I, M ]
786 filterTensorInfo.SetShape({ filterHeight,
787 filterWidth,
788 inputTensorInfo.GetShape()[3],
789 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
790
791 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
792 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
793
telsoa01c577f2c2018-08-31 09:22:23 +0100794 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
795 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
796
Matteo Martincigh747ef822018-12-18 09:26:39 +0000797 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100798 armnn::IConnectableLayer* layer;
799 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
800
801 if (inputs.size() == 3)
802 {
803 desc.m_BiasEnabled = true;
804 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000805 auto biasTensorAndData = CreateConstTensor(inputs[2],
806 biasTensorInfo,
807 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100808 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
809 filterTensorAndData.first,
810 biasTensorAndData.first,
811 layerName.c_str());
812 }
813 else
814 {
815 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
816 filterTensorAndData.first,
817 layerName.c_str());
818 }
819 BOOST_ASSERT(layer != nullptr);
820
telsoa01c577f2c2018-08-31 09:22:23 +0100821 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000822 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100823
824 // register the input connection slots for the layer, connections are made after all layers have been created
825 // only the tensors for the inputs are relevant, exclude the const tensors
826 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000827 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100828
jimfly01c25411c2018-11-14 17:47:22 +0000829 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100830 // register the output connection slots for the layer, connections are made after all layers have been created
831 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
832 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
833}
834
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100835void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
836{
837 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
838}
839
Bruno Goncalvesdb947e22019-02-08 18:52:21 -0200840void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
841{
842 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
843
844 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
845 CHECK_VALID_SIZE(inputs.size(), 3);
846
847 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
848 CHECK_VALID_SIZE(outputs.size(), 1);
849
850 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
851 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
852
853 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
854 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
855
856 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
857 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
858
859 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
860 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
861
862 size_t step = 2;
863 std::vector<std::pair<unsigned int, unsigned int>> crops;
864 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
865 {
866 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
867 }
868
869 armnn::BatchToSpaceNdDescriptor desc;
870 desc.m_BlockShape = blockShape;
871 desc.m_Crops = crops;
872 desc.m_DataLayout = armnn::DataLayout::NHWC;
873
874 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
875
876 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
877 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
878
879 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
880
881 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
882 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
883
884 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
885 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
886}
887
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100888void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
889{
890 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
891}
892
893void TfLiteParser::ParsePool(size_t subgraphIndex,
894 size_t operatorIndex,
895 PoolingAlgorithm algorithm)
896{
897 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
898
899 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
900 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
901
902 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
903
904 std::string layerName;
905
906 switch (algorithm)
907 {
908 case PoolingAlgorithm::Average:
909 layerName =
910 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
911 break;
912 case PoolingAlgorithm::Max:
913 layerName =
914 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
915 break;
916 default:
917 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
918 }
919
920 Pooling2dDescriptor desc;
921
922 desc.m_PoolType = algorithm;
923 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
924 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
925 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
926 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
927 desc.m_PaddingMethod = PaddingMethod::Exclude;
928 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000929 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100930
931 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
932 CHECK_VALID_SIZE(inputs.size(), 1);
933 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
934
935 // assuming input is NHWC
936 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
937 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
938
939 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
940 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
941
942 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
943 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100944
945 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
946
947 BOOST_ASSERT(layer != nullptr);
948
jimfly01c25411c2018-11-14 17:47:22 +0000949 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
950 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100951
952 // register the input connection slots for the layer, connections are made after all layers have been created
953 // only the tensors for the inputs are relevant, exclude the const tensors
954 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000955 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100956
jimfly01c25411c2018-11-14 17:47:22 +0000957 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100958 // register the output connection slots for the layer, connections are made after all layers have been created
959 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
960 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
961}
962
telsoa01c577f2c2018-08-31 09:22:23 +0100963void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
964{
965 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
966 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
967 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
968
969 SoftmaxDescriptor desc;
970 desc.m_Beta = options->beta;
971
972 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
973 CHECK_VALID_SIZE(inputs.size(), 1);
974 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
975 CHECK_VALID_SIZE(outputs.size(), 1);
976
977 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
978 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
979
980 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
981 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
982
983 // register the input connection slots for the layer, connections are made after all layers have been created
984 // only the tensors for the inputs are relevant, exclude the const tensors
985 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
986 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
987
988 // register the output connection slots for the layer, connections are made after all layers have been created
989 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
990 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
991}
992
993armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
994 const armnn::TensorInfo & inputTensorInfo)
995{
996 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
997 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
998 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
999
1000 if (inputTensorInfo.GetNumDimensions() > 4)
1001 {
1002 std::stringstream ss;
1003 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1004 << " shape:" << inputTensorInfo.GetShape() << " "
1005 << CHECK_LOCATION().AsString();
1006 throw ParseException(ss.str());
1007 }
1008
1009 if (squeezeDims.empty())
1010 {
1011 squeezeDims.assign(dimensionSequence,
1012 dimensionSequence+inputTensorInfo.GetNumDimensions());
1013 }
1014
1015 std::vector<uint32_t> outputDims;
1016 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1017 {
1018 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1019 auto currentDimension = inputTensorInfo.GetShape()[i];
1020 if (skipSqueeze || currentDimension != 1)
1021 {
1022 outputDims.push_back(currentDimension);
1023 }
1024 }
1025
1026 if (outputDims.size() > 4)
1027 {
1028 std::stringstream ss;
1029 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1030 << " shape:" << inputTensorInfo.GetShape() << " "
1031 << CHECK_LOCATION().AsString();
1032 throw ParseException(ss.str());
1033 }
1034
1035 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1036 outputDims.data());
1037
1038 // we need to preserve the tensor type and the quantization data as well
1039 TensorInfo outTensorInfo = inputTensorInfo;
1040 outTensorInfo.SetShape(outShape);
1041
1042 return outTensorInfo;
1043}
1044
1045void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1046{
1047 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1048
1049 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1050 CHECK_VALID_SIZE(inputs.size(), 1);
1051
1052 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1053 CHECK_VALID_SIZE(outputs.size(), 1);
1054
1055 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1056 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1057
1058 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1059 armnn::TensorInfo outputTensorInfo =
1060 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1061 inputTensorInfo);
1062
1063 ReshapeDescriptor reshapeDesc;
1064 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1065
1066 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1067 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1068 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1069
1070 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1071 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1072
1073 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1074 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1075}
1076
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001077void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1078{
1079 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1080
1081 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1082 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1083
1084 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1085 CHECK_VALID_SIZE(inputs.size(), 2);
1086
1087 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1088 CHECK_VALID_SIZE(outputs.size(), 1);
1089
1090 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1091 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1092
1093 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1094 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1095
1096 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1097 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1098
1099 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1100 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1101 {
1102 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1103 }
1104 else
1105 {
1106 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1107 }
1108
1109 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1110
1111 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1112 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1113}
1114
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001115void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1116{
1117 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1118
1119 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1120 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1121
1122 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1123 CHECK_VALID_SIZE(inputs.size(), 2);
1124
1125 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1126 CHECK_VALID_SIZE(outputs.size(), 1);
1127
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001128 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1129 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1130
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001131 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1132 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1133
1134 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1135 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1136
1137 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001138 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1139 {
1140 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1141 }
1142 else
1143 {
1144 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1145 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001146
1147 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1148
1149 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1150 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1151}
1152
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001153void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1154{
1155 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1156
1157 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1158 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1159
1160 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1161 CHECK_VALID_SIZE(inputs.size(), 2);
1162
1163 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1164 CHECK_VALID_SIZE(outputs.size(), 1);
1165
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001166 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1167 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1168
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001169 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1170 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1171
1172 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1173 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1174
1175 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001176 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1177 {
1178 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1179 }
1180 else
1181 {
1182 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1183 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001184
1185 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1186
1187 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1188 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1189}
1190
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001191void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1192{
1193 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1194
1195 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1196
1197 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1198 CHECK_VALID_SIZE(outputs.size(), 1);
1199
1200 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1201 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1202
1203 armnn::MeanDescriptor desc;
1204 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1205 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1206 desc.m_Axis = axis;
1207
1208 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1209 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1210
1211 desc.m_KeepDims =
1212 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1213 true : false;
1214
1215 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1216 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1217
1218 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1219
1220 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1221 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1222
1223 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1224 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1225}
1226
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001227void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1228{
1229 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1230
1231 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1232
1233 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1234 CHECK_VALID_SIZE(outputs.size(), 1);
1235
1236 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1237 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1238
1239 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1240 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1241
1242 size_t step = 2;
1243 armnn::PadDescriptor desc;
1244 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1245 {
1246 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1247 }
1248
1249 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1250 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1251
1252 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1253 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1254
1255 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1256 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1257
1258 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1259 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1260}
1261
Finn Williamsc42c3842019-01-22 14:18:11 +00001262
Sadik Armagan58f39192018-09-17 14:14:39 +01001263void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1264{
Finn Williamsc42c3842019-01-22 14:18:11 +00001265 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001266}
1267
1268void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1269{
Finn Williamsc42c3842019-01-22 14:18:11 +00001270 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1271}
Sadik Armagan58f39192018-09-17 14:14:39 +01001272
Finn Williamsc42c3842019-01-22 14:18:11 +00001273void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1274{
1275 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1276}
1277
1278
1279void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1280{
1281 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001282 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1283 boost::ignore_unused(operatorPtr);
1284
1285 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1286 CHECK_VALID_SIZE(inputs.size(), 1);
1287
1288 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1289 CHECK_VALID_SIZE(outputs.size(), 1);
1290
Finn Williamsc42c3842019-01-22 14:18:11 +00001291 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001292 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001293 activationDesc.m_Function = activationType;
1294
1295 switch (activationType)
1296 {
1297 case ActivationFunction::ReLu:
1298 {
1299 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1300 break;
1301 }
1302 case ActivationFunction::BoundedReLu:
1303 {
1304 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1305 activationDesc.m_A = 6.0f;
1306 activationDesc.m_B = 0.0f;
1307 break;
1308 }
1309 case ActivationFunction::Sigmoid:
1310 {
1311 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1312 break;
1313 }
1314 default:
1315 {
1316 throw ParseException(
1317 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1318 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1319 }
1320 }
1321
1322 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001323
1324 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1325 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1326
1327 // register the input connection slots for the layer, connections are made after all layers have been created
1328 // only the tensors for the inputs are relevant, exclude the const tensors
1329 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1330 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1331
1332 // register the output connection slots for the layer, connections are made after all layers have been created
1333 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1334 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1335}
Sadikb94967b2018-09-19 15:30:00 +01001336armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1337 const std::vector<int32_t> & targetDimsIn)
1338{
1339 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1340 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1341
1342 if (stretchDim != targetDimsIn.end())
1343 {
1344 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1345 {
1346 throw ParseException(
1347 boost::str(
1348 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1349 }
1350
1351 auto targetNumElements =
1352 boost::numeric_cast<unsigned int>(
1353 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1354
1355 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1356 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1357 }
1358
1359 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1360
1361 TensorInfo reshapeInfo = inputTensorInfo;
1362 reshapeInfo.SetShape(outputShape);
1363
1364 return reshapeInfo;
1365}
1366
1367void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1368{
1369 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1370
1371 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001372
1373 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1374 CHECK_VALID_SIZE(outputs.size(), 1);
1375
1376 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1377 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1378
1379 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001380 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1381 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001382 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1383
kevmay0171972a82018-12-17 14:28:03 +00001384 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001385 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1386 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001387 {
1388 std::stringstream ss;
1389 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001390 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001391 << " does not equal output shape "
1392 << actualOutputTensorInfo.GetShape()
1393 << ": "
1394 << CHECK_LOCATION().AsString();
1395 throw ParseException(ss.str());
1396 }
1397
Sadikb94967b2018-09-19 15:30:00 +01001398 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001399 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001400
1401 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1402 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001403 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001404
1405 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1406 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1407
1408 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1409 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1410}
1411
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001412void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1413{
1414 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1415
1416 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1417 CHECK_VALID_SIZE(inputs.size(), 2);
1418
1419 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1420 CHECK_VALID_SIZE(outputs.size(), 1);
1421
1422 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1423
1424 // Data for the parsed tensor args (size) must be stored locally.
1425 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1426
1427 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1428 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1429
1430 ResizeBilinearDescriptor desc;
1431 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
1432 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1433 desc.m_DataLayout = armnn::DataLayout::NHWC;
1434
1435 auto layerName = boost::str(boost::format("ResizeBilinear:%1%:%2%") % subgraphIndex % operatorIndex);
1436 IConnectableLayer* layer = m_Network->AddResizeBilinearLayer(desc, layerName.c_str());
1437
1438 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1439 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1440
1441 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1442 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1443
1444 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1445 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1446}
1447
Sadik Armagan479045b2018-10-01 11:51:37 +01001448void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1449{
1450 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1451
1452 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1453 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1454
1455 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1456
1457 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1458 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1459 CHECK_VALID_SIZE(outputs.size(), 1);
1460
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001461 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1462 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001463
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001464 const unsigned int concatDimInput = static_cast<unsigned int>(
1465 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001466
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001467 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1468 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001469
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001470 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01001471
1472 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1473 {
1474 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1475
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001476 // This set up concatDescriptor view origin
1477 armnnUtils::ProcessConcatInputTensorInfo(
1478 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01001479 }
1480
1481 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1482 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1483
1484 BOOST_ASSERT(layer != nullptr);
1485
1486 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1487 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01001488
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001489 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01001490
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001491 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01001492
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001493 // add fused activation layer
1494 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01001495
Sadik Armagan479045b2018-10-01 11:51:37 +01001496 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1497 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1498}
1499
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001500void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1501{
1502 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1503
1504 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1505 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1506
1507 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1508
1509 FullyConnectedDescriptor desc;
1510 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001511 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001512
1513 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1514 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1515 CHECK_VALID_SIZE(outputs.size(), 1);
1516
1517 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1518
1519 // Fully Connected Layer accepts two dimensional weights input
1520 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1521 if (weightsDimension != 2)
1522 {
1523 throw ParseException(
1524 boost::str(
1525 boost::format(
1526 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1527 "Node %2%")
1528 % weightsDimension
1529 % CHECK_LOCATION().AsString()));
1530 }
1531
Matteo Martincigh747ef822018-12-18 09:26:39 +00001532 auto filterTensorAndData = CreateConstTensor(inputs[1],
1533 filterTensorInfo,
1534 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001535 armnn::IConnectableLayer* layer;
1536 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1537
1538 if (inputs.size() == 3)
1539 {
1540 desc.m_BiasEnabled = true;
1541 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001542 auto biasTensorAndData = CreateConstTensor(inputs[2],
1543 biasTensorInfo,
1544 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001545 layer = m_Network->AddFullyConnectedLayer(desc,
1546 filterTensorAndData.first,
1547 biasTensorAndData.first,
1548 layerName.c_str());
1549 }
1550 else
1551 {
1552 layer = m_Network->AddFullyConnectedLayer(desc,
1553 filterTensorAndData.first,
1554 layerName.c_str());
1555 }
1556 BOOST_ASSERT(layer != nullptr);
1557
1558 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1559 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1560
1561 // register the input connection slot for the layer
1562 // only the tensors for the inputs are relevant, exclude the const tensors
1563 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1564 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1565
1566 // we need to add the activation layer and fortunately we don't need to care about the data layout
1567 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1568 options->fused_activation_function);
1569 // register the output connection slots for the layer, connections are made after all layers have been created
1570 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1571 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1572}
1573
keidav011b3e2ea2019-02-21 10:07:37 +00001574void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
1575{
1576 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1577
1578 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1579
1580 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1581 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1582 CHECK_VALID_SIZE(outputs.size(), 4);
1583
1584 // Obtain custom options from flexbuffers
1585 auto custom_options = operatorPtr->custom_options;
1586 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
1587
1588 // Obtain descriptor information from tf lite
1589 DetectionPostProcessDescriptor desc;
1590 desc.m_MaxDetections = m["max_detections"].AsUInt32();
1591 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
1592 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
1593 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
1594 desc.m_NumClasses = m["num_classes"].AsUInt32();
1595 desc.m_ScaleH = m["h_scale"].AsFloat();
1596 desc.m_ScaleW = m["w_scale"].AsFloat();
1597 desc.m_ScaleX = m["x_scale"].AsFloat();
1598 desc.m_ScaleY = m["y_scale"].AsFloat();
1599
1600 if (!(m["use_regular_non_max_suppression"].IsNull()))
1601 {
1602 desc.m_UseRegularNms = m["use_regular_non_max_suppression"].AsBool();
1603 }
1604 if (!(m["detections_per_class"].IsNull()))
1605 {
1606 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
1607 }
1608
1609 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
1610 {
1611 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
1612 "must be positive and less than or equal to 1.");
1613 }
1614
1615 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
1616 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
1617 armnn::Optional<armnn::PermutationVector&>());
1618
1619 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
1620 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
1621 layerName.c_str());
1622
1623 BOOST_ASSERT(layer != nullptr);
1624
1625 // Register outputs
1626 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
1627 {
1628 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i]);
1629 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
1630 }
1631
1632 // Register the input connection slots for the layer, connections are made after all layers have been created
1633 // only the tensors for the inputs are relevant, exclude the const tensors
1634 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1635 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1636
1637 // Register the output connection slots for the layer, connections are made after all layers have been created
1638 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1639 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
1640 outputTensorIndexes[1],
1641 outputTensorIndexes[2],
1642 outputTensorIndexes[3]});
1643}
1644
Sadik Armagan58f39192018-09-17 14:14:39 +01001645armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1646 unsigned int outputSlot,
1647 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001648{
1649 ActivationDescriptor activationDesc;
1650 std::string layerName = prevLayer->GetName();
1651
1652 switch(activationType)
1653 {
1654 case tflite::ActivationFunctionType_NONE:
1655 {
1656 // this is a no-op: return previous layer
1657 return prevLayer;
1658 }
1659 case tflite::ActivationFunctionType_RELU:
1660 {
1661 activationDesc.m_Function = ActivationFunction::ReLu;
1662 layerName += ":RELU";
1663 break;
1664 }
1665 case tflite::ActivationFunctionType_RELU6:
1666 {
1667 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1668 activationDesc.m_A = 6.0f;
1669 activationDesc.m_B = 0.0f;
1670 layerName += ":RELU6";
1671 break;
1672 }
1673 case tflite::ActivationFunctionType_TANH:
1674 {
1675 activationDesc.m_Function = ActivationFunction::TanH;
1676 activationDesc.m_A = 1.0f;
1677 activationDesc.m_B = 1.0f;
1678 layerName += ":TANH";
1679 break;
1680 }
1681
1682 // I only put these here as a reminder what others we could support
1683 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1684 case tflite::ActivationFunctionType_SIGN_BIT:
1685 default:
1686 {
1687 throw ParseException(
1688 boost::str(
1689 boost::format("TfLite parser doesn't suppport fused activation: "
1690 "%1%/%2% %3% ") %
1691 activationType %
1692 tflite::EnumNameActivationFunctionType(activationType) %
1693 CHECK_LOCATION().AsString()));
1694
1695 }
1696 }
1697
1698 IConnectableLayer* activationLayer =
1699 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1700
1701 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1702 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1703 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1704 return activationLayer;
1705}
1706
1707TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1708{
1709 if (fileName == nullptr)
1710 {
1711 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1712 CHECK_LOCATION().AsString()));
1713 }
1714 boost::system::error_code errorCode;
1715 boost::filesystem::path pathToFile(fileName);
1716 if (!boost::filesystem::exists(pathToFile, errorCode))
1717 {
1718 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1719 fileName %
1720 errorCode %
1721 CHECK_LOCATION().AsString()));
1722 }
1723 std::ifstream file(fileName, std::ios::binary);
1724 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1725 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1726 fileContent.size());
1727}
1728
1729TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1730{
1731 if (binaryContent == nullptr)
1732 {
1733 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1734 CHECK_LOCATION().AsString()));
1735 }
1736 flatbuffers::Verifier verifier(binaryContent, len);
1737 if (verifier.VerifyBuffer<tflite::Model>() == false)
1738 {
1739 throw ParseException(
1740 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1741 "flatbuffers format. size:%1% %2%") %
1742 len %
1743 CHECK_LOCATION().AsString()));
1744 }
1745 return tflite::UnPackModel(binaryContent);
1746}
1747
1748TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1749 size_t subgraphIndex,
1750 size_t operatorIndex)
1751{
1752 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1753
1754 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1755 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1756
1757 size_t inputCount = operatorPtr->inputs.size();
1758 TensorRawPtrVector result(inputCount);
1759 for (size_t i=0; i<inputCount; ++i)
1760 {
1761 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1762 result[i] = subGraphPtr->tensors[inputId].get();
1763 }
1764 return result;
1765}
1766
1767TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1768 size_t subgraphIndex,
1769 size_t operatorIndex)
1770{
1771 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1772
1773 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1774 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1775
1776 size_t outputCount = operatorPtr->outputs.size();
1777 TensorRawPtrVector result(outputCount);
1778 for (size_t i=0; i<outputCount; ++i)
1779 {
1780 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1781 CHECK_TENSOR(model, subgraphIndex, outputId);
1782 result[i] = subGraphPtr->tensors[outputId].get();
1783 }
1784 return result;
1785}
1786
1787TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1788 size_t subgraphIndex)
1789{
1790 CHECK_SUBGRAPH(model, subgraphIndex);
1791 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1792
1793 size_t inputCount = subGraphPtr->inputs.size();
1794 TensorIdRawPtrVector result(inputCount);
1795 for (size_t i=0; i<inputCount; ++i)
1796 {
1797 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1798 CHECK_TENSOR(model, subgraphIndex, inputId);
1799 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1800 }
1801 return result;
1802}
1803
1804TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1805 size_t subgraphIndex)
1806{
1807 CHECK_SUBGRAPH(model, subgraphIndex);
1808 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1809
1810 size_t outputCount = subGraphPtr->outputs.size();
1811 TensorIdRawPtrVector result(outputCount);
1812 for (size_t i=0; i<outputCount; ++i)
1813 {
1814 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1815 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1816 }
1817 return result;
1818}
1819
1820std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1821 size_t subgraphIndex,
1822 size_t operatorIndex)
1823{
1824 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1825 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1826 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1827 return operatorPtr->inputs;
1828}
1829
1830std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1831 size_t subgraphIndex,
1832 size_t operatorIndex)
1833{
1834 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1835 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1836 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1837 return operatorPtr->outputs;
1838}
1839
1840void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1841 size_t operatorIndex,
1842 IConnectableLayer* layer,
1843 const std::vector<unsigned int>& tensorIndexes)
1844{
1845 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1846 BOOST_ASSERT(layer != nullptr);
1847 if (tensorIndexes.size() != layer->GetNumInputSlots())
1848 {
1849 throw ParseException(
1850 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1851 " for subgraph:%3% operator index:%4% %5%") %
1852 tensorIndexes.size() %
1853 layer->GetNumInputSlots() %
1854 subgraphIndex %
1855 operatorIndex %
1856 CHECK_LOCATION().AsString()));
1857 }
1858
1859 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1860 {
1861 unsigned int tensorIndex = tensorIndexes[slotIndex];
1862 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1863 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1864 }
1865}
1866
1867void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1868 size_t operatorIndex,
1869 IConnectableLayer* layer,
1870 const std::vector<unsigned int>& tensorIndexes)
1871{
1872 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1873 BOOST_ASSERT(layer != nullptr);
1874 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1875 {
1876 throw ParseException(
1877 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1878 " for subgraph:%3% operator index:%4% %5%") %
1879 tensorIndexes.size() %
1880 layer->GetNumOutputSlots() %
1881 subgraphIndex %
1882 operatorIndex %
1883 CHECK_LOCATION().AsString()));
1884 }
1885
1886 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1887 {
1888 unsigned int tensorIndex = tensorIndexes[slotIndex];
1889 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1890 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1891 }
1892}
1893
1894void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1895{
1896 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1897
1898 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1899 for (auto const & tensorIdAndPtr : inputs)
1900 {
1901 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1902 IConnectableLayer* layer =
1903 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1904
1905 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1906 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1907
1908 RegisterOutputSlots(subgraphIndex,
1909 VIRTUAL_OPERATOR_ID,
1910 layer,
1911 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1912 }
1913}
1914
1915void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1916{
1917 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1918
1919 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1920 for (auto const & tensorIdAndPtr : outputs)
1921 {
1922 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1923 IConnectableLayer* layer =
1924 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1925
1926 RegisterInputSlots(subgraphIndex,
1927 VIRTUAL_OPERATOR_ID,
1928 layer,
1929 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1930 }
1931}
1932
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02001933void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
1934{
1935 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1936
1937 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
1938 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
1939 {
1940 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
1941 {
1942 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
1943 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
1944 {
1945 TensorRawPtr tensorPtr = subGraphPtr->tensors[tensorIndex].get();
1946 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
1947 auto tensorAndData = CreateConstTensor(tensorPtr,
1948 tensorInfo,
1949 armnn::Optional<armnn::PermutationVector&>());
1950
1951 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
1952 IConnectableLayer *layer =
1953 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
1954
1955 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1956 RegisterOutputSlots(subgraphIndex,
1957 VIRTUAL_OPERATOR_ID,
1958 layer,
1959 { tensorIndex });
1960
1961 }
1962 }
1963 }
1964}
1965
telsoa01c577f2c2018-08-31 09:22:23 +01001966// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1967TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1968{
1969 CHECK_BUFFER(model, bufferIndex);
1970 return model->buffers[bufferIndex].get();
1971}
1972
Matteo Martincigh747ef822018-12-18 09:26:39 +00001973template<typename T>
1974std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1975TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
1976 TfLiteParser::TensorRawPtr tensorPtr,
1977 armnn::TensorInfo& tensorInfo,
1978 armnn::Optional<armnn::PermutationVector&> permutationVector)
1979{
1980 auto constData = CreateConstTensorImpl<T>(bufferPtr,
1981 tensorPtr,
1982 tensorInfo,
1983 permutationVector);
1984 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
1985 return std::make_pair(constData.first, std::move(storage));
1986}
1987
telsoa01c577f2c2018-08-31 09:22:23 +01001988std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1989TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00001990 armnn::TensorInfo& tensorInfo,
1991 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01001992{
1993 CHECK_TENSOR_PTR(tensorPtr);
1994 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1995 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1996
1997 switch (tensorInfo.GetDataType())
1998 {
1999 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002000 return CreateConstTensorAndStoreData<float>(bufferPtr,
2001 tensorPtr,
2002 tensorInfo,
2003 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002004 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002005 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2006 tensorPtr,
2007 tensorInfo,
2008 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002009 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002010 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2011 tensorPtr,
2012 tensorInfo,
2013 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002014 default:
2015 {
2016 std::stringstream errString;
2017 errString << "Unexpected datatype when creating const tensor: "
2018 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2019 << " shape:" << tensorInfo.GetShape()
2020 << CHECK_LOCATION().AsString();
2021 throw ParseException(errString.str());
2022 }
2023 }
2024}
2025
2026BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2027 const std::string& name) const
2028{
2029 CHECK_SUBGRAPH(m_Model, subgraphId);
2030 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2031 for (auto const & input : inputs)
2032 {
2033 if (input.second->name == name)
2034 {
2035 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2036 return std::make_pair(bindingId, ToTensorInfo(input.second));
2037 }
2038 }
2039
2040 std::stringstream bindings;
2041 for (auto const & input : inputs)
2042 {
2043 bindings << "'" << input.second->name << "' ";
2044 }
2045
2046 throw ParseException(
2047 boost::str(
2048 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2049 "Possible inputs are: [%3%] %4%") %
2050 subgraphId %
2051 name %
2052 bindings.str() %
2053 CHECK_LOCATION().AsString()));
2054}
2055
2056BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2057 const std::string& name) const
2058{
2059 CHECK_SUBGRAPH(m_Model, subgraphId);
2060 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2061 for (auto const & output : outputs)
2062 {
2063 if (output.second->name == name)
2064 {
2065 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
2066 return std::make_pair(bindingId, ToTensorInfo(output.second));
2067 }
2068 }
2069
2070 std::stringstream bindings;
2071 for (auto const & output : outputs)
2072 {
2073 bindings << "'" << output.second->name << "' ";
2074 }
2075
2076 throw ParseException(
2077 boost::str(
2078 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2079 "Possible outputs are: [%3%] %4%") %
2080 subgraphId %
2081 name %
2082 bindings.str() %
2083 CHECK_LOCATION().AsString()));
2084}
2085
2086size_t TfLiteParser::GetSubgraphCount() const
2087{
2088 return m_Model->subgraphs.size();
2089}
2090
2091std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2092{
2093 CHECK_SUBGRAPH(m_Model, subgraphId);
2094 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2095 std::vector<std::string> result;
2096 result.reserve(inputs.size());
2097 for (auto const & input : inputs)
2098 {
2099 result.push_back(input.second->name);
2100 }
2101 return result;
2102}
2103
2104std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2105{
2106 CHECK_SUBGRAPH(m_Model, subgraphId);
2107 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2108 std::vector<std::string> result;
2109 result.reserve(outputs.size());
2110 for (auto const & output : outputs)
2111 {
2112 result.push_back(output.second->name);
2113 }
2114 return result;
2115}
2116
2117ITfLiteParser* ITfLiteParser::CreateRaw()
2118{
2119 return new TfLiteParser();
2120}
2121
2122ITfLiteParserPtr ITfLiteParser::Create()
2123{
2124 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
2125}
2126
2127void ITfLiteParser::Destroy(ITfLiteParser* parser)
2128{
2129 delete parser;
2130}
2131
2132TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2133: m_FloatData(std::move(data))
2134, m_Uint8Data(nullptr)
2135, m_Int32Data(nullptr)
2136{
2137}
2138
2139TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2140: m_FloatData(nullptr)
2141, m_Uint8Data(std::move(data))
2142, m_Int32Data(nullptr)
2143{
2144}
2145
2146TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2147: m_FloatData(nullptr)
2148, m_Uint8Data(nullptr)
2149, m_Int32Data(std::move(data))
2150{
2151}
2152
2153} // armnnTfLiteParser