-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathannotations.py
171 lines (141 loc) · 5.89 KB
/
annotations.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from typing import List, Union
from torch import Tensor
import torch
from moonwatcher.utils.data import DataType
from moonwatcher.base.base import MoonwatcherObject
from moonwatcher.utils.data_storage import _prediction_name
class Annotation:
def __init__(self, datapoint_number):
self.datapoint_number = datapoint_number
class BoundingBoxes(Annotation):
def __init__(
self,
datapoint_id: int,
boxes_xyxy: Tensor,
labels: Tensor,
):
"""
Initializes a BoundingBoxes object
:param datapoint_id: The unique identifier for the data point.
:param boxes_xyxy: A tensor of shape (num_boxes, 4) representing the bounding box coordinates.
:param labels: An integer tensor of shape (num_boxes) representing labels for each bounding box.
:return:
"""
if not isinstance(boxes_xyxy, Tensor):
raise TypeError("bounding boxes must be a Tensor of shape (num_boxes, 4)")
if not isinstance(labels, Tensor):
raise TypeError("labels must be an int Tensor of shape (num_boxes)")
super().__init__(datapoint_id)
self.boxes_xyxy = boxes_xyxy
self.labels = labels
def to_dict(self):
return {
"boxes": self.boxes_xyxy,
"labels": self.labels,
}
class PredictedBoundingBoxes(BoundingBoxes):
def __init__(
self,
datapoint_number: int,
boxes_xyxy: Tensor,
labels: Tensor,
scores: Tensor,
):
"""
Initializes a PredictedBoundingBoxes object
:param datapoint_number: The unique identifier for the data point.
:param boxes_xyxy: A tensor of shape (num_boxes, 4) representing bounding box coordinates.
:param labels: An integer tensor of shape (num_boxes) representing labels for each bounding box.
:param scores: A float tensor of shape (num_boxes) representing the confidence score for each bounding box.
:return:
"""
if not isinstance(scores, Tensor):
raise TypeError("scores must be a float Tensor of shape (num_boxes)")
super().__init__(datapoint_number, boxes_xyxy, labels)
self.scores = scores
def to_dict(self):
return {
"boxes": self.boxes_xyxy,
"scores": self.scores,
"labels": self.labels,
}
class Labels(Annotation):
def __init__(self, datapoint_number: int, labels: Tensor):
"""
Initialize a Labels object
:param datapoint_number: The unique identifier for the data point.
:param labels: A 1-dimensional integer tensor of shape (1) representing the label.
:return:
"""
# TODO Check if torchmetrics accepts labels both as torch.tensor([1]) and torch.tensor(1)
if (
not isinstance(labels, Tensor)
or (labels.shape != (1,) and labels.shape != ())
or labels.dtype not in (torch.int8, torch.int16, torch.int32, torch.int64)
):
raise TypeError("labels must be a 1-dimensional int Tensor")
super().__init__(datapoint_number)
self.labels = labels
class PredictedLabels(Labels):
def __init__(self, datapoint_number: int, labels: Tensor, scores: Tensor):
"""
Initialize a Labels object
:param datapoint_number: The unique identifier for the data point.
:param labels: A 1-dimensional integer tensor of shape (1) representing the label.
:param scores: A float tensor of shape (num_classes) representing the confidence scores for each class.
:return:
"""
# TODO Check if torchmetrics accepts labels both as torch.tensor([1]) and torch.tensor(1)
if (
not isinstance(scores, Tensor)
or (scores.shape != (1,) and scores.shape != ())
or scores.dtype not in (torch.float16, torch.float32, torch.float64)
):
raise TypeError("scores must be a 1-dimensional float Tensor")
super().__init__(datapoint_number, labels)
self.scores = scores
class Annotations:
def __init__(self, annotations: List[Annotation] = None):
self.annotations = [] if annotations is None else annotations
self.datapoint_number_to_annotation_index = {}
for annotation_index, annotation in enumerate(self.annotations):
self.datapoint_number_to_annotation_index[
annotation.datapoint_number
] = annotation_index
def add(self, annotation: Annotation):
self.annotations.append(annotation)
self.datapoint_number_to_annotation_index[annotation.datapoint_number] = (
len(self.annotations) - 1
)
def get(self, datapoint_number):
return self.annotations[
self.datapoint_number_to_annotation_index[datapoint_number]
]
def get_datapoint_ids(self):
return list(self.datapoint_number_to_annotation_index.keys())
def __getitem__(self, datapoint_number):
return self.get(datapoint_number=datapoint_number)
def __len__(self):
return len(self.annotations)
def __iter__(self):
return iter(self.annotations)
class Predictions(Annotations, MoonwatcherObject):
def __init__(
self,
dataset,
model,
predictions: List[
Union[PredictedBoundingBoxes, BoundingBoxes, PredictedLabels, Labels]
] = None,
):
super().__init__(annotations=predictions)
name = _prediction_name(model_name=model.name, dataset_name=dataset.name)
MoonwatcherObject.__init__(self, name=name, datatype=DataType.PREDICTIONS)
class GroundTruths(Annotations, MoonwatcherObject):
def __init__(
self, dataset, groundtruths: List[Union[BoundingBoxes, Labels]] = None
):
super().__init__(annotations=groundtruths)
MoonwatcherObject.__init__(
self, name=dataset.name, datatype=DataType.GROUNDTRUTHS
)