Reputation: 53
I have a list of RGB data:
cdata=[R1, G1, B1, R2, G2, B2,..., Rn, Gn, Bn]
where each value is comprised between 0 to 255.
I am trying to rebuild this array as an image using Pillow 5.0.0. Under Python 2, I was able to convert the list of values into a byte string this way:
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)
and then reincode 'im' in base64 and display it as a PNG in an HTML template.
Unfortunately this does not work in Python 3, I am having errors like:
UnicodeEncodeError: 'charmap' codec can't encode characters in position 42099-42101: character maps to <undefined>
Furthermore, Pillow 5 documentation now suggests using
im = Image.open(StringIO(data))
but cannot make it work with my string built above. Is there any more clever way to do this? Thanks a lot in advance for your help.
Upvotes: 5
Views: 12273
Reputation: 4236
If you want to have nice Python2 and Python3 compatible code you can use struct or array module:
# 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
Or:
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
Upvotes: 1
Reputation: 55469
Here's an example using frombytes
. This is just using pure Python, no Numpy. If you're using Numpy to create the RGB values then you can use the Image.fromarray
method to convert the Numpy data into a PIL Image.
The important step here is to convert the list of RGB values into a bytes
object, which is easily done by passing it to the bytes
constructor.
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')
output
Upvotes: 6
Reputation: 43126
Use Image.frombytes
. Image.open
is intended for opening encoded images (like jpg or png), not raw RGB data.
Constructing the required byte data is trivial with the bytes
constructor:
img_bytes = bytes([R1, G1, B1, R2, G2, B2,..., Rn, Gn, Bn])
And then we can create an Image like so:
im = Image.frombytes("RGB", (width, height), img_bytes)
Upvotes: 4