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