1. nima_io: Microscopy Data Reading Tutorial#
This notebook demonstrates reading various microscopy file formats,
comparing nima_io (bioio-based) with tifffile, and inspecting
OME metadata for all available test data.
1.1. Setup#
%load_ext autoreload
%autoreload 2
from pathlib import Path
import matplotlib.pyplot as plt
import tifffile
import nima_io.read as ir
tdata = Path("../../tests/data")
1.2. 1. Single-scene OME-TIFF#
A simple multi-channel time-series OME-TIFF with known structure: 5 timepoints, 3 channels, 17x13 pixels.
# nima_io: returns xarray.DataArray backed by dask
da = ir.read_image(str(tdata / "im1s1z3c5t_a.ome.tif"))
print(f"dims={da.dims}, shape={da.shape}, dtype={da.dtype}")
da.data
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.6.3) or chardet (7.0.1)/charset_normalizer (3.4.5) doesn't match a supported version!
warnings.warn(
dims=('T', 'C', 'Z', 'Y', 'X'), shape=(5, 3, 1, 17, 13), dtype=uint16
|
||||||||||||||||
# Access OME metadata from attrs
da.attrs["ome_metadata"].images[0].pixels.channels
[Channel(
id='Channel:0:0',
light_source_settings={'id': 'LightSource:b3870238-479a-45b4-8627-ddb41680e7be_1', 'attenuation': 0.9, 'wavelength': 482.0},
detector_settings={'id': 'Detector:ec659ce6-9847-4cab-b64a-43b5e9d88f63', 'gain': 5.0, 'binning': <Binning.ONEBYONE: '1x1'>},
light_path={'excitation_filters': [{'id': 'Filter:cb860710-71c0-46ee-8903-f20019f4b54e'}, {'id': 'Filter:cc5646d0-d3e9-42b6-a147-ad2bda838adf'}, {'id': 'Filter:058c011a-165c-4b82-9cf1-62af018e70d1'}]},
samples_per_pixel=1,
illumination_type='Epifluorescence',
acquisition_mode='WideField',
),
Channel(
id='Channel:0:1',
light_source_settings={'id': 'LightSource:b3870238-479a-45b4-8627-ddb41680e7be_2', 'attenuation': 0.9, 'wavelength': 563.0},
detector_settings={'id': 'Detector:ec659ce6-9847-4cab-b64a-43b5e9d88f63', 'gain': 5.0, 'binning': <Binning.ONEBYONE: '1x1'>},
light_path={'excitation_filters': [{'id': 'Filter:cb860710-71c0-46ee-8903-f20019f4b54e'}, {'id': 'Filter:cc5646d0-d3e9-42b6-a147-ad2bda838adf'}, {'id': 'Filter:058c011a-165c-4b82-9cf1-62af018e70d1'}]},
samples_per_pixel=1,
illumination_type='Epifluorescence',
acquisition_mode='WideField',
),
Channel(
id='Channel:0:2',
light_source_settings={'id': 'LightSource:b3870238-479a-45b4-8627-ddb41680e7be_4', 'attenuation': 0.9, 'wavelength': 458.0},
detector_settings={'id': 'Detector:ec659ce6-9847-4cab-b64a-43b5e9d88f63', 'gain': 5.0, 'binning': <Binning.ONEBYONE: '1x1'>},
light_path={'excitation_filters': [{'id': 'Filter:cb860710-71c0-46ee-8903-f20019f4b54e'}, {'id': 'Filter:cc5646d0-d3e9-42b6-a147-ad2bda838adf'}, {'id': 'Filter:058c011a-165c-4b82-9cf1-62af018e70d1'}]},
samples_per_pixel=1,
illumination_type='Epifluorescence',
acquisition_mode='WideField',
)]
# Structured metadata (consolidated from OME)
md = da.attrs["metadata"]
print(md)
print()
print(f"Objective: {md.objective[0]}")
print(f"Pixel size: {md.voxel_size[0]}")
print(f"Date: {md.date[0]}")
print()
for ci, ch in enumerate(md.channels[0]):
print(
f"Ch[{ci}]: wavelength={ch.wavelength}nm, "
f"attenuation={ch.attenuation}, "
f"exposure={ch.exposure}s, "
f"gain={ch.gain}, "
f"binning={ch.binning}"
)
Metadata(S=1, T=[5], C=[3], Z=[1], Y=[17], X=[13]
bits=[16], obj=['Objective:60XWater:f3efd78d-8646-4017-b655-b9b63e83d037']
voxel_size=[VoxelSize(x=0.1364024, y=0.1364024, z=1000.0)]
channels=
[[Channel(λ=482, att=0.9, exp=0.045, gain=5.0, binning=1x1),
Channel(λ=563, att=0.9, exp=0.125, gain=5.0, binning=1x1),
Channel(λ=458, att=0.9, exp=0.25, gain=5.0, binning=1x1)]])
Objective: Objective:60XWater:f3efd78d-8646-4017-b655-b9b63e83d037
Pixel size: VoxelSize(x=0.1364024, y=0.1364024, z=1000.0)
Date: 2016-09-01 10:49:21
Ch[0]: wavelength=482nm, attenuation=0.9, exposure=0.045s, gain=5.0, binning=1x1
Ch[1]: wavelength=563nm, attenuation=0.9, exposure=0.125s, gain=5.0, binning=1x1
Ch[2]: wavelength=458nm, attenuation=0.9, exposure=0.25s, gain=5.0, binning=1x1
# tifffile comparison
with tifffile.TiffFile(tdata / "im1s1z3c5t_a.ome.tif") as tif:
print(f"Series: {len(tif.series)}")
s = tif.series[0]
print(f"shape={s.shape}, axes={s.axes}, dtype={s.dtype}")
print(f"OME: {tif.is_ome}")
Series: 1
shape=(5, 3, 17, 13), axes=TCYX, dtype=uint16
OME: True
1.2.1. OME Metadata#
Access the full OME metadata object via bioio.
from bioio import BioImage
img = BioImage(tdata / "im1s1z3c5t_a.ome.tif")
ome = img.ome_metadata
px = ome.images[0].pixels
print(f"Image: {ome.images[0].name or ome.images[0].id}")
print(
f"Dims: X={px.size_x}, Y={px.size_y}, C={px.size_c}, T={px.size_t}, Z={px.size_z}"
)
print(f"Pixel sizes: {img.physical_pixel_sizes}")
print("Channels:")
for ci, ch in enumerate(px.channels):
ls = ch.light_source_settings
wl = ls.wavelength if ls else None
print(f" [{ci}] id={ch.id}, wavelength={wl}")
Image: Image:0
Dims: X=13, Y=17, C=3, T=5, Z=1
Pixel sizes: PhysicalPixelSizes(Z=1000.0, Y=0.1364024, X=0.1364024)
Channels:
[0] id=Channel:0:0, wavelength=482.0
[1] id=Channel:0:1, wavelength=563.0
[2] id=Channel:0:2, wavelength=458.0
1.2.2. Per-channel acquisition settings#
Exposure time and timestamps live in planes (one per T/C/Z
combination). Channel-level settings (wavelength, attenuation,
binning, gain) are on the Channel object. Combine both for
a complete per-channel summary.
# Per-channel acquisition summary
for ci, ch in enumerate(px.channels):
ls = ch.light_source_settings
ds = ch.detector_settings
# Exposure from the first plane of this channel
plane = next(p for p in px.planes if p.the_c == ci)
print(
f"Ch[{ci}]: "
f"wl={ls.wavelength if ls else None}, "
f"att={ls.attenuation if ls else None}, "
f"exposure={plane.exposure_time}, "
f"binning={ds.binning if ds else None}, "
f"gain={ds.gain if ds else None}"
)
Ch[0]: wl=482.0, att=0.9, exposure=0.045, binning=Binning.ONEBYONE, gain=5.0
Ch[1]: wl=563.0, att=0.9, exposure=0.125, binning=Binning.ONEBYONE, gain=5.0
Ch[2]: wl=458.0, att=0.9, exposure=0.25, binning=Binning.ONEBYONE, gain=5.0
1.2.3. Channel naming#
Assign semantic channel names for ratio analysis.
da_named = ir.read_image(
str(tdata / "im1s1z3c5t_a.ome.tif"),
channels=["G", "R", "C"],
)
print(f"Channel coords: {list(da_named.coords['C'].values)}")
da_named.sel(C="G", T=0).data
Channel coords: [np.str_('G'), np.str_('R'), np.str_('C')]
|
||||||||||||||||
1.3. 2. Multi-channel time-series OME-TIFF#
A 7-timepoint, 3-channel image without wavelength metadata.
da_mcts = ir.read_image(str(tdata / "multi-channel-time-series.ome.tif"))
print(f"dims={da_mcts.dims}, shape={da_mcts.shape}, dtype={da_mcts.dtype}")
img_mcts = BioImage(tdata / "multi-channel-time-series.ome.tif")
print(f"Channel names: {img_mcts.channel_names}")
print(f"Pixel sizes: {img_mcts.physical_pixel_sizes}")
print(f"Image name: {img_mcts.ome_metadata.images[0].name}")
dims=('T', 'C', 'Z', 'Y', 'X'), shape=(7, 3, 1, 167, 439), dtype=int8
Channel names: [np.str_('Channel:0:0'), np.str_('Channel:0:1'), np.str_('Channel:0:2')]
Pixel sizes: PhysicalPixelSizes(Z=None, Y=None, X=None)
Image name: multi-channel-time-series
1.4. 3. File sequences with tifffile.TiffSequence#
For sets of related TIFF files, tifffile.TiffSequence stacks them.
This is useful when acquisitions split across multiple files.
fp_glob = str(tdata / "im1s1z3c5t_?.ome.tif")
tifs = tifffile.TiffSequence(fp_glob)
d = tifs.asarray()
print(f"Glob matched {len(tifs)} files")
print(f"Stacked shape: {d.shape}")
print("Individual files:")
for f in sorted(tifs):
print(f" {Path(f).name}")
Glob matched 2 files
Stacked shape: (2, 5, 3, 17, 13)
Individual files:
im1s1z3c5t_a.ome.tif
im1s1z3c5t_b.ome.tif
1.5. 4. Tiled images (FEI multi-scene)#
FEI microscopes save tiled acquisitions as multi-scene OME-TIFFs. Each scene is one tile with stage position metadata.
1.5.1. 4a. Regular tile grid (t4_1.tif)#
# bioio sees each tile as a separate scene
img_tile = BioImage(tdata / "t4_1.tif")
print(f"Scenes: {len(img_tile.scenes)}")
print(f"Per-tile shape: {img_tile.shape}")
print(f"Pixel sizes: {img_tile.physical_pixel_sizes}")
# tifffile comparison
with tifffile.TiffFile(tdata / "t4_1.tif") as tif:
print(f"\ntifffile series: {len(tif.series)}")
print(f"Per-series shape: {tif.series[0].shape}, axes={tif.series[0].axes}")
Scenes: 15
Per-tile shape: (3, 4, 1, 256, 512)
Pixel sizes: PhysicalPixelSizes(Z=1000.0, Y=0.1333333, X=0.1333333)
tifffile series: 15
Per-series shape: (3, 4, 256, 512), axes=TCYX
# Stitch into a single DataArray
stitched = ir.stitch_scenes(str(tdata / "t4_1.tif"))
print(f"Stitched: dims={stitched.dims}, shape=T{stitched.sizes['T']}")
print(f" Y={stitched.sizes['Y']}, X={stitched.sizes['X']}")
stitched.data
Stitched: dims=('T', 'C', 'Z', 'Y', 'X'), shape=T3
Y=1280, X=1536
|
||||||||||||||||
# Tilemap shows scene layout (row, col) -> scene_index
import numpy as np
tilemap = stitched.attrs["tilemap"]
print(f"Tile grid: {tilemap.shape[0]} rows x {tilemap.shape[1]} cols")
print(tilemap)
Tile grid: 5 rows x 3 cols
[[ 0 1 2]
[ 5 4 3]
[ 6 7 8]
[11 10 9]
[12 13 14]]
1.5.2. 4b. Tile grid with void tiles (tile6_1.tif)#
stitched_void = ir.stitch_scenes(str(tdata / "tile6_1.tif"))
print(f"Stitched: Y={stitched_void.sizes['Y']}, X={stitched_void.sizes['X']}")
tilemap_void = stitched_void.attrs["tilemap"]
print(f"Tile grid ({tilemap_void.shape}):")
print(tilemap_void)
print(f"Void tiles (=-1): {np.sum(tilemap_void == -1)}")
Stitched: Y=2560, X=2560
Tile grid ((5, 5)):
[[-1 -1 6 4 -1]
[-1 7 5 3 8]
[13 2 10 9 -1]
[12 1 11 -1 -1]
[-1 0 -1 -1 -1]]
Void tiles (=-1): 11
plt.imshow(stitched_void.sel(T=1, Z=0, C=1), cmap="Reds", vmax=1000, vmin=1)
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7fca7f104ad0>
1.5.3. Stage positions#
OME metadata provides physical stage positions for each tile.
ome_tile = BioImage(tdata / "tile6_1.tif").ome_metadata
print(f"{'Scene':>5} {'X pos':>10} {'Y pos':>10}")
for i, im in enumerate(ome_tile.images):
p = im.pixels.planes[0]
print(f"{i:5d} {float(p.position_x):10.2f} {float(p.position_y):10.2f}")
Scene X pos Y pos
0 50.63 81.84
1 50.63 81.73
2 50.63 81.63
3 50.83 81.53
4 50.83 81.43
5 50.73 81.53
6 50.73 81.43
7 50.63 81.53
8 50.93 81.53
9 50.83 81.63
10 50.73 81.63
11 50.73 81.73
12 50.52 81.73
13 50.52 81.63
1.6. 5. TF8 format (.tf8)#
TF8 files are TIFFs with a non-standard extension.
nima_io handles this transparently via a temp symlink.
da_tf8 = ir.read_image(str(tdata / "LC26GFP_1.tf8"))
print(f"dims={da_tf8.dims}, shape={da_tf8.shape}, dtype={da_tf8.dtype}")
cjdk: Installing JDK zulu-jre:11.0.30 to /home/runner/.cache/cjdk
Download 0% of 41.2 MiB | | Elapsed Time: 0:00:00 ETA: --:--:--
Download 17% of 41.2 MiB |## | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 45% of 41.2 MiB |##### | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 56% of 41.2 MiB |####### | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 65% of 41.2 MiB |######## | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 74% of 41.2 MiB |######### | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 82% of 41.2 MiB |########## | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 91% of 41.2 MiB |########### | Elapsed Time: 0:00:00 ETA: 0:00:00
Download 100% of 41.2 MiB |#############| Elapsed Time: 0:00:00 ETA: 00:00:00
Download 100% of 41.2 MiB |#############| Elapsed Time: 0:00:00 Time: 0:00:00
Extract / |# | 0 Elapsed Time: 0:00:00
Extract - | # | 24 Elapsed Time: 0:00:00
Extract \ | # | 41 Elapsed Time: 0:00:00
Extract | | # | 371 Elapsed Time: 0:00:00
cjdk: Installing Maven to /home/runner/.cache/cjdk
Download 0% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: --:--:--
Download 0% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:02:01
Download 0% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:01:15
Download 0% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:00:57
Download 1% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:00:50
Download 1% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:00:42
Download 2% of 8.7 MiB | | Elapsed Time: 0:00:00 ETA: 0:00:40
Download 2% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:37
Download 3% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:34
Download 3% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:34
Download 3% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:38
Download 4% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:32
Download 5% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:32
Download 5% of 8.7 MiB | | Elapsed Time: 0:00:01 ETA: 0:00:31
Download 6% of 8.7 MiB | | Elapsed Time: 0:00:02 ETA: 0:00:31
Download 6% of 8.7 MiB | | Elapsed Time: 0:00:02 ETA: 0:00:30
Download 6% of 8.7 MiB | | Elapsed Time: 0:00:02 ETA: 0:00:33
Download 7% of 8.7 MiB |# | Elapsed Time: 0:00:02 ETA: 0:00:30
Download 8% of 8.7 MiB |# | Elapsed Time: 0:00:02 ETA: 0:00:31
Download 8% of 8.7 MiB |# | Elapsed Time: 0:00:02 ETA: 0:00:31
Download 8% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:30
Download 9% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:30
Download 9% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:30
Download 10% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:30
Download 10% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:29
Download 10% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:29
Download 11% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:29
Download 11% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:29
Download 11% of 8.7 MiB |# | Elapsed Time: 0:00:03 ETA: 0:00:29
Download 12% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:29
Download 12% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:29
Download 12% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 13% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 13% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 13% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 14% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 14% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 14% of 8.7 MiB |# | Elapsed Time: 0:00:04 ETA: 0:00:28
Download 15% of 8.7 MiB |# | Elapsed Time: 0:00:05 ETA: 0:00:28
Download 15% of 8.7 MiB |# | Elapsed Time: 0:00:05 ETA: 0:00:28
Download 15% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:27
Download 16% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:27
Download 16% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:27
Download 17% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:27
Download 17% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:27
Download 17% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:26
Download 18% of 8.7 MiB |## | Elapsed Time: 0:00:05 ETA: 0:00:26
Download 18% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:26
Download 19% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:26
Download 19% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:26
Download 20% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:25
Download 20% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:25
Download 21% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:25
Download 21% of 8.7 MiB |## | Elapsed Time: 0:00:06 ETA: 0:00:25
Download 22% of 8.7 MiB |## | Elapsed Time: 0:00:07 ETA: 0:00:24
Download 22% of 8.7 MiB |## | Elapsed Time: 0:00:07 ETA: 0:00:24
Download 22% of 8.7 MiB |## | Elapsed Time: 0:00:07 ETA: 0:00:24
Download 23% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:24
Download 23% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:24
Download 24% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:23
Download 24% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:23
Download 25% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:23
Download 25% of 8.7 MiB |### | Elapsed Time: 0:00:07 ETA: 0:00:23
Download 25% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:23
Download 26% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:23
Download 26% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:22
Download 27% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:22
Download 27% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:22
Download 27% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:22
Download 28% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:22
Download 28% of 8.7 MiB |### | Elapsed Time: 0:00:08 ETA: 0:00:21
Download 29% of 8.7 MiB |### | Elapsed Time: 0:00:09 ETA: 0:00:21
Download 30% of 8.7 MiB |### | Elapsed Time: 0:00:09 ETA: 0:00:21
Download 30% of 8.7 MiB |### | Elapsed Time: 0:00:09 ETA: 0:00:21
Download 30% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:20
Download 31% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:20
Download 32% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:20
Download 32% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:20
Download 32% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:19
Download 33% of 8.7 MiB |#### | Elapsed Time: 0:00:09 ETA: 0:00:19
Download 33% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:19
Download 34% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:19
Download 34% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:19
Download 35% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:19
Download 35% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:18
Download 36% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:18
Download 36% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:18
Download 37% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:18
Download 37% of 8.7 MiB |#### | Elapsed Time: 0:00:10 ETA: 0:00:18
Download 37% of 8.7 MiB |#### | Elapsed Time: 0:00:11 ETA: 0:00:18
Download 38% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:17
Download 39% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:17
Download 39% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:17
Download 39% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:17
Download 40% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:17
Download 41% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:16
Download 41% of 8.7 MiB |##### | Elapsed Time: 0:00:11 ETA: 0:00:16
Download 41% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:16
Download 42% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:16
Download 43% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:16
Download 43% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:16
Download 44% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:15
Download 44% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:15
Download 44% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:15
Download 45% of 8.7 MiB |##### | Elapsed Time: 0:00:12 ETA: 0:00:15
Download 46% of 8.7 MiB |##### | Elapsed Time: 0:00:13 ETA: 0:00:15
Download 46% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:15
Download 47% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:14
Download 48% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:14
Download 48% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:14
Download 48% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:14
Download 49% of 8.7 MiB |###### | Elapsed Time: 0:00:13 ETA: 0:00:14
Download 50% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:14
Download 50% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 51% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 51% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 52% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 52% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 52% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 53% of 8.7 MiB |###### | Elapsed Time: 0:00:14 ETA: 0:00:13
Download 53% of 8.7 MiB |###### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 54% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 54% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 55% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 55% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 56% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 56% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:12
Download 57% of 8.7 MiB |####### | Elapsed Time: 0:00:15 ETA: 0:00:11
Download 57% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 57% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 58% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 58% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 59% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 59% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 59% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 60% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 60% of 8.7 MiB |####### | Elapsed Time: 0:00:16 ETA: 0:00:11
Download 60% of 8.7 MiB |####### | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 61% of 8.7 MiB |####### | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 61% of 8.7 MiB |######## | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 62% of 8.7 MiB |######## | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 62% of 8.7 MiB |######## | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 63% of 8.7 MiB |######## | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 63% of 8.7 MiB |######## | Elapsed Time: 0:00:17 ETA: 0:00:10
Download 64% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:10
Download 64% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 64% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 65% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 65% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 66% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 66% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 67% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 67% of 8.7 MiB |######## | Elapsed Time: 0:00:18 ETA: 0:00:09
Download 67% of 8.7 MiB |######## | Elapsed Time: 0:00:19 ETA: 0:00:09
Download 68% of 8.7 MiB |######## | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 68% of 8.7 MiB |######## | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 69% of 8.7 MiB |######## | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 69% of 8.7 MiB |######### | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 70% of 8.7 MiB |######### | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 70% of 8.7 MiB |######### | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 70% of 8.7 MiB |######### | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 71% of 8.7 MiB |######### | Elapsed Time: 0:00:19 ETA: 0:00:08
Download 71% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 72% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 72% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 73% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 73% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 73% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 74% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 74% of 8.7 MiB |######### | Elapsed Time: 0:00:20 ETA: 0:00:07
Download 75% of 8.7 MiB |######### | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 75% of 8.7 MiB |######### | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 75% of 8.7 MiB |######### | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 76% of 8.7 MiB |######### | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 76% of 8.7 MiB |######### | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 77% of 8.7 MiB |########## | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 77% of 8.7 MiB |########## | Elapsed Time: 0:00:21 ETA: 0:00:06
Download 77% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:06
Download 78% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 78% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 79% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 79% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 80% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 80% of 8.7 MiB |########## | Elapsed Time: 0:00:22 ETA: 0:00:05
Download 81% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:05
Download 81% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:05
Download 82% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:05
Download 82% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:04
Download 82% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:04
Download 83% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:04
Download 83% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:04
Download 83% of 8.7 MiB |########## | Elapsed Time: 0:00:23 ETA: 0:00:04
Download 84% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:04
Download 85% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:04
Download 85% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:04
Download 86% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:03
Download 86% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:03
Download 86% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:03
Download 87% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:03
Download 87% of 8.7 MiB |########### | Elapsed Time: 0:00:24 ETA: 0:00:03
Download 88% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:03
Download 88% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:03
Download 89% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:03
Download 89% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:02
Download 89% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:02
Download 90% of 8.7 MiB |########### | Elapsed Time: 0:00:25 ETA: 0:00:02
Download 90% of 8.7 MiB |########### | Elapsed Time: 0:00:26 ETA: 0:00:02
Download 92% of 8.7 MiB |########### | Elapsed Time: 0:00:26 ETA: 0:00:02
Download 92% of 8.7 MiB |############ | Elapsed Time: 0:00:26 ETA: 0:00:02
Download 92% of 8.7 MiB |############ | Elapsed Time: 0:00:26 ETA: 0:00:02
Download 93% of 8.7 MiB |############ | Elapsed Time: 0:00:26 ETA: 0:00:01
Download 93% of 8.7 MiB |############ | Elapsed Time: 0:00:26 ETA: 0:00:01
Download 93% of 8.7 MiB |############ | Elapsed Time: 0:00:26 ETA: 0:00:01
Download 94% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 94% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 95% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 95% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 95% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 96% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 96% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:01
Download 96% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:00
Download 97% of 8.7 MiB |############ | Elapsed Time: 0:00:27 ETA: 0:00:00
Download 97% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 97% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 98% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 98% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 99% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 99% of 8.7 MiB |############ | Elapsed Time: 0:00:28 ETA: 0:00:00
Download 100% of 8.7 MiB |#############| Elapsed Time: 0:00:28 ETA: 00:00:00
Download 100% of 8.7 MiB |#############| Elapsed Time: 0:00:28 Time: 0:00:28
Extract / |# | 0 Elapsed Time: 0:00:00
Extract | |# | 102 Elapsed Time: 0:00:00
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:6272fd82-9593-4afe-a826-6d427c2e8771' to 'LightSource:0'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
dims=('T', 'C', 'Z', 'Y', 'X'), shape=(1, 1, 1, 1200, 1600), dtype=uint16
1.7. 6. Large single-scene image (exp2_2.tif)#
81 timepoints, 2 channels (340nm/380nm ratiometric), 1200x1600 pixels.
img_exp = BioImage(tdata / "exp2_2.tif")
print(f"Shape: {img_exp.shape}")
print(f"Pixel sizes: {img_exp.physical_pixel_sizes}")
ome_exp = img_exp.ome_metadata
px_exp = ome_exp.images[0].pixels
for ci, ch in enumerate(px_exp.channels):
ls = ch.light_source_settings
wl = ls.wavelength if ls else None
print(f" Ch[{ci}]: wavelength={wl}")
# Instrument metadata
if ome_exp.instruments:
inst = ome_exp.instruments[0]
if inst.objectives:
obj = inst.objectives[0]
print(f"Objective: NA={obj.lens_na}, mag={obj.nominal_magnification}")
if inst.detectors:
print(f"Detector: {inst.detectors[0].model}")
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:1'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:2'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:3'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
Shape: (81, 2, 1, 1200, 1600)
Pixel sizes: PhysicalPixelSizes(Z=1000.0, Y=0.74, X=0.74)
Ch[0]: wavelength=380.0
Ch[1]: wavelength=340.0
Objective: NA=0.3, mag=10.0
Detector: QImaging Retiga 2000DC
# Lazy read - no data loaded until .values or .compute()
da_exp = ir.read_image(str(tdata / "exp2_2.tif"))
print(f"Lazy DataArray: {da_exp.dims}, {da_exp.shape}")
print(f"Dask chunks: {da_exp.data.chunks}")
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:4'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:5'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
/home/runner/work/nima_io/nima_io/.venv/lib/python3.14/site-packages/pydantic/main.py:250: UserWarning: Casting invalid LightSourceID 'Lightsource:e9ce7657-3552-460a-bc05-f6fc83991d36' to 'LightSource:6'
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
Lazy DataArray: ('T', 'C', 'Z', 'Y', 'X'), (81, 2, 1, 1200, 1600)
Dask chunks: ((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1), (1,), (1200,), (1600,))
1.8. 7. Leica LIF files#
Multi-scene confocal Z-stacks read via bioio-lif (pure Python, no Java).
img_lif = BioImage(tdata / "2015Aug28_TransHXB2_50min+DMSO.lif")
print(f"Reader: {type(img_lif.reader).__module__}")
print(f"Scenes ({len(img_lif.scenes)}): {img_lif.scenes}")
for si, scene in enumerate(img_lif.scenes):
img_lif.set_scene(si)
print(
f" {scene}: shape={img_lif.shape}, "
f"channels={img_lif.channel_names}, "
f"voxel_z={img_lif.physical_pixel_sizes.Z}"
)
Reader: bioio_lif.reader
Scenes (5): ('Series001', 'Series004', 'Series007', 'Series009', 'Series011')
Series001: shape=(1, 3, 41, 512, 512), channels=[np.str_('Green'), np.str_('Gray'), np.str_('Red')], voxel_z=0.2964
Series004: shape=(1, 3, 40, 512, 512), channels=[np.str_('Green'), np.str_('Gray'), np.str_('Red')], voxel_z=0.2964
Series007: shape=(1, 3, 43, 512, 512), channels=[np.str_('Green'), np.str_('Gray'), np.str_('Red')], voxel_z=0.2964
Series009: shape=(1, 3, 39, 512, 512), channels=[np.str_('Green'), np.str_('Gray'), np.str_('Red')], voxel_z=0.2964
Series011: shape=(1, 3, 37, 512, 512), channels=[np.str_('Green'), np.str_('Gray'), np.str_('Red')], voxel_z=0.2964
1.9. 8. File comparison (diff)#
Compare two files for pixel-level equality.
a = str(tdata / "im1s1z3c5t_a.ome.tif")
b = str(tdata / "im1s1z3c5t_b.ome.tif")
bpix = str(tdata / "im1s1z3c5t_bpix.ome.tif")
print(f"a vs a (identical): {ir.diff(a, a)}")
print(f"a vs b (same data): {ir.diff(a, b)}")
print(f"a vs bpix (1px off): {ir.diff(a, bpix)}")
a vs a (identical): True
a vs b (same data): True
a vs bpix (1px off): False
1.10. 9. Backend comparison: nima_io vs tifffile#
Key differences between reading with nima_io (bioio) and raw tifffile.
# tifffile: raw arrays, manual dimension handling
with tifffile.TiffFile(tdata / "t4_1.tif") as tif:
# Each series is a tile - tifffile doesn't auto-stitch
tf_data = tif.series[0].asarray()
print(f"tifffile single series: shape={tf_data.shape}, axes={tif.series[0].axes}")
# nima_io: auto-stitched, named dims, lazy
nio_data = ir.stitch_scenes(str(tdata / "t4_1.tif"))
print(f"nima_io stitched: shape={dict(nio_data.sizes)}")
print(f" lazy (dask): {type(nio_data.data).__name__}")
tifffile single series: shape=(3, 4, 256, 512), axes=TCYX
nima_io stitched: shape={'T': 3, 'C': 4, 'Z': 1, 'Y': 1280, 'X': 1536}
lazy (dask): Array
# tifffile: reading OME metadata requires manual XML parsing
with tifffile.TiffFile(tdata / "im1s1z3c5t_a.ome.tif") as tif:
# tifffile exposes raw OME-XML string
ome_xml = tif.ome_metadata # raw XML string
print(f"tifffile OME-XML: {type(ome_xml).__name__}, {len(ome_xml)} chars")
# nima_io/bioio: parsed OME object with typed attributes
img = BioImage(tdata / "im1s1z3c5t_a.ome.tif")
ome = img.ome_metadata # ome_types.OME object
print(f"bioio OME: {type(ome).__name__}")
print(f" images: {len(ome.images)}")
print(f" instruments: {len(ome.instruments)}")
ch0 = ome.images[0].pixels.channels[0]
wl = ch0.light_source_settings.wavelength
print(f" channels[0].wavelength: {wl}")
tifffile OME-XML: str, 47251 chars
bioio OME: OME
images: 1
instruments: 1
channels[0].wavelength: 482.0