1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // Copyright (C) 2018-2019, Intel Corporation, all rights reserved.
6 // Third party copyrights are property of their respective owners.
7
8 #include "precomp.hpp"
9
10 #include <fstream>
11
12 #include "ie_ngraph.hpp"
13
14 #include <opencv2/dnn/shape_utils.hpp>
15
16 #ifdef HAVE_DNN_NGRAPH
17 #include <ie_extension.h>
18 #endif // HAVE_DNN_NGRAPH
19
20 #include <opencv2/core/utils/configuration.private.hpp>
21 #include <opencv2/core/utils/logger.hpp>
22
23 #include "opencv2/core/utils/filesystem.hpp"
24 #include "opencv2/core/utils/filesystem.private.hpp"
25
26 namespace cv { namespace dnn {
27
28 #ifdef HAVE_DNN_NGRAPH
29
30 static bool DNN_IE_SERIALIZE = utils::getConfigurationParameterBool("OPENCV_DNN_IE_SERIALIZE", false);
31
32 // For networks with input layer which has an empty name, IE generates a name id[some_number].
33 // OpenCV lets users use an empty input name and to prevent unexpected naming,
34 // we can use some predefined name.
35 static std::string kDefaultInpLayerName = "opencv_ngraph_empty_inp_layer_name";
36 static constexpr const char* kOpenCVLayersType = "opencv_ngraph_layer";
37
shapesToStr(const std::vector<Mat> & mats)38 static std::string shapesToStr(const std::vector<Mat>& mats)
39 {
40 std::ostringstream shapes;
41 shapes << mats.size() << " ";
42 for (const Mat& m : mats)
43 {
44 shapes << m.dims << " ";
45 for (int i = 0; i < m.dims; ++i)
46 shapes << m.size[i] << " ";
47 }
48 return shapes.str();
49 }
50
strToShapes(const std::string & str,std::vector<std::vector<size_t>> & shapes)51 static void strToShapes(const std::string& str, std::vector<std::vector<size_t> >& shapes)
52 {
53 std::istringstream ss(str);
54 int num, dims;
55 ss >> num;
56 shapes.resize(num);
57 for (int i = 0; i < num; ++i)
58 {
59 ss >> dims;
60 shapes[i].resize(dims);
61 for (int j = 0; j < dims; ++j)
62 ss >> shapes[i][j];
63 }
64 }
65
66 static std::vector<Ptr<NgraphBackendWrapper> >
ngraphWrappers(const std::vector<Ptr<BackendWrapper>> & ptrs)67 ngraphWrappers(const std::vector<Ptr<BackendWrapper> >& ptrs)
68 {
69 std::vector<Ptr<NgraphBackendWrapper> > wrappers(ptrs.size());
70 for (int i = 0; i < ptrs.size(); ++i)
71 {
72 CV_Assert(!ptrs[i].empty());
73 wrappers[i] = ptrs[i].dynamicCast<NgraphBackendWrapper>();
74 CV_Assert(!wrappers[i].empty());
75 }
76 return wrappers;
77 }
78
79 class NgraphCustomOp: public ngraph::op::Op {
80 public:
get_type_info() const81 const ngraph::NodeTypeInfo& get_type_info() const override
82 {
83 static constexpr ngraph::NodeTypeInfo type_info{kOpenCVLayersType, 0};
84 return type_info;
85 }
86
87 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_3)
NgraphCustomOp(const ngraph::OutputVector & inputs,const std::map<std::string,InferenceEngine::Parameter> & params={})88 NgraphCustomOp(const ngraph::OutputVector& inputs,
89 #else
90 NgraphCustomOp(const ngraph::NodeVector& inputs,
91 #endif
92 const std::map<std::string, InferenceEngine::Parameter>& params = {}):
93 Op(inputs), params(params)
94 {
95 constructor_validate_and_infer_types();
96 }
97
~NgraphCustomOp()98 ~NgraphCustomOp()
99 {
100 // nothing
101 }
102
validate_and_infer_types()103 void validate_and_infer_types() override
104 {
105 std::vector<std::vector<size_t> > shapes;
106 strToShapes(params["outputs"], shapes);
107 set_output_size(shapes.size());
108 for (size_t i = 0; i < shapes.size(); ++i)
109 {
110 ngraph::Shape output_shape(shapes[i]);
111 set_output_type(i, get_input_element_type(0), output_shape);
112 }
113 }
114
115 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_4)
clone_with_new_inputs(const ngraph::OutputVector & new_args) const116 std::shared_ptr<ngraph::Node> clone_with_new_inputs(const ngraph::OutputVector& new_args) const override
117 {
118 return std::make_shared<NgraphCustomOp>(new_args, params);
119 }
120 #else
copy_with_new_args(const ngraph::NodeVector & new_args) const121 std::shared_ptr<ngraph::Node> copy_with_new_args(const ngraph::NodeVector& new_args) const override
122 {
123 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_3)
124 return std::make_shared<NgraphCustomOp>(ngraph::as_output_vector(new_args), params);
125 #else
126 return std::make_shared<NgraphCustomOp>(new_args, params);
127 #endif
128 }
129 #endif
130
visit_attributes(ngraph::AttributeVisitor & visitor)131 bool visit_attributes(ngraph::AttributeVisitor& visitor) override
132 {
133 for (auto& attr : params)
134 {
135 if (attr.second.is<std::string>())
136 visitor.on_attribute(attr.first, attr.second.as<std::string>());
137 }
138 return true;
139 }
140
141 std::map<std::string, InferenceEngine::Parameter> params;
142 };
143
144
145 class InfEngineNgraphCustomLayer : public InferenceEngine::ILayerExecImpl
146 {
147 public:
148 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2)
InfEngineNgraphCustomLayer(const std::shared_ptr<ngraph::Node> & _node)149 explicit InfEngineNgraphCustomLayer(const std::shared_ptr<ngraph::Node>& _node)
150 {
151 node = std::dynamic_pointer_cast<NgraphCustomOp>(_node);
152 CV_Assert(node);
153 std::string implStr = node->params["impl"];
154 std::istringstream iss(implStr);
155 #else
156 explicit InfEngineNgraphCustomLayer(const InferenceEngine::CNNLayer& layer) : cnnLayer(layer)
157 {
158 std::istringstream iss(layer.GetParamAsString("impl"));
159 #endif
160 size_t ptr;
161 iss >> ptr;
162 cvLayer = (Layer*)ptr;
163
164 std::vector<std::vector<size_t> > shapes;
165 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2)
166 strToShapes(node->params["internals"], shapes);
167 #else
168 strToShapes(layer.GetParamAsString("internals"), shapes);
169 #endif
170 internals.resize(shapes.size());
171 for (int i = 0; i < shapes.size(); ++i)
172 internals[i].create(std::vector<int>(shapes[i].begin(), shapes[i].end()), CV_32F);
173 }
174
175 ~InfEngineNgraphCustomLayer()
176 {
177 // nothing
178 }
179
180 virtual InferenceEngine::StatusCode execute(std::vector<InferenceEngine::Blob::Ptr>& inputs,
181 std::vector<InferenceEngine::Blob::Ptr>& outputs,
182 InferenceEngine::ResponseDesc *resp) noexcept
183 {
184 std::vector<Mat> inpMats, outMats;
185 infEngineBlobsToMats(inputs, inpMats);
186 infEngineBlobsToMats(outputs, outMats);
187
188 try
189 {
190 cvLayer->forward(inpMats, outMats, internals);
191 return InferenceEngine::StatusCode::OK;
192 }
193 catch (...)
194 {
195 return InferenceEngine::StatusCode::GENERAL_ERROR;
196 }
197 }
198
199 virtual InferenceEngine::StatusCode
200 getSupportedConfigurations(std::vector<InferenceEngine::LayerConfig>& conf,
201 InferenceEngine::ResponseDesc* resp) noexcept
202 {
203 std::vector<InferenceEngine::DataConfig> inDataConfig;
204 std::vector<InferenceEngine::DataConfig> outDataConfig;
205 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2)
206 InferenceEngine::SizeVector order;
207 size_t offset = std::numeric_limits<size_t>::max();
208 for (int i = 0; i < node->get_input_size(); ++i)
209 {
210 InferenceEngine::DataConfig conf;
211 auto shape = node->input_value(i).get_shape();
212 order.resize(shape.size());
213 std::iota(order.begin(), order.end(), 0);
214 conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order, offset});
215 inDataConfig.push_back(conf);
216 }
217
218 for (int i = 0; i < node->get_output_size(); ++i)
219 {
220 InferenceEngine::DataConfig conf;
221 auto shape = node->output(i).get_shape();
222 order.resize(shape.size());
223 std::iota(order.begin(), order.end(), 0);
224 conf.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order, offset});
225 outDataConfig.push_back(conf);
226 }
227 #else
228 for (auto& it : cnnLayer.insData)
229 {
230 InferenceEngine::DataConfig conf;
231 conf.desc = it.lock()->getTensorDesc();
232 inDataConfig.push_back(conf);
233 }
234
235 for (auto& it : cnnLayer.outData)
236 {
237 InferenceEngine::DataConfig conf;
238 conf.desc = it->getTensorDesc();
239 outDataConfig.push_back(conf);
240 }
241 #endif
242
243 InferenceEngine::LayerConfig layerConfig;
244 layerConfig.inConfs = inDataConfig;
245 layerConfig.outConfs = outDataConfig;
246
247 conf.push_back(layerConfig);
248 return InferenceEngine::StatusCode::OK;
249 }
250
251 InferenceEngine::StatusCode init(InferenceEngine::LayerConfig& config,
252 InferenceEngine::ResponseDesc *resp) noexcept
253 {
254 return InferenceEngine::StatusCode::OK;
255 }
256
257 private:
258 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2)
259 std::shared_ptr<NgraphCustomOp> node;
260 #else
261 InferenceEngine::CNNLayer cnnLayer;
262 #endif
263 dnn::Layer* cvLayer;
264 std::vector<Mat> internals;
265 };
266
267 #if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2020_2)
268 class InfEngineNgraphCustomLayerFactory : public InferenceEngine::ILayerImplFactory {
269 public:
InfEngineNgraphCustomLayerFactory(const InferenceEngine::CNNLayer * layer)270 explicit InfEngineNgraphCustomLayerFactory(const InferenceEngine::CNNLayer* layer) : cnnLayer(*layer)
271 {
272 // nothing
273 }
274
275 InferenceEngine::StatusCode
getImplementations(std::vector<InferenceEngine::ILayerImpl::Ptr> & impls,InferenceEngine::ResponseDesc * resp)276 getImplementations(std::vector<InferenceEngine::ILayerImpl::Ptr>& impls,
277 InferenceEngine::ResponseDesc* resp) noexcept override
278 {
279 impls.push_back(std::make_shared<InfEngineNgraphCustomLayer>(cnnLayer));
280 return InferenceEngine::StatusCode::OK;
281 }
282
283 private:
284 InferenceEngine::CNNLayer cnnLayer;
285 };
286 #endif
287
288
289 class InfEngineNgraphExtension : public InferenceEngine::IExtension
290 {
291 public:
Unload()292 void Unload() noexcept override {}
Release()293 void Release() noexcept override { delete this; }
GetVersion(const InferenceEngine::Version * &) const294 void GetVersion(const InferenceEngine::Version*&) const noexcept override {}
295
296 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_2)
getImplTypes(const std::shared_ptr<ngraph::Node> & node)297 std::vector<std::string> getImplTypes(const std::shared_ptr<ngraph::Node>& node) override {
298 return {"CPU"};
299 }
300
getImplementation(const std::shared_ptr<ngraph::Node> & node,const std::string & implType)301 InferenceEngine::ILayerImpl::Ptr getImplementation(const std::shared_ptr<ngraph::Node>& node, const std::string& implType) override {
302 if (std::dynamic_pointer_cast<NgraphCustomOp>(node) && implType == "CPU") {
303 return std::make_shared<InfEngineNgraphCustomLayer>(node);
304 }
305 return nullptr;
306 }
307 #else
SetLogCallback(InferenceEngine::IErrorListener &)308 virtual void SetLogCallback(InferenceEngine::IErrorListener&) noexcept {}
309
getPrimitiveTypes(char ** &,unsigned int &,InferenceEngine::ResponseDesc *)310 virtual InferenceEngine::StatusCode getPrimitiveTypes(char**&, unsigned int&,
311 InferenceEngine::ResponseDesc*) noexcept
312 {
313 return InferenceEngine::StatusCode::OK;
314 }
315
getFactoryFor(InferenceEngine::ILayerImplFactory * & factory,const InferenceEngine::CNNLayer * cnnLayer,InferenceEngine::ResponseDesc * resp)316 InferenceEngine::StatusCode getFactoryFor(InferenceEngine::ILayerImplFactory*& factory,
317 const InferenceEngine::CNNLayer* cnnLayer,
318 InferenceEngine::ResponseDesc* resp) noexcept
319 {
320 if (cnnLayer->type != kOpenCVLayersType)
321 return InferenceEngine::StatusCode::NOT_IMPLEMENTED;
322 factory = new InfEngineNgraphCustomLayerFactory(cnnLayer);
323 return InferenceEngine::StatusCode::OK;
324 }
325 #endif
326 };
327
328
329
InfEngineNgraphNode(std::shared_ptr<ngraph::Node> && _node)330 InfEngineNgraphNode::InfEngineNgraphNode(std::shared_ptr<ngraph::Node>&& _node)
331 : BackendNode(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH), node(std::move(_node)) {}
332
InfEngineNgraphNode(std::shared_ptr<ngraph::Node> & _node)333 InfEngineNgraphNode::InfEngineNgraphNode(std::shared_ptr<ngraph::Node>& _node)
334 : BackendNode(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH), node(_node) {}
335
InfEngineNgraphNode(const std::vector<Ptr<BackendNode>> & nodes,Ptr<Layer> & cvLayer_,std::vector<Mat * > & inputs,std::vector<Mat> & outputs,std::vector<Mat> & internals)336 InfEngineNgraphNode::InfEngineNgraphNode(const std::vector<Ptr<BackendNode> >& nodes,
337 Ptr<Layer>& cvLayer_, std::vector<Mat*>& inputs,
338 std::vector<Mat>& outputs, std::vector<Mat>& internals)
339 : BackendNode(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH), cvLayer(cvLayer_)
340 {
341 std::ostringstream oss;
342 oss << (size_t)cvLayer.get();
343
344 std::map<std::string, InferenceEngine::Parameter> params = {
345 {"impl", oss.str()},
346 {"outputs", shapesToStr(outputs)},
347 {"internals", shapesToStr(internals)}
348 };
349
350 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2020_3)
351 ngraph::OutputVector inp_nodes;
352 #else
353 ngraph::NodeVector inp_nodes;
354 #endif
355 for (const auto& node : nodes)
356 inp_nodes.emplace_back(node.dynamicCast<InfEngineNgraphNode>()->node);
357 node = std::make_shared<NgraphCustomOp>(inp_nodes, params);
358
359 CV_Assert(!cvLayer->name.empty());
360 setName(cvLayer->name);
361 }
362
setName(const std::string & name)363 void InfEngineNgraphNode::setName(const std::string& name) {
364 node->set_friendly_name(name);
365 }
366
InfEngineNgraphNet(detail::NetImplBase & netImpl)367 InfEngineNgraphNet::InfEngineNgraphNet(detail::NetImplBase& netImpl)
368 : netImpl_(netImpl)
369 {
370 hasNetOwner = false;
371 device_name = "CPU";
372 }
373
InfEngineNgraphNet(detail::NetImplBase & netImpl,InferenceEngine::CNNNetwork & net)374 InfEngineNgraphNet::InfEngineNgraphNet(detail::NetImplBase& netImpl, InferenceEngine::CNNNetwork& net)
375 : netImpl_(netImpl)
376 , cnn(net)
377 {
378 hasNetOwner = true;
379 device_name = "CPU";
380 }
381
addOutput(const std::string & name)382 void InfEngineNgraphNet::addOutput(const std::string& name)
383 {
384 requestedOutputs.push_back(name);
385 }
386
setNodePtr(std::shared_ptr<ngraph::Node> * ptr)387 void InfEngineNgraphNet::setNodePtr(std::shared_ptr<ngraph::Node>* ptr) {
388 all_nodes.emplace((*ptr)->get_friendly_name(), ptr);
389 }
390
release()391 void InfEngineNgraphNet::release() {
392 for (auto& node : components.back()) {
393 #if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4)
394 if (!(ngraph::op::is_parameter(node) || ngraph::op::is_output(node) || ngraph::op::is_constant(node)) ) {
395 #else
396 if (!(node->is_parameter() || node->is_output() || node->is_constant()) ) {
397 #endif
398 auto it = all_nodes.find(node->get_friendly_name());
399 if (it != all_nodes.end()) {
400 unconnectedNodes.erase(*(it->second));
401 it->second->reset();
402 all_nodes.erase(it);
403 }
404 }
405 }
406 }
407
408 void InfEngineNgraphNet::dfs(std::shared_ptr<ngraph::Node>& node,
409 std::vector<std::shared_ptr<ngraph::Node>>& comp,
410 std::unordered_map<std::string, bool>& used) {
411 used[node->get_friendly_name()] = true;
412 comp.push_back(node);
413 auto inputs = node->get_users();
414 for (size_t i = 0; i < node->get_input_size(); ++i) {
415 inputs.push_back(node->input_value(i).get_node()->shared_from_this());
416 }
417
418 for (auto& to : inputs) {
419 if (!used[to->get_friendly_name()]) {
420 dfs(to, comp, used);
421 }
422 }
423 }
424
425 int InfEngineNgraphNet::getNumComponents() {
426 if (!components.empty()) {
427 return components.size();
428 }
429 std::unordered_map<std::string, bool> used;
430 auto inputs = ngraph_function->get_ordered_ops();
431 for (auto& node : inputs) {
432 used.emplace(node->get_friendly_name(), false);
433 }
434
435 for (auto& node : inputs) {
436 if (!used[node->get_friendly_name()]) {
437 std::vector<std::shared_ptr<ngraph::Node>> current_comp;
438 dfs(node, current_comp, used);
439 components.push_back(current_comp);
440 }
441 }
442 return components.size();
443 }
444
445 void InfEngineNgraphNet::createNet(Target targetId) {
446 if (!hasNetOwner)
447 {
448 CV_Assert(!unconnectedNodes.empty());
449 ngraph::ResultVector outs;
450 for (auto& node : unconnectedNodes)
451 {
452 auto out = std::make_shared<ngraph::op::Result>(node);
453 outs.push_back(out);
454 }
455 CV_Assert_N(!inputs_vec.empty(), !outs.empty());
456 ngraph_function = std::make_shared<ngraph::Function>(outs, inputs_vec);
457
458 int num_comp = getNumComponents();
459 if (num_comp > 1) {
460 for (int i = num_comp - 1; i >= 0; --i) {
461 ngraph::ResultVector outputs;
462 ngraph::ParameterVector inps;
463 for (auto& node : components.back()) {
464 #if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4)
465 if (ngraph::op::is_parameter(node)) {
466 #else
467 if (node->is_parameter()) {
468 #endif
469 auto parameter = std::dynamic_pointer_cast<ngraph::op::Parameter>(node);
470 inps.push_back(parameter);
471 }
472 #if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4)
473 else if (ngraph::op::is_output(node)) {
474 #else
475 else if (node->is_output()) {
476 #endif
477 auto result = std::dynamic_pointer_cast<ngraph::op::Result>(node);
478 outputs.push_back(result);
479 }
480 }
481 isInit = false;
482 CV_Assert_N(!inps.empty(), !outputs.empty());
483 ngraph_function = std::make_shared<ngraph::Function>(outputs, inps);
484 release();
485 components.pop_back();
486 init(targetId);
487 }
488 } else {
489 release();
490 components.clear();
491 init(targetId);
492 }
493 }
494 }
495
496 void InfEngineNgraphNet::init(Target targetId)
497 {
498 if (!hasNetOwner)
499 {
500 if (targetId == DNN_TARGET_OPENCL_FP16)
501 {
502 auto nodes = ngraph_function->get_ordered_ops();
503 for (auto& node : nodes)
504 {
505 auto parameter = std::dynamic_pointer_cast<ngraph::op::Parameter>(node);
506 if (parameter && parameter->get_element_type() == ngraph::element::f32)
507 {
508 parameter->set_element_type(ngraph::element::f16);
509 }
510 auto constant = std::dynamic_pointer_cast<ngraph::op::Constant>(node);
511 if (constant && constant->get_element_type() == ngraph::element::f32)
512 {
513 const float* floatsData = constant->get_data_ptr<float>();
514 size_t total = ngraph::shape_size(constant->get_shape());
515 Mat floats(1, total, CV_32F, (void*)floatsData);
516 Mat halfs;
517 cv::convertFp16(floats, halfs);
518
519 auto new_const = std::make_shared<ngraph::op::Constant>(ngraph::element::f16, constant->get_shape(), halfs.data);
520 new_const->set_friendly_name(constant->get_friendly_name());
521 ngraph::replace_node(constant, new_const);
522 }
523 }
524 ngraph_function->validate_nodes_and_infer_types();
525 }
526 cnn = InferenceEngine::CNNNetwork(ngraph_function);
527
528 if (DNN_IE_SERIALIZE)
529 {
530 #ifndef OPENCV_DNN_DISABLE_NETWORK_AUTO_DUMP
531 std::string dumpFileNameBase = netImpl_.getDumpFileNameBase();
532 try
533 {
534 cnn.serialize(dumpFileNameBase + "_ngraph.xml", dumpFileNameBase + "_ngraph.bin");
535 }
536 catch (const std::exception& e)
537 {
538 std::ofstream out((dumpFileNameBase + "_ngraph.error").c_str(), std::ios::out);
539 out << "Exception: " << e.what() << std::endl;
540 }
541 catch (...)
542 {
543 std::ofstream out((dumpFileNameBase + "_ngraph.error").c_str(), std::ios::out);
544 out << "Can't dump: unknown exception" << std::endl;
545 }
546 #endif
547 }
548 }
549
550 switch (targetId)
551 {
552 case DNN_TARGET_CPU:
553 device_name = "CPU";
554 break;
555 case DNN_TARGET_OPENCL:
556 case DNN_TARGET_OPENCL_FP16:
557 device_name = "GPU";
558 break;
559 case DNN_TARGET_MYRIAD:
560 device_name = "MYRIAD";
561 break;
562 case DNN_TARGET_HDDL:
563 device_name = "HDDL";
564 break;
565 case DNN_TARGET_FPGA:
566 device_name = "FPGA";
567 break;
568 default:
569 CV_Error(Error::StsNotImplemented, "Unknown target");
570 };
571
572 if (!hasNetOwner) {
573 for (size_t i = 0; i < ngraph_function->get_output_size(); ++i) {
574 auto node = ngraph_function->output(i).get_node();
575 for (size_t j = 0; j < node->get_input_size(); ++j) {
576 std::string name = node->input_value(j).get_node()->get_friendly_name();
577 auto iter = std::find(requestedOutputs.begin(), requestedOutputs.end(), name);
578 if (iter != requestedOutputs.end()) {
579 requestedOutputs.erase(iter);
580 cnn.addOutput(name);
581 }
582 }
583 }
584 }
585 for (const auto& name : requestedOutputs)
586 {
587 cnn.addOutput(name);
588 }
589
590 for (const auto& it : cnn.getInputsInfo())
591 {
592 const std::string& name = it.first;
593 auto blobIt = allBlobs.find(name);
594 CV_Assert(blobIt != allBlobs.end());
595 it.second->setPrecision(blobIt->second->getTensorDesc().getPrecision());
596 }
597
598 for (const auto& it : cnn.getOutputsInfo())
599 {
600 const std::string& name = it.first;
601 auto blobIt = allBlobs.find(name);
602 CV_Assert(blobIt != allBlobs.end());
603 it.second->setPrecision(blobIt->second->getTensorDesc().getPrecision()); // Should be always FP32
604 }
605
606 initPlugin(cnn);
607 }
608
609 ngraph::ParameterVector InfEngineNgraphNet::setInputs(const std::vector<cv::Mat>& inputs,
610 const std::vector<std::string>& names) {
611 CV_Assert_N(inputs.size() == names.size());
612 ngraph::ParameterVector current_inp;
613 for (size_t i = 0; i < inputs.size(); i++)
614 {
615 std::vector<size_t> shape = getShape<size_t>(inputs[i]);
616 auto inp = std::make_shared<ngraph::op::Parameter>(ngraph::element::f32, ngraph::Shape(shape));
617 inp->set_friendly_name(names[i]);
618
619 auto it = std::find_if(inputs_vec.begin(), inputs_vec.end(),
620 [&inp](const std::shared_ptr<ngraph::op::Parameter>& a) {
621 return a->get_friendly_name() == inp->get_friendly_name();
622 });
623 if (it == inputs_vec.end()) {
624 inputs_vec.push_back(inp);
625 current_inp.push_back(inp);
626 } else {
627 current_inp.push_back(*it);
628 }
629 }
630 return current_inp;
631 }
632
633 void InfEngineNgraphNet::setUnconnectedNodes(Ptr<InfEngineNgraphNode>& node) {
634 unconnectedNodes.insert(node->node);
635 }
636
637 void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net)
638 {
639 CV_Assert(!isInitialized());
640
641 try
642 {
643 AutoLock lock(getInitializationMutex());
644 InferenceEngine::Core& ie = getCore(device_name);
645 {
646 isInit = true;
647 std::vector<std::string> candidates;
648 std::string param_pluginPath = utils::getConfigurationParameterString("OPENCV_DNN_IE_EXTRA_PLUGIN_PATH", "");
649 if (!param_pluginPath.empty())
650 {
651 candidates.push_back(param_pluginPath);
652 }
653 bool found = false;
654 for (size_t i = 0; i != candidates.size(); ++i)
655 {
656 const std::string& libName = candidates[i];
657 try
658 {
659 InferenceEngine::IExtensionPtr extension =
660 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4)
661 std::make_shared<InferenceEngine::Extension>(libName);
662 #else
663 InferenceEngine::make_so_pointer<InferenceEngine::IExtension>(libName);
664 #endif
665
666 ie.AddExtension(extension, "CPU");
667 CV_LOG_INFO(NULL, "DNN-IE: Loaded extension plugin: " << libName);
668 found = true;
669 break;
670 }
671 catch(...) {}
672 }
673 if (!found && !candidates.empty())
674 {
675 CV_LOG_WARNING(NULL, "DNN-IE: Can't load extension plugin (extra layers for some networks). Specify path via OPENCV_DNN_IE_EXTRA_PLUGIN_PATH parameter");
676 }
677 // Some of networks can work without a library of extra layers.
678 // OpenCV fallbacks as extensions.
679 try
680 {
681 ie.AddExtension(std::make_shared<InfEngineNgraphExtension>(), "CPU");
682 }
683 catch(const std::exception& e)
684 {
685 CV_LOG_INFO(NULL, "DNN-IE: Can't register OpenCV custom layers nGraph extension: " << e.what());
686 }
687 #ifndef _WIN32
688 // Limit the number of CPU threads.
689 if (device_name == "CPU")
690 ie.SetConfig({{
691 InferenceEngine::PluginConfigParams::KEY_CPU_THREADS_NUM, format("%d", getNumThreads()),
692 }}, device_name);
693 #endif
694 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_2)
695 if (device_name.find("GPU") == 0)
696 {
697 #if OPENCV_HAVE_FILESYSTEM_SUPPORT
698 std::string cache_path = utils::fs::getCacheDirectory((std::string("dnn_ie_cache_") + device_name).c_str(), "OPENCV_DNN_IE_GPU_CACHE_DIR");
699 #else
700 std::string cache_path = utils::getConfigurationParameterString("OPENCV_DNN_IE_GPU_CACHE_DIR", "");
701 #endif
702 if (!cache_path.empty() && cache_path != "disabled")
703 {
704 CV_LOG_INFO(NULL, "OpenCV/nGraph: using GPU kernels cache: " << cache_path);
705 ie.SetConfig({{
706 InferenceEngine::PluginConfigParams::KEY_CACHE_DIR, cache_path,
707 }}, device_name);
708 }
709 }
710 #endif
711 }
712 std::map<std::string, std::string> config;
713 if (device_name == "MYRIAD" || device_name == "HDDL") {
714 #if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4)
715 config.emplace("MYRIAD_DETECT_NETWORK_BATCH", CONFIG_VALUE(NO));
716 #else
717 config.emplace("VPU_DETECT_NETWORK_BATCH", CONFIG_VALUE(NO));
718 #endif
719 }
720
721 bool isHetero = device_name == "FPGA";
722 // It is actual only for non-CPU targets and networks built in runtime using nGraph.
723 // We do not check IR models because they can be with version less than IRv10
724 if (!isHetero && device_name != "CPU" && !hasNetOwner)
725 {
726 for (auto& node : net.getFunction()->get_ops())
727 {
728 if (node->description() == kOpenCVLayersType)
729 {
730 isHetero = true;
731 break;
732 }
733 }
734 }
735 if (isHetero)
736 netExec = ie.LoadNetwork(net, "HETERO:" + device_name + ",CPU", config);
737 else
738 netExec = ie.LoadNetwork(net, device_name, config);
739 }
740 catch (const std::exception& ex)
741 {
742 CV_Error(Error::StsError, format("Failed to initialize Inference Engine backend (device = %s): %s", device_name.c_str(), ex.what()));
743 }
744 }
745
746 bool InfEngineNgraphNet::isInitialized()
747 {
748 return isInit;
749 }
750
751 bool NgraphBackendLayer::getMemoryShapes(const std::vector<MatShape> &inputs,
752 const int requiredOutputs,
753 std::vector<MatShape> &outputs,
754 std::vector<MatShape> &internals) const
755 {
756 InferenceEngine::ICNNNetwork::InputShapes inShapes = t_net.getInputShapes();
757 InferenceEngine::ICNNNetwork::InputShapes::iterator itr;
758 bool equal_flag = true;
759 size_t i = 0;
760 for (itr = inShapes.begin(); itr != inShapes.end(); ++itr)
761 {
762 InferenceEngine::SizeVector currentInShape(inputs[i].begin(), inputs[i].end());
763 if (itr->second != currentInShape)
764 {
765 itr->second = currentInShape;
766 equal_flag = false;
767 }
768 i++;
769 }
770
771 if (!equal_flag)
772 {
773 InferenceEngine::CNNNetwork curr_t_net(t_net);
774 curr_t_net.reshape(inShapes);
775 }
776 std::vector<size_t> dims = t_net.getOutputsInfo()[name]->getDims();
777 outputs.push_back(MatShape(dims.begin(), dims.end()));
778 return false;
779 }
780
781 bool NgraphBackendLayer::supportBackend(int backendId)
782 {
783 CV_LOG_DEBUG(NULL, "NgraphBackendLayer::supportBackend(" << backendId << ")");
784 return backendId == DNN_BACKEND_DEFAULT ||
785 (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH);
786 }
787
788 void NgraphBackendLayer::forward(InputArrayOfArrays inputs, OutputArrayOfArrays outputs,
789 OutputArrayOfArrays internals)
790 {
791 CV_Error(Error::StsInternal, "Choose Inference Engine as a preferable backend.");
792 }
793
794
795 static InferenceEngine::Layout estimateLayout(const Mat& m)
796 {
797 if (m.dims == 4)
798 return InferenceEngine::Layout::NCHW;
799 else if (m.dims == 3)
800 return InferenceEngine::Layout::CHW;
801 else if (m.dims == 2)
802 return InferenceEngine::Layout::NC;
803 else if (m.dims == 1)
804 return InferenceEngine::Layout::C;
805 else if (m.dims == 5)
806 return InferenceEngine::Layout::NCDHW;
807 else
808 return InferenceEngine::Layout::ANY;
809 }
810
811 static InferenceEngine::DataPtr wrapToInfEngineDataNode(const Mat& m, const std::string& name = "")
812 {
813 std::vector<size_t> shape = getShape<size_t>(m);
814 if (m.type() == CV_32F)
815 return InferenceEngine::DataPtr(new InferenceEngine::Data(name,
816 {InferenceEngine::Precision::FP32, shape, estimateLayout(m)}));
817 else if (m.type() == CV_8U)
818 return InferenceEngine::DataPtr(new InferenceEngine::Data(name,
819 {InferenceEngine::Precision::U8, shape, estimateLayout(m)}));
820 else
821 CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str()));
822 }
823
824 InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m, const std::vector<size_t>& shape,
825 InferenceEngine::Layout layout)
826 {
827 if (m.type() == CV_32F)
828 return InferenceEngine::make_shared_blob<float>(
829 {InferenceEngine::Precision::FP32, shape, layout}, (float*)m.data);
830 else if (m.type() == CV_8U)
831 return InferenceEngine::make_shared_blob<uint8_t>(
832 {InferenceEngine::Precision::U8, shape, layout}, (uint8_t*)m.data);
833 else
834 CV_Error(Error::StsNotImplemented, format("Unsupported data type %s", typeToString(m.type()).c_str()));
835 }
836
837 InferenceEngine::Blob::Ptr wrapToNgraphBlob(const Mat& m, InferenceEngine::Layout layout)
838 {
839 std::vector<size_t> shape = getShape<size_t>(m);
840 return wrapToNgraphBlob(m, shape, layout);
841 }
842
843 NgraphBackendWrapper::NgraphBackendWrapper(int targetId, const cv::Mat& m)
844 : BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, targetId)
845 {
846 dataPtr = wrapToInfEngineDataNode(m);
847 blob = wrapToNgraphBlob(m, estimateLayout(m));
848 }
849
850 NgraphBackendWrapper::NgraphBackendWrapper(Ptr<BackendWrapper> wrapper)
851 : BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, wrapper->targetId)
852 {
853 Ptr<NgraphBackendWrapper> ieWrapper = wrapper.dynamicCast<NgraphBackendWrapper>();
854 CV_Assert(!ieWrapper.empty());
855 InferenceEngine::DataPtr srcData = ieWrapper->dataPtr;
856 dataPtr = InferenceEngine::DataPtr(new InferenceEngine::Data(srcData->getName(), srcData->getTensorDesc()));
857 blob = ieWrapper->blob;
858 }
859
860 Ptr<BackendWrapper> NgraphBackendWrapper::create(Ptr<BackendWrapper> wrapper)
861 {
862 return Ptr<BackendWrapper>(new NgraphBackendWrapper(wrapper));
863 }
864
865 NgraphBackendWrapper::~NgraphBackendWrapper()
866 {
867 // nothing
868 }
869
870 void NgraphBackendWrapper::copyToHost()
871 {
872 CV_LOG_DEBUG(NULL, "NgraphBackendWrapper::copyToHost()");
873 //CV_Error(Error::StsNotImplemented, "");
874 }
875
876 void NgraphBackendWrapper::setHostDirty()
877 {
878 CV_LOG_DEBUG(NULL, "NgraphBackendWrapper::setHostDirty()");
879 //CV_Error(Error::StsNotImplemented, "");
880 }
881
882 InferenceEngine::Blob::Ptr copyBlob(const InferenceEngine::Blob::Ptr& blob)
883 {
884 InferenceEngine::Blob::Ptr copy;
885 auto description = blob->getTensorDesc();
886 InferenceEngine::Precision precision = description.getPrecision();
887 if (precision == InferenceEngine::Precision::FP32)
888 {
889 copy = InferenceEngine::make_shared_blob<float>(description);
890 }
891 else if (precision == InferenceEngine::Precision::U8)
892 {
893 copy = InferenceEngine::make_shared_blob<uint8_t>(description);
894 }
895 else
896 CV_Error(Error::StsNotImplemented, "Unsupported blob precision");
897 copy->allocate();
898 return copy;
899 }
900
901 InferenceEngine::DataPtr ngraphDataNode(const Ptr<BackendWrapper>& ptr)
902 {
903 CV_Assert(!ptr.empty());
904 Ptr<NgraphBackendWrapper> p = ptr.dynamicCast<NgraphBackendWrapper>();
905 CV_Assert(!p.empty());
906 return p->dataPtr;
907 }
908
909
910 void forwardNgraph(const std::vector<Ptr<BackendWrapper> >& outBlobsWrappers,
911 Ptr<BackendNode>& node, bool isAsync)
912 {
913 CV_Assert(!node.empty());
914 Ptr<InfEngineNgraphNode> ieNode = node.dynamicCast<InfEngineNgraphNode>();
915 CV_Assert(!ieNode.empty());
916 ieNode->net->forward(outBlobsWrappers, isAsync);
917 }
918
919 void InfEngineNgraphNet::reset()
920 {
921 allBlobs.clear();
922 infRequests.clear();
923 isInit = false;
924 }
925
926 void InfEngineNgraphNet::addBlobs(const std::vector<cv::Ptr<BackendWrapper> >& ptrs)
927 {
928 auto wrappers = ngraphWrappers(ptrs);
929 for (const auto& wrapper : wrappers)
930 {
931 std::string name = wrapper->dataPtr->getName();
932 name = name.empty() ? kDefaultInpLayerName : name;
933 allBlobs.insert({name, wrapper->blob});
934 }
935 }
936
937 void InfEngineNgraphNet::NgraphReqWrapper::makePromises(const std::vector<Ptr<BackendWrapper> >& outsWrappers)
938 {
939 auto outs = ngraphWrappers(outsWrappers);
940 outProms.clear();
941 outProms.resize(outs.size());
942 outsNames.resize(outs.size());
943 for (int i = 0; i < outs.size(); ++i)
944 {
945 outs[i]->futureMat = outProms[i].getArrayResult();
946 outsNames[i] = outs[i]->dataPtr->getName();
947 }
948 }
949
950 Mat ngraphBlobToMat(const InferenceEngine::Blob::Ptr& blob)
951 {
952 std::vector<size_t> dims = blob->getTensorDesc().getDims();
953 std::vector<int> size(dims.begin(), dims.end());
954 auto precision = blob->getTensorDesc().getPrecision();
955
956 int type = -1;
957 switch (precision)
958 {
959 case InferenceEngine::Precision::FP32: type = CV_32F; break;
960 case InferenceEngine::Precision::U8: type = CV_8U; break;
961 default:
962 CV_Error(Error::StsNotImplemented, "Unsupported blob precision");
963 }
964 return Mat(size, type, (void*)blob->buffer());
965 }
966
967 void InfEngineNgraphNet::forward(const std::vector<Ptr<BackendWrapper> >& outBlobsWrappers, bool isAsync)
968 {
969 CV_LOG_DEBUG(NULL, "InfEngineNgraphNet::forward(" << (isAsync ? "async" : "sync") << ")");
970
971 // Look for finished requests.
972 Ptr<NgraphReqWrapper> reqWrapper;
973 for (auto& wrapper : infRequests)
974 {
975 if (wrapper->isReady)
976 {
977 reqWrapper = wrapper;
978 break;
979 }
980 }
981 if (reqWrapper.empty())
982 {
983 reqWrapper = Ptr<NgraphReqWrapper>(new NgraphReqWrapper());
984 try
985 {
986 reqWrapper->req = netExec.CreateInferRequest();
987 }
988 catch (const std::exception& ex)
989 {
990 CV_Error(Error::StsAssert, format("Failed to initialize Inference Engine backend: %s", ex.what()));
991 }
992 infRequests.push_back(reqWrapper);
993
994 InferenceEngine::BlobMap inpBlobs, outBlobs;
995 for (const auto& it : cnn.getInputsInfo())
996 {
997 const std::string& name = it.first;
998 auto blobIt = allBlobs.find(name);
999 CV_Assert(blobIt != allBlobs.end());
1000 inpBlobs[name] = isAsync ? copyBlob(blobIt->second) : blobIt->second;
1001 }
1002 for (const auto& it : cnn.getOutputsInfo())
1003 {
1004 const std::string& name = it.first;
1005 auto blobIt = allBlobs.find(name);
1006 CV_Assert(blobIt != allBlobs.end());
1007 outBlobs[name] = isAsync ? copyBlob(blobIt->second) : blobIt->second;
1008 }
1009 reqWrapper->req.SetInput(inpBlobs);
1010 reqWrapper->req.SetOutput(outBlobs);
1011
1012 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4)
1013 InferenceEngine::InferRequest infRequest = reqWrapper->req;
1014 NgraphReqWrapper* wrapperPtr = reqWrapper.get();
1015 CV_Assert(wrapperPtr && "Internal error");
1016 #else
1017 InferenceEngine::IInferRequest::Ptr infRequestPtr = reqWrapper->req;
1018 CV_Assert(infRequestPtr);
1019 InferenceEngine::IInferRequest& infRequest = *infRequestPtr.get();
1020 infRequest.SetUserData(reqWrapper.get(), 0);
1021 #endif
1022
1023 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4)
1024 // do NOT capture 'reqWrapper' (smart ptr) in the lambda callback
1025 infRequest.SetCompletionCallback<std::function<void(InferenceEngine::InferRequest, InferenceEngine::StatusCode)>>(
1026 [wrapperPtr](InferenceEngine::InferRequest /*request*/, InferenceEngine::StatusCode status)
1027 #else
1028 infRequest.SetCompletionCallback(
1029 [](InferenceEngine::IInferRequest::Ptr requestPtr, InferenceEngine::StatusCode status)
1030 #endif
1031 {
1032 CV_LOG_DEBUG(NULL, "DNN(nGraph): completionCallback(" << (int)status << ")");
1033 #if !INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2021_4)
1034 CV_Assert(requestPtr);
1035 InferenceEngine::IInferRequest& request = *requestPtr.get();
1036
1037 NgraphReqWrapper* wrapperPtr;
1038 request.GetUserData((void**)&wrapperPtr, 0);
1039 CV_Assert(wrapperPtr && "Internal error");
1040 #endif
1041 NgraphReqWrapper& wrapper = *wrapperPtr;
1042
1043 size_t processedOutputs = 0;
1044 try
1045 {
1046 for (; processedOutputs < wrapper.outProms.size(); ++processedOutputs)
1047 {
1048 const std::string& name = wrapper.outsNames[processedOutputs];
1049 Mat m = ngraphBlobToMat(wrapper.req.GetBlob(name));
1050
1051 try
1052 {
1053 CV_Assert(status == InferenceEngine::StatusCode::OK);
1054 wrapper.outProms[processedOutputs].setValue(m.clone());
1055 }
1056 catch (...)
1057 {
1058 try {
1059 wrapper.outProms[processedOutputs].setException(std::current_exception());
1060 } catch(...) {
1061 CV_LOG_ERROR(NULL, "DNN: Exception occurred during async inference exception propagation");
1062 }
1063 }
1064 }
1065 }
1066 catch (...)
1067 {
1068 std::exception_ptr e = std::current_exception();
1069 for (; processedOutputs < wrapper.outProms.size(); ++processedOutputs)
1070 {
1071 try {
1072 wrapper.outProms[processedOutputs].setException(e);
1073 } catch(...) {
1074 CV_LOG_ERROR(NULL, "DNN: Exception occurred during async inference exception propagation");
1075 }
1076 }
1077 }
1078 wrapper.isReady = true;
1079 }
1080 );
1081 }
1082
1083 if (isAsync)
1084 {
1085 // Copy actual data to infer request's input blobs.
1086 for (const auto& it : cnn.getInputsInfo())
1087 {
1088 const std::string& name = it.first;
1089 auto blobIt = allBlobs.find(name);
1090 Mat srcMat = ngraphBlobToMat(blobIt->second);
1091 Mat dstMat = ngraphBlobToMat(reqWrapper->req.GetBlob(name));
1092 srcMat.copyTo(dstMat);
1093 }
1094
1095 // Set promises to output blobs wrappers.
1096 reqWrapper->makePromises(outBlobsWrappers);
1097
1098 reqWrapper->isReady = false;
1099 reqWrapper->req.StartAsync();
1100 }
1101 else
1102 {
1103 reqWrapper->req.Infer();
1104 }
1105 }
1106
1107 #else
1108 void forwardNgraph(const std::vector<Ptr<BackendWrapper> >& outBlobsWrappers,
1109 Ptr<BackendNode>& node, bool isAsync)
1110 {
1111 CV_Assert(false && "nGraph is not enabled in this OpenCV build");
1112 }
1113 #endif
1114
1115 }}
1116