Skip to main content

Read and write TIFF files

Project description

Tifffile is a Python library to

  1. store numpy arrays in TIFF (Tagged Image File Format) files, and

  2. read image and metadata from TIFF-like files used in bioimaging.

Image and metadata can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, SGI, NIHImage, ImageJ, MicroManager, FluoView, ScanImage, SEQ, GEL, SVS, SCN, SIS, BIF, ZIF (Zoomable Image File Format), QPTIFF (QPI), NDPI, and GeoTIFF files.

Image data can be read as numpy arrays or zarr arrays/groups from strips, tiles, pages (IFDs), SubIFDs, higher order series, and pyramidal levels.

Numpy arrays can be written to TIFF, BigTIFF, OME-TIFF, and ImageJ hyperstack compatible files in multi-page, volumetric, pyramidal, memory-mappable, tiled, predicted, or compressed form.

A subset of the TIFF specification is supported, mainly 8, 16, 32 and 64-bit integer, 16, 32 and 64-bit float, grayscale and multi-sample images. Specifically, CCITT and OJPEG compression, chroma subsampling without JPEG compression, color space transformations, samples with differing types, or IPTC, ICC, and XMP metadata are not implemented.

TIFF, the Tagged Image File Format, was created by the Aldus Corporation and Adobe Systems Incorporated. BigTIFF allows for files larger than 4 GB. STK, LSM, FluoView, SGI, SEQ, GEL, QPTIFF, NDPI, SCN, SVS, ZIF, BIF, and OME-TIFF, are custom extensions defined by Molecular Devices (Universal Imaging Corporation), Carl Zeiss MicroImaging, Olympus, Silicon Graphics International, Media Cybernetics, Molecular Dynamics, PerkinElmer, Hamamatsu, Leica, ObjectivePathology, Roche Digital Pathology, and the Open Microscopy Environment consortium, respectively.

For command line usage run python -m tifffile --help

Author:

Christoph Gohlke

Organization:

Laboratory for Fluorescence Dynamics, University of California, Irvine

License:

BSD 3-Clause

Version:

2022.4.22

Requirements

This release has been tested with the following requirements and dependencies (other versions may work):

Revisions

2022.4.22

Pass 4753 tests. Add type hints for Python 3.10 (WIP). Fix mypy errors (breaking). Mark many parameters positional-only or keyword-only (breaking). Remove deprecated ‘pages’ parameter from imread (breaking). Remove deprecated ‘compress’ and ‘ijmetadata’ write parameters (breaking). Remove deprecated ‘fastij’ and ‘movie’ parameters from TiffFile (breaking). Remove deprecated ‘multifile’ parameters from TiffFile (breaking). Remove deprecated ‘tif’ parameter from TiffTag.overwrite (breaking). Remove deprecated ‘file’ parameter from FileSequence.asarray (breaking). Remove option to pass imread class to FileSequence (breaking). Remove optional parameters from ‘__str__’ functions (breaking). Rename TiffPageSeries.offset to dataoffset (breaking) Change TiffPage.pages to None if no SubIFDs are present (breaking). Change TiffPage.index to int (breaking). Change TiffPage.is_contiguous, is_imagej, and is_shaped to bool (breaking). Add TiffPage imagej_description and shaped_description properties. Add TiffFormat abstract base class. Deprecate ‘lazyattr’ and use functools.cached_property instead (breaking). Julian_datetime raises ValueError for dates before year 1 (breaking). Regressed import time due to typing.

2022.4.8

Add _ARRAY_DIMENSIONS attributes to ZarrTiffStore. Allow C instead of S axis when writing OME-TIFF. Fix writing OME-TIFF with separate samples. Fix reading unsqueezed pyramidal OME-TIFF series.

2022.3.25

