tifffile

repository·master·Indexed 20 days ago

https://github.com/cgohlke/tifffile

A comprehensive Python library for reading and writing TIFF-based image formats, optimized for bioimaging and scientific data. It supports NumPy and Zarr arrays, BigTIFF, OME-TIFF, GeoTIFF, and various proprietary formats such as Zeiss LSM, Leica SCN, and Hamamatsu NDPI. Key features include support for multi-dimensional structures, pyramidal levels, and various compression schemes via imagecodecs.

Tokens
3.6K
Snippets
11
Records
15
Agent score
21%

What's inside tifffile

  1. Overview of tifffile capabilities

    master

    Tifffile is a comprehensive Python library designed for:

    1. Storing NumPy arrays in TIFF (Tagged Image File Format) files.
    2. Reading image and metadata from a wide variety of bioimaging formats, including TIFF, BigTIFF, OME-TIFF, GeoTIFF, Adobe DNG, ZIF, and many proprietary formats (e.g., Zeiss LSM, Leica SCN, Hamamatsu NDPI).

    Key Features:

    • Data Access: Read image data as NumPy arrays or Zarr arrays/groups from strips, tiles, pages (IFDs), SubIFDs, higher-order series, and pyramidal levels.
    • Writing Support: Write to TIFF, BigTIFF, OME-TIFF, and ImageJ hyperstack compatible files in multi-page, volumetric, pyramidal, memory-mappable, tiled, predicted, or compressed forms.
    • Compression: Supports many schemes via the imagecodecs library, including LZW, Deflate, JPEG, JPEG 2000, Zstd, WebP, PNG, and more.
    • Advanced Inspection: Inspect TIFF structures, read multi-dimensional file sequences, write fsspec ReferenceFileSystems, and parse proprietary metadata.
  2. Limitations of Tifffile implementation

    master

    While Tifffile supports a wide range of TIFF specifications, the following features are not implemented:

    • OJPEG compression
    • Chroma subsampling without JPEG compression
    • Color space transformations
    • Samples with differing types
    • IPTC, ICC, and XMP metadata
  3. Use Zarr to read tiled and pyramidal TIFF files

    master

    For large, tiled, or pyramidal TIFF files (like OME-TIFF), you can interface with zarr for efficient chunked access.

    1. Read as Zarr store: Use imread(..., return_as='zarr') to get a ZarrTiffStore.
    2. Dask integration: Load the store into a Dask array using dask.array.from_zarr(store, 'layer_name').
    3. Reference Filesystem: You can write the Zarr store to a JSON file using store.write_fsspec(...) and later reopen it using kerchunk and zarr without the original TIFF file.
    import zarr
    import dask.array
    from tifffile import imread
    
    # Access as Zarr
    store = imread('temp.ome.tif', return_as='zarr')
    z = zarr.open(store, mode='r')
    print(z['0']) # Access base layer
    
    # Access as Dask
    dask_array = dask.array.from_zarr(store, '0')
    
    # Export to JSON reference
    store.write_fsspec('temp.ome.tif.json', url='file://', zarr_format=3)
  4. Supported TIFF and TIFF-like formats

    master

    Tifffile supports a large subset of the TIFF6 specification (mainly 1-32, 64-bit integer, 16, 32, and 64-bit float, grayscale and multi-sample images). It also provides support for several specialized TIFF-like formats used in scientific imaging:

    • BigTIFF: Uses 64-bit offsets and different header/tag structures. Supported for reading and writing.
    • ImageJ hyperstacks: Contiguous image data after the first IFD; size/shape determined from the ImageDescription tag. Supported for reading and writing.
    • OME-TIFF: Uses OME-XML metadata in the ImageDescription tag to define high-dimensional data. Supports reading and writing NumPy arrays to single-file OME-TIFF.
    • Micro-Manager NDTiff: Multi-dimensional data across one or more TIFF files using an NDTiff.index file. Supported for reading.
    • Micro-Manager MMStack: 6D image data in one or more TIFF files using non-TIFF binary structures and JSON. Supported for reading.
    • Carl Zeiss LSM: Handles 32-bit StripOffsets that wrap around 4 GB. Supported for reading.
    • MetaMorph STK: Additional image planes stored after the first page's data. Supported for reading.
    • ZIF (Zoomable Image File): A BigTIFF subspecification. Supported for reading and writing.
    • Hamamatsu NDPI: Uses 64-bit offsets and specific tag handling for large files. Supported for reading.
    • Philips TIFF: Uses padded ImageWidth and ImageLength tags. Supported for reading.
    • Ventana/Roche BIF: BigTIFF container for tiles and metadata. Supports reading and decoding individual tiles (does not perform stitching).
    • ScanImage: Supports reading corrupted non-BigTIFF files > 2 GB if data is contiguous.
    • GeoTIFF sparse: Supports reading files with zero offset/byte count segments.
    • Tifffile shaped: A custom format using JSON in the ImageDescription tag for array shape and metadata. Supports truncated series.
  5. Install tifffile with all dependencies

    master

    To install tifffile along with all optional dependencies (such as imagecodecs for compression support, xarray for DataArray reading, and matplotlib for plotting), use the [all] extra via pip.

    python -m pip install -U tifffile[all]
  6. Inspect TIFF metadata and structure using TiffFile

    master

    For detailed inspection without loading all image data, use the TiffFile class. This allows you to access page information, tags, and image series.

    • tif.pages: An iterable of all pages in the file.
    • tif.series: An iterable of image series (e.g., OME or generic).
    • page.tags: A dictionary-like object containing TIFF tags for a specific page.
    • page.asarray(): Reads the image data for a specific page.
    • tif.asxarray(): Returns the image stack as an xarray.DataArray (useful for ImageJ hyperstacks).
    • tif.imagej_metadata: Accesses ImageJ-specific metadata.
    from tifffile import TiffFile
    
    with TiffFile('temp.tif') as tif:
        # Get number of pages
        num_pages = len(tif.pages)
        
        # Inspect first page
        page = tif.pages[0]
        print(page.shape, page.dtype, page.axes)
        
        # Inspect tags
        tag = page.tags['XResolution']
        print(tag.value, tag.name, tag.code)
        
        # Inspect series
        series = tif.series[0]
        print(series.shape, series.dtype, series.axes)
    
    # Read as xarray
    with TiffFile('temp.tif') as tif:
        volume = tif.asxarray()
        metadata = tif.imagej_metadata
  7. Memory-map TIFF data with memmap()

    master

    Use memmap() to access contiguous image data without loading the entire file into memory.

    Note: Memory-mapping does not work with compressed or tiled TIFF files.

    from tifffile import memmap
    
    # Read existing file
    memmap_volume = memmap('temp.tif')
    
    # Create a new empty file via memmap
    memmap_image = memmap(
        'temp.tif',
        shape=(256, 256, 3),
        dtype='float32',
        photometric='rgb'
    )
    memmap_image[255, 255, 1] = 1.0
    memmap_image.flush()
  8. Read TIFF files as NumPy arrays with imread()

    master

    Use imread() to quickly load TIFF data into NumPy arrays.

    • To read the entire stack: imread('file.tif').
    • To read a specific page: imread('file.tif', key=0).
    • To read a range of pages: imread('file.tif', key=range(4, 40, 2)).
    • To read a specific series: imread('file.tif', series=1).
    • To read a sequence of files as a single array: imread(['file1.tif', 'file2.tif'], ioworkers=2).
    • To read a pyramidal level: imread('file.tif', series=0, level=1).
    # Read whole stack
    image_stack = imread('temp.tif')
    
    # Read specific page
    image = imread('temp.tif', key=0)
    
    # Read range of pages
    images = imread('temp.tif', key=range(4, 40, 2))
    
    # Read from a sequence of files
    image_sequence = imread(['temp_C001T001.tif', 'temp_C002T001.tif'], ioworkers=2)
  9. Modify TIFF tags using TiffFile

    master

    You can overwrite existing tags in a TIFF file by opening it in 'r+' mode using TiffFile.

    from tifffile import TiffFile
    
    with TiffFile('temp.tif', mode='r+') as tif:
        # Overwrite the XResolution tag on the first page
        tif.pages[0].tags['XResolution'].overwrite((96000, 1000))
  10. Handle image sequences with TiffSequence

    master

    If your data is spread across multiple TIFF files, TiffSequence can treat them as a single multi-dimensional array.

    • Pattern matching: Provide a file pattern (e.g., 'temp_C0*.tif') and a regex pattern to extract dimensions (like Channel 'C' or Time 'T').
    • Accessing data: Use .asarray() for a NumPy array or .aszarr() to get a Zarr interface for chunked access.
    from tifffile import TiffSequence
    
    # Load sequence using pattern
    image_sequence = TiffSequence('temp_C0*.tif', pattern=r'_(C)(\d+)(T)(\d+)')
    print(image_sequence.shape, image_sequence.axes)
    
    # Get NumPy array
    data = image_sequence.asarray()
    
    # Get Zarr interface
    store = image_sequence.aszarr()
  11. Write NumPy arrays to TIFF files with imwrite()

    master

    Use imwrite() to save NumPy arrays as TIFF files. You can specify various parameters to control the output format, such as photometric (e.g., 'rgb', 'minisblack'), planarconfig (e.g., 'separate' for planar RGB), extrasamples (e.g., ['unassalpha'] for unassociated alpha), and bigtiff=True for files exceeding 4GB.

    For advanced usage, you can specify tile dimensions, compression (e.g., 'zlib', 'jpeg'), predictor, and custom metadata.

    import numpy
    # Write a single-page RGB TIFF
    data = numpy.random.randint(0, 255, (256, 256, 3), 'uint8')
    imwrite('temp.tif', data, photometric='rgb')
    
    # Write a multi-dimensional array with advanced options
    data = numpy.random.rand(2, 5, 3, 301, 219).astype('float32')
    imwrite(
        'temp.tif',
        data,
        bigtiff=True,
        photometric='rgb',
        planarconfig='separate',
        tile=(32, 32),
        compression='zlib',
        compressionargs={'level': 8},
        predictor=True,
        metadata={'axes': 'TZCYX'},
    )
  12. Write multi-series and OME-TIFF files with TiffWriter

    master

    Use TiffWriter for fine-grained control when writing multiple series or complex formats like OME-TIFF.

    • Multi-series: Call tif.write() multiple times within a with TiffWriter(...) block. Note that other readers might not recognize multiple series unless you use OME-TIFF.
    • OME-TIFF: Use subifds to write pyramidal levels (sub-resolutions) and provide a metadata dictionary containing OME-compliant keys (e.g., axes, PhysicalSizeX, Channel).
    • Contiguous writing: Use contiguous=True in tif.write() to write frames of a single series successively.
    from tifffile import TiffWriter
    
    # Write multiple series
    with TiffWriter('temp.tif') as tif:
        tif.write(series0, photometric='rgb')
        tif.write(series1, photometric='minisblack')
    
    # Write OME-TIFF with pyramids
    with TiffWriter('temp.ome.tif', bigtiff=True) as tif:
        metadata = {'axes': 'TCYXS', 'PhysicalSizeX': 0.29, 'PhysicalSizeXUnit': 'µm'}
        options = {'photometric': 'rgb', 'tile': (128, 128), 'compression': 'jpeg'}
        
        tif.write(data, subifds=2, resolution=(1e4/0.29, 1e4/0.29), metadata=metadata, **options)
        
        # Write pyramid levels to subifds
        for level in range(2):
            mag = 2 ** (level + 1)
            tif.write(data[..., ::mag, ::mag, :], subfiletype=1, **options)