【问题标题】:Python PIL struggles with uncompressed 16-bit TIFF imagesPython PIL 与未压缩的 16 位 TIFF 图像作斗争
【发布时间】:2016-05-29 09:56:41
【问题描述】:

我的系统是 Mac OS X v10.8.2。我有几个 2560x500 未压缩的 16 位 TIFF 图像(灰度、无符号 16 位整数)。我首先尝试使用 PIL 加载它们(通过 Homebrew 安装,版本 1.7.8):

from PIL import Image
import numpy as np

filename = 'Rocks_2ptCal_750KHz_20ms_1ma_120KV_2013-03-06_20-02-12.tif'
img = Image.open(filename)

# >>> img
# <PIL.TiffImagePlugin.TiffImageFile image mode=I;16B size=2560x500 at 0x10A383C68>

img.show() 

# almost all pixels displayed as white.  Not correct.  
# MatLab, EZ-draw, even Mac Preview show correct images in grayscale.

imgdata = list(img.getdata()) 

# most values negative:
# >>> imgdata[0:10]
# [-26588, -24079, -27822, -26045, -27245, -25368, -26139, -28454, -30675, -28455]

imgarray = np.asarray(imgdata, dtype=np.uint16) 

# values now correct
# >>> imgarray
# array([38948, 41457, 37714, ..., 61922, 59565, 60035], dtype=uint16)

负值相差 65,536...可能不是巧合。

如果我假装通过 PIL 更改像素并恢复为 TIFF 图像(只需将数组作为图像放回原处):

newimg = Image.fromarray(imgarray)

我收到错误:

File "/usr/local/lib/python2.7/site-packages/PIL/Image.py", line 1884, in fromarray
    raise TypeError("Cannot handle this data type")
TypeError: Cannot handle this data type

我在 PIL 文档中找不到 Image.fromarray()。我已经尝试通过Image.fromstring() 加载,但我不了解 PIL 文档,并且示例方式很少。

如上面的代码所示,PIL 似乎将数据“检测”为I;16B。从 PIL 文档中我可以看出,模式 I 是:

*I* (32-bit signed integer pixels)

显然,这是不正确的。

我在 SX 上发现许多帖子表明 PIL 不支持 16 位图像。我找到了使用 pylibtiff 的建议,但我相信这仅限于 Windows?

我正在寻找一种在 Python 中处理这些 TIFF 图像的“轻量级”方式。我很惊讶它是如此困难,这让我相信这个问题对其他人来说是显而易见的。

【问题讨论】:

  • 你能在某处发布一个示例文件吗?
  • PIL 在很多事情上都遇到了困难,甚至像隔行扫描的 PNG 一样简单。我倾向于怀疑它根本不正确支持 16 位图像。
  • @nneonneo 不幸的是,我正在使用的文件中的数据是专有的,我不确定如何给出一个像这个一样失败的 TIFF 示例。我知道这使得故障排除非常困难,但我希望有人知道在这里做什么......我试图让脚本尽可能全面
  • @duskwuff 我很害怕。有没有更好的方法在 Python 中使用 TIFF(最好是非常轻量级且具有良好文档的东西?)

标签: python python-imaging-library tiff


【解决方案1】:

事实证明,Matplotlib 在 行代码中处理 16 位未压缩的 TIFF 图像:

import matplotlib.pyplot as plt
img = plt.imread(filename)

# >>> img
# array([[38948, 41457, 37714, ..., 61511, 61785, 61824],
#       [39704, 38083, 36690, ..., 61419, 60086, 61910],
#       [41449, 39169, 38178, ..., 60192, 60969, 63538],
#       ...,
#       [37963, 39531, 40339, ..., 62351, 62646, 61793],
#       [37462, 37409, 38370, ..., 61125, 62497, 59770],
#       [39753, 36905, 38778, ..., 61922, 59565, 60035]], dtype=uint16)

等等,瞧。我想这不符合我作为“轻量级”的要求,因为 Matplotlib(对我而言)是一个沉重的模块,但是将图像放入 Numpy 数组非常简单。我希望这可以帮助其他人快速找到解决方案,因为这对我来说并不明显。

【讨论】:

  • 我也在使用 16 位未压缩的 TIFF 图像。我必须有一个稍微不同的 Matplotlib 版本,或者稍微不同的 TIFF 图像,因为这段代码对我不起作用。 Matplotlib 返回一个维度为 (1024, 1024, 4) 而不是 (1024, 1024) 的数组,并且图像的最大值(如对 max(img.ravel()) 的调用所示)被错误地缩小为 255。为了它的价值,我求助于在 Matlab 中加载我的图像并将它们保存为 mat 文件,可以使用 scipy 可靠地加载。
  • @Mike Roberts,数组的最后一个维度 (4) 可能包含像素的每个组件。 RGB(A) 像素的每个分量通常为 8 位长,这就是最大值为 255 的原因,这意味着它甚至比获得一个简单的 1024x1024 数组还要好。您仍然可以通过对每个组件进行 ORing 二进制移位来将 4 个组件转换为一个 32 位长,例如。红色 |绿色
  • @SteveK 您好 SteveK,感谢您的建议。我不同意您的说法,即必须对返回的数组执行位旋转操作“甚至比获得一个简单的 1024x1024 数组更好”。 OP 询问 16 位灰度 TIFF 图像,因此唯一合适的返回类型是 1024x1024 的 uint16,至少在我看来。而且,作为这个bit-twiddling接口的客户端,我怎么知道alpha包含高位字节而red包含低位字节呢?并不总是遵守此约定(例如,彩色图像数据有时存储为 BGRA)。
  • 哦,我错过了这条关键信息(灰度)。显然,在这种情况下,每个像素只有一个(16 位字)整数要好得多。此外,对于像素排序,您是对的。但无论如何,对于 RGBA 图像,您将不得不处理字节并找到它们的顺序(BGRA、RGBA、ARGB ......)。确实不是那么明显。
【解决方案2】:

试试Pillow,“友好”的 PIL 分叉。他们最近增加了对 16 位和 32 位图像的更好支持,包括在 numpy 数组接口中。此代码适用于最新的 Pillow:

from PIL import Image
import numpy as np

img = Image.open('data.tif')
data = np.array(img)

【讨论】:

    猜你喜欢
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    • 2014-10-04
    • 1970-01-01
    相关资源
    最近更新 更多