Fix another ValueError using ZarrStore with zarr >= 2.11.0 (tiffslide #25). Add parser for Hamamatsu streak metadata. Improve hexdump.

2022.3.16

Use multi-threading to compress strips and tiles. Raise TiffFileError when reading corrupted strips and tiles (#122). Fix ScanImage single channel count (#121). Add parser for AstroTIFF FITS metadata.

2022.2.9

Fix ValueError using multiscale ZarrStore with zarr >= 2.11.0. Raise KeyError if ZarrStore does not contain key. Limit number of warnings for missing files in multifile series. Allow to save colormap to 32-bit ImageJ files (#115).

2022.2.2

Fix TypeError when second ImageDescription tag contains non-ASCII (#112). Fix parsing IJMetadata with many IJMetadataByteCounts (#111). Detect MicroManager NDTiffv2 header (not tested). Remove cache from ZarrFileSequenceStore (use zarr.LRUStoreCache). Raise limit on maximum number of pages. Use J2K format when encoding JPEG2000 segments. Formally deprecate imsave and TiffWriter.save. Drop support for Python 3.7 and numpy < 1.19 (NEP29).

2021.11.2

Lazy-load non-essential tag values (breaking). Warn when reading from closed file. Support ImageJ ‘prop’ metadata type (#103). Support writing indexed ImageJ format. Fix multi-threaded access of multi-page Zarr stores with chunkmode 2. Raise error if truncate is used with compression, packints, or tile. Read STK metadata without UIC2tag. Improve log and warning messages (WIP). Improve string representation of large tag values.

2021.10.12

Revert renaming of ‘file’ parameter in FileSequence.asarray (breaking). Deprecate ‘file’ parameter in FileSequence.asarray.

2021.10.10

Disallow letters as indices in FileSequence; use categories (breaking). Do not warn of missing files in FileSequence; use files_missing property. Support predictors in ZarrTiffStore.write_fsspec. Add option to specify zarr group name in write_fsspec. Add option to specify categories for FileSequence patterns (#76). Add option to specify chunk shape and dtype for ZarrFileSequenceStore. Add option to tile ZarrFileSequenceStore and FileSequence.asarray. Add option to pass additional zattrs to Zarr stores. Detect Roche BIF files.

2021.8.30

Fix horizontal differencing with non-native byte order. Fix multi-threaded access of memory-mappable, multi-page Zarr stores (#67).

2021.8.8

Fix tag offset and valueoffset for NDPI > 4 GB (#96).

2021.7.30

Deprecate first parameter to TiffTag.overwrite (no longer required). TiffTag init API change (breaking). Detect Ventana BIF series and warn that tiles are not stitched. Enable reading PreviewImage from RAW formats (#93, #94). Work around numpy.ndarray.tofile is very slow for non-contiguous arrays. Fix issues with PackBits compression (requires imagecodecs 2021.7.30).

2021.7.2

Decode complex integer images found in SAR GeoTIFF. Support reading NDPI with JPEG-XR compression. Deprecate TiffWriter RGB auto-detection, except for RGB24/48 and RGBA32/64.

2021.6.14

Set stacklevel for deprecation warnings (#89). Fix svs_description_metadata for SVS with double header (#88, breaking). Fix reading JPEG compressed CMYK images. Support ALT_JPEG and JPEG_2000_LOSSY compression found in Bio-Formats. Log warning if TiffWriter auto-detects RGB mode (specify photometric).

2021.6.6

Fix TIFF.COMPESSOR typo (#85). Round resolution numbers that do not fit in 64-bit rationals (#81). Add support for JPEG XL compression. Add numcodecs compatible TIFF codec. Rename ZarrFileStore to ZarrFileSequenceStore (breaking). Add method to export fsspec ReferenceFileSystem from ZarrFileStore. Fix fsspec ReferenceFileSystem v1 for multifile series. Fix creating OME-TIFF with micron character in OME-XML.

2021.4.8

Fix reading OJPEG with wrong photometric or samplesperpixel tags (#75). Fix fsspec ReferenceFileSystem v1 and JPEG compression. Use TiffTagRegistry for NDPI_TAGS, EXIF_TAGS, GPS_TAGS, IOP_TAGS constants. Make TIFF.GEO_KEYS an Enum (breaking).

2021.3.31

Use JPEG restart markers as tile offsets in NDPI. Support version 1 and more codecs in fsspec ReferenceFileSystem (untested).

2021.3.17

Fix regression reading multi-file OME-TIFF with missing files (#72). Fix fsspec ReferenceFileSystem with non-native byte order (#56).

2021.3.16

TIFF is no longer a defended trademark. Add method to export fsspec ReferenceFileSystem from ZarrTiffStore (#56).

2021.3.5

Preliminary support for EER format (#68). Do not warn about unknown compression (#68).

2021.3.4

Fix reading multi-file, multi-series OME-TIFF (#67). Detect ScanImage 2021 files (#46). Shape new version ScanImage series according to metadata (breaking). Remove Description key from TiffFile.scanimage_metadata dict (breaking). Also return ScanImage version from read_scanimage_metadata (breaking). Fix docstrings.

2021.2.26

Squeeze axes of LSM series by default (breaking). Add option to preserve single dimensions when reading from series (WIP). Do not allow appending to OME-TIFF files. Fix reading STK files without name attribute in metadata. Make TIFF constants multi-thread safe and pickleable (#64). Add detection of NDTiffStorage MajorVersion to read_micromanager_metadata. Support ScanImage v4 files in read_scanimage_metadata.

2021.2.1

Fix multi-threaded access of ZarrTiffStores using same TiffFile instance. Use fallback zlib and lzma codecs with imagecodecs lite builds. Open Olympus and Panasonic RAW files for parsing, albeit not supported. Support X2 and X4 differencing found in DNG. Support reading JPEG_LOSSY compression found in DNG.

2021.1.14

Try ImageJ series if OME series fails (#54) Add option to use pages as chunks in ZarrFileStore (experimental). Fix reading from file objects with no readinto function.

2021.1.11

Fix test errors on PyPy. Fix decoding bitorder with imagecodecs >= 2021.1.11.

2021.1.8

Decode float24 using imagecodecs >= 2021.1.8. Consolidate reading of segments if possible.

2020.12.8

Refer to the CHANGES file for older revisions.

Notes

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

Tested on little-endian platforms only.

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

Tifffile relies on the imagecodecs package for encoding and decoding LZW, JPEG, and other compressed image segments.

Several TIFF-like formats do not strictly adhere to the TIFF6 specification, some of which allow file or data sizes to exceed the 4 GB limit:

  • BigTIFF is identified by version number 43 and uses different file header, IFD, and tag structures with 64-bit offsets. It adds more data types. Tifffile can read and write BigTIFF files.

  • ImageJ hyperstacks store all image data, which may exceed 4 GB, contiguously after the first IFD. Files > 4 GB contain one IFD only. The size (shape and dtype) of the up to 6-dimensional image data can be determined from the ImageDescription tag of the first IFD, which is Latin-1 encoded. Tifffile can read and write ImageJ hyperstacks.

  • OME-TIFF stores up to 8-dimensional data in one or multiple TIFF of BigTIFF files. The 8-bit UTF-8 encoded OME-XML metadata found in the ImageDescription tag of the first IFD defines the position of TIFF IFDs in the high dimensional data. Tifffile can read OME-TIFF files, except when the OME-XML metadata are stored in a separate file. Tifffile can write numpy arrays to single-file OME-TIFF.

  • LSM stores all IFDs below 4 GB but wraps around 32-bit StripOffsets. The StripOffsets of each series and position require separate unwrapping. The StripByteCounts tag contains the number of bytes for the uncompressed data. Tifffile can read large LSM files.

  • STK (MetaMorph Stack) contains additional image planes stored contiguously after the image data of the first page. The total number of planes is equal to the counts of the UIC2tag. Tifffile can read STK files.

  • Hamamatsu NDPI uses some 64-bit offsets in the file header, IFD, and tag structures. Tag values/offsets can be corrected using high bits stored after IFD structures. Tifffile can read NDPI files > 4 GB. JPEG compressed segments with dimensions >65530 or missing restart markers are not decodable with libjpeg. Tifffile works around this limitation by separately decoding the MCUs between restart markers. BitsPerSample, SamplesPerPixel, and PhotometricInterpretation tags may contain wrong values, which can be corrected using the value of tag 65441.

  • Philips TIFF slides store wrong ImageWidth and ImageLength tag values for tiled pages. The values can be corrected using the DICOM_PIXEL_SPACING attributes of the XML formatted description of the first page. Tifffile can read Philips slides.

  • Ventana/Roche BIF slides store tiles and metadata in a BigTIFF container. Tiles may overlap and require stitching based on the TileJointInfo elements in the XMP tag. Volumetric scans are stored using the ImageDepth extension. Tifffile can read BIF and decode individual tiles, but does not perform stitching.

  • ScanImage optionally allows corrupted non-BigTIFF files > 2 GB. The values of StripOffsets and StripByteCounts can be recovered using the constant differences of the offsets of IFD and tag values throughout the file. Tifffile can read such files if the image data are stored contiguously in each page.

  • GeoTIFF sparse files allow strip or tile offsets and byte counts to be 0. Such segments are implicitly set to 0 or the NODATA value on reading. Tifffile can read GeoTIFF sparse files.

Other libraries for reading scientific TIFF files from Python:

Some libraries are using tifffile to write OME-TIFF files:

Other tools for inspecting and manipulating TIFF files:

References

Examples

Write a numpy array to a single-page RGB TIFF file:

>>> data = numpy.random.randint(0, 255, (256, 256, 3), 'uint8')
>>> imwrite('temp.tif', data, photometric='rgb')

Read the image from the TIFF file as numpy array:

>>> image = imread('temp.tif')
>>> image.shape
(256, 256, 3)

Write a 3D numpy array to a multi-page, 16-bit grayscale TIFF file:

>>> data = numpy.random.randint(0, 2**12, (64, 301, 219), 'uint16')
>>> imwrite('temp.tif', data, photometric='minisblack')

Read the whole image stack from the TIFF file as numpy array:

>>> image_stack = imread('temp.tif')
>>> image_stack.shape
(64, 301, 219)
>>> image_stack.dtype
dtype('uint16')

Read the image from the first page in the TIFF file as numpy array:

>>> image = imread('temp.tif', key=0)
>>> image.shape
(301, 219)

Read images from a selected range of pages:

>>> images = imread('temp.tif', key=range(4, 40, 2))
>>> images.shape
(18, 301, 219)

Iterate over all pages in the TIFF file and successively read images:

>>> with TiffFile('temp.tif') as tif:
...     for page in tif.pages:
...         image = page.asarray()

Get information about the image stack in the TIFF file without reading the image data:

>>> tif = TiffFile('temp.tif')
>>> len(tif.pages)  # number of pages in the file
64
>>> page = tif.pages[0]  # get shape and dtype of the image in the first page
>>> page.shape
(301, 219)
>>> page.dtype
dtype('uint16')
>>> page.axes
'YX'
>>> series = tif.series[0]  # get shape and dtype of the first image series
>>> series.shape
(64, 301, 219)
>>> series.dtype
dtype('uint16')
>>> series.axes
'QYX'
>>> tif.close()

Inspect the “XResolution” tag from the first page in the TIFF file:

>>> with TiffFile('temp.tif') as tif:
...     tag = tif.pages[0].tags['XResolution']
>>> tag.value
(1, 1)
>>> tag.name
'XResolution'
>>> tag.code
282
>>> tag.count
1
>>> tag.dtype
<DATATYPES.RATIONAL: 5>

Iterate over all tags in the TIFF file:

>>> with TiffFile('temp.tif') as tif:
...     for page in tif.pages:
...         for tag in page.tags:
...             tag_name, tag_value = tag.name, tag.value

Overwrite the value of an existing tag, e.g. XResolution:

>>> with TiffFile('temp.tif', mode='r+b') as tif:
...     _ = tif.pages[0].tags['XResolution'].overwrite((96000, 1000))

Write a floating-point ndarray and metadata using BigTIFF format, tiling, compression, and planar storage:

>>> data = numpy.random.rand(2, 5, 3, 301, 219).astype('float32')
>>> imwrite('temp.tif', data, bigtiff=True, photometric='minisblack',
...         compression='zlib', planarconfig='separate', tile=(32, 32),
...         metadata={'axes': 'TZCYX'})

Write a 10 fps time series of volumes with xyz voxel size 2.6755x2.6755x3.9474 micron^3 to an ImageJ hyperstack formatted TIFF file:

>>> volume = numpy.random.randn(6, 57, 256, 256).astype('float32')
>>> imwrite('temp.tif', volume, imagej=True, resolution=(1./2.6755, 1./2.6755),
...         metadata={'spacing': 3.947368, 'unit': 'um', 'finterval': 1/10,
...                   'axes': 'TZYX'})

Read the volume and metadata from the ImageJ file:

>>> with TiffFile('temp.tif') as tif:
...     volume = tif.asarray()
...     axes = tif.series[0].axes
...     imagej_metadata = tif.imagej_metadata
>>> volume.shape
(6, 57, 256, 256)
>>> axes
'TZYX'
>>> imagej_metadata['slices']
57
>>> imagej_metadata['frames']
6

Create a TIFF file containing an empty image and write to the memory-mapped numpy array:

>>> memmap_image = memmap(
...     'temp.tif', shape=(256, 256, 3), dtype='float32', photometric='rgb'
... )
>>> type(memmap_image)
<class 'numpy.memmap'>
>>> memmap_image[255, 255, 1] = 1.0
>>> memmap_image.flush()
>>> del memmap_image

Memory-map and read contiguous image data in the TIFF file:

>>> memmap_image = memmap('temp.tif')
>>> memmap_image.shape
(256, 256, 3)
>>> memmap_image[255, 255, 1]
1.0
>>> del memmap_image

Write two numpy arrays to a multi-series TIFF file:

>>> series0 = numpy.random.randint(0, 255, (32, 32, 3), 'uint8')
>>> series1 = numpy.random.randint(0, 1023, (4, 256, 256), 'uint16')
>>> with TiffWriter('temp.tif') as tif:
...     tif.write(series0, photometric='rgb')
...     tif.write(series1, photometric='minisblack')

Read the second image series from the TIFF file:

>>> series1 = imread('temp.tif', series=1)
>>> series1.shape
(4, 256, 256)

Successively write the frames of one contiguous series to a TIFF file:

>>> data = numpy.random.randint(0, 255, (30, 301, 219), 'uint8')
>>> with TiffWriter('temp.tif') as tif:
...     for frame in data:
...         tif.write(frame, contiguous=True)

Append an image series to the existing TIFF file:

>>> data = numpy.random.randint(0, 255, (301, 219, 3), 'uint8')
>>> imwrite('temp.tif', data, photometric='rgb', append=True)

Create a TIFF file from a generator of tiles:

>>> data = numpy.random.randint(0, 2**12, (31, 33, 3), 'uint16')
>>> def tiles(data, tileshape):
...     for y in range(0, data.shape[0], tileshape[0]):
...         for x in range(0, data.shape[1], tileshape[1]):
...             yield data[y : y + tileshape[0], x : x + tileshape[1]]
>>> imwrite('temp.tif', tiles(data, (16, 16)), tile=(16, 16),
...         shape=data.shape, dtype=data.dtype, photometric='rgb')

Write two numpy arrays to a multi-series OME-TIFF file:

>>> series0 = numpy.random.randint(0, 255, (32, 32, 3), 'uint8')
>>> series1 = numpy.random.randint(0, 1023, (4, 256, 256), 'uint16')
>>> with TiffWriter('temp.ome.tif') as tif:
...     tif.write(series0, photometric='rgb')
...     tif.write(series1, photometric='minisblack',
...               metadata={'axes': 'ZYX', 'SignificantBits': 10,
...                         'Plane': {'PositionZ': [0.0, 1.0, 2.0, 3.0]}})

Write a tiled, multi-resolution, pyramidal, OME-TIFF file using JPEG compression. Sub-resolution images are written to SubIFDs:

>>> data = numpy.arange(1024*1024*3, dtype='uint8').reshape((1024, 1024, 3))
>>> with TiffWriter('temp.ome.tif', bigtiff=True) as tif:
...     options = dict(tile=(256, 256), photometric='rgb', compression='jpeg')
...     tif.write(data, subifds=2, **options)
...     # save pyramid levels to the two subifds
...     # in production use resampling to generate sub-resolutions
...     tif.write(data[::2, ::2], subfiletype=1, **options)
...     tif.write(data[::4, ::4], subfiletype=1, **options)

Access the image levels in the pyramidal OME-TIFF file:

>>> baseimage = imread('temp.ome.tif')
>>> second_level = imread('temp.ome.tif', series=0, level=1)
>>> with TiffFile('temp.ome.tif') as tif:
...     baseimage = tif.series[0].asarray()
...     second_level = tif.series[0].levels[1].asarray()

Iterate over and decode single JPEG compressed tiles in the TIFF file:

>>> with TiffFile('temp.ome.tif') as tif:
...     fh = tif.filehandle
...     for page in tif.pages:
...         for index, (offset, bytecount) in enumerate(
...             zip(page.dataoffsets, page.databytecounts)
...         ):
...             _ = fh.seek(offset)
...             data = fh.read(bytecount)
...             tile, indices, shape = page.decode(
...                 data, index, jpegtables=page.jpegtables
...             )

Use zarr to read parts of the tiled, pyramidal images in the TIFF file:

>>> import zarr
>>> store = imread('temp.ome.tif', aszarr=True)
>>> z = zarr.open(store, mode='r')
>>> z
<zarr.hierarchy.Group '/' read-only>
>>> z[0]  # base layer
<zarr.core.Array '/0' (1024, 1024, 3) uint8 read-only>
>>> z[0][256:512, 512:768].shape  # read a tile from the base layer
(256, 256, 3)
>>> store.close()

Read images from a sequence of TIFF files as numpy array:

>>> imwrite('temp_C001T001.tif', numpy.random.rand(64, 64))
>>> imwrite('temp_C001T002.tif', numpy.random.rand(64, 64))
>>> image_sequence = imread(['temp_C001T001.tif', 'temp_C001T002.tif'])
>>> image_sequence.shape
(2, 64, 64)
>>> image_sequence.dtype
dtype('float64')

Read an image stack from a series of TIFF files with a file name pattern as numpy or zarr arrays:

>>> image_sequence = TiffSequence('temp_C0*.tif', pattern=r'_(C)(\d+)(T)(\d+)')
>>> image_sequence.shape
(1, 2)
>>> image_sequence.axes
'CT'
>>> data = image_sequence.asarray()
>>> data.shape
(1, 2, 64, 64)
>>> with image_sequence.aszarr() as store:
...     zarr.open(store, mode='r')
<zarr.core.Array (1, 2, 64, 64) float64 read-only>
>>> image_sequence.close()

Write the zarr store to a fsspec ReferenceFileSystem in JSON format:

>>> with image_sequence.aszarr() as store:
...     store.write_fsspec('temp.json', url='file://')

Open the fsspec ReferenceFileSystem as a zarr array:

>>> import fsspec
>>> import tifffile.numcodecs
>>> tifffile.numcodecs.register_codec()
>>> mapper = fsspec.get_mapper(
...     'reference://', fo='temp.json', target_protocol='file')
>>> zarr.open(mapper, mode='r')
<zarr.core.Array (1, 2, 64, 64) float64 read-only>

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tifffile-2022.4.22.tar.gz (288.7 kB view hashes)

Uploaded Source

Built Distribution

tifffile-2022.4.22-py3-none-any.whl (191.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page