valor-lite 0.33.7__py3-none-any.whl → 0.33.9__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.
- valor_lite/LICENSE +21 -0
- valor_lite/classification/annotation.py +30 -2
- valor_lite/classification/computation.py +31 -52
- valor_lite/classification/manager.py +230 -323
- valor_lite/classification/metric.py +273 -50
- valor_lite/object_detection/annotation.py +274 -0
- valor_lite/{detection → object_detection}/computation.py +130 -92
- valor_lite/{detection → object_detection}/manager.py +425 -769
- valor_lite/object_detection/metric.py +789 -0
- valor_lite/semantic_segmentation/__init__.py +27 -0
- valor_lite/semantic_segmentation/annotation.py +96 -0
- valor_lite/semantic_segmentation/computation.py +186 -0
- valor_lite/semantic_segmentation/manager.py +549 -0
- valor_lite/semantic_segmentation/metric.py +278 -0
- valor_lite/text_generation/__init__.py +0 -0
- valor_lite-0.33.9.dist-info/METADATA +179 -0
- valor_lite-0.33.9.dist-info/RECORD +24 -0
- valor_lite/detection/annotation.py +0 -98
- valor_lite/detection/metric.py +0 -408
- valor_lite-0.33.7.dist-info/METADATA +0 -41
- valor_lite-0.33.7.dist-info/RECORD +0 -17
- /valor_lite/{detection → object_detection}/__init__.py +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/LICENSE +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/WHEEL +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .annotation import Bitmask, Segmentation
|
|
2
|
+
from .manager import DataLoader, Evaluator
|
|
3
|
+
from .metric import (
|
|
4
|
+
F1,
|
|
5
|
+
Accuracy,
|
|
6
|
+
ConfusionMatrix,
|
|
7
|
+
IoU,
|
|
8
|
+
MetricType,
|
|
9
|
+
Precision,
|
|
10
|
+
Recall,
|
|
11
|
+
mIoU,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"DataLoader",
|
|
16
|
+
"Evaluator",
|
|
17
|
+
"Segmentation",
|
|
18
|
+
"Bitmask",
|
|
19
|
+
"MetricType",
|
|
20
|
+
"Precision",
|
|
21
|
+
"Recall",
|
|
22
|
+
"Accuracy",
|
|
23
|
+
"F1",
|
|
24
|
+
"IoU",
|
|
25
|
+
"mIoU",
|
|
26
|
+
"ConfusionMatrix",
|
|
27
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class Bitmask:
|
|
9
|
+
"""
|
|
10
|
+
Represents a binary mask with an associated semantic label.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
mask : NDArray[np.bool_]
|
|
15
|
+
A NumPy array of boolean values representing the mask.
|
|
16
|
+
label : str
|
|
17
|
+
The semantic label associated with the mask.
|
|
18
|
+
|
|
19
|
+
Examples
|
|
20
|
+
--------
|
|
21
|
+
>>> import numpy as np
|
|
22
|
+
>>> mask = np.array([[True, False], [False, True]], dtype=np.bool_)
|
|
23
|
+
>>> bitmask = Bitmask(mask=mask, label='ocean')
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
mask: NDArray[np.bool_]
|
|
27
|
+
label: str
|
|
28
|
+
|
|
29
|
+
def __post_init__(self):
|
|
30
|
+
if self.mask.dtype != np.bool_:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"Bitmask recieved mask with dtype `{self.mask.dtype}`."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Segmentation:
|
|
38
|
+
"""
|
|
39
|
+
Segmentation data structure holding ground truth and prediction bitmasks for semantic segmentation tasks.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
uid : str
|
|
44
|
+
Unique identifier for the image or sample.
|
|
45
|
+
groundtruths : List[Bitmask]
|
|
46
|
+
List of ground truth bitmasks.
|
|
47
|
+
predictions : List[Bitmask]
|
|
48
|
+
List of predicted bitmasks.
|
|
49
|
+
shape : tuple of int, optional
|
|
50
|
+
The shape of the segmentation masks. This is set automatically after initialization.
|
|
51
|
+
size : int, optional
|
|
52
|
+
The total number of pixels in the masks. This is set automatically after initialization.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> import numpy as np
|
|
57
|
+
>>> mask1 = np.array([[True, False], [False, True]], dtype=np.bool_)
|
|
58
|
+
>>> groundtruth = Bitmask(mask=mask1, label='object')
|
|
59
|
+
>>> mask2 = np.array([[False, True], [True, False]], dtype=np.bool_)
|
|
60
|
+
>>> prediction = Bitmask(mask=mask2, label='object')
|
|
61
|
+
>>> segmentation = Segmentation(
|
|
62
|
+
... uid='123',
|
|
63
|
+
... groundtruths=[groundtruth],
|
|
64
|
+
... predictions=[prediction]
|
|
65
|
+
... )
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
uid: str
|
|
69
|
+
groundtruths: list[Bitmask]
|
|
70
|
+
predictions: list[Bitmask]
|
|
71
|
+
shape: tuple[int, ...] = field(default_factory=lambda: (0, 0))
|
|
72
|
+
size: int = field(default=0)
|
|
73
|
+
|
|
74
|
+
def __post_init__(self):
|
|
75
|
+
|
|
76
|
+
groundtruth_shape = {
|
|
77
|
+
groundtruth.mask.shape for groundtruth in self.groundtruths
|
|
78
|
+
}
|
|
79
|
+
prediction_shape = {
|
|
80
|
+
prediction.mask.shape for prediction in self.predictions
|
|
81
|
+
}
|
|
82
|
+
if len(groundtruth_shape) == 0:
|
|
83
|
+
raise ValueError("The segmenation is missing ground truths.")
|
|
84
|
+
elif len(prediction_shape) == 0:
|
|
85
|
+
raise ValueError("The segmenation is missing predictions.")
|
|
86
|
+
elif (
|
|
87
|
+
len(groundtruth_shape) != 1
|
|
88
|
+
or len(prediction_shape) != 1
|
|
89
|
+
or groundtruth_shape != prediction_shape
|
|
90
|
+
):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"A shape mismatch exists within the segmentation."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
self.shape = groundtruth_shape.pop()
|
|
96
|
+
self.size = int(np.prod(np.array(self.shape)))
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def compute_intermediate_confusion_matrices(
|
|
6
|
+
groundtruths: NDArray[np.bool_],
|
|
7
|
+
predictions: NDArray[np.bool_],
|
|
8
|
+
groundtruth_labels: NDArray[np.int32],
|
|
9
|
+
prediction_labels: NDArray[np.int32],
|
|
10
|
+
n_labels: int,
|
|
11
|
+
) -> NDArray[np.int32]:
|
|
12
|
+
"""
|
|
13
|
+
Computes an intermediate confusion matrix containing label counts.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
groundtruths : NDArray[np.bool_]
|
|
18
|
+
A 2-D array containing flattened bitmasks for each label.
|
|
19
|
+
predictions : NDArray[np.bool_]
|
|
20
|
+
A 2-D array containing flattened bitmasks for each label.
|
|
21
|
+
groundtruth_labels : NDArray[np.int32]
|
|
22
|
+
A 1-D array containing label indices.
|
|
23
|
+
groundtruth_labels : NDArray[np.int32]
|
|
24
|
+
A 1-D array containing label indices.
|
|
25
|
+
n_labels : int
|
|
26
|
+
The number of unique labels.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
NDArray[np.int32]
|
|
31
|
+
A 2-D confusion matrix with shape (n_labels + 1, n_labels + 1).
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
n_gt_labels = groundtruth_labels.size
|
|
35
|
+
n_pd_labels = prediction_labels.size
|
|
36
|
+
|
|
37
|
+
groundtruth_counts = groundtruths.sum(axis=1)
|
|
38
|
+
prediction_counts = predictions.sum(axis=1)
|
|
39
|
+
|
|
40
|
+
background_counts = np.logical_not(
|
|
41
|
+
groundtruths.any(axis=0) | predictions.any(axis=0)
|
|
42
|
+
).sum()
|
|
43
|
+
|
|
44
|
+
intersection_counts = np.logical_and(
|
|
45
|
+
groundtruths.reshape(n_gt_labels, 1, -1),
|
|
46
|
+
predictions.reshape(1, n_pd_labels, -1),
|
|
47
|
+
).sum(axis=2)
|
|
48
|
+
|
|
49
|
+
intersected_groundtruth_counts = intersection_counts.sum(axis=0)
|
|
50
|
+
intersected_prediction_counts = intersection_counts.sum(axis=1)
|
|
51
|
+
|
|
52
|
+
confusion_matrix = np.zeros((n_labels + 1, n_labels + 1), dtype=np.int32)
|
|
53
|
+
confusion_matrix[0, 0] = background_counts
|
|
54
|
+
for gidx in range(n_gt_labels):
|
|
55
|
+
gt_label_idx = groundtruth_labels[gidx]
|
|
56
|
+
for pidx in range(n_pd_labels):
|
|
57
|
+
pd_label_idx = prediction_labels[pidx]
|
|
58
|
+
confusion_matrix[
|
|
59
|
+
gt_label_idx + 1,
|
|
60
|
+
pd_label_idx + 1,
|
|
61
|
+
] = intersection_counts[gidx, pidx]
|
|
62
|
+
|
|
63
|
+
if gidx == 0:
|
|
64
|
+
confusion_matrix[0, pd_label_idx + 1] = (
|
|
65
|
+
prediction_counts[pidx]
|
|
66
|
+
- intersected_prediction_counts[pidx]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
confusion_matrix[gt_label_idx + 1, 0] = (
|
|
70
|
+
groundtruth_counts[gidx] - intersected_groundtruth_counts[gidx]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return confusion_matrix
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def compute_metrics(
|
|
77
|
+
data: NDArray[np.float64],
|
|
78
|
+
label_metadata: NDArray[np.int32],
|
|
79
|
+
n_pixels: int,
|
|
80
|
+
) -> tuple[
|
|
81
|
+
NDArray[np.float64],
|
|
82
|
+
NDArray[np.float64],
|
|
83
|
+
NDArray[np.float64],
|
|
84
|
+
float,
|
|
85
|
+
NDArray[np.float64],
|
|
86
|
+
NDArray[np.float64],
|
|
87
|
+
NDArray[np.float64],
|
|
88
|
+
]:
|
|
89
|
+
"""
|
|
90
|
+
Computes semantic segmentation metrics.
|
|
91
|
+
|
|
92
|
+
Takes data with shape (3, N).
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
data : NDArray[np.float64]
|
|
97
|
+
A 3-D array containing confusion matrices for each datum.
|
|
98
|
+
label_metadata : NDArray[np.int32]
|
|
99
|
+
A 2-D array containing label metadata.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
NDArray[np.float64]
|
|
104
|
+
Precision.
|
|
105
|
+
NDArray[np.float64]
|
|
106
|
+
Recall.
|
|
107
|
+
NDArray[np.float64]
|
|
108
|
+
F1 Score.
|
|
109
|
+
float
|
|
110
|
+
Accuracy
|
|
111
|
+
NDArray[np.float64]
|
|
112
|
+
Confusion matrix containing IoU values.
|
|
113
|
+
NDArray[np.float64]
|
|
114
|
+
Hallucination ratios.
|
|
115
|
+
NDArray[np.float64]
|
|
116
|
+
Missing prediction ratios.
|
|
117
|
+
"""
|
|
118
|
+
n_labels = label_metadata.shape[0]
|
|
119
|
+
gt_counts = label_metadata[:, 0]
|
|
120
|
+
pd_counts = label_metadata[:, 1]
|
|
121
|
+
|
|
122
|
+
counts = data.sum(axis=0)
|
|
123
|
+
|
|
124
|
+
# compute iou, missing_predictions and hallucinations
|
|
125
|
+
intersection_ = counts[1:, 1:]
|
|
126
|
+
union_ = (
|
|
127
|
+
gt_counts[:, np.newaxis] + pd_counts[np.newaxis, :] - intersection_
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
ious = np.zeros((n_labels, n_labels), dtype=np.float64)
|
|
131
|
+
np.divide(
|
|
132
|
+
intersection_,
|
|
133
|
+
union_,
|
|
134
|
+
where=union_ > 1e-9,
|
|
135
|
+
out=ious,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
hallucination_ratio = np.zeros((n_labels), dtype=np.float64)
|
|
139
|
+
np.divide(
|
|
140
|
+
counts[0, 1:],
|
|
141
|
+
pd_counts,
|
|
142
|
+
where=pd_counts > 1e-9,
|
|
143
|
+
out=hallucination_ratio,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
missing_prediction_ratio = np.zeros((n_labels), dtype=np.float64)
|
|
147
|
+
np.divide(
|
|
148
|
+
counts[1:, 0],
|
|
149
|
+
gt_counts,
|
|
150
|
+
where=gt_counts > 1e-9,
|
|
151
|
+
out=missing_prediction_ratio,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# compute precision, recall, f1
|
|
155
|
+
tp_counts = counts.diagonal()[1:]
|
|
156
|
+
|
|
157
|
+
precision = np.zeros(n_labels, dtype=np.float64)
|
|
158
|
+
np.divide(tp_counts, pd_counts, where=pd_counts > 1e-9, out=precision)
|
|
159
|
+
|
|
160
|
+
recall = np.zeros_like(precision)
|
|
161
|
+
np.divide(tp_counts, gt_counts, where=gt_counts > 1e-9, out=recall)
|
|
162
|
+
|
|
163
|
+
f1_score = np.zeros_like(precision)
|
|
164
|
+
np.divide(
|
|
165
|
+
2 * (precision * recall),
|
|
166
|
+
(precision + recall),
|
|
167
|
+
where=(precision + recall) > 0,
|
|
168
|
+
out=f1_score,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# compute accuracy
|
|
172
|
+
tp_count = counts[1:, 1:].diagonal().sum()
|
|
173
|
+
background_count = counts[0, 0]
|
|
174
|
+
accuracy = (
|
|
175
|
+
(tp_count + background_count) / n_pixels if n_pixels > 0 else 0.0
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
precision,
|
|
180
|
+
recall,
|
|
181
|
+
f1_score,
|
|
182
|
+
accuracy,
|
|
183
|
+
ious,
|
|
184
|
+
hallucination_ratio,
|
|
185
|
+
missing_prediction_ratio,
|
|
186
|
+
)
|