Skip to content

Commit df40f5e

Browse files
Merge pull request #627 from Labelbox/kkim/AL-2954
[AL-2954] Add video point and polyline support in annotation serialization
2 parents 23c9d6e + 8f6d530 commit df40f5e

File tree

4 files changed

+153
-13
lines changed

4 files changed

+153
-13
lines changed

labelbox/data/serialization/ndjson/label.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ def _generate_annotations(
5151
if isinstance(annotation, NDSegments):
5252
annots.extend(
5353
NDSegments.to_common(annotation, annotation.schema_id))
54-
5554
elif isinstance(annotation, NDObjectType.__args__):
5655
annots.append(NDObject.to_common(annotation))
5756
elif isinstance(annotation, NDClassificationType.__args__):
@@ -62,12 +61,15 @@ def _generate_annotations(
6261
else:
6362
raise TypeError(
6463
f"Unsupported annotation. {type(annotation)}")
65-
data = self._infer_media_type(annotations)(uid=data_row_id)
64+
data = self._infer_media_type(annots)(uid=data_row_id)
6665
yield Label(annotations=annots, data=data)
6766

6867
def _infer_media_type(
69-
self, annotations: List[Union[NDObjectType, NDClassificationType]]
70-
) -> Union[TextEntity, TextData, ImageData]:
68+
self, annotations: List[Union[TextEntity, VideoClassificationAnnotation,
69+
VideoObjectAnnotation, ObjectAnnotation,
70+
ClassificationAnnotation, ScalarMetric,
71+
ConfusionMatrixMetric]]
72+
) -> Union[TextData, VideoData, ImageData]:
7173
types = {type(annotation) for annotation in annotations}
7274
if TextEntity in types:
7375
return TextData
@@ -100,7 +102,6 @@ def _create_video_annotations(
100102
for annotation_group in video_annotations.values():
101103
consecutive_frames = cls._get_consecutive_frames(
102104
sorted([annotation.frame for annotation in annotation_group]))
103-
104105
if isinstance(annotation_group[0], VideoClassificationAnnotation):
105106
annotation = annotation_group[0]
106107
frames_data = []

labelbox/data/serialization/ndjson/objects.py

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ def from_common(cls, point: Point,
6060
classifications=classifications)
6161

6262

63+
class NDFramePoint(VideoSupported):
64+
point: _Point
65+
66+
def to_common(self, feature_schema_id: Cuid) -> VideoObjectAnnotation:
67+
return VideoObjectAnnotation(frame=self.frame,
68+
keyframe=True,
69+
feature_schema_id=feature_schema_id,
70+
value=Point(x=self.point.x,
71+
y=self.point.y))
72+
73+
@classmethod
74+
def from_common(cls, frame: int, point: Point):
75+
return cls(frame=frame, point=_Point(x=point.x, y=point.y))
76+
77+
6378
class NDLine(NDBaseObject):
6479
line: List[_Point]
6580

@@ -81,6 +96,25 @@ def from_common(cls, line: Line,
8196
classifications=classifications)
8297

8398

99+
class NDFrameLine(VideoSupported):
100+
line: List[_Point]
101+
102+
def to_common(self, feature_schema_id: Cuid) -> VideoObjectAnnotation:
103+
return VideoObjectAnnotation(
104+
frame=self.frame,
105+
keyframe=True,
106+
feature_schema_id=feature_schema_id,
107+
value=Line(points=[Point(x=pt.x, y=pt.y) for pt in self.line]))
108+
109+
@classmethod
110+
def from_common(cls, frame: int, line: Line):
111+
return cls(frame=frame,
112+
line=[{
113+
'x': pt.x,
114+
'y': pt.y
115+
} for pt in line.points])
116+
117+
84118
class NDPolygon(NDBaseObject):
85119
polygon: List[_Point]
86120

@@ -147,18 +181,29 @@ def from_common(cls, frame: int, rectangle: Rectangle):
147181

148182

149183
class NDSegment(BaseModel):
150-
keyframes: List[NDFrameRectangle]
184+
keyframes: List[Union[NDFrameRectangle, NDFramePoint, NDFrameLine]]
151185

152186
@staticmethod
153187
def lookup_segment_object_type(segment: List) -> "NDFrameObjectType":
154188
"""Used for determining which object type the annotation contains
155189
returns the object type"""
156-
result = {Rectangle: NDFrameRectangle}.get(type(segment[0].value))
190+
result = {
191+
Rectangle: NDFrameRectangle,
192+
Point: NDFramePoint,
193+
Line: NDFrameLine,
194+
}.get(type(segment[0].value))
157195
return result
158196

159-
def to_common(self, feature_schema_id: Cuid):
197+
@staticmethod
198+
def segment_with_uuid(keyframe: Union[NDFrameRectangle, NDFramePoint,
199+
NDFrameLine], uuid: str):
200+
keyframe.extra = {'uuid': uuid}
201+
return keyframe
202+
203+
def to_common(self, feature_schema_id: Cuid, uuid: str):
160204
return [
161-
keyframe.to_common(feature_schema_id) for keyframe in self.keyframes
205+
self.segment_with_uuid(keyframe.to_common(feature_schema_id), uuid)
206+
for keyframe in self.keyframes
162207
]
163208

164209
@classmethod
@@ -178,7 +223,8 @@ class NDSegments(NDBaseObject):
178223
def to_common(self, feature_schema_id: Cuid):
179224
result = []
180225
for segment in self.segments:
181-
result.extend(NDSegment.to_common(segment, feature_schema_id))
226+
result.extend(
227+
NDSegment.to_common(segment, feature_schema_id, self.uuid))
182228
return result
183229

184230
@classmethod
@@ -330,4 +376,4 @@ def lookup_object(
330376
NDObjectType = Union[NDLine, NDPolygon, NDPoint, NDRectangle, NDMask,
331377
NDTextEntity]
332378

333-
NDFrameObjectType = NDFrameRectangle
379+
NDFrameObjectType = NDFrameRectangle, NDFramePoint, NDFrameLine
Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,94 @@
1-
[{"answer": {"schemaId": "ckrb1sfl8099g0y91cxbd5ftb"}, "schemaId": "ckrb1sfjx099a0y914hl319ie", "dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"}, "uuid": "f6879f59-d2b5-49c2-aceb-d9e8dc478673", "frames": [{"start": 30, "end": 35}, {"start": 50, "end": 51}]}, {"answer": [{"schemaId": "ckrb1sfl8099e0y919v260awv"}], "schemaId": "ckrb1sfkn099c0y910wbo0p1a", "dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"}, "uuid": "d009925d-91a3-4f67-abd9-753453f5a584", "frames": [{"start": 0, "end": 5}]}, {"answer": "a value", "schemaId": "ckrb1sfkn099c0y910wbo0p1a", "dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"}, "uuid": "d009925d-91a3-4f67-abd9-753453f5a584"}]
1+
[
2+
{
3+
"answer": {"schemaId": "ckrb1sfl8099g0y91cxbd5ftb"},
4+
"schemaId": "ckrb1sfjx099a0y914hl319ie",
5+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
6+
"uuid": "f6879f59-d2b5-49c2-aceb-d9e8dc478673",
7+
"frames": [{"start": 30, "end": 35}, {"start": 50, "end": 51}]
8+
},
9+
{
10+
"answer": [{"schemaId": "ckrb1sfl8099e0y919v260awv"}],
11+
"schemaId": "ckrb1sfkn099c0y910wbo0p1a",
12+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
13+
"uuid": "d009925d-91a3-4f67-abd9-753453f5a584",
14+
"frames": [{"start": 0, "end": 5}]
15+
},
16+
{
17+
"answer": "a value",
18+
"schemaId": "ckrb1sfkn099c0y910wbo0p1a",
19+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
20+
"uuid": "d009925d-91a3-4f67-abd9-753453f5a584"
21+
},
22+
{
23+
"classifications": [],
24+
"schemaId": "cl5islwg200gfci6g0oitaypu",
25+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
26+
"uuid": "6f7c835a-0139-4896-b73f-66a6baa89e94",
27+
"segments": [
28+
{
29+
"keyframes": [
30+
{
31+
"frame": 1,
32+
"line": [{"x": 10.0, "y": 10.0}, {"x": 100.0, "y": 100.0}, {"x": 50.0, "y": 30.0}]
33+
}
34+
]
35+
},
36+
{
37+
"keyframes": [
38+
{
39+
"frame": 5,
40+
"line": [{"x": 100.0, "y": 10.0}, {"x": 50.0, "y": 100.0}, {"x": 50.0, "y": 30.0}]
41+
}
42+
]
43+
}
44+
]
45+
},
46+
{
47+
"classifications": [],
48+
"schemaId": "cl5it7ktp00i5ci6gf80b1ysd",
49+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
50+
"uuid": "f963be22-227b-4efe-9be4-2738ed822216",
51+
"segments": [
52+
{
53+
"keyframes": [
54+
{
55+
"frame": 1,
56+
"point": {"x": 10.0, "y": 10.0}
57+
}
58+
]
59+
},
60+
{
61+
"keyframes": [
62+
{
63+
"frame": 5,
64+
"point": {"x": 50.0, "y": 50.0}
65+
}
66+
]
67+
}
68+
]
69+
},
70+
{
71+
"classifications": [],
72+
"schemaId": "cl5iw0roz00lwci6g5jni62vs",
73+
"dataRow": {"id": "ckrb1sf1i1g7i0ybcdc6oc8ct"},
74+
"uuid": "13b2ee0e-2355-4336-8b83-d74d09e3b1e7",
75+
"segments": [
76+
{
77+
"keyframes": [
78+
{
79+
"frame": 1,
80+
"bbox": {"top": 10.0, "left": 5.0, "height": 100.0, "width": 150.0}
81+
}
82+
]
83+
},
84+
{
85+
"keyframes": [
86+
{
87+
"frame": 5,
88+
"bbox": {"top": 300.0, "left": 200.0, "height": 400.0, "width": 150.0}
89+
}
90+
]
91+
}
92+
]
93+
}
94+
]

tests/data/serialization/ndjson/test_video.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ def test_video():
99

1010
res = NDJsonConverter.deserialize(data).as_list()
1111
res = list(NDJsonConverter.serialize(res))
12-
assert res == [data[2], data[0], data[1]]
12+
assert res == [data[2], data[0], data[1], data[3], data[4], data[5]]

0 commit comments

Comments
 (0)