Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / src / inference_engine / ie_format_parser.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <set>
6 #include <unordered_set>
7 #include "ie_format_parser.h"
8 #include "ie_layer_parsers.h"
9 #include "xml_parse_utils.h"
10 #include "ie_blob_proxy.hpp"
11 #include "range_iterator.hpp"
12 #include <fstream>
13 #include <sstream>
14 #include "ie_icnn_network_stats.hpp"
15
16 using namespace InferenceEngine;
17 using namespace InferenceEngine::details;
18 using namespace XMLParseUtils;
19 using namespace std;
20
21 void LayerParseParameters::addOutputPort(const LayerPortData &port) {
22     outputPorts.insert(std::upper_bound(outputPorts.begin(), outputPorts.end(), port,
23                                         [=](const LayerParseParameters::LayerPortData &lhs,
24                                                 const LayerParseParameters::LayerPortData &rhs) {
25                                             return lhs.portId < rhs.portId;
26                                         }), port);
27 }
28
29
30 void LayerParseParameters::addInputPort(const LayerPortData &port) {
31     inputPorts.insert(std::upper_bound(inputPorts.begin(), inputPorts.end(), port,
32                                        [=](const LayerParseParameters::LayerPortData &lhs,
33                                                const LayerParseParameters::LayerPortData &rhs) {
34                                            return lhs.portId < rhs.portId;
35                                        }), port);
36 }
37
38 inline void ParseSegment(LayerParseParameters& prms, const pugi::xml_node &blob) {
39     uint64_t size = GetUInt64Attr(blob, "size", 0);
40     uint64_t start = GetUInt64Attr(blob, "offset", 0);
41     if (!size)
42         return;
43
44     WeightSegment& segment = prms.blobs[blob.name()];
45     segment.start = static_cast<size_t>(start);
46     segment.size = static_cast<size_t>(size);
47     const std::string &preStr = GetStrAttr(blob, "precision", "");
48     if (!preStr.empty())
49         segment.precision = Precision::FromStr(preStr);
50     else
51         segment.precision = prms.prms.precision;
52 }
53
54 int BaseCreator::version_ = 4;
55
56 void FormatParser::ParsePort(LayerParseParameters::LayerPortData& port, pugi::xml_node &node) const {
57     port.portId = GetIntAttr(node, "id");
58     ParseDims(port.dims, node);
59     const std::string &preStr = GetStrAttr(node, "precision", "");
60     if (!preStr.empty()) port.precision = Precision::FromStr(preStr);
61 }
62
63 void FormatParser::ParseGenericParams(pugi::xml_node& node, LayerParseParameters& layerParsePrms) const {
64     layerParsePrms.layerId = GetIntAttr(node, "id");
65     layerParsePrms.underIRVersion = _version;
66
67     InferenceEngine::LayerParams& prms = layerParsePrms.prms;
68     prms.type = XMLParseUtils::GetStrAttr(node, "type");
69     prms.precision = _defPrecision;
70
71     prms.name = GetStrAttr(node, "name");
72     const std::string& preStr = GetStrAttr(node, "precision", "");
73     if (!preStr.empty())
74         prms.precision = Precision::FromStr(preStr);
75
76     if (prms.precision == Precision::MIXED) {
77         THROW_IE_EXCEPTION << "Layer precision must not be MIXED, at layer name: " << prms.name << ", offset: "
78                            << node.offset_debug();
79     }
80
81     auto outNode = node.child("output");
82     if (!outNode.empty()) {
83         FOREACH_CHILD(_cn, outNode, "port") {
84             LayerParseParameters::LayerPortData port;
85             port.precision = prms.precision;
86             ParsePort(port, _cn);
87             layerParsePrms.addOutputPort(port);
88         }
89     }
90     auto inpNode = node.child("input");
91     if (!inpNode.empty()) {
92         FOREACH_CHILD(_cn, inpNode, "port") {
93             LayerParseParameters::LayerPortData port;
94             port.precision = prms.precision;
95             ParsePort(port, _cn);
96             layerParsePrms.addInputPort(port);
97         }
98     }
99     auto blob = node.child("biases");
100     if (!blob.empty()) {
101         ParseSegment(layerParsePrms, blob);
102     }
103     blob = node.child("weights");
104     if (!blob.empty()) {
105         ParseSegment(layerParsePrms, blob);
106     }
107     auto blobs = node.child("blobs");
108     if (!blobs.empty()) {
109     for (blob = blobs.first_child(); !blob.empty(); blob = blob.next_sibling()) {
110         ParseSegment(layerParsePrms, blob);
111         }
112     }
113 }
114
115 static inline std::string gen_id(int layer_id, int port_id) {
116     return (std::to_string(layer_id) + '.' + std::to_string(port_id));
117 }
118
119 InferenceEngine::CNNLayer::Ptr FormatParser::CreateLayer(pugi::xml_node& node,
120                                                        LayerParseParameters& layerParsePrms) const {
121     for (auto &creator : getCreators()) {
122         if (!creator->shouldCreate(layerParsePrms.prms.type))
123             continue;
124         return creator->CreateLayer(node, layerParsePrms);
125     }
126     static LayerCreator<GenericLayer> genericCreator("");
127     return genericCreator.CreateLayer(node, layerParsePrms);
128 }
129
130 void FormatParser::SetLayerInput(CNNNetworkImpl& network, const std::string& dataId,
131                                    CNNLayerPtr& targetLayer, int inputPort) {
132     DataPtr& dataPtr = _portsToData[dataId];
133     if (!dataPtr) THROW_IE_EXCEPTION << "in Layer " << targetLayer->name
134                                      << ": trying to connect an edge to non existing output port: " << dataId;
135
136     dataPtr->getInputTo()[targetLayer->name] = targetLayer;
137     const LayerParseParameters& parseInfo = layersParseInfo[targetLayer->name];
138     if (targetLayer->insData.empty()) {
139         targetLayer->insData.resize(parseInfo.inputPorts.size());
140     }
141     for (unsigned i = 0; i < parseInfo.inputPorts.size(); i++) {
142         if (parseInfo.inputPorts[i].portId != inputPort) continue;
143         if (parseInfo.inputPorts[i].precision != dataPtr->getPrecision()) {
144             if (dataPtr->getPrecision() == Precision::UNSPECIFIED) {
145                 dataPtr->setPrecision(parseInfo.inputPorts[i].precision);
146             } else {
147                 // TODO: Make a correct exception
148
149                 /*THROW_IE_EXCEPTION << "in Layer " << targetLayer->name
150                                    << ": trying to connect an edge to mismatch precision of output port: "
151                                    << dataPtr->getName();*/
152             }
153         }
154         if (!equal(parseInfo.inputPorts[i].dims, dataPtr->getDims()))
155             THROW_IE_EXCEPTION << "in Layer " << targetLayer->name
156                                << ": trying to connect an edge to mismatch dimensions of output port: "
157                                << dataPtr->getName()
158                                << " dims input: " << dumpVec(parseInfo.inputPorts[i].dims)
159                                << " dims output: " << dumpVec(dataPtr->getDims());
160         targetLayer->insData[i] = dataPtr;
161         const auto insId = gen_id(parseInfo.layerId, parseInfo.inputPorts[i].portId);
162         _portsToData[insId] = dataPtr;
163         return;
164     }
165     THROW_IE_EXCEPTION << "input port " << inputPort << " does not exist in layer " << targetLayer->name;
166 }
167
168 FormatParser::FormatParser(int version): _version(version) {
169     BaseCreator::version_ = version;
170 }
171
172 CNNNetworkImplPtr FormatParser::Parse(pugi::xml_node& root) {
173     _network.reset(new CNNNetworkImpl());
174     _network->setName(GetStrAttr(root, "name", ""));
175     _defPrecision = Precision::FromStr(GetStrAttr(root, "precision", "UNSPECIFIED"));
176     _network->setPrecision(_defPrecision);
177     // parse the input Data
178     DataPtr inputData;
179     if (_version == 1) {
180         inputData = ParseInputData(root);
181         _portsToData[inputData->getName()] = inputData;  // hack as this input does not have ports ids
182         InputInfo::Ptr info(new InputInfo());
183         info->setInputData(inputData);
184         _network->setInputInfo(info);
185     }
186
187     // parse the graph layers
188     auto allLayersNode = root.child("layers");
189     std::vector< CNNLayer::Ptr> inputLayers;
190     int nodeCnt = 0;
191     std::map<int, CNNLayer::Ptr> layerById;
192     bool identifyNetworkPrecision = _defPrecision == Precision::UNSPECIFIED;
193     for (auto node = allLayersNode.child("layer"); !node.empty(); node = node.next_sibling("layer")) {
194         LayerParseParameters lprms;
195         ParseGenericParams(node, lprms);
196
197         CNNLayer::Ptr layer = CreateLayer(node, lprms);
198         if (!layer) THROW_IE_EXCEPTION << "Don't know how to create Layer type: " << lprms.prms.type;
199
200         layersParseInfo[layer->name] = lprms;
201         _network->addLayer(layer);
202         layerById[lprms.layerId] = layer;
203
204         if (equal(layer->type, "input")) {
205             inputLayers.push_back(layer);
206         }
207
208         if (identifyNetworkPrecision) {
209             if (!_network->getPrecision()) {
210                 _network->setPrecision(lprms.prms.precision);
211             }
212             if (_network->getPrecision() != lprms.prms.precision) {
213                 _network->setPrecision(Precision::MIXED);
214                 identifyNetworkPrecision = false;
215             }
216         }
217
218         for (int i = 0; i < lprms.outputPorts.size(); i++) {
219             const auto &outPort = lprms.outputPorts[i];
220             const auto outId = gen_id(lprms.layerId, outPort.portId);
221             const std::string outName = lprms.outputPorts.size() == 1
222                     ? lprms.prms.name
223                     : lprms.prms.name + "." + std::to_string(i);
224             DataPtr& ptr = _network->getData(outName.c_str());
225             if (!ptr) {
226                 ptr.reset(new Data(outName, outPort.dims, outPort.precision, TensorDesc::getLayoutByDims(outPort.dims)));
227                 ptr->setDims(outPort.dims);
228             }
229             _portsToData[outId] = ptr;
230
231             if (ptr->getCreatorLayer().lock())
232                 THROW_IE_EXCEPTION << "two layers set to the same output [" << outName << "], conflict at offset "
233                                    << node.offset_debug();
234
235             ptr->getCreatorLayer() = layer;
236             layer->outData.push_back(ptr);
237         }
238         nodeCnt++;
239     }
240
241     // connect the edges
242     pugi::xml_node edges = root.child("edges");
243
244     FOREACH_CHILD(_ec, edges, "edge") {
245         int fromLayer = GetIntAttr(_ec, "from-layer");
246         int fromPort = GetIntAttr(_ec, "from-port");
247         int toLayer = GetIntAttr(_ec, "to-layer");
248         int toPort = GetIntAttr(_ec, "to-port");
249
250         const auto dataId = gen_id(fromLayer, fromPort);
251         auto targetLayer = layerById[toLayer];
252         if (!targetLayer)
253             THROW_IE_EXCEPTION << "Layer ID " << toLayer << " was not found while connecting edge at offset "
254                                << _ec.offset_debug();
255
256         SetLayerInput(*_network, dataId, targetLayer, toPort);
257     }
258
259     if (_version == 1) {
260         // a hacK: set input to the first layer that is not connected...
261         bool inputWasSet = false;
262         for (auto& kvp : layerById) {
263             CNNLayer::Ptr& layer = kvp.second;
264             const LayerParseParameters& parseInfo = layersParseInfo[layer->name];
265             size_t inSize = layer->insData.size();
266             if (inSize != 0) continue;
267             if (parseInfo.inputPorts.size() == 0)
268                 THROW_IE_EXCEPTION << "Layer " << layer->name << " does not have any input";
269             SetLayerInput(*_network, inputData->getName(), layer, parseInfo.inputPorts[0].portId);
270             inputWasSet = true;
271
272             // Modification of default input precision which should be used for input blob
273             // Q78 needs an I16 otherwise pixels will overflow
274             // FP16 requires to pass FP32 as input from user
275             Precision inputPrecision;
276             inputPrecision = layer->precision == Precision::Q78 ? Precision::I16 :
277                 layer->precision == Precision::FP16 ? Precision::FP32 : static_cast<Precision::ePrecision>(layer->precision);
278
279             auto inputLayer = std::make_shared<GenericLayer>(LayerParams({inputData->getName(), "input",  inputPrecision}));
280             inputLayer->outData.push_back(inputData);
281             _network->addLayer(inputLayer);
282             inputData->creatorLayer = inputLayer;
283
284             InputsDataMap inputs;
285             _network->getInputsInfo(inputs);
286             if (inputs.size() != 1) {
287                 THROW_IE_EXCEPTION << "IR v1 must have one input layer";
288             }
289             inputs.begin()->second->setInputPrecision(inputPrecision);
290
291             // And we need to leave original input precision unmodified for proper handling in plugin
292             inputData->setPrecision(layer->precision);
293             break;
294         }
295         if (!inputWasSet) THROW_IE_EXCEPTION << "network does not have any input layer";
296     } else {  // version 2: inputs are marked as input layers
297         auto keep_input_info = [&] (DataPtr &in_data) {
298             InputInfo::Ptr info(new InputInfo());
299             info->setInputData(in_data);
300             Precision prc = info->getInputPrecision();
301
302             // Convert precision into native format (keep element size)
303             prc = prc == Precision::Q78 ? Precision::I16 :
304                   prc == Precision::FP16 ? Precision::FP32 :
305                   static_cast<Precision::ePrecision>(prc);
306
307             info->setInputPrecision(prc);
308             _network->setInputInfo(info);
309         };
310
311         // Keep all data from InputLayers
312         for (auto inLayer : inputLayers) {
313             if (inLayer->outData.size() != 1)
314                 THROW_IE_EXCEPTION << "Input layer must have 1 output. "
315                                       "See documentation for details.";
316             keep_input_info(inLayer->outData[0]);
317         }
318
319         // Keep all data which has no creator layer
320         for (auto &kvp : _network->allLayers()) {
321             const CNNLayer::Ptr& layer = kvp.second;
322             auto pars_info = layersParseInfo[layer->name];
323
324             if (layer->insData.empty())
325                 layer->insData.resize(pars_info.inputPorts.size());
326
327             for (int i = 0; i < layer->insData.size(); i++) {
328                 if (!layer->insData[i].lock()) {
329                     std::string data_name = (layer->insData.size() == 1)
330                             ? layer->name
331                             : layer->name + "." + std::to_string(i);
332
333                     DataPtr data(new Data(data_name,
334                             pars_info.inputPorts[i].dims,
335                             pars_info.inputPorts[i].precision,
336                             TensorDesc::getLayoutByDims(pars_info.inputPorts[i].dims)));
337                     data->setDims(pars_info.inputPorts[i].dims);
338
339                     layer->insData[i] = data;
340                     data->inputTo[layer->name] = layer;
341
342                     const auto insId = gen_id(pars_info.layerId, pars_info.inputPorts[i].portId);
343                     _portsToData[insId] = data;
344
345                     keep_input_info(data);
346                 }
347             }
348         }
349     }
350
351     auto statNode = root.child("statistics");
352     ParseStatisticSection(statNode);
353
354     if (!_network->allLayers().size())
355         THROW_IE_EXCEPTION << "Incorrect model! Network doesn't contain layers.";
356
357     size_t inputLayersNum(0);
358     CaselessEq<std::string> cmp;
359     for (const auto& kvp : _network->allLayers()) {
360         const CNNLayer::Ptr& layer = kvp.second;
361         if (cmp(layer->type, "Input") || cmp(layer->type, "Const"))
362             inputLayersNum++;
363     }
364
365     if (!inputLayersNum && !cmp(root.name(), "body"))
366         THROW_IE_EXCEPTION << "Incorrect model! Network doesn't contain input layers.";
367
368     // check all input ports are occupied
369     for (const auto& kvp : _network->allLayers()) {
370         const CNNLayer::Ptr& layer = kvp.second;
371         const LayerParseParameters& parseInfo = layersParseInfo[layer->name];
372         size_t inSize = layer->insData.size();
373         if (inSize != parseInfo.inputPorts.size())
374             THROW_IE_EXCEPTION << "Layer " << layer->name << " does not have any edge connected to it";
375
376         for (unsigned i = 0; i < inSize; i++) {
377             if (!layer->insData[i].lock()) {
378                 THROW_IE_EXCEPTION << "Layer " << layer->name.c_str() << " input port "
379                                    << parseInfo.inputPorts[i].portId << " is not connected to any data";
380             }
381         }
382         layer->validateLayer();
383     }
384     // parse mean image
385     ParsePreProcess(root);
386     _network->resolveOutput();
387
388     // Set default output precision to FP32 (for back-compatibility)
389     OutputsDataMap outputsInfo;
390     _network->getOutputsInfo(outputsInfo);
391     for (auto outputInfo : outputsInfo) {
392         if (outputInfo.second->getPrecision() != Precision::FP32 &&
393             outputInfo.second->getPrecision() != Precision::I32) {
394             outputInfo.second->setPrecision(Precision::FP32);
395         }
396     }
397
398     if (_version == 1) {
399         int batchSize = GetIntAttr(root, "batch", 1);
400         _network->setBatchSize(batchSize);
401     }
402
403     return _network;
404 }
405
406 template<typename BlobType>
407 inline Blob::Ptr GetTypedBlobFromSegment(const TBlob<uint8_t>::Ptr& weights, const WeightSegment& segment) {
408     if (segment.getEnd() > weights->size())
409         THROW_IE_EXCEPTION << "segment exceeds given buffer limits. Please, validate weights file";
410
411     size_t noOfElement = segment.size / sizeof(BlobType);
412     // RanC: TODO: IR does not provide me with weight slayout.
413     // So far I knew it since I know what layer it is. In generic layers I don't
414     // so until the IR will have the layout and sizes I will pass it as vector and the plugin will have to
415     // validate and undertand what he should get...
416     SizeVector w_dims({noOfElement});
417
418     typename TBlobProxy<BlobType>::Ptr binBlob(new TBlobProxy<BlobType>(segment.precision, Layout::C, weights, segment.start, w_dims));
419
420     /* this validation is not reduntant I have no prior knowledge of the weights anymore...
421     if (pbpWeights->byteSize() != lprms.weights.size)
422         THROW_IE_EXCEPTION << "bytes size weights for " << pWL->name << " mismatch, expecting "
423         << pbpWeights->byteSize() << " bytes which are " << pbpWeights->size() << " elements";
424         */
425     return binBlob;
426 }
427
428 Blob::Ptr FormatParser::GetBlobFromSegment(const TBlob<uint8_t>::Ptr& weights, const WeightSegment& segment) const {
429     if (segment.precision == Precision::FP32) {
430         return GetTypedBlobFromSegment<float>(weights, segment);
431     } else if (segment.precision == Precision::I32) {
432         return GetTypedBlobFromSegment<int32_t>(weights, segment);
433     } else if (segment.precision == Precision::I16 || segment.precision == Precision::Q78 || segment.precision == Precision::FP16) {
434         return GetTypedBlobFromSegment<short>(weights, segment);
435     } else if (segment.precision == Precision::U8) {
436         return GetTypedBlobFromSegment<uint8_t>(weights, segment);
437     } else if (segment.precision == Precision::I8 || segment.precision == Precision::BIN) {
438         return GetTypedBlobFromSegment<int8_t>(weights, segment);
439     } else {
440         THROW_IE_EXCEPTION << "precision " << segment.precision << " is not supported...";
441     }
442 }
443
444 void FormatParser::SetWeights(const TBlob<uint8_t>::Ptr& weights) {
445     for (auto& kvp : _network->allLayers()) {
446         auto fit = layersParseInfo.find(kvp.second->name);
447         // todo: may check that earlier - while parsing...
448         if (fit == layersParseInfo.end())
449             THROW_IE_EXCEPTION << "Internal Error: ParseInfo for " << kvp.second->name << " are missing...";
450         auto& lprms = fit->second;
451
452         WeightableLayer* pWL = dynamic_cast<WeightableLayer*>(kvp.second.get());
453         if (pWL != nullptr) {
454             if (lprms.blobs.find("weights") != lprms.blobs.end()) {
455                 if (lprms.prms.type == "BinaryConvolution") {
456                     auto segment = lprms.blobs["weights"];
457                     if (segment.getEnd() > weights->size())
458                         THROW_IE_EXCEPTION << "segment exceeds given buffer limits. Please, validate weights file";
459                     size_t noOfElement = segment.size;
460                     SizeVector w_dims({noOfElement});
461                     typename TBlobProxy<uint8_t>::Ptr binBlob(new TBlobProxy<uint8_t>(Precision::BIN, Layout::C, weights, segment.start, w_dims));
462
463                     pWL->_weights = binBlob;
464                 } else {
465                     pWL->_weights = GetBlobFromSegment(weights, lprms.blobs["weights"]);
466                 }
467                 pWL->blobs["weights"] = pWL->_weights;
468             }
469             if (lprms.blobs.find("biases") != lprms.blobs.end()) {
470                 pWL->_biases  = GetBlobFromSegment(weights, lprms.blobs["biases"]);
471                 pWL->blobs["biases"] = pWL->_biases;
472             }
473         }
474         auto pGL = kvp.second.get();
475         if (pGL == nullptr) continue;
476         for (auto s : lprms.blobs) {
477             pGL->blobs[s.first] = GetBlobFromSegment(weights, s.second);
478         }
479
480         // Some layer can specify additional action to prepare weights
481         if (fit->second.internalWeightSet)
482             fit->second.internalWeightSet(weights);
483     }
484     for (auto &kvp : _preProcessSegments) {
485         const std::string &inputName = kvp.first;
486         auto &segments = kvp.second;
487         auto inputInfo = _network->getInput(inputName);
488         if (!inputInfo) THROW_IE_EXCEPTION << "Internal error: missing input name " << inputName;
489
490         auto dims = inputInfo->getDims();
491         auto width = dims[0];
492         auto height = dims[1];
493
494         PreProcessInfo &pp = inputInfo->getPreProcess();
495
496         for (size_t c = 0; c < segments.size(); c++) {
497             if (segments[c].size == 0)
498                 continue;
499             Blob::Ptr blob = GetBlobFromSegment(weights, segments[c]);
500             blob->Reshape({ width, height }, Layout::HW);  // to fit input image sizes (summing it is an image)
501             pp.setMeanImageForChannel(blob, c);
502         }
503     }
504 }
505
506 void FormatParser::ParseDims(SizeVector& dims, const pugi::xml_node &parentNode) const {
507     for (auto node = parentNode.child("dim"); !node.empty(); node = node.next_sibling("dim")) {
508         unsigned int dim = 0;
509         const pugi::char_t* dimVal = node.child_value();
510         stringstream ss(dimVal);
511         if (!(ss >> dim) || dim == 0) {
512             THROW_IE_EXCEPTION << "dimension (" << dimVal << ") in node " << node.name() << " must be a positive integer: at offset "
513                 << node.offset_debug();
514         }
515         dims.push_back(dim);
516     }
517
518     if (_version == 1)
519         dims.insert(dims.begin(), 1);  // for batch, in version 1, in version 2 it is already there.
520 }
521
522 const DataPtr& FormatParser::GetDataBy(int layer_id, int port_id) const {
523     const auto id = gen_id(layer_id, port_id);
524     const auto &found = _portsToData.find(id);
525     if (found == _portsToData.end())
526         THROW_IE_EXCEPTION << "No data found for layer_id=" << layer_id << " port_id=" << port_id;
527     return found->second;
528 }
529
530 DataPtr FormatParser::ParseInputData(pugi::xml_node& root) const {
531     auto inputNode = root.child("input");
532     if (inputNode.empty()) {
533         THROW_IE_EXCEPTION << "No input node in network, missing <input>";
534     }
535
536     auto inputName = GetStrAttr(inputNode, "name", "input");
537     SizeVector inputDims;
538
539     ParseDims(inputDims, inputNode);
540
541     DataPtr& inputData = _network->getData(inputName);
542     inputData.reset(new Data(inputName, inputDims, _network->getPrecision(), TensorDesc::getLayoutByDims(inputDims)));
543     inputData->setDims(inputDims);
544     return inputData;
545 }
546
547 void FormatParser::ParsePreProcess(pugi::xml_node& root) {
548     /*
549     <pre-process mean-precision="FP32">
550         <channel id = ”0”>
551             <mean value = ”104” / >  // in case of constant
552         // or
553             <mean offset = "121930449" size = "51529" / >  // in case of array – ref to the .bin file
554             <scale value = "1.2">
555         </channel>
556     </pre-process>
557     */
558
559     auto ppNode = root.child("pre-process");
560     if (ppNode.empty()) {
561         return;
562     }
563     // find out to what input this belongs to
564     std::string inputName;
565     InputInfo::Ptr preProcessInput;
566
567     inputName = GetStrAttr(ppNode, "reference-layer-name", "");
568     inputName = trim(inputName);
569     if (inputName.empty()) {
570         // fallback (old format), look for the picture in the inputs
571         InputsDataMap inputs;
572         _network->getInputsInfo(inputs);
573
574         if (inputs.empty()) THROW_IE_EXCEPTION << "network has no input";
575
576         for (auto i : inputs) {
577             if (i.second->getDims().size() == 4) {
578                 preProcessInput = i.second;
579                 break;
580             }
581         }
582         if (!preProcessInput) {
583             preProcessInput = inputs.begin()->second;
584         }
585
586         inputName = preProcessInput->name();
587     } else {
588         preProcessInput = _network->getInput(inputName);
589         if (!preProcessInput)
590             THROW_IE_EXCEPTION << "pre-process name ref '" << inputName << "' refers to un-existing input";
591     }
592
593     // dims vector without batch size
594     SizeVector inputDims = preProcessInput->getDims();
595
596     if (inputDims.size() < 2)
597         THROW_IE_EXCEPTION << "network did not define input dimensions properly";
598     size_t noOfChannels = inputDims[inputDims.size() - 2];
599     size_t width = inputDims[0];
600     size_t height = inputDims[1];
601
602     PreProcessInfo &pp = preProcessInput->getPreProcess();
603     std::vector<WeightSegment> &segments = _preProcessSegments[inputName];
604
605     pp.init(noOfChannels);
606
607     segments.resize(noOfChannels);
608
609     auto meanSegmentPrecision = GetPrecisionAttr(ppNode, "mean-precision", Precision::UNSPECIFIED);
610
611     ResponseDesc resp;
612     InferenceEngine::PreProcessChannel::Ptr preProcessChannel;
613
614     int lastChanNo = -1;
615     std::unordered_set<int> idsForMeanValue;
616     std::unordered_set<int> idsForMeanImage;
617
618     FOREACH_CHILD(chan, ppNode, "channel") {
619         int chanNo = GetIntAttr(chan, "id", lastChanNo + 1);
620         if (chanNo >= static_cast<int>(noOfChannels) || chanNo < 0) {
621             THROW_IE_EXCEPTION << "Pre-process channel id invalid: " << chanNo;
622         }
623         lastChanNo = chanNo;
624         preProcessChannel = pp[chanNo];
625         WeightSegment& preProcessSegment = segments[chanNo];
626
627         auto meanNode = chan.child("mean");
628         if (!meanNode.empty()) {
629             if (!meanNode.attribute("value") && (!meanNode.attribute("size"))) {
630                 THROW_IE_EXCEPTION << "mean should have at least one of the following attribute: value, size";
631             }
632             if (meanNode.attribute("value")) {
633                 preProcessChannel->meanValue = GetFloatAttr(meanNode, "value");
634                 idsForMeanValue.insert(chanNo);
635             }
636             if (meanNode.attribute("size")) {
637                 idsForMeanImage.insert(chanNo);
638                 preProcessSegment.size = static_cast<size_t>(GetIntAttr(meanNode, "size"));
639                 preProcessSegment.start = static_cast<size_t>(GetIntAttr(meanNode, "offset"));
640                 preProcessSegment.precision = meanSegmentPrecision;
641                 if (width*height*meanSegmentPrecision.size() != preProcessSegment.size) {
642                     THROW_IE_EXCEPTION << "mean blob size mismatch expected input, got: "
643                                        << preProcessSegment.size << " extpecting " << width
644                                        << " x " << height << " x " << meanSegmentPrecision.size();
645                 }
646                 if (!meanSegmentPrecision || meanSegmentPrecision == Precision::MIXED)
647                     THROW_IE_EXCEPTION << "mean blob defined without specifying precision.";
648             }
649         }
650         auto scaleNode = chan.child("scale");
651         if (!scaleNode.empty() && scaleNode.attribute("value")) {
652             preProcessChannel->stdScale = GetFloatAttr(scaleNode, "value");
653         }
654     }
655
656     if (idsForMeanImage.size() == noOfChannels) {
657         pp.setVariant(MEAN_IMAGE);
658     } else if (idsForMeanValue.size() == noOfChannels) {
659         pp.setVariant(MEAN_VALUE);
660     } else if ((idsForMeanImage.size() == 0) && (idsForMeanValue.size() == 0)) {
661         pp.setVariant(NONE);
662     } else {
663         std::string validMeanValuesIds = "";
664         std::string validMeanImageIds = "";
665         for (auto id : idsForMeanValue) { validMeanValuesIds += std::to_string(id) + " "; }
666         for (auto id : idsForMeanImage) { validMeanImageIds += std::to_string(id) + " "; }
667         THROW_IE_EXCEPTION << "mean is not provided for all channels\n"
668                 "Provided mean values for : " << validMeanValuesIds << "\n"
669                                    "Provided mean image for: " << validMeanImageIds;
670     }
671 }
672
673 const std::vector<std::shared_ptr<BaseCreator> >& FormatParser::getCreators() const {
674     // there should be unique_ptr but it cant be used with initializer lists
675     static std::vector<std::shared_ptr<BaseCreator> > creators = {
676         std::make_shared<LayerCreator<PowerLayer>>("Power"),
677         std::make_shared<LayerCreator<ConvolutionLayer>>("Convolution"),
678         std::make_shared<LayerCreator<DeconvolutionLayer>>("Deconvolution"),
679         std::make_shared<LayerCreator<PoolingLayer>>("Pooling"),
680         std::make_shared<LayerCreator<FullyConnectedLayer>>("InnerProduct"),
681         std::make_shared<LayerCreator<FullyConnectedLayer>>("FullyConnected"),
682         std::make_shared<LayerCreator<NormLayer>>("LRN"),
683         std::make_shared<LayerCreator<NormLayer>>("Norm"),
684         std::make_shared<LayerCreator<SoftMaxLayer>>("Softmax"),
685         std::make_shared<LayerCreator<GRNLayer>>("GRN"),
686         std::make_shared<LayerCreator<MVNLayer>>("MVN"),
687         std::make_shared<LayerCreator<ReLULayer>>("ReLU"),
688         std::make_shared<LayerCreator<ClampLayer>>("Clamp"),
689         std::make_shared<LayerCreator<SplitLayer>>("Split"),
690         std::make_shared<LayerCreator<SplitLayer>>("Slice"),
691         std::make_shared<LayerCreator<ConcatLayer>>("Concat"),
692         std::make_shared<LayerCreator<EltwiseLayer>>("Eltwise"),
693         std::make_shared<LayerCreator<GemmLayer>>("Gemm"),
694         std::make_shared<LayerCreator<PadLayer>>("Pad"),
695         std::make_shared<LayerCreator<GatherLayer>>("Gather"),
696         std::make_shared<LayerCreator<StridedSliceLayer>>("StridedSlice"),
697         std::make_shared<LayerCreator<ShuffleChannelsLayer>>("ShuffleChannels"),
698         std::make_shared<LayerCreator<DepthToSpaceLayer>>("DepthToSpace"),
699         std::make_shared<LayerCreator<SpaceToDepthLayer>>("SpaceToDepth"),
700         std::make_shared<LayerCreator<ReverseSequenceLayer>>("ReverseSequence"),
701         std::make_shared<LayerCreator<SqueezeLayer>>("Squeeze"),
702         std::make_shared<LayerCreator<UnsqueezeLayer>>("Unsqueeze"),
703         std::make_shared<LayerCreator<RangeLayer>>("Range"),
704         std::make_shared<LayerCreator<ExpandLayer>>("Expand"),
705         std::make_shared<LayerCreator<ScaleShiftLayer>>("ScaleShift"),
706         std::make_shared<LayerCreator<PReLULayer>>("PReLU"),
707         std::make_shared<LayerCreator<CropLayer>>("Crop"),
708         std::make_shared<LayerCreator<ReshapeLayer>>("Reshape"),
709         std::make_shared<LayerCreator<ReshapeLayer>>("Flatten"),
710         std::make_shared<LayerCreator<TileLayer>>("Tile"),
711         std::make_shared<ActivationLayerCreator>("Activation"),
712         std::make_shared<LayerCreator<BatchNormalizationLayer>>("BatchNormalization"),
713         std::make_shared<TILayerCreator>("TensorIterator"),
714         std::make_shared<LayerCreator<LSTMCell>>("LSTMCell"),
715         std::make_shared<LayerCreator<GRUCell>>("GRUCell"),
716         std::make_shared<LayerCreator<RNNCell>>("RNNCell"),
717         std::make_shared<LayerCreator<RNNSequenceLayer>>("RNNSequence"),
718         std::make_shared<LayerCreator<RNNSequenceLayer>>("GRUSequence"),
719         std::make_shared<LayerCreator<RNNSequenceLayer>>("LSTMSequence"),
720         std::make_shared<LayerCreator<QuantizeLayer>>("Quantize"),
721         std::make_shared<LayerCreator<BinaryConvolutionLayer>>("BinaryConvolution"),
722     };
723     return creators;
724 }
725
726 void FormatParser::ParseStatisticSection(const pugi::xml_node& statNode) {
727     auto splitParseCommas = [&](const string& s) ->vector<float> {
728         vector<float> res;
729         stringstream ss(s);
730
731         float val;
732
733         while (ss >> val) {
734             res.push_back(val);
735
736             if (ss.peek() == ',')
737                 ss.ignore();
738         }
739
740         return res;
741     };
742
743     map<string, NetworkNodeStatsPtr> newNetNodesStats;
744
745     for (auto layer : statNode.children("layer")) {
746         NetworkNodeStatsPtr nodeStats = NetworkNodeStatsPtr(new NetworkNodeStats());
747
748         string name = layer.child("name").text().get();
749
750         newNetNodesStats[name] = nodeStats;
751
752         nodeStats->_minOutputs = splitParseCommas(layer.child("min").text().get());
753         nodeStats->_maxOutputs = splitParseCommas(layer.child("max").text().get());
754     }
755
756     ICNNNetworkStats *pstats = nullptr;
757     StatusCode s = _network->getStats(&pstats, nullptr);
758     if (s == StatusCode::OK && pstats) {
759         pstats->setNodesStats(newNetNodesStats);
760     }
761 }