Read and write TIFF files for scientific and bioimaging applications with comprehensive format support
Object-oriented interfaces for detailed TIFF file manipulation, providing granular control over reading and writing operations. These classes offer comprehensive access to TIFF file structure, metadata, and advanced features beyond the simple imread/imwrite functions.
Primary class for reading TIFF files with comprehensive metadata access and flexible data extraction options.
class TiffFile:
def __init__(
self,
file,
*,
mode='r',
name=None,
offset=None,
size=None,
omexml=None,
**is_flags
):
"""
Initialize TIFF file reader.
Parameters:
- file: str, PathLike, or file handle to TIFF file
- mode: str, file opening mode ('r', 'r+')
- name: str, name within container file
- offset: int, byte offset in file
- size: int, number of bytes to read
- omexml: str, override OME-XML metadata
- **is_flags: format detection flags (is_ome, is_imagej, etc.)
"""
def asarray(
self,
key=None,
series=None,
level=None,
squeeze=None,
out=None,
maxworkers=None,
**kwargs
):
"""
Return image data as NumPy array.
Parameters:
- key: int, slice, or sequence of page indices
- series: int, series index for multi-series files
- level: int, pyramid level for multi-resolution files
- squeeze: bool, remove singleton dimensions
- out: array-like, pre-allocated output array
- maxworkers: int, number of worker threads
Returns:
- np.ndarray: Image data array
"""
def aszarr(
self,
key=None,
series=None,
level=None,
chunkmode=None,
fillvalue=None,
zattrs=None,
**kwargs
):
"""
Return image data as Zarr store.
Parameters:
- key: int, slice, or sequence of page indices
- series: int, series index
- level: int, pyramid level
- chunkmode: CHUNKMODE enum, chunking strategy
- fillvalue: numeric, fill value for missing data
- zattrs: dict, additional Zarr attributes
Returns:
- ZarrTiffStore: Zarr store interface
"""
def close(self):
"""Close the TIFF file."""
# Properties
@property
def pages(self):
"""TiffPages: Sequence of pages in file."""
@property
def series(self):
"""list: Image series in file."""
@property
def byteorder(self):
"""str: Byte order of file ('<', '>')."""
@property
def flags(self):
"""dict: File format detection flags."""
@property
def filename(self):
"""str: Name of the file."""
# Metadata properties
@property
def ome_metadata(self):
"""dict: OME-XML metadata."""
@property
def imagej_metadata(self):
"""dict: ImageJ metadata."""
@property
def lsm_metadata(self):
"""dict: LSM metadata."""
@property
def stk_metadata(self):
"""dict: MetaMorph STK metadata."""
@property
def gdal_metadata(self):
"""dict: GDAL metadata."""
@property
def shaped_metadata(self):
"""tuple: Shaped array metadata."""# Basic file reading with metadata access
with tifffile.TiffFile('image.tif') as tif:
print(f"Pages: {len(tif.pages)}")
print(f"Shape: {tif.pages[0].shape}")
print(f"Description: {tif.pages[0].description}")
data = tif.asarray()
# Access specific series in multi-series file
with tifffile.TiffFile('multi_series.ome.tif') as tif:
print(f"Number of series: {len(tif.series)}")
series0 = tif.asarray(series=0)
series1 = tif.asarray(series=1)
# Read pyramid levels
with tifffile.TiffFile('pyramid.tif') as tif:
full_res = tif.asarray(level=0)
thumbnail = tif.asarray(level=2)
# Access as Zarr store for large files
with tifffile.TiffFile('large.tif') as tif:
zarr_store = tif.aszarr()
chunk = zarr_store[1000:2000, 1000:2000]
# Examine metadata
with tifffile.TiffFile('ome.tif') as tif:
if tif.ome_metadata:
print("OME metadata:", tif.ome_metadata)
if tif.imagej_metadata:
print("ImageJ metadata:", tif.imagej_metadata)Comprehensive class for writing TIFF files with detailed control over format, compression, and metadata.
class TiffWriter:
def __init__(
self,
file,
*,
mode='w',
bigtiff=None,
byteorder=None,
append=False,
imagej=False,
ome=None,
shaped=None
):
"""
Initialize TIFF file writer.
Parameters:
- file: str, PathLike, or file handle for output
- mode: str, file opening mode ('w', 'x', 'r+')
- bigtiff: bool, create BigTIFF format
- byteorder: str, byte order ('<', '>', '=', '|')
- append: bool, append to existing file
- imagej: bool, create ImageJ-compatible format
- ome: bool, create OME-TIFF format
- shaped: bool, create shaped array format
"""
def write(
self,
data,
*,
shape=None,
dtype=None,
photometric=None,
planarconfig=None,
extrasamples=None,
tile=None,
compression=None,
compressionargs=None,
predictor=None,
description=None,
datetime=None,
resolution=None,
metadata=None,
extratags=None,
contiguous=False,
**kwargs
):
"""
Write image data to TIFF file.
Parameters:
- data: array-like, image data to write
- shape: tuple, explicit data shape
- dtype: dtype, data type
- photometric: PHOTOMETRIC enum, color interpretation
- planarconfig: PLANARCONFIG enum, sample organization
- extrasamples: sequence, extra sample interpretation
- tile: tuple, tile dimensions
- compression: COMPRESSION enum, compression algorithm
- compressionargs: dict, compression parameters
- predictor: PREDICTOR enum, predictor scheme
- description: str, ImageDescription tag
- datetime: datetime or str, timestamp
- resolution: tuple, X/Y resolution
- metadata: dict, additional metadata
- extratags: sequence, custom tags
- contiguous: bool, write contiguous data
Returns:
- int: Number of bytes written
"""
def close(self):
"""Close the TIFF file and finalize writing."""
def overwrite_description(self, description):
"""
Overwrite ImageDescription tag of last written page.
Parameters:
- description: str, new description text
"""# Sequential writing with compression
with tifffile.TiffWriter('output.tif', bigtiff=True) as writer:
for i in range(10):
data = np.random.randint(0, 256, (512, 512), dtype=np.uint8)
writer.write(
data,
compression='lzw',
description=f'Frame {i}'
)
# Write ImageJ hyperstack
stack = np.random.randint(0, 256, (10, 5, 100, 100), dtype=np.uint8)
with tifffile.TiffWriter('stack.tif', imagej=True) as writer:
writer.write(
stack,
metadata={'axes': 'TCYX', 'fps': 10.0}
)
# Write OME-TIFF with metadata
with tifffile.TiffWriter('ome.tif', ome=True) as writer:
writer.write(
data,
metadata={
'axes': 'YX',
'PhysicalSizeX': 0.1,
'PhysicalSizeY': 0.1
}
)
# Write with custom tags
custom_tags = [(65000, 's', 1, 'Custom metadata', False)]
with tifffile.TiffWriter('custom.tif') as writer:
writer.write(data, extratags=custom_tags)Represents individual TIFF Image File Directory (IFD) with detailed access to page-specific data and metadata.
class TiffPage:
def asarray(
self,
squeeze=True,
colormapped=True,
rgbonly=True,
alpha=None,
out=None,
maxworkers=None,
**kwargs
):
"""
Return page image data as NumPy array.
Parameters:
- squeeze: bool, remove singleton dimensions
- colormapped: bool, apply colormap if present
- rgbonly: bool, return only RGB channels
- alpha: str, alpha channel handling ('ignore', 'premultiply')
- out: array-like, pre-allocated output array
- maxworkers: int, number of worker threads
Returns:
- np.ndarray: Image data
"""
def aszarr(self, **kwargs):
"""
Return page image data as Zarr store.
Returns:
- ZarrTiffStore: Zarr store interface
"""
# Properties
@property
def shape(self):
"""tuple: Shape of image data."""
@property
def dtype(self):
"""np.dtype: Data type of image."""
@property
def compression(self):
"""COMPRESSION: Compression scheme."""
@property
def photometric(self):
"""PHOTOMETRIC: Photometric interpretation."""
@property
def description(self):
"""str: ImageDescription tag content."""
@property
def datetime(self):
"""datetime: Image creation timestamp."""
@property
def resolution(self):
"""tuple: X and Y resolution."""
# Metadata tag properties
@property
def andor_tags(self):
"""dict: Andor metadata tags."""
@property
def epics_tags(self):
"""dict: EPICS area detector tags."""
@property
def geotiff_tags(self):
"""dict: GeoTIFF metadata tags."""
@property
def imagej_tags(self):
"""dict: ImageJ metadata tags."""
@property
def ome_tags(self):
"""dict: OME-TIFF metadata tags."""Sequence container for accessing multiple pages within a TIFF file.
class TiffPages:
def __len__(self):
"""Return number of pages."""
def __getitem__(self, key):
"""Return page(s) by index."""
def __iter__(self):
"""Iterate over pages."""
@property
def first(self):
"""TiffPage: First page in sequence."""
@property
def last(self):
"""TiffPage: Last page in sequence."""Represents a series of pages with compatible shape and data type, typically used for multi-dimensional datasets.
class TiffPageSeries:
def asarray(self, **kwargs):
"""
Return series data as NumPy array.
Returns:
- np.ndarray: Combined data from all pages in series
"""
def aszarr(self, **kwargs):
"""
Return series data as Zarr store.
Returns:
- ZarrTiffStore: Zarr store interface
"""
@property
def shape(self):
"""tuple: Shape of combined series data."""
@property
def dtype(self):
"""np.dtype: Data type of series."""
@property
def axes(self):
"""str: Axes labels for dimensions."""
@property
def pages(self):
"""TiffPages: Pages included in series."""Handle sequences of TIFF files as a single logical dataset.
class TiffSequence:
def __init__(
self,
files,
imread=None,
pattern=None,
axesorder=None,
categories=None,
**kwargs
):
"""
Initialize TIFF file sequence.
Parameters:
- files: sequence of file paths
- imread: callable, custom reading function
- pattern: str, glob pattern for file matching
- axesorder: sequence, axis reordering
- categories: dict, categorical data mappings
"""
def asarray(self, **kwargs):
"""
Return sequence data as NumPy array.
Returns:
- np.ndarray: Combined data from all files
"""
def aszarr(self, **kwargs):
"""
Return sequence data as Zarr store.
Returns:
- ZarrFileSequenceStore: Zarr store interface
"""
@property
def shape(self):
"""tuple: Shape of combined sequence data."""
@property
def axes(self):
"""str: Axes labels for dimensions."""# Process file sequence
files = ['img001.tif', 'img002.tif', 'img003.tif']
sequence = tifffile.TiffSequence(files)
combined = sequence.asarray()
print(f"Combined shape: {combined.shape}")
# Use pattern matching
sequence = tifffile.TiffSequence('img*.tif', pattern='img*.tif')
zarr_store = sequence.aszarr()Legacy interface for reading TIFF files (maintained for compatibility).
class TiffReader:
def __init__(self, file, **kwargs):
"""Initialize TIFF reader (legacy interface)."""
def close(self):
"""Close the file."""Represents a frame within a TIFF file sequence or animation.
class TiffFrame:
@property
def index(self):
"""int: Frame index in sequence."""
@property
def offset(self):
"""int: Byte offset in file."""
@property
def shape(self):
"""tuple: Frame dimensions."""# Parallel page processing
with tifffile.TiffFile('large_stack.tif') as tif:
data = tif.asarray(maxworkers=4) # Use 4 threads# Process large files without loading into memory
with tifffile.TiffFile('huge.tif') as tif:
zarr_store = tif.aszarr()
# Process in chunks
for i in range(0, zarr_store.shape[0], 1000):
chunk = zarr_store[i:i+1000]
# Process chunk...# Extract comprehensive metadata
with tifffile.TiffFile('scientific.tif') as tif:
page = tif.pages[0]
metadata = {
'shape': page.shape,
'dtype': page.dtype,
'compression': page.compression.name,
'photometric': page.photometric.name,
'resolution': page.resolution,
'description': page.description
}
# Format-specific metadata
if tif.ome_metadata:
metadata['ome'] = tif.ome_metadata
if tif.imagej_metadata:
metadata['imagej'] = tif.imagej_metadatatry:
with tifffile.TiffFile('problematic.tif') as tif:
data = tif.asarray()
except tifffile.TiffFileError as e:
print(f"TIFF format error: {e}")
except ValueError as e:
print(f"Invalid parameters: {e}")
except MemoryError as e:
print(f"Insufficient memory: {e}")
# Try with Zarr instead
zarr_store = tif.aszarr()Install with Tessl CLI
npx tessl i tessl/pypi-tifffile