Skip to content

Commit

Permalink
Merge pull request #713 from bnmajor/roi-multiscales
Browse files Browse the repository at this point in the history
Add get_roi_multiscales method
  • Loading branch information
thewtex authored Dec 18, 2023
2 parents f69cbec + 4d3e770 commit 9c5d082
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 36 deletions.
77 changes: 51 additions & 26 deletions examples/integrations/itk/SelectROI.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@
"source": [
"import itk\n",
"import pooch\n",
"from ngff_zarr import to_multiscales, ngff_image_to_itk_image, Methods\n",
"from itkwidgets import view"
"from ngff_zarr import ngff_image_to_itk_image\n",
"from itkwidgets import view\n",
"import zarr"
]
},
{
Expand Down Expand Up @@ -235,8 +236,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Slices for loaded scale: (slice(0, 250, None), slice(0, 350, None), slice(120, 173, None))\n",
"Slices with default parameter: (slice(0, 250, None), slice(0, 350, None), slice(120, 173, None))\n"
"Slices for loaded scale: (slice(0, 250, None), slice(0, 350, None), slice(122, 170, None))\n",
"Slices with default parameter: (slice(0, 250, None), slice(0, 350, None), slice(122, 170, None))\n"
]
}
],
Expand All @@ -246,6 +247,13 @@
"print(f'Slices with default parameter: {default_roi_slices}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The ROI region will return the physical, world coordinates for the selection"
]
},
{
"cell_type": "code",
"execution_count": 10,
Expand All @@ -256,10 +264,8 @@
},
"outputs": [],
"source": [
"# Create a new viewer using only the data in the ROI determined above\n",
"# get_roi_image will return a dict of ngff images with the keys representing the names for each\n",
"# For our example we should have one entry under \"Image\"\n",
"roi_image = viewer.get_roi_image(loaded_scale)"
"# Get the selected region for the current level\n",
"roi_region = viewer.get_roi_region()"
]
},
{
Expand All @@ -274,7 +280,8 @@
{
"data": {
"text/plain": [
"NgffImage(data=dask.array<getitem, shape=(250, 350, 53), dtype=int16, chunksize=(128, 128, 45), chunktype=numpy.ndarray>, dims=['z', 'y', 'x'], scale={'z': 0.2734, 'y': 0.2734, 'x': 0.2734}, translation={'x': 25.973146382696534, 'y': -6.971699714660643, 'z': -6.971699953079224}, name='Image', axes_units={'z': None, 'y': None, 'x': None}, computed_callbacks=[])"
"[{'x': 26.70976023219073, 'y': -6.971699714660643, 'z': -6.971699953079224},\n",
" {'x': 39.35382616126007, 'y': 88.71824073791504, 'z': 61.378257513046265}]"
]
},
"execution_count": 11,
Expand All @@ -283,14 +290,7 @@
}
],
"source": [
"roi_image"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The ROI region will return the physical, world coordinates for the selection"
"roi_region"
]
},
{
Expand All @@ -303,8 +303,9 @@
},
"outputs": [],
"source": [
"# Get the selected region for the current level\n",
"roi_region = viewer.get_roi_region()"
"# get_roi_image will return an ngff image for the currently selected image\n",
"# optionally a name can also be passed in to select a specific image if more than one image or label is loaded\n",
"roi_image = viewer.get_roi_image(loaded_scale)"
]
},
{
Expand All @@ -319,8 +320,7 @@
{
"data": {
"text/plain": [
"[{'x': 25.973146382696534, 'y': -6.971699714660643, 'z': -6.971699953079224},\n",
" {'x': 40.14316091031731, 'y': 88.71824073791504, 'z': 61.378257513046265}]"
"NgffImage(data=dask.array<getitem, shape=(250, 350, 48), dtype=int16, chunksize=(128, 128, 42), chunktype=numpy.ndarray>, dims=['z', 'y', 'x'], scale={'z': 0.2734, 'y': 0.2734, 'x': 0.2734}, translation={'x': 26.70976023219073, 'y': -6.971699714660643, 'z': -6.971699953079224}, name='Image', axes_units={'z': None, 'y': None, 'x': None}, computed_callbacks=[])"
]
},
"execution_count": 13,
Expand All @@ -329,7 +329,7 @@
}
],
"source": [
"roi_region"
"roi_image"
]
},
{
Expand All @@ -342,8 +342,8 @@
},
"outputs": [],
"source": [
"# Create an itk image from the ngff image returned from get_roi_image\n",
"itk_image = ngff_image_to_itk_image(roi_image)"
"# Optionally we can also grab the ngff images for all scales\n",
"roi_multiscales = viewer.get_roi_multiscale()"
]
},
{
Expand All @@ -354,6 +354,30 @@
"skip-execution"
]
},
"outputs": [
{
"data": {
"text/plain": [
"Multiscales(images=[NgffImage(data=dask.array<rechunk-merge, shape=(250, 350, 48), dtype=int16, chunksize=(128, 128, 48), chunktype=numpy.ndarray>, dims=['z', 'y', 'x'], scale={'z': 0.2734, 'y': 0.2734, 'x': 0.2734}, translation={'x': 26.70976023219073, 'y': -6.971699714660643, 'z': -6.971699953079224}, name='Image', axes_units={'z': None, 'y': None, 'x': None}, computed_callbacks=[]), NgffImage(data=dask.array<rechunk-merge, shape=(250, 175, 48), dtype=int16, chunksize=(128, 128, 48), chunktype=numpy.ndarray>, dims=['z', 'y', 'x'], scale={'z': 0.2734, 'y': 0.5468, 'x': 0.2734}, translation={'z': -6.971699953079224, 'y': -6.8349997146606425, 'x': 26.70976023219073}, name='image', axes_units=None, computed_callbacks=[])], metadata=Metadata(axes=[Axis(name='z', type='space', unit=None), Axis(name='y', type='space', unit=None), Axis(name='x', type='space', unit=None)], datasets=[Dataset(path='scale0/Image', coordinateTransformations=[Scale(scale=[0.2734, 0.2734, 0.2734], type='scale'), Translation(translation=[-6.971699953079224, -6.971699714660643, 26.70976023219073], type='translation')]), Dataset(path='scale1/Image', coordinateTransformations=[Scale(scale=[0.2734, 0.5468, 0.2734], type='scale'), Translation(translation=[-6.971699953079224, -6.8349997146606425, 26.70976023219073], type='translation')])], coordinateTransformations=None, name='Image', version='0.4'), scale_factors=[{'x': 1, 'y': 2, 'z': 1}], method=<Methods.DASK_IMAGE_GAUSSIAN: 'dask_image_gaussian'>, chunks={'z': 128, 'y': 128, 'x': 128})"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"roi_multiscales"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"tags": [
"skip-execution"
]
},
"outputs": [
{
"data": {
Expand Down Expand Up @@ -402,7 +426,8 @@
}
],
"source": [
"viewer2 = view(itk_image, rotate=True)"
"# Create a new viewer using only the data in the ROI determined above\n",
"viewer2 = view(roi_multiscales, rotate=True)"
]
},
{
Expand All @@ -416,7 +441,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 17,
"metadata": {
"tags": [
"skip-execution"
Expand Down
26 changes: 17 additions & 9 deletions itkwidgets/cell_watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,27 +181,35 @@ async def _execute_next_request(self):
else:
self.kernel._publish_status("idle")

self.current_request = None
if self.all_getters_resolved:
if not self.results:
self.current_request = None
if self.all_getters_resolved and not self._events.empty():
# Continue processing the remaining queued tasks
self.create_task(self.execute_next_request)

def update_namespace(self):
# Update the namespace variables with the results from the getters
# FIXME: This is a temporary "fix" and does not handle updating output
keys = [k for k in self.shell.user_ns.keys()]
for key in keys:
value = self.shell.user_ns[key]
if asyncio.isfuture(value) and (isinstance(value, FuturePromise) or isinstance(value, asyncio.Task)):
# Getters/setters return futures
# They should all be resolved now, so use the result
self.shell.user_ns[key] = value.result()
self.results.clear()
try:
for key in keys:
value = self.shell.user_ns[key]
if asyncio.isfuture(value) and (isinstance(value, FuturePromise) or isinstance(value, asyncio.Task)):
# Getters/setters return futures
# They should all be resolved now, so use the result
self.shell.user_ns[key] = value.result()
self.results.clear()
except Exception as e:
self.results.clear()
self.abort_all = True
self.create_task(self._execute_next_request)
raise e

def _callback(self, *args, **kwargs):
# After each getter/setter resolves check if they've all resolved
if self.all_getters_resolved:
self.update_namespace()
self.current_request = None
self.create_task(self.execute_next_request)

def post_run_cell(self, response):
Expand Down
31 changes: 30 additions & 1 deletion itkwidgets/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
from typing import List, Union, Tuple
from IPython.display import display, HTML
from IPython.lib import backgroundjobs as bg
from ngff_zarr import from_ngff_zarr, to_ngff_image, NgffImage
from ngff_zarr import (
from_ngff_zarr,
to_ngff_image,
Multiscales,
NgffImage
)
import uuid

from ._method_types import deferred_methods
Expand Down Expand Up @@ -434,6 +439,30 @@ async def get_roi_image(self, scale: int = -1, name: str = 'Image') -> NgffImage
)
raise ValueError(f'No image data found for {name}.')

@fetch_value
async def get_roi_multiscale(self, name: str = 'Image') -> Multiscales:
"""Build and return a new Multiscales NgffImage for the ROI.
:param name: Name of the loaded image data to use. 'Image', the
default, selects the first loaded image.
:type name: str
:return: roi_multiscales
:rtype: Multiscales NgffImage
"""
if store := self.stores.get(name):
multiscales = from_ngff_zarr(store)
scales = range(len(multiscales.images))
images = [await self.get_roi_image(s) for s in scales]
return Multiscales(
images=images,
metadata=multiscales.metadata,
scale_factors=multiscales.scale_factors,
method=multiscales.method,
chunks=multiscales.chunks
)
raise ValueError(f'No image data found for {name}.')

@fetch_value
async def get_roi_region(self):
"""Get the current region of interest in world / physical space.
Expand Down

0 comments on commit 9c5d082

Please sign in to comment.