Skip to content

Commit

Permalink
Tifffile updates (#3)
Browse files Browse the repository at this point in the history
* updated and pinned tifffile dependency (pathlib/OME support, closes #2), added software parameter
* set metadata to None when writing OME-TIFF files (see cgohlke/tifffile#21)
* version bump
* fix file suffix checks
  • Loading branch information
jwindhager authored Aug 14, 2020
1 parent d7462ca commit d0a7b5c
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 16 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The package provides the following main function for writing TIFF files:
to_tiff(img, file, image_name=None, image_date=None, channel_names=None, description=None,
profile=TiffProfile.OME_TIFF, big_endian=None, big_tiff=None, big_tiff_threshold=4261412864,
compression_type=None, compression_level=0, pixel_size=None, pixel_depth=None,
ome_xml_fun=get_ome_xml, **ome_xml_kwargs):
software='xtiff', ome_xml_fun=get_ome_xml, **ome_xml_kwargs):
```

Documentation of the function parameters is available via Python's internal help system: `help(xtiff.to_tiff)`
Expand Down Expand Up @@ -68,6 +68,7 @@ minimum.
2020-01-15 v0.5.0 - Simplified interface for OME-XML generation
2020-01-23 v0.6.0 - Switched to full XML tree for OME-XML generation
2020-01-23 v0.6.1 - Small bug fix in dimension checking
2020-08-14 v0.6.2 - Fixed tifffile compatibility, added software parameter

## License

Expand Down
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='xtiff',
version='0.6.1',
version='0.6.2',
description='A tiny Python 3 library for writing multi-channel TIFF stacks',
long_description=long_description,
long_description_content_type='text/markdown',
Expand All @@ -27,8 +27,11 @@
py_modules=['xtiff'],
install_requires=[
'numpy',
'tifffile',
'tifffile>=2020.6.3,!=2020.7.17',
],
extras_require={
'xtiff_support': ['xtiff'],
},
python_requires='>=3.7',
zip_safe=True,
)
31 changes: 18 additions & 13 deletions xtiff.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import numpy as np
import os
import sys
import tifffile
import warnings

from datetime import datetime
from enum import Enum
from io import BytesIO
from pathlib import Path
from tifffile import TiffWriter
from typing import Optional, Sequence, Union

# noinspection PyPep8Naming
import xml.etree.ElementTree as ET

try:
# noinspection PyPackageRequirements
import xarray as xr
except ImportError:
xr = None
Expand Down Expand Up @@ -94,7 +95,7 @@ def to_tiff(img, file, image_name: Union[str, bool, None] = None, image_date: Un
big_tiff: Optional[bool] = None, big_tiff_threshold: int = 2 ** 32 - 2 ** 25,
compression_type: Optional[str] = None, compression_level: int = 0,
pixel_size: Optional[float] = None, pixel_depth: Optional[float] = None,
ome_xml_fun=get_ome_xml, **ome_xml_kwargs) -> None:
software: str = 'xtiff', ome_xml_fun=get_ome_xml, **ome_xml_kwargs) -> None:
"""
Writes an image as TIFF file with TZCYX channel order.
Expand All @@ -109,7 +110,7 @@ def to_tiff(img, file, image_name: Union[str, bool, None] = None, image_date: Un
- any numpy data type when using TiffProfile.TIFF
- uint8, uint16, float32 when using TiffProfile.IMAGEJ (uint8 for RGB images)
- bool, int8, int16, int32, uint8, uint16, uint32, float32, float64 when using TiffProfile.OME_TIFF
:param file: File target supported by tifffile TiffWriter, e.g. path to file (str) or binary stream.
:param file: File target supported by tifffile TiffWriter, e.g. path to file (str, pathlib.Path) or binary stream.
:param image_name: Image name for OME-TIFF images. If True, the image name is determined using the DataArray name or
the file name (in that order); if False, the image name is not set. If None, defaults to the behavior for True
for named DataArrays and when the file path is provided, and to the behavior of False otherwise. Only relevant
Expand Down Expand Up @@ -141,32 +142,35 @@ def to_tiff(img, file, image_name: Union[str, bool, None] = None, image_date: Un
:param pixel_size: Planar (x/y) size of one pixel, in micrometer.
:param pixel_depth: Depth (z size) of one pixel, in micrometer. Only relevant when writing OME-TIFF files, any value
other than None will raise a warning for other TIFF profiles.
:param software: Name of the software used to create the file. Must be 7-bit ASCII. Saved with the first page only.
:param ome_xml_fun: Function that will be used for generating the OME-XML header. See the default implementation for
reference of the required signature. Only relevant when writing OME-TIFF files, ignored otherwise.
:param ome_xml_kwargs: Optional arguments that are passed to the ome_xml_fun function. Only relevant when writing
OME-TIFF files, will raise a warning if provided for other TIFF profiles.
"""
# file
if isinstance(file, str):
if not file.endswith('.tiff'):
file = Path(file)
if isinstance(file, Path):
if not file.suffix.lower() == '.tiff':
warnings.warn('The specified TIFF file name does not end with .tiff: {}'.format(file))
if profile == TiffProfile.OME_TIFF:
if not file.endswith('.ome.tiff'):
if not file.name.lower().endswith('.ome.tiff'):
warnings.warn('The specified OME-TIFF file name does not end with .ome.tiff: {}'.format(file))
else:
if file.endswith('.ome.tiff'):
if file.name.lower().endswith('.ome.tiff'):
warnings.warn('The specified non-OME-TIFF file name ends with .ome.tiff: {}'.format(file))

# image name
data_array_has_image_name = (_is_data_array(img) and img.name)
if image_name is None and (data_array_has_image_name or isinstance(file, str)) and profile == TiffProfile.OME_TIFF:
if image_name is None and (data_array_has_image_name or isinstance(file, Path)) and profile == TiffProfile.OME_TIFF:
image_name = True
if isinstance(image_name, bool):
if image_name:
if data_array_has_image_name:
image_name = img.name
elif isinstance(file, str):
image_name = os.path.basename(file)
elif isinstance(file, Path):
image_name = file.name
else:
raise ValueError('Cannot determine image name from non-DataArray images written to unknown file names')
else:
Expand Down Expand Up @@ -257,7 +261,6 @@ def to_tiff(img, file, image_name: Union[str, bool, None] = None, image_date: Un
raise ValueError('Cannot determine channel names from non-DataArray image')
if channel_axis is None:
raise ValueError('Cannot determine channel names from DataArrays without a channel dimension')
img: xr.DataArray = img
channel_names = img.coords[img.dims[channel_axis]].values
else:
channel_names = None
Expand Down Expand Up @@ -301,10 +304,12 @@ def to_tiff(img, file, image_name: Union[str, bool, None] = None, image_date: Un

# write image
byte_order = '>' if big_endian else '<'
with TiffWriter(file, imagej=(profile == TiffProfile.IMAGEJ), bigtiff=big_tiff, byteorder=byte_order) as writer:
imagej = (profile == TiffProfile.IMAGEJ)
metadata = None if profile == TiffProfile.OME_TIFF else {}
with TiffWriter(file, bigtiff=big_tiff, byteorder=byte_order, imagej=imagej) as writer:
# set photometric to 'MINISBLACK' to not treat three-channel images as RGB
writer.save(img, photometric='MINISBLACK', compress=compression, description=description, datetime=image_date,
resolution=resolution)
writer.save(data=img, photometric='MINISBLACK', compress=compression, description=description,
datetime=image_date, resolution=resolution, software=software, metadata=metadata)


def _is_data_array(img) -> bool:
Expand Down

0 comments on commit d0a7b5c

Please sign in to comment.