Grayscale Anglegraph

2013-10-05 20.40.43
I am a big fan of Daniel Rozin’s art, and while looking at his Wooden Mirror (http://www.youtube.com/watch?v=BZysu9QcceM) I though it might be fun to see if I could make static images using a similar visual technique. In the Wooden Mirror, Rozin assembled 835 little wooden tiles onto 835 little servo motors.  Using a video camera a computer translates what it see into rotations for the servos. Each wooden tile essentially becomes a pixel in the larger image. The wooden tile can be tilted up to catch the light and become white or can be tilted down become black by being shadowed. I figured I could make a static image by creating solid geometric model on the computer and 3d printing it.  Here is how I went about it.

In the past I probably would have chosen to do this project in a stand alone c++ or c# app. This time around, I decided to write a python script for Blender which is a great open source 3d creation tool that is cross platform compatible. I decided to go this route because it meant that while testing the geometry I could quickly simulate real world lighting on the object and see if this would actually work the way I thought it would. Also, in the end I wanted to be able to 3d print the object so having Blender’s export capabilities available is nice. As an added bonus the code is nice and portable between operating systems and you don’t have to deal with getting a project setup and linking properly (which I always find to be very bloody painful)

The code is pretty simple and consists of just a few steps:  Loop across the pixels in the image to create column paths, extrude path to get some depth, then convert the paths to meshes and join them into one big happy mesh.

To get started here is the function that I pieced together that takes in a list of points and creates a solid shape


def MakePolyLine(objname, curvename, cList):  
    curvedata = bpy.data.curves.new(name=curvename, type='CURVE')  
    curvedata.dimensions = '2D'  

    objectdata = bpy.data.objects.new(objname, curvedata)  
    objectdata.location = (0,0,0) #object origin  
    bpy.context.scene.objects.link(objectdata)

    polyline = curvedata.splines.new('POLY')  
    polyline.points.add(len(cList)-1)  
    for num in range(len(cList)):  
        polyline.points[num].co = (cList[num])+(w,)  

    polyline.order_u = len(polyline.points)-1
    polyline.use_endpoint_u = True
    polyline.use_cyclic_u = True
    curvedata.extrude = 0.5

    bpy.context.scene.objects.active = objectdata
    objectdata.select = True
    bpy.ops.object.shade_flat()
    bpy.ops.object.convert()

In Blender terms, this code creates a new 2d polyline path object from the points. Then extrudes it to give it some depth. Lastly it converts the path to a mesh and sets the shading to flat. That last bit about turning the shading flat is important for rendering properly. Otherwise Blender tries to smooth the lighting across the polygons to pretend like they are all one smooth shape.  This can be pretty nifty most of the time, but just screws . Alright that’s the boring stuff out of the way. Now lets look at how to convert and image into the cool geometry.


image = bpy.data.images["SphereMan.jpg"] #Get a reference to the image to load
p = image.pixels #put a reference to the pixels in a short variable name so we don't have to type as much
width = image.size[0]
height = image.size[1]
c = 0

#The image format is weird. Tt appears to be RGBA blocks of floats starting in the lower left moving to the right. Resets to the left and moves up one

for x in range(width): #Loop over the columns of pixels in the image
    points = [] #Make an array to hold the points of path
    points.append((-2, 0, x)) #Start the path
    for y in range(height): #Loop over all the individual pixels of the column
        v = p[(height - 1 - y) * width * 4 + x * 4] #gets the value of the pixel at the given X and Y position
        v = (v - 1) * 2 #scale the value to make it fit
        points.append((0, y, x))   #add a point at the start of the panel
        points.append((v, y+1, x)) #add a point at the end of the panel

    points.append((-2, height - 1, x)) #Tack on the last point to finish the path

    MakePolyLine("AngleSet{}".format(x), "GraphCurve", points)

To loop over the pixels in the image you have to understand the arrangement of data in the pixel array. After some quick experimenting I found that Blender (at least in this case) arranges the pixels in blocks of 4 floats ranging from 0 to 1. The first float is the red component, the second is blue, followed by the green. The last float is the alpha or transparency value (that we don’t care about here. In fact for our purposes here we only care about the first value in the block because we are assuming that the image is grayscale and the green and blue values match the red. The first block of floats is the pixel in the bottom left corner of the image. The second block is the pixel immediately to the right of the first one. The blocks continue to the right until you hit the right edge. Then it goes up one row and starts over at the left. Its like reading a book from the bottom up. Its a bit weird but manageable once you know what its doing. The code here loops over the columns of pixels from left to right in the outer loop and loops over the individual pixels from top to bottom in the inner loop and just computes the offset at it rolls through it.

This creates a sawtooth path for each column of pixels in the image. A fixed frequency, variable amplitude, peak aligned saw tooth wave, so to speak. Do this for each column and you get the image. Or rather do this and you get an image on the screen.  If you want to get the image into the real world just export to .STL and 3d print. (Well actually I had to run it through FabLab to fix some problems with the mesh (Blender seems to suck at converting paths to meshes), but we are going to look the other way on that)

About these ads
Tagged , ,

3 thoughts on “Grayscale Anglegraph

  1. […] Check out Matt Bell’s Blender hacking to create this Grayscale Anglegraph: […]

  2. […] Check out Matt Bell’s Blender hacking to create this Grayscale Anglegraph: […]

  3. […] Matt Bell shared his Blender + python hacking to create this Grayscale Anglegraph: “In the past I probably would have chosen to do this project in a stand alone c++ or c# app. This time around, I decided to write a python script for Blender which is a great open source 3d creation tool that is cross platform compatible. I decided to go this route because it meant that while testing the geometry I could quickly simulate real world lighting on the object and see if this would actually work the way I thought it would. Also, in the end I wanted to be able to 3d print the object so having Blender’s export capabilities available is nice. As an added bonus the code is nice and portable between operating systems and you don’t have to deal with getting a project setup and linking properly (which I always find to be very bloody painful) The code is pretty simple and consists of just a few steps: Loop across the pixels in the image to create column paths, extrude path to get some depth, then convert the paths to meshes and join them into one big happy mesh….” (read more) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 27 other followers

%d bloggers like this: