edgefirst-validator 4.1.12__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 +336 -0
- edgefirst/validator/datasets/__init__.py +131 -0
- edgefirst/validator/datasets/arrow.py +147 -0
- edgefirst/validator/datasets/cache.py +258 -0
- edgefirst/validator/datasets/core.py +245 -0
- edgefirst/validator/datasets/darknet.py +432 -0
- edgefirst/validator/datasets/database.py +1353 -0
- edgefirst/validator/datasets/instance/__init__.py +8 -0
- edgefirst/validator/datasets/instance/core.py +223 -0
- edgefirst/validator/datasets/instance/detection.py +348 -0
- edgefirst/validator/datasets/instance/fusion.py +22 -0
- edgefirst/validator/datasets/instance/multitask.py +80 -0
- edgefirst/validator/datasets/instance/pose.py +51 -0
- edgefirst/validator/datasets/instance/segmentation.py +120 -0
- edgefirst/validator/datasets/tfrecord.py +243 -0
- edgefirst/validator/datasets/utils/fetch.py +856 -0
- edgefirst/validator/datasets/utils/readers.py +598 -0
- edgefirst/validator/datasets/utils/transformations.py +1878 -0
- edgefirst/validator/evaluators/__init__.py +16 -0
- edgefirst/validator/evaluators/callbacks/__init__.py +3 -0
- edgefirst/validator/evaluators/callbacks/core.py +194 -0
- edgefirst/validator/evaluators/callbacks/plots.py +857 -0
- edgefirst/validator/evaluators/callbacks/studio.py +128 -0
- edgefirst/validator/evaluators/core.py +255 -0
- edgefirst/validator/evaluators/detection.py +795 -0
- edgefirst/validator/evaluators/multitask.py +294 -0
- edgefirst/validator/evaluators/parameters/__init__.py +53 -0
- edgefirst/validator/evaluators/parameters/core.py +443 -0
- edgefirst/validator/evaluators/parameters/dataset.py +176 -0
- edgefirst/validator/evaluators/parameters/model.py +288 -0
- edgefirst/validator/evaluators/parameters/validation.py +529 -0
- edgefirst/validator/evaluators/pose.py +155 -0
- edgefirst/validator/evaluators/segmentation.py +975 -0
- edgefirst/validator/evaluators/utils/__init__.py +2 -0
- edgefirst/validator/evaluators/utils/classify.py +284 -0
- edgefirst/validator/evaluators/utils/match.py +262 -0
- edgefirst/validator/metrics/__init__.py +12 -0
- edgefirst/validator/metrics/data/__init__.py +9 -0
- edgefirst/validator/metrics/data/label.py +727 -0
- edgefirst/validator/metrics/data/metrics.py +753 -0
- edgefirst/validator/metrics/data/plots.py +447 -0
- edgefirst/validator/metrics/data/stats.py +690 -0
- edgefirst/validator/metrics/detection.py +588 -0
- edgefirst/validator/metrics/pose.py +80 -0
- edgefirst/validator/metrics/segmentation.py +171 -0
- edgefirst/validator/metrics/utils/math.py +698 -0
- edgefirst/validator/publishers/__init__.py +3 -0
- edgefirst/validator/publishers/console.py +78 -0
- edgefirst/validator/publishers/studio.py +107 -0
- edgefirst/validator/publishers/tensorboard.py +118 -0
- edgefirst/validator/publishers/utils/logger.py +111 -0
- edgefirst/validator/publishers/utils/table.py +434 -0
- edgefirst/validator/runners/__init__.py +7 -0
- edgefirst/validator/runners/core.py +644 -0
- edgefirst/validator/runners/deepviewrt.py +169 -0
- edgefirst/validator/runners/hailo.py +281 -0
- edgefirst/validator/runners/keras.py +219 -0
- edgefirst/validator/runners/offline.py +228 -0
- edgefirst/validator/runners/onnx.py +332 -0
- edgefirst/validator/runners/processing/decode.py +105 -0
- edgefirst/validator/runners/processing/nms.py +824 -0
- edgefirst/validator/runners/processing/postprocess.py +520 -0
- edgefirst/validator/runners/processing/preprocess.py +134 -0
- edgefirst/validator/runners/tensorrt.py +352 -0
- edgefirst/validator/runners/tflite.py +272 -0
- edgefirst/validator/validate.py +701 -0
- edgefirst/validator/visualize/__init__.py +4 -0
- edgefirst/validator/visualize/detection.py +946 -0
- edgefirst/validator/visualize/pose.py +243 -0
- edgefirst/validator/visualize/segmentation.py +377 -0
- edgefirst/validator/visualize/utils/plots.py +686 -0
- edgefirst_validator-4.1.12.dist-info/METADATA +114 -0
- edgefirst_validator-4.1.12.dist-info/RECORD +78 -0
- edgefirst_validator-4.1.12.dist-info/WHEEL +5 -0
- edgefirst_validator-4.1.12.dist-info/entry_points.txt +2 -0
- edgefirst_validator-4.1.12.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,258 @@
|
|
|
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
|
|
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 EdgeFirstDatabase
|
|
19
|
+
from edgefirst.validator.publishers.utils.logger import logger
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from edgefirst.validator.evaluators import DatasetParameters
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StudioCache:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
parameters: DatasetParameters,
|
|
29
|
+
client: Client,
|
|
30
|
+
session_id: str = None,
|
|
31
|
+
val_group: str = "val"
|
|
32
|
+
):
|
|
33
|
+
"""
|
|
34
|
+
Communicate with EdgeFirst Studio for
|
|
35
|
+
fetching and caching the dataset and post the progress.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
parameters: DatasetParameters
|
|
40
|
+
This contains dataset parameters set from the command line.
|
|
41
|
+
client: Client
|
|
42
|
+
EdgeFirst Client object.
|
|
43
|
+
session_id: str
|
|
44
|
+
This is the validation session ID in EdgeFirst Studio for
|
|
45
|
+
posting validation metrics.
|
|
46
|
+
val_group: str
|
|
47
|
+
The dataset validation group set in EdgeFirst Studio.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
self.parameters = parameters
|
|
51
|
+
self.client = client
|
|
52
|
+
self.edgefirst_dataset = None
|
|
53
|
+
|
|
54
|
+
self.val_session = self.client.validation_session(session_id)
|
|
55
|
+
self.val_task_id = self.val_session.task.id
|
|
56
|
+
train_session_id = self.val_session.training_session_id
|
|
57
|
+
self.train_session = self.client.training_session(train_session_id)
|
|
58
|
+
|
|
59
|
+
self.val_group = val_group
|
|
60
|
+
|
|
61
|
+
def download(self, dataset: str):
|
|
62
|
+
"""
|
|
63
|
+
Download the dataset from EdgeFirst Studio into the device.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
dataset: str
|
|
68
|
+
The path to directory to save the dataset.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
pl.DataFrame
|
|
73
|
+
This is the polars dataframe of the annotations.
|
|
74
|
+
"""
|
|
75
|
+
annotation_types = [AnnotationType.Box2d, AnnotationType.Mask]
|
|
76
|
+
os.makedirs(dataset, exist_ok=True)
|
|
77
|
+
|
|
78
|
+
dataset_id = self.val_session.dataset_id
|
|
79
|
+
annotation_id = self.val_session.annotation_set_id
|
|
80
|
+
|
|
81
|
+
# Download Images
|
|
82
|
+
with tqdm.tqdm(
|
|
83
|
+
total=0,
|
|
84
|
+
desc=f"Downloading Images from Dataset ID: ds-{dataset_id.value:x}"
|
|
85
|
+
) as bar:
|
|
86
|
+
def progress(current, total):
|
|
87
|
+
if total != bar.total:
|
|
88
|
+
bar.reset(total)
|
|
89
|
+
bar.update(current - bar.n)
|
|
90
|
+
self.client.update_stage(
|
|
91
|
+
self.val_session.task.id,
|
|
92
|
+
stage="fetch_img",
|
|
93
|
+
status="Running",
|
|
94
|
+
description="Downloading Images",
|
|
95
|
+
percentage=int(100 * current / total)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
self.client.download_dataset(
|
|
99
|
+
dataset_id=dataset_id,
|
|
100
|
+
groups=[self.val_group],
|
|
101
|
+
types=[FileType.Image],
|
|
102
|
+
output=os.path.join(dataset, "images"),
|
|
103
|
+
progress=progress,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
total_images = len(glob.glob(os.path.join(dataset, "images", "*"),
|
|
107
|
+
recursive=True))
|
|
108
|
+
logger(f"Downloaded a total of {total_images} images.", code="INFO")
|
|
109
|
+
|
|
110
|
+
# Download Annotations
|
|
111
|
+
with tqdm.tqdm(
|
|
112
|
+
total=0,
|
|
113
|
+
desc=f"Downloading Annotations from Annotation ID: as-{annotation_id.value:x}"
|
|
114
|
+
) as bar:
|
|
115
|
+
def progress(current, total):
|
|
116
|
+
if total != bar.total:
|
|
117
|
+
bar.reset(total)
|
|
118
|
+
bar.update(current - bar.n)
|
|
119
|
+
self.client.update_stage(
|
|
120
|
+
self.val_session.task.id,
|
|
121
|
+
stage="fetch_as",
|
|
122
|
+
status="Running",
|
|
123
|
+
description="Downloading Annotations",
|
|
124
|
+
percentage=int(100 * current / total)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
dataframe = self.client.annotations_dataframe(
|
|
128
|
+
annotation_set_id=annotation_id,
|
|
129
|
+
groups=[self.val_group],
|
|
130
|
+
annotation_types=annotation_types,
|
|
131
|
+
progress=progress
|
|
132
|
+
)
|
|
133
|
+
dataframe.write_ipc(os.path.join(dataset, "dataset.arrow"))
|
|
134
|
+
|
|
135
|
+
logger(f"Downloaded a total of {dataframe.shape[0]} annotations.",
|
|
136
|
+
code="INFO")
|
|
137
|
+
|
|
138
|
+
return dataframe
|
|
139
|
+
|
|
140
|
+
def cache(self, dataset: str, cache: str = "cache/val.db"):
|
|
141
|
+
"""
|
|
142
|
+
Cache the dataset provided into an LMDB file.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
dataset: str
|
|
147
|
+
The path to the dataset directory.
|
|
148
|
+
cache: str
|
|
149
|
+
The path to the cache file to save the cache.
|
|
150
|
+
"""
|
|
151
|
+
group_name = os.path.basename(cache)
|
|
152
|
+
logger(f"Caching dataset to {group_name}", code="INFO")
|
|
153
|
+
|
|
154
|
+
os.makedirs(os.path.dirname(cache), exist_ok=True)
|
|
155
|
+
|
|
156
|
+
self.edgefirst_dataset = EdgeFirstDatabase(
|
|
157
|
+
source=dataset,
|
|
158
|
+
parameters=self.parameters,
|
|
159
|
+
)
|
|
160
|
+
ds_iterator = tqdm.tqdm(self.edgefirst_dataset)
|
|
161
|
+
|
|
162
|
+
# Remove existing cache file.
|
|
163
|
+
if os.path.exists(cache):
|
|
164
|
+
os.remove(cache)
|
|
165
|
+
|
|
166
|
+
dbenv = lmdb.open(
|
|
167
|
+
cache,
|
|
168
|
+
map_size=1024 ** 4,
|
|
169
|
+
max_dbs=10,
|
|
170
|
+
subdir=False,
|
|
171
|
+
lock=False
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
names_db = dbenv.open_db(b'names')
|
|
175
|
+
camera_db = dbenv.open_db(b'camera')
|
|
176
|
+
labels_db = dbenv.open_db(b'labels')
|
|
177
|
+
|
|
178
|
+
boxes_db = None
|
|
179
|
+
if self.parameters.common.with_boxes:
|
|
180
|
+
boxes_db = dbenv.open_db(b'box2d')
|
|
181
|
+
|
|
182
|
+
masks_db = None
|
|
183
|
+
if self.parameters.common.with_masks:
|
|
184
|
+
masks_db = dbenv.open_db(b'masks')
|
|
185
|
+
|
|
186
|
+
with dbenv.begin(write=True) as txn:
|
|
187
|
+
txn.put(
|
|
188
|
+
b'labels',
|
|
189
|
+
json.dumps(self.parameters.labels).encode(),
|
|
190
|
+
db=labels_db
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
for i, instance in enumerate(ds_iterator):
|
|
194
|
+
with dbenv.begin(write=True) as txn:
|
|
195
|
+
boxes = None
|
|
196
|
+
masks = None
|
|
197
|
+
|
|
198
|
+
if self.parameters.common.with_boxes and self.parameters.common.with_masks:
|
|
199
|
+
name, image_data, boxes, (masks, masks_shape) = instance
|
|
200
|
+
elif self.parameters.common.with_boxes:
|
|
201
|
+
name, image_data, boxes = instance
|
|
202
|
+
elif self.parameters.common.with_masks:
|
|
203
|
+
name, image_data, (masks, masks_shape) = instance
|
|
204
|
+
|
|
205
|
+
image, shapes, ratio, image_shape = image_data
|
|
206
|
+
|
|
207
|
+
if boxes is not None:
|
|
208
|
+
txn.put(name.encode(), boxes.tobytes(), db=boxes_db)
|
|
209
|
+
|
|
210
|
+
if masks is not None:
|
|
211
|
+
masks = masks.astype(np.uint8).tobytes()
|
|
212
|
+
txn.put(name.encode(), masks, db=masks_db)
|
|
213
|
+
|
|
214
|
+
txn.put(name.encode(), None, db=names_db)
|
|
215
|
+
|
|
216
|
+
if image is not None:
|
|
217
|
+
txn.put(name.encode(), image.tobytes(), db=camera_db)
|
|
218
|
+
|
|
219
|
+
if shapes is not None:
|
|
220
|
+
txn.put(f'{name}/shapes'.encode(),
|
|
221
|
+
json.dumps(shapes).encode(),
|
|
222
|
+
db=camera_db)
|
|
223
|
+
|
|
224
|
+
if ratio is not None:
|
|
225
|
+
txn.put(f'{name}/ratio'.encode(),
|
|
226
|
+
np.array([ratio], dtype=np.float32).tobytes(),
|
|
227
|
+
db=camera_db)
|
|
228
|
+
|
|
229
|
+
# These are the original image dimensions.
|
|
230
|
+
if image_shape is not None:
|
|
231
|
+
txn.put(f'{name}/shape'.encode(),
|
|
232
|
+
np.array(image_shape, dtype=np.int32).tobytes(),
|
|
233
|
+
db=camera_db)
|
|
234
|
+
|
|
235
|
+
# These are the shape of the mask.
|
|
236
|
+
if masks_shape is not None:
|
|
237
|
+
txn.put(f'{name}/mask_shape'.encode(),
|
|
238
|
+
np.array(masks_shape, dtype=np.int32).tobytes(),
|
|
239
|
+
db=masks_db)
|
|
240
|
+
|
|
241
|
+
self.client.update_stage(
|
|
242
|
+
self.val_session.task.id,
|
|
243
|
+
stage="cache",
|
|
244
|
+
status="Running",
|
|
245
|
+
description=f"Caching: {group_name}",
|
|
246
|
+
percentage=int(100 * i / len(ds_iterator))
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Close the database.
|
|
250
|
+
dbenv.close()
|
|
251
|
+
|
|
252
|
+
self.client.update_stage(
|
|
253
|
+
self.val_session.task.id,
|
|
254
|
+
stage="cache",
|
|
255
|
+
status="Running",
|
|
256
|
+
description=f"Caching: {group_name}",
|
|
257
|
+
percentage=100
|
|
258
|
+
)
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common parent dataset implementations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import io
|
|
8
|
+
import os
|
|
9
|
+
from typing import TYPE_CHECKING, Union
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from PIL import Image
|
|
13
|
+
|
|
14
|
+
from edgefirst.validator.datasets.instance import Instance
|
|
15
|
+
from edgefirst.validator.datasets.utils.transformations import (yolo2xyxy,
|
|
16
|
+
xywh2xyxy,
|
|
17
|
+
normalize,
|
|
18
|
+
denormalize_polygon)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from deepview.datasets.generators.detection import BaseObjectDetectionGenerator # type: ignore
|
|
22
|
+
from deepview.datasets.readers.darknet import DarknetDetectionReader # type: ignore
|
|
23
|
+
from edgefirst.validator.evaluators import DatasetParameters
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Dataset:
|
|
27
|
+
"""
|
|
28
|
+
Abstract dataset class for providing template methods in the dataset.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
source: str
|
|
33
|
+
The path to the source dataset.
|
|
34
|
+
parameters: DatasetParameters
|
|
35
|
+
This contains dataset parameters set from the command line.
|
|
36
|
+
|
|
37
|
+
Raises
|
|
38
|
+
------
|
|
39
|
+
ValueError
|
|
40
|
+
Raised if the provided parameters in certain methods
|
|
41
|
+
does not conform to the specified data type.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
source: str,
|
|
47
|
+
parameters: DatasetParameters
|
|
48
|
+
):
|
|
49
|
+
self.source = source
|
|
50
|
+
self.parameters = parameters
|
|
51
|
+
|
|
52
|
+
self.transformer = None
|
|
53
|
+
if self.parameters.box_format == 'yolo':
|
|
54
|
+
self.transformer = yolo2xyxy
|
|
55
|
+
elif self.parameters.box_format == 'coco':
|
|
56
|
+
self.transformer = xywh2xyxy
|
|
57
|
+
else:
|
|
58
|
+
self.transformer = None
|
|
59
|
+
|
|
60
|
+
self.normalizer = None
|
|
61
|
+
self.denormalizer = None
|
|
62
|
+
if self.parameters.normalized:
|
|
63
|
+
if self.parameters.common.with_masks:
|
|
64
|
+
self.denormalizer = denormalize_polygon
|
|
65
|
+
else:
|
|
66
|
+
if self.parameters.common.with_boxes:
|
|
67
|
+
self.normalizer = normalize
|
|
68
|
+
|
|
69
|
+
self.read_timings = list()
|
|
70
|
+
self.load_timings = list()
|
|
71
|
+
|
|
72
|
+
def build_dataset(self):
|
|
73
|
+
"""Abstract Method"""
|
|
74
|
+
raise NotImplementedError("This is an abstract method.")
|
|
75
|
+
|
|
76
|
+
def read_sample(self, instance):
|
|
77
|
+
"""Abstract Method"""
|
|
78
|
+
raise NotImplementedError("This is an abstract method.")
|
|
79
|
+
|
|
80
|
+
def read_all_samples(
|
|
81
|
+
self,
|
|
82
|
+
info: str = "Validation Progress",
|
|
83
|
+
silent: bool = False
|
|
84
|
+
):
|
|
85
|
+
"""
|
|
86
|
+
Reads all the samples in the dataset.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
info: str
|
|
91
|
+
The description of why image instances are being read.
|
|
92
|
+
By default it is to run validation, hence "Validation Progress".
|
|
93
|
+
silent: bool
|
|
94
|
+
If set to true, prevent validation logging.
|
|
95
|
+
|
|
96
|
+
Yields
|
|
97
|
+
-------
|
|
98
|
+
Instance
|
|
99
|
+
Yields one sample of the ground truth
|
|
100
|
+
instance which contains information on the image
|
|
101
|
+
as a numpy array, boxes, labels, and image path.
|
|
102
|
+
"""
|
|
103
|
+
if silent:
|
|
104
|
+
samples = self.build_dataset()
|
|
105
|
+
for sample in samples:
|
|
106
|
+
yield self.read_sample(sample)
|
|
107
|
+
else:
|
|
108
|
+
try:
|
|
109
|
+
from tqdm import tqdm
|
|
110
|
+
except ImportError:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
samples = tqdm(self.build_dataset(), colour="green")
|
|
115
|
+
samples.set_description(info)
|
|
116
|
+
for sample in samples:
|
|
117
|
+
yield self.read_sample(sample)
|
|
118
|
+
except NameError:
|
|
119
|
+
samples = self.build_dataset()
|
|
120
|
+
num_samples = len(samples)
|
|
121
|
+
for index in range(num_samples):
|
|
122
|
+
print("\t - [INFO]: Computing metrics for image: " +
|
|
123
|
+
"%i of %i [%2.f %s]" %
|
|
124
|
+
(index + 1,
|
|
125
|
+
num_samples,
|
|
126
|
+
100 * ((index + 1) / float(num_samples)),
|
|
127
|
+
'%'), end='\r')
|
|
128
|
+
yield self.read_sample(samples[index])
|
|
129
|
+
|
|
130
|
+
def timings(self):
|
|
131
|
+
"""
|
|
132
|
+
Returns a summary of all the timings:
|
|
133
|
+
(mean, avg, max) of the preprocessing time.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
timings in ms: dict
|
|
138
|
+
|
|
139
|
+
.. code-block:: python
|
|
140
|
+
|
|
141
|
+
{
|
|
142
|
+
'min_input_time': minimum time to load an image,
|
|
143
|
+
'max_input_time': maximum time to load an image,
|
|
144
|
+
'avg_input_time': average time to load an image,
|
|
145
|
+
}
|
|
146
|
+
"""
|
|
147
|
+
return {
|
|
148
|
+
'min_input_time': (np.min(self.load_timings)
|
|
149
|
+
if len(self.load_timings) else 0),
|
|
150
|
+
'max_input_time': (np.max(self.load_timings)
|
|
151
|
+
if len(self.load_timings) else 0),
|
|
152
|
+
'avg_input_time': (np.mean(self.load_timings)
|
|
153
|
+
if len(self.load_timings) else 0),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class BaseDataset(Dataset):
|
|
158
|
+
"""
|
|
159
|
+
This class utilizes deepview-datasets methods for iterating through
|
|
160
|
+
the images and annotations.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
source: str
|
|
165
|
+
The path to the dataset.
|
|
166
|
+
iterator: Iterator
|
|
167
|
+
Object in deepview-datasets for iterating through
|
|
168
|
+
the images or annotations. This can either be a generator if
|
|
169
|
+
a YAML file was passed, or a Reader if a directory was passed.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
source: str,
|
|
175
|
+
iterator: Union[BaseObjectDetectionGenerator, DarknetDetectionReader]
|
|
176
|
+
):
|
|
177
|
+
super(BaseDataset, self).__init__(source)
|
|
178
|
+
self.iterator = iterator
|
|
179
|
+
|
|
180
|
+
if isinstance(self.iterator, BaseObjectDetectionGenerator):
|
|
181
|
+
self.storage = self.iterator.reader.storage
|
|
182
|
+
self.labels = self.iterator.reader.classes
|
|
183
|
+
else:
|
|
184
|
+
self.storage = self.iterator.storage
|
|
185
|
+
self.labels = self.iterator.classes
|
|
186
|
+
|
|
187
|
+
def build_dataset(
|
|
188
|
+
self) -> Union[BaseObjectDetectionGenerator, DarknetDetectionReader]:
|
|
189
|
+
"""
|
|
190
|
+
Returns the iterator object which already contains all the images
|
|
191
|
+
and annotations read in the dataset.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
Union[BaseObjectDetectionGenerator, DarknetDetectionReader]
|
|
196
|
+
BaseObjectDetectionGenerator
|
|
197
|
+
A generator if a YAML file was passed.
|
|
198
|
+
DarknetDetectionReader
|
|
199
|
+
Reader if a directory was passed.
|
|
200
|
+
"""
|
|
201
|
+
return self.iterator
|
|
202
|
+
|
|
203
|
+
def read_sample(self, sample: tuple) -> Instance:
|
|
204
|
+
"""
|
|
205
|
+
Returns the ground truth instance object which is needed to be read
|
|
206
|
+
by validator.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
sample: tuple
|
|
211
|
+
This contains the (image, boxes) in one sample.
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
Instance
|
|
216
|
+
An object that contains the image, boxes, labels, etc.
|
|
217
|
+
"""
|
|
218
|
+
from edgefirst.validator.datasets.utils.transformations import yolo2xyxy
|
|
219
|
+
|
|
220
|
+
image, boxes = sample
|
|
221
|
+
if len(image.shape) < 2:
|
|
222
|
+
image = Image.open(io.BytesIO(image)).convert('RGB')
|
|
223
|
+
image = np.asarray(image, dtype=np.uint8)
|
|
224
|
+
height, width, _ = image.shape
|
|
225
|
+
|
|
226
|
+
if isinstance(self.iterator, BaseObjectDetectionGenerator):
|
|
227
|
+
image_path = self.iterator.reader.get_instance_id()
|
|
228
|
+
else:
|
|
229
|
+
image_path = self.iterator.get_instance_id()
|
|
230
|
+
|
|
231
|
+
# Add file extension to allow image saving in disk.
|
|
232
|
+
if os.path.splitext(image_path)[-1] == "":
|
|
233
|
+
image_path += ".png"
|
|
234
|
+
|
|
235
|
+
instance = Instance(image_path)
|
|
236
|
+
instance.height = height
|
|
237
|
+
instance.width = width
|
|
238
|
+
instance.image = image
|
|
239
|
+
|
|
240
|
+
boxes = boxes[np.sum(boxes, axis=-1) != 0]
|
|
241
|
+
instance.boxes = yolo2xyxy(boxes[..., 0:4])
|
|
242
|
+
|
|
243
|
+
labels = boxes[..., 4:5]
|
|
244
|
+
instance.labels = labels
|
|
245
|
+
return instance
|