blob: d5c48a10e28ad304acad1de224c3540aea7be173 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// See LICENSE file in the project root for full license information.
4//
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:
13#include <Permute.hpp>
14#include <VerificationHelpers.hpp>
15
16// The generated code based on the Tf Lite schema:
17#include <schema_generated.h>
18
19#include <boost/core/ignore_unused.hpp>
20#include <boost/assert.hpp>
21#include <boost/format.hpp>
22#include <boost/log/trivial.hpp>
23
24#include <fstream>
25#include <algorithm>
26#include <limits>
27
28using namespace armnn;
29using armnn::CheckLocation;
30namespace armnnTfLiteParser
31{
32namespace
33{
34const PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
35const PermutationVector ArmNNToNHWC = { 0, 3, 1, 2 };
36
37const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
38
39void CheckSubgraph(const TfLiteParser::ModelPtr & model,
40 size_t subgraphIndex,
41 const CheckLocation & location)
42{
43 if (model.get() == nullptr)
44 {
45 throw ParseException(
46 boost::str(
47 boost::format("%1% was called with invalid (null) model. "
48 "Possible reason is that the model is not yet loaded and Unpack(ed). "
49 "subgraph:%2% at %3%") %
50 location.m_Function %
51 subgraphIndex %
52 location.FileLine()));
53 }
54 else if (subgraphIndex >= model->subgraphs.size())
55 {
56 throw ParseException(
57 boost::str(
58 boost::format("%1% was called with an invalid subgraph index. "
59 "subgraph:%2% at %3%") %
60 location.m_Function %
61 subgraphIndex %
62 location.FileLine()));
63 }
64}
65
66#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
67 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
68
69void CheckModel(const TfLiteParser::ModelPtr & model,
70 size_t subgraphIndex,
71 size_t operatorIndex,
72 const CheckLocation & location)
73{
74 if (model.get() == nullptr)
75 {
76 throw ParseException(
77 boost::str(
78 boost::format("%1% was called with invalid (null) model. "
79 "Possible reason is that the model is not yet loaded and Unpack(ed). "
80 "subgraph:%2% operator:%3% at %4%") %
81 location.m_Function %
82 subgraphIndex %
83 operatorIndex %
84 location.FileLine()));
85 }
86 else if (subgraphIndex >= model->subgraphs.size())
87 {
88 throw ParseException(
89 boost::str(
90 boost::format("%1% was called with an invalid subgraph index. "
91 "subgraph:%2% operator:%3% at %4%") %
92 location.m_Function %
93 subgraphIndex %
94 operatorIndex %
95 location.FileLine()));
96 }
97 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
98 operatorIndex != VIRTUAL_OPERATOR_ID)
99 {
100 throw ParseException(
101 boost::str(
102 boost::format("%1% was called with an invalid operator index. "
103 "subgraph:%2% operator:%3% at %4%") %
104 location.m_Function %
105 subgraphIndex %
106 operatorIndex %
107 location.FileLine()));
108 }
109}
110
111#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
112 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
113
114void CheckTensor(const TfLiteParser::ModelPtr & model,
115 size_t subgraphIndex,
116 size_t tensorIndex,
117 const CheckLocation & location)
118{
119 // not checking model, because I assume CHECK_MODEL already run
120 // and checked that. An assert would do.
121 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
122
123 // also subgraph index should be checked by CHECK_MODEL so
124 // I only add an assert here
125 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
126
127 // the tensor index is the only one to check here
128 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
129 {
130 throw ParseException(
131 boost::str(
132 boost::format("%1% was called with an invalid tensor index. "
133 "subgraph:%2% tensor:%3% at %4%") %
134 location.m_Function %
135 subgraphIndex %
136 tensorIndex %
137 location.FileLine()));
138 }
139}
140
141#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
142 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
143
144void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
145 const CheckLocation & location)
146{
147 if (rawPtr == nullptr)
148 {
149 throw ParseException(
150 boost::str(
151 boost::format("%1% was called with a null tensor pointer. "
152 "at %2%") %
153 location.m_Function %
154 location.FileLine()));
155
156 }
157}
158
159#define CHECK_TENSOR_PTR(TENSOR_PTR) \
160 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
161
162void CheckBuffer(const TfLiteParser::ModelPtr & model,
163 size_t bufferIndex,
164 const CheckLocation & location)
165{
166 if (model.get() == nullptr)
167 {
168 throw ParseException(
169 boost::str(
170 boost::format("%1% was called with invalid (null) model. "
171 "Possible reason is that the model is not yet loaded and Unpack(ed). "
172 "buffer:%2% at %3%") %
173 location.m_Function %
174 bufferIndex %
175 location.FileLine()));
176 }
177 else if (bufferIndex >= model->buffers.size())
178 {
179 throw ParseException(
180 boost::str(
181 boost::format("%1% was called with an invalid buffer index. "
182 "buffer index:%2% at %3%") %
183 location.m_Function %
184 bufferIndex %
185 location.FileLine()));
186 }
187 else if (model->buffers[bufferIndex].get() == nullptr)
188 {
189 throw ParseException(
190 boost::str(
191 boost::format("The buffer #%1% is null. %3%") %
192 bufferIndex %
193 location.AsString()));
194 }
195}
196
197#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
198 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
199
200void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
201 const armnn::TensorInfo & tensorInfo,
202 uint32_t bufferId,
203 const CheckLocation & location)
204{
205 if (bufferPtr == nullptr)
206 {
207 throw ParseException(
208 boost::str(
209 boost::format("BufferPtr is null for buffer:%1%. %2%") %
210 bufferId %
211 location.AsString()));
212 }
213 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
214 tensorInfo.GetNumBytes() > bufferPtr->data.size())
215 {
216 std::stringstream ss;
217 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
218 << "For tensor: " << tensorInfo.GetShape()
219 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
220 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
221 throw ParseException(ss.str());
222 }
223}
224
225#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
226 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
227
228bool IsActivationSupported(tflite::ActivationFunctionType activationType)
229{
230 switch(activationType)
231 {
232 case tflite::ActivationFunctionType_NONE:
233 case tflite::ActivationFunctionType_RELU:
234 case tflite::ActivationFunctionType_RELU6:
235 case tflite::ActivationFunctionType_TANH:
236 {
237 return true;
238 }
239 default:
240 {
241 return false;
242 }
243 }
244}
245
246#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
247 do { \
248 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
249 { \
250 throw ParseException( \
251 boost::str( \
252 boost::format("TfLite parser doesn't suppport fused activation: " \
253 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
254 OPTION->fused_activation_function % \
255 tflite::EnumNameActivationFunctionType(\
256 OPTION->fused_activation_function) % \
257 __func__ % \
258 SUBGRAPH_INDEX % \
259 OPERATOR_INDEX % \
260 CHECK_LOCATION().FileLine())); \
261 } \
262 } while(false)
263
264
265std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
266{
267 std::vector<unsigned int> result;
268 result.reserve(in.size());
269 for (auto & i : in)
270 {
271 result.push_back(CHECKED_NON_NEGATIVE(i));
272 }
273 return result;
274}
275
276void CalcPadding(uint32_t inputSize,
277 uint32_t filterSize,
278 uint32_t stride,
279 uint32_t& paddingFront,
280 uint32_t& paddingBack,
281 tflite::Padding padding)
282{
283 paddingFront = 0;
284 paddingBack = 0;
285 if (padding == tflite::Padding_SAME)
286 {
287 uint32_t outputSize = (inputSize + stride - 1) / stride;
288 uint32_t temp = (outputSize - 1) * stride + filterSize;
289 if (temp > inputSize)
290 {
291 paddingFront = (temp - inputSize) / 2;
292 paddingBack = (temp - inputSize) - paddingFront;
293 }
294 }
295}
296
297armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
298{
299 armnn::DataType type;
300 CHECK_TENSOR_PTR(tensorPtr);
301
302 switch (tensorPtr->type)
303 {
304 case tflite::TensorType_UINT8:
305 type = armnn::DataType::QuantisedAsymm8;
306 break;
307 case tflite::TensorType_FLOAT32:
308 type = armnn::DataType::Float32;
309 break;
310 case tflite::TensorType_INT32:
311 type = armnn::DataType::Signed32;
312 break;
313
314 default:
315 {
316 CheckLocation location = CHECK_LOCATION();
317 throw ParseException(
318 boost::str(
319 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
320 tensorPtr->type %
321 tflite::EnumNameTensorType(tensorPtr->type) %
322 tensorPtr->name %
323 location.AsString()));
324 }
325 }
326
327 float quantizationScale = 0.0f;
328 int32_t quantizationOffset = 0;
329
330 if (tensorPtr->quantization.get())
331 {
332 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
333 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
334
335 if (tensorPtr->quantization->scale.size() == 1)
336 {
337 quantizationScale = tensorPtr->quantization->scale[0];
338 }
339 if (tensorPtr->quantization->zero_point.size() == 1)
340 {
341 // NOTE: we lose precision here when converting from 64 bit to 32
342 // but this is what we support at the monent in ArmNN
343 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
344 }
345 }
346
347 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
348
349 // two statements (on purpose) for easier debugging:
350 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
351 dimensions.data(),
352 type,
353 quantizationScale,
354 quantizationOffset);
355 return result;
356}
357
358template<typename T>
359std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
360CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
361 TfLiteParser::TensorRawPtr tensorPtr,
362 armnn::TensorInfo & tensorInfo,
363 bool convertFromTfToArmnnFormat)
364{
365 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
366 BOOST_ASSERT_MSG(bufferPtr != nullptr,
367 boost::str(
368 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
369
370 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
371
372 if (convertFromTfToArmnnFormat)
373 {
374 tensorInfo = armnnUtils::Permuted(tensorInfo, NHWCToArmNN);
375 armnnUtils::Permute(tensorInfo.GetShape(),
376 NHWCToArmNN,
377 reinterpret_cast<const T *>(bufferPtr->data.data()),
378 data.get());
379 }
380 else
381 {
382 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
383 }
384 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
385}
386
387IConnectableLayer* SwizzleIn(INetwork& network,
388 IConnectableLayer* layer,
389 unsigned int inputSlotIndex,
390 const TensorInfo & inputInfo)
391{
392 BOOST_ASSERT(layer != nullptr);
393 // Add swizzle layer
394 std::stringstream name;
395 name << "swizzle_for-" << layer->GetName() << ":in" << inputSlotIndex;
396 IConnectableLayer* const swizzleLayer = network.AddPermuteLayer(NHWCToArmNN, name.str().c_str());
397 // Set swizzled output shape
398 const TensorInfo swizzleOutInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
399 swizzleLayer->GetOutputSlot(0).SetTensorInfo(swizzleOutInfo);
400 // Connect the swizzle layer to the actual layer
401 swizzleLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(inputSlotIndex));
402
403 return swizzleLayer;
404}
405
406IConnectableLayer* DeswizzleOut(INetwork& network,
407 IConnectableLayer* layer,
408 unsigned int outputSlotIndex,
409 const TensorInfo & outputInfo)
410{
411 BOOST_ASSERT(layer != nullptr);
412 // Add deswizzle layer
413 std::stringstream name;
414 name << "deswizzle_for-" << layer->GetName() << ":out" << outputSlotIndex;
415 IConnectableLayer* const deswizzleLayer = network.AddPermuteLayer(ArmNNToNHWC, name.str().c_str());
416 // Set deswizzled output shape
417 deswizzleLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
418 // Set original layer output shape
419 const TensorInfo deswizzleOutInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
420 layer->GetOutputSlot(outputSlotIndex).SetTensorInfo(deswizzleOutInfo);
421 // Connect the actual layer to the deswizzle layer
422 layer->GetOutputSlot(outputSlotIndex).Connect(deswizzleLayer->GetInputSlot(0));
423
424 return deswizzleLayer;
425}
426
427std::pair<IConnectableLayer*, IConnectableLayer*> SwizzleInDeswizzleOut(INetwork& network,
428 IConnectableLayer* layer,
429 unsigned int inputSlotIndex,
430 const TensorInfo & inputInfo,
431 unsigned int outputSlotIndex,
432 const TensorInfo & outputInfo)
433{
434 IConnectableLayer* const swizzleLayer = SwizzleIn(network, layer, inputSlotIndex, inputInfo);
435 IConnectableLayer* const deswizzleLayer = DeswizzleOut(network, layer, outputSlotIndex, outputInfo);
436 return std::make_pair(swizzleLayer, deswizzleLayer);
437}
438
439armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
440{
441 // generate the binding id by shifting the tensor id by 8 bit
442 // and add the subgraph id, which allows 256 subgraphs
443 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
444}
445
446} // <anonymous>
447
448TfLiteParser::TfLiteParser()
449: m_Network(nullptr, nullptr)
450, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
451{
452 // register supported operators
453 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
454 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
455 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
456 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
457 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
458}
459
460void TfLiteParser::ResetParser()
461{
462 m_Network = armnn::INetworkPtr(nullptr, nullptr);
463 m_Model = nullptr;
464 m_SubgraphConnections.clear();
465}
466
467INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
468{
469 ResetParser();
470 m_Model = LoadModelFromFile(graphFile);
471 return CreateNetworkFromModel();
472}
473
474INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
475{
476 ResetParser();
477 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
478 return CreateNetworkFromModel();
479}
480
481INetworkPtr TfLiteParser::CreateNetworkFromModel()
482{
483 m_Network = INetwork::Create();
484 BOOST_ASSERT(m_Model.get() != nullptr);
485
486 bool failedToCreate = false;
487 std::stringstream errors;
488
489 if (m_Model->subgraphs.size() != 1)
490 {
491 throw ParseException(
492 boost::str(
493 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
494 m_Model->subgraphs.size() %
495 CHECK_LOCATION().AsString()));
496 }
497
498 size_t subgraphIndex = 0;
499 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
500 {
501 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
502
503 size_t operatorIndex = 0;
504 for (OperatorPtr const & op : subgraph->operators)
505 {
506 try
507 {
508 if (op->custom_options.size() > 0)
509 {
510 throw ParseException(
511 boost::str(
512 boost::format("Custom options for op: %1% is not supported. "
513 "It has %2% bytes of custom options. %3%") %
514 op->opcode_index %
515 op->custom_options.size() %
516 CHECK_LOCATION().AsString()));
517 }
518
519 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
520 auto builtinCode = opCodePtr->builtin_code;
521
522 if (builtinCode > tflite::BuiltinOperator_MAX)
523 {
524 throw ParseException(
525 boost::str(
526 boost::format("Operator code %1% is out of range 0-%2%. "
527 "subgraph:%3% operator idx:%4%. %5%") %
528 builtinCode %
529 tflite::BuiltinOperator_MAX %
530 subgraphIndex %
531 operatorIndex %
532 CHECK_LOCATION().AsString()));
533 }
534
535 // lookup and call the parser function
536 auto & parserFunction = m_ParserFunctions[builtinCode];
537 (this->*parserFunction)(subgraphIndex, operatorIndex);
538 }
539 catch (const ParseException& e)
540 {
541 failedToCreate = true;
542 std::stringstream errorString;
543
544 errorString << "Failed to parse operator #" << operatorIndex
545 << " within subgraph #" << subgraphIndex
546 << " error: " << e.what();
547 BOOST_LOG_TRIVIAL(error) << errorString.str();
548
549 errors << errorString.str() << "\n";
550 }
551 ++operatorIndex;
552 }
553
554 SetupInputLayers(subgraphIndex);
555 SetupOutputLayers(subgraphIndex);
556
557 ++subgraphIndex;
558 }
559
560 if (failedToCreate)
561 {
562 // we can skip everything and let the outer exception handler deal with the error
563 throw ParseException(errors.str());
564 }
565
566 // establish the connections from the layer outputs to the inputs of the subsequent layers
567 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
568 {
569 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
570 {
571 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
572 {
573 for (size_t inputSlotIdx = 0;
574 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
575 ++inputSlotIdx)
576 {
577 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
578 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
579 }
580 }
581 }
582 }
583
584 return std::move(m_Network);
585}
586
587void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
588 size_t tensorIndex,
589 armnn::IOutputSlot* slot)
590{
591 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
592 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
593 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
594
595 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
596
597 // assuming there is only one producer for that tensor
598 if (tensorSlots.outputSlot != nullptr)
599 {
600 throw ParseException(boost::str(
601 boost::format("Another layer has already registered itself as the producer of "
602 "subgraph:%1% tensor:%2% %3%") %
603 subgraphIndex %
604 tensorIndex %
605 CHECK_LOCATION().AsString()));
606 }
607
608 tensorSlots.outputSlot = slot;
609}
610
611void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
612 size_t tensorIndex,
613 armnn::IInputSlot* slot)
614{
615 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
616 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
617 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
618
619 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
620 tensorSlots.inputSlots.push_back(slot);
621}
622
623void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
624{
625 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
626 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
627 //
628 auto opcodeIndex = operatorPtr->opcode_index;
629 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
630
631 throw ParseException(
632 boost::str(
633 boost::format("Operator not supported. "
634 "subgraph:%1% operator:%2% "
635 "opcode_index:%3% opcode:%4% / %5% %6%") %
636 subgraphIndex %
637 operatorIndex %
638 opcodeIndex %
639 opcode %
640 tflite::EnumNameBuiltinOperator(opcode) %
641 CHECK_LOCATION().AsString()));
642}
643
644void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
645{
646 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
647
648 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
649 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
650
651 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
652
653 Pooling2dDescriptor desc;
654
655 desc.m_PoolType = PoolingAlgorithm::Average;
656 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
657 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
658 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
659 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
660 desc.m_PaddingMethod = PaddingMethod::Exclude;
661 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
662
663 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
664 CHECK_VALID_SIZE(inputs.size(), 1);
665 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
666
667 // assuming input is NHWC
668 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
669 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
670
671 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
672 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
673
674 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
675 CHECK_VALID_SIZE(outputs.size(), 1);
676 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
677
678 auto layerName = boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
679 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
680
681 BOOST_ASSERT(layer != nullptr);
682
683 // add permute layers to swizzle the input and deswizzle the output
684 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
685 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
686
687 // register the input connection slots for the layer, connections are made after all layers have been created
688 // only the tensors for the inputs are relevant, exclude the const tensors
689 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
690 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
691
692 // we need to add the activation layer and fortunately we don't need to care about the data layout
693 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
694 // swizzle layer
695 layer = AddActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
696 // register the output connection slots for the layer, connections are made after all layers have been created
697 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
698 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
699}
700
701void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
702{
703 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
704
705 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
706 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
707
708 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
709
710 Convolution2dDescriptor desc;
711 desc.m_BiasEnabled = false;
712 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
713 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
714
715 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
716 CHECK_VALID_SIZE(inputs.size(), 2, 3);
717
718 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
719 CHECK_VALID_SIZE(outputs.size(), 1);
720
721 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
722 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
723
724 // assuming input is NHWC
725 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
726 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
727
728 // assuming the filter is OHWI : Output, H, W, Input
729 // which is essentially the same as NHWC
730 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
731 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
732
733 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
734 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
735
736 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, true);
737 armnn::IConnectableLayer* layer;
738
739 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
740
741 if (inputs.size() == 3)
742 {
743 desc.m_BiasEnabled = true;
744 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
745 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo, false);
746 layer = m_Network->AddConvolution2dLayer(desc,
747 filterTensorAndData.first,
748 biasTensorAndData.first,
749 layerName.c_str());
750 }
751 else
752 {
753 layer = m_Network->AddConvolution2dLayer(desc,
754 filterTensorAndData.first,
755 layerName.c_str());
756 }
757
758 BOOST_ASSERT(layer != nullptr);
759
760 // add permute layers to swizzle the input and deswizzle the output
761 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
762 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
763 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
764
765 // register the input connection slots for the layer, connections are made after all layers have been created
766 // only the tensors for the inputs are relevant, exclude the const tensors
767 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
768 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
769
770 // we need to add the activation layer and fortunately we don't need to care about the data layout
771 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
772 // swizzle layer
773 layer = AddActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
774 // register the output connection slots for the layer, connections are made after all layers have been created
775 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
776 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
777}
778
779void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
780{
781 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
782
783 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
784 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
785
786 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
787
788 DepthwiseConvolution2dDescriptor desc;
789 desc.m_BiasEnabled = false;
790 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
791 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
792 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
793 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
794
795 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
796 CHECK_VALID_SIZE(inputs.size(), 2, 3);
797 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
798 CHECK_VALID_SIZE(outputs.size(), 1);
799
800 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
801 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
802
803 // assuming input is NHWC
804 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
805 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
806 // assuming the filter is OHWI : Output, H, W, Input
807 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
808 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
809
810 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
811 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
812
813 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, true);
814 armnn::IConnectableLayer* layer;
815 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
816
817 if (inputs.size() == 3)
818 {
819 desc.m_BiasEnabled = true;
820 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
821 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo, false);
822 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
823 filterTensorAndData.first,
824 biasTensorAndData.first,
825 layerName.c_str());
826 }
827 else
828 {
829 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
830 filterTensorAndData.first,
831 layerName.c_str());
832 }
833 BOOST_ASSERT(layer != nullptr);
834
835 // add permute layers to swizzle the input and deswizzle the output
836 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
837 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
838 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
839
840 // register the input connection slots for the layer, connections are made after all layers have been created
841 // only the tensors for the inputs are relevant, exclude the const tensors
842 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
843 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
844
845 // we need to add the activation layer and fortunately we don't need to care about the data layout
846 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
847 // swizzle layer
848 layer = AddActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
849 // register the output connection slots for the layer, connections are made after all layers have been created
850 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
851 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
852}
853
854void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
855{
856 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
857 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
858 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
859
860 SoftmaxDescriptor desc;
861 desc.m_Beta = options->beta;
862
863 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
864 CHECK_VALID_SIZE(inputs.size(), 1);
865 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
866 CHECK_VALID_SIZE(outputs.size(), 1);
867
868 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
869 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
870
871 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
872 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
873
874 // register the input connection slots for the layer, connections are made after all layers have been created
875 // only the tensors for the inputs are relevant, exclude the const tensors
876 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
877 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
878
879 // register the output connection slots for the layer, connections are made after all layers have been created
880 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
881 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
882}
883
884armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
885 const armnn::TensorInfo & inputTensorInfo)
886{
887 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
888 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
889 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
890
891 if (inputTensorInfo.GetNumDimensions() > 4)
892 {
893 std::stringstream ss;
894 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
895 << " shape:" << inputTensorInfo.GetShape() << " "
896 << CHECK_LOCATION().AsString();
897 throw ParseException(ss.str());
898 }
899
900 if (squeezeDims.empty())
901 {
902 squeezeDims.assign(dimensionSequence,
903 dimensionSequence+inputTensorInfo.GetNumDimensions());
904 }
905
906 std::vector<uint32_t> outputDims;
907 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
908 {
909 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
910 auto currentDimension = inputTensorInfo.GetShape()[i];
911 if (skipSqueeze || currentDimension != 1)
912 {
913 outputDims.push_back(currentDimension);
914 }
915 }
916
917 if (outputDims.size() > 4)
918 {
919 std::stringstream ss;
920 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
921 << " shape:" << inputTensorInfo.GetShape() << " "
922 << CHECK_LOCATION().AsString();
923 throw ParseException(ss.str());
924 }
925
926 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
927 outputDims.data());
928
929 // we need to preserve the tensor type and the quantization data as well
930 TensorInfo outTensorInfo = inputTensorInfo;
931 outTensorInfo.SetShape(outShape);
932
933 return outTensorInfo;
934}
935
936void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
937{
938 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
939
940 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
941 CHECK_VALID_SIZE(inputs.size(), 1);
942
943 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
944 CHECK_VALID_SIZE(outputs.size(), 1);
945
946 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
947 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
948
949 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
950 armnn::TensorInfo outputTensorInfo =
951 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
952 inputTensorInfo);
953
954 ReshapeDescriptor reshapeDesc;
955 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
956
957 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
958 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
959 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
960
961 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
962 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
963
964 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
965 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
966}
967
968armnn::IConnectableLayer* TfLiteParser::AddActivationLayer(armnn::IConnectableLayer* prevLayer,
969 unsigned int outputSlot,
970 tflite::ActivationFunctionType activationType)
971{
972 ActivationDescriptor activationDesc;
973 std::string layerName = prevLayer->GetName();
974
975 switch(activationType)
976 {
977 case tflite::ActivationFunctionType_NONE:
978 {
979 // this is a no-op: return previous layer
980 return prevLayer;
981 }
982 case tflite::ActivationFunctionType_RELU:
983 {
984 activationDesc.m_Function = ActivationFunction::ReLu;
985 layerName += ":RELU";
986 break;
987 }
988 case tflite::ActivationFunctionType_RELU6:
989 {
990 activationDesc.m_Function = ActivationFunction::BoundedReLu;
991 activationDesc.m_A = 6.0f;
992 activationDesc.m_B = 0.0f;
993 layerName += ":RELU6";
994 break;
995 }
996 case tflite::ActivationFunctionType_TANH:
997 {
998 activationDesc.m_Function = ActivationFunction::TanH;
999 activationDesc.m_A = 1.0f;
1000 activationDesc.m_B = 1.0f;
1001 layerName += ":TANH";
1002 break;
1003 }
1004
1005 // I only put these here as a reminder what others we could support
1006 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1007 case tflite::ActivationFunctionType_SIGN_BIT:
1008 default:
1009 {
1010 throw ParseException(
1011 boost::str(
1012 boost::format("TfLite parser doesn't suppport fused activation: "
1013 "%1%/%2% %3% ") %
1014 activationType %
1015 tflite::EnumNameActivationFunctionType(activationType) %
1016 CHECK_LOCATION().AsString()));
1017
1018 }
1019 }
1020
1021 IConnectableLayer* activationLayer =
1022 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1023
1024 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1025 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1026 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1027 return activationLayer;
1028}
1029
1030TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1031{
1032 if (fileName == nullptr)
1033 {
1034 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1035 CHECK_LOCATION().AsString()));
1036 }
1037 boost::system::error_code errorCode;
1038 boost::filesystem::path pathToFile(fileName);
1039 if (!boost::filesystem::exists(pathToFile, errorCode))
1040 {
1041 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1042 fileName %
1043 errorCode %
1044 CHECK_LOCATION().AsString()));
1045 }
1046 std::ifstream file(fileName, std::ios::binary);
1047 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1048 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1049 fileContent.size());
1050}
1051
1052TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1053{
1054 if (binaryContent == nullptr)
1055 {
1056 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1057 CHECK_LOCATION().AsString()));
1058 }
1059 flatbuffers::Verifier verifier(binaryContent, len);
1060 if (verifier.VerifyBuffer<tflite::Model>() == false)
1061 {
1062 throw ParseException(
1063 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1064 "flatbuffers format. size:%1% %2%") %
1065 len %
1066 CHECK_LOCATION().AsString()));
1067 }
1068 return tflite::UnPackModel(binaryContent);
1069}
1070
1071TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1072 size_t subgraphIndex,
1073 size_t operatorIndex)
1074{
1075 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1076
1077 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1078 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1079
1080 size_t inputCount = operatorPtr->inputs.size();
1081 TensorRawPtrVector result(inputCount);
1082 for (size_t i=0; i<inputCount; ++i)
1083 {
1084 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1085 result[i] = subGraphPtr->tensors[inputId].get();
1086 }
1087 return result;
1088}
1089
1090TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1091 size_t subgraphIndex,
1092 size_t operatorIndex)
1093{
1094 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1095
1096 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1097 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1098
1099 size_t outputCount = operatorPtr->outputs.size();
1100 TensorRawPtrVector result(outputCount);
1101 for (size_t i=0; i<outputCount; ++i)
1102 {
1103 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1104 CHECK_TENSOR(model, subgraphIndex, outputId);
1105 result[i] = subGraphPtr->tensors[outputId].get();
1106 }
1107 return result;
1108}
1109
1110TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1111 size_t subgraphIndex)
1112{
1113 CHECK_SUBGRAPH(model, subgraphIndex);
1114 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1115
1116 size_t inputCount = subGraphPtr->inputs.size();
1117 TensorIdRawPtrVector result(inputCount);
1118 for (size_t i=0; i<inputCount; ++i)
1119 {
1120 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1121 CHECK_TENSOR(model, subgraphIndex, inputId);
1122 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1123 }
1124 return result;
1125}
1126
1127TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1128 size_t subgraphIndex)
1129{
1130 CHECK_SUBGRAPH(model, subgraphIndex);
1131 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1132
1133 size_t outputCount = subGraphPtr->outputs.size();
1134 TensorIdRawPtrVector result(outputCount);
1135 for (size_t i=0; i<outputCount; ++i)
1136 {
1137 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1138 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1139 }
1140 return result;
1141}
1142
1143std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1144 size_t subgraphIndex,
1145 size_t operatorIndex)
1146{
1147 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1148 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1149 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1150 return operatorPtr->inputs;
1151}
1152
1153std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1154 size_t subgraphIndex,
1155 size_t operatorIndex)
1156{
1157 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1158 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1159 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1160 return operatorPtr->outputs;
1161}
1162
1163void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1164 size_t operatorIndex,
1165 IConnectableLayer* layer,
1166 const std::vector<unsigned int>& tensorIndexes)
1167{
1168 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1169 BOOST_ASSERT(layer != nullptr);
1170 if (tensorIndexes.size() != layer->GetNumInputSlots())
1171 {
1172 throw ParseException(
1173 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1174 " for subgraph:%3% operator index:%4% %5%") %
1175 tensorIndexes.size() %
1176 layer->GetNumInputSlots() %
1177 subgraphIndex %
1178 operatorIndex %
1179 CHECK_LOCATION().AsString()));
1180 }
1181
1182 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1183 {
1184 unsigned int tensorIndex = tensorIndexes[slotIndex];
1185 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1186 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1187 }
1188}
1189
1190void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1191 size_t operatorIndex,
1192 IConnectableLayer* layer,
1193 const std::vector<unsigned int>& tensorIndexes)
1194{
1195 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1196 BOOST_ASSERT(layer != nullptr);
1197 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1198 {
1199 throw ParseException(
1200 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1201 " for subgraph:%3% operator index:%4% %5%") %
1202 tensorIndexes.size() %
1203 layer->GetNumOutputSlots() %
1204 subgraphIndex %
1205 operatorIndex %
1206 CHECK_LOCATION().AsString()));
1207 }
1208
1209 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1210 {
1211 unsigned int tensorIndex = tensorIndexes[slotIndex];
1212 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1213 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1214 }
1215}
1216
1217void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1218{
1219 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1220
1221 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1222 for (auto const & tensorIdAndPtr : inputs)
1223 {
1224 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1225 IConnectableLayer* layer =
1226 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1227
1228 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1229 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1230
1231 RegisterOutputSlots(subgraphIndex,
1232 VIRTUAL_OPERATOR_ID,
1233 layer,
1234 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1235 }
1236}
1237
1238void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1239{
1240 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1241
1242 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1243 for (auto const & tensorIdAndPtr : outputs)
1244 {
1245 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1246 IConnectableLayer* layer =
1247 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1248
1249 RegisterInputSlots(subgraphIndex,
1250 VIRTUAL_OPERATOR_ID,
1251 layer,
1252 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1253 }
1254}
1255
1256// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1257TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1258{
1259 CHECK_BUFFER(model, bufferIndex);
1260 return model->buffers[bufferIndex].get();
1261}
1262
1263std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1264TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
1265 armnn::TensorInfo & tensorInfo,
1266 bool convertFromTfToArmnnFormat)
1267{
1268 CHECK_TENSOR_PTR(tensorPtr);
1269 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1270 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1271
1272 switch (tensorInfo.GetDataType())
1273 {
1274 case armnn::DataType::Float32:
1275 {
1276 auto constData = CreateConstTensorImpl<float>(bufferPtr,
1277 tensorPtr,
1278 tensorInfo,
1279 convertFromTfToArmnnFormat);
1280 SupportedDataStorage storage(std::move(constData.second));
1281 return std::make_pair(constData.first, std::move(storage));
1282 }
1283 case armnn::DataType::QuantisedAsymm8:
1284 {
1285 auto constData = CreateConstTensorImpl<uint8_t>(bufferPtr,
1286 tensorPtr,
1287 tensorInfo,
1288 convertFromTfToArmnnFormat);
1289 SupportedDataStorage storage(std::move(constData.second));
1290 return std::make_pair(constData.first, std::move(storage));
1291 }
1292 case armnn::DataType::Signed32:
1293 {
1294 auto constData = CreateConstTensorImpl<int32_t>(bufferPtr,
1295 tensorPtr,
1296 tensorInfo,
1297 convertFromTfToArmnnFormat);
1298 SupportedDataStorage storage(std::move(constData.second));
1299 return std::make_pair(constData.first, std::move(storage));
1300 }
1301 default:
1302 {
1303 std::stringstream errString;
1304 errString << "Unexpected datatype when creating const tensor: "
1305 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1306 << " shape:" << tensorInfo.GetShape()
1307 << CHECK_LOCATION().AsString();
1308 throw ParseException(errString.str());
1309 }
1310 }
1311}
1312
1313BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1314 const std::string& name) const
1315{
1316 CHECK_SUBGRAPH(m_Model, subgraphId);
1317 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1318 for (auto const & input : inputs)
1319 {
1320 if (input.second->name == name)
1321 {
1322 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1323 return std::make_pair(bindingId, ToTensorInfo(input.second));
1324 }
1325 }
1326
1327 std::stringstream bindings;
1328 for (auto const & input : inputs)
1329 {
1330 bindings << "'" << input.second->name << "' ";
1331 }
1332
1333 throw ParseException(
1334 boost::str(
1335 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1336 "Possible inputs are: [%3%] %4%") %
1337 subgraphId %
1338 name %
1339 bindings.str() %
1340 CHECK_LOCATION().AsString()));
1341}
1342
1343BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1344 const std::string& name) const
1345{
1346 CHECK_SUBGRAPH(m_Model, subgraphId);
1347 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1348 for (auto const & output : outputs)
1349 {
1350 if (output.second->name == name)
1351 {
1352 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1353 return std::make_pair(bindingId, ToTensorInfo(output.second));
1354 }
1355 }
1356
1357 std::stringstream bindings;
1358 for (auto const & output : outputs)
1359 {
1360 bindings << "'" << output.second->name << "' ";
1361 }
1362
1363 throw ParseException(
1364 boost::str(
1365 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1366 "Possible outputs are: [%3%] %4%") %
1367 subgraphId %
1368 name %
1369 bindings.str() %
1370 CHECK_LOCATION().AsString()));
1371}
1372
1373size_t TfLiteParser::GetSubgraphCount() const
1374{
1375 return m_Model->subgraphs.size();
1376}
1377
1378std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1379{
1380 CHECK_SUBGRAPH(m_Model, subgraphId);
1381 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1382 std::vector<std::string> result;
1383 result.reserve(inputs.size());
1384 for (auto const & input : inputs)
1385 {
1386 result.push_back(input.second->name);
1387 }
1388 return result;
1389}
1390
1391std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1392{
1393 CHECK_SUBGRAPH(m_Model, subgraphId);
1394 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1395 std::vector<std::string> result;
1396 result.reserve(outputs.size());
1397 for (auto const & output : outputs)
1398 {
1399 result.push_back(output.second->name);
1400 }
1401 return result;
1402}
1403
1404ITfLiteParser* ITfLiteParser::CreateRaw()
1405{
1406 return new TfLiteParser();
1407}
1408
1409ITfLiteParserPtr ITfLiteParser::Create()
1410{
1411 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1412}
1413
1414void ITfLiteParser::Destroy(ITfLiteParser* parser)
1415{
1416 delete parser;
1417}
1418
1419TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1420: m_FloatData(std::move(data))
1421, m_Uint8Data(nullptr)
1422, m_Int32Data(nullptr)
1423{
1424}
1425
1426TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1427: m_FloatData(nullptr)
1428, m_Uint8Data(std::move(data))
1429, m_Int32Data(nullptr)
1430{
1431}
1432
1433TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1434: m_FloatData(nullptr)
1435, m_Uint8Data(nullptr)
1436, m_Int32Data(std::move(data))
1437{
1438}
1439
1440} // armnnTfLiteParser