blob: f06e24422397fed25032bcaeb89bc07de75dda9b [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;
317 case tflite::TensorType_INT32:
318 type = armnn::DataType::Signed32;
319 break;
320
321 default:
322 {
323 CheckLocation location = CHECK_LOCATION();
324 throw ParseException(
325 boost::str(
326 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
327 tensorPtr->type %
328 tflite::EnumNameTensorType(tensorPtr->type) %
329 tensorPtr->name %
330 location.AsString()));
331 }
332 }
333
334 float quantizationScale = 0.0f;
335 int32_t quantizationOffset = 0;
336
337 if (tensorPtr->quantization.get())
338 {
339 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
340 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
341
342 if (tensorPtr->quantization->scale.size() == 1)
343 {
344 quantizationScale = tensorPtr->quantization->scale[0];
345 }
346 if (tensorPtr->quantization->zero_point.size() == 1)
347 {
348 // NOTE: we lose precision here when converting from 64 bit to 32
349 // but this is what we support at the monent in ArmNN
350 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
351 }
352 }
353
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100354 std::vector<unsigned int> safeShape = shapes;
355 if (safeShape.size() == 0)
356 {
357 safeShape.push_back(1);
358 }
359
telsoa01c577f2c2018-08-31 09:22:23 +0100360 // two statements (on purpose) for easier debugging:
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100361 armnn::TensorInfo result(static_cast<unsigned int>(safeShape.size()),
362 safeShape.data(),
telsoa01c577f2c2018-08-31 09:22:23 +0100363 type,
364 quantizationScale,
365 quantizationOffset);
366 return result;
367}
368
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000369armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
370{
371 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
372 return ToTensorInfo(tensorPtr, dimensions);
373}
374
telsoa01c577f2c2018-08-31 09:22:23 +0100375template<typename T>
376std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
377CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
378 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000379 armnn::TensorInfo& tensorInfo,
380 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100381{
382 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
383 BOOST_ASSERT_MSG(bufferPtr != nullptr,
384 boost::str(
385 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
386
387 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000388
389 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
390 {
391 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000392 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
393 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000394 }
395 else
396 {
397 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
398 }
399
telsoa01c577f2c2018-08-31 09:22:23 +0100400 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
401}
402
telsoa01c577f2c2018-08-31 09:22:23 +0100403armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
404{
405 // generate the binding id by shifting the tensor id by 8 bit
406 // and add the subgraph id, which allows 256 subgraphs
407 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
408}
409
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000410bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
411{
412 const unsigned int actualSize = actual.GetNumDimensions();
413 if (actualSize != expected.size())
414 {
415 return false;
416 }
417
418 for (unsigned int i = 0u; i < actualSize; i++)
419 {
420 if (expected[i] < 0 ||
421 actual[i] != static_cast<unsigned int>(expected[i]))
422 {
423 return false;
424 }
425 }
426
427 return true;
428}
429
telsoa01c577f2c2018-08-31 09:22:23 +0100430} // <anonymous>
431
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100432TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
433: m_Options(options)
434, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100435, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
436{
437 // register supported operators
Sadik Armagana3b31f02019-12-05 09:08:53 +0000438 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
439 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
440 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
441 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
442 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
443 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
444 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
445 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
446 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
447 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
448 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
449 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
450 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
451 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
452 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
453 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
454 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
455 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
456 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
457 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
458 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
459 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
460 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
461 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
462 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
463 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
464 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
465 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
466 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
467 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
468 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
469 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
470 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100471
472 // register supported custom operators
473 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100474}
475
476void TfLiteParser::ResetParser()
477{
478 m_Network = armnn::INetworkPtr(nullptr, nullptr);
479 m_Model = nullptr;
480 m_SubgraphConnections.clear();
481}
482
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200483void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
484 size_t operatorIndex,
485 IConnectableLayer *layer)
486{
487 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
488 BOOST_ASSERT(layer != nullptr);
489
Derek Lambertiff05cc52019-04-26 13:05:17 +0100490 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
491 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200492
493 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
494
495 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100496 TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200497 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100498 TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200499
500 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
501 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
502
503 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
504 {
505 uint32_t id = reshapedInputId;
506 reshapedInputId = inputId;
507 inputId = id;
508
509 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
510 inputTensorInfo = ToTensorInfo(tensorPtr);
511 }
512
513 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
514
515 std::vector<unsigned> reshapedDim;
516 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
517 {
518 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
519 }
520
521 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
522 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
523
524 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
525
526 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
527 armnn::ReshapeDescriptor desc;
528 desc.m_TargetShape = reshapedTensorInfo.GetShape();
529 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
530
531 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
532 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
533
534 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
535
536 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
537 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
538}
539
telsoa01c577f2c2018-08-31 09:22:23 +0100540INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
541{
542 ResetParser();
543 m_Model = LoadModelFromFile(graphFile);
544 return CreateNetworkFromModel();
545}
546
547INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
548{
549 ResetParser();
550 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
551 return CreateNetworkFromModel();
552}
553
554INetworkPtr TfLiteParser::CreateNetworkFromModel()
555{
556 m_Network = INetwork::Create();
557 BOOST_ASSERT(m_Model.get() != nullptr);
558
559 bool failedToCreate = false;
560 std::stringstream errors;
561
562 if (m_Model->subgraphs.size() != 1)
563 {
564 throw ParseException(
565 boost::str(
566 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
567 m_Model->subgraphs.size() %
568 CHECK_LOCATION().AsString()));
569 }
570
571 size_t subgraphIndex = 0;
Derek Lambertiff05cc52019-04-26 13:05:17 +0100572 for (SubgraphPtr const & subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100573 {
574 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
575
576 size_t operatorIndex = 0;
577 for (OperatorPtr const & op : subgraph->operators)
578 {
579 try
580 {
telsoa01c577f2c2018-08-31 09:22:23 +0100581 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
582 auto builtinCode = opCodePtr->builtin_code;
583
584 if (builtinCode > tflite::BuiltinOperator_MAX)
585 {
586 throw ParseException(
587 boost::str(
588 boost::format("Operator code %1% is out of range 0-%2%. "
589 "subgraph:%3% operator idx:%4%. %5%") %
590 builtinCode %
591 tflite::BuiltinOperator_MAX %
592 subgraphIndex %
593 operatorIndex %
594 CHECK_LOCATION().AsString()));
595 }
596
597 // lookup and call the parser function
598 auto & parserFunction = m_ParserFunctions[builtinCode];
599 (this->*parserFunction)(subgraphIndex, operatorIndex);
600 }
601 catch (const ParseException& e)
602 {
603 failedToCreate = true;
604 std::stringstream errorString;
605
606 errorString << "Failed to parse operator #" << operatorIndex
607 << " within subgraph #" << subgraphIndex
608 << " error: " << e.what();
Derek Lamberti08446972019-11-26 16:38:31 +0000609 ARMNN_LOG(error) << errorString.str();
telsoa01c577f2c2018-08-31 09:22:23 +0100610
611 errors << errorString.str() << "\n";
612 }
613 ++operatorIndex;
614 }
615
616 SetupInputLayers(subgraphIndex);
617 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200618 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100619
620 ++subgraphIndex;
621 }
622
623 if (failedToCreate)
624 {
625 // we can skip everything and let the outer exception handler deal with the error
626 throw ParseException(errors.str());
627 }
628
629 // establish the connections from the layer outputs to the inputs of the subsequent layers
630 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
631 {
632 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
633 {
634 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
635 {
636 for (size_t inputSlotIdx = 0;
637 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
638 ++inputSlotIdx)
639 {
640 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
641 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
642 }
643 }
644 }
645 }
646
647 return std::move(m_Network);
648}
649
650void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
651 size_t tensorIndex,
652 armnn::IOutputSlot* slot)
653{
654 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
655 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
656 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
657
658 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
659
660 // assuming there is only one producer for that tensor
661 if (tensorSlots.outputSlot != nullptr)
662 {
663 throw ParseException(boost::str(
664 boost::format("Another layer has already registered itself as the producer of "
665 "subgraph:%1% tensor:%2% %3%") %
666 subgraphIndex %
667 tensorIndex %
668 CHECK_LOCATION().AsString()));
669 }
670
671 tensorSlots.outputSlot = slot;
672}
673
674void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
675 size_t tensorIndex,
676 armnn::IInputSlot* slot)
677{
678 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
679 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
680 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
681
682 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
683 tensorSlots.inputSlots.push_back(slot);
684}
685
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100686void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
687{
688 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
689
690 // NOTE: By default we presume the custom operator is not supported
691 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
692
693 // Identify custom code defined for custom operator
694 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
695 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
696
697 // Find parser function that correspondes to custom code (if any)
698 auto iterator = m_CustomParserFunctions.find(customCode);
699 if (iterator != m_CustomParserFunctions.end())
700 {
701 customParserFunction = iterator->second;
702 }
703
704 // Run parser function
705 (this->*customParserFunction)(subgraphIndex, operatorIndex);
706}
707
telsoa01c577f2c2018-08-31 09:22:23 +0100708void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
709{
710 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100711
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100712 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
713
714 auto opcodeIndex = operatorPtr->opcode_index;
715 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
716
717 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
718 {
719 // Do not add StandInLayer, throw ParseException instead
720 throw ParseException(
721 boost::str(
722 boost::format("Operator not supported. "
723 "subgraph:%1% operator:%2% "
724 "opcode_index:%3% opcode:%4% / %5% %6%") %
725 subgraphIndex %
726 operatorIndex %
727 opcodeIndex %
728 opcode %
729 tflite::EnumNameBuiltinOperator(opcode) %
730 CHECK_LOCATION().AsString()));
731 }
732
733 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
734 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
735
736 const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
737 const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
738
739 StandInDescriptor descriptor(numInputs, numOutputs);
740 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
741
742 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
743 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
744 for (unsigned int i = 0u; i < numOutputs; ++i)
745 {
746 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
747 }
748
749 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
750 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
751
752 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
753 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100754}
755
telsoa01c577f2c2018-08-31 09:22:23 +0100756void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
757{
758 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
759
760 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
761 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
762
763 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
764
765 Convolution2dDescriptor desc;
766 desc.m_BiasEnabled = false;
767 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
768 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000769 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100770 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
771 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000772
telsoa01c577f2c2018-08-31 09:22:23 +0100773 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
774 CHECK_VALID_SIZE(inputs.size(), 2, 3);
775
776 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
777 CHECK_VALID_SIZE(outputs.size(), 1);
778
779 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
780 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
781
782 // assuming input is NHWC
783 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
784 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
785
786 // assuming the filter is OHWI : Output, H, W, Input
787 // which is essentially the same as NHWC
788 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
789 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
790
Pablo Tellof0bd6832019-04-26 17:58:13 +0100791 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
792 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
793 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
794 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100795
Matteo Martincigh747ef822018-12-18 09:26:39 +0000796 auto filterTensorAndData = CreateConstTensor(inputs[1],
797 filterTensorInfo,
798 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100799 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100800
801 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
802
803 if (inputs.size() == 3)
804 {
805 desc.m_BiasEnabled = true;
806 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000807 auto biasTensorAndData = CreateConstTensor(inputs[2],
808 biasTensorInfo,
809 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100810 layer = m_Network->AddConvolution2dLayer(desc,
811 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100812 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100813 layerName.c_str());
814 }
815 else
816 {
817 layer = m_Network->AddConvolution2dLayer(desc,
818 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100819 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100820 layerName.c_str());
821 }
822
823 BOOST_ASSERT(layer != nullptr);
824
telsoa01c577f2c2018-08-31 09:22:23 +0100825 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000826 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100827
828 // register the input connection slots for the layer, connections are made after all layers have been created
829 // only the tensors for the inputs are relevant, exclude the const tensors
830 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000831 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100832
jimfly01c25411c2018-11-14 17:47:22 +0000833 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100834 // register the output connection slots for the layer, connections are made after all layers have been created
835 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
836 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
837}
838
839void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
840{
841 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
842
843 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
844 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
845
846 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
847
848 DepthwiseConvolution2dDescriptor desc;
849 desc.m_BiasEnabled = false;
850 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
851 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000852 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100853 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100854
855 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
856 CHECK_VALID_SIZE(inputs.size(), 2, 3);
857 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
858 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100859 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
860 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000861
telsoa01c577f2c2018-08-31 09:22:23 +0100862 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
863 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
864
Matteo Martincigh747ef822018-12-18 09:26:39 +0000865 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100866 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
867 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000868
869 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100870 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
871 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
872
Matteo Martincigh747ef822018-12-18 09:26:39 +0000873 // Reshape weights as [ H, W, I, M ]
874 filterTensorInfo.SetShape({ filterHeight,
875 filterWidth,
876 inputTensorInfo.GetShape()[3],
877 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
878
879 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
880 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
881
Pablo Tellof0bd6832019-04-26 17:58:13 +0100882 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
883 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
884 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
885 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100886
Matteo Martincigh747ef822018-12-18 09:26:39 +0000887 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100888 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100889 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
890
891 if (inputs.size() == 3)
892 {
893 desc.m_BiasEnabled = true;
894 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000895 auto biasTensorAndData = CreateConstTensor(inputs[2],
896 biasTensorInfo,
897 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100898 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
899 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100900 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100901 layerName.c_str());
902 }
903 else
904 {
905 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
906 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100907 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100908 layerName.c_str());
909 }
910 BOOST_ASSERT(layer != nullptr);
911
telsoa01c577f2c2018-08-31 09:22:23 +0100912 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000913 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100914
915 // register the input connection slots for the layer, connections are made after all layers have been created
916 // only the tensors for the inputs are relevant, exclude the const tensors
917 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000918 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100919
jimfly01c25411c2018-11-14 17:47:22 +0000920 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100921 // register the output connection slots for the layer, connections are made after all layers have been created
922 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
923 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
924}
925
Keith Davis4cd29a02019-09-09 14:49:20 +0100926void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
927{
928 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
929
930 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +0100931 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +0100932
933 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
934 CHECK_VALID_SIZE(outputs.size(), 1);
935
936 armnn::IConnectableLayer* layer = nullptr;
937 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
938
939 PermuteDescriptor desc;
940
josh minorba424d22019-11-13 10:55:17 -0600941 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +0100942 {
943 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
944 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -0600945 auto numPermVecElements = permuteTensorInfo.GetNumElements();
946 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +0100947 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
948
josh minorba424d22019-11-13 10:55:17 -0600949 // permuteShape assumes Tf/Np permute vectors, we must translate to armnn expected form
950 // 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)
951 std::vector<unsigned int> armnnPermuteShape(numPermVecElements);
952 std::vector<unsigned int>::iterator it;
953 for (unsigned int i = 0u; i < numPermVecElements; ++i)
954 {
955 it = std::find(permuteShape.begin(), permuteShape.end(), i);
956 armnnPermuteShape[i] = static_cast<unsigned int>(std::distance(permuteShape.begin(), it));
957 }
Kevin May85d92602019-09-27 17:21:06 +0100958
josh minorba424d22019-11-13 10:55:17 -0600959 PermutationVector permutationVector(armnnPermuteShape.data(), permuteTensorInfo.GetNumElements());
960
961 desc = PermuteDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +0100962 }
963
Keith Davis4cd29a02019-09-09 14:49:20 +0100964 layer = m_Network->AddPermuteLayer(desc, layerName.c_str());
965
966 BOOST_ASSERT(layer != nullptr);
967
968 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
969 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
970
971 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
972 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
973
974 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
975 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
976}
977
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100978void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
979{
980 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
981
982 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
983 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
984
985 TransposeConvolution2dDescriptor desc;
986 desc.m_BiasEnabled = false;
987 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
988 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
989 desc.m_DataLayout = armnn::DataLayout::NHWC;
990
991 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +0100992 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100993
994 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
995 CHECK_VALID_SIZE(outputs.size(), 1);
996
Matthew Jacksonccb25ea2019-08-20 17:18:33 +0100997 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100998 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
999
1000 // TfLite uses NHWC tensors
1001 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1002 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1003
1004 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1005 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1006
1007 CalcPadding(inputHeight,
1008 filterHeight,
1009 desc.m_StrideY,
1010 1, // DilationY
1011 desc.m_PadTop,
1012 desc.m_PadBottom,
1013 options->padding);
1014
1015 CalcPadding(inputWidth,
1016 filterWidth,
1017 desc.m_StrideX,
1018 1, // DilationX
1019 desc.m_PadLeft,
1020 desc.m_PadRight,
1021 options->padding);
1022
1023 auto filterTensorAndData = CreateConstTensor(inputs[1],
1024 filterTensorInfo,
1025 armnn::Optional<armnn::PermutationVector&>());
1026
1027 armnn::IConnectableLayer* layer = nullptr;
1028 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1029
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001030 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1031 filterTensorAndData.first,
1032 EmptyOptional(),
1033 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001034
1035 BOOST_ASSERT(layer != nullptr);
1036
1037 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1038 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1039
1040 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1041 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001042 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001043
1044 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1045 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1046}
1047
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001048void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1049{
1050 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1051}
1052
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001053void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1054{
1055 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1056
1057 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1058 CHECK_VALID_SIZE(inputs.size(), 3);
1059
1060 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1061 CHECK_VALID_SIZE(outputs.size(), 1);
1062
1063 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1064 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1065
1066 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1067 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1068
1069 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1070 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1071
1072 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1073 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1074
1075 size_t step = 2;
1076 std::vector<std::pair<unsigned int, unsigned int>> crops;
1077 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1078 {
1079 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1080 }
1081
1082 armnn::BatchToSpaceNdDescriptor desc;
1083 desc.m_BlockShape = blockShape;
1084 desc.m_Crops = crops;
1085 desc.m_DataLayout = armnn::DataLayout::NHWC;
1086
1087 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1088
1089 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1090 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1091
1092 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1093
1094 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1095 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1096
1097 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1098 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1099}
1100
Matthew Jackson28c94572019-07-18 10:47:03 +01001101void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1102{
1103 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1104
1105 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1106 CHECK_VALID_SIZE(inputs.size(), 1);
1107
1108 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1109 CHECK_VALID_SIZE(outputs.size(), 1);
1110
1111 L2NormalizationDescriptor desc;
1112 desc.m_DataLayout = armnn::DataLayout::NHWC;
1113 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1114 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1115
1116 BOOST_ASSERT(layer != nullptr);
1117
1118 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1119 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1120
1121 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1122 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1123
1124 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1125 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1126}
1127
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001128void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1129{
1130 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1131}
1132
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001133void TfLiteParser::ParseMaximum(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(), 2);
1139
1140 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1141 CHECK_VALID_SIZE(outputs.size(), 1);
1142
1143 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1144 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1145
1146 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1147 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1148
1149 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1150 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1151
1152 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1153 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1154 {
1155 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1156 }
1157 else
1158 {
1159 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1160 }
1161
1162 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1163 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1164}
1165
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001166void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1167{
1168 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1169
1170 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1171 CHECK_VALID_SIZE(inputs.size(), 2);
1172
1173 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1174 CHECK_VALID_SIZE(outputs.size(), 1);
1175
1176 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1177 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1178
1179 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1180 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1181
1182 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1183 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1184
1185 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1186 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1187 {
1188 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1189 }
1190 else
1191 {
1192 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1193 }
1194
1195 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1196 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1197}
1198
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001199void TfLiteParser::ParsePool(size_t subgraphIndex,
1200 size_t operatorIndex,
1201 PoolingAlgorithm algorithm)
1202{
1203 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1204
1205 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1206 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1207
1208 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1209
1210 std::string layerName;
1211
1212 switch (algorithm)
1213 {
1214 case PoolingAlgorithm::Average:
1215 layerName =
1216 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1217 break;
1218 case PoolingAlgorithm::Max:
1219 layerName =
1220 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1221 break;
1222 default:
1223 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1224 }
1225
1226 Pooling2dDescriptor desc;
1227
1228 desc.m_PoolType = algorithm;
1229 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1230 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1231 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1232 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1233 desc.m_PaddingMethod = PaddingMethod::Exclude;
1234 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001235 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001236
1237 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1238 CHECK_VALID_SIZE(inputs.size(), 1);
1239 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1240
1241 // assuming input is NHWC
1242 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1243 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1244
Pablo Tellof0bd6832019-04-26 17:58:13 +01001245 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1246 desc.m_PadTop, desc.m_PadBottom, options->padding);
1247 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1248 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001249
1250 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1251 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001252
1253 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1254
1255 BOOST_ASSERT(layer != nullptr);
1256
jimfly01c25411c2018-11-14 17:47:22 +00001257 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1258 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001259
1260 // register the input connection slots for the layer, connections are made after all layers have been created
1261 // only the tensors for the inputs are relevant, exclude the const tensors
1262 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001263 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001264
jimfly01c25411c2018-11-14 17:47:22 +00001265 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001266 // register the output connection slots for the layer, connections are made after all layers have been created
1267 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1268 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1269}
1270
josh minorba424d22019-11-13 10:55:17 -06001271void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1272{
1273 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1274
1275 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1276 CHECK_VALID_SIZE(inputs.size(), 3);
1277 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1278 CHECK_VALID_SIZE(outputs.size(), 1);
1279
1280 SliceDescriptor desc;
1281
1282 // set begin tensor info for slice descriptor
1283 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1284 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1285
1286 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1287 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1288
1289 // set size tensor info for slice descriptor
1290 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1291 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1292
1293 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1294 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1295 desc = SliceDescriptor(begin, size);
1296
1297 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1298 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1299
1300 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1301 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1302
1303 // register the input connection slots for the layer, connections are made after all layers have been created
1304 // only the tensors for the inputs are relevant, exclude the const tensors
1305 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1306 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1307
1308 // register the output connection slots for the layer, connections are made after all layers have been created
1309 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1310 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1311}
1312
telsoa01c577f2c2018-08-31 09:22:23 +01001313void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1314{
1315 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1316 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1317 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1318
1319 SoftmaxDescriptor desc;
1320 desc.m_Beta = options->beta;
1321
1322 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1323 CHECK_VALID_SIZE(inputs.size(), 1);
1324 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1325 CHECK_VALID_SIZE(outputs.size(), 1);
1326
1327 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1328 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1329
1330 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1331 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1332
1333 // register the input connection slots for the layer, connections are made after all layers have been created
1334 // only the tensors for the inputs are relevant, exclude the const tensors
1335 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1336 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1337
1338 // register the output connection slots for the layer, connections are made after all layers have been created
1339 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1340 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1341}
1342
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001343void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1344{
1345 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1346
1347 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1348 CHECK_VALID_SIZE(inputs.size(), 3);
1349
1350 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1351 CHECK_VALID_SIZE(outputs.size(), 1);
1352
1353 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1354 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1355
1356 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1357 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1358
1359 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1360 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1361
1362 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1363 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1364
1365 size_t step = 2;
1366 std::vector<std::pair<unsigned int, unsigned int>> padList;
1367 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1368 {
1369 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1370 }
1371
1372 armnn::SpaceToBatchNdDescriptor desc;
1373 desc.m_BlockShape = blockShape;
1374 desc.m_PadList = padList;
1375 desc.m_DataLayout = armnn::DataLayout::NHWC;
1376
1377 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1378
1379 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1380 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1381
1382 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1383
1384 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1385 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1386
1387 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1388 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1389}
1390
telsoa01c577f2c2018-08-31 09:22:23 +01001391armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1392 const armnn::TensorInfo & inputTensorInfo)
1393{
1394 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1395 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1396 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1397
1398 if (inputTensorInfo.GetNumDimensions() > 4)
1399 {
1400 std::stringstream ss;
1401 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1402 << " shape:" << inputTensorInfo.GetShape() << " "
1403 << CHECK_LOCATION().AsString();
1404 throw ParseException(ss.str());
1405 }
1406
1407 if (squeezeDims.empty())
1408 {
1409 squeezeDims.assign(dimensionSequence,
1410 dimensionSequence+inputTensorInfo.GetNumDimensions());
1411 }
1412
1413 std::vector<uint32_t> outputDims;
1414 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1415 {
1416 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1417 auto currentDimension = inputTensorInfo.GetShape()[i];
1418 if (skipSqueeze || currentDimension != 1)
1419 {
1420 outputDims.push_back(currentDimension);
1421 }
1422 }
1423
1424 if (outputDims.size() > 4)
1425 {
1426 std::stringstream ss;
1427 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1428 << " shape:" << inputTensorInfo.GetShape() << " "
1429 << CHECK_LOCATION().AsString();
1430 throw ParseException(ss.str());
1431 }
1432
1433 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1434 outputDims.data());
1435
1436 // we need to preserve the tensor type and the quantization data as well
1437 TensorInfo outTensorInfo = inputTensorInfo;
1438 outTensorInfo.SetShape(outShape);
1439
1440 return outTensorInfo;
1441}
1442
1443void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1444{
1445 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1446
1447 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1448 CHECK_VALID_SIZE(inputs.size(), 1);
1449
1450 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1451 CHECK_VALID_SIZE(outputs.size(), 1);
1452
1453 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1454 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1455
1456 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1457 armnn::TensorInfo outputTensorInfo =
1458 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1459 inputTensorInfo);
1460
1461 ReshapeDescriptor reshapeDesc;
1462 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1463
1464 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1465 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1466 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1467
1468 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1469 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1470
1471 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1472 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1473}
1474
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001475void TfLiteParser::ParseStridedSlice(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(), 4);
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.AsStridedSliceOptions();
1487
1488 StridedSliceDescriptor desc;
1489 desc.m_BeginMask = options->begin_mask;
1490 desc.m_EllipsisMask = options->ellipsis_mask;
1491 desc.m_EndMask = options->end_mask;
1492 desc.m_NewAxisMask = options->new_axis_mask;
1493 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1494 desc.m_DataLayout = armnn::DataLayout::NHWC;
1495
1496 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1497 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1498
1499 std::vector<int> begin(beginTensorInfo.GetNumElements());
1500 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1501
1502 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1503 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1504
1505 std::vector<int> end(endTensorInfo.GetNumElements());
1506 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1507
1508 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1509 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1510
1511 std::vector<int> stride(strideTensorInfo.GetNumElements());
1512 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1513
1514 desc.m_Begin = begin;
1515 desc.m_End = end;
1516 desc.m_Stride = stride;
1517
1518 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1519 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1520
1521 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1522 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1523
1524 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1525 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1526
1527 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1528 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1529}
1530
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001531void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1532{
1533 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1534
1535 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1536 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1537
1538 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1539 CHECK_VALID_SIZE(inputs.size(), 2);
1540
1541 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1542 CHECK_VALID_SIZE(outputs.size(), 1);
1543
1544 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1545 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1546
1547 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1548 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1549
1550 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1551 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1552
1553 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1554 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1555 {
1556 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1557 }
1558 else
1559 {
1560 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1561 }
1562
1563 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1564
1565 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1566 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1567}
1568
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001569void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1570{
1571 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1572
1573 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1574 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1575
1576 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1577 CHECK_VALID_SIZE(inputs.size(), 2);
1578
1579 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1580 CHECK_VALID_SIZE(outputs.size(), 1);
1581
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001582 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1583 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1584
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001585 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1586 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1587
1588 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1589 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1590
1591 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001592 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1593 {
1594 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1595 }
1596 else
1597 {
1598 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1599 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001600
1601 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1602
1603 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1604 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1605}
1606
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001607void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1608{
1609 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1610
1611 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1612 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1613
1614 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1615 CHECK_VALID_SIZE(inputs.size(), 2);
1616
1617 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1618 CHECK_VALID_SIZE(outputs.size(), 1);
1619
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001620 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1621 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1622
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001623 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1624 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1625
1626 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1627 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1628
1629 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001630 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1631 {
1632 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1633 }
1634 else
1635 {
1636 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1637 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001638
1639 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1640
1641 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1642 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1643}
1644
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001645void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1646{
1647 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1648
1649 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1650
1651 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1652 CHECK_VALID_SIZE(outputs.size(), 1);
1653
1654 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1655 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1656
1657 armnn::MeanDescriptor desc;
1658 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1659 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1660 desc.m_Axis = axis;
1661
1662 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1663 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1664
1665 desc.m_KeepDims =
1666 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1667 true : false;
1668
1669 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1670 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1671
1672 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1673
1674 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1675 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1676
1677 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1678 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1679}
1680
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001681void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1682{
1683 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1684
1685 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1686
1687 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1688 CHECK_VALID_SIZE(outputs.size(), 1);
1689
1690 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1691 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1692
1693 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1694 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1695
1696 size_t step = 2;
1697 armnn::PadDescriptor desc;
1698 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1699 {
1700 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1701 }
1702
1703 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1704 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1705
1706 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1707 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1708
1709 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1710 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1711
1712 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1713 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1714}
1715
Finn Williamsc42c3842019-01-22 14:18:11 +00001716
Sadik Armagan58f39192018-09-17 14:14:39 +01001717void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1718{
Finn Williamsc42c3842019-01-22 14:18:11 +00001719 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001720}
1721
1722void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1723{
Finn Williamsc42c3842019-01-22 14:18:11 +00001724 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1725}
Sadik Armagan58f39192018-09-17 14:14:39 +01001726
Finn Williamsc42c3842019-01-22 14:18:11 +00001727void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1728{
1729 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1730}
1731
Nina Drozd99851762019-04-09 09:37:38 +01001732void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1733{
1734 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1735}
1736
Finn Williamsc42c3842019-01-22 14:18:11 +00001737
1738void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1739{
1740 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001741 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1742 boost::ignore_unused(operatorPtr);
1743
1744 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1745 CHECK_VALID_SIZE(inputs.size(), 1);
1746
1747 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1748 CHECK_VALID_SIZE(outputs.size(), 1);
1749
Finn Williamsc42c3842019-01-22 14:18:11 +00001750 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001751 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001752 activationDesc.m_Function = activationType;
1753
1754 switch (activationType)
1755 {
1756 case ActivationFunction::ReLu:
1757 {
1758 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1759 break;
1760 }
1761 case ActivationFunction::BoundedReLu:
1762 {
1763 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1764 activationDesc.m_A = 6.0f;
1765 activationDesc.m_B = 0.0f;
1766 break;
1767 }
1768 case ActivationFunction::Sigmoid:
1769 {
1770 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1771 break;
1772 }
Nina Drozd99851762019-04-09 09:37:38 +01001773 case ActivationFunction::TanH:
1774 {
1775 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1776 activationDesc.m_A = 1.0f;
1777 activationDesc.m_B = 1.0f;
1778 break;
1779 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001780 default:
1781 {
1782 throw ParseException(
1783 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1784 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1785 }
1786 }
1787
1788 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001789
1790 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1791 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1792
1793 // register the input connection slots for the layer, connections are made after all layers have been created
1794 // only the tensors for the inputs are relevant, exclude the const tensors
1795 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1796 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1797
1798 // register the output connection slots for the layer, connections are made after all layers have been created
1799 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1800 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1801}
Sadikb94967b2018-09-19 15:30:00 +01001802armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1803 const std::vector<int32_t> & targetDimsIn)
1804{
1805 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1806 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1807
1808 if (stretchDim != targetDimsIn.end())
1809 {
1810 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1811 {
1812 throw ParseException(
1813 boost::str(
1814 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1815 }
1816
1817 auto targetNumElements =
1818 boost::numeric_cast<unsigned int>(
1819 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1820
1821 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1822 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1823 }
1824
1825 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1826
1827 TensorInfo reshapeInfo = inputTensorInfo;
1828 reshapeInfo.SetShape(outputShape);
1829
1830 return reshapeInfo;
1831}
1832
1833void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1834{
1835 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1836
1837 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001838
1839 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1840 CHECK_VALID_SIZE(outputs.size(), 1);
1841
1842 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1843 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1844
1845 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001846 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1847 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001848 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1849
kevmay0171972a82018-12-17 14:28:03 +00001850 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001851 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1852 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001853 {
1854 std::stringstream ss;
1855 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001856 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001857 << " does not equal output shape "
1858 << actualOutputTensorInfo.GetShape()
1859 << ": "
1860 << CHECK_LOCATION().AsString();
1861 throw ParseException(ss.str());
1862 }
1863
Sadikb94967b2018-09-19 15:30:00 +01001864 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001865 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001866
1867 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1868 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001869 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001870
1871 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1872 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1873
1874 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1875 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1876}
1877
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001878void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1879{
Sadik Armagana3b31f02019-12-05 09:08:53 +00001880 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
1881}
1882
1883void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
1884{
1885 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
1886}
1887
1888void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
1889{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001890 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1891
1892 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1893 CHECK_VALID_SIZE(inputs.size(), 2);
1894
1895 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1896 CHECK_VALID_SIZE(outputs.size(), 1);
1897
1898 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1899
1900 // Data for the parsed tensor args (size) must be stored locally.
1901 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1902
1903 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1904 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1905
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001906 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00001907 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001908 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001909 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1910 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001911
Sadik Armagana3b31f02019-12-05 09:08:53 +00001912 auto layerName = str(boost::format("Resize:"));
1913
1914 switch (resizeMethod)
1915 {
1916 case ResizeMethod::Bilinear:
1917 {
1918 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
1919 break;
1920 }
1921 case ResizeMethod::NearestNeighbor:
1922 {
1923 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
1924 break;
1925 }
1926 default:
1927 {
1928 throw ParseException(
1929 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
1930 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
1931 }
1932 }
1933
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001934 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001935
1936 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1937 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1938
1939 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1940 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1941
1942 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1943 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1944}
1945
Sadik Armagan479045b2018-10-01 11:51:37 +01001946void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1947{
1948 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1949
1950 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1951 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1952
1953 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1954
1955 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1956 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1957 CHECK_VALID_SIZE(outputs.size(), 1);
1958
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001959 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1960 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001961
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001962 const unsigned int concatDimInput = static_cast<unsigned int>(
1963 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001964
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001965 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1966 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001967
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001968 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01001969
1970 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1971 {
1972 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1973
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001974 // This set up concatDescriptor view origin
1975 armnnUtils::ProcessConcatInputTensorInfo(
1976 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01001977 }
1978
1979 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Jim Flynn906f9462019-05-10 13:55:21 +01001980 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Sadik Armagan479045b2018-10-01 11:51:37 +01001981
1982 BOOST_ASSERT(layer != nullptr);
1983
1984 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1985 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01001986
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001987 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01001988
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001989 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01001990
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001991 // add fused activation layer
1992 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01001993
Sadik Armagan479045b2018-10-01 11:51:37 +01001994 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1995 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1996}
1997
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001998void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1999{
2000 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2001
2002 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2003 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2004
2005 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2006
2007 FullyConnectedDescriptor desc;
2008 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002009 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002010
2011 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2012 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2013 CHECK_VALID_SIZE(outputs.size(), 1);
2014
2015 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2016
2017 // Fully Connected Layer accepts two dimensional weights input
2018 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2019 if (weightsDimension != 2)
2020 {
2021 throw ParseException(
2022 boost::str(
2023 boost::format(
2024 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2025 "Node %2%")
2026 % weightsDimension
2027 % CHECK_LOCATION().AsString()));
2028 }
2029
Matteo Martincigh747ef822018-12-18 09:26:39 +00002030 auto filterTensorAndData = CreateConstTensor(inputs[1],
2031 filterTensorInfo,
2032 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002033 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002034 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2035
2036 if (inputs.size() == 3)
2037 {
2038 desc.m_BiasEnabled = true;
2039 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002040 auto biasTensorAndData = CreateConstTensor(inputs[2],
2041 biasTensorInfo,
2042 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002043 layer = m_Network->AddFullyConnectedLayer(desc,
2044 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002045 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002046 layerName.c_str());
2047 }
2048 else
2049 {
2050 layer = m_Network->AddFullyConnectedLayer(desc,
2051 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002052 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002053 layerName.c_str());
2054 }
2055 BOOST_ASSERT(layer != nullptr);
2056
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002057 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2058
2059 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2060
2061 if (inputTensorInfo.GetNumDimensions() > 2)
2062 {
2063 // Add reshape to flatten to 2D [batch_size, input_size],
2064 // where "input_size" corresponds to the number of inputs to the layer,
2065 // matching the second dimension of weights,
2066 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2067 std::vector<unsigned int> reshapedDimensions(2);
2068 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2069 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2070
2071 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2072 {
2073 throw ParseException(
2074 boost::str(
2075 boost::format(
2076 "Failed to deduce input tensor shape from filter size %1%")
2077 % reshapedDimensions[1]
2078 % CHECK_LOCATION().AsString()));
2079 }
2080
2081 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2082 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2083
2084 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2085 armnn::ReshapeDescriptor desc;
2086 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2087 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2088
2089 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2090 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2091
2092 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2093 }
2094 else
2095 {
2096 // register the input connection slot for the layer
2097 // only the tensors for the inputs are relevant, exclude the const tensors
2098 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2099 }
2100
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002101 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2102 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2103
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002104 // we need to add the activation layer and fortunately we don't need to care about the data layout
2105 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2106 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002107
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002108 // register the output connection slots for the layer, connections are made after all layers have been created
2109 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2110 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2111}
2112
keidav011b3e2ea2019-02-21 10:07:37 +00002113void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2114{
2115 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2116
2117 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2118
2119 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2120 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2121 CHECK_VALID_SIZE(outputs.size(), 4);
2122
2123 // Obtain custom options from flexbuffers
2124 auto custom_options = operatorPtr->custom_options;
2125 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2126
2127 // Obtain descriptor information from tf lite
2128 DetectionPostProcessDescriptor desc;
2129 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2130 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2131 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2132 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2133 desc.m_NumClasses = m["num_classes"].AsUInt32();
2134 desc.m_ScaleH = m["h_scale"].AsFloat();
2135 desc.m_ScaleW = m["w_scale"].AsFloat();
2136 desc.m_ScaleX = m["x_scale"].AsFloat();
2137 desc.m_ScaleY = m["y_scale"].AsFloat();
2138
keidav0107d58c72019-02-26 11:57:39 +00002139 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002140 {
keidav0107d58c72019-02-26 11:57:39 +00002141 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002142 }
2143 if (!(m["detections_per_class"].IsNull()))
2144 {
2145 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2146 }
2147
2148 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2149 {
2150 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2151 "must be positive and less than or equal to 1.");
2152 }
2153
2154 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2155 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2156 armnn::Optional<armnn::PermutationVector&>());
2157
2158 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2159 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2160 layerName.c_str());
2161
2162 BOOST_ASSERT(layer != nullptr);
2163
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002164 // The model does not specify the output shapes.
2165 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2166 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2167 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2168 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2169 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2170 m_OverridenOutputShapes.push_back({ 1 });
2171
keidav011b3e2ea2019-02-21 10:07:37 +00002172 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2173 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002174 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002175 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2176 }
2177
2178 // Register the input connection slots for the layer, connections are made after all layers have been created
2179 // only the tensors for the inputs are relevant, exclude the const tensors
2180 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2181 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2182
2183 // Register the output connection slots for the layer, connections are made after all layers have been created
2184 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2185 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2186 outputTensorIndexes[1],
2187 outputTensorIndexes[2],
2188 outputTensorIndexes[3]});
2189}
2190
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002191/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2192void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2193{
2194 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2195
2196 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2197 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2198 CHECK_VALID_SIZE(outputs.size(), 1);
2199
2200 if (inputs.size() < 1)
2201 {
2202 throw ParseException("Pack must have at least one input.");
2203 }
2204
2205 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2206 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2207
2208 StackDescriptor desc;
2209 desc.m_Axis = static_cast<uint32_t>(options->axis);
2210 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2211
2212 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2213 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2214 desc.m_InputShape = inputTensorInfo.GetShape();
2215
2216 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2217 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2218
2219 BOOST_ASSERT(layer != nullptr);
2220
2221 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2222 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2223
2224 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2225 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2226
2227 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2228 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2229}
2230
Nina Drozd200e3802019-04-15 09:47:39 +01002231void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2232{
2233 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2234
2235 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2236 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2237
2238 // This unpackAxis indicates the axis to unpack
2239 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2240
2241 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2242 CHECK_VALID_SIZE(inputs.size(), 1);
2243
2244 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002245
2246 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2247 {
2248 throw ParseException(
2249 boost::str(
2250 boost::format(
2251 "The unpack axis: %1% cannot be greater than or equal to "
2252 "the number of input dimension %2% %3%")
2253 % unpackAxis
2254 % inputTensorInfo.GetNumDimensions()
2255 % CHECK_LOCATION().AsString()));
2256 }
2257
Nina Drozd200e3802019-04-15 09:47:39 +01002258 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2259 // If num is not defined, automatically infer from the length of the dimension axis.
2260 if(unpackNum == 0)
2261 {
2262 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2263 }
2264
2265 // If unpack number cannot be inferred and is still zero, throw ParseException.
2266 if(unpackNum == 0)
2267 {
2268 throw ParseException("Number to unpack must greater than zero.");
2269 }
2270
2271 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2272 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2273
2274 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2275 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2276
2277 // Add current input shape to unpackDimSizes
2278 for (unsigned int i = 0; i < inputDimSize; ++i)
2279 {
2280 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2281 }
2282
2283 if (unpackDimSizes[unpackAxis] != unpackNum)
2284 {
2285 throw ParseException("Number to unpack must be the same as length of the dimension to "
2286 "unpack along.");
2287 }
2288
2289 unpackDimSizes[unpackAxis] /= unpackNum;
2290
2291 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2292 for (unsigned int j = 0; j < unpackNum; ++j)
2293 {
2294 // Set the size of the views.
2295 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2296 {
2297 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2298 }
2299 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2300 }
2301
2302 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2303 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2304
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002305 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2306 unpackDimSizes.data());
2307
Nina Drozd200e3802019-04-15 09:47:39 +01002308 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2309 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2310
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002311 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2312 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2313 {
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002314 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002315 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2316 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002317 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002318 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2319
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002320 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2321 outputTensorInfo.GetDataType(),
2322 outputTensorInfo.GetQuantizationScale(),
2323 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002324 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2325
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002326 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002327
2328 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2329 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2330 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2331 }
Nina Drozd200e3802019-04-15 09:47:39 +01002332}
2333
Nina Drozd0324f482019-04-08 10:52:10 +01002334void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2335{
2336 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2337
2338 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2339 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2340
2341 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2342
Nina Drozd200e3802019-04-15 09:47:39 +01002343 // If number of splits cannot be inferred and is zero, throw ParseException.
2344 if(numSplits == 0)
2345 {
2346 throw ParseException("Number to splits must greater than zero.");
2347 }
2348
Nina Drozd0324f482019-04-08 10:52:10 +01002349 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2350 CHECK_VALID_SIZE(inputs.size(), 2);
2351 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2352 CHECK_VALID_SIZE(outputs.size(), numSplits);
2353
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002354 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2355 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002356
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002357 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2358 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2359 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2360
2361 BOOST_ASSERT(axisTensorInfo.GetNumElements() == 1);
2362 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002363
2364 // Armnn supports split along the channel dimension for data formats NHWC and NCHW.
2365 if (splitDim == 0 || splitDim == 2)
2366 {
2367 throw ParseException(
2368 boost::str(
2369 boost::format(
2370 "Dimension %1% for split is not supported by Armnn. %2%")
2371 % splitDim
2372 % CHECK_LOCATION().AsString()));
2373 }
2374
2375 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002376 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002377 {
2378 throw ParseException(
2379 boost::str(
2380 boost::format(
2381 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002382 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002383 % inputTensorInfo.GetNumDimensions()
2384 % MaxNumOfTensorDimensions
2385 % CHECK_LOCATION().AsString()));
2386 }
2387
2388 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2389
2390 // Add current input shape to splitterDimSizes
2391 for (unsigned int i = 0; i < inputDimSize; ++i)
2392 {
2393 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2394 }
2395
2396 if (splitterDimSizes[splitDim] % numSplits != 0)
2397 {
2398 throw ParseException("Number of splits must evenly divide the dimension");
2399 }
2400 splitterDimSizes[splitDim] /= numSplits;
2401
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002402 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002403 for (unsigned int j = 0; j < numSplits; ++j)
2404 {
2405 // Set the size of the views.
2406 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2407 {
2408 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2409 }
2410 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2411 }
2412
2413 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2414 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2415
2416 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002417 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002418
Nina Drozd0324f482019-04-08 10:52:10 +01002419 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2420 {
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002421 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2422 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002423 }
2424
2425 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2426 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2427}
2428
Sadik Armagan58f39192018-09-17 14:14:39 +01002429armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2430 unsigned int outputSlot,
2431 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002432{
2433 ActivationDescriptor activationDesc;
2434 std::string layerName = prevLayer->GetName();
2435
2436 switch(activationType)
2437 {
2438 case tflite::ActivationFunctionType_NONE:
2439 {
2440 // this is a no-op: return previous layer
2441 return prevLayer;
2442 }
2443 case tflite::ActivationFunctionType_RELU:
2444 {
2445 activationDesc.m_Function = ActivationFunction::ReLu;
2446 layerName += ":RELU";
2447 break;
2448 }
2449 case tflite::ActivationFunctionType_RELU6:
2450 {
2451 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2452 activationDesc.m_A = 6.0f;
2453 activationDesc.m_B = 0.0f;
2454 layerName += ":RELU6";
2455 break;
2456 }
2457 case tflite::ActivationFunctionType_TANH:
2458 {
2459 activationDesc.m_Function = ActivationFunction::TanH;
2460 activationDesc.m_A = 1.0f;
2461 activationDesc.m_B = 1.0f;
2462 layerName += ":TANH";
2463 break;
2464 }
2465
2466 // I only put these here as a reminder what others we could support
2467 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2468 case tflite::ActivationFunctionType_SIGN_BIT:
2469 default:
2470 {
2471 throw ParseException(
2472 boost::str(
2473 boost::format("TfLite parser doesn't suppport fused activation: "
2474 "%1%/%2% %3% ") %
2475 activationType %
2476 tflite::EnumNameActivationFunctionType(activationType) %
2477 CHECK_LOCATION().AsString()));
2478
2479 }
2480 }
2481
2482 IConnectableLayer* activationLayer =
2483 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2484
2485 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2486 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2487 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2488 return activationLayer;
2489}
2490
2491TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2492{
2493 if (fileName == nullptr)
2494 {
2495 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2496 CHECK_LOCATION().AsString()));
2497 }
2498 boost::system::error_code errorCode;
2499 boost::filesystem::path pathToFile(fileName);
2500 if (!boost::filesystem::exists(pathToFile, errorCode))
2501 {
2502 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2503 fileName %
2504 errorCode %
2505 CHECK_LOCATION().AsString()));
2506 }
2507 std::ifstream file(fileName, std::ios::binary);
2508 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2509 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2510 fileContent.size());
2511}
2512
2513TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2514{
2515 if (binaryContent == nullptr)
2516 {
2517 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2518 CHECK_LOCATION().AsString()));
2519 }
2520 flatbuffers::Verifier verifier(binaryContent, len);
2521 if (verifier.VerifyBuffer<tflite::Model>() == false)
2522 {
2523 throw ParseException(
2524 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2525 "flatbuffers format. size:%1% %2%") %
2526 len %
2527 CHECK_LOCATION().AsString()));
2528 }
2529 return tflite::UnPackModel(binaryContent);
2530}
2531
2532TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2533 size_t subgraphIndex,
2534 size_t operatorIndex)
2535{
2536 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2537
Derek Lambertiff05cc52019-04-26 13:05:17 +01002538 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2539 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002540
2541 size_t inputCount = operatorPtr->inputs.size();
2542 TensorRawPtrVector result(inputCount);
2543 for (size_t i=0; i<inputCount; ++i)
2544 {
2545 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002546 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002547 }
2548 return result;
2549}
2550
2551TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2552 size_t subgraphIndex,
2553 size_t operatorIndex)
2554{
2555 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2556
Derek Lambertiff05cc52019-04-26 13:05:17 +01002557 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2558 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002559
2560 size_t outputCount = operatorPtr->outputs.size();
2561 TensorRawPtrVector result(outputCount);
2562 for (size_t i=0; i<outputCount; ++i)
2563 {
2564 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2565 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002566 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002567 }
2568 return result;
2569}
2570
2571TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2572 size_t subgraphIndex)
2573{
2574 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002575 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002576
Derek Lambertiff05cc52019-04-26 13:05:17 +01002577 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002578 TensorIdRawPtrVector result(inputCount);
2579 for (size_t i=0; i<inputCount; ++i)
2580 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002581 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01002582 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002583 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002584 }
2585 return result;
2586}
2587
2588TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2589 size_t subgraphIndex)
2590{
2591 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002592 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002593
Derek Lambertiff05cc52019-04-26 13:05:17 +01002594 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002595 TensorIdRawPtrVector result(outputCount);
2596 for (size_t i=0; i<outputCount; ++i)
2597 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002598 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2599 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002600 }
2601 return result;
2602}
2603
2604std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2605 size_t subgraphIndex,
2606 size_t operatorIndex)
2607{
2608 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002609 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2610 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002611 return operatorPtr->inputs;
2612}
2613
2614std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2615 size_t subgraphIndex,
2616 size_t operatorIndex)
2617{
2618 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002619 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2620 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002621 return operatorPtr->outputs;
2622}
2623
2624void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2625 size_t operatorIndex,
2626 IConnectableLayer* layer,
2627 const std::vector<unsigned int>& tensorIndexes)
2628{
2629 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2630 BOOST_ASSERT(layer != nullptr);
2631 if (tensorIndexes.size() != layer->GetNumInputSlots())
2632 {
2633 throw ParseException(
2634 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2635 " for subgraph:%3% operator index:%4% %5%") %
2636 tensorIndexes.size() %
2637 layer->GetNumInputSlots() %
2638 subgraphIndex %
2639 operatorIndex %
2640 CHECK_LOCATION().AsString()));
2641 }
2642
2643 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2644 {
2645 unsigned int tensorIndex = tensorIndexes[slotIndex];
2646 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2647 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2648 }
2649}
2650
2651void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2652 size_t operatorIndex,
2653 IConnectableLayer* layer,
2654 const std::vector<unsigned int>& tensorIndexes)
2655{
2656 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2657 BOOST_ASSERT(layer != nullptr);
2658 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2659 {
2660 throw ParseException(
2661 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2662 " for subgraph:%3% operator index:%4% %5%") %
2663 tensorIndexes.size() %
2664 layer->GetNumOutputSlots() %
2665 subgraphIndex %
2666 operatorIndex %
2667 CHECK_LOCATION().AsString()));
2668 }
2669
2670 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2671 {
2672 unsigned int tensorIndex = tensorIndexes[slotIndex];
2673 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2674 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2675 }
2676}
2677
2678void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2679{
2680 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2681
2682 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2683 for (auto const & tensorIdAndPtr : inputs)
2684 {
2685 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2686 IConnectableLayer* layer =
2687 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2688
2689 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2690 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2691
2692 RegisterOutputSlots(subgraphIndex,
2693 VIRTUAL_OPERATOR_ID,
2694 layer,
2695 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2696 }
2697}
2698
2699void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2700{
2701 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2702
2703 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2704 for (auto const & tensorIdAndPtr : outputs)
2705 {
2706 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2707 IConnectableLayer* layer =
2708 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2709
2710 RegisterInputSlots(subgraphIndex,
2711 VIRTUAL_OPERATOR_ID,
2712 layer,
2713 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2714 }
2715}
2716
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002717void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2718{
2719 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2720
Derek Lambertiff05cc52019-04-26 13:05:17 +01002721 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002722 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2723 {
2724 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2725 {
2726 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2727 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2728 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002729 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002730 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2731 auto tensorAndData = CreateConstTensor(tensorPtr,
2732 tensorInfo,
2733 armnn::Optional<armnn::PermutationVector&>());
2734
2735 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2736 IConnectableLayer *layer =
2737 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2738
2739 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2740 RegisterOutputSlots(subgraphIndex,
2741 VIRTUAL_OPERATOR_ID,
2742 layer,
2743 { tensorIndex });
2744
2745 }
2746 }
2747 }
2748}
2749
telsoa01c577f2c2018-08-31 09:22:23 +01002750// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2751TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2752{
2753 CHECK_BUFFER(model, bufferIndex);
2754 return model->buffers[bufferIndex].get();
2755}
2756
Matteo Martincigh747ef822018-12-18 09:26:39 +00002757template<typename T>
2758std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2759TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2760 TfLiteParser::TensorRawPtr tensorPtr,
2761 armnn::TensorInfo& tensorInfo,
2762 armnn::Optional<armnn::PermutationVector&> permutationVector)
2763{
2764 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2765 tensorPtr,
2766 tensorInfo,
2767 permutationVector);
2768 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2769 return std::make_pair(constData.first, std::move(storage));
2770}
2771
telsoa01c577f2c2018-08-31 09:22:23 +01002772std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2773TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002774 armnn::TensorInfo& tensorInfo,
2775 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002776{
2777 CHECK_TENSOR_PTR(tensorPtr);
2778 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2779 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2780
2781 switch (tensorInfo.GetDataType())
2782 {
2783 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002784 return CreateConstTensorAndStoreData<float>(bufferPtr,
2785 tensorPtr,
2786 tensorInfo,
2787 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002788 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002789 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2790 tensorPtr,
2791 tensorInfo,
2792 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002793 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002794 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2795 tensorPtr,
2796 tensorInfo,
2797 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002798 default:
2799 {
2800 std::stringstream errString;
2801 errString << "Unexpected datatype when creating const tensor: "
2802 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2803 << " shape:" << tensorInfo.GetShape()
2804 << CHECK_LOCATION().AsString();
2805 throw ParseException(errString.str());
2806 }
2807 }
2808}
2809
2810BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2811 const std::string& name) const
2812{
2813 CHECK_SUBGRAPH(m_Model, subgraphId);
2814 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2815 for (auto const & input : inputs)
2816 {
2817 if (input.second->name == name)
2818 {
2819 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2820 return std::make_pair(bindingId, ToTensorInfo(input.second));
2821 }
2822 }
2823
2824 std::stringstream bindings;
2825 for (auto const & input : inputs)
2826 {
2827 bindings << "'" << input.second->name << "' ";
2828 }
2829
2830 throw ParseException(
2831 boost::str(
2832 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2833 "Possible inputs are: [%3%] %4%") %
2834 subgraphId %
2835 name %
2836 bindings.str() %
2837 CHECK_LOCATION().AsString()));
2838}
2839
2840BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2841 const std::string& name) const
2842{
2843 CHECK_SUBGRAPH(m_Model, subgraphId);
2844 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002845 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01002846 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002847 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01002848 if (output.second->name == name)
2849 {
2850 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002851 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
2852 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
2853 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01002854 }
2855 }
2856
2857 std::stringstream bindings;
2858 for (auto const & output : outputs)
2859 {
2860 bindings << "'" << output.second->name << "' ";
2861 }
2862
2863 throw ParseException(
2864 boost::str(
2865 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2866 "Possible outputs are: [%3%] %4%") %
2867 subgraphId %
2868 name %
2869 bindings.str() %
2870 CHECK_LOCATION().AsString()));
2871}
2872
2873size_t TfLiteParser::GetSubgraphCount() const
2874{
2875 return m_Model->subgraphs.size();
2876}
2877
2878std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2879{
2880 CHECK_SUBGRAPH(m_Model, subgraphId);
2881 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2882 std::vector<std::string> result;
2883 result.reserve(inputs.size());
2884 for (auto const & input : inputs)
2885 {
2886 result.push_back(input.second->name);
2887 }
2888 return result;
2889}
2890
2891std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2892{
2893 CHECK_SUBGRAPH(m_Model, subgraphId);
2894 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2895 std::vector<std::string> result;
2896 result.reserve(outputs.size());
2897 for (auto const & output : outputs)
2898 {
2899 result.push_back(output.second->name);
2900 }
2901 return result;
2902}
2903
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002904ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002905{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002906 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01002907}
2908
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002909ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002910{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002911 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01002912}
2913
2914void ITfLiteParser::Destroy(ITfLiteParser* parser)
2915{
2916 delete parser;
2917}
2918
2919TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2920: m_FloatData(std::move(data))
2921, m_Uint8Data(nullptr)
2922, m_Int32Data(nullptr)
2923{
2924}
2925
2926TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2927: m_FloatData(nullptr)
2928, m_Uint8Data(std::move(data))
2929, m_Int32Data(nullptr)
2930{
2931}
2932
2933TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2934: m_FloatData(nullptr)
2935, m_Uint8Data(nullptr)
2936, m_Int32Data(std::move(data))
2937{
2938}
2939
2940} // armnnTfLiteParser