TIFF Container Policies and Special Layouts
This section describes how OMIO handles more complex TIFF and LSM container layouts, including multi-series files, paginated stacks, and multi-file OME-TIFF series. OMIO follows strict and explicit policies to avoid ambiguous interpretations.
Reading Multi-Series TIFF Stacks
OMIO’s imread function also supports reading of multi-series TIFF and LSM stacks,
however, with some limitations.
TIFF and LSM containers may store multiple datasets (“series”) in a single file.
While tifffile exposes these as TIFF series, OMIO enforces a strict and predictable
policy to avoid ambiguous interpretations:
If a file contains exactly one series (
len(tif.series) == 1), OMIO guarantees correct reading and normalization to canonical OME axis order (TZCYX).If a file contains multiple series (
len(tif.series) > 1), OMIO will process only the first series (series 0) and ignore all others. A warning is emitted in this case, and the policy decision is recorded in the returned metadata.OMIO does not attempt to infer relationships between multiple series, does not concatenate them, and does not inspect their shapes, axes, or photometric interpretation beyond series 0.
This policy is intentional and favors reproducibility and explicit behavior over heuristic reconstruction of complex TIFF layouts.
fname_multi_series = "example_data/tif_dummy_data/multiseries_tif/multiseries_rgb_with_equal_shapes.tif"
image_multi_series, metadata_multi_series = om.imread(fname_multi_series)
pprint.pprint(metadata_multi_series)
>>>
{'Annotations': {'Namespace': 'omio:metadata',
'OMIO_MultiSeriesAxes': ['YXS', 'YXS'],
'OMIO_MultiSeriesDetected': True,
'OMIO_MultiSeriesPhotometric': ['RGB', 'RGB'],
'OMIO_MultiSeriesPolicy': 'only_series_0',
'OMIO_MultiSeriesShapes': [[16, 16, 3], [16, 16, 3]],
'OMIO_ProcessedSeries': 0,
'OMIO_TotalSeries': 2,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-27T16:57:29',
'original_filename': 'multiseries_rgb_with_equal_shapes.tif',
'original_filetype': 'tif',
'original_metadata_type': 'multipage RGB TIFF',
'original_parentfolder': 'example_data/tif_dummy_data/multiseries_tif',
'spacing': 1.0,
'unit': 'micron'},
'PhysicalSizeX': 1.0,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 1.0,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 1.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 3,
'SizeT': 1,
'SizeX': 16,
'SizeY': 16,
'SizeZ': 1,
'axes': 'TZCYX',
'shape': (1, 1, 3, 16, 16)}
Inspecting the "Annotations" in the retrieved metadata shows that OMIO has detected a
multi-series TIFF file ('OMIO_MultiSeriesDetected': True) which initially contained
two series with axes ['YXS', 'YXS'] and shapes [[16, 16, 3], [16, 16, 3]]. Thus,
the two series seem to be compatible for concatenation along a new axis. However,
OMIO does not infer, by intention, any such relationships and only reads the first series
(series 0) with shape (16, 16, 3) and axes YXS.
The reason for this policy is to avoid ambiguous interpretations of multi-series TIFF files, which may contain series with different dimensionalities, axes, or photometric interpretations:
fname_multi_series = "example_data/tif_dummy_data/multiseries_tif/multiseries_rgb_with_unequal_series.tif"
image_multi_series, metadata_multi_series = om.imread(fname_multi_series)
pprint.pprint(metadata_multi_series)
>>>
{'Annotations': {'Namespace': 'omio:metadata',
'OMIO_MultiSeriesAxes': ['YXS', 'YXS'],
'OMIO_MultiSeriesDetected': True,
'OMIO_MultiSeriesPhotometric': ['RGB', 'RGB'],
'OMIO_MultiSeriesPolicy': 'only_series_0',
'OMIO_MultiSeriesShapes': [[16, 16, 3], [17, 17, 3]],
'OMIO_ProcessedSeries': 0,
'OMIO_TotalSeries': 2,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-27T16:57:29',
'original_filename': 'multiseries_rgb_with_unequal_series.tif',
'original_filetype': 'tif',
'original_metadata_type': 'multipage RGB TIFF',
'original_parentfolder': 'example_data/tif_dummy_data/multiseries_tif',
'spacing': 1.0,
'unit': 'micron'},
'PhysicalSizeX': 1.0,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 1.0,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 1.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 3,
'SizeT': 1,
'SizeX': 16,
'SizeY': 16,
'SizeZ': 1,
'axes': 'TZCYX',
'shape': (1, 1, 3, 16, 16)}
fname_multi_series = "example_data/tif_dummy_data/multiseries_tif/multiseries_rgb_minisblack_mixture.tif"
image_multi_series, metadata_multi_series = om.imread(fname_multi_series)
pprint.pprint(metadata_multi_series)
>>>
{'Annotations': {'Namespace': 'omio:metadata',
'OMIO_MultiSeriesAxes': ['YXS', 'QYX'],
'OMIO_MultiSeriesDetected': True,
'OMIO_MultiSeriesPhotometric': ['RGB', 'MINISBLACK'],
'OMIO_MultiSeriesPolicy': 'only_series_0',
'OMIO_MultiSeriesShapes': [[16, 16, 3], [2, 32, 32]],
'OMIO_ProcessedSeries': 0,
'OMIO_TotalSeries': 2,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-27T16:57:29',
'original_filename': 'multiseries_rgb_minisblack_mixture.tif',
'original_filetype': 'tif',
'original_metadata_type': 'multipage RGB TIFF',
'original_parentfolder': 'example_data/tif_dummy_data/multiseries_tif',
'spacing': 1.0,
'unit': 'micron'},
'PhysicalSizeX': 1.0,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 1.0,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 1.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 3,
'SizeT': 1,
'SizeX': 16,
'SizeY': 16,
'SizeZ': 1,
'axes': 'TZCYX',
'shape': (1, 1, 3, 16, 16)}
fname_multi_series = "example_data/tif_dummy_data/multiseries_tif/multiseries_minisblack.tif"
image_multi_series, metadata_multi_series = om.imread(fname_multi_series)
pprint.pprint(metadata_multi_series)
>>>
{'Annotations': {'Namespace': 'omio:metadata',
'OMIO_MultiSeriesAxes': ['QYX', 'QYX'],
'OMIO_MultiSeriesDetected': True,
'OMIO_MultiSeriesPhotometric': ['MINISBLACK', 'MINISBLACK'],
'OMIO_MultiSeriesPolicy': 'only_series_0',
'OMIO_MultiSeriesShapes': [[2, 32, 32], [2, 32, 32]],
'OMIO_ProcessedSeries': 0,
'OMIO_TotalSeries': 2,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-27T16:57:29',
'original_filename': 'multiseries_minisblack.tif',
'original_filetype': 'tif',
'original_metadata_type': 'N/A',
'original_parentfolder': 'example_data/tif_dummy_data/multiseries_tif',
'spacing': 1.0,
'unit': 'micron'},
'PhysicalSizeX': 1.0,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 1.0,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 1.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 2,
'SizeT': 1,
'SizeX': 32,
'SizeY': 32,
'SizeZ': 1,
'axes': 'TZCYX',
'shape': (1, 1, 2, 32, 32)}
fname_multi_series = "example_data/tif_dummy_data/multiseries_tif/multiseries_TCYXS.ome.tif"
image_multi_series, metadata_multi_series = om.imread(fname_multi_series)
pprint.pprint(metadata_multi_series)
>>>
{'Annotations': {'1': '256 256',
'2': '128 128',
'Namespace': 'omio:metadata',
'OMIO_MultiSeriesAxes': ['TCYXS', 'YXS'],
'OMIO_MultiSeriesDetected': True,
'OMIO_MultiSeriesPhotometric': ['RGB', 'RGB'],
'OMIO_MultiSeriesPolicy': 'only_series_0',
'OMIO_MultiSeriesShapes': [[8, 2, 20, 100, 3], [3, 13, 3]],
'OMIO_ProcessedSeries': 0,
'OMIO_TotalSeries': 2,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-28T10:24:12',
'original_filename': 'multiseries_TCYXS.ome.tif',
'original_filetype': 'tif',
'original_metadata_type': 'OME_XML',
'original_parentfolder': 'example_data/tif_dummy_data/multiseries_tif',
'spacing': 1.0,
'unit': 'micron'},
'Channel_Count': 2,
'PhysicalSizeX': 0.29,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 0.29,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 1.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 6,
'SizeT': 8,
'SizeX': 100,
'SizeY': 20,
'SizeZ': 1,
'TimeIncrement': 0.1,
'TimeIncrementUnit': 's',
'axes': 'TZCYX',
'shape': (8, 1, 6, 20, 100)}
As a conequence of this policy, users who wish to work with multiple series in a multi-series TIFF file must explicitly handle the separation and reading of each series themselves (e.g., by using as ImageJ/Fiji and store each series in its own single-series TIFF file).
Reading Paginated TIFF Stacks
OMIO’s imread function also supports reading of paginated LSM stacks that contain
multiple pages or tiles stored sequentially.
OMIO’s policy here is that each page or tile is treated as a separate image stack, and the returned image becomes a list of images and a list of metadata dictionaries, one for each page. This allows for flexible handling of paginated stacks, where each page may have different dimensionalities, axes, or metadata.
fname_paginated = "example_data/tif_dummy_data/paginated_tif/paginated_tif.tif"
images, metadata_paginated = om.imread(fname_paginated)
print(f"Number of pages read: {len(images)}")
for i, (img, meta) in enumerate(zip(images, metadata_paginated)):
print(f"Page {i}: shape={img.shape}, axes={meta.get('axes', 'N/A')}")
pprint.pprint(metadata_paginated[0])
pprint.pprint(metadata_paginated[1])
pprint.pprint(metadata_paginated[2])
Output (truncated):
>>>
Number of pages read: 3
Page 0: shape=(1, 1, 1, 16, 16), axes=TZCYX
Page 1: shape=(1, 1, 1, 16, 16), axes=TZCYX
Page 2: shape=(1, 1, 1, 16, 16), axes=TZCYX
Note that imread has an optional argument return_list which is set to False
by default. If set to True, imread will always return a list of images and a
list of metadata dictionaries, even if the input file contains only a single page.
This can be useful for consistent handling of paginated stacks in batch processing
scenarios.
Reading Multi-File OME-TIFF Stacks
A multi-file OME-TIFF series consists of multiple TIFF files, each representing a single time point, channel, or Z-slice of a larger multidimensional dataset.
OMIO supports reading such multi-file OME-TIFF series via the imread function by
providing the file name of any one of the individual TIFF files in the series.
OMIO will automatically detect and read all files in the series, sort them correctly
based on their OME metadata, and assemble them into a single multidimensional NumPy
array along with the associated OME-compliant metadata:
fname_multifile_ometiff = "example_data/tif_dummy_data/tif_ome_multi_file_series/TZCYX_T5_Z10_C2_Z00_C0_T0.ome.tif"
image_multifile_ometiff, metadata_multifile_ometiff = om.imread(fname_multifile_ometiff)
print(f"Multi-file OME-TIFF image shape: {image_multifile_ometiff.shape}")
pprint.pprint(metadata_multifile_ometiff)
om.open_in_napari(image_multifile_ometiff, metadata_multifile_ometiff, fname_multifile_ometiff)
>>>
Multi-file OME-TIFF image shape: (5, 10, 2, 20, 100)
{'Annotations': {'Namespace': 'omio:metadata',
'OMIO_MultiSeriesDetected': False,
'OMIO_VERSION': '0.1.4',
'original_creation_or_change_date': '2025-12-27T16:57:29',
'original_filename': 'TZCYX_T5_Z10_C2_Z00_C0_T0.ome.tif',
'original_filetype': 'tif',
'original_metadata_type': 'OME_XML',
'original_parentfolder': 'example_data/tif_dummy_data/tif_ome_multi_file_series',
'spacing': 2.0,
'unit': 'micron'},
'Channel_Count': 2,
'PhysicalSizeX': 0.19,
'PhysicalSizeXUnit': 'micron',
'PhysicalSizeY': 0.19,
'PhysicalSizeYUnit': 'micron',
'PhysicalSizeZ': 2.0,
'PhysicalSizeZUnit': 'micron',
'SizeC': 2,
'SizeT': 5,
'SizeX': 100,
'SizeY': 20,
'SizeZ': 10,
'TimeIncrement': 3.0,
'TimeIncrementUnit': 's',
'axes': 'TZCYX',
'shape': (5, 10, 2, 20, 100)}
Note that this only works for multi-file OME-TIFF series where each individual TIFF file contains the necessary OME metadata to correctly sort and assemble the files into a multidimensional dataset. You cannot simply provide a list of arbitrary TIFF files and expect OMIO to assemble them correctly without the required OME metadata, even though the single TIFF files’ names may contain hints about their position in the series (for example Z-slice or time point).