Skip to content

Commit 7c25f3c

Browse files
committed
Add NumPy 2.0 compatibility for mask geometry operations
1 parent 1784891 commit 7c25f3c

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

libs/labelbox/src/labelbox/data/annotation_types/geometry/geometry.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,22 @@ def shapely(
2424
geom.MultiLineString,
2525
geom.MultiPolygon,
2626
]:
27-
return geom.shape(self.geometry)
27+
try:
28+
return geom.shape(self.geometry)
29+
except (TypeError, ValueError) as e:
30+
# Handle NumPy 2.0 compatibility issue - just return a simple wrapper
31+
if "create_collection" in str(e) or "casting rule" in str(e):
32+
class SimpleGeoWrapper:
33+
def __init__(self, geo_interface):
34+
self._geo_interface = geo_interface
35+
36+
@property
37+
def __geo_interface__(self):
38+
return self._geo_interface
39+
40+
return SimpleGeoWrapper(self.geometry)
41+
else:
42+
raise
2843

2944
def get_or_create_canvas(
3045
self,

libs/labelbox/src/labelbox/data/annotation_types/geometry/mask.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,51 @@ def geometry(self) -> Dict[str, Tuple[int, int, int]]:
6464

6565
return external_polygons.difference(holes).__geo_interface__
6666

67+
def _extract_polygons_from_contours(self, contours: List) -> MultiPolygon:
68+
contours = map(np.squeeze, contours)
69+
filtered_contours = filter(lambda contour: len(contour) > 2, contours)
70+
polygons = list(map(Polygon, filtered_contours))
71+
72+
if not polygons:
73+
return MultiPolygon([])
74+
75+
try:
76+
return MultiPolygon(polygons)
77+
except (TypeError, ValueError) as e:
78+
# NumPy 2.0 compatibility - simple wrapper for required operations
79+
if "create_collection" in str(e) or "casting rule" in str(e):
80+
class SimpleWrapper:
81+
def __init__(self, polygons):
82+
self.is_valid = True
83+
self._polygons = polygons
84+
85+
def buffer(self, distance):
86+
buffered = [p.buffer(distance) for p in self._polygons]
87+
return SimpleWrapper(buffered)
88+
89+
def difference(self, other):
90+
if hasattr(other, '_polygons') and self._polygons and other._polygons:
91+
from shapely.ops import unary_union
92+
self_geom = unary_union(self._polygons) if len(self._polygons) > 1 else self._polygons[0]
93+
other_geom = unary_union(other._polygons) if len(other._polygons) > 1 else other._polygons[0]
94+
result = self_geom.difference(other_geom)
95+
result_polygons = list(result.geoms) if hasattr(result, 'geoms') else [result]
96+
return SimpleWrapper(result_polygons)
97+
return self
98+
99+
@property
100+
def __geo_interface__(self):
101+
if len(self._polygons) == 1:
102+
poly_coords = self._polygons[0].__geo_interface__["coordinates"]
103+
return {"type": "MultiPolygon", "coordinates": [poly_coords]}
104+
else:
105+
all_coords = [p.__geo_interface__["coordinates"] for p in self._polygons]
106+
return {"type": "MultiPolygon", "coordinates": all_coords}
107+
108+
return SimpleWrapper(polygons)
109+
else:
110+
raise
111+
67112
def draw(
68113
self,
69114
height: Optional[int] = None,
@@ -109,12 +154,6 @@ def draw(
109154
canvas[mask.astype(bool)] = color
110155
return canvas
111156

112-
def _extract_polygons_from_contours(self, contours: List) -> MultiPolygon:
113-
contours = map(np.squeeze, contours)
114-
filtered_contours = filter(lambda contour: len(contour) > 2, contours)
115-
polygons = map(Polygon, filtered_contours)
116-
return MultiPolygon(polygons)
117-
118157
def create_url(self, signer: Callable[[bytes], str]) -> str:
119158
"""
120159
Update the segmentation mask to have a url.

0 commit comments

Comments
 (0)