Skip to content

Commit be88bf1

Browse files
author
Matt Sokoloff
committed
update drawing logic
1 parent e1d29db commit be88bf1

File tree

9 files changed

+56
-30
lines changed

9 files changed

+56
-30
lines changed

labelbox/data/annotation_types/geometry/geometry.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Any, Optional, Union
1+
from typing import Dict, Any, Optional, Union, Tuple
22

33
import geojson
44
import numpy as np
@@ -23,7 +23,12 @@ def shapely(
2323
geom.MultiLineString, geom.MultiPolygon]:
2424
return geom.shape(self.geometry)
2525

26-
def raster(self, *args, **kwargs) -> np.ndarray:
26+
def raster(self,
27+
height: Optional[int] = None,
28+
width: Optional[int] = None,
29+
canvas: Optional[np.ndarray] = None,
30+
color: Optional[Union[int, Tuple[int,int,int]]] = None,
31+
thickness: Optional[int] = 1) -> np.ndarray:
2732
raise NotImplementedError("Subclass must override this")
2833

2934
def get_or_create_canvas(self, height: Optional[int], width: Optional[int],

labelbox/data/annotation_types/geometry/line.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ def geometry(self) -> geojson.MultiLineString:
1717
[[[point.x, point.y] for point in self.points]])
1818

1919
def raster(self,
20-
height: Optional[int] = None,
20+
height: Optional[int] = None,
2121
width: Optional[int] = None,
22-
thickness: int = 1,
23-
color: Union[Tuple, int] = (255, 255, 255),
24-
canvas: Optional[np.ndarray] = None) -> np.ndarray:
22+
canvas: Optional[np.ndarray] = None,
23+
color: Union[int, Tuple[int,int,int]] = (255, 255, 255),
24+
thickness: int = 5) -> np.ndarray:
2525
"""
2626
Draw the line onto a 3d mask
2727
Args:

labelbox/data/annotation_types/geometry/mask.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Mask(Geometry):
1818

1919
@property
2020
def geometry(self):
21-
mask = self.raster(binary=True)
21+
mask = self.raster(color = 1)
2222
polygons = (
2323
shape(shp)
2424
for shp, val in shapes(mask, mask=None)
@@ -29,28 +29,48 @@ def geometry(self):
2929
def raster(self,
3030
height: Optional[int] = None,
3131
width: Optional[int] = None,
32-
binary=False) -> np.ndarray:
32+
canvas: Optional[np.ndarray] = None,
33+
color: Optional[Union[int, Tuple[int,int,int]]] = None,
34+
thickness = None
35+
) -> np.ndarray:
3336
"""
37+
# TODO: Optionally use the color. a color of 1 will result in a binary canvas
38+
39+
3440
Removes all pixels from the segmentation mask that do not equal self.color
3541
3642
Args:
37-
height:
38-
43+
height (int): Optionally resize mask height before drawing.
44+
width (int): Optionally resize mask width before drawing.
45+
canvas (np.ndarray): Optionall provide a canvas to draw on
46+
color (Union[int, Tuple[int,int,int]]): Color to draw the canvas.
47+
Defaults to using the encoded color in the mask.
48+
int will return the mask as a 1d array
49+
tuple[int,int,int] will return the mask as a 3d array
50+
thickness (None): Unused, exists for a consistent interface.
3951
Returns:
4052
np.ndarray representing only this object
53+
as opposed to the mask that this object references which might have multiple objects determined by colors
4154
"""
55+
4256
mask = self.mask.value
4357
mask = np.alltrue(mask == self.color, axis=2).astype(np.uint8)
58+
4459
if height is not None or width is not None:
4560
mask = cv2.resize(mask,
4661
(width or mask.shape[1], height or mask.shape[0]))
47-
if binary:
48-
return mask
49-
else:
50-
color_image = np.zeros((mask.shape[0], mask.shape[1], 3),
51-
dtype=np.uint8)
52-
color_image[mask.astype(np.bool)] = self.color
53-
return color_image
62+
63+
dims = [mask.shape[0], mask.shape[1]]
64+
color = color or self.color
65+
if isinstance(color, (tuple, list)):
66+
dims = dims + [len(color)]
67+
68+
canvas = canvas if canvas is not None else np.zeros(tuple(dims), dtype=np.uint8)
69+
canvas[mask.astype(np.bool)] = color
70+
return canvas
71+
72+
73+
5474

5575
def create_url(self, signer: Callable[[bytes], str]) -> str:
5676
"""

