Skip to content

cgohlke/czifile

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Read Carl Zeiss image files (CZI)

Czifile is a Python library for reading image data and metadata from Carl Zeiss Image (CZI) files, the native file format of ZEN by Carl Zeiss Microscopy GmbH.

  • Pure-Python implementation under BSD-3-Clause license.
  • Single-call array access to scenes and spatial ROIs.
  • Xarray DataArray output with physical axis coordinates.
  • Multi-scene merging into a single array.
  • Per-dimension selection with integer, slice, and list indexing.
  • Plane-by-plane iterator.
  • Pyramid-level access for multi-resolution images.
  • Tile upsampling (Fast Airyscan), downsampling (PALM), and stitching (single-point FCS) with optional stored-resolution output.
  • Support for compression schemes (Zstd and JPEG XR) and pixel type promotion across channels.
  • Direct access to every ZISRAW segment and file-level attachments.
Author:Christoph Gohlke
License:BSD-3-Clause
Version:2026.3.14
DOI:10.5281/zenodo.14948581

Quickstart

Install the czifile package and all dependencies from the Python Package Index:

python -m pip install -U czifile[all]

See Examples for using the programming interface.

Source code, examples, and support are available on GitHub.

Requirements

This revision was tested with the following requirements and dependencies (other versions may work):

Revisions

2026.3.14

  • Add storedsize option to return pixel data at stored resolution.
  • Allow sequence and slice of scene indices in imread and asarray/asxarray.
  • Interpret dimension slice selection as absolute coordinates.
  • Fix CziImagePlanes not upsampling Airyscan fast-scan tiles.
  • Fix CziImagePlanes not downsampling PALM super-resolution tiles.
  • Add command line options to select dimensions.

2026.3.12

  • Rewrite with many breaking changes.
  • Support Zstd compression schemes.
  • Support reading subblock masks.
  • Add CziFile.scenes interface.
  • Add pyramid level access via CziImage.levels.
  • Add option to read subset of image data.
  • Add option to iterate over image planes in any dimension order.
  • Add xarray-style attributes.
  • Add asxarray method to return image as xarray DataArray with metadata.
  • Add fillvalue and maxworkers parameters to asarray.
  • Add option to specify pixel type.
  • Promote pixel type when channels have mixed types.
  • Remove Mosaic dimension from CziDirectoryEntryDV.dims; use mosaic_index.
  • Reduce caching of CziDirectoryEntryDV properties.
  • Remove resize and order parameters from asarray (breaking).
  • Remove czi2tif function and command line script.
  • Prefix public class names with Czi.
  • Raise CziFileError for issues with CZI file structure.
  • Use logging instead of warnings.
  • Improve representation of instances.
  • Add pytest-based unit tests.
  • Add type hints.
  • Convert docstrings to Google style with Sphinx directives.
  • Remove imagecodecs-lite fallback; require imagecodecs.
  • Remove scipy/ndimage dependency.
  • Make tifffile an optional dependency.
  • Drop support for Python < 3.12 and numpy < 2 (SPEC 0).

2019.7.2.3

Refer to the CHANGES file for older revisions.

Notes

The API is not stable yet and might change between revisions.

Python 32-bit versions are deprecated. Python < 3.12 are no longer supported.

"ZEISS" and "Carl Zeiss" are registered trademarks of Carl Zeiss AG.

The ZISRAW file format design specification [1] is confidential and the license agreement does not permit to write data into CZI files.

Only a subset of the 2016 specification is implemented. Specifically, multi-file images and topography images are not supported. Some features are untested due to lack of sample files.

Tested on Windows with a few example files only.

Czifile relies on the imagecodecs package for decoding LZW, ZStandard, JPEG, and JPEG XR compressed images.

Other libraries for reading CZI files (all GPL or LGPL licensed): libczi, pylibCZIrw, bioio-czi, bio-formats, libCZI (deprecated), and pylibczi (deprecated).

References

  1. ZISRAW (CZI) File Format Design Specification Release Version 1.2.2. "CZI 07-2016/CZI-DOC ZEN 2.3/DS_ZISRAW-FileFormat.pdf" (confidential).

