Skip to content

Commit 55360e1

Browse files
author
Matt Sokoloff
committed
annotation type mal notebook
1 parent d9876b1 commit 55360e1

File tree

14 files changed

+709
-226
lines changed

14 files changed

+709
-226
lines changed

examples/annotation_types/annotation_type_basics.ipynb

Lines changed: 59 additions & 60 deletions
Large diffs are not rendered by default.

examples/annotation_types/converters.ipynb

Lines changed: 42 additions & 34 deletions
Large diffs are not rendered by default.

examples/annotation_types/label_containers.ipynb

Lines changed: 81 additions & 73 deletions
Large diffs are not rendered by default.

examples/annotation_types/mal_with_annotation_types.ipynb

Lines changed: 452 additions & 0 deletions
Large diffs are not rendered by default.

labelbox/data/annotation_types/collection.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from labelbox.data.ontology import get_classifications, get_tools
12
import logging
23
from concurrent.futures import ThreadPoolExecutor, as_completed
34
from typing import Callable, Generator, Iterable, Union
@@ -110,6 +111,16 @@ def add_url_to_data(self, signer, max_concurrency=20) -> "LabelList":
110111
...
111112
return self
112113

114+
def get_ontology(self) -> ontology.OntologyBuilder:
115+
classifications = []
116+
tools = []
117+
for label in self._data:
118+
tools = get_tools(label.object_annotations(), tools)
119+
classifications = get_classifications(
120+
label.classification_annotations(), classifications)
121+
return ontology.OntologyBuilder(tools=tools,
122+
classifications=classifications)
123+
113124
def _ensure_unique_external_ids(self) -> None:
114125
external_ids = set()
115126
for label in self._data:
@@ -122,6 +133,9 @@ def _ensure_unique_external_ids(self) -> None:
122133
)
123134
external_ids.add(label.data.external_id)
124135

136+
def append(self, label: Label):
137+
self._data.append(label)
138+
125139
def __iter__(self) -> "LabelList":
126140
self._index = 0
127141
return self

labelbox/data/annotation_types/data/raster.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ class RasterData(BaseData):
2020
url: Optional[str] = None
2121
arr: Optional[TypedArray[Literal['uint8']]] = None
2222

23+
@classmethod
24+
def from_2D_arr(cls, arr: TypedArray[Literal['uint8']], **kwargs):
25+
if len(arr.shape):
26+
raise ValueError(
27+
f"Found array with shape {arr.shape}. Expected two dimensions ([W,H])"
28+
)
29+
arr = np.stack((arr,) * 3, axis=-1)
30+
return cls(arr=arr, **kwargs)
31+
2332
def bytes_to_np(self, image_bytes: bytes) -> np.ndarray:
2433
"""
2534
Converts image bytes to a numpy array
@@ -38,9 +47,9 @@ def np_to_bytes(self, arr: np.ndarray) -> bytes:
3847
Returns:
3948
png encoded bytes
4049
"""
41-
if len(arr.shape) not in [2, 3]:
42-
raise ValueError("unsupported image format")
43-
50+
if len(arr.shape) != 3:
51+
raise ValueError("unsupported image format. Must be 3D ([H,W,C])."
52+
"Use RasterData.from_2D_arr to construct from 2D")
4453
if arr.dtype != np.uint8:
4554
raise TypeError(f"image data type must be uint8. Found {arr.dtype}")
4655

@@ -125,12 +134,19 @@ def validate_args(cls, values):
125134
raise TypeError(
126135
"Numpy array representing segmentation mask must be np.uint8"
127136
)
128-
elif len(arr.shape) not in [2, 3]:
129-
raise TypeError(
130-
f"Numpy array must have 2 or 3 dims. Found shape {arr.shape}"
131-
)
137+
elif len(arr.shape) != 3:
138+
raise ValueError(
139+
"unsupported image format. Must be 3D ([H,W,C])."
140+
"Use RasterData.from_2D_arr to construct from 2D")
132141
return values
133142

143+
def __repr__(self) -> str:
144+
symbol_or_none = lambda data: '...' if data is not None else None
145+
return f"RasterData(im_bytes={symbol_or_none(self.im_bytes)}," \
146+
f"file_path={self.file_path}," \
147+
f"url={self.url}," \
148+
f"arr={symbol_or_none(self.arr)})"
149+
134150
class Config:
135151
# Required for sharing references
136152
copy_on_model_validation = False

labelbox/data/annotation_types/data/text.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ def validate_date(cls, values):
8282
"One of `file_path`, `text`, `uid`, or `url` required.")
8383
return values
8484

85+
def __repr__(self) -> str:
86+
return f"TextData(file_path={self.file_path}," \
87+
f"text={self.text[:30] + '...' if self.text is not None else None}," \
88+
f"url={self.url})"
89+
8590
class config:
8691
# Required for discriminating between data types
8792
extra = 'forbid'

labelbox/data/annotation_types/data/video.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ def validate_data(cls, values):
156156
"One of `file_path`, `frames`, `uid`, or `url` required.")
157157
return values
158158

159+
def __repr__(self) -> str:
160+
return f"TextData(file_path={self.file_path}," \
161+
f"frames={'...' if self.frames is not None else None}," \
162+
f"url={self.url})"
163+
159164
class Config:
160165
# Required for discriminating between data types
161166
extra = 'forbid'

labelbox/data/annotation_types/geometry/line.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ def raster(self,
2020
height: int,
2121
width: int,
2222
thickness=1,
23-
color=255) -> np.ndarray:
23+
color=(255, 255, 255)) -> np.ndarray:
2424
"""
25-
Draw the line onto a 2d mask
25+
Draw the line onto a 3d mask
2626
2727
Args:
2828
height (int): height of the mask
@@ -32,8 +32,7 @@ def raster(self,
3232
Returns:
3333
numpy array representing the mask with the line drawn on it.
3434
"""
35-
36-
canvas = np.zeros((height, width), dtype=np.uint8)
35+
canvas = np.zeros((height, width, 3), dtype=np.uint8)
3736
pts = np.array(self.geometry['coordinates']).astype(np.int32)
3837
return cv2.polylines(canvas,
3938
pts,

labelbox/data/annotation_types/geometry/mask.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Mask(Geometry):
1414
# Raster data can be shared across multiple masks... or not
1515
mask: RasterData
1616
# RGB or Grayscale
17-
color: Union[int, Tuple[int, int, int]]
17+
color: Tuple[int, int, int]
1818

1919
@property
2020
def geometry(self):
@@ -40,17 +40,12 @@ def raster(self,
4040
np.ndarray representing only this object
4141
"""
4242
mask = self.mask.data
43-
if len(mask.shape) == 2:
44-
mask = np.expand_dims(mask, axis=-1)
4543
mask = np.alltrue(mask == self.color, axis=2).astype(np.uint8)
4644
if height is not None or width is not None:
4745
mask = cv2.resize(mask,
4846
(width or mask.shape[1], height or mask.shape[0]))
49-
5047
if binary:
5148
return mask
52-
elif isinstance(self.color, int):
53-
return mask * self.color
5449
else:
5550
color_image = np.zeros((mask.shape[0], mask.shape[1], 3),
5651
dtype=np.uint8)

0 commit comments

Comments
 (0)