Python: Read and write TIFF 16 bit , three channel , colour images

99,505

Solution 1

It has limited functionality, especially when it comes to writing back to disk non RGB images, but Christoph Gohlke's tifffile module reads in 3 channel 16-bit TIFFs with no problems, I just tested it:

>>> import tifffile as tiff
>>> a = tiff.imread('Untitled-1.tif')
>>> a.shape
(100L, 100L, 3L)
>>> a.dtype
dtype('uint16')

And Photoshop reads without complaining what I get from doing:

>>> tiff.imsave('new.tiff', a)

Solution 2

The answer by @Jaime works.

In the mean time I managed to also solve the problem using cv2.imread in OpenCV.

By default cv2.imread will convert a 16 bit, three channel image in a.tif to 8 bit as shown in the question.

cv2.imread accepts a flag after the filename ( cv2.imread(filename[, flags]) ) which specifies the colour type of the loaded image cf. the documentation:

  1. >0 returns a 3 channel colour image. This results in conversion to 8 bit as shown above.
  2. 0 returns a greyscale image. Also results in conversion to 8 bit.
  3. <0 returns the image as is. This will return a 16 bit image.

So the following will read the image without conversion:

>>> im = cv2.imread('a.tif', -1)
>>> im.dtype
dtype('uint16')
>>> im.shape
(288, 384, 3)

Note that OpenCV returns the R, G and B channels in reverse order so im[:,:,0] is the B channel, im[:,:,1] the G channel and im[:,:,2] is the R channel.

I have also found that cv2.imwrite can write 16 bit, three channel TIFF files.

>>> cv2.imwrite('out.tif', im)

Checking the bit depth with ImageMagick:

$ identify -verbose out.tif
  Format: TIFF (Tagged Image File Format)
  Class: DirectClass
  Geometry: 384x288+0+0
  Resolution: 72x72
  Print size: 5.33333x4
  Units: PixelsPerInch
  Type: TrueColor
  Base type: TrueColor
  Endianess: MSB
  Colorspace: sRGB
  Depth: 16-bit
  Channel depth:
    red: 16-bit
    green: 16-bit
    blue: 16-bit
  ....

Solution 3

I found an additional alternative to the two methods above.

The scikit-image package can also read 16 bit, three channel TIFF files using both tifffile.py and FreeImage and specifying them as the plugin to be used.

While reading using tifffile.py is probably done more simply in the manner shown by @Jaime, I thought I would show how it is used along with scikit-image in case anyone wants to do it in this manner.

For anyone using Ubuntu, FreeImage is available as libfreeimage3 using apt.

If the tifffile.py plugin option is used the tifffile.py must be copied to the skimage/io/_plugins directory (f.ex. on Ubuntu the full path in my case was /usr/local/lib/python2.7/dist-packages/skimage/io/_plugins/).

>>> import skimage.io
>>> im = skimage.io.imread('a.tif', plugin='tifffile')
>>> im.dtype
dtype('uint16')
>>> im.shape
(288, 384, 3)
>>> im = skimage.io.imread('a.tif', plugin='freeimage')
>>> im.dtype
dtype('uint16')
>>> im.shape
(288, 384, 3)

Writing TIFF files:

>>> skimage.io.imsave('b.tif', im, plugin='tifffile')
>>> skimage.io.imsave('c.tif', im, plugin='freeimage')

Checking the bitdepth of both b.tif and c.tif using ImageMagick shows that each channel in both images are 16 bit.

Solution 4

For me the previous alternatives did not work. I have used gdal successfully for reading a 16bit images of 1 GB.

You can open an image with something like this:

from osgeo import gdal
import numpy as np
ds = gdal.Open("name.tif")
channel = np.array(ds.GetRasterBand(1).ReadAsArray())

There is a list of supported diver that you can use to write the data.

Solution 5

I recommend using the python bindings to OpenImageIO, it's the standard for dealing with various image formats in the vfx domain (which usually are 16/32bit).

import OpenImageIO as oiio
input = oiio.ImageInput.open ("/path/to/image.tif")
Share:
99,505
Lars Chr
Author by

Lars Chr

I work primarily with optical investigation multiphase flows. I use Julia and Python and the many excellent associated packages for numerical computations.

Updated on May 07, 2020

Comments

  • Lars Chr
    Lars Chr about 4 years

    Does anyone have a method for importing a 16 bit per channel, 3 channel TIFF image in Python?

    I have yet to find a method which will preserve the 16 bit depth per channel when dealing with the TIFF format. I am hoping that some helpful soul will have a solution.

    Here is a list of what I have tried so far without success and the results:

    import numpy as np
    import PIL.Image as Image
    import libtiff
    import cv2
    
    im = Image.open('a.tif')
    # IOError: cannot identify image file
    
    tif = libtiff.TIFF.open('a.tif')
    im = tif.read_image()
    # im only contains one of the three channels. im.dtype is uint16 as desired.
    im = []
    for i in tif.iter_images():
        # still only returns one channel
    
    im = np.array(cv2.imread('a.tif'))
    # im.dtype is uint8 and not uint16 as desired.
    # specifying dtype as uint16 does not correct this
    

    So far the only solution I have found is to convert the image to PNG with ImageMagick. Then the bog standard matplotlib.pyplot.imread reads the PNG file without any problems.

    Another problem I have is saving any numpy arrays as 16 bit PNG files which so far has not been straightforward either.

  • zml
    zml over 9 years
    would it be possible to display the image using cv2.imshow()? I was able to reproduce your example, but not to display the image however..
  • Lars Chr
    Lars Chr over 9 years
    I am able to display the image with cv2.imshow so I'm not sure why it does not work in your case.
  • antibus
    antibus about 7 years
    For viewing the image with imshow I recommend to shift and scale to a 8 bit range. Here my code snippet imin, imax = np.min(im), np.max(im); im -= imin; imf = np.array(im,'float32'); imf *= 255./(imax-imin); im = np.asarray(np.round(imf), 'uint8')
  • MonsieurBeilto
    MonsieurBeilto almost 6 years
    This works! But is this the correct way to handle tiff images? I used gdal_translate convert tiff ti png and used this method to read tiff and output png by converting to PIL images. The image pixel values differ. The values using this method are a little lower than those obtained using gdal
  • MonsieurBeilto
    MonsieurBeilto almost 6 years
    tifffile can be acquired with 'pip install tifffile'. ref - pypi.org/project/tifffile Using the pip installation is recommended, it's operation is much faster. For example, the imread is about 99 times faster.
  • Re Dream
    Re Dream about 4 years
    This method gives ValueError: cannot decompress jpeg error