Notebook: Advanced
Passing workflows between functions¶
Demo to use workflows in functions for end to end image analysis
This is slightly advanced, where we need to define a custom function for image analysis.
When you have a custom function and want to use it in napari-lattice with the GUI or CLI, you will need to create a .py file
import os
import pyclesperanto_prototype as cle
from napari_workflows import Workflow
from napari_workflows import _io_yaml_v1 as io_yaml
cle.get_device()
<Intel(R) Iris(R) Xe Graphics on Platform: Intel(R) OpenCL Graphics (1 refs)>
#download sample data from https://zenodo.org/records/14903188
img_path = "./sample_data/RBC_tiny.czi"
#make dir if not exists
if not os.path.exists("./sample_data"):
os.makedirs("./sample_data")
import urllib.request
url = "https://zenodo.org/records/14903188/files/RBC_tiny.czi?download=1"
if not os.path.exists(img_path):
print("Downloading file")
urllib.request.urlretrieve(url, img_path)
print(f"Downloaded at : {img_path}")
else:
print(f"File already exists at : {img_path}")
File already exists at : ./sample_data/RBC_tiny.czi
#absolute path for saving
save_path = "C:/Users/rajasekhar.p/napari_lattice/notebooks/sample_data"
#make dir if not exists
if not os.path.exists(save_path):
os.makedirs(save_path)
Lets apply a workflow, where we perform
- median filter
- binarise using otsu algorithm with threshold of 1000
- label each object using connected components labeling-
- measure each object using connected components
As we have multiple steps, we can define it in a function and then call this function in a workflow.
We would like to save the deskewed image, segmented image and the measurements as a csv file, so we return all 3.
from skimage.measure import regionprops_table
import numpy as np
import pandas as pd
def image_seg_measure(input_image):
# Apply median filter
median_filtered = cle.median_sphere(input_image, radius_x=2, radius_y=2, radius_z=2)
# Apply Otsu thresholding
binary_image = cle.threshold_otsu(median_filtered)
# Apply connected components labeling
labeled_image = cle.connected_components_labeling_box(binary_image)
#convert labeled_image to np array as original image will be an opencl array
labeled_image_np = np.array(labeled_image)
#measure using regionprops_table
props = regionprops_table(labeled_image_np, input_image,
properties=['label', 'bbox','area',
'intensity_mean'])
#convert to pandas dataframe
#props_df = pd.DataFrame(props)
return (input_image, labeled_image_np, props)
from lls_core import LatticeData
image_seg_workflow = Workflow()
#Otsu thresholding
input_arg = "deskewed_image"
#define a simple gaussian filter
task_name = "measurement"
#Apply function
image_seg_workflow.set(task_name, image_seg_measure, input_arg )
print(image_seg_workflow)
Workflow: measurement <- (<function image_seg_measure at 0x000001F9238FC160>, 'deskewed_image')
#Before we run this, lets look at whats in the directory
os.listdir(save_path)
['gaussian_workflow.yaml', 'RBC_tiny.czi']
params_workflow = LatticeData(
input_image=img_path,
save_dir=save_path,
workflow=image_seg_workflow,
)
params_workflow.save()
[INFO:2025-08-29 11:48:41,214] Processing File ./sample_data/RBC_tiny.czi
Timepoints: 0%| | 0/1 [00:00<?, ?it/s][INFO:2025-08-29 11:48:42,038] Processing File <xarray.DataArray 'transpose-d511a424874269c0d63d068279594e59' (Z: 834, Y: 118,
X: 209)> Size: 41MB
dask.array<getitem, shape=(834, 118, 209), dtype=uint16, chunksize=(834, 118, 209), chunktype=numpy.ndarray>
Coordinates:
C <U17 68B 'LatticeLightsheet'
* Z (Z) float64 7kB 0.0 0.3 0.6 0.9 1.2 ... 249.0 249.3 249.6 249.9
* Y (Y) float64 944B 0.0 0.145 0.29 0.435 ... 16.53 16.67 16.82 16.96
* X (X) float64 2kB 0.0 0.145 0.29 0.435 ... 29.72 29.87 30.01 30.16
Attributes:
unprocessed: <Element 'ImageDocument' at 0x000001F9755C1440>
INFO: blockdim levels (1) < subsamp levels (3): First-level block size (4, 256, 256) will be used for all levels INFO: blockdim levels (1) < subsamp levels (3): First-level block size (4, 256, 256) will be used for all levels
Timepoints: 100%|██████████| 1/1 [00:13<00:00, 13.41s/it]
#list files in save_path
os.listdir(save_path)
['gaussian_workflow.yaml', 'RBC_tiny.czi', 'RBC_tiny_deskewed.h5', 'RBC_tiny_deskewed.xml', 'RBC_tiny_deskewed_1.h5', 'RBC_tiny_deskewed_1.xml', 'RBC_tiny_deskewed_output_2.csv']
csv_path = "C:/Users/rajasekhar.p/napari_lattice/notebooks/sample_data/RBC_tiny_deskewed_output_2.csv"
df = pd.read_csv(csv_path)
#apply series.explode
df.apply(pd.Series.explode)
| Unnamed: 0 | time | channel | label | bbox-0 | bbox-1 | bbox-2 | bbox-3 | bbox-4 | bbox-5 | area | intensity_mean | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | T0 | C0 | [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... | [23 8 5 7 7 18 5 6 15 6 7 4 6 5 34 ... | [ 49 179 85 550 676 748 651 1072 1289 ... | [ 0 0 0 0 0 0 0 0 0 0 0 ... | [32 57 58 14 50 41 52 41 16 58 40 32 33 50 36 ... | [ 64 291 615 556 742 795 1118 1295 1290 ... | [ 22 38 209 5 49 9 209 186 1 35 23 ... | [ 669 47513 889527 128 48386 30... | [ 824.44012451 815.22802734 897.43670654 82... |
- RBC_tiny_deskewed.h5 is the deskewed image
- RBC_tiny_deskewed_1.h5 is the label image
- RBC_tiny_deskewed_output_2.csv is the table with measurements
Troubleshooting
To inspect the components of workflows, you can use the process_workflow function to access the data.
It returns a generator object, which can be accessed using the next function
As we were returning 2 images and a measurement table/dictionary, it will have 3 outputs
for slice in params_workflow.process_workflow():
first_slice = slice
break
slice
('slices',
<generator object LatticeData.process_workflow.<locals>._generator at 0x0000020086544900>)
data_set = next(first_slice[1])
data_set
Timepoints: 0%| | 0/1 [00:00<?, ?it/s][INFO:2025-08-29 11:37:29,403] Processing File <xarray.DataArray 'transpose-2f7dac7b3adef14da02a10aa2414f35b' (Z: 834, Y: 118,
X: 209)> Size: 41MB
dask.array<getitem, shape=(834, 118, 209), dtype=uint16, chunksize=(834, 118, 209), chunktype=numpy.ndarray>
Coordinates:
C <U17 68B 'LatticeLightsheet'
* Z (Z) float64 7kB 0.0 0.3 0.6 0.9 1.2 ... 249.0 249.3 249.6 249.9
* Y (Y) float64 944B 0.0 0.145 0.29 0.435 ... 16.53 16.67 16.82 16.96
* X (X) float64 2kB 0.0 0.145 0.29 0.435 ... 29.72 29.87 30.01 30.16
Attributes:
unprocessed: <Element 'ImageDocument' at 0x00000200D8185260>
ProcessedSlice(data=(array([[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]],
[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]],
[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]],
...,
[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]],
[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[170.021 , 175.5255 , 170.96855, ..., 124.722 , 110.69399,
111.24305],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]],
[[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
...,
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[ 0. , 0. , 0. , ..., 0. , 0. ,
0. ]]], dtype=float32), array([[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],
[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],
[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],
...,
[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],
[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],
[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]]], dtype=uint32), {'label': array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90, 91, 92, 93, 94]), 'bbox-0': array([23, 8, 5, 7, 7, 18, 5, 6, 15, 6, 7, 4, 6, 5, 34, 56, 23,
19, 9, 7, 6, 5, 7, 8, 10, 6, 42, 5, 45, 6, 7, 6, 45, 3,
51, 0, 6, 7, 6, 37, 8, 48, 41, 22, 9, 5, 9, 8, 8, 48, 8,
20, 9, 8, 9, 8, 32, 8, 32, 38, 38, 6, 31, 8, 38, 30, 14, 8,
9, 7, 32, 40, 31, 41, 9, 27, 10, 41, 31, 49, 7, 9, 8, 6, 8,
32, 7, 6, 8, 7, 11, 6, 56, 8]), 'bbox-1': array([ 49, 179, 85, 550, 676, 748, 651, 1072, 1289, 1306, 1375,
1435, 1528, 1634, 1238, 25, 581, 582, 315, 55, 555, 928,
1420, 1433, 459, 1324, 198, 1415, 1597, 1406, 1421, 1438, 1591,
1281, 1665, 1183, 1408, 1423, 1430, 1587, 996, 1378, 856, 1393,
1002, 1683, 987, 656, 998, 12, 643, 31, 998, 1144, 617,
715, 510, 1633, 511, 1179, 1177, 1186, 1354, 1566, 963, 1351,
42, 647, 767, 390, 84, 87, 1200, 83, 556, 1659, 957,
226, 1374, 1199, 452, 1730, 622, 1192, 1698, 1372, 1727, 954,
992, 1310, 379, 1700, 1673, 1721]), 'bbox-2': array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 10, 13, 17, 21, 23, 23, 45, 46, 46, 48, 49,
50, 51, 51, 52, 58, 58, 59, 60, 66, 69, 70, 72, 72,
73, 80, 80, 84, 84, 86, 86, 90, 91, 91, 94, 95, 101,
102, 108, 111, 112, 116, 121, 123, 123, 126, 137, 138, 139, 142,
143, 145, 145, 150, 157, 161, 161, 162, 165, 167, 175, 177, 180,
180, 188, 190, 192, 193, 196, 196, 198, 199, 203, 205, 206, 207,
207, 208, 208]), 'bbox-3': array([32, 57, 58, 14, 50, 41, 52, 41, 16, 58, 40, 32, 33, 50, 36, 57, 27,
22, 13, 36, 17, 14, 11, 11, 11, 12, 47, 36, 55, 12, 11, 12, 49, 38,
54, 34, 12, 11, 12, 40, 12, 58, 43, 26, 11, 36, 11, 13, 12, 58, 13,
43, 11, 13, 12, 13, 34, 9, 33, 40, 48, 37, 32, 10, 41, 33, 41, 13,
12, 36, 38, 44, 34, 43, 12, 56, 11, 44, 33, 58, 13, 10, 35, 45, 10,
33, 10, 14, 28, 12, 25, 12, 57, 9]), 'bbox-4': array([ 64, 291, 615, 556, 742, 795, 1118, 1295, 1290, 1367, 1427,
1489, 1621, 1813, 1241, 26, 585, 585, 322, 160, 580, 937,
1424, 1437, 460, 1329, 205, 1634, 1606, 1416, 1426, 1444, 1596,
1417, 1667, 1299, 1418, 1427, 1437, 1589, 1000, 1388, 860, 1397,
1007, 1814, 988, 659, 1009, 24, 647, 68, 1000, 1150, 626,
719, 515, 1634, 513, 1182, 1193, 1246, 1355, 1567, 966, 1358,
82, 651, 771, 444, 90, 92, 1203, 86, 559, 1675, 958,
229, 1379, 1208, 457, 1731, 676, 1242, 1700, 1373, 1730, 971,
1020, 1314, 402, 1712, 1674, 1722]), 'bbox-5': array([ 22, 38, 209, 5, 49, 9, 209, 186, 1, 35, 23, 46, 29,
91, 5, 12, 18, 20, 29, 91, 53, 53, 55, 48, 49, 53,
55, 209, 60, 62, 63, 66, 66, 209, 69, 139, 75, 75, 80,
77, 83, 94, 87, 91, 88, 209, 92, 94, 99, 115, 100, 136,
104, 113, 115, 116, 122, 122, 126, 126, 139, 194, 139, 140, 144,
151, 196, 148, 152, 207, 168, 166, 167, 168, 170, 206, 178, 183,
188, 201, 194, 193, 209, 209, 197, 200, 202, 209, 209, 209, 209,
209, 209, 209]), 'area': array([ 669, 47513, 889527, 128, 48386, 3056, 1045361,
289997, 1, 55812, 14386, 33372, 24939, 178109,
8, 2, 14, 8, 92, 72377, 3599,
309, 59, 15, 1, 66, 59, 305493,
175, 228, 55, 120, 56, 183802, 8,
80719, 121, 23, 144, 8, 22, 377,
13, 39, 16, 158236, 3, 24, 98,
631, 43, 4997, 6, 57, 50, 39,
17, 1, 4, 10, 445, 52906, 1,
2, 9, 64, 16023, 26, 18, 40098,
100, 44, 18, 11, 10, 1395, 1,
12, 45, 409, 55, 1, 11618, 9142,
3, 2, 20, 364, 986, 32, 298,
101, 1, 1]), 'intensity_mean': array([ 824.44012451, 815.22802734, 897.43670654, 826.90734863,
927.54815674, 715.90979004, 882.14837646, 893.55303955,
507.86376953, 953.51800537, 813.11102295, 878.25036621,
882.16064453, 882.97283936, 587.64257812, 761.0078125 ,
672.73260498, 913.13238525, 650.95477295, 907.18945312,
997.35333252, 930.35559082, 699.47155762, 709.3369751 ,
662.49304199, 874.21893311, 989.14361572, 893.8817749 ,
1042.01049805, 748.62005615, 724.85894775, 737.92016602,
1091.32250977, 909.79138184, 788.19958496, 1103.34802246,
722.1472168 , 765.64434814, 747.50366211, 867.94067383,
735.35430908, 1404.96728516, 547.01049805, 765.47375488,
671.72058105, 864.14581299, 719.02142334, 829.05761719,
691.89819336, 967.984375 , 705.89770508, 899.51959229,
681.39361572, 794.60510254, 693.56719971, 788.46417236,
547.57000732, 717.15216064, 571.09234619, 681.67669678,
940.87121582, 922.46777344, 573.95263672, 534.56640625,
552.05053711, 585.19360352, 924.40423584, 861.67199707,
800.25622559, 919.29553223, 823.79602051, 861.19018555,
557.7947998 , 692.59185791, 722.04248047, 1550.2956543 ,
625.28936768, 562.4229126 , 555.0067749 , 1380.77600098,
831.05633545, 644.28527832, 915.66485596, 872.54400635,
705.61248779, 566.88433838, 675.20892334, 832.4006958 ,
726.69799805, 725.35168457, 642.75158691, 755.10247803,
1137.56420898, 734.17675781])}), time_index=0, time=0, channel_index=0, channel=0, roi_index=None)
measurements = data_set.data[2]#.apply(Series.explode)
measurements