Python: 读取和写入TIFF格式的16位、三通道、彩色图像

34

有人有导入Python中16位每通道、3通道TIFF图像的方法吗?

我尚未找到一种方法,可以在处理TIFF格式时保留每通道16位的深度。我希望有善良的人能提供解决方案。

以下是我迄今为止尝试但未成功的方法和结果列表:

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

到目前为止,我找到的唯一解决方法是使用ImageMagick将图像转换为PNG格式。然后标准的matplotlib.pyplot.imread函数就可以无问题地读取PNG文件了。

另一个问题是保存numpy数组为16位PNG文件,这也迄今为止并不简单。

6个回答

40

它的功能有限,特别是在写回非 RGB 图像到磁盘时,但是Christoph Gohlke 的 tifffile 模块可以轻松读取 3 通道 16 位 TIFF 文件,我刚测试过:

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

而且 Photoshop 读取我从以下操作中获取的内容时并不抱怨:

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

这个可以工作!但是这是处理tiff图像的正确方法吗?我使用gdal_translate将tiff转换为png,并使用此方法读取tiff并通过转换为PIL图像输出png。图像像素值不同。使用此方法得到的值比使用gdal获得的值略低。 - MonsieurBeilto
1
tifffile可以通过'pip install tifffile'获取。参考 - https://pypi.org/project/tifffile/ 推荐使用pip安装,其操作速度更快。例如,imread的速度大约快99倍。 - MonsieurBeilto

31

@Jaime 的回答是正确的。

与此同时,我也使用 OpenCV 中的 cv2.imread 解决了这个问题。

默认情况下,cv2.imread 会将一个16位、三通道的图像(如问题所示的 a.tif)转换为8位。

cv2.imread 函数接受文件名后的标志参数(cv2.imread(filename[, flags])),用于指定所加载图像的颜色类型,详情请参见文档

  1. >0 返回3通道彩色图像,结果同上述一样转换为8位。
  2. 0 返回灰度图像,同样转换为8位。
  3. <0 返回原始图像。这将返回16位图像。

因此,以下代码将读取图像而不进行转换:

>>> im = cv2.imread('a.tif', -1)
>>> im.dtype
dtype('uint16')
>>> im.shape
(288, 384, 3)
请注意,OpenCV 返回的R、G和B通道顺序相反,因此 im[:,:,0] 是B通道,im[:,:,1] 是G通道,im[:,:,2] 是R通道。
我还发现 cv2.imwrite 可以写入16位、三通道的TIFF文件。
>>> cv2.imwrite('out.tif', im)

使用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
  ....

能否使用cv2.imshow()来显示图像?我能够复现你的例子,但是无法显示图像。 - zml
1
我能够使用cv2.imshow显示图像,所以我不确定为什么在你的情况下它不起作用。 - Lars Chr
为了使用imshow查看图像,我建议将其移动和缩放到8位范围内。这是我的代码片段: 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') - antibus

12
我发现了另一种方法来处理上述两种方法之外的情况。 scikit-image包还可以使用tifffile.py和FreeImage来读取16位,三通道TIFF文件,并将它们指定为要使用的插件。
虽然使用tifffile.py进行阅读可能更简单,就像@Jaime所示的那样,但我想展示如何与scikit-image一起使用它,以防有人想用这种方式进行操作。
对于任何使用Ubuntu的人,FreeImage可以使用apt作为libfreeimage3来获取。
如果使用tifffile.py插件选项,则必须将tifffile.py复制到skimage/io/_plugins目录中(例如,在我的情况下,Ubuntu中的完整路径为/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)

编写TIFF文件:

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

使用ImageMagick检查b.tifc.tif的位深度,结果显示两个图像中的每个通道均为16位。


该方法出现 ValueError: 无法解压缩 jpeg 错误。 - Re Dream

6

对我而言,之前的替代方案都没有起到作用。我已经成功使用 gdal 读取了一个1GB的16位图像。

您可以用以下代码打开一张图像:

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

这里有一个支持的驱动列表,你可以使用它们来写入数据。


1

我曾经花费很大力气尝试使用Scikits-Image (skimage.io)读取带有JPEG压缩的多图像TIFF。我使用的是Windows 10版本的Anaconda Python3;tifffile是通过Anaconda Navigator或'conda install'安装的。

最终,我使用'conda remove tifffile'卸载了'tifffile'。然后使用'pip install tifffile'重新安装了'tifffile'。这样安装了最新的'tifffile'插件-版本为2020.5.5。接下来使用'pip install imagecodecs'安装了图像编解码器。现在以下代码可以正常工作:

import skimage.io
img = skimage.io.imread('picture.tiff', plugin='tifffile')

请注意,只有按照上述顺序安装“tifffile”和“imagecodes”(并删除Anaconda的“tifffile”)才能使此方法起作用。

1

我建议使用OpenImageIO的Python绑定,这是在vfx领域处理各种图像格式(通常为16/32位)的标准。

import OpenImageIO as oiio
input = oiio.ImageInput.open ("/path/to/image.tif")

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