0
|
1 # lightsim.py by Drew Perttula, 6/28/2002
|
|
2
|
|
3 version="1.01"
|
|
4
|
|
5 from __future__ import division
|
|
6 import Image, ImageTk, ImageChops,ImageEnhance
|
|
7 from Tkinter import Tk,Label,Scale,DoubleVar,Frame
|
|
8 import time, glob
|
|
9
|
|
10 class Imagemixer(Label):
|
|
11 """tk widget that can load images (based on a glob pattern: each file should
|
|
12 be named like 'scene.light') and displays a weighted mix of the images"""
|
|
13 def __init__(self,parent,*k,**kw):
|
|
14 Label.__init__(self,parent,*k,**kw)
|
|
15 self.im={}
|
|
16
|
|
17 self.itk = ImageTk.PhotoImage(Image.new('RGB',(100,100)))
|
|
18 self.config(image=self.itk)
|
|
19 self.loadedscale=0
|
|
20 self.amounts={}
|
|
21
|
|
22 def loadallimgs(self,basename,scalefactor=1.0):
|
|
23 """load images from disk, scale them immediately, subtract off
|
|
24 the image called 'ambient' (if present) from all other
|
|
25 images. Given basename like path/to/foo, loads images with
|
|
26 names like path/to/foo.light1, path/to/foo.light2, etc. The
|
|
27 filename after the . is considered to be the light name
|
|
28 throughout the program. """
|
|
29
|
|
30 if self.loadedscale==scalefactor: # already loaded at this scale
|
|
31 return
|
|
32
|
|
33 self.im={}
|
|
34 ambientimg=None
|
|
35 sizenotset=1
|
|
36 for fullname in glob.glob(basename+".*"):
|
|
37 x=fullname[fullname.find('.')+1:]
|
|
38 self.im[x]=Image.open(fullname)
|
|
39 if scalefactor!=1.0:
|
|
40 self.im[x]=self.im[x].resize([a*scalefactor for a in self.im[x].size])
|
|
41 if x=='ambient':
|
|
42 ambientimg=self.im[x]
|
|
43 if sizenotset:
|
|
44 self.config(width=self.im[x].size[0],height=self.im[x].size[1])
|
|
45 self.itk=ImageTk.PhotoImage(Image.new('RGB',self.im[x].size))
|
|
46 self.config(image=self.itk)
|
|
47 sizenotset=0
|
|
48
|
|
49 # subtract off an image called 'ambient' from all the rest
|
|
50 if ambientimg is not None:
|
|
51 for k in self.im.keys():
|
|
52 if k!='ambient':
|
|
53 self.im[k] = ImageChops.subtract(self.im[k],ambientimg)
|
|
54
|
|
55 self.loadedscale=scalefactor
|
|
56 self._remix() # update the image
|
|
57
|
|
58 def _remix(self):
|
|
59 """mix all the images in self.im together according to the
|
|
60 weights in self.amounts. Each of those attributes are dicts
|
|
61 with the light names for keys."""
|
|
62 global fpslabel
|
|
63 i=None
|
|
64 amounts = self.amounts
|
|
65 layers=0
|
|
66 start=time.time()
|
|
67 for k in self.im.keys():
|
|
68 scale =amounts.get(k,0)
|
|
69 if scale!=0:
|
|
70 layers+=1
|
|
71 acc=ImageEnhance.Brightness(self.im[k]).enhance(scale) # scale the image before adding
|
|
72 # acc = ImageChops.add(base,self.im[k],1/scale) ## slower!
|
|
73 if i==None:
|
|
74 i=acc # use first layer directly
|
|
75 else:
|
|
76 i=ImageChops.add(i,acc) # add subsequent layers
|
|
77 dur = time.time()-start
|
|
78 fps=1.0/dur
|
|
79 fpslabel.config(text="%.02f fps, %.02f layers/sec"%(fps,layers/dur))
|
|
80 if i is not None:
|
|
81 self.itk.paste(i) # put image i in the PhotoImage
|
|
82
|
|
83 def setamounts(self,amounts):
|
|
84 self.amounts = amounts.copy()
|
|
85 self._remix()
|
|
86
|
|
87 def lightnames(self):
|
|
88 return self.im.keys()
|
|
89
|
|
90 basename='room'
|
|
91
|
|
92 root=Tk()
|
|
93
|
|
94 # Imagemixer._remix accesses this label, so it needs a name
|
|
95 fpslabel=Label()
|
|
96 fpslabel.pack()
|
|
97
|
|
98 Label(root,text="Use +/- to change image scale").pack()
|
|
99
|
|
100 mix=Imagemixer(root,relief='raised',bd=3)
|
|
101
|
|
102 scalefactor=.5
|
|
103 mix.loadallimgs(basename,scalefactor)
|
|
104 mix.pack()
|
|
105
|
|
106 #
|
|
107 # +/- keys should reload the images at new scales
|
|
108 #
|
|
109 def changescale(by=0):
|
|
110 global mix,scalefactor
|
|
111 scalefactor+=by
|
|
112 mix.loadallimgs(basename,scalefactor)
|
|
113 root.bind("<Key-plus>",lambda ev: changescale(.15))
|
|
114 root.bind("<Key-minus>",lambda ev: changescale(-.15))
|
|
115
|
|
116 amountvars={} # each light name maps to a Tkinter.DoubleVar object which the Scale adjusts
|
|
117
|
|
118 def redraw(*args):
|
|
119 global l,amountvars,update
|
|
120 # read the value out of each Scale
|
|
121 amounts = dict([(k,v.get()) for k,v in amountvars.items()])
|
|
122 mix.setamounts(amounts)
|
|
123
|
|
124 for x in mix.lightnames():
|
|
125 # the Scale sets this; the redraw() callback reads it
|
|
126 amountvars[x]=DoubleVar()
|
|
127
|
|
128 # a new frame for each slider row
|
|
129 f=Frame(root,bd=1,relief='groove')
|
|
130 f.pack(fill='x',expand=1)
|
|
131 # title
|
|
132 Label(f,text=x,width=10,anchor='e').pack(side='left')
|
|
133 # slider
|
|
134 s=Scale(f,from_=0,to=1,res=.01,
|
|
135 orient='horiz',
|
|
136 variable=amountvars[x],command=redraw)
|
|
137 s.pack(side='left',fill='x',expand=1)
|
|
138 if x=='ambient': # the 'ambient' level (if present) starts at 1
|
|
139 s.set(1)
|
|
140
|
|
141 root.mainloop()
|
|
142
|