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()
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
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()
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)
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
> 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()
>[...] 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
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
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.