Featured post

new redirect for blender.org bpy docs.

http://www.blender.org/api/blender_python_api_current/ As of 10/11 november 2015 we can now link to the current api docs and not be worr...

August 01, 2012

Adjusting image pixels internally in Blender with bpy

The naive approach

Tasked with representing some big data, let's see if blender can handle it. Here is some exploring first.
import bpy
D = bpy.data
# BlendDataImages.new(name, width, height, alpha=False, float_buffer=False)
image_object = D.images.new(name='pixeltest', width=4000, height=3000)
# image_object points to the newly created: bpy.data.images['pixeltest']
num_pixels = len(image_object.pixels)
# well, turns out pixels is a little bit of a misnomer.
print(num_pixels)
# >>> 480000
# A number 4 timeslarger than the expected 120000 pixels. It appears that
# .pixels is a list of the RGBA values of every pixel in sequence.
print( bpy.data.images['pixeltest'].file_format )
# >>> 'TARGA'
# seems to be the internal default
print(image_object.pixels[:4])
# >>> (0.0, 0.0, 0.0, 1.0)
# drawing a pixel, changing pixel content
for i in range(4):
image_object.pixels[0+i] = (1.0)
view raw tmp.py hosted with ❤ by GitHub
adjusting the pixel on the last two lines above takes the most time. This image shows what the result is, zoomed in.


baby steps

This is relatively fast, but it's only 120 pixels in total. Try changing to 400*300 and you can expect it to take a lot longer, far too long to scale for big data.
import bpy
D = bpy.data
image_object = D.images.new(name='pixeltest', width=40, height=30)
num_pixels = len(image_object.pixels)
# drawing a pixel, changing pixel content
for px in range(0, num_pixels-5, 12):
for i in range(4):
image_object.pixels[px+i] = (1.0)
view raw pixmod_01.py hosted with ❤ by GitHub


import bpy
import random
D = bpy.data
image_object = D.images.new(name='pixeltest', width=40, height=30)
num_pixels = len(image_object.pixels)
# drawing a pixel, changing pixel content
for px in range(0, num_pixels-5, 12):
r = random.random()
g = random.random()
b = random.random()
a = (1.0)
cols = (r,g,b,a)
for i in range(4):
image_object.pixels[px+i] = cols[i]
view raw pixmod_02.py hosted with ❤ by GitHub
results in something profoundly uninteresting
If that isn't a good method, then perhaps construct the data and overwrite image_object in one go. You'll probably want to make sure the dimensions make sense.

What we know - end of naive

>>> bpy.data.images['pixeltest'].pixels.__class__
#<class 'bpy_prop_array'>
view raw pixel_array.py hosted with ❤ by GitHub
with a 40*30 image, i don't expect to notice much time difference but i'll know if the operation is possible.
dm = [(1.0) for i in range(4800)]
bpy.data.images['pixeltest'].pixels = dm
# turns them all white, so maybe try constructing the array first, then assigning.
This leads to a much faster way of pushing pixels. First create the image, then the array, then modify the array, then overwrite the image with the array data. The snippet below overwrites with a dark gray.
import bpy
import random
D = bpy.data
image_name = 'pixeltest2'
if not image_name in D.images:
image_object = D.images.new(name=image_name, width=400, height=300)
image_object = D.images[image_name]
num_pixels = len(image_object.pixels)
dm = [(0.2) for cp in range(num_pixels)]
image_object.pixels = dm


And it seems that the speed is now closer to acceptable, here is a version that does a 400*300 px overwrite.
import bpy
import random
D = bpy.data
image_name = 'pixeltest3'
if not image_name in D.images:
image_object = D.images.new(name=image_name, width=400, height=300)
image_object = D.images[image_name]
num_pixels = len(image_object.pixels)
dm = [(0.2) for cp in range(num_pixels)]
for i in range(0,len(dm),16):
for j in range(4):
dm[i+j] = (1.0)
image_object.pixels = dm
4000*3000 will still take 10 seconds or so (on 2.4ghz 2core) but that's not too bad.

Great, what good is that to me?

I can think of a few applications but Firefly removal would be a top option. Find outliers and average the px values with the surrounding pixels

Finished firefly removal script can be found here: link