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,507 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Union, List
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from edgefirst.validator.evaluators import ModelParameters
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Outputs:
|
|
12
|
+
"""
|
|
13
|
+
Store the metadata output details needed for decoding the model outputs.
|
|
14
|
+
|
|
15
|
+
If the metadata exists, then parse the metadata to store the output details.
|
|
16
|
+
Otherwise, rely on a separate logic to define the output types based on shape.
|
|
17
|
+
|
|
18
|
+
If the metadata exists, reinitialize the index based on the existing
|
|
19
|
+
output tensors, as we cannot rely on the current output index stored in
|
|
20
|
+
the metadata.
|
|
21
|
+
|
|
22
|
+
Prioritize decoding the variant types such as "detection" and "segmentation".
|
|
23
|
+
Otherwise, decode the known types such as "boxes", "scores", and "masks".
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
metadata: dict
|
|
28
|
+
The contents of the model metadata for decoding the outputs.
|
|
29
|
+
parameters: ModelParameters
|
|
30
|
+
These are the model parameters set from the command line.
|
|
31
|
+
outputs: Union[List[dict], List[np.ndarray]]
|
|
32
|
+
This is either a List[dict] from a TFLite output details
|
|
33
|
+
or a List[np.ndarray] containing the shapes from the model outputs.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self,
|
|
37
|
+
metadata: dict,
|
|
38
|
+
parameters: ModelParameters,
|
|
39
|
+
outputs: Union[List[dict], List[np.ndarray]]):
|
|
40
|
+
|
|
41
|
+
self.metadata = metadata
|
|
42
|
+
self.parameters = parameters
|
|
43
|
+
self.outputs = outputs
|
|
44
|
+
|
|
45
|
+
self.boxes = {
|
|
46
|
+
"anchors": None,
|
|
47
|
+
"decode": False,
|
|
48
|
+
"decoder": None,
|
|
49
|
+
"index": None,
|
|
50
|
+
"shape": None,
|
|
51
|
+
"type": "boxes",
|
|
52
|
+
"dtype": None,
|
|
53
|
+
"quantization": None
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
self.scores = {
|
|
57
|
+
"anchors": None,
|
|
58
|
+
"decode": False,
|
|
59
|
+
"decoder": None,
|
|
60
|
+
"index": None,
|
|
61
|
+
"shape": None,
|
|
62
|
+
"type": "scores",
|
|
63
|
+
"dtype": None,
|
|
64
|
+
"quantization": None
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
self.classes = {
|
|
68
|
+
"anchors": None,
|
|
69
|
+
"decode": False,
|
|
70
|
+
"decoder": None,
|
|
71
|
+
"index": None,
|
|
72
|
+
"shape": None,
|
|
73
|
+
"type": "classes",
|
|
74
|
+
"dtype": None,
|
|
75
|
+
"quantization": None
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
self.masks = {
|
|
79
|
+
"anchors": None,
|
|
80
|
+
"decode": False,
|
|
81
|
+
"decoder": None,
|
|
82
|
+
"index": None,
|
|
83
|
+
"shape": None,
|
|
84
|
+
"type": "masks",
|
|
85
|
+
"dtype": None,
|
|
86
|
+
"quantization": None
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Variant types from the metadata to decode modelpack.
|
|
90
|
+
# Currently contains types such as "detection" or "segmentation"
|
|
91
|
+
# which can appear common for multiple outputs.
|
|
92
|
+
self.mpk_types = []
|
|
93
|
+
self.num_boxes = 0 # The number of boxes in the model output shape.
|
|
94
|
+
|
|
95
|
+
# Parse the metadata if it exists to determine the output types.
|
|
96
|
+
# Otherwise check the output shapes to automatically determine the
|
|
97
|
+
# types.
|
|
98
|
+
if self.metadata is not None and "outputs" in self.metadata.keys():
|
|
99
|
+
self.parse_metadata()
|
|
100
|
+
else:
|
|
101
|
+
self.create_metadata()
|
|
102
|
+
|
|
103
|
+
def parse_metadata(self):
|
|
104
|
+
"""
|
|
105
|
+
Parses the contents of the metadata and redefines the output
|
|
106
|
+
index based on the existing output as we cannot rely on the
|
|
107
|
+
output index stored in the metadata.
|
|
108
|
+
"""
|
|
109
|
+
# Get the model output types.
|
|
110
|
+
box_index = self.get_boxes_index()
|
|
111
|
+
mask_index = self.get_masks_index()
|
|
112
|
+
scores_index = self.get_scores_index()
|
|
113
|
+
decoded_masks_index = self.get_decoded_masks_index()
|
|
114
|
+
|
|
115
|
+
for output_details in self.metadata["outputs"]:
|
|
116
|
+
config_shape = output_details["shape"]
|
|
117
|
+
output_index = output_details["output_index"]
|
|
118
|
+
decoder = output_details["decoder"]
|
|
119
|
+
output_type = output_details["type"]
|
|
120
|
+
dtype = output_details["dtype"]
|
|
121
|
+
anchors = output_details["anchors"]
|
|
122
|
+
anchors = np.array(anchors) if anchors is not None else None
|
|
123
|
+
quantization = None
|
|
124
|
+
|
|
125
|
+
# Redefine the output index based on the actual model outputs.
|
|
126
|
+
for i, output in enumerate(self.outputs):
|
|
127
|
+
if isinstance(output, dict):
|
|
128
|
+
shape = output["shape"].tolist()
|
|
129
|
+
quantization = output["quantization"]
|
|
130
|
+
else:
|
|
131
|
+
shape = list(output.shape)
|
|
132
|
+
|
|
133
|
+
# Check for edge case in Ultralytics ONNX [1, 32, 160, 160] vs.
|
|
134
|
+
# TFLite [1, 160, 160, 32].
|
|
135
|
+
if decoder == "yolov8" and len(
|
|
136
|
+
config_shape) == len(shape) == 4:
|
|
137
|
+
output_index = i
|
|
138
|
+
break
|
|
139
|
+
elif config_shape == shape:
|
|
140
|
+
output_index = i
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
context = {
|
|
144
|
+
"index": output_index,
|
|
145
|
+
"decode": output_details["decode"],
|
|
146
|
+
"decoder": decoder,
|
|
147
|
+
"anchors": anchors,
|
|
148
|
+
"shape": config_shape,
|
|
149
|
+
"type": output_type,
|
|
150
|
+
"dtype": dtype,
|
|
151
|
+
"quantization": quantization
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if output_type == "boxes":
|
|
155
|
+
self.boxes = context
|
|
156
|
+
self.boxes["index"] = box_index
|
|
157
|
+
self.parameters.common.with_boxes = True
|
|
158
|
+
elif output_type == "scores":
|
|
159
|
+
self.scores = context
|
|
160
|
+
self.scores["index"] = scores_index
|
|
161
|
+
self.parameters.common.with_boxes = True
|
|
162
|
+
elif output_type == "masks":
|
|
163
|
+
self.masks = context
|
|
164
|
+
self.masks["index"] = (decoded_masks_index if
|
|
165
|
+
decoded_masks_index is not None
|
|
166
|
+
else mask_index)
|
|
167
|
+
# Segmentation models from Ultralytics are multitask.
|
|
168
|
+
if decoder == "yolov8":
|
|
169
|
+
self.masks["index"] = mask_index
|
|
170
|
+
self.parameters.common.with_boxes = True
|
|
171
|
+
self.parameters.common.semantic = False
|
|
172
|
+
self.parameters.common.with_masks = True
|
|
173
|
+
else:
|
|
174
|
+
if decoder == "yolov8":
|
|
175
|
+
self.parameters.common.with_boxes = True
|
|
176
|
+
self.parameters.common.semantic = False
|
|
177
|
+
# shape [1, 160, 160, 32] are the mask protos outputs.
|
|
178
|
+
if (len(config_shape) > 3 or output_type == "protos"):
|
|
179
|
+
self.masks = context
|
|
180
|
+
self.masks["index"] = mask_index
|
|
181
|
+
self.parameters.common.with_masks = True
|
|
182
|
+
# shape [1, 116, 8400], [1, 85, 115200] are the score
|
|
183
|
+
# and/or mask outputs.
|
|
184
|
+
else:
|
|
185
|
+
self.scores = context
|
|
186
|
+
self.scores["index"] = scores_index
|
|
187
|
+
else:
|
|
188
|
+
context["type"] = output_type
|
|
189
|
+
if output_type == "detection":
|
|
190
|
+
self.parameters.common.with_boxes = True
|
|
191
|
+
elif output_type == "segmentation":
|
|
192
|
+
self.parameters.common.with_masks = True
|
|
193
|
+
self.mpk_types.append(context)
|
|
194
|
+
|
|
195
|
+
def create_metadata(self):
|
|
196
|
+
"""
|
|
197
|
+
Defines the output types based on the output shape
|
|
198
|
+
as either a boxes, scores, classes, or masks tensors.
|
|
199
|
+
This is called when the model metadata does not exist. The model
|
|
200
|
+
metadata is created here based on the logic for specifying the
|
|
201
|
+
model outputs.
|
|
202
|
+
|
|
203
|
+
This method currently only supports finding decoded output types
|
|
204
|
+
for ModelPack.
|
|
205
|
+
"""
|
|
206
|
+
# Get the model output types.
|
|
207
|
+
self.boxes["index"] = self.get_boxes_index()
|
|
208
|
+
self.masks["index"] = self.get_masks_index()
|
|
209
|
+
self.scores["index"] = self.get_scores_index()
|
|
210
|
+
|
|
211
|
+
decoded_masks_index = self.get_decoded_masks_index()
|
|
212
|
+
if decoded_masks_index is not None:
|
|
213
|
+
self.masks["index"] = decoded_masks_index
|
|
214
|
+
self.masks["decode"] = False
|
|
215
|
+
self.masks["type"] = "masks"
|
|
216
|
+
else:
|
|
217
|
+
self.masks["decode"] = True
|
|
218
|
+
self.masks["type"] = "segmentation"
|
|
219
|
+
self.classes["index"] = self.get_classes_index()
|
|
220
|
+
|
|
221
|
+
# Get the model tasks.
|
|
222
|
+
if (self.boxes["index"] is not None or
|
|
223
|
+
self.scores["index"] is not None or
|
|
224
|
+
self.classes["index"] is not None):
|
|
225
|
+
self.parameters.common.with_boxes = True
|
|
226
|
+
else:
|
|
227
|
+
self.parameters.common.with_boxes = False
|
|
228
|
+
|
|
229
|
+
if self.masks["index"] is not None:
|
|
230
|
+
self.parameters.common.with_masks = True
|
|
231
|
+
else:
|
|
232
|
+
self.parameters.common.with_masks = False
|
|
233
|
+
|
|
234
|
+
# MobileNet SSD has embedded NMS.
|
|
235
|
+
if self.classes["index"] is not None:
|
|
236
|
+
self.parameters.nms = "embedded"
|
|
237
|
+
|
|
238
|
+
# Formulate model metadata format.
|
|
239
|
+
self.metadata = {"outputs": []}
|
|
240
|
+
# ModelPack or Kinara
|
|
241
|
+
if (None not in [self.boxes["index"], self.scores["index"]]):
|
|
242
|
+
boxes = self.outputs[self.boxes["index"]]
|
|
243
|
+
if isinstance(boxes, dict):
|
|
244
|
+
shape = boxes["shape"].tolist()
|
|
245
|
+
dtype = np.dtype(boxes["dtype"]).name
|
|
246
|
+
self.boxes["quantization"] = boxes["quantization"]
|
|
247
|
+
else:
|
|
248
|
+
shape = list(boxes.shape)
|
|
249
|
+
dtype = boxes.dtype.name if hasattr(
|
|
250
|
+
boxes, "dtype") and hasattr(
|
|
251
|
+
boxes.dtype, "name") else boxes.dtype if hasattr(
|
|
252
|
+
boxes, "dtype") else boxes.type
|
|
253
|
+
self.boxes["shape"] = shape
|
|
254
|
+
self.boxes["dtype"] = dtype
|
|
255
|
+
|
|
256
|
+
scores = self.outputs[self.scores["index"]]
|
|
257
|
+
if isinstance(scores, dict):
|
|
258
|
+
shape = scores["shape"].tolist()
|
|
259
|
+
dtype = np.dtype(scores["dtype"]).name
|
|
260
|
+
self.scores["quantization"] = scores["quantization"]
|
|
261
|
+
else:
|
|
262
|
+
shape = list(scores.shape)
|
|
263
|
+
dtype = scores.dtype.name if hasattr(
|
|
264
|
+
scores, "dtype") and hasattr(
|
|
265
|
+
scores.dtype, "name") else scores.dtype if hasattr(
|
|
266
|
+
scores, "dtype") else scores.type
|
|
267
|
+
self.scores["shape"] = shape
|
|
268
|
+
self.scores["dtype"] = dtype
|
|
269
|
+
|
|
270
|
+
# Kinara YOLOv8
|
|
271
|
+
if len(self.boxes["shape"]) == 3:
|
|
272
|
+
self.boxes["decode"] = True
|
|
273
|
+
self.scores["decode"] = True
|
|
274
|
+
self.boxes["decoder"] = "yolov8"
|
|
275
|
+
self.scores["decoder"] = "yolov8"
|
|
276
|
+
self.boxes["channels_first"] = False
|
|
277
|
+
self.scores["channels_first"] = False
|
|
278
|
+
else:
|
|
279
|
+
# Creating metadata assumes ModelPack already has decoded
|
|
280
|
+
# outputs.
|
|
281
|
+
self.boxes["decode"] = False
|
|
282
|
+
self.scores["decode"] = False
|
|
283
|
+
self.boxes["decoder"] = "modelpack"
|
|
284
|
+
self.scores["decoder"] = "modelpack"
|
|
285
|
+
|
|
286
|
+
self.metadata["outputs"].append(self.boxes)
|
|
287
|
+
self.metadata["outputs"].append(self.scores)
|
|
288
|
+
|
|
289
|
+
if self.masks["index"] is not None:
|
|
290
|
+
self.masks["decoder"] = "modelpack"
|
|
291
|
+
|
|
292
|
+
masks = self.outputs[self.masks["index"]]
|
|
293
|
+
if isinstance(masks, dict):
|
|
294
|
+
shape = masks["shape"].tolist()
|
|
295
|
+
dtype = np.dtype(masks["dtype"]).name
|
|
296
|
+
self.masks["quantization"] = masks["quantization"]
|
|
297
|
+
else:
|
|
298
|
+
shape = list(masks.shape)
|
|
299
|
+
dtype = masks.dtype.name if hasattr(
|
|
300
|
+
masks, "dtype") and hasattr(
|
|
301
|
+
masks.dtype, "name") else masks.dtype if hasattr(
|
|
302
|
+
masks, "dtype") else masks.type
|
|
303
|
+
self.masks["shape"] = shape
|
|
304
|
+
self.masks["dtype"] = dtype
|
|
305
|
+
self.metadata["outputs"].append(self.masks)
|
|
306
|
+
|
|
307
|
+
# YOLOv5, YOLOv8, YOLOv11
|
|
308
|
+
elif self.scores["index"] is not None:
|
|
309
|
+
self.scores["decode"] = True
|
|
310
|
+
self.scores["decoder"] = "yolov8"
|
|
311
|
+
self.scores["type"] = "detection"
|
|
312
|
+
|
|
313
|
+
scores = self.outputs[self.scores["index"]]
|
|
314
|
+
if isinstance(scores, dict):
|
|
315
|
+
shape = scores["shape"].tolist()
|
|
316
|
+
dtype = np.dtype(scores["dtype"]).name
|
|
317
|
+
self.scores["quantization"] = scores["quantization"]
|
|
318
|
+
else:
|
|
319
|
+
shape = list(scores.shape)
|
|
320
|
+
dtype = scores.dtype.name if hasattr(
|
|
321
|
+
scores, "dtype") and hasattr(
|
|
322
|
+
scores.dtype, "name") else scores.dtype if hasattr(
|
|
323
|
+
scores, "dtype") else scores.type
|
|
324
|
+
self.scores["shape"] = shape
|
|
325
|
+
self.scores["dtype"] = dtype
|
|
326
|
+
|
|
327
|
+
if self.masks["index"] is not None:
|
|
328
|
+
self.parameters.common.semantic = False
|
|
329
|
+
self.scores["type"] = "segmentation"
|
|
330
|
+
self.masks["decoder"] = "yolov8"
|
|
331
|
+
|
|
332
|
+
masks = self.outputs[self.masks["index"]]
|
|
333
|
+
if isinstance(masks, dict):
|
|
334
|
+
shape = masks["shape"].tolist()
|
|
335
|
+
dtype = np.dtype(masks["dtype"]).name
|
|
336
|
+
self.masks["quantization"] = masks["quantization"]
|
|
337
|
+
else:
|
|
338
|
+
shape = list(masks.shape)
|
|
339
|
+
dtype = masks.dtype.name if hasattr(
|
|
340
|
+
masks, "dtype") and hasattr(
|
|
341
|
+
masks.dtype, "name") else masks.dtype if hasattr(
|
|
342
|
+
masks, "dtype") else masks.type
|
|
343
|
+
# NOTE: HAL decoder requires shape [1, 160, 160, 32]
|
|
344
|
+
if shape[1] == 32:
|
|
345
|
+
shape = [shape[0], shape[2], shape[3], shape[1]]
|
|
346
|
+
self.masks["shape"] = shape
|
|
347
|
+
self.masks["dtype"] = dtype
|
|
348
|
+
self.masks["type"] = "protos"
|
|
349
|
+
|
|
350
|
+
self.metadata["outputs"].append(self.masks)
|
|
351
|
+
self.metadata["outputs"].append(self.scores)
|
|
352
|
+
# ModelPack segmentation
|
|
353
|
+
else:
|
|
354
|
+
if self.masks["index"] is not None:
|
|
355
|
+
self.masks["decoder"] = "modelpack"
|
|
356
|
+
|
|
357
|
+
masks = self.outputs[self.masks["index"]]
|
|
358
|
+
if isinstance(masks, dict):
|
|
359
|
+
shape = masks["shape"].tolist()
|
|
360
|
+
dtype = np.dtype(masks["dtype"]).name
|
|
361
|
+
self.masks["quantization"] = masks["quantization"]
|
|
362
|
+
else:
|
|
363
|
+
shape = list(masks.shape)
|
|
364
|
+
dtype = masks.dtype.name if hasattr(
|
|
365
|
+
masks, "dtype") and hasattr(
|
|
366
|
+
masks.dtype, "name") else masks.dtype if hasattr(
|
|
367
|
+
masks, "dtype") else masks.type
|
|
368
|
+
self.masks["shape"] = shape
|
|
369
|
+
self.masks["dtype"] = dtype
|
|
370
|
+
self.metadata["outputs"].append(self.masks)
|
|
371
|
+
|
|
372
|
+
def get_boxes_index(self) -> Union[int, None]:
|
|
373
|
+
"""
|
|
374
|
+
Get the index of the bounding box outputs from the model.
|
|
375
|
+
Checking for Ultralytics and ModelPack variations.
|
|
376
|
+
Box output shapes can be in these variations:
|
|
377
|
+
[1, 6000, 1, 4], [1, 6000, 4], [1, 4, 8400]
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
Union[int, None]
|
|
382
|
+
The index is returned if the bounding box output shape exists.
|
|
383
|
+
Otherwise None is returned.
|
|
384
|
+
"""
|
|
385
|
+
# Checking ModelPack outputs.
|
|
386
|
+
for i, output in enumerate(self.outputs):
|
|
387
|
+
if isinstance(output, dict):
|
|
388
|
+
shape = output["shape"]
|
|
389
|
+
else:
|
|
390
|
+
shape = output.shape
|
|
391
|
+
|
|
392
|
+
if (len(shape) == 4 and shape[-1] == 4):
|
|
393
|
+
self.num_boxes = shape[1]
|
|
394
|
+
return i
|
|
395
|
+
elif len(shape) == 3:
|
|
396
|
+
if shape[1] == 4:
|
|
397
|
+
self.num_boxes = shape[-1]
|
|
398
|
+
return i
|
|
399
|
+
elif shape[-1] == 4:
|
|
400
|
+
self.num_boxes = shape[1]
|
|
401
|
+
return i
|
|
402
|
+
return None
|
|
403
|
+
|
|
404
|
+
def get_masks_index(self) -> Union[int, None]:
|
|
405
|
+
"""
|
|
406
|
+
Get the index of the encoded mask outputs from the model.
|
|
407
|
+
Checking for ModelPack variations only.
|
|
408
|
+
Mask shapes are typically [1, h, w, nc].
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
Union[int, None]
|
|
413
|
+
The index is returned if the mask output shape exists.
|
|
414
|
+
Otherwise None is returned.
|
|
415
|
+
"""
|
|
416
|
+
for i, output in enumerate(self.outputs):
|
|
417
|
+
if isinstance(output, dict):
|
|
418
|
+
shape = output["shape"]
|
|
419
|
+
else:
|
|
420
|
+
shape = output.shape
|
|
421
|
+
if len(shape) == 4 and shape[-2] != 1: # Avoid shape of the boxes.
|
|
422
|
+
return i
|
|
423
|
+
return None
|
|
424
|
+
|
|
425
|
+
def get_scores_index(self) -> Union[int, None]:
|
|
426
|
+
"""
|
|
427
|
+
Get the index of the score outputs from the model.
|
|
428
|
+
Checking for ModelPack and Ultralytics variations.
|
|
429
|
+
Score output shapes can be in these variations:
|
|
430
|
+
[1, 6000, 14], [1, 37, 8400], [1, 25200, 85], [1, 80, 8400]
|
|
431
|
+
|
|
432
|
+
Returns
|
|
433
|
+
-------
|
|
434
|
+
Union[int, None]
|
|
435
|
+
The index is returned if the score output shape exists.
|
|
436
|
+
Otherwise None is returned.
|
|
437
|
+
"""
|
|
438
|
+
for i, output in enumerate(self.outputs):
|
|
439
|
+
if isinstance(output, dict):
|
|
440
|
+
shape = output["shape"]
|
|
441
|
+
else:
|
|
442
|
+
shape = output.shape
|
|
443
|
+
if self.num_boxes != 0:
|
|
444
|
+
if len(shape) == 3 and self.num_boxes in [shape[1], shape[-1]]:
|
|
445
|
+
return i
|
|
446
|
+
# MobileNet SSD [1, 10]
|
|
447
|
+
elif len(shape) == 2 and i == 2 and shape[1] == self.num_boxes:
|
|
448
|
+
return i
|
|
449
|
+
else:
|
|
450
|
+
if len(shape) == 3:
|
|
451
|
+
if (((shape[1] > shape[2]) and (shape[1] / shape[2] > 5))
|
|
452
|
+
or ((shape[1] < shape[2]) and (shape[2] / shape[1] > 5))
|
|
453
|
+
and i != self.boxes["index"]):
|
|
454
|
+
return i
|
|
455
|
+
return None
|
|
456
|
+
|
|
457
|
+
def get_decoded_masks_index(self) -> Union[dict, None]:
|
|
458
|
+
"""
|
|
459
|
+
Get the index of the decoded mask outputs from the model.
|
|
460
|
+
Checking for ModelPack variations only.
|
|
461
|
+
|
|
462
|
+
Returns
|
|
463
|
+
-------
|
|
464
|
+
Union[int, None]
|
|
465
|
+
The index is returned if the decoded mask output shape exists.
|
|
466
|
+
Otherwise None is returned.
|
|
467
|
+
"""
|
|
468
|
+
# Segmentation will contain both encoded and decoded masks.
|
|
469
|
+
if len(self.outputs) > 1:
|
|
470
|
+
for i, output in enumerate(self.outputs):
|
|
471
|
+
if isinstance(output, dict):
|
|
472
|
+
shape = output["shape"]
|
|
473
|
+
else:
|
|
474
|
+
shape = output.shape
|
|
475
|
+
if self.num_boxes != 0:
|
|
476
|
+
if len(shape) == 3 and self.num_boxes not in [
|
|
477
|
+
shape[1], shape[-1]]:
|
|
478
|
+
return i
|
|
479
|
+
else:
|
|
480
|
+
if len(shape) == 3:
|
|
481
|
+
if ((shape[1] >= shape[2]) and (shape[1] / shape[2] < 5)) or (
|
|
482
|
+
(shape[1] <= shape[2]) and (shape[2] / shape[1] < 5)):
|
|
483
|
+
return i
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
def get_classes_index(self) -> Union[int, None]:
|
|
487
|
+
"""
|
|
488
|
+
Get the index of the class outputs. This is primarily seen
|
|
489
|
+
in MobileNet SSD models.
|
|
490
|
+
|
|
491
|
+
Returns
|
|
492
|
+
-------
|
|
493
|
+
Union[int, None]
|
|
494
|
+
The index is returned if the class output shape exists.
|
|
495
|
+
Otherwise None is returned.
|
|
496
|
+
"""
|
|
497
|
+
# Score outputs are in these variations: [1, 10].
|
|
498
|
+
for i, output in enumerate(self.outputs):
|
|
499
|
+
if isinstance(output, dict):
|
|
500
|
+
shape = output["shape"]
|
|
501
|
+
else:
|
|
502
|
+
shape = output.shape
|
|
503
|
+
if self.num_boxes != 0:
|
|
504
|
+
# MobileNet SSD [1, 10]
|
|
505
|
+
if len(shape) == 2 and i == 1 and shape[1] == self.num_boxes:
|
|
506
|
+
return i
|
|
507
|
+
return None
|