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,234 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import traceback
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import threading
|
|
7
|
+
from queue import Queue, Empty
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
from edgefirst.validator.evaluators.callbacks import PlotsCallback, CallbacksList
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from edgefirst.validator.publishers import StudioPublisher
|
|
14
|
+
from edgefirst.validator.evaluators import Evaluator
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StudioProgress:
|
|
18
|
+
"""
|
|
19
|
+
Deploy standard validation from the existing evaluator objects
|
|
20
|
+
but also provide communication to EdgeFirst Studio to report
|
|
21
|
+
the progress and the final metrics and evaluation of the model
|
|
22
|
+
performance.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
evaluator: Evaluator
|
|
27
|
+
This object handles running validation by iterating through
|
|
28
|
+
the dataset samples and run model inference to calculate the
|
|
29
|
+
validation metrics at the end of the process.
|
|
30
|
+
studio_publisher: StudioPublisher
|
|
31
|
+
Handles the state updates to the validation session in EdgeFirst Studio.
|
|
32
|
+
stage: str
|
|
33
|
+
The current stage to update for the progress in Studio.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
evaluator: Evaluator,
|
|
39
|
+
studio_publisher: StudioPublisher,
|
|
40
|
+
stage: str = "validate"
|
|
41
|
+
):
|
|
42
|
+
self.evaluator = evaluator
|
|
43
|
+
plots_callback = PlotsCallback(studio_publisher=studio_publisher,
|
|
44
|
+
parameters=self.evaluator.parameters,
|
|
45
|
+
stage=stage)
|
|
46
|
+
self.callbacks = CallbacksList([plots_callback])
|
|
47
|
+
|
|
48
|
+
# Non-blocking queue + single background worker thread to execute callbacks
|
|
49
|
+
# Bounded queue prevents unbounded memory growth;
|
|
50
|
+
# if full we fallback to sync execution.
|
|
51
|
+
self._queue: Queue = Queue(maxsize=1000)
|
|
52
|
+
self._stop_event = threading.Event()
|
|
53
|
+
self._last_update = 0
|
|
54
|
+
# NOTE: Increasing this interval results in the functions not being
|
|
55
|
+
# called.
|
|
56
|
+
self._update_interval = 0.0001 # Minimum seconds between updates.
|
|
57
|
+
self._worker = threading.Thread(target=self._worker_loop,
|
|
58
|
+
name="studio-callback-worker",
|
|
59
|
+
daemon=True)
|
|
60
|
+
self._worker.start()
|
|
61
|
+
|
|
62
|
+
def _worker_loop(self):
|
|
63
|
+
"""
|
|
64
|
+
Background worker that executes
|
|
65
|
+
callback functions from the queue.
|
|
66
|
+
"""
|
|
67
|
+
while True:
|
|
68
|
+
try:
|
|
69
|
+
fn, kwargs = self._queue.get(timeout=0.5)
|
|
70
|
+
except Empty:
|
|
71
|
+
if self._stop_event.is_set() and self._queue.empty():
|
|
72
|
+
break
|
|
73
|
+
continue
|
|
74
|
+
try:
|
|
75
|
+
try:
|
|
76
|
+
fn(**kwargs)
|
|
77
|
+
except Exception:
|
|
78
|
+
traceback.print_exc()
|
|
79
|
+
finally:
|
|
80
|
+
try:
|
|
81
|
+
self._queue.task_done()
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def _dispatch(self, method_name: str, /, **kwargs):
|
|
86
|
+
"""
|
|
87
|
+
Enqueue a callback method for asynchronous execution.
|
|
88
|
+
If the queue is full, execute synchronously as a best-effort
|
|
89
|
+
fallback to avoid dropping important updates.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
method_name: str
|
|
94
|
+
The name of the callback method to invoke.
|
|
95
|
+
**kwargs: dict
|
|
96
|
+
The keyword arguments to pass to the callback method.
|
|
97
|
+
"""
|
|
98
|
+
fn = getattr(self.callbacks, method_name)
|
|
99
|
+
# Always process begin/end/error events
|
|
100
|
+
critical = method_name in ('on_test_begin',
|
|
101
|
+
'on_test_end',
|
|
102
|
+
'on_test_error')
|
|
103
|
+
current_time = time.monotonic()
|
|
104
|
+
if critical or (current_time -
|
|
105
|
+
self._last_update) >= self._update_interval:
|
|
106
|
+
try:
|
|
107
|
+
self._queue.put_nowait((fn, kwargs))
|
|
108
|
+
self._last_update = current_time
|
|
109
|
+
except Exception:
|
|
110
|
+
if critical:
|
|
111
|
+
# Only fall back to sync for critical updates.
|
|
112
|
+
try:
|
|
113
|
+
fn(**kwargs)
|
|
114
|
+
except Exception:
|
|
115
|
+
traceback.print_exc()
|
|
116
|
+
|
|
117
|
+
def _drain_and_stop(self, timeout: float = 10.0):
|
|
118
|
+
"""
|
|
119
|
+
Wait for queued tasks to finish
|
|
120
|
+
and stop the background worker.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
timeout: float
|
|
125
|
+
The maximum time to wait for the worker to stop.
|
|
126
|
+
"""
|
|
127
|
+
# Wait for tasks to be processed
|
|
128
|
+
try:
|
|
129
|
+
self._queue.join()
|
|
130
|
+
except Exception:
|
|
131
|
+
pass
|
|
132
|
+
self._stop_event.set()
|
|
133
|
+
self._worker.join(timeout=timeout)
|
|
134
|
+
|
|
135
|
+
def group_evaluation(self, epoch: int = 0, reset: bool = True):
|
|
136
|
+
"""
|
|
137
|
+
Runs model validation on all samples in the dataset.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
epoch: int
|
|
142
|
+
This is the training epoch number. This
|
|
143
|
+
parameter is internal for ModelPack usage.
|
|
144
|
+
Standalone validation does not use this parameter.
|
|
145
|
+
reset: bool
|
|
146
|
+
This is an optional parameter that controls the reset state.
|
|
147
|
+
By default, it will reset at the end of validation to erase
|
|
148
|
+
the data in the containers.
|
|
149
|
+
"""
|
|
150
|
+
save_image = bool(self.evaluator.parameters.validation.visualize or
|
|
151
|
+
self.evaluator.parameters.validation.tensorboard)
|
|
152
|
+
|
|
153
|
+
logs = {
|
|
154
|
+
"total": len(self.evaluator.dataset)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
i = 0
|
|
158
|
+
try:
|
|
159
|
+
self._dispatch("on_test_begin", logs=logs)
|
|
160
|
+
for instance in self.evaluator.instance_collector():
|
|
161
|
+
i += 1
|
|
162
|
+
self._dispatch("on_test_batch_begin", step=i, logs=logs)
|
|
163
|
+
if self.evaluator.parameters.validation.display >= 0:
|
|
164
|
+
if (self.evaluator.counter <
|
|
165
|
+
self.evaluator.parameters.validation.display):
|
|
166
|
+
save_image = True
|
|
167
|
+
self.evaluator.counter += 1
|
|
168
|
+
else:
|
|
169
|
+
save_image = False
|
|
170
|
+
|
|
171
|
+
self.evaluator.single_evaluation(
|
|
172
|
+
instance, epoch=epoch, save_image=save_image)
|
|
173
|
+
self._dispatch("on_test_batch_end", step=i, logs=logs)
|
|
174
|
+
metrics, plots = self.end(epoch=epoch, reset=reset)
|
|
175
|
+
|
|
176
|
+
if (self.evaluator.parameters.model.common.with_boxes and
|
|
177
|
+
self.evaluator.parameters.model.common.with_masks):
|
|
178
|
+
logs["multitask"] = metrics
|
|
179
|
+
logs["plots"] = plots
|
|
180
|
+
logs["timings"] = metrics.timings
|
|
181
|
+
|
|
182
|
+
elif self.evaluator.parameters.model.common.with_boxes:
|
|
183
|
+
logs["detection"] = metrics
|
|
184
|
+
logs["plots"] = plots
|
|
185
|
+
logs["timings"] = metrics.timings
|
|
186
|
+
|
|
187
|
+
elif self.evaluator.parameters.model.common.with_masks:
|
|
188
|
+
logs["segmentation"] = metrics
|
|
189
|
+
logs["plots"] = plots
|
|
190
|
+
logs["timings"] = metrics.timings
|
|
191
|
+
|
|
192
|
+
self._dispatch("on_test_end", logs=logs)
|
|
193
|
+
# Ensure queued callback tasks complete before returning from
|
|
194
|
+
# evaluation
|
|
195
|
+
self._drain_and_stop()
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self._dispatch("on_test_error", step=i, error=e, logs=logs)
|
|
199
|
+
# Ensure error callback is processed and worker stopped
|
|
200
|
+
self._drain_and_stop()
|
|
201
|
+
error = traceback.format_exc()
|
|
202
|
+
print(error)
|
|
203
|
+
raise e
|
|
204
|
+
|
|
205
|
+
def end(self, epoch: int = 0, reset: bool = True):
|
|
206
|
+
"""
|
|
207
|
+
Calculate final metrics and publish the results into
|
|
208
|
+
EdgeFirst Studio.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
epoch: int
|
|
213
|
+
This is the training epoch number. This
|
|
214
|
+
parameter is internal for ModelPack usage.
|
|
215
|
+
Standalone validation does not use this parameter.
|
|
216
|
+
reset: bool
|
|
217
|
+
This is an optional parameter that controls the reset state.
|
|
218
|
+
By default, it will reset at the end of validation to erase
|
|
219
|
+
the data in the containers.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
Tuple[Metrics, Metrics]
|
|
224
|
+
This returns the detection and segmentation metrics for
|
|
225
|
+
multitask. Otherwise, for single tasks, only one or
|
|
226
|
+
the other is returned.
|
|
227
|
+
"""
|
|
228
|
+
# Ensure any pending callback tasks are flushed
|
|
229
|
+
# before final metrics are posted
|
|
230
|
+
try:
|
|
231
|
+
self._queue.join()
|
|
232
|
+
except Exception:
|
|
233
|
+
pass
|
|
234
|
+
return self.evaluator.end(epoch=epoch, reset=reset)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from typing import TYPE_CHECKING, Tuple, List
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib.figure
|
|
9
|
+
|
|
10
|
+
from edgefirst.validator.publishers.utils.logger import logger
|
|
11
|
+
from edgefirst.validator.visualize.utils.plots import close_figures
|
|
12
|
+
from edgefirst.validator.datasets.utils.transformations import labels2string
|
|
13
|
+
from edgefirst.validator.publishers import ConsolePublisher, TensorBoardPublisher
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from edgefirst.validator.evaluators import CombinedParameters
|
|
17
|
+
from edgefirst.validator.datasets import Dataset
|
|
18
|
+
from edgefirst.validator.metrics import Metrics
|
|
19
|
+
from edgefirst.validator.runners import Runner
|
|
20
|
+
from edgefirst.validator.metrics import Plots
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Evaluator:
|
|
24
|
+
"""
|
|
25
|
+
Abstract class that provides a template for the
|
|
26
|
+
Evaluators (detection or segmentation).
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
parameters: CombinedParameters
|
|
31
|
+
This is a container for the model, dataset, and validation parameters
|
|
32
|
+
runner: Runner
|
|
33
|
+
This object provides methods to run inference on the model provided.
|
|
34
|
+
dataset: Dataset
|
|
35
|
+
A type of dataset object responsible for reading different types
|
|
36
|
+
of datasets such as Darknet, TFRecords, or EdgeFirst Datasets.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
parameters: CombinedParameters,
|
|
42
|
+
runner: Runner,
|
|
43
|
+
dataset: Dataset,
|
|
44
|
+
):
|
|
45
|
+
self.parameters = parameters
|
|
46
|
+
self.runner = runner
|
|
47
|
+
self.dataset = dataset
|
|
48
|
+
|
|
49
|
+
self.console_writer = ConsolePublisher(
|
|
50
|
+
self.parameters.validation.visualize)
|
|
51
|
+
self.tensorboard_writer = None
|
|
52
|
+
if self.parameters.validation.tensorboard:
|
|
53
|
+
self.tensorboard_writer = TensorBoardPublisher(
|
|
54
|
+
self.parameters.validation.tensorboard)
|
|
55
|
+
|
|
56
|
+
self.model_name = os.path.basename(os.path.normpath(
|
|
57
|
+
self.parameters.model.model_path))
|
|
58
|
+
if os.path.isfile(self.parameters.dataset.dataset_path):
|
|
59
|
+
self.dataset_name = os.path.basename(os.path.normpath(
|
|
60
|
+
os.path.dirname(self.parameters.dataset.dataset_path)))
|
|
61
|
+
else:
|
|
62
|
+
self.dataset_name = os.path.basename(os.path.normpath(
|
|
63
|
+
self.parameters.dataset.dataset_path))
|
|
64
|
+
|
|
65
|
+
if self.parameters.validation.tensorboard:
|
|
66
|
+
self.save_path = self.parameters.validation.tensorboard
|
|
67
|
+
elif self.tensorboard_writer:
|
|
68
|
+
self.save_path = self.tensorboard_writer.logdir
|
|
69
|
+
elif self.parameters.validation.visualize:
|
|
70
|
+
self.save_path = self.parameters.validation.visualize
|
|
71
|
+
else:
|
|
72
|
+
self.save_path = None
|
|
73
|
+
|
|
74
|
+
self.metrics = None
|
|
75
|
+
self.confusion_matrix = None
|
|
76
|
+
|
|
77
|
+
# This counter is used to determine the number of images saved.
|
|
78
|
+
self.counter = 0
|
|
79
|
+
|
|
80
|
+
def instance_collector(self):
|
|
81
|
+
"""Abstract Method"""
|
|
82
|
+
raise NotImplementedError("This is an abstract method")
|
|
83
|
+
|
|
84
|
+
def evaluate(self, instance: dict):
|
|
85
|
+
"""Abstract Method"""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def single_evaluation(self, instance: dict, epoch: int, save_image: bool):
|
|
89
|
+
"""
|
|
90
|
+
Run model evaluation on a single image/sample.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
instance: dict
|
|
95
|
+
This contains the ground truth and model prediction instances
|
|
96
|
+
with keys "gt_instance", "dt_instance".
|
|
97
|
+
epoch: int
|
|
98
|
+
This is the training epoch number. This
|
|
99
|
+
parameter is internal for ModelPack usage.
|
|
100
|
+
Standalone validation does not use this parameter.
|
|
101
|
+
save_image: bool
|
|
102
|
+
If set to True, this will save the image
|
|
103
|
+
with drawn bounding box results.
|
|
104
|
+
"""
|
|
105
|
+
self.evaluate(instance=instance)
|
|
106
|
+
|
|
107
|
+
if save_image:
|
|
108
|
+
gt_instance = instance.get("gt_instance", None)
|
|
109
|
+
dt_instance = instance.get("dt_instance", None)
|
|
110
|
+
|
|
111
|
+
# Convert labels from integers to string for detection.
|
|
112
|
+
gt_instance.labels = np.array(labels2string(
|
|
113
|
+
gt_instance.labels, self.parameters.dataset.labels))
|
|
114
|
+
dt_instance.labels = np.array(labels2string(
|
|
115
|
+
dt_instance.labels, self.parameters.dataset.labels))
|
|
116
|
+
self.visualize(gt_instance, dt_instance, epoch=epoch)
|
|
117
|
+
|
|
118
|
+
def group_evaluation(self, epoch: int = 0, reset: bool = True):
|
|
119
|
+
"""
|
|
120
|
+
Runs model validation on all samples in the dataset.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
epoch: int
|
|
125
|
+
This is the training epoch number. This
|
|
126
|
+
parameter is internal for ModelPack usage.
|
|
127
|
+
Standalone validation does not use this parameter.
|
|
128
|
+
reset: bool
|
|
129
|
+
This is an optional parameter that controls the reset state.
|
|
130
|
+
By default, it will reset at the end of validation to erase
|
|
131
|
+
the data in the containers.
|
|
132
|
+
"""
|
|
133
|
+
save_image = bool(self.parameters.validation.visualize or
|
|
134
|
+
self.parameters.validation.tensorboard)
|
|
135
|
+
|
|
136
|
+
for instance in self.instance_collector():
|
|
137
|
+
if self.parameters.validation.display >= 0:
|
|
138
|
+
if self.counter < self.parameters.validation.display:
|
|
139
|
+
save_image = True
|
|
140
|
+
self.counter += 1
|
|
141
|
+
else:
|
|
142
|
+
save_image = False
|
|
143
|
+
|
|
144
|
+
if instance.get("dt_instance", None) is None:
|
|
145
|
+
logger(
|
|
146
|
+
"VisionPack Trial Expired. Please use a licensed version" +
|
|
147
|
+
" for complete validation. Contact support@au-zone.com" +
|
|
148
|
+
" for more information.", code="WARNING")
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
self.single_evaluation(
|
|
152
|
+
instance=instance, epoch=epoch, save_image=save_image)
|
|
153
|
+
return self.end(epoch=epoch, reset=reset)
|
|
154
|
+
|
|
155
|
+
def visualize(self):
|
|
156
|
+
"""Absract Method"""
|
|
157
|
+
raise NotImplementedError("This is an abstract method")
|
|
158
|
+
|
|
159
|
+
def end(
|
|
160
|
+
self,
|
|
161
|
+
epoch: int = 0,
|
|
162
|
+
reset: bool = True,
|
|
163
|
+
publish: bool = True
|
|
164
|
+
) -> Tuple[Metrics, Plots]:
|
|
165
|
+
"""
|
|
166
|
+
Computes the final metrics and generates the validation plots
|
|
167
|
+
to save the results in disk or publishes to Tensorboard.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
epoch: int
|
|
172
|
+
This is the training epoch number. This
|
|
173
|
+
parameter is internal for ModelPack usage.
|
|
174
|
+
Standalone validation does not use this parameter.
|
|
175
|
+
reset: bool
|
|
176
|
+
This is an optional parameter that controls the reset state.
|
|
177
|
+
By default, it will reset at the end of validation to erase
|
|
178
|
+
the data in the containers.
|
|
179
|
+
publish: bool
|
|
180
|
+
Specify to publish and print the metrics. Default to True.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
metrics: Metrics
|
|
185
|
+
This is a container for the detection metrics.
|
|
186
|
+
plots: Plots
|
|
187
|
+
This is a container for the validation data for plotting.
|
|
188
|
+
"""
|
|
189
|
+
if hasattr(self.runner, "stop"):
|
|
190
|
+
self.runner.stop()
|
|
191
|
+
|
|
192
|
+
if self.runner:
|
|
193
|
+
self.metrics.metrics.timings = self.runner.timer.to_dict()
|
|
194
|
+
self.metrics.run_metrics()
|
|
195
|
+
|
|
196
|
+
# Plot Operations
|
|
197
|
+
if self.parameters.validation.plots:
|
|
198
|
+
if self.parameters.model.common.with_boxes:
|
|
199
|
+
self.metrics.plots.curve_labels = labels2string(
|
|
200
|
+
self.metrics.plots.curve_labels, self.parameters.dataset.labels)
|
|
201
|
+
if (self.parameters.validation.method in ["ultralytics", "yolov7"]
|
|
202
|
+
and self.confusion_matrix is not None):
|
|
203
|
+
self.metrics.plots.confusion_matrix = self.confusion_matrix.matrix
|
|
204
|
+
|
|
205
|
+
if self.parameters.validation.visualize or self.tensorboard_writer:
|
|
206
|
+
plots = self.get_plots()
|
|
207
|
+
|
|
208
|
+
if self.parameters.validation.visualize:
|
|
209
|
+
self.save_plots(plots)
|
|
210
|
+
elif self.tensorboard_writer:
|
|
211
|
+
self.publish_plots(plots, epoch)
|
|
212
|
+
close_figures(plots)
|
|
213
|
+
|
|
214
|
+
if publish:
|
|
215
|
+
# Metric Operations
|
|
216
|
+
if self.tensorboard_writer:
|
|
217
|
+
self.tensorboard_writer.publish_metrics(
|
|
218
|
+
metrics=self.metrics.metrics,
|
|
219
|
+
parameters=self.parameters,
|
|
220
|
+
step=epoch,
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
table = self.console_writer(metrics=self.metrics.metrics,
|
|
224
|
+
parameters=self.parameters)
|
|
225
|
+
if self.parameters.validation.visualize:
|
|
226
|
+
self.console_writer.save_metrics(table)
|
|
227
|
+
|
|
228
|
+
if self.parameters.validation.csv_out:
|
|
229
|
+
self.console_writer.save_csv_metrics(
|
|
230
|
+
metrics=self.metrics.metrics, parameters=self.parameters)
|
|
231
|
+
|
|
232
|
+
# Prevent the reset from taking effect.
|
|
233
|
+
metrics = deepcopy(self.metrics.metrics)
|
|
234
|
+
plots = deepcopy(self.metrics.plots)
|
|
235
|
+
if reset:
|
|
236
|
+
self.metrics.reset()
|
|
237
|
+
return metrics, plots
|
|
238
|
+
|
|
239
|
+
def stop(self):
|
|
240
|
+
"""
|
|
241
|
+
Stops any active running processes.
|
|
242
|
+
"""
|
|
243
|
+
if hasattr(self.runner, "stop"):
|
|
244
|
+
self.runner.stop()
|
|
245
|
+
|
|
246
|
+
def get_plots(self) -> List[matplotlib.figure.Figure]:
|
|
247
|
+
"""Absract Method"""
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
def save_plots(self, plots: List[matplotlib.figure.Figure]):
|
|
251
|
+
"""Absract Method"""
|
|
252
|
+
pass
|
|
253
|
+
|
|
254
|
+
def publish_plots(
|
|
255
|
+
self, plots: List[matplotlib.figure.Figure], epoch: int = 0):
|
|
256
|
+
"""Absract Method"""
|
|
257
|
+
pass
|