Skip to content

Commit a0eaa08

Browse files
authored
add support for image bytes as a source for raster video mask import (#1277)
2 parents 748b562 + ef545f1 commit a0eaa08

File tree

6 files changed

+35
-7
lines changed

6 files changed

+35
-7
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
build-image:
3-
docker build -t local/labelbox-python:test .
3+
docker build --quiet -t local/labelbox-python:test .
44

55
test-local: build-image
66

labelbox/data/annotation_types/video.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from enum import Enum
22
from typing import List, Optional, Tuple
33

4-
from pydantic import BaseModel, validator
4+
from pydantic import BaseModel, validator, root_validator
55
from labelbox.data.annotation_types.annotation import ClassificationAnnotation, ObjectAnnotation
66

77
from labelbox.data.annotation_types.annotation import ClassificationAnnotation, ObjectAnnotation
@@ -81,14 +81,24 @@ class DICOMObjectAnnotation(VideoObjectAnnotation):
8181
keyframe (bool): Whether or not this annotation was a human generated or interpolated annotation
8282
segment_id (Optional[Int]): Index of video segment this annotation belongs to
8383
classifications (List[ClassificationAnnotation]) = []
84-
extra (Dict[str, Any])
84+
extra (Dict[str, Any])
8585
"""
8686
group_key: GroupKey
8787

8888

8989
class MaskFrame(_CamelCaseMixin, BaseModel):
9090
index: int
91-
instance_uri: str
91+
instance_uri: Optional[str] = None
92+
im_bytes: Optional[bytes] = None
93+
94+
@root_validator()
95+
def validate_args(cls, values):
96+
im_bytes = values.get("im_bytes")
97+
instance_uri = values.get("instance_uri")
98+
99+
if im_bytes == instance_uri == None:
100+
raise ValueError("One of `instance_uri`, `im_bytes` required.")
101+
return values
92102

93103
@validator("instance_uri")
94104
def validate_uri(cls, v):

labelbox/data/serialization/ndjson/objects.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,13 +501,23 @@ class NDVideoMasks(NDJsonBase, ConfidenceMixin):
501501
masks: NDVideoMasksFramesInstances
502502

503503
def to_common(self) -> VideoMaskAnnotation:
504+
for mask_frame in self.masks.frames:
505+
if mask_frame.im_bytes:
506+
mask_frame.im_bytes = base64.b64decode(
507+
mask_frame.im_bytes.encode('utf-8'))
508+
504509
return VideoMaskAnnotation(
505510
frames=self.masks.frames,
506511
instances=self.masks.instances,
507512
)
508513

509514
@classmethod
510515
def from_common(cls, annotation, data):
516+
for mask_frame in annotation.frames:
517+
if mask_frame.im_bytes:
518+
mask_frame.im_bytes = base64.b64encode(
519+
mask_frame.im_bytes).decode('utf-8')
520+
511521
return cls(
512522
data_row=DataRow(id=data.uid, global_key=data.global_key),
513523
masks=NDVideoMasksFramesInstances(frames=annotation.frames,

tests/data/annotation_types/test_video.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ def test_mask_frame():
66
instance_uri="http://path/to/frame.png")
77
assert mask_frame.dict(by_alias=True) == {
88
'index': 1,
9+
'imBytes': None,
910
'instanceURI': 'http://path/to/frame.png'
1011
}
1112

tests/data/serialization/ndjson/test_dicom.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from copy import copy
22
import pytest
3+
import base64
34
import labelbox.types as lb_types
45
from labelbox.data.serialization import NDJsonConverter
56
from labelbox.data.serialization.ndjson.objects import NDDicomSegments, NDDicomSegment, NDDicomLine
@@ -89,9 +90,11 @@
8990
'masks': {
9091
'frames': [{
9192
'index': 1,
93+
'imBytes': None,
9294
'instanceURI': instance_uri_1
9395
}, {
9496
'index': 5,
97+
'imBytes': None,
9598
'instanceURI': instance_uri_5
9699
}],
97100
'instances': [
@@ -157,9 +160,12 @@
157160
video_mask_label_with_global_key
158161
]
159162
ndjsons = [
160-
polyline_annotation_ndjson, polyline_annotation_ndjson_with_global_key,
161-
dicom_mask_annotation_ndjson, dicom_mask_annotation_ndjson_with_global_key,
162-
video_mask_annotation_ndjson, video_mask_annotation_ndjson_with_global_key
163+
polyline_annotation_ndjson,
164+
polyline_annotation_ndjson_with_global_key,
165+
dicom_mask_annotation_ndjson,
166+
dicom_mask_annotation_ndjson_with_global_key,
167+
video_mask_annotation_ndjson,
168+
video_mask_annotation_ndjson_with_global_key,
163169
]
164170
labels_ndjsons = list(zip(labels, ndjsons))
165171

tests/integration/annotation_import/test_data_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def get_annotation_comparison_dicts_from_labels(labels):
6565
if 'masks' in annotation:
6666
for frame in annotation['masks']['frames']:
6767
frame.pop('instanceURI')
68+
frame.pop('imBytes')
6869
for instance in annotation['masks']['instances']:
6970
instance.pop('colorRGB')
7071
return labels_ndjson

0 commit comments

Comments
 (0)