2

I have a TIFF image file from a confocal microscope which I can open in ImageJ, but which I would like to get into Python.

The format of the TIFF is as follows: There are 30 stacks in the Z dimension. Each Z layer has three channels from different fluorescent markers. Each channel has a depth of 8 bits. The image dimensions are 1024x1024.

I can, in principle, read the file with skimage (which I plan to use to further analyse the data) using the tifffile plugin. However, what I get is not quite what I expect.

merged = io.imread("merge.tif", plugin="tifffile")
merged.shape
# (30, 3, 3, 1024, 1024)
# (zslice, RGB?, channel?, height, width)
merged.dtype
# dtype('uint16')

What confused me initially was the fact that I get two axes of length 3. I think that this is because tifffile treats each channel as separate RGB images, but I can work around this by subsetting or using skimage.color.rgb2grey on the individual channels. What concerns me more is that the file is imported as a 16 bit image. I can convert it back using skimage.img_as_ubyte, but afterwards, the histogram does no longer match the one I see in ImageJ.

I am not fixated on using skimage to import the file, but I would like to get the image into a numpy array eventually to use skimage's functionality on it.

Vertho
  • 200
  • 1
  • 7
  • 1
    I use the tifffile plugin the same way and it works fine. Try some examplary tiff files and check their shape, e.g. the "smulti-channel-time-series.ome.tif" from here: http://www.openmicroscopy.org/site/support/ome-model/ome-tiff/data.html. Maybe something is wrong with your file? – a.smiet Oct 01 '15 at 12:52
  • Huh. The sample file does indeed work just fine. I noticed, however, that in ImageJ, this sample file only has one slider: It cycles through the focal planes on a per-channel basis, and since there are 5 focal planes and three channels, this slider has 15 steps. For my file, there are actually two separate sliders: one for channels (three steps), and one for focal planes (30 stacks). I will look into that, thank you. – Vertho Oct 01 '15 at 13:09
  • A-ha! ImageJ's `Hyperstack to Stack` function did the trick. Thank you very much. This is an unexpected turn of events, I wonder when I would have figured that out. I am willing to mark your comment as an accepted answer, if you post it as one. – Vertho Oct 01 '15 at 13:19
  • Maybe explain to the rest of us what Hyperstack to Stack does in the answer as well. I am curious whether the skimage tiff reader is doing the right thing. If not, I'd like to know how to fix it. – Stefan van der Walt Oct 01 '15 at 14:29
  • I'm afraid I'm not familiar enough with ImageJ's internals and the peculiarities of the TIFF image format to give any sane answer as to what is happening in the conversion, exactly. Sorry. What I can tell you, however, is that the reader works as I would expect with the resulting `stack`. – Vertho Oct 04 '15 at 14:01
  • Can you make the `merge.tif` file available? – cgohlke Oct 07 '15 at 00:22
  • @cgohlke Please excuse the delayed answer. I'm afraid I cant, however, I created a new Hyperstack in ImageJ and could reproduce the same problem. Converting said Hyperstack to a stack solved the problem there, too. `from skimage import io` `io.imread("hyperstack.tif", plugin="tifffile").shape` `# (5, 3, 3, 300, 400)` `io.imread("hyperstack_to_stack.tif", plugin="tifffile").shape` `# (5, 3, 300, 400)` Hyperstack: http://wikisend.com/download/384964/hyperstack.tif Stack: http://wikisend.com/download/111538/hyperstack_to_stack.tif – Vertho Oct 16 '15 at 20:45
  • 1
    The hyperstack TIFF has the `PhotometricInterpretation` tag set to `Palette`, causing tifffile.py and other TIFF readers to use the image values to index the `ColorMap` field to get RGB samples. The latest version of [tifffile.py](http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html) has disabled color-mapping for ImageJ hyperstacks. – cgohlke Oct 17 '15 at 08:41

2 Answers2

3

I've encountered the same issue working on .tif files. I recommend to use bioformats python package.

    import javabridge
    import bioformats

    javabridge.start_vm(class_path=bioformats.JARS)

    path_to_data = '/path/to/data/file_name.tif'

    # get XML metadata of complete file
    xml_string = bioformats.get_omexml_metadata(path_to_data)
    ome = bioformats.OMEXML(xml_string) # be sure everything is ascii
    print ome.image_count

