views:

77

answers:

2

Hello stackoverflow,

I have a python function that takes a string s-expression like "(add (sub 10 5) 5)", where "add" and "sub" are actually image processing functions, and evaluates and creates the image represented in the string. The image processing functions take constants, variables, or other images (represented as lists of vectors), and return images represented the same way. PIL is used to convert the image represented as a list of vectors into an image file.

To evaluate the prefix notation s-expressions, I convert the s-expr to a list, reverse it, and iterate the tokens until a function is found, at which point the image processing function is performed and the resulting image replaces the function and its arguments in the list. This is done until only one element is left in the list, which is the final image.

The image processing functions are simple- most perform some mathematical operation to each of the (r,g,b) values in the image(s).

The problem is my computer comes to a standstill if I want to make decent sized images for more complex expressions. Can this be optimised to use less memory?

def createImage(self, sexpr, filename, (picWidth, picHeight)):
    """Use the image processing functions in ImgProcessing
       to create an image from the procedural information
       contained in the s-expression."""

    img = Image.new("RGB",(picWidth,picHeight),(255,255,255))
    ip = ImgProcessing(picWidth,picHeight)

    # Split s-expression into list of tokens and reverse
    sList = sexpr.replace("(","").replace(")","").split()
    sList.reverse()

    while len(sList) > 1:

        for index,token in enumerate(sList):
            # If token is an image processing function
            if type(token) == str and self.FuncSet.has_key(token):
                # If this function takes one argument
                if self.FuncSet[token] == 1:
                    rawImage = eval("ip." + token + "(" + "\"" + str(sList[index-1]) +
                                    "\"" + ")")
                    sList = sList[:index-1] + [rawImage] + sList[index+1:]
                    break
                # If this function takes two arguments
                elif self.FuncSet[token] == 2:
                    rawImage = eval("ip." + token + "(" + "\"" + str(sList[index-1]) +
                                    "\"" + "," + "\"" + str(sList[index-2]) + "\"" +
                                    ")")
                    sList = sList[:index-2] + [rawImage] + sList[index+1:]
                    break

    img.putdata(sList[0])
    img.save(filename)
+2  A: 

Profiling can tell you where the program is spending most of its time.

Second, is str(sList[index-1]) converting an Image into a string? Does ip.token(...) return an Image? If so, you are converting between string and Image a number of times. That might be very slow.

It might help to change

rawImage = eval("ip." + token + "(" + "\"" + str(sList[index-1]) +
                                    "\"" + ")")

to something like

getattr(ip,token)(sList[index-1])

but of course this depends on what type of argument ip.token expects. I couldn't find any information on ImgProcessing from Googling. Is this a custom class? If so, it might help to explain more about how it works. If ip.token can be changed from taking strings to taking Images, that might be a big improvement.

unutbu
A: 

In my experience, anything that you do in pure Python or PIL pixel-by-pixel on a large image is going to be slow as molasses in January. Consider moving the low-level stuff into a Python extension written in C. I have used OpenCV, but it takes some learning.

Jive Dadson
I don't think jumping into C is the right answer, until you have profiled and optimized your Python code as best as you can. In my opinion, jumping to a lower-level language early is just another form of premature optimization.
Thomas Owens