1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 import wx
36 import os
37 import os.path
38 import csv
39 import random
40 '''
41 This program is a simple gui for selecting eye coordinates for a
42 set of images.
43
44 The program takes a directory as an argument.
45
46 It loads each image in that directory into a list frame.
47
48 It displays the image in the frame.
49
50 The image is clicked on to select the eye coordinates.
51
52 Eye coordinates are saved to a file.
53 '''
54
55 IMAGE_FORMATS=[".JPG",".PNG",".PPM",".PGM",".GIF",".TIF",".TIFF",]
56
58
59 - def __init__(self,parent,id,name,image_dir,n_points=None,randomize=False,scale=1.0):
60 wx.Frame.__init__(self,parent,id,name)
61
62
63 self.image_dir = image_dir
64 self.n_points = n_points
65 self.image_names = []
66 self.current_image = None
67 self.image_name = None
68 self.scale = scale
69 for name in os.listdir(image_dir):
70 for format in IMAGE_FORMATS:
71 if name.upper().endswith(format):
72 self.image_names.append(name)
73 if randomize:
74 random.shuffle(self.image_names)
75 self.filename = None
76 self.coords = {}
77
78
79 self.CreateStatusBar()
80
81
82
83 filemenu= wx.Menu()
84 id_about = wx.NewId()
85 id_open = wx.NewId()
86 id_save = wx.NewId()
87 id_save_as = wx.NewId()
88 id_exit = wx.NewId()
89
90 filemenu.Append(wx.ID_ABOUT, wx.EmptyString)
91 filemenu.AppendSeparator()
92 filemenu.Append(wx.ID_OPEN, wx.EmptyString)
93 filemenu.Append(wx.ID_SAVE, wx.EmptyString)
94 filemenu.Append(wx.ID_SAVEAS, wx.EmptyString)
95 filemenu.AppendSeparator()
96 filemenu.Append(wx.ID_EXIT,wx.EmptyString)
97
98 menuBar = wx.MenuBar()
99 menuBar.Append(filemenu,"&File")
100 self.SetMenuBar(menuBar)
101
102
103
104 self.list=wx.ListBox(self,wx.NewId(),style=wx.LC_REPORT|wx.SUNKEN_BORDER,choices=self.image_names)
105 self.list.Show(True)
106
107
108 self.static_bitmap = wx.StaticBitmap(self,wx.NewId(), bitmap=wx.EmptyBitmap(300, 300))
109 self.static_bitmap.SetCursor(wx.CROSS_CURSOR)
110
111
112 box = wx.BoxSizer(wx.HORIZONTAL)
113 box.Add(self.list, 1, wx.EXPAND)
114 box.Add(self.static_bitmap, 3, wx.EXPAND)
115
116 self.SetAutoLayout(True)
117 self.SetSizer(box)
118 self.Layout()
119
120
121 wx.EVT_LISTBOX(self, self.list.GetId(), self.onSelect)
122 wx.EVT_SIZE(self.static_bitmap, self.onBitmapResize)
123 wx.EVT_LEFT_DOWN(self.static_bitmap, self.onClick)
124 wx.EVT_LEFT_UP(self.static_bitmap, self.onRelease)
125 wx.EVT_MOTION(self.static_bitmap, self.onMotion)
126 self.moving = None
127
128 wx.EVT_MENU(self,wx.ID_OPEN,self.onOpen)
129 wx.EVT_MENU(self,wx.ID_SAVE,self.onSave)
130 wx.EVT_MENU(self,wx.ID_SAVEAS,self.onSaveAs)
131
132 wx.EVT_MENU(self,wx.ID_ABOUT,self.onAbout)
133
134
135 wx.EVT_CLOSE(self,self.onClose)
136
137
139
140 reader = csv.reader(open(path, "rb"))
141 first = True
142 eyes = False
143 coords = {}
144 for row in reader:
145 filename = row[0]
146 row = row[1:]
147
148 if len(row)%2 != 0:
149 print "Error Loading File"
150 raise TypeError("Odd number of values in this row")
151
152 points = []
153 for i in range(0,len(row),2):
154 point = (float(row[i]),float(row[i+1]))
155 points.append(point)
156
157 coords[filename] = points
158
159
160
161
162
163 print "CSV File Data: ", coords
164
165 self.coords = coords
166
167 - def save(self,path):
168 ''' Save the coords to a csv file. '''
169 writer = csv.writer(open(path,'wb'))
170
171 keys = self.coords.keys()
172 keys.sort()
173 for key in keys:
174 row = [key]
175 for point in self.coords[key]:
176 row.append(point[0])
177 row.append(point[1])
178 writer.writerow(row)
179
180
182 if self.image_name:
183 if self.n_points != None and len(self.coords[self.image_name]) != self.n_points:
184 print "ERROR: incorrect number of points."
185
186 self.image_name = event.GetString()
187
188 if not self.coords.has_key(self.image_name):
189 self.coords[self.image_name] = []
190
191 filename = os.path.join(self.image_dir,self.image_name)
192 self.current_image = wx.Image(filename)
193 self.first_click = True
194 self.DisplayImage()
195
196
198 w = event.GetSize().GetWidth()
199 h = event.GetSize().GetHeight()
200
201 self.static_bitmap.SetSize(event.GetSize())
202
203 self.DisplayImage()
204
205
207 if self.current_image:
208 tw = self.static_bitmap.GetSize().GetWidth()
209 th = self.static_bitmap.GetSize().GetHeight()
210 sw = self.current_image.GetSize().GetWidth()
211 sh = self.current_image.GetSize().GetHeight()
212
213
214
215
216 tw = int(sw*self.scale)
217 th = int(sh*self.scale)
218
219 im = self.current_image.Copy()
220 im.Rescale(tw,th)
221 bm = im.ConvertToBitmap()
222 bmdc = wx.MemoryDC(bm)
223 bmdc.SetBrush(wx.TRANSPARENT_BRUSH)
224 bmdc.SetPen(wx.RED_PEN)
225 bmdc.SetTextForeground(wx.RED)
226
227 i = 1
228 for point in self.coords[self.image_name]:
229 bmdc.DrawCircle(self.scale*point[0], self.scale*point[1], 5)
230 w,h = bmdc.GetTextExtent(str(i))
231 bmdc.DrawText(str(i),self.scale*point[0]-w/2, self.scale*point[1]+5)
232 i += 1
233
234 del bmdc
235
236 self.static_bitmap.SetBitmap(bm)
237
238
239
241 x = event.GetX()/self.scale
242 y = event.GetY()/self.scale
243
244 if not self.image_name: return
245
246
247 for i in range(len(self.coords[self.image_name])):
248 px,py = self.coords[self.image_name][i]
249 if abs(px - x) < 4 and abs(py - y) < 4:
250 self.coords[self.image_name][i] = (x,y,)
251 self.moving = i
252 self.DisplayImage()
253 return
254
255
256 if self.first_click:
257 self.coords[self.image_name] = []
258 self.first_click = False
259
260 if len(self.coords[self.image_name]) < self.n_points or self.n_points == None:
261 self.coords[self.image_name].append((x,y,))
262 self.moving = len(self.coords[self.image_name]) - 1
263 self.DisplayImage()
264
266 x = event.GetX()/self.scale
267 y = event.GetY()/self.scale
268
269 if self.moving != None:
270 self.coords[self.image_name][self.moving] = (x,y,)
271 self.DisplayImage()
272
273
275 x = event.GetX()/self.scale
276 y = event.GetY()/self.scale
277
278 if self.moving != None:
279 self.coords[self.image_name][self.moving] = (x,y,)
280 self.DisplayImage()
281
282 self.moving = None
283
285 dlg = wx.MessageDialog(self,message="For more information visit:\n\nhttp://pyvision.sourceforge.net",style = wx.OK )
286 result = dlg.ShowModal()
287
288
290 print "Open"
291 fd = wx.FileDialog(self,style=wx.FD_OPEN)
292 fd.ShowModal()
293 self.filename = fd.GetPath()
294 print "On Open...",self.filename
295
296 self.openCSVFile(self.filename)
297
298
300 if self.filename == None:
301
302 self.onSaveAs(event)
303 else:
304 self.save(self.filename)
305
307 fd = wx.FileDialog(self,message="Save the coordinates as...",style=wx.FD_SAVE,
308 wildcard="Comma separated value (*.csv)|*.csv")
309 fd.ShowModal()
310 self.filename = fd.GetPath()
311
312 self.save(self.filename)
313
315 dlg = wx.MessageDialog(self,message="Would you like to save the coordinates before exiting?",style = wx.YES_NO | wx.YES_DEFAULT)
316 result = dlg.ShowModal()
317 if result == wx.ID_YES:
318 print "Saving..."
319 self.onSave(event)
320 else:
321 print "Discarding changes..."
322
323
324 event.Skip()
325
326
327 if __name__ == '__main__':
328 app = wx.App(False)
329
330 dir_dialog = wx.DirDialog(None, message = "Please select a directory that contains images.")
331 err = dir_dialog.ShowModal()
332 image_dir = '.'
333 if(err == wx.ID_OK):
334 image_dir = dir_dialog.GetPath()
335 else:
336 print "Error getting path:",err
337
338 print "Image Dir",image_dir
339 scale = 1.0
340
341 frame = EyePickerFrame(None, wx.ID_ANY, "Eye Selector",image_dir,n_points=None,randomize=True,scale=scale)
342 frame.Show(True)
343 app.MainLoop()
344