blob: 6853512c8f39b9ae817228a236c0bee83a35e3eb [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
Matteo Martincighe011d202019-11-28 11:35:47 +00005
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "TfLiteParser.hpp"
7
8#include <armnn/ArmNN.hpp>
9#include <armnn/Exceptions.hpp>
Derek Lamberti08446972019-11-26 16:38:31 +000010#include <armnn/Logging.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011#include <armnn/TypesUtils.hpp>
12#include <boost/filesystem.hpp>
13
14// armnnUtils:
Matteo Martincighe011d202019-11-28 11:35:47 +000015#include <armnnUtils/Permute.hpp>
16
Sadik Armagan479045b2018-10-01 11:51:37 +010017#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010018#include <VerificationHelpers.hpp>
19
20// The generated code based on the Tf Lite schema:
21#include <schema_generated.h>
22
Matteo Martincighe011d202019-11-28 11:35:47 +000023#include <flatbuffers/flexbuffers.h>
24
telsoa01c577f2c2018-08-31 09:22:23 +010025#include <boost/core/ignore_unused.hpp>
26#include <boost/assert.hpp>
27#include <boost/format.hpp>
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +010028#include <boost/numeric/conversion/cast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010029
30#include <fstream>
31#include <algorithm>
32#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010033#include <numeric>
telsoa01c577f2c2018-08-31 09:22:23 +010034
35using namespace armnn;
36using armnn::CheckLocation;
37namespace armnnTfLiteParser
38{
39namespace
40{
jimfly01c25411c2018-11-14 17:47:22 +000041
telsoa01c577f2c2018-08-31 09:22:23 +010042const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
43
44void CheckSubgraph(const TfLiteParser::ModelPtr & model,
45 size_t subgraphIndex,
46 const CheckLocation & location)
47{
48 if (model.get() == nullptr)
49 {
50 throw ParseException(
51 boost::str(
52 boost::format("%1% was called with invalid (null) model. "
53 "Possible reason is that the model is not yet loaded and Unpack(ed). "
54 "subgraph:%2% at %3%") %
55 location.m_Function %
56 subgraphIndex %
57 location.FileLine()));
58 }
59 else if (subgraphIndex >= model->subgraphs.size())
60 {
61 throw ParseException(
62 boost::str(
63 boost::format("%1% was called with an invalid subgraph index. "
64 "subgraph:%2% at %3%") %
65 location.m_Function %
66 subgraphIndex %
67 location.FileLine()));
68 }
69}
70
71#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
72 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
73
74void CheckModel(const TfLiteParser::ModelPtr & model,
75 size_t subgraphIndex,
76 size_t operatorIndex,
77 const CheckLocation & location)
78{
79 if (model.get() == nullptr)
80 {
81 throw ParseException(
82 boost::str(
83 boost::format("%1% was called with invalid (null) model. "
84 "Possible reason is that the model is not yet loaded and Unpack(ed). "
85 "subgraph:%2% operator:%3% at %4%") %
86 location.m_Function %
87 subgraphIndex %
88 operatorIndex %
89 location.FileLine()));
90 }
91 else if (subgraphIndex >= model->subgraphs.size())
92 {
93 throw ParseException(
94 boost::str(
95 boost::format("%1% was called with an invalid subgraph index. "
96 "subgraph:%2% operator:%3% at %4%") %
97 location.m_Function %
98 subgraphIndex %
99 operatorIndex %
100 location.FileLine()));
101 }
102 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
103 operatorIndex != VIRTUAL_OPERATOR_ID)
104 {
105 throw ParseException(
106 boost::str(
107 boost::format("%1% was called with an invalid operator index. "
108 "subgraph:%2% operator:%3% at %4%") %
109 location.m_Function %
110 subgraphIndex %
111 operatorIndex %
112 location.FileLine()));
113 }
114}
115
116#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
117 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
118
119void CheckTensor(const TfLiteParser::ModelPtr & model,
120 size_t subgraphIndex,
121 size_t tensorIndex,
122 const CheckLocation & location)
123{
124 // not checking model, because I assume CHECK_MODEL already run
125 // and checked that. An assert would do.
126 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
127
128 // also subgraph index should be checked by CHECK_MODEL so
129 // I only add an assert here
130 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
131
132 // the tensor index is the only one to check here
133 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
134 {
135 throw ParseException(
136 boost::str(
137 boost::format("%1% was called with an invalid tensor index. "
138 "subgraph:%2% tensor:%3% at %4%") %
139 location.m_Function %
140 subgraphIndex %
141 tensorIndex %
142 location.FileLine()));
143 }
144}
145
146#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
147 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
148
149void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
150 const CheckLocation & location)
151{
152 if (rawPtr == nullptr)
153 {
154 throw ParseException(
155 boost::str(
156 boost::format("%1% was called with a null tensor pointer. "
157 "at %2%") %
158 location.m_Function %
159 location.FileLine()));
160
161 }
162}
163
164#define CHECK_TENSOR_PTR(TENSOR_PTR) \
165 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
166
167void CheckBuffer(const TfLiteParser::ModelPtr & model,
168 size_t bufferIndex,
169 const CheckLocation & location)
170{
171 if (model.get() == nullptr)
172 {
173 throw ParseException(
174 boost::str(
175 boost::format("%1% was called with invalid (null) model. "
176 "Possible reason is that the model is not yet loaded and Unpack(ed). "
177 "buffer:%2% at %3%") %
178 location.m_Function %
179 bufferIndex %
180 location.FileLine()));
181 }
182 else if (bufferIndex >= model->buffers.size())
183 {
184 throw ParseException(
185 boost::str(
186 boost::format("%1% was called with an invalid buffer index. "
187 "buffer index:%2% at %3%") %
188 location.m_Function %
189 bufferIndex %
190 location.FileLine()));
191 }
192 else if (model->buffers[bufferIndex].get() == nullptr)
193 {
194 throw ParseException(
195 boost::str(
196 boost::format("The buffer #%1% is null. %3%") %
197 bufferIndex %
198 location.AsString()));
199 }
200}
201
202#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
203 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
204
205void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
206 const armnn::TensorInfo & tensorInfo,
207 uint32_t bufferId,
208 const CheckLocation & location)
209{
210 if (bufferPtr == nullptr)
211 {
212 throw ParseException(
213 boost::str(
214 boost::format("BufferPtr is null for buffer:%1%. %2%") %
215 bufferId %
216 location.AsString()));
217 }
218 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
219 tensorInfo.GetNumBytes() > bufferPtr->data.size())
220 {
221 std::stringstream ss;
222 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
223 << "For tensor: " << tensorInfo.GetShape()
224 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
225 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
226 throw ParseException(ss.str());
227 }
228}
229
230#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
231 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
232
233bool IsActivationSupported(tflite::ActivationFunctionType activationType)
234{
235 switch(activationType)
236 {
237 case tflite::ActivationFunctionType_NONE:
238 case tflite::ActivationFunctionType_RELU:
239 case tflite::ActivationFunctionType_RELU6:
240 case tflite::ActivationFunctionType_TANH:
241 {
242 return true;
243 }
244 default:
245 {
246 return false;
247 }
248 }
249}
250
251#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
252 do { \
253 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
254 { \
255 throw ParseException( \
256 boost::str( \
257 boost::format("TfLite parser doesn't suppport fused activation: " \
258 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
259 OPTION->fused_activation_function % \
260 tflite::EnumNameActivationFunctionType(\
261 OPTION->fused_activation_function) % \
262 __func__ % \
263 SUBGRAPH_INDEX % \
264 OPERATOR_INDEX % \
265 CHECK_LOCATION().FileLine())); \
266 } \
267 } while(false)
268
269
270std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
271{
272 std::vector<unsigned int> result;
273 result.reserve(in.size());
274 for (auto & i : in)
275 {
276 result.push_back(CHECKED_NON_NEGATIVE(i));
277 }
278 return result;
279}
280
281void CalcPadding(uint32_t inputSize,
282 uint32_t filterSize,
283 uint32_t stride,
Pablo Tellof0bd6832019-04-26 17:58:13 +0100284 uint32_t dilation,
telsoa01c577f2c2018-08-31 09:22:23 +0100285 uint32_t& paddingFront,
286 uint32_t& paddingBack,
287 tflite::Padding padding)
288{
289 paddingFront = 0;
290 paddingBack = 0;
291 if (padding == tflite::Padding_SAME)
292 {
293 uint32_t outputSize = (inputSize + stride - 1) / stride;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100294 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
295 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
telsoa01c577f2c2018-08-31 09:22:23 +0100296 if (temp > inputSize)
297 {
298 paddingFront = (temp - inputSize) / 2;
299 paddingBack = (temp - inputSize) - paddingFront;
300 }
301 }
302}
303
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000304armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes)
telsoa01c577f2c2018-08-31 09:22:23 +0100305{
306 armnn::DataType type;
307 CHECK_TENSOR_PTR(tensorPtr);
308
309 switch (tensorPtr->type)
310 {
311 case tflite::TensorType_UINT8:
312 type = armnn::DataType::QuantisedAsymm8;
313 break;
314 case tflite::TensorType_FLOAT32:
315 type = armnn::DataType::Float32;
316 break;
Finn Williamsed66d142019-12-06 09:55:55 +0000317 case tflite::TensorType_INT8:
318 type = armnn::DataType::QSymmS8;
319 break;
320 case tflite::TensorType_INT16:
321 type = armnn::DataType::QuantisedSymm16;
322 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100323 case tflite::TensorType_INT32:
324 type = armnn::DataType::Signed32;
325 break;
326
327 default:
328 {
329 CheckLocation location = CHECK_LOCATION();
330 throw ParseException(
331 boost::str(
332 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
333 tensorPtr->type %
334 tflite::EnumNameTensorType(tensorPtr->type) %
335 tensorPtr->name %
336 location.AsString()));
337 }
338 }
339
340 float quantizationScale = 0.0f;
341 int32_t quantizationOffset = 0;
342
343 if (tensorPtr->quantization.get())
344 {
345 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
346 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
347
348 if (tensorPtr->quantization->scale.size() == 1)
349 {
350 quantizationScale = tensorPtr->quantization->scale[0];
351 }
352 if (tensorPtr->quantization->zero_point.size() == 1)
353 {
354 // NOTE: we lose precision here when converting from 64 bit to 32
355 // but this is what we support at the monent in ArmNN
356 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
357 }
358 }
359
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100360 std::vector<unsigned int> safeShape = shapes;
361 if (safeShape.size() == 0)
362 {
363 safeShape.push_back(1);
364 }
365
telsoa01c577f2c2018-08-31 09:22:23 +0100366 // two statements (on purpose) for easier debugging:
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100367 armnn::TensorInfo result(static_cast<unsigned int>(safeShape.size()),
368 safeShape.data(),
telsoa01c577f2c2018-08-31 09:22:23 +0100369 type,
370 quantizationScale,
371 quantizationOffset);
372 return result;
373}
374
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000375armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
376{
377 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
378 return ToTensorInfo(tensorPtr, dimensions);
379}
380
telsoa01c577f2c2018-08-31 09:22:23 +0100381template<typename T>
382std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
383CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
384 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000385 armnn::TensorInfo& tensorInfo,
386 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100387{
388 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
389 BOOST_ASSERT_MSG(bufferPtr != nullptr,
390 boost::str(
391 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
392
393 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000394
395 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
396 {
397 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000398 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
399 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000400 }
401 else
402 {
403 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
404 }
405
telsoa01c577f2c2018-08-31 09:22:23 +0100406 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
407}
408
telsoa01c577f2c2018-08-31 09:22:23 +0100409armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
410{
411 // generate the binding id by shifting the tensor id by 8 bit
412 // and add the subgraph id, which allows 256 subgraphs
413 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
414}
415
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000416bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
417{
418 const unsigned int actualSize = actual.GetNumDimensions();
419 if (actualSize != expected.size())
420 {
421 return false;
422 }
423
424 for (unsigned int i = 0u; i < actualSize; i++)
425 {
426 if (expected[i] < 0 ||
427 actual[i] != static_cast<unsigned int>(expected[i]))
428 {
429 return false;
430 }
431 }
432
433 return true;
434}
435
telsoa01c577f2c2018-08-31 09:22:23 +0100436} // <anonymous>
437
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100438TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
439: m_Options(options)
440, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100441, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
442{
443 // register supported operators
Sadik Armagana3b31f02019-12-05 09:08:53 +0000444 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
445 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
446 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
447 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
448 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Finn Williamsed66d142019-12-06 09:55:55 +0000449 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000450 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
451 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
452 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
453 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
454 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
455 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
456 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
457 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
458 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
459 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
460 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
461 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
462 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
463 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
464 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
465 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
466 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
467 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
468 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
469 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
470 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
471 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
472 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
473 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
474 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
475 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
476 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
477 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100478
479 // register supported custom operators
480 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100481}
482
483void TfLiteParser::ResetParser()
484{
485 m_Network = armnn::INetworkPtr(nullptr, nullptr);
486 m_Model = nullptr;
487 m_SubgraphConnections.clear();
488}
489
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200490void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
491 size_t operatorIndex,
492 IConnectableLayer *layer)
493{
494 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
495 BOOST_ASSERT(layer != nullptr);
496
Derek Lambertiff05cc52019-04-26 13:05:17 +0100497 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
498 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200499
500 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
501
502 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100503 TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200504 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100505 TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200506
507 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
508 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
509
510 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
511 {
512 uint32_t id = reshapedInputId;
513 reshapedInputId = inputId;
514 inputId = id;
515
516 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
517 inputTensorInfo = ToTensorInfo(tensorPtr);
518 }
519
520 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
521
522 std::vector<unsigned> reshapedDim;
523 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
524 {
525 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
526 }
527
528 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
529 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
530
531 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
532
533 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
534 armnn::ReshapeDescriptor desc;
535 desc.m_TargetShape = reshapedTensorInfo.GetShape();
536 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
537
538 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
539 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
540
541 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
542
543 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
544 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
545}
546
telsoa01c577f2c2018-08-31 09:22:23 +0100547INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
548{
549 ResetParser();
550 m_Model = LoadModelFromFile(graphFile);
551 return CreateNetworkFromModel();
552}
553
554INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
555{
556 ResetParser();
557 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
558 return CreateNetworkFromModel();
559}
560
561INetworkPtr TfLiteParser::CreateNetworkFromModel()
562{
563 m_Network = INetwork::Create();
564 BOOST_ASSERT(m_Model.get() != nullptr);
565
566 bool failedToCreate = false;
567 std::stringstream errors;
568
569 if (m_Model->subgraphs.size() != 1)
570 {
571 throw ParseException(
572 boost::str(
573 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
574 m_Model->subgraphs.size() %
575 CHECK_LOCATION().AsString()));
576 }
577
578 size_t subgraphIndex = 0;
Derek Lambertiff05cc52019-04-26 13:05:17 +0100579 for (SubgraphPtr const & subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100580 {
581 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
582
583 size_t operatorIndex = 0;
584 for (OperatorPtr const & op : subgraph->operators)
585 {
586 try
587 {
telsoa01c577f2c2018-08-31 09:22:23 +0100588 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
589 auto builtinCode = opCodePtr->builtin_code;
590
591 if (builtinCode > tflite::BuiltinOperator_MAX)
592 {
593 throw ParseException(
594 boost::str(
595 boost::format("Operator code %1% is out of range 0-%2%. "
596 "subgraph:%3% operator idx:%4%. %5%") %
597 builtinCode %
598 tflite::BuiltinOperator_MAX %
599 subgraphIndex %
600 operatorIndex %
601 CHECK_LOCATION().AsString()));
602 }
603
604 // lookup and call the parser function
605 auto & parserFunction = m_ParserFunctions[builtinCode];
606 (this->*parserFunction)(subgraphIndex, operatorIndex);
607 }
608 catch (const ParseException& e)
609 {
610 failedToCreate = true;
611 std::stringstream errorString;
612
613 errorString << "Failed to parse operator #" << operatorIndex
614 << " within subgraph #" << subgraphIndex
615 << " error: " << e.what();
Derek Lamberti08446972019-11-26 16:38:31 +0000616 ARMNN_LOG(error) << errorString.str();
telsoa01c577f2c2018-08-31 09:22:23 +0100617
618 errors << errorString.str() << "\n";
619 }
620 ++operatorIndex;
621 }
622
623 SetupInputLayers(subgraphIndex);
624 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200625 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100626
627 ++subgraphIndex;
628 }
629
630 if (failedToCreate)
631 {
632 // we can skip everything and let the outer exception handler deal with the error
633 throw ParseException(errors.str());
634 }
635
636 // establish the connections from the layer outputs to the inputs of the subsequent layers
637 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
638 {
639 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
640 {
641 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
642 {
643 for (size_t inputSlotIdx = 0;
644 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
645 ++inputSlotIdx)
646 {
647 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
648 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
649 }
650 }
651 }
652 }
653
654 return std::move(m_Network);
655}
656
657void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
658 size_t tensorIndex,
659 armnn::IOutputSlot* slot)
660{
661 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
662 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
663 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
664
665 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
666
667 // assuming there is only one producer for that tensor
668 if (tensorSlots.outputSlot != nullptr)
669 {
670 throw ParseException(boost::str(
671 boost::format("Another layer has already registered itself as the producer of "
672 "subgraph:%1% tensor:%2% %3%") %
673 subgraphIndex %
674 tensorIndex %
675 CHECK_LOCATION().AsString()));
676 }
677
678 tensorSlots.outputSlot = slot;
679}
680
681void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
682 size_t tensorIndex,
683 armnn::IInputSlot* slot)
684{
685 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
686 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
687 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
688
689 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
690 tensorSlots.inputSlots.push_back(slot);
691}
692
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100693void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
694{
695 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
696
697 // NOTE: By default we presume the custom operator is not supported
698 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
699
700 // Identify custom code defined for custom operator
701 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
702 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
703
704 // Find parser function that correspondes to custom code (if any)
705 auto iterator = m_CustomParserFunctions.find(customCode);
706 if (iterator != m_CustomParserFunctions.end())
707 {
708 customParserFunction = iterator->second;
709 }
710
711 // Run parser function
712 (this->*customParserFunction)(subgraphIndex, operatorIndex);
713}
714
telsoa01c577f2c2018-08-31 09:22:23 +0100715void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
716{
717 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100718
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100719 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
720
721 auto opcodeIndex = operatorPtr->opcode_index;
722 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
723
724 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
725 {
726 // Do not add StandInLayer, throw ParseException instead
727 throw ParseException(
728 boost::str(
729 boost::format("Operator not supported. "
730 "subgraph:%1% operator:%2% "
731 "opcode_index:%3% opcode:%4% / %5% %6%") %
732 subgraphIndex %
733 operatorIndex %
734 opcodeIndex %
735 opcode %
736 tflite::EnumNameBuiltinOperator(opcode) %
737 CHECK_LOCATION().AsString()));
738 }
739
740 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
741 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
742
743 const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
744 const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
745
746 StandInDescriptor descriptor(numInputs, numOutputs);
747 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
748
749 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
750 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
751 for (unsigned int i = 0u; i < numOutputs; ++i)
752 {
753 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
754 }
755
756 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
757 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
758
759 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
760 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100761}
762
telsoa01c577f2c2018-08-31 09:22:23 +0100763void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
764{
765 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
766
767 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
768 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
769
770 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
771
772 Convolution2dDescriptor desc;
773 desc.m_BiasEnabled = false;
774 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
775 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000776 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100777 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
778 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000779
telsoa01c577f2c2018-08-31 09:22:23 +0100780 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
781 CHECK_VALID_SIZE(inputs.size(), 2, 3);
782
783 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
784 CHECK_VALID_SIZE(outputs.size(), 1);
785
786 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
787 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
788
789 // assuming input is NHWC
790 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
791 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
792
793 // assuming the filter is OHWI : Output, H, W, Input
794 // which is essentially the same as NHWC
795 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
796 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
797
Pablo Tellof0bd6832019-04-26 17:58:13 +0100798 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
799 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
800 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
801 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100802
Matteo Martincigh747ef822018-12-18 09:26:39 +0000803 auto filterTensorAndData = CreateConstTensor(inputs[1],
804 filterTensorInfo,
805 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100806 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100807
808 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
809
810 if (inputs.size() == 3)
811 {
812 desc.m_BiasEnabled = true;
813 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000814 auto biasTensorAndData = CreateConstTensor(inputs[2],
815 biasTensorInfo,
816 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100817 layer = m_Network->AddConvolution2dLayer(desc,
818 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100819 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100820 layerName.c_str());
821 }
822 else
823 {
824 layer = m_Network->AddConvolution2dLayer(desc,
825 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100826 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100827 layerName.c_str());
828 }
829
830 BOOST_ASSERT(layer != nullptr);
831
telsoa01c577f2c2018-08-31 09:22:23 +0100832 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000833 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100834
835 // register the input connection slots for the layer, connections are made after all layers have been created
836 // only the tensors for the inputs are relevant, exclude the const tensors
837 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000838 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100839
jimfly01c25411c2018-11-14 17:47:22 +0000840 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100841 // register the output connection slots for the layer, connections are made after all layers have been created
842 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
843 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
844}
845
846void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
847{
848 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
849
850 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
851 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
852
853 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
854
855 DepthwiseConvolution2dDescriptor desc;
856 desc.m_BiasEnabled = false;
857 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
858 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000859 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100860 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100861
862 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
863 CHECK_VALID_SIZE(inputs.size(), 2, 3);
864 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
865 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100866 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
867 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000868
telsoa01c577f2c2018-08-31 09:22:23 +0100869 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
870 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
871
Matteo Martincigh747ef822018-12-18 09:26:39 +0000872 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100873 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
874 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000875
876 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100877 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
878 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
879
Matteo Martincigh747ef822018-12-18 09:26:39 +0000880 // Reshape weights as [ H, W, I, M ]
881 filterTensorInfo.SetShape({ filterHeight,
882 filterWidth,
883 inputTensorInfo.GetShape()[3],
884 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
885
886 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
887 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
888
Pablo Tellof0bd6832019-04-26 17:58:13 +0100889 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
890 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
891 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
892 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100893
Matteo Martincigh747ef822018-12-18 09:26:39 +0000894 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100895 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100896 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
897
898 if (inputs.size() == 3)
899 {
900 desc.m_BiasEnabled = true;
901 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000902 auto biasTensorAndData = CreateConstTensor(inputs[2],
903 biasTensorInfo,
904 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100905 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
906 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100907 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100908 layerName.c_str());
909 }
910 else
911 {
912 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
913 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100914 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100915 layerName.c_str());
916 }
917 BOOST_ASSERT(layer != nullptr);
918
telsoa01c577f2c2018-08-31 09:22:23 +0100919 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000920 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100921
922 // register the input connection slots for the layer, connections are made after all layers have been created
923 // only the tensors for the inputs are relevant, exclude the const tensors
924 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000925 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100926
jimfly01c25411c2018-11-14 17:47:22 +0000927 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100928 // register the output connection slots for the layer, connections are made after all layers have been created
929 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
930 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
931}
932
Finn Williamsed66d142019-12-06 09:55:55 +0000933void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
934{
935 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
936
937 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
938 CHECK_VALID_SIZE(inputs.size(), 1);
939
940 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
941 CHECK_VALID_SIZE(outputs.size(), 1);
942
943 auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
944
945 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
946 BOOST_ASSERT(layer != nullptr);
947
948 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
949 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
950
951 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
952 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
953
954 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
955 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
956}
957
Keith Davis4cd29a02019-09-09 14:49:20 +0100958void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
959{
960 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
961
962 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +0100963 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +0100964
965 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
966 CHECK_VALID_SIZE(outputs.size(), 1);
967
968 armnn::IConnectableLayer* layer = nullptr;
969 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
970
971 PermuteDescriptor desc;
972
josh minorba424d22019-11-13 10:55:17 -0600973 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +0100974 {
975 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
976 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -0600977 auto numPermVecElements = permuteTensorInfo.GetNumElements();
978 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +0100979 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
980
josh minorba424d22019-11-13 10:55:17 -0600981 // permuteShape assumes Tf/Np permute vectors, we must translate to armnn expected form
982 // to do so we find the perm vector which would invert what a tf perm vector would do (ex 3,0,1,2 -> 1,2,3,0)
983 std::vector<unsigned int> armnnPermuteShape(numPermVecElements);
984 std::vector<unsigned int>::iterator it;
985 for (unsigned int i = 0u; i < numPermVecElements; ++i)
986 {
987 it = std::find(permuteShape.begin(), permuteShape.end(), i);
988 armnnPermuteShape[i] = static_cast<unsigned int>(std::distance(permuteShape.begin(), it));
989 }
Kevin May85d92602019-09-27 17:21:06 +0100990
josh minorba424d22019-11-13 10:55:17 -0600991 PermutationVector permutationVector(armnnPermuteShape.data(), permuteTensorInfo.GetNumElements());
992
993 desc = PermuteDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +0100994 }
995
Keith Davis4cd29a02019-09-09 14:49:20 +0100996 layer = m_Network->AddPermuteLayer(desc, layerName.c_str());
997
998 BOOST_ASSERT(layer != nullptr);
999
1000 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1001 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1002
1003 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1004 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1005
1006 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1007 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1008}
1009
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001010void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1011{
1012 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1013
1014 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1015 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1016
1017 TransposeConvolution2dDescriptor desc;
1018 desc.m_BiasEnabled = false;
1019 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1020 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1021 desc.m_DataLayout = armnn::DataLayout::NHWC;
1022
1023 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001024 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001025
1026 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1027 CHECK_VALID_SIZE(outputs.size(), 1);
1028
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001029 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001030 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1031
1032 // TfLite uses NHWC tensors
1033 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1034 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1035
1036 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1037 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1038
1039 CalcPadding(inputHeight,
1040 filterHeight,
1041 desc.m_StrideY,
1042 1, // DilationY
1043 desc.m_PadTop,
1044 desc.m_PadBottom,
1045 options->padding);
1046
1047 CalcPadding(inputWidth,
1048 filterWidth,
1049 desc.m_StrideX,
1050 1, // DilationX
1051 desc.m_PadLeft,
1052 desc.m_PadRight,
1053 options->padding);
1054
1055 auto filterTensorAndData = CreateConstTensor(inputs[1],
1056 filterTensorInfo,
1057 armnn::Optional<armnn::PermutationVector&>());
1058
1059 armnn::IConnectableLayer* layer = nullptr;
1060 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1061
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001062 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1063 filterTensorAndData.first,
1064 EmptyOptional(),
1065 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001066
1067 BOOST_ASSERT(layer != nullptr);
1068
1069 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1070 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1071
1072 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1073 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001074 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001075
1076 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1077 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1078}
1079
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001080void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1081{
1082 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1083}
1084
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001085void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1086{
1087 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1088
1089 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1090 CHECK_VALID_SIZE(inputs.size(), 3);
1091
1092 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1093 CHECK_VALID_SIZE(outputs.size(), 1);
1094
1095 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1096 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1097
1098 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1099 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1100
1101 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1102 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1103
1104 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1105 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1106
1107 size_t step = 2;
1108 std::vector<std::pair<unsigned int, unsigned int>> crops;
1109 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1110 {
1111 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1112 }
1113
1114 armnn::BatchToSpaceNdDescriptor desc;
1115 desc.m_BlockShape = blockShape;
1116 desc.m_Crops = crops;
1117 desc.m_DataLayout = armnn::DataLayout::NHWC;
1118
1119 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1120
1121 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1122 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1123
1124 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1125
1126 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1127 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1128
1129 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1130 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1131}
1132
Matthew Jackson28c94572019-07-18 10:47:03 +01001133void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1134{
1135 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1136
1137 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1138 CHECK_VALID_SIZE(inputs.size(), 1);
1139
1140 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1141 CHECK_VALID_SIZE(outputs.size(), 1);
1142
1143 L2NormalizationDescriptor desc;
1144 desc.m_DataLayout = armnn::DataLayout::NHWC;
1145 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1146 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1147
1148 BOOST_ASSERT(layer != nullptr);
1149
1150 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1151 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1152
1153 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1154 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1155
1156 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1157 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1158}
1159
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001160void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1161{
1162 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1163}
1164
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001165void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1166{
1167 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1168
1169 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1170 CHECK_VALID_SIZE(inputs.size(), 2);
1171
1172 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1173 CHECK_VALID_SIZE(outputs.size(), 1);
1174
1175 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1176 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1177
1178 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1179 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1180
1181 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1182 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1183
1184 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1185 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1186 {
1187 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1188 }
1189 else
1190 {
1191 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1192 }
1193
1194 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1195 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1196}
1197
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001198void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1199{
1200 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1201
1202 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1203 CHECK_VALID_SIZE(inputs.size(), 2);
1204
1205 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1206 CHECK_VALID_SIZE(outputs.size(), 1);
1207
1208 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1209 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1210
1211 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1212 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1213
1214 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1215 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1216
1217 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1218 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1219 {
1220 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1221 }
1222 else
1223 {
1224 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1225 }
1226
1227 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1228 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1229}
1230
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001231void TfLiteParser::ParsePool(size_t subgraphIndex,
1232 size_t operatorIndex,
1233 PoolingAlgorithm algorithm)
1234{
1235 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1236
1237 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1238 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1239
1240 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1241
1242 std::string layerName;
1243
1244 switch (algorithm)
1245 {
1246 case PoolingAlgorithm::Average:
1247 layerName =
1248 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1249 break;
1250 case PoolingAlgorithm::Max:
1251 layerName =
1252 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1253 break;
1254 default:
1255 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1256 }
1257
1258 Pooling2dDescriptor desc;
1259
1260 desc.m_PoolType = algorithm;
1261 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1262 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1263 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1264 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1265 desc.m_PaddingMethod = PaddingMethod::Exclude;
1266 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001267 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001268
1269 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1270 CHECK_VALID_SIZE(inputs.size(), 1);
1271 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1272
1273 // assuming input is NHWC
1274 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1275 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1276
Pablo Tellof0bd6832019-04-26 17:58:13 +01001277 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1278 desc.m_PadTop, desc.m_PadBottom, options->padding);
1279 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1280 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001281
1282 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1283 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001284
1285 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1286
1287 BOOST_ASSERT(layer != nullptr);
1288
jimfly01c25411c2018-11-14 17:47:22 +00001289 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1290 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001291
1292 // register the input connection slots for the layer, connections are made after all layers have been created
1293 // only the tensors for the inputs are relevant, exclude the const tensors
1294 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001295 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001296
jimfly01c25411c2018-11-14 17:47:22 +00001297 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001298 // register the output connection slots for the layer, connections are made after all layers have been created
1299 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1300 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1301}
1302
josh minorba424d22019-11-13 10:55:17 -06001303void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1304{
1305 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1306
1307 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1308 CHECK_VALID_SIZE(inputs.size(), 3);
1309 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1310 CHECK_VALID_SIZE(outputs.size(), 1);
1311
1312 SliceDescriptor desc;
1313
1314 // set begin tensor info for slice descriptor
1315 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1316 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1317
1318 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1319 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1320
1321 // set size tensor info for slice descriptor
1322 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1323 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1324
1325 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1326 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1327 desc = SliceDescriptor(begin, size);
1328
1329 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1330 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1331
1332 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1333 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1334
1335 // register the input connection slots for the layer, connections are made after all layers have been created
1336 // only the tensors for the inputs are relevant, exclude the const tensors
1337 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1338 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1339
1340 // register the output connection slots for the layer, connections are made after all layers have been created
1341 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1342 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1343}
1344
telsoa01c577f2c2018-08-31 09:22:23 +01001345void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1346{
1347 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1348 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1349 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1350
1351 SoftmaxDescriptor desc;
1352 desc.m_Beta = options->beta;
1353
1354 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1355 CHECK_VALID_SIZE(inputs.size(), 1);
1356 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1357 CHECK_VALID_SIZE(outputs.size(), 1);
1358
1359 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1360 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1361
1362 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1363 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1364
1365 // register the input connection slots for the layer, connections are made after all layers have been created
1366 // only the tensors for the inputs are relevant, exclude the const tensors
1367 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1368 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1369
1370 // register the output connection slots for the layer, connections are made after all layers have been created
1371 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1372 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1373}
1374
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001375void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1376{
1377 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1378
1379 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1380 CHECK_VALID_SIZE(inputs.size(), 3);
1381
1382 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1383 CHECK_VALID_SIZE(outputs.size(), 1);
1384
1385 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1386 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1387
1388 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1389 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1390
1391 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1392 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1393
1394 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1395 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1396
1397 size_t step = 2;
1398 std::vector<std::pair<unsigned int, unsigned int>> padList;
1399 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1400 {
1401 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1402 }
1403
1404 armnn::SpaceToBatchNdDescriptor desc;
1405 desc.m_BlockShape = blockShape;
1406 desc.m_PadList = padList;
1407 desc.m_DataLayout = armnn::DataLayout::NHWC;
1408
1409 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1410
1411 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1412 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1413
1414 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1415
1416 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1417 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1418
1419 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1420 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1421}
1422
telsoa01c577f2c2018-08-31 09:22:23 +01001423armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1424 const armnn::TensorInfo & inputTensorInfo)
1425{
1426 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1427 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1428 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1429
1430 if (inputTensorInfo.GetNumDimensions() > 4)
1431 {
1432 std::stringstream ss;
1433 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1434 << " shape:" << inputTensorInfo.GetShape() << " "
1435 << CHECK_LOCATION().AsString();
1436 throw ParseException(ss.str());
1437 }
1438
1439 if (squeezeDims.empty())
1440 {
1441 squeezeDims.assign(dimensionSequence,
1442 dimensionSequence+inputTensorInfo.GetNumDimensions());
1443 }
1444
1445 std::vector<uint32_t> outputDims;
1446 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1447 {
1448 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1449 auto currentDimension = inputTensorInfo.GetShape()[i];
1450 if (skipSqueeze || currentDimension != 1)
1451 {
1452 outputDims.push_back(currentDimension);
1453 }
1454 }
1455
1456 if (outputDims.size() > 4)
1457 {
1458 std::stringstream ss;
1459 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1460 << " shape:" << inputTensorInfo.GetShape() << " "
1461 << CHECK_LOCATION().AsString();
1462 throw ParseException(ss.str());
1463 }
1464
1465 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1466 outputDims.data());
1467
1468 // we need to preserve the tensor type and the quantization data as well
1469 TensorInfo outTensorInfo = inputTensorInfo;
1470 outTensorInfo.SetShape(outShape);
1471
1472 return outTensorInfo;
1473}
1474
1475void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1476{
1477 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1478
1479 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1480 CHECK_VALID_SIZE(inputs.size(), 1);
1481
1482 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1483 CHECK_VALID_SIZE(outputs.size(), 1);
1484
1485 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1486 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1487
1488 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1489 armnn::TensorInfo outputTensorInfo =
1490 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1491 inputTensorInfo);
1492
1493 ReshapeDescriptor reshapeDesc;
1494 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1495
1496 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1497 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1498 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1499
1500 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1501 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1502
1503 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1504 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1505}
1506
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001507void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1508{
1509 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1510
1511 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1512 CHECK_VALID_SIZE(inputs.size(), 4);
1513
1514 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1515 CHECK_VALID_SIZE(outputs.size(), 1);
1516
1517 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1518 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1519
1520 StridedSliceDescriptor desc;
1521 desc.m_BeginMask = options->begin_mask;
1522 desc.m_EllipsisMask = options->ellipsis_mask;
1523 desc.m_EndMask = options->end_mask;
1524 desc.m_NewAxisMask = options->new_axis_mask;
1525 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1526 desc.m_DataLayout = armnn::DataLayout::NHWC;
1527
1528 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1529 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1530
1531 std::vector<int> begin(beginTensorInfo.GetNumElements());
1532 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1533
1534 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1535 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1536
1537 std::vector<int> end(endTensorInfo.GetNumElements());
1538 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1539
1540 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1541 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1542
1543 std::vector<int> stride(strideTensorInfo.GetNumElements());
1544 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1545
1546 desc.m_Begin = begin;
1547 desc.m_End = end;
1548 desc.m_Stride = stride;
1549
1550 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1551 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1552
1553 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1554 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1555
1556 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1557 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1558
1559 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1560 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1561}
1562
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001563void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1564{
1565 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1566
1567 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1568 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1569
1570 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1571 CHECK_VALID_SIZE(inputs.size(), 2);
1572
1573 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1574 CHECK_VALID_SIZE(outputs.size(), 1);
1575
1576 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1577 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1578
1579 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1580 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1581
1582 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1583 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1584
1585 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1586 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1587 {
1588 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1589 }
1590 else
1591 {
1592 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1593 }
1594
1595 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1596
1597 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1598 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1599}
1600
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001601void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1602{
1603 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1604
1605 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1606 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1607
1608 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1609 CHECK_VALID_SIZE(inputs.size(), 2);
1610
1611 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1612 CHECK_VALID_SIZE(outputs.size(), 1);
1613
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001614 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1615 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1616
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001617 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1618 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1619
1620 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1621 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1622
1623 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001624 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1625 {
1626 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1627 }
1628 else
1629 {
1630 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1631 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001632
1633 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1634
1635 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1636 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1637}
1638
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001639void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1640{
1641 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1642
1643 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1644 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1645
1646 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1647 CHECK_VALID_SIZE(inputs.size(), 2);
1648
1649 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1650 CHECK_VALID_SIZE(outputs.size(), 1);
1651
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001652 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1653 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1654
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001655 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1656 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1657
1658 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1659 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1660
1661 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001662 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1663 {
1664 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1665 }
1666 else
1667 {
1668 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1669 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001670
1671 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1672
1673 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1674 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1675}
1676
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001677void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1678{
1679 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1680
1681 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1682
1683 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1684 CHECK_VALID_SIZE(outputs.size(), 1);
1685
1686 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1687 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1688
1689 armnn::MeanDescriptor desc;
1690 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1691 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1692 desc.m_Axis = axis;
1693
1694 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1695 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1696
1697 desc.m_KeepDims =
1698 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1699 true : false;
1700
1701 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1702 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1703
1704 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1705
1706 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1707 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1708
1709 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1710 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1711}
1712
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001713void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1714{
1715 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1716
1717 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1718
1719 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1720 CHECK_VALID_SIZE(outputs.size(), 1);
1721
1722 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1723 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1724
1725 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1726 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1727
1728 size_t step = 2;
1729 armnn::PadDescriptor desc;
1730 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1731 {
1732 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1733 }
1734
1735 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1736 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1737
1738 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1739 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1740
1741 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1742 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1743
1744 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1745 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1746}
1747
Finn Williamsc42c3842019-01-22 14:18:11 +00001748
Sadik Armagan58f39192018-09-17 14:14:39 +01001749void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1750{
Finn Williamsc42c3842019-01-22 14:18:11 +00001751 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001752}
1753
1754void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1755{
Finn Williamsc42c3842019-01-22 14:18:11 +00001756 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1757}
Sadik Armagan58f39192018-09-17 14:14:39 +01001758
Finn Williamsc42c3842019-01-22 14:18:11 +00001759void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1760{
1761 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1762}
1763
Nina Drozd99851762019-04-09 09:37:38 +01001764void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1765{
1766 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1767}
1768
Finn Williamsc42c3842019-01-22 14:18:11 +00001769
1770void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1771{
1772 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001773 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1774 boost::ignore_unused(operatorPtr);
1775
1776 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1777 CHECK_VALID_SIZE(inputs.size(), 1);
1778
1779 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1780 CHECK_VALID_SIZE(outputs.size(), 1);
1781
Finn Williamsc42c3842019-01-22 14:18:11 +00001782 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001783 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001784 activationDesc.m_Function = activationType;
1785
1786 switch (activationType)
1787 {
1788 case ActivationFunction::ReLu:
1789 {
1790 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1791 break;
1792 }
1793 case ActivationFunction::BoundedReLu:
1794 {
1795 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1796 activationDesc.m_A = 6.0f;
1797 activationDesc.m_B = 0.0f;
1798 break;
1799 }
1800 case ActivationFunction::Sigmoid:
1801 {
1802 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1803 break;
1804 }
Nina Drozd99851762019-04-09 09:37:38 +01001805 case ActivationFunction::TanH:
1806 {
1807 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1808 activationDesc.m_A = 1.0f;
1809 activationDesc.m_B = 1.0f;
1810 break;
1811 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001812 default:
1813 {
1814 throw ParseException(
1815 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1816 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1817 }
1818 }
1819
1820 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001821
1822 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1823 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1824
1825 // register the input connection slots for the layer, connections are made after all layers have been created
1826 // only the tensors for the inputs are relevant, exclude the const tensors
1827 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1828 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1829
1830 // register the output connection slots for the layer, connections are made after all layers have been created
1831 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1832 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1833}
Sadikb94967b2018-09-19 15:30:00 +01001834armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1835 const std::vector<int32_t> & targetDimsIn)
1836{
1837 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1838 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1839
1840 if (stretchDim != targetDimsIn.end())
1841 {
1842 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1843 {
1844 throw ParseException(
1845 boost::str(
1846 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1847 }
1848
1849 auto targetNumElements =
1850 boost::numeric_cast<unsigned int>(
1851 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1852
1853 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1854 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1855 }
1856
1857 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1858
1859 TensorInfo reshapeInfo = inputTensorInfo;
1860 reshapeInfo.SetShape(outputShape);
1861
1862 return reshapeInfo;
1863}
1864
1865void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1866{
1867 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1868
1869 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001870
1871 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1872 CHECK_VALID_SIZE(outputs.size(), 1);
1873
1874 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1875 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1876
1877 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001878 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1879 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001880 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1881
kevmay0171972a82018-12-17 14:28:03 +00001882 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001883 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1884 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001885 {
1886 std::stringstream ss;
1887 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001888 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001889 << " does not equal output shape "
1890 << actualOutputTensorInfo.GetShape()
1891 << ": "
1892 << CHECK_LOCATION().AsString();
1893 throw ParseException(ss.str());
1894 }
1895
Sadikb94967b2018-09-19 15:30:00 +01001896 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001897 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001898
1899 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1900 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001901 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001902
1903 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1904 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1905
1906 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1907 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1908}
1909
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001910void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1911{
Sadik Armagana3b31f02019-12-05 09:08:53 +00001912 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
1913}
1914
1915void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
1916{
1917 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
1918}
1919
1920void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
1921{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001922 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1923
1924 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1925 CHECK_VALID_SIZE(inputs.size(), 2);
1926
1927 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1928 CHECK_VALID_SIZE(outputs.size(), 1);
1929
1930 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1931
1932 // Data for the parsed tensor args (size) must be stored locally.
1933 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1934
1935 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1936 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1937
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001938 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00001939 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001940 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001941 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1942 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001943
Sadik Armagana3b31f02019-12-05 09:08:53 +00001944 auto layerName = str(boost::format("Resize:"));
1945
1946 switch (resizeMethod)
1947 {
1948 case ResizeMethod::Bilinear:
1949 {
1950 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
1951 break;
1952 }
1953 case ResizeMethod::NearestNeighbor:
1954 {
1955 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
1956 break;
1957 }
1958 default:
1959 {
1960 throw ParseException(
1961 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
1962 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
1963 }
1964 }
1965
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001966 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001967
1968 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1969 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1970
1971 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1972 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1973
1974 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1975 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1976}
1977
Sadik Armagan479045b2018-10-01 11:51:37 +01001978void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1979{
1980 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1981
1982 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1983 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1984
1985 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1986
1987 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1988 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1989 CHECK_VALID_SIZE(outputs.size(), 1);
1990
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001991 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1992 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001993
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001994 const unsigned int concatDimInput = static_cast<unsigned int>(
1995 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001996
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001997 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1998 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001999
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002000 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01002001
2002 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2003 {
2004 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2005
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002006 // This set up concatDescriptor view origin
2007 armnnUtils::ProcessConcatInputTensorInfo(
2008 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01002009 }
2010
2011 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Jim Flynn906f9462019-05-10 13:55:21 +01002012 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Sadik Armagan479045b2018-10-01 11:51:37 +01002013
2014 BOOST_ASSERT(layer != nullptr);
2015
2016 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2017 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01002018
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002019 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01002020
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002021 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01002022
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002023 // add fused activation layer
2024 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01002025
Sadik Armagan479045b2018-10-01 11:51:37 +01002026 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2027 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2028}
2029
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002030void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2031{
2032 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2033
2034 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2035 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2036
2037 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2038
2039 FullyConnectedDescriptor desc;
2040 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002041 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002042
2043 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2044 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2045 CHECK_VALID_SIZE(outputs.size(), 1);
2046
2047 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2048
2049 // Fully Connected Layer accepts two dimensional weights input
2050 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2051 if (weightsDimension != 2)
2052 {
2053 throw ParseException(
2054 boost::str(
2055 boost::format(
2056 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2057 "Node %2%")
2058 % weightsDimension
2059 % CHECK_LOCATION().AsString()));
2060 }
2061
Matteo Martincigh747ef822018-12-18 09:26:39 +00002062 auto filterTensorAndData = CreateConstTensor(inputs[1],
2063 filterTensorInfo,
2064 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002065 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002066 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2067
2068 if (inputs.size() == 3)
2069 {
2070 desc.m_BiasEnabled = true;
2071 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002072 auto biasTensorAndData = CreateConstTensor(inputs[2],
2073 biasTensorInfo,
2074 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002075 layer = m_Network->AddFullyConnectedLayer(desc,
2076 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002077 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002078 layerName.c_str());
2079 }
2080 else
2081 {
2082 layer = m_Network->AddFullyConnectedLayer(desc,
2083 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002084 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002085 layerName.c_str());
2086 }
2087 BOOST_ASSERT(layer != nullptr);
2088
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002089 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2090
2091 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2092
2093 if (inputTensorInfo.GetNumDimensions() > 2)
2094 {
2095 // Add reshape to flatten to 2D [batch_size, input_size],
2096 // where "input_size" corresponds to the number of inputs to the layer,
2097 // matching the second dimension of weights,
2098 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2099 std::vector<unsigned int> reshapedDimensions(2);
2100 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2101 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2102
2103 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2104 {
2105 throw ParseException(
2106 boost::str(
2107 boost::format(
2108 "Failed to deduce input tensor shape from filter size %1%")
2109 % reshapedDimensions[1]
2110 % CHECK_LOCATION().AsString()));
2111 }
2112
2113 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2114 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2115
2116 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2117 armnn::ReshapeDescriptor desc;
2118 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2119 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2120
2121 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2122 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2123
2124 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2125 }
2126 else
2127 {
2128 // register the input connection slot for the layer
2129 // only the tensors for the inputs are relevant, exclude the const tensors
2130 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2131 }
2132
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002133 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2134 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2135
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002136 // we need to add the activation layer and fortunately we don't need to care about the data layout
2137 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2138 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002139
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002140 // register the output connection slots for the layer, connections are made after all layers have been created
2141 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2142 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2143}
2144
keidav011b3e2ea2019-02-21 10:07:37 +00002145void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2146{
2147 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2148
2149 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2150
2151 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2152 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2153 CHECK_VALID_SIZE(outputs.size(), 4);
2154
2155 // Obtain custom options from flexbuffers
2156 auto custom_options = operatorPtr->custom_options;
2157 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2158
2159 // Obtain descriptor information from tf lite
2160 DetectionPostProcessDescriptor desc;
2161 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2162 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2163 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2164 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2165 desc.m_NumClasses = m["num_classes"].AsUInt32();
2166 desc.m_ScaleH = m["h_scale"].AsFloat();
2167 desc.m_ScaleW = m["w_scale"].AsFloat();
2168 desc.m_ScaleX = m["x_scale"].AsFloat();
2169 desc.m_ScaleY = m["y_scale"].AsFloat();
2170
keidav0107d58c72019-02-26 11:57:39 +00002171 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002172 {
keidav0107d58c72019-02-26 11:57:39 +00002173 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002174 }
2175 if (!(m["detections_per_class"].IsNull()))
2176 {
2177 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2178 }
2179
2180 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2181 {
2182 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2183 "must be positive and less than or equal to 1.");
2184 }
2185
2186 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2187 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2188 armnn::Optional<armnn::PermutationVector&>());
2189
2190 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2191 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2192 layerName.c_str());
2193
2194 BOOST_ASSERT(layer != nullptr);
2195
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002196 // The model does not specify the output shapes.
2197 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2198 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2199 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2200 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2201 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2202 m_OverridenOutputShapes.push_back({ 1 });
2203
keidav011b3e2ea2019-02-21 10:07:37 +00002204 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2205 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002206 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002207 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2208 }
2209
2210 // Register the input connection slots for the layer, connections are made after all layers have been created
2211 // only the tensors for the inputs are relevant, exclude the const tensors
2212 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2213 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2214
2215 // Register the output connection slots for the layer, connections are made after all layers have been created
2216 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2217 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2218 outputTensorIndexes[1],
2219 outputTensorIndexes[2],
2220 outputTensorIndexes[3]});
2221}
2222
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002223/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2224void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2225{
2226 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2227
2228 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2229 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2230 CHECK_VALID_SIZE(outputs.size(), 1);
2231
2232 if (inputs.size() < 1)
2233 {
2234 throw ParseException("Pack must have at least one input.");
2235 }
2236
2237 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2238 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2239
2240 StackDescriptor desc;
2241 desc.m_Axis = static_cast<uint32_t>(options->axis);
2242 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2243
2244 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2245 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2246 desc.m_InputShape = inputTensorInfo.GetShape();
2247
2248 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2249 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2250
2251 BOOST_ASSERT(layer != nullptr);
2252
2253 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2254 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2255
2256 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2257 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2258
2259 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2260 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2261}
2262
Nina Drozd200e3802019-04-15 09:47:39 +01002263void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2264{
2265 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2266
2267 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2268 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2269
2270 // This unpackAxis indicates the axis to unpack
2271 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2272
2273 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2274 CHECK_VALID_SIZE(inputs.size(), 1);
2275
2276 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002277
2278 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2279 {
2280 throw ParseException(
2281 boost::str(
2282 boost::format(
2283 "The unpack axis: %1% cannot be greater than or equal to "
2284 "the number of input dimension %2% %3%")
2285 % unpackAxis
2286 % inputTensorInfo.GetNumDimensions()
2287 % CHECK_LOCATION().AsString()));
2288 }
2289
Nina Drozd200e3802019-04-15 09:47:39 +01002290 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2291 // If num is not defined, automatically infer from the length of the dimension axis.
2292 if(unpackNum == 0)
2293 {
2294 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2295 }
2296
2297 // If unpack number cannot be inferred and is still zero, throw ParseException.
2298 if(unpackNum == 0)
2299 {
2300 throw ParseException("Number to unpack must greater than zero.");
2301 }
2302
2303 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2304 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2305
2306 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2307 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2308
2309 // Add current input shape to unpackDimSizes
2310 for (unsigned int i = 0; i < inputDimSize; ++i)
2311 {
2312 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2313 }
2314
2315 if (unpackDimSizes[unpackAxis] != unpackNum)
2316 {
2317 throw ParseException("Number to unpack must be the same as length of the dimension to "
2318 "unpack along.");
2319 }
2320
2321 unpackDimSizes[unpackAxis] /= unpackNum;
2322
2323 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2324 for (unsigned int j = 0; j < unpackNum; ++j)
2325 {
2326 // Set the size of the views.
2327 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2328 {
2329 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2330 }
2331 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2332 }
2333
2334 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2335 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2336
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002337 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2338 unpackDimSizes.data());
2339
Nina Drozd200e3802019-04-15 09:47:39 +01002340 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2341 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2342
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002343 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2344 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2345 {
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002346 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002347 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2348 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002349 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002350 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2351
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002352 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2353 outputTensorInfo.GetDataType(),
2354 outputTensorInfo.GetQuantizationScale(),
2355 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002356 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2357
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002358 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002359
2360 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2361 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2362 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2363 }
Nina Drozd200e3802019-04-15 09:47:39 +01002364}
2365
Nina Drozd0324f482019-04-08 10:52:10 +01002366void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2367{
2368 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2369
2370 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2371 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2372
2373 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2374
Nina Drozd200e3802019-04-15 09:47:39 +01002375 // If number of splits cannot be inferred and is zero, throw ParseException.
2376 if(numSplits == 0)
2377 {
2378 throw ParseException("Number to splits must greater than zero.");
2379 }
2380
Nina Drozd0324f482019-04-08 10:52:10 +01002381 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2382 CHECK_VALID_SIZE(inputs.size(), 2);
2383 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2384 CHECK_VALID_SIZE(outputs.size(), numSplits);
2385
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002386 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2387 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002388
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002389 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2390 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2391 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2392
2393 BOOST_ASSERT(axisTensorInfo.GetNumElements() == 1);
2394 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002395
2396 // Armnn supports split along the channel dimension for data formats NHWC and NCHW.
2397 if (splitDim == 0 || splitDim == 2)
2398 {
2399 throw ParseException(
2400 boost::str(
2401 boost::format(
2402 "Dimension %1% for split is not supported by Armnn. %2%")
2403 % splitDim
2404 % CHECK_LOCATION().AsString()));
2405 }
2406
2407 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002408 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002409 {
2410 throw ParseException(
2411 boost::str(
2412 boost::format(
2413 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002414 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002415 % inputTensorInfo.GetNumDimensions()
2416 % MaxNumOfTensorDimensions
2417 % CHECK_LOCATION().AsString()));
2418 }
2419
2420 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2421
2422 // Add current input shape to splitterDimSizes
2423 for (unsigned int i = 0; i < inputDimSize; ++i)
2424 {
2425 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2426 }
2427
2428 if (splitterDimSizes[splitDim] % numSplits != 0)
2429 {
2430 throw ParseException("Number of splits must evenly divide the dimension");
2431 }
2432 splitterDimSizes[splitDim] /= numSplits;
2433
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002434 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002435 for (unsigned int j = 0; j < numSplits; ++j)
2436 {
2437 // Set the size of the views.
2438 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2439 {
2440 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2441 }
2442 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2443 }
2444
2445 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2446 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2447
2448 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002449 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002450
Nina Drozd0324f482019-04-08 10:52:10 +01002451 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2452 {
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002453 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2454 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002455 }
2456
2457 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2458 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2459}
2460
Sadik Armagan58f39192018-09-17 14:14:39 +01002461armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2462 unsigned int outputSlot,
2463 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002464{
2465 ActivationDescriptor activationDesc;
2466 std::string layerName = prevLayer->GetName();
2467
2468 switch(activationType)
2469 {
2470 case tflite::ActivationFunctionType_NONE:
2471 {
2472 // this is a no-op: return previous layer
2473 return prevLayer;
2474 }
2475 case tflite::ActivationFunctionType_RELU:
2476 {
2477 activationDesc.m_Function = ActivationFunction::ReLu;
2478 layerName += ":RELU";
2479 break;
2480 }
2481 case tflite::ActivationFunctionType_RELU6:
2482 {
2483 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2484 activationDesc.m_A = 6.0f;
2485 activationDesc.m_B = 0.0f;
2486 layerName += ":RELU6";
2487 break;
2488 }
2489 case tflite::ActivationFunctionType_TANH:
2490 {
2491 activationDesc.m_Function = ActivationFunction::TanH;
2492 activationDesc.m_A = 1.0f;
2493 activationDesc.m_B = 1.0f;
2494 layerName += ":TANH";
2495 break;
2496 }
2497
2498 // I only put these here as a reminder what others we could support
2499 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2500 case tflite::ActivationFunctionType_SIGN_BIT:
2501 default:
2502 {
2503 throw ParseException(
2504 boost::str(
2505 boost::format("TfLite parser doesn't suppport fused activation: "
2506 "%1%/%2% %3% ") %
2507 activationType %
2508 tflite::EnumNameActivationFunctionType(activationType) %
2509 CHECK_LOCATION().AsString()));
2510
2511 }
2512 }
2513
2514 IConnectableLayer* activationLayer =
2515 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2516
2517 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2518 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2519 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2520 return activationLayer;
2521}
2522
2523TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2524{
2525 if (fileName == nullptr)
2526 {
2527 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2528 CHECK_LOCATION().AsString()));
2529 }
2530 boost::system::error_code errorCode;
2531 boost::filesystem::path pathToFile(fileName);
2532 if (!boost::filesystem::exists(pathToFile, errorCode))
2533 {
2534 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2535 fileName %
2536 errorCode %
2537 CHECK_LOCATION().AsString()));
2538 }
2539 std::ifstream file(fileName, std::ios::binary);
2540 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2541 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2542 fileContent.size());
2543}
2544
2545TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2546{
2547 if (binaryContent == nullptr)
2548 {
2549 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2550 CHECK_LOCATION().AsString()));
2551 }
2552 flatbuffers::Verifier verifier(binaryContent, len);
2553 if (verifier.VerifyBuffer<tflite::Model>() == false)
2554 {
2555 throw ParseException(
2556 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2557 "flatbuffers format. size:%1% %2%") %
2558 len %
2559 CHECK_LOCATION().AsString()));
2560 }
2561 return tflite::UnPackModel(binaryContent);
2562}
2563
2564TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2565 size_t subgraphIndex,
2566 size_t operatorIndex)
2567{
2568 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2569
Derek Lambertiff05cc52019-04-26 13:05:17 +01002570 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2571 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002572
2573 size_t inputCount = operatorPtr->inputs.size();
2574 TensorRawPtrVector result(inputCount);
2575 for (size_t i=0; i<inputCount; ++i)
2576 {
2577 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002578 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002579 }
2580 return result;
2581}
2582
2583TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2584 size_t subgraphIndex,
2585 size_t operatorIndex)
2586{
2587 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2588
Derek Lambertiff05cc52019-04-26 13:05:17 +01002589 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2590 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002591
2592 size_t outputCount = operatorPtr->outputs.size();
2593 TensorRawPtrVector result(outputCount);
2594 for (size_t i=0; i<outputCount; ++i)
2595 {
2596 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2597 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002598 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002599 }
2600 return result;
2601}
2602
2603TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2604 size_t subgraphIndex)
2605{
2606 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002607 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002608
Derek Lambertiff05cc52019-04-26 13:05:17 +01002609 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002610 TensorIdRawPtrVector result(inputCount);
2611 for (size_t i=0; i<inputCount; ++i)
2612 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002613 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01002614 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002615 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002616 }
2617 return result;
2618}
2619
2620TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2621 size_t subgraphIndex)
2622{
2623 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002624 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002625
Derek Lambertiff05cc52019-04-26 13:05:17 +01002626 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002627 TensorIdRawPtrVector result(outputCount);
2628 for (size_t i=0; i<outputCount; ++i)
2629 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002630 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2631 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002632 }
2633 return result;
2634}
2635
2636std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2637 size_t subgraphIndex,
2638 size_t operatorIndex)
2639{
2640 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002641 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2642 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002643 return operatorPtr->inputs;
2644}
2645
2646std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2647 size_t subgraphIndex,
2648 size_t operatorIndex)
2649{
2650 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002651 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2652 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002653 return operatorPtr->outputs;
2654}
2655
2656void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2657 size_t operatorIndex,
2658 IConnectableLayer* layer,
2659 const std::vector<unsigned int>& tensorIndexes)
2660{
2661 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2662 BOOST_ASSERT(layer != nullptr);
2663 if (tensorIndexes.size() != layer->GetNumInputSlots())
2664 {
2665 throw ParseException(
2666 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2667 " for subgraph:%3% operator index:%4% %5%") %
2668 tensorIndexes.size() %
2669 layer->GetNumInputSlots() %
2670 subgraphIndex %
2671 operatorIndex %
2672 CHECK_LOCATION().AsString()));
2673 }
2674
2675 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2676 {
2677 unsigned int tensorIndex = tensorIndexes[slotIndex];
2678 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2679 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2680 }
2681}
2682
2683void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2684 size_t operatorIndex,
2685 IConnectableLayer* layer,
2686 const std::vector<unsigned int>& tensorIndexes)
2687{
2688 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2689 BOOST_ASSERT(layer != nullptr);
2690 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2691 {
2692 throw ParseException(
2693 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2694 " for subgraph:%3% operator index:%4% %5%") %
2695 tensorIndexes.size() %
2696 layer->GetNumOutputSlots() %
2697 subgraphIndex %
2698 operatorIndex %
2699 CHECK_LOCATION().AsString()));
2700 }
2701
2702 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2703 {
2704 unsigned int tensorIndex = tensorIndexes[slotIndex];
2705 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2706 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2707 }
2708}
2709
2710void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2711{
2712 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2713
2714 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2715 for (auto const & tensorIdAndPtr : inputs)
2716 {
2717 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2718 IConnectableLayer* layer =
2719 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2720
2721 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2722 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2723
2724 RegisterOutputSlots(subgraphIndex,
2725 VIRTUAL_OPERATOR_ID,
2726 layer,
2727 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2728 }
2729}
2730
2731void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2732{
2733 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2734
2735 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2736 for (auto const & tensorIdAndPtr : outputs)
2737 {
2738 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2739 IConnectableLayer* layer =
2740 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2741
2742 RegisterInputSlots(subgraphIndex,
2743 VIRTUAL_OPERATOR_ID,
2744 layer,
2745 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2746 }
2747}
2748
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002749void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2750{
2751 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2752
Derek Lambertiff05cc52019-04-26 13:05:17 +01002753 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002754 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2755 {
2756 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2757 {
2758 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2759 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2760 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002761 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002762 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2763 auto tensorAndData = CreateConstTensor(tensorPtr,
2764 tensorInfo,
2765 armnn::Optional<armnn::PermutationVector&>());
2766
2767 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2768 IConnectableLayer *layer =
2769 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2770
2771 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2772 RegisterOutputSlots(subgraphIndex,
2773 VIRTUAL_OPERATOR_ID,
2774 layer,
2775 { tensorIndex });
2776
2777 }
2778 }
2779 }
2780}
2781
telsoa01c577f2c2018-08-31 09:22:23 +01002782// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2783TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2784{
2785 CHECK_BUFFER(model, bufferIndex);
2786 return model->buffers[bufferIndex].get();
2787}
2788
Matteo Martincigh747ef822018-12-18 09:26:39 +00002789template<typename T>
2790std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2791TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2792 TfLiteParser::TensorRawPtr tensorPtr,
2793 armnn::TensorInfo& tensorInfo,
2794 armnn::Optional<armnn::PermutationVector&> permutationVector)
2795{
2796 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2797 tensorPtr,
2798 tensorInfo,
2799 permutationVector);
2800 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2801 return std::make_pair(constData.first, std::move(storage));
2802}
2803
telsoa01c577f2c2018-08-31 09:22:23 +01002804std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2805TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002806 armnn::TensorInfo& tensorInfo,
2807 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002808{
2809 CHECK_TENSOR_PTR(tensorPtr);
2810 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2811 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2812
2813 switch (tensorInfo.GetDataType())
2814 {
2815 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002816 return CreateConstTensorAndStoreData<float>(bufferPtr,
2817 tensorPtr,
2818 tensorInfo,
2819 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002820 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002821 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2822 tensorPtr,
2823 tensorInfo,
2824 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002825 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002826 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2827 tensorPtr,
2828 tensorInfo,
2829 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002830 default:
2831 {
2832 std::stringstream errString;
2833 errString << "Unexpected datatype when creating const tensor: "
2834 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2835 << " shape:" << tensorInfo.GetShape()
2836 << CHECK_LOCATION().AsString();
2837 throw ParseException(errString.str());
2838 }
2839 }
2840}
2841
2842BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2843 const std::string& name) const
2844{
2845 CHECK_SUBGRAPH(m_Model, subgraphId);
2846 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2847 for (auto const & input : inputs)
2848 {
2849 if (input.second->name == name)
2850 {
2851 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2852 return std::make_pair(bindingId, ToTensorInfo(input.second));
2853 }
2854 }
2855
2856 std::stringstream bindings;
2857 for (auto const & input : inputs)
2858 {
2859 bindings << "'" << input.second->name << "' ";
2860 }
2861
2862 throw ParseException(
2863 boost::str(
2864 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2865 "Possible inputs are: [%3%] %4%") %
2866 subgraphId %
2867 name %
2868 bindings.str() %
2869 CHECK_LOCATION().AsString()));
2870}
2871
2872BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2873 const std::string& name) const
2874{
2875 CHECK_SUBGRAPH(m_Model, subgraphId);
2876 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002877 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01002878 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002879 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01002880 if (output.second->name == name)
2881 {
2882 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002883 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
2884 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
2885 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01002886 }
2887 }
2888
2889 std::stringstream bindings;
2890 for (auto const & output : outputs)
2891 {
2892 bindings << "'" << output.second->name << "' ";
2893 }
2894
2895 throw ParseException(
2896 boost::str(
2897 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2898 "Possible outputs are: [%3%] %4%") %
2899 subgraphId %
2900 name %
2901 bindings.str() %
2902 CHECK_LOCATION().AsString()));
2903}
2904
2905size_t TfLiteParser::GetSubgraphCount() const
2906{
2907 return m_Model->subgraphs.size();
2908}
2909
2910std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2911{
2912 CHECK_SUBGRAPH(m_Model, subgraphId);
2913 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2914 std::vector<std::string> result;
2915 result.reserve(inputs.size());
2916 for (auto const & input : inputs)
2917 {
2918 result.push_back(input.second->name);
2919 }
2920 return result;
2921}
2922
2923std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2924{
2925 CHECK_SUBGRAPH(m_Model, subgraphId);
2926 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2927 std::vector<std::string> result;
2928 result.reserve(outputs.size());
2929 for (auto const & output : outputs)
2930 {
2931 result.push_back(output.second->name);
2932 }
2933 return result;
2934}
2935
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002936ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002937{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002938 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01002939}
2940
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002941ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002942{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002943 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01002944}
2945
2946void ITfLiteParser::Destroy(ITfLiteParser* parser)
2947{
2948 delete parser;
2949}
2950
2951TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2952: m_FloatData(std::move(data))
2953, m_Uint8Data(nullptr)
2954, m_Int32Data(nullptr)
2955{
2956}
2957
2958TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2959: m_FloatData(nullptr)
2960, m_Uint8Data(std::move(data))
2961, m_Int32Data(nullptr)
2962{
2963}
2964
2965TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2966: m_FloatData(nullptr)
2967, m_Uint8Data(nullptr)
2968, m_Int32Data(std::move(data))
2969{
2970}
2971
2972} // armnnTfLiteParser