135

I need a python method to open and import TIFF images into numpy arrays so I can analyze and modify the pixel data and then save them as TIFFs again. (They are basically light intensity maps in greyscale, representing the respective values per pixel)

I couldn't find any documentation on PIL methods concerning TIFF. I tried to figure it out, but only got "bad mode" or "file type not supported" errors.

What do I need to use here?

David B
  • 61
  • 8
Jakob
  • 1,897
  • 2
  • 16
  • 17

12 Answers12

145

First, I downloaded a test TIFF image from this page called a_image.tif. Then I opened with PIL like this:

>>> from PIL import Image
>>> im = Image.open('a_image.tif')
>>> im.show()

This showed the rainbow image. To convert to a numpy array, it's as simple as:

>>> import numpy
>>> imarray = numpy.array(im)

We can see that the size of the image and the shape of the array match up:

>>> imarray.shape
(44, 330)
>>> im.size
(330, 44)

And the array contains uint8 values:

>>> imarray
array([[  0,   1,   2, ..., 244, 245, 246],
       [  0,   1,   2, ..., 244, 245, 246],
       [  0,   1,   2, ..., 244, 245, 246],
       ..., 
       [  0,   1,   2, ..., 244, 245, 246],
       [  0,   1,   2, ..., 244, 245, 246],
       [  0,   1,   2, ..., 244, 245, 246]], dtype=uint8)

Once you're done modifying the array, you can turn it back into a PIL image like this:

