使用numpy在Python中处理TIFF文件(导入、导出)

137
我需要一个Python方法来打开和导入TIFF图像到numpy数组中,以便我可以分析和修改像素数据,然后再将它们保存为TIFF格式。(它们基本上是灰度的光强度地图,表示每个像素的相应值)
我没有找到关于PIL方法的任何TIFF文档。我尝试着解决这个问题,但只得到了“坏模式”或“不支持的文件类型”错误。
在这里我需要使用什么?
12个回答

148

首先,我从此页面下载了一个名为a_image.tif的测试TIFF图像。然后我用PIL打开它,代码如下:

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

这显示了一张彩虹图片。要转换成numpy数组,只需:

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

我们可以看到图像的尺寸与数组的形状相匹配:

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

而且该数组包含 uint8 值:

>>> 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)

当你完成对数组的修改后,你可以像这样将其转换回 PIL 图像:

>>> Image.fromarray(imarray)
<Image.Image image mode=L size=330x44 at 0x2786518>

4
我在处理数据类型时遇到了麻烦。对于一些数据,例如我的数组中有numpy.int16类型的数字,运行正常。但是对于numpy.uint16类型的数据,使用image.fromarray就会出现错误:"TypeError: Cannot handle this data type"。 - Jakob
4
从fromarray函数的源代码来看,它似乎不能处理无符号的16位数组。 - jterrace
1
当我尝试打开一张对于PIL来说太大的图片时,出现了以下错误信息:DecompressionBombError: Image size (900815608 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack. - devforfu
对我来说,imarray.shape 给出的是 (x, y, 3),我错过了什么吗? - pippo1980
没错,我在考虑OpenCV的image.shape和Pillow的image.size,没有意识到它是关于numpy.array(im).shape的。谢谢@nebroth。 - pippo1980
显示剩余4条评论

68

我使用matplotlib读取TIFF文件:

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

并且I将会是ndarray类型。

根据文档,当处理TIFF文件时,实际上是PIL在后台工作,因为matplotlib本身只能原生地读取PNG格式的文件,但对于我来说这已经很好用了。

还有一个plt.imsave函数可用于保存文件。


这绝对是处理TIFF文件最简单的方法!我试过十几种方法,但这是最好的。肯定点赞! - zachd1_618
那查看部分怎么样? - Monica Heddneck
8
看起来 matplotlib 改变了策略:ValueError: Only know how to handle extensions: ['png']; with Pillow installed matplotlib can handle more images。意思是,似乎 matplotlib 改变了处理图片的方式,如果安装了 Pillow 库,它可以处理更多类型的图片。 - strpeter

24

PyLibTiff 对我来说比 PIL 更好用,因为截至 2023 年 4 月,仍不支持每个颜色超过 8 位的彩色图像。

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)

您可以使用以下方式安装PyLibTiff:

pip3 install numpy pylibtiff

PyLibTiff的自述文件还提到了tifffile库,但我还没有尝试过。

4
很好。目前,tifffile已被包含在SciKit skimage.external.tifffile中,但如果你从Christoph Gohlke先生下载tifffile.py,它也可以作为一个模块导入。 - lesolorzanov
在Windows上,pip安装不会“自动工作”,请参见https://stackoverflow.com/questions/39483328/successfully-installed-libtiff-but-while-importing-getting-error - N4ppeL
这很好,但他们在处理方向时有问题,我甚至没有找到一种简单的方法来读取方向标签,参见 https://github.com/pearu/pylibtiff/blob/892df2482c869777798f37cdac29bf6dee221a5a/libtiff/libtiff_ctypes.py#L571 - VojtaK

21

您也可以使用GDAL来实现此操作。我知道它是一个地理空间工具包,但没有任何要求您必须拥有制图产品。

在这里提供预编译的Windows GDAL二进制文件链接(假设您正在使用Windows):链接

要访问数组:

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()

