blob: d3f382adbbe63f9ae6ff9013920aa487d6a8a2db [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>
telsoa01c577f2c2018-08-31 09:22:23 +010029
30using namespace armnn;
31using armnn::CheckLocation;
32namespace armnnTfLiteParser
33{
34namespace
35{
36const PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
37const PermutationVector ArmNNToNHWC = { 0, 3, 1, 2 };
38
jimfly01c25411c2018-11-14 17:47:22 +000039IConnectableLayer* SwizzleIn(INetwork& network,
40 IConnectableLayer* layer,
41 unsigned int inputSlotIndex,
42 const TensorInfo & inputInfo)
43{
44 BOOST_ASSERT(layer != nullptr);
45 // Add swizzle layer
46 std::stringstream name;
47 name << "swizzle_for-" << layer->GetName() << ":in" << inputSlotIndex;
48 IConnectableLayer* const swizzleLayer = network.AddPermuteLayer(NHWCToArmNN, name.str().c_str());
49 // Set swizzled output shape
50 const TensorInfo swizzleOutInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
51 swizzleLayer->GetOutputSlot(0).SetTensorInfo(swizzleOutInfo);
52 // Connect the swizzle layer to the actual layer
53 swizzleLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(inputSlotIndex));
54
55 return swizzleLayer;
56}
57
58IConnectableLayer* DeswizzleOut(INetwork& network,
59 IConnectableLayer* layer,
60 unsigned int outputSlotIndex,
61 const TensorInfo & outputInfo)
62{
63 BOOST_ASSERT(layer != nullptr);
64 // Add deswizzle layer
65 std::stringstream name;
66 name << "deswizzle_for-" << layer->GetName() << ":out" << outputSlotIndex;
67 IConnectableLayer* const deswizzleLayer = network.AddPermuteLayer(ArmNNToNHWC, name.str().c_str());
68 // Set deswizzled output shape
69 deswizzleLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
70 // Set original layer output shape
71 const TensorInfo deswizzleOutInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
72 layer->GetOutputSlot(outputSlotIndex).SetTensorInfo(deswizzleOutInfo);
73 // Connect the actual layer to the deswizzle layer
74 layer->GetOutputSlot(outputSlotIndex).Connect(deswizzleLayer->GetInputSlot(0));
75
76 return deswizzleLayer;
77}
78
telsoa01c577f2c2018-08-31 09:22:23 +010079const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
80
81void CheckSubgraph(const TfLiteParser::ModelPtr & model,
82 size_t subgraphIndex,
83 const CheckLocation & location)
84{
85 if (model.get() == nullptr)
86 {
87 throw ParseException(
88 boost::str(
89 boost::format("%1% was called with invalid (null) model. "
90 "Possible reason is that the model is not yet loaded and Unpack(ed). "
91 "subgraph:%2% at %3%") %
92 location.m_Function %
93 subgraphIndex %
94 location.FileLine()));
95 }
96 else if (subgraphIndex >= model->subgraphs.size())
97 {
98 throw ParseException(
99 boost::str(
100 boost::format("%1% was called with an invalid subgraph index. "
101 "subgraph:%2% at %3%") %
102 location.m_Function %
103 subgraphIndex %
104 location.FileLine()));
105 }
106}
107
108#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
109 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
110
111void CheckModel(const TfLiteParser::ModelPtr & model,
112 size_t subgraphIndex,
113 size_t operatorIndex,
114 const CheckLocation & location)
115{
116 if (model.get() == nullptr)
117 {
118 throw ParseException(
119 boost::str(
120 boost::format("%1% was called with invalid (null) model. "
121 "Possible reason is that the model is not yet loaded and Unpack(ed). "
122 "subgraph:%2% operator:%3% at %4%") %
123 location.m_Function %
124 subgraphIndex %
125 operatorIndex %
126 location.FileLine()));
127 }
128 else if (subgraphIndex >= model->subgraphs.size())
129 {
130 throw ParseException(
131 boost::str(
132 boost::format("%1% was called with an invalid subgraph index. "
133 "subgraph:%2% operator:%3% at %4%") %
134 location.m_Function %
135 subgraphIndex %
136 operatorIndex %
137 location.FileLine()));
138 }
139 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
140 operatorIndex != VIRTUAL_OPERATOR_ID)
141 {
142 throw ParseException(
143 boost::str(
144 boost::format("%1% was called with an invalid operator index. "
145 "subgraph:%2% operator:%3% at %4%") %
146 location.m_Function %
147 subgraphIndex %
148 operatorIndex %
149 location.FileLine()));
150 }
151}
152
153#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
154 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
155
156void CheckTensor(const TfLiteParser::ModelPtr & model,
157 size_t subgraphIndex,
158 size_t tensorIndex,
159 const CheckLocation & location)
160{
161 // not checking model, because I assume CHECK_MODEL already run
162 // and checked that. An assert would do.
163 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
164
165 // also subgraph index should be checked by CHECK_MODEL so
166 // I only add an assert here
167 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
168
169 // the tensor index is the only one to check here
170 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
171 {
172 throw ParseException(
173 boost::str(
174 boost::format("%1% was called with an invalid tensor index. "
175 "subgraph:%2% tensor:%3% at %4%") %
176 location.m_Function %
177 subgraphIndex %
178 tensorIndex %
179 location.FileLine()));
180 }
181}
182
183#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
184 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
185
186void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
187 const CheckLocation & location)
188{
189 if (rawPtr == nullptr)
190 {
191 throw ParseException(
192 boost::str(
193 boost::format("%1% was called with a null tensor pointer. "
194 "at %2%") %
195 location.m_Function %
196 location.FileLine()));
197
198 }
199}
200
201#define CHECK_TENSOR_PTR(TENSOR_PTR) \
202 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
203
204void CheckBuffer(const TfLiteParser::ModelPtr & model,
205 size_t bufferIndex,
206 const CheckLocation & location)
207{
208 if (model.get() == nullptr)
209 {
210 throw ParseException(
211 boost::str(
212 boost::format("%1% was called with invalid (null) model. "
213 "Possible reason is that the model is not yet loaded and Unpack(ed). "
214 "buffer:%2% at %3%") %
215 location.m_Function %
216 bufferIndex %
217 location.FileLine()));
218 }
219 else if (bufferIndex >= model->buffers.size())
220 {
221 throw ParseException(
222 boost::str(
223 boost::format("%1% was called with an invalid buffer index. "
224 "buffer index:%2% at %3%") %
225 location.m_Function %
226 bufferIndex %
227 location.FileLine()));
228 }
229 else if (model->buffers[bufferIndex].get() == nullptr)
230 {
231 throw ParseException(
232 boost::str(
233 boost::format("The buffer #%1% is null. %3%") %
234 bufferIndex %
235 location.AsString()));
236 }
237}
238
239#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
240 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
241
242void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
243 const armnn::TensorInfo & tensorInfo,
244 uint32_t bufferId,
245 const CheckLocation & location)
246{
247 if (bufferPtr == nullptr)
248 {
249 throw ParseException(
250 boost::str(
251 boost::format("BufferPtr is null for buffer:%1%. %2%") %
252 bufferId %
253 location.AsString()));
254 }
255 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
256 tensorInfo.GetNumBytes() > bufferPtr->data.size())
257 {
258 std::stringstream ss;
259 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
260 << "For tensor: " << tensorInfo.GetShape()
261 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
262 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
263 throw ParseException(ss.str());
264 }
265}
266
267#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
268 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
269
270bool IsActivationSupported(tflite::ActivationFunctionType activationType)
271{
272 switch(activationType)
273 {
274 case tflite::ActivationFunctionType_NONE:
275 case tflite::ActivationFunctionType_RELU:
276 case tflite::ActivationFunctionType_RELU6:
277 case tflite::ActivationFunctionType_TANH:
278 {
279 return true;
280 }
281 default:
282 {
283 return false;
284 }
285 }
286}
287
288#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
289 do { \
290 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
291 { \
292 throw ParseException( \
293 boost::str( \
294 boost::format("TfLite parser doesn't suppport fused activation: " \
295 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
296 OPTION->fused_activation_function % \
297 tflite::EnumNameActivationFunctionType(\
298 OPTION->fused_activation_function) % \
299 __func__ % \
300 SUBGRAPH_INDEX % \
301 OPERATOR_INDEX % \
302 CHECK_LOCATION().FileLine())); \
303 } \
304 } while(false)
305
306
307std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
308{
309 std::vector<unsigned int> result;
310 result.reserve(in.size());
311 for (auto & i : in)
312 {
313 result.push_back(CHECKED_NON_NEGATIVE(i));
314 }
315 return result;
316}
317
318void CalcPadding(uint32_t inputSize,
319 uint32_t filterSize,
320 uint32_t stride,
321 uint32_t& paddingFront,
322 uint32_t& paddingBack,
323 tflite::Padding padding)
324{
325 paddingFront = 0;
326 paddingBack = 0;
327 if (padding == tflite::Padding_SAME)
328 {
329 uint32_t outputSize = (inputSize + stride - 1) / stride;
330 uint32_t temp = (outputSize - 1) * stride + filterSize;
331 if (temp > inputSize)
332 {
333 paddingFront = (temp - inputSize) / 2;
334 paddingBack = (temp - inputSize) - paddingFront;
335 }
336 }
337}
338
339armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
340{
341 armnn::DataType type;
342 CHECK_TENSOR_PTR(tensorPtr);
343
344 switch (tensorPtr->type)
345 {
346 case tflite::TensorType_UINT8:
347 type = armnn::DataType::QuantisedAsymm8;
348 break;
349 case tflite::TensorType_FLOAT32:
350 type = armnn::DataType::Float32;
351 break;
352 case tflite::TensorType_INT32:
353 type = armnn::DataType::Signed32;
354 break;
355
356 default:
357 {
358 CheckLocation location = CHECK_LOCATION();
359 throw ParseException(
360 boost::str(
361 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
362 tensorPtr->type %
363 tflite::EnumNameTensorType(tensorPtr->type) %
364 tensorPtr->name %
365 location.AsString()));
366 }
367 }
368
369 float quantizationScale = 0.0f;
370 int32_t quantizationOffset = 0;
371
372 if (tensorPtr->quantization.get())
373 {
374 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
375 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
376
377 if (tensorPtr->quantization->scale.size() == 1)
378 {
379 quantizationScale = tensorPtr->quantization->scale[0];
380 }
381 if (tensorPtr->quantization->zero_point.size() == 1)
382 {
383 // NOTE: we lose precision here when converting from 64 bit to 32
384 // but this is what we support at the monent in ArmNN
385 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
386 }
387 }
388
389 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
390
391 // two statements (on purpose) for easier debugging:
392 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
393 dimensions.data(),
394 type,
395 quantizationScale,
396 quantizationOffset);
397 return result;
398}
399
400template<typename T>
401std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
402CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
403 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000404 armnn::TensorInfo& tensorInfo,
405 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100406{
407 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
408 BOOST_ASSERT_MSG(bufferPtr != nullptr,
409 boost::str(
410 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
411
412 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000413
414 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
415 {
416 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000417 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
418 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000419 }
420 else
421 {
422 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
423 }
424
telsoa01c577f2c2018-08-31 09:22:23 +0100425 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
426}
427
telsoa01c577f2c2018-08-31 09:22:23 +0100428armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
429{
430 // generate the binding id by shifting the tensor id by 8 bit
431 // and add the subgraph id, which allows 256 subgraphs
432 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
433}
434
435} // <anonymous>
436
437TfLiteParser::TfLiteParser()
438: m_Network(nullptr, nullptr)
439, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
440{
441 // register supported operators
442 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Sadik Armagan479045b2018-10-01 11:51:37 +0100443 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100444 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
445 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100446 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100447 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Sadik Armagan58f39192018-09-17 14:14:39 +0100448 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
449 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100450 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Sadik Armagan479045b2018-10-01 11:51:37 +0100451 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
452 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
telsoa01c577f2c2018-08-31 09:22:23 +0100453}
454
455void TfLiteParser::ResetParser()
456{
457 m_Network = armnn::INetworkPtr(nullptr, nullptr);
458 m_Model = nullptr;
459 m_SubgraphConnections.clear();
460}
461
462INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
463{
464 ResetParser();
465 m_Model = LoadModelFromFile(graphFile);
466 return CreateNetworkFromModel();
467}
468
469INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
470{
471 ResetParser();
472 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
473 return CreateNetworkFromModel();
474}
475
476INetworkPtr TfLiteParser::CreateNetworkFromModel()
477{
478 m_Network = INetwork::Create();
479 BOOST_ASSERT(m_Model.get() != nullptr);
480
481 bool failedToCreate = false;
482 std::stringstream errors;
483
484 if (m_Model->subgraphs.size() != 1)
485 {
486 throw ParseException(
487 boost::str(
488 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
489 m_Model->subgraphs.size() %
490 CHECK_LOCATION().AsString()));
491 }
492
493 size_t subgraphIndex = 0;
494 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
495 {
496 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
497
498 size_t operatorIndex = 0;
499 for (OperatorPtr const & op : subgraph->operators)
500 {
501 try
502 {
503 if (op->custom_options.size() > 0)
504 {
505 throw ParseException(
506 boost::str(
507 boost::format("Custom options for op: %1% is not supported. "
508 "It has %2% bytes of custom options. %3%") %
509 op->opcode_index %
510 op->custom_options.size() %
511 CHECK_LOCATION().AsString()));
512 }
513
514 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
515 auto builtinCode = opCodePtr->builtin_code;
516
517 if (builtinCode > tflite::BuiltinOperator_MAX)
518 {
519 throw ParseException(
520 boost::str(
521 boost::format("Operator code %1% is out of range 0-%2%. "
522 "subgraph:%3% operator idx:%4%. %5%") %
523 builtinCode %
524 tflite::BuiltinOperator_MAX %
525 subgraphIndex %
526 operatorIndex %
527 CHECK_LOCATION().AsString()));
528 }
529
530 // lookup and call the parser function
531 auto & parserFunction = m_ParserFunctions[builtinCode];
532 (this->*parserFunction)(subgraphIndex, operatorIndex);
533 }
534 catch (const ParseException& e)
535 {
536 failedToCreate = true;
537 std::stringstream errorString;
538
539 errorString << "Failed to parse operator #" << operatorIndex
540 << " within subgraph #" << subgraphIndex
541 << " error: " << e.what();
542 BOOST_LOG_TRIVIAL(error) << errorString.str();
543
544 errors << errorString.str() << "\n";
545 }
546 ++operatorIndex;
547 }
548
549 SetupInputLayers(subgraphIndex);
550 SetupOutputLayers(subgraphIndex);
551
552 ++subgraphIndex;
553 }
554
555 if (failedToCreate)
556 {
557 // we can skip everything and let the outer exception handler deal with the error
558 throw ParseException(errors.str());
559 }
560
561 // establish the connections from the layer outputs to the inputs of the subsequent layers
562 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
563 {
564 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
565 {
566 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
567 {
568 for (size_t inputSlotIdx = 0;
569 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
570 ++inputSlotIdx)
571 {
572 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
573 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
574 }
575 }
576 }
577 }
578
579 return std::move(m_Network);
580}
581
582void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
583 size_t tensorIndex,
584 armnn::IOutputSlot* slot)
585{
586 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
587 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
588 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
589
590 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
591
592 // assuming there is only one producer for that tensor
593 if (tensorSlots.outputSlot != nullptr)
594 {
595 throw ParseException(boost::str(
596 boost::format("Another layer has already registered itself as the producer of "
597 "subgraph:%1% tensor:%2% %3%") %
598 subgraphIndex %
599 tensorIndex %
600 CHECK_LOCATION().AsString()));
601 }
602
603 tensorSlots.outputSlot = slot;
604}
605
606void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
607 size_t tensorIndex,
608 armnn::IInputSlot* slot)
609{
610 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
611 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
612 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
613
614 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
615 tensorSlots.inputSlots.push_back(slot);
616}
617
618void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
619{
620 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
621 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
622 //
623 auto opcodeIndex = operatorPtr->opcode_index;
624 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
625
626 throw ParseException(
627 boost::str(
628 boost::format("Operator not supported. "
629 "subgraph:%1% operator:%2% "
630 "opcode_index:%3% opcode:%4% / %5% %6%") %
631 subgraphIndex %
632 operatorIndex %
633 opcodeIndex %
634 opcode %
635 tflite::EnumNameBuiltinOperator(opcode) %
636 CHECK_LOCATION().AsString()));
637}
638
telsoa01c577f2c2018-08-31 09:22:23 +0100639void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
640{
641 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
642
643 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
644 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
645
646 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
647
648 Convolution2dDescriptor desc;
649 desc.m_BiasEnabled = false;
650 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
651 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000652 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100653
654 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
655 CHECK_VALID_SIZE(inputs.size(), 2, 3);
656
657 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
658 CHECK_VALID_SIZE(outputs.size(), 1);
659
660 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
661 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
662
663 // assuming input is NHWC
664 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
665 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
666
667 // assuming the filter is OHWI : Output, H, W, Input
668 // which is essentially the same as NHWC
669 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
670 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
671
672 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
673 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
674
Matteo Martincigh747ef822018-12-18 09:26:39 +0000675 auto filterTensorAndData = CreateConstTensor(inputs[1],
676 filterTensorInfo,
677 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100678 armnn::IConnectableLayer* layer;
679
680 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
681
682 if (inputs.size() == 3)
683 {
684 desc.m_BiasEnabled = true;
685 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000686 auto biasTensorAndData = CreateConstTensor(inputs[2],
687 biasTensorInfo,
688 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100689 layer = m_Network->AddConvolution2dLayer(desc,
690 filterTensorAndData.first,
691 biasTensorAndData.first,
692 layerName.c_str());
693 }
694 else
695 {
696 layer = m_Network->AddConvolution2dLayer(desc,
697 filterTensorAndData.first,
698 layerName.c_str());
699 }
700
701 BOOST_ASSERT(layer != nullptr);
702
telsoa01c577f2c2018-08-31 09:22:23 +0100703 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000704 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100705
706 // register the input connection slots for the layer, connections are made after all layers have been created
707 // only the tensors for the inputs are relevant, exclude the const tensors
708 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000709 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100710
jimfly01c25411c2018-11-14 17:47:22 +0000711 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100712 // register the output connection slots for the layer, connections are made after all layers have been created
713 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
714 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
715}
716
717void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
718{
719 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
720
721 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
722 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
723
724 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
725
726 DepthwiseConvolution2dDescriptor desc;
727 desc.m_BiasEnabled = false;
728 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
729 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000730 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100731 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
732 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
733
734 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
735 CHECK_VALID_SIZE(inputs.size(), 2, 3);
736 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
737 CHECK_VALID_SIZE(outputs.size(), 1);
738
739 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
740 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
741
Matteo Martincigh747ef822018-12-18 09:26:39 +0000742 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100743 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
744 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000745
746 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100747 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
748 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
749
Matteo Martincigh747ef822018-12-18 09:26:39 +0000750 // Reshape weights as [ H, W, I, M ]
751 filterTensorInfo.SetShape({ filterHeight,
752 filterWidth,
753 inputTensorInfo.GetShape()[3],
754 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
755
756 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
757 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
758
telsoa01c577f2c2018-08-31 09:22:23 +0100759 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
760 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
761
Matteo Martincigh747ef822018-12-18 09:26:39 +0000762 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100763 armnn::IConnectableLayer* layer;
764 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
765
766 if (inputs.size() == 3)
767 {
768 desc.m_BiasEnabled = true;
769 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000770 auto biasTensorAndData = CreateConstTensor(inputs[2],
771 biasTensorInfo,
772 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100773 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
774 filterTensorAndData.first,
775 biasTensorAndData.first,
776 layerName.c_str());
777 }
778 else
779 {
780 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
781 filterTensorAndData.first,
782 layerName.c_str());
783 }
784 BOOST_ASSERT(layer != nullptr);
785
telsoa01c577f2c2018-08-31 09:22:23 +0100786 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000787 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100788
789 // register the input connection slots for the layer, connections are made after all layers have been created
790 // only the tensors for the inputs are relevant, exclude the const tensors
791 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000792 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100793
jimfly01c25411c2018-11-14 17:47:22 +0000794 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100795 // register the output connection slots for the layer, connections are made after all layers have been created
796 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
797 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
798}
799
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100800void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
801{
802 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
803}
804
805void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
806{
807 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
808}
809
810void TfLiteParser::ParsePool(size_t subgraphIndex,
811 size_t operatorIndex,
812 PoolingAlgorithm algorithm)
813{
814 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
815
816 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
817 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
818
819 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
820
821 std::string layerName;
822
823 switch (algorithm)
824 {
825 case PoolingAlgorithm::Average:
826 layerName =
827 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
828 break;
829 case PoolingAlgorithm::Max:
830 layerName =
831 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
832 break;
833 default:
834 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
835 }
836
837 Pooling2dDescriptor desc;
838
839 desc.m_PoolType = algorithm;
840 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
841 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
842 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
843 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
844 desc.m_PaddingMethod = PaddingMethod::Exclude;
845 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000846 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100847
848 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
849 CHECK_VALID_SIZE(inputs.size(), 1);
850 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
851
852 // assuming input is NHWC
853 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
854 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
855
856 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
857 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
858
859 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
860 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100861
862 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
863
864 BOOST_ASSERT(layer != nullptr);
865
jimfly01c25411c2018-11-14 17:47:22 +0000866 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
867 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100868
869 // register the input connection slots for the layer, connections are made after all layers have been created
870 // only the tensors for the inputs are relevant, exclude the const tensors
871 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000872 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100873
jimfly01c25411c2018-11-14 17:47:22 +0000874 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100875 // register the output connection slots for the layer, connections are made after all layers have been created
876 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
877 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
878}
879
telsoa01c577f2c2018-08-31 09:22:23 +0100880void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
881{
882 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
883 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
884 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
885
886 SoftmaxDescriptor desc;
887 desc.m_Beta = options->beta;
888
889 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
890 CHECK_VALID_SIZE(inputs.size(), 1);
891 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
892 CHECK_VALID_SIZE(outputs.size(), 1);
893
894 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
895 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
896
897 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
898 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
899
900 // register the input connection slots for the layer, connections are made after all layers have been created
901 // only the tensors for the inputs are relevant, exclude the const tensors
902 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
903 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
904
905 // register the output connection slots for the layer, connections are made after all layers have been created
906 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
907 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
908}
909
910armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
911 const armnn::TensorInfo & inputTensorInfo)
912{
913 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
914 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
915 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
916
917 if (inputTensorInfo.GetNumDimensions() > 4)
918 {
919 std::stringstream ss;
920 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
921 << " shape:" << inputTensorInfo.GetShape() << " "
922 << CHECK_LOCATION().AsString();
923 throw ParseException(ss.str());
924 }
925
926 if (squeezeDims.empty())
927 {
928 squeezeDims.assign(dimensionSequence,
929 dimensionSequence+inputTensorInfo.GetNumDimensions());
930 }
931
932 std::vector<uint32_t> outputDims;
933 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
934 {
935 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
936 auto currentDimension = inputTensorInfo.GetShape()[i];
937 if (skipSqueeze || currentDimension != 1)
938 {
939 outputDims.push_back(currentDimension);
940 }
941 }
942
943 if (outputDims.size() > 4)
944 {
945 std::stringstream ss;
946 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
947 << " shape:" << inputTensorInfo.GetShape() << " "
948 << CHECK_LOCATION().AsString();
949 throw ParseException(ss.str());
950 }
951
952 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
953 outputDims.data());
954
955 // we need to preserve the tensor type and the quantization data as well
956 TensorInfo outTensorInfo = inputTensorInfo;
957 outTensorInfo.SetShape(outShape);
958
959 return outTensorInfo;
960}
961
962void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
963{
964 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
965
966 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
967 CHECK_VALID_SIZE(inputs.size(), 1);
968
969 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
970 CHECK_VALID_SIZE(outputs.size(), 1);
971
972 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
973 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
974
975 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
976 armnn::TensorInfo outputTensorInfo =
977 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
978 inputTensorInfo);
979
980 ReshapeDescriptor reshapeDesc;
981 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
982
983 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
984 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
985 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
986
987 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
988 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
989
990 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
991 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
992}
993
Sadik Armagan58f39192018-09-17 14:14:39 +0100994void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
995{
996 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
997
998 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
999 boost::ignore_unused(operatorPtr);
1000
1001 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1002 CHECK_VALID_SIZE(inputs.size(), 1);
1003
1004 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1005 CHECK_VALID_SIZE(outputs.size(), 1);
1006
1007 auto layerName = str(boost::format("Activation:RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1008 ActivationDescriptor activationDesc;
1009 activationDesc.m_Function = ActivationFunction::ReLu;
1010 IConnectableLayer* const layer =
1011 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1012
1013 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1014 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1015
1016 // register the input connection slots for the layer, connections are made after all layers have been created
1017 // only the tensors for the inputs are relevant, exclude the const tensors
1018 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1019 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1020
1021 // register the output connection slots for the layer, connections are made after all layers have been created
1022 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1023 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1024}
1025
1026void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1027{
1028 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1029
1030 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1031 boost::ignore_unused(operatorPtr);
1032
1033 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1034 CHECK_VALID_SIZE(inputs.size(), 1);
1035
1036 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1037 CHECK_VALID_SIZE(outputs.size(), 1);
1038
1039 auto layerName = str(boost::format("Activation:RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1040 ActivationDescriptor activationDesc;
1041 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1042 activationDesc.m_A = 6.0f;
1043 activationDesc.m_B = 0.0f;
1044 IConnectableLayer* const layer =
1045 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1046
1047 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1048 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1049
1050 // register the input connection slots for the layer, connections are made after all layers have been created
1051 // only the tensors for the inputs are relevant, exclude the const tensors
1052 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1053 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1054
1055 // register the output connection slots for the layer, connections are made after all layers have been created
1056 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1057 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1058}
1059
Sadikb94967b2018-09-19 15:30:00 +01001060armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1061 const std::vector<int32_t> & targetDimsIn)
1062{
1063 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1064 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1065
1066 if (stretchDim != targetDimsIn.end())
1067 {
1068 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1069 {
1070 throw ParseException(
1071 boost::str(
1072 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1073 }
1074
1075 auto targetNumElements =
1076 boost::numeric_cast<unsigned int>(
1077 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1078
1079 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1080 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1081 }
1082
1083 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1084
1085 TensorInfo reshapeInfo = inputTensorInfo;
1086 reshapeInfo.SetShape(outputShape);
1087
1088 return reshapeInfo;
1089}
1090
1091void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1092{
1093 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1094
1095 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001096
1097 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1098 CHECK_VALID_SIZE(outputs.size(), 1);
1099
1100 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1101 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1102
1103 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001104 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1105 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001106 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1107
kevmay0171972a82018-12-17 14:28:03 +00001108 // Check for valid input size and that reshape parameters equal output shape
1109 if (inputs.size() > 1 && (options->new_shape != outputs[0]->shape))
1110 {
1111 std::stringstream ss;
1112 ss << "New shape defined in reshape parameters "
1113 << reshapeOutputTensorInfo.GetShape()
1114 << " does not equal output shape "
1115 << actualOutputTensorInfo.GetShape()
1116 << ": "
1117 << CHECK_LOCATION().AsString();
1118 throw ParseException(ss.str());
1119 }
1120
Sadikb94967b2018-09-19 15:30:00 +01001121 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001122 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001123
1124 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1125 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001126 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001127
1128 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1129 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1130
1131 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1132 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1133}
1134
Sadik Armagan479045b2018-10-01 11:51:37 +01001135void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1136{
1137 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1138
1139 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1140 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1141
1142 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1143
1144 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1145 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1146 CHECK_VALID_SIZE(outputs.size(), 1);
1147
1148 unsigned int numInputs = static_cast<unsigned int>(inputs.size());
1149 unsigned int numConcatView = numInputs;
1150
1151 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), MaxNumOfTensorDimensions);
1152 std::vector<unsigned int>mergeDimSizes(MaxNumOfTensorDimensions, 0u);
1153
1154 unsigned int mergeDim = 0;
1155
1156 // This concatDim indicates the data format: 3 is the NHWC, 1 is the NCHW.
1157 // axis could also be negative numbers. Negative axis are interpreted as counting from the end of the rank,
1158 // i.e., axis + rank(values)-th dimension.
1159 int32_t inputRank = static_cast<int32_t>(ToTensorInfo(inputs[0]).GetNumDimensions());
1160 const unsigned int concatDimInput = static_cast<unsigned int>((inputRank + options->axis) % inputRank);
1161
1162 // ArmNN supports concatenation along the channel dimension for data formats NHWC and NCHW.
1163 if (concatDimInput == 0 || concatDimInput == 2)
1164 {
1165 throw ParseException(
1166 boost::str(
1167 boost::format(
1168 "Dimension %1% for concatenation is not supported by Armnn. "
1169 "Node %2%")
1170 % concatDimInput
1171 % CHECK_LOCATION().AsString()));
1172 }
1173
1174 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1175 {
1176 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1177
1178 // process the input tensor info
1179 armnnUtils::ProcessConcatInputTensorInfo(inputTensorInfo, concatDescriptor,
1180 concatDimInput, viewIndex, mergeDimSizes, mergeDim);
1181 }
1182
1183 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1184 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1185
1186 BOOST_ASSERT(layer != nullptr);
1187
1188 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1189 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1190 if (concatDimInput == 3)
1191 {
1192 // Adding Fused Activation Layer after this moment....
1193 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1194 {
1195 // add permute layers to swizzle the inputs
1196 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1197 IConnectableLayer* const swizzleLayer = SwizzleIn(*m_Network, layer, viewIndex, inputTensorInfo);
1198
1199 BOOST_ASSERT(swizzleLayer != nullptr);
1200
1201 // register the input connection slots for the layer
1202 // only the tensors for the inputs are relevant, exclude the const tensors
1203 RegisterInputSlots(subgraphIndex, operatorIndex, swizzleLayer, {inputTensorIndexes[viewIndex]});
1204 }
1205
1206 // add permute layer to deswizzle the output
1207 IConnectableLayer* const deswizzleLayer = DeswizzleOut(*m_Network, layer, 0, outputTensorInfo);
1208
1209 // add fused activation layer after the trailing swizzle layer
1210 layer = AddFusedActivationLayer(deswizzleLayer, 0, options->fused_activation_function);
1211 }
1212 else
1213 {
1214 // set the layer output tensor info
1215 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1216
1217 // register the input connection slots for the layer, connections are made after all layers have been created
1218 // only the tensors for the inputs are relevant, exclude the const tensors
1219 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
1220 }
1221
1222 // register the output connection slots for the layer, connections are made after all layers have been created
1223 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1224 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1225}
1226
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001227void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1228{
1229 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1230
1231 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1232 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1233
1234 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1235
1236 FullyConnectedDescriptor desc;
1237 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001238 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001239
1240 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1241 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1242 CHECK_VALID_SIZE(outputs.size(), 1);
1243
1244 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1245
1246 // Fully Connected Layer accepts two dimensional weights input
1247 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1248 if (weightsDimension != 2)
1249 {
1250 throw ParseException(
1251 boost::str(
1252 boost::format(
1253 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1254 "Node %2%")
1255 % weightsDimension
1256 % CHECK_LOCATION().AsString()));
1257 }
1258
Matteo Martincigh747ef822018-12-18 09:26:39 +00001259 auto filterTensorAndData = CreateConstTensor(inputs[1],
1260 filterTensorInfo,
1261 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001262 armnn::IConnectableLayer* layer;
1263 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1264
1265 if (inputs.size() == 3)
1266 {
1267 desc.m_BiasEnabled = true;
1268 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001269 auto biasTensorAndData = CreateConstTensor(inputs[2],
1270 biasTensorInfo,
1271 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001272 layer = m_Network->AddFullyConnectedLayer(desc,
1273 filterTensorAndData.first,
1274 biasTensorAndData.first,
1275 layerName.c_str());
1276 }
1277 else
1278 {
1279 layer = m_Network->AddFullyConnectedLayer(desc,
1280 filterTensorAndData.first,
1281 layerName.c_str());
1282 }
1283 BOOST_ASSERT(layer != nullptr);
1284
1285 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1286 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1287
1288 // register the input connection slot for the layer
1289 // only the tensors for the inputs are relevant, exclude the const tensors
1290 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1291 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1292
1293 // we need to add the activation layer and fortunately we don't need to care about the data layout
1294 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1295 options->fused_activation_function);
1296 // register the output connection slots for the layer, connections are made after all layers have been created
1297 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1298 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1299}
1300
Sadik Armagan58f39192018-09-17 14:14:39 +01001301armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1302 unsigned int outputSlot,
1303 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001304{
1305 ActivationDescriptor activationDesc;
1306 std::string layerName = prevLayer->GetName();
1307
1308 switch(activationType)
1309 {
1310 case tflite::ActivationFunctionType_NONE:
1311 {
1312 // this is a no-op: return previous layer
1313 return prevLayer;
1314 }
1315 case tflite::ActivationFunctionType_RELU:
1316 {
1317 activationDesc.m_Function = ActivationFunction::ReLu;
1318 layerName += ":RELU";
1319 break;
1320 }
1321 case tflite::ActivationFunctionType_RELU6:
1322 {
1323 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1324 activationDesc.m_A = 6.0f;
1325 activationDesc.m_B = 0.0f;
1326 layerName += ":RELU6";
1327 break;
1328 }
1329 case tflite::ActivationFunctionType_TANH:
1330 {
1331 activationDesc.m_Function = ActivationFunction::TanH;
1332 activationDesc.m_A = 1.0f;
1333 activationDesc.m_B = 1.0f;
1334 layerName += ":TANH";
1335 break;
1336 }
1337
1338 // I only put these here as a reminder what others we could support
1339 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1340 case tflite::ActivationFunctionType_SIGN_BIT:
1341 default:
1342 {
1343 throw ParseException(
1344 boost::str(
1345 boost::format("TfLite parser doesn't suppport fused activation: "
1346 "%1%/%2% %3% ") %
1347 activationType %
1348 tflite::EnumNameActivationFunctionType(activationType) %
1349 CHECK_LOCATION().AsString()));
1350
1351 }
1352 }
1353
1354 IConnectableLayer* activationLayer =
1355 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1356
1357 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1358 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1359 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1360 return activationLayer;
1361}
1362
1363TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1364{
1365 if (fileName == nullptr)
1366 {
1367 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1368 CHECK_LOCATION().AsString()));
1369 }
1370 boost::system::error_code errorCode;
1371 boost::filesystem::path pathToFile(fileName);
1372 if (!boost::filesystem::exists(pathToFile, errorCode))
1373 {
1374 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1375 fileName %
1376 errorCode %
1377 CHECK_LOCATION().AsString()));
1378 }
1379 std::ifstream file(fileName, std::ios::binary);
1380 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1381 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1382 fileContent.size());
1383}
1384
1385TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1386{
1387 if (binaryContent == nullptr)
1388 {
1389 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1390 CHECK_LOCATION().AsString()));
1391 }
1392 flatbuffers::Verifier verifier(binaryContent, len);
1393 if (verifier.VerifyBuffer<tflite::Model>() == false)
1394 {
1395 throw ParseException(
1396 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1397 "flatbuffers format. size:%1% %2%") %
1398 len %
1399 CHECK_LOCATION().AsString()));
1400 }
1401 return tflite::UnPackModel(binaryContent);
1402}
1403
1404TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1405 size_t subgraphIndex,
1406 size_t operatorIndex)
1407{
1408 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1409
1410 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1411 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1412
1413 size_t inputCount = operatorPtr->inputs.size();
1414 TensorRawPtrVector result(inputCount);
1415 for (size_t i=0; i<inputCount; ++i)
1416 {
1417 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1418 result[i] = subGraphPtr->tensors[inputId].get();
1419 }
1420 return result;
1421}
1422
1423TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1424 size_t subgraphIndex,
1425 size_t operatorIndex)
1426{
1427 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1428
1429 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1430 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1431
1432 size_t outputCount = operatorPtr->outputs.size();
1433 TensorRawPtrVector result(outputCount);
1434 for (size_t i=0; i<outputCount; ++i)
1435 {
1436 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1437 CHECK_TENSOR(model, subgraphIndex, outputId);
1438 result[i] = subGraphPtr->tensors[outputId].get();
1439 }
1440 return result;
1441}
1442
1443TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1444 size_t subgraphIndex)
1445{
1446 CHECK_SUBGRAPH(model, subgraphIndex);
1447 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1448
1449 size_t inputCount = subGraphPtr->inputs.size();
1450 TensorIdRawPtrVector result(inputCount);
1451 for (size_t i=0; i<inputCount; ++i)
1452 {
1453 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1454 CHECK_TENSOR(model, subgraphIndex, inputId);
1455 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1456 }
1457 return result;
1458}
1459
1460TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1461 size_t subgraphIndex)
1462{
1463 CHECK_SUBGRAPH(model, subgraphIndex);
1464 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1465
1466 size_t outputCount = subGraphPtr->outputs.size();
1467 TensorIdRawPtrVector result(outputCount);
1468 for (size_t i=0; i<outputCount; ++i)
1469 {
1470 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1471 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1472 }
1473 return result;
1474}
1475
1476std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1477 size_t subgraphIndex,
1478 size_t operatorIndex)
1479{
1480 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1481 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1482 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1483 return operatorPtr->inputs;
1484}
1485
1486std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1487 size_t subgraphIndex,
1488 size_t operatorIndex)
1489{
1490 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1491 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1492 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1493 return operatorPtr->outputs;
1494}
1495
1496void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1497 size_t operatorIndex,
1498 IConnectableLayer* layer,
1499 const std::vector<unsigned int>& tensorIndexes)
1500{
1501 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1502 BOOST_ASSERT(layer != nullptr);
1503 if (tensorIndexes.size() != layer->GetNumInputSlots())
1504 {
1505 throw ParseException(
1506 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1507 " for subgraph:%3% operator index:%4% %5%") %
1508 tensorIndexes.size() %
1509 layer->GetNumInputSlots() %
1510 subgraphIndex %
1511 operatorIndex %
1512 CHECK_LOCATION().AsString()));
1513 }
1514
1515 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1516 {
1517 unsigned int tensorIndex = tensorIndexes[slotIndex];
1518 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1519 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1520 }
1521}
1522
1523void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1524 size_t operatorIndex,
1525 IConnectableLayer* layer,
1526 const std::vector<unsigned int>& tensorIndexes)
1527{
1528 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1529 BOOST_ASSERT(layer != nullptr);
1530 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1531 {
1532 throw ParseException(
1533 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1534 " for subgraph:%3% operator index:%4% %5%") %
1535 tensorIndexes.size() %
1536 layer->GetNumOutputSlots() %
1537 subgraphIndex %
1538 operatorIndex %
1539 CHECK_LOCATION().AsString()));
1540 }
1541
1542 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1543 {
1544 unsigned int tensorIndex = tensorIndexes[slotIndex];
1545 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1546 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1547 }
1548}
1549
1550void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1551{
1552 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1553
1554 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1555 for (auto const & tensorIdAndPtr : inputs)
1556 {
1557 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1558 IConnectableLayer* layer =
1559 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1560
1561 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1562 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1563
1564 RegisterOutputSlots(subgraphIndex,
1565 VIRTUAL_OPERATOR_ID,
1566 layer,
1567 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1568 }
1569}
1570
1571void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1572{
1573 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1574
1575 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1576 for (auto const & tensorIdAndPtr : outputs)
1577 {
1578 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1579 IConnectableLayer* layer =
1580 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1581
1582 RegisterInputSlots(subgraphIndex,
1583 VIRTUAL_OPERATOR_ID,
1584 layer,
1585 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1586 }
1587}
1588
1589// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1590TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1591{
1592 CHECK_BUFFER(model, bufferIndex);
1593 return model->buffers[bufferIndex].get();
1594}
1595
Matteo Martincigh747ef822018-12-18 09:26:39 +00001596template<typename T>
1597std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1598TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
1599 TfLiteParser::TensorRawPtr tensorPtr,
1600 armnn::TensorInfo& tensorInfo,
1601 armnn::Optional<armnn::PermutationVector&> permutationVector)
1602{
1603 auto constData = CreateConstTensorImpl<T>(bufferPtr,
1604 tensorPtr,
1605 tensorInfo,
1606 permutationVector);
1607 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
1608 return std::make_pair(constData.first, std::move(storage));
1609}
1610
telsoa01c577f2c2018-08-31 09:22:23 +01001611std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1612TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00001613 armnn::TensorInfo& tensorInfo,
1614 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01001615{
1616 CHECK_TENSOR_PTR(tensorPtr);
1617 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1618 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1619
1620 switch (tensorInfo.GetDataType())
1621 {
1622 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001623 return CreateConstTensorAndStoreData<float>(bufferPtr,
1624 tensorPtr,
1625 tensorInfo,
1626 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001627 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001628 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
1629 tensorPtr,
1630 tensorInfo,
1631 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001632 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001633 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
1634 tensorPtr,
1635 tensorInfo,
1636 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001637 default:
1638 {
1639 std::stringstream errString;
1640 errString << "Unexpected datatype when creating const tensor: "
1641 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1642 << " shape:" << tensorInfo.GetShape()
1643 << CHECK_LOCATION().AsString();
1644 throw ParseException(errString.str());
1645 }
1646 }
1647}
1648
1649BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1650 const std::string& name) const
1651{
1652 CHECK_SUBGRAPH(m_Model, subgraphId);
1653 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1654 for (auto const & input : inputs)
1655 {
1656 if (input.second->name == name)
1657 {
1658 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1659 return std::make_pair(bindingId, ToTensorInfo(input.second));
1660 }
1661 }
1662
1663 std::stringstream bindings;
1664 for (auto const & input : inputs)
1665 {
1666 bindings << "'" << input.second->name << "' ";
1667 }
1668
1669 throw ParseException(
1670 boost::str(
1671 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1672 "Possible inputs are: [%3%] %4%") %
1673 subgraphId %
1674 name %
1675 bindings.str() %
1676 CHECK_LOCATION().AsString()));
1677}
1678
1679BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1680 const std::string& name) const
1681{
1682 CHECK_SUBGRAPH(m_Model, subgraphId);
1683 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1684 for (auto const & output : outputs)
1685 {
1686 if (output.second->name == name)
1687 {
1688 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1689 return std::make_pair(bindingId, ToTensorInfo(output.second));
1690 }
1691 }
1692
1693 std::stringstream bindings;
1694 for (auto const & output : outputs)
1695 {
1696 bindings << "'" << output.second->name << "' ";
1697 }
1698
1699 throw ParseException(
1700 boost::str(
1701 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1702 "Possible outputs are: [%3%] %4%") %
1703 subgraphId %
1704 name %
1705 bindings.str() %
1706 CHECK_LOCATION().AsString()));
1707}
1708
1709size_t TfLiteParser::GetSubgraphCount() const
1710{
1711 return m_Model->subgraphs.size();
1712}
1713
1714std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1715{
1716 CHECK_SUBGRAPH(m_Model, subgraphId);
1717 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1718 std::vector<std::string> result;
1719 result.reserve(inputs.size());
1720 for (auto const & input : inputs)
1721 {
1722 result.push_back(input.second->name);
1723 }
1724 return result;
1725}
1726
1727std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1728{
1729 CHECK_SUBGRAPH(m_Model, subgraphId);
1730 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1731 std::vector<std::string> result;
1732 result.reserve(outputs.size());
1733 for (auto const & output : outputs)
1734 {
1735 result.push_back(output.second->name);
1736 }
1737 return result;
1738}
1739
1740ITfLiteParser* ITfLiteParser::CreateRaw()
1741{
1742 return new TfLiteParser();
1743}
1744
1745ITfLiteParserPtr ITfLiteParser::Create()
1746{
1747 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1748}
1749
1750void ITfLiteParser::Destroy(ITfLiteParser* parser)
1751{
1752 delete parser;
1753}
1754
1755TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1756: m_FloatData(std::move(data))
1757, m_Uint8Data(nullptr)
1758, m_Int32Data(nullptr)
1759{
1760}
1761
1762TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1763: m_FloatData(nullptr)
1764, m_Uint8Data(std::move(data))
1765, m_Int32Data(nullptr)
1766{
1767}
1768
1769TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1770: m_FloatData(nullptr)
1771, m_Uint8Data(nullptr)
1772, m_Int32Data(std::move(data))
1773{
1774}
1775
1776} // armnnTfLiteParser