-
Notifications
You must be signed in to change notification settings - Fork 1
/
generate.py
240 lines (193 loc) · 9.09 KB
/
generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import os, argparse, tempfile, re, shutil
from zipfile import ZipFile
from EIDownload import EIDownload
from utils import *
import logging
parser = argparse.ArgumentParser(description='Multi-impulse transformation block')
parser.add_argument('--api-keys', type=str, help='List of API Keys', required=False)
parser.add_argument('--projects', type=str, help='List of project IDs separated by a comma', required=False)
parser.add_argument('--tmp-directory', type=str, required=False)
parser.add_argument('--out-directory', type=str, default='/home/output', required=False)
parser.add_argument("--float32", action="store_true", help="Use float32 model")
parser.add_argument("--force-build", action="store_true", help="Force build libraries, no cache")
parser.add_argument("--engine", type=str, choices = ['eon', 'tflite'], default='eon', help="Inferencing engine to use.")
parser.add_argument("--quantization-map", type=str, help="Description of quantization policy for each impulse", required=False)
# EG
# --api-keys apiA,apiB \
# --quantization-map 0,1
# This means that the first impulse of the first project will NOT be quantized and the second impulse WILL be quantized
args, unknown = parser.parse_known_args()
logging.basicConfig()
logger = logging.getLogger("main")
logger.setLevel(logging.INFO)
# Get projects API Keys
#projectIDs = args.projects.replace(' ', '').split(',')
## DOWNLOADING LIBS
# We bypass download if we already have projects locally in a tmp directory
if not (args.projects and args.tmp_directory):
if not args.api_keys:
raise(Exception('--api-keys argument not set'))
apiKeys = args.api_keys.replace(' ', '').split(',') # comma between keys
if not args.quantization_map:
raise(Exception('--quantization-map argument not set'))
quantizationMap = args.quantization_map.replace(' ', '').split(',') # comma between mask
# check for duplicate projects
apiKeysSet = list(set(apiKeys))
if len(apiKeysSet) != len(apiKeys):
raise(Exception('Duplicate projects detected. Please provide unique API keys'))
# verify that the input file exists and create the output directory if needed
if not os.path.exists(args.out_directory):
os.makedirs(args.out_directory)
# Create temp directory to store zip files and manipulate files
# tmp_directory argument used for tests
if args.tmp_directory:
if not os.path.exists(args.tmp_directory):
os.makedirs(args.tmp_directory)
tmpdir = args.tmp_directory
else:
tmpdir = tempfile.mkdtemp()
project_ids = []
# Download C++ libs and unzip
for i in range(len(apiKeys)):
dzip = EIDownload(api_key = apiKeys[i])
project_ids += [str(dzip.get_project_id())]
download_path = os.path.join(tmpdir, str(project_ids[i]))
os.makedirs(download_path)
if quantizationMap[i] == '0':
quantized = False
else:
quantized = True
zipfile_path = dzip.download_model(download_path, eon = (args.engine == 'eon'), quantized = quantized, force_build = args.force_build)
with ZipFile(zipfile_path, 'r') as zObject:
zObject.extractall(download_path)
os.remove(zipfile_path)
else:
project_ids = args.projects.split(',')
tmpdir = args.tmp_directory
## EDITING FILES
# create a target dir
target_dir = os.path.join(args.out_directory, "output")
# copy from the first project
shutil.copytree(os.path.join(tmpdir, project_ids[0]), target_dir, dirs_exist_ok=True)
include_lines = []
# Save intersection of trained_model_ops_define.h files
f1 = os.path.join(tmpdir, project_ids[0], "tflite-model/trained_model_ops_define.h")
f2 = os.path.join(tmpdir, project_ids[1], "tflite-model/trained_model_ops_define.h")
merge_model_ops(f1, f2)
shutil.copy(f2, os.path.join(target_dir, "tflite-model/trained_model_ops_define.h"))
# merge the resolvers if tflite
if args.engine == 'tflite':
f1 = os.path.join(tmpdir, project_ids[0], "tflite-model/tflite-resolver.h")
f2 = os.path.join(tmpdir, project_ids[1], "tflite-model/tflite-resolver.h")
merge_model_ops(f1, f2)
shutil.copy(f2, os.path.join(target_dir, "tflite-model/tflite-resolver.h"))
# merge the model metadata
f1 = os.path.join(tmpdir, project_ids[0], "model-parameters/model_metadata.h")
f2 = os.path.join(tmpdir, project_ids[1], "model-parameters/model_metadata.h")
merge_model_metadata(f1, f2)
shutil.copy(f2, os.path.join(target_dir, "model-parameters/model_metadata.h"))
for p in project_ids:
# suffix added to different functions and variables
suffix = "_" + p
logger.info(f"Processing Project{str(suffix)}")
# Edit compiled files in tflite-model/
model_dir = os.path.join(tmpdir, p, 'tflite-model')
for f in os.listdir(model_dir):
if "compiled" in f:
# List of patterns to look for and append with suffix
# Get learn block ID pattern to add projectID as suffix
patterns = [
r"tflite_learn_\d+"
]
edit_file(os.path.join(model_dir, f), patterns, suffix)
# Rename filenames
new_f = f.replace("_compiled", f"{suffix}_compiled")
os.rename(os.path.join(model_dir, f), os.path.join(model_dir, new_f))
# copy to target_dir (1st project)
if project_ids.index(p) > 0:
shutil.copy(os.path.join(model_dir, new_f), os.path.join(target_dir, 'tflite-model', new_f))
elif f.startswith("tflite_learn_"):
patterns = [
r"tflite_learn_\d+"
]
edit_file(os.path.join(model_dir, f), patterns, suffix)
# Rename filenames
name, ext = os.path.splitext(f)
new_f = f"{name}{suffix}{ext}"
os.rename(os.path.join(model_dir, f), os.path.join(model_dir, new_f))
# copy to target_dir (1st project)
if project_ids.index(p) > 0:
shutil.copy(os.path.join(model_dir, new_f), os.path.join(target_dir, 'tflite-model', new_f))
# Edit model_variables.h
f = os.path.join(tmpdir, p, "model-parameters/model_variables.h")
# Patterns may be missing for anomaly detection blocks
patterns = [
r"tflite_learn_\d+",
r"tflite_graph_\d+",
"ei_classifier_inferencing_categories",
r"ei_dsp_config_\d+",
"ei_dsp_blocks",
"ei_learning_blocks",
r"ei_learning_block_config_\d+",
r"ei_learning_block_\d+_inputs",
"ei_object_detection_nms(?!_config)",
"ei_calibration"
]
edit_file(f, patterns, suffix)
# Merge model_variables.h into 1st project
if project_ids.index(p) > 0:
merge_model_variables(f, os.path.join(target_dir, "model-parameters/model_variables.h"))
# Copy template files to tmpdir
shutil.copytree('templates', target_dir, dirs_exist_ok=True)
# Get sample code to customize main.cpp
# Get impulses ID from model_variables.h
with open(os.path.join(target_dir, 'model-parameters/model_variables.h'), 'r') as file:
file_content = file.read()
impulses_id_set = set(re.findall(r"impulse_(\d+)_(\d+)", file_content))
impulses_id = {}
for i in impulses_id_set:
impulses_id[i[0]] = i[1]
get_signal_code = "\n"
raw_features_code = "\n"
run_classifier_code = "\n"
callback_function_code = "\n"
newline = "\n"
# custom code for each project
for p in project_ids:
get_signal_code += f"static int get_signal_data_{p}(size_t offset, size_t length, float *out_ptr);{newline}"
raw_features_code += f"static const float features_{p}[] = {{ ... }}; // copy features from project {p}{newline}"
deploy_version = impulses_id[p]
run_classifier_code += f"""
// new process_impulse call for project ID {p}
signal.total_length = impulse_{p}_{deploy_version}.dsp_input_frame_size;
signal.get_data = &get_signal_data_{p};
res = process_impulse(&impulse_handle_{p}_{deploy_version}, &signal, &result, false);
printf("process_impulse for project {p} returned: %d\\r\\n", res);
display_custom_results(&result, &impulse_{p}_{deploy_version});
{newline}"""
callback_function_code += f"""
static int get_signal_data_{p}(size_t offset, size_t length, float *out_ptr) {{
for (size_t i = 0; i < length; i++) {{
out_ptr[i] = (features_{p} + offset)[i];
}}
return EIDSP_OK;
}}
{newline}"""
# Insert custom code in main.cpp
with open(os.path.join(target_dir, 'source/main.cpp'), 'r') as file1:
main_template = file1.readlines()
idx = main_template.index("// get_signal declaration inserted here\n") +1
main_template[idx:idx] = get_signal_code
idx = main_template.index("// raw features array inserted here\n") + 1
main_template[idx:idx] = raw_features_code
idx = main_template.index("// process_impulse inserted here\n") + 1
main_template[idx:idx] = run_classifier_code
idx = main_template.index("// callback functions inserted here\n") + 1
main_template[idx:idx] = callback_function_code
logger.info("Editing main.cpp")
with open(os.path.join(target_dir, 'source/main.cpp'), 'w') as file1:
file1.writelines(main_template)
logger.info("main.cpp edited")
logger.info("Merging done!")
# Create archive
shutil.make_archive(os.path.join(args.out_directory, 'deploy'), 'zip', target_dir)