Plotting image pixels with Python

This is a template script that creates a new image object and plots its pixels using Python.
Doing this with a script rather than the material or compositing nodes will give you absolute control over the result. You’re dealing with the raw pixels.

blenderProceduralImage

How to use

On a Text Editor view, press the New button to create a text object and then paste this code:

import bpy
from time import time
from itertools import chain


if __name__ == '__main__':

    # Image information. Change these to your liking.
    NAME   = 'Procedural Image'
    WIDTH  = 256
    HEIGHT = 256
    USE_ALPHA = True

    # Optional, delete the image if it already exists.
    oldImage = bpy.data.images.get(NAME, None)
    if oldImage:
        bpy.data.images.remove(oldImage)

    # Create a new image.
    newImage = bpy.data.images.new(NAME, WIDTH, HEIGHT, alpha=USE_ALPHA)

    # To use a preexisting image to sample from, uncomment the next line and the next function:
    #_otherImagePixels = bpy.data.images['My Other Image'].pixels


    #def getImagePixel(x, y):
    #    '''
    #    Example function to grab the color of a pixel from a preexisting Blender image.
    #
    #    :param x: The X coordinate of the pixel to grab, starting from the left (0) to the right (last value is WIDTH - 1).
    #    :param y: The Y coordinate of the pixel to grab, starting from the bottom (0) to the top (last value is HEIGHT - 1).
    #    :returns: The RGBA colour of the pixel as a list of 4 floats with values from 0.0 to 1.0.
    #    '''
    #    index = (x + (y * WIDTH)) * 4
    #    return _otherImagePixels[index : index+4]


    def generatePixels():
        for y in range(HEIGHT):
            for x in range(WIDTH):
                # Generate a color for the pixel at (x, y).
                # This is the OpenGL coordinate system, so (0, 0) is the bottom-left corner of the image
                # and (WIDTH-1, HEIGHT-1) is the top-right corner of the image.
                red = x / (WIDTH-1)
                green = y / (HEIGHT-1)
                blue = 0.0
                alpha = 1.0
                yield red, green, blue, alpha


    start = time()
    
    newImage.pixels = tuple(chain.from_iterable(generatePixels()))
    newImage.update()
    
    print('TIME TAKEN: %f seconds' % (time() - start)) # Outputs to the system console.

    # Make all UV/Image Editor views show the new image.
    for area in bpy.context.screen.areas:
        if area.type == 'IMAGE_EDITOR':
            for space in area.spaces:
                if space.type == 'IMAGE_EDITOR':
                    space.image = newImage

    # All done.

Then press the Run Script button in the Text Editor header. It should create a new image that’s coloured based on a simple formula.

You need to modify the code in the generatePixels() function to do what you want. Note that if you create images larger than 4096px the script might take a while to finish, like from 30 seconds up depending on your CPU.

4 Likes

if you run it from script editor
this will create a new image in the UV editor

right now get a black image !

still need to save it manually !

will this run/ work in 2.8 too ?

thanks
happy bl

Hi, you can do an image check like so and overwrite safely.

# find and use existing image else create a new one
if "Procedural Image.exr" not in bpy.data.images.keys():	
    bpy.data.images.new("Procedural Image.exr", width=256, height=256, alpha=True, float_buffer=True)

#write to image stuff etc

Numpy could yield a speed up too. A similar example https://blender.stackexchange.com/questions/643/is-it-possible-to-create-image-data-and-save-to-a-file-from-a-script

1 Like

Any image that you create in a script will have the “Generated” type by default. You need to pack it in the .blend file or use the Image > Save As Image (F3) menu to save it to an external file.

If you just save your .blend without doing any of that, the next time you open your scene that image will reset to its default colour (black in this case).

but when you run your script does it make some color image
or need to be modified to do some pattern ?

thanks
happy bl

@LoboTommy thanks for the comments.
Since bpy.data.images is like an ordered-dict, you don’t need to use the .keys() to see if a key is inside it, you can use the “in” operator directly with it:
if 'Procedural Image.exr' not in bpy.data.images:

But in any case I think it’s better to always delete and recreate the image: what if you want to use the same name, but a different image size? You would end up reusing the image but plotting outside of it if you reused an image but set a bigger width or height.

I did try NumPy but I did not find a speed increase – it was slower in fact. I think it would be faster if you had the array ready and then needed to do an operation on all pixels at once (like an image filter), instead of generating each pixel individually like we’re doing here.
That stackexchange example you pointed to doesn’t use NumPy anyway, just pure Python.

From my tests I found my template script was faster than that one because of two reasons:

  • The pixels are created in a generator function (the yield keyword) instead of in a temporary list.
  • It uses itertools.chain.from_iterable to flatten the pixel list, it’s a bit faster than using something like a list comprehension.
1 Like

It makes that red-green image from the first post, but just as an example.
You need to modifiy the code inside the “generatePixels” function to do what you want.

For example, replace the alpha = line with this…
alpha = 1.0 * ((x % 64 < 32) if (y % 64 < 32) else (x % 64 > 31))

…to make the alpha channel have a checkered pattern:

blenderProceduralImageCheckered

Which is fun and all, but when talking about production use, maybe you need a data-texture to use in a shader effect for a game, like a special algorithmic gradient or something like that. You can write the formula for it in script and generate an image this way.

1 Like