This part of the tutorial series will work out how to load an image to the nailboard created in How to create a nailboard – part 1 (creating the nails) so that it looks like in my video keep on nailin’:
As expected no project tooks the way you thought it will go when you started it. When i began to create the nailboard i worked with blender 2.49b. Now blender 2.5 is out and i thought it will be quite easy to port the scripts to the new version. You already guess it – this was not the case 😀 !
In blender 2.5 runs a python interpreter of the version 3.x your python scripts. In blender 2.49b this was done by a python interpreter version 2.x. And here comes the point where things get tricky: there is no “Python Image Library”-package for python 3.x. One can compile the source by himself and use it with his own python 3.x, but cause blender has its own python interpreter the including of your own PIL-compilation gets harder. Cause i want to keep things as simple as possible, i don’t want you to create your own blender build or get in touch with the ctypes-module… so we will go a little detour and have an easier way (we will use the old python version outside blender for preparing the image data and load it in blender to apply it to the nails). At the end of this part of the tutorial i will give you the 2.5 and the 2.49b files for comparison. The tutorial will stay at the blender version 2.5.
The last part of this tutorial series has focused on the creation of our nails. There every nail got an unique name. In this part we will discuss the scripts which load an image to the nailboard.
In a previous post i wrote about another (and faster) way to come to a nailboard. I will discuss the differences short at the end of this post.
As mentioned before we have to go a little detour. So my former blender 2.49b script is divided into two parts: one script to read the input image and one script to apply the gained information to our nailboard. The second script not only applies the height values to the several nails, it also saves them to the graphs, which where in former blender versions the ipo-curves.
As in the first tutorial part i will go from top to bottom through the scripts.
The script generator script
Let us begin with the main function “generateScript” of the generation script. Here it is:
# Script to generate the imageload
size = (102,76)
def generateScript( in_file, base_file, out_file, frame ):
base = file2string( base_file )
s = prepareImage( in_file, frame )
string2file( out_file, base + s )
generateScript( 'beaker_full.png', 'base.py', 'tmp.py', 1 )
As you can see our main function has four parameters: the name of the image, the name of the file in which the header for our loading sequence is placed, the name of our generated image loading script and the frame number where the image should appear. The first command reads, like the function name says, the base file to the string “base”. The second command reads the image and prepares a string with commands to set the nails. We will see this soon. The last command finally writes the concatenated strings to the given file. I will omit the file handling here to keep things short.
Now we come back to the function “prepareImage”. Here it is:
def prepareImage( name, frame ):
img = Image.open( name )
img.thumbnail( size )
s = ""
for x in range( 0, size ):
for y in range( 0, size ):
colour = getPixel( img, x, y )
s += prepareString( x, y, frame, colour )
The first two commands open the image file and scale it down to the previous defined dimensions “size”. This is quite useful, so we don’t have to scale for ourselves. I have chosen this dimensions (for the nailboard and the “size” variable) cause now we can simply take an image with 1024×768 pixels. I haven’t try what happens if you take an image with other dimensions, but i guess it will work too ;).
The function “prepareString” returns a string with a python command which looks like this:
setNail( 5, 5, 1, 0.329411764706)
Where the first two integers are the coordinates on the nailboard (here (5,5)), the third value is the frame in which the height value will be displayed (in a domain from zero upto one, where zero means the bottom value for a nail and one means the top value for a nail).
Let us now take a look on the results of the “getPixel” function:
def getPixel( img, x, y ):
colour = 0
if ( img.mode == 'P' ):
colour = float( img.getpixel( ( x, y ) ) )
elif ( img.mode == 'L' ):
colour = img.getpixel( ( x, y ) ) / float( 255 )
elif ( img.mode == 'RGB' ):
rgb = img.getpixel( ( x, y ) )
colour = ( rgb + rgb + rgb ) / float( 3*255 )
It is a case differentiation and scales the pixel values of the image to the domain between zero and one. The cases i implemented are the full colour, the greyscale and the black and white images. This is necessary if you want to use the script easily with arbitrary image types – here are three basic examples:
(175, 73, 5)
Applying the script
The generated script is written to a file with name “tmp.py”. To generate this script you need the base script, where the header with the functions for the generated script lies in. Open a shell and change to the directory where your image and your scripts are in. Then type
and hit enter. If you have not installed python 2.6, then install it by typing
sudo apt-get install python2.6
and run the script again. If all went well, you have now the file “tmp.py”. Test it by typing
to your shell.
The generated script
The last pieces of code i want to look at are parts of the generated script. You already know the functions “unselectAll”, “floatRange” and “moveToLayer” from the last part of this tutorial series. We have only to look at
def getNail( x, y ):
str_name = 'nail' + '_' + str(x) + '_' + str(y)
return bpy.data.objects[ str_name ]
def setNail( x, y, frame, height ):
max = 3.5
nail = getNail( x, y )
nail.location.z = height * max
nail.keyframe_insert( data_path="location", frame=frame, index=2)
# after this line the generated part starts
The generated part of the script is a list of calls of the “setNail” function. It takes the nailboard coordinates x and y, the frame number and the new height of the nail. The function “getNail” helps us to get the pointer to the correct nail. The value max is the maximum height of your nails. If you want to use another domain than 0..3,5 correct it here (also the lower limit could be moved at the position where “height * max” stands)! To end up with a positioned nail we set it by the “nail.location.z” value. Last, but nearly equally important, we set a new keyframe at our frame to the location.
Running the generated script
Open your blend file with your nailboard from the last part of this tutorial series. Then open a Text Editor and the generated script in it:
And finally run the script by hitting “Alt+P”. This will take a while…
Now you are able to load any image to your nailboard!
I assume this may be interesting points:
- as usual there are multiple ways to come to a good result (this one and the displacement method),
- both lead to displayed images,
- displacement method is faster realized,
- i have not compared running/rendering times,
- if you set two frames (at least two frames away from each other), with this method, then the blender internal graphs care for our interpolation,
- the latter point is important for more realistic movies,
- the displacement method leaves you alone with this (i haven’t tried to reach smooth movements by interpolation between two images with a video program – if you only want transfer two images, this will not be hard, but if you want to have smooth nail movements in a movie sequence then this could get harsh).
What i learned
- i will never start a tutorial before i’m shure everything works as expected ;),
- a very little bit of the new blender 2.5 python API and the new console,
- an important thing about blender: the python interpreter is hard coded into it,
- next time i animate something like a nailboard, i choose at first the music and then go and make an animation storyboard and finally
- sharing information is fun and makes you learn more about the systems you use!
Here you can download the two scripts:
I hope you had fun and learned something! If you liked this tutorial series or have any suggestions, hints and/or critiques, let me know it and write a comment and/or a mail!