Examples

Read image data of the first scene from a CZI file as numpy array:

>>> image = imread('Example.czi')
>>> assert image.shape == (2, 2, 3, 486, 1178)
>>> assert image.dtype == 'uint16'

Access scenes, shape, and metadata:

>>> with CziFile('Example.czi') as czi:
...     assert len(czi.scenes) == 3
...     img = czi.scenes[0]  # 0 is the absolute coordinate of the first scene
...     assert img.shape == (2, 2, 3, 486, 1178)
...     assert img.dims == ('T', 'C', 'Z', 'Y', 'X')
...     assert img.dtype == 'uint16'
...     assert img.compression.name == 'ZSTDHDR'
...     assert list(img.channels) == ['DAPI', 'EGFP']
...     assert czi.metadata().startswith('<ImageDocument>')
...

Select dimensions and read as numpy array:

>>> with CziFile('Example.czi') as czi:
...     img = czi.scenes[0]
...     assert img.sizes == {'T': 2, 'C': 2, 'Z': 3, 'Y': 486, 'X': 1178}
...
...     # integer selection: fix T=0 and C=0; result has Z, but no T or C axis
...     volume = img(T=0, C=0).asarray()
...     assert volume.shape == (3, 486, 1178)
...
...     # None selection: keep all values but reorder dimensions
...     # dims order follows the kwargs order, then spatial dims
...     # T (unspecified) comes first, then C, Z (in kwargs order), then Y X
...     tczyx = img(C=None, Z=None).asarray()
...     assert tczyx.shape == (2, 2, 3, 486, 1178)
...
...     # read in C-outer, Z-inner, T-innermost order with parallelism
...     arr = img(C=None, Z=None, T=None).asarray(maxworkers=8)
...     assert arr.shape == (2, 3, 2, 486, 1178)  # 'C', 'Z', 'T', 'Y', 'X'
...
...     # img.bbox gives (x, y, width, height) in global CZI coordinates
...     x0, y0, *_ = img.bbox
...     plane_roi = img(T=0, C=0, roi=(x0, y0, 128, 128)).asarray()
...     assert plane_roi.shape == (3, 128, 128)  # 'Z', 'Y', 'X'
...
...     # fill pixels outside subblock coverage with a specific value
...     padded = img(C=0, roi=(0, 0, 2048, 2048)).asarray(fillvalue=0)
...     assert padded.shape == (2, 3, 2048, 2048)  # 'T', 'Z', 'Y', 'X'
...

Iterate individual Y/X planes:

>>> import numpy
>>> with CziFile('Example.czi') as czi:
...     img = czi.scenes[0]
...
...     # img has non-spatial dims T, C, Z
...     # unspecified dims (T) come first, then kwargs order (C, Z)
...     # iterates T-outer, C-middle, Z-inner
...     for plane in img(C=None, Z=None).planes:
...         assert plane.shape == (486, 1178)
...
...     # iterate with absolute coordinate values
...     for coords, plane in img(C=None, Z=None).planes.items():
...         assert plane.shape == (486, 1178)
...         assert len(coords) == 3  # (t_coord, c_coord, z_coord)
...
...     # assemble a full array plane-by-plane (equivalent to asarray(),
...     # but much slower - use asarray() for bulk reads):
...     # spatial dims are always last, so zip truncates at len(coords),
...     # skipping Y/X from sel.start and converting to 0-based positions.
...     sel = img(C=None, Z=None)
...     out = numpy.empty(sel.shape, sel.dtype)
...     for coords, plane in sel.planes.items():
...         out[tuple(i - j for i, j in zip(coords, sel.start))] = plane
...     assert numpy.array_equal(out, sel.asarray())
...
...     # indexed access: pass the same absolute coordinate values
...     # that .planes.items() / .planes.keys() returns
...     plane = img(C=None, Z=None).planes[0, 0, 0]  # T=0, C=0, Z=0
...     assert plane.shape == (486, 1178)
...

Read image as xarray DataArray with physical coordinates and attributes:

