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,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Implementations for downloading and caching the dataset.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import glob
|
|
10
|
+
from typing import TYPE_CHECKING, List, Tuple
|
|
11
|
+
|
|
12
|
+
import tqdm
|
|
13
|
+
import lmdb
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from edgefirst_client import Client, FileType, AnnotationType
|
|
17
|
+
|
|
18
|
+
from edgefirst.validator.datasets import LMDBDatabase
|
|
19
|
+
from edgefirst.validator.publishers.utils.logger import logger
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from edgefirst.validator.evaluators import DatasetParameters
|
|
23
|
+
from edgefirst.validator.datasets import Dataset
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class StudioCache:
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
parameters: DatasetParameters,
|
|
30
|
+
stages: List[Tuple[str, str]],
|
|
31
|
+
client: Client = None,
|
|
32
|
+
session_id: str = None,
|
|
33
|
+
val_group: str = "val"
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Communicate with EdgeFirst Studio for
|
|
37
|
+
fetching and caching the dataset and post the progress.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
parameters: DatasetParameters
|
|
42
|
+
This contains dataset parameters set from the command line.
|
|
43
|
+
stages: List[Tuple[str, str]]
|
|
44
|
+
This contains the stages that tracks each progress in Studio.
|
|
45
|
+
A stage contains ("stage identifier", "stage description").
|
|
46
|
+
client: Client
|
|
47
|
+
EdgeFirst Client object.
|
|
48
|
+
session_id: str
|
|
49
|
+
This is the validation session ID in EdgeFirst Studio for
|
|
50
|
+
posting validation metrics.
|
|
51
|
+
val_group: str
|
|
52
|
+
The dataset validation group set in EdgeFirst Studio.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
self.parameters = parameters
|
|
56
|
+
self.stages = stages
|
|
57
|
+
self.client = client
|
|
58
|
+
self.val_session = None
|
|
59
|
+
self.val_group = val_group
|
|
60
|
+
|
|
61
|
+
if self.client is not None:
|
|
62
|
+
self.val_session = self.client.validation_session(session_id)
|
|
63
|
+
|
|
64
|
+
def complete_stage(self, stage: str, message: str):
|
|
65
|
+
"""
|
|
66
|
+
Completes the stage on studio at the end of the task.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
stage: str
|
|
71
|
+
The stage identifier.
|
|
72
|
+
message: str
|
|
73
|
+
The message to project in the Studio GUI.
|
|
74
|
+
"""
|
|
75
|
+
self.client.update_stage(
|
|
76
|
+
self.val_session.task.id,
|
|
77
|
+
stage=stage,
|
|
78
|
+
status="Running",
|
|
79
|
+
message=message,
|
|
80
|
+
percentage=100
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def download(self, dataset: str):
|
|
84
|
+
"""
|
|
85
|
+
Download the dataset from EdgeFirst Studio into the device.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
dataset: str
|
|
90
|
+
The path to directory to save the dataset.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
pl.DataFrame
|
|
95
|
+
This is the polars dataframe of the annotations.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
if self.client is None:
|
|
99
|
+
raise ValueError(
|
|
100
|
+
"EdgeFirst Client needs to be defined to download the dataset.")
|
|
101
|
+
|
|
102
|
+
annotation_types = [AnnotationType.Box2d, AnnotationType.Mask]
|
|
103
|
+
os.makedirs(dataset, exist_ok=True)
|
|
104
|
+
|
|
105
|
+
dataset_id = self.val_session.dataset_id
|
|
106
|
+
annotation_id = self.val_session.annotation_set_id
|
|
107
|
+
|
|
108
|
+
# Download Images
|
|
109
|
+
with tqdm.tqdm(
|
|
110
|
+
total=0,
|
|
111
|
+
desc=f"Downloading Images from Dataset ID: ds-{dataset_id.value:x}"
|
|
112
|
+
) as bar:
|
|
113
|
+
def progress(current, total):
|
|
114
|
+
if total != bar.total:
|
|
115
|
+
bar.reset(total)
|
|
116
|
+
bar.update(current - bar.n)
|
|
117
|
+
self.client.update_stage(
|
|
118
|
+
self.val_session.task.id,
|
|
119
|
+
stage=self.stages[0][0],
|
|
120
|
+
status="Running",
|
|
121
|
+
message=self.stages[0][1],
|
|
122
|
+
percentage=int(100 * current / total)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
self.client.download_dataset(
|
|
126
|
+
dataset_id=dataset_id,
|
|
127
|
+
groups=[self.val_group],
|
|
128
|
+
types=[FileType.Image],
|
|
129
|
+
output=os.path.join(dataset, "images"),
|
|
130
|
+
progress=progress,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
total_images = len(glob.glob(os.path.join(dataset, "images", "*"),
|
|
134
|
+
recursive=True))
|
|
135
|
+
logger(f"Downloaded a total of {total_images} images.", code="INFO")
|
|
136
|
+
|
|
137
|
+
# Download Annotations
|
|
138
|
+
with tqdm.tqdm(
|
|
139
|
+
total=0,
|
|
140
|
+
desc=f"Downloading Annotations from Annotation ID: as-{annotation_id.value:x}"
|
|
141
|
+
) as bar:
|
|
142
|
+
def progress(current, total):
|
|
143
|
+
if total != bar.total:
|
|
144
|
+
bar.reset(total)
|
|
145
|
+
bar.update(current - bar.n)
|
|
146
|
+
self.client.update_stage(
|
|
147
|
+
self.val_session.task.id,
|
|
148
|
+
stage=self.stages[1][0],
|
|
149
|
+
status="Running",
|
|
150
|
+
message=self.stages[1][1],
|
|
151
|
+
percentage=int(100 * current / total)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
dataframe = self.client.annotations_dataframe(
|
|
155
|
+
annotation_set_id=annotation_id,
|
|
156
|
+
groups=[self.val_group],
|
|
157
|
+
annotation_types=annotation_types,
|
|
158
|
+
progress=progress
|
|
159
|
+
)
|
|
160
|
+
dataframe.write_ipc(os.path.join(dataset, "dataset.arrow"))
|
|
161
|
+
|
|
162
|
+
logger(f"Downloaded a total of {dataframe.shape[0]} annotations.",
|
|
163
|
+
code="INFO")
|
|
164
|
+
|
|
165
|
+
return dataframe
|
|
166
|
+
|
|
167
|
+
def cache(self, dataset: Dataset,
|
|
168
|
+
cache: str = "cache/val.db") -> LMDBDatabase:
|
|
169
|
+
"""
|
|
170
|
+
Cache the dataset provided into an LMDB file.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
dataset: Dataset
|
|
175
|
+
This can either be a DarkNet or EdgeFirst Dataset object.
|
|
176
|
+
cache: str
|
|
177
|
+
The path to the cache file to save the cache.
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
LMDBDatabase
|
|
182
|
+
The instantiated cached dataset.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
# Remove existing cache file.
|
|
186
|
+
if os.path.exists(cache):
|
|
187
|
+
os.remove(cache)
|
|
188
|
+
os.makedirs(os.path.dirname(cache), exist_ok=True)
|
|
189
|
+
|
|
190
|
+
dbenv = lmdb.open(
|
|
191
|
+
cache,
|
|
192
|
+
map_size=1024 ** 4,
|
|
193
|
+
max_dbs=10,
|
|
194
|
+
subdir=False,
|
|
195
|
+
lock=False
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
classes_db = dbenv.open_db(b'classes') # Unique labels in the dataset.
|
|
199
|
+
# Name of each image in the dataset.
|
|
200
|
+
names_db = dbenv.open_db(b'names')
|
|
201
|
+
# All preprocessed images in the dataset.
|
|
202
|
+
images_db = dbenv.open_db(b'images')
|
|
203
|
+
# Images used for visualization.
|
|
204
|
+
visual_images_db = dbenv.open_db(b'visual')
|
|
205
|
+
boxes_db = dbenv.open_db(b'box2d') # 2D box annotations.
|
|
206
|
+
# Integer labels for each boxes or masks.
|
|
207
|
+
labels_db = dbenv.open_db(b'labels')
|
|
208
|
+
masks_db = dbenv.open_db(b'masks') # 2D masks annotations.
|
|
209
|
+
|
|
210
|
+
# Store the unique labels in the dataset.
|
|
211
|
+
with dbenv.begin(write=True) as txn:
|
|
212
|
+
txn.put(
|
|
213
|
+
b'classes',
|
|
214
|
+
json.dumps(self.parameters.labels).encode(),
|
|
215
|
+
db=classes_db
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
samples = tqdm.tqdm(dataset.collect_samples(), colour="green")
|
|
219
|
+
samples.set_description("Caching dataset")
|
|
220
|
+
|
|
221
|
+
for i, sample in enumerate(samples):
|
|
222
|
+
gt_instance = dataset.read_sample(sample)
|
|
223
|
+
|
|
224
|
+
with dbenv.begin(write=True) as txn:
|
|
225
|
+
image_path = gt_instance.image_path
|
|
226
|
+
image = gt_instance.image
|
|
227
|
+
visual_image = gt_instance.visual_image
|
|
228
|
+
shapes = gt_instance.shapes
|
|
229
|
+
image_shape = gt_instance.image_shape
|
|
230
|
+
labels = gt_instance.labels
|
|
231
|
+
|
|
232
|
+
if self.parameters.common.with_boxes:
|
|
233
|
+
boxes = gt_instance.boxes
|
|
234
|
+
else:
|
|
235
|
+
boxes = np.array([], dtype=np.float32)
|
|
236
|
+
|
|
237
|
+
if self.parameters.common.with_masks:
|
|
238
|
+
masks = gt_instance.mask
|
|
239
|
+
else:
|
|
240
|
+
masks = np.zeros(shapes[0], dtype=np.uint8)
|
|
241
|
+
|
|
242
|
+
name = os.path.basename(image_path)
|
|
243
|
+
txn.put(name.encode(), None, db=names_db)
|
|
244
|
+
txn.put(name.encode(), image.tobytes(), db=images_db)
|
|
245
|
+
# Place the current shape of the image.
|
|
246
|
+
txn.put(f'{name}/shape'.encode(),
|
|
247
|
+
np.array(image.shape, dtype=np.int32).tobytes(),
|
|
248
|
+
db=images_db)
|
|
249
|
+
|
|
250
|
+
if visual_image is not None:
|
|
251
|
+
txn.put(
|
|
252
|
+
name.encode(),
|
|
253
|
+
visual_image.tobytes(),
|
|
254
|
+
db=visual_images_db)
|
|
255
|
+
# Place the current shape of the visualization image.
|
|
256
|
+
txn.put(f'{name}/shape'.encode(),
|
|
257
|
+
np.array(visual_image.shape,
|
|
258
|
+
dtype=np.int32).tobytes(),
|
|
259
|
+
db=visual_images_db)
|
|
260
|
+
|
|
261
|
+
# Place the label transformation shapes after letterboxing.
|
|
262
|
+
txn.put(f'{name}/shapes'.encode(),
|
|
263
|
+
json.dumps(shapes).encode(),
|
|
264
|
+
db=images_db)
|
|
265
|
+
# Place the original shape of the image.
|
|
266
|
+
txn.put(f'{name}/im_shape'.encode(),
|
|
267
|
+
np.array(image_shape, dtype=np.int32).tobytes(),
|
|
268
|
+
db=images_db)
|
|
269
|
+
txn.put(name.encode(), boxes.tobytes(), db=boxes_db)
|
|
270
|
+
txn.put(name.encode(), labels.tobytes(), db=labels_db)
|
|
271
|
+
txn.put(name.encode(), masks.tobytes(), db=masks_db)
|
|
272
|
+
txn.put(f'{name}/mask_shape'.encode(),
|
|
273
|
+
np.array(masks.shape, dtype=np.int32).tobytes(),
|
|
274
|
+
db=masks_db)
|
|
275
|
+
|
|
276
|
+
if self.client is not None:
|
|
277
|
+
self.client.update_stage(
|
|
278
|
+
self.val_session.task.id,
|
|
279
|
+
stage=self.stages[2][0],
|
|
280
|
+
status="Running",
|
|
281
|
+
message=self.stages[2][1],
|
|
282
|
+
percentage=int(100 * i / len(dataset))
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
dbenv.close()
|
|
286
|
+
if self.client is not None:
|
|
287
|
+
self.complete_stage(
|
|
288
|
+
stage=self.stages[2][0],
|
|
289
|
+
message=self.stages[2][1]
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return LMDBDatabase(
|
|
293
|
+
source=cache,
|
|
294
|
+
parameters=self.parameters,
|
|
295
|
+
timer=dataset.timer
|
|
296
|
+
)
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common parent dataset implementations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import TYPE_CHECKING, Union, Tuple
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import tqdm
|
|
10
|
+
|
|
11
|
+
from edgefirst.validator.datasets.utils.readers import read_image
|
|
12
|
+
from edgefirst.validator.datasets.utils.transformations import (xcycwh2xyxy,
|
|
13
|
+
xywh2xyxy,
|
|
14
|
+
normalize,
|
|
15
|
+
denormalize_polygon)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from edgefirst.validator.evaluators import DatasetParameters, TimerContext
|
|
19
|
+
from edgefirst_python import TensorImage # type: ignore
|
|
20
|
+
|
|
21
|
+
from edgefirst.validator.datasets import (
|
|
22
|
+
SegmentationInstance, DetectionInstance, MultitaskInstance, Instance)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Dataset:
|
|
26
|
+
"""
|
|
27
|
+
Abstract dataset class for providing template methods in the dataset.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
source: str
|
|
32
|
+
The path to the source dataset.
|
|
33
|
+
parameters: DatasetParameters
|
|
34
|
+
This contains dataset parameters set from the command line.
|
|
35
|
+
timer: TimerContext
|
|
36
|
+
A timer object for handling validation timings for the model.
|
|
37
|
+
info_dataset: dict
|
|
38
|
+
Contains information such as:
|
|
39
|
+
|
|
40
|
+
.. code-block:: python
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
"classes": [list of unique labels],
|
|
44
|
+
"validation":
|
|
45
|
+
{
|
|
46
|
+
"images: 'path to the images',
|
|
47
|
+
"annotations": 'path to the annotations'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
*Note: the classes are optional and the path to the images
|
|
52
|
+
and annotations can be the same.*
|
|
53
|
+
|
|
54
|
+
Raises
|
|
55
|
+
------
|
|
56
|
+
ValueError
|
|
57
|
+
Raised if the provided parameters in certain methods
|
|
58
|
+
does not conform to the specified data type.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
source: str,
|
|
64
|
+
parameters: DatasetParameters,
|
|
65
|
+
timer: TimerContext,
|
|
66
|
+
info_dataset: dict = None
|
|
67
|
+
):
|
|
68
|
+
self.source = source
|
|
69
|
+
self.parameters = parameters
|
|
70
|
+
self.timer = timer
|
|
71
|
+
self.info_dataset = info_dataset
|
|
72
|
+
self.samples = []
|
|
73
|
+
|
|
74
|
+
self.transformer = None
|
|
75
|
+
if self.parameters.box_format == 'xcycwh':
|
|
76
|
+
self.transformer = xcycwh2xyxy
|
|
77
|
+
elif self.parameters.box_format == 'xywh':
|
|
78
|
+
self.transformer = xywh2xyxy
|
|
79
|
+
else:
|
|
80
|
+
self.transformer = None
|
|
81
|
+
|
|
82
|
+
self.normalizer = None
|
|
83
|
+
self.denormalizer = None
|
|
84
|
+
if self.parameters.normalized:
|
|
85
|
+
if self.parameters.common.with_masks:
|
|
86
|
+
self.denormalizer = denormalize_polygon
|
|
87
|
+
else:
|
|
88
|
+
if self.parameters.common.with_boxes:
|
|
89
|
+
self.normalizer = normalize
|
|
90
|
+
|
|
91
|
+
def __len__(self) -> int:
|
|
92
|
+
"""
|
|
93
|
+
Returns the number of samples in the dataset.
|
|
94
|
+
"""
|
|
95
|
+
return len(self.samples)
|
|
96
|
+
|
|
97
|
+
def __iter__(self):
|
|
98
|
+
"""
|
|
99
|
+
Reads all the samples in the dataset.
|
|
100
|
+
|
|
101
|
+
Yields
|
|
102
|
+
-------
|
|
103
|
+
Instance
|
|
104
|
+
Yields one sample of the ground truth
|
|
105
|
+
instance which contains information on the image
|
|
106
|
+
as a NumPy array, boxes, labels, and image path.
|
|
107
|
+
"""
|
|
108
|
+
if self.parameters.silent:
|
|
109
|
+
samples = self.collect_samples()
|
|
110
|
+
for sample in samples:
|
|
111
|
+
yield self.read_sample(sample)
|
|
112
|
+
else:
|
|
113
|
+
samples = tqdm.tqdm(self.collect_samples(), colour="green")
|
|
114
|
+
samples.set_description("Validation Progress")
|
|
115
|
+
for sample in samples:
|
|
116
|
+
yield self.read_sample(sample)
|
|
117
|
+
|
|
118
|
+
def verify_dataset(self):
|
|
119
|
+
"""Abstract Method"""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
def read_sample(self,
|
|
123
|
+
sample: Union[list, Tuple[str, str], str]) -> Instance:
|
|
124
|
+
"""
|
|
125
|
+
Reads one sample from the dataset.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
-----------
|
|
129
|
+
sample: Union[list, Tuple[str, str], str]
|
|
130
|
+
For EdgeFirstDatabase, this is a list. For Darknet datasets,
|
|
131
|
+
this is a Tuple[str, str] containing the path to the image
|
|
132
|
+
and annotations. For dataset cache, this is a string
|
|
133
|
+
as the image name.
|
|
134
|
+
|
|
135
|
+
A single dataset sample contains the indices
|
|
136
|
+
in the dataframe pointing to all the annotations
|
|
137
|
+
in the dataset for this sample.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
Instance
|
|
142
|
+
The ground truth instance objects contains the annotations
|
|
143
|
+
representing the ground truth of the image.
|
|
144
|
+
"""
|
|
145
|
+
if self.parameters.common.with_boxes and self.parameters.common.with_masks:
|
|
146
|
+
return self.build_multitask_instance(sample)
|
|
147
|
+
elif self.parameters.common.with_boxes:
|
|
148
|
+
return self.build_detection_instance(sample)
|
|
149
|
+
elif self.parameters.common.with_masks:
|
|
150
|
+
return self.build_segmentation_instance(sample)
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
"Could not determine model task as detection or segmentation.")
|
|
154
|
+
|
|
155
|
+
def load_image(
|
|
156
|
+
self,
|
|
157
|
+
image_path: str,
|
|
158
|
+
backend: str = "hal"
|
|
159
|
+
) -> Union[TensorImage, np.ndarray]:
|
|
160
|
+
"""
|
|
161
|
+
Load the image into memory using various libraries: "hal", "opencv",
|
|
162
|
+
or "pillow".
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
image_path: str
|
|
167
|
+
The path to the image.
|
|
168
|
+
backend: str
|
|
169
|
+
Specify the backend library for resizing the image
|
|
170
|
+
from the options "hal", "opencv", "pillow".
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
Union[edgefirst_python.TensorImage, np.ndarray]
|
|
175
|
+
TensorImage is returned when using "hal". Otherwise, a
|
|
176
|
+
NumPy array is returned.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
if backend == "hal":
|
|
180
|
+
try:
|
|
181
|
+
import edgefirst_python # type: ignore
|
|
182
|
+
except ImportError:
|
|
183
|
+
raise ImportError(
|
|
184
|
+
"EdgeFirst HAL is needed to read the image.")
|
|
185
|
+
# Read the image.
|
|
186
|
+
return edgefirst_python.TensorImage.load(
|
|
187
|
+
image_path, fourcc=edgefirst_python.FourCC.RGBA)
|
|
188
|
+
elif backend == "opencv":
|
|
189
|
+
try:
|
|
190
|
+
import cv2 # type: ignore
|
|
191
|
+
except ImportError:
|
|
192
|
+
raise ImportError("OpenCV is needed to read the image.")
|
|
193
|
+
|
|
194
|
+
return cv2.imread(image_path)
|
|
195
|
+
else:
|
|
196
|
+
return read_image(image_path, rotate=True)
|
|
197
|
+
|
|
198
|
+
def image(self, sample: Union[tuple, list]):
|
|
199
|
+
"""Abstract Method"""
|
|
200
|
+
raise NotImplementedError("Abstract Method")
|
|
201
|
+
|
|
202
|
+
def labels(self, sample: Union[tuple, list]) -> np.ndarray:
|
|
203
|
+
"""
|
|
204
|
+
Fetch the labels of the specified sample.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
sample: Union[tuple, list]
|
|
209
|
+
A tuple containing the (image path, annotation path) or
|
|
210
|
+
a list of indices in the polars dataframe for the current sample.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
np.ndarray
|
|
215
|
+
The labels in the sample containing np.int32 elements.
|
|
216
|
+
"""
|
|
217
|
+
return np.array([])
|
|
218
|
+
|
|
219
|
+
def boxes(self, index: int) -> np.ndarray:
|
|
220
|
+
"""Abstract Method"""
|
|
221
|
+
raise NotImplementedError("Abstract Method")
|
|
222
|
+
|
|
223
|
+
def mask(self, index: int) -> np.ndarray:
|
|
224
|
+
"""Abstract Method"""
|
|
225
|
+
raise NotImplementedError("Abstract Method")
|
|
226
|
+
|
|
227
|
+
def segments(self, index: int) -> np.ndarray:
|
|
228
|
+
"""Abstract Method"""
|
|
229
|
+
raise NotImplementedError("Absract Method")
|
|
230
|
+
|
|
231
|
+
def name(self, index: int) -> str:
|
|
232
|
+
"""Abstract Method"""
|
|
233
|
+
raise NotImplementedError("Abstract Method")
|
|
234
|
+
|
|
235
|
+
def collect_samples(self):
|
|
236
|
+
"""Abstract Method"""
|
|
237
|
+
raise NotImplementedError("This is an abstract method.")
|
|
238
|
+
|
|
239
|
+
def build_detection_instance(self, sample: list) -> DetectionInstance:
|
|
240
|
+
"""Abstract Method"""
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
def build_segmentation_instance(
|
|
244
|
+
self, sample: list) -> SegmentationInstance:
|
|
245
|
+
"""Abstract Method"""
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
def build_multitask_instance(self, sample: list) -> MultitaskInstance:
|
|
249
|
+
"""Abstract Method"""
|
|
250
|
+
pass
|