blob: 89c72c52e0fdaaf677e197ec78bb3d52cad9ca76 [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,
jimfly01c25411c2018-11-14 17:47:22 +0000404 armnn::TensorInfo & tensorInfo)
telsoa01c577f2c2018-08-31 09:22:23 +0100405{
406 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
407 BOOST_ASSERT_MSG(bufferPtr != nullptr,
408 boost::str(
409 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
410
411 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
jimfly01c25411c2018-11-14 17:47:22 +0000412 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
telsoa01c577f2c2018-08-31 09:22:23 +0100413 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
414}
415
telsoa01c577f2c2018-08-31 09:22:23 +0100416armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
417{
418 // generate the binding id by shifting the tensor id by 8 bit
419 // and add the subgraph id, which allows 256 subgraphs
420 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
421}
422
423} // <anonymous>
424
425TfLiteParser::TfLiteParser()
426: m_Network(nullptr, nullptr)
427, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
428{
429 // register supported operators
430 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Sadik Armagan479045b2018-10-01 11:51:37 +0100431 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100432 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
433 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100434 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100435 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Sadik Armagan58f39192018-09-17 14:14:39 +0100436 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
437 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100438 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Sadik Armagan479045b2018-10-01 11:51:37 +0100439 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
440 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
telsoa01c577f2c2018-08-31 09:22:23 +0100441}
442
443void TfLiteParser::ResetParser()
444{
445 m_Network = armnn::INetworkPtr(nullptr, nullptr);
446 m_Model = nullptr;
447 m_SubgraphConnections.clear();
448}
449
450INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
451{
452 ResetParser();
453 m_Model = LoadModelFromFile(graphFile);
454 return CreateNetworkFromModel();
455}
456
457INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
458{
459 ResetParser();
460 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
461 return CreateNetworkFromModel();
462}
463
464INetworkPtr TfLiteParser::CreateNetworkFromModel()
465{
466 m_Network = INetwork::Create();
467 BOOST_ASSERT(m_Model.get() != nullptr);
468
469 bool failedToCreate = false;
470 std::stringstream errors;
471
472 if (m_Model->subgraphs.size() != 1)
473 {
474 throw ParseException(
475 boost::str(
476 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
477 m_Model->subgraphs.size() %
478 CHECK_LOCATION().AsString()));
479 }
480
481 size_t subgraphIndex = 0;
482 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
483 {
484 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
485
486 size_t operatorIndex = 0;
487 for (OperatorPtr const & op : subgraph->operators)
488 {
489 try
490 {
491 if (op->custom_options.size() > 0)
492 {
493 throw ParseException(
494 boost::str(
495 boost::format("Custom options for op: %1% is not supported. "
496 "It has %2% bytes of custom options. %3%") %
497 op->opcode_index %
498 op->custom_options.size() %
499 CHECK_LOCATION().AsString()));
500 }
501
502 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
503 auto builtinCode = opCodePtr->builtin_code;
504
505 if (builtinCode > tflite::BuiltinOperator_MAX)
506 {
507 throw ParseException(
508 boost::str(
509 boost::format("Operator code %1% is out of range 0-%2%. "
510 "subgraph:%3% operator idx:%4%. %5%") %
511 builtinCode %
512 tflite::BuiltinOperator_MAX %
513 subgraphIndex %
514 operatorIndex %
515 CHECK_LOCATION().AsString()));
516 }
517
518 // lookup and call the parser function
519 auto & parserFunction = m_ParserFunctions[builtinCode];
520 (this->*parserFunction)(subgraphIndex, operatorIndex);
521 }
522 catch (const ParseException& e)
523 {
524 failedToCreate = true;
525 std::stringstream errorString;
526
527 errorString << "Failed to parse operator #" << operatorIndex
528 << " within subgraph #" << subgraphIndex
529 << " error: " << e.what();
530 BOOST_LOG_TRIVIAL(error) << errorString.str();
531
532 errors << errorString.str() << "\n";
533 }
534 ++operatorIndex;
535 }
536
537 SetupInputLayers(subgraphIndex);
538 SetupOutputLayers(subgraphIndex);
539
540 ++subgraphIndex;
541 }
542
543 if (failedToCreate)
544 {
545 // we can skip everything and let the outer exception handler deal with the error
546 throw ParseException(errors.str());
547 }
548
549 // establish the connections from the layer outputs to the inputs of the subsequent layers
550 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
551 {
552 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
553 {
554 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
555 {
556 for (size_t inputSlotIdx = 0;
557 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
558 ++inputSlotIdx)
559 {
560 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
561 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
562 }
563 }
564 }
565 }
566
567 return std::move(m_Network);
568}
569
570void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
571 size_t tensorIndex,
572 armnn::IOutputSlot* slot)
573{
574 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
575 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
576 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
577
578 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
579
580 // assuming there is only one producer for that tensor
581 if (tensorSlots.outputSlot != nullptr)
582 {
583 throw ParseException(boost::str(
584 boost::format("Another layer has already registered itself as the producer of "
585 "subgraph:%1% tensor:%2% %3%") %
586 subgraphIndex %
587 tensorIndex %
588 CHECK_LOCATION().AsString()));
589 }
590
591 tensorSlots.outputSlot = slot;
592}
593
594void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
595 size_t tensorIndex,
596 armnn::IInputSlot* slot)
597{
598 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
599 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
600 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
601
602 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
603 tensorSlots.inputSlots.push_back(slot);
604}
605
606void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
607{
608 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
609 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
610 //
611 auto opcodeIndex = operatorPtr->opcode_index;
612 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
613
614 throw ParseException(
615 boost::str(
616 boost::format("Operator not supported. "
617 "subgraph:%1% operator:%2% "
618 "opcode_index:%3% opcode:%4% / %5% %6%") %
619 subgraphIndex %
620 operatorIndex %
621 opcodeIndex %
622 opcode %
623 tflite::EnumNameBuiltinOperator(opcode) %
624 CHECK_LOCATION().AsString()));
625}
626
telsoa01c577f2c2018-08-31 09:22:23 +0100627void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
628{
629 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
630
631 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
632 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
633
634 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
635
636 Convolution2dDescriptor desc;
637 desc.m_BiasEnabled = false;
638 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
639 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000640 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100641
642 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
643 CHECK_VALID_SIZE(inputs.size(), 2, 3);
644
645 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
646 CHECK_VALID_SIZE(outputs.size(), 1);
647
648 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
649 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
650
651 // assuming input is NHWC
652 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
653 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
654
655 // assuming the filter is OHWI : Output, H, W, Input
656 // which is essentially the same as NHWC
657 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
658 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
659
660 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
661 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
662
jimfly01c25411c2018-11-14 17:47:22 +0000663 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100664 armnn::IConnectableLayer* layer;
665
666 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
667
668 if (inputs.size() == 3)
669 {
670 desc.m_BiasEnabled = true;
671 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
jimfly01c25411c2018-11-14 17:47:22 +0000672 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100673 layer = m_Network->AddConvolution2dLayer(desc,
674 filterTensorAndData.first,
675 biasTensorAndData.first,
676 layerName.c_str());
677 }
678 else
679 {
680 layer = m_Network->AddConvolution2dLayer(desc,
681 filterTensorAndData.first,
682 layerName.c_str());
683 }
684
685 BOOST_ASSERT(layer != nullptr);
686
telsoa01c577f2c2018-08-31 09:22:23 +0100687 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000688 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100689
690 // register the input connection slots for the layer, connections are made after all layers have been created
691 // only the tensors for the inputs are relevant, exclude the const tensors
692 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000693 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100694
jimfly01c25411c2018-11-14 17:47:22 +0000695 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100696 // 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::ParseDepthwiseConv2D(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.AsDepthwiseConv2DOptions();
707
708 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
709
710 DepthwiseConvolution2dDescriptor 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);
jimfly01c25411c2018-11-14 17:47:22 +0000714 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100715 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
716 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
717
718 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
719 CHECK_VALID_SIZE(inputs.size(), 2, 3);
720 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
721 CHECK_VALID_SIZE(outputs.size(), 1);
722
723 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
724 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
725
726 // assuming input is NHWC
727 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
728 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
729 // assuming the filter is OHWI : Output, H, W, Input
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
jimfly01c25411c2018-11-14 17:47:22 +0000736 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100737 armnn::IConnectableLayer* layer;
738 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
739
740 if (inputs.size() == 3)
741 {
742 desc.m_BiasEnabled = true;
743 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
jimfly01c25411c2018-11-14 17:47:22 +0000744 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100745 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
746 filterTensorAndData.first,
747 biasTensorAndData.first,
748 layerName.c_str());
749 }
750 else
751 {
752 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
753 filterTensorAndData.first,
754 layerName.c_str());
755 }
756 BOOST_ASSERT(layer != nullptr);
757
telsoa01c577f2c2018-08-31 09:22:23 +0100758 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000759 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100760
761 // register the input connection slots for the layer, connections are made after all layers have been created
762 // only the tensors for the inputs are relevant, exclude the const tensors
763 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000764 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100765
jimfly01c25411c2018-11-14 17:47:22 +0000766 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100767 // register the output connection slots for the layer, connections are made after all layers have been created
768 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
769 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
770}
771
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100772void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
773{
774 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
775}
776
777void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
778{
779 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
780}
781
782void TfLiteParser::ParsePool(size_t subgraphIndex,
783 size_t operatorIndex,
784 PoolingAlgorithm algorithm)
785{
786 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
787
788 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
789 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
790
791 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
792
793 std::string layerName;
794
795 switch (algorithm)
796 {
797 case PoolingAlgorithm::Average:
798 layerName =
799 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
800 break;
801 case PoolingAlgorithm::Max:
802 layerName =
803 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
804 break;
805 default:
806 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
807 }
808
809 Pooling2dDescriptor desc;
810
811 desc.m_PoolType = algorithm;
812 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
813 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
814 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
815 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
816 desc.m_PaddingMethod = PaddingMethod::Exclude;
817 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000818 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100819
820 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
821 CHECK_VALID_SIZE(inputs.size(), 1);
822 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
823
824 // assuming input is NHWC
825 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
826 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
827
828 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
829 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
830
831 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
832 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100833
834 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
835
836 BOOST_ASSERT(layer != nullptr);
837
jimfly01c25411c2018-11-14 17:47:22 +0000838 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
839 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100840
841 // register the input connection slots for the layer, connections are made after all layers have been created
842 // only the tensors for the inputs are relevant, exclude the const tensors
843 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000844 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100845
jimfly01c25411c2018-11-14 17:47:22 +0000846 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100847 // register the output connection slots for the layer, connections are made after all layers have been created
848 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
849 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
850}
851
telsoa01c577f2c2018-08-31 09:22:23 +0100852void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
853{
854 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
855 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
856 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
857
858 SoftmaxDescriptor desc;
859 desc.m_Beta = options->beta;
860
861 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
862 CHECK_VALID_SIZE(inputs.size(), 1);
863 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
864 CHECK_VALID_SIZE(outputs.size(), 1);
865
866 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
867 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
868
869 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
870 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
871
872 // register the input connection slots for the layer, connections are made after all layers have been created
873 // only the tensors for the inputs are relevant, exclude the const tensors
874 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
875 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
876
877 // register the output connection slots for the layer, connections are made after all layers have been created
878 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
879 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
880}
881
882armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
883 const armnn::TensorInfo & inputTensorInfo)
884{
885 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
886 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
887 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
888
889 if (inputTensorInfo.GetNumDimensions() > 4)
890 {
891 std::stringstream ss;
892 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
893 << " shape:" << inputTensorInfo.GetShape() << " "
894 << CHECK_LOCATION().AsString();
895 throw ParseException(ss.str());
896 }
897
898 if (squeezeDims.empty())
899 {
900 squeezeDims.assign(dimensionSequence,
901 dimensionSequence+inputTensorInfo.GetNumDimensions());
902 }
903
904 std::vector<uint32_t> outputDims;
905 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
906 {
907 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
908 auto currentDimension = inputTensorInfo.GetShape()[i];
909 if (skipSqueeze || currentDimension != 1)
910 {
911 outputDims.push_back(currentDimension);
912 }
913 }
914
915 if (outputDims.size() > 4)
916 {
917 std::stringstream ss;
918 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
919 << " shape:" << inputTensorInfo.GetShape() << " "
920 << CHECK_LOCATION().AsString();
921 throw ParseException(ss.str());
922 }
923
924 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
925 outputDims.data());
926
927 // we need to preserve the tensor type and the quantization data as well
928 TensorInfo outTensorInfo = inputTensorInfo;
929 outTensorInfo.SetShape(outShape);
930
931 return outTensorInfo;
932}
933
934void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
935{
936 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
937
938 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
939 CHECK_VALID_SIZE(inputs.size(), 1);
940
941 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
942 CHECK_VALID_SIZE(outputs.size(), 1);
943
944 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
945 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
946
947 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
948 armnn::TensorInfo outputTensorInfo =
949 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
950 inputTensorInfo);
951
952 ReshapeDescriptor reshapeDesc;
953 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
954
955 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
956 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
957 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
958
959 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
960 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
961
962 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
963 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
964}
965
Sadik Armagan58f39192018-09-17 14:14:39 +0100966void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
967{
968 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
969
970 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
971 boost::ignore_unused(operatorPtr);
972
973 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
974 CHECK_VALID_SIZE(inputs.size(), 1);
975
976 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
977 CHECK_VALID_SIZE(outputs.size(), 1);
978
979 auto layerName = str(boost::format("Activation:RELU:%1%:%2%") % subgraphIndex % operatorIndex);
980 ActivationDescriptor activationDesc;
981 activationDesc.m_Function = ActivationFunction::ReLu;
982 IConnectableLayer* const layer =
983 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
984
985 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
986 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
987
988 // register the input connection slots for the layer, connections are made after all layers have been created
989 // only the tensors for the inputs are relevant, exclude the const tensors
990 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
991 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
992
993 // register the output connection slots for the layer, connections are made after all layers have been created
994 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
995 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
996}
997
998void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
999{
1000 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1001
1002 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1003 boost::ignore_unused(operatorPtr);
1004
1005 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1006 CHECK_VALID_SIZE(inputs.size(), 1);
1007
1008 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1009 CHECK_VALID_SIZE(outputs.size(), 1);
1010
1011 auto layerName = str(boost::format("Activation:RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1012 ActivationDescriptor activationDesc;
1013 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1014 activationDesc.m_A = 6.0f;
1015 activationDesc.m_B = 0.0f;
1016 IConnectableLayer* const layer =
1017 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1018
1019 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1020 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1021
1022 // register the input connection slots for the layer, connections are made after all layers have been created
1023 // only the tensors for the inputs are relevant, exclude the const tensors
1024 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1025 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1026
1027 // register the output connection slots for the layer, connections are made after all layers have been created
1028 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1029 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1030}
1031
Sadikb94967b2018-09-19 15:30:00 +01001032armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1033 const std::vector<int32_t> & targetDimsIn)
1034{
1035 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1036 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1037
1038 if (stretchDim != targetDimsIn.end())
1039 {
1040 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1041 {
1042 throw ParseException(
1043 boost::str(
1044 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1045 }
1046
1047 auto targetNumElements =
1048 boost::numeric_cast<unsigned int>(
1049 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1050
1051 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1052 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1053 }
1054
1055 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1056
1057 TensorInfo reshapeInfo = inputTensorInfo;
1058 reshapeInfo.SetShape(outputShape);
1059
1060 return reshapeInfo;
1061}
1062
1063void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1064{
1065 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1066
1067 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1068 CHECK_VALID_SIZE(inputs.size(), 1);
1069
1070 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1071 CHECK_VALID_SIZE(outputs.size(), 1);
1072
1073 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1074 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1075
1076 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1077 armnn::TensorInfo outputTensorInfo =
1078 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1079
1080 ReshapeDescriptor reshapeDesc;
1081 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1082
1083 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1084 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1085 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1086
1087 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1088 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1089
1090 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1091 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1092}
1093
Sadik Armagan479045b2018-10-01 11:51:37 +01001094void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1095{
1096 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1097
1098 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1099 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1100
1101 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1102
1103 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1104 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1105 CHECK_VALID_SIZE(outputs.size(), 1);
1106
1107 unsigned int numInputs = static_cast<unsigned int>(inputs.size());
1108 unsigned int numConcatView = numInputs;
1109
1110 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), MaxNumOfTensorDimensions);
1111 std::vector<unsigned int>mergeDimSizes(MaxNumOfTensorDimensions, 0u);
1112
1113 unsigned int mergeDim = 0;
1114
1115 // This concatDim indicates the data format: 3 is the NHWC, 1 is the NCHW.
1116 // axis could also be negative numbers. Negative axis are interpreted as counting from the end of the rank,
1117 // i.e., axis + rank(values)-th dimension.
1118 int32_t inputRank = static_cast<int32_t>(ToTensorInfo(inputs[0]).GetNumDimensions());
1119 const unsigned int concatDimInput = static_cast<unsigned int>((inputRank + options->axis) % inputRank);
1120
1121 // ArmNN supports concatenation along the channel dimension for data formats NHWC and NCHW.
1122 if (concatDimInput == 0 || concatDimInput == 2)
1123 {
1124 throw ParseException(
1125 boost::str(
1126 boost::format(
1127 "Dimension %1% for concatenation is not supported by Armnn. "
1128 "Node %2%")
1129 % concatDimInput
1130 % CHECK_LOCATION().AsString()));
1131 }
1132
1133 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1134 {
1135 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1136
1137 // process the input tensor info
1138 armnnUtils::ProcessConcatInputTensorInfo(inputTensorInfo, concatDescriptor,
1139 concatDimInput, viewIndex, mergeDimSizes, mergeDim);
1140 }
1141
1142 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1143 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1144
1145 BOOST_ASSERT(layer != nullptr);
1146
1147 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1148 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1149 if (concatDimInput == 3)
1150 {
1151 // Adding Fused Activation Layer after this moment....
1152 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1153 {
1154 // add permute layers to swizzle the inputs
1155 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1156 IConnectableLayer* const swizzleLayer = SwizzleIn(*m_Network, layer, viewIndex, inputTensorInfo);
1157
1158 BOOST_ASSERT(swizzleLayer != nullptr);
1159
1160 // register the input connection slots for the layer
1161 // only the tensors for the inputs are relevant, exclude the const tensors
1162 RegisterInputSlots(subgraphIndex, operatorIndex, swizzleLayer, {inputTensorIndexes[viewIndex]});
1163 }
1164
1165 // add permute layer to deswizzle the output
1166 IConnectableLayer* const deswizzleLayer = DeswizzleOut(*m_Network, layer, 0, outputTensorInfo);
1167
1168 // add fused activation layer after the trailing swizzle layer
1169 layer = AddFusedActivationLayer(deswizzleLayer, 0, options->fused_activation_function);
1170 }
1171 else
1172 {
1173 // set the layer output tensor info
1174 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1175
1176 // register the input connection slots for the layer, connections are made after all layers have been created
1177 // only the tensors for the inputs are relevant, exclude the const tensors
1178 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
1179 }
1180
1181 // register the output connection slots for the layer, connections are made after all layers have been created
1182 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1183 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1184}
1185
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001186void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1187{
1188 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1189
1190 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1191 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1192
1193 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1194
1195 FullyConnectedDescriptor desc;
1196 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001197 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001198
1199 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1200 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1201 CHECK_VALID_SIZE(outputs.size(), 1);
1202
1203 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1204
1205 // Fully Connected Layer accepts two dimensional weights input
1206 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1207 if (weightsDimension != 2)
1208 {
1209 throw ParseException(
1210 boost::str(
1211 boost::format(
1212 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1213 "Node %2%")
1214 % weightsDimension
1215 % CHECK_LOCATION().AsString()));
1216 }
1217
jimfly01c25411c2018-11-14 17:47:22 +00001218 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo);
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001219 armnn::IConnectableLayer* layer;
1220 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1221
1222 if (inputs.size() == 3)
1223 {
1224 desc.m_BiasEnabled = true;
1225 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
jimfly01c25411c2018-11-14 17:47:22 +00001226 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo);
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001227 layer = m_Network->AddFullyConnectedLayer(desc,
1228 filterTensorAndData.first,
1229 biasTensorAndData.first,
1230 layerName.c_str());
1231 }
1232 else
1233 {
1234 layer = m_Network->AddFullyConnectedLayer(desc,
1235 filterTensorAndData.first,
1236 layerName.c_str());
1237 }
1238 BOOST_ASSERT(layer != nullptr);
1239
1240 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1241 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1242
1243 // register the input connection slot for the layer
1244 // only the tensors for the inputs are relevant, exclude the const tensors
1245 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1246 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1247
1248 // we need to add the activation layer and fortunately we don't need to care about the data layout
1249 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1250 options->fused_activation_function);
1251 // register the output connection slots for the layer, connections are made after all layers have been created
1252 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1253 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1254}
1255
Sadik Armagan58f39192018-09-17 14:14:39 +01001256armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1257 unsigned int outputSlot,
1258 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001259{
1260 ActivationDescriptor activationDesc;
1261 std::string layerName = prevLayer->GetName();
1262
1263 switch(activationType)
1264 {
1265 case tflite::ActivationFunctionType_NONE:
1266 {
1267 // this is a no-op: return previous layer
1268 return prevLayer;
1269 }
1270 case tflite::ActivationFunctionType_RELU:
1271 {
1272 activationDesc.m_Function = ActivationFunction::ReLu;
1273 layerName += ":RELU";
1274 break;
1275 }
1276 case tflite::ActivationFunctionType_RELU6:
1277 {
1278 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1279 activationDesc.m_A = 6.0f;
1280 activationDesc.m_B = 0.0f;
1281 layerName += ":RELU6";
1282 break;
1283 }
1284 case tflite::ActivationFunctionType_TANH:
1285 {
1286 activationDesc.m_Function = ActivationFunction::TanH;
1287 activationDesc.m_A = 1.0f;
1288 activationDesc.m_B = 1.0f;
1289 layerName += ":TANH";
1290 break;
1291 }
1292
1293 // I only put these here as a reminder what others we could support
1294 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1295 case tflite::ActivationFunctionType_SIGN_BIT:
1296 default:
1297 {
1298 throw ParseException(
1299 boost::str(
1300 boost::format("TfLite parser doesn't suppport fused activation: "
1301 "%1%/%2% %3% ") %
1302 activationType %
1303 tflite::EnumNameActivationFunctionType(activationType) %
1304 CHECK_LOCATION().AsString()));
1305
1306 }
1307 }
1308
1309 IConnectableLayer* activationLayer =
1310 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1311
1312 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1313 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1314 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1315 return activationLayer;
1316}
1317
1318TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1319{
1320 if (fileName == nullptr)
1321 {
1322 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1323 CHECK_LOCATION().AsString()));
1324 }
1325 boost::system::error_code errorCode;
1326 boost::filesystem::path pathToFile(fileName);
1327 if (!boost::filesystem::exists(pathToFile, errorCode))
1328 {
1329 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1330 fileName %
1331 errorCode %
1332 CHECK_LOCATION().AsString()));
1333 }
1334 std::ifstream file(fileName, std::ios::binary);
1335 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1336 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1337 fileContent.size());
1338}
1339
1340TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1341{
1342 if (binaryContent == nullptr)
1343 {
1344 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1345 CHECK_LOCATION().AsString()));
1346 }
1347 flatbuffers::Verifier verifier(binaryContent, len);
1348 if (verifier.VerifyBuffer<tflite::Model>() == false)
1349 {
1350 throw ParseException(
1351 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1352 "flatbuffers format. size:%1% %2%") %
1353 len %
1354 CHECK_LOCATION().AsString()));
1355 }
1356 return tflite::UnPackModel(binaryContent);
1357}
1358
1359TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1360 size_t subgraphIndex,
1361 size_t operatorIndex)
1362{
1363 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1364
1365 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1366 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1367
1368 size_t inputCount = operatorPtr->inputs.size();
1369 TensorRawPtrVector result(inputCount);
1370 for (size_t i=0; i<inputCount; ++i)
1371 {
1372 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1373 result[i] = subGraphPtr->tensors[inputId].get();
1374 }
1375 return result;
1376}
1377
1378TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1379 size_t subgraphIndex,
1380 size_t operatorIndex)
1381{
1382 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1383
1384 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1385 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1386
1387 size_t outputCount = operatorPtr->outputs.size();
1388 TensorRawPtrVector result(outputCount);
1389 for (size_t i=0; i<outputCount; ++i)
1390 {
1391 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1392 CHECK_TENSOR(model, subgraphIndex, outputId);
1393 result[i] = subGraphPtr->tensors[outputId].get();
1394 }
1395 return result;
1396}
1397
1398TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1399 size_t subgraphIndex)
1400{
1401 CHECK_SUBGRAPH(model, subgraphIndex);
1402 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1403
1404 size_t inputCount = subGraphPtr->inputs.size();
1405 TensorIdRawPtrVector result(inputCount);
1406 for (size_t i=0; i<inputCount; ++i)
1407 {
1408 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1409 CHECK_TENSOR(model, subgraphIndex, inputId);
1410 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1411 }
1412 return result;
1413}
1414
1415TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1416 size_t subgraphIndex)
1417{
1418 CHECK_SUBGRAPH(model, subgraphIndex);
1419 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1420
1421 size_t outputCount = subGraphPtr->outputs.size();
1422 TensorIdRawPtrVector result(outputCount);
1423 for (size_t i=0; i<outputCount; ++i)
1424 {
1425 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1426 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1427 }
1428 return result;
1429}
1430
1431std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1432 size_t subgraphIndex,
1433 size_t operatorIndex)
1434{
1435 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1436 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1437 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1438 return operatorPtr->inputs;
1439}
1440
1441std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1442 size_t subgraphIndex,
1443 size_t operatorIndex)
1444{
1445 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1446 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1447 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1448 return operatorPtr->outputs;
1449}
1450
1451void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1452 size_t operatorIndex,
1453 IConnectableLayer* layer,
1454 const std::vector<unsigned int>& tensorIndexes)
1455{
1456 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1457 BOOST_ASSERT(layer != nullptr);
1458 if (tensorIndexes.size() != layer->GetNumInputSlots())
1459 {
1460 throw ParseException(
1461 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1462 " for subgraph:%3% operator index:%4% %5%") %
1463 tensorIndexes.size() %
1464 layer->GetNumInputSlots() %
1465 subgraphIndex %
1466 operatorIndex %
1467 CHECK_LOCATION().AsString()));
1468 }
1469
1470 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1471 {
1472 unsigned int tensorIndex = tensorIndexes[slotIndex];
1473 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1474 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1475 }
1476}
1477
1478void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1479 size_t operatorIndex,
1480 IConnectableLayer* layer,
1481 const std::vector<unsigned int>& tensorIndexes)
1482{
1483 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1484 BOOST_ASSERT(layer != nullptr);
1485 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1486 {
1487 throw ParseException(
1488 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1489 " for subgraph:%3% operator index:%4% %5%") %
1490 tensorIndexes.size() %
1491 layer->GetNumOutputSlots() %
1492 subgraphIndex %
1493 operatorIndex %
1494 CHECK_LOCATION().AsString()));
1495 }
1496
1497 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1498 {
1499 unsigned int tensorIndex = tensorIndexes[slotIndex];
1500 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1501 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1502 }
1503}
1504
1505void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1506{
1507 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1508
1509 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1510 for (auto const & tensorIdAndPtr : inputs)
1511 {
1512 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1513 IConnectableLayer* layer =
1514 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1515
1516 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1517 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1518
1519 RegisterOutputSlots(subgraphIndex,
1520 VIRTUAL_OPERATOR_ID,
1521 layer,
1522 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1523 }
1524}
1525
1526void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1527{
1528 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1529
1530 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1531 for (auto const & tensorIdAndPtr : outputs)
1532 {
1533 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1534 IConnectableLayer* layer =
1535 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1536
1537 RegisterInputSlots(subgraphIndex,
1538 VIRTUAL_OPERATOR_ID,
1539 layer,
1540 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1541 }
1542}
1543
1544// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1545TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1546{
1547 CHECK_BUFFER(model, bufferIndex);
1548 return model->buffers[bufferIndex].get();
1549}
1550
1551std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1552TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
jimfly01c25411c2018-11-14 17:47:22 +00001553 armnn::TensorInfo & tensorInfo)
telsoa01c577f2c2018-08-31 09:22:23 +01001554{
1555 CHECK_TENSOR_PTR(tensorPtr);
1556 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1557 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1558
1559 switch (tensorInfo.GetDataType())
1560 {
1561 case armnn::DataType::Float32:
1562 {
1563 auto constData = CreateConstTensorImpl<float>(bufferPtr,
1564 tensorPtr,
jimfly01c25411c2018-11-14 17:47:22 +00001565 tensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +01001566 SupportedDataStorage storage(std::move(constData.second));
1567 return std::make_pair(constData.first, std::move(storage));
1568 }
1569 case armnn::DataType::QuantisedAsymm8:
1570 {
1571 auto constData = CreateConstTensorImpl<uint8_t>(bufferPtr,
1572 tensorPtr,
jimfly01c25411c2018-11-14 17:47:22 +00001573 tensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +01001574 SupportedDataStorage storage(std::move(constData.second));
1575 return std::make_pair(constData.first, std::move(storage));
1576 }
1577 case armnn::DataType::Signed32:
1578 {
1579 auto constData = CreateConstTensorImpl<int32_t>(bufferPtr,
1580 tensorPtr,
jimfly01c25411c2018-11-14 17:47:22 +00001581 tensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +01001582 SupportedDataStorage storage(std::move(constData.second));
1583 return std::make_pair(constData.first, std::move(storage));
1584 }
1585 default:
1586 {
1587 std::stringstream errString;
1588 errString << "Unexpected datatype when creating const tensor: "
1589 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1590 << " shape:" << tensorInfo.GetShape()
1591 << CHECK_LOCATION().AsString();
1592 throw ParseException(errString.str());
1593 }
1594 }
1595}
1596
1597BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1598 const std::string& name) const
1599{
1600 CHECK_SUBGRAPH(m_Model, subgraphId);
1601 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1602 for (auto const & input : inputs)
1603 {
1604 if (input.second->name == name)
1605 {
1606 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1607 return std::make_pair(bindingId, ToTensorInfo(input.second));
1608 }
1609 }
1610
1611 std::stringstream bindings;
1612 for (auto const & input : inputs)
1613 {
1614 bindings << "'" << input.second->name << "' ";
1615 }
1616
1617 throw ParseException(
1618 boost::str(
1619 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1620 "Possible inputs are: [%3%] %4%") %
1621 subgraphId %
1622 name %
1623 bindings.str() %
1624 CHECK_LOCATION().AsString()));
1625}
1626
1627BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1628 const std::string& name) const
1629{
1630 CHECK_SUBGRAPH(m_Model, subgraphId);
1631 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1632 for (auto const & output : outputs)
1633 {
1634 if (output.second->name == name)
1635 {
1636 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1637 return std::make_pair(bindingId, ToTensorInfo(output.second));
1638 }
1639 }
1640
1641 std::stringstream bindings;
1642 for (auto const & output : outputs)
1643 {
1644 bindings << "'" << output.second->name << "' ";
1645 }
1646
1647 throw ParseException(
1648 boost::str(
1649 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1650 "Possible outputs are: [%3%] %4%") %
1651 subgraphId %
1652 name %
1653 bindings.str() %
1654 CHECK_LOCATION().AsString()));
1655}
1656
1657size_t TfLiteParser::GetSubgraphCount() const
1658{
1659 return m_Model->subgraphs.size();
1660}
1661
1662std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1663{
1664 CHECK_SUBGRAPH(m_Model, subgraphId);
1665 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1666 std::vector<std::string> result;
1667 result.reserve(inputs.size());
1668 for (auto const & input : inputs)
1669 {
1670 result.push_back(input.second->name);
1671 }
1672 return result;
1673}
1674
1675std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1676{
1677 CHECK_SUBGRAPH(m_Model, subgraphId);
1678 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1679 std::vector<std::string> result;
1680 result.reserve(outputs.size());
1681 for (auto const & output : outputs)
1682 {
1683 result.push_back(output.second->name);
1684 }
1685 return result;
1686}
1687
1688ITfLiteParser* ITfLiteParser::CreateRaw()
1689{
1690 return new TfLiteParser();
1691}
1692
1693ITfLiteParserPtr ITfLiteParser::Create()
1694{
1695 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1696}
1697
1698void ITfLiteParser::Destroy(ITfLiteParser* parser)
1699{
1700 delete parser;
1701}
1702
1703TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1704: m_FloatData(std::move(data))
1705, m_Uint8Data(nullptr)
1706, m_Int32Data(nullptr)
1707{
1708}
1709
1710TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1711: m_FloatData(nullptr)
1712, m_Uint8Data(std::move(data))
1713, m_Int32Data(nullptr)
1714{
1715}
1716
1717TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1718: m_FloatData(nullptr)
1719, m_Uint8Data(nullptr)
1720, m_Int32Data(std::move(data))
1721{
1722}
1723
1724} // armnnTfLiteParser