以上代码是用于单页TIF还是多页TIF?我想使用gdal将16位Tiff堆栈加载到nparrays中。 - user391339
这应该读取输入数据类型或将所有内容移动到numpy的float64中。您可以在“ReadAsArray()”调用的末尾添加“.astype(sometype)”调用以进行转换。不确定是否会复制(只是没有测试过)。 - Jzl5325
@Chikinn 来自审核:https://stackoverflow.com/review/suggested-edits/17962780 xrange 不是拼写错误,xrange 是 Python 2 版本的 range。我接受了这个编辑,因为 Python 3 仍在积极改进,而 Python 2 则不是。 - Taku
请注意,如果您使用的是macOS系统,安装gdal几乎会成为一次痛苦之旅,因此,如果您只需要处理tiff图像,请不要使用gdal。 - Mike 'Pomax' Kamermans
请注意,如果您使用的是 macOS 系统,安装 GDAL 可能会带来很多麻烦。因此,如果您只需要处理 TIFF 图像,建议不要使用 GDAL。 - Mike 'Pomax' Kamermans

14

对于图像堆栈,我发现使用scikit-image进行读取,使用matplotlib进行显示或保存更容易。我已经使用以下代码处理了16位TIFF图像堆栈。

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)

1
你的意思是将 "plt.imshow(mol..." 改为 "plt.imshow(img..." 吗? - EngrStudent

10
您也可以使用我编写的 pytiff 来进行操作。
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]

它是一个相对较小的模块,并且可能没有其他模块那么多的功能,但它支持平铺的TIFF和BigTIFF格式,因此您可以读取大图像的部分内容。


1
这个功能正是我所需要的!(能够读取大文件的一小部分)。但是当我尝试使用pip安装它时,我遇到了gcc错误。 - Fractaly
如果您在 issue 中提供错误信息,我会看看能否找出问题所在。 - hnfl
是的,我也很感兴趣,但当我试图安装它时遇到了错误。我在Windows和Ubuntu下使用pip进行安装。很遗憾它无法运行!我在这里创建了一个问题:https://github.com/FZJ-INM1-BDA/pytiff/issues/15 - Dobedani
无法安装 - Sadaf Shafi

10

有一个很棒的包叫做tifffile,可以让处理.tif或.tiff文件变得非常简单。

使用pip安装包:

pip install tifffile

现在,要以NumPy数组格式读取.tif /.tiff文件:
from tifffile import tifffile
image = tifffile.imread('path/to/your/image')
# type(image) = numpy.ndarray

如果您想将一个numpy数组保存成.tif/.tiff文件:

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

或者

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

你可以在此处阅读更多关于这个软件包的信息。

1
pip install tifffile was not sufficient for me. You might need also pip install imagecodecs - mathfux

4

使用cv2

import cv2
image = cv2.imread(tiff_file.tif)
cv2.imshow('tif image',image)

1
这里最简单的方法! - S P Sharan

1
如果你想使用geoTiff保存tiff编码,可以使用rasterio包。
一个简单的代码:
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()

如果你想了解更多关于numpy转GEOTiff的内容,可以点击这个链接:https://gis.stackexchange.com/questions/279953/numpy-array-to-gtiff-using-rasterio-without-source-raster


0

读取tiff文件的另一种方法是使用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)

输出:

(512, 512, 4)

可以在这里找到tensorflow文档。

为了使此模块正常工作,必须安装一个名为tensorflow-io的Python包

尽管我无法找到一种方法来查看输出张量(转换为nd.array后),因为输出图像有4个通道。我尝试使用cv2.cvtcolor()将其转换为cv2.COLOR_BGRA2BGR标志,参考这篇文章,但仍然无法查看图像。


这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。如果您想在此问题获得新的答案时得到通知,您可以关注此问题。一旦您拥有足够的声望,您还可以添加悬赏以吸引更多关注此问题的人。- 来自审核 - Francisco Puga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接