blob: a59725cbd2d1983ab65a745ec517b7d4c3b54005 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5
6#include "RecordByRecordCaffeParser.hpp"
7
8#include "armnn/Exceptions.hpp"
9#include "armnn/Utils.hpp"
Matthew Sloyan589e3e82020-09-11 16:17:48 +010010#include <armnn/utility/NumericCast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010011
12#include "GraphTopologicalSort.hpp"
13
telsoa01c577f2c2018-08-31 09:22:23 +010014// Caffe
15#include <google/protobuf/wire_format.h>
16
17
18//#include <stdio.h>
19#include <limits.h>
20#include <sstream>
21//#include <iostream>
22#include <fstream>
23
24namespace armnnCaffeParser
25{
26// class which holds information on the absolute position in the stream
27// of the data and the length of the data record.
28class VarLenDataInfo
29{
30public:
31 VarLenDataInfo(std::streamoff positionOfData, size_t sizeOfData) :
32 m_PositionOfData(positionOfData), m_SizeOfData(sizeOfData) {}
33
34 VarLenDataInfo(const VarLenDataInfo& x) :
35 m_PositionOfData(x.PositionOfData()), m_SizeOfData (x.SizeOfData()) {}
36
37 VarLenDataInfo& operator=(const VarLenDataInfo& x)
38 {
39 // handle self assignment
40 if (this == &x) {
41 return *this;
42 }
43 m_PositionOfData = x.PositionOfData(); m_SizeOfData = x.SizeOfData(); return *this;
44 }
45
46 std::streamoff PositionOfData() const {return m_PositionOfData;}
47 size_t SizeOfData() const {return m_SizeOfData;}
48
49private:
50 std::streamoff m_PositionOfData;
51 size_t m_SizeOfData;
52
53};
54
55// class which holds enough information on a LayerParameter in the Caffe protobuf
56// format to allow it to be resolved for in place layering and sorted topologically
57// prior to the entire record being parsed into memory.
58//
59// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
60class LayerParameterInfo : public VarLenDataInfo
61{
62public:
63 static const std::string INPUT;
64 LayerParameterInfo(const VarLenDataInfo& varLenDataInfo) :
65 VarLenDataInfo(varLenDataInfo.PositionOfData(), varLenDataInfo.SizeOfData()),
66 m_newTops(false), m_newBottoms(false) {}
67
68 LayerParameterInfo(std::streamoff positionOfData, size_t sizeOfData) :
69 VarLenDataInfo(positionOfData, sizeOfData), m_newTops(false), m_newBottoms(false) {}
70
71 LayerParameterInfo(const LayerParameterInfo& x) :
72 VarLenDataInfo(x.PositionOfData(), x.SizeOfData()),
73 m_name(x.m_name),
74 m_type(x.m_type),
75 m_tops(x.m_tops),
76 m_bottoms(x.m_bottoms),
77 m_newTops(x.m_newTops),
78 m_newBottoms(x.m_newBottoms) {}
79
80 LayerParameterInfo& operator=(const LayerParameterInfo& x)
81 {
82 if (this == &x) {
83 return *this;
84 }
85 VarLenDataInfo::operator=(x);
86 m_name = x.m_name;
87 m_type = x.m_type;
88 m_tops = x.m_tops;
89 m_bottoms = x.m_bottoms;
90 m_newTops = x.m_newTops;
91 m_newBottoms = x.m_newBottoms;
92 return *this;
93 }
94
95 const std::string name() const {return m_name;}
96 void set_name(const std::unique_ptr<char[]>& theName, size_t length)
97 {
98 m_name = std::string(theName.get(), length);
99 }
100 void set_name(const std::string& theName) {m_name = theName;}
101
102 const std::string type() const {return m_type;}
103 void set_type(const std::unique_ptr<char[]>& theType, size_t length)
104 {
105 m_type = std::string(theType.get(), length);
106 }
107 void set_type(const std::string& theType) {m_type = theType;}
108
109 void add_top(const std::unique_ptr<char[]>& top, size_t length)
110 {
111 std::string topName(top.get(), length);
112 m_tops.push_back(topName);
113 }
114 void add_top(const std::string& topName)
115 {
116 m_tops.push_back(topName);
117 }
118 const std::string top(unsigned long i) const {return m_tops[i];}
119 unsigned long top_size() const {return m_tops.size();}
120 void set_top(unsigned long i, const std::string& newName) {m_tops[i] = newName; m_newTops = true;}
121 bool new_tops() const {return m_newTops;}
122
123 void add_bottom(const std::unique_ptr<char[]>& bottom, size_t length)
124 {
125 std::string bottomName(bottom.get(), length);
126 m_bottoms.push_back(bottomName);
127 }
128 unsigned long bottom_size() const {return m_bottoms.size();}
129 const std::string bottom(unsigned long i) const {return m_bottoms[i];}
130 void set_bottom(unsigned long i, const std::string& newName) {m_bottoms[i] = newName; m_newBottoms = true;}
131 bool new_bottoms() const {return m_newBottoms;}
132
133 // if the position and size of the data is zero and the type is "Input" then this is an 'Implicit Input Layer'
134 // and needs to be handled differently from ordinary layers.
135 bool isImplicitInputLayer() const
136 {
137 if ((PositionOfData() == 0) && (SizeOfData() == 0) && INPUT.compare(type()) == 0)
138 {return true;} else {return false;}
139 }
140
141private:
142 std::string m_name;
143 std::string m_type;
144 std::vector<std::string> m_tops;
145 std::vector<std::string> m_bottoms;
146 // mark the layers whose topology was changed
147 // by the ResolveInPlaceLayers method.
148 bool m_newTops;
149 bool m_newBottoms;
150};
151
152// class which holds the field type (wire type) and field id (id from the .proto schema)
153// read from the protobuf messages as per the binary encoding described in
154// https://developers.google.com/protocol-buffers/docs/encoding
155//
156// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
157class ProtobufFieldInfo
158{
159public:
160 ProtobufFieldInfo(int field_type, int field_id) :
161 m_eof(false), m_field_type(field_type), m_field_id(field_id) {}
162 ProtobufFieldInfo() : m_eof(true), m_field_type(0), m_field_id(0) {}
163
164 bool eof() {return m_eof;}
165 int field_type() {return m_field_type;}
166 int field_id() {return m_field_id;}
167
168private:
169 bool m_eof;
170 int m_field_type;
171 int m_field_id;
172};
173
174
175// There are some NetParameter level data which are required
176// to correctly processes some Caffe models. Specifically those which
177// have 'implicit' input layers. Also it is nice to have the name of the model.
178//
179// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
180class NetParameterInfo
181{
182public:
183 const std::string name() const {return m_name;}
184 void set_name(const std::unique_ptr<char[]>& theName, size_t length)
185 {
186 m_name = std::string(theName.get(), length);
187 }
188
189 void add_input(const std::unique_ptr<char[]>& input, size_t length)
190 {
191 std::string inputName(input.get(), length);
192 m_inputs.push_back(inputName);
193 }
194 const std::string input(unsigned long i) const {return m_inputs[i];}
195 unsigned long input_size() const {return m_inputs.size();}
196
197 void add_input_dimension(int input_dimension) {
198 m_input_dimensions.push_back(input_dimension);
199 }
200 int input_dimension(unsigned long i) const {return m_input_dimensions[i];}
201 unsigned long input_dimensions_size() const {return m_input_dimensions.size();}
202
203 void add_blob_shape(caffe::BlobShape shape) {
204 m_blob_shapes.push_back(shape);
205 }
206 const caffe::BlobShape blob_shape(unsigned long i) const {return m_blob_shapes[i];}
207 unsigned long blob_shapes_size() const {return m_blob_shapes.size();}
208
209private:
210 std::string m_name;
211 std::vector<std::string> m_inputs;
212 std::vector<int> m_input_dimensions;
213 std::vector<caffe::BlobShape> m_blob_shapes;
214
215};
216
217}; // namespace armnnCaffeParser
218
219using namespace armnnCaffeParser;
220
221// Initialise the class const
222const std::string LayerParameterInfo::INPUT = "Input";
223
224namespace
225{
226
227ProtobufFieldInfo readFieldInfo(std::ifstream& ifs)
228{
229 unsigned char first_byte = static_cast<unsigned char>(ifs.get());
230 if (!ifs.good())
231 {
232 ProtobufFieldInfo eof;
233 return eof;
234 }
235 int field_type = first_byte&7;
236 int field_id = first_byte>>3;
237 if ((field_id & 16) == 16)
238 {
239 unsigned char second_byte = static_cast<unsigned char>(ifs.get());
240 if (!ifs.good())
241 {
242 ProtobufFieldInfo eof;
243 return eof;
244 }
245 field_id = (field_id-16) + ((second_byte&127)<<4);
246 }
247 ProtobufFieldInfo fieldInfo(field_type, field_id);
248 return fieldInfo;
249}
250
251const static int MAX_NUM_BYTES = 5;
252
253int ReadBase128(std::ifstream& ifs)
254{
255 int result = 0;
256 unsigned int shift_by = 0;
257 int bytesRead = 0;
258 while (true)
259 {
260 unsigned char a_byte = static_cast<unsigned char>(ifs.get());
261 ++bytesRead;
262 if (bytesRead > MAX_NUM_BYTES)
263 {
264 throw armnn::ParseException(
265 "ReadBase128 exceeded the maximum number of bytes expected for an integer representation");
266 }
267 result += (a_byte & 127) << shift_by;
268 shift_by += 7;
269 if ((a_byte & 128) != 128)
270 {
271 break;
272 }
273 }
274 return result;
275}
276
277
278std::unique_ptr<char[]> AllocateBuffer(std::ifstream& ifs, VarLenDataInfo& dataInfo)
279{
280 std::unique_ptr<char[]> ptr(new char[dataInfo.SizeOfData()]);
281 ifs.clear();
282 ifs.seekg(dataInfo.PositionOfData(), std::ios_base::beg);
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100283 ifs.read(ptr.get(), armnn::numeric_cast<std::streamsize>(dataInfo.SizeOfData()));
telsoa01c577f2c2018-08-31 09:22:23 +0100284 return ptr;
285}
286
287VarLenDataInfo CreateVarLenDataInfo(std::streamoff bufferStart, std::streamoff endOfLayer) {
288 std::streamoff sizeOfLayer = endOfLayer - bufferStart;
289 if (sizeOfLayer < 0)
290 {
291 std::stringstream ss;
292 ss << "error when determining buffer size, negative value [" << sizeOfLayer << "]";
293 throw armnn::ParseException(ss.str());
294 }
295 // NOTE: as some of the data being read in will be translated into strings (names of layers etc)
296 // the maximum size we can deal with is the upper size limit of a string i.e. size_t
297 // on the platform in which I am currently compiling std::streamoff is signed long int and
298 // size_t is unsigned long int so there is no way this error condition can fire but this stuff
299 // is supposed to be portable so the check remains in place
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100300 if (armnn::numeric_cast<size_t>(sizeOfLayer) > SIZE_MAX) {
telsoa01c577f2c2018-08-31 09:22:23 +0100301 std::stringstream ss;
302 ss << "layer is greater than " << SIZE_MAX << " in size cannot process. layer size = [" << sizeOfLayer << "]";
303 throw armnn::ParseException(ss.str());
304 }
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100305 LayerParameterInfo info(bufferStart, armnn::numeric_cast<size_t>(sizeOfLayer));
telsoa01c577f2c2018-08-31 09:22:23 +0100306 return info;
307}
308
309void ReadTopologicalInfoForLayerParameter(LayerParameterInfo& layerInfo, std::ifstream& ifs)
310{
311 // position the file pointer to the start of the layer data
312 ifs.clear();
313 ifs.seekg(layerInfo.PositionOfData(), std::ios_base::beg);
314 std::streamoff endOfLayer = layerInfo.PositionOfData() +
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100315 armnn::numeric_cast<std::streamoff>(layerInfo.SizeOfData());
telsoa01c577f2c2018-08-31 09:22:23 +0100316 while(true)
317 {
318 // check to see if we have reached the end of the record
319 std::streamoff currentPosition = ifs.tellg();
320 if (currentPosition >= endOfLayer) {
321 return;
322 }
323 // read the information for the next field.
324 ProtobufFieldInfo fieldInfo = readFieldInfo(ifs);
325 if (fieldInfo.eof())
326 {
327 return;
328 // TODO: figure out whether this is an error condition or not...
329 //throw armnn::ParseException("failed to read field from LayerParameter data");
330 }
331 // process the field
332 switch (fieldInfo.field_type())
333 {
334 case 0:
335 {
336 ReadBase128(ifs);
337 break;
338 }
339 case 2:
340 {
341 int size = ReadBase128(ifs);
342 std::streamoff posStartOfData = ifs.tellg();
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100343 VarLenDataInfo dataInfo(posStartOfData, armnn::numeric_cast<size_t>(size));
telsoa01c577f2c2018-08-31 09:22:23 +0100344 //optional string name = 1; // the layer name
345 //optional string type = 2; // the layer type
346 //repeated string bottom = 3; // the name of each bottom blob
347 //repeated string top = 4; // the name of each top blob
348 if (fieldInfo.field_id() == 1)
349 {
350 // read and set the name of the layer
351 auto layerName = AllocateBuffer(ifs, dataInfo);
352 layerInfo.set_name(layerName, dataInfo.SizeOfData());
353 }
354 else if (fieldInfo.field_id() == 2)
355 {
356 // read and set the type of the layer
357 auto layerType = AllocateBuffer(ifs, dataInfo);
358 layerInfo.set_type(layerType, dataInfo.SizeOfData());
359 }
360 else if (fieldInfo.field_id() == 3)
361 {
362 // read and add a bottom to the layer
363 auto bottom = AllocateBuffer(ifs, dataInfo);
364 layerInfo.add_bottom(bottom, dataInfo.SizeOfData());
365 }
366 else if (fieldInfo.field_id() == 4)
367 {
368 // read and add a top to the layer
369 auto top = AllocateBuffer(ifs, dataInfo);
370 layerInfo.add_top(top, dataInfo.SizeOfData());
371 }
372 else
373 {
374 ifs.seekg(size, std::ios_base::cur);
375 if (!ifs.good())
376 {
377 // TODO: error out?
378 return;
379 }
380 }
381 break;
382 }
383 case 1:
384 {
385 // 64 bit
386 // advance by eight bytes
387 ifs.seekg(8, std::ios_base::cur);
388 if (!ifs.good())
389 {
390 // TODO: error out?
391 return;
392 }
393 break;
394 }
395 case 5:
396 {
397 // 32 bit
398 // advance by four bytes
399 ifs.seekg(4, std::ios_base::cur);
400 if (!ifs.good())
401 {
402 // TODO: error out?
403 return;
404 }
405 break;
406 }
407 default:
408 {
409 throw armnn::ParseException("Encounted an unknown field type");
410 break;
411 }
412 }
413 }
414}
415
416void ResolveInPlaceLayers(std::vector<LayerParameterInfo>& layerInfo)
417{
418 std::map<std::string, std::vector<LayerParameterInfo*>> layersByTop;
419 for (auto& info : layerInfo)
420 {
421 for (unsigned long i = 0; i < info.top_size(); ++i)
422 {
423 layersByTop[info.top(i)].push_back(&info);
424 }
425 }
426 // For each set of layers with the same top, resolve them to a linear chain rather than in-place layers.
427 // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
428 for (auto& layersWithSameTopIterator : layersByTop)
429 {
430 const std::string& top = layersWithSameTopIterator.first;
431 const std::vector<LayerParameterInfo*> layersWithSameTop = layersWithSameTopIterator.second;
432
433 // Chain the layers together in the order that they are listed in the prototxt (hopefully this is correct).
434 // Note that the last layer will not have its top modified so that other layers will continue to reference it.
435 for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
436 {
437 LayerParameterInfo* layer1 = layersWithSameTop[layerIdx];
438 LayerParameterInfo* layer2 = layersWithSameTop[layerIdx + 1];
439 if (layer1->top_size() != 1)
440 {
441 throw armnn::ParseException("Node '" + layer1->name() + "' is an in-place layer but "
442 "doesn't have exactly one top.");
443 }
444 std::string newTop = layer1->name() + "_top";
445 layer1->set_top(0, newTop);
446 if (layer2->bottom_size() != 1 || layer2->bottom(0) != top)
447 {
448 throw armnn::ParseException("Node '" + layer2->name() + "' is an in-place layer but "
449 " doesn't have exactly one bottom, or it doesn't match its top.");
450 }
451 layer2->set_bottom(0, newTop);
452
453 }
454 }
455}
456
457} // anonymous namespace, can't be seen outside this source file
458
459RecordByRecordCaffeParser::RecordByRecordCaffeParser() : CaffeParserBase()
460{}
461
462armnn::INetworkPtr RecordByRecordCaffeParser::CreateNetworkFromBinaryFile(
463 const char* graphFile,
464 const std::map<std::string, armnn::TensorShape>& inputShapes,
465 const std::vector<std::string>& requestedOutputs)
466{
467
468 m_InputShapes = inputShapes;
469 if (requestedOutputs.size() == 0)
470 {
471 throw armnn::ParseException("requestedOutputs must have at least one entry");
472 }
473 m_RequestedOutputs = requestedOutputs;
474
telsoa01c577f2c2018-08-31 09:22:23 +0100475 std::ifstream ifs(graphFile, std::ifstream::in|std::ifstream::binary);
Aron Virginas-Tar83450fa2019-10-02 16:05:44 +0100476 if (ifs.fail())
477 {
478 throw armnn::FileNotFoundException("Failed to open graph file '" + std::string(graphFile) + "'");
479 }
480
telsoa01c577f2c2018-08-31 09:22:23 +0100481 std::vector<LayerParameterInfo> layerInfo;
482 NetParameterInfo netParameterInfo;
483 while(true)
484 {
485 ProtobufFieldInfo fieldInfo = readFieldInfo(ifs);
486 if (fieldInfo.eof())
487 {
488 break;
489 }
490 switch(fieldInfo.field_type())
491 {
492 case 0:
493 {
494 ReadBase128(ifs);
495 break;
496 }
497 case 2:
498 {
499 // The values of interest from the caffe.proto schema are:
500 // optional string name = 1; // consider giving the network a name
501 // DEPRECATED. See InputParameter. The input blobs to the network.
502 // repeated string input = 3;
503 // DEPRECATED. See InputParameter. The shape of the input blobs.
504 // repeated BlobShape input_shape = 8;
505
506 // 4D input dimensions -- deprecated. Use "input_shape" instead.
507 // If specified, for each input blob there should be four
508 // values specifying the num, channels, height and width of the input blob.
509 // Thus, there should be a total of (4 * #input) numbers.
510 // repeated int32 input_dim = 4;
511
512 // The layers that make up the net. Each of their configurations, including
513 // connectivity and behavior, is specified as a LayerParameter.
514 // repeated LayerParameter layer = 100; // ID 100 so layers are printed last.
515
516 // The first four will (if present) be read into the NetParameterInfo
517 // the LayerParameters will be read into the LayerParameterInfo vector.
518
519 int size = ReadBase128(ifs);
520 std::streamoff posStartOfData = ifs.tellg();
521 ifs.seekg(size, std::ios_base::cur);
522 if(!ifs.good())
523 {
524 throw armnn::ParseException("failed to seek ahead in binary caffe file");
525 }
526 std::streamoff endOfLayer = ifs.tellg();
527 if (fieldInfo.field_id() == 1)
528 {
529 VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
530 auto graphName = AllocateBuffer(ifs, dataInfo);
531 netParameterInfo.set_name(graphName, dataInfo.SizeOfData());
532 }
533 if (fieldInfo.field_id() == 3)
534 {
535 VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
536 auto inputName = AllocateBuffer(ifs, dataInfo);
537 netParameterInfo.add_input(inputName, dataInfo.SizeOfData());
538 }
539 if (fieldInfo.field_id() == 8)
540 {
541 VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
542 auto inputShape = AllocateBuffer(ifs, dataInfo);
543 caffe::BlobShape blobShape;
544 bool bRet = blobShape.ParseFromArray(inputShape.get(), static_cast<int>(dataInfo.SizeOfData()));
545 if (!bRet)
546 {
547 throw armnn::ParseException("Failed to parse input shape");
548 }
549 netParameterInfo.add_blob_shape(blobShape);
550 }
551 if (fieldInfo.field_id() == 4)
552 {
553 int input_dim = ReadBase128(ifs);
554 netParameterInfo.add_input_dimension(input_dim);
555 }
556 if (fieldInfo.field_id() == 100)
557 {
558 LayerParameterInfo info(CreateVarLenDataInfo(posStartOfData, endOfLayer));
559 ReadTopologicalInfoForLayerParameter(info, ifs);
560 layerInfo.push_back(info);
561 }
562 break;
563 }
564 default:
565 {
566 break;
567 }
568 }
569 }
570 std::vector<const LayerParameterInfo*> sortedNodes;
571 ProcessLayers(netParameterInfo, layerInfo, m_RequestedOutputs, sortedNodes);
572 armnn::INetworkPtr networkPtr = LoadLayers(ifs, sortedNodes, netParameterInfo);
573 return networkPtr;
574
575}
576
577void RecordByRecordCaffeParser::ProcessLayers(
578 const NetParameterInfo& netParameterInfo,
579 std::vector<LayerParameterInfo>& layerInfo,
580 const std::vector<std::string>& m_RequestedOutputs,
581 std::vector<const LayerParameterInfo*>& sortedNodes)
582{
583 // if there is an implicit input layer add it to the layerInfo list
584 if (netParameterInfo.input_size() > 0)
585 {
586 LayerParameterInfo implicitInputLayer(0, 0);
587 implicitInputLayer.set_type(LayerParameterInfo::INPUT);
588 implicitInputLayer.set_name(netParameterInfo.input(0));
589 implicitInputLayer.add_top(netParameterInfo.input(0));
590 layerInfo.push_back(implicitInputLayer);
591 }
592 ::ResolveInPlaceLayers(layerInfo);
593
594 for (LayerParameterInfo& info : layerInfo)
595 {
596 for (unsigned long i = 0; i < info.top_size(); ++i)
597 {
598 m_CaffeLayersByTopName[info.top(i)] = &info;
599 }
600 }
601
602 // Find the output layers the user requested
603 std::vector<const LayerParameterInfo*> targetLayers;
604 for (const std::string& requestedOutputName : m_RequestedOutputs)
605 {
606 auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
607 if (nodeIt == m_CaffeLayersByTopName.end())
608 {
609 throw armnn::ParseException(
610 "Couldn't find requested output layer '" + requestedOutputName + "' in graph");
611 }
612 targetLayers.push_back(nodeIt->second);
613 }
614
615 // Sort them into a linear ordering such that all inputs of a node are before the node itself
616 if (!armnnUtils::GraphTopologicalSort<const LayerParameterInfo*>(
617 targetLayers,
618 [this](const LayerParameterInfo* node)
619 {
620 return GetInputs(*node);
621 },
622 sortedNodes))
623 {
624 throw armnn::ParseException("Cycle detected in graph");
625 }
626}
627
628
629std::vector<const LayerParameterInfo*> RecordByRecordCaffeParser::GetInputs(
630 const LayerParameterInfo& layerParam)
631{
632 std::vector<const LayerParameterInfo*> ret;
633 ret.reserve(layerParam.bottom_size());
634 for (unsigned long j = 0; j < layerParam.bottom_size(); ++j)
635 {
636 std::string inputName = layerParam.bottom(j);
637 auto inputIt = m_CaffeLayersByTopName.find(inputName);
638 if (inputIt == m_CaffeLayersByTopName.end())
639 {
640 throw armnn::ParseException(
641 "Can't find Caffe layer with top called '" + inputName + "', which is listed as an input of '" +
642 layerParam.name() + "'");
643 }
644 ret.push_back(inputIt->second);
645 }
646
647 return ret;
648}
649
650armnn::INetworkPtr RecordByRecordCaffeParser::LoadLayers(std::ifstream& ifs,
651 std::vector<const LayerParameterInfo *>& sortedNodes,
652 const NetParameterInfo& netParameterInfo)
653{
654
655 m_NetworkInputsBindingInfo.clear();
656 m_NetworkOutputsBindingInfo.clear();
657
658 m_Network = armnn::INetwork::Create();
659
660 for (auto info : sortedNodes)
661 {
662 caffe::LayerParameter layer;
663 if (info->isImplicitInputLayer())
664 {
665 // create the matching Layer Parameter programatically from the data in the
666 // net parameter info which has been passed in...
667 layer.set_type(LayerParameterInfo::INPUT);
668 layer.set_name(netParameterInfo.input(0));
669 layer.add_top(netParameterInfo.input(0));
670
671 caffe::InputParameter* inputParam = layer.mutable_input_param();
672 caffe::BlobShape* shape = inputParam->add_shape();
673
674 long unsigned int dim_size = netParameterInfo.input_dimensions_size();
675 for (long unsigned int i = 0; i < dim_size; ++i)
676 {
677 shape->add_dim(netParameterInfo.input_dimension(i));
678 }
679 }
680 else
681 {
682 char *buffer = new char[info->SizeOfData()];
683 ifs.clear();
684 ifs.seekg(info->PositionOfData(), std::ios_base::beg);
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100685 ifs.read(buffer, armnn::numeric_cast<std::streamsize>(info->SizeOfData()));
telsoa01c577f2c2018-08-31 09:22:23 +0100686 bool bRet = layer.ParseFromArray(buffer, static_cast<int>(info->SizeOfData()));
687 delete[] buffer;
688 if (!bRet)
689 {
690 throw armnn::ParseException("Failed to parse layer [" + info->name() + "]");
691 }
692 }
693
694 if (info->new_tops())
695 {
696 //update the tops
697 layer.set_top(0, info->top(0));
698 }
699 if (info->new_bottoms())
700 {
701 //update the bottoms
702 layer.set_bottom(0, info->bottom(0));
703 }
704
705 auto it = ms_CaffeLayerNameToParsingFunctions.find(layer.type());
706 if (it == ms_CaffeLayerNameToParsingFunctions.end())
707 {
708 throw armnn::ParseException("Unsupported layer type '" + layer.type() + "'");
709 }
710 auto func = it->second;
711 (this->*func)(layer);
712 }
713 ifs.close();
714
715 // Add ArmNN output layers connected to each requested output
716 for (const std::string& requestedOutput : m_RequestedOutputs)
717 {
718 armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
719
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100720 const armnn::LayerBindingId outputId = armnn::numeric_cast<armnn::LayerBindingId>(
telsoa01c577f2c2018-08-31 09:22:23 +0100721 m_NetworkOutputsBindingInfo.size());
722 armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
723 outputSlot.Connect(outputLayer->GetInputSlot(0));
724
725 TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
726 }
727
728 Cleanup();
729
730 return move(m_Network);
731}