55from inspect import isawaitable
66from typing import List , Union , Tuple
77from IPython .display import display , HTML
8+ from ngff_zarr import from_ngff_zarr , to_ngff_image , NgffImage
89import uuid
910
1011from ._type_aliases import Gaussians , Style , Image , PointSet
@@ -136,9 +137,10 @@ def __init__(
136137 self , ui_collapsed = True , rotate = False , ui = "pydata-sphinx" , ** add_data_kwargs
137138 ):
138139 """Create a viewer."""
140+ self .stores = {}
139141 self .name = self .__str__ ()
140142 input_data = parse_input_data (add_data_kwargs )
141- data = build_init_data (input_data )
143+ data = build_init_data (input_data , self . stores )
142144 if compare := input_data .get ('compare' ):
143145 data ['compare' ] = compare
144146 if ENVIRONMENT is not Env .HYPHA :
@@ -213,6 +215,7 @@ def set_image(self, image: Image, name: str = 'Image'):
213215 render_type = _detect_render_type (image , 'image' )
214216 if render_type is RenderType .IMAGE :
215217 image = _get_viewer_image (image , label = False )
218+ self .stores [name ] = image
216219 if ENVIRONMENT is Env .HYPHA :
217220 self .image = image
218221 svc_name = f'{ self .workspace } /itkwidgets-server:data-set'
@@ -225,8 +228,28 @@ def set_image(self, image: Image, name: str = 'Image'):
225228 image = _get_viewer_point_set (image )
226229 self .viewer_rpc .itk_viewer .setPointSets (image )
227230 @fetch_value
228- async def get_image (self ):
229- return await self .viewer_rpc .itk_viewer .getImage ()
231+ async def get_image (self , name : str = 'Image' ) -> NgffImage :
232+ """Get the full, highest resolution image.
233+
234+ :param name: Name of the loaded image data to use. 'Image', the
235+ default, selects the first loaded image.
236+ :type name: str
237+
238+ :return: image
239+ :rtype: NgffImage
240+ """
241+ if store := self .stores .get (name ):
242+ multiscales = from_ngff_zarr (store )
243+ loaded_image = multiscales .images [0 ]
244+ roi_data = loaded_image .data
245+ return to_ngff_image (
246+ roi_data ,
247+ dims = loaded_image .dims ,
248+ scale = loaded_image .scale ,
249+ name = name ,
250+ axes_units = loaded_image .axes_units
251+ )
252+ raise ValueError (f'No image data found for { name } .' )
230253
231254 @fetch_value
232255 def set_image_blend_mode (self , mode : str ):
@@ -323,6 +346,36 @@ async def get_current_scale(self):
323346 """
324347 return await self .viewer_rpc .itk_viewer .getLoadedScale ()
325348
349+ @fetch_value
350+ async def get_roi_image (self , scale : int = - 1 , name : str = 'Image' ) -> NgffImage :
351+ """Get the image for the current ROI.
352+
353+ :param scale: scale of the primary image to get the slices for the
354+ current roi. -1, the default, uses the current scale.
355+ :type scale: int
356+ :param name: Name of the loaded image data to use. 'Image', the
357+ default, selects the first loaded image.
358+ :type name: str
359+
360+ :return: roi_image
361+ :rtype: NgffImage
362+ """
363+ roi_slices = await self .get_roi_slice (scale )
364+ roi_region = await self .get_roi_region ()
365+ if store := self .stores .get (name ):
366+ multiscales = from_ngff_zarr (store )
367+ loaded_image = multiscales .images [scale ]
368+ roi_data = loaded_image .data [roi_slices ]
369+ return to_ngff_image (
370+ roi_data ,
371+ dims = loaded_image .dims ,
372+ scale = loaded_image .scale ,
373+ translation = roi_region [0 ],
374+ name = name ,
375+ axes_units = loaded_image .axes_units
376+ )
377+ raise ValueError (f'No image data found for { name } .' )
378+
326379 @fetch_value
327380 async def get_roi_region (self ):
328381 """Get the current region of interest in world / physical space.
@@ -339,7 +392,7 @@ async def get_roi_region(self):
339392 return [{ 'x' : x0 , 'y' : y0 , 'z' : z0 }, { 'x' : x1 , 'y' : y1 , 'z' : z1 }]
340393
341394 @fetch_value
342- async def get_roi_slice (self , scale = - 1 ):
395+ async def get_roi_slice (self , scale : int = - 1 ):
343396 """Get the current region of interest as Python slice objects for the
344397 current resolution of the primary image. The result is in the order:
345398
@@ -395,6 +448,7 @@ def set_label_image(self, label_image: Image):
395448 render_type = _detect_render_type (label_image , 'image' )
396449 if render_type is RenderType .IMAGE :
397450 label_image = _get_viewer_image (label_image , label = True )
451+ self .stores ['LabelImage' ] = label_image
398452 if ENVIRONMENT is Env .HYPHA :
399453 self .label_image = label_image
400454 svc_name = f"{ self .workspace } /itkwidgets-server:data-set"
@@ -407,8 +461,24 @@ def set_label_image(self, label_image: Image):
407461 label_image = _get_viewer_point_set (label_image )
408462 self .viewer_rpc .itk_viewer .setPointSets (label_image )
409463 @fetch_value
410- async def get_label_image (self ):
411- return await self .viewer_rpc .itk_viewer .getLabelImage ()
464+ async def get_label_image (self ) -> NgffImage :
465+ """Get the full, highest resolution label image.
466+
467+ :return: label_image
468+ :rtype: NgffImage
469+ """
470+ if store := self .stores .get ('LabelImage' ):
471+ multiscales = from_ngff_zarr (store )
472+ loaded_image = multiscales .images [0 ]
473+ roi_data = loaded_image .data
474+ return to_ngff_image (
475+ roi_data ,
476+ dims = loaded_image .dims ,
477+ scale = loaded_image .scale ,
478+ name = 'LabelImage' ,
479+ axes_units = loaded_image .axes_units
480+ )
481+ raise ValueError (f'No label image data found.' )
412482
413483 @fetch_value
414484 def set_label_image_blend (self , blend : float ):
@@ -452,6 +522,10 @@ def set_layer_visibility(self, visible: bool, name: str):
452522 async def get_layer_visibility (self , name : str ):
453523 return await self .viewer_rpc .itk_viewer .getLayerVisibility (name )
454524
525+ @fetch_value
526+ def get_loaded_image_names (self ):
527+ return list (self .stores .keys ())
528+
455529 @fetch_value
456530 def add_point_set (self , pointSet : PointSet ):
457531 pointSet = _get_viewer_point_set (pointSet )
0 commit comments