xtiff icon indicating copy to clipboard operation
xtiff copied to clipboard

Compression error/ how to define the "compression_type" argument?

Open Phlpp-S opened this issue 1 year ago • 7 comments

Hello,

I like this package as it's easy to install and use, however I have a question regarding the compression setting, specifically the compression type.

compression_type=None

What to pass here instead of the None to activate a compression? I saw in the code that it counterchecks with the tifffile.TIFF.COMPRESSION(), but was not able to figure out what to pass here.

Help would be appreciated

Phlpp-S avatar Jan 20 '24 12:01 Phlpp-S

@Phlpp-S Thank you for bringing up this issue. The options for compression_type as specified by tifffile.TIFF.COMPRESSION() should be:

NONE, CCITTRLE, CCITT_T4, CCITT_T6, LZW, OJPEG, JPEG, ADOBE_DEFLATE , JBIG_BW , JBIG_COLOR , JPEG_99 , KODAK_262 , JPEGXR_NDPI , NEXT , SONY_ARW , PACKED_RAW , SAMSUNG_SRW , CCIRLEW , SAMSUNG_SRW2 , PACKBITS , THUNDERSCAN , IT8CTPAD , IT8LW , IT8MP , IT8BL , PIXARFILM , PIXARLOG , DEFLATE , DCS , APERIO_JP2000_YCBC , JPEG_2000_LOSSY , APERIO_JP2000_RGB , ALT_JPEG , JBIG , SGILOG , SGILOG24 , JPEG2000 , NIKON_NEF , JBIG2 , MDI_BINARY , MDI_PROGRESSIVE , MDI_VECTOR , LERC , JPEG_LOSSY , LZMA , ZSTD_DEPRECATED , WEBP_DEPRECATED , PNG , JPEGXR , JETRAW , ZSTD , WEBP , JPEGXL , PIXTIFF , EER_V0 , EER_V1 , EER_V2

However xtiff.to_tiff(<a numpy ndarray>,<path/to/tiff/file>, compression_type = 'JPEG') returns the following error for me:

TypeError: __call__() missing 1 required positional argument: 'value'

This is caused by the line 191 and compression_type not in tifffile.TIFF.COMPRESSION(). It appears that tifffile.TIFF.COMPRESSION() is not what is expected in the current code. substituting the check with something like: hasattr(tifffile.TIFF.COMPRESSION, 'compression_type') seems to fix the issue.

@jwindhager what do you think? @Phlpp-S Could you please confirm that you get the above error if you specify: compression_type = 'JPEG'?

Milad4849 avatar Jan 23 '24 17:01 Milad4849

Hi @Milad4849

Without looking into this in too much detail, I would suspect that the TiffWriter.write API may have changed since the last release of xtiff: instead of a single compression argument, there now seems to be compression and compressionargs: https://github.com/cgohlke/tifffile/blob/5ced58caa74936ecc53f58ade94938e251e83138/tifffile/tifffile.py#L1637-L1638

Searching for compressionargs in the tifffile changelog, this is indeed the case (version 2022.7.28):

Deprecate tuple type compression argument on write (use compressionargs).

