1 #distutils: language=c++
2 from cython.operator cimport dereference as deref
3 from .cimport ie_api_impl_defs as C
4 from .ie_api_impl_defs cimport Blob, TensorDesc, SizeVector, Precision
5 from libcpp.string cimport string
6 from libcpp.vector cimport vector
7 from libcpp.pair cimport pair
8 from libcpp.map cimport map
9 from libcpp.memory cimport unique_ptr
10 from libc.stdint cimport int64_t
13 from copy import deepcopy
15 from collections import OrderedDict
17 cdef extern from "<utility>" namespace "std" nogil:
18 cdef unique_ptr[C.IEExecNetwork] move(unique_ptr[C.IEExecNetwork])
20 cdef string to_std_string(str py_string):
21 return py_string.encode()
23 cdef to_py_string(const string & std_string):
24 return bytes(std_string).decode()
26 cdef dict_to_c_map(py_dict):
27 cdef map[string, string] c_map
28 for k, v in py_dict.items():
29 if type(k) != str or type(v) != str:
30 raise TypeError("Only string keys and values are allowed!")
31 c_map[k.encode()] = v.encode()
34 supported_precisions = ["FP32", "FP16", "Q78", "I32", "I16", "I8", "U32", "U16"]
35 supported_layouts = ["NCHW", "NHWC", "OIHW", "C", "CHW", "HW", "NC", "CN", "BLOCKED", "NCDHW"]
36 known_plugins = ['CPU', 'GPU', 'FPGA', 'MYRIAD', 'HETERO', 'HDDL']
39 return C.get_version().decode()
41 cdef class IENetLayer:
44 return self.impl.name.decode()
47 return self.impl.type.decode()
50 return self.impl.precision.decode()
53 return self.impl.affinity.decode()
56 cdef map[string, Blob.Ptr] c_weights_map
57 c_weights_map = self.impl.getWeights()
59 cdef BlobBuffer weights_buffer
60 for weights in c_weights_map:
61 weights_buffer = BlobBuffer()
62 weights_buffer.reset(weights.second)
63 weights_map[weights.first.decode()] = weights_buffer.to_numpy()
68 return {k.decode(): v.decode() for k, v in self.impl.params}
71 cdef vector[string] c_parents = self.impl.parents
73 return [parent.decode() for parent in c_parents]
76 cdef vector[string] c_children = self.impl.children
78 return [child.decode() for child in c_children]
81 string_shape = self.impl.shape.decode()
82 return [int(i) for i in string_shape.split(' ')]
85 return self.impl.layout.decode()
87 def affinity(self, target_affinity):
88 self.impl.setAffinity(target_affinity.encode())
90 def params(self, params_map):
91 self.impl.setParams(dict_to_c_map(params_map))
94 def precision(self, precision: str):
95 self.impl.setPrecision(precision.upper().encode())
100 return self.impl.precision.decode()
103 return self.impl.layout.decode()
106 return self.impl.dims
109 def precision(self, precision):
110 if precision.upper() not in supported_precisions:
111 raise AttributeError(
112 "Unsupported precision {}! List of supported precisions: {}".format(precision, supported_precisions))
113 self.impl.setPrecision(precision.encode())
115 def layout(self, layout):
116 if layout.upper() not in supported_layouts:
117 raise AttributeError(
118 "Unsupported layout {}! List of supported layouts: {}".format(layout, supported_layouts))
119 self.impl.setLayout(layout.encode())
121 cdef class OutputInfo:
124 return self.impl.precision.decode()
127 return self.impl.layout.decode()
130 return self.impl.dims
132 def precision(self, precision):
133 if precision.upper() not in supported_precisions:
134 raise AttributeError(
135 "Unsupported precision {}! List of supported precisions: {}".format(precision, supported_precisions))
136 self.impl.setPrecision(precision.encode())
138 cdef class ExecutableNetwork:
144 def infer(self, inputs=None):
145 current_request = self.requests[0]
146 current_request.infer(inputs)
147 return deepcopy(current_request.outputs)
149 def start_async(self, request_id, inputs=None):
150 if request_id not in list(range(len(self.requests))):
151 raise ValueError("Incorrect request_id specified!")
152 current_request = self.requests[request_id]
153 current_request.async_infer(inputs)
154 return current_request
159 for i in range(deref(self.impl).infer_requests.size()):
160 infer_request = InferRequest()
161 infer_request.impl = &(deref(self.impl).infer_requests[i])
162 infer_request._inputs_list = self.inputs
163 infer_request._outputs_list = self.outputs
164 requests.append(infer_request)
167 cdef class InferRequest:
169 self._inputs_list = []
170 self._outputs_list = []
172 cpdef BlobBuffer _get_blob_buffer(self, const string & blob_name):
173 cdef BlobBuffer buffer = BlobBuffer()
174 cdef Blob.Ptr blob_ptr
175 deref(self.impl).getBlobPtr(blob_name, blob_ptr)
176 buffer.reset(blob_ptr)
179 cpdef infer(self, inputs=None):
180 if inputs is not None:
181 self._fill_inputs(inputs)
183 deref(self.impl).infer()
185 cpdef async_infer(self, inputs=None):
186 if inputs is not None:
187 self._fill_inputs(inputs)
189 deref(self.impl).infer_async()
191 cpdef wait(self, timeout=None):
194 return deref(self.impl).wait(<int64_t> timeout)
196 cpdef get_perf_counts(self):
197 cdef map[string, C.ProfileInfo] c_profile = deref(self.impl).getPerformanceCounts()
201 # TODO: add execution index. Check if unsigned int is properly converted to int in python.
202 profile[l.first.decode()] = {"status": info.status.decode(), "exec_type": info.exec_type.decode(),
203 "layer_type": info.layer_type.decode(), "real_time": info.real_time,
204 "cpu_time": info.cpu_time}
210 for input in self._inputs_list:
211 inputs[input] = self._get_blob_buffer(input.encode()).to_numpy()
217 for output in self._outputs_list:
218 outputs[output] = self._get_blob_buffer(output.encode()).to_numpy()
219 return deepcopy(outputs)
223 return self.impl.exec_time
225 def set_batch(self, size):
227 raise ValueError("Batch size should be positive integer number but {} specified".format(size))
228 deref(self.impl).setBatch(size)
230 def _fill_inputs(self, inputs):
231 for k, v in inputs.items():
232 assert k in self._inputs_list, "No input with name {} found in network".format(k)
233 self.inputs[k][:] = v
237 def __init__(self, min: tuple = (), max: tuple = ()):
249 cdef class LayersStatsMap(dict):
250 def update(self, other=None, **kwargs):
251 super(LayersStatsMap, self).update(other, **kwargs)
252 cdef map[string, map[string, vector[float]]] c_stats_map
253 cdef map[string, vector[float]] c_node_stats
254 for k, v in self.items():
255 c_node_stats["min".encode()] = v.min
256 c_node_stats["max".encode()] = v.max
257 c_stats_map[k.encode()] = c_node_stats
258 self.net_impl.setStats(c_stats_map)
260 cdef class IENetwork:
261 def __cinit__(self, model: str="", weights: str=""):
264 if model and weights:
265 if not os.path.isfile(model):
266 raise Exception("Path to the model {} doesn't exists or it's a directory".format(model))
267 if not os.path.isfile(weights):
268 raise Exception("Path to the weights {} doesn't exists or it's a directory".format(weights))
269 model_ = model.encode()
270 weights_ = weights.encode()
271 self.impl = C.IENetwork(model_, weights_)
273 self.impl = C.IENetwork()
276 name = bytes(self.impl.name)
281 cdef map[string, C.InputInfo] c_inputs = self.impl.getInputs()
283 cdef InputInfo in_info
284 for input in c_inputs:
285 in_info = InputInfo()
286 in_info.impl = input.second
287 inputs[input.first.decode()] = in_info
292 cdef map[string, C.OutputInfo] c_outputs = self.impl.getOutputs()
294 cdef OutputInfo out_info
295 for out in c_outputs:
296 out_info = OutputInfo()
297 out_info.impl = out.second
298 outputs[out.first.decode()] = out_info
302 def batch_size(self):
303 return self.impl.batch_size
306 def batch_size(self, batch: int):
308 raise AttributeError("Invalid batch size {}! Batch size should be positive integer value".format(batch))
309 self.impl.setBatch(batch)
310 self.impl.batch_size = batch
314 cdef vector[pair[string, C.IENetLayer]] c_layers = self.impl.getLayers()
315 layers = OrderedDict()
316 cdef IENetLayer net_l = IENetLayer()
319 net_l.impl = l.second
320 layers[l.first.decode()] = net_l
324 cdef map[string, map[string, vector[float]]] c_stats_map = self.impl.getStats()
325 py_stats_map = LayersStatsMap()
326 py_stats_map.net_impl = self.impl
327 for it in c_stats_map:
328 stats_map = LayersStatsMap()
329 py_stats_map[it.first.decode()] = LayerStats(min=tuple(it.second["min".encode()]),
330 max=tuple(it.second["max".encode()]))
334 def from_ir(cls, model: str, weights: str):
335 warnings.filterwarnings("always",category=DeprecationWarning)
336 warnings.warn("from_ir() method of IENetwork is deprecated. "
337 "Please use IENetwork class constructor to create valid IENetwork instance",
339 if not os.path.isfile(model):
340 raise Exception("Path to the model {} doesn't exists or it's a directory".format(model))
341 if not os.path.isfile(weights):
342 raise Exception("Path to the weights {} doesn't exists or it's a directory".format(weights))
343 cdef IENetwork net = IENetwork(model, weights)
346 # TODO: Use enum with precision type instead of srting parameter when python2 support will not be required.
347 def add_outputs(self, outputs, precision="FP32"):
348 if precision.upper() not in supported_precisions:
349 raise AttributeError(
350 "Unsupported precision {}! List of supported precisions: {}".format(precision, supported_precisions))
351 if not isinstance(outputs, list):
353 cdef vector[string] _outputs
355 _outputs.push_back(l.encode())
356 self.impl.addOutputs(_outputs, precision.upper().encode())
358 def serialize(self, path_to_xml, path_to_bin):
359 self.impl.serialize(path_to_xml.encode(), path_to_bin.encode())
360 def reshape(self, input_shapes: dict):
361 cdef map[string, vector[size_t]] c_input_shapes;
362 cdef vector[size_t] c_shape
363 net_inputs = self.inputs
364 for input, shape in input_shapes.items():
366 if input not in net_inputs:
367 raise AttributeError("Specified {} layer not in network inputs {}! ".format(input, net_inputs))
370 c_input_shapes[input.encode()] = c_shape
371 self.impl.reshape(c_input_shapes)
374 def __cinit__(self, device: str, plugin_dirs=None):
375 plugin_base = device.split(':')[0]
376 if plugin_base not in known_plugins:
377 raise ValueError("Unknown plugin: {}, expected one of: {}"
378 .format(plugin_base, ",".join(known_plugins)))
379 if plugin_dirs is None:
381 elif isinstance(plugin_dirs, str):
382 plugin_dirs = [plugin_dirs]
384 # add package directory to plugin_dirs
385 lib_location = os.path.dirname(os.path.realpath(__file__))
386 plugin_dirs.append(lib_location)
388 cpdef string device_ = <string> device.encode()
389 cdef vector[string] dirs_
390 for d in plugin_dirs:
391 dirs_.push_back(<string> d.encode())
393 self.impl = C.IEPlugin(device_, dirs_)
395 cpdef ExecutableNetwork load(self, IENetwork network, int num_requests=1, config=None):
396 if num_requests <= 0:
398 "Incorrect number of requests specified: {}. Expected positive integer number.".format(num_requests))
399 cdef ExecutableNetwork exec_net = ExecutableNetwork()
400 cdef map[string, string] c_config
403 for k, v in config.items():
404 c_config[to_std_string(k)] = to_std_string(v)
405 exec_net.plugin_impl = self.impl
406 exec_net.impl = move(self.impl.load(network.impl, num_requests, c_config))
407 exec_net.inputs = network.inputs.keys()
408 exec_net.outputs = list(network.outputs.keys())
411 cpdef void set_initial_affinity(self, IENetwork net) except *:
412 if self.device.find("HETERO") == -1:
413 raise RuntimeError("set_initial_affinity method applicable only for HETERO device")
414 self.impl.setInitialAffinity(net.impl)
416 cpdef set get_supported_layers(self, IENetwork net):
417 return set([l.decode() for l in self.impl.queryNetwork(net.impl)])
421 device_name = bytes(self.impl.device_name)
422 return to_py_string(device_name)
426 version = bytes(self.impl.version)
427 return version.decode()
429 cpdef void add_cpu_extension(self, str extension_path) except *:
430 if self.device.find("CPU") == -1:
431 raise RuntimeError("add_cpu_extension method applicable only for CPU or HETERO devices")
432 cdef string extension_str = extension_path.encode()
433 self.impl.addCpuExtension(extension_str)
435 cpdef void set_config(self, config):
436 cdef map[string, string] c_config
437 for k, v in config.items():
438 c_config[to_std_string(k)] = to_std_string(v)
439 self.impl.setConfig(c_config)
442 cdef class BlobBuffer:
443 """Copy-less accessor for Inference Engine Blob"""
445 cdef reset(self, Blob.Ptr & ptr):
447 cdef TensorDesc desc = deref(ptr).getTensorDesc()
448 cdef SizeVector shape = desc.getDims()
449 cdef Py_ssize_t itemsize = deref(ptr).element_size()
450 self.strides.resize(shape.size())
451 self.shape.resize(shape.size())
453 total_stride = itemsize
454 # dims are in row major (C - style),
455 # thence strides are computed starting from latest dimension
456 for i in reversed(range(shape.size())):
457 self.strides[i] = total_stride
458 self.shape[i] = shape[i]
459 total_stride *= shape[i]
461 self.total_stride = total_stride
462 self.format = self._get_blob_format(desc)
463 self.item_size = itemsize
465 def __getbuffer__(self, Py_buffer *buffer, int flags):
466 buffer.buf = C.get_buffer[char](deref(self.ptr))
467 buffer.format = self.format
468 buffer.internal = NULL
469 buffer.itemsize = self.item_size
470 buffer.len = self.total_stride
471 buffer.ndim = self.shape.size()
474 buffer.shape = self.shape.data()
475 buffer.strides = self.strides.data()
476 buffer.suboffsets = NULL
478 cdef char*_get_blob_format(self, const TensorDesc & desc):
479 cdef Precision precision = desc.getPrecision()
480 name = bytes(precision.name()).decode()
482 precision_to_format = {
484 'FP16': 'h', # signed short
485 'Q78': 'h', # signed short
486 'I16': 'h', # signed short
487 'U8': 'B', # unsigned char
488 'I8': 'b', # signed char
489 'U16': 'H', # unsigned short
490 'I32': 'i' # signed int
493 if name not in precision_to_format:
494 raise ValueError("Unknown Blob precision: {}".format(name))
496 return precision_to_format[name].encode()
499 return np.asarray(self)