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.
Files changed (73) hide show
  1. deepview/modelpack/utils/argmax.py +16 -0
  2. edgefirst/validator/__init__.py +1 -0
  3. edgefirst/validator/__main__.py +375 -0
  4. edgefirst/validator/datasets/__init__.py +118 -0
  5. edgefirst/validator/datasets/cache.py +296 -0
  6. edgefirst/validator/datasets/core.py +250 -0
  7. edgefirst/validator/datasets/darknet.py +446 -0
  8. edgefirst/validator/datasets/database.py +1067 -0
  9. edgefirst/validator/datasets/instance/__init__.py +4 -0
  10. edgefirst/validator/datasets/instance/core.py +222 -0
  11. edgefirst/validator/datasets/instance/detection.py +145 -0
  12. edgefirst/validator/datasets/instance/multitask.py +80 -0
  13. edgefirst/validator/datasets/instance/segmentation.py +120 -0
  14. edgefirst/validator/datasets/utils/fetch.py +682 -0
  15. edgefirst/validator/datasets/utils/readers.py +425 -0
  16. edgefirst/validator/datasets/utils/transformations.py +1695 -0
  17. edgefirst/validator/evaluators/__init__.py +17 -0
  18. edgefirst/validator/evaluators/callbacks/__init__.py +3 -0
  19. edgefirst/validator/evaluators/callbacks/core.py +192 -0
  20. edgefirst/validator/evaluators/callbacks/plots.py +900 -0
  21. edgefirst/validator/evaluators/callbacks/studio.py +234 -0
  22. edgefirst/validator/evaluators/core.py +257 -0
  23. edgefirst/validator/evaluators/detection.py +749 -0
  24. edgefirst/validator/evaluators/multitask.py +270 -0
  25. edgefirst/validator/evaluators/parameters/__init__.py +53 -0
  26. edgefirst/validator/evaluators/parameters/core.py +554 -0
  27. edgefirst/validator/evaluators/parameters/dataset.py +239 -0
  28. edgefirst/validator/evaluators/parameters/model.py +338 -0
  29. edgefirst/validator/evaluators/parameters/validation.py +528 -0
  30. edgefirst/validator/evaluators/segmentation.py +729 -0
  31. edgefirst/validator/evaluators/utils/__init__.py +3 -0
  32. edgefirst/validator/evaluators/utils/classify.py +292 -0
  33. edgefirst/validator/evaluators/utils/match.py +262 -0
  34. edgefirst/validator/evaluators/utils/timer.py +132 -0
  35. edgefirst/validator/metrics/__init__.py +9 -0
  36. edgefirst/validator/metrics/data/__init__.py +7 -0
  37. edgefirst/validator/metrics/data/label.py +668 -0
  38. edgefirst/validator/metrics/data/metrics.py +759 -0
  39. edgefirst/validator/metrics/data/plots.py +476 -0
  40. edgefirst/validator/metrics/data/stats.py +507 -0
  41. edgefirst/validator/metrics/detection.py +595 -0
  42. edgefirst/validator/metrics/segmentation.py +173 -0
  43. edgefirst/validator/metrics/utils/math.py +717 -0
  44. edgefirst/validator/publishers/__init__.py +3 -0
  45. edgefirst/validator/publishers/console.py +147 -0
  46. edgefirst/validator/publishers/studio.py +128 -0
  47. edgefirst/validator/publishers/tensorboard.py +119 -0
  48. edgefirst/validator/publishers/utils/logger.py +111 -0
  49. edgefirst/validator/publishers/utils/table.py +403 -0
  50. edgefirst/validator/runners/__init__.py +8 -0
  51. edgefirst/validator/runners/core.py +727 -0
  52. edgefirst/validator/runners/deepviewrt.py +177 -0
  53. edgefirst/validator/runners/hailo.py +263 -0
  54. edgefirst/validator/runners/keras.py +150 -0
  55. edgefirst/validator/runners/kinara.py +265 -0
  56. edgefirst/validator/runners/offline.py +228 -0
  57. edgefirst/validator/runners/onnx.py +241 -0
  58. edgefirst/validator/runners/processing/decode.py +320 -0
  59. edgefirst/validator/runners/processing/dvapi.py +4192 -0
  60. edgefirst/validator/runners/processing/nms.py +637 -0
  61. edgefirst/validator/runners/processing/outputs.py +507 -0
  62. edgefirst/validator/runners/tensorrt.py +321 -0
  63. edgefirst/validator/runners/tflite.py +221 -0
  64. edgefirst/validator/validate.py +843 -0
  65. edgefirst/validator/visualize/__init__.py +3 -0
  66. edgefirst/validator/visualize/detection.py +623 -0
  67. edgefirst/validator/visualize/segmentation.py +281 -0
  68. edgefirst/validator/visualize/utils/plots.py +635 -0
  69. edgefirst_validator-4.2.1.dist-info/METADATA +111 -0
  70. edgefirst_validator-4.2.1.dist-info/RECORD +73 -0
  71. edgefirst_validator-4.2.1.dist-info/WHEEL +5 -0
  72. edgefirst_validator-4.2.1.dist-info/entry_points.txt +2 -0
  73. edgefirst_validator-4.2.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,446 @@
