Workflows in napari#

For using napari and dask together to define and design workflows, we made a small library to make these things more convenient: napari-workflows. Goal was to allow the user to setup a workflows by clicking in napari. These workflows are then accessible as object, similar to a dictionary, that can be asked for results of given workflow steps. For example, in the workflow shown below, an input image is blurred using a Gaussian kernel and then thresholded using Otsu’s method and scikit-image.

from napari_workflows import Workflow

from skimage.io import imread
from skimage.filters import threshold_otsu, gaussian
from skimage.measure import label
from pyclesperanto_prototype import imshow

First, we define a workflow and add operations to it. E.g. an operation that takes an image named "input" and produces a "denoised" image out of it using the gaussian() function and sigma=2 as parameter.

workflow = Workflow()

# define denoising
workflow.set("denoised", gaussian, "input", sigma=2)

We can also add custom function, which is for exmaple necessary to produce a binary image out of an intensity image directly with a single function call.

# define segmentation
def threshold(image):
    return image > threshold_otsu(image)
workflow.set("binarized", threshold, "denoised")

We also apply connected component labeling to the binary image.

workflow.set("labeled", label, "binarized")

# Let's print out the whole workflow
print(str(workflow))
Workflow:
denoised <- (<function gaussian at 0x19b297940>, 'input', 2, None, 'nearest', 0, None, False, 4.0)
binarized <- (<function threshold at 0x19dbd8430>, 'denoised')
labeled <- (<function label at 0x19ba7c820>, 'binarized', None, False)

Executing workflows#

Until here, there were no images involved. We now load an image and store it in the workflow as "input".

workflow.set("input", imread("https://samples.fiji.sc/blobs.png"))

print(str(workflow))
Workflow:
denoised <- (<function gaussian at 0x19b297940>, 'input', 2, None, 'nearest', 0, None, False, 4.0)
binarized <- (<function threshold at 0x19dbd8430>, 'denoised')
labeled <- (<function label at 0x19ba7c820>, 'binarized', None, False)
input <- [[ 40  32  24 ... 216 200 200]
 [ 56  40  24 ... 232 216 216]
 [ 64  48  24 ... 240 232 232]
 ...
 [ 72  80  80 ...  48  48  48]
 [ 80  80  80 ...  48  48  48]
 [ 96  88  80 ...  48  48  48]]

To actually execute computation, we call the workflow.get() function that produces a single specified output. Under the hood, it will also execute all intermediate operations that are necessary to come to the final output.

result = workflow.get("labeled")

imshow(result, labels=True)
../_images/b46f00c2a30b6b117eb2e39466aeb14acc0b44b46a27ed2c5f2e130939bad9ac.png