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 This is a simplified correlation filter implementation used to locate eyes
36 using ASEF correlation filters. This file contains two classes:
37 OpenCVFilterEyeLocator and FilterEyeLocator. The first is the bare minimum
38 required to locate eye and only requires opencv. The second need is a wrapper
39 that includes a nice pyvision compatible interface. This class is not integrated with
40 PyVision. PyVision supplies an interface to this class which cleans
41 up the interface and provides a bridge to many of the PyVision data
42 structures.
43 '''
44
45 import cv
46 import math
47 import struct
48 import array
49 import os.path
50 import pyvision as pv
51 import numpy as np
52 import sys
53
54 __author__ = "David S. Bolme - Colorado State Univeristy"
55 __version__ = "$Revision: 729 $"
56
57 if pv.WARN_COMMERCIAL_USE:
58 warning = '''
59 WARNING: A patent protection is anticipated for ASEF and
60 similar filters by the Colorado State University
61 Research Foundation (CSURF).
62
63 This module, "FilterEyeLocator.py", my not be
64 suitable for commercial use.
65
66 Commercial and government users should contact
67 CSURF for additional details:
68 http://www.csurf.org/tto/pdfs/ncs_forms/09-017_csurf_ncs.pdf
69 '''
70 sys.stderr.write(warning)
71
72
73
74
75
76
77
78
79
80
81
82
83
85 '''
86 File Format
87 - Line 1: CFEL
88 - Line 2: <comment>
89 - Line 3: <copyright>
90 - Line 4: ROWS COLS
91 - Line 5: LEFT_RECT
92 - Line 6: RIGHT_RECT
93 - Line 7: BYTE_ORDER(0x41424344 or 'ABCD')
94 - Line 8: <binary data: two single precision floating point arrays of 4*WIDTH*HEIGHT bytes)
95 '''
96 r,c = el.left_filter.rows,el.left_filter.cols
97
98 f = open(filename,'wb')
99 f.write("CFEL\n")
100 f.write(comment.strip()+"\n")
101 f.write(copyright_str.strip()+"\n")
102 f.write("%d %d\n"%(r,c))
103 f.write("%d %d %d %d\n"%(el.left_rect.x,el.left_rect.y,el.left_rect.width,el.left_rect.height))
104 f.write("%d %d %d %d\n"%(el.right_rect.x,el.right_rect.y,el.right_rect.width,el.right_rect.height))
105 f.write("%s\n"%struct.pack("i",0x41424344))
106
107 assert len(el.left_filter.imageData) == 4*r*c
108 f.write(el.left_filter.imageData)
109
110 assert len(el.right_filter.imageData) == 4*r*c
111 f.write(el.right_filter.imageData)
112
113
115 '''
116 Loads the eye locator from a file.'
117 '''
118
119
120 f = open(filename,'rb')
121
122
123 line = f.readline().strip()
124 assert line == "CFEL"
125
126
127 f.readline()
128 f.readline()
129
130
131 r,c = f.readline().split()
132 r,c = int(r),int(c)
133
134
135 x,y,w,h = f.readline().split()
136 left_rect = (int(x),int(y),int(w),int(h))
137
138
139 x,y,w,h = f.readline().split()
140 right_rect = (int(x),int(y),int(w),int(h))
141
142
143 magic_number = f.readline().strip()
144 assert len(magic_number) == 4
145 magic_number = struct.unpack('i',magic_number)[0]
146
147
148 lf = array.array('f')
149 rf = array.array('f')
150
151 lf.fromfile(f,r*c)
152 rf.fromfile(f,r*c)
153
154
155 if magic_number == 0x41424344:
156 pass
157 elif magic_number == 0x44434241:
158 lf.byteswap()
159 rf.byteswap()
160 else:
161 raise ValueError("Bad Magic Number: Unknown byte ordering in file")
162
163
164 left_filter = cv.CreateMat(r,c,cv.CV_32F)
165 right_filter = cv.CreateMat(r,c,cv.CV_32F)
166
167
168 cv.SetData(left_filter, lf.tostring())
169 cv.SetData(right_filter, rf.tostring())
170
171 tmp = pv.OpenCVToNumpy(left_filter)
172 t1 = tmp.mean()
173 t2 = tmp.std()
174 cv.Scale(left_filter,left_filter,1.0/t2,-t1*1.0/t2)
175
176 tmp = pv.OpenCVToNumpy(right_filter)
177 t1 = tmp.mean()
178 t2 = tmp.std()
179 cv.Scale(right_filter,right_filter,1.0/t2,-t1*1.0/t2)
180
181
182
183
184 if ilog != None:
185
186
187
188 lf = pv.OpenCVToNumpy(left_filter)
189 rf = pv.OpenCVToNumpy(right_filter)
190
191 lf = np.fft.fftshift(lf).transpose()
192 rf = np.fft.fftshift(rf).transpose()
193
194 ilog.log(pv.Image(lf),label="LeftEyeFilter")
195 ilog.log(pv.Image(rf),label="RightEyeFilter")
196
197
198 return OpenCVFilterEyeLocator(left_filter,right_filter,left_rect,right_rect)
199
200
202 '''
203 This class is used for someone only interested in locating the eyes in an
204 image using correlation filters. This class does not include any support
205 for training correlation filters. For training see ASEF. This class
206 is written only using OpenCV and is much faster than the ASEF class.
207
208 For details see the paper:
209
210 David S. Bolme, Bruce A. Draper, and J. Ross Beveridge. Average of
211 Synthetic Exact Filters. Submitted to Computer Vision and Pattern
212 Recoginition. 2009.
213
214 The class uses two ASEF filters to find the eyes. The eyes are located by
215 first computing the correlation of the face tile with each filter. The
216 max value from the correlation plain is returned as the eye coordinate.
217 Also returned is the full correlation output from the image.
218
219 The images are normalized by computing log transforming the pixel values
220
221 To improve performance, this class is not thread safe. The class reuses
222 data arrays allocated for each call to use this class for multiple threads
223 you should create an instance for each threads. Also note that each method
224 call may overwrite arrays returned by this application. So if you need
225 the returned data to persist be sure to create a copy.
226
227 - Left and right eyes are in relation to the location in the image.
228 '''
229
230
231 - def __init__(self,left_filter,right_filter, left_rect, right_rect):
232 '''
233 @param left_filter: is in the Fourier domain where the left eye
234 corresponds to the real output and the right eye corresponds to
235 the imaginary output
236 '''
237
238 r,c = left_filter.rows,left_filter.cols
239
240 assert left_filter.width == right_filter.width
241 assert left_filter.height == right_filter.height
242 assert left_filter.channels == 1
243 assert right_filter.channels == 1
244
245
246 self.left_filter = cv.CreateMat(r,c,cv.CV_32F)
247 self.right_filter = cv.CreateMat(r,c,cv.CV_32F)
248 self.left_filter_dft = cv.CreateMat(r,c,cv.CV_32F)
249 self.right_filter_dft = cv.CreateMat(r,c,cv.CV_32F)
250 self.image = cv.CreateMat(r,c,cv.CV_32F)
251 self.left_corr = cv.CreateMat(r,c,cv.CV_32F)
252 self.right_corr = cv.CreateMat(r,c,cv.CV_32F)
253
254
255 cv.ConvertScale(left_filter, self.left_filter)
256 cv.ConvertScale(right_filter, self.right_filter)
257
258
259 cv.DFT(self.left_filter, self.left_filter_dft, cv.CV_DXT_FORWARD)
260 cv.DFT(self.right_filter, self.right_filter_dft, cv.CV_DXT_FORWARD)
261
262
263 self.left_rect = left_rect
264 self.right_rect = right_rect
265
266 self.left_roi = cv.GetSubRect(self.left_corr,self.left_rect)
267 self.right_roi = cv.GetSubRect(self.right_corr,self.right_rect)
268
269
270 self.lut = cv.CreateMat(256,1,cv.CV_32F)
271
272 for i in range(256):
273 self.lut[i,0] = math.log(i+1)
274
275
277 '''
278 @param image_tile: is an 32-bit gray scale opencv image tile of a face
279 that is the same size as the filter
280 @type image_tile: 8-bit gray scale opencv image
281
282 @returns: a tuple consisting of the location of the left and right eyes
283 (opencv 2D points), and the complex correlation plain output
284
285 @raises AssertionError: is raised if the image is not 8-bit or not the
286 same size as the filter
287 '''
288 self.correlate(image_tile)
289
290 leye = cv.MinMaxLoc(self.left_roi)[3]
291 leye = (self.left_rect[0]+leye[0],self.left_rect[1]+leye[1])
292
293 reye = cv.MinMaxLoc(self.right_roi)[3]
294 reye = (self.right_rect[0]+reye[0],self.right_rect[1]+reye[1])
295
296 return leye,reye,self.left_corr,self.right_corr
297
298
300 '''
301 preprocess an image tile.
302 '''
303
304 image_tile = pv.OpenCVToNumpy(image_tile)
305 self.image = pv.NumpyToOpenCV(np.log(image_tile + 1.0).astype(np.float32))
306
307 return self.image
308
309
311 '''
312 Correlate the image with the left and right filters.
313 '''
314 self._preprocess(image_tile)
315
316 cv.DFT(self.image, self.image, cv.CV_DXT_FORWARD)
317 cv.MulSpectrums( self.image, self.left_filter_dft, self.left_corr, cv.CV_DXT_MUL_CONJ )
318 cv.MulSpectrums( self.image, self.right_filter_dft, self.right_corr, cv.CV_DXT_MUL_CONJ )
319
320 cv.DFT(self.left_corr,self.left_corr,cv.CV_DXT_INV_SCALE)
321 cv.DFT(self.right_corr,self.right_corr,cv.CV_DXT_INV_SCALE)
322
323 return self.left_corr,self.right_corr
324
325
327 '''
328 This class provides a PyVision interface to the ASEF eye locator.
329 '''
330
331 - def __init__(self,filename=None,ilog=None):
332 '''
333 Load the eye detector from the file.
334 '''
335 if filename == None:
336 filename = os.path.join(pv.__path__[0],"config","EyeLocatorASEF128x128.fel")
337
338 self.fel = loadFilterEyeLocator(filename,ilog=ilog)
339
340 self.bwtile = cv.CreateMat(128,128,cv.CV_8U)
341
342
343 - def __call__(self,im,face_rects,ilog=None):
344 return self.locateEyes(im,face_rects,ilog=ilog)
345
346
348 '''
349 Finds the eyes in the image.
350
351 @param im: full sized image
352 @param face_rects: list of rectangle which are the output from the cascade face detector.
353 '''
354 cvim = im.asOpenCVBW()
355
356 faces = []
357
358 for rect in face_rects:
359 faceim = cv.GetSubRect(cvim, rect.asOpenCV())
360 cv.Resize(faceim,self.bwtile)
361
362 affine = pv.AffineFromRect(rect,(128,128))
363
364
365
366 leye,reye,lcp,rcp = self.fel.locateEyes(self.bwtile)
367 le = pv.Point(leye)
368 re = pv.Point(reye)
369
370 leye = affine.invertPoint(le)
371 reye = affine.invertPoint(re)
372
373 faces.append([rect,leye,reye])
374
375 if ilog != None:
376 ilog.log(pv.Image(self.bwtile),label="FaceDetection")
377 lcp = pv.OpenCVToNumpy(lcp).transpose()
378 lcp = lcp*(lcp > 0.0)
379 rcp = pv.OpenCVToNumpy(rcp).transpose()
380 rcp = rcp*(rcp > 0.0)
381 ilog.log(pv.Image(lcp),label="Left_Corr")
382 ilog.log(pv.Image(rcp),label="Right_Corr")
383 tmp = pv.Image(self.bwtile)
384 tmp.annotatePoint(le)
385 tmp.annotatePoint(re)
386 ilog.log(tmp,"EyeLocations")
387
388 return faces
389
390
391
392
393
394 import unittest
395 import pyvision.face.CascadeDetector as cd
396 from pyvision.analysis.FaceAnalysis.FaceDatabase import ScrapShotsDatabase
397 from pyvision.analysis.FaceAnalysis.EyeDetectionTest import EyeDetectionTest
398
400
402 '''
403 This trains the FaceFinder on the scraps database.
404 '''
405
406 ssdb = ScrapShotsDatabase()
407
408
409 face_detector = cd.CascadeDetector()
410
411
412 eye_locator = FilterEyeLocator()
413
414
415 edt = EyeDetectionTest(name='asef_scraps')
416
417
418 for face_id in ssdb.keys():
419 face = ssdb[face_id]
420 im = face.image
421
422
423 faces = face_detector.detect(im)
424
425
426 pred_eyes = eye_locator(im,faces)
427
428 truth_eyes = [[face.left_eye,face.right_eye]]
429 pred_eyes = [ [leye,reye] for _,leye,reye in pred_eyes]
430
431
432 edt.addSample(truth_eyes, pred_eyes, im=im, annotate=False)
433
434 edt.createSummary()
435 self.assertAlmostEqual( edt.face_rate , 0.97109826589595372, places = 3 )
436 self.assertAlmostEqual( edt.both25_rate , 0.82658959537572252, places = 3 )
437 self.assertAlmostEqual( edt.both10_rate , 0.47976878612716761, places = 3 )
438 self.assertAlmostEqual( edt.both05_rate , 0.30635838150289019, places = 3 )
439