[med-svn] [Git][python-team/modules/tifffile][upstream] New upstream version 20200603

Ole Streicher gitlab at salsa.debian.org
Fri Jun 12 11:09:03 BST 2020



Ole Streicher pushed to branch upstream at Debian Python Team / DPMT / tifffile


Commits:
c3951b75 by Ole Streicher at 2020-06-12T12:06:02+02:00
New upstream version 20200603
- - - - -


8 changed files:

- CHANGES.rst
- PKG-INFO
- README.rst
- setup.py
- tests/test_tifffile.py
- tifffile.egg-info/PKG-INFO
- tifffile.egg-info/requires.txt
- tifffile/tifffile.py


Changes:

=====================================
CHANGES.rst
=====================================
@@ -1,7 +1,17 @@
 Revisions
 ---------
-2020.5.11
+2020.6.3
     Pass 2908 tests.
+    Support os.PathLike file names (#9).
+2020.5.30
+    Re-add pure Python PackBits decoder.
+2020.5.25
+    Make imagecodecs an optional dependency again.
+    Disable multi-threaded decoding of small LZW compressed segments.
+    Fix caching of TiffPage.decode function.
+    Fix xml.etree.cElementTree ImportError on Python 3.9.
+    Fix tostring DeprecationWarning.
+2020.5.11
     Fix reading ImageJ grayscale mode RGB images (#6).
     Remove napari reader plugin.
 2020.5.7


=====================================
PKG-INFO
=====================================
@@ -1,11 +1,13 @@
 Metadata-Version: 2.1
 Name: tifffile
-Version: 2020.5.11
+Version: 2020.6.3
 Summary: Read and write TIFF(r) files
 Home-page: https://www.lfd.uci.edu/~gohlke/
 Author: Christoph Gohlke
 Author-email: cgohlke at uci.edu
 License: BSD
+Project-URL: Bug Tracker, https://github.com/cgohlke/tifffile/issues
+Project-URL: Source Code, https://github.com/cgohlke/tifffile
 Description: Read and write TIFF(r) files
         ============================
         
@@ -46,23 +48,33 @@ Description: Read and write TIFF(r) files
         
         :License: BSD 3-Clause
         
-        :Version: 2020.5.11
+        :Version: 2020.6.3
         
         Requirements
         ------------
         This release has been tested with the following requirements and dependencies
         (other versions may work):
         
-        * `CPython 3.6.8, 3.7.7, 3.8.2 64-bit <https://www.python.org>`_
-        * `Numpy 1.16.6 <https://www.numpy.org>`_
-        * `Imagecodecs 2020.2.18 <https://pypi.org/project/imagecodecs/>`_
+        * `CPython 3.6.8, 3.7.7, 3.8.3 64-bit <https://www.python.org>`_
+        * `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+        * `Imagecodecs 2020.5.30 <https://pypi.org/project/imagecodecs/>`_
           (required only for encoding or decoding LZW, JPEG, etc.)
         * `Matplotlib 3.1 <https://www.matplotlib.org>`_ (required only for plotting)
         
         Revisions
         ---------
-        2020.5.11
+        2020.6.3
             Pass 2908 tests.
+            Support os.PathLike file names (#9).
+        2020.5.30
+            Re-add pure Python PackBits decoder.
+        2020.5.25
+            Make imagecodecs an optional dependency again.
+            Disable multi-threaded decoding of small LZW compressed segments.
+            Fix caching of TiffPage.decode function.
+            Fix xml.etree.cElementTree ImportError on Python 3.9.
+            Fix tostring DeprecationWarning.
+        2020.5.11
             Fix reading ImageJ grayscale mode RGB images (#6).
             Remove napari reader plugin.
         2020.5.7
@@ -208,10 +220,6 @@ Description: Read and write TIFF(r) files
         
         Python 32-bit versions are deprecated.
         
-        Update pip and setuptools to the latest version before installing tifffile:
-        
-            ``python -m pip install --upgrade pip setuptools``
-        
         Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
         package for encoding and decoding LZW, JPEG, and other compressed images.
         
@@ -273,6 +281,7 @@ Description: Read and write TIFF(r) files
           <https://github.com/apeer-micro/apeer-ometiff-library>`_
         * `Allen Institute for Cell Science imageio
           <https://pypi.org/project/aicsimageio>`_
+        * `xtiff <https://github.com/BodenmillerGroup/xtiff>`_
         
         References
         ----------
@@ -475,5 +484,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Requires-Python: >=3.6
 Provides-Extra: all


=====================================
README.rst
=====================================
@@ -38,23 +38,33 @@ For command line usage run ``python -m tifffile --help``
 
 :License: BSD 3-Clause
 
-:Version: 2020.5.11
+:Version: 2020.6.3
 
 Requirements
 ------------
 This release has been tested with the following requirements and dependencies
 (other versions may work):
 
-* `CPython 3.6.8, 3.7.7, 3.8.2 64-bit <https://www.python.org>`_
-* `Numpy 1.16.6 <https://www.numpy.org>`_
-* `Imagecodecs 2020.2.18 <https://pypi.org/project/imagecodecs/>`_
+* `CPython 3.6.8, 3.7.7, 3.8.3 64-bit <https://www.python.org>`_
+* `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+* `Imagecodecs 2020.5.30 <https://pypi.org/project/imagecodecs/>`_
   (required only for encoding or decoding LZW, JPEG, etc.)
 * `Matplotlib 3.1 <https://www.matplotlib.org>`_ (required only for plotting)
 
 Revisions
 ---------
-2020.5.11
+2020.6.3
     Pass 2908 tests.
+    Support os.PathLike file names (#9).
+2020.5.30
+    Re-add pure Python PackBits decoder.
+2020.5.25
+    Make imagecodecs an optional dependency again.
+    Disable multi-threaded decoding of small LZW compressed segments.
+    Fix caching of TiffPage.decode function.
+    Fix xml.etree.cElementTree ImportError on Python 3.9.
+    Fix tostring DeprecationWarning.
+2020.5.11
     Fix reading ImageJ grayscale mode RGB images (#6).
     Remove napari reader plugin.
 2020.5.7
@@ -200,10 +210,6 @@ Tested on little-endian platforms only.
 
 Python 32-bit versions are deprecated.
 
-Update pip and setuptools to the latest version before installing tifffile:
-
-    ``python -m pip install --upgrade pip setuptools``
-
 Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
 package for encoding and decoding LZW, JPEG, and other compressed images.
 
@@ -265,6 +271,7 @@ Some libraries are using tifffile to write OME-TIFF files:
   <https://github.com/apeer-micro/apeer-ometiff-library>`_
 * `Allen Institute for Cell Science imageio
   <https://pypi.org/project/aicsimageio>`_
+* `xtiff <https://github.com/BodenmillerGroup/xtiff>`_
 
 References
 ----------


=====================================
setup.py
=====================================
@@ -57,19 +57,25 @@ setup(
     long_description=readme,
     author='Christoph Gohlke',
     author_email='cgohlke at uci.edu',
-    url='https://www.lfd.uci.edu/~gohlke/',
     license='BSD',
+    url='https://www.lfd.uci.edu/~gohlke/',
+    project_urls={
+        'Bug Tracker': 'https://github.com/cgohlke/tifffile/issues',
+        'Source Code': 'https://github.com/cgohlke/tifffile',
+        # 'Documentation': 'https://',
+    },
     packages=['tifffile'],
     python_requires='>=3.6',
     install_requires=[
         'numpy>=1.15.1',
-        'imagecodecs>=2020.2.18',
+        # 'imagecodecs>=2020.2.18',
     ],
     extras_require={
-        'all': ['matplotlib>=3.1'],
+        'all': ['imagecodecs>=2020.2.18', 'matplotlib>=3.1'],
     },
     tests_require=[
-        'pytest', 'czifile', 'cmapfile', 'oiffile', 'lfdfiles', 'roifile'
+        'pytest', 'imagecodecs', 'czifile', 'cmapfile', 'oiffile', 'lfdfiles',
+        'roifile'
         ],
     entry_points={
         'console_scripts': [
@@ -89,5 +95,6 @@ setup(
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
     ],
 )


=====================================
tests/test_tifffile.py
=====================================
@@ -42,7 +42,7 @@ Private data files are not available due to size and copyright restrictions.
 
 :License: BSD 3-Clause
 
-:Version: 2020.5.11
+:Version: 2020.6.3
 
 """
 
@@ -216,6 +216,9 @@ if not SKIP_CODECS:
         import imagecodecs  # noqa
     except ImportError:
         SKIP_CODECS = True
+    else:
+        if imagecodecs is None:
+            SKIP_CODECS = True
 
 
 def config():
@@ -753,14 +756,45 @@ def test_issue_pathlib():
     data = random_data('uint16', (219, 301))
     with TempFileName('pathlib') as fname:
         fname = pathlib.Path(fname)
+        assert isinstance(fname, os.PathLike)
+        # imwrite
         imwrite(fname, data)
+        # imread
+        im = imread(fname)
+        assert_array_equal(im, data)
+        # memmap
+        im = memmap(fname)
+        try:
+            assert_array_equal(im, data)
+        finally:
+            del im
+        # TiffFile
         with TiffFile(fname) as tif:
             with TempFileName('pathlib_out') as outfname:
                 outfname = pathlib.Path(outfname)
+                # out=file
                 im = tif.asarray(out=outfname)
-                assert isinstance(im, numpy.core.memmap)
-                assert_array_equal(im, data)
-                assert os.path.samefile(im.filename, str(outfname))
+                try:
+                    assert isinstance(im, numpy.core.memmap)
+                    assert_array_equal(im, data)
+                    assert os.path.samefile(im.filename, str(outfname))
+                finally:
+                    del im
+        # TiffSequence
+        with TiffSequence(fname) as tifs:
+            im = tifs.asarray()
+            assert_array_equal(im[0], data)
+        with TiffSequence([fname]) as tifs:
+            im = tifs.asarray()
+            assert_array_equal(im[0], data)
+
+    # TiffSequence container
+    if SKIP_PRIVATE or SKIP_CODECS:
+        pytest.skip(REASON)
+    fname = pathlib.Path(private_file('TiffSequence.zip'))
+    with TiffSequence('*.tif', container=fname, pattern=None) as tifs:
+        im = tifs.asarray()
+        assert im[9, 256, 256] == 135
 
 
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
@@ -9602,12 +9636,12 @@ def test_embed_mm_bytesio():
 # Test sequence of image files
 
 def test_sequence_stream_list():
-    """Test TiffSequence with list of ByteIO streams raises ValueError."""
+    """Test TiffSequence with list of ByteIO streams raises TypeError."""
     data = numpy.random.rand(7, 9)
     files = [BytesIO(), BytesIO()]
     for buffer in files:
         imwrite(buffer, data)
-    with pytest.raises(ValueError):
+    with pytest.raises(TypeError):
         imread(files)
 
 
@@ -9685,16 +9719,16 @@ def test_sequence_leica_series():
 def test_sequence_zip_container():
     """Test TiffSequence with glob pattern without axes pattern."""
     fname = private_file('TiffSequence.zip')
-    tifs = TiffSequence('*.tif', container=fname, pattern=None)
-    assert len(tifs) == 10
-    assert tifs.shape == (10,)
-    assert tifs.axes == 'I'
-    data = tifs.asarray()
-    assert isinstance(data, numpy.ndarray)
-    assert data.flags['C_CONTIGUOUS']
-    assert data.shape == (10, 480, 640)
-    assert data.dtype == 'uint8'
-    assert data[9, 256, 256] == 135
+    with TiffSequence('*.tif', container=fname, pattern=None) as tifs:
+        assert len(tifs) == 10
+        assert tifs.shape == (10,)
+        assert tifs.axes == 'I'
+        data = tifs.asarray()
+        assert isinstance(data, numpy.ndarray)
+        assert data.flags['C_CONTIGUOUS']
+        assert data.shape == (10, 480, 640)
+        assert data.dtype == 'uint8'
+        assert data[9, 256, 256] == 135
 
 
 @pytest.mark.skipif(


=====================================
tifffile.egg-info/PKG-INFO
=====================================
@@ -1,11 +1,13 @@
 Metadata-Version: 2.1
 Name: tifffile
-Version: 2020.5.11
+Version: 2020.6.3
 Summary: Read and write TIFF(r) files
 Home-page: https://www.lfd.uci.edu/~gohlke/
 Author: Christoph Gohlke
 Author-email: cgohlke at uci.edu
 License: BSD
+Project-URL: Bug Tracker, https://github.com/cgohlke/tifffile/issues
+Project-URL: Source Code, https://github.com/cgohlke/tifffile
 Description: Read and write TIFF(r) files
         ============================
         
@@ -46,23 +48,33 @@ Description: Read and write TIFF(r) files
         
         :License: BSD 3-Clause
         
-        :Version: 2020.5.11
+        :Version: 2020.6.3
         
         Requirements
         ------------
         This release has been tested with the following requirements and dependencies
         (other versions may work):
         
-        * `CPython 3.6.8, 3.7.7, 3.8.2 64-bit <https://www.python.org>`_
-        * `Numpy 1.16.6 <https://www.numpy.org>`_
-        * `Imagecodecs 2020.2.18 <https://pypi.org/project/imagecodecs/>`_
+        * `CPython 3.6.8, 3.7.7, 3.8.3 64-bit <https://www.python.org>`_
+        * `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+        * `Imagecodecs 2020.5.30 <https://pypi.org/project/imagecodecs/>`_
           (required only for encoding or decoding LZW, JPEG, etc.)
         * `Matplotlib 3.1 <https://www.matplotlib.org>`_ (required only for plotting)
         
         Revisions
         ---------
-        2020.5.11
+        2020.6.3
             Pass 2908 tests.
+            Support os.PathLike file names (#9).
+        2020.5.30
+            Re-add pure Python PackBits decoder.
+        2020.5.25
+            Make imagecodecs an optional dependency again.
+            Disable multi-threaded decoding of small LZW compressed segments.
+            Fix caching of TiffPage.decode function.
+            Fix xml.etree.cElementTree ImportError on Python 3.9.
+            Fix tostring DeprecationWarning.
+        2020.5.11
             Fix reading ImageJ grayscale mode RGB images (#6).
             Remove napari reader plugin.
         2020.5.7
@@ -208,10 +220,6 @@ Description: Read and write TIFF(r) files
         
         Python 32-bit versions are deprecated.
         
-        Update pip and setuptools to the latest version before installing tifffile:
-        
-            ``python -m pip install --upgrade pip setuptools``
-        
         Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
         package for encoding and decoding LZW, JPEG, and other compressed images.
         
@@ -273,6 +281,7 @@ Description: Read and write TIFF(r) files
           <https://github.com/apeer-micro/apeer-ometiff-library>`_
         * `Allen Institute for Cell Science imageio
           <https://pypi.org/project/aicsimageio>`_
+        * `xtiff <https://github.com/BodenmillerGroup/xtiff>`_
         
         References
         ----------
@@ -475,5 +484,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Requires-Python: >=3.6
 Provides-Extra: all


=====================================
tifffile.egg-info/requires.txt
=====================================
@@ -1,5 +1,5 @@
 numpy>=1.15.1
-imagecodecs>=2020.2.18
 
 [all]
+imagecodecs>=2020.2.18
 matplotlib>=3.1


=====================================
tifffile/tifffile.py
=====================================
@@ -68,23 +68,33 @@ For command line usage run ``python -m tifffile --help``
 
 :License: BSD 3-Clause
 
-:Version: 2020.5.11
+:Version: 2020.6.3
 
 Requirements
 ------------
 This release has been tested with the following requirements and dependencies
 (other versions may work):
 
-* `CPython 3.6.8, 3.7.7, 3.8.2 64-bit <https://www.python.org>`_
-* `Numpy 1.16.6 <https://www.numpy.org>`_
-* `Imagecodecs 2020.2.18 <https://pypi.org/project/imagecodecs/>`_
+* `CPython 3.6.8, 3.7.7, 3.8.3 64-bit <https://www.python.org>`_
+* `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+* `Imagecodecs 2020.5.30 <https://pypi.org/project/imagecodecs/>`_
   (required only for encoding or decoding LZW, JPEG, etc.)
 * `Matplotlib 3.1 <https://www.matplotlib.org>`_ (required only for plotting)
 
 Revisions
 ---------
-2020.5.11
+2020.6.3
     Pass 2908 tests.
+    Support os.PathLike file names (#9).
+2020.5.30
+    Re-add pure Python PackBits decoder.
+2020.5.25
+    Make imagecodecs an optional dependency again.
+    Disable multi-threaded decoding of small LZW compressed segments.
+    Fix caching of TiffPage.decode function.
+    Fix xml.etree.cElementTree ImportError on Python 3.9.
+    Fix tostring DeprecationWarning.
+2020.5.11
     Fix reading ImageJ grayscale mode RGB images (#6).
     Remove napari reader plugin.
 2020.5.7
@@ -230,10 +240,6 @@ Tested on little-endian platforms only.
 
 Python 32-bit versions are deprecated.
 
-Update pip and setuptools to the latest version before installing tifffile:
-
-    ``python -m pip install --upgrade pip setuptools``
-
 Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
 package for encoding and decoding LZW, JPEG, and other compressed images.
 
@@ -295,6 +301,7 @@ Some libraries are using tifffile to write OME-TIFF files:
   <https://github.com/apeer-micro/apeer-ometiff-library>`_
 * `Allen Institute for Cell Science imageio
   <https://pypi.org/project/aicsimageio>`_
+* `xtiff <https://github.com/BodenmillerGroup/xtiff>`_
 
 References
 ----------
@@ -489,7 +496,7 @@ Create a TIFF file from an iterator of tiles:
 
 """
 
-__version__ = '2020.5.11'
+__version__ = '2020.6.3'
 
 __all__ = (
     'imwrite',
@@ -541,7 +548,6 @@ import time
 import json
 import enum
 import struct
-import pathlib
 import warnings
 import binascii
 import datetime
@@ -570,7 +576,7 @@ def imread(files, **kwargs):
 
     Parameters
     ----------
-    files : str, binary stream, or sequence
+    files : str, path-like, binary stream, or sequence
         File name, seekable binary stream, glob pattern, or sequence of
         file names.
     kwargs : dict
@@ -614,10 +620,14 @@ def imread(files, **kwargs):
             files = glob.glob(files)
         if not files:
             raise ValueError('no files found')
-        if not hasattr(files, 'seek') and len(files) == 1:
+        if (
+            not hasattr(files, 'seek') and
+            not isinstance(files, (str, os.PathLike)) and
+            len(files) == 1
+        ):
             files = files[0]
 
-        if isinstance(files, str) or hasattr(files, 'seek'):
+        if isinstance(files, (str, os.PathLike)) or hasattr(files, 'seek'):
             with TiffFile(files, **kwargs_file) as tif:
                 return tif.asarray(**kwargs)
 
@@ -636,7 +646,7 @@ def imwrite(file, data=None, shape=None, dtype=None, **kwargs):
 
     Parameters
     ----------
-    file : str or binary stream
+    file : str, path-like, or binary stream
         File name or writable binary stream, such as an open file or BytesIO.
     data : array_like
         Input image. The last dimensions are assumed to be image depth,
@@ -704,7 +714,7 @@ def memmap(filename, shape=None, dtype=None, page=None, series=0, mode='r+',
 
     Parameters
     ----------
-    filename : str
+    filename : str or path-like
         Name of the TIFF file which stores the array.
     shape : tuple
         Shape of the empty array.
@@ -810,7 +820,7 @@ class TiffWriter:
 
         Parameters
         ----------
-        file : str, binary stream, or FileHandle
+        file : str, path-like, binary stream, or FileHandle
             File name or writable binary stream, such as an open file
             or BytesIO.
         bigtiff : bool
@@ -1494,7 +1504,7 @@ class TiffWriter:
                         raise RuntimeError('value.size != count')
                     if value.dtype.char != dtype:
                         raise RuntimeError('value.dtype.char != dtype')
-                    ifdvalue = value.tostring()
+                    ifdvalue = value.tobytes()
                 elif isinstance(value, (tuple, list)):
                     ifdvalue = pack(str(count) + dtype, *value)
                 else:
@@ -2139,7 +2149,7 @@ class TiffFile:
     """
 
     def __init__(self, arg, name=None, offset=None, size=None, multifile=True,
-                 _useframes=None, **kwargs):
+                 _useframes=None, _master=None, **kwargs):
         """Initialize instance from file.
 
         Parameters
@@ -2185,6 +2195,8 @@ class TiffFile:
         self._multifile = bool(multifile)
         self._files = {fh.name: self}  # cache of TiffFiles
         self._decoders = {}  # cache of TiffPage.decode functions
+        self._master = self if _master is None else _master
+
         try:
             fh.seek(0)
             header = fh.read(4)
@@ -2739,7 +2751,8 @@ class TiffFile:
 
     def _series_ome(self):
         """Return image series in OME-TIFF file(s)."""
-        from xml.etree import cElementTree as etree  # delayed import
+        from xml.etree import ElementTree as etree  # delayed import
+
         omexml = self.pages[0].description
         try:
             root = etree.fromstring(omexml)
@@ -2853,7 +2866,10 @@ class TiffFile:
                                 return []
                             fname = uuid.attrib['FileName']
                             try:
-                                tif = TiffFile(os.path.join(dirname, fname))
+                                tif = TiffFile(
+                                    os.path.join(dirname, fname),
+                                    _master=self
+                                )
                                 tif.pages.cache = True
                                 tif.pages.useframes = True
                                 tif.pages.keyframe = 0
@@ -4306,8 +4322,12 @@ class TiffPage:
         Raises ValueError or NotImplementedError if decoding is not supported.
 
         """
-        if self.hash in self.parent._decoders:
-            return self.parent._decoders[self.hash]
+        if self.hash in self.parent._master._decoders:
+            return self.parent._master._decoders[self.hash]
+
+        def cache(decode):
+            self.parent._master._decoders[self.hash] = decode
+            return decode
 
         if self.dtype is None:
             def decode(*args, **kwargs):
@@ -4315,23 +4335,27 @@ class TiffPage:
                     f'TiffPage {self.index}: data type not supported: '
                     f'{self.sampleformat}{self.bitspersample}'
                 )
-            return decode
+            return cache(decode)
 
-        if self.compression not in TIFF.DECOMPESSORS:
-            if imagecodecs is None:
-                def decode(*args, **kwargs):
-                    raise ValueError(
-                        f'TiffPage {self.index}: '
-                        f'cannot decompress {self.compression.name}. '
-                        "The 'imagecodecs' package is not installed"
-                    )
+        try:
+            if self.compression == 1:
+                decompress = None
             else:
-                def decode(*args, **kwargs):
-                    raise ValueError(
-                        f'TiffPage {self.index}: '
-                        f'cannot decompress {self.compression.name}'
-                    )
-            return decode
+                decompress = TIFF.DECOMPESSORS[self.compression]
+        except KeyError as exc:
+            def decode(*args, exc=str(exc)[1:-1], **kwargs):
+                raise ValueError(f'TiffPage {self.index}: {exc}')
+            return cache(decode)
+
+        try:
+            if self.predictor == 1:
+                unpredict = None
+            else:
+                unpredict = TIFF.UNPREDICTORS[self.predictor]
+        except KeyError as exc:
+            def decode(*args, exc=str(exc)[1:-1], **kwargs):
+                raise ValueError(f'TiffPage {self.index}: {exc}')
+            return cache(decode)
 
         if self.tags.get(339) is not None:
             tag = self.tags[339]  # SampleFormat
@@ -4341,7 +4365,7 @@ class TiffPage:
                         f'TiffPage {self.index}: '
                         f'sample formats do not match {tag.value}'
                     )
-                return decode
+                return cache(decode)
 
         if (
             self.is_subsampled and
@@ -4351,17 +4375,7 @@ class TiffPage:
                 raise NotImplementedError(
                     f'TiffPage {self.index}: chroma subsampling not supported'
                 )
-            return decode
-
-        if self.predictor == 1:
-            unpredict = None
-        else:
-            unpredict = TIFF.UNPREDICTORS[self.predictor]
-
-        if self.compression == 1:
-            decompress = None
-        else:
-            decompress = TIFF.DECOMPESSORS[self.compression]
+            return cache(decode)
 
         # normalize segments shape to [depth, length, height, contig]
         if self.is_tiled:
@@ -4475,7 +4489,7 @@ class TiffPage:
                         f'TiffPage {self.index}: cannot decode JPEG '
                         f'with {self.bitspersample} bits per sample'
                     )
-                return decode
+                return cache(decode)
 
             if self.fillorder == 2:
                 log_warning(
@@ -4521,8 +4535,7 @@ class TiffPage:
                 data = reshape(data, index, shape)
                 return data, index, shape
 
-            self.parent._decoders[self.hash] = decode
-            return decode
+            return cache(decode)
 
         dtype = numpy.dtype(self.parent.byteorder + self._dtype.char)
 
@@ -4580,8 +4593,7 @@ class TiffPage:
                 data = unpredict(data, axis=-2, out=data)
             return data, index, shape
 
-        self.parent._decoders[self.hash] = decode
-        return decode
+        return cache(decode)
 
     def segments(self, lock=None, maxworkers=None, func=None, sort=False):
         """Return iterator over decoded segments in TiffPage.
@@ -4744,6 +4756,7 @@ class TiffPage:
             # decode individual strips or tiles
             result = create_output(out, keyframe.shaped, keyframe._dtype)
             out = result[0]
+            keyframe.decode  # init TiffPage.decode function
 
             def func(decoderesult, keyframe=keyframe, out=out):
                 # copy decoded segments to output array
@@ -4889,9 +4902,14 @@ class TiffPage:
     @lazyattr
     def maxworkers(self):
         """Return maximum number of threads for decoding strips or tiles."""
+        if self.is_contiguous:
+            return 1
         if len(self._offsetscounts[0]) < 4:
             return 1
         if self.compression != 1 or self.fillorder != 1 or self.predictor != 1:
+            if self.compression == 5 and self._offsetscounts[1][0] < 8192:
+                # disable multi-threading for small LZW compressed segments
+                return 1
             if imagecodecs is not None:
                 return min(TIFF.MAXWORKERS, len(self._offsetscounts[0]))
         return 2  # optimum for large number of uncompressed tiles
@@ -6140,7 +6158,7 @@ class FileSequence:
         fromfile : function or class
             Array read function or class with asarray function returning numpy
             array from single file.
-        files : str, pathlib.Path, or sequence thereof
+        files : str, path-like, or sequence thereof
             Glob filename pattern or sequence of file names. Default: \\*.
             Binary streams are not supported.
         container : str or container instance
@@ -6167,7 +6185,7 @@ class FileSequence:
         self._container = container
         if container:
             import fnmatch
-            if isinstance(container, str):
+            if isinstance(container, (str, os.PathLike)):
                 import zipfile
                 self._container = zipfile.ZipFile(container)
             elif not hasattr(self._container, 'open'):
@@ -6176,23 +6194,16 @@ class FileSequence:
                 files = fnmatch.filter(self._container.namelist(), files)
                 if sort:
                     files = sort(files)
-        else:
-            if isinstance(files, pathlib.Path):
-                files = str(files)
-            if isinstance(files, str):
-                files = glob.glob(files)
-                if sort:
-                    files = sort(files)
-            if not files:
-                raise ValueError('no files found')
+        elif isinstance(files, os.PathLike):
+            files = [os.fspath(files)]
+        elif isinstance(files, str):
+            files = glob.glob(files)
+            if sort:
+                files = sort(files)
 
-        files = list(files)
+        files = [os.fspath(f) for f in files]
         if not files:
             raise ValueError('no files found')
-        if isinstance(files[0], pathlib.Path):
-            files = [str(pathlib.Path(f)) for f in files]
-        elif not isinstance(files[0], str):
-            raise ValueError('not a file name')
 
         if hasattr(fromfile, 'asarray'):
             # redefine fromfile to use asarray from fromfile class
@@ -6380,7 +6391,7 @@ class FileHandle:
 
         Parameters
         ----------
-        file : str, pathlib.Path, binary stream, or FileHandle
+        file : str, path-like, binary stream, or FileHandle
             File name or seekable binary stream, such as an open file
             or BytesIO.
         mode : str
@@ -6412,8 +6423,8 @@ class FileHandle:
         if self._fh:
             return  # file is open
 
-        if isinstance(self._file, pathlib.Path):
-            self._file = str(self._file)
+        if isinstance(self._file, os.PathLike):
+            self._file = os.fspath(self._file)
         if isinstance(self._file, str):
             # file name
             self._file = os.path.realpath(self._file)
@@ -6654,7 +6665,7 @@ class FileHandle:
             data.tofile(self._fh)
         except Exception:
             # BytesIO
-            self._fh.write(data.tostring())
+            self._fh.write(data.tobytes())
 
     def tell(self):
         """Return file's current position."""
@@ -6836,6 +6847,8 @@ class Timer:
         i = 0
         while i < len(s) and s[i:i + 2] in '0:0010203040506070809':
             i += 1
+        if s[i:i + 1] == ':':
+            i += 1
         return f'{s[i:]} s'
 
     def __enter__(self):
@@ -8152,14 +8165,18 @@ class TIFF:
             def __getitem__(self, key):
                 if key in self._codecs:
                     return self._codecs[key]
-                if imagecodecs is None:
-                    raise KeyError(key)
-                if key == 2:
-                    codec = imagecodecs.delta_encode
-                elif key == 3:
-                    codec = imagecodecs.floatpred_encode
-                else:
-                    raise KeyError(key)
+                try:
+                    if key == 2:
+                        codec = imagecodecs.delta_encode
+                    elif key == 3:
+                        codec = imagecodecs.floatpred_encode
+                    else:
+                        raise KeyError(f'{key} is not a valid PREDICTOR')
+                except AttributeError:
+                    raise KeyError(
+                        f'{TIFF.PREDICTOR(key)!r}'
+                        " requires the 'imagecodecs' package"
+                    )
                 self._codecs[key] = codec
                 return codec
 
@@ -8177,14 +8194,18 @@ class TIFF:
             def __getitem__(self, key):
                 if key in self._codecs:
                     return self._codecs[key]
-                if imagecodecs is None:
-                    raise KeyError(key)
-                if key == 2:
-                    codec = imagecodecs.delta_decode
-                elif key == 3:
-                    codec = imagecodecs.floatpred_decode
-                else:
-                    raise KeyError(key)
+                try:
+                    if key == 2:
+                        codec = imagecodecs.delta_decode
+                    elif key == 3:
+                        codec = imagecodecs.floatpred_decode
+                    else:
+                        raise KeyError(f'{key} is not a valid PREDICTOR')
+                except AttributeError:
+                    raise KeyError(
+                        f'{TIFF.PREDICTOR(key)!r}'
+                        " requires the 'imagecodecs' package"
+                    )
                 self._codecs[key] = codec
                 return codec
 
@@ -8204,32 +8225,40 @@ class TIFF:
             def __getitem__(self, key):
                 if key in self._codecs:
                     return self._codecs[key]
-                if imagecodecs is None:
-                    raise KeyError(key)
-                if key == 5:
-                    codec = imagecodecs.lzw_encode
-                elif key == 7:
-                    codec = imagecodecs.jpeg_encode
-                elif key == 8 or key == 32946:
-                    codec = imagecodecs.zlib_encode
-                elif key == 32773:
-                    codec = imagecodecs.packbits_encode
-                elif key == 34712:
-                    codec = imagecodecs.jpeg2k_encode
-                elif key == 34887:
-                    codec = imagecodecs.lerc_encode
-                elif key == 34925:
-                    codec = imagecodecs.lzma_encode
-                elif key == 34933:
-                    codec = imagecodecs.png_encode
-                elif key == 34934:
-                    codec = imagecodecs.jpegxr_encode
-                elif key == 50000:
-                    codec = imagecodecs.zstd_encode
-                elif key == 50001:
-                    codec = imagecodecs.webp_encode
-                else:
-                    raise KeyError(key)
+                try:
+                    if key == 5:
+                        codec = imagecodecs.lzw_encode
+                    elif key == 7:
+                        codec = imagecodecs.jpeg_encode
+                    elif key == 8 or key == 32946:
+                        codec = imagecodecs.zlib_encode
+                    elif key == 32773:
+                        codec = imagecodecs.packbits_encode
+                    elif key == 34712:
+                        codec = imagecodecs.jpeg2k_encode
+                    elif key == 34887:
+                        codec = imagecodecs.lerc_encode
+                    elif key == 34925:
+                        codec = imagecodecs.lzma_encode
+                    elif key == 34933:
+                        codec = imagecodecs.png_encode
+                    elif key == 34934:
+                        codec = imagecodecs.jpegxr_encode
+                    elif key == 50000:
+                        codec = imagecodecs.zstd_encode
+                    elif key == 50001:
+                        codec = imagecodecs.webp_encode
+                    else:
+                        try:
+                            msg = f'{TIFF.COMPRESSION(key)!r} not supported'
+                        except ValueError:
+                            msg = f'{key} is not a valid COMPRESSION'
+                        raise KeyError(msg)
+                except AttributeError:
+                    raise KeyError(
+                        f'{TIFF.COMPRESSION(key)!r} '
+                        "requires the 'imagecodecs' package"
+                    )
                 self._codecs[key] = codec
                 return codec
 
@@ -8243,40 +8272,49 @@ class TIFF:
                 self._codecs = {None: identityfunc, 1: identityfunc}
                 if imagecodecs is None:
                     self._codecs[8] = zlib_decode
+                    self._codecs[32773] = packbits_decode
                     self._codecs[32946] = zlib_decode
                     self._codecs[34925] = lzma_decode
 
             def __getitem__(self, key):
                 if key in self._codecs:
                     return self._codecs[key]
-                if imagecodecs is None:
-                    raise KeyError(key)
-                if key == 5:
-                    codec = imagecodecs.lzw_decode
-                elif key == 6 or key == 7:
-                    codec = imagecodecs.jpeg_decode
-                elif key == 8 or key == 32946:
-                    codec = imagecodecs.zlib_decode
-                elif key == 32773:
-                    codec = imagecodecs.packbits_decode
-                # elif key == 34892:
-                #     codec = imagecodecs.jpeg_decode  # DNG lossy
-                elif key == 33003 or key == 33005 or key == 34712:
-                    codec = imagecodecs.jpeg2k_decode
-                elif key == 34887:
-                    codec = imagecodecs.lerc_decode
-                elif key == 34925:
-                    codec = imagecodecs.lzma_decode
-                elif key == 34933:
-                    codec = imagecodecs.png_decode
-                elif key == 34934:
-                    codec = imagecodecs.jpegxr_decode
-                elif key == 50000 or key == 34926:  # 34926 deprecated
-                    codec = imagecodecs.zstd_decode
-                elif key == 50001 or key == 34927:  # 34927 deprecated
-                    codec = imagecodecs.webp_decode
-                else:
-                    raise KeyError(key)
+                try:
+                    if key == 5:
+                        codec = imagecodecs.lzw_decode
+                    elif key == 6 or key == 7:
+                        codec = imagecodecs.jpeg_decode
+                    elif key == 8 or key == 32946:
+                        codec = imagecodecs.zlib_decode
+                    elif key == 32773:
+                        codec = imagecodecs.packbits_decode
+                    # elif key == 34892:
+                    #     codec = imagecodecs.jpeg_decode  # DNG lossy
+                    elif key == 33003 or key == 33005 or key == 34712:
+                        codec = imagecodecs.jpeg2k_decode
+                    elif key == 34887:
+                        codec = imagecodecs.lerc_decode
+                    elif key == 34925:
+                        codec = imagecodecs.lzma_decode
+                    elif key == 34933:
+                        codec = imagecodecs.png_decode
+                    elif key == 34934:
+                        codec = imagecodecs.jpegxr_decode
+                    elif key == 50000 or key == 34926:  # 34926 deprecated
+                        codec = imagecodecs.zstd_decode
+                    elif key == 50001 or key == 34927:  # 34927 deprecated
+                        codec = imagecodecs.webp_decode
+                    else:
+                        try:
+                            msg = f'{TIFF.COMPRESSION(key)!r} not supported'
+                        except ValueError:
+                            msg = f'{key} is not a valid COMPRESSION'
+                        raise KeyError(msg)
+                except AttributeError:
+                    raise KeyError(
+                        f'{TIFF.COMPRESSION(key)!r} '
+                        "requires the 'imagecodecs' package"
+                    )
                 self._codecs[key] = codec
                 return codec
 
@@ -9852,7 +9890,7 @@ def read_tvips_header(fh, byteorder, dtype, count, offsetsize):
         # decode utf16 strings
         for name, typestr in TIFF.TVIPS_HEADER_V2:
             if typestr.startswith('V'):
-                s = header[name].tostring().decode('utf-16', errors='ignore')
+                s = header[name].tobytes().decode('utf-16', errors='ignore')
                 result[name] = stripnull(s, null='\0')
             else:
                 result[name] = header[name].tolist()
@@ -10513,7 +10551,7 @@ def metaseries_description_metadata(description):
     if not description.startswith('<MetaData>'):
         raise ValueError('invalid MetaSeries image description')
 
-    from xml.etree import cElementTree as etree  # delayed import
+    from xml.etree import ElementTree as etree  # delayed import
 
     root = etree.fromstring(description)
     types = {
@@ -10861,6 +10899,38 @@ if imagecodecs is None:
             f'to {numpy.dtype(dtype)} not supported'
         )
 
+    def packbits_decode(encoded, out=None):
+        r"""Decompress PackBits encoded byte string.
+
+        >>> packbits_decode(b'\x80\x80')  # NOP
+        b''
+        >>> packbits_decode(b'\x02123')
+        b'123'
+        >>> packbits_decode(
+        ...   b'\xfe\xaa\x02\x80\x00\x2a\xfd\xaa\x03\x80\x00\x2a\x22\xf7\xaa'
+        ...     )[:-5]
+        b'\xaa\xaa\xaa\x80\x00*\xaa\xaa\xaa\xaa\x80\x00*"\xaa\xaa\xaa\xaa\xaa'
+
+        """
+        out = []
+        out_extend = out.extend
+        i = 0
+        try:
+            while True:
+                n = ord(encoded[i:i + 1]) + 1
+                i += 1
+                if n > 129:
+                    # replicate
+                    out_extend(encoded[i:i + 1] * (258 - n))
+                    i += 1
+                elif n < 129:
+                    # literal
+                    out_extend(encoded[i:i + n])
+                    i += n
+        except TypeError:
+            pass
+        return bytes(out)
+
 else:
     bitorder_decode = imagecodecs.bitorder_decode  # noqa
     packints_decode = imagecodecs.packints_decode  # noqa
@@ -11218,39 +11288,36 @@ def stack_pages(pages, out=None, maxworkers=None, **kwargs):
     dtype = page0.dtype
     out = create_output(out, shape, dtype)
 
-    page_maxworkers = 1
+    # TODO: benchmark and optimize this
     if maxworkers is None or maxworkers < 1:
         # auto-detect
-        maxworkers = TIFF.MAXWORKERS
-        if maxworkers == 1:
-            kwargs['maxworkers'] = 1
+        page_maxworkers = page0.maxworkers
+        maxworkers = min(npages, TIFF.MAXWORKERS)
+        if maxworkers == 1 or page0.is_contiguous:
+            maxworkers = page_maxworkers = 1
         elif npages < 3:
             maxworkers = 1
+        elif (
+            page_maxworkers <= 2 and
+            page0.compression == 1 and
+            page0.fillorder == 1 and
+            page0.predictor == 1
+        ):
+            maxworkers = 1
+        elif page0.compression == 5 and page0._offsetscounts[1][0] < 8192:
+            # disable for small LZW compressed segments
+            maxworkers = page_maxworkers = 1
         else:
-            # TODO: optimize this
-            page_maxworkers = page0.maxworkers
-            kwargs['maxworkers'] = page_maxworkers
-            if (
-                page_maxworkers <= 2 and
-                page0.compression == 1 and
-                page0.fillorder == 1 and
-                page0.predictor == 1
-            ):
-                # multi-threading not worth
-                maxworkers = 1
-            elif page_maxworkers > 2:
-                maxworkers = max(maxworkers - page_maxworkers, 1)
+            page_maxworkers = 1
     elif maxworkers == 1:
-        # disable
-        kwargs['maxworkers'] = 1
+        maxworkers = page_maxworkers = 1
+    elif npages > maxworkers or page0.maxworkers < 2:
+        page_maxworkers = 1
     else:
-        # prefer parallel decoding of tiles or stripes
-        # use remaining threads for parallel decoding of pages
-        page_maxworkers = page0.maxworkers
-        kwargs['maxworkers'] = page_maxworkers
-        if page_maxworkers > 1:
-            maxworkers = max(maxworkers - page_maxworkers, 1)
+        page_maxworkers = maxworkers
+        maxworkers = 1
 
+    kwargs['maxworkers'] = page_maxworkers
     page0.parent.filehandle.lock = maxworkers > 1 or page_maxworkers > 1
 
     filecache = OpenFileCache(size=max(4, maxworkers),
@@ -11268,6 +11335,7 @@ def stack_pages(pages, out=None, maxworkers=None, **kwargs):
         for i, page in enumerate(pages):
             func(page, i)
     else:
+        page0.decode  # init TiffPage.decode function
         with ThreadPoolExecutor(maxworkers) as executor:
             for _ in executor.map(func, pages, range(npages)):
                 pass
@@ -11311,8 +11379,6 @@ def create_output(out, shape, dtype, mode='w+', suffix=None):
         if not numpy.can_cast(dtype, out.dtype):
             raise ValueError('incompatible output dtype')
         return out.reshape(shape)
-    if isinstance(out, pathlib.Path):
-        out = str(out)
     return numpy.memmap(out, shape=shape, dtype=dtype, mode=mode)
 
 
@@ -11717,7 +11783,7 @@ def xml2dict(xml, sanitize=True, prefix=None):
     {'level1': {'level2': 3.5322}}
 
     """
-    from xml.etree import cElementTree as etree  # delayed import
+    from xml.etree import ElementTree as etree  # delayed import
 
     at = tx = ''
     if prefix:



View it on GitLab: https://salsa.debian.org/python-team/modules/tifffile/-/commit/c3951b7521d4022a8306736820729794b84e15eb

-- 
View it on GitLab: https://salsa.debian.org/python-team/modules/tifffile/-/commit/c3951b7521d4022a8306736820729794b84e15eb
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20200612/7ba22499/attachment-0001.html>


More information about the debian-med-commit mailing list