Files
ANSLibs/OpenVINO/python/openvino/tools/benchmark/utils/utils.py

835 lines
33 KiB
Python
Raw Normal View History

# Copyright (C) 2018-2025 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from collections import defaultdict
from datetime import timedelta
import enum
from openvino import Core, Model, PartialShape, Dimension, Layout, Type, serialize, properties, OVAny
from openvino.preprocess import PrePostProcessor
from .constants import DEVICE_DURATION_IN_SECS, UNKNOWN_DEVICE_TYPE, \
AUTO_DEVICE_NAME, MULTI_DEVICE_NAME, HETERO_DEVICE_NAME
from .logging import logger
import json
import re
import numpy as np
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
@static_vars(step_id=0)
def next_step(additional_info='', step_id=0):
step_names = {
1: "Parsing and validating input arguments",
2: "Loading OpenVINO Runtime",
3: "Setting device configuration",
4: "Reading model files",
5: "Resizing model to match image sizes and given batch",
6: "Configuring input of the model",
7: "Loading the model to the device",
8: "Querying optimal runtime parameters",
9: "Creating infer requests and preparing input tensors",
10: "Measuring performance",
11: "Dumping statistics report",
}
if step_id != 0:
next_step.step_id = step_id
else:
next_step.step_id += 1
if next_step.step_id not in step_names.keys():
raise Exception(f'Step ID {next_step.step_id} is out of total steps number {str(len(step_names))}')
step_info_template = '[Step {}/{}] {}'
step_name = step_names[next_step.step_id] + (f' ({additional_info})' if additional_info else '')
step_info_template = step_info_template.format(next_step.step_id, len(step_names), step_name)
print(step_info_template)
def get_element_type(precision):
format_map = {
'bool' : Type.boolean,
'f16' : Type.f16,
'f32' : Type.f32,
'f64' : Type.f64,
'i8' : Type.i8,
'i16' : Type.i16,
'i32' : Type.i32,
'i64' : Type.i64,
'u8' : Type.u8,
'u16' : Type.u16,
'u32' : Type.u32,
'u64' : Type.u64,
}
if precision in format_map.keys():
return format_map[precision]
raise Exception(f"Undefined precision: '{precision}' !")
def fuse_mean_scale(preproc: PrePostProcessor, app_inputs_info):
# TODO: remove warning after 23.3 release
warned = False
warn_msg = 'Mean/scale values are fused into the model. This slows down performance compared to --imean and --iscale which existed before'
for input_info in app_inputs_info:
if input_info.mean.size:
if not warned:
logger.warning(warn_msg)
warned = True
preproc.input(input_info.name).preprocess().convert_element_type(Type.f32).mean(input_info.mean)
if input_info.scale.size:
if not warned:
logger.warning(warn_msg)
warned = True
preproc.input(input_info.name).preprocess().convert_element_type(Type.f32).scale(input_info.scale)
def pre_post_processing(model: Model, app_inputs_info, input_precision: str, output_precision: str, input_output_precision: str):
pre_post_processor = PrePostProcessor(model)
if input_precision:
element_type = get_element_type(input_precision)
for i in range(len(model.inputs)):
pre_post_processor.input(i).tensor().set_element_type(element_type)
app_inputs_info[i].element_type = element_type
if output_precision:
element_type = get_element_type(output_precision)
for i in range(len(model.outputs)):
pre_post_processor.output(i).tensor().set_element_type(element_type)
user_precision_map = {}
if input_output_precision:
user_precision_map = parse_input_output_precision(input_output_precision)
input_names = get_input_output_names(model.inputs)
input_node_names = get_node_names(model.inputs)
output_names = get_input_output_names(model.outputs)
output_node_names = get_node_names(model.outputs)
for node_name, precision in user_precision_map.items():
user_precision_map[node_name] = get_element_type(precision)
for name, element_type in user_precision_map.items():
if name in input_names or name in input_node_names:
input_index = input_names.index(name) if name in input_names else input_node_names.index(name)
app_inputs_info[input_index].element_type = element_type
pre_post_processor.input(input_index).tensor().set_element_type(element_type)
elif name in output_names or name in output_node_names:
if name in output_names:
pre_post_processor.output(name).tensor().set_element_type(element_type)
else:
pre_post_processor.output(output_node_names.index(name)).tensor().set_element_type(element_type)
else:
raise Exception(f"Node '{name}' does not exist in model")
# update app_inputs_info
if not input_precision:
inputs = model.inputs
input_node_names = get_node_names(model.inputs)
for i in range(len(inputs)):
if app_inputs_info[i].name in user_precision_map:
app_inputs_info[i].element_type = user_precision_map[app_inputs_info[i].name]
elif input_node_names[i] in user_precision_map:
app_inputs_info[i].element_type = user_precision_map[input_node_names[i]]
elif app_inputs_info[i].is_image:
app_inputs_info[i].element_type = Type.u8
pre_post_processor.input(i).tensor().set_element_type(Type.u8)
fuse_mean_scale(pre_post_processor, app_inputs_info)
# set layout for model input
for info in app_inputs_info:
pre_post_processor.input(info.name).model().set_layout(info.layout)
model = pre_post_processor.build()
def parse_input_output_precision(arg_map: str):
arg_map = arg_map.replace(" ", "")
pairs = [x.strip() for x in arg_map.split(',')]
parsed_map = {}
for pair in pairs:
key_value = [x.strip() for x in pair.split(':')]
name, precision = key_value[0], key_value[1]
# input's name can contain ':'
if len(key_value) == 3:
name = key_value[0] + ':' + key_value[1]
precision = key_value[2]
parsed_map.update({name:precision})
return parsed_map
def print_inputs_and_outputs_info(model: Model):
inputs = model.inputs
logger.info("Model inputs:")
for input in inputs:
in_name = " , ".join(input.get_names())
node_name = input.node.get_friendly_name()
if in_name=="": in_name = "***NO_NAME***"
if node_name=="": node_name = "***NO_NAME***"
logger.info(f" {in_name} (node: {node_name}) : {input.element_type.get_type_name()} / "
f"{str(input.node.layout)} / {input.partial_shape}")
outputs = model.outputs
logger.info("Model outputs:")
for output in outputs:
out_name = " , ".join(output.get_names())
node_name = output.get_node().input(0).get_source_output().get_node().get_friendly_name()
if out_name=="": out_name = "***NO_NAME***"
if node_name=="": node_name = "***NO_NAME***"
logger.info(f" {out_name} (node: {node_name}) : {output.element_type.get_type_name()} / "
f"{str(output.node.layout)} / {output.partial_shape}")
def get_number_iterations(number_iterations: int, nireq: int, num_shapes: int, api_type: str):
niter = number_iterations
if api_type == 'async' and niter:
if num_shapes > nireq:
niter = int(((niter + num_shapes -1) / num_shapes) * num_shapes)
if number_iterations != niter:
logger.warning('Number of iterations was aligned by number of input shapes '
f'from {number_iterations} to {niter} using number of possible input shapes {num_shapes}')
else:
niter = int((niter + nireq - 1) / nireq) * nireq
if number_iterations != niter:
logger.warning('Number of iterations was aligned by request number '
f'from {number_iterations} to {niter} using number of requests {nireq}')
return niter
def get_duration_seconds(time, number_iterations, device):
if time:
# time limit
return time
if not number_iterations:
return get_duration_in_secs(device)
return 0
class LatencyGroup:
def __init__(self, input_names, input_shapes):
self.input_names = input_names
self.input_shapes = input_shapes
self.times = list()
self.median = 0.
self.avg = 0.
self.min = 0.
self.max = 0.
def __str__(self):
return str().join(f" {name}: {str(shape)}" for name, shape in zip(self.input_names, self.input_shapes))
def get_latency_groups(app_input_info):
num_groups = max(len(info.shapes) for info in app_input_info)
latency_groups = []
for i in range(num_groups):
names = list()
shapes = list()
for info in app_input_info:
names.append(info.name)
shapes.append(info.shapes[i % len(info.shapes)])
latency_groups.append(LatencyGroup(names, shapes))
return latency_groups
def get_duration_in_milliseconds(duration):
return duration * 1000
def get_duration_in_secs(target_device):
duration = 0
for device in DEVICE_DURATION_IN_SECS:
if device in target_device:
duration = max(duration, DEVICE_DURATION_IN_SECS[device])
if duration == 0:
duration = DEVICE_DURATION_IN_SECS[UNKNOWN_DEVICE_TYPE]
logger.warning(f'Default duration {duration} seconds is used for unknown device {target_device}')
return duration
def check_for_static(app_input_info):
for info in app_input_info:
if info.is_dynamic:
return False
return True
def can_measure_as_static(app_input_info):
for info in app_input_info:
if len(info.shapes) > 1:
return False
return True
meta_plugins = [ MULTI_DEVICE_NAME, HETERO_DEVICE_NAME, AUTO_DEVICE_NAME ]
def is_virtual_device(device_name) -> bool:
return device_name in meta_plugins
def is_virtual_device_found(device_names) -> bool:
return any(is_virtual_device(device_name) for device_name in device_names)
def parse_devices(device_string):
result = []
target_device = device_string.partition(":")[0]
result.append(target_device)
if device_string.find(":") != -1:
hw_devices_str = device_string.partition(":")[-1]
for hw_device in hw_devices_str.split(','):
if hw_device[0] == '-':
hw_device = hw_device[1:]
result.append(hw_device)
return result
def parse_value_per_device(devices, values_string, value_type):
# Format: <device1>:<value1>,<device2>:<value2> or just <value>
result = {}
if not values_string:
return result
device_value_strings = values_string.split(',')
for device_value_string in device_value_strings:
device_value_vec = device_value_string.split(':')
if len(device_value_vec) == 2:
device_name = device_value_vec[0]
value = device_value_vec[1]
if device_name in devices:
result[device_name] = value
else:
devices_str = ""
for device in devices:
devices_str += device + " "
devices_str = devices_str.strip()
raise Exception(f"Failed to set property to '{device_name}' " \
f"which is not found in the target devices list '{devices_str}'!")
elif len(device_value_vec) == 1:
value = device_value_vec[0]
for device in devices:
result[device] = value
elif not device_value_vec:
raise Exception('Unknown string format: ' + values_string)
return result
def parse_value_for_virtual_device(device, values_string):
isExist = device in values_string.keys()
if isExist and len(values_string) > 1:
if device == MULTI_DEVICE_NAME:
# Remove the element that the key is virtual device MULTI
# e.g. MULTI:xxx -nstreams 2 will set nstreams 2 to xxx.
values_string.pop(device)
elif device == AUTO_DEVICE_NAME or device == HETERO_DEVICE_NAME:
# Just keep the element that the key is virtual device AUTO/HETERO
# e.g. AUTO:xxx,xxx -nstreams 2 will trigger exception that AUTO plugin didn't support nstream property.
value = values_string.get(device)
values_string.clear()
values_string[device] = value
keys = values_string.keys()
for key in list(values_string):
if device not in list(values_string):
values_string[device] = '{'
else:
values_string[device] += ','
values_string[device] += key + ":" + values_string.get(key)
del values_string[key]
if device in values_string.keys():
values_string[device] = values_string[device].strip()
if values_string[device] != '':
values_string[device] += '}'
return
def process_help_inference_string(benchmark_app, device_number_streams):
output_string = f'Start inference {benchmark_app.api_type}hronously'
if benchmark_app.api_type == 'async':
output_string += f', {benchmark_app.nireq} inference requests'
device_ss = ''
for device, streams in device_number_streams.items():
device_ss += ', ' if device_ss else ''
device_ss += f'{streams} streams for {device}'
if device_ss:
output_string += ' using ' + device_ss
output_string += ', limits: '
if benchmark_app.duration_seconds:
output_string += f'{get_duration_in_milliseconds(benchmark_app.duration_seconds)} ms duration'
if benchmark_app.niter:
if benchmark_app.duration_seconds > 0:
output_string += ', '
output_string += f'{benchmark_app.niter} iterations'
return output_string
def dump_exec_graph(compiled_model, model_path):
serialize(compiled_model.get_runtime_model(), model_path)
def print_perf_counters_sort(perf_counts_list,sort_flag="sort"):
""" Print opts time cost and can be sorted according by each opts time cost
"""
for ni in range(len(perf_counts_list)):
perf_counts = perf_counts_list[ni]
total_time = timedelta()
total_time_cpu = timedelta()
logger.info(f"Performance counts sorted for {ni}-th infer request")
for pi in perf_counts:
total_time += pi.real_time
total_time_cpu += pi.cpu_time
total_time = total_time.microseconds
total_time_cpu = total_time_cpu.microseconds
total_real_time_proportion = 0
total_detail_data=[]
for pi in perf_counts:
node_name = pi.node_name
layerStatus = pi.status
layerType = pi.node_type
real_time = pi.real_time.microseconds
cpu_time = pi.cpu_time.microseconds
real_proportion = round(real_time/total_time,4)
execType = pi.exec_type
tmp_data=[node_name,layerStatus,layerType,real_time,cpu_time,real_proportion,execType]
total_detail_data.append(tmp_data)
total_real_time_proportion += real_proportion
total_detail_data = np.array(total_detail_data)
if sort_flag=="sort":
total_detail_data = sorted(total_detail_data,key=lambda tmp_data:tmp_data[-4],reverse=True)
elif sort_flag=="no_sort":
total_detail_data = total_detail_data
elif sort_flag=="simple_sort":
total_detail_data = sorted(total_detail_data,key=lambda tmp_data:tmp_data[-4],reverse=True)
total_detail_data = [tmp_data for tmp_data in total_detail_data if str(tmp_data[1])!="Status.NOT_RUN"]
print_detail_result(total_detail_data)
print(f'Total time: {total_time / 1000:.3f} milliseconds')
print(f'Total CPU time: {total_time_cpu / 1000:.3f} milliseconds')
print(f'Total proportion: {"%.2f"%(round(total_real_time_proportion)*100)} % \n')
return total_detail_data
def print_detail_result(result_list):
""" Print_perf_counters_sort result
"""
max_print_length = 20
for tmp_result in result_list:
node_name = tmp_result[0]
layerStatus = tmp_result[1]
layerType = tmp_result[2]
real_time = tmp_result[3]
cpu_time = tmp_result[4]
real_proportion = "%.2f" % (tmp_result[5] * 100)
if real_proportion == "0.00":
real_proportion = "N/A"
execType = tmp_result[6]
print(f"{node_name[:max_print_length - 4] + '...' if (len(node_name) >= max_print_length) else node_name:<20} "
f"{str(layerStatus):<20} "
f"layerType: {layerType[:max_print_length - 4] + '...' if (len(layerType) >= max_print_length) else layerType:<20} "
f"execType: {execType:<20} "
f"realTime (ms): {real_time / 1000:<10.3f} "
f"cpuTime (ms): {cpu_time / 1000:<10.3f}"
f"proportion: {str(real_proportion +'%'):<8}")
def print_perf_counters(perf_counts_list):
max_print_length = 20
for ni in range(len(perf_counts_list)):
perf_counts = perf_counts_list[ni]
total_time = timedelta()
total_time_cpu = timedelta()
logger.info(f"Performance counts for {ni}-th infer request")
for pi in perf_counts:
print(f"{pi.node_name[:max_print_length - 4] + '...' if (len(pi.node_name) >= max_print_length) else pi.node_name:<20} "
f"{str(pi.status):<20} "
f"layerType: {pi.node_type[:max_print_length - 4] + '...' if (len(pi.node_type) >= max_print_length) else pi.node_type:<20} "
f"execType: {pi.exec_type:<20} "
f"realTime (ms): {pi.real_time / timedelta(milliseconds=1):<10.3f} "
f"cpuTime (ms): {pi.cpu_time / timedelta(milliseconds=1):<10.3f}")
total_time += pi.real_time
total_time_cpu += pi.cpu_time
print(f'Total time: {total_time / timedelta(milliseconds=1)} milliseconds')
print(f'Total CPU time: {total_time_cpu / timedelta(milliseconds=1)} milliseconds\n')
def get_command_line_arguments(argv):
parameters = []
arg_name = ''
arg_value = ''
for arg in argv[1:]:
if '=' in arg:
if arg_name != '':
parameters.append((arg_name, arg_value))
arg_name, arg_value = arg.split('=')
parameters.append((arg_name, arg_value))
arg_name = ''
arg_value = ''
else:
if arg[0] == '-':
if arg_name != '':
parameters.append((arg_name, arg_value))
arg_value = ''
arg_name = arg
else:
arg_value = arg
if arg_name != '':
parameters.append((arg_name, arg_value))
return parameters
def get_input_output_names(ports):
return [port.any_name if port.get_names() else port.node.get_friendly_name() for port in ports]
def get_node_names(ports):
return [port.node.friendly_name for port in ports]
def get_data_shapes_map(data_shape_string, input_names):
# Parse parameter string like "input0[shape1][shape2],input1[shape1]" or "[shape1][shape2]" (applied to all inputs)
return_value = {}
if data_shape_string:
data_shape_string += ','
matches = re.findall(r'(.*?\[.*?\]),', data_shape_string)
if matches:
for match in matches:
input_name = match[:match.find('[')]
shapes = re.findall(r'\[(.*?)\]', match[len(input_name):])
if input_name:
return_value[input_name] = list(PartialShape(shape_str) for shape_str in shapes)
else:
data_shapes = list(PartialShape(shape_str) for shape_str in shapes)
num_inputs, num_shapes = len(input_names), len(data_shapes)
if num_shapes != 1 and num_shapes % num_inputs != 0:
raise Exception(f"Number of provided data_shapes is not a multiple of the number of model inputs!")
return_value = defaultdict(list)
for i in range(max(num_shapes, num_inputs)):
return_value[input_names[i % num_inputs]].append(data_shapes[i % num_shapes])
return return_value
else:
raise Exception(f"Can't parse input parameter: {data_shape_string}")
return return_value
def parse_input_parameters(parameter_string, input_names):
# Parse parameter string like "input0[value0],input1[value1]" or "[value]" (applied to all inputs)
return_value = {}
if parameter_string:
matches = re.findall(r'(.*?)\[(.*?)\],?', parameter_string)
if matches:
for match in matches:
input_name, value = match
if input_name != '':
return_value[input_name] = value
else:
return_value = { k:value for k in input_names }
break
else:
raise Exception(f"Can't parse input parameter: {parameter_string}")
return return_value
def parse_scale_or_mean(parameter_string, input_info):
# Parse parameter string like "input0[value0],input1[value1]" or "[value]" (applied to all inputs)
return_value = {}
if parameter_string:
matches = re.findall(r'(.*?)\[(.*?)\],?', parameter_string)
if matches:
for match in matches:
input_name, value = match
f_value = np.array(value.split(",")).astype(float)
if input_name != '':
return_value[input_name] = f_value
else:
for input in input_info:
if input.is_image:
return_value[input.name] = f_value
else:
raise Exception(f"Can't parse input parameter: {parameter_string}")
return return_value
class AppInputInfo:
def __init__(self):
self.element_type = None
self.layout = Layout()
self.partial_shape = None
self.data_shapes = []
self.scale = np.empty([0])
self.mean = np.empty([0])
self.name = None
self.node_name = None
@property
def is_image(self):
if str(self.layout) not in [ "[N,C,H,W]", "[N,H,W,C]", "[C,H,W]", "[H,W,C]" ]:
return False
return self.channels == 3
@property
def is_image_info(self):
if str(self.layout) != "[N,C]":
return False
return len(self.channels) >= 2 if self.channels.is_static else self.channels.relaxes(Dimension(2))
def getDimensionByLayout(self, character):
if self.layout.has_name(character):
return self.partial_shape[self.layout.get_index_by_name(character)]
else:
return Dimension(0)
def getDimensionsByLayout(self, character):
if self.layout.has_name(character):
d_index = self.layout.get_index_by_name(character)
dims = []
for shape in self.data_shapes:
dims.append(shape[d_index])
return dims
else:
return [0] * len(self.data_shapes)
@property
def shapes(self):
if self.is_static:
return [self.partial_shape.to_shape()]
else:
return self.data_shapes
@property
def width(self):
return len(self.getDimensionByLayout("W"))
@property
def widths(self):
return self.getDimensionsByLayout("W")
@property
def height(self):
return len(self.getDimensionByLayout("H"))
@property
def heights(self):
return self.getDimensionsByLayout("H")
@property
def channels(self):
return self.getDimensionByLayout("C")
@property
def is_static(self):
return self.partial_shape.is_static
@property
def is_dynamic(self):
return self.partial_shape.is_dynamic
def get_inputs_info(shape_string, data_shape_string, layout_string, batch_size, scale_string, mean_string, inputs):
input_names = get_input_output_names(inputs)
input_node_names = get_node_names(inputs)
shape_map = parse_input_parameters(shape_string, input_names)
data_shape_map = get_data_shapes_map(data_shape_string, input_names)
layout_map = parse_input_parameters(layout_string, input_names)
batch_size = Dimension(batch_size)
reshape = False
batch_found = False
input_info = []
for i in range(len(inputs)):
info = AppInputInfo()
# Input name
info.name = input_names[i]
# Input node name
info.node_name = input_node_names[i]
# Input precision
info.element_type = inputs[i].element_type
# Shape
if info.name in shape_map:
info.partial_shape = PartialShape(shape_map[info.name])
reshape = True
elif info.node_name in shape_map:
info.partial_shape = PartialShape(shape_map[info.node_name])
reshape = True
else:
info.partial_shape = inputs[i].partial_shape
# Layout
if info.name in layout_map:
info.layout = Layout(layout_map[info.name])
elif info.node_name in layout_map:
info.layout = Layout(layout_map[info.node_name])
elif inputs[i].node.layout != Layout():
info.layout = inputs[i].node.layout
else:
image_colors_dim_max = 4
shape = info.partial_shape
num_dims = len(shape)
if num_dims == 4:
if shape[1].get_max_length() <= image_colors_dim_max and shape[3].get_max_length() > image_colors_dim_max:
info.layout = Layout("NCHW")
elif shape[3].get_max_length() <= image_colors_dim_max and shape[1].get_max_length() > image_colors_dim_max:
info.layout = Layout("NHWC")
elif num_dims == 3:
if shape[0].get_max_length() <= image_colors_dim_max and shape[2].get_max_length() > image_colors_dim_max:
info.layout = Layout("CHW")
elif shape[2].get_max_length() <= image_colors_dim_max and shape[0].get_max_length() > image_colors_dim_max:
info.layout = Layout("HWC")
# Update shape with batch if needed
if batch_size != 0:
if batch_size.is_static and data_shape_map:
logger.warning(f"Batch size will be ignored. Provide batch deminsion in data_shape parameter.")
else:
batch_index = -1
if info.layout.has_name('N'):
batch_index = info.layout.get_index_by_name('N')
elif info.layout == Layout():
supposed_batch = info.partial_shape[0]
if supposed_batch.is_dynamic or supposed_batch in [0, 1]:
logger.warning(f"Batch dimension is not specified for input '{info.name}'. "
"The first dimension will be interpreted as batch size.")
batch_index = 0
info.layout = Layout("N...")
if batch_index != -1 and info.partial_shape[batch_index] != batch_size:
info.partial_shape[batch_index] = batch_size
reshape = True
batch_found = True
elif batch_index == -1 and not batch_found and i == len(inputs) - 1:
raise RuntimeError("-b option is provided in command line, but there's no inputs with batch(B) " \
"dimension in input layout, so batch cannot be set. " \
"You may specify layout explicitly using -layout option.")
# Data shape
if (info.name in data_shape_map or info.node_name in data_shape_map) and info.is_dynamic:
used_name = info.name if info.name in data_shape_map else info.node_name
for p_shape in data_shape_map[used_name]:
if p_shape.is_dynamic:
raise Exception(f"Data shape always should be static, {str(p_shape)} is dynamic.")
elif info.partial_shape.compatible(p_shape):
info.data_shapes.append(p_shape.to_shape())
else:
raise Exception(f"Data shape '{str(p_shape)}' provided for input '{info.name}' "
f"is not compatible with partial shape '{str(info.partial_shape)}' for this input.")
elif info.name in data_shape_map or input_node_names[i] in data_shape_map:
logger.warning(f"Input '{info.name}' has static shape. Provided data shapes for this input will be ignored.")
input_info.append(info)
# Update scale and mean
scale_map = parse_scale_or_mean(scale_string, input_info)
mean_map = parse_scale_or_mean(mean_string, input_info)
for input in input_info:
if input.name in scale_map:
input.scale = scale_map[input.name]
elif input.node_name in scale_map:
input.scale = scale_map[input.node_name]
if input.name in mean_map:
input.mean = mean_map[input.name]
elif input.node_name in mean_map:
input.mean = mean_map[input.node_name]
return input_info, reshape
def get_network_batch_size(inputs_info):
null_dimension = Dimension(0)
batch_size = null_dimension
for info in inputs_info:
batch_index = info.layout.get_index_by_name('N') if info.layout.has_name('N') else -1
if batch_index != -1:
if batch_size == null_dimension:
batch_size = info.partial_shape[batch_index]
elif batch_size != info.partial_shape[batch_index]:
raise Exception("Can't deterimine batch size: batch is different for different inputs!")
if batch_size == null_dimension:
batch_size = Dimension(1)
return batch_size
def show_available_devices():
print("\nAvailable target devices: ", (" ".join(Core().available_devices)))
def device_properties_to_string(config):
ret = "{"
for k, v in config.items():
if isinstance(v, dict):
sub_str = "{"
for sk, sv in v.items():
if isinstance(sv, bool):
sv = "YES" if sv else "NO"
sub_str += "{0}:{1},".format(sk, sv)
sub_str = sub_str[:-1]
sub_str += "}"
ret += "{0}:{1},".format(k, sub_str)
else:
ret += "{0}:{1},".format(k, v)
ret = ret[:-1]
ret += "}"
return ret
def string_to_device_properties(device_properties_str):
ret = {}
if not device_properties_str:
return ret
if not device_properties_str.startswith("{") or not device_properties_str.endswith("}"):
raise Exception(
"Failed to parse device properties. Value of device properties should be started with '{' and ended with '}'."
"They are actually {} and {}".format(device_properties_str[0], device_properties_str[-1]))
pattern = r'(\w+):({.+?}|[^,}]+)'
pairs = re.findall(pattern, device_properties_str)
for key, value in pairs:
if value.startswith("{") and value.endswith("}"):
value = value[1:-1]
nested_pairs = re.findall(pattern, value)
nested_dict = {}
for nested_key, nested_value in nested_pairs:
nested_dict[nested_key] = nested_value
value = nested_dict
ret[key] = value
return ret
def dump_config(filename, config):
json_config = {}
for device_name, device_config in config.items():
json_config[device_name] = {}
for key, value in device_config.items():
if isinstance(value, OVAny) and (isinstance(value.value, dict)):
value_string = device_properties_to_string(value.get())
elif isinstance(value, properties.hint.PerformanceMode):
value_string = value.name
elif isinstance(value, OVAny):
value_string = str(value.value)
else:
value_string = str(value)
if isinstance(value, bool):
value_string = "YES" if value else "NO"
json_config[device_name][key] = value_string
with open(filename, 'w') as f:
json.dump(json_config, f, indent=4)
def load_config(filename, config):
with open(filename) as f:
original_config = json.load(f)
for device in original_config:
config[device] = {}
for property_name in original_config[device]:
property_value = original_config[device][property_name]
if property_name == properties.device.properties():
property_value = string_to_device_properties(property_value)
elif property_value in ("YES", "NO"):
property_value = True if property_value == "YES" else False
config[device][property_name] = OVAny(property_value)