| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- # -*- coding: utf-8 -*-
- # imageio is distributed under the terms of the (new) BSD License.
- """Read/Write TIFF files.
- Backend: internal
- Provides support for a wide range of Tiff images using the tifffile
- backend.
- Parameters for reading
- ----------------------
- offset : int
- Optional start position of embedded file. By default this is
- the current file position.
- size : int
- Optional size of embedded file. By default this is the number
- of bytes from the 'offset' to the end of the file.
- multifile : bool
- If True (default), series may include pages from multiple files.
- Currently applies to OME-TIFF only.
- multifile_close : bool
- If True (default), keep the handles of other files in multifile
- series closed. This is inefficient when few files refer to
- many pages. If False, the C runtime may run out of resources.
- Parameters for saving
- ---------------------
- bigtiff : bool
- If True, the BigTIFF format is used.
- byteorder : {'<', '>'}
- The endianness of the data in the file.
- By default this is the system's native byte order.
- software : str
- Name of the software used to create the image.
- Saved with the first page only.
- Metadata for reading
- --------------------
- planar_configuration : {'contig', 'planar'}
- Specifies if samples are stored contiguous or in separate planes.
- By default this setting is inferred from the data shape.
- 'contig': last dimension contains samples.
- 'planar': third last dimension contains samples.
- resolution_unit : int
- The resolution unit stored in the TIFF tag. Usually 1 means no/unknown unit,
- 2 means dpi (inch), 3 means dpc (centimeter).
- resolution : (float, float, str)
- A tuple formatted as (X_resolution, Y_resolution, unit). The unit is a
- string representing one of the following units::
- NONE # No unit or unit unknown
- INCH # dpi
- CENTIMETER # cpi
- MILLIMETER
- MICROMETER
- compression : int
- Value indicating the compression algorithm used, e.g. 5 is LZW,
- 7 is JPEG, 8 is deflate.
- If 1, data are uncompressed.
- predictor : int
- Value 2 indicates horizontal differencing was used before compression,
- while 3 indicates floating point horizontal differencing.
- If 1, no prediction scheme was used before compression.
- orientation : {'top_left', 'bottom_right', ...}
- Oriented of image array.
- is_rgb : bool
- True if page contains a RGB image.
- is_contig : bool
- True if page contains a contiguous image.
- is_tiled : bool
- True if page contains tiled image.
- is_palette : bool
- True if page contains a palette-colored image and not OME or STK.
- is_reduced : bool
- True if page is a reduced image of another image.
- is_shaped : bool
- True if page contains shape in image_description tag.
- is_fluoview : bool
- True if page contains FluoView MM_STAMP tag.
- is_nih : bool
- True if page contains NIH image header.
- is_micromanager : bool
- True if page contains Micro-Manager metadata.
- is_ome : bool
- True if page contains OME-XML in image_description tag.
- is_sgi : bool
- True if page contains SGI image and tile depth tags.
- is_mdgel : bool
- True if page contains md_file_tag tag.
- is_mediacy : bool
- True if page contains Media Cybernetics Id tag.
- is_stk : bool
- True if page contains UIC2Tag tag.
- is_lsm : bool
- True if page contains LSM CZ_LSM_INFO tag.
- description : str
- Image description
- description1 : str
- Additional description
- is_imagej : None or str
- ImageJ metadata
- software : str
- Software used to create the TIFF file
- datetime : datetime.datetime
- Creation date and time
- Metadata for writing
- --------------------
- photometric : {'minisblack', 'miniswhite', 'rgb'}
- The color space of the image data.
- By default this setting is inferred from the data shape.
- planarconfig : {'contig', 'planar'}
- Specifies if samples are stored contiguous or in separate planes.
- By default this setting is inferred from the data shape.
- 'contig': last dimension contains samples.
- 'planar': third last dimension contains samples.
- resolution : (float, float) or ((int, int), (int, int))
- X and Y resolution in dots per inch as float or rational numbers.
- description : str
- The subject of the image. Saved with the first page only.
- compress : int
- Values from 0 to 9 controlling the level of zlib (deflate) compression.
- If 0, data are written uncompressed (default).
- compression : str, (int, int)
- Compression scheme used while writing the image. If omitted (default) the
- image is not uncompressed. Compression cannot be used to write contiguous
- series. Compressors may require certain data shapes, types or value ranges.
- For example, JPEG compression requires grayscale or RGB(A), uint8 or 12-bit
- uint16. JPEG compression is experimental. JPEG markers and TIFF tags may not
- match. Only a limited set of compression schemes are implemented. 'ZLIB' is
- short for ADOBE_DEFLATE. The value is written to the Compression tag.
- compressionargs:
- Extra arguments passed to compression codec, e.g., compression level. Refer
- to the Imagecodecs implementation for supported arguments.
- predictor : bool
- If True, horizontal differencing is applied before compression.
- Note that using an int literal 1 actually means no prediction scheme
- will be used.
- volume : bool
- If True, volume data are stored in one tile (if applicable) using
- the SGI image_depth and tile_depth tags.
- Image width and depth must be multiple of 16.
- Few software can read this format, e.g. MeVisLab.
- writeshape : bool
- If True, write the data shape to the image_description tag
- if necessary and no other description is given.
- extratags: sequence of tuples
- Additional tags as [(code, dtype, count, value, writeonce)].
- code : int
- The TIFF tag Id.
- dtype : str
- Data type of items in 'value' in Python struct format.
- One of B, s, H, I, 2I, b, h, i, f, d, Q, or q.
- count : int
- Number of data values. Not used for string values.
- value : sequence
- 'Count' values compatible with 'dtype'.
- writeonce : bool
- If True, the tag is written to the first page only.
- Notes
- -----
- Global metadata is stored with the first frame in a TIFF file.
- Thus calling :py:meth:`Format.Writer.set_meta_data` after the first frame
- was written has no effect. Also, global metadata is ignored if metadata is
- provided via the `meta` argument of :py:meth:`Format.Writer.append_data`.
- If you have installed tifffile as a Python package, imageio will attempt
- to use that as backend instead of the bundled backend. Doing so can
- provide access to new performance improvements and bug fixes.
- """
- import datetime
- from ..core import Format
- from ..core.request import URI_BYTES, URI_FILE
- import numpy as np
- import warnings
- try:
- import tifffile as _tifffile
- except ImportError:
- warnings.warn(
- "ImageIO's vendored tifffile backend is deprecated and will be"
- " removed in ImageIO v3. Install the tifffile directly:"
- " `pip install imageio[tifffile]`",
- DeprecationWarning,
- )
- from . import _tifffile
- TIFF_FORMATS = (".tif", ".tiff", ".stk", ".lsm")
- WRITE_METADATA_KEYS = (
- "photometric",
- "planarconfig",
- "resolution",
- "description",
- "compress",
- "compression",
- "compressionargs",
- "predictor",
- "volume",
- "writeshape",
- "extratags",
- "datetime",
- )
- READ_METADATA_KEYS = (
- "planar_configuration",
- "is_fluoview",
- "is_nih",
- "is_contig",
- "is_micromanager",
- "is_ome",
- "is_lsm",
- "is_palette",
- "is_reduced",
- "is_rgb",
- "is_sgi",
- "is_shaped",
- "is_stk",
- "is_tiled",
- "is_mdgel",
- "resolution_unit",
- "compression",
- "predictor",
- "is_mediacy",
- "orientation",
- "description",
- "description1",
- "is_imagej",
- "software",
- )
- class TiffFormat(Format):
- """Provides support for a wide range of Tiff images using the tifffile
- backend.
- Images that contain multiple pages can be read using ``imageio.mimread()``
- to read the individual pages, or ``imageio.volread()`` to obtain a
- single (higher dimensional) array.
- Note that global metadata is stored with the first frame in a TIFF file.
- Thus calling :py:meth:`Format.Writer.set_meta_data` after the first frame
- was written has no effect. Also, global metadata is ignored if metadata is
- provided via the `meta` argument of :py:meth:`Format.Writer.append_data`.
- If you have installed tifffile as a Python package, imageio will attempt
- to use that as backend instead of the bundled backend. Doing so can
- provide access to new performance improvements and bug fixes.
- Parameters for reading
- ----------------------
- offset : int
- Optional start position of embedded file. By default this is
- the current file position.
- size : int
- Optional size of embedded file. By default this is the number
- of bytes from the 'offset' to the end of the file.
- multifile : bool
- If True (default), series may include pages from multiple files.
- Currently applies to OME-TIFF only.
- multifile_close : bool
- If True (default), keep the handles of other files in multifile
- series closed. This is inefficient when few files refer to
- many pages. If False, the C runtime may run out of resources.
- Parameters for saving
- ---------------------
- bigtiff : bool
- If True, the BigTIFF format is used.
- byteorder : {'<', '>'}
- The endianness of the data in the file.
- By default this is the system's native byte order.
- software : str
- Name of the software used to create the image.
- Saved with the first page only.
- Metadata for reading
- --------------------
- planar_configuration : {'contig', 'planar'}
- Specifies if samples are stored contiguous or in separate planes.
- By default this setting is inferred from the data shape.
- 'contig': last dimension contains samples.
- 'planar': third last dimension contains samples.
- resolution_unit : (float, float) or ((int, int), (int, int))
- X and Y resolution in dots per inch as float or rational numbers.
- compression : int
- Value indicating the compression algorithm used, e.g. 5 is LZW,
- 7 is JPEG, 8 is deflate.
- If 1, data are uncompressed.
- predictor : int
- Value 2 indicates horizontal differencing was used before compression,
- while 3 indicates floating point horizontal differencing.
- If 1, no prediction scheme was used before compression.
- orientation : {'top_left', 'bottom_right', ...}
- Oriented of image array.
- is_rgb : bool
- True if page contains a RGB image.
- is_contig : bool
- True if page contains a contiguous image.
- is_tiled : bool
- True if page contains tiled image.
- is_palette : bool
- True if page contains a palette-colored image and not OME or STK.
- is_reduced : bool
- True if page is a reduced image of another image.
- is_shaped : bool
- True if page contains shape in image_description tag.
- is_fluoview : bool
- True if page contains FluoView MM_STAMP tag.
- is_nih : bool
- True if page contains NIH image header.
- is_micromanager : bool
- True if page contains Micro-Manager metadata.
- is_ome : bool
- True if page contains OME-XML in image_description tag.
- is_sgi : bool
- True if page contains SGI image and tile depth tags.
- is_stk : bool
- True if page contains UIC2Tag tag.
- is_mdgel : bool
- True if page contains md_file_tag tag.
- is_mediacy : bool
- True if page contains Media Cybernetics Id tag.
- is_stk : bool
- True if page contains UIC2Tag tag.
- is_lsm : bool
- True if page contains LSM CZ_LSM_INFO tag.
- description : str
- Image description
- description1 : str
- Additional description
- is_imagej : None or str
- ImageJ metadata
- software : str
- Software used to create the TIFF file
- datetime : datetime.datetime
- Creation date and time
- Metadata for writing
- --------------------
- photometric : {'minisblack', 'miniswhite', 'rgb'}
- The color space of the image data.
- By default this setting is inferred from the data shape.
- planarconfig : {'contig', 'planar'}
- Specifies if samples are stored contiguous or in separate planes.
- By default this setting is inferred from the data shape.
- 'contig': last dimension contains samples.
- 'planar': third last dimension contains samples.
- resolution : (float, float) or ((int, int), (int, int))
- X and Y resolution in dots per inch as float or rational numbers.
- description : str
- The subject of the image. Saved with the first page only.
- compress : int
- Values from 0 to 9 controlling the level of zlib (deflate) compression.
- If 0, data are written uncompressed (default).
- predictor : bool
- If True, horizontal differencing is applied before compression.
- Note that using an int literal 1 actually means no prediction scheme
- will be used.
- volume : bool
- If True, volume data are stored in one tile (if applicable) using
- the SGI image_depth and tile_depth tags.
- Image width and depth must be multiple of 16.
- Few software can read this format, e.g. MeVisLab.
- writeshape : bool
- If True, write the data shape to the image_description tag
- if necessary and no other description is given.
- extratags: sequence of tuples
- Additional tags as [(code, dtype, count, value, writeonce)].
- code : int
- The TIFF tag Id.
- dtype : str
- Data type of items in 'value' in Python struct format.
- One of B, s, H, I, 2I, b, h, i, f, d, Q, or q.
- count : int
- Number of data values. Not used for string values.
- value : sequence
- 'Count' values compatible with 'dtype'.
- writeonce : bool
- If True, the tag is written to the first page only.
- """
- def _can_read(self, request):
- try:
- _tifffile.TiffFile(request.get_file(), **request.kwargs)
- except ValueError:
- # vendored backend raises value exception
- return False
- except _tifffile.TiffFileError: # pragma: no-cover
- # current version raises custom exception
- return False
- finally:
- request.get_file().seek(0)
- return True
- def _can_write(self, request):
- if request._uri_type in [URI_FILE, URI_BYTES]:
- pass # special URI
- elif request.extension not in self.extensions:
- return False
- try:
- _tifffile.TiffWriter(request.get_file(), **request.kwargs)
- except ValueError:
- # vendored backend raises value exception
- return False
- except _tifffile.TiffFileError: # pragma: no-cover
- # current version raises custom exception
- return False
- finally:
- request.get_file().seek(0)
- return True
- # -- reader
- class Reader(Format.Reader):
- def _open(self, **kwargs):
- # Allow loading from http; tifffile uses seek, so download first
- if self.request.filename.startswith(("http://", "https://")):
- self._f = f = open(self.request.get_local_filename(), "rb")
- else:
- self._f = None
- f = self.request.get_file()
- self._tf = _tifffile.TiffFile(f, **kwargs)
- def _close(self):
- self._tf.close()
- if self._f is not None:
- self._f.close()
- def _get_length(self):
- return len(self._tf.series)
- def _get_data(self, index):
- if index < 0 or index >= self._get_length():
- raise IndexError("Index out of range while reading from tiff file")
- im = self._tf.asarray(series=index)
- meta = self._get_meta_data(index)
- return im, meta
- def _get_meta_data(self, index):
- meta = {}
- page = self._tf.pages[index or 0]
- for key in READ_METADATA_KEYS:
- try:
- meta[key] = getattr(page, key)
- except Exception:
- pass
- # tifffile <= 0.12.1 use datetime, newer use DateTime
- for key in ("datetime", "DateTime"):
- try:
- meta["datetime"] = datetime.datetime.strptime(
- page.tags[key].value, "%Y:%m:%d %H:%M:%S"
- )
- break
- except Exception:
- pass
- if 296 in page.tags:
- meta["resolution_unit"] = page.tags[296].value.value
- if 282 in page.tags and 283 in page.tags and 296 in page.tags:
- resolution_x = page.tags[282].value
- resolution_y = page.tags[283].value
- if resolution_x[1] == 0 or resolution_y[1] == 0:
- warnings.warn(
- "Ignoring resolution metadata, "
- "because at least one direction has a 0 denominator.",
- RuntimeWarning,
- )
- else:
- meta["resolution"] = (
- resolution_x[0] / resolution_x[1],
- resolution_y[0] / resolution_y[1],
- page.tags[296].value.name,
- )
- return meta
- # -- writer
- class Writer(Format.Writer):
- def _open(self, bigtiff=None, byteorder=None, software=None):
- try:
- self._tf = _tifffile.TiffWriter(
- self.request.get_file(),
- bigtiff=bigtiff,
- byteorder=byteorder,
- software=software,
- )
- self._software = None
- except TypeError:
- # In tifffile >= 0.15, the `software` arg is passed to
- # TiffWriter.save
- self._tf = _tifffile.TiffWriter(
- self.request.get_file(), bigtiff=bigtiff, byteorder=byteorder
- )
- self._software = software
- self._meta = {}
- self._frames_written = 0
- def _close(self):
- self._tf.close()
- def _append_data(self, im, meta):
- if meta is not None:
- meta = self._sanitize_meta(meta)
- else:
- # Use global metadata for first frame
- meta = self._meta if self._frames_written == 0 else {}
- if self._software is not None and self._frames_written == 0:
- meta["software"] = self._software
- # No need to check self.request.mode; tifffile figures out whether
- # this is a single page, or all page data at once.
- try:
- # TiffWriter.save has been deprecated in version 2020.9.30
- write_meth = self._tf.write
- except AttributeError:
- write_meth = self._tf.save
- write_meth(np.asanyarray(im), contiguous=False, **meta)
- self._frames_written += 1
- @staticmethod
- def _sanitize_meta(meta):
- ret = {}
- for key, value in meta.items():
- if key in WRITE_METADATA_KEYS:
- # Special case of previously read `predictor` int value
- # 1(=NONE) translation to False expected by TiffWriter.save
- if key == "predictor" and not isinstance(value, bool):
- ret[key] = value > 1
- elif key == "compress" and value != 0:
- warnings.warn(
- "The use of `compress` is deprecated. Use `compression` and `compressionargs` instead.",
- DeprecationWarning,
- )
- if _tifffile.__version__ < "2022":
- ret["compression"] = (8, value)
- else:
- ret["compression"] = "zlib"
- ret["compressionargs"] = {"level": value}
- else:
- ret[key] = value
- return ret
- def set_meta_data(self, meta):
- self._meta = self._sanitize_meta(meta)
|