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

Ole Streicher gitlab at salsa.debian.org
Sat Jul 18 09:39:37 BST 2020



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


Commits:
2a5bc8b5 by Ole Streicher at 2020-07-18T10:31:29+02:00
New upstream version 20200717
- - - - -


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,25 @@
 Revisions
 ---------
+2020.7.17
+    Pass 3022 tests.
+    Initial support for writing OME-TIFF (WIP).
+    Return samples as separate dimension in OME series (breaking).
+    Fix modulo dimensions for multiple OME series.
+    Fix some test errors on big endian systems (#18).
+    Fix BytesWarning.
+    Allow to pass TIFF.PREDICTOR values to TiffWriter.save.
+2020.7.4
+    Deprecate support for Python 3.6 (NEP 29).
+    Move pyramidal subresolution series to TiffPageSeries.levels (breaking).
+    Add parser for SVS, SCN, NDPI, and QPI pyramidal series.
+    Read single-file OME-TIFF pyramids.
+    Read NDPI files > 4 GB (#15).
+    Include SubIFDs in generic series.
+    Preliminary support for writing packed integer arrays (#11, WIP).
+    Read more LSM info subrecords.
+    Fix missing ReferenceBlackWhite tag for YCbCr photometrics.
+    Fix reading lossless JPEG compressed DNG files.
 2020.6.3
-    Pass 2908 tests.
     Support os.PathLike file names (#9).
 2020.5.30
     Re-add pure Python PackBits decoder.


=====================================
PKG-INFO
=====================================
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tifffile
-Version: 2020.6.3
+Version: 2020.7.17
 Summary: Read and write TIFF(r) files
 Home-page: https://www.lfd.uci.edu/~gohlke/
 Author: Christoph Gohlke
@@ -18,10 +18,11 @@ Description: Read and write TIFF(r) files
         
         Image and metadata can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, SGI,
         NIHImage, ImageJ, MicroManager, FluoView, ScanImage, SEQ, GEL, SVS, SCN, SIS,
-        ZIF, QPTIFF, NDPI, and GeoTIFF files.
+        ZIF (Zoomable Image File Format), QPTIFF (QPI), NDPI, and GeoTIFF files.
         
-        Numpy arrays can be written to TIFF, BigTIFF, and ImageJ hyperstack compatible
-        files in multi-page, memory-mappable, tiled, predicted, or compressed form.
+        Numpy arrays can be written to TIFF, BigTIFF, OME-TIFF, and ImageJ hyperstack
+        compatible files in multi-page, memory-mappable, tiled, predicted, or
+        compressed form.
         
         A subset of the TIFF specification is supported, mainly uncompressed and
         losslessly compressed 8, 16, 32 and 64-bit integer, 16, 32 and 64-bit float,
@@ -36,7 +37,7 @@ Description: Read and write TIFF(r) files
         extensions defined by Molecular Devices (Universal Imaging Corporation),
         Carl Zeiss MicroImaging, Olympus, Silicon Graphics International,
         Media Cybernetics, Molecular Dynamics, PerkinElmer, Hamamatsu, and the
-        Open Microscopy Environment consortium respectively.
+        Open Microscopy Environment consortium, respectively.
         
         For command line usage run ``python -m tifffile --help``
         
@@ -48,23 +49,44 @@ Description: Read and write TIFF(r) files
         
         :License: BSD 3-Clause
         
-        :Version: 2020.6.3
+        :Version: 2020.7.17
         
         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.3 64-bit <https://www.python.org>`_
-        * `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+        * `CPython 3.7.8, 3.8.4, 3.9.0b4 64-bit <https://www.python.org>`_
+        * `Numpy 1.18.5 <https://pypi.org/project/numpy/>`_
         * `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)
+        * `Matplotlib 3.2.2 <https://pypi.org/project/matplotlib/>`_
+          (required only for plotting)
+        * `Lxml 4.5.2 <https://github.com/lxml/lxml>`_
+          (required only for validating and printing XML)
         
         Revisions
         ---------
+        2020.7.17
+            Pass 3022 tests.
+            Initial support for writing OME-TIFF (WIP).
+            Return samples as separate dimension in OME series (breaking).
+            Fix modulo dimensions for multiple OME series.
+            Fix some test errors on big endian systems (#18).
+            Fix BytesWarning.
+            Allow to pass TIFF.PREDICTOR values to TiffWriter.save.
+        2020.7.4
+            Deprecate support for Python 3.6 (NEP 29).
+            Move pyramidal subresolution series to TiffPageSeries.levels (breaking).
+            Add parser for SVS, SCN, NDPI, and QPI pyramidal series.
+            Read single-file OME-TIFF pyramids.
+            Read NDPI files > 4 GB (#15).
+            Include SubIFDs in generic series.
+            Preliminary support for writing packed integer arrays (#11, WIP).
+            Read more LSM info subrecords.
+            Fix missing ReferenceBlackWhite tag for YCbCr photometrics.
+            Fix reading lossless JPEG compressed DNG files.
         2020.6.3
-            Pass 2908 tests.
             Support os.PathLike file names (#9).
         2020.5.30
             Re-add pure Python PackBits decoder.
@@ -218,10 +240,11 @@ Description: Read and write TIFF(r) files
         
         Tested on little-endian platforms only.
         
-        Python 32-bit versions are deprecated.
+        Python 32-bit versions are deprecated. Python <= 3.6 are no longer supported.
         
         Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
-        package for encoding and decoding LZW, JPEG, and other compressed images.
+        package for encoding and decoding LZW, JPEG, and other compressed image
+        segments.
         
         Several TIFF-like formats do not strictly adhere to the TIFF6 specification,
         some of which allow file or data sizes to exceed the 4 GB limit:
@@ -238,21 +261,23 @@ Description: Read and write TIFF(r) files
           files. The 8-bit UTF-8 encoded OME-XML metadata found in the ImageDescription
           tag of the first IFD defines the position of TIFF IFDs in the high
           dimensional data. Tifffile can read OME-TIFF files, except when the OME-XML
-          metadata are stored in a separate file.
+          metadata are stored in a separate file. Tifffile can write numpy arrays
+          to single-file, non-pyramidal OME-TIFF.
         * *LSM* stores all IFDs below 4 GB but wraps around 32-bit StripOffsets.
           The StripOffsets of each series and position require separate unwrapping.
           The StripByteCounts tag contains the number of bytes for the uncompressed
           data. Tifffile can read large LSM files.
         * *NDPI* uses some 64-bit offsets in the file header, IFD, and tag structures.
           Tag values/offsets can be corrected using high bits stored after IFD
-          structures. JPEG compressed tiles with dimensions > 65536 are not readable
-          with libjpeg. Tifffile can read NDPI files < 4 GB and decompress large JPEG
-          tiles using the imagecodecs library on Windows.
+          structures. JPEG compressed segments with dimensions >65536 or missing
+          restart markers are not readable with libjpeg. Tifffile can read NDPI
+          files > 4 GB. JPEG segments with restart markers and dimensions >65536 can
+          be decoded with the imagecodecs library on Windows.
         * *ScanImage* optionally allows corrupt non-BigTIFF files > 2 GB. The values
           of StripOffsets and StripByteCounts can be recovered using the constant
           differences of the offsets of IFD and tag values throughout the file.
-          Tifffile can read such files on Python 3 if the image data are stored
-          contiguously in each page.
+          Tifffile can read such files if the image data are stored contiguously in
+          each page.
         * *GeoTIFF* sparse files allow strip or tile offsets and byte counts to be 0.
           Such segments are implicitly set to 0 or the NODATA value on reading.
           Tifffile can read GeoTIFF sparse files.
@@ -285,29 +310,33 @@ Description: Read and write TIFF(r) files
         
         References
         ----------
-        1.  TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
-            https://www.adobe.io/open/standards/TIFF.html
-        2.  TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
-        3.  MetaMorph Stack (STK) Image File Format.
-            http://mdc.custhelp.com/app/answers/detail/a_id/18862
-        4.  Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
-            Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
-        5.  The OME-TIFF format.
-            https://docs.openmicroscopy.org/ome-model/5.6.4/ome-tiff/
-        6.  UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
-            http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
-        7.  Micro-Manager File Formats.
-            https://micro-manager.org/wiki/Micro-Manager_File_Formats
-        8.  Tags for TIFF and Related Specifications. Digital Preservation.
-            https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
-        9.  ScanImage BigTiff Specification - ScanImage 2016.
-            http://scanimage.vidriotechnologies.com/display/SI2016/
-            ScanImage+BigTiff+Specification
-        10. CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
-            Exif Version 2.31.
-            http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
-        11. ZIF, the Zoomable Image File format. http://zif.photo/
-        12. GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+        * TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
+          https://www.adobe.io/open/standards/TIFF.html
+        * TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
+        * The BigTIFF File Format.
+          https://www.awaresystems.be/imaging/tiff/bigtiff.html
+        * MetaMorph Stack (STK) Image File Format.
+          http://mdc.custhelp.com/app/answers/detail/a_id/18862
+        * Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
+          Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
+        * The OME-TIFF format.
+          https://docs.openmicroscopy.org/ome-model/latest/
+        * UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
+          http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
+        * Micro-Manager File Formats.
+          https://micro-manager.org/wiki/Micro-Manager_File_Formats
+        * ScanImage BigTiff Specification - ScanImage 2016.
+          http://scanimage.vidriotechnologies.com/display/SI2016/
+          ScanImage+BigTiff+Specification
+        * ZIF, the Zoomable Image File format. http://zif.photo/
+        * GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+        * Cloud optimized GeoTIFF.
+          https://github.com/cogeotiff/cog-spec/blob/master/spec.md
+        * Tags for TIFF and Related Specifications. Digital Preservation.
+          https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
+        * CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
+          Exif Version 2.31.
+          http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
         
         Examples
         --------
@@ -440,19 +469,21 @@ Description: Read and write TIFF(r) files
         ...             tag_name, tag_value = tag.name, tag.value
         ...         image = page.asarray()
         
-        Save two image series to a TIFF file:
+        Write two numpy arrays to a multi-series OME-TIFF file:
         
-        >>> data0 = numpy.random.randint(0, 255, (301, 219, 3), 'uint8')
-        >>> data1 = numpy.random.randint(0, 255, (5, 301, 219), 'uint16')
-        >>> with TiffWriter('temp.tif') as tif:
+        >>> data0 = numpy.random.randint(0, 255, (32, 32, 3), 'uint8')
+        >>> data1 = numpy.random.randint(0, 1023, (5, 256, 256), 'uint16')
+        >>> with TiffWriter('temp.ome.tif', ome=True) as tif:
         ...     tif.save(data0, compress=6, photometric='rgb')
-        ...     tif.save(data1, compress=6, photometric='minisblack', contiguous=False)
+        ...     tif.save(data1, photometric='minisblack', contiguous=False,
+        ...              metadata=dict(axes='ZYX', SignificantBits=10,
+        ...                            PositionZ=[0.0, 1.0, 2.0, 3.0, 4.0]))
         
-        Read the second image series from the TIFF file:
+        Read the second image series from the OME-TIFF file:
         
-        >>> series1 = imread('temp.tif', series=1)
+        >>> series1 = imread('temp.ome.tif', series=1)
         >>> series1.shape
-        (5, 301, 219)
+        (5, 256, 256)
         
         Read an image stack from a series of TIFF files with a file name pattern:
         
@@ -467,13 +498,46 @@ Description: Read and write TIFF(r) files
         >>> data.shape
         (1, 2, 64, 64)
         
-        Create a TIFF file from an iterator of tiles:
+        Create a TIFF file from a generator of tiles:
         
         >>> def tiles():
         ...     data = numpy.arange(3*4*16*16, dtype='uint16').reshape((3*4, 16, 16))
         ...     for i in range(data.shape[0]): yield data[i]
         >>> imwrite('temp.tif', tiles(), dtype='uint16', shape=(48, 64), tile=(16, 16))
         
+        Write a tiled, multi-resolution, pyramidal TIFF file using JPEG compression:
+        
+        >>> data = numpy.arange(1024*1024*3, dtype='uint8').reshape((1024, 1024, 3))
+        >>> with TiffWriter('temp.tif') as tif:
+        ...     options = dict(tile=(256, 256), compress='jpeg', metadata=None)
+        ...     tif.save(data, **options)
+        ...     # save pyramid levels. In production use resampling to generate levels!
+        ...     tif.save(data[::2, ::2], subfiletype=1, **options)
+        ...     tif.save(data[::4, ::4], subfiletype=1, **options)
+        
+        Access the image levels in the pyramidal TIFF file:
+        
+        >>> baseimage = imread('temp.tif')
+        >>> second_level = imread('temp.tif', series=0, level=1)
+        >>> with TiffFile('temp.tif') as tif:
+        ...     baseimage = tif.series[0].asarray()
+        ...     second_level = tif.series[0].levels[1].asarray()
+        
+        Iterate over and decode single JPEG compressed tiles in the TIFF file:
+        
+        >>> with TiffFile('temp.tif') as tif:
+        ...     fh = tif.filehandle
+        ...     for page in tif.pages:
+        ...         jpegtables = page.tags.get('JPEGTables', None)
+        ...         if jpegtables is not None:
+        ...             jpegtables = jpegtables.value
+        ...         for index, (offset, bytecount) in enumerate(
+        ...             zip(page.dataoffsets, page.databytecounts)
+        ...         ):
+        ...             fh.seek(offset)
+        ...             data = fh.read(bytecount)
+        ...             tile, indices, shape = page.decode(data, index, jpegtables)
+        
 Platform: any
 Classifier: Development Status :: 4 - Beta
 Classifier: License :: OSI Approved :: BSD License
@@ -481,7 +545,6 @@ Classifier: Intended Audience :: Science/Research
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 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


=====================================
README.rst
=====================================
@@ -8,10 +8,11 @@ Tifffile is a Python library to
 
 Image and metadata can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, SGI,
 NIHImage, ImageJ, MicroManager, FluoView, ScanImage, SEQ, GEL, SVS, SCN, SIS,
-ZIF, QPTIFF, NDPI, and GeoTIFF files.
+ZIF (Zoomable Image File Format), QPTIFF (QPI), NDPI, and GeoTIFF files.
 
-Numpy arrays can be written to TIFF, BigTIFF, and ImageJ hyperstack compatible
-files in multi-page, memory-mappable, tiled, predicted, or compressed form.
+Numpy arrays can be written to TIFF, BigTIFF, OME-TIFF, and ImageJ hyperstack
+compatible files in multi-page, memory-mappable, tiled, predicted, or
+compressed form.
 
 A subset of the TIFF specification is supported, mainly uncompressed and
 losslessly compressed 8, 16, 32 and 64-bit integer, 16, 32 and 64-bit float,
@@ -26,7 +27,7 @@ STK, LSM, FluoView, SGI, SEQ, GEL, QPTIFF, NDPI, and OME-TIFF, are custom
 extensions defined by Molecular Devices (Universal Imaging Corporation),
 Carl Zeiss MicroImaging, Olympus, Silicon Graphics International,
 Media Cybernetics, Molecular Dynamics, PerkinElmer, Hamamatsu, and the
-Open Microscopy Environment consortium respectively.
+Open Microscopy Environment consortium, respectively.
 
 For command line usage run ``python -m tifffile --help``
 
@@ -38,23 +39,44 @@ For command line usage run ``python -m tifffile --help``
 
 :License: BSD 3-Clause
 
-:Version: 2020.6.3
+:Version: 2020.7.17
 
 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.3 64-bit <https://www.python.org>`_
-* `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+* `CPython 3.7.8, 3.8.4, 3.9.0b4 64-bit <https://www.python.org>`_
+* `Numpy 1.18.5 <https://pypi.org/project/numpy/>`_
 * `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)
+* `Matplotlib 3.2.2 <https://pypi.org/project/matplotlib/>`_
+  (required only for plotting)
+* `Lxml 4.5.2 <https://github.com/lxml/lxml>`_
+  (required only for validating and printing XML)
 
 Revisions
 ---------
+2020.7.17
+    Pass 3022 tests.
+    Initial support for writing OME-TIFF (WIP).
+    Return samples as separate dimension in OME series (breaking).
+    Fix modulo dimensions for multiple OME series.
+    Fix some test errors on big endian systems (#18).
+    Fix BytesWarning.
+    Allow to pass TIFF.PREDICTOR values to TiffWriter.save.
+2020.7.4
+    Deprecate support for Python 3.6 (NEP 29).
+    Move pyramidal subresolution series to TiffPageSeries.levels (breaking).
+    Add parser for SVS, SCN, NDPI, and QPI pyramidal series.
+    Read single-file OME-TIFF pyramids.
+    Read NDPI files > 4 GB (#15).
+    Include SubIFDs in generic series.
+    Preliminary support for writing packed integer arrays (#11, WIP).
+    Read more LSM info subrecords.
+    Fix missing ReferenceBlackWhite tag for YCbCr photometrics.
+    Fix reading lossless JPEG compressed DNG files.
 2020.6.3
-    Pass 2908 tests.
     Support os.PathLike file names (#9).
 2020.5.30
     Re-add pure Python PackBits decoder.
@@ -208,10 +230,11 @@ The API is not stable yet and might change between revisions.
 
 Tested on little-endian platforms only.
 
-Python 32-bit versions are deprecated.
+Python 32-bit versions are deprecated. Python <= 3.6 are no longer supported.
 
 Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
-package for encoding and decoding LZW, JPEG, and other compressed images.
+package for encoding and decoding LZW, JPEG, and other compressed image
+segments.
 
 Several TIFF-like formats do not strictly adhere to the TIFF6 specification,
 some of which allow file or data sizes to exceed the 4 GB limit:
@@ -228,21 +251,23 @@ some of which allow file or data sizes to exceed the 4 GB limit:
   files. The 8-bit UTF-8 encoded OME-XML metadata found in the ImageDescription
   tag of the first IFD defines the position of TIFF IFDs in the high
   dimensional data. Tifffile can read OME-TIFF files, except when the OME-XML
-  metadata are stored in a separate file.
+  metadata are stored in a separate file. Tifffile can write numpy arrays
+  to single-file, non-pyramidal OME-TIFF.
 * *LSM* stores all IFDs below 4 GB but wraps around 32-bit StripOffsets.
   The StripOffsets of each series and position require separate unwrapping.
   The StripByteCounts tag contains the number of bytes for the uncompressed
   data. Tifffile can read large LSM files.
 * *NDPI* uses some 64-bit offsets in the file header, IFD, and tag structures.
   Tag values/offsets can be corrected using high bits stored after IFD
-  structures. JPEG compressed tiles with dimensions > 65536 are not readable
-  with libjpeg. Tifffile can read NDPI files < 4 GB and decompress large JPEG
-  tiles using the imagecodecs library on Windows.
+  structures. JPEG compressed segments with dimensions >65536 or missing
+  restart markers are not readable with libjpeg. Tifffile can read NDPI
+  files > 4 GB. JPEG segments with restart markers and dimensions >65536 can
+  be decoded with the imagecodecs library on Windows.
 * *ScanImage* optionally allows corrupt non-BigTIFF files > 2 GB. The values
   of StripOffsets and StripByteCounts can be recovered using the constant
   differences of the offsets of IFD and tag values throughout the file.
-  Tifffile can read such files on Python 3 if the image data are stored
-  contiguously in each page.
+  Tifffile can read such files if the image data are stored contiguously in
+  each page.
 * *GeoTIFF* sparse files allow strip or tile offsets and byte counts to be 0.
   Such segments are implicitly set to 0 or the NODATA value on reading.
   Tifffile can read GeoTIFF sparse files.
@@ -275,29 +300,33 @@ Some libraries are using tifffile to write OME-TIFF files:
 
 References
 ----------
-1.  TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
-    https://www.adobe.io/open/standards/TIFF.html
-2.  TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
-3.  MetaMorph Stack (STK) Image File Format.
-    http://mdc.custhelp.com/app/answers/detail/a_id/18862
-4.  Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
-    Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
-5.  The OME-TIFF format.
-    https://docs.openmicroscopy.org/ome-model/5.6.4/ome-tiff/
-6.  UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
-    http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
-7.  Micro-Manager File Formats.
-    https://micro-manager.org/wiki/Micro-Manager_File_Formats
-8.  Tags for TIFF and Related Specifications. Digital Preservation.
-    https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
-9.  ScanImage BigTiff Specification - ScanImage 2016.
-    http://scanimage.vidriotechnologies.com/display/SI2016/
-    ScanImage+BigTiff+Specification
-10. CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
-    Exif Version 2.31.
-    http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
-11. ZIF, the Zoomable Image File format. http://zif.photo/
-12. GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+* TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
+  https://www.adobe.io/open/standards/TIFF.html
+* TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
+* The BigTIFF File Format.
+  https://www.awaresystems.be/imaging/tiff/bigtiff.html
+* MetaMorph Stack (STK) Image File Format.
+  http://mdc.custhelp.com/app/answers/detail/a_id/18862
+* Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
+  Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
+* The OME-TIFF format.
+  https://docs.openmicroscopy.org/ome-model/latest/
+* UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
+  http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
+* Micro-Manager File Formats.
+  https://micro-manager.org/wiki/Micro-Manager_File_Formats
+* ScanImage BigTiff Specification - ScanImage 2016.
+  http://scanimage.vidriotechnologies.com/display/SI2016/
+  ScanImage+BigTiff+Specification
+* ZIF, the Zoomable Image File format. http://zif.photo/
+* GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+* Cloud optimized GeoTIFF.
+  https://github.com/cogeotiff/cog-spec/blob/master/spec.md
+* Tags for TIFF and Related Specifications. Digital Preservation.
+  https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
+* CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
+  Exif Version 2.31.
+  http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
 
 Examples
 --------
@@ -430,19 +459,21 @@ Iterate over pages and tags in the TIFF file and successively read images:
 ...             tag_name, tag_value = tag.name, tag.value
 ...         image = page.asarray()
 
-Save two image series to a TIFF file:
+Write two numpy arrays to a multi-series OME-TIFF file:
 
->>> data0 = numpy.random.randint(0, 255, (301, 219, 3), 'uint8')
->>> data1 = numpy.random.randint(0, 255, (5, 301, 219), 'uint16')
->>> with TiffWriter('temp.tif') as tif:
+>>> data0 = numpy.random.randint(0, 255, (32, 32, 3), 'uint8')
+>>> data1 = numpy.random.randint(0, 1023, (5, 256, 256), 'uint16')
+>>> with TiffWriter('temp.ome.tif', ome=True) as tif:
 ...     tif.save(data0, compress=6, photometric='rgb')
-...     tif.save(data1, compress=6, photometric='minisblack', contiguous=False)
+...     tif.save(data1, photometric='minisblack', contiguous=False,
+...              metadata=dict(axes='ZYX', SignificantBits=10,
+...                            PositionZ=[0.0, 1.0, 2.0, 3.0, 4.0]))
 
-Read the second image series from the TIFF file:
+Read the second image series from the OME-TIFF file:
 
->>> series1 = imread('temp.tif', series=1)
+>>> series1 = imread('temp.ome.tif', series=1)
 >>> series1.shape
-(5, 301, 219)
+(5, 256, 256)
 
 Read an image stack from a series of TIFF files with a file name pattern:
 
@@ -457,9 +488,42 @@ Read an image stack from a series of TIFF files with a file name pattern:
 >>> data.shape
 (1, 2, 64, 64)
 
-Create a TIFF file from an iterator of tiles:
+Create a TIFF file from a generator of tiles:
 
 >>> def tiles():
 ...     data = numpy.arange(3*4*16*16, dtype='uint16').reshape((3*4, 16, 16))
 ...     for i in range(data.shape[0]): yield data[i]
 >>> imwrite('temp.tif', tiles(), dtype='uint16', shape=(48, 64), tile=(16, 16))
+
+Write a tiled, multi-resolution, pyramidal TIFF file using JPEG compression:
+
+>>> data = numpy.arange(1024*1024*3, dtype='uint8').reshape((1024, 1024, 3))
+>>> with TiffWriter('temp.tif') as tif:
+...     options = dict(tile=(256, 256), compress='jpeg', metadata=None)
+...     tif.save(data, **options)
+...     # save pyramid levels. In production use resampling to generate levels!
+...     tif.save(data[::2, ::2], subfiletype=1, **options)
+...     tif.save(data[::4, ::4], subfiletype=1, **options)
+
+Access the image levels in the pyramidal TIFF file:
+
+>>> baseimage = imread('temp.tif')
+>>> second_level = imread('temp.tif', series=0, level=1)
+>>> with TiffFile('temp.tif') as tif:
+...     baseimage = tif.series[0].asarray()
+...     second_level = tif.series[0].levels[1].asarray()
+
+Iterate over and decode single JPEG compressed tiles in the TIFF file:
+
+>>> with TiffFile('temp.tif') as tif:
+...     fh = tif.filehandle
+...     for page in tif.pages:
+...         jpegtables = page.tags.get('JPEGTables', None)
+...         if jpegtables is not None:
+...             jpegtables = jpegtables.value
+...         for index, (offset, bytecount) in enumerate(
+...             zip(page.dataoffsets, page.databytecounts)
+...         ):
+...             fh.seek(offset)
+...             data = fh.read(bytecount)
+...             tile, indices, shape = page.decode(data, index, jpegtables)


=====================================
setup.py
=====================================
@@ -71,11 +71,11 @@ setup(
         # 'imagecodecs>=2020.2.18',
     ],
     extras_require={
-        'all': ['imagecodecs>=2020.2.18', 'matplotlib>=3.1'],
+        'all': ['imagecodecs>=2020.2.18', 'matplotlib>=3.1', 'lxml'],
     },
     tests_require=[
         'pytest', 'imagecodecs', 'czifile', 'cmapfile', 'oiffile', 'lfdfiles',
-        'roifile'
+        'roifile', 'lxml',
         ],
     entry_points={
         'console_scripts': [
@@ -92,7 +92,6 @@ setup(
         'Intended Audience :: Developers',
         'Operating System :: OS Independent',
         'Programming Language :: Python :: 3 :: Only',
-        '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.6.3
+:Version: 2020.7.17
 
 """
 
@@ -66,6 +66,8 @@ from numpy.testing import (
     assert_allclose,
 )
 
+from lxml import etree
+
 import tifffile
 
 try:
@@ -97,6 +99,7 @@ except NameError:
     STAR_IMPORTED = None
 
 from tifffile.tifffile import (
+    imagecodecs,
     TIFF,
     imwrite,
     imread,
@@ -112,6 +115,8 @@ from tifffile.tifffile import (
     TiffPageSeries,
     TiffTag,
     TiffTags,
+    OmeXmlError,
+    OmeXml,
     lazyattr,
     natural_sorted,
     stripnull,
@@ -153,6 +158,8 @@ from tifffile.tifffile import (
     hexdump,
     validate_jhove,
     xml2dict,
+    subresolution,
+    asbool,
 )
 
 # skip certain tests
@@ -161,7 +168,7 @@ SKIP_EXTENDED = False
 SKIP_ROUNDTRIPS = False
 SKIP_PUBLIC = False  # skip public files
 SKIP_PRIVATE = False  # skip private files
-SKIP_VALIDATE = True  # validate written files with jhove
+SKIP_VALIDATE = True  # skip validate written files with jhove
 SKIP_CODECS = False
 SKIP_32BIT = sys.maxsize < 2**32
 SKIP_BE = sys.byteorder == 'big'
@@ -212,13 +219,7 @@ if not os.path.exists(PRIVATE_DIR):
     SKIP_PRIVATE = True
 
 if not SKIP_CODECS:
-    try:
-        import imagecodecs  # noqa
-    except ImportError:
-        SKIP_CODECS = True
-    else:
-        if imagecodecs is None:
-            SKIP_CODECS = True
+    SKIP_CODECS = imagecodecs is None
 
 
 def config():
@@ -274,6 +275,22 @@ def assert__str__(tif, detail=3):
         TiffFile.__str__(tif, detail=i)
 
 
+def assert_valid_omexml(omexml, omexsd=None, _schema=[]):
+    """Validate OME-XML schema."""
+    if not _schema:
+        if omexsd is None:
+            omexsd = os.path.join(os.path.dirname(__file__), 'ome.xsd')
+        try:
+            _schema.append(etree.XMLSchema(etree.parse(omexsd)))
+        except Exception:
+            _schema.append(None)
+    if _schema and _schema[0] is not None:
+        if omexml.startswith('<?xml'):
+            omexml = omexml.split('>', 1)[-1]
+        xml = etree.fromstring(omexml)
+        _schema[0].assert_(xml)
+
+
 if SKIP_VALIDATE:
     def assert_valid(*args, **kwargs):
         """Do not validate TIFF file."""
@@ -384,6 +401,20 @@ def test_issue_infinite_loop():
         assert__str__(tif)
 
 
+ at pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
+def test_issue_jpeg_ia():
+    """Test JPEG compressed intensity image with alpha channel."""
+    # no extrasamples!
+    fname = private_file('issues/jpeg_ia.tiff')
+    with TiffFile(fname) as tif:
+        page = tif.pages[0]
+        assert page.compression == JPEG
+        assert_array_equal(
+            page.asarray(), numpy.array([[[0, 0], [255, 255]]], dtype='uint8')
+        )
+        assert__str__(tif)
+
+
 def test_issue_specific_pages():
     """Test read second page."""
     data = random_data('uint8', (3, 21, 31))
@@ -634,14 +665,16 @@ def test_issue_valueoffset():
                 page = tif.pages[0]
                 # inline value
                 fh.seek(page.tags['ImageLength'].valueoffset)
-                assert page.imagelength == unpack('H', fh.read(2))[0]
+                assert page.imagelength == unpack(tif.byteorder + 'H',
+                                                  fh.read(2))[0]
                 # separate value
                 fh.seek(page.tags['Software'].valueoffset)
                 assert page.software == bytes2str(fh.read(13))
                 # TiffFrame
                 page = tif.pages[1].aspage()
                 fh.seek(page.tags['StripOffsets'].valueoffset)
-                assert page.dataoffsets[0] == unpack('I', fh.read(4))[0]
+                assert page.dataoffsets[0] == unpack(tif.byteorder + 'I',
+                                                     fh.read(4))[0]
 
 
 @pytest.mark.skipif(SKIP_PUBLIC, reason=REASON)
@@ -816,7 +849,7 @@ def test_class_tifftags():
     data = random_data('uint8', (21, 31))
 
     with TempFileName('class_tifftags') as fname:
-        imwrite(fname, data, description='test', software=None)
+        imwrite(fname, data, description='test', software=False)
 
         with TiffFile(fname) as tif:
             tags = tif.pages[0].tags
@@ -938,6 +971,200 @@ def test_class_tifftagregistry():
     assert "41483, 'FlashEnergy'" in info
 
 
+ at pytest.mark.parametrize(
+    'shape, storedshape, dtype, axes, error',
+    [
+        # separate and contig
+        ((32, 32), (1, 2, 1, 32, 32, 2), 'uint8', None, ValueError),
+        # depth
+        ((32, 32, 32), (1, 1, 32, 32, 32, 1), 'uint8', None, OmeXmlError),
+        # dtype
+        ((32, 32), (1, 1, 1, 32, 32, 1), 'float16', None, OmeXmlError),
+        # empty
+        ((0, 0), (1, 1, 1, 0, 0, 1), 'uint8', None, OmeXmlError),
+        # not YX
+        ((32, 32), (1, 1, 1, 32, 32, 1), 'uint8', 'XZ', OmeXmlError),
+        # unknown axis
+        ((1, 32, 32), (1, 1, 1, 32, 32, 1), 'uint8', 'KYX', OmeXmlError),
+        # double axis
+        ((1, 32, 32), (1, 1, 1, 32, 32, 1), 'uint8', 'YYX', OmeXmlError),
+        # more than 5 dimensions
+        ((1, 1, 1, 5, 32, 32), (5, 1, 1, 32, 32, 1), 'uint8', None,
+         OmeXmlError),
+        # more than 6 dimensions
+        ((1, 1, 1, 1, 32, 32, 3), (1, 1, 1, 32, 32, 3), 'uint8', None,
+         OmeXmlError),
+        # more than 8 dimensions
+        ((1, 1, 1, 1, 1, 1, 1, 32, 32), (1, 1, 1, 32, 32, 1), 'uint8',
+         'ARHETZCYX', OmeXmlError),
+        # more than 9 dimensions
+        ((1, 1, 1, 1, 1, 1, 1, 32, 32, 3), (1, 1, 1, 32, 32, 3), 'uint8',
+         'ARHETZCYXS', OmeXmlError),
+        # double axis
+        ((1, 32, 32), (1, 1, 1, 32, 32, 1), 'uint8', 'YYX', OmeXmlError),
+        # planecount mismatch
+        ((3, 32, 32), (1, 1, 1, 32, 32, 1), 'uint8', 'CYX', ValueError),
+        # stored shape mismatch
+        ((3, 32, 32), (1, 2, 1, 32, 32, 1), 'uint8', 'SYX', ValueError),
+        ((32, 32, 3), (1, 1, 1, 32, 32, 2), 'uint8', 'YXS', ValueError),
+        ((3, 32, 32), (1, 3, 1, 31, 31, 1), 'uint8', 'SYX', ValueError),
+        ((32, 32, 3), (1, 1, 1, 31, 31, 3), 'uint8', 'YXS', ValueError),
+        ((32, 32), (1, 1, 1, 32, 31, 1), 'uint8', None, ValueError),
+        # too many modulo dimensions
+        ((2, 3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), 'uint8', 'RHEQYX',
+         OmeXmlError),
+    ]
+)
+def test_class_omexml_fail(shape, storedshape, dtype, axes, error):
+    """Test OmeXml class failures."""
+    metadata = {'axes': axes} if axes else {}
+    ox = OmeXml()
+    with pytest.raises(error):
+        ox.addimage(dtype, shape, storedshape, **metadata)
+
+
+ at pytest.mark.parametrize(
+    'axes, autoaxes, shape, storedshape, dimorder',
+    [
+        ('YX', 'YX', (32, 32), (1, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('YXS', 'YXS', (32, 32, 1), (1, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('SYX', 'SYX', (1, 32, 32), (1, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('YXS', 'YXS', (32, 32, 3), (1, 1, 1, 32, 32, 3), 'XYCZT'),
+        ('SYX', 'SYX', (3, 32, 32), (1, 3, 1, 32, 32, 1), 'XYCZT'),
+        ('CYX', 'CYX', (5, 32, 32), (5, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('CYXS', 'CYXS', (5, 32, 32, 1), (5, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('CSYX', 'ZCYX', (5, 1, 32, 32), (5, 1, 1, 32, 32, 1), 'XYCZT'),  # !
+        ('CYXS', 'CYXS', (5, 32, 32, 3), (5, 1, 1, 32, 32, 3), 'XYCZT'),
+        ('CSYX', 'CSYX', (5, 3, 32, 32), (5, 3, 1, 32, 32, 1), 'XYCZT'),
+        ('TZCYX', 'TZCYX', (3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), 'XYCZT'),
+        ('TZCYXS', 'TZCYXS', (3, 4, 5, 32, 32, 1), (60, 1, 1, 32, 32, 1),
+         'XYCZT'),
+        ('TZCSYX', 'TZCSYX', (3, 4, 5, 1, 32, 32), (60, 1, 1, 32, 32, 1),
+         'XYCZT'),
+        ('TZCYXS', 'TZCYXS', (3, 4, 5, 32, 32, 3), (60, 1, 1, 32, 32, 3),
+         'XYCZT'),
+        ('ZTCSYX', '', (3, 4, 5, 3, 32, 32), (60, 3, 1, 32, 32, 1), 'XYCTZ'),
+    ]
+)
+ at pytest.mark.parametrize('metadata', ('axes', None))
+def test_class_omexml(axes, autoaxes, shape, storedshape, dimorder, metadata):
+    """Test OmeXml class."""
+    dtype = 'uint8'
+    if not metadata and dimorder != 'XYCZT':
+        pytest.xfail('')
+    metadata = dict(axes=axes) if metadata else dict()
+    omexml = OmeXml()
+    omexml.addimage(dtype, shape, storedshape, **metadata)
+    assert '\n  ' in str(omexml)
+    omexml = omexml.tostring()
+    assert dimorder in omexml
+    if metadata:
+        autoaxes = axes
+    for ax in 'XYCZT':
+        if ax in autoaxes:
+            size = shape[autoaxes.index(ax)]
+        else:
+            size = 1
+        if ax == 'C':
+            size *= storedshape[1] * storedshape[-1]
+        assert f'Size{ax}="{size}"' in omexml
+    assert_valid_omexml(omexml)
+
+
+ at pytest.mark.parametrize(
+    'axes, shape, storedshape, sizetzc, dimorder',
+    [
+        ('ZAYX', (3, 4, 32, 32), (12, 1, 1, 32, 32, 1), (1, 12, 1),
+         'XYCZT'),
+        ('AYX', (3, 32, 32), (3, 1, 1, 32, 32, 1), (3, 1, 1),
+         'XYCZT'),
+        ('APYX', (3, 4, 32, 32), (12, 1, 1, 32, 32, 1), (3, 4, 1),
+         'XYCZT'),
+        ('TAYX', (3, 4, 32, 32), (12, 1, 1, 32, 32, 1), (12, 1, 1),
+         'XYCZT'),
+        ('CHYXS', (3, 4, 32, 32, 3), (12, 1, 1, 32, 32, 3), (1, 1, 36),
+         'XYCZT'),
+        ('CHSYX', (3, 4, 3, 32, 32), (12, 3, 1, 32, 32, 1), (1, 1, 36),
+         'XYCZT'),
+        ('APRYX', (3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), (3, 4, 5),
+         'XYCZT'),
+        ('TAPYX', (3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), (12, 5, 1),
+         'XYCZT'),
+        ('TZAYX', (3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), (3, 20, 1),
+         'XYCZT'),
+        ('ZCHYX', (3, 4, 5, 32, 32), (60, 1, 1, 32, 32, 1), (1, 3, 20),
+         'XYCZT'),
+        ('EPYX', (10, 5, 200, 200), (50, 1, 1, 200, 200, 1), (10, 5, 1),
+         'XYCZT'),
+        ('TQCPZRYX', (2, 3, 4, 5, 6, 7, 32, 32), (5040, 1, 1, 32, 32, 1),
+         (6, 42, 20), 'XYZCT'),
+    ]
+)
+def test_class_omexml_modulo(axes, shape, storedshape, sizetzc, dimorder):
+    """Test OmeXml class with modulo dimensions."""
+    dtype = 'uint8'
+    omexml = OmeXml()
+    omexml.addimage(dtype, shape, storedshape, axes=axes)
+    assert '\n  ' in str(omexml)
+    omexml = omexml.tostring()
+    assert dimorder in omexml
+    for ax, size in zip('TZC', sizetzc):
+        assert f'Size{ax}="{size}"' in omexml
+    assert_valid_omexml(omexml)
+
+
+def test_class_omexml_attributes():
+    """Test OmeXml class with attributes and elements."""
+    from uuid import uuid1  # noqa: delayed import
+
+    uuid = str(uuid1())
+    metadata = dict(
+        # document
+        uuid=uuid,
+        Creator=f'test_tifffile.py {tifffile.__version__}',
+        # image
+        axes='ZYXS',
+        Name='ImageName',
+        Acquisitiondate='2011-09-16T10:45:48',
+        Description='Image "Description" < & >\n{test}',
+        SignificantBits=12,
+        PhysicalSizeX=1.1,
+        PhysicalSizeXUnit='nm',
+        PhysicalSizeY=1.2,
+        PhysicalSizeYUnit='\xb5m',
+        PhysicalSizeZ=1.3,
+        PhysicalSizeZUnit='\xc5',
+        TimeIncrement=1.4,
+        TimeIncrementUnit='\xb5s',
+        Channel=dict(Name=['ChannelName']),  # one channel with 3 samples
+        Plane=dict(PositionZ=[0.0, 2.0, 4.0]),  # 3 Z-planes
+    )
+
+    omexml = OmeXml(**metadata)
+    omexml.addimage('uint16', (3, 32, 32, 3), (3, 1, 1, 32, 32, 3), **metadata)
+    assert '\n  ' in str(omexml)
+    omexml = omexml.tostring()
+    assert_valid_omexml(omexml)
+    assert uuid in omexml
+    assert 'SignificantBits="12"' in omexml
+    assert 'SamplesPerPixel="3" Name="ChannelName"' in omexml
+    assert 'TheC="0" TheZ="2" TheT="0" PositionZ="4.0"' in omexml
+
+
+def test_class_omexml_multiimage():
+    """Test OmeXml class with multiple images."""
+    omexml = OmeXml(description='multiimage')
+    omexml.addimage('uint8', (32, 32, 3), (1, 1, 1, 32, 32, 3), name='preview')
+    omexml.addimage('float32', (4, 256, 256), (4, 1, 1, 256, 256, 1), name='1')
+    omexml.addimage('bool', (256, 256), (1, 1, 1, 256, 256, 1), name='mask')
+    assert '\n  ' in str(omexml)
+    omexml = omexml.tostring()
+    assert 'TiffData IFD="0" PlaneCount="1"' in omexml
+    assert 'TiffData IFD="1" PlaneCount="4"' in omexml
+    assert 'TiffData IFD="5" PlaneCount="1"' in omexml
+    assert_valid_omexml(omexml)
+
+
 def test_func_xml2dict():
     """Test xml2dict function."""
     d = xml2dict("""<?xml version="1.0" ?>
@@ -996,7 +1223,8 @@ def test_func_memmap_fail():
     """Test non-native byteorder can not be memory mapped."""
     with TempFileName('memmap_fail') as fname:
         with pytest.raises(ValueError):
-            memmap(fname, shape=(16, 16), dtype='float32', byteorder='>')
+            memmap(fname, shape=(16, 16), dtype='float32',
+                   byteorder='>' if sys.byteorder == 'little' else '<')
 
 
 def test_func_repeat_nd():
@@ -1014,10 +1242,14 @@ def test_func_repeat_nd():
 
 def test_func_byteorder_isnative():
     """Test byteorder_isnative function."""
-    assert not byteorder_isnative('>')
-    assert byteorder_isnative('<')
-    assert byteorder_isnative('=')
     assert byteorder_isnative(sys.byteorder)
+    assert byteorder_isnative('=')
+    if sys.byteorder == 'little':
+        assert byteorder_isnative('<')
+        assert not byteorder_isnative('>')
+    else:
+        assert byteorder_isnative('>')
+        assert not byteorder_isnative('<')
 
 
 def test_func_reshape_nd():
@@ -1147,6 +1379,11 @@ def test_func_natural_sorted():
 def test_func_stripnull():
     """Test stripnull function."""
     assert stripnull(b'string\x00') == b'string'
+    assert stripnull('string\x00', null='\0') == 'string'
+    assert stripnull(b'string\x00string\x00\x00', first=False
+                     ) == b'string\x00string'
+    assert stripnull('string\x00string\x00\x00', null='\0', first=False
+                     ) == 'string\x00string'
 
 
 def test_func_stripascii():
@@ -1178,6 +1415,37 @@ def test_func_transpose_axes():
                           asaxes='CTZYX').shape == (5, 2, 1, 3, 4)
 
 
+def test_func_subresolution():
+    """Test subresolution function."""
+    class a:
+        dtype = 'uint8'
+        axes = 'QzyxS'
+        shape = (3, 256, 512, 1024, 4)
+
+    class b:
+        dtype = 'uint8'
+        axes = 'QzyxS'
+        shape = (3, 128, 256, 512, 4)
+
+    assert subresolution(a, a) == 0
+    assert subresolution(a, b) == 1
+    assert subresolution(a, b, p=2, n=2) == 1
+    assert subresolution(a, b, p=3) is None
+    b.shape = (3, 86, 171, 342, 4)
+    assert subresolution(a, b, p=3) == 1
+    b.shape = (3, 128, 256, 512, 2)
+    assert subresolution(a, b) is None
+    b.shape = (3, 64, 256, 512, 4)
+    assert subresolution(a, b) is None
+    b.shape = (3, 128, 64, 512, 4)
+    assert subresolution(a, b) is None
+    b.shape = (3, 128, 256, 1024, 4)
+    assert subresolution(a, b) is None
+    b.shape = (3, 32, 64, 128, 4)
+    assert subresolution(a, b) == 3
+
+
+ at pytest.mark.skipif(SKIP_BE, reason=REASON)
 def test_func_unpack_rgb():
     """Test unpack_rgb function."""
     data = struct.pack('BBBB', 0x21, 0x08, 0xFF, 0xFF)
@@ -1192,7 +1460,7 @@ def test_func_unpack_rgb():
 def test_func_json_description():
     """Test json_description function."""
     descr = json_description((256, 256, 3), axes='YXS')
-    assert json.loads(descr) == {"shape": [256, 256, 3], "axes": "YXS"}
+    assert json.loads(descr) == {'shape': [256, 256, 3], 'axes': 'YXS'}
 
 
 def test_func_json_description_metadata():
@@ -1400,6 +1668,22 @@ def test_func_hexdump():
         '................')
 
 
+def test_func_asbool():
+    """Test asbool function."""
+    for true in ('TRUE', ' True ', 'true '):
+        assert asbool(true)
+        assert asbool(true.encode())
+    for false in ('FALSE', ' False ', 'false '):
+        assert not asbool(false)
+        assert not asbool(false.encode())
+    assert asbool('ON', ['on'], ['off'])
+    assert asbool('ON', 'on', 'off')
+    with pytest.raises(TypeError):
+        assert asbool('Yes')
+    with pytest.raises(TypeError):
+        assert asbool('True', ['on'], ['off'])
+
+
 def test_func_snipstr():
     """Test snipstr function."""
     # cut middle
@@ -1849,7 +2133,7 @@ def test_filehandle_bytesio():
     with open(FILEHANDLE_NAME, 'rb') as fh:
         stream = BytesIO(fh.read())
     with FileHandle(stream) as fh:
-        assert fh.name == "Unnamed binary stream"
+        assert fh.name == 'Unnamed binary stream'
         assert not fh.is_file
         assert_filehandle(fh)
 
@@ -1861,7 +2145,7 @@ def test_filehandle_bytesio_offset():
         stream = BytesIO(fh.read())
     with FileHandle(stream, offset=FILEHANDLE_OFFSET,
                     size=FILEHANDLE_LENGTH) as fh:
-        assert fh.name == "Unnamed binary stream"
+        assert fh.name == 'Unnamed binary stream'
         assert not fh.is_file
         assert_filehandle(fh, offset=FILEHANDLE_OFFSET)
 
@@ -2293,7 +2577,7 @@ def test_read_vips():
     with TiffFile(fname) as tif:
         assert tif.byteorder == '<'
         assert len(tif.pages) == 4
-        assert len(tif.series) == 4
+        assert len(tif.series) == 1
         # assert page properties
         page = tif.pages[0]
         assert not page.is_reduced
@@ -2305,10 +2589,13 @@ def test_read_vips():
         assert page.samplesperpixel == 3
         # assert series properties
         series = tif.series[0]
+        assert series.is_pyramid
+        assert len(series.levels) == 4
         assert series.shape == (347, 641, 3)
         assert series.dtype.name == 'uint8'
         assert series.axes == 'YXS'
-        series = tif.series[3]
+        # level 3
+        series = series.levels[3]
         page = series.pages[0]
         assert page.is_reduced
         assert page.is_tiled
@@ -3343,6 +3630,36 @@ def test_read_zstd():
         assert__str__(tif)
 
 
+ at pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
+def test_read_cfa():
+    """Test read 14-bit uncompressed and JPEG compressed CFA image."""
+    fname = private_file('DNG/cinemadng/M14-1451_000085_cDNG_uncompressed.dng')
+    with TiffFile(fname) as tif:
+        assert len(tif.pages) == 1
+        page = tif.pages[0]
+        assert page.compression == 1
+        assert page.photometric == CFA
+        assert page.imagewidth == 960
+        assert page.imagelength == 540
+        assert page.bitspersample == 14
+        assert page.tags['CFARepeatPatternDim'].value == (2, 2)
+        assert page.tags['CFAPattern'].value == b'\x00\x01\x01\x02'
+        data = page.asarray()
+
+    fname = private_file('DNG/cinemadng/M14-1451_000085_cDNG_compressed.dng')
+    with TiffFile(fname) as tif:
+        assert len(tif.pages) == 1
+        page = tif.pages[0]
+        assert page.compression == JPEG
+        assert page.photometric == CFA
+        assert page.imagewidth == 960
+        assert page.imagelength == 540
+        assert page.bitspersample == 14
+        assert page.tags['CFARepeatPatternDim'].value == (2, 2)
+        assert page.tags['CFAPattern'].value == b'\x00\x01\x01\x02'
+        assert_array_equal(page.asarray(), data)
+
+
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
 def test_read_lena_be_f16_contig():
     """Test read big endian float16 horizontal differencing."""
@@ -3785,8 +4102,16 @@ def test_read_subifds_array():
     """Test read SubIFDs."""
     fname = public_file('Tiff-Library-4J/IFD struct/SubIFDs array E.tif')
     with TiffFile(fname) as tif:
-        assert len(tif.series) == 1
         assert len(tif.pages) == 1
+
+        # make sure no pyramid was detected
+        assert len(tif.series) == 5
+        assert tif.series[0].shape == (1500, 2000, 3)
+        assert tif.series[1].shape == (1200, 1600, 3)
+        assert tif.series[2].shape == (900, 1200, 3)
+        assert tif.series[3].shape == (600, 800, 3)
+        assert tif.series[4].shape == (300, 400, 3)
+
         page = tif.pages[0]
         assert page.photometric == RGB
         assert page.imagewidth == 2000
@@ -3884,6 +4209,30 @@ def test_read_subifd8():
         assert__str__(tif)
 
 
+def test_read_tiles():
+    """Test iteration over tiles, manually and via page.segments."""
+    data = numpy.arange(600*500*3, dtype='uint8').reshape((600, 500, 3))
+    with TempFileName('read_tiles') as fname:
+        with TiffWriter(fname) as tif:
+            options = dict(tile=(256, 256), compress='jpeg', metadata=None)
+            tif.save(data, **options)
+            tif.save(data[::2, ::2], subfiletype=1, **options)
+        with TiffFile(fname) as tif:
+            fh = tif.filehandle
+            for page in tif.pages:
+                segments = page.segments()
+                jpegtables = page.tags.get('JPEGTables', None)
+                if jpegtables is not None:
+                    jpegtables = jpegtables.value
+                for index, (offset, bytecount) in enumerate(
+                    zip(page.dataoffsets, page.databytecounts)
+                ):
+                    fh.seek(offset)
+                    data = fh.read(bytecount)
+                    tile, indices, shape = page.decode(data, index, jpegtables)
+                    assert_array_equal(tile, next(segments)[0])
+
+
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_32BIT, reason=REASON)
 def test_read_lsm_mosaic():
     """Test read LSM: PTZCYX (Mosaic mode), two areas, 32 samples, >4 GB."""
@@ -4567,13 +4916,13 @@ def test_read_stk_112508h100():
 
 
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
-def test_read_ndpi_cmu_1_ndpi():
+def test_read_ndpi_cmu1():
     """Test read Hamamatsu NDPI slide, JPEG."""
     fname = private_file('HamamatsuNDPI/CMU-1.ndpi')
     with TiffFile(fname) as tif:
         assert tif.is_ndpi
         assert len(tif.pages) == 5
-        assert len(tif.series) == 5
+        assert len(tif.series) == 2
         for page in tif.pages:
             assert page.ndpi_tags['Model'] == 'NanoZoomer'
         # first page
@@ -4596,14 +4945,14 @@ def test_read_ndpi_cmu_1_ndpi():
 
 @pytest.mark.skipif(
     SKIP_PRIVATE or SKIP_CODECS or SKIP_32BIT or SKIP_EXTENDED, reason=REASON)
-def test_read_ndpi_cmu_2():
+def test_read_ndpi_cmu2():
     """Test read Hamamatsu NDPI slide, JPEG."""
     # JPEG stream too large to be opened with unpatched libjpeg
     fname = private_file('HamamatsuNDPI/CMU-2.ndpi')
     with TiffFile(fname) as tif:
         assert tif.is_ndpi
         assert len(tif.pages) == 6
-        assert len(tif.series) == 6
+        assert len(tif.series) == 2
         for page in tif.pages:
             assert page.ndpi_tags['Model'] == 'NanoZoomer'
         # first page
@@ -4618,7 +4967,7 @@ def test_read_ndpi_cmu_2():
         assert data.shape == (33792, 79872, 3)
         assert data.dtype == 'uint8'
         # page 5
-        page = tif.pages[-1]
+        page = tif.pages[5]
         assert page.is_ndpi
         assert page.photometric == YCBCR
         assert page.compression == JPEG
@@ -4629,14 +4978,61 @@ def test_read_ndpi_cmu_2():
 
 
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
-def test_read_svs_cmu_1():
+def test_read_ndpi_4gb():
+    """Test read > 4GB Hamamatsu NDPI slide, JPEG 103680x188160."""
+    fname = private_file('HamamatsuNDPI/103680x188160.ndpi')
+    with TiffFile(fname) as tif:
+        assert tif.is_ndpi
+        assert len(tif.pages) == 8
+        assert len(tif.series) == 3
+        for page in tif.pages:
+            assert page.ndpi_tags['Model'] == 'C13220'
+        # first page
+        page = tif.pages[0]
+        assert page.is_ndpi
+        assert page.databytecounts == (4461521316,)
+        assert page.photometric == YCBCR
+        assert page.compression == JPEG
+        assert page.shape == (103680, 188160, 3)
+        assert page.ndpi_tags['Magnification'] == 40.0
+        assert page.ndpi_tags['McuStarts'][-1] == 4461516507  # corrected
+        # page 7
+        page = tif.pages[7]
+        assert page.is_ndpi
+        assert page.photometric == MINISBLACK
+        assert page.compression == NONE
+        assert page.shape == (200, 600)
+        assert page.ndpi_tags['Magnification'] == -2.0
+        # assert page.asarray()[226, 629, 0] == 167
+        # first series
+        series = tif.series[0]
+        assert series.kind == 'NDPI'
+        assert series.name == 'S10533009'
+        assert series.shape == (103680, 188160, 3)
+        assert series.is_pyramid
+        assert len(series.levels) == 6
+        assert len(series.pages) == 1
+        # pyramid levels
+        assert series.levels[1].shape == (51840, 94080, 3)
+        assert series.levels[2].shape == (25920, 47040, 3)
+        assert series.levels[3].shape == (12960, 23520, 3)
+        assert series.levels[4].shape == (6480, 11760, 3)
+        assert series.levels[5].shape == (3240, 5880, 3)
+        assert tuple(series.levels[5].asarray()[1000, 1000]) == (222, 165, 200)
+        # cannot decode base levels since JPEG compressed size > 2 GB
+        # series.levels[0].asarray()
+        assert__str__(tif)
+
+
+ at pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
+def test_read_svs_cmu1():
     """Test read Aperio SVS slide, JPEG and LZW."""
     fname = private_file('AperioSVS/CMU-1.svs')
     with TiffFile(fname) as tif:
         assert tif.is_svs
         assert not tif.is_scanimage
         assert len(tif.pages) == 6
-        assert len(tif.series) == 6
+        assert len(tif.series) == 4
         for page in tif.pages:
             svs_description_metadata(page.description)
         # first page
@@ -4670,7 +5066,7 @@ def test_read_svs_jp2k_33003_1():
         assert tif.is_svs
         assert not tif.is_scanimage
         assert len(tif.pages) == 6
-        assert len(tif.series) == 6
+        assert len(tif.series) == 4
         for page in tif.pages:
             svs_description_metadata(page.description)
         # first page
@@ -5420,7 +5816,7 @@ def test_read_ome_rgb():
         series = tif.series[0]
         assert series.shape == (3, 720, 1280)
         assert series.dtype.name == 'uint8'
-        assert series.axes == 'CYX'
+        assert series.axes == 'SYX'
         assert series.offset == 17524
         # assert data
         data = tif.asarray()
@@ -5452,7 +5848,7 @@ def test_read_ome_samplesperpixel():
         series = tif.series[0]
         assert series.shape == (6, 3, 1024, 1024)
         assert series.dtype.name == 'uint8'
-        assert series.axes == 'ZCYX'
+        assert series.axes == 'ZSYX'
         # assert data
         data = tif.asarray()
         assert data.shape == (6, 3, 1024, 1024)
@@ -6613,7 +7009,7 @@ def test_read_geotiff_spaf27_markedcorrect():
 
 @pytest.mark.skipif(SKIP_PRIVATE or SKIP_CODECS, reason=REASON)
 def test_read_qpi():
-    """Test read PerkinElmer-QPI."""
+    """Test read PerkinElmer-QPI, non Pyramid."""
     fname = private_file(
         'PerkinElmer-QPI/LuCa-7color_[13860,52919]_1x1component_data.tiff')
     with TiffFile(fname) as tif:
@@ -6629,6 +7025,14 @@ def test_read_qpi():
         assert page.bitspersample == 32
         assert page.samplesperpixel == 1
         assert page.tags['Software'].value == 'PerkinElmer-QPI'
+        series = tif.series[0]
+        assert series.shape == (8, 1400, 1868)
+        assert series.dtype == 'float32'
+        assert not series.is_pyramid
+        series = tif.series[1]
+        assert series.shape == (350, 467, 3)
+        assert series.dtype == 'uint8'
+        assert not series.is_pyramid
         # assert data
         image = tif.asarray()
         assert image.shape == (8, 1400, 1868)
@@ -6648,7 +7052,7 @@ def test_read_zif():
     with TiffFile(fname) as tif:
         # assert tif.is_zif
         assert len(tif.pages) == 5
-        assert len(tif.series) == 5
+        assert len(tif.series) == 1
         for page in tif.pages:
             assert page.description == ('Created by Objective '
                                         'Pathology Services')
@@ -6664,6 +7068,14 @@ def test_read_zif():
         assert page.compression == JPEG
         assert page.shape == (195, 130, 3)
         assert tuple(page.asarray()[191, 127, :]) == (30, 49, 66)
+        # series
+        series = tif.series[0]
+        assert series.is_pyramid
+        assert len(series.levels) == 5
+        assert series.shape == (3120, 2080, 3)
+        assert tuple(series.asarray()[3110, 2070, :]) == (27, 45, 59)
+        assert series.levels[-1].shape == (195, 130, 3)
+        assert tuple(series.asarray(level=-1)[191, 127, :]) == (30, 49, 66)
         assert__str__(tif)
 
 
@@ -7265,18 +7677,33 @@ def test_write_is_shaped():
             assert__str__(tif)
 
 
+def test_write_bytes_str():
+    """Test write bytes in place of 7-bit ascii string."""
+    micron = 'micron \xB5'.encode('latin-1')  # can't be encoded as 7-bit ascii
+    data = numpy.arange(4, dtype='uint32').reshape((2, 2))
+    with TempFileName('write_bytes_str') as fname:
+        imwrite(fname, data, description=micron, software=micron,
+                extratags=[(50001, 's', 8, micron, True)])
+        with TiffFile(fname) as tif:
+            page = tif.pages[0]
+            assert page.description == 'micron \xB5'
+            assert page.software == 'micron \xB5'
+            assert page.tags[50001].value == 'micron \xB5'
+
+
 def test_write_extratags():
     """Test write extratags."""
     data = random_data('uint8', (2, 219, 301))
-    description = "Created by TestTiffWriter\nLorem ipsum dolor..."
-    pagename = "Page name"
-    extratags = [(270, 's', 0, description, True),
-                 ('PageName', 's', 0, pagename, False),
-                 (50001, 'b', 1, b'1', True),
-                 (50002, 'b', 2, b'12', True),
-                 (50004, 'b', 4, b'1234', True),
-                 (50008, 'B', 8, b'12345678', True),
-                 ]
+    description = 'Created by TestTiffWriter\nLorem ipsum dolor...'
+    pagename = 'Page name'
+    extratags = [
+        (270, 's', 0, description, True),
+        ('PageName', 's', 0, pagename, False),
+        (50001, 'b', 1, b'1', True),
+        (50002, 'b', 2, b'12', True),
+        (50004, 'b', 4, b'1234', True),
+        (50008, 'B', 8, b'12345678', True),
+    ]
     with TempFileName('extratags') as fname:
         imwrite(fname, data, extratags=extratags)
         assert_valid(fname)
@@ -7490,6 +7917,49 @@ def test_write_resolution_unit():
             assert__str__(tif)
 
 
+ at pytest.mark.skipif(SKIP_CODECS, reason=REASON)
+ at pytest.mark.parametrize('bps', [1, 2, 7, 8])
+ at pytest.mark.parametrize('dtype', ['u1', 'u2', 'u4'])
+def test_write_bitspersample(bps, dtype):
+    """Test write with packints."""
+    dtype = numpy.dtype(dtype)
+    bps += (dtype.itemsize // 2) * 8
+    data = numpy.arange(256*256*3, dtype=dtype).reshape((256, 256, 3))
+    with TempFileName(f'write_bitspersample_{dtype.char}{bps}') as fname:
+        # TODO: enable all cases once imagecodecs.packints_encode works
+        if bps == dtype.itemsize * 8:
+            imwrite(fname, data, bitspersample=bps)
+            assert_array_equal(imread(fname), data)
+        else:
+            with pytest.raises(NotImplementedError):
+                imwrite(fname, data, bitspersample=bps)
+                assert_array_equal(imread(fname), data)
+
+
+def test_write_bitspersample_fail():
+    """Test write with packints fails."""
+    data = numpy.arange(32*32*3, dtype='uint32').reshape((32, 32, 3))
+    with TempFileName('write_bitspersample_fail') as fname:
+        with TiffWriter(fname) as tif:
+            # not working with compression
+            with pytest.raises(ValueError):
+                tif.save(data.astype('uint8'), bitspersample=4, compress=6)
+            # dtype.itemsize != bitspersample
+            for dtype in ('int8', 'int16', 'float32', 'uint64'):
+                with pytest.raises(ValueError):
+                    tif.save(data.astype(dtype), bitspersample=4)
+            # bitspersample out of data range
+            for bps in (0, 9, 16, 32):
+                with pytest.raises(ValueError):
+                    tif.save(data.astype('uint8'), bitspersample=bps)
+            for bps in (1, 8, 17, 32):
+                with pytest.raises(ValueError):
+                    tif.save(data.astype('uint16'), bitspersample=bps)
+            for bps in (1, 8, 16, 33, 64):
+                with pytest.raises(ValueError):
+                    tif.save(data.astype('uint32'), bitspersample=bps)
+
+
 def test_write_compress_none():
     """Test write compress=0."""
     data = WRITE_DATA
@@ -7761,7 +8231,7 @@ def test_write_compress_predictor():
     """Test write horizontal differencing."""
     data = WRITE_DATA
     with TempFileName('compress_predictor') as fname:
-        imwrite(fname, data, compress=6, predictor=True)
+        imwrite(fname, data, compress=6, predictor=TIFF.PREDICTOR.HORIZONTAL)
         assert_valid(fname)
         with TiffFile(fname) as tif:
             assert len(tif.pages) == 1
@@ -8557,6 +9027,37 @@ def test_write_extrasamples_planar_rgb():
             assert__str__(tif)
 
 
+def test_write_cfa():
+    """Test write uncompressed CFA image."""
+    # TODO: write a valid TIFF/EP file
+    data = imread(
+        private_file('DNG/cinemadng/M14-1451_000085_cDNG_uncompressed.dng')
+    )
+    extratags = [
+        (271, 's', 4, 'Make', False),
+        (272, 's', 5, 'Model', False),
+        (33421, 'H', 2, (2, 2), False),  # CFARepeatPatternDim
+        (33422, 'B', 4, b'\x00\x01\x01\x02', False),  # CFAPattern
+        # (37398, 'B', 4, b'\x01\x00\x00\x00', False),  # TIFF/EPStandardID
+        # (37399, 'H', 1, 0)  # SensingMethod Undefined
+        # (50706, 'B', 4, b'\x01\x04\x00\x00', False),  # DNGVersion
+    ]
+    with TempFileName('write_cfa') as fname:
+        imwrite(fname, data, photometric='CFA', software='Tifffile',
+                datetime=True, extratags=extratags)
+        with TiffFile(fname) as tif:
+            assert len(tif.pages) == 1
+            page = tif.pages[0]
+            assert page.compression == 1
+            assert page.photometric == CFA
+            assert page.imagewidth == 960
+            assert page.imagelength == 540
+            assert page.bitspersample == 16
+            assert page.tags['CFARepeatPatternDim'].value == (2, 2)
+            assert page.tags['CFAPattern'].value == b'\x00\x01\x01\x02'
+            assert_array_equal(page.asarray(), data)
+
+
 def test_write_tiled_compressed():
     """Test write compressed tiles."""
     data = random_data('uint8', (3, 219, 301))
@@ -8736,6 +9237,68 @@ def test_write_tileiter_separate():
                 assert_array_equal(numpy.squeeze(segment[0]), data[i])
 
 
+def test_write_pyramids():
+    """Test write two pyramids to shaped file."""
+    data = random_data('uint8', (31, 64, 96, 3))
+    with TempFileName('pyramids') as fname:
+        with TiffWriter(fname) as tif:
+            # use pages
+            tif.save(data, tile=(16, 16))
+            # interrupt pyramid, e.g. thumbnail
+            tif.save(data[0, :, :, 0])
+            # pyramid levels
+            tif.save(data[:, ::2, ::2], tile=(16, 16), subfiletype=1)
+            tif.save(data[:, ::4, ::4], tile=(16, 16), subfiletype=1)
+            # second pyramid using sgi with downsampling factor 3
+            tif.save(data, tile=(16, 16, 16))
+            tif.save(data[::3, ::3, ::3], tile=(16, 16, 16), subfiletype=1)
+
+        assert_valid(fname)
+
+        with TiffFile(fname) as tif:
+            assert len(tif.pages) == 3 * 31 + 2 + 1
+            assert len(tif.series) == 3
+
+            series = tif.series[0]
+            assert series.is_pyramid
+            assert len(series.levels) == 3
+            assert len(series.levels[0].pages) == 31
+            assert len(series.levels[1].pages) == 31
+            assert len(series.levels[2].pages) == 31
+            assert series.levels[0].shape == (31, 64, 96, 3)
+            assert series.levels[1].shape == (31, 32, 48, 3)
+            assert series.levels[2].shape == (31, 16, 24, 3)
+
+            series = tif.series[1]
+            assert not series.is_pyramid
+            assert series.shape == (64, 96)
+
+            series = tif.series[2]
+            assert series.is_pyramid
+            assert len(series.levels) == 2
+            assert len(series.levels[0].pages) == 1
+            assert len(series.levels[1].pages) == 1
+            assert series.levels[0].keyframe.is_sgi
+            assert series.levels[1].keyframe.is_sgi
+            assert series.levels[0].shape == (31, 64, 96, 3)
+            assert series.levels[1].shape == (11, 22, 32, 3)
+
+            assert_array_equal(data, tif.asarray())
+            assert_array_equal(data, tif.asarray(series=0, level=0))
+            assert_array_equal(data[:, ::2, ::2],
+                               tif.asarray(series=0, level=1))
+            assert_array_equal(data[:, ::4, ::4],
+                               tif.asarray(series=0, level=2))
+
+            assert_array_equal(data[0, :, :, 0], tif.asarray(series=1))
+
+            assert_array_equal(data, tif.asarray(series=2))
+            assert_array_equal(data[::3, ::3, ::3],
+                               tif.asarray(series=2, level=1))
+
+            assert__str__(tif)
+
+
 def test_write_volume():
     """Test write tiled volume."""
     data = random_data('uint8', (253, 64, 96))
@@ -8762,6 +9325,33 @@ def test_write_volume():
             assert__str__(tif)
 
 
+def test_write_volume_png():
+    """Test write tiled volume using an image compressor."""
+    data = random_data('uint8', (16, 64, 96, 3))
+    with TempFileName('volume_png') as fname:
+        imwrite(fname, data, tile=(1, 64, 64), compress='png')
+        assert_valid(fname)
+        with TiffFile(fname) as tif:
+            assert len(tif.pages) == 1
+            page = tif.pages[0]
+            assert page.is_sgi
+            assert page.is_tiled
+            assert page.compression == TIFF.COMPRESSION.PNG
+            assert not page.is_contiguous
+            assert page.planarconfig == CONTIG
+            assert page.photometric == RGB
+            assert page.imagewidth == 96
+            assert page.imagelength == 64
+            assert page.imagedepth == 16
+            assert page.tilewidth == 64
+            assert page.tilelength == 64
+            assert page.tiledepth == 1
+            assert page.samplesperpixel == 3
+            image = tif.asarray()
+            assert_array_equal(data, image)
+            assert__str__(tif)
+
+
 def test_write_volume_5d_planar_rgb():
     """Test write 5D array as grayscale volumes."""
     shape = (2, 3, 256, 64, 96)
@@ -9269,52 +9859,28 @@ def test_write_imagej_roundtrip():
     assert_valid(fname)
 
 
-def test_write_imagej_hyperstack():
-    """Test write truncated ImageJ hyperstack."""
+ at pytest.mark.parametrize('mmap', [False, True])
+ at pytest.mark.parametrize('truncate', [False, True])
+def test_write_imagej_hyperstack(truncate, mmap):
+    """Test write ImageJ hyperstack."""
     shape = (5, 6, 7, 49, 61, 3)
     data = numpy.empty(shape, dtype='uint8')
     data[:] = numpy.arange(210, dtype='uint8').reshape(5, 6, 7, 1, 1, 1)
 
-    with TempFileName('imagej_hyperstack') as fname:
-        imwrite(fname, data, imagej=True, truncate=True)
-        # assert file
-        with TiffFile(fname) as tif:
-            assert not tif.is_bigtiff
-            assert len(tif.pages) == 1
-            page = tif.pages[0]
-            assert page.is_contiguous
-            assert page.planarconfig == CONTIG
-            assert page.photometric == RGB
-            assert page.imagewidth == 61
-            assert page.imagelength == 49
-            assert page.samplesperpixel == 3
-            # assert series properties
-            series = tif.series[0]
-            assert series.shape == shape
-            assert len(series._pages) == 1
-            assert len(series.pages) == 1
-            assert series.dtype.name == 'uint8'
-            assert series.axes == 'TZCYXS'
-            # assert data
-            image = tif.asarray(out='memmap')
-            assert_array_equal(data.squeeze(), image.squeeze())
+    _truncate = ['', '_trunc'][truncate]
+    _memmap = ['', '_memmap'][mmap]
+    with TempFileName(f'imagej_hyperstack{_truncate}{_memmap}') as fname:
+        if mmap:
+            image = memmap(fname, shape=data.shape, dtype=data.dtype,
+                           imagej=True, truncate=truncate)
+            image[:] = data
             del image
-            assert__str__(tif)
-        assert_valid(fname)
-
-
-def test_write_imagej_hyperstack_nontrunc():
-    """Test write non-truncated ImageJ hyperstack."""
-    shape = (5, 6, 7, 49, 61, 3)
-    data = numpy.empty(shape, dtype='uint8')
-    data[:] = numpy.arange(210, dtype='uint8').reshape(5, 6, 7, 1, 1, 1)
-
-    with TempFileName('imagej_hyperstack_nontrunc') as fname:
-        imwrite(fname, data, imagej=True)
+        else:
+            imwrite(fname, data, truncate=truncate, imagej=True)
         # assert file
         with TiffFile(fname) as tif:
             assert not tif.is_bigtiff
-            assert len(tif.pages) == 210
+            assert len(tif.pages) == 1 if truncate else 210
             page = tif.pages[0]
             assert page.is_contiguous
             assert page.planarconfig == CONTIG
@@ -9326,7 +9892,7 @@ def test_write_imagej_hyperstack_nontrunc():
             series = tif.series[0]
             assert series.shape == shape
             assert len(series._pages) == 1
-            assert len(series.pages) == 210
+            assert len(series.pages) == 1 if truncate else 210
             assert series.dtype.name == 'uint8'
             assert series.axes == 'TZCYXS'
             # assert data
@@ -9338,6 +9904,7 @@ def test_write_imagej_hyperstack_nontrunc():
             for i, page in enumerate(series.pages):
                 image = page.asarray()
                 assert_array_equal(data[i], image)
+            del image
             assert__str__(tif)
         assert_valid(fname)
 
@@ -9413,6 +9980,103 @@ def test_write_imagej_raw():
             assert__str__(tif)
 
 
+ at pytest.mark.skipif(SKIP_EXTENDED, reason=REASON)
+ at pytest.mark.parametrize(
+    'shape, axes',
+    [
+        ((219, 301, 1), None),
+        ((219, 301, 2), None),
+        ((219, 301, 3), None),
+        ((219, 301, 4), None),
+        ((219, 301, 5), None),
+        ((1, 219, 301), None),
+        ((2, 219, 301), None),
+        ((3, 219, 301), None),
+        ((4, 219, 301), None),
+        ((5, 219, 301), None),
+        ((4, 3, 219, 301), None),
+        ((4, 219, 301, 3), None),
+        ((3, 4, 219, 301), None),
+        ((1, 3, 1, 219, 301), None),
+        ((3, 1, 1, 219, 301), None),
+        ((1, 3, 4, 219, 301), None),
+        ((3, 1, 4, 219, 301), None),
+        ((3, 4, 1, 219, 301), None),
+        ((3, 4, 1, 219, 301, 3), None),
+        ((2, 3, 4, 219, 301), None),
+        ((4, 3, 2, 219, 301, 3), None),
+        ((5, 1, 32, 32), 'CSYX'),
+        ((5, 1, 32, 32), 'ZCYX'),
+        ((2, 3, 4, 219, 301, 3), 'TCZYXS'),
+        ((10, 5, 200, 200), 'EPYX'),
+        ((2, 3, 4, 5, 6, 7, 32, 32, 3), 'TQCPZRYXS'),
+    ]
+)
+def test_write_ome(shape, axes):
+    """Test write OME-TIFF format."""
+    metadata = {'axes': axes} if axes is not None else {}
+    data = random_data('uint8', shape)
+    fname = 'write_ome_{}.ome.tif'.format(str(shape).replace(' ', ''))
+    with TempFileName(fname) as fname:
+        imwrite(fname, data, metadata=metadata)
+        with TiffFile(fname) as tif:
+            image = tif.asarray()
+            omexml = tif.ome_metadata
+            if axes:
+                assert tif.series[0].axes == squeeze_axes(shape, axes)[-1]
+        assert_array_equal(data.squeeze(), image.squeeze())
+        assert_valid_omexml(omexml)
+        assert_valid(fname)
+
+
+ at pytest.mark.skipif(SKIP_PUBLIC, reason=REASON)
+def test_write_ome_copy():
+    """Test re-write OME time-series of volumes."""
+    # 4D (7 time points, 5 focal planes)
+    fname = public_file('OME/bioformats-artificial/4D-series.ome.tiff')
+    with TiffFile(fname) as tif:
+        omexml = tif.ome_metadata
+        data = tif.asarray()
+
+    with TempFileName('ome_copy') as fname:
+        # process OME-XML
+        omexml = omexml.replace('4D-series.ome.tiff', os.path.split(fname)[-1])
+        # omexml = omexml.replace('BigEndian="true"', 'BigEndian="false"')
+        data = data.newbyteorder('>')
+        # save image planes in the order referenced in the OME-XML
+        # make sure storage options (compression, byteorder, photometric mode)
+        #   match OME-XML
+        # write OME-XML to first page only
+        with TiffWriter(fname, bigtiff=True, byteorder='>') as tif:
+            for i, image in enumerate(data.reshape(-1, *data.shape[-2:])):
+                description = omexml if i == 0 else None
+                tif.save(image, description=description,
+                         photometric='minisblack', metadata=None,
+                         contiguous=False)
+
+        with TiffFile(fname) as tif:
+            assert tif.is_ome
+            assert tif.byteorder == '>'
+            assert len(tif.pages) == 35
+            assert len(tif.series) == 1
+            # assert page properties
+            page = tif.pages[0]
+            assert page.is_contiguous
+            assert page.compression == NONE
+            assert page.imagewidth == 439
+            assert page.imagelength == 167
+            assert page.bitspersample == 8
+            assert page.samplesperpixel == 1
+            # assert series properties
+            series = tif.series[0]
+            assert series.shape == (7, 5, 167, 439)
+            assert series.dtype.name == 'int8'
+            assert series.axes == 'TZYX'
+            # assert data
+            assert_array_equal(data, tif.asarray())
+            assert__str__(tif)
+
+
 ###############################################################################
 
 # Test embedded TIFF files
@@ -9509,7 +10173,7 @@ def assert_embed_micromanager(tif):
     assert len(tif.series) == 1
     # assert non-tiff micromanager_metadata
     tags = tif.micromanager_metadata['Summary']
-    assert tags["MicroManagerVersion"] == "1.4.x dev"
+    assert tags["MicroManagerVersion"] == '1.4.x dev'
     # assert page properties
     page = tif.pages[0]
     assert page.is_contiguous
@@ -9527,8 +10191,8 @@ def assert_embed_micromanager(tif):
     assert tags['slices'] == 3
     assert tags['Ranges'] == (706.0, 5846.0)
     tags = tif.micromanager_metadata
-    assert tags["FileName"] == "Untitled_1_MMStack.ome.tif"
-    assert tags["PixelType"] == "GRAY16"
+    assert tags["FileName"] == 'Untitled_1_MMStack.ome.tif'
+    assert tags["PixelType"] == 'GRAY16'
     # assert series properties
     series = tif.series[0]
     assert series.shape == (5, 3, 512, 512)
@@ -9773,14 +10437,15 @@ def test_depend_lfdfiles():
 
     filename = private_file('SimFCS/simfcs.Z64')
     with TempFileName('simfcsz_z64', ext='.tif') as outfile:
-        z64 = SimfcsZ64(filename)
-        z64.totiff(outfile)
+        with SimfcsZ64(filename) as z64:
+            data = z64.asarray()
+            z64.totiff(outfile)
         with TiffFile(outfile) as tif:
             assert len(tif.pages) == 256
             assert len(tif.series) == 1
             assert tif.series[0].shape == (256, 256, 256)
             assert tif.series[0].dtype == 'float32'
-            assert_array_equal(z64.asarray(), tif.asarray())
+            assert_array_equal(data, tif.asarray())
 
 
 @pytest.mark.skipif(SKIP_PRIVATE, reason=REASON)


=====================================
tifffile.egg-info/PKG-INFO
=====================================
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tifffile
-Version: 2020.6.3
+Version: 2020.7.17
 Summary: Read and write TIFF(r) files
 Home-page: https://www.lfd.uci.edu/~gohlke/
 Author: Christoph Gohlke
@@ -18,10 +18,11 @@ Description: Read and write TIFF(r) files
         
         Image and metadata can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, SGI,
         NIHImage, ImageJ, MicroManager, FluoView, ScanImage, SEQ, GEL, SVS, SCN, SIS,
-        ZIF, QPTIFF, NDPI, and GeoTIFF files.
+        ZIF (Zoomable Image File Format), QPTIFF (QPI), NDPI, and GeoTIFF files.
         
-        Numpy arrays can be written to TIFF, BigTIFF, and ImageJ hyperstack compatible
-        files in multi-page, memory-mappable, tiled, predicted, or compressed form.
+        Numpy arrays can be written to TIFF, BigTIFF, OME-TIFF, and ImageJ hyperstack
+        compatible files in multi-page, memory-mappable, tiled, predicted, or
+        compressed form.
         
         A subset of the TIFF specification is supported, mainly uncompressed and
         losslessly compressed 8, 16, 32 and 64-bit integer, 16, 32 and 64-bit float,
@@ -36,7 +37,7 @@ Description: Read and write TIFF(r) files
         extensions defined by Molecular Devices (Universal Imaging Corporation),
         Carl Zeiss MicroImaging, Olympus, Silicon Graphics International,
         Media Cybernetics, Molecular Dynamics, PerkinElmer, Hamamatsu, and the
-        Open Microscopy Environment consortium respectively.
+        Open Microscopy Environment consortium, respectively.
         
         For command line usage run ``python -m tifffile --help``
         
@@ -48,23 +49,44 @@ Description: Read and write TIFF(r) files
         
         :License: BSD 3-Clause
         
-        :Version: 2020.6.3
+        :Version: 2020.7.17
         
         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.3 64-bit <https://www.python.org>`_
-        * `Numpy 1.16.6, 1.18.4 <https://www.numpy.org>`_
+        * `CPython 3.7.8, 3.8.4, 3.9.0b4 64-bit <https://www.python.org>`_
+        * `Numpy 1.18.5 <https://pypi.org/project/numpy/>`_
         * `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)
+        * `Matplotlib 3.2.2 <https://pypi.org/project/matplotlib/>`_
+          (required only for plotting)
+        * `Lxml 4.5.2 <https://github.com/lxml/lxml>`_
+          (required only for validating and printing XML)
         
         Revisions
         ---------
+        2020.7.17
+            Pass 3022 tests.
+            Initial support for writing OME-TIFF (WIP).
+            Return samples as separate dimension in OME series (breaking).
+            Fix modulo dimensions for multiple OME series.
+            Fix some test errors on big endian systems (#18).
+            Fix BytesWarning.
+            Allow to pass TIFF.PREDICTOR values to TiffWriter.save.
+        2020.7.4
+            Deprecate support for Python 3.6 (NEP 29).
+            Move pyramidal subresolution series to TiffPageSeries.levels (breaking).
+            Add parser for SVS, SCN, NDPI, and QPI pyramidal series.
+            Read single-file OME-TIFF pyramids.
+            Read NDPI files > 4 GB (#15).
+            Include SubIFDs in generic series.
+            Preliminary support for writing packed integer arrays (#11, WIP).
+            Read more LSM info subrecords.
+            Fix missing ReferenceBlackWhite tag for YCbCr photometrics.
+            Fix reading lossless JPEG compressed DNG files.
         2020.6.3
-            Pass 2908 tests.
             Support os.PathLike file names (#9).
         2020.5.30
             Re-add pure Python PackBits decoder.
@@ -218,10 +240,11 @@ Description: Read and write TIFF(r) files
         
         Tested on little-endian platforms only.
         
-        Python 32-bit versions are deprecated.
+        Python 32-bit versions are deprecated. Python <= 3.6 are no longer supported.
         
         Tifffile relies on the `imagecodecs <https://pypi.org/project/imagecodecs/>`_
-        package for encoding and decoding LZW, JPEG, and other compressed images.
+        package for encoding and decoding LZW, JPEG, and other compressed image
+        segments.
         
         Several TIFF-like formats do not strictly adhere to the TIFF6 specification,
         some of which allow file or data sizes to exceed the 4 GB limit:
@@ -238,21 +261,23 @@ Description: Read and write TIFF(r) files
           files. The 8-bit UTF-8 encoded OME-XML metadata found in the ImageDescription
           tag of the first IFD defines the position of TIFF IFDs in the high
           dimensional data. Tifffile can read OME-TIFF files, except when the OME-XML
-          metadata are stored in a separate file.
+          metadata are stored in a separate file. Tifffile can write numpy arrays
+          to single-file, non-pyramidal OME-TIFF.
         * *LSM* stores all IFDs below 4 GB but wraps around 32-bit StripOffsets.
           The StripOffsets of each series and position require separate unwrapping.
           The StripByteCounts tag contains the number of bytes for the uncompressed
           data. Tifffile can read large LSM files.
         * *NDPI* uses some 64-bit offsets in the file header, IFD, and tag structures.
           Tag values/offsets can be corrected using high bits stored after IFD
-          structures. JPEG compressed tiles with dimensions > 65536 are not readable
-          with libjpeg. Tifffile can read NDPI files < 4 GB and decompress large JPEG
-          tiles using the imagecodecs library on Windows.
+          structures. JPEG compressed segments with dimensions >65536 or missing
+          restart markers are not readable with libjpeg. Tifffile can read NDPI
+          files > 4 GB. JPEG segments with restart markers and dimensions >65536 can
+          be decoded with the imagecodecs library on Windows.
         * *ScanImage* optionally allows corrupt non-BigTIFF files > 2 GB. The values
           of StripOffsets and StripByteCounts can be recovered using the constant
           differences of the offsets of IFD and tag values throughout the file.
-          Tifffile can read such files on Python 3 if the image data are stored
-          contiguously in each page.
+          Tifffile can read such files if the image data are stored contiguously in
+          each page.
         * *GeoTIFF* sparse files allow strip or tile offsets and byte counts to be 0.
           Such segments are implicitly set to 0 or the NODATA value on reading.
           Tifffile can read GeoTIFF sparse files.
@@ -285,29 +310,33 @@ Description: Read and write TIFF(r) files
         
         References
         ----------
-        1.  TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
-            https://www.adobe.io/open/standards/TIFF.html
-        2.  TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
-        3.  MetaMorph Stack (STK) Image File Format.
-            http://mdc.custhelp.com/app/answers/detail/a_id/18862
-        4.  Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
-            Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
-        5.  The OME-TIFF format.
-            https://docs.openmicroscopy.org/ome-model/5.6.4/ome-tiff/
-        6.  UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
-            http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
-        7.  Micro-Manager File Formats.
-            https://micro-manager.org/wiki/Micro-Manager_File_Formats
-        8.  Tags for TIFF and Related Specifications. Digital Preservation.
-            https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
-        9.  ScanImage BigTiff Specification - ScanImage 2016.
-            http://scanimage.vidriotechnologies.com/display/SI2016/
-            ScanImage+BigTiff+Specification
-        10. CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
-            Exif Version 2.31.
-            http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
-        11. ZIF, the Zoomable Image File format. http://zif.photo/
-        12. GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+        * TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
+          https://www.adobe.io/open/standards/TIFF.html
+        * TIFF File Format FAQ. https://www.awaresystems.be/imaging/tiff/faq.html
+        * The BigTIFF File Format.
+          https://www.awaresystems.be/imaging/tiff/bigtiff.html
+        * MetaMorph Stack (STK) Image File Format.
+          http://mdc.custhelp.com/app/answers/detail/a_id/18862
+        * Image File Format Description LSM 5/7 Release 6.0 (ZEN 2010).
+          Carl Zeiss MicroImaging GmbH. BioSciences. May 10, 2011
+        * The OME-TIFF format.
+          https://docs.openmicroscopy.org/ome-model/latest/
+        * UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
+          http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
+        * Micro-Manager File Formats.
+          https://micro-manager.org/wiki/Micro-Manager_File_Formats
+        * ScanImage BigTiff Specification - ScanImage 2016.
+          http://scanimage.vidriotechnologies.com/display/SI2016/
+          ScanImage+BigTiff+Specification
+        * ZIF, the Zoomable Image File format. http://zif.photo/
+        * GeoTIFF File Format https://gdal.org/drivers/raster/gtiff.html
+        * Cloud optimized GeoTIFF.
+          https://github.com/cogeotiff/cog-spec/blob/master/spec.md
+        * Tags for TIFF and Related Specifications. Digital Preservation.
+          https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
+        * CIPA DC-008-2016: Exchangeable image file format for digital still cameras:
+          Exif Version 2.31.
+          http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
         
         Examples
         --------
@@ -440,19 +469,21 @@ Description: Read and write TIFF(r) files
         ...             tag_name, tag_value = tag.name, tag.value
         ...         image = page.asarray()
         
-        Save two image series to a TIFF file:
+        Write two numpy arrays to a multi-series OME-TIFF file:
         
-        >>> data0 = numpy.random.randint(0, 255, (301, 219, 3), 'uint8')
-        >>> data1 = numpy.random.randint(0, 255, (5, 301, 219), 'uint16')
-        >>> with TiffWriter('temp.tif') as tif:
+        >>> data0 = numpy.random.randint(0, 255, (32, 32, 3), 'uint8')
+        >>> data1 = numpy.random.randint(0, 1023, (5, 256, 256), 'uint16')
+        >>> with TiffWriter('temp.ome.tif', ome=True) as tif:
         ...     tif.save(data0, compress=6, photometric='rgb')
-        ...     tif.save(data1, compress=6, photometric='minisblack', contiguous=False)
+        ...     tif.save(data1, photometric='minisblack', contiguous=False,
+        ...              metadata=dict(axes='ZYX', SignificantBits=10,
+        ...                            PositionZ=[0.0, 1.0, 2.0, 3.0, 4.0]))
         
-        Read the second image series from the TIFF file:
+        Read the second image series from the OME-TIFF file:
         
-        >>> series1 = imread('temp.tif', series=1)
+        >>> series1 = imread('temp.ome.tif', series=1)
         >>> series1.shape
-        (5, 301, 219)
+        (5, 256, 256)
         
         Read an image stack from a series of TIFF files with a file name pattern:
         
@@ -467,13 +498,46 @@ Description: Read and write TIFF(r) files
         >>> data.shape
         (1, 2, 64, 64)
         
-        Create a TIFF file from an iterator of tiles:
+        Create a TIFF file from a generator of tiles:
         
         >>> def tiles():
         ...     data = numpy.arange(3*4*16*16, dtype='uint16').reshape((3*4, 16, 16))
         ...     for i in range(data.shape[0]): yield data[i]
         >>> imwrite('temp.tif', tiles(), dtype='uint16', shape=(48, 64), tile=(16, 16))
         
+        Write a tiled, multi-resolution, pyramidal TIFF file using JPEG compression:
+        
+        >>> data = numpy.arange(1024*1024*3, dtype='uint8').reshape((1024, 1024, 3))
+        >>> with TiffWriter('temp.tif') as tif:
+        ...     options = dict(tile=(256, 256), compress='jpeg', metadata=None)
+        ...     tif.save(data, **options)
+        ...     # save pyramid levels. In production use resampling to generate levels!
+        ...     tif.save(data[::2, ::2], subfiletype=1, **options)
+        ...     tif.save(data[::4, ::4], subfiletype=1, **options)
+        
+        Access the image levels in the pyramidal TIFF file:
+        
+        >>> baseimage = imread('temp.tif')
+        >>> second_level = imread('temp.tif', series=0, level=1)
+        >>> with TiffFile('temp.tif') as tif:
+        ...     baseimage = tif.series[0].asarray()
+        ...     second_level = tif.series[0].levels[1].asarray()
+        
+        Iterate over and decode single JPEG compressed tiles in the TIFF file:
+        
+        >>> with TiffFile('temp.tif') as tif:
+        ...     fh = tif.filehandle
+        ...     for page in tif.pages:
+        ...         jpegtables = page.tags.get('JPEGTables', None)
+        ...         if jpegtables is not None:
+        ...             jpegtables = jpegtables.value
+        ...         for index, (offset, bytecount) in enumerate(
+        ...             zip(page.dataoffsets, page.databytecounts)
+        ...         ):
+        ...             fh.seek(offset)
+        ...             data = fh.read(bytecount)
+        ...             tile, indices, shape = page.decode(data, index, jpegtables)
+        
 Platform: any
 Classifier: Development Status :: 4 - Beta
 Classifier: License :: OSI Approved :: BSD License
@@ -481,7 +545,6 @@ Classifier: Intended Audience :: Science/Research
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 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


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


=====================================
tifffile/tifffile.py
=====================================
The diff for this file was not included because it is too large.


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

-- 
View it on GitLab: https://salsa.debian.org/python-team/modules/tifffile/-/commit/2a5bc8b5fe528ce2d93f5d400258cbe8f2088e3a
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/20200718/06697d09/attachment-0001.html>


More information about the debian-med-commit mailing list