使用Pillow和Python 3从RGB列表创建图像

5

我有一个RGB数据列表:

cdata=[R1, G1, B1, R2, G2, B2,..., Rn, Gn, Bn]

每个值都介于0到255之间。

我正在尝试使用Pillow 5.0.0将此数组重新构建为图像。 在Python 2下,我可以通过以下方式将值列表转换为字节字符串:

        cdata2 = []
        gidx = len(cdata)//3
        bidx = len(cdata)//3*2
        for i in range(len(cdata)//3):
            cdata2.append(cdata[i])
            cdata2.append(cdata[i+gidx])
            cdata2.append(cdata[i+bidx])

        data = ""
        for c in cdata2:
            data += chr(c)

        im = Image.frombytes("RGB", (420, 560), data)

然后将'im'重新编码为base64,并在HTML模板中以PNG格式显示。

不幸的是,在Python 3中无法实现此功能,我遇到了如下错误:

UnicodeEncodeError: 'charmap' codec can't encode characters in position 42099-42101: character maps to <undefined>

此外,Pillow 5文档现在建议使用:
im = Image.open(StringIO(data))

但我不能使其与我上面构建的字符串一起工作。有没有更聪明的方法来做这个?非常感谢您的帮助。

在Python 3中,普通字符串用于(Unicode)文本,不要尝试将它们用于二进制数据。相反,您应该使用bytearray或可能的bytes对象。Numpy是一个选项吗?为什么你的RGB值范围是0-254而不是0-255? - PM 2Ring
谢谢PM 2Ring。是的,它们确实是255,我会更正的。我可以使用Numpy。请问您如何编码RGB通道数据以便在Pillow中使用呢? - Zebulon
3个回答

6
这里有一个使用frombytes的例子。这仅仅是使用纯Python,没有Numpy。如果你正在使用Numpy创建RGB值,那么可以使用Image.fromarray方法将Numpy数据转换为PIL图像。
这里的重要步骤是将RGB值列表转换为bytes对象,只需将其传递给bytes构造函数即可轻松完成。
from colorsys import hsv_to_rgb
from PIL import Image

# Make some RGB values. 
# Cycle through hue vertically & saturation horizontally
colors = []
for hue in range(360):
    for sat in range(100):
        # Convert color from HSV to RGB
        rgb = hsv_to_rgb(hue/360, sat/100, 1)
        rgb = [int(0.5 + 255*u) for u in rgb]
        colors.extend(rgb)

# Convert list to bytes
colors = bytes(colors)
img = Image.frombytes('RGB', (100, 360), colors)
img.show()
img.save('hues.png')

输出

色相和饱和度演示图像


3
使用 Image.frombytes 方法。Image.open 用于打开编码图像(如 jpg 或 png),而非原始的 RGB 数据。
使用 bytes 构造函数可以轻松构建所需的字节数据。
img_bytes = bytes([R1, G1, B1, R2, G2, B2,..., Rn, Gn, Bn])

然后我们可以这样创建一个图像:

im = Image.frombytes("RGB", (width, height), img_bytes)

1
如果您想要编写既兼容Python2又兼容Python3的代码,您可以使用struct或array模块:
# Works from Python 2.5 (maybe earlier) to Python 3.x
import struct
cdata = [...]
bindata = struct.pack("<%dB" % len(cdata), *cdata)
# And then use PIL's Image.frombytes() to construct the Image() from bindata

或者:

import array
cdata = [...]
a = array.array("B", cdata)
bindata = a.tostring()
# And then use PIL's Image.frombytes() to construct the Image() from bindata
# This should be faster than struct, but I didn't test it for speed

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