1616import matplotlib .patches as mpatches
1717import matplotlib .path as mpath
1818import matplotlib .pyplot as plt
19+ import matplotlib .colors as mcolors
1920import matplotlib .ticker
2021import matplotlib .transforms as mtransforms
2122import numpy as np
@@ -1160,6 +1161,7 @@ def _map_color_seg(
11601161 na_color : Color ,
11611162 seg_erosionpx : int | None = None ,
11621163 seg_boundaries : bool = False ,
1164+ outline_color : Color | None = None ,
11631165) -> ArrayLike :
11641166 cell_id = np .array (cell_id )
11651167
@@ -1211,8 +1213,33 @@ def _map_color_seg(
12111213 if seg_boundaries :
12121214 if seg .shape [0 ] == 1 :
12131215 seg = np .squeeze (seg , axis = 0 )
1214- seg_bound : ArrayLike = np .clip (seg_im - find_boundaries (seg )[:, :, None ], 0 , 1 )
1215- return np .dstack ((seg_bound , np .where (val_im > 0 , 1 , 0 ))) # add transparency here
1216+
1217+ # Binary boundary mask
1218+ boundary_mask = find_boundaries (seg ) # True where boundaries are
1219+
1220+ # Ensure seg_im is float in 0-1 and has 3 channels
1221+ seg_float = seg_im .astype (float )
1222+ if seg_float .ndim == 2 :
1223+ seg_float = np .stack ([seg_float ] * 3 , axis = - 1 ) # H x W x 3
1224+
1225+ # Add alpha channel from val_im (preserve original mask)
1226+ alpha_channel = (val_im > 0 ).astype (float )
1227+ seg_float = np .dstack ((seg_float , alpha_channel )) # H x W x 4
1228+
1229+ # Convert outline_color to RGBA
1230+ if outline_color is None :
1231+ outline_rgba = (0 , 0 , 0 , 1.0 ) # default black
1232+ elif isinstance (outline_color , str ):
1233+ outline_rgba = mcolors .to_rgba (outline_color ) # named color or hex string
1234+ else :
1235+ # assume it's your Color object
1236+ outline_rgba = mcolors .to_rgba (outline_color .get_hex_with_alpha ())
1237+
1238+ # Apply outline color to boundary pixels, but keep original alpha from val_im
1239+ seg_float [boundary_mask , :3 ] = outline_rgba [:3 ] # RGB
1240+ seg_float [boundary_mask , 3 ] = alpha_channel [boundary_mask ] * outline_rgba [3 ] # scale alpha
1241+
1242+ return seg_float # H x W x 4, valid RGBA
12161243
12171244 if len (val_im .shape ) != len (seg_im .shape ):
12181245 val_im = np .expand_dims ((val_im > 0 ).astype (int ), axis = - 1 )
@@ -2437,6 +2464,7 @@ def _validate_label_render_params(
24372464 na_color : ColorLike | None ,
24382465 norm : Normalize | None ,
24392466 outline_alpha : float | int ,
2467+ outline_color : ColorLike | tuple [ColorLike ] | None ,
24402468 scale : str | None ,
24412469 table_name : str | None ,
24422470 table_layer : str | None ,
@@ -2453,6 +2481,7 @@ def _validate_label_render_params(
24532481 "color" : color ,
24542482 "na_color" : na_color ,
24552483 "outline_alpha" : outline_alpha ,
2484+ "outline_color" : outline_color ,
24562485 "cmap" : cmap ,
24572486 "norm" : norm ,
24582487 "scale" : scale ,
@@ -2475,6 +2504,7 @@ def _validate_label_render_params(
24752504 element_params [el ]["fill_alpha" ] = param_dict ["fill_alpha" ]
24762505 element_params [el ]["scale" ] = param_dict ["scale" ]
24772506 element_params [el ]["outline_alpha" ] = param_dict ["outline_alpha" ]
2507+ element_params [el ]["outline_color" ] = param_dict ["outline_color" ]
24782508 element_params [el ]["contour_px" ] = param_dict ["contour_px" ]
24792509 element_params [el ]["table_layer" ] = param_dict ["table_layer" ]
24802510
0 commit comments