1
+ """
2
+ Implementations for reading DarkNet datasets.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ from typing import TYPE_CHECKING, List, Tuple
9
+
10
+ import numpy as np
11
+
12
+ from edgefirst.validator.datasets import Dataset
13
+ from edgefirst.validator.datasets import (SegmentationInstance,
14
+ DetectionInstance,
15
+ MultitaskInstance)
16
+ from edgefirst.validator.publishers.utils.logger import logger
17
+ from edgefirst.validator.datasets.utils.fetch import (get_shape,
18
+ get_image_files,
19
+ classify_dataset,
20
+ get_annotation_files,
21
+ validate_dataset_source)
22
+ from edgefirst.validator.datasets.utils.readers import (read_detection_text_file,
23
+ read_segmentation_text_file)
24
+ from edgefirst.validator.datasets.utils.transformations import (preprocess_hal,
25
+ preprocess_native,
26
+ format_segments,
27
+ scale)
28
+
29
+ if TYPE_CHECKING:
30
+ from edgefirst.validator.evaluators import DatasetParameters, TimerContext
31
+ from edgefirst.validator.datasets import Instance
32
+
33
+
34
+ class DarkNetDataset(Dataset):
35
+ """
36
+ Reads Darknet format datasets.
37
+ Dataset format should be the same as coco128 at
38
+ `https://www.kaggle.com/datasets/ultralytics/coco128`.
39
+ Optionally, the images and text annotations can be in the same directory.
40
+
41
+ Parameters
42
+ ----------
43
+ source: str
44
+ The path to the source dataset.
45
+ parameters: DatasetParameters
46
+ This contains dataset parameters set from the command line.
47
+ timer: TimerContext
48
+ A timer object for handling validation timings for the model.
49
+ info_dataset: dict
50
+ Contains information such as:
51
+
52
+ .. code-block:: python
53
+
54
+ {
55
+ "classes": [list of unique labels],
56
+ "validation":
57
+ {
58
+ "images: 'path to the images',
59
+ "annotations": 'path to the annotations'
60
+ }
61
+ }
62
+
63
+ *Note: the classes are optional and the path to the images
64
+ and annotations can be the same.*
65
+
66
+ Raises
67
+ ------
68
+ ValueError
69
+ Raised if the provided path to the images or
70
+ annotations is not a string.
71
+ EmptyDatasetException
72
+ Raised if the provided path to the images or
73
+ text files does not contain any image files or
74
+ text files respectively.
75
+ """
76
+
77
+ def __init__(
78
+ self,
79
+ source: str,
80
+ parameters: DatasetParameters,
81
+ timer: TimerContext,
82
+ info_dataset: dict = None,
83
+ ):
84
+ super(DarkNetDataset, self).__init__(
85
+ source=source,
86
+ parameters=parameters,
87
+ timer=timer,
88
+ info_dataset=info_dataset
89
+ )
90
+
91
+ if self.info_dataset is None:
92
+ self.info_dataset = classify_dataset(source)
93
+
94
+ try:
95
+ images_path = self.info_dataset.get(
96
+ "dataset").get('validation').get('images')
97
+ annotations_path = self.info_dataset.get(
98
+ "dataset").get('validation').get('annotations')
99
+ except AttributeError:
100
+ images_path = self.info_dataset.get('validation').get('images')
101
+ annotations_path = self.info_dataset.get(
102
+ 'validation').get('annotations')
103
+
104
+ self.image_source = validate_dataset_source(images_path)
105
+ self.annotation_source = validate_dataset_source(annotations_path)
106
+
107
+ labels = self.info_dataset.get('classes', None)
108
+ if labels is not None:
109
+ self.parameters.labels = [str(label) for label in labels]
110
+
111
+ self.images = get_image_files(self.image_source)
112
+ self.annotations = get_annotation_files(self.annotation_source)
113
+ # This is used to map the image name to the annotation file.
114
+ self.annotation_extension = os.path.splitext(self.annotations[0])[1]
115
+
116
+ def collect_samples(self) -> List[tuple]:
117
+ """
118
+ Collect all samples in the dataset.
119
+
120
+ Returns
121
+ -------
122
+ List[tuple]
123
+ One instance contains the
124
+ (path to the image, path to the annotation).
125
+ """
126
+ missing_annotations = 0
127
+ samples = list()
128
+ for image_path in self.images:
129
+ annotation_path = os.path.join(
130
+ self.annotation_source,
131
+ os.path.splitext(os.path.basename(image_path))[0] +
132
+ self.annotation_extension)
133
+
134
+ if os.path.exists(annotation_path):
135
+ samples.append((image_path, annotation_path))
136
+ else:
137
+ samples.append((image_path, None))
138
+ if self.parameters.show_missing_annotations:
139
+ logger(
140
+ "Could not find the annotation " +
141
+ "for this image: {}. ".format(
142
+ os.path.basename(image_path)) +
143
+ "Looking for {}".format(
144
+ os.path.splitext(
145
+ os.path.basename(image_path))[0] +
146
+ self.annotation_extension),
147
+ code="WARNING")
148
+ missing_annotations += 1
149
+
150
+ if not self.parameters.show_missing_annotations and missing_annotations > 0:
151
+ logger(
152
+ "There were {} images without annotations. ".format(
153
+ missing_annotations) + "To see the names of the images, " +
154
+ "enable --show_missing_annotations in the command line.",
155
+ code="WARNING")
156
+
157
+ if len(samples) == 0:
158
+ raise ValueError(
159
+ "There are no validation samples found in this dataset.")
160
+ return samples
161
+
162
+ def image(self,
163
+ image_path: str) -> Tuple[np.ndarray, np.ndarray, list, tuple]:
164
+ """
165
+ Read and preprocess the image.
166
+
167
+ Parameters
168
+ ----------
169
+ image_path: str
170
+ The path to the image in the dataset.
171
+
172
+ Returns
173
+ -------
174
+ image: np.ndarray
175
+ The image input after being preprocessed.
176
+ visual_image: np.ndarray
177
+ The image that is used for visualization post
178
+ letterbox, padding, resize transformations.
179
+ shapes: list
180
+ This is used to scale the bounding boxes of the ground
181
+ truth and the model detections based on the letterbox/padding
182
+ transformation.
183
+
184
+ .. code-block:: python
185
+
186
+ [[input_height, input_width],
187
+ [[scale_y, scale_x], [pad_w, pad_h]]]
188
+ image_shape: tuple
189
+ The original image dimensions.
190
+ """
191
+
192
+ # Read the image.
193
+ image = self.load_image(image_path,
194
+ backend=self.parameters.common.backend)
195
+
196
+ with self.timer.time("input"):
197
+ if self.parameters.common.backend == "hal":
198
+ image, visual_image, shapes, image_shape = preprocess_hal(
199
+ image=image,
200
+ shape=self.parameters.common.shape,
201
+ input_type=self.parameters.common.dtype,
202
+ dst=self.parameters.common.input_dst,
203
+ transpose=self.parameters.common.transpose,
204
+ input_tensor=self.parameters.common.input_tensor,
205
+ preprocessing=self.parameters.common.preprocessing,
206
+ normalization=self.parameters.common.norm,
207
+ quantization=self.parameters.common.input_quantization,
208
+ visualize=self.parameters.visualize
209
+ )
210
+ else:
211
+ image, visual_image, shapes, image_shape = preprocess_native(
212
+ image=image,
213
+ shape=self.parameters.common.shape,
214
+ input_type=self.parameters.common.dtype,
215
+ transpose=self.parameters.common.transpose,
216
+ input_tensor=self.parameters.common.input_tensor,
217
+ preprocessing=self.parameters.common.preprocessing,
218
+ normalization=self.parameters.common.norm,
219
+ quantization=self.parameters.common.input_quantization,
220
+ backend=self.parameters.common.backend
221
+ )
222
+ return image, visual_image, shapes, image_shape
223
+
224
+ def build_detection_instance(
225
+ self,
226
+ sample: Tuple[str, str]
227
+ ) -> DetectionInstance:
228
+ """
229
+ Builds a 2D detection instance container.
230
+
231
+ Parameters
232
+ ----------
233
+ sample: Tuple[str, str]
234
+ This contains the (image path, annotation path).
235
+
236
+ Returns
237
+ -------
238
+ DetectionInstance
239
+ The ground truth instance objects contains the 2D bounding boxes
240
+ and the labels representing the ground truth of the image.
241
+ """
242
+ image_path, annotation_path = sample
243
+
244
+ image, visual_image, shapes, image_shape = self.image(image_path)
245
+ height, width = get_shape(image.shape)
246
+
247
+ annotations = read_detection_text_file(
248
+ annotation_path=annotation_path,
249
+ label_offset=self.parameters.label_offset,
250
+ shape=(height, width),
251
+ normalizer=self.normalizer,
252
+ transformer=self.transformer
253
+ )
254
+ boxes = annotations["boxes"]
255
+ labels = annotations["labels"]
256
+
257
+ # Transform the ground truth boxes based on the preprocessed image.
258
+ if len(boxes):
259
+ # Scale ground truth coordinates to center around objects
260
+ # in an image with letterbox transformation.
261
+ if self.parameters.common.preprocessing == "letterbox":
262
+ boxes = scale(
263
+ boxes=boxes,
264
+ w=shapes[1][0][1] * image_shape[1],
265
+ h=shapes[1][0][0] * image_shape[0],
266
+ padw=shapes[1][1][0],
267
+ padh=shapes[1][1][1],
268
+ )
269
+ # Scale ground truth coordinates to center around objects
270
+ # in an image with padding transformation.
271
+ elif self.parameters.common.preprocessing == "pad":
272
+ boxes = scale(
273
+ boxes=boxes,
274
+ w=shapes[1][0][1] * width,
275
+ h=shapes[1][0][0] * height,
276
+ )
277
+ # Scale ground truth coordinates to center around objects
278
+ # in an image with resize transformation.
279
+ else:
280
+ # Denormalize boxes
281
+ boxes *= np.array([width, height, width, height])
282
+
283
+ instance = DetectionInstance(image_path)
284
+ instance.image = image
285
+ instance.visual_image = visual_image
286
+ instance.height = height
287
+ instance.width = width
288
+ instance.boxes = boxes.astype(np.float32)
289
+ instance.labels = labels.astype(np.int32)
290
+ instance.shapes = shapes
291
+ instance.image_shape = image_shape
292
+ return instance
293
+
294
+ def build_segmentation_instance(
295
+ self, sample: Tuple[str, str]) -> SegmentationInstance:
296
+ """
297
+ Builds a segmentation instance container.
298
+
299
+ Parameters
300
+ ----------
301
+ sample: Tuple[str, str]
302
+ This contains the (image path, annotation path).
303
+
304
+ Returns
305
+ -------
306
+ SegmentationInstance
307
+ The ground truth instance objects contains the polygon, mask,
308
+ and the labels representing the ground truth of the image.
309
+ """
310
+ image_path, annotation_path = sample
311
+
312
+ image, visual_image, shapes, image_shape = self.image(image_path)
313
+ height, width = get_shape(image.shape)
314
+
315
+ annotations = read_segmentation_text_file(
316
+ annotation_path=annotation_path,
317
+ label_offset=self.parameters.label_offset,
318
+ shape=image_shape,
319
+ normalizer=self.normalizer,
320
+ transformer=self.transformer
321
+ )
322
+ segments = annotations["segments"]
323
+ labels = annotations["labels"]
324
+
325
+ imgsz = shapes[0]
326
+ ratio_pad = shapes[1]
327
+
328
+ # Scale ground truth mask to center around objects
329
+ # in an image with padding transformation.
330
+ if self.parameters.common.preprocessing == "pad":
331
+ ratio_pad[1] = [0.0, 0.0]
332
+
333
+ masks, _ = format_segments(
334
+ segments=segments,
335
+ shape=imgsz,
336
+ ratio_pad=ratio_pad,
337
+ colors=labels,
338
+ semantic=self.parameters.common.semantic,
339
+ backend=self.parameters.common.backend
340
+ )
341
+
342
+ instance = SegmentationInstance(image_path)
343
+ instance.image = image
344
+ instance.visual_image = visual_image
345
+ instance.height = height
346
+ instance.width = width
347
+ instance.mask = masks
348
+ instance.shapes = shapes
349
+ instance.image_shape = image_shape
350
+ return instance
351
+
352
+ def build_multitask_instance(
353
+ self, sample: Tuple[str, str]) -> MultitaskInstance:
354
+ """
355
+ Builds a multitask instance container.
356
+
357
+ Parameters
358
+ ----------
359
+ sample: Tuple[str, str]
360
+ This contains the (image path, annotation path).
361
+
362
+ Returns
363
+ -------
364
+ MultitaskInstance
365
+ The ground truth instance objects contains the bounding boxes
366
+ and the segmentation mask representing the ground truth of
367
+ the image
368
+ """
369
+
370
+ image_path, annotation_path = sample
371
+
372
+ image, visual_image, shapes, image_shape = self.image(image_path)
373
+ height, width = get_shape(image.shape)
374
+
375
+ annotations = read_segmentation_text_file(
376
+ annotation_path=annotation_path,
377
+ label_offset=self.parameters.label_offset,
378
+ shape=image_shape,
379
+ normalizer=self.normalizer,
380
+ transformer=self.transformer
381
+ )
382
+ segments = annotations["segments"]
383
+ labels = annotations["labels"]
384
+ boxes = annotations["boxes"]
385
+
386
+ imgsz = shapes[0]
387
+ ratio_pad = shapes[1]
388
+
389
+ # Format boxes
390
+ if len(boxes):
391
+ # Transform the ground truth boxes based on the preprocessed image.
392
+ # Scale ground truth coordinates to center around objects
393
+ # in an image with letterbox transformation.
394
+ if self.parameters.common.preprocessing == "letterbox":
395
+ boxes = scale(
396
+ boxes=boxes,
397
+ w=shapes[1][0][1] * image_shape[1],
398
+ h=shapes[1][0][0] * image_shape[0],
399
+ padw=shapes[1][1][0],
400
+ padh=shapes[1][1][1],
401
+ )
402
+ # Scale ground truth coordinates to center around objects
403
+ # in an image with padding transformation.
404
+ elif self.parameters.common.preprocessing == "pad":
405
+ boxes = scale(
406
+ boxes=boxes,
407
+ w=shapes[1][0][1] * width,
408
+ h=shapes[1][0][0] * height,
409
+ )
410
+ # Scale ground truth coordinates to center around objects
411
+ # in an image with resize transformation.
412
+ else:
413
+ # Denormalize boxes
414
+ boxes *= np.array([width, height, width, height])
415
+
416
+ # Scale ground truth mask to center around objects
417
+ # in an image with padding transformation.
418
+ if self.parameters.common.preprocessing == "pad":
419
+ ratio_pad[1] = [0.0, 0.0]
420
+
421
+ masks, sorted_idx = format_segments(
422
+ segments=segments,
423
+ shape=imgsz,
424
+ ratio_pad=ratio_pad,
425
+ colors=labels,
426
+ semantic=self.parameters.common.semantic,
427
+ backend=self.parameters.common.backend
428
+ )
429
+
430
+ if sorted_idx is not None and len(sorted_idx) > 0:
431
+ if len(labels):
432
+ labels = labels[sorted_idx]
433
+ if len(boxes):
434
+ boxes = boxes[sorted_idx]
435
+
436
+ instance = MultitaskInstance(image_path)
437
+ instance.image = image
438
+ instance.visual_image = visual_image
439
+ instance.height = height
440
+ instance.width = width
441
+ instance.boxes = boxes.astype(np.float32)
442
+ instance.labels = labels.astype(np.int32)
443
+ instance.mask = masks
444
+ instance.shapes = shapes
445
+ instance.image_shape = image_shape
446
+ return instance