>>> with CziFile('Example.czi') as czi:
...     xarr = czi.scenes[0].asxarray()
...     assert xarr.name == 'Scene 0'
...     assert xarr.sizes == {'T': 2, 'C': 2, 'Z': 3, 'Y': 486, 'X': 1178}
...     assert xarr.coords['X'].size == 1178  # physical axis coordinates
...

Access multiple scenes:

>>> with CziFile('Example.czi') as czi:
...     # iterate scenes individually and read as arrays
...     for scene in czi.scenes.values():
...         arr = scene.asarray()
...
...     # query which scenes (indices) are available
...     assert list(czi.scenes.keys()) == [0, 1, 2]
...
...     # select the second scene
...     assert czi.scenes[1].sizes == {
...         'T': 2,
...         'C': 2,
...         'Z': 3,
...         'Y': 256,
...         'X': 256,
...     }
...
...     # merge selected scenes into one
...     scenes = czi.scenes(scene=[0, 1])  # first 2 scenes
...     assert scenes.sizes == {'T': 2, 'C': 2, 'Z': 3, 'Y': 1109, 'X': 1760}
...
...     # merge all scenes into one
...     scenes = czi.scenes()
...     assert scenes.sizes == {'T': 2, 'C': 2, 'Z': 3, 'Y': 2055, 'X': 2581}
...

Access pyramid levels:

>>> with CziFile('Example.czi') as czi:
...     img = czi.scenes[0]
...     assert img.is_pyramid
...     assert len(img.levels) == 2  # full resolution + 1 downsampled level
...     assert img.levels[0] is img  # full resolution level is the same as img
...     overview = img.levels[1]  # lowest-res level
...     assert overview.sizes == {'T': 2, 'C': 2, 'Z': 3, 'Y': 243, 'X': 589}
...

Access attachments:

>>> with CziFile('Example.czi') as czi:
...     for attachment in czi.attachments():
...         name = attachment.attachment_entry.name
...         data = attachment.data()  # decoded (ndarray, tuple, bytes...)
...         raw = attachment.data(raw=True)  # always bytes
...
...     # convenience shortcut for TimeStamps attachment data
...     assert czi.timestamps.shape == (2,)
...

Low-level access to CZI file segments:

>>> with CziFile('Example.czi') as czi:
...     # file header: version, GUIDs, and segment offsets
...     hdr = czi.header
...     assert hdr.version == (1, 0)
...     assert str(hdr.file_guid) == 'f8a61493-053e-c94e-bae0-bc7e96d18997'
...     assert not hdr.update_pending
...
...     # iterate all subblock segments sequentially via the directory
...     for sb in czi.subblocks():
...         entry = sb.directory_entry
...         assert entry.dims == ('H', 'T', 'C', 'Z', 'Y', 'X', 'S')
...         assert entry.start == (0, 0, 0, 0, 0, 582, 0)
...         assert entry.shape == (1, 1, 1, 1, 486, 1178, 1)
...         assert entry.stored_shape == (1, 1, 1, 1, 243, 589, 1)
...         assert entry.compression == CziCompressionType.ZSTDHDR
...         assert sb.data().shape == (1, 1, 1, 1, 243, 589, 1)  # stored_shape
...         assert isinstance(sb.data(raw=True), bytes)
...         assert sb.metadata().startswith('<METADATA>')
...         break  # just the first subblock segment for demonstration
...
...     # walk all file segments by type using their ZISRAW segment IDs
...     for segdata in czi.segments(CziSegmentId.ZISRAWSUBBLOCK):
...         assert isinstance(segdata, CziSubBlockSegmentData)
...
...     # direct low-level segment header at a known file offset
...     seg = CziSegment(czi, czi.header.directory_position)
...     assert seg.sid == CziSegmentId.ZISRAWDIRECTORY
...     assert seg.used_size == 68768
...     seg_data = seg.data()
...     assert isinstance(seg_data, CziSubBlockDirectorySegmentData)
...

View the images and metadata in a CZI file from the console:

$ python -m czifile Example.czi