>>> Image.fromarray(imarray)
<Image.Image image mode=L size=330x44 at 0x2786518>
jterrace
  • 64,866
  • 22
  • 157
  • 202
  • 4
    i am having troubles with data types. works fine for some, f.e. if i have numpy.int16 numbers in my array, but for numpy.uint16 image.fromarray yields: "TypeError: Cannot handle this data type" – Jakob Sep 28 '11 at 09:42
  • 4
    Looking at the source of fromarray, it doesn't look like it handles unsigned 16-bit arrays. – jterrace Sep 28 '11 at 13:32
  • @Jakob as of June 2020 PIL [doesn't support color images with more than 8 bits per color](https://github.com/python-pillow/Pillow/issues/1888), you're going to have to use a different library (or contribute the functionality yourself). – Boris Verkhovskiy Jun 03 '20 at 18:39
  • 1
    Here is what I've got when tried to open an image too big for PIL: `DecompressionBombError: Image size (900815608 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack.` – devforfu Nov 18 '20 at 19:37
  • to me imarray.shape gives (x,y , 3) ?? what am I missing ?? – pippo1980 Jan 20 '21 at 19:18
  • @pippo1980 You probably have a colour image, hence the three channels RGB. – nebroth Jun 03 '21 at 15:35
  • yep sure was thinking about image.shape of openCV and image.size of pillow dind't realize it was about numpy.array(im).shape. Thanks @nebroth – pippo1980 Jun 03 '21 at 17:06
  • If someone was wondering how the test image looks like since the link is broken, it is larger in horizontal dimension, e.g. width is bigger, thus xdim=330. Here is the image from archive https://web.archive.org/web/20150514030002/http://www-eng-x.llnl.gov/documents/a_image.tif – VojtaK Sep 07 '22 at 06:39
  • but then what if you want to save the PIL image as a tif? – seeker_after_truth Jan 16 '23 at 18:34
67

I use matplotlib for reading TIFF files:

import matplotlib.pyplot as plt
I = plt.imread(tiff_file)

and I will be of type ndarray.

According to the documentation though it is actually PIL that works behind the scenes when handling TIFFs as matplotlib only reads PNGs natively, but this has been working fine for me.

There's also a plt.imsave function for saving.

23

PyLibTiff worked better for me than PIL, which as of April 2023 still doesn't support color images with more than 8 bits per color.

from libtiff import TIFF

tif = TIFF.open('filename.tif') # open tiff file in read mode
# read an image in the current TIFF directory as a numpy array
image = tif.read_image()

# read all images in a TIFF file:
for image in tif.iter_images(): 
    pass

tif = TIFF.open('filename.tif', mode='w')
tif.write_image(image)

You can install PyLibTiff with

pip3 install numpy pylibtiff

The readme of PyLibTiff also mentions the tifffile library but I haven't tried it.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
  • 4
    This is very good. By now, tifffile is included in SciKit skimage.external.tifffile but it can also be imported as a module if you download tifffile.py from Mr. Christoph Gohlke – lesolorzanov Jan 15 '18 at 15:30
  • pip install won't "just work" on windows, see https://stackoverflow.com/questions/39483328/successfully-installed-libtiff-but-while-importing-getting-error – N4ppeL Nov 23 '21 at 16:30
  • This is nice but they have problem handling orientation and I did not find an easy way how to even read the orientation tag, see also https://github.com/pearu/pylibtiff/blob/892df2482c869777798f37cdac29bf6dee221a5a/libtiff/libtiff_ctypes.py#L571 – VojtaK Jul 22 '22 at 09:27
20

You could also use GDAL to do this. I realize that it is a geospatial toolkit, but nothing requires you to have a cartographic product.

Link to precompiled GDAL binaries for windows (assuming windows here) Link

To access the array:

from osgeo import gdal

dataset = gdal.Open("path/to/dataset.tiff", gdal.GA_ReadOnly)
for x in range(1, dataset.RasterCount + 1):
    band = dataset.GetRasterBand(x)
    array = band.ReadAsArray()
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Jzl5325
  • 3,898
  • 8
  • 42
  • 62
  • is the above code for a single TIF or multipage TIF? I'd like to use gdal to load 16 bit tiff stacks into nparrays. – user391339 May 29 '15 at 06:37
  • This should read in either the input data type or move everything to numpy's float64. You can add an `.astype(sometype)` call to the end of the `ReadAsArray()` call to cast. Not sure if this makes a copy (just have not tested). – Jzl5325 Jun 02 '15 at 22:09
  • @Chikinn From Review: https://stackoverflow.com/review/suggested-edits/17962780 `xrange` is no typo, `xrange` is the python 2 version of `range`. I accepted this edit because python 3 is still being actively improved while python 2 is not. – Taku Nov 16 '17 at 06:18
  • note that installing gdal will pretty much be a journey into pain if you're on macos, so if you just need to work with tiff images, don't use gdal. – Mike 'Pomax' Kamermans Feb 08 '23 at 03:56
14

In case of image stacks, I find it easier to use scikit-image to read, and matplotlib to show or save. I have handled 16-bit TIFF image stacks with the following code.

from skimage import io
import matplotlib.pyplot as plt

# read the image stack
img = io.imread('a_image.tif')
# show the image
plt.imshow(img,cmap='gray')
plt.axis('off')
# save the image
plt.savefig('output.tif', transparent=True, dpi=300, bbox_inches="tight", pad_inches=0.0)
Claire
  • 639
  • 9
  • 25
10

You can also use pytiff of which I am the author.

import pytiff

with pytiff.Tiff("filename.tif") as handle:
    part = handle[100:200, 200:400]

# multipage tif
with pytiff.Tiff("multipage.tif") as handle:
    for page in handle:
        part = page[100:200, 200:400]

It's a fairly small module and may not have as many features as other modules, but it supports tiled TIFFs and BigTIFF, so you can read parts of large images.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
hnfl
  • 289
  • 3
  • 7
  • 1
    This feature is exactly what I need! (Being able to read a small chunk of a large file). However when I try to pip install it I get a gcc error – Fractaly Jun 01 '19 at 02:46
  • If you create an [issue](https://github.com/FZJ-INM1-BDA/pytiff/issues) with the error message, I'll see if I can figure out the problem. – hnfl Jun 03 '19 at 09:43
  • Yes, I'm also interested but also got an error when I tried to install it. I did so by means of pip - under Windows and under Ubuntu. It's unfortunate that it does not work! I have created an issue here: https://github.com/FZJ-INM1-BDA/pytiff/issues/15 – Dobedani Sep 03 '19 at 17:47
  • unable to install – Sadaf Shafi Dec 15 '20 at 17:29
9

There is a nice package called tifffile which makes working with .tif or .tiff files very easy.

Install package with pip

pip install tifffile

Now, to read .tif/.tiff file in numpy array format:

from tifffile import tifffile
image = tifffile.imread('path/to/your/image')
# type(image) = numpy.ndarray

If you want to save a numpy array as a .tif/.tiff file:

tifffile.imwrite('my_image.tif', my_numpy_data, photometric='rgb')

or

tifffile.imsave('my_image.tif', my_numpy_data)

You can read more about this package here.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
jd95
  • 404
  • 6
  • 14
  • 1
    `pip install tifffile` was not sufficient for me. You might need also `pip install imagecodecs` – mathfux Oct 22 '21 at 00:32
4

Using cv2

import cv2
image = cv2.imread(tiff_file.tif)
cv2.imshow('tif image',image)
Kukesh
  • 490
  • 1
  • 5
  • 18
1

if you want save tiff encoding with geoTiff. You can use rasterio package

a simple code:

import rasterio

out = np.random.randint(low=10, high=20, size=(360, 720)).astype('float64')
new_dataset = rasterio.open('test.tiff', 'w', driver='GTiff',
                            height=out.shape[0], width=out.shape[1],
                            count=1, dtype=str(out.dtype),
                            )
new_dataset.write(out, 1)
new_dataset.close()

for more detail about numpy 2 GEOTiff .you can click this: https://gis.stackexchange.com/questions/279953/numpy-array-to-gtiff-using-rasterio-without-source-raster

yuanzz
  • 1,359
  • 12
  • 15
0

I recommend using the python bindings to OpenImageIO, it's the standard for dealing with various image formats in the vfx world. I've ovten found it more reliable in reading various compression types compared to PIL.

import OpenImageIO as oiio
input = oiio.ImageInput.open ("/path/to/image.tif")
zeno
  • 81
  • 8
0

Another method of reading tiff files is using tensorflow api

import tensorflow_io as tfio
image = tf.io.read_file(image_path)
tf_image = tfio.experimental.image.decode_tiff(image)
print(tf_image.shape)

Output:

(512, 512, 4)

tensorflow documentation can be found here

For this module to work, a python package called tensorflow-io has to installed.

Athough I couldn't find a way to look at the output tensor (after converting to nd.array), as the output image had 4 channels. I tried to convert using cv2.cvtcolor() with the flag cv2.COLOR_BGRA2BGR after looking at this post but still wasn't able to view the image.

Anik De
  • 73
  • 5
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/31203448) – Francisco Puga Mar 07 '22 at 10:16
-1

no answers to this question did not work for me. so i found another way to view tif/tiff files:

import rasterio
from matplotlib import pyplot as plt
src = rasterio.open("ch4.tif")
plt.imshow(src.read(1), cmap='gray')

the code above will help you to view the tif files. also check below to be sure:

type(src.read(1)) #see that src.read(1) is a numpy array

src.read(1) #prints the matrix