2026-01-28 06:16:04 +00:00

164 lines
6.3 KiB
Python

"""
Utility functions to help perform Dongle inference.
"""
import os
import re
import sys
import time
import json
import numpy as np
from python_flow.common import directory_manager as dm
from python_flow.common import exceptions
from python_flow.utils import csim, utils
from sys_flow.compiler_v2 import combine_nef as sys_combine_nef
from sys_flow.inference import data_converter
def run_dongle(output_folder, inputs, model_id):
"""Performs Dongle inference.
Arguments:
output_folder: String path to directory where outputs will be stored.
model_id: Integer model to run.
inputs: List of string paths to each of the input image RGBA binaries.
Returns:
A tuple of 3 lists, where the first list holds the Dongle response data, the
second list holds the radices for each output node, and the third list holds the
scales for each output node.
"""
import python_flow.dongle.dongle_client as dongle
from python_flow.dongle import dongle_config
internal_id = dongle_config.pId_internalId[os.getpid()]
# Extract the values and make them into a list
inputs = [value for values in inputs.values() for value in values]
with dm.DirectoryManager(output_folder):
try:
infer_start = time.time()
# dongle inference
response, radices, scales = dongle.dongle_clients[internal_id].kdp_inference(
input_files=inputs, model_id=model_id,
fixed_output=dongle_config.dongle_fixed_mode)
infer_end = time.time()
print(f"Dongle inference time: {infer_end - infer_start}")
except:
raise exceptions.LibError(f"Unknown Dongle Error!\n")
return response, radices, scales
def dongle_to_np(response, reordering, ioinfo_out, first, radices, scales,
fixed_output=False, model_id=None, output_dir=None):
"""Reorder dongle output into a list of NumPy arrays according to ioinfo_file.
The resulting NumPy arrays are values in channel first format if first is true (bchw).
Otherwise, it will be in channel last format (bhwc).
Arguments:
response: response data from dongle
reordering: List of indices to set output node order
ioinfo_out: Ioinfo of output nodes.
first: Boolean to keep result in channel first format
"""
from python_flow.dongle import dongle_config
# dump the inference results when dongle debug mode is Enabled
if dongle_config.dongle_debug_mode:
for idx, data in enumerate(response):
if fixed_output:
file_name = 'dongle_output_fixed_model_{}_node_{}_shape_{}.txt'.format(
model_id, idx, data.shape)
file_path = os.path.join(output_dir, file_name)
np.savetxt(file_path, data.flatten(), fmt='%d')
else:
file_name = 'dongle_output_float_model_{}_node_{}_shape_{}.txt'.format(
model_id, idx, data.shape)
file_path = os.path.join(output_dir, file_name)
np.savetxt(file_path, data.flatten(), fmt='%f')
# convert fixed result to float result using e2e method
if fixed_output:
response = csim.convert_results(response, radices, scales, "fx2fl")
reorder_output = [None] * len(response)
try:
ioinfo_out_order = {}
for idx, out_node in enumerate(ioinfo_out):
ioinfo_out_order[out_node['name']] = idx
for i, order_name in enumerate(reordering):
response_idx = int(ioinfo_out_order[order_name])
reorder_output[i] = response[response_idx]
if first:
reorder_output[i] = reorder_output[i].transpose(0, 3, 1, 2)
except exceptions.ConfigError as error:
sys.exit(error)
return reorder_output
def prep_bin_input(pre_results, input_names, ioinfo_in):
"""
Prepare binary input files as the inputs of dongle inference service.
Arguments:
pre_results: List of preprocessed NumPy arrays in channel last format.
input_names: Node names of the ordered inputs.
ioinfo_in: Input ioinfo.
Returns:
Paths to the binary files.
"""
inputs = utils.prep_inputs(pre_results, input_names, False, None)
csim_bin_list, _, _ = data_converter(inputs, ioinfo_in, p_working=None)
return csim_bin_list
def combine_nef(user_config, platform):
"""
Combine one or more nef models to a single combined nef model.
Extract the ioinfo data stored in each model.
Arguments:
user_config: Config params of current settings.
platform: Interger Field(520, 630, 720, 730, etc...)
Returns:
combined_nef_path: Path to the combined nef file.
combined_ioinfo: ioinfo of all models({model_id: ioinfo, ...})
"""
nef_paths = []
for key in user_config.keys():
# skip the model with emu mode != dongle
try:
emu_mode = user_config[key].config["emu"]["emu_mode"]
except:
continue
# add model info into list waiting for compiling if emu mode == dongle
if emu_mode == "dongle":
try:
nef_file = user_config[key].orig_emu['nef_file']
assert os.path.exists(nef_file) == True
nef_paths.append(nef_file)
app_name = re.findall(r'app/(.*?)/models', nef_file)[0]
dongle_out_dir = os.path.join(user_config[key].orig_emu['dongle_output'], app_name)
except KeyError as error:
raise KeyError(r'Failed to collect nef model from inputs. Please check if you include nef_file field in the dongle related input json files.', error)
except AssertionError:
raise FileNotFoundError(r'Failed to find the model files. Please check if the nef_file of the dongle related models exist.')
if len(nef_paths) == 0:
raise ValueError("Failed to collect nef model from inputs. Please check the input json files.")
# combine several nef files to a single nef file
_, p_nef, p_ioinfo, _ = sys_combine_nef(nef_paths, platform, dongle_out_dir)
# read ioinfo and combined nef path
combined_nef_path = p_nef
with open(p_ioinfo, 'r') as f_ioinfo:
combined_ioinfo = json.load(f_ioinfo)
return combined_nef_path, combined_ioinfo