Read and write TIFF files for scientific and bioimaging applications with comprehensive format support
Comprehensive TIFF tag system for reading and manipulating image metadata, including specialized parsers for scientific imaging formats, support for custom tag definitions, and utilities for extracting format-specific metadata from various microscopy and scientific imaging systems.
Core classes for working with individual TIFF tags and tag collections.
class TiffTag:
def overwrite(self, value):
"""
Overwrite tag value in TIFF file.
Parameters:
- value: new value for the tag (type must match original)
Note: Modifies the tag value directly in the file
"""
@property
def name(self):
"""str: Tag name."""
@property
def code(self):
"""int: Tag code/number."""
@property
def dtype(self):
"""str: Tag data type."""
@property
def count(self):
"""int: Number of values."""
@property
def value(self):
"""Value of the tag (decoded)."""
@property
def offset(self):
"""int: Byte offset in file."""class TiffTags:
def get(self, key, default=None):
"""
Get tag by name or code.
Parameters:
- key: str (name) or int (code)
- default: value to return if tag not found
Returns:
- TiffTag or default value
"""
def __getitem__(self, key):
"""Get tag by name or code (raises KeyError if not found)."""
def __contains__(self, key):
"""Check if tag exists."""
def __iter__(self):
"""Iterate over all tags."""
def items(self):
"""Return (name, tag) pairs."""
def keys(self):
"""Return tag names."""
def values(self):
"""Return TiffTag objects."""
@property
def tagnames(self):
"""set: All tag names in collection."""class TiffTagRegistry:
def add(
self,
code,
name,
count=1,
dtype='I',
default=None,
**kwargs
):
"""
Add custom tag definition to registry.
Parameters:
- code: int, tag code/number
- name: str, tag name
- count: int, expected number of values
- dtype: str, data type code
- default: default value for tag
"""
def get(self, code):
"""Get tag definition by code."""
def __contains__(self, code):
"""Check if tag code is registered."""# Access tags from TIFF file
with tifffile.TiffFile('image.tif') as tif:
tags = tif.pages[0].tags
# Get specific tags
width = tags.get('ImageWidth')
height = tags.get('ImageLength')
description = tags.get('ImageDescription', 'No description')
# Check if tag exists
if 'XResolution' in tags:
resolution = tags['XResolution'].value
# Iterate over all tags
for name, tag in tags.items():
print(f"{name}: {tag.value}")
# Modify tag values
with tifffile.TiffFile('image.tif', mode='r+') as tif:
description_tag = tif.pages[0].tags['ImageDescription']
description_tag.overwrite('Modified description')
# Register custom tags
registry = tifffile.TiffTagRegistry()
registry.add(65000, 'CustomTag', count=1, dtype='s')Specialized functions for extracting metadata from various scientific imaging formats.
def read_micromanager_metadata(filename):
"""
Read Micro-Manager metadata from TIFF file.
Parameters:
- filename: str or PathLike, path to Micro-Manager TIFF file
Returns:
- dict: Micro-Manager metadata including acquisition settings,
channel information, and position data
"""
def read_scanimage_metadata(filename):
"""
Read ScanImage metadata from TIFF file.
Parameters:
- filename: str or PathLike, path to ScanImage TIFF file
Returns:
- dict: ScanImage metadata including scan parameters,
acquisition settings, and ROI information
"""
def read_gdal_structural_metadata(filename):
"""
Read GDAL structural metadata from GeoTIFF file.
Parameters:
- filename: str or PathLike, path to GeoTIFF file
Returns:
- dict: GDAL metadata including coordinate reference system,
geotransform parameters, and projection information
"""
def read_ndtiff_index(filename):
"""
Read NDTiff index file metadata.
Parameters:
- filename: str or PathLike, path to NDTiff index file
Returns:
- dict: NDTiff index metadata including dataset structure
and file locations
"""# Extract Micro-Manager metadata
mm_metadata = tifffile.read_micromanager_metadata('acquisition.tif')
print(f"Channels: {mm_metadata.get('channels', [])}")
print(f"Positions: {mm_metadata.get('positions', [])}")
# Extract ScanImage metadata
si_metadata = tifffile.read_scanimage_metadata('scanimage.tif')
print(f"Zoom: {si_metadata.get('zoom', 'Unknown')}")
print(f"Scan rate: {si_metadata.get('scanFrameRate', 'Unknown')}")
# Extract GeoTIFF metadata
gdal_metadata = tifffile.read_gdal_structural_metadata('geo.tif')
print(f"CRS: {gdal_metadata.get('crs', 'Unknown')}")
print(f"Transform: {gdal_metadata.get('geotransform', [])}")Functions for reading and writing TIFF ImageDescription tags and comments.
def tiffcomment(filename, comment=None):
"""
Read or write TIFF ImageDescription tag.
Parameters:
- filename: str or PathLike, path to TIFF file
- comment: str or bytes, new comment to write (None to read)
Returns:
- str: Current ImageDescription content (when reading)
- None: When writing comment
"""# Read current description
current_desc = tifffile.tiffcomment('image.tif')
print(f"Current description: {current_desc}")
# Write new description
tifffile.tiffcomment('image.tif', 'New image description')
# Clear description
tifffile.tiffcomment('image.tif', '')Classes and functions for working with OME-XML metadata in OME-TIFF files.
class OmeXml:
def __init__(self, xml_string=None):
"""
Initialize OME-XML parser/generator.
Parameters:
- xml_string: str, OME-XML content to parse
"""
def to_dict(self):
"""
Convert OME-XML to dictionary representation.
Returns:
- dict: Structured OME metadata
"""
def to_xml(self, pretty=True):
"""
Generate OME-XML string.
Parameters:
- pretty: bool, format XML with indentation
Returns:
- str: OME-XML formatted string
"""
@property
def images(self):
"""list: Image metadata entries."""
@property
def pixels(self):
"""list: Pixel metadata entries."""
@property
def channels(self):
"""list: Channel metadata entries."""
class OmeXmlError(Exception):
"""Exception raised for OME-XML parsing errors."""Functions for working with ImageJ-specific metadata formats.
def imagej_description(
shape,
rgb=None,
colormaped=False,
version='1.11a',
hyperstack=None,
mode=None,
loop=None,
**kwargs
):
"""
Create ImageJ-compatible description string.
Parameters:
- shape: tuple, dimensions of image data
- rgb: bool, whether image is RGB
- colormaped: bool, whether image uses colormap
- version: str, ImageJ version identifier
- hyperstack: bool, create hyperstack format
- mode: str, display mode
- loop: bool, enable animation looping
Returns:
- str: ImageJ-compatible description string
"""
def imagej_metadata_tag(
metadata,
byteorder='<'
):
"""
Create ImageJ metadata tag from dictionary.
Parameters:
- metadata: dict, ImageJ metadata
- byteorder: str, byte order for encoding
Returns:
- bytes: Encoded ImageJ metadata tag
"""# Create ImageJ description
shape = (10, 5, 100, 100) # T, C, Y, X
desc = tifffile.imagej_description(
shape,
hyperstack=True,
mode='composite'
)
# Create ImageJ metadata tag
metadata = {
'fps': 10.0,
'unit': 'micron',
'finterval': 0.1
}
metadata_tag = tifffile.imagej_metadata_tag(metadata)
# Write ImageJ-compatible file
tifffile.imwrite(
'imagej.tif',
data,
imagej=True,
description=desc,
extratags=[(50839, 'B', len(metadata_tag), metadata_tag, False)]
)Access format-specific metadata through TiffFile properties:
# Available through TiffFile instances
@property
def ome_metadata(self):
"""dict: OME-XML metadata parsed to dictionary."""
@property
def imagej_metadata(self):
"""dict: ImageJ metadata from hyperstack files."""
@property
def lsm_metadata(self):
"""dict: Zeiss LSM metadata."""
@property
def stk_metadata(self):
"""dict: MetaMorph STK metadata."""
@property
def gdal_metadata(self):
"""dict: GDAL GeoTIFF metadata."""
@property
def shaped_metadata(self):
"""tuple: Shaped array metadata (shape, dtype)."""
@property
def andor_metadata(self):
"""dict: Andor camera metadata."""
@property
def epics_metadata(self):
"""dict: EPICS area detector metadata."""
@property
def fluoview_metadata(self):
"""dict: Olympus FluoView metadata."""
@property
def nih_metadata(self):
"""dict: NIH Image metadata."""
@property
def pilatus_metadata(self):
"""dict: Pilatus detector metadata."""
@property
def tvips_metadata(self):
"""dict: TVIPS camera metadata."""# Access format-specific metadata
with tifffile.TiffFile('multi_format.tif') as tif:
# Check what metadata is available
if tif.ome_metadata:
print("OME metadata:", tif.ome_metadata)
if tif.imagej_metadata:
print("ImageJ metadata:", tif.imagej_metadata)
if tif.lsm_metadata:
print("LSM metadata:", tif.lsm_metadata)
if tif.gdal_metadata:
print("GDAL metadata:", tif.gdal_metadata)
# Extract specific information
with tifffile.TiffFile('ome.tif') as tif:
if tif.ome_metadata:
images = tif.ome_metadata.get('images', [])
for i, image in enumerate(images):
pixels = image.get('pixels', {})
print(f"Image {i}: {pixels.get('SizeX')}x{pixels.get('SizeY')}")Creating and working with custom TIFF tags:
# Tag tuple format: (code, dtype, count, value, writeonce)
TagTuple = Tuple[int, Union[int, str], Any, Union[int, str], bool]# Define custom tags
custom_tags = [
(65000, 's', 1, 'Custom string metadata', False),
(65001, 'I', 1, 12345, False),
(65002, 'd', 2, [1.5, 2.5], False),
(65003, 'B', 100, bytes(range(100)), True) # Write-once tag
]
# Write file with custom tags
tifffile.imwrite(
'custom_tags.tif',
data,
extratags=custom_tags
)
# Read custom tags
with tifffile.TiffFile('custom_tags.tif') as tif:
tags = tif.pages[0].tags
if 65000 in tags:
custom_string = tags[65000].value
print(f"Custom string: {custom_string}")
if 65001 in tags:
custom_int = tags[65001].value
print(f"Custom integer: {custom_int}")Additional utilities for metadata processing:
def xml2dict(xml_string):
"""
Convert XML string to dictionary representation.
Parameters:
- xml_string: str, XML content to parse
Returns:
- dict: Parsed XML as nested dictionary
"""
def validate_jhove(filename):
"""
Validate TIFF file using JHOVE-compatible rules.
Parameters:
- filename: str or PathLike, path to TIFF file
Returns:
- dict: Validation results and issues found
"""Common metadata-related exceptions:
try:
metadata = tifffile.read_micromanager_metadata('corrupt.tif')
except tifffile.TiffFileError as e:
print(f"File format error: {e}")
except tifffile.OmeXmlError as e:
print(f"OME-XML parsing error: {e}")
except ValueError as e:
print(f"Invalid metadata format: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-tifffile