depending on data, one file can hold multiple images. Each image can be accessed as follows:

    # read some metadata
    iome = ome.image(0) # e.g. first image
    print iome.get_Name()
    print iome.get_ID()

    # get pixel meta data
    print iome.Pixels.get_DimensionOrder()
    print iome.Pixels.get_PixelType()
    print iome.Pixels.get_SizeX()
    print iome.Pixels.get_SizeY()
    print iome.Pixels.get_SizeZ()
    print iome.Pixels.get_SizeT()
    print iome.Pixels.get_SizeC()
    print iome.Pixels.DimensionOrder

loading raw data of image 0 into numpy array is done like that:

    reader = bioformats.ImageReader(path_to_data)
    raw_data = []
    for z in range(iome.Pixels.get_SizeZ()):
        # returns 512 x 512 x SizeC array (SizeC = number of channels)
        raw_image = reader.read(z=z, series=0, rescale=False) 
        raw_data.append(raw_image)
    raw_data = np.array(raw_data) # 512 x 512 x SizeC x SizeZ array

Hope this helps processing .tif files, Cheers!

Patrick
  • 39
  • 2
0

I am not sure if the 'hyperstack to stack' function is that what you want. Hyperstacks are simply multidimensional images, could be 4D or 5D (width, hight, slices, channels (e.g. 3 for RGB) and time frames). In ImageJ you have a slider for each dimension in a hyperstack.

Stacks are just stacked 2D images that are somehow related and you have only one slider, in the simplest case it represents the z-slices in a 3D data set.

The 'hyperstack to stack' function stacks all dimensions in your hyperstack. So if you have a hyperstack with 3 channels, 4 slices and 5 time frames (3 sliders) you will get a stack of 3x4x5 = 60 images (one slider). Basically the same thing as you mentioned above with sliding through the focal planes on a per-channel basis. You can go the other way around using 'stack to hyperstack' and make a hyperstack by defining which slices from your stack represent which dimension. In the example file I mentioned above just select order xyzct, 3 channels and 7 time points.

So if your tiff file has 2 sliders, it seems that it is a 4D hyperstack with hight, width, 30 slices and 3 channels. 'hyperstack to stack' would stack all dimensions on top of each other, so you will get 3x30=90 slices.

However, according to the skimage tiff reader it seems that your tiff file is some kind of a 5D hyperstack. Width, hight (1024x1024), 30 z-slices, 3 channels (RGB) and another dimension with 3 entries (e.g. time frames).

In order to find out what is wrong, I would suggest to compare the dimensions with 3 entries of the array you get from skimage. Find out which one of them represents the RGB channels and what the other one is. You can for example use pyqtgraph's image function:

import pyqtgraph as pg
merged = io.imread("merge.tif", plugin="tifffile")

#pg.image takes the dimensions in the following order: z-slider,x,y,RGB channel
#if merged.shape = (30, 3, 3, 1024, 1024), you have to compare the 1st and 2nd dimension

pg.image(merged[:,0,:,:,:].transpose(0, 2, 3, 1))
pg.image(merged[:,1,:,:,:].transpose(0, 2, 3, 1))
pg.image(merged[:,2,:,:,:].transpose(0, 2, 3, 1))

pg.image(merged[:,:,0,:,:].transpose(0, 2, 3, 1))
pg.image(merged[:,:,1,:,:].transpose(0, 2, 3, 1))
pg.image(merged[:,:,2,:,:].transpose(0, 2, 3, 1))
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
a.smiet
  • 1,727
  • 3
  • 21
  • 36
  • You're correct in saying that the skimage TIFF reader seems to interpret it as a 5D image, but that is exactly what threw me off, because I know for a fact it is not. Width, height, three channels and 30 focal planes is all there is to it, and just converting it from a hyperstack to a stack in ImageJ solves the problem and I can work with the resulting array as expected. – Vertho Oct 04 '15 at 13:59