Search code examples
pythonimagepython-imaging-librarystringio

Create image from RGB list with Pillow and Python 3


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.


Solution

  • 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

    hue & saturation demo image