@@ -37,6 +37,7 @@ class Mask(Geometry):
3737
3838 @property
3939 def geometry (self ) -> Dict [str , Tuple [int , int , int ]]:
40+ # Extract mask contours and build geometry
4041 mask = self .draw (color = 1 )
4142 contours , hierarchy = cv2 .findContours (
4243 image = mask , mode = cv2 .RETR_TREE , method = cv2 .CHAIN_APPROX_NONE
@@ -62,7 +63,44 @@ def geometry(self) -> Dict[str, Tuple[int, int, int]]:
6263 if not holes .is_valid :
6364 holes = holes .buffer (0 )
6465
65- return external_polygons .difference (holes ).__geo_interface__
66+ # Get geometry result
67+ result_geometry = external_polygons .difference (holes )
68+
69+ # Ensure consistent MultiPolygon format across shapely versions
70+ if (
71+ hasattr (result_geometry , "geom_type" )
72+ and result_geometry .geom_type == "Polygon"
73+ ):
74+ from shapely .geometry import MultiPolygon
75+
76+ result_geometry = MultiPolygon ([result_geometry ])
77+
78+ geometry_dict = result_geometry .__geo_interface__
79+ return self ._normalize_coordinates (geometry_dict )
80+
81+ def _normalize_coordinates (self , geometry_dict ):
82+ """Normalize coordinates to ensure consistent tuple format across shapely versions"""
83+
84+ def ensure_tuple_coords (obj ):
85+ """Recursively ensure coordinate pairs are tuples"""
86+ if isinstance (obj , (list , tuple )):
87+ # Check if this is a coordinate pair [x, y]
88+ if len (obj ) == 2 and all (
89+ isinstance (x , (int , float )) for x in obj
90+ ):
91+ return (float (obj [0 ]), float (obj [1 ]))
92+ else :
93+ # Recursively process nested structures, preserving list/tuple types
94+ if isinstance (obj , list ):
95+ return [ensure_tuple_coords (item ) for item in obj ]
96+ else :
97+ return tuple (ensure_tuple_coords (item ) for item in obj )
98+ return obj
99+
100+ result = geometry_dict .copy ()
101+ if "coordinates" in result :
102+ result ["coordinates" ] = ensure_tuple_coords (result ["coordinates" ])
103+ return result
66104
67105 def draw (
68106 self ,
0 commit comments