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