'PIL opens only first chanel of TIF image

I'm trying to open (and then process) a 3-channel Tif image (8-bits) created with ImageJ.

im = Image.open('spinal.tif')
im.show()

shows me a png for the first channel

n = np.array(im)
print(n.shape)

gives me (400, 450), thus considers only the first channel

How could I work on the different channels? Many thanks

Info on my tif file from ImageJ:
Title: spinal.tif
Width:  1986.4042 microns (450)
Height:  1765.6926 microns (400)
Size:  527K
Resolution:  0.2265 pixels per micron
Voxel size: 4.4142x4.4142x1 micron^3
ID: -466
Bits per pixel: 8 (grayscale LUT)
Display ranges
  1: 0-255
  2: 0-255
  3: 0-255
Image: 1/3 (c:1/3 - 64_spinal_20x lame2.ndpis #1)
  Channels: 3
  Composite mode: "grayscale"

The file is temporarily available here : https://filesender.renater.fr/?s=download&token=ab39ca56-24c3-4993-ae78-19ac5cf916ee



Solution 1:[1]

I finally found a way around using the scikit-image library. This opens correctly the 3 channels (matplotlib didn't, nor PIL). Once I have the array, I can go back to PIL using Image.fromarray to resume the processing.

from skimage.io import imread
from PIL import Image

img = imread('spinal.tif')
im_pil = Image.fromarray(img)
im_pil.show()
print(np.array(im_pil).shape)

This shows the composite image, and the correct (400, 450, 3) shape. I can then get the different channels with Image.getchannel(channel) as in :

im_BF = im_pil.getchannel(0)
im_BF.show()

Thank you to the contributors who tried to solve my issue (I saw that the file was downloaded several times) and there might be a better way to process these multiple-channels TIF images with PIL, but this looks like working !

Solution 2:[2]

Your image is not a 3-channel RGB image. Rather, it is 3 separate images, each one a single greyscale channel. You can see that with ImageMagick:

magick identify spinal.tif
spinal.tif[0] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 545338B 0.000u 0:00.000
spinal.tif[1] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 0.000u 0:00.000
spinal.tif[2] TIFF 450x400 450x400+0+0 8-bit Grayscale Gray 0.000u 0:00.000

Or with tiffinfo which comes with libtiff:

TIFF Directory at offset 0x8 (8)
  Subfile Type: (0 = 0x0)
  Image Width: 450 Image Length: 400
  Resolution: 0.22654, 0.22654 (unitless)
  Bits/Sample: 8
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Samples/Pixel: 1
  Rows/Strip: 400
  Planar Configuration: single image plane
  ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false

TIFF Directory at offset 0x545014 (850f6)
  Subfile Type: (0 = 0x0)
  Image Width: 450 Image Length: 400
  Resolution: 0.22654, 0.22654 (unitless)
  Bits/Sample: 8
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Samples/Pixel: 1
  Rows/Strip: 400
  Planar Configuration: single image plane
  ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false

TIFF Directory at offset 0x545176 (85198)
  Subfile Type: (0 = 0x0)
  Image Width: 450 Image Length: 400
  Resolution: 0.22654, 0.22654 (unitless)
  Bits/Sample: 8
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Samples/Pixel: 1
  Rows/Strip: 400
  Planar Configuration: single image plane
  ImageDescription: ImageJ=1.53f
images=3
channels=3
mode=grayscale
unit=micron
loop=false

If it is meant to be 3-channel RGB, rather than 3 separate greyscale channels, you need to save it differently in ImageJ. I cannot advise on that.

If you want combine the 3 channels into a single image on the command-line, you can do that with ImageMagick:

magick spinal.tif -combine spinal-RGB.png

If you want to read it with PIL/Pillow, you need to treat it as an image sequence:

from PIL import Image, ImageSequence

with Image.open("spinal.tif") as im:
    for frame in ImageSequence.Iterator(im):
        print(frame)

which gives this:

<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>
<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>
<PIL.TiffImagePlugin.TiffImageFile image mode=L size=450x400 at 0x11DB64220>

Or, if you want to assemble into RGB, something more like this:

from PIL import Image

# Open image and hunt down separate channels
with Image.open("spinal.tif") as im:
    R = im.copy()
    im.seek(1)
    G = im.copy()
    im.seek(2)
    B = im.copy()

# Merge the three separate channels into single RGB image
RGB = Image.merge("RGB", (R, G, B))
RGB.save('result.png')

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tilde
Solution 2