What is the fastest way to draw an image from discrete pixel values in Python?

I wish to draw an image based on computed pixel values, as a means to visualize some data. Essentially, I wish to take a 2-dimensional matrix of color triplets and render it.

Do note that this is not image processing, since I'm not transforming an existing image nor doing any sort of whole-image transformations, and it's also not vector graphics as there is no pre-determined structure to the image I'm rendering- I'm probably going to be producing amorphous blobs of color one pixel at a time.

I need to render images about 1kx1k pixels for now, but something scalable would be useful. Final target format is PNG or any other lossless format.

I've been using PIL at the moment via ImageDraw's draw.point , and I was wondering, given the very specific and relatively basic features I require, is there any faster library available?


Solution 1:

If you have numpy and scipy available (and if you are manipulating large arrays in Python, I would recommend them), then the scipy.misc.pilutil.toimage function is very handy. A simple example:

import numpy as np
import scipy.misc as smp

# Create a 1024x1024x3 array of 8 bit unsigned integers
data = np.zeros( (1024,1024,3), dtype=np.uint8 )

data[512,512] = [254,0,0]       # Makes the middle pixel red
data[512,513] = [0,0,255]       # Makes the next pixel blue

img = smp.toimage( data )       # Create a PIL image
img.show()                      # View in default viewer

The nice thing is toimage copes with different data types very well, so a 2D array of floating-point numbers gets sensibly converted to grayscale etc.

You can download numpy and scipy from here. Or using pip:

pip install numpy scipy

Solution 2:

import Image
im= Image.new('RGB', (1024, 1024))
im.putdata([(255,0,0), (0,255,0), (0,0,255)])
im.save('test.png')

Puts a red, green and blue pixel in the top-left of the image.

im.fromstring() is faster still if you prefer to deal with byte values.

Solution 3:

Requirements

For this example, install Numpy and Pillow.

Example

The goal is to first represent the image you want to create as an array arrays of sets of 3 (RGB) numbers - use Numpy's array(), for performance and simplicity:

import numpy

data = numpy.zeros((1024, 1024, 3), dtype=numpy.uint8)

Now, set the middle 3 pixels' RGB values to red, green, and blue:

data[512, 511] = [255, 0, 0]
data[512, 512] = [0, 255, 0]
data[512, 513] = [0, 0, 255]

Then, use Pillow's Image.fromarray() to generate an Image from the array:

from PIL import Image

image = Image.fromarray(data)

Now, "show" the image (on OS X, this will open it as a temp-file in Preview):

image.show()

Note

This answer was inspired by BADCODE's answer, which was too out of date to use and too different to simply update without completely rewriting.

Solution 4:

A different approach is to use Pyxel, an open source implementation of the TIC-80 API in Python3 (TIC-80 is the open source PICO-8).

Here's a complete app that just draws one yellow pixel on a black background:

import pyxel

def update():

    """This function just maps the Q key to `pyxel.quit`,
    which works just like `sys.exit`."""

    if pyxel.btnp(pyxel.KEY_Q): pyxel.quit()

def draw():

    """This function clears the screen and draws a single
    pixel, whenever the buffer needs updating. Note that
    colors are specified as palette indexes (0-15)."""

    pyxel.cls(0)            # clear screen (color)
    pyxel.pix(10, 10, 10)   # blit a pixel (x, y, color)

pyxel.init(160, 120)        # initilize gui (width, height)
pyxel.run(update, draw)     # run the game  (*callbacks)

Note: The library only allows for up to sixteen colors, but you can change which colors, and you could probably get it to support more without too much work.

Solution 5:

I think you use PIL to generate an image file on the disk, and you later load it with an image reader software.

You should get a small speed improvement by rendering directly the picture in memory (you will save the cost of writing the image on the disk and then re-loading it). Have a look at this thread https://stackoverflow.com/questions/326300/python-best-library-for-drawing for how to render that image with various python modules.

I would personally try wxpython and the dc.DrawBitmap function. If you use such a module rather than an external image reader you will have many benefits:

  • speed
  • you will be able to create an interactive user interface with buttons for parameters.
  • you will be able to easily program a Zoomin and Zoomout function
  • you will be able to plot the image as you compute it, which can be quite useful if the computation takes a lot of time