ArmNN
 20.05
TfLiteParser.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "TfLiteParser.hpp"
7 
8 #include <armnn/Descriptors.hpp>
9 #include <armnn/Exceptions.hpp>
10 #include <armnn/Logging.hpp>
11 #include <armnn/TypesUtils.hpp>
12 #include <armnn/utility/Assert.hpp>
15 
16 // armnnUtils:
17 #include <armnnUtils/Permute.hpp>
18 
19 #include <ParserHelper.hpp>
20 #include <VerificationHelpers.hpp>
21 
22 // The generated code based on the Tf Lite schema:
23 #include <schema_generated.h>
24 
25 #include <flatbuffers/flexbuffers.h>
26 
27 #include <boost/format.hpp>
28 #include <boost/numeric/conversion/cast.hpp>
29 #include <boost/filesystem.hpp>
30 
31 #include <fstream>
32 #include <algorithm>
33 #include <limits>
34 #include <numeric>
35 #include <sstream>
36 
37 #define ARMNN_THROW_PARSE_EXCEPTION(msg) \
38  { \
39  throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
40  << ": " \
41  << CHECK_LOCATION().AsString()).str()); \
42  }
43 
44 using namespace armnn;
46 namespace armnnTfLiteParser
47 {
48 namespace
49 {
50 
51 const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
52 
53 void CheckSubgraph(const TfLiteParser::ModelPtr & model,
54  size_t subgraphIndex,
55  const CheckLocation & location)
56 {
57  if (model.get() == nullptr)
58  {
59  throw ParseException(
60  boost::str(
61  boost::format("%1% was called with invalid (null) model. "
62  "Possible reason is that the model is not yet loaded and Unpack(ed). "
63  "subgraph:%2% at %3%") %
64  location.m_Function %
65  subgraphIndex %
66  location.FileLine()));
67  }
68  else if (subgraphIndex >= model->subgraphs.size())
69  {
70  throw ParseException(
71  boost::str(
72  boost::format("%1% was called with an invalid subgraph index. "
73  "subgraph:%2% at %3%") %
74  location.m_Function %
75  subgraphIndex %
76  location.FileLine()));
77  }
78 }
79 
80 #define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
81  CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
82 
83 void CheckModel(const TfLiteParser::ModelPtr & model,
84  size_t subgraphIndex,
85  size_t operatorIndex,
86  const CheckLocation & location)
87 {
88  if (model.get() == nullptr)
89  {
90  throw ParseException(
91  boost::str(
92  boost::format("%1% was called with invalid (null) model. "
93  "Possible reason is that the model is not yet loaded and Unpack(ed). "
94  "subgraph:%2% operator:%3% at %4%") %
95  location.m_Function %
96  subgraphIndex %
97  operatorIndex %
98  location.FileLine()));
99  }
100  else if (subgraphIndex >= model->subgraphs.size())
101  {
102  throw ParseException(
103  boost::str(
104  boost::format("%1% was called with an invalid subgraph index. "
105  "subgraph:%2% operator:%3% at %4%") %
106  location.m_Function %
107  subgraphIndex %
108  operatorIndex %
109  location.FileLine()));
110  }
111  else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
112  operatorIndex != VIRTUAL_OPERATOR_ID)
113  {
114  throw ParseException(
115  boost::str(
116  boost::format("%1% was called with an invalid operator index. "
117  "subgraph:%2% operator:%3% at %4%") %
118  location.m_Function %
119  subgraphIndex %
120  operatorIndex %
121  location.FileLine()));
122  }
123 }
124 
125 #define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
126  CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
127 
128 void CheckTensor(const TfLiteParser::ModelPtr & model,
129  size_t subgraphIndex,
130  size_t tensorIndex,
131  const CheckLocation & location)
132 {
133  // not checking model, because I assume CHECK_MODEL already run
134  // and checked that. An assert would do.
135  ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
136 
137  // also subgraph index should be checked by CHECK_MODEL so
138  // I only add an assert here
139  ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
140 
141  // the tensor index is the only one to check here
142  if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
143  {
144  throw ParseException(
145  boost::str(
146  boost::format("%1% was called with an invalid tensor index. "
147  "subgraph:%2% tensor:%3% at %4%") %
148  location.m_Function %
149  subgraphIndex %
150  tensorIndex %
151  location.FileLine()));
152  }
153 }
154 
155 #define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
156  CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
157 
158 void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
159  const CheckLocation & location)
160 {
161  if (rawPtr == nullptr)
162  {
163  throw ParseException(
164  boost::str(
165  boost::format("%1% was called with a null tensor pointer. "
166  "at %2%") %
167  location.m_Function %
168  location.FileLine()));
169 
170  }
171 }
172 
173 #define CHECK_TENSOR_PTR(TENSOR_PTR) \
174  CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
175 
176 void CheckBuffer(const TfLiteParser::ModelPtr & model,
177  size_t bufferIndex,
178  const CheckLocation & location)
179 {
180  if (model.get() == nullptr)
181  {
182  throw ParseException(
183  boost::str(
184  boost::format("%1% was called with invalid (null) model. "
185  "Possible reason is that the model is not yet loaded and Unpack(ed). "
186  "buffer:%2% at %3%") %
187  location.m_Function %
188  bufferIndex %
189  location.FileLine()));
190  }
191  else if (bufferIndex >= model->buffers.size())
192  {
193  throw ParseException(
194  boost::str(
195  boost::format("%1% was called with an invalid buffer index. "
196  "buffer index:%2% at %3%") %
197  location.m_Function %
198  bufferIndex %
199  location.FileLine()));
200  }
201  else if (model->buffers[bufferIndex].get() == nullptr)
202  {
203  throw ParseException(
204  boost::str(
205  boost::format("The buffer #%1% is null. %3%") %
206  bufferIndex %
207  location.AsString()));
208  }
209 }
210 
211 #define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
212  CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
213 
214 void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
215  const armnn::TensorInfo & tensorInfo,
216  uint32_t bufferId,
217  const CheckLocation & location)
218 {
219  if (bufferPtr == nullptr)
220  {
221  throw ParseException(
222  boost::str(
223  boost::format("BufferPtr is null for buffer:%1%. %2%") %
224  bufferId %
225  location.AsString()));
226  }
227  else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
228  tensorInfo.GetNumBytes() > bufferPtr->data.size())
229  {
230  std::stringstream ss;
231  ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
232  << "For tensor: " << tensorInfo.GetShape()
233  << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
234  << tensorInfo.GetNumElements() << " elements. " << location.AsString();
235  throw ParseException(ss.str());
236  }
237 }
238 
239 #define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
240  CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
241 
242 bool IsActivationSupported(tflite::ActivationFunctionType activationType)
243 {
244  switch(activationType)
245  {
246  case tflite::ActivationFunctionType_NONE:
247  case tflite::ActivationFunctionType_RELU:
248  case tflite::ActivationFunctionType_RELU6:
249  case tflite::ActivationFunctionType_TANH:
250  {
251  return true;
252  }
253  default:
254  {
255  return false;
256  }
257  }
258 }
259 
260 #define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
261  do { \
262  if (IsActivationSupported(OPTION->fused_activation_function) == false) \
263  { \
264  throw ParseException( \
265  boost::str( \
266  boost::format("TfLite parser doesn't suppport fused activation: " \
267  "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
268  OPTION->fused_activation_function % \
269  tflite::EnumNameActivationFunctionType(\
270  OPTION->fused_activation_function) % \
271  __func__ % \
272  SUBGRAPH_INDEX % \
273  OPERATOR_INDEX % \
274  CHECK_LOCATION().FileLine())); \
275  } \
276  } while(false)
277 
278 
279 std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
280 {
281  std::vector<unsigned int> result;
282  result.reserve(in.size());
283  for (auto & i : in)
284  {
285  result.push_back(CHECKED_NON_NEGATIVE(i));
286  }
287  return result;
288 }
289 
290 void CalcPadding(uint32_t inputSize,
291  uint32_t filterSize,
292  uint32_t stride,
293  uint32_t dilation,
294  uint32_t& paddingFront,
295  uint32_t& paddingBack,
296  tflite::Padding padding)
297 {
298  paddingFront = 0;
299  paddingBack = 0;
300  if (padding == tflite::Padding_SAME)
301  {
302  uint32_t outputSize = (inputSize + stride - 1) / stride;
303  uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
304  uint32_t temp = (outputSize - 1) * stride + dilatedSize;
305  if (temp > inputSize)
306  {
307  paddingFront = (temp - inputSize) / 2;
308  paddingBack = (temp - inputSize) - paddingFront;
309  }
310  }
311 }
312 
313 armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes,
314  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
315 {
316  armnn::DataType type;
317  CHECK_TENSOR_PTR(tensorPtr);
318 
319  switch (tensorPtr->type)
320  {
321  case tflite::TensorType_UINT8:
323  break;
324  case tflite::TensorType_FLOAT32:
326  break;
327  case tflite::TensorType_INT8:
328  if (tensorPtr->quantization->zero_point.size() == 1)
329  {
330  // Per-tensor
332  }
333  else
334  {
335  // Per-channel
337  }
338  break;
339  case tflite::TensorType_INT16:
341  break;
342  case tflite::TensorType_INT32:
344  break;
345 
346  default:
347  {
348  CheckLocation location = CHECK_LOCATION();
349  throw ParseException(
350  boost::str(
351  boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
352  tensorPtr->type %
353  tflite::EnumNameTensorType(tensorPtr->type) %
354  tensorPtr->name %
355  location.AsString()));
356  }
357  }
358  std::vector<unsigned int> safeShape = shapes;
359  if (safeShape.size() == 0)
360  {
361  safeShape.push_back(1);
362  }
363 
364  float quantizationScale = 0.0f;
365  int32_t quantizationOffset = 0;
366 
367  if (tensorPtr->quantization.get())
368  {
369  if (tensorPtr->quantization->scale.size() <= 1)
370  {
371  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
372  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
373 
374  if (tensorPtr->quantization->scale.size() == 1)
375  {
376  quantizationScale = tensorPtr->quantization->scale[0];
377  }
378  if (tensorPtr->quantization->zero_point.size() == 1)
379  {
380  // NOTE: we lose precision here when converting from 64 bit to 32
381  // but this is what we support at the moment in ArmNN
382  quantizationOffset = boost::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
383  }
384 
385  armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
386  safeShape.data(),
387  type,
388  quantizationScale,
389  quantizationOffset);
390 
391  return result;
392  }
393  else
394  {
395  std::vector<float> quantizationScales;
396  std::vector<int32_t> quantizationOffsets;
397 
398  // Scale
399  std::copy(tensorPtr->quantization->scale.begin(),
400  tensorPtr->quantization->scale.end(),
401  std::back_inserter(quantizationScales));
402 
403  // QSymmS8 Per-axis
404  armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
405  safeShape.data(),
406  type,
407  quantizationScales,
408  dimensionMappings[boost::numeric_cast<unsigned int>(
409  tensorPtr->quantization->quantized_dimension)]);
410  return result;
411  }
412  }
413  else
414  {
415  armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
416  safeShape.data(),
417  type,
418  quantizationScale,
419  quantizationOffset);
420  return result;
421  }
422 }
423 
425  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
426 {
427  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
428  return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
429 }
430 
431 template<typename T>
432 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
433 CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
434  TfLiteParser::TensorRawPtr tensorPtr,
435  armnn::TensorInfo& tensorInfo,
437 {
438  IgnoreUnused(tensorPtr);
439  ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
440  ARMNN_ASSERT_MSG(bufferPtr != nullptr,
441  boost::str(
442  boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
443 
444  std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
445 
446  if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
447  {
448  tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
449  armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
450  reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
451  }
452  else
453  {
454  ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
455  }
456 
457  return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
458 }
459 
460 armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
461 {
462  // generate the binding id by shifting the tensor id by 8 bit
463  // and add the subgraph id, which allows 256 subgraphs
464  return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
465 }
466 
467 bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
468 {
469  const unsigned int actualSize = actual.GetNumDimensions();
470  if (actualSize != expected.size())
471  {
472  return false;
473  }
474 
475  for (unsigned int i = 0u; i < actualSize; i++)
476  {
477  if (expected[i] < 0 ||
478  actual[i] != static_cast<unsigned int>(expected[i]))
479  {
480  return false;
481  }
482  }
483 
484  return true;
485 }
486 
487 } // <anonymous>
488 
489 TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
490 : m_Options(options)
491 , m_Network(nullptr, nullptr)
492 , m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
493 {
494  // register supported operators
495  m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
496  m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
497  m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
498  m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
499  m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
500  m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
501  m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
502  m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
503  m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParser::ParseExp;
504  m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
505  m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
506  m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
507  m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
508  m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
509  m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
510  m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
511  m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
512  m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
513  m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
514  m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
515  m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
516  m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
517  m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
518  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
519  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
520  m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
521  m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
522  m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
523  m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
524  m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParser::ParseSplitV;
525  m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
526  m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
527  m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
528  m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
529  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
530  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
531  m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
532 
533  // register supported custom operators
534  m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
535 }
536 
537 void TfLiteParser::ResetParser()
538 {
539  m_Network = armnn::INetworkPtr(nullptr, nullptr);
540  m_Model = nullptr;
541  m_SubgraphConnections.clear();
542 }
543 
544 void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
545  size_t operatorIndex,
546  IConnectableLayer *layer)
547 {
548  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
549  ARMNN_ASSERT(layer != nullptr);
550 
551  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
552  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
553 
554  ARMNN_ASSERT(operatorPtr->inputs.size() > 1);
555 
556  uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
557  TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
558  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
559  TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
560 
561  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
562  armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
563 
564  if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
565  {
566  uint32_t id = reshapedInputId;
567  reshapedInputId = inputId;
568  inputId = id;
569 
570  reshapedTensorInfo = ToTensorInfo(tensorPtr1);
571  inputTensorInfo = ToTensorInfo(tensorPtr);
572  }
573 
574  uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
575 
576  std::vector<unsigned> reshapedDim;
577  for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
578  {
579  reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
580  }
581 
582  std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
583  std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
584 
585  reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
586 
587  std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
589  desc.m_TargetShape = reshapedTensorInfo.GetShape();
590  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
591 
592  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
593  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
594 
595  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
596 
597  armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
598  RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
599 }
600 
602 {
603  ResetParser();
604  m_Model = LoadModelFromFile(graphFile);
605  return CreateNetworkFromModel();
606 }
607 
608 INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
609 {
610  ResetParser();
611  m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
612  return CreateNetworkFromModel();
613 }
614 
615 INetworkPtr TfLiteParser::CreateNetworkFromModel()
616 {
617  m_Network = INetwork::Create();
618  ARMNN_ASSERT(m_Model.get() != nullptr);
619 
620  bool failedToCreate = false;
621  std::stringstream errors;
622 
623  if (m_Model->subgraphs.size() != 1)
624  {
625  throw ParseException(
626  boost::str(
627  boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
628  m_Model->subgraphs.size() %
629  CHECK_LOCATION().AsString()));
630  }
631 
632  size_t subgraphIndex = 0;
633  for (SubgraphPtr const & subgraph : m_Model->subgraphs)
634  {
635  m_SubgraphConnections.emplace_back(subgraph->tensors.size());
636 
637  size_t operatorIndex = 0;
638  for (OperatorPtr const & op : subgraph->operators)
639  {
640  try
641  {
642  auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
643  auto builtinCode = opCodePtr->builtin_code;
644 
645  if (builtinCode > tflite::BuiltinOperator_MAX)
646  {
647  throw ParseException(
648  boost::str(
649  boost::format("Operator code %1% is out of range 0-%2%. "
650  "subgraph:%3% operator idx:%4%. %5%") %
651  builtinCode %
652  tflite::BuiltinOperator_MAX %
653  subgraphIndex %
654  operatorIndex %
655  CHECK_LOCATION().AsString()));
656  }
657 
658  // lookup and call the parser function
659  auto & parserFunction = m_ParserFunctions[builtinCode];
660  (this->*parserFunction)(subgraphIndex, operatorIndex);
661  }
662  catch (const ParseException& e)
663  {
664  failedToCreate = true;
665  std::stringstream errorString;
666 
667  errorString << "Failed to parse operator #" << operatorIndex
668  << " within subgraph #" << subgraphIndex
669  << " error: " << e.what();
670  ARMNN_LOG(error) << errorString.str();
671 
672  errors << errorString.str() << "\n";
673  }
674  ++operatorIndex;
675  }
676 
677  SetupInputLayers(subgraphIndex);
678  SetupOutputLayers(subgraphIndex);
679  SetupConstantLayers(subgraphIndex);
680 
681  ++subgraphIndex;
682  }
683 
684  if (failedToCreate)
685  {
686  // we can skip everything and let the outer exception handler deal with the error
687  throw ParseException(errors.str());
688  }
689 
690  // establish the connections from the layer outputs to the inputs of the subsequent layers
691  for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
692  {
693  for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
694  {
695  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
696  {
697  for (size_t inputSlotIdx = 0;
698  inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
699  ++inputSlotIdx)
700  {
701  m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
702  *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
703  }
704  }
705  }
706  }
707 
708  return std::move(m_Network);
709 }
710 
711 void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
712  size_t tensorIndex,
713  armnn::IOutputSlot* slot)
714 {
715  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
716  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
717  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
718 
719  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
720 
721  // assuming there is only one producer for that tensor
722  if (tensorSlots.outputSlot != nullptr)
723  {
724  throw ParseException(boost::str(
725  boost::format("Another layer has already registered itself as the producer of "
726  "subgraph:%1% tensor:%2% %3%") %
727  subgraphIndex %
728  tensorIndex %
729  CHECK_LOCATION().AsString()));
730  }
731 
732  tensorSlots.outputSlot = slot;
733 }
734 
735 void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
736  size_t tensorIndex,
737  armnn::IInputSlot* slot)
738 {
739  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
740  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
741  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
742 
743  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
744  tensorSlots.inputSlots.push_back(slot);
745 }
746 
747 void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
748 {
749  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
750 
751  // NOTE: By default we presume the custom operator is not supported
752  auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
753 
754  // Identify custom code defined for custom operator
755  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
756  const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
757 
758  // Find parser function that correspondes to custom code (if any)
759  auto iterator = m_CustomParserFunctions.find(customCode);
760  if (iterator != m_CustomParserFunctions.end())
761  {
762  customParserFunction = iterator->second;
763  }
764 
765  // Run parser function
766  (this->*customParserFunction)(subgraphIndex, operatorIndex);
767 }
768 
769 void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
770 {
771  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
772 
773  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
774 
775  auto opcodeIndex = operatorPtr->opcode_index;
776  auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
777 
778  if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
779  {
780  // Do not add StandInLayer, throw ParseException instead
781  throw ParseException(
782  boost::str(
783  boost::format("Operator not supported. "
784  "subgraph:%1% operator:%2% "
785  "opcode_index:%3% opcode:%4% / %5% %6%") %
786  subgraphIndex %
787  operatorIndex %
788  opcodeIndex %
789  opcode %
790  tflite::EnumNameBuiltinOperator(opcode) %
791  CHECK_LOCATION().AsString()));
792  }
793 
794  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
795  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
796 
797  const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
798  const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
799 
800  StandInDescriptor descriptor(numInputs, numOutputs);
801  auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
802 
803  // Add a non-executable StandInLayer as a placeholder for any unsupported operator
804  IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
805  for (unsigned int i = 0u; i < numOutputs; ++i)
806  {
807  layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
808  }
809 
810  auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
811  auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
812 
813  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
814  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
815 }
816 
817 void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
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.AsConv2DOptions();
823 
824  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
825 
827  desc.m_BiasEnabled = false;
828  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
829  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
831  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
832  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
833 
834  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
835  CHECK_VALID_SIZE(inputs.size(), 2, 3);
836 
837  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
838  CHECK_VALID_SIZE(outputs.size(), 1);
839 
840  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
841  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
842 
843  // assuming input is NHWC
844  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
845  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
846 
847  // assuming the filter is OHWI : Output, H, W, Input
848  // which is essentially the same as NHWC
849  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
850  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
851 
852  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
853  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
854  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
855  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
856 
857  auto filterTensorAndData = CreateConstTensor(inputs[1],
858  filterTensorInfo,
860  armnn::IConnectableLayer* layer = nullptr;
861 
862  auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
863 
864  if (inputs.size() == 3)
865  {
866  desc.m_BiasEnabled = true;
867  armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
868  auto biasTensorAndData = CreateConstTensor(inputs[2],
869  biasTensorInfo,
871  layer = m_Network->AddConvolution2dLayer(desc,
872  filterTensorAndData.first,
873  Optional<ConstTensor>(biasTensorAndData.first),
874  layerName.c_str());
875  }
876  else
877  {
878  layer = m_Network->AddConvolution2dLayer(desc,
879  filterTensorAndData.first,
880  EmptyOptional(),
881  layerName.c_str());
882  }
883 
884  ARMNN_ASSERT(layer != nullptr);
885 
886  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
887  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
888 
889  // register the input connection slots for the layer, connections are made after all layers have been created
890  // only the tensors for the inputs are relevant, exclude the const tensors
891  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
892  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
893 
894  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
895  // register the output connection slots for the layer, connections are made after all layers have been created
896  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
897  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
898 }
899 
900 void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
901 {
902  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
903 
904  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
905  const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
906 
907  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
908 
910  desc.m_BiasEnabled = false;
911  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
912  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
914  CHECKED_NON_NEGATIVE(options->depth_multiplier);
915 
916  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
917  CHECK_VALID_SIZE(inputs.size(), 2, 3);
918  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
919  CHECK_VALID_SIZE(outputs.size(), 1);
920  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
921  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
922 
923  // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
924  PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
925 
926  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
927  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
928 
929  // Assuming input is NHWC
930  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
931  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
932 
933  // TensorflowLite weights come in the format [1, H, W, I * M]
934  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
935  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
936 
937  // Reshape weights as [ H, W, I, M ]
938  filterTensorInfo.SetShape({ filterHeight,
939  filterWidth,
940  inputTensorInfo.GetShape()[3],
941  filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
942 
943  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
944  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
945  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
946  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
947 
948  auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
949  armnn::IConnectableLayer* layer = nullptr;
950  auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
951 
952  if (inputs.size() == 3)
953  {
954  desc.m_BiasEnabled = true;
955  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
956  auto biasTensorAndData = CreateConstTensor(inputs[2],
957  biasTensorInfo,
959  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
960  filterTensorAndData.first,
961  Optional<ConstTensor>(biasTensorAndData.first),
962  layerName.c_str());
963  }
964  else
965  {
966  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
967  filterTensorAndData.first,
968  EmptyOptional(),
969  layerName.c_str());
970  }
971  ARMNN_ASSERT(layer != nullptr);
972 
973  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
974  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
975 
976  // register the input connection slots for the layer, connections are made after all layers have been created
977  // only the tensors for the inputs are relevant, exclude the const tensors
978  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
979  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
980 
981  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
982  // register the output connection slots for the layer, connections are made after all layers have been created
983  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
984  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
985 }
986 
987 void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
988 {
989  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
990 
991  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
992  CHECK_VALID_SIZE(inputs.size(), 1);
993 
994  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
995  CHECK_VALID_SIZE(outputs.size(), 1);
996 
997  auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
998 
999  IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
1000  ARMNN_ASSERT(layer != nullptr);
1001 
1002  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1003  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1004 
1005  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1006  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1007 
1008  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1009  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1010 }
1011 
1012 void TfLiteParser::ParseExp(size_t subgraphIndex, size_t operatorIndex)
1013 {
1014  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1015 
1016  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1017  CHECK_VALID_SIZE(inputs.size(), 1);
1018 
1019  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1020  CHECK_VALID_SIZE(outputs.size(), 1);
1021 
1022  auto layerName = boost::str(boost::format("Exp:%1%:%2%") % subgraphIndex % operatorIndex);
1023 
1025  desc.m_Operation = UnaryOperation::Exp;
1026  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerName.c_str());
1027  ARMNN_ASSERT(layer != nullptr);
1028 
1029  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1030  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1031 
1032  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1033  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1034 
1035  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1036  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1037 }
1038 
1039 void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1040 {
1041  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1042 
1043  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1044  CHECK_VALID_SIZE(inputs.size(), 1, 2);
1045 
1046  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1047  CHECK_VALID_SIZE(outputs.size(), 1);
1048 
1049  armnn::IConnectableLayer* layer = nullptr;
1050  auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
1051 
1052  TransposeDescriptor desc;
1053 
1054  if (inputs.size() == 2)
1055  {
1056  armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1057  BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1058  auto numPermVecElements = permuteTensorInfo.GetNumElements();
1059  std::vector<unsigned int> permuteShape(numPermVecElements);
1060  ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1061  PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1062 
1063  desc = TransposeDescriptor(permutationVector);
1064  }
1065 
1066  layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1067 
1068  ARMNN_ASSERT(layer != nullptr);
1069 
1070  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1071  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1072 
1073  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1074  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1075 
1076  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1077  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1078 }
1079 
1080 void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1081 {
1082  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1083 
1084  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1085  const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1086 
1088  desc.m_BiasEnabled = false;
1089  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1090  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1092 
1093  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1094  CHECK_VALID_SIZE(inputs.size(), 3);
1095 
1096  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1097  CHECK_VALID_SIZE(outputs.size(), 1);
1098 
1099  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
1100  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1101 
1102  // TfLite uses NHWC tensors
1103  const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1104  const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1105 
1106  const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1107  const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1108 
1109  CalcPadding(inputHeight,
1110  filterHeight,
1111  desc.m_StrideY,
1112  1, // DilationY
1113  desc.m_PadTop,
1114  desc.m_PadBottom,
1115  options->padding);
1116 
1117  CalcPadding(inputWidth,
1118  filterWidth,
1119  desc.m_StrideX,
1120  1, // DilationX
1121  desc.m_PadLeft,
1122  desc.m_PadRight,
1123  options->padding);
1124 
1125  auto filterTensorAndData = CreateConstTensor(inputs[1],
1126  filterTensorInfo,
1128 
1129  armnn::IConnectableLayer* layer = nullptr;
1130  auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1131 
1132  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1133  filterTensorAndData.first,
1134  EmptyOptional(),
1135  layerName.c_str());
1136 
1137  ARMNN_ASSERT(layer != nullptr);
1138 
1139  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1140  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1141 
1142  // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1143  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1144  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1145 
1146  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1147  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1148 }
1149 
1150 void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1151 {
1152  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1153 }
1154 
1155 void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1156 {
1157  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1158 
1159  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1160  CHECK_VALID_SIZE(inputs.size(), 3);
1161 
1162  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1163  CHECK_VALID_SIZE(outputs.size(), 1);
1164 
1165  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1166  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1167 
1168  armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1169  BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1170 
1171  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1172  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1173 
1174  std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1175  ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1176 
1177  size_t step = 2;
1178  std::vector<std::pair<unsigned int, unsigned int>> crops;
1179  for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1180  {
1181  crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1182  }
1183 
1185  desc.m_BlockShape = blockShape;
1186  desc.m_Crops = crops;
1188 
1189  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1190 
1191  auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1192  IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1193 
1194  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1195 
1196  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1197  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1198 
1199  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1200  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1201 }
1202 
1203 void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1204 {
1205  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1206 
1207  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1208  CHECK_VALID_SIZE(inputs.size(), 1);
1209 
1210  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1211  CHECK_VALID_SIZE(outputs.size(), 1);
1212 
1215  auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1216  IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1217 
1218  ARMNN_ASSERT(layer != nullptr);
1219 
1220  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1221  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1222 
1223  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1224  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1225 
1226  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1227  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1228 }
1229 
1230 void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1231 {
1232  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1233 }
1234 
1235 void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1236 {
1237  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1238 
1239  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1240  CHECK_VALID_SIZE(inputs.size(), 2);
1241 
1242  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1243  CHECK_VALID_SIZE(outputs.size(), 1);
1244 
1245  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1246  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1247 
1248  auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1249  IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1250 
1251  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1252  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1253 
1254  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1255  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1256  {
1257  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1258  }
1259  else
1260  {
1261  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1262  }
1263 
1264  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1265  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1266 }
1267 
1268 void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1269 {
1270  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1271 
1272  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1273  CHECK_VALID_SIZE(inputs.size(), 2);
1274 
1275  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1276  CHECK_VALID_SIZE(outputs.size(), 1);
1277 
1278  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1279  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1280 
1281  auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1282  IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1283 
1284  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1285  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1286 
1287  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1288  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1289  {
1290  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1291  }
1292  else
1293  {
1294  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1295  }
1296 
1297  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1298  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1299 }
1300 
1301 void TfLiteParser::ParsePool(size_t subgraphIndex,
1302  size_t operatorIndex,
1303  PoolingAlgorithm algorithm)
1304 {
1305  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1306 
1307  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1308  const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1309 
1310  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1311 
1312  std::string layerName;
1313 
1314  switch (algorithm)
1315  {
1316  case PoolingAlgorithm::Average:
1317  layerName =
1318  boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1319  break;
1320  case PoolingAlgorithm::Max:
1321  layerName =
1322  boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1323  break;
1324  default:
1325  ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1326  }
1327 
1328  Pooling2dDescriptor desc;
1329 
1330  desc.m_PoolType = algorithm;
1331  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1332  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1333  desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1334  desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1335  desc.m_PaddingMethod = PaddingMethod::Exclude;
1336  desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1338 
1339  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1340  CHECK_VALID_SIZE(inputs.size(), 1);
1341  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1342 
1343  // assuming input is NHWC
1344  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1345  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1346 
1347  CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1348  desc.m_PadTop, desc.m_PadBottom, options->padding);
1349  CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1350  desc.m_PadLeft, desc.m_PadRight, options->padding);
1351 
1352  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1353  CHECK_VALID_SIZE(outputs.size(), 1);
1354 
1355  IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1356 
1357  ARMNN_ASSERT(layer != nullptr);
1358 
1359  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1360  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1361 
1362  // register the input connection slots for the layer, connections are made after all layers have been created
1363  // only the tensors for the inputs are relevant, exclude the const tensors
1364  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1365  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1366 
1367  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1368  // register the output connection slots for the layer, connections are made after all layers have been created
1369  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1370  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1371 }
1372 
1373 void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1374 {
1375  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1376 
1377  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1378  CHECK_VALID_SIZE(inputs.size(), 3);
1379  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1380  CHECK_VALID_SIZE(outputs.size(), 1);
1381 
1382  SliceDescriptor desc;
1383 
1384  // set begin tensor info for slice descriptor
1385  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1386  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1387 
1388  std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1389  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1390 
1391  // set size tensor info for slice descriptor
1392  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1393  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1394 
1395  std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1396  ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1397  desc = SliceDescriptor(begin, size);
1398 
1399  auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1400  IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1401 
1402  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1403  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1404 
1405  // register the input connection slots for the layer, connections are made after all layers have been created
1406  // only the tensors for the inputs are relevant, exclude the const tensors
1407  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1408  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1409 
1410  // register the output connection slots for the layer, connections are made after all layers have been created
1411  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1412  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1413 }
1414 
1415 void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1416 {
1417  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1418  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1419  const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1420 
1421  SoftmaxDescriptor desc;
1422  desc.m_Beta = options->beta;
1423 
1424  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1425  CHECK_VALID_SIZE(inputs.size(), 1);
1426  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1427  CHECK_VALID_SIZE(outputs.size(), 1);
1428 
1429  auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1430  IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1431 
1432  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1433  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1434 
1435  // register the input connection slots for the layer, connections are made after all layers have been created
1436  // only the tensors for the inputs are relevant, exclude the const tensors
1437  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1438  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1439 
1440  // register the output connection slots for the layer, connections are made after all layers have been created
1441  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1442  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1443 }
1444 
1445 void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1446 {
1447  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1448 
1449  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1450  CHECK_VALID_SIZE(inputs.size(), 3);
1451 
1452  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1453  CHECK_VALID_SIZE(outputs.size(), 1);
1454 
1455  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1456  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1457 
1458  armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1459  BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1460 
1461  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1462  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1463 
1464  std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1465  ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1466 
1467  size_t step = 2;
1468  std::vector<std::pair<unsigned int, unsigned int>> padList;
1469  for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1470  {
1471  padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1472  }
1473 
1475  desc.m_BlockShape = blockShape;
1476  desc.m_PadList = padList;
1478 
1479  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1480 
1481  auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1482  IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1483 
1484  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1485 
1486  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1487  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1488 
1489  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1490  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1491 }
1492 
1493 armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1494  const armnn::TensorInfo & inputTensorInfo)
1495 {
1496  CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1497  std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1498  static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1499 
1500  if (inputTensorInfo.GetNumDimensions() > 4)
1501  {
1502  std::stringstream ss;
1503  ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1504  << " shape:" << inputTensorInfo.GetShape() << " "
1505  << CHECK_LOCATION().AsString();
1506  throw ParseException(ss.str());
1507  }
1508 
1509  if (squeezeDims.empty())
1510  {
1511  squeezeDims.assign(dimensionSequence,
1512  dimensionSequence+inputTensorInfo.GetNumDimensions());
1513  }
1514 
1515  std::vector<uint32_t> outputDims;
1516  for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1517  {
1518  bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1519  auto currentDimension = inputTensorInfo.GetShape()[i];
1520  if (skipSqueeze || currentDimension != 1)
1521  {
1522  outputDims.push_back(currentDimension);
1523  }
1524  }
1525 
1526  if (outputDims.size() > 4)
1527  {
1528  std::stringstream ss;
1529  ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1530  << " shape:" << inputTensorInfo.GetShape() << " "
1531  << CHECK_LOCATION().AsString();
1532  throw ParseException(ss.str());
1533  }
1534 
1535  TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1536  outputDims.data());
1537 
1538  // we need to preserve the tensor type and the quantization data as well
1539  TensorInfo outTensorInfo = inputTensorInfo;
1540  outTensorInfo.SetShape(outShape);
1541 
1542  return outTensorInfo;
1543 }
1544 
1545 void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1546 {
1547  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1548 
1549  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1550  CHECK_VALID_SIZE(inputs.size(), 1);
1551 
1552  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1553  CHECK_VALID_SIZE(outputs.size(), 1);
1554 
1555  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1556  const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1557 
1558  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1559  armnn::TensorInfo outputTensorInfo =
1560  TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1561  inputTensorInfo);
1562 
1563  ReshapeDescriptor reshapeDesc;
1564  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1565 
1566  auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1567  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1568  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1569 
1570  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1571  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1572 
1573  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1574  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1575 }
1576 
1577 void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1578 {
1579  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1580 
1581  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1582  CHECK_VALID_SIZE(inputs.size(), 4);
1583 
1584  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1585  CHECK_VALID_SIZE(outputs.size(), 1);
1586 
1587  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1588  const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1589 
1591  desc.m_BeginMask = options->begin_mask;
1592  desc.m_EllipsisMask = options->ellipsis_mask;
1593  desc.m_EndMask = options->end_mask;
1594  desc.m_NewAxisMask = options->new_axis_mask;
1595  desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1597 
1598  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1599  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1600 
1601  std::vector<int> begin(beginTensorInfo.GetNumElements());
1602  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1603 
1604  armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1605  BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1606 
1607  std::vector<int> end(endTensorInfo.GetNumElements());
1608  ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1609 
1610  armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1611  BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1612 
1613  std::vector<int> stride(strideTensorInfo.GetNumElements());
1614  ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1615 
1616  desc.m_Begin = begin;
1617  desc.m_End = end;
1618  desc.m_Stride = stride;
1619 
1620  auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1621  IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1622 
1623  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1624  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1625 
1626  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1627  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1628 
1629  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1630  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1631 }
1632 
1633 void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1634 {
1635  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1636 
1637  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1638  const auto * options = operatorPtr->builtin_options.AsSubOptions();
1639 
1640  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1641  CHECK_VALID_SIZE(inputs.size(), 2);
1642 
1643  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1644  CHECK_VALID_SIZE(outputs.size(), 1);
1645 
1646  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1647  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1648 
1649  auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1650  IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1651 
1652  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1653  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1654 
1655  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1656  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1657  {
1658  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1659  }
1660  else
1661  {
1662  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1663  }
1664 
1665  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1666 
1667  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1668  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1669 }
1670 
1671 void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1672 {
1673  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1674 
1675  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1676  const auto * options = operatorPtr->builtin_options.AsAddOptions();
1677 
1678  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1679  CHECK_VALID_SIZE(inputs.size(), 2);
1680 
1681  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1682  CHECK_VALID_SIZE(outputs.size(), 1);
1683 
1684  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1685  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1686 
1687  auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1688  IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1689 
1690  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1691  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1692 
1693  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1694  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1695  {
1696  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1697  }
1698  else
1699  {
1700  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1701  }
1702 
1703  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1704 
1705  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1706  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1707 }
1708 
1709 void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1710 {
1711  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1712 
1713  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1714  const auto * options = operatorPtr->builtin_options.AsMulOptions();
1715 
1716  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1717  CHECK_VALID_SIZE(inputs.size(), 2);
1718 
1719  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1720  CHECK_VALID_SIZE(outputs.size(), 1);
1721 
1722  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1723  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1724 
1725  auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1726  IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1727 
1728  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1729  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1730 
1731  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1732  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1733  {
1734  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1735  }
1736  else
1737  {
1738  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1739  }
1740 
1741  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1742 
1743  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1744  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1745 }
1746 
1747 void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1748 {
1749  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1750 
1751  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1752 
1753  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1754  CHECK_VALID_SIZE(outputs.size(), 1);
1755 
1756  armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1757  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1758 
1759  armnn::MeanDescriptor desc;
1760  std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1761  ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1762  desc.m_Axis = axis;
1763 
1764  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1765  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1766 
1767  desc.m_KeepDims =
1768  inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1769  true : false;
1770 
1771  auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1772  IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1773 
1774  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1775 
1776  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1777  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1778 
1779  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1780  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1781 }
1782 
1783 void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1784 {
1785  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1786 
1787  TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1788 
1789  TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1790  CHECK_VALID_SIZE(outputs.size(), 1);
1791 
1792  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1793  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1794 
1795  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1796  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1797 
1798  size_t step = 2;
1799  armnn::PadDescriptor desc;
1800  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1801  {
1802  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1803  }
1804 
1805  auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1806  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1807 
1808  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1809  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1810 
1811  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1812  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1813 
1814  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1815  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1816 }
1817 
1818 void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
1819 {
1820  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1821 
1822  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1823  CHECK_VALID_SIZE(inputs.size(), 1);
1824 
1825  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1826  CHECK_VALID_SIZE(outputs.size(), 1);
1827 
1828  auto layerName = boost::str(boost::format("Quantize:%1%:%2%") % subgraphIndex % operatorIndex);
1829 
1830  IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
1831  ARMNN_ASSERT(layer != nullptr);
1832 
1833  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1834  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1835 
1836  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1837  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1838 
1839  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1840  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1841 }
1842 
1843 void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1844 {
1845  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
1846 }
1847 
1848 void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1849 {
1850  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1851 }
1852 
1853 void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1854 {
1855  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1856 }
1857 
1858 void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1859 {
1860  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1861 }
1862 
1863 
1864 void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1865 {
1866  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1867  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1868  IgnoreUnused(operatorPtr);
1869 
1870  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1871  CHECK_VALID_SIZE(inputs.size(), 1);
1872 
1873  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1874  CHECK_VALID_SIZE(outputs.size(), 1);
1875 
1876  auto layerName = str(boost::format("Activation:"));
1877  ActivationDescriptor activationDesc;
1878  activationDesc.m_Function = activationType;
1879 
1880  switch (activationType)
1881  {
1882  case ActivationFunction::ReLu:
1883  {
1884  layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1885  break;
1886  }
1887  case ActivationFunction::BoundedReLu:
1888  {
1889  layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1890  activationDesc.m_A = 6.0f;
1891  activationDesc.m_B = 0.0f;
1892  break;
1893  }
1894  case ActivationFunction::Sigmoid:
1895  {
1896  layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1897  break;
1898  }
1899  case ActivationFunction::TanH:
1900  {
1901  layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1902  activationDesc.m_A = 1.0f;
1903  activationDesc.m_B = 1.0f;
1904  break;
1905  }
1906  default:
1907  {
1908  throw ParseException(
1909  boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1910  " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1911  }
1912  }
1913 
1914  IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1915 
1916  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1917  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1918 
1919  // register the input connection slots for the layer, connections are made after all layers have been created
1920  // only the tensors for the inputs are relevant, exclude the const tensors
1921  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1922  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1923 
1924  // register the output connection slots for the layer, connections are made after all layers have been created
1925  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1926  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1927 }
1929  const std::vector<int32_t> & targetDimsIn)
1930 {
1931  std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1932  const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1933 
1934  if (stretchDim != targetDimsIn.end())
1935  {
1936  if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1937  {
1938  throw ParseException(
1939  boost::str(
1940  boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1941  }
1942 
1943  auto targetNumElements =
1944  boost::numeric_cast<unsigned int>(
1945  std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1946 
1947  auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1948  outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1949  }
1950 
1951  TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1952 
1953  TensorInfo reshapeInfo = inputTensorInfo;
1954  reshapeInfo.SetShape(outputShape);
1955 
1956  return reshapeInfo;
1957 }
1958 
1959 void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1960 {
1961  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1962 
1963  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1964 
1965  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1966  CHECK_VALID_SIZE(outputs.size(), 1);
1967 
1968  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1969  const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1970 
1971  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1972  armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1973 
1974  std::vector<int32_t> targetShape;
1975  if (inputs.size() > 1 && inputs[1] != nullptr)
1976  {
1977  if (inputs[1]->is_variable)
1978  {
1979  ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
1980  }
1981 
1982  if (inputs[1]->shape.size() != 1)
1983  {
1984  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
1985  }
1986 
1987  if (inputs[1]->type != tflite::TensorType_INT32)
1988  {
1989  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
1990  }
1991 
1992  auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1993  auto vals = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
1994  for (int i=0; i < inputs[1]->shape[0]; i++)
1995  {
1996  targetShape.push_back(vals[i]);
1997  }
1998 
1999  if (options != nullptr &&
2000  options->new_shape.empty() == false &&
2001  options->new_shape != targetShape)
2002  {
2003  ARMNN_THROW_PARSE_EXCEPTION("Target shape defined in reshape parameters and as input tensor but "
2004  "the values do not match");
2005  }
2006  }
2007  else
2008  {
2009  if (options == nullptr)
2010  {
2011  ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2012  "At least one method required");
2013  }
2014 
2015  targetShape = options->new_shape;
2016  }
2017 
2018  armnn::TensorInfo reshapeOutputTensorInfo =
2019  TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
2020 
2021  // Check for valid input size and that reshape parameters equal output shape
2022  const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2023  if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
2024  {
2025  std::stringstream ss;
2026  ss << "New shape defined in reshape parameters "
2027  << reshapeOutputTensorShape
2028  << " does not equal output shape "
2029  << actualOutputTensorInfo.GetShape()
2030  << ": "
2031  << CHECK_LOCATION().AsString();
2032  throw ParseException(ss.str());
2033  }
2034 
2035  ReshapeDescriptor reshapeDesc;
2036  reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
2037 
2038  auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
2039  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2040  layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
2041 
2042  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2043  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2044 
2045  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2046  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2047 }
2048 
2049 void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2050 {
2051  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2052 }
2053 
2054 void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2055 {
2056  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2057 }
2058 
2059 void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2060 {
2061  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2062 
2063  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2064  CHECK_VALID_SIZE(inputs.size(), 2);
2065 
2066  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2067  CHECK_VALID_SIZE(outputs.size(), 1);
2068 
2069  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2070 
2071  // Data for the parsed tensor args (size) must be stored locally.
2072  std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2073 
2074  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2075  ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2076 
2077  ResizeDescriptor desc;
2078  desc.m_Method = resizeMethod;
2079  desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
2080  desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2081  desc.m_DataLayout = armnn::DataLayout::NHWC;
2082 
2083  auto layerName = str(boost::format("Resize:"));
2084 
2085  switch (resizeMethod)
2086  {
2087  case ResizeMethod::Bilinear:
2088  {
2089  layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
2090 
2091  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2092  const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2093 
2094  desc.m_BilinearAlignCorners = options->align_corners;
2095  break;
2096  }
2097  case ResizeMethod::NearestNeighbor:
2098  {
2099  layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
2100  break;
2101  }
2102  default:
2103  {
2104  throw ParseException(
2105  boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
2106  " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
2107  }
2108  }
2109 
2110  IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2111 
2112  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2113  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2114 
2115  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2116  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2117 
2118  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2119  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2120 }
2121 
2122 void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2123 {
2124  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2125 
2126  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2127  const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2128 
2129  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2130 
2131  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2132  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2133  CHECK_VALID_SIZE(outputs.size(), 1);
2134 
2135  unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2136  uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
2137 
2138  const unsigned int concatDimInput = static_cast<unsigned int>(
2139  (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
2140 
2141  OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2142  concatDescriptor.SetConcatAxis(concatDimInput);
2143 
2144  unsigned int mergeDimOrigin = 0;
2145 
2146  for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2147  {
2148  TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2149 
2150  // This set up concatDescriptor view origin
2152  inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
2153  }
2154 
2155  auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
2156  IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
2157 
2158  ARMNN_ASSERT(layer != nullptr);
2159 
2160  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2161  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2162 
2163  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2164 
2165  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2166 
2167  // add fused activation layer
2168  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2169 
2170  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2171  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2172 }
2173 
2174 void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2175 {
2176  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2177 
2178  const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2179  const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2180 
2181  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2182 
2184  desc.m_BiasEnabled = false;
2185  desc.m_TransposeWeightMatrix = true;
2186 
2187  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2188  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2189  CHECK_VALID_SIZE(outputs.size(), 1);
2190 
2191  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2192 
2193  // Fully Connected Layer accepts two dimensional weights input
2194  int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2195  if (weightsDimension != 2)
2196  {
2197  throw ParseException(
2198  boost::str(
2199  boost::format(
2200  "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2201  "Node %2%")
2202  % weightsDimension
2203  % CHECK_LOCATION().AsString()));
2204  }
2205 
2206  auto filterTensorAndData = CreateConstTensor(inputs[1],
2207  filterTensorInfo,
2209  armnn::IConnectableLayer* layer = nullptr;
2210  auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2211 
2212  if (inputs.size() == 3)
2213  {
2214  desc.m_BiasEnabled = true;
2215  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
2216  auto biasTensorAndData = CreateConstTensor(inputs[2],
2217  biasTensorInfo,
2219  layer = m_Network->AddFullyConnectedLayer(desc,
2220  filterTensorAndData.first,
2221  Optional<ConstTensor>(biasTensorAndData.first),
2222  layerName.c_str());
2223  }
2224  else
2225  {
2226  layer = m_Network->AddFullyConnectedLayer(desc,
2227  filterTensorAndData.first,
2228  EmptyOptional(),
2229  layerName.c_str());
2230  }
2231  ARMNN_ASSERT(layer != nullptr);
2232 
2233  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2234 
2235  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2236 
2237  if (inputTensorInfo.GetNumDimensions() > 2)
2238  {
2239  // Add reshape to flatten to 2D [batch_size, input_size],
2240  // where "input_size" corresponds to the number of inputs to the layer,
2241  // matching the second dimension of weights,
2242  // and "batch_size" is calculated by dividing the number of elements by "input_size".
2243  std::vector<unsigned int> reshapedDimensions(2);
2244  reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2245  reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2246 
2247  if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2248  {
2249  throw ParseException(
2250  boost::str(
2251  boost::format(
2252  "Failed to deduce input tensor shape from filter size %1%")
2253  % reshapedDimensions[1]
2254  % CHECK_LOCATION().AsString()));
2255  }
2256 
2257  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2258  reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2259 
2260  std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2262  desc.m_TargetShape = reshapedTensorInfo.GetShape();
2263  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2264 
2265  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2266  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2267 
2268  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2269  }
2270  else
2271  {
2272  // register the input connection slot for the layer
2273  // only the tensors for the inputs are relevant, exclude the const tensors
2274  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2275  }
2276 
2277  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2278  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2279 
2280  // we need to add the activation layer and fortunately we don't need to care about the data layout
2281  armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2282  options->fused_activation_function);
2283 
2284  // register the output connection slots for the layer, connections are made after all layers have been created
2285  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2286  RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2287 }
2288 
2289 void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2290 {
2291  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2292 
2293  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2294 
2295  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2296  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2297  CHECK_VALID_SIZE(outputs.size(), 4);
2298 
2299  // Obtain custom options from flexbuffers
2300  auto custom_options = operatorPtr->custom_options;
2301  const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2302 
2303  // Obtain descriptor information from tf lite
2305  desc.m_MaxDetections = m["max_detections"].AsUInt32();
2306  desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2307  desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2308  desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2309  desc.m_NumClasses = m["num_classes"].AsUInt32();
2310  desc.m_ScaleH = m["h_scale"].AsFloat();
2311  desc.m_ScaleW = m["w_scale"].AsFloat();
2312  desc.m_ScaleX = m["x_scale"].AsFloat();
2313  desc.m_ScaleY = m["y_scale"].AsFloat();
2314 
2315  if (!(m["use_regular_nms"].IsNull()))
2316  {
2317  desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
2318  }
2319  if (!(m["detections_per_class"].IsNull()))
2320  {
2321  desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2322  }
2323 
2324  if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2325  {
2326  throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2327  "must be positive and less than or equal to 1.");
2328  }
2329 
2330  armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2331  auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2333 
2334  auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2335  IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2336  layerName.c_str());
2337 
2338  ARMNN_ASSERT(layer != nullptr);
2339 
2340  // The model does not specify the output shapes.
2341  // The output shapes are calculated from the max_detection and max_classes_per_detection.
2342  unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2343  m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2344  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2345  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2346  m_OverridenOutputShapes.push_back({ 1 });
2347 
2348  for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2349  {
2350  armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
2351  layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2352  }
2353 
2354  // Register the input connection slots for the layer, connections are made after all layers have been created
2355  // only the tensors for the inputs are relevant, exclude the const tensors
2356  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2357  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2358 
2359  // Register the output connection slots for the layer, connections are made after all layers have been created
2360  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2361  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2362  outputTensorIndexes[1],
2363  outputTensorIndexes[2],
2364  outputTensorIndexes[3]});
2365 }
2366 
2367 /// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2368 void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2369 {
2370  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2371 
2372  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2373  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2374  CHECK_VALID_SIZE(outputs.size(), 1);
2375 
2376  if (inputs.size() < 1)
2377  {
2378  throw ParseException("Pack must have at least one input.");
2379  }
2380 
2381  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2382  const auto* options = operatorPtr->builtin_options.AsPackOptions();
2383 
2384  StackDescriptor desc;
2385  desc.m_Axis = static_cast<uint32_t>(options->axis);
2386  desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2387 
2388  // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2389  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2390  desc.m_InputShape = inputTensorInfo.GetShape();
2391 
2392  auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2393  IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2394 
2395  ARMNN_ASSERT(layer != nullptr);
2396 
2397  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2398  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2399 
2400  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2401  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2402 
2403  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2404  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2405 }
2406 
2407 void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2408 {
2409  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2410 
2411  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2412  const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2413 
2414  // This unpackAxis indicates the axis to unpack
2415  const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2416 
2417  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2418  CHECK_VALID_SIZE(inputs.size(), 1);
2419 
2420  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2421 
2422  if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2423  {
2424  throw ParseException(
2425  boost::str(
2426  boost::format(
2427  "The unpack axis: %1% cannot be greater than or equal to "
2428  "the number of input dimension %2% %3%")
2429  % unpackAxis
2430  % inputTensorInfo.GetNumDimensions()
2431  % CHECK_LOCATION().AsString()));
2432  }
2433 
2434  unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2435  // If num is not defined, automatically infer from the length of the dimension axis.
2436  if(unpackNum == 0)
2437  {
2438  unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2439  }
2440 
2441  // If unpack number cannot be inferred and is still zero, throw ParseException.
2442  if(unpackNum == 0)
2443  {
2444  throw ParseException("Number to unpack must greater than zero.");
2445  }
2446 
2447  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2448  CHECK_VALID_SIZE(outputs.size(), unpackNum);
2449 
2450  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2451  std::vector<unsigned int> unpackDimSizes(inputDimSize);
2452 
2453  // Add current input shape to unpackDimSizes
2454  for (unsigned int i = 0; i < inputDimSize; ++i)
2455  {
2456  unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2457  }
2458 
2459  if (unpackDimSizes[unpackAxis] != unpackNum)
2460  {
2461  throw ParseException("Number to unpack must be the same as length of the dimension to "
2462  "unpack along.");
2463  }
2464 
2465  unpackDimSizes[unpackAxis] /= unpackNum;
2466 
2467  SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2468  for (unsigned int j = 0; j < unpackNum; ++j)
2469  {
2470  // Set the size of the views.
2471  for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2472  {
2473  splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2474  }
2475  splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2476  }
2477 
2478  auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2479  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2480 
2481  TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2482  unpackDimSizes.data());
2483 
2484  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2485  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2486 
2487  // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2488  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2489  {
2490  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
2491  std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2493  desc.m_TargetShape = outputTensorInfo.GetShape();
2494  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2495 
2496  layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2497  outputTensorInfo.GetDataType(),
2498  outputTensorInfo.GetQuantizationScale(),
2499  outputTensorInfo.GetQuantizationOffset()));
2500  layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2501 
2502  reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2503 
2504  uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2505  armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2506  RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2507  }
2508 }
2509 
2510 void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2511 {
2512  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2513 
2514  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2515  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2516 
2517  const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2518 
2519  // If number of splits cannot be inferred and is zero, throw ParseException.
2520  if(numSplits == 0)
2521  {
2522  throw ParseException("Number to splits must greater than zero.");
2523  }
2524 
2525  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2526  CHECK_VALID_SIZE(inputs.size(), 2);
2527  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2528  CHECK_VALID_SIZE(outputs.size(), numSplits);
2529 
2530  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2531  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
2532 
2533  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2534  std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2535  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2536 
2537  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2538  const unsigned int splitDim = axisData[0];
2539 
2540  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2541  if (inputDimSize > MaxNumOfTensorDimensions)
2542  {
2543  throw ParseException(
2544  boost::str(
2545  boost::format(
2546  "The number of dimensions: %1% for input tensors of the "
2547  "split op cannot be greater than %2% %3%")
2548  % inputTensorInfo.GetNumDimensions()
2550  % CHECK_LOCATION().AsString()));
2551  }
2552 
2553  std::vector<unsigned int> splitterDimSizes(inputDimSize);
2554 
2555  // Add current input shape to splitterDimSizes
2556  for (unsigned int i = 0; i < inputDimSize; ++i)
2557  {
2558  splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2559  }
2560 
2561  if (splitterDimSizes[splitDim] % numSplits != 0)
2562  {
2563  throw ParseException("Number of splits must evenly divide the dimension");
2564  }
2565  splitterDimSizes[splitDim] /= numSplits;
2566 
2567  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2568  for (unsigned int j = 0; j < numSplits; ++j)
2569  {
2570  // Set the size of the views.
2571  for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2572  {
2573  splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2574  }
2575  splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2576  }
2577 
2578  auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2579  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2580 
2581  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2582  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
2583 
2584  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2585  {
2586  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2587  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2588  }
2589 
2590  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2591  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2592 }
2593 
2594 unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2595 {
2596  int numDims = armnn::numeric_cast<int>(numDimsIn);
2597  int v = idx < 0 ? numDims + idx : idx;
2598  ARMNN_ASSERT(v >= 0);
2599  ARMNN_ASSERT(v < numDims);
2600 
2601  return static_cast<unsigned int>(v);
2602 }
2603 
2604 void TfLiteParser::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2605 {
2606  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2607 
2608  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2609 
2610 
2611  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2612  CHECK_VALID_SIZE(inputs.size(), 3);
2613 
2614  auto& inputTensor = inputs[0];
2615  auto& splitsTensor = inputs[1];
2616  auto& axisTensor = inputs[2];
2617 
2618  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
2619  armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
2620  armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
2621  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2622 
2623  // Inputs
2624  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2625  if (inputDimSize > MaxNumOfTensorDimensions)
2626  {
2627  throw ParseException(
2628  boost::str(
2629  boost::format(
2630  "The number of dimensions: %1% for input tensors of the "
2631  "split op cannot be greater than %2% %3%")
2632  % inputTensorInfo.GetNumDimensions()
2634  % CHECK_LOCATION().AsString()));
2635  }
2636 
2637  // Get split axis
2638  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
2639  std::vector<int> axisData(axisTensorInfo.GetNumElements());
2640  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2641  const unsigned int splitDim = ComputeWrappedIndex(axisData[0], inputTensorInfo.GetNumDimensions());
2642 
2643 
2644  // Set split sizes
2645  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2646  CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
2647  unsigned int numSplits = 0;
2648  std::vector<int> splitsData(0);
2649  if (options)
2650  {
2651  numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2652  splitsData.resize(numSplits);
2653 
2654  if (inputTensorInfo.GetShape()[splitDim] % numSplits != 0)
2655  {
2656  throw ParseException("Number of splits must evenly divide the split axis");
2657  }
2658  unsigned int splitSize = inputTensorInfo.GetShape()[splitDim] / numSplits;
2659  for (auto& split : splitsData)
2660  {
2661  split = numeric_cast<int>(splitSize);
2662  }
2663  }
2664  else
2665  {
2666  numSplits = splitsInfo.GetShape()[0];
2667  splitsData.resize(numSplits);
2668 
2669  BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
2670  ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
2671 
2672  int numInferred = 0;
2673  int specifiedSizes = 0;
2674  unsigned int inferIdx = 0;
2675  unsigned int idx = 0;
2676  for (auto split : splitsData)
2677  {
2678  if (split < 0)
2679  {
2680  numInferred++;
2681  inferIdx = idx;
2682  }
2683  else
2684  {
2685  specifiedSizes += split;
2686  }
2687  idx++;
2688  }
2689 
2690  if (numInferred > 0)
2691  {
2692  if (numInferred > 1)
2693  {
2694  throw ParseException("Cannot infer split size for more than one split");
2695  }
2696  splitsData[inferIdx] = numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - specifiedSizes;
2697  }
2698  }
2699 
2700  if (numSplits <=0)
2701  {
2702  throw ParseException("SplitV has invalid number of splits");
2703  }
2704 
2705  //Ouput size validation
2706  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2707  CHECK_VALID_SIZE(outputs.size(), numSplits);
2708 
2709  // Setup Armnn descriptor
2710  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2711  unsigned int accumSplit = 0;
2712  for (unsigned int j = 0; j < numSplits; ++j)
2713  {
2714  unsigned int splitSize = numeric_cast<unsigned int>(splitsData[j]);
2715 
2716  // Set the size of the views.
2717  for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
2718  {
2719  unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
2720  if (dimIdx == splitDim)
2721  {
2722  dimSize = splitSize;
2723  }
2724  splitDesc.SetViewSize(j, dimIdx, dimSize);
2725  }
2726 
2727  splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
2728  accumSplit += splitSize;
2729  }
2730 
2731  auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2732  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2733 
2734  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2735  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2736 
2737  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2738  {
2739  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2740  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2741  }
2742 
2743  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2744  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2745 }
2746 
2747 armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2748  unsigned int outputSlot,
2749  tflite::ActivationFunctionType activationType)
2750 {
2751  ActivationDescriptor activationDesc;
2752  std::string layerName = prevLayer->GetName();
2753 
2754  switch(activationType)
2755  {
2756  case tflite::ActivationFunctionType_NONE:
2757  {
2758  // this is a no-op: return previous layer
2759  return prevLayer;
2760  }
2761  case tflite::ActivationFunctionType_RELU:
2762  {
2763  activationDesc.m_Function = ActivationFunction::ReLu;
2764  layerName += ":RELU";
2765  break;
2766  }
2767  case tflite::ActivationFunctionType_RELU6:
2768  {
2769  activationDesc.m_Function = ActivationFunction::BoundedReLu;
2770  activationDesc.m_A = 6.0f;
2771  activationDesc.m_B = 0.0f;
2772  layerName += ":RELU6";
2773  break;
2774  }
2775  case tflite::ActivationFunctionType_TANH:
2776  {
2777  activationDesc.m_Function = ActivationFunction::TanH;
2778  activationDesc.m_A = 1.0f;
2779  activationDesc.m_B = 1.0f;
2780  layerName += ":TANH";
2781  break;
2782  }
2783 
2784  // I only put these here as a reminder what others we could support
2785  case tflite::ActivationFunctionType_RELU_N1_TO_1:
2786  case tflite::ActivationFunctionType_SIGN_BIT:
2787  default:
2788  {
2789  throw ParseException(
2790  boost::str(
2791  boost::format("TfLite parser doesn't suppport fused activation: "
2792  "%1%/%2% %3% ") %
2793  activationType %
2794  tflite::EnumNameActivationFunctionType(activationType) %
2795  CHECK_LOCATION().AsString()));
2796 
2797  }
2798  }
2799 
2800  IConnectableLayer* activationLayer =
2801  m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2802 
2803  auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2804  prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2805  activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2806  return activationLayer;
2807 }
2808 
2810 {
2811  if (fileName == nullptr)
2812  {
2813  throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2814  CHECK_LOCATION().AsString()));
2815  }
2816  boost::system::error_code errorCode;
2817  boost::filesystem::path pathToFile(fileName);
2818  if (!boost::filesystem::exists(pathToFile, errorCode))
2819  {
2820  std::string locationString = CHECK_LOCATION().AsString();
2821  std::string msg = boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2822  fileName %
2823  errorCode %
2824  locationString);
2825  throw FileNotFoundException(msg);
2826  }
2827  std::ifstream file(fileName, std::ios::binary);
2828  std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2829  return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2830  fileContent.size());
2831 }
2832 
2833 TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2834 {
2835  if (binaryContent == nullptr)
2836  {
2837  throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2838  CHECK_LOCATION().AsString()));
2839  }
2840  flatbuffers::Verifier verifier(binaryContent, len);
2841  if (verifier.VerifyBuffer<tflite::Model>() == false)
2842  {
2843  throw ParseException(
2844  boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2845  "flatbuffers format. size:%1% %2%") %
2846  len %
2847  CHECK_LOCATION().AsString()));
2848  }
2849  return tflite::UnPackModel(binaryContent);
2850 }
2851 
2853  size_t subgraphIndex,
2854  size_t operatorIndex)
2855 {
2856  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2857 
2858  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2859  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2860 
2861  size_t inputCount = operatorPtr->inputs.size();
2862  TensorRawPtrVector result(inputCount);
2863  for (size_t i=0; i<inputCount; ++i)
2864  {
2865  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
2866  result[i] = subgraphPtr->tensors[inputId].get();
2867  }
2868  return result;
2869 }
2870 
2872  size_t subgraphIndex,
2873  size_t operatorIndex)
2874 {
2875  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2876 
2877  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2878  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2879 
2880  size_t outputCount = operatorPtr->outputs.size();
2881  TensorRawPtrVector result(outputCount);
2882  for (size_t i=0; i<outputCount; ++i)
2883  {
2884  uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2885  CHECK_TENSOR(model, subgraphIndex, outputId);
2886  result[i] = subgraphPtr->tensors[outputId].get();
2887  }
2888  return result;
2889 }
2890 
2892  size_t subgraphIndex)
2893 {
2894  CHECK_SUBGRAPH(model, subgraphIndex);
2895  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2896 
2897  size_t inputCount = subgraphPtr->inputs.size();
2898  TensorIdRawPtrVector result(inputCount);
2899  for (size_t i=0; i<inputCount; ++i)
2900  {
2901  uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
2902  CHECK_TENSOR(model, subgraphIndex, inputId);
2903  result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
2904  }
2905  return result;
2906 }
2907 
2909  size_t subgraphIndex)
2910 {
2911  CHECK_SUBGRAPH(model, subgraphIndex);
2912  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2913 
2914  size_t outputCount = subgraphPtr->outputs.size();
2915  TensorIdRawPtrVector result(outputCount);
2916  for (size_t i=0; i<outputCount; ++i)
2917  {
2918  uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2919  result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
2920  }
2921  return result;
2922 }
2923 
2924 std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2925  size_t subgraphIndex,
2926  size_t operatorIndex)
2927 {
2928  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2929  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2930  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2931  return operatorPtr->inputs;
2932 }
2933 
2934 std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2935  size_t subgraphIndex,
2936  size_t operatorIndex)
2937 {
2938  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2939  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2940  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2941  return operatorPtr->outputs;
2942 }
2943 
2944 void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2945  size_t operatorIndex,
2946  IConnectableLayer* layer,
2947  const std::vector<unsigned int>& tensorIndexes)
2948 {
2949  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2950  ARMNN_ASSERT(layer != nullptr);
2951  if (tensorIndexes.size() != layer->GetNumInputSlots())
2952  {
2953  throw ParseException(
2954  boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2955  " for subgraph:%3% operator index:%4% %5%") %
2956  tensorIndexes.size() %
2957  layer->GetNumInputSlots() %
2958  subgraphIndex %
2959  operatorIndex %
2960  CHECK_LOCATION().AsString()));
2961  }
2962 
2963  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2964  {
2965  unsigned int tensorIndex = tensorIndexes[slotIndex];
2966  armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2967  RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2968  }
2969 }
2970 
2971 void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2972  size_t operatorIndex,
2973  IConnectableLayer* layer,
2974  const std::vector<unsigned int>& tensorIndexes)
2975 {
2976  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2977  ARMNN_ASSERT(layer != nullptr);
2978  if (tensorIndexes.size() != layer->GetNumOutputSlots())
2979  {
2980  throw ParseException(
2981  boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2982  " for subgraph:%3% operator index:%4% %5%") %
2983  tensorIndexes.size() %
2984  layer->GetNumOutputSlots() %
2985  subgraphIndex %
2986  operatorIndex %
2987  CHECK_LOCATION().AsString()));
2988  }
2989 
2990  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2991  {
2992  unsigned int tensorIndex = tensorIndexes[slotIndex];
2993  armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2994  RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2995  }
2996 }
2997 
2998 void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2999 {
3000  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3001 
3002  auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3003  for (auto const & tensorIdAndPtr : inputs)
3004  {
3005  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3006  IConnectableLayer* layer =
3007  m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3008 
3009  auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3010  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3011 
3012  RegisterOutputSlots(subgraphIndex,
3013  VIRTUAL_OPERATOR_ID,
3014  layer,
3015  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3016  }
3017 }
3018 
3019 void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
3020 {
3021  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3022 
3023  auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3024  for (auto const & tensorIdAndPtr : outputs)
3025  {
3026  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3027  IConnectableLayer* layer =
3028  m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3029 
3030  RegisterInputSlots(subgraphIndex,
3031  VIRTUAL_OPERATOR_ID,
3032  layer,
3033  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3034  }
3035 }
3036 
3037 void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
3038 {
3039  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3040 
3041  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
3042  for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3043  {
3044  for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3045  {
3046  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3047  m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3048  {
3049  TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
3050  armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3051  auto tensorAndData = CreateConstTensor(tensorPtr,
3052  tensorInfo,
3054 
3055  std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
3056  IConnectableLayer *layer =
3057  m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
3058 
3059  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3060  RegisterOutputSlots(subgraphIndex,
3061  VIRTUAL_OPERATOR_ID,
3062  layer,
3063  { tensorIndex });
3064 
3065  }
3066  }
3067  }
3068 }
3069 
3070 // example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3072 {
3073  CHECK_BUFFER(model, bufferIndex);
3074  return model->buffers[bufferIndex].get();
3075 }
3076 
3077 template<typename T>
3078 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3079 TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
3080  TfLiteParser::TensorRawPtr tensorPtr,
3081  armnn::TensorInfo& tensorInfo,
3083 {
3084  auto constData = CreateConstTensorImpl<T>(bufferPtr,
3085  tensorPtr,
3086  tensorInfo,
3087  permutationVector);
3088  TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
3089  return std::make_pair(constData.first, std::move(storage));
3090 }
3091 
3092 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3093 TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
3094  armnn::TensorInfo& tensorInfo,
3096 {
3097  CHECK_TENSOR_PTR(tensorPtr);
3098  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3099  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3100 
3101  switch (tensorInfo.GetDataType())
3102  {
3104  return CreateConstTensorAndStoreData<float>(bufferPtr,
3105  tensorPtr,
3106  tensorInfo,
3107  permutationVector);
3109  return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3110  tensorPtr,
3111  tensorInfo,
3112  permutationVector);
3114  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3115  tensorPtr,
3116  tensorInfo,
3117  permutationVector);
3119  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3120  tensorPtr,
3121  tensorInfo,
3122  permutationVector);
3124  return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3125  tensorPtr,
3126  tensorInfo,
3127  permutationVector);
3128  default:
3129  {
3130  std::stringstream errString;
3131  errString << "Unexpected datatype when creating const tensor: "
3132  << armnn::GetDataTypeName(tensorInfo.GetDataType())
3133  << " shape:" << tensorInfo.GetShape()
3134  << CHECK_LOCATION().AsString();
3135  throw ParseException(errString.str());
3136  }
3137  }
3138 }
3139 
3141  const std::string& name) const
3142 {
3143  CHECK_SUBGRAPH(m_Model, subgraphId);
3144  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3145  for (auto const & input : inputs)
3146  {
3147  if (input.second->name == name)
3148  {
3149  auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3150  return std::make_pair(bindingId, ToTensorInfo(input.second));
3151  }
3152  }
3153 
3154  std::stringstream bindings;
3155  for (auto const & input : inputs)
3156  {
3157  bindings << "'" << input.second->name << "' ";
3158  }
3159 
3160  throw ParseException(
3161  boost::str(
3162  boost::format("No input binding found for subgraph:%1% and name:%2%. "
3163  "Possible inputs are: [%3%] %4%") %
3164  subgraphId %
3165  name %
3166  bindings.str() %
3167  CHECK_LOCATION().AsString()));
3168 }
3169 
3171  const std::string& name) const
3172 {
3173  CHECK_SUBGRAPH(m_Model, subgraphId);
3174  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3175  for (unsigned int i = 0; i < outputs.size(); ++i)
3176  {
3177  auto const output = outputs[i];
3178  if (output.second->name == name)
3179  {
3180  auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
3181  std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3182  m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3183  return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
3184  }
3185  }
3186 
3187  std::stringstream bindings;
3188  for (auto const & output : outputs)
3189  {
3190  bindings << "'" << output.second->name << "' ";
3191  }
3192 
3193  throw ParseException(
3194  boost::str(
3195  boost::format("No output binding found for subgraph:%1% and name:%2%. "
3196  "Possible outputs are: [%3%] %4%") %
3197  subgraphId %
3198  name %
3199  bindings.str() %
3200  CHECK_LOCATION().AsString()));
3201 }
3202 
3204 {
3205  return m_Model->subgraphs.size();
3206 }
3207 
3208 std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3209 {
3210  CHECK_SUBGRAPH(m_Model, subgraphId);
3211  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3212  std::vector<std::string> result;
3213  result.reserve(inputs.size());
3214  for (auto const & input : inputs)
3215  {
3216  result.push_back(input.second->name);
3217  }
3218  return result;
3219 }
3220 
3221 std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3222 {
3223  CHECK_SUBGRAPH(m_Model, subgraphId);
3224  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3225  std::vector<std::string> result;
3226  result.reserve(outputs.size());
3227  for (auto const & output : outputs)
3228  {
3229  result.push_back(output.second->name);
3230  }
3231  return result;
3232 }
3233 
3235 {
3236  return new TfLiteParser(options);
3237 }
3238 
3240 {
3242 }
3243 
3245 {
3246  delete parser;
3247 }
3248 
3249 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3250 : m_FloatData(std::move(data))
3251 , m_Uint8Data(nullptr)
3252 , m_Int8Data(nullptr)
3253 , m_Int32Data(nullptr)
3254 {
3255 }
3256 
3257 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3258 : m_FloatData(nullptr)
3259 , m_Uint8Data(std::move(data))
3260 , m_Int8Data(nullptr)
3261 , m_Int32Data(nullptr)
3262 {
3263 }
3264 
3265 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3266 : m_FloatData(nullptr)
3267 , m_Uint8Data(nullptr)
3268 , m_Int8Data(std::move(data))
3269 , m_Int32Data(nullptr)
3270 {
3271 }
3272 
3273 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3274 : m_FloatData(nullptr)
3275 , m_Uint8Data(nullptr)
3276 , m_Int8Data(nullptr)
3277 , m_Int32Data(std::move(data))
3278 {
3279 }
3280 
3281 } // armnnTfLiteParser
virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile) override
Create the network from a flatbuffers binary file on disk.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX)
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
UnaryOperation m_Operation
Specifies the elementwiseUnary operation to execute.
uint32_t m_Axis
0-based axis along which to stack the input tensors.
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
static BufferRawPtr GetBuffer(const ModelPtr &model, size_t bufferIndex)
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
float m_ScaleW
Center size encoding scale weight.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
static ModelPtr LoadModelFromBinary(const uint8_t *binaryContent, size_t len)
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
A TransposeConvolution2dDescriptor for the TransposeConvolution2dLayer.
#define ARMNN_THROW_PARSE_EXCEPTION(msg)
const TensorShape & GetShape() const
Definition: Tensor.hpp:88
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
virtual armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent) override
Create the network from a flatbuffers binary.
std::string AsString() const
Definition: Exceptions.hpp:29
int32_t m_ShrinkAxisMask
Shrink axis mask value. If set, the nth specification shrinks the dimensionality by 1...
A ReshapeDescriptor for the ReshapeLayer.
std::vector< int > m_Begin
Begin values for the input that will be sliced.
std::vector< TensorRawPtr > TensorRawPtrVector
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
float m_ScaleX
Center size encoding scale x.
TensorShape m_InputShape
Required shape of all input tensors.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
static ModelPtr LoadModelFromFile(const char *fileName)
bool m_BiasEnabled
Enable/disable bias.
unsigned int GetNumBytes() const
Definition: Tensor.cpp:214
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
float m_Beta
Exponentiation value.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_DetectionsPerClass
Detections per classes, used in Regular NMS.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_BUFFER(MODEL, BUFFER_INDEX)
static TensorRawPtrVector GetInputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
uint32_t m_PadTop
Padding top value in the height dimension.
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t &outPadHead, uint32_t &outPadTail, bool samePadding)
Definition: TfParser.cpp:420
uint32_t m_PadRight
Padding right value in the width dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
virtual BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
Copyright (c) 2020 ARM Limited.
void IgnoreUnused(Ts &&...)
uint32_t m_PadBottom
Padding bottom value in the height dimension.
int32_t m_BeginMask
Begin mask value.
virtual size_t GetSubgraphCount() const override
Return the number of subgraphs in the parsed model.
SizeType GetSize() const
Definition: Types.hpp:202
uint32_t m_DilationY
Dilation along y axis.
int32_t m_EndMask
End mask value.
PoolingAlgorithm
Definition: Types.hpp:96
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left, right}.
uint32_t m_DilationY
Dilation factor value for height dimension.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
static armnn::TensorInfo OutputShapeOfReshape(const armnn::TensorInfo &inputTensorInfo, const std::vector< int32_t > &targetDimsIn)
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:171
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:168
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:90
A ResizeDescriptor for the ResizeLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_MaxClassesPerDetection
Maximum numbers of classes per detection, used in Fast NMS.
std::vector< unsigned int > m_Axis
Values for the dimensions to reduce.
A StackDescriptor for the StackLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
TensorShape m_TargetShape
Target shape value.
static TensorIdRawPtrVector GetSubgraphInputs(const ModelPtr &model, size_t subgraphIndex)
static TensorRawPtrVector GetOutputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_MaxDetections
Maximum numbers of detections.
A PadDescriptor for the PadLayer.
armnn::TensorInfo ToTensorInfo(Deserializer::TensorRawPtr tensorPtr)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< onnx::ModelProto > ModelPtr
Definition: OnnxParser.hpp:23
#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
void CheckTensor(const ConstTensor &t)
Definition: TensorTest.cpp:100
static TensorIdRawPtrVector GetSubgraphOutputs(const ModelPtr &model, size_t subgraphIndex)
bool CheckShape(const armnn::TensorShape &actual, const std::vector< uint32_t > &expected)
DataType
Definition: Types.hpp:32
float m_NmsIouThreshold
Intersection over union threshold.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationX
Dilation factor value for width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
std::string FileLine() const
Definition: Exceptions.hpp:37
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t m_NewAxisMask
New axis mask value.
bool m_KeepDims
Enable/disable keep dimensions. If true, then the reduced dimensions that are of length 1 are kept...
armnnSerializer::TensorInfo * TensorRawPtr
std::vector< unsigned int > m_BlockShape
Block shape values.
static void Destroy(ITfLiteParser *parser)
An output connection slot for a layer.
Definition: INetwork.hpp:37
A L2NormalizationDescriptor for the L2NormalizationLayer.
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:265
float GetQuantizationScale() const
Definition: Tensor.cpp:248
static std::vector< int32_t > & GetOutputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataType GetDataType() const
Definition: Tensor.hpp:95
An OriginsDescriptor for the ConcatLayer.
bool has_value() const noexcept
Definition: Optional.hpp:53
A FullyConnectedDescriptor for the FullyConnectedLayer.
int32_t m_EllipsisMask
Ellipsis mask value.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:199
static ITfLiteParser * CreateRaw(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
#define CHECK_VALID_SIZE(ACTUAL,...)
uint32_t m_NumClasses
Number of classes.
#define CHECKED_NON_NEGATIVE(VALUE)
uint32_t m_PadTop
Padding top value in the height dimension.
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
A StandInDescriptor for the StandIn layer.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:33
bool m_UseRegularNms
Use Regular NMS.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< unsigned int > m_BlockShape
Block shape value.
std::vector< int > m_Stride
Stride values for the input that will be sliced.
bool IsActivationSupported(const BackendId &backend, const TensorInfo &input, const TensorInfo &output, const ActivationDescriptor &descriptor, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:20
#define CHECK_LOCATION()
Definition: Exceptions.hpp:192
uint32_t m_NumInputs
Number of input tensors.
A SliceDescriptor for the SliceLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
static std::vector< int32_t > & GetInputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_TENSOR_PTR(TENSOR_PTR)
float m_ScaleH
Center size encoding scale height.
std::vector< int > m_End
End values for the input that will be sliced.
A SpaceToBatchNdDescriptor for the SpaceToBatchNdLayer.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp:45
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadLeft
Padding left value in the width dimension.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
static armnn::TensorInfo OutputShapeOfSqueeze(const std::vector< uint32_t > &squeezeDims, const armnn::TensorInfo &inputTensorInfo)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX)
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:90
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_Crops
The values to crop from the input dimension.
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:43
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
const tflite::BufferT * BufferRawPtr
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
ResizeMethod
Definition: Types.hpp:103
A MeanDescriptor for the MeanLayer.
virtual std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const override
Return the input tensor names for a given subgraph.
armnn::BindingPointInfo BindingPointInfo
uint32_t m_PadRight
Padding right value in the width dimension.
A TransposeDescriptor for the TransposeLayer.
A StridedSliceDescriptor for the StridedSliceLayer.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
virtual const char * GetName() const =0
Returns the name of the layer.
virtual std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const override
Return the output tensor names for a given subgraph.
float m_ScaleY
Center size encoding scale y.
float m_NmsScoreThreshold
NMS score threshold.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:101
virtual int Connect(IInputSlot &destination)=0
const char * m_Function
Definition: Exceptions.hpp:16
A Pooling2dDescriptor for the Pooling2dLayer.
armnn::Runtime::CreationOptions::ExternalProfilingOptions options
std::unique_ptr< tflite::ModelT > ModelPtr
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:92
#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID)
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:47
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition: Permute.cpp:98
A SoftmaxDescriptor for the SoftmaxLayer.
const tflite::TensorT * TensorRawPtr
DataLayout::NCHW DataLayout::NCHW DataLayout::NHWC DataLayout::NHWC true
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
Definition: Descriptors.hpp:43
std::unique_ptr< tflite::OperatorT > OperatorPtr
An input connection slot for a layer.
Definition: INetwork.hpp:24
TfLiteParser(const armnn::Optional< ITfLiteParser::TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
constexpr unsigned int MaxNumOfTensorDimensions
Definition: Types.hpp:18
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Definition: Tensor.hpp:93
ActivationFunction
Definition: Types.hpp:55
std::vector< TensorIdRawPtr > TensorIdRawPtrVector
uint32_t m_PadRight
Padding right value in the width dimension.