aicsimageio icon indicating copy to clipboard operation
aicsimageio copied to clipboard

Support Reading and Writing of Pyramidal Formats

Open evamaxfield opened this issue 5 years ago • 11 comments

Use Case

Please provide a use case to help us understand your request in context

Many OME-Tiffs and CZI files are pyramidal where LODs have different array shapes. It would be great to support the reading of these one shape (scene) at a time and the writing of these through iterable of arrays.

Solution

Please describe your ideal solution

tifffile very recently added support for both reading and writing of pyramidal tiffs, pull in these changes and wrap in our API and Reader and future OmeTiffWriter.

Note, these will likely not be supported until after current institute high priority projects are done and after aicsimageio==4.0.0 release.

evamaxfield avatar Jul 17 '20 20:07 evamaxfield

Waiting for test file data, pyramids of CZI and TIFF

evamaxfield avatar Dec 14 '20 23:12 evamaxfield

Adopt the levels API for reading:

img = AICSImage("my-pyramid.czi")
img.levels  # returns tuple of levels
img.current_level  # returns current level, default / starts at level 0 (full resolution)
img.set_level  # update current level

Undecided how to adopt for Writing.

evamaxfield avatar Jan 26 '21 03:01 evamaxfield

Handle known bug of multi-resolution (multi-level) OME TIFFs selecting the wrong metadata due to "Images" being created correctly for the total number of scenes in the file, but they don't use the matching IDs for the correct scenes / resolutions.

evamaxfield avatar Jan 27 '21 05:01 evamaxfield

Tracking ome-types metadata bug over on ome-types: https://github.com/tlambert03/ome-types/issues/71

evamaxfield avatar Jan 27 '21 06:01 evamaxfield

Going to work with Zarr devs to see if the chunked read issue on multi-resolution / pyramid is a:

  • dask from_zarr implementation issue
  • tifffile ZarrTiffStore spec implementation issue
  • or a Zarr "the spec is still undetermined" issue

Reasoning here is that dask from_zarr is assuming there will be a key of just ./zarray on the MutableMapping object and tifffile aszarr supports multi-resolution zarr by creating keys with names: 0/.zarray, 1/.zarray, etc. for the different resolution levels. This causes chunk read time issues when they key is requested and a ValueError occurs.

Minimum reproduction:

In [1]: import dask.array as da

In [2]: from tifffile import TiffFile

In [3]: t = TiffFile("aicsimageio/tests/resources/variable_scene_shape_first_scene_pyramid.ome.tiff")

In [4]: da.from_zarr(t.series[0].levels[0].aszarr())
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-d757ba4b5cab> in <module>
----> 1 t_as_d = da.from_zarr(t.series[0].levels[0].aszarr())

~/miniconda3/envs/aicsimageio/lib/python3.9/site-packages/dask/array/core.py in from_zarr(url, component, storage_options, chunks, name, inline_array, **kwargs)
   3159     else:
   3160         mapper = url
-> 3161         z = zarr.Array(mapper, read_only=True, path=component, **kwargs)
   3162     chunks = chunks if chunks is not None else z.chunks
   3163     if name is None:

~/miniconda3/envs/aicsimageio/lib/python3.9/site-packages/zarr/core.py in __init__(self, store, path, read_only, chunk_store, synchronizer, cache_metadata, cache_attrs)
    121 
    122         # initialize metadata
--> 123         self._load_metadata()
    124 
    125         # initialize attributes

~/miniconda3/envs/aicsimageio/lib/python3.9/site-packages/zarr/core.py in _load_metadata(self)
    138         """(Re)load metadata from store."""
    139         if self._synchronizer is None:
--> 140             self._load_metadata_nosync()
    141         else:
    142             mkey = self._key_prefix + array_meta_key

~/miniconda3/envs/aicsimageio/lib/python3.9/site-packages/zarr/core.py in _load_metadata_nosync(self)
    147         try:
    148             mkey = self._key_prefix + array_meta_key
--> 149             meta_bytes = self._store[mkey]
    150         except KeyError:
    151             raise ArrayNotFoundError(self._path)

~/active/cell/tifffile/tifffile/tifffile.py in __getitem__(self, key)
   7945         if key in self._store:
   7946             return self._store[key]
-> 7947         return self._getitem(key)
   7948 
   7949     def _getitem(self, key):

~/active/cell/tifffile/tifffile/tifffile.py in _getitem(self, key)
   8104     def _getitem(self, key):
   8105         """Return chunk from file."""
-> 8106         keyframe, page, chunkindex, offset, bytecount = self._parse_key(key)
   8107 
   8108         if self._chunkmode:

~/active/cell/tifffile/tifffile/tifffile.py in _parse_key(self, key)
   8144             # multiscales
   8145             print(key)
-> 8146             level, key = key.split('/')
   8147             series = self._data[int(level)]
   8148         else:

ValueError: not enough values to unpack (expected 2, got 1)

When I manually printed the key that dask was trying to get from the MutableMapping it was just .zarray.

evamaxfield avatar Jan 27 '21 07:01 evamaxfield

In [4]: da.from_zarr(t.series[0].levels[0].aszarr())
<snip>
ValueError: not enough values to unpack (expected 2, got 1)

To access the first level of the first multi-resolution series as zarr array, not group, use t.series[0].aszarr(level=0). This is currently necessary because t.series[0].levels[0] is the same as t.series[0].

cgohlke avatar Jan 27 '21 07:01 cgohlke

Thanks @cgohlke. Patched in the current open PR to default reading level 0 of pyramid files for now.

evamaxfield avatar Jan 27 '21 18:01 evamaxfield

@JacksonMaxfield Reading of pyramid levels is not available in aicsimageio 4.0.3, is it?

The CZI files I provided for #274 can be used as pyramid examples, if that's useful

https://drive.google.com/drive/folders/1XukPEpCfIvibTT_6992sCgXeszxg-hUK?usp=sharing

Is there a way to open a CZI file with aicsimageio and save it to a TIFF or some other format with the pyramid?

rcasero avatar Jul 06 '21 17:07 rcasero

Is there a way to open a CZI file with aicsimageio and save it to a TIFF or some other format with the pyramid?

Replying to myself, I think maybe with pyvips.Image.tiffsave(), where it's possible to select an output with a pyramid. But here, it seems that what we'd do is load the largest level with aicsimageio, and then re-create a pyramid with pyvips, rather than copying over the already existing pyramid.

rcasero avatar Jul 06 '21 18:07 rcasero

@rcasero 4.0.3 does not support multi-scale / pyramid level selection. This issue is slated for 4.1.

You can use bioformats or pyvips but I am not an expert in either of them to give you the best guidance there.

evamaxfield avatar Jul 06 '21 20:07 evamaxfield

@JacksonMaxfield Thanks. I made it work with pyvips.Image.tiffsave

im_vips.tiffsave(tif_filename, tile=True, pyramid=True, resunit='cm',
                             xres=float(1e-3/xres), yres=float(1e-3/yres), bigtiff=True)

https://github.com/rcasero/cytometer/blob/a3f9abf792f24dba771d9940fb49c9bde25efc26/cytometer/data.py#L1618

rcasero avatar Jul 08 '21 14:07 rcasero

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Apr 15 '23 01:04 github-actions[bot]