Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Slow speed of canvas PhotoImage pixel display...

2,191 views
Skip to first unread message

David Sheets

unread,
Sep 29, 1998, 3:00:00 AM9/29/98
to

Hello, I am trying to use either Python or Tcl to make a GUI wrapper
for some C functions. When it is all put together, I hope to have the
GUI give a nice display of some math functions calculated in the C code.
However, I have run into problems with the pixel display speed using the
canvas widget in Tk. Using Tcl, I got about 10,000 pixels in 10 seconds
(give or take), which was a bit too slow. So I tried doing the same
thing in Python, still using Tk though, and got a slightly better time
(8 seconds of CPU time). I was hoping someone might be able to recommend
another GUI library that might be faster than Tk and still portable
between Windows 95 and X. Apart from that, I'm not quite sure what would
be best to speed up the display of pixels. I searched the archives of
this newsgroup and got a similar question which was answered by Guido.
He gave the suggestion of cutting out the middle-routine by explicitely
calling the _tkinter functions. I tried doing this, but didn't give as
much improvement as I would have hoped for. I'm afraid that I will just
have to deal with the speed issue if I want to stick with the portable
GUI. :(
But, any help or advice would be appreciated. Also, for your
reference, and so you can belittle my 1st Python program, my code for
testing the pixel display speed follows:


from Tkinter import *
class App:

def __init__(self, master):

frame = Frame(master)
frame.pack()
self.button = Button(frame, text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.test_B = Button(frame, text="Test", command=self.test)
self.test_B.pack(side=LEFT)
self.img = PhotoImage(height=100, width=100)
self.canvas = Canvas(frame, width=100, height=100)
self.canvas.create_image(0, 0, anchor=NW, image=self.img)
self.canvas.pack()

def test(self):
import time
max_x = max_y = 100
start = time.time()
start_cpu = time.clock()

for x in range(max_x):
for y in range(max_y):
self.img.put("black", (x,y))

elapsed = time.time() - start
elapsed_cpu = time.clock() - start_cpu

print "done -- it took us: "
print elapsed
print elapsed_cpu

root = Tk()
app = App(root)
root.mainloop()

David Ascher

unread,
Sep 29, 1998, 3:00:00 AM9/29/98
to
> Hello, I am trying to use either Python or Tcl to make a GUI wrapper
> for some C functions. When it is all put together, I hope to have the
> GUI give a nice display of some math functions calculated in the C code.
> However, I have run into problems with the pixel display speed using the
> canvas widget in Tk. Using Tcl, I got about 10,000 pixels in 10 seconds
> (give or take), which was a bit too slow. So I tried doing the same
> thing in Python, still using Tk though, and got a slightly better time
> (8 seconds of CPU time). I was hoping someone might be able to recommend
> another GUI library that might be faster than Tk and still portable
> between Windows 95 and X. Apart from that, I'm not quite sure what would
> be best to speed up the display of pixels. I searched the archives of
> this newsgroup and got a similar question which was answered by Guido.
> He gave the suggestion of cutting out the middle-routine by explicitely
> calling the _tkinter functions. I tried doing this, but didn't give as
> much improvement as I would have hoped for. I'm afraid that I will just
> have to deal with the speed issue if I want to stick with the portable
> GUI. :(

I've written something to do among other things what you describe, which I
use daily. It is based on Python, Tk, PyOpenGL and NumPy. It's plenty
fast. I do plan on releasing it at some point, but not for a while yet --
the framework is still undergoing radical changes every month or so. More
practically, my suggestion would be to use OpenGL and PyOpenGL -- it's
portable across lots of platforms (using Mesa on Unixen w/o native
OpenGL). I'd also recommend using NumPy arrays to pass the data between
the C code and the PyOpenGL routines for maximal throughput. The only
problem with this solution is that Togl (Tk-OpenGL) widgets don't interact
great with other Tk widgets (can't put one in a Canvas, for example, or
put a Label on one).

--david

http://starship.skyport.net/~da/PyOpenGL


Charles G Waldman

unread,
Sep 29, 1998, 3:00:00 AM9/29/98
to

If you are interested in cross-platform GUIs, there really isn't much
of an alternative to Tkinter. It's not perfect, but it's the best we have.
But you *can* get reasonable performance on pixel operations.

You would benefit greatly from the Python Imaging Library. See
http://www.pythonware.com, or the Imaging-SIG page on www.python.org,
for more on PIL.

I modified your test program to use a PIL Image object instead of the
Tkinter Image object. The wall-clock time went from 26 seconds to
0.8 seconds. (This is on a dual-processor P200 with 128MB RAM,
running Linux 2.0.34)

The "putpixel" method in the Imaging library is clearly a lot faster
than the "put" method in the Tkinter Image object.

If you need even more speed, you can convert the PIL Image object into
a Numeric Python array, which then can be accessed efficiently with
x,y indexing. I can post an example of how to do this if anyone is
interested.


Here's my modified version of your test program:

from Tkinter import *
from ImageTk import PhotoImage
import Image

class App:

def __init__(self, master):

frame = Frame(master)
frame.pack()
self.button = Button(frame, text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.test_B = Button(frame, text="Test", command=self.test)
self.test_B.pack(side=LEFT)

self.img = Image.new("L",(100,100))
self.photo = PhotoImage(image=self.img)


self.canvas = Canvas(frame, width=100, height=100)

self.canvas.create_image(0, 0, anchor=NW, image=self.photo)
self.canvas.pack()

def test(self):
import time
max_x = max_y = 100
start = time.time()
start_cpu = time.clock()

for x in range(max_x):
for y in range(max_y):

self.img.putpixel((x,y),255)
self.photo = PhotoImage(self.img)
self.canvas.create_image(0, 0, anchor=NW, image=self.photo)
self.canvas.update()

Fredrik Lundh

unread,
Sep 29, 1998, 3:00:00 AM9/29/98
to
> Hello, I am trying to use either Python or Tcl to make a GUI wrapper
>for some C functions. When it is all put together, I hope to have the
>GUI give a nice display of some math functions calculated in the C code.
>However, I have run into problems with the pixel display speed using the
>canvas widget in Tk. Using Tcl, I got about 10,000 pixels in 10 seconds
>(give or take), which was a bit too slow. So I tried doing the same
>thing in Python, still using Tk though, and got a slightly better time
>(8 seconds of CPU time

On my PII 266, your benchmark takes about 6.5 seconds.

1) remove the image from the canvas while drawing the
pixels. drawing in a visible image is much slower, since
Tk has to tell the canvas to redraw.

On my box, this brings the time down to 3 seconds.

2) draw the pixels in a PIL Image object, and use the ImageTk
extension to display that image. You can get the PIL sources
via:

http://www.pythonware.com/products/pil/

On my PII 266, the attached script needs about 0.25 seconds
to update the display.

Cheers /F
fre...@pythonware.com
http://www.pythonware.com

from Tkinter import *

import Image
from ImageTk import PhotoImage

class App:

def __init__(self, master):

frame = Frame(master)
frame.pack()
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=LEFT)
self.test_B = Button(frame, text="Test", command=self.test)
self.test_B.pack(side=LEFT)

self.canvas = Canvas(frame, width=100, height=100)

self.canvas.pack()

def test(self):
import time
max_x = max_y = 100
start = time.time()
start_cpu = time.clock()

image = Image.new("L", (100, 100))
put = image.putpixel

for x in range(max_x):
for y in range(max_y):

put((x, y), 0)

self.img = PhotoImage(image)
self.canvas.create_image(0, 0, anchor=NW, image=self.img)

Cameron Laird

unread,
Sep 30, 1998, 3:00:00 AM9/30/98
to
In article <x43e9at...@janus.pgt.com>,

Charles G Waldman <c...@pgt.com> wrote:
>
>If you are interested in cross-platform GUIs, there really isn't much
>of an alternative to Tkinter. It's not perfect, but it's the best we have.
>But you *can* get reasonable performance on pixel operations.
>
>You would benefit greatly from the Python Imaging Library. See
.
.
.
There *are* other approaches to GUI with Python <URL:http://
starbase.neosoft.com/~claird/comp.lang.python/python_GUI.html>.
I agree with your conclusions about PIL and Tkinter.

Tk canvases perform quite zippily for what they were designed
to do (make sure you learn about tags). They don't shine at
pixel operations, though.
--

Cameron Laird http://starbase.neosoft.com/~claird/home.html
cla...@NeoSoft.com +1 281 996 8546 FAX

Kenneth Gosier

unread,
Oct 1, 1998, 3:00:00 AM10/1/98
to
Well, I read through the PIL stuff and it looked like something that
would be very useful. So, I installed the package onto the Windows
machine I am using and tried to run your modified version of my sample
program, but I got the following error when I clicked the test button:

> Exception in Tkinter callback
> Traceback (innermost last):
> File "C:\PY15\lib\lib-tk\Tkinter.py", line 752, in __call__
> return apply(self.func, args)
> File "piltest.py", line 29, in test
> self.img.putpixel((x,y))
> AttributeError: putpixel
>
I assumed this meant that I had installed PIL incorrectly, but all
the scripts that came with PIL ran perfectly. I was going to try
installing PIL onto the DEC Alpha I use, but that machine is down it
seems. So, I looked through the Image.py and ImageTk.py that come with
PIL for any mention of a putpixel method, but I couldn't find it, or
anything else that looked like it was made for putting single pixels to
an Image object. I'm not sure if I've missed something or what. If
someone might be able to help with this I would appreciate it.
Also, I might be interested in using Numeric Python, if that would
provide a faster speed. But how involved would it be to convert from PIL
to the N.P. array? I am completely new to Python, so I'm not exactly
sure what is involved.
Thanks again for any help
Charles G Waldman wrote:

> You would benefit greatly from the Python Imaging Library. See

> http://www.pythonware.com, or the Imaging-SIG page on www.python.org,
> for more on PIL.
>
> I modified your test program to use a PIL Image object instead of the
> Tkinter Image object. The wall-clock time went from 26 seconds to
> 0.8 seconds. (This is on a dual-processor P200 with 128MB RAM,
> running Linux 2.0.34)
> The "putpixel" method in the Imaging library is clearly a lot faster
> than the "put" method in the Tkinter Image object.
>
> If you need even more speed, you can convert the PIL Image object into
>
> a Numeric Python array, which then can be accessed efficiently with
> x,y indexing. I can post an example of how to do this if anyone is
> interested.
>
> Here's my modified version of your test program:
>
> from Tkinter import *
> from ImageTk import PhotoImage
> import Image
>

> class App:
>
> def __init__(self, master):
>
> frame = Frame(master)
> frame.pack()
> self.button = Button(frame, text="QUIT", fg="red",
> command=frame.quit)
> self.button.pack(side=LEFT)
> self.test_B = Button(frame, text="Test", command=self.test)
> self.test_B.pack(side=LEFT)

> self.img = Image.new("L",(100,100))
> self.photo = PhotoImage(image=self.img)

> self.canvas = Canvas(frame, width=100, height=100)

> self.canvas.create_image(0, 0, anchor=NW, image=self.photo)


> self.canvas.pack()
>
> def test(self):
> import time
> max_x = max_y = 100
> start = time.time()
> start_cpu = time.clock()
>

> for x in range(max_x):
> for y in range(max_y):

> self.img.putpixel((x,y),255)
> self.photo = PhotoImage(self.img)
> self.canvas.create_image(0, 0, anchor=NW, image=self.photo)
> self.canvas.update()

Steinar Knutsen

unread,
Oct 1, 1998, 3:00:00 AM10/1/98
to
In article <3613B522...@glue.umd.edu>, Kenneth Gosier
<kgo...@wam.umd.edu> wrote:

>[...] but I couldn't find it, or anything else that looked like it was


>made for putting single pixels to an Image object. I'm not sure if I've
>missed something or what. If someone might be able to help with this I
>would appreciate it.

Well:

img = Image.open("somefile")
draw = ImageDraw.ImageDraw(img)
draw.point((a, b))

Draws a point at the coordinates (a, b).

> Also, I might be interested in using Numeric Python, if that would
>provide a faster speed. But how involved would it be to convert from
>PIL to the N.P. array?

picture_as_array = Numeric.array(map(ord, img.tostring()))
picture_as_array.shape = img.size[1], img.size[0]

is one of several ways of converting a picture to a two dimensional array.
--
Steinar

David Ascher

unread,
Oct 1, 1998, 3:00:00 AM10/1/98
to
> Also, I might be interested in using Numeric Python, if that would
> provide a faster speed. But how involved would it be to convert from PIL
> to the N.P. array? I am completely new to Python, so I'm not exactly
> sure what is involved.


The following is probably more complicated than you need, but has a few
nice features (undocumented of course). It's also not designed for speed,
so might not be a good choice as is.

-- david

########################

import Image
def normalize(a):
a = array(a, copy=1)
a =1.0-(a - min(a.flat))/(max(a.flat) - min(a.flat))
return a

def ArrayToImage(a, height=200, scale=1):
if len(a.shape) == 2:
a = (normalize(a)*254).astype('b')
i = Image.new("L", (a.shape[1], a.shape[0]), color=255)
i.fromstring(a.tostring())
elif len(a.shape) == 1:
a = 1-normalize(a)
a = array(a*height).astype('i')
i = Image.new("L", (a.shape[0], height), color=255)
id = ImageDraw.ImageDraw(i)
id.setink(0)
def map_xandy_to_xy(x, y):
import _imaging
return _imaging.path(ravel(transpose(array((x, y)))).tolist())
id.line(xy=map_xandy_to_xy(arange(a.shape[0]), a))
if scale != 1:
i = i.resize((i.size[0]*scale, i.size[1]*scale))
return i

def ImageToArray(i):
a = fromstring(i.tostring(), 'b')
a.shape = i.im.size[1], i.im.size[0]
return a

Fredrik Lundh

unread,
Oct 2, 1998, 3:00:00 AM10/2/98
to
> Well, I read through the PIL stuff and it looked like something that
>would be very useful. So, I installed the package onto the Windows
>machine I am using and tried to run your modified version of my sample
>program, but I got the following error when I clicked the test button:
>
>> Exception in Tkinter callback
>> Traceback (innermost last):
>> File "C:\PY15\lib\lib-tk\Tkinter.py", line 752, in __call__
>> return apply(self.func, args)
>> File "piltest.py", line 29, in test
>> self.img.putpixel((x,y))
>> AttributeError: putpixel

Hmm. According to my notes, it should be in the 0.3b2 release
(get it from http://www.pythonware.com/downloads.htm).

It's definitely there in 0.3b3 ;-)

If you cannot find it in your copy of b2, use ImageDraw point
instead.

0 new messages