blob: 9c7dda8aecad8e53c3275e34cb46245bc8bc3045 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
Matteo Martincighe011d202019-11-28 11:35:47 +00005
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "TfLiteParser.hpp"
7
8#include <armnn/ArmNN.hpp>
9#include <armnn/Exceptions.hpp>
Derek Lamberti08446972019-11-26 16:38:31 +000010#include <armnn/Logging.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011#include <armnn/TypesUtils.hpp>
12#include <boost/filesystem.hpp>
13
14// armnnUtils:
Matteo Martincighe011d202019-11-28 11:35:47 +000015#include <armnnUtils/Permute.hpp>
16
Sadik Armagan479045b2018-10-01 11:51:37 +010017#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010018#include <VerificationHelpers.hpp>
19
20// The generated code based on the Tf Lite schema:
21#include <schema_generated.h>
22
Matteo Martincighe011d202019-11-28 11:35:47 +000023#include <flatbuffers/flexbuffers.h>
24
telsoa01c577f2c2018-08-31 09:22:23 +010025#include <boost/core/ignore_unused.hpp>
26#include <boost/assert.hpp>
27#include <boost/format.hpp>
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +010028#include <boost/numeric/conversion/cast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010029
30#include <fstream>
31#include <algorithm>
32#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010033#include <numeric>
telsoa01c577f2c2018-08-31 09:22:23 +010034
35using namespace armnn;
36using armnn::CheckLocation;
37namespace armnnTfLiteParser
38{
39namespace
40{
jimfly01c25411c2018-11-14 17:47:22 +000041
telsoa01c577f2c2018-08-31 09:22:23 +010042const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
43
44void CheckSubgraph(const TfLiteParser::ModelPtr & model,
45 size_t subgraphIndex,
46 const CheckLocation & location)
47{
48 if (model.get() == nullptr)
49 {
50 throw ParseException(
51 boost::str(
52 boost::format("%1% was called with invalid (null) model. "
53 "Possible reason is that the model is not yet loaded and Unpack(ed). "
54 "subgraph:%2% at %3%") %
55 location.m_Function %
56 subgraphIndex %
57 location.FileLine()));
58 }
59 else if (subgraphIndex >= model->subgraphs.size())
60 {
61 throw ParseException(
62 boost::str(
63 boost::format("%1% was called with an invalid subgraph index. "
64 "subgraph:%2% at %3%") %
65 location.m_Function %
66 subgraphIndex %
67 location.FileLine()));
68 }
69}
70
71#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
72 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
73
74void CheckModel(const TfLiteParser::ModelPtr & model,
75 size_t subgraphIndex,
76 size_t operatorIndex,
77 const CheckLocation & location)
78{
79 if (model.get() == nullptr)
80 {
81 throw ParseException(
82 boost::str(
83 boost::format("%1% was called with invalid (null) model. "
84 "Possible reason is that the model is not yet loaded and Unpack(ed). "
85 "subgraph:%2% operator:%3% at %4%") %
86 location.m_Function %
87 subgraphIndex %
88 operatorIndex %
89 location.FileLine()));
90 }
91 else if (subgraphIndex >= model->subgraphs.size())
92 {
93 throw ParseException(
94 boost::str(
95 boost::format("%1% was called with an invalid subgraph index. "
96 "subgraph:%2% operator:%3% at %4%") %
97 location.m_Function %
98 subgraphIndex %
99 operatorIndex %
100 location.FileLine()));
101 }
102 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
103 operatorIndex != VIRTUAL_OPERATOR_ID)
104 {
105 throw ParseException(
106 boost::str(
107 boost::format("%1% was called with an invalid operator index. "
108 "subgraph:%2% operator:%3% at %4%") %
109 location.m_Function %
110 subgraphIndex %
111 operatorIndex %
112 location.FileLine()));
113 }
114}
115
116#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
117 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
118
119void CheckTensor(const TfLiteParser::ModelPtr & model,
120 size_t subgraphIndex,
121 size_t tensorIndex,
122 const CheckLocation & location)
123{
124 // not checking model, because I assume CHECK_MODEL already run
125 // and checked that. An assert would do.
126 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
127
128 // also subgraph index should be checked by CHECK_MODEL so
129 // I only add an assert here
130 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
131
132 // the tensor index is the only one to check here
133 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
134 {
135 throw ParseException(
136 boost::str(
137 boost::format("%1% was called with an invalid tensor index. "
138 "subgraph:%2% tensor:%3% at %4%") %
139 location.m_Function %
140 subgraphIndex %
141 tensorIndex %
142 location.FileLine()));
143 }
144}
145
146#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
147 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
148
149void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
150 const CheckLocation & location)
151{
152 if (rawPtr == nullptr)
153 {
154 throw ParseException(
155 boost::str(
156 boost::format("%1% was called with a null tensor pointer. "
157 "at %2%") %
158 location.m_Function %
159 location.FileLine()));
160
161 }
162}
163
164#define CHECK_TENSOR_PTR(TENSOR_PTR) \
165 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
166
167void CheckBuffer(const TfLiteParser::ModelPtr & model,
168 size_t bufferIndex,
169 const CheckLocation & location)
170{
171 if (model.get() == nullptr)
172 {
173 throw ParseException(
174 boost::str(
175 boost::format("%1% was called with invalid (null) model. "
176 "Possible reason is that the model is not yet loaded and Unpack(ed). "
177 "buffer:%2% at %3%") %
178 location.m_Function %
179 bufferIndex %
180 location.FileLine()));
181 }
182 else if (bufferIndex >= model->buffers.size())
183 {
184 throw ParseException(
185 boost::str(
186 boost::format("%1% was called with an invalid buffer index. "
187 "buffer index:%2% at %3%") %
188 location.m_Function %
189 bufferIndex %
190 location.FileLine()));
191 }
192 else if (model->buffers[bufferIndex].get() == nullptr)
193 {
194 throw ParseException(
195 boost::str(
196 boost::format("The buffer #%1% is null. %3%") %
197 bufferIndex %
198 location.AsString()));
199 }
200}
201
202#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
203 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
204
205void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
206 const armnn::TensorInfo & tensorInfo,
207 uint32_t bufferId,
208 const CheckLocation & location)
209{
210 if (bufferPtr == nullptr)
211 {
212 throw ParseException(
213 boost::str(
214 boost::format("BufferPtr is null for buffer:%1%. %2%") %
215 bufferId %
216 location.AsString()));
217 }
218 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
219 tensorInfo.GetNumBytes() > bufferPtr->data.size())
220 {
221 std::stringstream ss;
222 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
223 << "For tensor: " << tensorInfo.GetShape()
224 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
225 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
226 throw ParseException(ss.str());
227 }
228}
229
230#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
231 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
232
233bool IsActivationSupported(tflite::ActivationFunctionType activationType)
234{
235 switch(activationType)
236 {
237 case tflite::ActivationFunctionType_NONE:
238 case tflite::ActivationFunctionType_RELU:
239 case tflite::ActivationFunctionType_RELU6:
240 case tflite::ActivationFunctionType_TANH:
241 {
242 return true;
243 }
244 default:
245 {
246 return false;
247 }
248 }
249}
250
251#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
252 do { \
253 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
254 { \
255 throw ParseException( \
256 boost::str( \
257 boost::format("TfLite parser doesn't suppport fused activation: " \
258 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
259 OPTION->fused_activation_function % \
260 tflite::EnumNameActivationFunctionType(\
261 OPTION->fused_activation_function) % \
262 __func__ % \
263 SUBGRAPH_INDEX % \
264 OPERATOR_INDEX % \
265 CHECK_LOCATION().FileLine())); \
266 } \
267 } while(false)
268
269
270std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
271{
272 std::vector<unsigned int> result;
273 result.reserve(in.size());
274 for (auto & i : in)
275 {
276 result.push_back(CHECKED_NON_NEGATIVE(i));
277 }
278 return result;
279}
280
281void CalcPadding(uint32_t inputSize,
282 uint32_t filterSize,
283 uint32_t stride,
Pablo Tellof0bd6832019-04-26 17:58:13 +0100284 uint32_t dilation,
telsoa01c577f2c2018-08-31 09:22:23 +0100285 uint32_t& paddingFront,
286 uint32_t& paddingBack,
287 tflite::Padding padding)
288{
289 paddingFront = 0;
290 paddingBack = 0;
291 if (padding == tflite::Padding_SAME)
292 {
293 uint32_t outputSize = (inputSize + stride - 1) / stride;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100294 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
295 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
telsoa01c577f2c2018-08-31 09:22:23 +0100296 if (temp > inputSize)
297 {
298 paddingFront = (temp - inputSize) / 2;
299 paddingBack = (temp - inputSize) - paddingFront;
300 }
301 }
302}
303
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000304armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes)
telsoa01c577f2c2018-08-31 09:22:23 +0100305{
306 armnn::DataType type;
307 CHECK_TENSOR_PTR(tensorPtr);
308
309 switch (tensorPtr->type)
310 {
311 case tflite::TensorType_UINT8:
312 type = armnn::DataType::QuantisedAsymm8;
313 break;
314 case tflite::TensorType_FLOAT32:
315 type = armnn::DataType::Float32;
316 break;
Finn Williamsed66d142019-12-06 09:55:55 +0000317 case tflite::TensorType_INT8:
318 type = armnn::DataType::QSymmS8;
319 break;
320 case tflite::TensorType_INT16:
321 type = armnn::DataType::QuantisedSymm16;
322 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100323 case tflite::TensorType_INT32:
324 type = armnn::DataType::Signed32;
325 break;
326
327 default:
328 {
329 CheckLocation location = CHECK_LOCATION();
330 throw ParseException(
331 boost::str(
332 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
333 tensorPtr->type %
334 tflite::EnumNameTensorType(tensorPtr->type) %
335 tensorPtr->name %
336 location.AsString()));
337 }
338 }
339
340 float quantizationScale = 0.0f;
341 int32_t quantizationOffset = 0;
342
343 if (tensorPtr->quantization.get())
344 {
345 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
346 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
347
348 if (tensorPtr->quantization->scale.size() == 1)
349 {
350 quantizationScale = tensorPtr->quantization->scale[0];
351 }
352 if (tensorPtr->quantization->zero_point.size() == 1)
353 {
354 // NOTE: we lose precision here when converting from 64 bit to 32
355 // but this is what we support at the monent in ArmNN
356 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
357 }
358 }
359
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100360 std::vector<unsigned int> safeShape = shapes;
361 if (safeShape.size() == 0)
362 {
363 safeShape.push_back(1);
364 }
365
telsoa01c577f2c2018-08-31 09:22:23 +0100366 // two statements (on purpose) for easier debugging:
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100367 armnn::TensorInfo result(static_cast<unsigned int>(safeShape.size()),
368 safeShape.data(),
telsoa01c577f2c2018-08-31 09:22:23 +0100369 type,
370 quantizationScale,
371 quantizationOffset);
372 return result;
373}
374
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000375armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
376{
377 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
378 return ToTensorInfo(tensorPtr, dimensions);
379}
380
telsoa01c577f2c2018-08-31 09:22:23 +0100381template<typename T>
382std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
383CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
384 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000385 armnn::TensorInfo& tensorInfo,
386 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100387{
Derek Lambertibaa177f2019-12-10 22:00:43 +0000388 boost::ignore_unused(tensorPtr);
telsoa01c577f2c2018-08-31 09:22:23 +0100389 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
390 BOOST_ASSERT_MSG(bufferPtr != nullptr,
391 boost::str(
392 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
393
394 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000395
396 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
397 {
398 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000399 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
400 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000401 }
402 else
403 {
404 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
405 }
406
telsoa01c577f2c2018-08-31 09:22:23 +0100407 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
408}
409
telsoa01c577f2c2018-08-31 09:22:23 +0100410armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
411{
412 // generate the binding id by shifting the tensor id by 8 bit
413 // and add the subgraph id, which allows 256 subgraphs
414 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
415}
416
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000417bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
418{
419 const unsigned int actualSize = actual.GetNumDimensions();
420 if (actualSize != expected.size())
421 {
422 return false;
423 }
424
425 for (unsigned int i = 0u; i < actualSize; i++)
426 {
427 if (expected[i] < 0 ||
428 actual[i] != static_cast<unsigned int>(expected[i]))
429 {
430 return false;
431 }
432 }
433
434 return true;
435}
436
telsoa01c577f2c2018-08-31 09:22:23 +0100437} // <anonymous>
438
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100439TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
440: m_Options(options)
441, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100442, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
443{
444 // register supported operators
Sadik Armagana3b31f02019-12-05 09:08:53 +0000445 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
446 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
447 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
448 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
449 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Finn Williamsed66d142019-12-06 09:55:55 +0000450 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000451 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
452 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
453 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
454 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
455 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
456 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
457 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
458 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
459 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
460 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
461 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
462 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
463 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
464 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
465 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
466 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
467 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
468 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
469 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
470 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
471 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
472 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
473 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
474 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
475 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
476 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
477 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
478 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100479
480 // register supported custom operators
481 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100482}
483
484void TfLiteParser::ResetParser()
485{
486 m_Network = armnn::INetworkPtr(nullptr, nullptr);
487 m_Model = nullptr;
488 m_SubgraphConnections.clear();
489}
490
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200491void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
492 size_t operatorIndex,
493 IConnectableLayer *layer)
494{
495 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
496 BOOST_ASSERT(layer != nullptr);
497
Derek Lambertiff05cc52019-04-26 13:05:17 +0100498 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
499 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200500
501 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
502
503 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100504 TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200505 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100506 TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200507
508 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
509 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
510
511 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
512 {
513 uint32_t id = reshapedInputId;
514 reshapedInputId = inputId;
515 inputId = id;
516
517 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
518 inputTensorInfo = ToTensorInfo(tensorPtr);
519 }
520
521 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
522
523 std::vector<unsigned> reshapedDim;
524 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
525 {
526 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
527 }
528
529 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
530 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
531
532 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
533
534 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
535 armnn::ReshapeDescriptor desc;
536 desc.m_TargetShape = reshapedTensorInfo.GetShape();
537 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
538
539 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
540 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
541
542 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
543
544 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
545 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
546}
547
telsoa01c577f2c2018-08-31 09:22:23 +0100548INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
549{
550 ResetParser();
551 m_Model = LoadModelFromFile(graphFile);
552 return CreateNetworkFromModel();
553}
554
555INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
556{
557 ResetParser();
558 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
559 return CreateNetworkFromModel();
560}
561
562INetworkPtr TfLiteParser::CreateNetworkFromModel()
563{
564 m_Network = INetwork::Create();
565 BOOST_ASSERT(m_Model.get() != nullptr);
566
567 bool failedToCreate = false;
568 std::stringstream errors;
569
570 if (m_Model->subgraphs.size() != 1)
571 {
572 throw ParseException(
573 boost::str(
574 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
575 m_Model->subgraphs.size() %
576 CHECK_LOCATION().AsString()));
577 }
578
579 size_t subgraphIndex = 0;
Derek Lambertiff05cc52019-04-26 13:05:17 +0100580 for (SubgraphPtr const & subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100581 {
582 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
583
584 size_t operatorIndex = 0;
585 for (OperatorPtr const & op : subgraph->operators)
586 {
587 try
588 {
telsoa01c577f2c2018-08-31 09:22:23 +0100589 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
590 auto builtinCode = opCodePtr->builtin_code;
591
592 if (builtinCode > tflite::BuiltinOperator_MAX)
593 {
594 throw ParseException(
595 boost::str(
596 boost::format("Operator code %1% is out of range 0-%2%. "
597 "subgraph:%3% operator idx:%4%. %5%") %
598 builtinCode %
599 tflite::BuiltinOperator_MAX %
600 subgraphIndex %
601 operatorIndex %
602 CHECK_LOCATION().AsString()));
603 }
604
605 // lookup and call the parser function
606 auto & parserFunction = m_ParserFunctions[builtinCode];
607 (this->*parserFunction)(subgraphIndex, operatorIndex);
608 }
609 catch (const ParseException& e)
610 {
611 failedToCreate = true;
612 std::stringstream errorString;
613
614 errorString << "Failed to parse operator #" << operatorIndex
615 << " within subgraph #" << subgraphIndex
616 << " error: " << e.what();
Derek Lamberti08446972019-11-26 16:38:31 +0000617 ARMNN_LOG(error) << errorString.str();
telsoa01c577f2c2018-08-31 09:22:23 +0100618
619 errors << errorString.str() << "\n";
620 }
621 ++operatorIndex;
622 }
623
624 SetupInputLayers(subgraphIndex);
625 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200626 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100627
628 ++subgraphIndex;
629 }
630
631 if (failedToCreate)
632 {
633 // we can skip everything and let the outer exception handler deal with the error
634 throw ParseException(errors.str());
635 }
636
637 // establish the connections from the layer outputs to the inputs of the subsequent layers
638 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
639 {
640 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
641 {
642 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
643 {
644 for (size_t inputSlotIdx = 0;
645 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
646 ++inputSlotIdx)
647 {
648 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
649 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
650 }
651 }
652 }
653 }
654
655 return std::move(m_Network);
656}
657
658void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
659 size_t tensorIndex,
660 armnn::IOutputSlot* slot)
661{
662 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
663 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
664 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
665
666 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
667
668 // assuming there is only one producer for that tensor
669 if (tensorSlots.outputSlot != nullptr)
670 {
671 throw ParseException(boost::str(
672 boost::format("Another layer has already registered itself as the producer of "
673 "subgraph:%1% tensor:%2% %3%") %
674 subgraphIndex %
675 tensorIndex %
676 CHECK_LOCATION().AsString()));
677 }
678
679 tensorSlots.outputSlot = slot;
680}
681
682void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
683 size_t tensorIndex,
684 armnn::IInputSlot* slot)
685{
686 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
687 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
688 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
689
690 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
691 tensorSlots.inputSlots.push_back(slot);
692}
693
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100694void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
695{
696 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
697
698 // NOTE: By default we presume the custom operator is not supported
699 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
700
701 // Identify custom code defined for custom operator
702 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
703 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
704
705 // Find parser function that correspondes to custom code (if any)
706 auto iterator = m_CustomParserFunctions.find(customCode);
707 if (iterator != m_CustomParserFunctions.end())
708 {
709 customParserFunction = iterator->second;
710 }
711
712 // Run parser function
713 (this->*customParserFunction)(subgraphIndex, operatorIndex);
714}
715
telsoa01c577f2c2018-08-31 09:22:23 +0100716void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
717{
718 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100719
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100720 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
721
722 auto opcodeIndex = operatorPtr->opcode_index;
723 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
724
725 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
726 {
727 // Do not add StandInLayer, throw ParseException instead
728 throw ParseException(
729 boost::str(
730 boost::format("Operator not supported. "
731 "subgraph:%1% operator:%2% "
732 "opcode_index:%3% opcode:%4% / %5% %6%") %
733 subgraphIndex %
734 operatorIndex %
735 opcodeIndex %
736 opcode %
737 tflite::EnumNameBuiltinOperator(opcode) %
738 CHECK_LOCATION().AsString()));
739 }
740
741 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
742 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
743
744 const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
745 const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
746
747 StandInDescriptor descriptor(numInputs, numOutputs);
748 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
749
750 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
751 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
752 for (unsigned int i = 0u; i < numOutputs; ++i)
753 {
754 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
755 }
756
757 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
758 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
759
760 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
761 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100762}
763
telsoa01c577f2c2018-08-31 09:22:23 +0100764void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
765{
766 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
767
768 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
769 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
770
771 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
772
773 Convolution2dDescriptor desc;
774 desc.m_BiasEnabled = false;
775 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
776 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000777 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100778 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
779 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000780
telsoa01c577f2c2018-08-31 09:22:23 +0100781 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
782 CHECK_VALID_SIZE(inputs.size(), 2, 3);
783
784 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
785 CHECK_VALID_SIZE(outputs.size(), 1);
786
787 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
788 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
789
790 // assuming input is NHWC
791 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
792 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
793
794 // assuming the filter is OHWI : Output, H, W, Input
795 // which is essentially the same as NHWC
796 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
797 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
798
Pablo Tellof0bd6832019-04-26 17:58:13 +0100799 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
800 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
801 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
802 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100803
Matteo Martincigh747ef822018-12-18 09:26:39 +0000804 auto filterTensorAndData = CreateConstTensor(inputs[1],
805 filterTensorInfo,
806 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100807 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100808
809 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
810
811 if (inputs.size() == 3)
812 {
813 desc.m_BiasEnabled = true;
814 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000815 auto biasTensorAndData = CreateConstTensor(inputs[2],
816 biasTensorInfo,
817 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100818 layer = m_Network->AddConvolution2dLayer(desc,
819 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100820 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100821 layerName.c_str());
822 }
823 else
824 {
825 layer = m_Network->AddConvolution2dLayer(desc,
826 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100827 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100828 layerName.c_str());
829 }
830
831 BOOST_ASSERT(layer != nullptr);
832
telsoa01c577f2c2018-08-31 09:22:23 +0100833 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000834 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100835
836 // register the input connection slots for the layer, connections are made after all layers have been created
837 // only the tensors for the inputs are relevant, exclude the const tensors
838 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000839 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100840
jimfly01c25411c2018-11-14 17:47:22 +0000841 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100842 // register the output connection slots for the layer, connections are made after all layers have been created
843 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
844 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
845}
846
847void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
848{
849 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
850
851 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
852 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
853
854 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
855
856 DepthwiseConvolution2dDescriptor desc;
857 desc.m_BiasEnabled = false;
858 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
859 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000860 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100861 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100862
863 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
864 CHECK_VALID_SIZE(inputs.size(), 2, 3);
865 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
866 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100867 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
868 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000869
telsoa01c577f2c2018-08-31 09:22:23 +0100870 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
871 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
872
Matteo Martincigh747ef822018-12-18 09:26:39 +0000873 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100874 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
875 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000876
877 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100878 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
879 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
880
Matteo Martincigh747ef822018-12-18 09:26:39 +0000881 // Reshape weights as [ H, W, I, M ]
882 filterTensorInfo.SetShape({ filterHeight,
883 filterWidth,
884 inputTensorInfo.GetShape()[3],
885 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
886
887 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
888 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
889
Pablo Tellof0bd6832019-04-26 17:58:13 +0100890 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
891 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
892 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
893 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100894
Matteo Martincigh747ef822018-12-18 09:26:39 +0000895 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100896 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100897 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
898
899 if (inputs.size() == 3)
900 {
901 desc.m_BiasEnabled = true;
902 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000903 auto biasTensorAndData = CreateConstTensor(inputs[2],
904 biasTensorInfo,
905 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100906 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
907 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100908 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100909 layerName.c_str());
910 }
911 else
912 {
913 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
914 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100915 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100916 layerName.c_str());
917 }
918 BOOST_ASSERT(layer != nullptr);
919
telsoa01c577f2c2018-08-31 09:22:23 +0100920 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000921 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100922
923 // register the input connection slots for the layer, connections are made after all layers have been created
924 // only the tensors for the inputs are relevant, exclude the const tensors
925 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000926 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100927
jimfly01c25411c2018-11-14 17:47:22 +0000928 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100929 // register the output connection slots for the layer, connections are made after all layers have been created
930 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
931 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
932}
933
Finn Williamsed66d142019-12-06 09:55:55 +0000934void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
935{
936 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
937
938 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
939 CHECK_VALID_SIZE(inputs.size(), 1);
940
941 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
942 CHECK_VALID_SIZE(outputs.size(), 1);
943
944 auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
945
946 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
947 BOOST_ASSERT(layer != nullptr);
948
949 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
950 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
951
952 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
953 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
954
955 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
956 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
957}
958
Keith Davis4cd29a02019-09-09 14:49:20 +0100959void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
960{
961 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
962
963 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +0100964 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +0100965
966 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
967 CHECK_VALID_SIZE(outputs.size(), 1);
968
969 armnn::IConnectableLayer* layer = nullptr;
970 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
971
972 PermuteDescriptor desc;
973
josh minorba424d22019-11-13 10:55:17 -0600974 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +0100975 {
976 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
977 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -0600978 auto numPermVecElements = permuteTensorInfo.GetNumElements();
979 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +0100980 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
981
josh minorba424d22019-11-13 10:55:17 -0600982 // permuteShape assumes Tf/Np permute vectors, we must translate to armnn expected form
983 // 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)
984 std::vector<unsigned int> armnnPermuteShape(numPermVecElements);
985 std::vector<unsigned int>::iterator it;
986 for (unsigned int i = 0u; i < numPermVecElements; ++i)
987 {
988 it = std::find(permuteShape.begin(), permuteShape.end(), i);
989 armnnPermuteShape[i] = static_cast<unsigned int>(std::distance(permuteShape.begin(), it));
990 }
Kevin May85d92602019-09-27 17:21:06 +0100991
josh minorba424d22019-11-13 10:55:17 -0600992 PermutationVector permutationVector(armnnPermuteShape.data(), permuteTensorInfo.GetNumElements());
993
994 desc = PermuteDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +0100995 }
996
Keith Davis4cd29a02019-09-09 14:49:20 +0100997 layer = m_Network->AddPermuteLayer(desc, layerName.c_str());
998
999 BOOST_ASSERT(layer != nullptr);
1000
1001 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1002 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1003
1004 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1005 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1006
1007 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1008 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1009}
1010
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001011void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1012{
1013 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1014
1015 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1016 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1017
1018 TransposeConvolution2dDescriptor desc;
1019 desc.m_BiasEnabled = false;
1020 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1021 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1022 desc.m_DataLayout = armnn::DataLayout::NHWC;
1023
1024 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001025 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001026
1027 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1028 CHECK_VALID_SIZE(outputs.size(), 1);
1029
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001030 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001031 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1032
1033 // TfLite uses NHWC tensors
1034 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1035 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1036
1037 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1038 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1039
1040 CalcPadding(inputHeight,
1041 filterHeight,
1042 desc.m_StrideY,
1043 1, // DilationY
1044 desc.m_PadTop,
1045 desc.m_PadBottom,
1046 options->padding);
1047
1048 CalcPadding(inputWidth,
1049 filterWidth,
1050 desc.m_StrideX,
1051 1, // DilationX
1052 desc.m_PadLeft,
1053 desc.m_PadRight,
1054 options->padding);
1055
1056 auto filterTensorAndData = CreateConstTensor(inputs[1],
1057 filterTensorInfo,
1058 armnn::Optional<armnn::PermutationVector&>());
1059
1060 armnn::IConnectableLayer* layer = nullptr;
1061 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1062
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001063 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1064 filterTensorAndData.first,
1065 EmptyOptional(),
1066 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001067
1068 BOOST_ASSERT(layer != nullptr);
1069
1070 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1071 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1072
1073 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1074 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001075 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001076
1077 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1078 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1079}
1080
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001081void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1082{
1083 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1084}
1085
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001086void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1087{
1088 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1089
1090 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1091 CHECK_VALID_SIZE(inputs.size(), 3);
1092
1093 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1094 CHECK_VALID_SIZE(outputs.size(), 1);
1095
1096 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1097 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1098
1099 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1100 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1101
1102 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1103 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1104
1105 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1106 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1107
1108 size_t step = 2;
1109 std::vector<std::pair<unsigned int, unsigned int>> crops;
1110 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1111 {
1112 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1113 }
1114
1115 armnn::BatchToSpaceNdDescriptor desc;
1116 desc.m_BlockShape = blockShape;
1117 desc.m_Crops = crops;
1118 desc.m_DataLayout = armnn::DataLayout::NHWC;
1119
1120 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1121
1122 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1123 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1124
1125 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1126
1127 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1128 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1129
1130 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1131 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1132}
1133
Matthew Jackson28c94572019-07-18 10:47:03 +01001134void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1135{
1136 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1137
1138 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1139 CHECK_VALID_SIZE(inputs.size(), 1);
1140
1141 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1142 CHECK_VALID_SIZE(outputs.size(), 1);
1143
1144 L2NormalizationDescriptor desc;
1145 desc.m_DataLayout = armnn::DataLayout::NHWC;
1146 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1147 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1148
1149 BOOST_ASSERT(layer != nullptr);
1150
1151 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1152 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1153
1154 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1155 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1156
1157 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1158 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1159}
1160
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001161void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1162{
1163 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1164}
1165
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001166void TfLiteParser::ParseMaximum(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("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1180 IConnectableLayer* layer = m_Network->AddMaximumLayer(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
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001199void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1200{
1201 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1202
1203 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1204 CHECK_VALID_SIZE(inputs.size(), 2);
1205
1206 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1207 CHECK_VALID_SIZE(outputs.size(), 1);
1208
1209 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1210 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1211
1212 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1213 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1214
1215 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1216 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1217
1218 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1219 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1220 {
1221 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1222 }
1223 else
1224 {
1225 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1226 }
1227
1228 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1229 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1230}
1231
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001232void TfLiteParser::ParsePool(size_t subgraphIndex,
1233 size_t operatorIndex,
1234 PoolingAlgorithm algorithm)
1235{
1236 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1237
1238 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1239 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1240
1241 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1242
1243 std::string layerName;
1244
1245 switch (algorithm)
1246 {
1247 case PoolingAlgorithm::Average:
1248 layerName =
1249 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1250 break;
1251 case PoolingAlgorithm::Max:
1252 layerName =
1253 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1254 break;
1255 default:
1256 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1257 }
1258
1259 Pooling2dDescriptor desc;
1260
1261 desc.m_PoolType = algorithm;
1262 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1263 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1264 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1265 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1266 desc.m_PaddingMethod = PaddingMethod::Exclude;
1267 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001268 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001269
1270 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1271 CHECK_VALID_SIZE(inputs.size(), 1);
1272 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1273
1274 // assuming input is NHWC
1275 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1276 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1277
Pablo Tellof0bd6832019-04-26 17:58:13 +01001278 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1279 desc.m_PadTop, desc.m_PadBottom, options->padding);
1280 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1281 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001282
1283 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1284 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001285
1286 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1287
1288 BOOST_ASSERT(layer != nullptr);
1289
jimfly01c25411c2018-11-14 17:47:22 +00001290 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1291 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001292
1293 // register the input connection slots for the layer, connections are made after all layers have been created
1294 // only the tensors for the inputs are relevant, exclude the const tensors
1295 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001296 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001297
jimfly01c25411c2018-11-14 17:47:22 +00001298 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001299 // register the output connection slots for the layer, connections are made after all layers have been created
1300 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1301 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1302}
1303
josh minorba424d22019-11-13 10:55:17 -06001304void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1305{
1306 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1307
1308 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1309 CHECK_VALID_SIZE(inputs.size(), 3);
1310 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1311 CHECK_VALID_SIZE(outputs.size(), 1);
1312
1313 SliceDescriptor desc;
1314
1315 // set begin tensor info for slice descriptor
1316 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1317 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1318
1319 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1320 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1321
1322 // set size tensor info for slice descriptor
1323 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1324 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1325
1326 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1327 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1328 desc = SliceDescriptor(begin, size);
1329
1330 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1331 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1332
1333 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1334 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1335
1336 // register the input connection slots for the layer, connections are made after all layers have been created
1337 // only the tensors for the inputs are relevant, exclude the const tensors
1338 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1339 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1340
1341 // register the output connection slots for the layer, connections are made after all layers have been created
1342 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1343 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1344}
1345
telsoa01c577f2c2018-08-31 09:22:23 +01001346void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1347{
1348 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1349 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1350 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1351
1352 SoftmaxDescriptor desc;
1353 desc.m_Beta = options->beta;
1354
1355 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1356 CHECK_VALID_SIZE(inputs.size(), 1);
1357 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1358 CHECK_VALID_SIZE(outputs.size(), 1);
1359
1360 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1361 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1362
1363 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1364 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1365
1366 // register the input connection slots for the layer, connections are made after all layers have been created
1367 // only the tensors for the inputs are relevant, exclude the const tensors
1368 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1369 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1370
1371 // register the output connection slots for the layer, connections are made after all layers have been created
1372 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1373 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1374}
1375
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001376void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1377{
1378 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1379
1380 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1381 CHECK_VALID_SIZE(inputs.size(), 3);
1382
1383 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1384 CHECK_VALID_SIZE(outputs.size(), 1);
1385
1386 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1387 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1388
1389 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1390 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1391
1392 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1393 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1394
1395 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1396 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1397
1398 size_t step = 2;
1399 std::vector<std::pair<unsigned int, unsigned int>> padList;
1400 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1401 {
1402 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1403 }
1404
1405 armnn::SpaceToBatchNdDescriptor desc;
1406 desc.m_BlockShape = blockShape;
1407 desc.m_PadList = padList;
1408 desc.m_DataLayout = armnn::DataLayout::NHWC;
1409
1410 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1411
1412 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1413 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1414
1415 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1416
1417 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1418 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1419
1420 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1421 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1422}
1423
telsoa01c577f2c2018-08-31 09:22:23 +01001424armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1425 const armnn::TensorInfo & inputTensorInfo)
1426{
1427 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1428 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1429 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1430
1431 if (inputTensorInfo.GetNumDimensions() > 4)
1432 {
1433 std::stringstream ss;
1434 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1435 << " shape:" << inputTensorInfo.GetShape() << " "
1436 << CHECK_LOCATION().AsString();
1437 throw ParseException(ss.str());
1438 }
1439
1440 if (squeezeDims.empty())
1441 {
1442 squeezeDims.assign(dimensionSequence,
1443 dimensionSequence+inputTensorInfo.GetNumDimensions());
1444 }
1445
1446 std::vector<uint32_t> outputDims;
1447 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1448 {
1449 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1450 auto currentDimension = inputTensorInfo.GetShape()[i];
1451 if (skipSqueeze || currentDimension != 1)
1452 {
1453 outputDims.push_back(currentDimension);
1454 }
1455 }
1456
1457 if (outputDims.size() > 4)
1458 {
1459 std::stringstream ss;
1460 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1461 << " shape:" << inputTensorInfo.GetShape() << " "
1462 << CHECK_LOCATION().AsString();
1463 throw ParseException(ss.str());
1464 }
1465
1466 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1467 outputDims.data());
1468
1469 // we need to preserve the tensor type and the quantization data as well
1470 TensorInfo outTensorInfo = inputTensorInfo;
1471 outTensorInfo.SetShape(outShape);
1472
1473 return outTensorInfo;
1474}
1475
1476void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1477{
1478 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1479
1480 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1481 CHECK_VALID_SIZE(inputs.size(), 1);
1482
1483 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1484 CHECK_VALID_SIZE(outputs.size(), 1);
1485
1486 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1487 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1488
1489 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1490 armnn::TensorInfo outputTensorInfo =
1491 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1492 inputTensorInfo);
1493
1494 ReshapeDescriptor reshapeDesc;
1495 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1496
1497 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1498 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1499 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1500
1501 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1502 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1503
1504 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1505 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1506}
1507
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001508void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1509{
1510 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1511
1512 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1513 CHECK_VALID_SIZE(inputs.size(), 4);
1514
1515 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1516 CHECK_VALID_SIZE(outputs.size(), 1);
1517
1518 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1519 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1520
1521 StridedSliceDescriptor desc;
1522 desc.m_BeginMask = options->begin_mask;
1523 desc.m_EllipsisMask = options->ellipsis_mask;
1524 desc.m_EndMask = options->end_mask;
1525 desc.m_NewAxisMask = options->new_axis_mask;
1526 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1527 desc.m_DataLayout = armnn::DataLayout::NHWC;
1528
1529 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1530 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1531
1532 std::vector<int> begin(beginTensorInfo.GetNumElements());
1533 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1534
1535 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1536 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1537
1538 std::vector<int> end(endTensorInfo.GetNumElements());
1539 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1540
1541 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1542 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1543
1544 std::vector<int> stride(strideTensorInfo.GetNumElements());
1545 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1546
1547 desc.m_Begin = begin;
1548 desc.m_End = end;
1549 desc.m_Stride = stride;
1550
1551 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1552 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1553
1554 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1555 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1556
1557 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1558 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1559
1560 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1561 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1562}
1563
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001564void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1565{
1566 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1567
1568 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1569 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1570
1571 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1572 CHECK_VALID_SIZE(inputs.size(), 2);
1573
1574 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1575 CHECK_VALID_SIZE(outputs.size(), 1);
1576
1577 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1578 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1579
1580 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1581 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1582
1583 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1584 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1585
1586 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1587 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1588 {
1589 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1590 }
1591 else
1592 {
1593 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1594 }
1595
1596 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1597
1598 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1599 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1600}
1601
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001602void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1603{
1604 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1605
1606 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1607 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1608
1609 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1610 CHECK_VALID_SIZE(inputs.size(), 2);
1611
1612 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1613 CHECK_VALID_SIZE(outputs.size(), 1);
1614
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001615 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1616 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1617
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001618 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1619 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1620
1621 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1622 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1623
1624 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001625 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1626 {
1627 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1628 }
1629 else
1630 {
1631 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1632 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001633
1634 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1635
1636 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1637 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1638}
1639
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001640void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1641{
1642 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1643
1644 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1645 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1646
1647 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1648 CHECK_VALID_SIZE(inputs.size(), 2);
1649
1650 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1651 CHECK_VALID_SIZE(outputs.size(), 1);
1652
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001653 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1654 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1655
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001656 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1657 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1658
1659 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1660 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1661
1662 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001663 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1664 {
1665 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1666 }
1667 else
1668 {
1669 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1670 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001671
1672 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1673
1674 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1675 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1676}
1677
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001678void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1679{
1680 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1681
1682 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1683
1684 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1685 CHECK_VALID_SIZE(outputs.size(), 1);
1686
1687 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1688 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1689
1690 armnn::MeanDescriptor desc;
1691 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1692 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1693 desc.m_Axis = axis;
1694
1695 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1696 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1697
1698 desc.m_KeepDims =
1699 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1700 true : false;
1701
1702 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1703 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1704
1705 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1706
1707 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1708 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1709
1710 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1711 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1712}
1713
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001714void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1715{
1716 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1717
1718 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1719
1720 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1721 CHECK_VALID_SIZE(outputs.size(), 1);
1722
1723 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1724 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1725
1726 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1727 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1728
1729 size_t step = 2;
1730 armnn::PadDescriptor desc;
1731 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1732 {
1733 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1734 }
1735
1736 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1737 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1738
1739 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1740 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1741
1742 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1743 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1744
1745 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1746 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1747}
1748
Finn Williamsc42c3842019-01-22 14:18:11 +00001749
Sadik Armagan58f39192018-09-17 14:14:39 +01001750void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1751{
Finn Williamsc42c3842019-01-22 14:18:11 +00001752 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001753}
1754
1755void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1756{
Finn Williamsc42c3842019-01-22 14:18:11 +00001757 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1758}
Sadik Armagan58f39192018-09-17 14:14:39 +01001759
Finn Williamsc42c3842019-01-22 14:18:11 +00001760void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1761{
1762 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1763}
1764
Nina Drozd99851762019-04-09 09:37:38 +01001765void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1766{
1767 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1768}
1769
Finn Williamsc42c3842019-01-22 14:18:11 +00001770
1771void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1772{
1773 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001774 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1775 boost::ignore_unused(operatorPtr);
1776
1777 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1778 CHECK_VALID_SIZE(inputs.size(), 1);
1779
1780 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1781 CHECK_VALID_SIZE(outputs.size(), 1);
1782
Finn Williamsc42c3842019-01-22 14:18:11 +00001783 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001784 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001785 activationDesc.m_Function = activationType;
1786
1787 switch (activationType)
1788 {
1789 case ActivationFunction::ReLu:
1790 {
1791 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1792 break;
1793 }
1794 case ActivationFunction::BoundedReLu:
1795 {
1796 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1797 activationDesc.m_A = 6.0f;
1798 activationDesc.m_B = 0.0f;
1799 break;
1800 }
1801 case ActivationFunction::Sigmoid:
1802 {
1803 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1804 break;
1805 }
Nina Drozd99851762019-04-09 09:37:38 +01001806 case ActivationFunction::TanH:
1807 {
1808 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1809 activationDesc.m_A = 1.0f;
1810 activationDesc.m_B = 1.0f;
1811 break;
1812 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001813 default:
1814 {
1815 throw ParseException(
1816 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1817 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1818 }
1819 }
1820
1821 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001822
1823 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1824 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1825
1826 // register the input connection slots for the layer, connections are made after all layers have been created
1827 // only the tensors for the inputs are relevant, exclude the const tensors
1828 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1829 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1830
1831 // register the output connection slots for the layer, connections are made after all layers have been created
1832 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1833 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1834}
Sadikb94967b2018-09-19 15:30:00 +01001835armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1836 const std::vector<int32_t> & targetDimsIn)
1837{
1838 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1839 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1840
1841 if (stretchDim != targetDimsIn.end())
1842 {
1843 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1844 {
1845 throw ParseException(
1846 boost::str(
1847 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1848 }
1849
1850 auto targetNumElements =
1851 boost::numeric_cast<unsigned int>(
1852 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1853
1854 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1855 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1856 }
1857
1858 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1859
1860 TensorInfo reshapeInfo = inputTensorInfo;
1861 reshapeInfo.SetShape(outputShape);
1862
1863 return reshapeInfo;
1864}
1865
1866void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1867{
1868 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1869
1870 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001871
1872 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1873 CHECK_VALID_SIZE(outputs.size(), 1);
1874
1875 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1876 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1877
1878 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001879 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1880 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001881 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1882
kevmay0171972a82018-12-17 14:28:03 +00001883 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001884 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1885 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001886 {
1887 std::stringstream ss;
1888 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001889 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001890 << " does not equal output shape "
1891 << actualOutputTensorInfo.GetShape()
1892 << ": "
1893 << CHECK_LOCATION().AsString();
1894 throw ParseException(ss.str());
1895 }
1896
Sadikb94967b2018-09-19 15:30:00 +01001897 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001898 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001899
1900 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1901 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001902 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001903
1904 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1905 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1906
1907 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1908 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1909}
1910
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001911void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1912{
Sadik Armagana3b31f02019-12-05 09:08:53 +00001913 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
1914}
1915
1916void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
1917{
1918 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
1919}
1920
1921void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
1922{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001923 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1924
1925 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1926 CHECK_VALID_SIZE(inputs.size(), 2);
1927
1928 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1929 CHECK_VALID_SIZE(outputs.size(), 1);
1930
1931 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1932
1933 // Data for the parsed tensor args (size) must be stored locally.
1934 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1935
1936 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1937 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1938
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001939 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00001940 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001941 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001942 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1943 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001944
Sadik Armagana3b31f02019-12-05 09:08:53 +00001945 auto layerName = str(boost::format("Resize:"));
1946
1947 switch (resizeMethod)
1948 {
1949 case ResizeMethod::Bilinear:
1950 {
1951 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
1952 break;
1953 }
1954 case ResizeMethod::NearestNeighbor:
1955 {
1956 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
1957 break;
1958 }
1959 default:
1960 {
1961 throw ParseException(
1962 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
1963 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
1964 }
1965 }
1966
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001967 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001968
1969 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1970 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1971
1972 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1973 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1974
1975 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1976 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1977}
1978
Sadik Armagan479045b2018-10-01 11:51:37 +01001979void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1980{
1981 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1982
1983 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1984 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1985
1986 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1987
1988 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1989 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1990 CHECK_VALID_SIZE(outputs.size(), 1);
1991
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001992 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1993 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001994
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001995 const unsigned int concatDimInput = static_cast<unsigned int>(
1996 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001997
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001998 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1999 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01002000
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002001 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01002002
2003 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2004 {
2005 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2006
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002007 // This set up concatDescriptor view origin
2008 armnnUtils::ProcessConcatInputTensorInfo(
2009 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01002010 }
2011
2012 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Jim Flynn906f9462019-05-10 13:55:21 +01002013 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Sadik Armagan479045b2018-10-01 11:51:37 +01002014
2015 BOOST_ASSERT(layer != nullptr);
2016
2017 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2018 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01002019
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002020 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01002021
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002022 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01002023
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002024 // add fused activation layer
2025 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01002026
Sadik Armagan479045b2018-10-01 11:51:37 +01002027 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2028 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2029}
2030
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002031void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2032{
2033 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2034
2035 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2036 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2037
2038 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2039
2040 FullyConnectedDescriptor desc;
2041 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002042 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002043
2044 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2045 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2046 CHECK_VALID_SIZE(outputs.size(), 1);
2047
2048 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2049
2050 // Fully Connected Layer accepts two dimensional weights input
2051 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2052 if (weightsDimension != 2)
2053 {
2054 throw ParseException(
2055 boost::str(
2056 boost::format(
2057 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2058 "Node %2%")
2059 % weightsDimension
2060 % CHECK_LOCATION().AsString()));
2061 }
2062
Matteo Martincigh747ef822018-12-18 09:26:39 +00002063 auto filterTensorAndData = CreateConstTensor(inputs[1],
2064 filterTensorInfo,
2065 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002066 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002067 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2068
2069 if (inputs.size() == 3)
2070 {
2071 desc.m_BiasEnabled = true;
2072 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002073 auto biasTensorAndData = CreateConstTensor(inputs[2],
2074 biasTensorInfo,
2075 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002076 layer = m_Network->AddFullyConnectedLayer(desc,
2077 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002078 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002079 layerName.c_str());
2080 }
2081 else
2082 {
2083 layer = m_Network->AddFullyConnectedLayer(desc,
2084 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002085 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002086 layerName.c_str());
2087 }
2088 BOOST_ASSERT(layer != nullptr);
2089
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002090 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2091
2092 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2093
2094 if (inputTensorInfo.GetNumDimensions() > 2)
2095 {
2096 // Add reshape to flatten to 2D [batch_size, input_size],
2097 // where "input_size" corresponds to the number of inputs to the layer,
2098 // matching the second dimension of weights,
2099 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2100 std::vector<unsigned int> reshapedDimensions(2);
2101 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2102 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2103
2104 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2105 {
2106 throw ParseException(
2107 boost::str(
2108 boost::format(
2109 "Failed to deduce input tensor shape from filter size %1%")
2110 % reshapedDimensions[1]
2111 % CHECK_LOCATION().AsString()));
2112 }
2113
2114 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2115 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2116
2117 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2118 armnn::ReshapeDescriptor desc;
2119 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2120 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2121
2122 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2123 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2124
2125 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2126 }
2127 else
2128 {
2129 // register the input connection slot for the layer
2130 // only the tensors for the inputs are relevant, exclude the const tensors
2131 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2132 }
2133
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002134 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2135 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2136
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002137 // we need to add the activation layer and fortunately we don't need to care about the data layout
2138 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2139 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002140
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002141 // register the output connection slots for the layer, connections are made after all layers have been created
2142 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2143 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2144}
2145
keidav011b3e2ea2019-02-21 10:07:37 +00002146void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2147{
2148 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2149
2150 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2151
2152 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2153 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2154 CHECK_VALID_SIZE(outputs.size(), 4);
2155
2156 // Obtain custom options from flexbuffers
2157 auto custom_options = operatorPtr->custom_options;
2158 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2159
2160 // Obtain descriptor information from tf lite
2161 DetectionPostProcessDescriptor desc;
2162 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2163 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2164 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2165 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2166 desc.m_NumClasses = m["num_classes"].AsUInt32();
2167 desc.m_ScaleH = m["h_scale"].AsFloat();
2168 desc.m_ScaleW = m["w_scale"].AsFloat();
2169 desc.m_ScaleX = m["x_scale"].AsFloat();
2170 desc.m_ScaleY = m["y_scale"].AsFloat();
2171
keidav0107d58c72019-02-26 11:57:39 +00002172 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002173 {
keidav0107d58c72019-02-26 11:57:39 +00002174 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002175 }
2176 if (!(m["detections_per_class"].IsNull()))
2177 {
2178 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2179 }
2180
2181 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2182 {
2183 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2184 "must be positive and less than or equal to 1.");
2185 }
2186
2187 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2188 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2189 armnn::Optional<armnn::PermutationVector&>());
2190
2191 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2192 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2193 layerName.c_str());
2194
2195 BOOST_ASSERT(layer != nullptr);
2196
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002197 // The model does not specify the output shapes.
2198 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2199 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2200 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2201 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2202 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2203 m_OverridenOutputShapes.push_back({ 1 });
2204
keidav011b3e2ea2019-02-21 10:07:37 +00002205 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2206 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002207 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002208 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2209 }
2210
2211 // Register the input connection slots for the layer, connections are made after all layers have been created
2212 // only the tensors for the inputs are relevant, exclude the const tensors
2213 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2214 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2215
2216 // Register the output connection slots for the layer, connections are made after all layers have been created
2217 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2218 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2219 outputTensorIndexes[1],
2220 outputTensorIndexes[2],
2221 outputTensorIndexes[3]});
2222}
2223
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002224/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2225void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2226{
2227 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2228
2229 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2230 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2231 CHECK_VALID_SIZE(outputs.size(), 1);
2232
2233 if (inputs.size() < 1)
2234 {
2235 throw ParseException("Pack must have at least one input.");
2236 }
2237
2238 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2239 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2240
2241 StackDescriptor desc;
2242 desc.m_Axis = static_cast<uint32_t>(options->axis);
2243 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2244
2245 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2246 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2247 desc.m_InputShape = inputTensorInfo.GetShape();
2248
2249 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2250 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2251
2252 BOOST_ASSERT(layer != nullptr);
2253
2254 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2255 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2256
2257 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2258 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2259
2260 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2261 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2262}
2263
Nina Drozd200e3802019-04-15 09:47:39 +01002264void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2265{
2266 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2267
2268 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2269 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2270
2271 // This unpackAxis indicates the axis to unpack
2272 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2273
2274 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2275 CHECK_VALID_SIZE(inputs.size(), 1);
2276
2277 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002278
2279 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2280 {
2281 throw ParseException(
2282 boost::str(
2283 boost::format(
2284 "The unpack axis: %1% cannot be greater than or equal to "
2285 "the number of input dimension %2% %3%")
2286 % unpackAxis
2287 % inputTensorInfo.GetNumDimensions()
2288 % CHECK_LOCATION().AsString()));
2289 }
2290
Nina Drozd200e3802019-04-15 09:47:39 +01002291 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2292 // If num is not defined, automatically infer from the length of the dimension axis.
2293 if(unpackNum == 0)
2294 {
2295 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2296 }
2297
2298 // If unpack number cannot be inferred and is still zero, throw ParseException.
2299 if(unpackNum == 0)
2300 {
2301 throw ParseException("Number to unpack must greater than zero.");
2302 }
2303
2304 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2305 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2306
2307 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2308 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2309
2310 // Add current input shape to unpackDimSizes
2311 for (unsigned int i = 0; i < inputDimSize; ++i)
2312 {
2313 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2314 }
2315
2316 if (unpackDimSizes[unpackAxis] != unpackNum)
2317 {
2318 throw ParseException("Number to unpack must be the same as length of the dimension to "
2319 "unpack along.");
2320 }
2321
2322 unpackDimSizes[unpackAxis] /= unpackNum;
2323
2324 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2325 for (unsigned int j = 0; j < unpackNum; ++j)
2326 {
2327 // Set the size of the views.
2328 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2329 {
2330 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2331 }
2332 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2333 }
2334
2335 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2336 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2337
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002338 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2339 unpackDimSizes.data());
2340
Nina Drozd200e3802019-04-15 09:47:39 +01002341 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2342 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2343
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002344 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2345 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2346 {
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002347 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002348 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2349 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002350 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002351 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2352
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002353 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2354 outputTensorInfo.GetDataType(),
2355 outputTensorInfo.GetQuantizationScale(),
2356 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002357 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2358
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002359 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002360
2361 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2362 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2363 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2364 }
Nina Drozd200e3802019-04-15 09:47:39 +01002365}
2366
Nina Drozd0324f482019-04-08 10:52:10 +01002367void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2368{
2369 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2370
2371 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2372 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2373
2374 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2375
Nina Drozd200e3802019-04-15 09:47:39 +01002376 // If number of splits cannot be inferred and is zero, throw ParseException.
2377 if(numSplits == 0)
2378 {
2379 throw ParseException("Number to splits must greater than zero.");
2380 }
2381
Nina Drozd0324f482019-04-08 10:52:10 +01002382 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2383 CHECK_VALID_SIZE(inputs.size(), 2);
2384 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2385 CHECK_VALID_SIZE(outputs.size(), numSplits);
2386
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002387 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2388 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002389
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002390 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2391 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2392 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2393
2394 BOOST_ASSERT(axisTensorInfo.GetNumElements() == 1);
2395 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002396
2397 // Armnn supports split along the channel dimension for data formats NHWC and NCHW.
2398 if (splitDim == 0 || splitDim == 2)
2399 {
2400 throw ParseException(
2401 boost::str(
2402 boost::format(
2403 "Dimension %1% for split is not supported by Armnn. %2%")
2404 % splitDim
2405 % CHECK_LOCATION().AsString()));
2406 }
2407
2408 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002409 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002410 {
2411 throw ParseException(
2412 boost::str(
2413 boost::format(
2414 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002415 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002416 % inputTensorInfo.GetNumDimensions()
2417 % MaxNumOfTensorDimensions
2418 % CHECK_LOCATION().AsString()));
2419 }
2420
2421 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2422
2423 // Add current input shape to splitterDimSizes
2424 for (unsigned int i = 0; i < inputDimSize; ++i)
2425 {
2426 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2427 }
2428
2429 if (splitterDimSizes[splitDim] % numSplits != 0)
2430 {
2431 throw ParseException("Number of splits must evenly divide the dimension");
2432 }
2433 splitterDimSizes[splitDim] /= numSplits;
2434
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002435 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002436 for (unsigned int j = 0; j < numSplits; ++j)
2437 {
2438 // Set the size of the views.
2439 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2440 {
2441 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2442 }
2443 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2444 }
2445
2446 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2447 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2448
2449 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002450 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002451
Nina Drozd0324f482019-04-08 10:52:10 +01002452 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2453 {
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002454 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2455 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002456 }
2457
2458 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2459 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2460}
2461
Sadik Armagan58f39192018-09-17 14:14:39 +01002462armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2463 unsigned int outputSlot,
2464 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002465{
2466 ActivationDescriptor activationDesc;
2467 std::string layerName = prevLayer->GetName();
2468
2469 switch(activationType)
2470 {
2471 case tflite::ActivationFunctionType_NONE:
2472 {
2473 // this is a no-op: return previous layer
2474 return prevLayer;
2475 }
2476 case tflite::ActivationFunctionType_RELU:
2477 {
2478 activationDesc.m_Function = ActivationFunction::ReLu;
2479 layerName += ":RELU";
2480 break;
2481 }
2482 case tflite::ActivationFunctionType_RELU6:
2483 {
2484 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2485 activationDesc.m_A = 6.0f;
2486 activationDesc.m_B = 0.0f;
2487 layerName += ":RELU6";
2488 break;
2489 }
2490 case tflite::ActivationFunctionType_TANH:
2491 {
2492 activationDesc.m_Function = ActivationFunction::TanH;
2493 activationDesc.m_A = 1.0f;
2494 activationDesc.m_B = 1.0f;
2495 layerName += ":TANH";
2496 break;
2497 }
2498
2499 // I only put these here as a reminder what others we could support
2500 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2501 case tflite::ActivationFunctionType_SIGN_BIT:
2502 default:
2503 {
2504 throw ParseException(
2505 boost::str(
2506 boost::format("TfLite parser doesn't suppport fused activation: "
2507 "%1%/%2% %3% ") %
2508 activationType %
2509 tflite::EnumNameActivationFunctionType(activationType) %
2510 CHECK_LOCATION().AsString()));
2511
2512 }
2513 }
2514
2515 IConnectableLayer* activationLayer =
2516 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2517
2518 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2519 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2520 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2521 return activationLayer;
2522}
2523
2524TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2525{
2526 if (fileName == nullptr)
2527 {
2528 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2529 CHECK_LOCATION().AsString()));
2530 }
2531 boost::system::error_code errorCode;
2532 boost::filesystem::path pathToFile(fileName);
2533 if (!boost::filesystem::exists(pathToFile, errorCode))
2534 {
2535 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2536 fileName %
2537 errorCode %
2538 CHECK_LOCATION().AsString()));
2539 }
2540 std::ifstream file(fileName, std::ios::binary);
2541 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2542 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2543 fileContent.size());
2544}
2545
2546TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2547{
2548 if (binaryContent == nullptr)
2549 {
2550 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2551 CHECK_LOCATION().AsString()));
2552 }
2553 flatbuffers::Verifier verifier(binaryContent, len);
2554 if (verifier.VerifyBuffer<tflite::Model>() == false)
2555 {
2556 throw ParseException(
2557 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2558 "flatbuffers format. size:%1% %2%") %
2559 len %
2560 CHECK_LOCATION().AsString()));
2561 }
2562 return tflite::UnPackModel(binaryContent);
2563}
2564
2565TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2566 size_t subgraphIndex,
2567 size_t operatorIndex)
2568{
2569 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2570
Derek Lambertiff05cc52019-04-26 13:05:17 +01002571 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2572 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002573
2574 size_t inputCount = operatorPtr->inputs.size();
2575 TensorRawPtrVector result(inputCount);
2576 for (size_t i=0; i<inputCount; ++i)
2577 {
2578 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002579 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002580 }
2581 return result;
2582}
2583
2584TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2585 size_t subgraphIndex,
2586 size_t operatorIndex)
2587{
2588 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2589
Derek Lambertiff05cc52019-04-26 13:05:17 +01002590 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2591 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002592
2593 size_t outputCount = operatorPtr->outputs.size();
2594 TensorRawPtrVector result(outputCount);
2595 for (size_t i=0; i<outputCount; ++i)
2596 {
2597 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2598 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002599 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002600 }
2601 return result;
2602}
2603
2604TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2605 size_t subgraphIndex)
2606{
2607 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002608 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002609
Derek Lambertiff05cc52019-04-26 13:05:17 +01002610 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002611 TensorIdRawPtrVector result(inputCount);
2612 for (size_t i=0; i<inputCount; ++i)
2613 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002614 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01002615 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002616 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002617 }
2618 return result;
2619}
2620
2621TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2622 size_t subgraphIndex)
2623{
2624 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002625 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002626
Derek Lambertiff05cc52019-04-26 13:05:17 +01002627 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002628 TensorIdRawPtrVector result(outputCount);
2629 for (size_t i=0; i<outputCount; ++i)
2630 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002631 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2632 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002633 }
2634 return result;
2635}
2636
2637std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2638 size_t subgraphIndex,
2639 size_t operatorIndex)
2640{
2641 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002642 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2643 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002644 return operatorPtr->inputs;
2645}
2646
2647std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2648 size_t subgraphIndex,
2649 size_t operatorIndex)
2650{
2651 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002652 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2653 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002654 return operatorPtr->outputs;
2655}
2656
2657void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2658 size_t operatorIndex,
2659 IConnectableLayer* layer,
2660 const std::vector<unsigned int>& tensorIndexes)
2661{
2662 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2663 BOOST_ASSERT(layer != nullptr);
2664 if (tensorIndexes.size() != layer->GetNumInputSlots())
2665 {
2666 throw ParseException(
2667 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2668 " for subgraph:%3% operator index:%4% %5%") %
2669 tensorIndexes.size() %
2670 layer->GetNumInputSlots() %
2671 subgraphIndex %
2672 operatorIndex %
2673 CHECK_LOCATION().AsString()));
2674 }
2675
2676 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2677 {
2678 unsigned int tensorIndex = tensorIndexes[slotIndex];
2679 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2680 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2681 }
2682}
2683
2684void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2685 size_t operatorIndex,
2686 IConnectableLayer* layer,
2687 const std::vector<unsigned int>& tensorIndexes)
2688{
2689 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2690 BOOST_ASSERT(layer != nullptr);
2691 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2692 {
2693 throw ParseException(
2694 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2695 " for subgraph:%3% operator index:%4% %5%") %
2696 tensorIndexes.size() %
2697 layer->GetNumOutputSlots() %
2698 subgraphIndex %
2699 operatorIndex %
2700 CHECK_LOCATION().AsString()));
2701 }
2702
2703 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2704 {
2705 unsigned int tensorIndex = tensorIndexes[slotIndex];
2706 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2707 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2708 }
2709}
2710
2711void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2712{
2713 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2714
2715 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2716 for (auto const & tensorIdAndPtr : inputs)
2717 {
2718 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2719 IConnectableLayer* layer =
2720 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2721
2722 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2723 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2724
2725 RegisterOutputSlots(subgraphIndex,
2726 VIRTUAL_OPERATOR_ID,
2727 layer,
2728 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2729 }
2730}
2731
2732void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2733{
2734 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2735
2736 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2737 for (auto const & tensorIdAndPtr : outputs)
2738 {
2739 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2740 IConnectableLayer* layer =
2741 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2742
2743 RegisterInputSlots(subgraphIndex,
2744 VIRTUAL_OPERATOR_ID,
2745 layer,
2746 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2747 }
2748}
2749
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002750void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2751{
2752 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2753
Derek Lambertiff05cc52019-04-26 13:05:17 +01002754 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002755 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2756 {
2757 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2758 {
2759 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2760 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2761 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002762 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002763 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2764 auto tensorAndData = CreateConstTensor(tensorPtr,
2765 tensorInfo,
2766 armnn::Optional<armnn::PermutationVector&>());
2767
2768 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2769 IConnectableLayer *layer =
2770 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2771
2772 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2773 RegisterOutputSlots(subgraphIndex,
2774 VIRTUAL_OPERATOR_ID,
2775 layer,
2776 { tensorIndex });
2777
2778 }
2779 }
2780 }
2781}
2782
telsoa01c577f2c2018-08-31 09:22:23 +01002783// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2784TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2785{
2786 CHECK_BUFFER(model, bufferIndex);
2787 return model->buffers[bufferIndex].get();
2788}
2789
Matteo Martincigh747ef822018-12-18 09:26:39 +00002790template<typename T>
2791std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2792TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2793 TfLiteParser::TensorRawPtr tensorPtr,
2794 armnn::TensorInfo& tensorInfo,
2795 armnn::Optional<armnn::PermutationVector&> permutationVector)
2796{
2797 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2798 tensorPtr,
2799 tensorInfo,
2800 permutationVector);
2801 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2802 return std::make_pair(constData.first, std::move(storage));
2803}
2804
telsoa01c577f2c2018-08-31 09:22:23 +01002805std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2806TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002807 armnn::TensorInfo& tensorInfo,
2808 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002809{
2810 CHECK_TENSOR_PTR(tensorPtr);
2811 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2812 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2813
2814 switch (tensorInfo.GetDataType())
2815 {
2816 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002817 return CreateConstTensorAndStoreData<float>(bufferPtr,
2818 tensorPtr,
2819 tensorInfo,
2820 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002821 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002822 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2823 tensorPtr,
2824 tensorInfo,
2825 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002826 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002827 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2828 tensorPtr,
2829 tensorInfo,
2830 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002831 default:
2832 {
2833 std::stringstream errString;
2834 errString << "Unexpected datatype when creating const tensor: "
2835 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2836 << " shape:" << tensorInfo.GetShape()
2837 << CHECK_LOCATION().AsString();
2838 throw ParseException(errString.str());
2839 }
2840 }
2841}
2842
2843BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2844 const std::string& name) const
2845{
2846 CHECK_SUBGRAPH(m_Model, subgraphId);
2847 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2848 for (auto const & input : inputs)
2849 {
2850 if (input.second->name == name)
2851 {
2852 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2853 return std::make_pair(bindingId, ToTensorInfo(input.second));
2854 }
2855 }
2856
2857 std::stringstream bindings;
2858 for (auto const & input : inputs)
2859 {
2860 bindings << "'" << input.second->name << "' ";
2861 }
2862
2863 throw ParseException(
2864 boost::str(
2865 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2866 "Possible inputs are: [%3%] %4%") %
2867 subgraphId %
2868 name %
2869 bindings.str() %
2870 CHECK_LOCATION().AsString()));
2871}
2872
2873BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2874 const std::string& name) const
2875{
2876 CHECK_SUBGRAPH(m_Model, subgraphId);
2877 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002878 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01002879 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002880 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01002881 if (output.second->name == name)
2882 {
2883 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002884 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
2885 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
2886 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01002887 }
2888 }
2889
2890 std::stringstream bindings;
2891 for (auto const & output : outputs)
2892 {
2893 bindings << "'" << output.second->name << "' ";
2894 }
2895
2896 throw ParseException(
2897 boost::str(
2898 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2899 "Possible outputs are: [%3%] %4%") %
2900 subgraphId %
2901 name %
2902 bindings.str() %
2903 CHECK_LOCATION().AsString()));
2904}
2905
2906size_t TfLiteParser::GetSubgraphCount() const
2907{
2908 return m_Model->subgraphs.size();
2909}
2910
2911std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2912{
2913 CHECK_SUBGRAPH(m_Model, subgraphId);
2914 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2915 std::vector<std::string> result;
2916 result.reserve(inputs.size());
2917 for (auto const & input : inputs)
2918 {
2919 result.push_back(input.second->name);
2920 }
2921 return result;
2922}
2923
2924std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2925{
2926 CHECK_SUBGRAPH(m_Model, subgraphId);
2927 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2928 std::vector<std::string> result;
2929 result.reserve(outputs.size());
2930 for (auto const & output : outputs)
2931 {
2932 result.push_back(output.second->name);
2933 }
2934 return result;
2935}
2936
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002937ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002938{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002939 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01002940}
2941
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002942ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01002943{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01002944 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01002945}
2946
2947void ITfLiteParser::Destroy(ITfLiteParser* parser)
2948{
2949 delete parser;
2950}
2951
2952TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2953: m_FloatData(std::move(data))
2954, m_Uint8Data(nullptr)
2955, m_Int32Data(nullptr)
2956{
2957}
2958
2959TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2960: m_FloatData(nullptr)
2961, m_Uint8Data(std::move(data))
2962, m_Int32Data(nullptr)
2963{
2964}
2965
2966TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2967: m_FloatData(nullptr)
2968, m_Uint8Data(nullptr)
2969, m_Int32Data(std::move(data))
2970{
2971}
2972
2973} // armnnTfLiteParser