blob: 57333439aaf82493b596e9db65be209b1ab06713 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5#include "TfLiteParser.hpp"
6
7#include <armnn/ArmNN.hpp>
8#include <armnn/Exceptions.hpp>
9#include <armnn/TypesUtils.hpp>
10#include <boost/filesystem.hpp>
11
12// armnnUtils:
Sadik Armagan479045b2018-10-01 11:51:37 +010013#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010014#include <Permute.hpp>
15#include <VerificationHelpers.hpp>
16
17// The generated code based on the Tf Lite schema:
18#include <schema_generated.h>
19
20#include <boost/core/ignore_unused.hpp>
21#include <boost/assert.hpp>
22#include <boost/format.hpp>
23#include <boost/log/trivial.hpp>
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +010024#include <boost/format.hpp>
25#include <boost/numeric/conversion/cast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010026
27#include <fstream>
28#include <algorithm>
29#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010030#include <numeric>
keidav011b3e2ea2019-02-21 10:07:37 +000031#include <flatbuffers/flexbuffers.h>
telsoa01c577f2c2018-08-31 09:22:23 +010032
33using namespace armnn;
34using armnn::CheckLocation;
35namespace armnnTfLiteParser
36{
37namespace
38{
jimfly01c25411c2018-11-14 17:47:22 +000039
telsoa01c577f2c2018-08-31 09:22:23 +010040const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
41
42void CheckSubgraph(const TfLiteParser::ModelPtr & model,
43 size_t subgraphIndex,
44 const CheckLocation & location)
45{
46 if (model.get() == nullptr)
47 {
48 throw ParseException(
49 boost::str(
50 boost::format("%1% was called with invalid (null) model. "
51 "Possible reason is that the model is not yet loaded and Unpack(ed). "
52 "subgraph:%2% at %3%") %
53 location.m_Function %
54 subgraphIndex %
55 location.FileLine()));
56 }
57 else if (subgraphIndex >= model->subgraphs.size())
58 {
59 throw ParseException(
60 boost::str(
61 boost::format("%1% was called with an invalid subgraph index. "
62 "subgraph:%2% at %3%") %
63 location.m_Function %
64 subgraphIndex %
65 location.FileLine()));
66 }
67}
68
69#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
70 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
71
72void CheckModel(const TfLiteParser::ModelPtr & model,
73 size_t subgraphIndex,
74 size_t operatorIndex,
75 const CheckLocation & location)
76{
77 if (model.get() == nullptr)
78 {
79 throw ParseException(
80 boost::str(
81 boost::format("%1% was called with invalid (null) model. "
82 "Possible reason is that the model is not yet loaded and Unpack(ed). "
83 "subgraph:%2% operator:%3% at %4%") %
84 location.m_Function %
85 subgraphIndex %
86 operatorIndex %
87 location.FileLine()));
88 }
89 else if (subgraphIndex >= model->subgraphs.size())
90 {
91 throw ParseException(
92 boost::str(
93 boost::format("%1% was called with an invalid subgraph index. "
94 "subgraph:%2% operator:%3% at %4%") %
95 location.m_Function %
96 subgraphIndex %
97 operatorIndex %
98 location.FileLine()));
99 }
100 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
101 operatorIndex != VIRTUAL_OPERATOR_ID)
102 {
103 throw ParseException(
104 boost::str(
105 boost::format("%1% was called with an invalid operator index. "
106 "subgraph:%2% operator:%3% at %4%") %
107 location.m_Function %
108 subgraphIndex %
109 operatorIndex %
110 location.FileLine()));
111 }
112}
113
114#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
115 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
116
117void CheckTensor(const TfLiteParser::ModelPtr & model,
118 size_t subgraphIndex,
119 size_t tensorIndex,
120 const CheckLocation & location)
121{
122 // not checking model, because I assume CHECK_MODEL already run
123 // and checked that. An assert would do.
124 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
125
126 // also subgraph index should be checked by CHECK_MODEL so
127 // I only add an assert here
128 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
129
130 // the tensor index is the only one to check here
131 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
132 {
133 throw ParseException(
134 boost::str(
135 boost::format("%1% was called with an invalid tensor index. "
136 "subgraph:%2% tensor:%3% at %4%") %
137 location.m_Function %
138 subgraphIndex %
139 tensorIndex %
140 location.FileLine()));
141 }
142}
143
144#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
145 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
146
147void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
148 const CheckLocation & location)
149{
150 if (rawPtr == nullptr)
151 {
152 throw ParseException(
153 boost::str(
154 boost::format("%1% was called with a null tensor pointer. "
155 "at %2%") %
156 location.m_Function %
157 location.FileLine()));
158
159 }
160}
161
162#define CHECK_TENSOR_PTR(TENSOR_PTR) \
163 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
164
165void CheckBuffer(const TfLiteParser::ModelPtr & model,
166 size_t bufferIndex,
167 const CheckLocation & location)
168{
169 if (model.get() == nullptr)
170 {
171 throw ParseException(
172 boost::str(
173 boost::format("%1% was called with invalid (null) model. "
174 "Possible reason is that the model is not yet loaded and Unpack(ed). "
175 "buffer:%2% at %3%") %
176 location.m_Function %
177 bufferIndex %
178 location.FileLine()));
179 }
180 else if (bufferIndex >= model->buffers.size())
181 {
182 throw ParseException(
183 boost::str(
184 boost::format("%1% was called with an invalid buffer index. "
185 "buffer index:%2% at %3%") %
186 location.m_Function %
187 bufferIndex %
188 location.FileLine()));
189 }
190 else if (model->buffers[bufferIndex].get() == nullptr)
191 {
192 throw ParseException(
193 boost::str(
194 boost::format("The buffer #%1% is null. %3%") %
195 bufferIndex %
196 location.AsString()));
197 }
198}
199
200#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
201 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
202
203void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
204 const armnn::TensorInfo & tensorInfo,
205 uint32_t bufferId,
206 const CheckLocation & location)
207{
208 if (bufferPtr == nullptr)
209 {
210 throw ParseException(
211 boost::str(
212 boost::format("BufferPtr is null for buffer:%1%. %2%") %
213 bufferId %
214 location.AsString()));
215 }
216 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
217 tensorInfo.GetNumBytes() > bufferPtr->data.size())
218 {
219 std::stringstream ss;
220 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
221 << "For tensor: " << tensorInfo.GetShape()
222 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
223 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
224 throw ParseException(ss.str());
225 }
226}
227
228#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
229 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
230
Kevin May83add212019-03-26 11:39:19 +0000231uint32_t CheckDilation(const int32_t dilationFactor,
232 size_t operatorIndex,
233 const CheckLocation& location)
234{
235 if (dilationFactor != 1)
236 {
237 std::stringstream ss;
238 ss << "ArmNN only supports convolution layers with dilations [1,1,1,1] for operator with index "
239 << operatorIndex << location.AsString();
240 throw ParseException(ss.str());
241 }
242
243 return static_cast<uint32_t>(dilationFactor);
244}
245
246#define CHECK_DILATION(DILATION_FACTOR, OPERATOR_INDEX) \
247 CheckDilation(DILATION_FACTOR, OPERATOR_INDEX, CHECK_LOCATION())
248
telsoa01c577f2c2018-08-31 09:22:23 +0100249bool IsActivationSupported(tflite::ActivationFunctionType activationType)
250{
251 switch(activationType)
252 {
253 case tflite::ActivationFunctionType_NONE:
254 case tflite::ActivationFunctionType_RELU:
255 case tflite::ActivationFunctionType_RELU6:
256 case tflite::ActivationFunctionType_TANH:
257 {
258 return true;
259 }
260 default:
261 {
262 return false;
263 }
264 }
265}
266
267#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
268 do { \
269 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
270 { \
271 throw ParseException( \
272 boost::str( \
273 boost::format("TfLite parser doesn't suppport fused activation: " \
274 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
275 OPTION->fused_activation_function % \
276 tflite::EnumNameActivationFunctionType(\
277 OPTION->fused_activation_function) % \
278 __func__ % \
279 SUBGRAPH_INDEX % \
280 OPERATOR_INDEX % \
281 CHECK_LOCATION().FileLine())); \
282 } \
283 } while(false)
284
285
286std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
287{
288 std::vector<unsigned int> result;
289 result.reserve(in.size());
290 for (auto & i : in)
291 {
292 result.push_back(CHECKED_NON_NEGATIVE(i));
293 }
294 return result;
295}
296
297void CalcPadding(uint32_t inputSize,
298 uint32_t filterSize,
299 uint32_t stride,
300 uint32_t& paddingFront,
301 uint32_t& paddingBack,
302 tflite::Padding padding)
303{
304 paddingFront = 0;
305 paddingBack = 0;
306 if (padding == tflite::Padding_SAME)
307 {
308 uint32_t outputSize = (inputSize + stride - 1) / stride;
309 uint32_t temp = (outputSize - 1) * stride + filterSize;
310 if (temp > inputSize)
311 {
312 paddingFront = (temp - inputSize) / 2;
313 paddingBack = (temp - inputSize) - paddingFront;
314 }
315 }
316}
317
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000318armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes)
telsoa01c577f2c2018-08-31 09:22:23 +0100319{
320 armnn::DataType type;
321 CHECK_TENSOR_PTR(tensorPtr);
322
323 switch (tensorPtr->type)
324 {
325 case tflite::TensorType_UINT8:
326 type = armnn::DataType::QuantisedAsymm8;
327 break;
328 case tflite::TensorType_FLOAT32:
329 type = armnn::DataType::Float32;
330 break;
331 case tflite::TensorType_INT32:
332 type = armnn::DataType::Signed32;
333 break;
334
335 default:
336 {
337 CheckLocation location = CHECK_LOCATION();
338 throw ParseException(
339 boost::str(
340 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
341 tensorPtr->type %
342 tflite::EnumNameTensorType(tensorPtr->type) %
343 tensorPtr->name %
344 location.AsString()));
345 }
346 }
347
348 float quantizationScale = 0.0f;
349 int32_t quantizationOffset = 0;
350
351 if (tensorPtr->quantization.get())
352 {
353 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
354 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
355
356 if (tensorPtr->quantization->scale.size() == 1)
357 {
358 quantizationScale = tensorPtr->quantization->scale[0];
359 }
360 if (tensorPtr->quantization->zero_point.size() == 1)
361 {
362 // NOTE: we lose precision here when converting from 64 bit to 32
363 // but this is what we support at the monent in ArmNN
364 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
365 }
366 }
367
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100368 std::vector<unsigned int> safeShape = shapes;
369 if (safeShape.size() == 0)
370 {
371 safeShape.push_back(1);
372 }
373
telsoa01c577f2c2018-08-31 09:22:23 +0100374 // two statements (on purpose) for easier debugging:
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100375 armnn::TensorInfo result(static_cast<unsigned int>(safeShape.size()),
376 safeShape.data(),
telsoa01c577f2c2018-08-31 09:22:23 +0100377 type,
378 quantizationScale,
379 quantizationOffset);
380 return result;
381}
382
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000383armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
384{
385 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
386 return ToTensorInfo(tensorPtr, dimensions);
387}
388
telsoa01c577f2c2018-08-31 09:22:23 +0100389template<typename T>
390std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
391CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
392 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000393 armnn::TensorInfo& tensorInfo,
394 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100395{
396 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
397 BOOST_ASSERT_MSG(bufferPtr != nullptr,
398 boost::str(
399 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
400
401 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000402
403 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
404 {
405 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000406 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
407 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000408 }
409 else
410 {
411 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
412 }
413
telsoa01c577f2c2018-08-31 09:22:23 +0100414 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
415}
416
telsoa01c577f2c2018-08-31 09:22:23 +0100417armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
418{
419 // generate the binding id by shifting the tensor id by 8 bit
420 // and add the subgraph id, which allows 256 subgraphs
421 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
422}
423
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000424bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
425{
426 const unsigned int actualSize = actual.GetNumDimensions();
427 if (actualSize != expected.size())
428 {
429 return false;
430 }
431
432 for (unsigned int i = 0u; i < actualSize; i++)
433 {
434 if (expected[i] < 0 ||
435 actual[i] != static_cast<unsigned int>(expected[i]))
436 {
437 return false;
438 }
439 }
440
441 return true;
442}
443
telsoa01c577f2c2018-08-31 09:22:23 +0100444} // <anonymous>
445
446TfLiteParser::TfLiteParser()
447: m_Network(nullptr, nullptr)
448, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
449{
450 // register supported operators
451 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Bruno Goncalvesdb947e22019-02-08 18:52:21 -0200452 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
Sadik Armagan479045b2018-10-01 11:51:37 +0100453 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100454 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
455 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
keidav011b3e2ea2019-02-21 10:07:37 +0000456 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseDetectionPostProcess;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100457 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Finn Williamsc42c3842019-01-22 14:18:11 +0000458 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100459 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -0200460 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -0200461 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
Sadik Armagan58f39192018-09-17 14:14:39 +0100462 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
463 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100464 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -0200465 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
Sadik Armagan479045b2018-10-01 11:51:37 +0100466 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
Bruno Goncalvesbaded142019-02-08 19:02:48 -0200467 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
Sadik Armagan479045b2018-10-01 11:51:37 +0100468 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Bruno Goncalves451d95b2019-02-12 22:59:22 -0200469 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
Bruno Goncalvesbbeae262019-02-07 18:37:39 -0200470 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200471 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Bruno Goncalvesf803f782018-12-18 13:40:30 -0200472 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Bruno Goncalves2235cee2018-12-19 12:51:45 -0200473 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Bruno Goncalves6c2355b2018-12-19 12:52:01 -0200474 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
Nina Drozd0324f482019-04-08 10:52:10 +0100475 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
Nina Drozd99851762019-04-09 09:37:38 +0100476 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
Nina Drozd200e3802019-04-15 09:47:39 +0100477 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
telsoa01c577f2c2018-08-31 09:22:23 +0100478}
479
480void TfLiteParser::ResetParser()
481{
482 m_Network = armnn::INetworkPtr(nullptr, nullptr);
483 m_Model = nullptr;
484 m_SubgraphConnections.clear();
485}
486
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200487void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
488 size_t operatorIndex,
489 IConnectableLayer *layer)
490{
491 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
492 BOOST_ASSERT(layer != nullptr);
493
494 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
495 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
496
497 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
498
499 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
500 TensorRawPtr tensorPtr = subGraphPtr->tensors[reshapedInputId].get();
501 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
502 TensorRawPtr tensorPtr1 = subGraphPtr->tensors[inputId].get();
503
504 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
505 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
506
507 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
508 {
509 uint32_t id = reshapedInputId;
510 reshapedInputId = inputId;
511 inputId = id;
512
513 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
514 inputTensorInfo = ToTensorInfo(tensorPtr);
515 }
516
517 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
518
519 std::vector<unsigned> reshapedDim;
520 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
521 {
522 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
523 }
524
525 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
526 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
527
528 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
529
530 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
531 armnn::ReshapeDescriptor desc;
532 desc.m_TargetShape = reshapedTensorInfo.GetShape();
533 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
534
535 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
536 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
537
538 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
539
540 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
541 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
542}
543
telsoa01c577f2c2018-08-31 09:22:23 +0100544INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
545{
546 ResetParser();
547 m_Model = LoadModelFromFile(graphFile);
548 return CreateNetworkFromModel();
549}
550
551INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
552{
553 ResetParser();
554 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
555 return CreateNetworkFromModel();
556}
557
558INetworkPtr TfLiteParser::CreateNetworkFromModel()
559{
560 m_Network = INetwork::Create();
561 BOOST_ASSERT(m_Model.get() != nullptr);
562
563 bool failedToCreate = false;
564 std::stringstream errors;
565
566 if (m_Model->subgraphs.size() != 1)
567 {
568 throw ParseException(
569 boost::str(
570 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
571 m_Model->subgraphs.size() %
572 CHECK_LOCATION().AsString()));
573 }
574
575 size_t subgraphIndex = 0;
576 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
577 {
578 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
579
580 size_t operatorIndex = 0;
581 for (OperatorPtr const & op : subgraph->operators)
582 {
583 try
584 {
telsoa01c577f2c2018-08-31 09:22:23 +0100585 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
586 auto builtinCode = opCodePtr->builtin_code;
587
588 if (builtinCode > tflite::BuiltinOperator_MAX)
589 {
590 throw ParseException(
591 boost::str(
592 boost::format("Operator code %1% is out of range 0-%2%. "
593 "subgraph:%3% operator idx:%4%. %5%") %
594 builtinCode %
595 tflite::BuiltinOperator_MAX %
596 subgraphIndex %
597 operatorIndex %
598 CHECK_LOCATION().AsString()));
599 }
600
601 // lookup and call the parser function
602 auto & parserFunction = m_ParserFunctions[builtinCode];
603 (this->*parserFunction)(subgraphIndex, operatorIndex);
604 }
605 catch (const ParseException& e)
606 {
607 failedToCreate = true;
608 std::stringstream errorString;
609
610 errorString << "Failed to parse operator #" << operatorIndex
611 << " within subgraph #" << subgraphIndex
612 << " error: " << e.what();
613 BOOST_LOG_TRIVIAL(error) << errorString.str();
614
615 errors << errorString.str() << "\n";
616 }
617 ++operatorIndex;
618 }
619
620 SetupInputLayers(subgraphIndex);
621 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200622 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100623
624 ++subgraphIndex;
625 }
626
627 if (failedToCreate)
628 {
629 // we can skip everything and let the outer exception handler deal with the error
630 throw ParseException(errors.str());
631 }
632
633 // establish the connections from the layer outputs to the inputs of the subsequent layers
634 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
635 {
636 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
637 {
638 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
639 {
640 for (size_t inputSlotIdx = 0;
641 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
642 ++inputSlotIdx)
643 {
644 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
645 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
646 }
647 }
648 }
649 }
650
651 return std::move(m_Network);
652}
653
654void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
655 size_t tensorIndex,
656 armnn::IOutputSlot* slot)
657{
658 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
659 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
660 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
661
662 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
663
664 // assuming there is only one producer for that tensor
665 if (tensorSlots.outputSlot != nullptr)
666 {
667 throw ParseException(boost::str(
668 boost::format("Another layer has already registered itself as the producer of "
669 "subgraph:%1% tensor:%2% %3%") %
670 subgraphIndex %
671 tensorIndex %
672 CHECK_LOCATION().AsString()));
673 }
674
675 tensorSlots.outputSlot = slot;
676}
677
678void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
679 size_t tensorIndex,
680 armnn::IInputSlot* slot)
681{
682 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
683 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
684 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
685
686 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
687 tensorSlots.inputSlots.push_back(slot);
688}
689
690void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
691{
692 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
693 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
694 //
695 auto opcodeIndex = operatorPtr->opcode_index;
696 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
697
698 throw ParseException(
699 boost::str(
700 boost::format("Operator not supported. "
701 "subgraph:%1% operator:%2% "
702 "opcode_index:%3% opcode:%4% / %5% %6%") %
703 subgraphIndex %
704 operatorIndex %
705 opcodeIndex %
706 opcode %
707 tflite::EnumNameBuiltinOperator(opcode) %
708 CHECK_LOCATION().AsString()));
709}
710
telsoa01c577f2c2018-08-31 09:22:23 +0100711void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
712{
713 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
714
715 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
716 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
717
718 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
719
720 Convolution2dDescriptor desc;
721 desc.m_BiasEnabled = false;
722 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
723 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000724 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100725
Kevin May83add212019-03-26 11:39:19 +0000726 CHECK_DILATION(options->dilation_h_factor, operatorIndex);
727 CHECK_DILATION(options->dilation_w_factor, operatorIndex);
728
telsoa01c577f2c2018-08-31 09:22:23 +0100729 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
730 CHECK_VALID_SIZE(inputs.size(), 2, 3);
731
732 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
733 CHECK_VALID_SIZE(outputs.size(), 1);
734
735 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
736 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
737
738 // assuming input is NHWC
739 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
740 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
741
742 // assuming the filter is OHWI : Output, H, W, Input
743 // which is essentially the same as NHWC
744 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
745 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
746
747 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
748 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
749
Matteo Martincigh747ef822018-12-18 09:26:39 +0000750 auto filterTensorAndData = CreateConstTensor(inputs[1],
751 filterTensorInfo,
752 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100753 armnn::IConnectableLayer* layer;
754
755 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
756
757 if (inputs.size() == 3)
758 {
759 desc.m_BiasEnabled = true;
760 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000761 auto biasTensorAndData = CreateConstTensor(inputs[2],
762 biasTensorInfo,
763 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100764 layer = m_Network->AddConvolution2dLayer(desc,
765 filterTensorAndData.first,
766 biasTensorAndData.first,
767 layerName.c_str());
768 }
769 else
770 {
771 layer = m_Network->AddConvolution2dLayer(desc,
772 filterTensorAndData.first,
773 layerName.c_str());
774 }
775
776 BOOST_ASSERT(layer != nullptr);
777
telsoa01c577f2c2018-08-31 09:22:23 +0100778 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000779 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100780
781 // register the input connection slots for the layer, connections are made after all layers have been created
782 // only the tensors for the inputs are relevant, exclude the const tensors
783 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000784 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100785
jimfly01c25411c2018-11-14 17:47:22 +0000786 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100787 // register the output connection slots for the layer, connections are made after all layers have been created
788 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
789 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
790}
791
792void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
793{
794 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
795
796 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
797 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
798
799 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
800
801 DepthwiseConvolution2dDescriptor desc;
802 desc.m_BiasEnabled = false;
803 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
804 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000805 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100806 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
807 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
808
809 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
810 CHECK_VALID_SIZE(inputs.size(), 2, 3);
811 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
812 CHECK_VALID_SIZE(outputs.size(), 1);
813
Kevin May83add212019-03-26 11:39:19 +0000814 CHECK_DILATION(options->dilation_h_factor, operatorIndex);
815 CHECK_DILATION(options->dilation_w_factor, operatorIndex);
816
telsoa01c577f2c2018-08-31 09:22:23 +0100817 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
818 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
819
Matteo Martincigh747ef822018-12-18 09:26:39 +0000820 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100821 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
822 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000823
824 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100825 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
826 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
827
Matteo Martincigh747ef822018-12-18 09:26:39 +0000828 // Reshape weights as [ H, W, I, M ]
829 filterTensorInfo.SetShape({ filterHeight,
830 filterWidth,
831 inputTensorInfo.GetShape()[3],
832 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
833
834 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
835 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
836
telsoa01c577f2c2018-08-31 09:22:23 +0100837 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
838 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
839
Matteo Martincigh747ef822018-12-18 09:26:39 +0000840 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100841 armnn::IConnectableLayer* layer;
842 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
843
844 if (inputs.size() == 3)
845 {
846 desc.m_BiasEnabled = true;
847 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000848 auto biasTensorAndData = CreateConstTensor(inputs[2],
849 biasTensorInfo,
850 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100851 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
852 filterTensorAndData.first,
853 biasTensorAndData.first,
854 layerName.c_str());
855 }
856 else
857 {
858 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
859 filterTensorAndData.first,
860 layerName.c_str());
861 }
862 BOOST_ASSERT(layer != nullptr);
863
telsoa01c577f2c2018-08-31 09:22:23 +0100864 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000865 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100866
867 // register the input connection slots for the layer, connections are made after all layers have been created
868 // only the tensors for the inputs are relevant, exclude the const tensors
869 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000870 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100871
jimfly01c25411c2018-11-14 17:47:22 +0000872 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100873 // register the output connection slots for the layer, connections are made after all layers have been created
874 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
875 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
876}
877
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100878void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
879{
880 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
881}
882
Bruno Goncalvesdb947e22019-02-08 18:52:21 -0200883void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
884{
885 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
886
887 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
888 CHECK_VALID_SIZE(inputs.size(), 3);
889
890 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
891 CHECK_VALID_SIZE(outputs.size(), 1);
892
893 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
894 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
895
896 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
897 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
898
899 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
900 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
901
902 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
903 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
904
905 size_t step = 2;
906 std::vector<std::pair<unsigned int, unsigned int>> crops;
907 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
908 {
909 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
910 }
911
912 armnn::BatchToSpaceNdDescriptor desc;
913 desc.m_BlockShape = blockShape;
914 desc.m_Crops = crops;
915 desc.m_DataLayout = armnn::DataLayout::NHWC;
916
917 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
918
919 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
920 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
921
922 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
923
924 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
925 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
926
927 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
928 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
929}
930
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100931void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
932{
933 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
934}
935
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -0200936void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
937{
938 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
939
940 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
941 CHECK_VALID_SIZE(inputs.size(), 2);
942
943 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
944 CHECK_VALID_SIZE(outputs.size(), 1);
945
946 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
947 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
948
949 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
950 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
951
952 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
953 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
954
955 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
956 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
957 {
958 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
959 }
960 else
961 {
962 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
963 }
964
965 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
966 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
967}
968
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -0200969void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
970{
971 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
972
973 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
974 CHECK_VALID_SIZE(inputs.size(), 2);
975
976 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
977 CHECK_VALID_SIZE(outputs.size(), 1);
978
979 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
980 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
981
982 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
983 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
984
985 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
986 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
987
988 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
989 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
990 {
991 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
992 }
993 else
994 {
995 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
996 }
997
998 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
999 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1000}
1001
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001002void TfLiteParser::ParsePool(size_t subgraphIndex,
1003 size_t operatorIndex,
1004 PoolingAlgorithm algorithm)
1005{
1006 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1007
1008 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1009 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1010
1011 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1012
1013 std::string layerName;
1014
1015 switch (algorithm)
1016 {
1017 case PoolingAlgorithm::Average:
1018 layerName =
1019 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1020 break;
1021 case PoolingAlgorithm::Max:
1022 layerName =
1023 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1024 break;
1025 default:
1026 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1027 }
1028
1029 Pooling2dDescriptor desc;
1030
1031 desc.m_PoolType = algorithm;
1032 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1033 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1034 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1035 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1036 desc.m_PaddingMethod = PaddingMethod::Exclude;
1037 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001038 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001039
1040 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1041 CHECK_VALID_SIZE(inputs.size(), 1);
1042 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1043
1044 // assuming input is NHWC
1045 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1046 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1047
1048 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1049 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1050
1051 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1052 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001053
1054 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1055
1056 BOOST_ASSERT(layer != nullptr);
1057
jimfly01c25411c2018-11-14 17:47:22 +00001058 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1059 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001060
1061 // register the input connection slots for the layer, connections are made after all layers have been created
1062 // only the tensors for the inputs are relevant, exclude the const tensors
1063 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001064 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001065
jimfly01c25411c2018-11-14 17:47:22 +00001066 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001067 // register the output connection slots for the layer, connections are made after all layers have been created
1068 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1069 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1070}
1071
telsoa01c577f2c2018-08-31 09:22:23 +01001072void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1073{
1074 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1075 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1076 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1077
1078 SoftmaxDescriptor desc;
1079 desc.m_Beta = options->beta;
1080
1081 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1082 CHECK_VALID_SIZE(inputs.size(), 1);
1083 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1084 CHECK_VALID_SIZE(outputs.size(), 1);
1085
1086 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1087 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1088
1089 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1090 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1091
1092 // register the input connection slots for the layer, connections are made after all layers have been created
1093 // only the tensors for the inputs are relevant, exclude the const tensors
1094 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1095 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1096
1097 // register the output connection slots for the layer, connections are made after all layers have been created
1098 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1099 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1100}
1101
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001102void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1103{
1104 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1105
1106 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1107 CHECK_VALID_SIZE(inputs.size(), 3);
1108
1109 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1110 CHECK_VALID_SIZE(outputs.size(), 1);
1111
1112 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1113 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1114
1115 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1116 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1117
1118 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1119 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1120
1121 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1122 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1123
1124 size_t step = 2;
1125 std::vector<std::pair<unsigned int, unsigned int>> padList;
1126 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1127 {
1128 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1129 }
1130
1131 armnn::SpaceToBatchNdDescriptor desc;
1132 desc.m_BlockShape = blockShape;
1133 desc.m_PadList = padList;
1134 desc.m_DataLayout = armnn::DataLayout::NHWC;
1135
1136 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1137
1138 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1139 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1140
1141 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1142
1143 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1144 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1145
1146 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1147 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1148}
1149
telsoa01c577f2c2018-08-31 09:22:23 +01001150armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1151 const armnn::TensorInfo & inputTensorInfo)
1152{
1153 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1154 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1155 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1156
1157 if (inputTensorInfo.GetNumDimensions() > 4)
1158 {
1159 std::stringstream ss;
1160 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1161 << " shape:" << inputTensorInfo.GetShape() << " "
1162 << CHECK_LOCATION().AsString();
1163 throw ParseException(ss.str());
1164 }
1165
1166 if (squeezeDims.empty())
1167 {
1168 squeezeDims.assign(dimensionSequence,
1169 dimensionSequence+inputTensorInfo.GetNumDimensions());
1170 }
1171
1172 std::vector<uint32_t> outputDims;
1173 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1174 {
1175 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1176 auto currentDimension = inputTensorInfo.GetShape()[i];
1177 if (skipSqueeze || currentDimension != 1)
1178 {
1179 outputDims.push_back(currentDimension);
1180 }
1181 }
1182
1183 if (outputDims.size() > 4)
1184 {
1185 std::stringstream ss;
1186 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1187 << " shape:" << inputTensorInfo.GetShape() << " "
1188 << CHECK_LOCATION().AsString();
1189 throw ParseException(ss.str());
1190 }
1191
1192 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1193 outputDims.data());
1194
1195 // we need to preserve the tensor type and the quantization data as well
1196 TensorInfo outTensorInfo = inputTensorInfo;
1197 outTensorInfo.SetShape(outShape);
1198
1199 return outTensorInfo;
1200}
1201
1202void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1203{
1204 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1205
1206 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1207 CHECK_VALID_SIZE(inputs.size(), 1);
1208
1209 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1210 CHECK_VALID_SIZE(outputs.size(), 1);
1211
1212 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1213 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1214
1215 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1216 armnn::TensorInfo outputTensorInfo =
1217 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1218 inputTensorInfo);
1219
1220 ReshapeDescriptor reshapeDesc;
1221 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1222
1223 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1224 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1225 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1226
1227 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1228 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1229
1230 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1231 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1232}
1233
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001234void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1235{
1236 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1237
1238 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1239 CHECK_VALID_SIZE(inputs.size(), 4);
1240
1241 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1242 CHECK_VALID_SIZE(outputs.size(), 1);
1243
1244 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1245 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1246
1247 StridedSliceDescriptor desc;
1248 desc.m_BeginMask = options->begin_mask;
1249 desc.m_EllipsisMask = options->ellipsis_mask;
1250 desc.m_EndMask = options->end_mask;
1251 desc.m_NewAxisMask = options->new_axis_mask;
1252 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1253 desc.m_DataLayout = armnn::DataLayout::NHWC;
1254
1255 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1256 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1257
1258 std::vector<int> begin(beginTensorInfo.GetNumElements());
1259 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1260
1261 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1262 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1263
1264 std::vector<int> end(endTensorInfo.GetNumElements());
1265 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1266
1267 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1268 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1269
1270 std::vector<int> stride(strideTensorInfo.GetNumElements());
1271 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1272
1273 desc.m_Begin = begin;
1274 desc.m_End = end;
1275 desc.m_Stride = stride;
1276
1277 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1278 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1279
1280 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1281 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1282
1283 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1284 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1285
1286 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1287 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1288}
1289
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001290void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1291{
1292 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1293
1294 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1295 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1296
1297 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1298 CHECK_VALID_SIZE(inputs.size(), 2);
1299
1300 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1301 CHECK_VALID_SIZE(outputs.size(), 1);
1302
1303 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1304 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1305
1306 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1307 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1308
1309 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1310 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1311
1312 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1313 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1314 {
1315 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1316 }
1317 else
1318 {
1319 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1320 }
1321
1322 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1323
1324 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1325 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1326}
1327
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001328void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1329{
1330 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1331
1332 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1333 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1334
1335 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1336 CHECK_VALID_SIZE(inputs.size(), 2);
1337
1338 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1339 CHECK_VALID_SIZE(outputs.size(), 1);
1340
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001341 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1342 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1343
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001344 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1345 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1346
1347 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1348 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1349
1350 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001351 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1352 {
1353 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1354 }
1355 else
1356 {
1357 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1358 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001359
1360 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1361
1362 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1363 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1364}
1365
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001366void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1367{
1368 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1369
1370 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1371 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1372
1373 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1374 CHECK_VALID_SIZE(inputs.size(), 2);
1375
1376 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1377 CHECK_VALID_SIZE(outputs.size(), 1);
1378
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001379 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1380 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1381
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001382 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1383 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1384
1385 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1386 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1387
1388 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001389 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1390 {
1391 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1392 }
1393 else
1394 {
1395 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1396 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001397
1398 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1399
1400 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1401 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1402}
1403
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001404void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1405{
1406 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1407
1408 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1409
1410 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1411 CHECK_VALID_SIZE(outputs.size(), 1);
1412
1413 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1414 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1415
1416 armnn::MeanDescriptor desc;
1417 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1418 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1419 desc.m_Axis = axis;
1420
1421 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1422 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1423
1424 desc.m_KeepDims =
1425 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1426 true : false;
1427
1428 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1429 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1430
1431 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1432
1433 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1434 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1435
1436 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1437 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1438}
1439
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001440void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1441{
1442 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1443
1444 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1445
1446 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1447 CHECK_VALID_SIZE(outputs.size(), 1);
1448
1449 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1450 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1451
1452 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1453 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1454
1455 size_t step = 2;
1456 armnn::PadDescriptor desc;
1457 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1458 {
1459 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1460 }
1461
1462 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1463 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1464
1465 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1466 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1467
1468 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1469 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1470
1471 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1472 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1473}
1474
Finn Williamsc42c3842019-01-22 14:18:11 +00001475
Sadik Armagan58f39192018-09-17 14:14:39 +01001476void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1477{
Finn Williamsc42c3842019-01-22 14:18:11 +00001478 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001479}
1480
1481void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1482{
Finn Williamsc42c3842019-01-22 14:18:11 +00001483 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1484}
Sadik Armagan58f39192018-09-17 14:14:39 +01001485
Finn Williamsc42c3842019-01-22 14:18:11 +00001486void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1487{
1488 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1489}
1490
Nina Drozd99851762019-04-09 09:37:38 +01001491void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1492{
1493 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1494}
1495
Finn Williamsc42c3842019-01-22 14:18:11 +00001496
1497void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1498{
1499 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001500 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1501 boost::ignore_unused(operatorPtr);
1502
1503 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1504 CHECK_VALID_SIZE(inputs.size(), 1);
1505
1506 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1507 CHECK_VALID_SIZE(outputs.size(), 1);
1508
Finn Williamsc42c3842019-01-22 14:18:11 +00001509 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001510 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001511 activationDesc.m_Function = activationType;
1512
1513 switch (activationType)
1514 {
1515 case ActivationFunction::ReLu:
1516 {
1517 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1518 break;
1519 }
1520 case ActivationFunction::BoundedReLu:
1521 {
1522 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1523 activationDesc.m_A = 6.0f;
1524 activationDesc.m_B = 0.0f;
1525 break;
1526 }
1527 case ActivationFunction::Sigmoid:
1528 {
1529 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1530 break;
1531 }
Nina Drozd99851762019-04-09 09:37:38 +01001532 case ActivationFunction::TanH:
1533 {
1534 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1535 activationDesc.m_A = 1.0f;
1536 activationDesc.m_B = 1.0f;
1537 break;
1538 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001539 default:
1540 {
1541 throw ParseException(
1542 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1543 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1544 }
1545 }
1546
1547 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001548
1549 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1550 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1551
1552 // register the input connection slots for the layer, connections are made after all layers have been created
1553 // only the tensors for the inputs are relevant, exclude the const tensors
1554 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1555 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1556
1557 // register the output connection slots for the layer, connections are made after all layers have been created
1558 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1559 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1560}
Sadikb94967b2018-09-19 15:30:00 +01001561armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1562 const std::vector<int32_t> & targetDimsIn)
1563{
1564 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1565 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1566
1567 if (stretchDim != targetDimsIn.end())
1568 {
1569 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1570 {
1571 throw ParseException(
1572 boost::str(
1573 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1574 }
1575
1576 auto targetNumElements =
1577 boost::numeric_cast<unsigned int>(
1578 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1579
1580 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1581 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1582 }
1583
1584 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1585
1586 TensorInfo reshapeInfo = inputTensorInfo;
1587 reshapeInfo.SetShape(outputShape);
1588
1589 return reshapeInfo;
1590}
1591
1592void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1593{
1594 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1595
1596 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001597
1598 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1599 CHECK_VALID_SIZE(outputs.size(), 1);
1600
1601 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1602 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1603
1604 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001605 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1606 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001607 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1608
kevmay0171972a82018-12-17 14:28:03 +00001609 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001610 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1611 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001612 {
1613 std::stringstream ss;
1614 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001615 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001616 << " does not equal output shape "
1617 << actualOutputTensorInfo.GetShape()
1618 << ": "
1619 << CHECK_LOCATION().AsString();
1620 throw ParseException(ss.str());
1621 }
1622
Sadikb94967b2018-09-19 15:30:00 +01001623 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001624 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001625
1626 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1627 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001628 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001629
1630 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1631 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1632
1633 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1634 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1635}
1636
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02001637void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
1638{
1639 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1640
1641 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1642 CHECK_VALID_SIZE(inputs.size(), 2);
1643
1644 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1645 CHECK_VALID_SIZE(outputs.size(), 1);
1646
1647 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
1648
1649 // Data for the parsed tensor args (size) must be stored locally.
1650 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
1651
1652 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1653 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1654
1655 ResizeBilinearDescriptor desc;
1656 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
1657 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
1658 desc.m_DataLayout = armnn::DataLayout::NHWC;
1659
1660 auto layerName = boost::str(boost::format("ResizeBilinear:%1%:%2%") % subgraphIndex % operatorIndex);
1661 IConnectableLayer* layer = m_Network->AddResizeBilinearLayer(desc, layerName.c_str());
1662
1663 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1664 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1665
1666 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1667 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1668
1669 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1670 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1671}
1672
Sadik Armagan479045b2018-10-01 11:51:37 +01001673void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1674{
1675 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1676
1677 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1678 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1679
1680 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1681
1682 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1683 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1684 CHECK_VALID_SIZE(outputs.size(), 1);
1685
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001686 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1687 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001688
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001689 const unsigned int concatDimInput = static_cast<unsigned int>(
1690 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001691
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001692 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1693 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001694
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001695 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01001696
1697 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1698 {
1699 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1700
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001701 // This set up concatDescriptor view origin
1702 armnnUtils::ProcessConcatInputTensorInfo(
1703 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01001704 }
1705
1706 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1707 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1708
1709 BOOST_ASSERT(layer != nullptr);
1710
1711 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1712 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01001713
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001714 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01001715
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001716 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01001717
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001718 // add fused activation layer
1719 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01001720
Sadik Armagan479045b2018-10-01 11:51:37 +01001721 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1722 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1723}
1724
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001725void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1726{
1727 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1728
1729 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1730 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1731
1732 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1733
1734 FullyConnectedDescriptor desc;
1735 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001736 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001737
1738 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1739 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1740 CHECK_VALID_SIZE(outputs.size(), 1);
1741
1742 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1743
1744 // Fully Connected Layer accepts two dimensional weights input
1745 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1746 if (weightsDimension != 2)
1747 {
1748 throw ParseException(
1749 boost::str(
1750 boost::format(
1751 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1752 "Node %2%")
1753 % weightsDimension
1754 % CHECK_LOCATION().AsString()));
1755 }
1756
Matteo Martincigh747ef822018-12-18 09:26:39 +00001757 auto filterTensorAndData = CreateConstTensor(inputs[1],
1758 filterTensorInfo,
1759 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001760 armnn::IConnectableLayer* layer;
1761 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1762
1763 if (inputs.size() == 3)
1764 {
1765 desc.m_BiasEnabled = true;
1766 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001767 auto biasTensorAndData = CreateConstTensor(inputs[2],
1768 biasTensorInfo,
1769 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001770 layer = m_Network->AddFullyConnectedLayer(desc,
1771 filterTensorAndData.first,
1772 biasTensorAndData.first,
1773 layerName.c_str());
1774 }
1775 else
1776 {
1777 layer = m_Network->AddFullyConnectedLayer(desc,
1778 filterTensorAndData.first,
1779 layerName.c_str());
1780 }
1781 BOOST_ASSERT(layer != nullptr);
1782
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01001783 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1784
1785 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1786
1787 if (inputTensorInfo.GetNumDimensions() > 2)
1788 {
1789 // Add reshape to flatten to 2D [batch_size, input_size],
1790 // where "input_size" corresponds to the number of inputs to the layer,
1791 // matching the second dimension of weights,
1792 // and "batch_size" is calculated by dividing the number of elements by "input_size".
1793 std::vector<unsigned int> reshapedDimensions(2);
1794 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
1795 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
1796
1797 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
1798 {
1799 throw ParseException(
1800 boost::str(
1801 boost::format(
1802 "Failed to deduce input tensor shape from filter size %1%")
1803 % reshapedDimensions[1]
1804 % CHECK_LOCATION().AsString()));
1805 }
1806
1807 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
1808 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
1809
1810 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
1811 armnn::ReshapeDescriptor desc;
1812 desc.m_TargetShape = reshapedTensorInfo.GetShape();
1813 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
1814
1815 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
1816 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
1817
1818 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
1819 }
1820 else
1821 {
1822 // register the input connection slot for the layer
1823 // only the tensors for the inputs are relevant, exclude the const tensors
1824 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1825 }
1826
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001827 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1828 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1829
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001830 // we need to add the activation layer and fortunately we don't need to care about the data layout
1831 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1832 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01001833
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001834 // register the output connection slots for the layer, connections are made after all layers have been created
1835 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1836 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1837}
1838
keidav011b3e2ea2019-02-21 10:07:37 +00001839void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
1840{
1841 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1842
1843 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1844
1845 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1846 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1847 CHECK_VALID_SIZE(outputs.size(), 4);
1848
1849 // Obtain custom options from flexbuffers
1850 auto custom_options = operatorPtr->custom_options;
1851 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
1852
1853 // Obtain descriptor information from tf lite
1854 DetectionPostProcessDescriptor desc;
1855 desc.m_MaxDetections = m["max_detections"].AsUInt32();
1856 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
1857 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
1858 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
1859 desc.m_NumClasses = m["num_classes"].AsUInt32();
1860 desc.m_ScaleH = m["h_scale"].AsFloat();
1861 desc.m_ScaleW = m["w_scale"].AsFloat();
1862 desc.m_ScaleX = m["x_scale"].AsFloat();
1863 desc.m_ScaleY = m["y_scale"].AsFloat();
1864
keidav0107d58c72019-02-26 11:57:39 +00001865 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00001866 {
keidav0107d58c72019-02-26 11:57:39 +00001867 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00001868 }
1869 if (!(m["detections_per_class"].IsNull()))
1870 {
1871 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
1872 }
1873
1874 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
1875 {
1876 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
1877 "must be positive and less than or equal to 1.");
1878 }
1879
1880 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
1881 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
1882 armnn::Optional<armnn::PermutationVector&>());
1883
1884 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
1885 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
1886 layerName.c_str());
1887
1888 BOOST_ASSERT(layer != nullptr);
1889
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00001890 // The model does not specify the output shapes.
1891 // The output shapes are calculated from the max_detection and max_classes_per_detection.
1892 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
1893 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
1894 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
1895 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
1896 m_OverridenOutputShapes.push_back({ 1 });
1897
keidav011b3e2ea2019-02-21 10:07:37 +00001898 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
1899 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00001900 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00001901 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
1902 }
1903
1904 // Register the input connection slots for the layer, connections are made after all layers have been created
1905 // only the tensors for the inputs are relevant, exclude the const tensors
1906 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1907 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1908
1909 // Register the output connection slots for the layer, connections are made after all layers have been created
1910 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1911 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
1912 outputTensorIndexes[1],
1913 outputTensorIndexes[2],
1914 outputTensorIndexes[3]});
1915}
1916
Nina Drozd200e3802019-04-15 09:47:39 +01001917void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
1918{
1919 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1920
1921 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1922 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
1923
1924 // This unpackAxis indicates the axis to unpack
1925 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
1926
1927 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1928 CHECK_VALID_SIZE(inputs.size(), 1);
1929
1930 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01001931
1932 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
1933 {
1934 throw ParseException(
1935 boost::str(
1936 boost::format(
1937 "The unpack axis: %1% cannot be greater than or equal to "
1938 "the number of input dimension %2% %3%")
1939 % unpackAxis
1940 % inputTensorInfo.GetNumDimensions()
1941 % CHECK_LOCATION().AsString()));
1942 }
1943
Nina Drozd200e3802019-04-15 09:47:39 +01001944 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
1945 // If num is not defined, automatically infer from the length of the dimension axis.
1946 if(unpackNum == 0)
1947 {
1948 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
1949 }
1950
1951 // If unpack number cannot be inferred and is still zero, throw ParseException.
1952 if(unpackNum == 0)
1953 {
1954 throw ParseException("Number to unpack must greater than zero.");
1955 }
1956
1957 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1958 CHECK_VALID_SIZE(outputs.size(), unpackNum);
1959
1960 auto inputDimSize = inputTensorInfo.GetNumDimensions();
1961 std::vector<unsigned int> unpackDimSizes(inputDimSize);
1962
1963 // Add current input shape to unpackDimSizes
1964 for (unsigned int i = 0; i < inputDimSize; ++i)
1965 {
1966 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
1967 }
1968
1969 if (unpackDimSizes[unpackAxis] != unpackNum)
1970 {
1971 throw ParseException("Number to unpack must be the same as length of the dimension to "
1972 "unpack along.");
1973 }
1974
1975 unpackDimSizes[unpackAxis] /= unpackNum;
1976
1977 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
1978 for (unsigned int j = 0; j < unpackNum; ++j)
1979 {
1980 // Set the size of the views.
1981 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
1982 {
1983 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
1984 }
1985 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
1986 }
1987
1988 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
1989 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
1990
Narumol Prangnawarat672de572019-04-23 15:28:06 +01001991 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
1992 unpackDimSizes.data());
1993
Nina Drozd200e3802019-04-15 09:47:39 +01001994 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1995 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1996
Narumol Prangnawarat672de572019-04-23 15:28:06 +01001997 // Reshape to remove unpacked dimension
1998 unsigned int reshapedNumDimensions = inputDimSize - 1;
1999 std::vector<unsigned int> reshapedDimensions(reshapedNumDimensions);
Nina Drozd200e3802019-04-15 09:47:39 +01002000
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002001 unsigned int reshapeIndex = 0;
2002 for (unsigned int i = 0; i < inputDimSize; ++i)
Nina Drozd200e3802019-04-15 09:47:39 +01002003 {
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002004 if (i == unpackAxis)
2005 {
2006 continue;
2007 }
2008 reshapedDimensions[reshapeIndex++] = unpackDimSizes[i];
Nina Drozd200e3802019-04-15 09:47:39 +01002009 }
2010
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002011 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2012 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2013 {
2014 armnn::TensorInfo reshapedTensorInfo = inputTensorInfo;
2015 reshapedTensorInfo.SetShape(armnn::TensorShape{ reshapedNumDimensions, reshapedDimensions.data() });
2016
2017 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2018 armnn::ReshapeDescriptor desc;
2019 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2020 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2021
2022 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape, inputTensorInfo.GetDataType()));
2023 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2024
2025 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2026
2027 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2028 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2029 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2030 }
Nina Drozd200e3802019-04-15 09:47:39 +01002031}
2032
Nina Drozd0324f482019-04-08 10:52:10 +01002033void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2034{
2035 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2036
2037 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2038 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2039
2040 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2041
Nina Drozd200e3802019-04-15 09:47:39 +01002042 // If number of splits cannot be inferred and is zero, throw ParseException.
2043 if(numSplits == 0)
2044 {
2045 throw ParseException("Number to splits must greater than zero.");
2046 }
2047
Nina Drozd0324f482019-04-08 10:52:10 +01002048 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2049 CHECK_VALID_SIZE(inputs.size(), 2);
2050 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2051 CHECK_VALID_SIZE(outputs.size(), numSplits);
2052
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002053 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2054 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002055
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002056 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2057 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2058 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2059
2060 BOOST_ASSERT(axisTensorInfo.GetNumElements() == 1);
2061 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002062
2063 // Armnn supports split along the channel dimension for data formats NHWC and NCHW.
2064 if (splitDim == 0 || splitDim == 2)
2065 {
2066 throw ParseException(
2067 boost::str(
2068 boost::format(
2069 "Dimension %1% for split is not supported by Armnn. %2%")
2070 % splitDim
2071 % CHECK_LOCATION().AsString()));
2072 }
2073
2074 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002075 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002076 {
2077 throw ParseException(
2078 boost::str(
2079 boost::format(
2080 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002081 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002082 % inputTensorInfo.GetNumDimensions()
2083 % MaxNumOfTensorDimensions
2084 % CHECK_LOCATION().AsString()));
2085 }
2086
2087 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2088
2089 // Add current input shape to splitterDimSizes
2090 for (unsigned int i = 0; i < inputDimSize; ++i)
2091 {
2092 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2093 }
2094
2095 if (splitterDimSizes[splitDim] % numSplits != 0)
2096 {
2097 throw ParseException("Number of splits must evenly divide the dimension");
2098 }
2099 splitterDimSizes[splitDim] /= numSplits;
2100
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002101 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002102 for (unsigned int j = 0; j < numSplits; ++j)
2103 {
2104 // Set the size of the views.
2105 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2106 {
2107 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2108 }
2109 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2110 }
2111
2112 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2113 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2114
2115 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002116 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002117
2118 TensorShape outShape = TensorShape(static_cast<unsigned int>(splitterDimSizes.size()),
2119 splitterDimSizes.data());
2120
2121 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2122 {
2123 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(outShape,
2124 inputTensorInfo.GetDataType()));
2125 }
2126
2127 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2128 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2129}
2130
Sadik Armagan58f39192018-09-17 14:14:39 +01002131armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2132 unsigned int outputSlot,
2133 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002134{
2135 ActivationDescriptor activationDesc;
2136 std::string layerName = prevLayer->GetName();
2137
2138 switch(activationType)
2139 {
2140 case tflite::ActivationFunctionType_NONE:
2141 {
2142 // this is a no-op: return previous layer
2143 return prevLayer;
2144 }
2145 case tflite::ActivationFunctionType_RELU:
2146 {
2147 activationDesc.m_Function = ActivationFunction::ReLu;
2148 layerName += ":RELU";
2149 break;
2150 }
2151 case tflite::ActivationFunctionType_RELU6:
2152 {
2153 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2154 activationDesc.m_A = 6.0f;
2155 activationDesc.m_B = 0.0f;
2156 layerName += ":RELU6";
2157 break;
2158 }
2159 case tflite::ActivationFunctionType_TANH:
2160 {
2161 activationDesc.m_Function = ActivationFunction::TanH;
2162 activationDesc.m_A = 1.0f;
2163 activationDesc.m_B = 1.0f;
2164 layerName += ":TANH";
2165 break;
2166 }
2167
2168 // I only put these here as a reminder what others we could support
2169 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2170 case tflite::ActivationFunctionType_SIGN_BIT:
2171 default:
2172 {
2173 throw ParseException(
2174 boost::str(
2175 boost::format("TfLite parser doesn't suppport fused activation: "
2176 "%1%/%2% %3% ") %
2177 activationType %
2178 tflite::EnumNameActivationFunctionType(activationType) %
2179 CHECK_LOCATION().AsString()));
2180
2181 }
2182 }
2183
2184 IConnectableLayer* activationLayer =
2185 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2186
2187 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2188 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2189 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2190 return activationLayer;
2191}
2192
2193TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2194{
2195 if (fileName == nullptr)
2196 {
2197 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2198 CHECK_LOCATION().AsString()));
2199 }
2200 boost::system::error_code errorCode;
2201 boost::filesystem::path pathToFile(fileName);
2202 if (!boost::filesystem::exists(pathToFile, errorCode))
2203 {
2204 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2205 fileName %
2206 errorCode %
2207 CHECK_LOCATION().AsString()));
2208 }
2209 std::ifstream file(fileName, std::ios::binary);
2210 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2211 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2212 fileContent.size());
2213}
2214
2215TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2216{
2217 if (binaryContent == nullptr)
2218 {
2219 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2220 CHECK_LOCATION().AsString()));
2221 }
2222 flatbuffers::Verifier verifier(binaryContent, len);
2223 if (verifier.VerifyBuffer<tflite::Model>() == false)
2224 {
2225 throw ParseException(
2226 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2227 "flatbuffers format. size:%1% %2%") %
2228 len %
2229 CHECK_LOCATION().AsString()));
2230 }
2231 return tflite::UnPackModel(binaryContent);
2232}
2233
2234TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2235 size_t subgraphIndex,
2236 size_t operatorIndex)
2237{
2238 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2239
2240 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2241 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
2242
2243 size_t inputCount = operatorPtr->inputs.size();
2244 TensorRawPtrVector result(inputCount);
2245 for (size_t i=0; i<inputCount; ++i)
2246 {
2247 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
2248 result[i] = subGraphPtr->tensors[inputId].get();
2249 }
2250 return result;
2251}
2252
2253TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2254 size_t subgraphIndex,
2255 size_t operatorIndex)
2256{
2257 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2258
2259 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2260 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
2261
2262 size_t outputCount = operatorPtr->outputs.size();
2263 TensorRawPtrVector result(outputCount);
2264 for (size_t i=0; i<outputCount; ++i)
2265 {
2266 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2267 CHECK_TENSOR(model, subgraphIndex, outputId);
2268 result[i] = subGraphPtr->tensors[outputId].get();
2269 }
2270 return result;
2271}
2272
2273TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2274 size_t subgraphIndex)
2275{
2276 CHECK_SUBGRAPH(model, subgraphIndex);
2277 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2278
2279 size_t inputCount = subGraphPtr->inputs.size();
2280 TensorIdRawPtrVector result(inputCount);
2281 for (size_t i=0; i<inputCount; ++i)
2282 {
2283 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
2284 CHECK_TENSOR(model, subgraphIndex, inputId);
2285 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
2286 }
2287 return result;
2288}
2289
2290TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2291 size_t subgraphIndex)
2292{
2293 CHECK_SUBGRAPH(model, subgraphIndex);
2294 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2295
2296 size_t outputCount = subGraphPtr->outputs.size();
2297 TensorIdRawPtrVector result(outputCount);
2298 for (size_t i=0; i<outputCount; ++i)
2299 {
2300 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
2301 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
2302 }
2303 return result;
2304}
2305
2306std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2307 size_t subgraphIndex,
2308 size_t operatorIndex)
2309{
2310 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2311 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2312 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
2313 return operatorPtr->inputs;
2314}
2315
2316std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2317 size_t subgraphIndex,
2318 size_t operatorIndex)
2319{
2320 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2321 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
2322 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
2323 return operatorPtr->outputs;
2324}
2325
2326void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2327 size_t operatorIndex,
2328 IConnectableLayer* layer,
2329 const std::vector<unsigned int>& tensorIndexes)
2330{
2331 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2332 BOOST_ASSERT(layer != nullptr);
2333 if (tensorIndexes.size() != layer->GetNumInputSlots())
2334 {
2335 throw ParseException(
2336 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2337 " for subgraph:%3% operator index:%4% %5%") %
2338 tensorIndexes.size() %
2339 layer->GetNumInputSlots() %
2340 subgraphIndex %
2341 operatorIndex %
2342 CHECK_LOCATION().AsString()));
2343 }
2344
2345 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2346 {
2347 unsigned int tensorIndex = tensorIndexes[slotIndex];
2348 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2349 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2350 }
2351}
2352
2353void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2354 size_t operatorIndex,
2355 IConnectableLayer* layer,
2356 const std::vector<unsigned int>& tensorIndexes)
2357{
2358 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2359 BOOST_ASSERT(layer != nullptr);
2360 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2361 {
2362 throw ParseException(
2363 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2364 " for subgraph:%3% operator index:%4% %5%") %
2365 tensorIndexes.size() %
2366 layer->GetNumOutputSlots() %
2367 subgraphIndex %
2368 operatorIndex %
2369 CHECK_LOCATION().AsString()));
2370 }
2371
2372 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2373 {
2374 unsigned int tensorIndex = tensorIndexes[slotIndex];
2375 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2376 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2377 }
2378}
2379
2380void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2381{
2382 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2383
2384 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2385 for (auto const & tensorIdAndPtr : inputs)
2386 {
2387 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2388 IConnectableLayer* layer =
2389 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2390
2391 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2392 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2393
2394 RegisterOutputSlots(subgraphIndex,
2395 VIRTUAL_OPERATOR_ID,
2396 layer,
2397 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2398 }
2399}
2400
2401void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2402{
2403 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2404
2405 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2406 for (auto const & tensorIdAndPtr : outputs)
2407 {
2408 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2409 IConnectableLayer* layer =
2410 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2411
2412 RegisterInputSlots(subgraphIndex,
2413 VIRTUAL_OPERATOR_ID,
2414 layer,
2415 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2416 }
2417}
2418
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002419void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2420{
2421 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2422
2423 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
2424 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2425 {
2426 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2427 {
2428 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2429 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2430 {
2431 TensorRawPtr tensorPtr = subGraphPtr->tensors[tensorIndex].get();
2432 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2433 auto tensorAndData = CreateConstTensor(tensorPtr,
2434 tensorInfo,
2435 armnn::Optional<armnn::PermutationVector&>());
2436
2437 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2438 IConnectableLayer *layer =
2439 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2440
2441 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2442 RegisterOutputSlots(subgraphIndex,
2443 VIRTUAL_OPERATOR_ID,
2444 layer,
2445 { tensorIndex });
2446
2447 }
2448 }
2449 }
2450}
2451
telsoa01c577f2c2018-08-31 09:22:23 +01002452// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2453TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2454{
2455 CHECK_BUFFER(model, bufferIndex);
2456 return model->buffers[bufferIndex].get();
2457}
2458
Matteo Martincigh747ef822018-12-18 09:26:39 +00002459template<typename T>
2460std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2461TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2462 TfLiteParser::TensorRawPtr tensorPtr,
2463 armnn::TensorInfo& tensorInfo,
2464 armnn::Optional<armnn::PermutationVector&> permutationVector)
2465{
2466 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2467 tensorPtr,
2468 tensorInfo,
2469 permutationVector);
2470 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2471 return std::make_pair(constData.first, std::move(storage));
2472}
2473
telsoa01c577f2c2018-08-31 09:22:23 +01002474std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2475TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002476 armnn::TensorInfo& tensorInfo,
2477 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002478{
2479 CHECK_TENSOR_PTR(tensorPtr);
2480 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2481 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2482
2483 switch (tensorInfo.GetDataType())
2484 {
2485 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002486 return CreateConstTensorAndStoreData<float>(bufferPtr,
2487 tensorPtr,
2488 tensorInfo,
2489 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002490 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002491 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2492 tensorPtr,
2493 tensorInfo,
2494 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002495 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002496 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2497 tensorPtr,
2498 tensorInfo,
2499 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002500 default:
2501 {
2502 std::stringstream errString;
2503 errString << "Unexpected datatype when creating const tensor: "
2504 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2505 << " shape:" << tensorInfo.GetShape()
2506 << CHECK_LOCATION().AsString();
2507 throw ParseException(errString.str());
2508 }
2509 }
2510}
2511
2512BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2513 const std::string& name) const
2514{
2515 CHECK_SUBGRAPH(m_Model, subgraphId);
2516 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2517 for (auto const & input : inputs)
2518 {
2519 if (input.second->name == name)
2520 {
2521 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2522 return std::make_pair(bindingId, ToTensorInfo(input.second));
2523 }
2524 }
2525
2526 std::stringstream bindings;
2527 for (auto const & input : inputs)
2528 {
2529 bindings << "'" << input.second->name << "' ";
2530 }
2531
2532 throw ParseException(
2533 boost::str(
2534 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2535 "Possible inputs are: [%3%] %4%") %
2536 subgraphId %
2537 name %
2538 bindings.str() %
2539 CHECK_LOCATION().AsString()));
2540}
2541
2542BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2543 const std::string& name) const
2544{
2545 CHECK_SUBGRAPH(m_Model, subgraphId);
2546 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002547 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01002548 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002549 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01002550 if (output.second->name == name)
2551 {
2552 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002553 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
2554 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
2555 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01002556 }
2557 }
2558
2559 std::stringstream bindings;
2560 for (auto const & output : outputs)
2561 {
2562 bindings << "'" << output.second->name << "' ";
2563 }
2564
2565 throw ParseException(
2566 boost::str(
2567 boost::format("No output binding found for subgraph:%1% and name:%2%. "
2568 "Possible outputs are: [%3%] %4%") %
2569 subgraphId %
2570 name %
2571 bindings.str() %
2572 CHECK_LOCATION().AsString()));
2573}
2574
2575size_t TfLiteParser::GetSubgraphCount() const
2576{
2577 return m_Model->subgraphs.size();
2578}
2579
2580std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
2581{
2582 CHECK_SUBGRAPH(m_Model, subgraphId);
2583 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2584 std::vector<std::string> result;
2585 result.reserve(inputs.size());
2586 for (auto const & input : inputs)
2587 {
2588 result.push_back(input.second->name);
2589 }
2590 return result;
2591}
2592
2593std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
2594{
2595 CHECK_SUBGRAPH(m_Model, subgraphId);
2596 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
2597 std::vector<std::string> result;
2598 result.reserve(outputs.size());
2599 for (auto const & output : outputs)
2600 {
2601 result.push_back(output.second->name);
2602 }
2603 return result;
2604}
2605
2606ITfLiteParser* ITfLiteParser::CreateRaw()
2607{
2608 return new TfLiteParser();
2609}
2610
2611ITfLiteParserPtr ITfLiteParser::Create()
2612{
2613 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
2614}
2615
2616void ITfLiteParser::Destroy(ITfLiteParser* parser)
2617{
2618 delete parser;
2619}
2620
2621TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
2622: m_FloatData(std::move(data))
2623, m_Uint8Data(nullptr)
2624, m_Int32Data(nullptr)
2625{
2626}
2627
2628TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
2629: m_FloatData(nullptr)
2630, m_Uint8Data(std::move(data))
2631, m_Int32Data(nullptr)
2632{
2633}
2634
2635TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
2636: m_FloatData(nullptr)
2637, m_Uint8Data(nullptr)
2638, m_Int32Data(std::move(data))
2639{
2640}
2641
2642} // armnnTfLiteParser