@@ -1714,11 +1714,28 @@ def sam_map_gui(sam, basemap="SATELLITE", repeat_mode=True, out_dir=None, **kwar
17141714 layout = widgets .Layout (width = widget_width , padding = padding ),
17151715 style = style ,
17161716 )
1717+
1718+ rectangular = widgets .Checkbox (
1719+ value = False ,
1720+ description = "Regularize" ,
1721+ layout = widgets .Layout (width = "130px" , padding = padding ),
1722+ style = style ,
1723+ )
1724+
1725+ colorpicker = widgets .ColorPicker (
1726+ concise = False ,
1727+ description = "Color" ,
1728+ value = "#ffff00" ,
1729+ layout = widgets .Layout (width = "140px" , padding = padding ),
1730+ style = style ,
1731+ )
1732+
17171733 buttons = widgets .VBox (
17181734 [
17191735 radio_buttons ,
17201736 widgets .HBox ([fg_count , bg_count ]),
17211737 opacity_slider ,
1738+ widgets .HBox ([rectangular , colorpicker ]),
17221739 widgets .HBox (
17231740 [segment_button , save_button , reset_button ],
17241741 layout = widgets .Layout (padding = "0px 4px 0px 4px" ),
@@ -1950,6 +1967,8 @@ def segment_button_click(change):
19501967 )
19511968 if m .find_layer ("Masks" ) is not None :
19521969 m .remove_layer (m .find_layer ("Masks" ))
1970+ if m .find_layer ("Regularized" ) is not None :
1971+ m .remove_layer (m .find_layer ("Regularized" ))
19531972
19541973 if hasattr (sam , "prediction_fp" ) and os .path .exists (
19551974 sam .prediction_fp
@@ -1966,6 +1985,21 @@ def segment_button_click(change):
19661985 layer_name = "Masks" ,
19671986 zoom_to_layer = False ,
19681987 )
1988+
1989+ if rectangular .value :
1990+ vector = filename .replace (".tif" , ".gpkg" )
1991+ vector_rec = filename .replace (".tif" , "_rect.gpkg" )
1992+ raster_to_vector (filename , vector )
1993+ regularize (vector , vector_rec )
1994+ vector_style = {"color" : colorpicker .value }
1995+ m .add_vector (
1996+ vector_rec ,
1997+ layer_name = "Regularized" ,
1998+ style = vector_style ,
1999+ info_mode = None ,
2000+ zoom_to_layer = False ,
2001+ )
2002+
19692003 except :
19702004 pass
19712005 output .clear_output ()
@@ -1984,7 +2018,10 @@ def filechooser_callback(chooser):
19842018 filename = chooser .selected
19852019 shutil .copy (sam .prediction_fp , filename )
19862020 vector = filename .replace (".tif" , ".gpkg" )
1987- raster_to_gpkg (filename , vector )
2021+ raster_to_vector (filename , vector )
2022+ if rectangular .value :
2023+ vector_rec = filename .replace (".tif" , "_rect.gpkg" )
2024+ regularize (vector , vector_rec )
19882025
19892026 fg_points = [
19902027 [marker .location [1 ], marker .location [0 ]]
@@ -2040,9 +2077,13 @@ def reset_button_click(change):
20402077 segment_button .value = False
20412078 reset_button .value = False
20422079 opacity_slider .value = 0.5
2080+ rectangular .value = False
2081+ colorpicker .value = "#ffff00"
20432082 output .clear_output ()
20442083 try :
20452084 m .remove_layer (m .find_layer ("Masks" ))
2085+ if m .find_layer ("Regularized" ) is not None :
2086+ m .remove_layer (m .find_layer ("Regularized" ))
20462087 m .clear_drawings ()
20472088 if hasattr (m , "fg_markers" ):
20482089 m .user_rois = None
@@ -2284,7 +2325,7 @@ def text_sam_gui(sam, basemap="SATELLITE", out_dir=None, **kwargs):
22842325 description = "Box threshold:" ,
22852326 min = 0 ,
22862327 max = 1 ,
2287- value = 0.5 ,
2328+ value = 0.25 ,
22882329 step = 0.01 ,
22892330 readout = True ,
22902331 continuous_update = True ,
@@ -2297,7 +2338,7 @@ def text_sam_gui(sam, basemap="SATELLITE", out_dir=None, **kwargs):
22972338 min = 0 ,
22982339 max = 1 ,
22992340 step = 0.01 ,
2300- value = 0.5 ,
2341+ value = 0.25 ,
23012342 readout = True ,
23022343 continuous_update = True ,
23032344 layout = widgets .Layout (width = widget_width , padding = padding ),
@@ -2332,6 +2373,21 @@ def opacity_changed(change):
23322373
23332374 opacity_slider .observe (opacity_changed , "value" )
23342375
2376+ rectangular = widgets .Checkbox (
2377+ value = False ,
2378+ description = "Regularize" ,
2379+ layout = widgets .Layout (width = "130px" , padding = padding ),
2380+ style = style ,
2381+ )
2382+
2383+ colorpicker = widgets .ColorPicker (
2384+ concise = False ,
2385+ description = "Color" ,
2386+ value = "#ffff00" ,
2387+ layout = widgets .Layout (width = "140px" , padding = padding ),
2388+ style = style ,
2389+ )
2390+
23352391 segment_button = widgets .ToggleButton (
23362392 description = "Segment" ,
23372393 value = False ,
@@ -2365,6 +2421,7 @@ def opacity_changed(change):
23652421 text_slider ,
23662422 cmap_dropdown ,
23672423 opacity_slider ,
2424+ widgets .HBox ([rectangular , colorpicker ]),
23682425 widgets .HBox (
23692426 [segment_button , save_button , reset_button ],
23702427 layout = widgets .Layout (padding = "0px 4px 0px 4px" ),
@@ -2433,6 +2490,8 @@ def segment_button_click(change):
24332490 sam .output = filename
24342491 if m .find_layer (layer_name ) is not None :
24352492 m .remove_layer (m .find_layer (layer_name ))
2493+ if m .find_layer (f"{ layer_name } _rect" ) is not None :
2494+ m .remove_layer (m .find_layer (f"{ layer_name } Regularized" ))
24362495 if os .path .exists (filename ):
24372496 try :
24382497 m .add_raster (
@@ -2444,6 +2503,21 @@ def segment_button_click(change):
24442503 zoom_to_layer = False ,
24452504 )
24462505 m .layer_name = layer_name
2506+
2507+ if rectangular .value :
2508+ vector = filename .replace (".tif" , ".gpkg" )
2509+ vector_rec = filename .replace (".tif" , "_rect.gpkg" )
2510+ raster_to_vector (filename , vector )
2511+ regularize (vector , vector_rec )
2512+ vector_style = {"color" : colorpicker .value }
2513+ m .add_vector (
2514+ vector_rec ,
2515+ layer_name = f"{ layer_name } Regularized" ,
2516+ style = vector_style ,
2517+ info_mode = None ,
2518+ zoom_to_layer = False ,
2519+ )
2520+
24472521 output .clear_output ()
24482522 except Exception as e :
24492523 print (e )
@@ -2456,9 +2530,11 @@ def filechooser_callback(chooser):
24562530 try :
24572531 filename = chooser .selected
24582532 shutil .copy (sam .output , filename )
2459- vector = filename .replace (".tif" , ".shp " )
2533+ vector = filename .replace (".tif" , ".gpkg " )
24602534 raster_to_vector (filename , vector )
2461-
2535+ if rectangular .value :
2536+ vector_rec = filename .replace (".tif" , "_rect.gpkg" )
2537+ regularize (vector , vector_rec )
24622538 except Exception as e :
24632539 print (e )
24642540
@@ -2524,3 +2600,32 @@ def reset_button_click(change):
25242600 m .toolbar_control = toolbar_control
25252601
25262602 return m
2603+
2604+
2605+ def regularize (source , output = None , crs = "EPSG:4326" , ** kwargs ):
2606+ """Regularize a polygon GeoDataFrame.
2607+
2608+ Args:
2609+ source (str | gpd.GeoDataFrame): The input file path or a GeoDataFrame.
2610+ output (str, optional): The output file path. Defaults to None.
2611+
2612+
2613+ Returns:
2614+ gpd.GeoDataFrame: The output GeoDataFrame.
2615+ """
2616+ if isinstance (source , str ):
2617+ gdf = gpd .read_file (source )
2618+ elif isinstance (source , gpd .GeoDataFrame ):
2619+ gdf = source
2620+ else :
2621+ raise ValueError ("The input source must be a GeoDataFrame or a file path." )
2622+
2623+ polygons = gdf .geometry .apply (lambda geom : geom .minimum_rotated_rectangle )
2624+ result = gpd .GeoDataFrame (geometry = polygons , data = gdf .drop ("geometry" , axis = 1 ))
2625+
2626+ if crs is not None :
2627+ result .to_crs (crs , inplace = True )
2628+ if output is not None :
2629+ result .to_file (output , ** kwargs )
2630+ else :
2631+ return result
0 commit comments