0

I attempted today to create a Python code to invert the colors of a .gif image using the Tkinter library. The code works and does exactly what I expected, but it takes about 50 seconds to run on a 3.4ghz processor. I'm having trouble seeing what I could change to optimize this. Basically, I loop through every pixel in the image , grab the color values, convert them to a list of integers (in order to manipulate them mathematically), invert each color value (new color value = 255 - old color value), convert them back to a string so that PhotoImage's "put" method can process them and rewrite the image, and finally display the inverted image. I can't think of what to change. I mean, looping through every single pixel must be a part of the slowness, but isn't this process completely necessary?

from Tkinter import *
import tkMessageBox

class GUIFramework(Frame):
    def __init__(self, master=None):

    Frame.__init__(self, master)

    self.grid(padx=0, pady=0)
    self.btnDisplay = Button(self, text="Display!", command=self.Display)
    self.btnDisplay.grid(row=0, column=0)

    def Display(self):
        a = ''
        b = []
        self.imageDisplay = PhotoImage(file='C:\image.gif')
        for x in range(0, self.imageDisplay.width()):
            for y in range(0, self.imageDisplay.height()):
                value = self.imageDisplay.get(x,y)
                for i in value:
                    try:
                        c = int(i)
                        a += (i)
                    except:
                        b.append(int(a))
                        a = ''
                b.append(int(a))
                for i in range(0,3):
                    b[i] = (255 - b[i])
                self.imageDisplay.put('#%02x%02x%02x' %tuple(b), (x,y))
                b = []
                a = ''
        c = Canvas(self, width=700, height=700); c.pack()
        c.grid(padx=0,pady=0, column=0, row=0)
        c.create_image(0,0, image = self.imageDisplay, anchor = NW)

if __name__ == "__main__":
    guiFrame = GUIFramework()
    guiFrame.mainloop()

Thanks in advance for your help. -Seth

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
Mortimer McMire
  • 330
  • 2
  • 5
  • 16
  • Would you mind posting the contents of `value`? It will help see what can be improved, hopefully. – TorelTwiddler Oct 13 '11 at 04:45
  • Ah, yes. PhotoImage's get method returns a string that looks like this: '255 85 0'. It's just the RGB values with spaces in between. Seems quite unnecessary to give those as a string, but whatever. – Mortimer McMire Oct 13 '11 at 05:11

1 Answers1

4

Try this:

def Display(self):
    self.imageDisplay = PhotoImage(file='C:\image.gif')
    for x in xrange(0, self.imageDisplay.width()):
        for y in xrange(0, self.imageDisplay.height()):
            raw = self.imageDisplay.get(x, y)
            rgb = tuple(255 - int(component) for component in raw.split())
            self.imageDisplay.put('#%02x%02x%02x' % rgb, (x, y))
    c = Canvas(self, width=700, height=700); c.pack()
    c.grid(padx=0,pady=0, column=0, row=0)
    c.create_image(0,0, image = self.imageDisplay, anchor = NW)

Edit: New version (faster and with optimizations from Justin Peel).

def Display(self):
    self.imageDisplay = PhotoImage(file='C:\image.gif')

    wrange = xrange(0, self.imageDisplay.width())
    hrange = xrange(0, self.imageDisplay.height())
    get = self.imageDisplay.get
    put = self.imageDisplay.put

    def convert_pixel(raw):
        return ('#%02x%02x%02x' %
            tuple(255 - int(component) for component in raw.split(' ')))

    for y in hrange:
        put('{' + ' '.join(convert_pixel(get(x, y)) for x in wrange) + '}', (0, y))

    c = Canvas(self, width=700, height=700);
    c.pack()
    c.grid(padx=0, pady=0, column=0, row=0)
    c.create_image(0, 0, image=self.imageDisplay, anchor=NW)
refaim
  • 1,875
  • 1
  • 14
  • 9
  • 1
    Yes, just was about to post mine which looks almost the same. Last small optimizations are to make variables for the height, the get and put functions, and the format string. Also, raw.split(' ') is slightly faster than raw.split(). However, these are quite small optimizations at this point. Most of the rest of the time is spent in the get and put functions and doing the string formatting. – Justin Peel Oct 13 '11 at 05:01
  • This really does cut it down quite a bit, and it's very elegant. Takes about 10 seconds now. Thanks, guys! What I don't understand, though, is how standard image editing applications can do this in a matter of seconds. Where is the issue here? – Mortimer McMire Oct 13 '11 at 13:01