@@ -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,38 @@ 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 hasattr (result_geometry , 'geom_type' ) and result_geometry .geom_type == 'Polygon' :
71+ from shapely .geometry import MultiPolygon
72+ result_geometry = MultiPolygon ([result_geometry ])
73+
74+ geometry_dict = result_geometry .__geo_interface__
75+ return self ._normalize_coordinates (geometry_dict )
76+
77+ def _normalize_coordinates (self , geometry_dict ):
78+ """Normalize coordinates to ensure consistent tuple format across shapely versions"""
79+
80+ def ensure_tuple_coords (obj ):
81+ """Recursively ensure coordinate pairs are tuples"""
82+ if isinstance (obj , (list , tuple )):
83+ # Check if this is a coordinate pair [x, y]
84+ if len (obj ) == 2 and all (isinstance (x , (int , float )) for x in obj ):
85+ return (float (obj [0 ]), float (obj [1 ]))
86+ else :
87+ # Recursively process nested structures, preserving list/tuple types
88+ if isinstance (obj , list ):
89+ return [ensure_tuple_coords (item ) for item in obj ]
90+ else :
91+ return tuple (ensure_tuple_coords (item ) for item in obj )
92+ return obj
93+
94+ result = geometry_dict .copy ()
95+ if 'coordinates' in result :
96+ result ['coordinates' ] = ensure_tuple_coords (result ['coordinates' ])
97+ return result
6698
6799 def draw (
68100 self ,
0 commit comments