Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/dnn/src/op_inf_engine.cpp
16337 views
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, Intel Corporation, all rights reserved.
6
// Third party copyrights are property of their respective owners.
7
8
#include "precomp.hpp"
9
#include "op_inf_engine.hpp"
10
#include <opencv2/dnn/shape_utils.hpp>
11
12
#ifdef HAVE_INF_ENGINE
13
#include <ie_extension.h>
14
#include <ie_plugin_dispatcher.hpp>
15
#endif // HAVE_INF_ENGINE
16
17
namespace cv { namespace dnn {
18
19
#ifdef HAVE_INF_ENGINE
20
21
InfEngineBackendNode::InfEngineBackendNode(const InferenceEngine::CNNLayerPtr& _layer)
22
: BackendNode(DNN_BACKEND_INFERENCE_ENGINE), layer(_layer) {}
23
24
void InfEngineBackendNode::connect(std::vector<Ptr<BackendWrapper> >& inputs,
25
std::vector<Ptr<BackendWrapper> >& outputs)
26
{
27
layer->insData.resize(inputs.size());
28
for (int i = 0; i < inputs.size(); ++i)
29
{
30
InferenceEngine::DataPtr dataPtr = infEngineDataNode(inputs[i]);
31
layer->insData[i] = InferenceEngine::DataWeakPtr(dataPtr);
32
dataPtr->inputTo[layer->name] = layer;
33
}
34
35
CV_Assert(!outputs.empty());
36
37
layer->outData.resize(1);
38
InferenceEngine::DataPtr dataPtr = infEngineDataNode(outputs[0]);
39
dataPtr->name = layer->name;
40
layer->outData[0] = dataPtr;
41
dataPtr->creatorLayer = InferenceEngine::CNNLayerWeakPtr(layer);
42
}
43
44
static std::vector<Ptr<InfEngineBackendWrapper> >
45
infEngineWrappers(const std::vector<Ptr<BackendWrapper> >& ptrs)
46
{
47
std::vector<Ptr<InfEngineBackendWrapper> > wrappers(ptrs.size());
48
for (int i = 0; i < ptrs.size(); ++i)
49
{
50
CV_Assert(!ptrs[i].empty());
51
wrappers[i] = ptrs[i].dynamicCast<InfEngineBackendWrapper>();
52
CV_Assert(!wrappers[i].empty());
53
}
54
return wrappers;
55
}
56
57
static InferenceEngine::Layout estimateLayout(const Mat& m)
58
{
59
if (m.dims == 4)
60
return InferenceEngine::Layout::NCHW;
61
else if (m.dims == 2)
62
return InferenceEngine::Layout::NC;
63
else
64
return InferenceEngine::Layout::ANY;
65
}
66
67
static InferenceEngine::DataPtr wrapToInfEngineDataNode(const Mat& m, const std::string& name = "")
68
{
69
std::vector<size_t> reversedShape(&m.size[0], &m.size[0] + m.dims);
70
std::reverse(reversedShape.begin(), reversedShape.end());
71
if (m.type() == CV_32F)
72
return InferenceEngine::DataPtr(
73
new InferenceEngine::Data(name, reversedShape, InferenceEngine::Precision::FP32, estimateLayout(m))
74
);
75
else if (m.type() == CV_8U)
76
return InferenceEngine::DataPtr(
77
new InferenceEngine::Data(name, reversedShape, InferenceEngine::Precision::U8, estimateLayout(m))
78
);
79
else
80
CV_Error(Error::StsNotImplemented, format("Unsupported data type %d", m.type()));
81
}
82
83
InferenceEngine::Blob::Ptr wrapToInfEngineBlob(const Mat& m, const std::vector<size_t>& shape,
84
InferenceEngine::Layout layout)
85
{
86
if (m.type() == CV_32F)
87
return InferenceEngine::make_shared_blob<float>(InferenceEngine::Precision::FP32,
88
layout, shape, (float*)m.data);
89
else if (m.type() == CV_8U)
90
return InferenceEngine::make_shared_blob<uint8_t>(InferenceEngine::Precision::U8,
91
layout, shape, (uint8_t*)m.data);
92
else
93
CV_Error(Error::StsNotImplemented, format("Unsupported data type %d", m.type()));
94
}
95
96
InferenceEngine::Blob::Ptr wrapToInfEngineBlob(const Mat& m, InferenceEngine::Layout layout)
97
{
98
std::vector<size_t> reversedShape(&m.size[0], &m.size[0] + m.dims);
99
std::reverse(reversedShape.begin(), reversedShape.end());
100
return wrapToInfEngineBlob(m, reversedShape, layout);
101
}
102
103
InferenceEngine::DataPtr infEngineDataNode(const Ptr<BackendWrapper>& ptr)
104
{
105
CV_Assert(!ptr.empty());
106
Ptr<InfEngineBackendWrapper> p = ptr.dynamicCast<InfEngineBackendWrapper>();
107
CV_Assert(!p.empty());
108
return p->dataPtr;
109
}
110
111
InfEngineBackendWrapper::InfEngineBackendWrapper(int targetId, const cv::Mat& m)
112
: BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE, targetId)
113
{
114
dataPtr = wrapToInfEngineDataNode(m);
115
blob = wrapToInfEngineBlob(m, estimateLayout(m));
116
}
117
118
InfEngineBackendWrapper::InfEngineBackendWrapper(Ptr<BackendWrapper> wrapper)
119
: BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE, wrapper->targetId)
120
{
121
Ptr<InfEngineBackendWrapper> ieWrapper = wrapper.dynamicCast<InfEngineBackendWrapper>();
122
CV_Assert(!ieWrapper.empty());
123
InferenceEngine::DataPtr srcData = ieWrapper->dataPtr;
124
dataPtr = InferenceEngine::DataPtr(
125
new InferenceEngine::Data(srcData->name, srcData->dims, srcData->precision,
126
srcData->layout)
127
);
128
blob = ieWrapper->blob;
129
}
130
131
Ptr<BackendWrapper> InfEngineBackendWrapper::create(Ptr<BackendWrapper> wrapper)
132
{
133
return Ptr<BackendWrapper>(new InfEngineBackendWrapper(wrapper));
134
}
135
136
InfEngineBackendWrapper::~InfEngineBackendWrapper()
137
{
138
139
}
140
141
void InfEngineBackendWrapper::copyToHost()
142
{
143
144
}
145
146
void InfEngineBackendWrapper::setHostDirty()
147
{
148
149
}
150
151
InfEngineBackendNet::InfEngineBackendNet()
152
{
153
targetDevice = InferenceEngine::TargetDevice::eCPU;
154
precision = InferenceEngine::Precision::FP32;
155
}
156
157
InfEngineBackendNet::InfEngineBackendNet(InferenceEngine::CNNNetwork& net)
158
{
159
targetDevice = InferenceEngine::TargetDevice::eCPU;
160
precision = InferenceEngine::Precision::FP32;
161
inputs = net.getInputsInfo();
162
outputs = net.getOutputsInfo();
163
layers.resize(net.layerCount()); // A hack to execute InfEngineBackendNet::layerCount correctly.
164
netOwner = net;
165
}
166
167
void InfEngineBackendNet::Release() CV_NOEXCEPT
168
{
169
layers.clear();
170
inputs.clear();
171
outputs.clear();
172
}
173
174
void InfEngineBackendNet::setPrecision(InferenceEngine::Precision p) CV_NOEXCEPT
175
{
176
precision = p;
177
}
178
179
InferenceEngine::Precision InfEngineBackendNet::getPrecision() CV_NOEXCEPT
180
{
181
return precision;
182
}
183
184
InferenceEngine::Precision InfEngineBackendNet::getPrecision() const CV_NOEXCEPT
185
{
186
return precision;
187
}
188
189
// Assume that outputs of network is unconnected blobs.
190
void InfEngineBackendNet::getOutputsInfo(InferenceEngine::OutputsDataMap &outputs_) CV_NOEXCEPT
191
{
192
const_cast<const InfEngineBackendNet*>(this)->getOutputsInfo(outputs_);
193
}
194
void InfEngineBackendNet::getOutputsInfo(InferenceEngine::OutputsDataMap &outputs_) const CV_NOEXCEPT
195
{
196
outputs_ = outputs;
197
}
198
199
// Returns input references that aren't connected to internal outputs.
200
void InfEngineBackendNet::getInputsInfo(InferenceEngine::InputsDataMap &inputs_) CV_NOEXCEPT
201
{
202
const_cast<const InfEngineBackendNet*>(this)->getInputsInfo(inputs_);
203
}
204
205
// Returns input references that aren't connected to internal outputs.
206
void InfEngineBackendNet::getInputsInfo(InferenceEngine::InputsDataMap &inputs_) const CV_NOEXCEPT
207
{
208
inputs_ = inputs;
209
}
210
211
InferenceEngine::InputInfo::Ptr InfEngineBackendNet::getInput(const std::string &inputName) CV_NOEXCEPT
212
{
213
return const_cast<const InfEngineBackendNet*>(this)->getInput(inputName);
214
}
215
216
InferenceEngine::InputInfo::Ptr InfEngineBackendNet::getInput(const std::string &inputName) const CV_NOEXCEPT
217
{
218
const auto& it = inputs.find(inputName);
219
CV_Assert(it != inputs.end());
220
return it->second;
221
}
222
223
void InfEngineBackendNet::getName(char*, size_t) CV_NOEXCEPT
224
{
225
}
226
227
void InfEngineBackendNet::getName(char*, size_t) const CV_NOEXCEPT
228
{
229
}
230
231
const std::string& InfEngineBackendNet::getName() const CV_NOEXCEPT
232
{
233
return name;
234
}
235
236
size_t InfEngineBackendNet::layerCount() CV_NOEXCEPT
237
{
238
return const_cast<const InfEngineBackendNet*>(this)->layerCount();
239
}
240
241
size_t InfEngineBackendNet::layerCount() const CV_NOEXCEPT
242
{
243
return layers.size();
244
}
245
246
InferenceEngine::DataPtr& InfEngineBackendNet::getData(const char *dname) CV_NOEXCEPT
247
{
248
CV_Error(Error::StsNotImplemented, "");
249
return outputs.begin()->second; // Just return something.
250
}
251
252
void InfEngineBackendNet::addLayer(const InferenceEngine::CNNLayerPtr &layer) CV_NOEXCEPT
253
{
254
layers.push_back(layer);
255
inputs.clear();
256
outputs.clear();
257
}
258
259
InferenceEngine::StatusCode
260
InfEngineBackendNet::addOutput(const std::string &layerName, size_t outputIndex,
261
InferenceEngine::ResponseDesc *resp) CV_NOEXCEPT
262
{
263
for (const auto& l : layers)
264
{
265
for (const InferenceEngine::DataPtr& out : l->outData)
266
{
267
if (out->name == layerName)
268
{
269
outputs[out->name] = out;
270
return InferenceEngine::StatusCode::OK;
271
}
272
}
273
}
274
CV_Error(Error::StsObjectNotFound, "Cannot find a layer " + layerName);
275
return InferenceEngine::StatusCode::OK;
276
}
277
278
InferenceEngine::StatusCode
279
InfEngineBackendNet::getLayerByName(const char *layerName, InferenceEngine::CNNLayerPtr &out,
280
InferenceEngine::ResponseDesc *resp) CV_NOEXCEPT
281
{
282
return const_cast<const InfEngineBackendNet*>(this)->getLayerByName(layerName, out, resp);
283
}
284
285
InferenceEngine::StatusCode InfEngineBackendNet::getLayerByName(const char *layerName,
286
InferenceEngine::CNNLayerPtr &out,
287
InferenceEngine::ResponseDesc *resp) const CV_NOEXCEPT
288
{
289
for (auto& l : layers)
290
{
291
if (l->name == layerName)
292
{
293
out = l;
294
return InferenceEngine::StatusCode::OK;
295
}
296
}
297
CV_Error(Error::StsObjectNotFound, cv::format("Cannot find a layer %s", layerName));
298
return InferenceEngine::StatusCode::NOT_FOUND;
299
}
300
301
void InfEngineBackendNet::setTargetDevice(InferenceEngine::TargetDevice device) CV_NOEXCEPT
302
{
303
if (device != InferenceEngine::TargetDevice::eCPU &&
304
device != InferenceEngine::TargetDevice::eGPU &&
305
device != InferenceEngine::TargetDevice::eMYRIAD)
306
CV_Error(Error::StsNotImplemented, "");
307
targetDevice = device;
308
}
309
310
InferenceEngine::TargetDevice InfEngineBackendNet::getTargetDevice() CV_NOEXCEPT
311
{
312
return targetDevice;
313
}
314
315
InferenceEngine::TargetDevice InfEngineBackendNet::getTargetDevice() const CV_NOEXCEPT
316
{
317
return targetDevice;
318
}
319
320
InferenceEngine::StatusCode InfEngineBackendNet::setBatchSize(const size_t) CV_NOEXCEPT
321
{
322
CV_Error(Error::StsNotImplemented, "");
323
return InferenceEngine::StatusCode::OK;
324
}
325
326
InferenceEngine::StatusCode InfEngineBackendNet::setBatchSize(size_t size, InferenceEngine::ResponseDesc *responseDesc) CV_NOEXCEPT
327
{
328
CV_Error(Error::StsNotImplemented, "");
329
return InferenceEngine::StatusCode::OK;
330
}
331
332
size_t InfEngineBackendNet::getBatchSize() const CV_NOEXCEPT
333
{
334
size_t batchSize = 0;
335
for (const auto& inp : inputs)
336
{
337
CV_Assert(inp.second);
338
std::vector<size_t> dims = inp.second->getDims();
339
CV_Assert(!dims.empty());
340
if (batchSize != 0)
341
CV_Assert(batchSize == dims.back());
342
else
343
batchSize = dims.back();
344
}
345
return batchSize;
346
}
347
348
#if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2018R2)
349
InferenceEngine::StatusCode InfEngineBackendNet::AddExtension(const InferenceEngine::IShapeInferExtensionPtr &extension, InferenceEngine::ResponseDesc *resp) CV_NOEXCEPT
350
{
351
CV_Error(Error::StsNotImplemented, "");
352
return InferenceEngine::StatusCode::OK;
353
}
354
355
InferenceEngine::StatusCode InfEngineBackendNet::reshape(const InferenceEngine::ICNNNetwork::InputShapes &inputShapes, InferenceEngine::ResponseDesc *resp) CV_NOEXCEPT
356
{
357
CV_Error(Error::StsNotImplemented, "");
358
return InferenceEngine::StatusCode::OK;
359
}
360
#endif
361
362
void InfEngineBackendNet::init(int targetId)
363
{
364
if (inputs.empty())
365
{
366
// Collect all external input blobs.
367
inputs.clear();
368
std::map<std::string, InferenceEngine::DataPtr> internalOutputs;
369
for (const auto& l : layers)
370
{
371
for (const InferenceEngine::DataWeakPtr& ptr : l->insData)
372
{
373
InferenceEngine::DataPtr inp(ptr);
374
if (internalOutputs.find(inp->name) == internalOutputs.end())
375
{
376
InferenceEngine::InputInfo::Ptr inpInfo(new InferenceEngine::InputInfo());
377
inpInfo->setInputData(inp);
378
if (inputs.find(inp->name) == inputs.end())
379
inputs[inp->name] = inpInfo;
380
}
381
}
382
for (const InferenceEngine::DataPtr& out : l->outData)
383
{
384
// TODO: Replace to uniqueness assertion.
385
if (internalOutputs.find(out->name) == internalOutputs.end())
386
internalOutputs[out->name] = out;
387
}
388
}
389
CV_Assert(!inputs.empty());
390
}
391
392
if (outputs.empty())
393
{
394
// Add all unconnected blobs to output blobs.
395
InferenceEngine::OutputsDataMap unconnectedOuts;
396
for (const auto& l : layers)
397
{
398
// Add all outputs.
399
for (const InferenceEngine::DataPtr& out : l->outData)
400
{
401
// TODO: Replace to uniqueness assertion.
402
if (unconnectedOuts.find(out->name) == unconnectedOuts.end())
403
unconnectedOuts[out->name] = out;
404
}
405
// Remove internally connected outputs.
406
for (const InferenceEngine::DataWeakPtr& inp : l->insData)
407
{
408
unconnectedOuts.erase(InferenceEngine::DataPtr(inp)->name);
409
}
410
}
411
CV_Assert(!unconnectedOuts.empty());
412
413
for (auto it = unconnectedOuts.begin(); it != unconnectedOuts.end(); ++it)
414
{
415
outputs[it->first] = it->second;
416
}
417
}
418
419
// Set up input blobs.
420
inpBlobs.clear();
421
for (const auto& it : inputs)
422
{
423
CV_Assert(allBlobs.find(it.first) != allBlobs.end());
424
inpBlobs[it.first] = allBlobs[it.first];
425
it.second->setPrecision(inpBlobs[it.first]->precision());
426
}
427
428
// Set up output blobs.
429
outBlobs.clear();
430
for (const auto& it : outputs)
431
{
432
CV_Assert(allBlobs.find(it.first) != allBlobs.end());
433
outBlobs[it.first] = allBlobs[it.first];
434
}
435
436
switch (targetId)
437
{
438
case DNN_TARGET_CPU: setTargetDevice(InferenceEngine::TargetDevice::eCPU); break;
439
case DNN_TARGET_OPENCL_FP16:
440
setPrecision(InferenceEngine::Precision::FP16);
441
/* Falls through. */
442
case DNN_TARGET_OPENCL: setTargetDevice(InferenceEngine::TargetDevice::eGPU); break;
443
case DNN_TARGET_MYRIAD:
444
{
445
setPrecision(InferenceEngine::Precision::FP16);
446
setTargetDevice(InferenceEngine::TargetDevice::eMYRIAD); break;
447
}
448
default:
449
CV_Error(Error::StsError, format("Unknown target identifier: %d", targetId));
450
}
451
452
if (!isInitialized())
453
initPlugin(*this);
454
}
455
456
static std::map<InferenceEngine::TargetDevice, InferenceEngine::InferenceEnginePluginPtr> sharedPlugins;
457
458
void InfEngineBackendNet::initPlugin(InferenceEngine::ICNNNetwork& net)
459
{
460
CV_Assert(!isInitialized());
461
462
try
463
{
464
auto pluginIt = sharedPlugins.find(targetDevice);
465
if (pluginIt != sharedPlugins.end())
466
{
467
enginePtr = pluginIt->second;
468
}
469
else
470
{
471
enginePtr = InferenceEngine::PluginDispatcher({""}).getSuitablePlugin(targetDevice);
472
sharedPlugins[targetDevice] = enginePtr;
473
474
if (targetDevice == InferenceEngine::TargetDevice::eCPU)
475
{
476
std::string suffixes[] = {"_avx2", "_sse4", ""};
477
bool haveFeature[] = {
478
checkHardwareSupport(CPU_AVX2),
479
checkHardwareSupport(CPU_SSE4_2),
480
true
481
};
482
for (int i = 0; i < 3; ++i)
483
{
484
if (!haveFeature[i])
485
continue;
486
#ifdef _WIN32
487
std::string libName = "cpu_extension" + suffixes[i] + ".dll";
488
#else
489
std::string libName = "libcpu_extension" + suffixes[i] + ".so";
490
#endif // _WIN32
491
try
492
{
493
InferenceEngine::IExtensionPtr extension =
494
InferenceEngine::make_so_pointer<InferenceEngine::IExtension>(libName);
495
enginePtr->AddExtension(extension, 0);
496
break;
497
}
498
catch(...) {}
499
}
500
// Some of networks can work without a library of extra layers.
501
}
502
}
503
plugin = InferenceEngine::InferencePlugin(enginePtr);
504
505
netExec = plugin.LoadNetwork(net, {});
506
infRequest = netExec.CreateInferRequest();
507
infRequest.SetInput(inpBlobs);
508
infRequest.SetOutput(outBlobs);
509
}
510
catch (const std::exception& ex)
511
{
512
CV_Error(Error::StsAssert, format("Failed to initialize Inference Engine backend: %s", ex.what()));
513
}
514
}
515
516
bool InfEngineBackendNet::isInitialized()
517
{
518
return (bool)enginePtr;
519
}
520
521
void InfEngineBackendNet::addBlobs(const std::vector<Ptr<BackendWrapper> >& ptrs)
522
{
523
auto wrappers = infEngineWrappers(ptrs);
524
for (const auto& wrapper : wrappers)
525
{
526
allBlobs.insert({wrapper->dataPtr->name, wrapper->blob});
527
}
528
}
529
530
void InfEngineBackendNet::forward()
531
{
532
infRequest.Infer();
533
}
534
535
Mat infEngineBlobToMat(const InferenceEngine::Blob::Ptr& blob)
536
{
537
// NOTE: Inference Engine sizes are reversed.
538
std::vector<size_t> dims = blob->dims();
539
std::vector<int> size(dims.rbegin(), dims.rend());
540
return Mat(size, CV_32F, (void*)blob->buffer());
541
}
542
543
InfEngineBackendLayer::InfEngineBackendLayer(const InferenceEngine::DataPtr& output_)
544
{
545
output = output_;
546
}
547
548
bool InfEngineBackendLayer::getMemoryShapes(const std::vector<MatShape> &inputs,
549
const int requiredOutputs,
550
std::vector<MatShape> &outputs,
551
std::vector<MatShape> &internals) const
552
{
553
std::vector<size_t> dims = output->dims;
554
std::vector<int> shape(dims.rbegin(), dims.rend());
555
outputs.assign(1, shape);
556
return false;
557
}
558
559
bool InfEngineBackendLayer::supportBackend(int backendId)
560
{
561
return backendId == DNN_BACKEND_DEFAULT ||
562
backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine();
563
}
564
565
void InfEngineBackendLayer::forward(InputArrayOfArrays inputs, OutputArrayOfArrays outputs,
566
OutputArrayOfArrays internals)
567
{
568
CV_Error(Error::StsInternal, "Choose Inference Engine as a preferable backend.");
569
}
570
571
InferenceEngine::TBlob<int16_t>::Ptr convertFp16(const InferenceEngine::Blob::Ptr& blob)
572
{
573
auto halfs = InferenceEngine::make_shared_blob<int16_t>(InferenceEngine::Precision::FP16, blob->layout(), blob->dims());
574
halfs->allocate();
575
Mat floatsData(1, blob->size(), CV_32F, blob->buffer());
576
Mat halfsData(1, blob->size(), CV_16SC1, halfs->buffer());
577
convertFp16(floatsData, halfsData);
578
return halfs;
579
}
580
581
#endif // HAVE_INF_ENGINE
582
583
bool haveInfEngine()
584
{
585
#ifdef HAVE_INF_ENGINE
586
return true;
587
#else
588
return false;
589
#endif // HAVE_INF_ENGINE
590
}
591
592
void forwardInfEngine(Ptr<BackendNode>& node)
593
{
594
CV_Assert(haveInfEngine());
595
#ifdef HAVE_INF_ENGINE
596
CV_Assert(!node.empty());
597
Ptr<InfEngineBackendNode> ieNode = node.dynamicCast<InfEngineBackendNode>();
598
CV_Assert(!ieNode.empty());
599
ieNode->net->forward();
600
#endif // HAVE_INF_ENGINE
601
}
602
603
CV__DNN_INLINE_NS_BEGIN
604
605
void resetMyriadDevice()
606
{
607
#ifdef HAVE_INF_ENGINE
608
sharedPlugins.erase(InferenceEngine::TargetDevice::eMYRIAD);
609
#endif // HAVE_INF_ENGINE
610
}
611
612
CV__DNN_INLINE_NS_END
613
}} // namespace dnn, namespace cv
614
615