如何使用PIL读取原始图像?

33

我有一张原始图像,其中每个像素对应于一个16位无符号整数。我正在尝试使用PIL Image.fromstring()函数读取,代码如下:

if __name__ == "__main__":
    if (len(sys.argv) != 4):
        print 'Error: missing input argument'
        sys.exit()

    file = open(sys.argv[1], 'rb')
    rawData = file.read()
    file.close()

    imgSize = (int(sys.argv[2]), int(sys.argv[3]))

    # Use the PIL raw decoder to read the data.
    #   - the 'F;16' informs the raw decoder that we are reading a little endian, unsigned integer 16 bit data.
    img = Image.fromstring('L', imgSize, rawData, 'raw', 'F;16')

    im.save('out.png')
PIL文档中提到fromstring()函数的第一个参数是'mode',但是在查看文档和谷歌搜索后,我没有找到关于该参数真正含义的详细信息(我认为它与颜色空间或类似的东西有关)。是否有人知道我可以在哪里找到更详细的fromstring()函数参考以及mode参数的含义?
4个回答

22

具体文档在http://effbot.org/imagingbook/concepts.htm

模式

图像的模式定义了图像中像素的类型和深度。当前版本支持以下标准模式:

  • 1(1位像素,黑白,每字节存储一个像素)
  • L(8位像素,灰度)
  • P(8位像素,通过调色板映射到任何其他模式)
  • RGB(3x8位像素,真彩色)
  • RGBA(4x8位像素,带有透明掩码的真彩色)
  • CMYK(4x8位像素,颜色分离)
  • YCbCr(3x8位像素,彩色视频格式)
  • I(32位有符号整数像素)
  • F(32位浮点像素)

PIL 还提供了对一些特殊模式的有限支持,包括 LA (带有 alpha 通道的 L),RGBX(带填充的真彩色)和 RGBa(带预乘 alpha 的真彩色)。


12

Image.frombuffer(mode, size, data) => image

(PIL 1.1.4中新增). 使用标准的“raw”解码器,从字符串或缓冲区对象中的像素数据创建图像内存。对于某些模式,图像内存将与原始缓冲区共享内存(这意味着对原始缓冲区对象的更改会反映在图像中)。并非所有模式都可以共享内存;支持的模式包括“L”、“RGBX”、“RGBA”和“CMYK”。对于其他模式,此函数的行为类似于调用fromstring函数。

我不确定“L”代表什么,但“RGBA”代表红-绿-蓝-Alpha,所以我认为RGBX等同于RGB(编辑:经过测试这并不是真的)? CMYK是一种颜色空间,代表青色-洋红-黄色-开尔文。当然,我假设如果您了解PIL,也了解颜色空间。如果没有,Wikipedia有一篇很棒的文章。

关于它的真正含义(如果这还不够清楚):像素值将在每个颜色空间中以不同的方式编码。在常规RGB中,每个像素有3个字节-0-254、0-254、0-254。对于Alpha,您需要为每个像素添加另一个字节。如果您将RGB图像解码为RGBA,则最终会将右侧第一个像素旁边的R像素读取为alpha,这意味着您将获得G像素作为R值。这将根据您的图像大小而放大,但它确实会使您的颜色变得奇怪。类似地,尝试将CMYK编码的图像读取为RGB(或RGBA)将使您的图像看起来非常不像它应该的样子。例如,尝试使用此图像进行操作:
i = Image.open('image.png')
imgSize = i.size
rawData = i.tostring()
img = Image.fromstring('L', imgSize, rawData)
img.save('lmode.png')
img = Image.fromstring('RGB', imgSize, rawData)
img.save('rgbmode.png')
img = Image.fromstring('RGBX', imgSize, rawData)
img.save('rgbxmode.jfif')
img = Image.fromstring('RGBA', imgSize, rawData)
img.save('rgbamode.png')
img = Image.fromstring('CMYK', imgSize, rawData)
img.save('rgbamode.tiff')

你会看到不同的模式有什么不同 - 尝试使用各种输入图像:带alpha通道的png、不带alpha通道的png、bmp、gif和jpeg。实际上,这是一项有趣的实验。


1
我认为“L”代表亮度,比如灰度图像。CMYK代表青色、品红色、黄色和黑色(请参阅此Wikipedia文章)。 - martineau
我不是完全确定,但从我测试的结果来看,当使用“L”模式时,图像将保存为二进制图像(黑白),每个像素占用一个字节,而不是像“1”模式中一样占用1位。 - Alceu Costa
1
8位RGB(A / X)和CMYK组件通常范围从0-255或0x00-0xFF,而不是0-254。 - martineau

6
如果其他方法都失败了,你总是可以阅读源代码。对于PIL来说,下载链接在这里
你没有明确说明16位无符号整数中的像素数据格式是什么,但我猜它类似于RRRRRGGGGGGBBBBBB(5位红色,6位绿色,5位蓝色)或RRRRRGGGGGBBBBBA(5位红色,5位绿色,5位蓝色,1位Alpha或透明度)。我自己快速查看了一些源代码后并没有看到对这些格式的支持,但无法确定是否有支持。
在与PIL下载相同的网页上,他们提到可以将问题发送到Python Image SIG邮件列表,并提供了一个链接。这可能比在这里询问更好。
希望这有所帮助。

我读取的图像是灰度的。因此,每个像素都表示为16位无符号整数值。katrielalex找到的文档很有用。然而,PIL对保存这些16位灰度图像的支持非常有限。我认为我将不得不坚持使用matlab来完成这个任务... - Alceu Costa
由于16位比你的眼睛可以区分的灰度级别要多得多,因此你可以在Python中轻松将其即时转换为更常见的8位灰度图像,而不会有任何视觉损失。 - martineau

5
这是一个旧问题,但这可能会在未来帮助某人。原始代码片段的一个问题是,在Image.fromstring('L', imgSize, rawData, 'raw', 'F;16')中,F;16部分仅适用于'F'模式。
这对我有效:
image = Image.fromstring('F', imgSize, rawData, 'raw', 'F;16')
image.convert('L').save('out.png')

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