edgefirst-validator 4.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepview/modelpack/utils/argmax.py +16 -0
- edgefirst/validator/__init__.py +1 -0
- edgefirst/validator/__main__.py +375 -0
- edgefirst/validator/datasets/__init__.py +118 -0
- edgefirst/validator/datasets/cache.py +296 -0
- edgefirst/validator/datasets/core.py +250 -0
- edgefirst/validator/datasets/darknet.py +446 -0
- edgefirst/validator/datasets/database.py +1067 -0
- edgefirst/validator/datasets/instance/__init__.py +4 -0
- edgefirst/validator/datasets/instance/core.py +222 -0
- edgefirst/validator/datasets/instance/detection.py +145 -0
- edgefirst/validator/datasets/instance/multitask.py +80 -0
- edgefirst/validator/datasets/instance/segmentation.py +120 -0
- edgefirst/validator/datasets/utils/fetch.py +682 -0
- edgefirst/validator/datasets/utils/readers.py +425 -0
- edgefirst/validator/datasets/utils/transformations.py +1695 -0
- edgefirst/validator/evaluators/__init__.py +17 -0
- edgefirst/validator/evaluators/callbacks/__init__.py +3 -0
- edgefirst/validator/evaluators/callbacks/core.py +192 -0
- edgefirst/validator/evaluators/callbacks/plots.py +900 -0
- edgefirst/validator/evaluators/callbacks/studio.py +234 -0
- edgefirst/validator/evaluators/core.py +257 -0
- edgefirst/validator/evaluators/detection.py +749 -0
- edgefirst/validator/evaluators/multitask.py +270 -0
- edgefirst/validator/evaluators/parameters/__init__.py +53 -0
- edgefirst/validator/evaluators/parameters/core.py +554 -0
- edgefirst/validator/evaluators/parameters/dataset.py +239 -0
- edgefirst/validator/evaluators/parameters/model.py +338 -0
- edgefirst/validator/evaluators/parameters/validation.py +528 -0
- edgefirst/validator/evaluators/segmentation.py +729 -0
- edgefirst/validator/evaluators/utils/__init__.py +3 -0
- edgefirst/validator/evaluators/utils/classify.py +292 -0
- edgefirst/validator/evaluators/utils/match.py +262 -0
- edgefirst/validator/evaluators/utils/timer.py +132 -0
- edgefirst/validator/metrics/__init__.py +9 -0
- edgefirst/validator/metrics/data/__init__.py +7 -0
- edgefirst/validator/metrics/data/label.py +668 -0
- edgefirst/validator/metrics/data/metrics.py +759 -0
- edgefirst/validator/metrics/data/plots.py +476 -0
- edgefirst/validator/metrics/data/stats.py +507 -0
- edgefirst/validator/metrics/detection.py +595 -0
- edgefirst/validator/metrics/segmentation.py +173 -0
- edgefirst/validator/metrics/utils/math.py +717 -0
- edgefirst/validator/publishers/__init__.py +3 -0
- edgefirst/validator/publishers/console.py +147 -0
- edgefirst/validator/publishers/studio.py +128 -0
- edgefirst/validator/publishers/tensorboard.py +119 -0
- edgefirst/validator/publishers/utils/logger.py +111 -0
- edgefirst/validator/publishers/utils/table.py +403 -0
- edgefirst/validator/runners/__init__.py +8 -0
- edgefirst/validator/runners/core.py +727 -0
- edgefirst/validator/runners/deepviewrt.py +177 -0
- edgefirst/validator/runners/hailo.py +263 -0
- edgefirst/validator/runners/keras.py +150 -0
- edgefirst/validator/runners/kinara.py +265 -0
- edgefirst/validator/runners/offline.py +228 -0
- edgefirst/validator/runners/onnx.py +241 -0
- edgefirst/validator/runners/processing/decode.py +320 -0
- edgefirst/validator/runners/processing/dvapi.py +4192 -0
- edgefirst/validator/runners/processing/nms.py +637 -0
- edgefirst/validator/runners/processing/outputs.py +507 -0
- edgefirst/validator/runners/tensorrt.py +321 -0
- edgefirst/validator/runners/tflite.py +221 -0
- edgefirst/validator/validate.py +843 -0
- edgefirst/validator/visualize/__init__.py +3 -0
- edgefirst/validator/visualize/detection.py +623 -0
- edgefirst/validator/visualize/segmentation.py +281 -0
- edgefirst/validator/visualize/utils/plots.py +635 -0
- edgefirst_validator-4.2.1.dist-info/METADATA +111 -0
- edgefirst_validator-4.2.1.dist-info/RECORD +73 -0
- edgefirst_validator-4.2.1.dist-info/WHEEL +5 -0
- edgefirst_validator-4.2.1.dist-info/entry_points.txt +2 -0
- edgefirst_validator-4.2.1.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from edgefirst.validator.runners.core import Runner
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from edgefirst.validator.evaluators import ModelParameters, TimerContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TensorRTRunner(Runner):
|
|
16
|
+
"""
|
|
17
|
+
Loads and runs TensorRT Engines (.engine, .trt). These models
|
|
18
|
+
are intended to be deployed on a device with a dedicated GPU.
|
|
19
|
+
This implementation was taken from the following sources:
|
|
20
|
+
https://github.com/NVIDIA/TensorRT/blob/main/samples/python/efficientdet/infer.py
|
|
21
|
+
https://github.com/ultralytics/ultralytics/blob/main/ultralytics/nn/autobackend.py#L326
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
model: Any
|
|
26
|
+
This is typically the path to the model or the loaded TensorRT model.
|
|
27
|
+
parameters: ModelParameters
|
|
28
|
+
These are the model parameters set from the command line.
|
|
29
|
+
metadata: dict
|
|
30
|
+
The model metadata which contains information for decoding
|
|
31
|
+
the model outputs.
|
|
32
|
+
timer: TimerContext
|
|
33
|
+
A timer object for handling validation timings for the model.
|
|
34
|
+
|
|
35
|
+
Raises
|
|
36
|
+
------
|
|
37
|
+
ImportError
|
|
38
|
+
Missing tensorrt library.
|
|
39
|
+
FileNotFoundError
|
|
40
|
+
Raised if the path to the model does not exist.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
model: Any,
|
|
46
|
+
parameters: ModelParameters,
|
|
47
|
+
metadata: dict,
|
|
48
|
+
timer: TimerContext
|
|
49
|
+
):
|
|
50
|
+
super(TensorRTRunner, self).__init__(model, parameters, timer=timer)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
import tensorrt as trt
|
|
54
|
+
except ImportError:
|
|
55
|
+
raise ImportError(
|
|
56
|
+
"tensorrt is needed to run TensorRT models.")
|
|
57
|
+
try:
|
|
58
|
+
import pycuda.driver as cuda # type: ignore
|
|
59
|
+
except ImportError:
|
|
60
|
+
raise ImportError(
|
|
61
|
+
"pycuda is needed to perform memory allocations for TensorRT.")
|
|
62
|
+
|
|
63
|
+
# Use autoprimaryctx if available (pycuda >= 2021.1) to
|
|
64
|
+
# prevent issues with other modules that rely on the primary
|
|
65
|
+
# device context.
|
|
66
|
+
try:
|
|
67
|
+
import pycuda.autoprimaryctx # type: ignore
|
|
68
|
+
except ModuleNotFoundError:
|
|
69
|
+
try:
|
|
70
|
+
import pycuda.autoinit # type: ignore
|
|
71
|
+
except ImportError:
|
|
72
|
+
raise ImportError(
|
|
73
|
+
"supported NVIDIA GPU device is needed.")
|
|
74
|
+
|
|
75
|
+
# TensorRT are intended to run on the GPU.
|
|
76
|
+
self.parameters.engine = "gpu"
|
|
77
|
+
self.logger = trt.Logger(trt.Logger.INFO)
|
|
78
|
+
trt.init_libnvinfer_plugins(self.logger, namespace="")
|
|
79
|
+
|
|
80
|
+
# Read file
|
|
81
|
+
if isinstance(model, str):
|
|
82
|
+
with open(model, "rb") as f, trt.Runtime(self.logger) as runtime:
|
|
83
|
+
assert runtime
|
|
84
|
+
try:
|
|
85
|
+
meta_len = int.from_bytes(
|
|
86
|
+
f.read(4), byteorder="little") # read metadata length
|
|
87
|
+
metadata = json.loads(
|
|
88
|
+
f.read(meta_len).decode("utf-8")) # read metadata
|
|
89
|
+
dla = metadata.get("dla", None)
|
|
90
|
+
if dla is not None:
|
|
91
|
+
runtime.DLA_core = int(dla)
|
|
92
|
+
except UnicodeDecodeError:
|
|
93
|
+
# engine file may lack embedded Ultralytics metadata
|
|
94
|
+
f.seek(0)
|
|
95
|
+
self.model = runtime.deserialize_cuda_engine(
|
|
96
|
+
f.read()) # read engine
|
|
97
|
+
assert self.model
|
|
98
|
+
|
|
99
|
+
self.context = self.model.create_execution_context()
|
|
100
|
+
assert self.context
|
|
101
|
+
|
|
102
|
+
self.output_names = []
|
|
103
|
+
self.output = []
|
|
104
|
+
self.input = []
|
|
105
|
+
self.allocations = []
|
|
106
|
+
self.scales = [] # Float values for dequantizing INT8 outputs.
|
|
107
|
+
|
|
108
|
+
num = range(self.model.num_bindings if hasattr(self.model, "num_bindings")
|
|
109
|
+
else self.model.num_io_tensors)
|
|
110
|
+
|
|
111
|
+
for i in num:
|
|
112
|
+
name = self.model.get_tensor_name(i)
|
|
113
|
+
dtype = np.dtype(trt.nptype(self.model.get_tensor_dtype(name)))
|
|
114
|
+
shape = tuple(self.context.get_tensor_shape(name))
|
|
115
|
+
is_input = self.model.get_tensor_mode(
|
|
116
|
+
name) == trt.TensorIOMode.INPUT
|
|
117
|
+
if is_input:
|
|
118
|
+
if -1 in tuple(self.model.get_tensor_shape(name)):
|
|
119
|
+
self.context.set_input_shape(
|
|
120
|
+
name, tuple(self.model.get_tensor_profile_shape(name, 0)[1]))
|
|
121
|
+
else:
|
|
122
|
+
self.output_names.append(name)
|
|
123
|
+
|
|
124
|
+
dyn_range = None
|
|
125
|
+
if hasattr(self.model, "get_tensor_dynamic_range"):
|
|
126
|
+
dyn_range = self.model.get_tensor_dynamic_range(i)
|
|
127
|
+
elif hasattr(self.model, "get_binding_dynamic_range"):
|
|
128
|
+
dyn_range = self.model.get_binding_dynamic_range(i)
|
|
129
|
+
|
|
130
|
+
if dyn_range and dyn_range > 0:
|
|
131
|
+
scale = float(dyn_range) / 127.0
|
|
132
|
+
self.scales.append(scale)
|
|
133
|
+
|
|
134
|
+
shape = tuple(self.context.get_tensor_shape(name))
|
|
135
|
+
size = dtype.itemsize
|
|
136
|
+
for s in shape:
|
|
137
|
+
size *= s
|
|
138
|
+
allocation = cuda.mem_alloc(size)
|
|
139
|
+
host_allocation = None if is_input else np.zeros(shape, dtype)
|
|
140
|
+
|
|
141
|
+
binding = {
|
|
142
|
+
"index": i,
|
|
143
|
+
"name": name,
|
|
144
|
+
"dtype": dtype,
|
|
145
|
+
"shape": list(shape),
|
|
146
|
+
"allocation": allocation,
|
|
147
|
+
"host_allocation": host_allocation,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
self.allocations.append(allocation)
|
|
151
|
+
if is_input:
|
|
152
|
+
self.input.append(binding)
|
|
153
|
+
else:
|
|
154
|
+
self.output.append(binding)
|
|
155
|
+
|
|
156
|
+
assert len(self.input) > 0
|
|
157
|
+
assert len(self.output) > 0
|
|
158
|
+
assert len(self.allocations) > 0
|
|
159
|
+
|
|
160
|
+
outputs = [np.zeros(out["shape"], np.dtype(out["dtype"]))
|
|
161
|
+
for out in self.output]
|
|
162
|
+
self.init_decoder(metadata=metadata, outputs=outputs)
|
|
163
|
+
|
|
164
|
+
if self.parameters.warmup > 0:
|
|
165
|
+
self.warmup()
|
|
166
|
+
|
|
167
|
+
def infer(self, image: np.ndarray) -> list:
|
|
168
|
+
"""
|
|
169
|
+
Executes inference on a batch of images.
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
image: np.ndarray
|
|
174
|
+
The image input after being preprocessed.
|
|
175
|
+
Typically this is an RGB image array with the same
|
|
176
|
+
input shape as the model.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
list
|
|
181
|
+
Raw model outputs stored inside a list.
|
|
182
|
+
|
|
183
|
+
Raises
|
|
184
|
+
------
|
|
185
|
+
ImportError
|
|
186
|
+
Raised if the pycuda library is not installed.
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
import pycuda.driver as cuda # type: ignore
|
|
190
|
+
except ImportError:
|
|
191
|
+
raise ImportError(
|
|
192
|
+
"pycuda driver is needed for TensorRT inference.")
|
|
193
|
+
|
|
194
|
+
start = time.perf_counter()
|
|
195
|
+
# Copy I/O and Execute.
|
|
196
|
+
image = np.ascontiguousarray(image)
|
|
197
|
+
cuda.memcpy_htod(self.input[0]['allocation'], image)
|
|
198
|
+
elapsed = time.perf_counter() - start
|
|
199
|
+
# Setting the tensor as part of the preprocess time.
|
|
200
|
+
self.timer.add_time("input", elapsed * 1e3) # Convert to ms.
|
|
201
|
+
|
|
202
|
+
with self.timer.time("inference"):
|
|
203
|
+
self.context.execute_v2(self.allocations)
|
|
204
|
+
|
|
205
|
+
def run_single_instance(self, image: np.ndarray) -> Any:
|
|
206
|
+
"""
|
|
207
|
+
Run TensorRT inference on a single image and record the timings.
|
|
208
|
+
Memory copying to and from the GPU device be performed here.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
image: np.ndarray
|
|
213
|
+
The input image after being preprocessed.
|
|
214
|
+
Typically this is an RGB image array.
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
Any
|
|
219
|
+
This could either return detection outputs after NMS.
|
|
220
|
+
np.ndarray
|
|
221
|
+
The prediction bounding boxes.. [[box1], [box2], ...].
|
|
222
|
+
np.ndarray
|
|
223
|
+
The prediction labels.. [cl1, cl2, ...].
|
|
224
|
+
np.ndarray
|
|
225
|
+
The prediction confidence scores.. [score, score, ...]
|
|
226
|
+
normalized between 0 and 1.
|
|
227
|
+
This could also return segmentation masks.
|
|
228
|
+
np.ndarray
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
import pycuda.driver as cuda # type: ignore
|
|
232
|
+
except ImportError:
|
|
233
|
+
raise ImportError(
|
|
234
|
+
"pycuda driver is needed for TensorRT inference.")
|
|
235
|
+
|
|
236
|
+
# Inference
|
|
237
|
+
self.infer(image)
|
|
238
|
+
|
|
239
|
+
start = time.perf_counter()
|
|
240
|
+
for o in range(len(self.output)):
|
|
241
|
+
cuda.memcpy_dtoh(
|
|
242
|
+
self.output[o]['host_allocation'],
|
|
243
|
+
self.output[o]['allocation'])
|
|
244
|
+
|
|
245
|
+
# Dequantize INT8 outputs if engine provides dynamic ranges.
|
|
246
|
+
if len(self.scales):
|
|
247
|
+
outputs = []
|
|
248
|
+
for i, out in enumerate(self.output):
|
|
249
|
+
o = out['host_allocation']
|
|
250
|
+
# only handle integer types here
|
|
251
|
+
if o.dtype in [np.int8, np.uint8]:
|
|
252
|
+
o = o.astype(np.float32) * self.scales[i]
|
|
253
|
+
outputs.append(o)
|
|
254
|
+
else:
|
|
255
|
+
outputs = [o['host_allocation'] for o in self.output]
|
|
256
|
+
elapsed = time.perf_counter() - start
|
|
257
|
+
|
|
258
|
+
# Postprocessing
|
|
259
|
+
outputs = self.postprocessing(outputs)
|
|
260
|
+
|
|
261
|
+
# Fetching the tensor as part of the postprocess time.
|
|
262
|
+
self.timer.add_time("output", elapsed * 1e3) # Convert to ms.
|
|
263
|
+
|
|
264
|
+
return outputs
|
|
265
|
+
|
|
266
|
+
def input_spec(self) -> Tuple[tuple, np.dtype]:
|
|
267
|
+
"""
|
|
268
|
+
Grabs the specs for the input tensor
|
|
269
|
+
of the network. Useful to prepare memory allocations.
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
shape: tuple
|
|
274
|
+
The shape of the input tensor.
|
|
275
|
+
dtype: np.dtype
|
|
276
|
+
The input datatype.
|
|
277
|
+
"""
|
|
278
|
+
return self.input[0]['shape'], self.input[0]['dtype']
|
|
279
|
+
|
|
280
|
+
def output_spec(self) -> list:
|
|
281
|
+
"""
|
|
282
|
+
Grabs the specs for the output tensors of the network.
|
|
283
|
+
Useful to prepare memory allocations.
|
|
284
|
+
|
|
285
|
+
Returns
|
|
286
|
+
-------
|
|
287
|
+
list
|
|
288
|
+
A list with two items per element, the shape and (numpy)
|
|
289
|
+
datatype of each output tensor.
|
|
290
|
+
"""
|
|
291
|
+
specs = list()
|
|
292
|
+
for o in self.output:
|
|
293
|
+
specs.append((o['shape'], o['dtype']))
|
|
294
|
+
return specs
|
|
295
|
+
|
|
296
|
+
def get_input_type(self) -> np.dtype:
|
|
297
|
+
"""
|
|
298
|
+
This returns the input type of the model for the
|
|
299
|
+
input with shape in the form
|
|
300
|
+
(batch size, channels, height, width) or
|
|
301
|
+
(batch size, height, width, channels).
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
np.dtype
|
|
306
|
+
The input type of the model.
|
|
307
|
+
"""
|
|
308
|
+
return self.input[0]['dtype']
|
|
309
|
+
|
|
310
|
+
def get_input_shape(self) -> list:
|
|
311
|
+
"""
|
|
312
|
+
This fetches the model input shape.
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
list
|
|
317
|
+
The model input shape
|
|
318
|
+
[batch size, channels, height, width] or
|
|
319
|
+
[batch size, height, width, channels].
|
|
320
|
+
"""
|
|
321
|
+
return self.input[0]['shape']
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from edgefirst.validator.publishers.utils.logger import logger
|
|
10
|
+
from edgefirst.validator.runners.core import Runner
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from edgefirst.validator.evaluators import ModelParameters, TimerContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TFliteRunner(Runner):
|
|
17
|
+
"""
|
|
18
|
+
Loads and runs TensorFlow Lite models for inference.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
model: Any
|
|
23
|
+
The is typically the path to the model or the loaded TFLite model.
|
|
24
|
+
parameters: ModelParameters
|
|
25
|
+
These are the model parameters set from the command line.
|
|
26
|
+
metadata: dict
|
|
27
|
+
The model metadata which contains information for decoding
|
|
28
|
+
the model outputs.
|
|
29
|
+
timer: TimerContext
|
|
30
|
+
A timer object for handling validation timings for the model.
|
|
31
|
+
|
|
32
|
+
Raises
|
|
33
|
+
------
|
|
34
|
+
ImportError
|
|
35
|
+
Raised if tflite_runtime and TensorFlow is not intalled.
|
|
36
|
+
FileNotFoundError
|
|
37
|
+
Raised if the path to the model does not exist.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
model: Any,
|
|
43
|
+
parameters: ModelParameters,
|
|
44
|
+
metadata: dict,
|
|
45
|
+
timer: TimerContext
|
|
46
|
+
):
|
|
47
|
+
super(TFliteRunner, self).__init__(model, parameters, timer=timer)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
from tflite_runtime.interpreter import Interpreter # type: ignore
|
|
51
|
+
except ImportError:
|
|
52
|
+
logger("tflite_runtime is not installed. Falling back to TensorFlow.",
|
|
53
|
+
code="WARNING")
|
|
54
|
+
try:
|
|
55
|
+
import tensorflow as tf # type: ignore
|
|
56
|
+
Interpreter = tf.lite.Interpreter
|
|
57
|
+
except ImportError:
|
|
58
|
+
raise ImportError(
|
|
59
|
+
"TensorFlow or tflite_runtime is needed to run TFLite models.")
|
|
60
|
+
|
|
61
|
+
if isinstance(model, str):
|
|
62
|
+
if not os.path.exists(model):
|
|
63
|
+
raise FileNotFoundError(
|
|
64
|
+
"The model '{}' does not exist.".format(model))
|
|
65
|
+
|
|
66
|
+
ext_delegate = self.select_delegates()
|
|
67
|
+
logger(f"Engine: {self.parameters.engine}", code="INFO")
|
|
68
|
+
|
|
69
|
+
if ext_delegate:
|
|
70
|
+
self.model = Interpreter(
|
|
71
|
+
model_path=model,
|
|
72
|
+
experimental_delegates=[ext_delegate]
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
self.model = Interpreter(model_path=model)
|
|
76
|
+
|
|
77
|
+
self.model.allocate_tensors()
|
|
78
|
+
|
|
79
|
+
self.input_details = self.model.get_input_details()
|
|
80
|
+
self.output_details = self.model.get_output_details()
|
|
81
|
+
self.parameters.common.input_quantization = self.input_details[0][
|
|
82
|
+
"quantization"]
|
|
83
|
+
self.init_decoder(metadata=metadata, outputs=self.output_details)
|
|
84
|
+
|
|
85
|
+
# The model NumPy view object.
|
|
86
|
+
self.parameters.common.input_tensor = self.model.tensor(
|
|
87
|
+
self.input_details[0]["index"])
|
|
88
|
+
|
|
89
|
+
if self.parameters.warmup > 0:
|
|
90
|
+
self.warmup()
|
|
91
|
+
|
|
92
|
+
def select_delegates(self) -> Any:
|
|
93
|
+
"""
|
|
94
|
+
Specify the delegates to load based on
|
|
95
|
+
the type of engine specified.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
Any
|
|
100
|
+
This is either the loaded delegate object or None
|
|
101
|
+
if it doesn't exist.
|
|
102
|
+
"""
|
|
103
|
+
try: # https://coral.ai/docs/edgetpu/tflite-python/#update-existing-tf-lite-code-for-the-edge-tpu
|
|
104
|
+
from tflite_runtime.interpreter import load_delegate # type: ignore
|
|
105
|
+
except ImportError:
|
|
106
|
+
try:
|
|
107
|
+
import tensorflow as tf # type: ignore
|
|
108
|
+
load_delegate = tf.lite.experimental.load_delegate
|
|
109
|
+
except ImportError:
|
|
110
|
+
raise ImportError(
|
|
111
|
+
"TensorFlow or tflite_runtime is needed to run TFLite models.")
|
|
112
|
+
|
|
113
|
+
ext_delegate = None
|
|
114
|
+
if (os.path.exists(self.parameters.engine) and
|
|
115
|
+
self.parameters.engine.endswith(".so")):
|
|
116
|
+
ext_delegate = load_delegate(self.parameters.engine, {})
|
|
117
|
+
elif self.parameters.engine.lower() == "npu":
|
|
118
|
+
if os.path.exists("/usr/lib/libvx_delegate.so"):
|
|
119
|
+
self.parameters.engine = "/usr/lib/libvx_delegate.so"
|
|
120
|
+
ext_delegate = load_delegate(self.parameters.engine, {})
|
|
121
|
+
logger("Using '/usr/lib/libvx_delegate.so' for NPU inference.",
|
|
122
|
+
code="INFO")
|
|
123
|
+
elif os.path.exists("/usr/lib/libneutron_delegate.so"):
|
|
124
|
+
self.parameters.engine = "/usr/lib/libneutron_delegate.so"
|
|
125
|
+
ext_delegate = load_delegate(self.parameters.engine, {})
|
|
126
|
+
logger("Using '/usr/lib/libneutron_delegate.so' for NPU inference.",
|
|
127
|
+
code="INFO")
|
|
128
|
+
else:
|
|
129
|
+
logger(
|
|
130
|
+
"Specified NPU, but cannot find '/usr/lib/lib<>_delegate.so'. " +
|
|
131
|
+
"Specify the path to libvx_delegate.so or libneutron_delegate.so " +
|
|
132
|
+
"in your system. Falling back to use the CPU instead.", code="WARNING")
|
|
133
|
+
self.parameters.engine = "cpu"
|
|
134
|
+
elif self.parameters.engine.lower() == "gpu":
|
|
135
|
+
logger(
|
|
136
|
+
"Inference with the GPU is currently not supported for TFLite. " +
|
|
137
|
+
"Falling back to use the CPU instead.", code="WARNING")
|
|
138
|
+
self.parameters.engine = "cpu"
|
|
139
|
+
return ext_delegate
|
|
140
|
+
|
|
141
|
+
def run_single_instance(self, image: np.ndarray = None) -> Any:
|
|
142
|
+
"""
|
|
143
|
+
Run TFLite inference on a single image and record the timings.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
image: np.ndarray
|
|
148
|
+
The input image after being preprocessed.
|
|
149
|
+
Typically this is an RGB image array. This is by default None.
|
|
150
|
+
Currently setting the image tensor using the logic below.
|
|
151
|
+
|
|
152
|
+
.. code-block:: python
|
|
153
|
+
|
|
154
|
+
input_tensor = self.model.tensor(self.input_details[0]["index"])
|
|
155
|
+
np.copyto(input_tensor(), image)
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
Any
|
|
160
|
+
This could either return detection outputs after NMS.
|
|
161
|
+
np.ndarray
|
|
162
|
+
The prediction bounding boxes.. [[box1], [box2], ...].
|
|
163
|
+
np.ndarray
|
|
164
|
+
The prediction labels.. [cl1, cl2, ...].
|
|
165
|
+
np.ndarray
|
|
166
|
+
The prediction confidence scores.. [score, score, ...]
|
|
167
|
+
normalized between 0 and 1.
|
|
168
|
+
This could also return segmentation masks.
|
|
169
|
+
np.ndarray
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
# Inference
|
|
173
|
+
with self.timer.time("inference"):
|
|
174
|
+
self.model.invoke()
|
|
175
|
+
|
|
176
|
+
start = time.perf_counter()
|
|
177
|
+
outputs = [self.model.get_tensor(output["index"])
|
|
178
|
+
for output in self.output_details]
|
|
179
|
+
elapsed = time.perf_counter() - start
|
|
180
|
+
|
|
181
|
+
# Postprocessing
|
|
182
|
+
outputs = self.postprocessing(outputs)
|
|
183
|
+
|
|
184
|
+
# Fetching the tensor as part of the postprocess time.
|
|
185
|
+
self.timer.add_time("output", elapsed * 1e3) # Convert to ms.
|
|
186
|
+
|
|
187
|
+
return outputs
|
|
188
|
+
|
|
189
|
+
def get_input_type(self) -> np.dtype:
|
|
190
|
+
"""
|
|
191
|
+
This returns the input type of the model with shape
|
|
192
|
+
(batch size, channels, height, width) or
|
|
193
|
+
(batch size, height, width, channels).
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
np.dtype
|
|
198
|
+
The input type of the model.
|
|
199
|
+
"""
|
|
200
|
+
return self.input_details[0]["dtype"]
|
|
201
|
+
|
|
202
|
+
def get_input_shape(self) -> np.ndarray:
|
|
203
|
+
"""
|
|
204
|
+
Grabs the model input shape.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
np.ndarray
|
|
209
|
+
The model input shape (batch size, channels, height, width) or
|
|
210
|
+
(batch size, height, width, channels).
|
|
211
|
+
"""
|
|
212
|
+
for input in self.input_details:
|
|
213
|
+
shape = input["shape"]
|
|
214
|
+
# Detection shapes are in the form (batch size, height, width,
|
|
215
|
+
# channels=3).
|
|
216
|
+
if len(shape) == 4:
|
|
217
|
+
if shape[1] == 3 or shape[-1] == [3]:
|
|
218
|
+
return shape
|
|
219
|
+
# If it does not conform with expected format, return the first
|
|
220
|
+
# element.
|
|
221
|
+
return self.input_details[0]["shape"]
|