As you can see in xtiff's setup.cfg, the last tested version for xtiff was 2022.4.22. I therefore see two options:

  1. Pin the max tifffile version to the last supported version (probably anything between 2022.4.22 and 2022.7.28, you'd need to check the changelog) and deprecate xtiff (add a deprecation warning to both the Python package and the GitHub repository)
  2. Work through the changes in the tifffile changelog and update xtiff accordingly (not just compression-related issues)

Option 1 is probably the easiest, but xtiff seems to be used quite regularly, still (it's also a dependency of steinbock!). In general, I suspect that tifffile has become much more powerful wrt. OME-TIFF since the last version of xtiff was released, so I'm not sure how justified xtiff's existence still is nowadays?

@Phlpp-S as a quick fix, could you try installing tifffile==2022.4.22 into your environment where xtiff is installed?

jwindhager avatar Jan 26 '24 16:01 jwindhager

Hi @Milad4849 and @jwindhager , thank you very much for taking a look into this issue.

  • First, I got the same error TypeError: __call__() missing 1 required positional argument: 'value'.
  • Substituting with hasattr(tifffile.TIFF.COMPRESSION, 'compression_type') also fixed this issue for me, however I got a follow up error stating that the compression needs the "imagecodecs" package. This was easily solved with pip install imagecodecs and from imagecodecs import *.
  • Without the substitution, but with the installation of tifffile==2022.4.22 the value error remains for me.

With the substitution and imagecodecs, now the .tiff export works with a specified compression.

However now it seems that the array is not exported correct. I tried it with

comp = 'JPEG'
prof = xtiff.TiffProfile.OME_TIFF

xtiff.to_tiff(img_out_tif.astype('uint8'), 'xtiff_test_out.ome.tiff', image_name=None, image_date=None, channel_names=['Red', 'Green', 'Blue', 'Classnumber'], description=None,
        profile = prof, big_endian=None, big_tiff=None, big_tiff_threshold=4261412864,
        compression_type = comp, compression_level = 8, pixel_size=None, pixel_depth=None,
        interleaved=True, software='xtiff', ome_xml_fun=xtiff.get_ome_xml)

where img_out_tif is a (4 x N x M) matrix, where the first three channels are RGB from my input image, and the fourth channel contains class-values. I guess my simple list channel_names=['Red', 'Green', 'Blue', 'Classnumber'] is not sufficient? The corresponding xtiff doc states channel_names: A list of channel names. If True, channel names are determined using the DataArray channel coordinate;. How to specify the DataArray channel coordinate names?

Phlpp-S avatar Jan 28 '24 12:01 Phlpp-S

Hi @Phlpp-S What is the exact problem when you execute the above command? I tested your command, I can write out arrays of shape (4 x N x M) and when I read it the the channel names are correctly assigned in the metadata.

Milad4849 avatar Jan 30 '24 17:01 Milad4849

Hi @Milad484 The export with the command I posted works, but when reading the exported file again it only contains 1 channel. What library (& function) do you use to read the .tiff image? I tried cv2.imread() which reads 3 channels, and matplotlib plt.imread() which reads only 1 channel.

Phlpp-S avatar Jan 31 '24 20:01 Phlpp-S

This is the code I use check the metadata of the tiff file:

import tifffile
import xml.dom.minidom

with tifffile.TiffFile('xtiff_test_out.ome.tiff') as tiff:
    #ImageDescription tag
    Image_description = tiff.pages[0].tags.values()[5].value

    xml_tree = xml.dom.minidom.parseString(Image_description)
    prettified_xml = xml_tree.toprettyxml(indent="  ")
    print(prettified_xml)

and the output:

<?xml version="1.0" ?>
<OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd">
  <Image ID="Image:0" Name="xtiff_test_out.ome.tiff">
    <Pixels ID="Pixels:0" Type="uint8" SizeX="600" SizeY="400" SizeC="4" SizeZ="1" SizeT="1" DimensionOrder="XYCZT" Interleaved="true" BigEndian="false">
      <Channel ID="Channel:0:0" SamplesPerPixel="1" Name="Red"/>
      <Channel ID="Channel:0:1" SamplesPerPixel="1" Name="Green"/>
      <Channel ID="Channel:0:2" SamplesPerPixel="1" Name="Blue"/>
      <Channel ID="Channel:0:3" SamplesPerPixel="1" Name="Classnumber"/>
      <TiffData/>
    </Pixels>
  </Image>
</OME>

The value of the ImageDescription tag in the first page of the TiffFile object is the above OME XML. Reading the image with tifffile: tifffile.imread('xtiff_test_out.ome.tiff'), I get an array of shape (4 x N xM). As far as know the functions that you are using are primary for reading standard formats, tifffile or imageio can handle multi-channel TIFFs better.

Milad4849 avatar Feb 01 '24 12:02 Milad4849

Hi @Milad484 thanks for your answer. tifffile.imread() did what I wanted, to read in the image as a matrix for further processing.

Phlpp-S avatar Feb 04 '24 14:02 Phlpp-S