scverse / scverse/napari-spatialdata
Saving and reloading images directly on sdata within napari
Open
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 90
- Forks
- 25
- Avg merge
- 22m
- Merged PRs (30d)
- 1
Description
Hello,
Following the agenda from the 2024/09/05 SpatialData community meeting, below is a code example to reproduce the process of:
- Loading an SpatialData object into napari_spatialdata
- Performing image operations directly on the image (i.e. creating a binary mask -> Labels2DModel)
- Saving and writing the output image directly back onto the SpatialData object
- Refreshing the elements widget (in future to be done via public methods/API?)
# example image
import numpy as np
from spatialdata import SpatialData
from spatialdata.models import Image2DModel, Labels2DModel
from napari_spatialdata import Interactive
import tempfile
from pathlib import Path
import os
# example test SpatialData object containing only an image
temp_dir = tempfile.TemporaryDirectory()
shape = (5, 100, 100)
image_name = "test_image"
image_np = np.random.randint(0, 255, shape, dtype=np.uint8)
image_model = Image2DModel.parse(
image_np,
dims=("c", "y", "x"),
c_coords=[f"chan{x}" for x in range(shape[0])],
scale_factors=[2, 2, 2] # mimick pyramidal/multiscale
)
sdata = SpatialData(images={image_name: image_model})
temp_dir_zarr = os.path.join(Path(temp_dir.name), Path(f"{image_name}.zarr"))
sdata.write(temp_dir_zarr) # Write to disk
# Launch napari-spatialdata
interactive = Interactive(sdata)
# Below mimicks the user selecting the global coord, and adding the image as a layer
viewer = interactive._viewer
sd_widget = viewer.window._dock_widgets["SpatialData"].widget()
sd_widget.coordinate_system_widget.setCurrentRow(0) # User selects the global coord
image_element = sd_widget.elements_widget.item(0) # Get the 'test_image'
sd_widget.elements_widget.itemDoubleClicked.emit(image_element) # User double clicks the image, adds to layers
# Below mimicks an external plugin that operates on the contained sdata object(s)
current_channel = interactive._viewer.dims.current_step[0] # Gets the user selected channel (using the var widget)
selected_layer = viewer.layers.selection.active # Gets the user selected image layer
ext_ref_sdata = selected_layer.metadata["sdata"] # Gets the sdata metadata from the image layer
# Some skimage funcs arent compatible with DataArrays, so these need to be passed the base numpy/dask arrays
working_image_scale0 = ext_ref_sdata.images["test_image"]["scale0"].isel(c=current_channel).image.data
working_image = working_image_scale0 - 255 # example image op 1
working_image_binary = working_image < 50 # example image op 2
label_name = "test_label"
labels = Labels2DModel.parse(working_image_binary, dims=("y", "x"))
# 'in-place' write operations
if ext_ref_sdata.is_backed:
# If same label_name exists, 'overwrite' it by deleting the object from disk if it exists
if label_name in ext_ref_sdata.labels:
del ext_ref_sdata.labels[label_name]
ext_ref_sdata.delete_element_from_disk(label_name)
ext_ref_sdata.labels[label_name] = labels
ext_ref_sdata.write_element(label_name)
# Refresh widgets to show updated sdata with new elements.
# NOTE: comment this line to see the effect of above not being updated in the viewer
sd_widget.elements_widget._onItemChange(selected_layer.metadata["_current_cs"])
# Cleanup
temp_dir.cleanup()
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the reproduction using Interactive, the SpatialData dock widget, and the elements widget callbacks. Investigate how selected layers expose sdata and how backed elements are written, then determine the supported public API for updating and refreshing elements. Done means the demonstrated image operation can save a new or replaced label and the viewer reflects it without relying on private widget members.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data-visualization
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100