labelbox/data/annotation_types/geometry/point.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ def geometry(self) -> geojson.Point:
1818
def raster(self,
1919
height: Optional[int] = None,
2020
width: Optional[int] = None,
21-
thickness: int = 1,
22-
color: Union[Tuple, int] = (255, 255, 255),
23-
canvas: Optional[np.ndarray] = None) -> np.ndarray:
21+
canvas: Optional[np.ndarray] = None,
22+
color: Union[int, Tuple[int,int,int]] = (255, 255, 255),
23+
thickness: int = 10
24+
) -> np.ndarray:
2425
"""
2526
Draw the point onto a 3d mask
2627
Args:

labelbox/data/annotation_types/geometry/polygon.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ def geometry(self) -> geojson.MultiPolygon:
2121
def raster(self,
2222
height: Optional[int] = None,
2323
width: Optional[int] = None,
24-
color: Union[int, Tuple] = (255, 255, 255),
25-
thickness: int = -1,
26-
canvas: Optional[np.ndarray] = None) -> np.ndarray:
24+
canvas: Optional[np.ndarray] = None,
25+
color: Union[int, Tuple[int,int,int]] = (255, 255, 255),
26+
thickness: int = -1) -> np.ndarray:
2727
"""
2828
Draw the polygon onto a 3d mask
2929
Args:

labelbox/data/annotation_types/geometry/rectangle.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ def geometry(self) -> geojson.geometry.Geometry:
3131
def raster(self,
3232
height: Optional[int] = None,
3333
width: Optional[int] = None,
34-
color: Union[int, Tuple] = (255, 255, 255),
35-
thickness: int = -1,
36-
canvas: Optional[np.ndarray] = None) -> np.ndarray:
34+
canvas: Optional[np.ndarray] = None,
35+
color: Union[int, Tuple[int,int,int]] = (255, 255, 255),
36+
thickness: int = -1) -> np.ndarray:
3737
"""
3838
Draw the rectangle onto a 3d mask
3939
Args:

labelbox/data/metrics/iou.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ def mask_miou(ground_truths: List[ObjectAnnotation],
129129
float representing the iou score for the masks
130130
"""
131131
prediction_np = np.max(
132-
[pred.value.raster(binary=True) for pred in predictions], axis=0)
132+
[pred.value.raster(color=1) for pred in predictions], axis=0)
133133
ground_truth_np = np.max([
134-
ground_truth.value.raster(binary=True) for ground_truth in ground_truths
134+
ground_truth.value.raster(color=1) for ground_truth in ground_truths
135135
],
136136
axis=0)
137137
if prediction_np.shape != ground_truth_np.shape:

tests/data/annotation_types/geometry/test_line.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ def test_line():
1919
expected['coordinates'] = tuple([tuple([tuple(x) for x in points])])
2020
assert line.shapely.__geo_interface__ == expected
2121

22-
raster = line.raster(height=32, width=32)
22+
raster = line.raster(height=32, width=32, thickness = 1)
2323
assert (cv2.imread("tests/data/assets/line.png") == raster).all()

tests/data/annotation_types/geometry/test_point.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ def test_point():
1818
expected['coordinates'] = tuple(expected['coordinates'])
1919
assert point.shapely.__geo_interface__ == expected
2020

21-
raster = point.raster(height=32, width=32)
21+
raster = point.raster(height=32, width=32, thickness = 1)
2222
assert (cv2.imread("tests/data/assets/point.png") == raster).all()

0 commit comments

Comments
 (0)