Search code examples
pythonpython-imaging-librarydaskscikit-image

Loading non RGBA 4-tile multi page tiff images with skimage into dask array


I need to read a folder full of multi-page tiffs generated by the Suite2p neurobiology package. From the Suite2p source code the multi-tiffs are created as follows:

import numpy as np
from tifffile import TiffWriter

# fake example images
img_5_tiles = np.random.randint(0,65535,(5,10,20), dtype='uint16')
img_4_tiles = img_5_tiles[1:,...]

# save fake images
with TiffWriter(r'D:\5tiles.tiff') as tif:
    for frame in img_5_tiles:
        tif.save(frame)

with TiffWriter(r'D:\4tiles.tiff') as tif:
    for frame in img_4_tiles:
        tif.save(frame)

When I try to read them into dask, skimage.io default tifffile plugin fails to get the correct shape:

from dask.array.image import imread
from skimage import io

def read_with_tifffile(path):
    return io.imread(path, plugin='tifffile')

# should produce (5,10,20) instead of (1,10,20)
imread(r'D:\5tiles.tiff', imread=read_with_tifffile).shape

I can overcome this by using the non-default skimage.io.imread plugin 'pil'

def read_with_pil(path):
    return io.imread(path, plugin='pil', mode='L')

# gives (1,5,10,20) which is acceptable
imread(r'D:\5tiles.tiff', imread=read_with_pil).shape

Unfortunately, if the number of tiles equals to 4, skimage starts to handle the shape differently:

# gives (1,10,20,4) instead of (1,4,10,20)
imread(r'D:\4tiles.tiff', imread=read_with_pil).shape

From reading skimage docs, it's probably trying to interpret my image as RGBA and then fails. Is there a solution to force 'uint16' multipage read for all image shapes?

Any advice is appreciated!


Solution

  • Based on cgohlke's comment:

    from dask.array.image import imread
    import numpy as np
    from skimage import io
    import tifffile
    
    # fake data
    img_with_4_tiles = np.random.randint(0,65535,(4,10,20), dtype='uint16')
    
    with tifffile.TiffWriter(r'D:\4tiles.tiff') as tif:
        for frame in img_with_4_tiles:
            tif.save(frame)
            
    # my original bad solution
    def read_with_pil(path):
        return io.imread(path, plugin='pil', mode='L')
    
    print(imread(r'D:\4tiles.tiff', imread=read_with_pil).shape)
    
    # a good solution from the horse's mouth (https://pypi.org/user/cgohlke/)
    def read_with_tifffile(path):
        return tifffile.imread(path, is_shaped=False)
    
    print(imread(r'D:\4tiles.tiff', imread=read_with_tifffile).shape)