blob: fc5041bf9af1074c01426ce1c231c26d4d2029c9 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
Matteo Martincighe011d202019-11-28 11:35:47 +00005
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "TfLiteParser.hpp"
7
Matthew Bentham39ef3e52020-01-20 10:09:09 +00008#include <armnn/Descriptors.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +01009#include <armnn/Exceptions.hpp>
Derek Lamberti08446972019-11-26 16:38:31 +000010#include <armnn/Logging.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011#include <armnn/TypesUtils.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000012#include <armnn/utility/IgnoreUnused.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010013
14// armnnUtils:
Matteo Martincighe011d202019-11-28 11:35:47 +000015#include <armnnUtils/Permute.hpp>
16
Sadik Armagan479045b2018-10-01 11:51:37 +010017#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010018#include <VerificationHelpers.hpp>
19
20// The generated code based on the Tf Lite schema:
21#include <schema_generated.h>
22
Matteo Martincighe011d202019-11-28 11:35:47 +000023#include <flatbuffers/flexbuffers.h>
24
telsoa01c577f2c2018-08-31 09:22:23 +010025#include <boost/assert.hpp>
26#include <boost/format.hpp>
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +010027#include <boost/numeric/conversion/cast.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000028#include <boost/filesystem.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010029
30#include <fstream>
31#include <algorithm>
32#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010033#include <numeric>
Derek Lambertic9e52792020-03-11 11:42:26 +000034#include <sstream>
35
36#define ARMNN_THROW_PARSE_EXCEPTION(msg) \
37 { \
38 throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
39 << ": " \
40 << CHECK_LOCATION().AsString()).str()); \
41 }
telsoa01c577f2c2018-08-31 09:22:23 +010042
43using namespace armnn;
44using armnn::CheckLocation;
45namespace armnnTfLiteParser
46{
47namespace
48{
jimfly01c25411c2018-11-14 17:47:22 +000049
telsoa01c577f2c2018-08-31 09:22:23 +010050const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
51
52void CheckSubgraph(const TfLiteParser::ModelPtr & model,
53 size_t subgraphIndex,
54 const CheckLocation & location)
55{
56 if (model.get() == nullptr)
57 {
58 throw ParseException(
59 boost::str(
60 boost::format("%1% was called with invalid (null) model. "
61 "Possible reason is that the model is not yet loaded and Unpack(ed). "
62 "subgraph:%2% at %3%") %
63 location.m_Function %
64 subgraphIndex %
65 location.FileLine()));
66 }
67 else if (subgraphIndex >= model->subgraphs.size())
68 {
69 throw ParseException(
70 boost::str(
71 boost::format("%1% was called with an invalid subgraph index. "
72 "subgraph:%2% at %3%") %
73 location.m_Function %
74 subgraphIndex %
75 location.FileLine()));
76 }
77}
78
79#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
80 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
81
82void CheckModel(const TfLiteParser::ModelPtr & model,
83 size_t subgraphIndex,
84 size_t operatorIndex,
85 const CheckLocation & location)
86{
87 if (model.get() == nullptr)
88 {
89 throw ParseException(
90 boost::str(
91 boost::format("%1% was called with invalid (null) model. "
92 "Possible reason is that the model is not yet loaded and Unpack(ed). "
93 "subgraph:%2% operator:%3% at %4%") %
94 location.m_Function %
95 subgraphIndex %
96 operatorIndex %
97 location.FileLine()));
98 }
99 else if (subgraphIndex >= model->subgraphs.size())
100 {
101 throw ParseException(
102 boost::str(
103 boost::format("%1% was called with an invalid subgraph index. "
104 "subgraph:%2% operator:%3% at %4%") %
105 location.m_Function %
106 subgraphIndex %
107 operatorIndex %
108 location.FileLine()));
109 }
110 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
111 operatorIndex != VIRTUAL_OPERATOR_ID)
112 {
113 throw ParseException(
114 boost::str(
115 boost::format("%1% was called with an invalid operator index. "
116 "subgraph:%2% operator:%3% at %4%") %
117 location.m_Function %
118 subgraphIndex %
119 operatorIndex %
120 location.FileLine()));
121 }
122}
123
124#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
125 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
126
127void CheckTensor(const TfLiteParser::ModelPtr & model,
128 size_t subgraphIndex,
129 size_t tensorIndex,
130 const CheckLocation & location)
131{
132 // not checking model, because I assume CHECK_MODEL already run
133 // and checked that. An assert would do.
134 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
135
136 // also subgraph index should be checked by CHECK_MODEL so
137 // I only add an assert here
138 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
139
140 // the tensor index is the only one to check here
141 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
142 {
143 throw ParseException(
144 boost::str(
145 boost::format("%1% was called with an invalid tensor index. "
146 "subgraph:%2% tensor:%3% at %4%") %
147 location.m_Function %
148 subgraphIndex %
149 tensorIndex %
150 location.FileLine()));
151 }
152}
153
154#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
155 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
156
157void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
158 const CheckLocation & location)
159{
160 if (rawPtr == nullptr)
161 {
162 throw ParseException(
163 boost::str(
164 boost::format("%1% was called with a null tensor pointer. "
165 "at %2%") %
166 location.m_Function %
167 location.FileLine()));
168
169 }
170}
171
172#define CHECK_TENSOR_PTR(TENSOR_PTR) \
173 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
174
175void CheckBuffer(const TfLiteParser::ModelPtr & model,
176 size_t bufferIndex,
177 const CheckLocation & location)
178{
179 if (model.get() == nullptr)
180 {
181 throw ParseException(
182 boost::str(
183 boost::format("%1% was called with invalid (null) model. "
184 "Possible reason is that the model is not yet loaded and Unpack(ed). "
185 "buffer:%2% at %3%") %
186 location.m_Function %
187 bufferIndex %
188 location.FileLine()));
189 }
190 else if (bufferIndex >= model->buffers.size())
191 {
192 throw ParseException(
193 boost::str(
194 boost::format("%1% was called with an invalid buffer index. "
195 "buffer index:%2% at %3%") %
196 location.m_Function %
197 bufferIndex %
198 location.FileLine()));
199 }
200 else if (model->buffers[bufferIndex].get() == nullptr)
201 {
202 throw ParseException(
203 boost::str(
204 boost::format("The buffer #%1% is null. %3%") %
205 bufferIndex %
206 location.AsString()));
207 }
208}
209
210#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
211 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
212
213void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
214 const armnn::TensorInfo & tensorInfo,
215 uint32_t bufferId,
216 const CheckLocation & location)
217{
218 if (bufferPtr == nullptr)
219 {
220 throw ParseException(
221 boost::str(
222 boost::format("BufferPtr is null for buffer:%1%. %2%") %
223 bufferId %
224 location.AsString()));
225 }
226 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
227 tensorInfo.GetNumBytes() > bufferPtr->data.size())
228 {
229 std::stringstream ss;
230 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
231 << "For tensor: " << tensorInfo.GetShape()
232 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
233 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
234 throw ParseException(ss.str());
235 }
236}
237
238#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
239 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
240
241bool IsActivationSupported(tflite::ActivationFunctionType activationType)
242{
243 switch(activationType)
244 {
245 case tflite::ActivationFunctionType_NONE:
246 case tflite::ActivationFunctionType_RELU:
247 case tflite::ActivationFunctionType_RELU6:
248 case tflite::ActivationFunctionType_TANH:
249 {
250 return true;
251 }
252 default:
253 {
254 return false;
255 }
256 }
257}
258
259#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
260 do { \
261 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
262 { \
263 throw ParseException( \
264 boost::str( \
265 boost::format("TfLite parser doesn't suppport fused activation: " \
266 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
267 OPTION->fused_activation_function % \
268 tflite::EnumNameActivationFunctionType(\
269 OPTION->fused_activation_function) % \
270 __func__ % \
271 SUBGRAPH_INDEX % \
272 OPERATOR_INDEX % \
273 CHECK_LOCATION().FileLine())); \
274 } \
275 } while(false)
276
277
278std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
279{
280 std::vector<unsigned int> result;
281 result.reserve(in.size());
282 for (auto & i : in)
283 {
284 result.push_back(CHECKED_NON_NEGATIVE(i));
285 }
286 return result;
287}
288
289void CalcPadding(uint32_t inputSize,
290 uint32_t filterSize,
291 uint32_t stride,
Pablo Tellof0bd6832019-04-26 17:58:13 +0100292 uint32_t dilation,
telsoa01c577f2c2018-08-31 09:22:23 +0100293 uint32_t& paddingFront,
294 uint32_t& paddingBack,
295 tflite::Padding padding)
296{
297 paddingFront = 0;
298 paddingBack = 0;
299 if (padding == tflite::Padding_SAME)
300 {
301 uint32_t outputSize = (inputSize + stride - 1) / stride;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100302 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
303 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
telsoa01c577f2c2018-08-31 09:22:23 +0100304 if (temp > inputSize)
305 {
306 paddingFront = (temp - inputSize) / 2;
307 paddingBack = (temp - inputSize) - paddingFront;
308 }
309 }
310}
311
Keith Davis0c2eeac2020-02-11 16:51:50 +0000312armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr, const std::vector<unsigned int>& shapes,
313 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
telsoa01c577f2c2018-08-31 09:22:23 +0100314{
315 armnn::DataType type;
316 CHECK_TENSOR_PTR(tensorPtr);
317
318 switch (tensorPtr->type)
319 {
320 case tflite::TensorType_UINT8:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000321 type = armnn::DataType::QAsymmU8;
telsoa01c577f2c2018-08-31 09:22:23 +0100322 break;
323 case tflite::TensorType_FLOAT32:
324 type = armnn::DataType::Float32;
325 break;
Finn Williamsed66d142019-12-06 09:55:55 +0000326 case tflite::TensorType_INT8:
Keith Davis67e6c542020-02-19 10:08:33 +0000327 if (tensorPtr->quantization->zero_point.size() == 1)
Ryan OShea03181ff2020-02-07 17:22:22 +0000328 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000329 // Per-tensor
Ryan OShea03181ff2020-02-07 17:22:22 +0000330 type = armnn::DataType::QAsymmS8;
331 }
332 else
333 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000334 // Per-channel
Ryan OShea03181ff2020-02-07 17:22:22 +0000335 type = armnn::DataType::QSymmS8;
336 }
Finn Williamsed66d142019-12-06 09:55:55 +0000337 break;
338 case tflite::TensorType_INT16:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000339 type = armnn::DataType::QSymmS16;
Finn Williamsed66d142019-12-06 09:55:55 +0000340 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100341 case tflite::TensorType_INT32:
342 type = armnn::DataType::Signed32;
343 break;
344
345 default:
346 {
347 CheckLocation location = CHECK_LOCATION();
348 throw ParseException(
349 boost::str(
350 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
351 tensorPtr->type %
352 tflite::EnumNameTensorType(tensorPtr->type) %
353 tensorPtr->name %
354 location.AsString()));
355 }
356 }
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100357 std::vector<unsigned int> safeShape = shapes;
358 if (safeShape.size() == 0)
359 {
360 safeShape.push_back(1);
361 }
362
Keith Davisd305e1a2020-01-22 11:57:54 +0000363 float quantizationScale = 0.0f;
364 int32_t quantizationOffset = 0;
365
366 if (tensorPtr->quantization.get())
367 {
368 if (tensorPtr->quantization->scale.size() <= 1)
369 {
370 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
371 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
372
373 if (tensorPtr->quantization->scale.size() == 1)
374 {
375 quantizationScale = tensorPtr->quantization->scale[0];
376 }
377 if (tensorPtr->quantization->zero_point.size() == 1)
378 {
379 // NOTE: we lose precision here when converting from 64 bit to 32
Ryan OShea03181ff2020-02-07 17:22:22 +0000380 // but this is what we support at the moment in ArmNN
Keith Davisd305e1a2020-01-22 11:57:54 +0000381 quantizationOffset = boost::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
382 }
383
384 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
385 safeShape.data(),
386 type,
387 quantizationScale,
388 quantizationOffset);
389
390 return result;
391 }
392 else
393 {
394 std::vector<float> quantizationScales;
395 std::vector<int32_t> quantizationOffsets;
396
397 // Scale
398 std::copy(tensorPtr->quantization->scale.begin(),
399 tensorPtr->quantization->scale.end(),
400 std::back_inserter(quantizationScales));
401
Keith Davis0c2eeac2020-02-11 16:51:50 +0000402 // QSymmS8 Per-axis
Keith Davisd305e1a2020-01-22 11:57:54 +0000403 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
404 safeShape.data(),
405 type,
406 quantizationScales,
Keith Davis0c2eeac2020-02-11 16:51:50 +0000407 dimensionMappings[boost::numeric_cast<unsigned int>(
408 tensorPtr->quantization->quantized_dimension)]);
Keith Davisd305e1a2020-01-22 11:57:54 +0000409 return result;
410 }
411 }
412 else
413 {
414 armnn::TensorInfo result(boost::numeric_cast<unsigned int>(safeShape.size()),
415 safeShape.data(),
416 type,
417 quantizationScale,
418 quantizationOffset);
419 return result;
420 }
telsoa01c577f2c2018-08-31 09:22:23 +0100421}
422
Keith Davis0c2eeac2020-02-11 16:51:50 +0000423armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
424 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000425{
426 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000427 return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000428}
429
telsoa01c577f2c2018-08-31 09:22:23 +0100430template<typename T>
431std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
432CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
433 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000434 armnn::TensorInfo& tensorInfo,
435 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100436{
Jan Eilers8eb25602020-03-09 12:13:48 +0000437 IgnoreUnused(tensorPtr);
telsoa01c577f2c2018-08-31 09:22:23 +0100438 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
439 BOOST_ASSERT_MSG(bufferPtr != nullptr,
440 boost::str(
441 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
442
443 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000444
445 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
446 {
447 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000448 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
449 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000450 }
451 else
452 {
453 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
454 }
455
telsoa01c577f2c2018-08-31 09:22:23 +0100456 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
457}
458
telsoa01c577f2c2018-08-31 09:22:23 +0100459armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
460{
461 // generate the binding id by shifting the tensor id by 8 bit
462 // and add the subgraph id, which allows 256 subgraphs
463 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
464}
465
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000466bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
467{
468 const unsigned int actualSize = actual.GetNumDimensions();
469 if (actualSize != expected.size())
470 {
471 return false;
472 }
473
474 for (unsigned int i = 0u; i < actualSize; i++)
475 {
476 if (expected[i] < 0 ||
477 actual[i] != static_cast<unsigned int>(expected[i]))
478 {
479 return false;
480 }
481 }
482
483 return true;
484}
485
telsoa01c577f2c2018-08-31 09:22:23 +0100486} // <anonymous>
487
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100488TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
489: m_Options(options)
490, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100491, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
492{
493 // register supported operators
Sadik Armagan66dedc72019-12-10 16:32:07 +0000494 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000495 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
496 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
497 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
498 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000499 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000500 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Finn Williamsed66d142019-12-06 09:55:55 +0000501 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000502 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
503 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
504 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
505 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
506 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000507 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000508 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000509 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
510 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
511 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
512 m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000513 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
514 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
515 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
516 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
517 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000518 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000519 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
520 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000521 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000522 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
523 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
524 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000525 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
526 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
527 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
528 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100529
530 // register supported custom operators
531 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100532}
533
534void TfLiteParser::ResetParser()
535{
536 m_Network = armnn::INetworkPtr(nullptr, nullptr);
537 m_Model = nullptr;
538 m_SubgraphConnections.clear();
539}
540
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200541void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
542 size_t operatorIndex,
543 IConnectableLayer *layer)
544{
545 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
546 BOOST_ASSERT(layer != nullptr);
547
Derek Lambertiff05cc52019-04-26 13:05:17 +0100548 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
549 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200550
551 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
552
553 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100554 TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200555 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
Derek Lambertiff05cc52019-04-26 13:05:17 +0100556 TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200557
558 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
559 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
560
561 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
562 {
563 uint32_t id = reshapedInputId;
564 reshapedInputId = inputId;
565 inputId = id;
566
567 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
568 inputTensorInfo = ToTensorInfo(tensorPtr);
569 }
570
571 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
572
573 std::vector<unsigned> reshapedDim;
574 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
575 {
576 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
577 }
578
579 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
580 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
581
582 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
583
584 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
585 armnn::ReshapeDescriptor desc;
586 desc.m_TargetShape = reshapedTensorInfo.GetShape();
587 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
588
589 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
590 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
591
592 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
593
594 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
595 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
596}
597
telsoa01c577f2c2018-08-31 09:22:23 +0100598INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
599{
600 ResetParser();
601 m_Model = LoadModelFromFile(graphFile);
602 return CreateNetworkFromModel();
603}
604
605INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
606{
607 ResetParser();
608 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
609 return CreateNetworkFromModel();
610}
611
612INetworkPtr TfLiteParser::CreateNetworkFromModel()
613{
614 m_Network = INetwork::Create();
615 BOOST_ASSERT(m_Model.get() != nullptr);
616
617 bool failedToCreate = false;
618 std::stringstream errors;
619
620 if (m_Model->subgraphs.size() != 1)
621 {
622 throw ParseException(
623 boost::str(
624 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
625 m_Model->subgraphs.size() %
626 CHECK_LOCATION().AsString()));
627 }
628
629 size_t subgraphIndex = 0;
Derek Lambertiff05cc52019-04-26 13:05:17 +0100630 for (SubgraphPtr const & subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100631 {
632 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
633
634 size_t operatorIndex = 0;
635 for (OperatorPtr const & op : subgraph->operators)
636 {
637 try
638 {
telsoa01c577f2c2018-08-31 09:22:23 +0100639 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
640 auto builtinCode = opCodePtr->builtin_code;
641
642 if (builtinCode > tflite::BuiltinOperator_MAX)
643 {
644 throw ParseException(
645 boost::str(
646 boost::format("Operator code %1% is out of range 0-%2%. "
647 "subgraph:%3% operator idx:%4%. %5%") %
648 builtinCode %
649 tflite::BuiltinOperator_MAX %
650 subgraphIndex %
651 operatorIndex %
652 CHECK_LOCATION().AsString()));
653 }
654
655 // lookup and call the parser function
656 auto & parserFunction = m_ParserFunctions[builtinCode];
657 (this->*parserFunction)(subgraphIndex, operatorIndex);
658 }
659 catch (const ParseException& e)
660 {
661 failedToCreate = true;
662 std::stringstream errorString;
663
664 errorString << "Failed to parse operator #" << operatorIndex
665 << " within subgraph #" << subgraphIndex
666 << " error: " << e.what();
Derek Lamberti08446972019-11-26 16:38:31 +0000667 ARMNN_LOG(error) << errorString.str();
telsoa01c577f2c2018-08-31 09:22:23 +0100668
669 errors << errorString.str() << "\n";
670 }
671 ++operatorIndex;
672 }
673
674 SetupInputLayers(subgraphIndex);
675 SetupOutputLayers(subgraphIndex);
Bruno Goncalves3d7efe92018-12-27 14:21:43 -0200676 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100677
678 ++subgraphIndex;
679 }
680
681 if (failedToCreate)
682 {
683 // we can skip everything and let the outer exception handler deal with the error
684 throw ParseException(errors.str());
685 }
686
687 // establish the connections from the layer outputs to the inputs of the subsequent layers
688 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
689 {
690 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
691 {
692 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
693 {
694 for (size_t inputSlotIdx = 0;
695 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
696 ++inputSlotIdx)
697 {
698 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
699 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
700 }
701 }
702 }
703 }
704
705 return std::move(m_Network);
706}
707
708void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
709 size_t tensorIndex,
710 armnn::IOutputSlot* slot)
711{
712 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
713 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
714 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
715
716 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
717
718 // assuming there is only one producer for that tensor
719 if (tensorSlots.outputSlot != nullptr)
720 {
721 throw ParseException(boost::str(
722 boost::format("Another layer has already registered itself as the producer of "
723 "subgraph:%1% tensor:%2% %3%") %
724 subgraphIndex %
725 tensorIndex %
726 CHECK_LOCATION().AsString()));
727 }
728
729 tensorSlots.outputSlot = slot;
730}
731
732void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
733 size_t tensorIndex,
734 armnn::IInputSlot* slot)
735{
736 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
737 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
738 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
739
740 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
741 tensorSlots.inputSlots.push_back(slot);
742}
743
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100744void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
745{
746 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
747
748 // NOTE: By default we presume the custom operator is not supported
749 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
750
751 // Identify custom code defined for custom operator
752 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
753 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
754
755 // Find parser function that correspondes to custom code (if any)
756 auto iterator = m_CustomParserFunctions.find(customCode);
757 if (iterator != m_CustomParserFunctions.end())
758 {
759 customParserFunction = iterator->second;
760 }
761
762 // Run parser function
763 (this->*customParserFunction)(subgraphIndex, operatorIndex);
764}
765
telsoa01c577f2c2018-08-31 09:22:23 +0100766void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
767{
768 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100769
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100770 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
771
772 auto opcodeIndex = operatorPtr->opcode_index;
773 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
774
775 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
776 {
777 // Do not add StandInLayer, throw ParseException instead
778 throw ParseException(
779 boost::str(
780 boost::format("Operator not supported. "
781 "subgraph:%1% operator:%2% "
782 "opcode_index:%3% opcode:%4% / %5% %6%") %
783 subgraphIndex %
784 operatorIndex %
785 opcodeIndex %
786 opcode %
787 tflite::EnumNameBuiltinOperator(opcode) %
788 CHECK_LOCATION().AsString()));
789 }
790
791 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
792 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
793
794 const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
795 const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
796
797 StandInDescriptor descriptor(numInputs, numOutputs);
798 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
799
800 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
801 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
802 for (unsigned int i = 0u; i < numOutputs; ++i)
803 {
804 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i]));
805 }
806
807 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
808 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
809
810 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
811 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100812}
813
telsoa01c577f2c2018-08-31 09:22:23 +0100814void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
815{
816 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
817
818 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
819 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
820
821 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
822
823 Convolution2dDescriptor desc;
824 desc.m_BiasEnabled = false;
825 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
826 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000827 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100828 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
829 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000830
telsoa01c577f2c2018-08-31 09:22:23 +0100831 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
832 CHECK_VALID_SIZE(inputs.size(), 2, 3);
833
834 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
835 CHECK_VALID_SIZE(outputs.size(), 1);
836
837 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
838 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
839
840 // assuming input is NHWC
841 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
842 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
843
844 // assuming the filter is OHWI : Output, H, W, Input
845 // which is essentially the same as NHWC
846 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
847 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
848
Pablo Tellof0bd6832019-04-26 17:58:13 +0100849 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
850 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
851 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
852 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100853
Matteo Martincigh747ef822018-12-18 09:26:39 +0000854 auto filterTensorAndData = CreateConstTensor(inputs[1],
855 filterTensorInfo,
856 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100857 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100858
859 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
860
861 if (inputs.size() == 3)
862 {
863 desc.m_BiasEnabled = true;
864 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000865 auto biasTensorAndData = CreateConstTensor(inputs[2],
866 biasTensorInfo,
867 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100868 layer = m_Network->AddConvolution2dLayer(desc,
869 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100870 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100871 layerName.c_str());
872 }
873 else
874 {
875 layer = m_Network->AddConvolution2dLayer(desc,
876 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100877 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100878 layerName.c_str());
879 }
880
881 BOOST_ASSERT(layer != nullptr);
882
telsoa01c577f2c2018-08-31 09:22:23 +0100883 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000884 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100885
886 // register the input connection slots for the layer, connections are made after all layers have been created
887 // only the tensors for the inputs are relevant, exclude the const tensors
888 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000889 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100890
jimfly01c25411c2018-11-14 17:47:22 +0000891 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100892 // register the output connection slots for the layer, connections are made after all layers have been created
893 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
894 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
895}
896
897void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
898{
899 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
900
901 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
902 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
903
904 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
905
906 DepthwiseConvolution2dDescriptor desc;
907 desc.m_BiasEnabled = false;
908 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
909 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000910 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100911 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100912
913 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
914 CHECK_VALID_SIZE(inputs.size(), 2, 3);
915 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
916 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100917 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
918 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000919
Keith Davis0c2eeac2020-02-11 16:51:50 +0000920 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
921 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
922
telsoa01c577f2c2018-08-31 09:22:23 +0100923 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000924 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100925
Matteo Martincigh747ef822018-12-18 09:26:39 +0000926 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100927 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
928 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000929
930 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100931 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
932 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
933
Matteo Martincigh747ef822018-12-18 09:26:39 +0000934 // Reshape weights as [ H, W, I, M ]
935 filterTensorInfo.SetShape({ filterHeight,
936 filterWidth,
937 inputTensorInfo.GetShape()[3],
938 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
939
Pablo Tellof0bd6832019-04-26 17:58:13 +0100940 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
941 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
942 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
943 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100944
Matteo Martincigh747ef822018-12-18 09:26:39 +0000945 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100946 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100947 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
948
949 if (inputs.size() == 3)
950 {
951 desc.m_BiasEnabled = true;
952 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000953 auto biasTensorAndData = CreateConstTensor(inputs[2],
954 biasTensorInfo,
955 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100956 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
957 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100958 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100959 layerName.c_str());
960 }
961 else
962 {
963 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
964 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100965 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100966 layerName.c_str());
967 }
968 BOOST_ASSERT(layer != nullptr);
969
telsoa01c577f2c2018-08-31 09:22:23 +0100970 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000971 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100972
973 // register the input connection slots for the layer, connections are made after all layers have been created
974 // only the tensors for the inputs are relevant, exclude the const tensors
975 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000976 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100977
jimfly01c25411c2018-11-14 17:47:22 +0000978 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100979 // register the output connection slots for the layer, connections are made after all layers have been created
980 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
981 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
982}
983
Finn Williamsed66d142019-12-06 09:55:55 +0000984void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
985{
986 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
987
988 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
989 CHECK_VALID_SIZE(inputs.size(), 1);
990
991 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
992 CHECK_VALID_SIZE(outputs.size(), 1);
993
994 auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
995
996 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
997 BOOST_ASSERT(layer != nullptr);
998
999 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1000 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1001
1002 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1003 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1004
1005 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1006 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1007}
1008
Keith Davis4cd29a02019-09-09 14:49:20 +01001009void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1010{
1011 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1012
1013 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +01001014 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +01001015
1016 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1017 CHECK_VALID_SIZE(outputs.size(), 1);
1018
1019 armnn::IConnectableLayer* layer = nullptr;
1020 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
1021
Mike Kelly08759e22020-03-02 11:41:31 +00001022 TransposeDescriptor desc;
Keith Davis4cd29a02019-09-09 14:49:20 +01001023
josh minorba424d22019-11-13 10:55:17 -06001024 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +01001025 {
1026 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1027 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -06001028 auto numPermVecElements = permuteTensorInfo.GetNumElements();
1029 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +01001030 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
Mike Kelly08759e22020-03-02 11:41:31 +00001031 PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
Kevin May85d92602019-09-27 17:21:06 +01001032
Mike Kelly08759e22020-03-02 11:41:31 +00001033 desc = TransposeDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +01001034 }
1035
Mike Kelly08759e22020-03-02 11:41:31 +00001036 layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
Keith Davis4cd29a02019-09-09 14:49:20 +01001037
1038 BOOST_ASSERT(layer != nullptr);
1039
1040 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1041 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1042
1043 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1044 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1045
1046 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1047 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1048}
1049
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001050void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1051{
1052 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1053
1054 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1055 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1056
1057 TransposeConvolution2dDescriptor desc;
1058 desc.m_BiasEnabled = false;
1059 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1060 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1061 desc.m_DataLayout = armnn::DataLayout::NHWC;
1062
1063 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001064 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001065
1066 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1067 CHECK_VALID_SIZE(outputs.size(), 1);
1068
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001069 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001070 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1071
1072 // TfLite uses NHWC tensors
1073 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1074 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1075
1076 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1077 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1078
1079 CalcPadding(inputHeight,
1080 filterHeight,
1081 desc.m_StrideY,
1082 1, // DilationY
1083 desc.m_PadTop,
1084 desc.m_PadBottom,
1085 options->padding);
1086
1087 CalcPadding(inputWidth,
1088 filterWidth,
1089 desc.m_StrideX,
1090 1, // DilationX
1091 desc.m_PadLeft,
1092 desc.m_PadRight,
1093 options->padding);
1094
1095 auto filterTensorAndData = CreateConstTensor(inputs[1],
1096 filterTensorInfo,
1097 armnn::Optional<armnn::PermutationVector&>());
1098
1099 armnn::IConnectableLayer* layer = nullptr;
1100 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1101
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001102 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1103 filterTensorAndData.first,
1104 EmptyOptional(),
1105 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001106
1107 BOOST_ASSERT(layer != nullptr);
1108
1109 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1110 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1111
1112 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1113 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001114 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001115
1116 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1117 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1118}
1119
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001120void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1121{
1122 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1123}
1124
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001125void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1126{
1127 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1128
1129 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1130 CHECK_VALID_SIZE(inputs.size(), 3);
1131
1132 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1133 CHECK_VALID_SIZE(outputs.size(), 1);
1134
1135 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1136 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1137
1138 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1139 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1140
1141 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1142 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1143
1144 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1145 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1146
1147 size_t step = 2;
1148 std::vector<std::pair<unsigned int, unsigned int>> crops;
1149 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1150 {
1151 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1152 }
1153
1154 armnn::BatchToSpaceNdDescriptor desc;
1155 desc.m_BlockShape = blockShape;
1156 desc.m_Crops = crops;
1157 desc.m_DataLayout = armnn::DataLayout::NHWC;
1158
1159 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1160
1161 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1162 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1163
1164 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1165
1166 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1167 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1168
1169 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1170 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1171}
1172
Matthew Jackson28c94572019-07-18 10:47:03 +01001173void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1174{
1175 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1176
1177 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1178 CHECK_VALID_SIZE(inputs.size(), 1);
1179
1180 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1181 CHECK_VALID_SIZE(outputs.size(), 1);
1182
1183 L2NormalizationDescriptor desc;
1184 desc.m_DataLayout = armnn::DataLayout::NHWC;
1185 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1186 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1187
1188 BOOST_ASSERT(layer != nullptr);
1189
1190 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1191 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1192
1193 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1194 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1195
1196 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1197 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1198}
1199
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001200void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1201{
1202 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1203}
1204
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001205void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1206{
1207 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1208
1209 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1210 CHECK_VALID_SIZE(inputs.size(), 2);
1211
1212 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1213 CHECK_VALID_SIZE(outputs.size(), 1);
1214
1215 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1216 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1217
1218 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1219 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1220
1221 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1222 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1223
1224 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1225 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1226 {
1227 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1228 }
1229 else
1230 {
1231 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1232 }
1233
1234 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1235 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1236}
1237
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001238void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1239{
1240 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1241
1242 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1243 CHECK_VALID_SIZE(inputs.size(), 2);
1244
1245 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1246 CHECK_VALID_SIZE(outputs.size(), 1);
1247
1248 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1249 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1250
1251 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1252 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1253
1254 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1255 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1256
1257 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1258 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1259 {
1260 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1261 }
1262 else
1263 {
1264 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1265 }
1266
1267 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1268 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1269}
1270
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001271void TfLiteParser::ParsePool(size_t subgraphIndex,
1272 size_t operatorIndex,
1273 PoolingAlgorithm algorithm)
1274{
1275 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1276
1277 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1278 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1279
1280 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1281
1282 std::string layerName;
1283
1284 switch (algorithm)
1285 {
1286 case PoolingAlgorithm::Average:
1287 layerName =
1288 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1289 break;
1290 case PoolingAlgorithm::Max:
1291 layerName =
1292 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1293 break;
1294 default:
1295 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1296 }
1297
1298 Pooling2dDescriptor desc;
1299
1300 desc.m_PoolType = algorithm;
1301 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1302 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1303 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1304 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1305 desc.m_PaddingMethod = PaddingMethod::Exclude;
1306 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001307 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001308
1309 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1310 CHECK_VALID_SIZE(inputs.size(), 1);
1311 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1312
1313 // assuming input is NHWC
1314 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1315 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1316
Pablo Tellof0bd6832019-04-26 17:58:13 +01001317 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1318 desc.m_PadTop, desc.m_PadBottom, options->padding);
1319 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1320 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001321
1322 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1323 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001324
1325 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1326
1327 BOOST_ASSERT(layer != nullptr);
1328
jimfly01c25411c2018-11-14 17:47:22 +00001329 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1330 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001331
1332 // register the input connection slots for the layer, connections are made after all layers have been created
1333 // only the tensors for the inputs are relevant, exclude the const tensors
1334 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001335 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001336
jimfly01c25411c2018-11-14 17:47:22 +00001337 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001338 // register the output connection slots for the layer, connections are made after all layers have been created
1339 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1340 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1341}
1342
josh minorba424d22019-11-13 10:55:17 -06001343void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1344{
1345 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1346
1347 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1348 CHECK_VALID_SIZE(inputs.size(), 3);
1349 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1350 CHECK_VALID_SIZE(outputs.size(), 1);
1351
1352 SliceDescriptor desc;
1353
1354 // set begin tensor info for slice descriptor
1355 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1356 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1357
1358 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1359 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1360
1361 // set size tensor info for slice descriptor
1362 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1363 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1364
1365 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1366 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1367 desc = SliceDescriptor(begin, size);
1368
1369 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1370 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1371
1372 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1373 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1374
1375 // register the input connection slots for the layer, connections are made after all layers have been created
1376 // only the tensors for the inputs are relevant, exclude the const tensors
1377 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1378 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1379
1380 // register the output connection slots for the layer, connections are made after all layers have been created
1381 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1382 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1383}
1384
telsoa01c577f2c2018-08-31 09:22:23 +01001385void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1386{
1387 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1388 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1389 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1390
1391 SoftmaxDescriptor desc;
1392 desc.m_Beta = options->beta;
1393
1394 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1395 CHECK_VALID_SIZE(inputs.size(), 1);
1396 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1397 CHECK_VALID_SIZE(outputs.size(), 1);
1398
1399 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1400 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1401
1402 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1403 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1404
1405 // register the input connection slots for the layer, connections are made after all layers have been created
1406 // only the tensors for the inputs are relevant, exclude the const tensors
1407 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1408 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1409
1410 // register the output connection slots for the layer, connections are made after all layers have been created
1411 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1412 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1413}
1414
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001415void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1416{
1417 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1418
1419 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1420 CHECK_VALID_SIZE(inputs.size(), 3);
1421
1422 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1423 CHECK_VALID_SIZE(outputs.size(), 1);
1424
1425 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1426 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1427
1428 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1429 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1430
1431 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1432 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1433
1434 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1435 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1436
1437 size_t step = 2;
1438 std::vector<std::pair<unsigned int, unsigned int>> padList;
1439 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1440 {
1441 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1442 }
1443
1444 armnn::SpaceToBatchNdDescriptor desc;
1445 desc.m_BlockShape = blockShape;
1446 desc.m_PadList = padList;
1447 desc.m_DataLayout = armnn::DataLayout::NHWC;
1448
1449 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1450
1451 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1452 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1453
1454 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1455
1456 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1457 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1458
1459 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1460 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1461}
1462
telsoa01c577f2c2018-08-31 09:22:23 +01001463armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1464 const armnn::TensorInfo & inputTensorInfo)
1465{
1466 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1467 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1468 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1469
1470 if (inputTensorInfo.GetNumDimensions() > 4)
1471 {
1472 std::stringstream ss;
1473 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1474 << " shape:" << inputTensorInfo.GetShape() << " "
1475 << CHECK_LOCATION().AsString();
1476 throw ParseException(ss.str());
1477 }
1478
1479 if (squeezeDims.empty())
1480 {
1481 squeezeDims.assign(dimensionSequence,
1482 dimensionSequence+inputTensorInfo.GetNumDimensions());
1483 }
1484
1485 std::vector<uint32_t> outputDims;
1486 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1487 {
1488 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1489 auto currentDimension = inputTensorInfo.GetShape()[i];
1490 if (skipSqueeze || currentDimension != 1)
1491 {
1492 outputDims.push_back(currentDimension);
1493 }
1494 }
1495
1496 if (outputDims.size() > 4)
1497 {
1498 std::stringstream ss;
1499 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1500 << " shape:" << inputTensorInfo.GetShape() << " "
1501 << CHECK_LOCATION().AsString();
1502 throw ParseException(ss.str());
1503 }
1504
1505 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1506 outputDims.data());
1507
1508 // we need to preserve the tensor type and the quantization data as well
1509 TensorInfo outTensorInfo = inputTensorInfo;
1510 outTensorInfo.SetShape(outShape);
1511
1512 return outTensorInfo;
1513}
1514
1515void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1516{
1517 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1518
1519 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1520 CHECK_VALID_SIZE(inputs.size(), 1);
1521
1522 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1523 CHECK_VALID_SIZE(outputs.size(), 1);
1524
1525 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1526 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1527
1528 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1529 armnn::TensorInfo outputTensorInfo =
1530 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1531 inputTensorInfo);
1532
1533 ReshapeDescriptor reshapeDesc;
1534 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1535
1536 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1537 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1538 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1539
1540 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1541 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1542
1543 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1544 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1545}
1546
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001547void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1548{
1549 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1550
1551 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1552 CHECK_VALID_SIZE(inputs.size(), 4);
1553
1554 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1555 CHECK_VALID_SIZE(outputs.size(), 1);
1556
1557 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1558 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1559
1560 StridedSliceDescriptor desc;
1561 desc.m_BeginMask = options->begin_mask;
1562 desc.m_EllipsisMask = options->ellipsis_mask;
1563 desc.m_EndMask = options->end_mask;
1564 desc.m_NewAxisMask = options->new_axis_mask;
1565 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1566 desc.m_DataLayout = armnn::DataLayout::NHWC;
1567
1568 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1569 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1570
1571 std::vector<int> begin(beginTensorInfo.GetNumElements());
1572 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1573
1574 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1575 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1576
1577 std::vector<int> end(endTensorInfo.GetNumElements());
1578 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1579
1580 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1581 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1582
1583 std::vector<int> stride(strideTensorInfo.GetNumElements());
1584 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1585
1586 desc.m_Begin = begin;
1587 desc.m_End = end;
1588 desc.m_Stride = stride;
1589
1590 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1591 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1592
1593 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1594 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1595
1596 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1597 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1598
1599 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1600 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1601}
1602
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001603void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1604{
1605 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1606
1607 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1608 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1609
1610 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1611 CHECK_VALID_SIZE(inputs.size(), 2);
1612
1613 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1614 CHECK_VALID_SIZE(outputs.size(), 1);
1615
1616 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1617 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1618
1619 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1620 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1621
1622 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1623 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1624
1625 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1626 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1627 {
1628 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1629 }
1630 else
1631 {
1632 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1633 }
1634
1635 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1636
1637 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1638 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1639}
1640
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001641void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1642{
1643 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1644
1645 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1646 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1647
1648 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1649 CHECK_VALID_SIZE(inputs.size(), 2);
1650
1651 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1652 CHECK_VALID_SIZE(outputs.size(), 1);
1653
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001654 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1655 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1656
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001657 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1658 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1659
1660 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1661 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1662
1663 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001664 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1665 {
1666 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1667 }
1668 else
1669 {
1670 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1671 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001672
1673 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1674
1675 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1676 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1677}
1678
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001679void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1680{
1681 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1682
1683 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1684 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1685
1686 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1687 CHECK_VALID_SIZE(inputs.size(), 2);
1688
1689 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1690 CHECK_VALID_SIZE(outputs.size(), 1);
1691
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001692 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1693 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1694
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001695 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1696 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1697
1698 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1699 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1700
1701 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001702 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1703 {
1704 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1705 }
1706 else
1707 {
1708 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1709 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001710
1711 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1712
1713 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1714 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1715}
1716
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001717void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1718{
1719 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1720
1721 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1722
1723 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1724 CHECK_VALID_SIZE(outputs.size(), 1);
1725
1726 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1727 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1728
1729 armnn::MeanDescriptor desc;
1730 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1731 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1732 desc.m_Axis = axis;
1733
1734 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1735 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1736
1737 desc.m_KeepDims =
1738 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1739 true : false;
1740
1741 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1742 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1743
1744 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1745
1746 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1747 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1748
1749 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1750 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1751}
1752
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001753void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1754{
1755 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1756
1757 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1758
1759 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1760 CHECK_VALID_SIZE(outputs.size(), 1);
1761
1762 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1763 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1764
1765 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1766 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1767
1768 size_t step = 2;
1769 armnn::PadDescriptor desc;
1770 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1771 {
1772 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1773 }
1774
1775 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1776 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1777
1778 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1779 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1780
1781 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1782 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1783
1784 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1785 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1786}
1787
Sadik Armagan66dedc72019-12-10 16:32:07 +00001788void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
1789{
1790 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1791
1792 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1793 CHECK_VALID_SIZE(inputs.size(), 1);
1794
1795 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1796 CHECK_VALID_SIZE(outputs.size(), 1);
1797
1798 auto layerName = boost::str(boost::format("Quantize:%1%:%2%") % subgraphIndex % operatorIndex);
1799
1800 IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
1801 BOOST_ASSERT(layer != nullptr);
1802
1803 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1804 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1805
1806 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1807 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1808
1809 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1810 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1811}
Finn Williamsc42c3842019-01-22 14:18:11 +00001812
Sadik Armagan58f39192018-09-17 14:14:39 +01001813void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1814{
Finn Williamsc42c3842019-01-22 14:18:11 +00001815 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001816}
1817
1818void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1819{
Finn Williamsc42c3842019-01-22 14:18:11 +00001820 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1821}
Sadik Armagan58f39192018-09-17 14:14:39 +01001822
Finn Williamsc42c3842019-01-22 14:18:11 +00001823void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1824{
1825 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1826}
1827
Nina Drozd99851762019-04-09 09:37:38 +01001828void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1829{
1830 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1831}
1832
Finn Williamsc42c3842019-01-22 14:18:11 +00001833
1834void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1835{
1836 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001837 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
Jan Eilers8eb25602020-03-09 12:13:48 +00001838 IgnoreUnused(operatorPtr);
Sadik Armagan58f39192018-09-17 14:14:39 +01001839
1840 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1841 CHECK_VALID_SIZE(inputs.size(), 1);
1842
1843 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1844 CHECK_VALID_SIZE(outputs.size(), 1);
1845
Finn Williamsc42c3842019-01-22 14:18:11 +00001846 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001847 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001848 activationDesc.m_Function = activationType;
1849
1850 switch (activationType)
1851 {
1852 case ActivationFunction::ReLu:
1853 {
1854 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1855 break;
1856 }
1857 case ActivationFunction::BoundedReLu:
1858 {
1859 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1860 activationDesc.m_A = 6.0f;
1861 activationDesc.m_B = 0.0f;
1862 break;
1863 }
1864 case ActivationFunction::Sigmoid:
1865 {
1866 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1867 break;
1868 }
Nina Drozd99851762019-04-09 09:37:38 +01001869 case ActivationFunction::TanH:
1870 {
1871 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1872 activationDesc.m_A = 1.0f;
1873 activationDesc.m_B = 1.0f;
1874 break;
1875 }
Finn Williamsc42c3842019-01-22 14:18:11 +00001876 default:
1877 {
1878 throw ParseException(
1879 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1880 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1881 }
1882 }
1883
1884 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001885
1886 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1887 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1888
1889 // register the input connection slots for the layer, connections are made after all layers have been created
1890 // only the tensors for the inputs are relevant, exclude the const tensors
1891 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1892 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1893
1894 // register the output connection slots for the layer, connections are made after all layers have been created
1895 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1896 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1897}
Sadikb94967b2018-09-19 15:30:00 +01001898armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1899 const std::vector<int32_t> & targetDimsIn)
1900{
1901 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1902 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1903
1904 if (stretchDim != targetDimsIn.end())
1905 {
1906 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1907 {
1908 throw ParseException(
1909 boost::str(
1910 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1911 }
1912
1913 auto targetNumElements =
1914 boost::numeric_cast<unsigned int>(
1915 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1916
1917 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1918 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1919 }
1920
1921 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1922
1923 TensorInfo reshapeInfo = inputTensorInfo;
1924 reshapeInfo.SetShape(outputShape);
1925
1926 return reshapeInfo;
1927}
1928
1929void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1930{
1931 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1932
1933 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001934
1935 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1936 CHECK_VALID_SIZE(outputs.size(), 1);
1937
1938 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1939 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1940
1941 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001942 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
Derek Lambertic9e52792020-03-11 11:42:26 +00001943
1944 std::vector<int32_t> targetShape;
1945 if (inputs.size() > 1 && inputs[1] != nullptr)
1946 {
1947 if (options != nullptr)
1948 {
1949 ARMNN_THROW_PARSE_EXCEPTION("Target shape defined in reshape parameters and input tensor. "
1950 "Only one method expected");
1951 }
1952
1953 if (inputs[1]->is_variable)
1954 {
1955 ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
1956 }
1957
1958 if (inputs[1]->shape.size() != 1)
1959 {
1960 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
1961 }
1962
1963 if (inputs[1]->type != tflite::TensorType_INT32)
1964 {
1965 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
1966 }
1967
1968 auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1969 auto vals = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
1970 for (int i=0; i < inputs[1]->shape[0]; i++)
1971 {
1972 targetShape.push_back(vals[i]);
1973 }
1974 }
1975 else
1976 {
1977 if (options == nullptr)
1978 {
1979 ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
1980 "At least one method required");
1981 }
1982
1983 targetShape = options->new_shape;
1984 }
1985
kevmay0171972a82018-12-17 14:28:03 +00001986 armnn::TensorInfo reshapeOutputTensorInfo =
Derek Lambertic9e52792020-03-11 11:42:26 +00001987 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
Sadikb94967b2018-09-19 15:30:00 +01001988
kevmay0171972a82018-12-17 14:28:03 +00001989 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001990 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1991 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001992 {
1993 std::stringstream ss;
1994 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001995 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001996 << " does not equal output shape "
1997 << actualOutputTensorInfo.GetShape()
1998 << ": "
1999 << CHECK_LOCATION().AsString();
2000 throw ParseException(ss.str());
2001 }
2002
Sadikb94967b2018-09-19 15:30:00 +01002003 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00002004 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01002005
2006 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
2007 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00002008 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01002009
2010 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2011 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2012
2013 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2014 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2015}
2016
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002017void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2018{
Sadik Armagana3b31f02019-12-05 09:08:53 +00002019 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2020}
2021
2022void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2023{
2024 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2025}
2026
2027void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2028{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002029 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2030
2031 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2032 CHECK_VALID_SIZE(inputs.size(), 2);
2033
2034 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2035 CHECK_VALID_SIZE(outputs.size(), 1);
2036
2037 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2038
2039 // Data for the parsed tensor args (size) must be stored locally.
2040 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2041
2042 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2043 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2044
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002045 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002046 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002047 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002048 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2049 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002050
Sadik Armagana3b31f02019-12-05 09:08:53 +00002051 auto layerName = str(boost::format("Resize:"));
2052
2053 switch (resizeMethod)
2054 {
2055 case ResizeMethod::Bilinear:
2056 {
2057 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
Sang-Hoon Park820eb142020-01-08 10:25:24 +00002058
2059 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2060 const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2061
2062 desc.m_BilinearAlignCorners = options->align_corners;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002063 break;
2064 }
2065 case ResizeMethod::NearestNeighbor:
2066 {
2067 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
2068 break;
2069 }
2070 default:
2071 {
2072 throw ParseException(
2073 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
2074 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
2075 }
2076 }
2077
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002078 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002079
2080 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2081 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2082
2083 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2084 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2085
2086 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2087 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2088}
2089
Sadik Armagan479045b2018-10-01 11:51:37 +01002090void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2091{
2092 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2093
2094 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2095 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2096
2097 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2098
2099 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2100 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2101 CHECK_VALID_SIZE(outputs.size(), 1);
2102
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002103 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2104 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01002105
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002106 const unsigned int concatDimInput = static_cast<unsigned int>(
2107 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01002108
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002109 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2110 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01002111
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002112 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01002113
2114 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2115 {
2116 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2117
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002118 // This set up concatDescriptor view origin
2119 armnnUtils::ProcessConcatInputTensorInfo(
2120 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01002121 }
2122
2123 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Jim Flynn906f9462019-05-10 13:55:21 +01002124 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Sadik Armagan479045b2018-10-01 11:51:37 +01002125
2126 BOOST_ASSERT(layer != nullptr);
2127
2128 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2129 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01002130
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002131 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01002132
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002133 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01002134
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002135 // add fused activation layer
2136 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01002137
Sadik Armagan479045b2018-10-01 11:51:37 +01002138 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2139 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2140}
2141
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002142void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2143{
2144 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2145
2146 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2147 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2148
2149 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2150
2151 FullyConnectedDescriptor desc;
2152 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002153 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002154
2155 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2156 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2157 CHECK_VALID_SIZE(outputs.size(), 1);
2158
2159 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2160
2161 // Fully Connected Layer accepts two dimensional weights input
2162 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2163 if (weightsDimension != 2)
2164 {
2165 throw ParseException(
2166 boost::str(
2167 boost::format(
2168 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2169 "Node %2%")
2170 % weightsDimension
2171 % CHECK_LOCATION().AsString()));
2172 }
2173
Matteo Martincigh747ef822018-12-18 09:26:39 +00002174 auto filterTensorAndData = CreateConstTensor(inputs[1],
2175 filterTensorInfo,
2176 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002177 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002178 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2179
2180 if (inputs.size() == 3)
2181 {
2182 desc.m_BiasEnabled = true;
2183 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002184 auto biasTensorAndData = CreateConstTensor(inputs[2],
2185 biasTensorInfo,
2186 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002187 layer = m_Network->AddFullyConnectedLayer(desc,
2188 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002189 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002190 layerName.c_str());
2191 }
2192 else
2193 {
2194 layer = m_Network->AddFullyConnectedLayer(desc,
2195 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002196 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002197 layerName.c_str());
2198 }
2199 BOOST_ASSERT(layer != nullptr);
2200
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002201 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2202
2203 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2204
2205 if (inputTensorInfo.GetNumDimensions() > 2)
2206 {
2207 // Add reshape to flatten to 2D [batch_size, input_size],
2208 // where "input_size" corresponds to the number of inputs to the layer,
2209 // matching the second dimension of weights,
2210 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2211 std::vector<unsigned int> reshapedDimensions(2);
2212 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2213 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2214
2215 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2216 {
2217 throw ParseException(
2218 boost::str(
2219 boost::format(
2220 "Failed to deduce input tensor shape from filter size %1%")
2221 % reshapedDimensions[1]
2222 % CHECK_LOCATION().AsString()));
2223 }
2224
2225 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2226 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2227
2228 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2229 armnn::ReshapeDescriptor desc;
2230 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2231 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2232
2233 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2234 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2235
2236 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2237 }
2238 else
2239 {
2240 // register the input connection slot for the layer
2241 // only the tensors for the inputs are relevant, exclude the const tensors
2242 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2243 }
2244
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002245 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2246 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2247
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002248 // we need to add the activation layer and fortunately we don't need to care about the data layout
2249 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2250 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002251
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002252 // register the output connection slots for the layer, connections are made after all layers have been created
2253 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2254 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2255}
2256
keidav011b3e2ea2019-02-21 10:07:37 +00002257void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2258{
2259 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2260
2261 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2262
2263 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2264 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2265 CHECK_VALID_SIZE(outputs.size(), 4);
2266
2267 // Obtain custom options from flexbuffers
2268 auto custom_options = operatorPtr->custom_options;
2269 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2270
2271 // Obtain descriptor information from tf lite
2272 DetectionPostProcessDescriptor desc;
2273 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2274 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2275 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2276 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2277 desc.m_NumClasses = m["num_classes"].AsUInt32();
2278 desc.m_ScaleH = m["h_scale"].AsFloat();
2279 desc.m_ScaleW = m["w_scale"].AsFloat();
2280 desc.m_ScaleX = m["x_scale"].AsFloat();
2281 desc.m_ScaleY = m["y_scale"].AsFloat();
2282
keidav0107d58c72019-02-26 11:57:39 +00002283 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002284 {
keidav0107d58c72019-02-26 11:57:39 +00002285 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002286 }
2287 if (!(m["detections_per_class"].IsNull()))
2288 {
2289 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2290 }
2291
2292 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2293 {
2294 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2295 "must be positive and less than or equal to 1.");
2296 }
2297
2298 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2299 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2300 armnn::Optional<armnn::PermutationVector&>());
2301
2302 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2303 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2304 layerName.c_str());
2305
2306 BOOST_ASSERT(layer != nullptr);
2307
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002308 // The model does not specify the output shapes.
2309 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2310 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2311 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2312 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2313 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2314 m_OverridenOutputShapes.push_back({ 1 });
2315
keidav011b3e2ea2019-02-21 10:07:37 +00002316 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2317 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002318 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002319 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2320 }
2321
2322 // Register the input connection slots for the layer, connections are made after all layers have been created
2323 // only the tensors for the inputs are relevant, exclude the const tensors
2324 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2325 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2326
2327 // Register the output connection slots for the layer, connections are made after all layers have been created
2328 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2329 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2330 outputTensorIndexes[1],
2331 outputTensorIndexes[2],
2332 outputTensorIndexes[3]});
2333}
2334
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002335/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2336void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2337{
2338 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2339
2340 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2341 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2342 CHECK_VALID_SIZE(outputs.size(), 1);
2343
2344 if (inputs.size() < 1)
2345 {
2346 throw ParseException("Pack must have at least one input.");
2347 }
2348
2349 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2350 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2351
2352 StackDescriptor desc;
2353 desc.m_Axis = static_cast<uint32_t>(options->axis);
2354 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2355
2356 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2357 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2358 desc.m_InputShape = inputTensorInfo.GetShape();
2359
2360 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2361 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2362
2363 BOOST_ASSERT(layer != nullptr);
2364
2365 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2366 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2367
2368 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2369 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2370
2371 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2372 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2373}
2374
Nina Drozd200e3802019-04-15 09:47:39 +01002375void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2376{
2377 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2378
2379 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2380 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2381
2382 // This unpackAxis indicates the axis to unpack
2383 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2384
2385 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2386 CHECK_VALID_SIZE(inputs.size(), 1);
2387
2388 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002389
2390 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2391 {
2392 throw ParseException(
2393 boost::str(
2394 boost::format(
2395 "The unpack axis: %1% cannot be greater than or equal to "
2396 "the number of input dimension %2% %3%")
2397 % unpackAxis
2398 % inputTensorInfo.GetNumDimensions()
2399 % CHECK_LOCATION().AsString()));
2400 }
2401
Nina Drozd200e3802019-04-15 09:47:39 +01002402 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2403 // If num is not defined, automatically infer from the length of the dimension axis.
2404 if(unpackNum == 0)
2405 {
2406 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2407 }
2408
2409 // If unpack number cannot be inferred and is still zero, throw ParseException.
2410 if(unpackNum == 0)
2411 {
2412 throw ParseException("Number to unpack must greater than zero.");
2413 }
2414
2415 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2416 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2417
2418 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2419 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2420
2421 // Add current input shape to unpackDimSizes
2422 for (unsigned int i = 0; i < inputDimSize; ++i)
2423 {
2424 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2425 }
2426
2427 if (unpackDimSizes[unpackAxis] != unpackNum)
2428 {
2429 throw ParseException("Number to unpack must be the same as length of the dimension to "
2430 "unpack along.");
2431 }
2432
2433 unpackDimSizes[unpackAxis] /= unpackNum;
2434
2435 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2436 for (unsigned int j = 0; j < unpackNum; ++j)
2437 {
2438 // Set the size of the views.
2439 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2440 {
2441 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2442 }
2443 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2444 }
2445
2446 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2447 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2448
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002449 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2450 unpackDimSizes.data());
2451
Nina Drozd200e3802019-04-15 09:47:39 +01002452 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2453 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2454
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002455 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2456 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2457 {
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002458 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002459 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2460 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002461 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002462 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2463
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002464 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2465 outputTensorInfo.GetDataType(),
2466 outputTensorInfo.GetQuantizationScale(),
2467 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002468 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2469
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002470 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002471
2472 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2473 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2474 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2475 }
Nina Drozd200e3802019-04-15 09:47:39 +01002476}
2477
Nina Drozd0324f482019-04-08 10:52:10 +01002478void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2479{
2480 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2481
2482 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2483 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2484
2485 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2486
Nina Drozd200e3802019-04-15 09:47:39 +01002487 // If number of splits cannot be inferred and is zero, throw ParseException.
2488 if(numSplits == 0)
2489 {
2490 throw ParseException("Number to splits must greater than zero.");
2491 }
2492
Nina Drozd0324f482019-04-08 10:52:10 +01002493 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2494 CHECK_VALID_SIZE(inputs.size(), 2);
2495 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2496 CHECK_VALID_SIZE(outputs.size(), numSplits);
2497
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002498 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2499 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002500
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002501 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2502 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2503 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2504
2505 BOOST_ASSERT(axisTensorInfo.GetNumElements() == 1);
2506 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002507
Nina Drozd0324f482019-04-08 10:52:10 +01002508 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002509 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002510 {
2511 throw ParseException(
2512 boost::str(
2513 boost::format(
2514 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002515 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002516 % inputTensorInfo.GetNumDimensions()
2517 % MaxNumOfTensorDimensions
2518 % CHECK_LOCATION().AsString()));
2519 }
2520
2521 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2522
2523 // Add current input shape to splitterDimSizes
2524 for (unsigned int i = 0; i < inputDimSize; ++i)
2525 {
2526 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2527 }
2528
2529 if (splitterDimSizes[splitDim] % numSplits != 0)
2530 {
2531 throw ParseException("Number of splits must evenly divide the dimension");
2532 }
2533 splitterDimSizes[splitDim] /= numSplits;
2534
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002535 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002536 for (unsigned int j = 0; j < numSplits; ++j)
2537 {
2538 // Set the size of the views.
2539 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2540 {
2541 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2542 }
2543 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2544 }
2545
2546 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2547 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2548
2549 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002550 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002551
Nina Drozd0324f482019-04-08 10:52:10 +01002552 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2553 {
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002554 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k]);
2555 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002556 }
2557
2558 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2559 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2560}
2561
Sadik Armagan58f39192018-09-17 14:14:39 +01002562armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2563 unsigned int outputSlot,
2564 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002565{
2566 ActivationDescriptor activationDesc;
2567 std::string layerName = prevLayer->GetName();
2568
2569 switch(activationType)
2570 {
2571 case tflite::ActivationFunctionType_NONE:
2572 {
2573 // this is a no-op: return previous layer
2574 return prevLayer;
2575 }
2576 case tflite::ActivationFunctionType_RELU:
2577 {
2578 activationDesc.m_Function = ActivationFunction::ReLu;
2579 layerName += ":RELU";
2580 break;
2581 }
2582 case tflite::ActivationFunctionType_RELU6:
2583 {
2584 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2585 activationDesc.m_A = 6.0f;
2586 activationDesc.m_B = 0.0f;
2587 layerName += ":RELU6";
2588 break;
2589 }
2590 case tflite::ActivationFunctionType_TANH:
2591 {
2592 activationDesc.m_Function = ActivationFunction::TanH;
2593 activationDesc.m_A = 1.0f;
2594 activationDesc.m_B = 1.0f;
2595 layerName += ":TANH";
2596 break;
2597 }
2598
2599 // I only put these here as a reminder what others we could support
2600 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2601 case tflite::ActivationFunctionType_SIGN_BIT:
2602 default:
2603 {
2604 throw ParseException(
2605 boost::str(
2606 boost::format("TfLite parser doesn't suppport fused activation: "
2607 "%1%/%2% %3% ") %
2608 activationType %
2609 tflite::EnumNameActivationFunctionType(activationType) %
2610 CHECK_LOCATION().AsString()));
2611
2612 }
2613 }
2614
2615 IConnectableLayer* activationLayer =
2616 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2617
2618 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2619 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2620 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2621 return activationLayer;
2622}
2623
2624TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2625{
2626 if (fileName == nullptr)
2627 {
2628 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2629 CHECK_LOCATION().AsString()));
2630 }
2631 boost::system::error_code errorCode;
2632 boost::filesystem::path pathToFile(fileName);
2633 if (!boost::filesystem::exists(pathToFile, errorCode))
2634 {
Derek Lambertic9e52792020-03-11 11:42:26 +00002635 std::string locationString = CHECK_LOCATION().AsString();
2636 std::string msg = boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2637 fileName %
2638 errorCode %
2639 locationString);
2640 throw FileNotFoundException(msg);
telsoa01c577f2c2018-08-31 09:22:23 +01002641 }
2642 std::ifstream file(fileName, std::ios::binary);
2643 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2644 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2645 fileContent.size());
2646}
2647
2648TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2649{
2650 if (binaryContent == nullptr)
2651 {
2652 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2653 CHECK_LOCATION().AsString()));
2654 }
2655 flatbuffers::Verifier verifier(binaryContent, len);
2656 if (verifier.VerifyBuffer<tflite::Model>() == false)
2657 {
2658 throw ParseException(
2659 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2660 "flatbuffers format. size:%1% %2%") %
2661 len %
2662 CHECK_LOCATION().AsString()));
2663 }
2664 return tflite::UnPackModel(binaryContent);
2665}
2666
2667TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
2668 size_t subgraphIndex,
2669 size_t operatorIndex)
2670{
2671 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2672
Derek Lambertiff05cc52019-04-26 13:05:17 +01002673 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2674 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002675
2676 size_t inputCount = operatorPtr->inputs.size();
2677 TensorRawPtrVector result(inputCount);
2678 for (size_t i=0; i<inputCount; ++i)
2679 {
2680 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002681 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002682 }
2683 return result;
2684}
2685
2686TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
2687 size_t subgraphIndex,
2688 size_t operatorIndex)
2689{
2690 CHECK_MODEL(model, subgraphIndex, operatorIndex);
2691
Derek Lambertiff05cc52019-04-26 13:05:17 +01002692 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2693 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002694
2695 size_t outputCount = operatorPtr->outputs.size();
2696 TensorRawPtrVector result(outputCount);
2697 for (size_t i=0; i<outputCount; ++i)
2698 {
2699 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2700 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002701 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01002702 }
2703 return result;
2704}
2705
2706TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
2707 size_t subgraphIndex)
2708{
2709 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002710 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002711
Derek Lambertiff05cc52019-04-26 13:05:17 +01002712 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002713 TensorIdRawPtrVector result(inputCount);
2714 for (size_t i=0; i<inputCount; ++i)
2715 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002716 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01002717 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002718 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002719 }
2720 return result;
2721}
2722
2723TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
2724 size_t subgraphIndex)
2725{
2726 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002727 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002728
Derek Lambertiff05cc52019-04-26 13:05:17 +01002729 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01002730 TensorIdRawPtrVector result(outputCount);
2731 for (size_t i=0; i<outputCount; ++i)
2732 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002733 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
2734 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01002735 }
2736 return result;
2737}
2738
2739std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
2740 size_t subgraphIndex,
2741 size_t operatorIndex)
2742{
2743 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002744 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2745 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002746 return operatorPtr->inputs;
2747}
2748
2749std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
2750 size_t subgraphIndex,
2751 size_t operatorIndex)
2752{
2753 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01002754 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2755 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01002756 return operatorPtr->outputs;
2757}
2758
2759void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
2760 size_t operatorIndex,
2761 IConnectableLayer* layer,
2762 const std::vector<unsigned int>& tensorIndexes)
2763{
2764 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2765 BOOST_ASSERT(layer != nullptr);
2766 if (tensorIndexes.size() != layer->GetNumInputSlots())
2767 {
2768 throw ParseException(
2769 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
2770 " for subgraph:%3% operator index:%4% %5%") %
2771 tensorIndexes.size() %
2772 layer->GetNumInputSlots() %
2773 subgraphIndex %
2774 operatorIndex %
2775 CHECK_LOCATION().AsString()));
2776 }
2777
2778 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
2779 {
2780 unsigned int tensorIndex = tensorIndexes[slotIndex];
2781 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
2782 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
2783 }
2784}
2785
2786void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
2787 size_t operatorIndex,
2788 IConnectableLayer* layer,
2789 const std::vector<unsigned int>& tensorIndexes)
2790{
2791 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2792 BOOST_ASSERT(layer != nullptr);
2793 if (tensorIndexes.size() != layer->GetNumOutputSlots())
2794 {
2795 throw ParseException(
2796 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
2797 " for subgraph:%3% operator index:%4% %5%") %
2798 tensorIndexes.size() %
2799 layer->GetNumOutputSlots() %
2800 subgraphIndex %
2801 operatorIndex %
2802 CHECK_LOCATION().AsString()));
2803 }
2804
2805 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
2806 {
2807 unsigned int tensorIndex = tensorIndexes[slotIndex];
2808 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
2809 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
2810 }
2811}
2812
2813void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
2814{
2815 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2816
2817 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
2818 for (auto const & tensorIdAndPtr : inputs)
2819 {
2820 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2821 IConnectableLayer* layer =
2822 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2823
2824 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
2825 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2826
2827 RegisterOutputSlots(subgraphIndex,
2828 VIRTUAL_OPERATOR_ID,
2829 layer,
2830 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2831 }
2832}
2833
2834void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
2835{
2836 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2837
2838 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
2839 for (auto const & tensorIdAndPtr : outputs)
2840 {
2841 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
2842 IConnectableLayer* layer =
2843 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
2844
2845 RegisterInputSlots(subgraphIndex,
2846 VIRTUAL_OPERATOR_ID,
2847 layer,
2848 { static_cast<uint32_t>(tensorIdAndPtr.first) });
2849 }
2850}
2851
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002852void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
2853{
2854 CHECK_SUBGRAPH(m_Model, subgraphIndex);
2855
Derek Lambertiff05cc52019-04-26 13:05:17 +01002856 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002857 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
2858 {
2859 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
2860 {
2861 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
2862 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
2863 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01002864 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02002865 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
2866 auto tensorAndData = CreateConstTensor(tensorPtr,
2867 tensorInfo,
2868 armnn::Optional<armnn::PermutationVector&>());
2869
2870 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
2871 IConnectableLayer *layer =
2872 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
2873
2874 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2875 RegisterOutputSlots(subgraphIndex,
2876 VIRTUAL_OPERATOR_ID,
2877 layer,
2878 { tensorIndex });
2879
2880 }
2881 }
2882 }
2883}
2884
telsoa01c577f2c2018-08-31 09:22:23 +01002885// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2886TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
2887{
2888 CHECK_BUFFER(model, bufferIndex);
2889 return model->buffers[bufferIndex].get();
2890}
2891
Matteo Martincigh747ef822018-12-18 09:26:39 +00002892template<typename T>
2893std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2894TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
2895 TfLiteParser::TensorRawPtr tensorPtr,
2896 armnn::TensorInfo& tensorInfo,
2897 armnn::Optional<armnn::PermutationVector&> permutationVector)
2898{
2899 auto constData = CreateConstTensorImpl<T>(bufferPtr,
2900 tensorPtr,
2901 tensorInfo,
2902 permutationVector);
2903 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
2904 return std::make_pair(constData.first, std::move(storage));
2905}
2906
telsoa01c577f2c2018-08-31 09:22:23 +01002907std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
2908TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00002909 armnn::TensorInfo& tensorInfo,
2910 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01002911{
2912 CHECK_TENSOR_PTR(tensorPtr);
2913 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
2914 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
2915
2916 switch (tensorInfo.GetDataType())
2917 {
2918 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002919 return CreateConstTensorAndStoreData<float>(bufferPtr,
2920 tensorPtr,
2921 tensorInfo,
2922 permutationVector);
Derek Lambertif90c56d2020-01-10 17:14:08 +00002923 case armnn::DataType::QAsymmU8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002924 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
2925 tensorPtr,
2926 tensorInfo,
2927 permutationVector);
Keith Davisd305e1a2020-01-22 11:57:54 +00002928 case armnn::DataType::QSymmS8:
2929 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
2930 tensorPtr,
2931 tensorInfo,
2932 permutationVector);
Keith Davis67e6c542020-02-19 10:08:33 +00002933 case armnn::DataType::QAsymmS8:
2934 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
2935 tensorPtr,
2936 tensorInfo,
2937 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002938 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00002939 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
2940 tensorPtr,
2941 tensorInfo,
2942 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01002943 default:
2944 {
2945 std::stringstream errString;
2946 errString << "Unexpected datatype when creating const tensor: "
2947 << armnn::GetDataTypeName(tensorInfo.GetDataType())
2948 << " shape:" << tensorInfo.GetShape()
2949 << CHECK_LOCATION().AsString();
2950 throw ParseException(errString.str());
2951 }
2952 }
2953}
2954
2955BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
2956 const std::string& name) const
2957{
2958 CHECK_SUBGRAPH(m_Model, subgraphId);
2959 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
2960 for (auto const & input : inputs)
2961 {
2962 if (input.second->name == name)
2963 {
2964 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
2965 return std::make_pair(bindingId, ToTensorInfo(input.second));
2966 }
2967 }
2968
2969 std::stringstream bindings;
2970 for (auto const & input : inputs)
2971 {
2972 bindings << "'" << input.second->name << "' ";
2973 }
2974
2975 throw ParseException(
2976 boost::str(
2977 boost::format("No input binding found for subgraph:%1% and name:%2%. "
2978 "Possible inputs are: [%3%] %4%") %
2979 subgraphId %
2980 name %
2981 bindings.str() %
2982 CHECK_LOCATION().AsString()));
2983}
2984
2985BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
2986 const std::string& name) const
2987{
2988 CHECK_SUBGRAPH(m_Model, subgraphId);
2989 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002990 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01002991 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002992 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01002993 if (output.second->name == name)
2994 {
2995 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002996 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
2997 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
2998 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01002999 }
3000 }
3001
3002 std::stringstream bindings;
3003 for (auto const & output : outputs)
3004 {
3005 bindings << "'" << output.second->name << "' ";
3006 }
3007
3008 throw ParseException(
3009 boost::str(
3010 boost::format("No output binding found for subgraph:%1% and name:%2%. "
3011 "Possible outputs are: [%3%] %4%") %
3012 subgraphId %
3013 name %
3014 bindings.str() %
3015 CHECK_LOCATION().AsString()));
3016}
3017
3018size_t TfLiteParser::GetSubgraphCount() const
3019{
3020 return m_Model->subgraphs.size();
3021}
3022
3023std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3024{
3025 CHECK_SUBGRAPH(m_Model, subgraphId);
3026 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3027 std::vector<std::string> result;
3028 result.reserve(inputs.size());
3029 for (auto const & input : inputs)
3030 {
3031 result.push_back(input.second->name);
3032 }
3033 return result;
3034}
3035
3036std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3037{
3038 CHECK_SUBGRAPH(m_Model, subgraphId);
3039 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3040 std::vector<std::string> result;
3041 result.reserve(outputs.size());
3042 for (auto const & output : outputs)
3043 {
3044 result.push_back(output.second->name);
3045 }
3046 return result;
3047}
3048
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003049ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003050{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003051 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01003052}
3053
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003054ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003055{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003056 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01003057}
3058
3059void ITfLiteParser::Destroy(ITfLiteParser* parser)
3060{
3061 delete parser;
3062}
3063
3064TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3065: m_FloatData(std::move(data))
3066, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003067, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003068, m_Int32Data(nullptr)
3069{
3070}
3071
3072TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3073: m_FloatData(nullptr)
3074, m_Uint8Data(std::move(data))
Keith Davisd305e1a2020-01-22 11:57:54 +00003075, m_Int8Data(nullptr)
3076, m_Int32Data(nullptr)
3077{
3078}
3079
3080TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3081: m_FloatData(nullptr)
3082, m_Uint8Data(nullptr)
3083, m_Int8Data(std::move(data))
telsoa01c577f2c2018-08-31 09:22:23 +01003084, m_Int32Data(nullptr)
3085{
3086}
3087
3088TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3089: m_FloatData(nullptr)
3090, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003091, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003092, m_Int32Data(std::move(data))
3093{
3094}
3095
3096} // armnnTfLiteParser