Package pyvision :: Package analysis :: Package FaceAnalysis :: Module FaceDetectionTest
[hide private]
[frames] | no frames]

Source Code for Module pyvision.analysis.FaceAnalysis.FaceDetectionTest

  1  # PyVision License 
  2  # 
  3  # Copyright (c) 2006-2008 David S. Bolme 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  #  
 10  # 1. Redistributions of source code must retain the above copyright 
 11  # notice, this list of conditions and the following disclaimer. 
 12  #  
 13  # 2. Redistributions in binary form must reproduce the above copyright 
 14  # notice, this list of conditions and the following disclaimer in the 
 15  # documentation and/or other materials provided with the distribution. 
 16  #  
 17  # 3. Neither name of copyright holders nor the names of its contributors 
 18  # may be used to endorse or promote products derived from this software 
 19  # without specific prior written permission. 
 20  #  
 21  #  
 22  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 23  # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 24  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 25  # A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR 
 26  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 27  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 28  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 29  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 30  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 31  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 32  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 33   
 34  import copy 
 35  import time 
 36   
 37  import pyvision as pv 
 38   
39 -def face_from_eyes(eye1, eye2):
40 ''' 41 Given eye coordinates estimate the face rectangle 42 Assumes the face is reasonably horizontal. 43 ''' 44 truth_rect = pv.BoundingRect(eye1, eye2) 45 truth_rect.w = 3.0 * truth_rect.w 46 truth_rect.h = truth_rect.w 47 truth_rect.x = truth_rect.x - 0.33 * truth_rect.w 48 truth_rect.y = truth_rect.y - 0.4 * truth_rect.w 49 return truth_rect
50 51 52
53 -def is_success(truth_rect, detected_rect, threshhold=0.25):
54 ''' 55 This code takes a truth rect and a detected rect and determines 56 if the detection is a success or a failure. The default behavior 57 is true if the intersection of the two rects is has 50% of the 58 area of the larger rectangle. 59 ''' 60 61 if overlap_score(truth_rect, detected_rect) < threshhold: 62 return False 63 return True
64 65
66 -def overlap_score(truth_rect, detected_rect):
67 ''' 68 This code takes a truth rect and a detected rect and determines 69 if the detection is a success or a failure. The default behavior 70 is true if the intersection of the two rects is has 50% of the 71 area of the larger rectangle. 72 ''' 73 same = truth_rect.intersect(detected_rect) 74 max_area = max(truth_rect.area(), detected_rect.area()) 75 if same == None: 76 return 0.0 77 return same.area()/max_area
78 79 80 #############################################################################
81 -class FaceDetectionTest:
82 - def __init__(self,name=None,threshold=0.25):
83 ''' 84 Create a face detection test. 85 86 INPUTS: 87 @param name: Label for the test. 88 @param threshold: The fraction of joint area that counts as success. 89 ''' 90 self.name = name 91 self.threshold=threshold 92 self.sample_id = 1 93 94 self.table = pv.Table() 95 self.summary_table = pv.Table() 96 97 # Cumulative statistic 98 self.images = 0 99 self.positives = 0 100 self.successes = 0 101 self.negatives = 0 102 self.pixels = 0 103 104 # Summary statistics 105 self.pos_rate = 0.0 106 self.pos_bounds = (0.0,0.0) 107 self.neg_rate = 0.0 108 109 self.image_time = None 110 self.total_time = None 111 self.start_time = time.time() 112 self.end_time = None
113
114 - def addSample(self, truth_rects, detected_rects, im=None, annotate=False):
115 ''' 116 Adds a sample to face detection test. 117 118 @param truth_rects: truth for an image. 119 @param detected_rects: output of the detector 120 @param im: the image or filename to assciate with the sample. 121 @param annotate: add diagnostic annotations to the images. 122 ''' 123 self.images += 1 124 name = None 125 detected_rects = copy.copy(detected_rects) 126 127 if isinstance(im,pv.Image): 128 name = im.filename 129 if self.pixels != None: 130 self.pixels += im.asPIL().size[0] * im.asPIL().size[1] 131 elif isinstance(im,str): 132 name = im 133 self.pixels = None 134 else: 135 name = "%d"%self.sample_id 136 self.pixels = None 137 138 table = self.table 139 140 for i in range(len(truth_rects)): 141 truth = truth_rects[i] 142 self.positives += 1 143 success = False 144 best_overlap = 0.0 145 best_detection = None 146 147 for j in range(len(detected_rects)): 148 detected = detected_rects[j] 149 overlap = overlap_score(truth,detected) 150 if overlap >= self.threshold and overlap > best_overlap: 151 success = True 152 best_overlap = overlap 153 best_detection = j 154 155 table.setData(self.sample_id,'id',self.sample_id) 156 table.setData(self.sample_id,'name',name) 157 table.setData(self.sample_id,'truth_rect',str(truth)) 158 if best_detection != None: 159 table.setData(self.sample_id,'detection_rect',str(detected_rects[best_detection])) 160 else: 161 table.setData(self.sample_id,'detection_rect',None) 162 table.setData(self.sample_id,'success',success) 163 table.setData(self.sample_id,'overlap',best_overlap) 164 self.sample_id+=1 165 166 if success: 167 self.successes += 1 168 if annotate and isinstance(im,pv.Image): 169 if success: 170 im.annotateEllipse(truth,color='green') 171 else: 172 im.annotateEllipse(truth,color='red') 173 174 175 # Remove the best detection if success 176 if best_detection != None: 177 del detected_rects[best_detection] 178 179 if annotate: 180 for each in detected_rects: 181 im.annotateRect(each,color='red') 182 183 self.negatives += len(detected_rects) 184 185 self.end_time = time.time() 186 self.total_time = self.end_time - self.start_time 187 self.image_time = self.total_time/float(self.images) 188 # Update summary statistics 189 if self.positives > 0: 190 self.pos_rate = float(self.successes)/self.positives 191 self.pos_bounds = pv.cibinom(self.positives,self.successes,alpha=0.05) 192 if self.pixels != None: 193 self.neg_rate = float(self.negatives)/float(1.0e-6*self.pixels) 194 195 self.createSummary()
196
197 - def createSummary(self):
198 ''' 199 Summary of a test as a table. 200 ''' 201 self.summary_table.setElement('PosRate','Value',self.pos_rate) 202 self.summary_table.setElement('Lower95','Value',self.pos_bounds[0]) 203 self.summary_table.setElement('Upper95','Value',self.pos_bounds[1]) 204 self.summary_table.setElement('NegRate','Value',self.neg_rate) 205 self.summary_table.setElement('NegCount','Value',self.negatives) 206 self.summary_table.setElement('ImageCount','Value',self.images) 207 self.summary_table.setElement('TotalTime','Value',self.total_time) 208 self.summary_table.setElement('TimePerImage','Value',self.image_time)
209
210 - def __str__(self):
211 ''' One line summary of the test ''' 212 return "FaceDetectionTest(name:%s,PosRate:%f,PosBounds:%s,NegRate:%f,Neg:%d,Im:%d)"%(self.name,self.pos_rate,self.pos_bounds,self.neg_rate,self.negatives,self.images)
213 214 215 #############################################################################
216 -def summarizeDetectionTests(tests):
217 ''' 218 Create a summary table for a list containing FaceDetectionTest objects. 219 ''' 220 summary = pv.Table() 221 summary.setColumnFormat('PosRate','%0.4f') 222 summary.setColumnFormat('Lower95','%0.4f') 223 summary.setColumnFormat('Upper95','%0.4f') 224 summary.setColumnFormat('NegRate','%0.4f') 225 summary.setColumnFormat('Time','%0.2f') 226 for test in tests: 227 summary.setElement(test.name,'PosRate',test.pos_rate) 228 summary.setElement(test.name,'Lower95',test.pos_bounds[0]) 229 summary.setElement(test.name,'Upper95',test.pos_bounds[1]) 230 summary.setElement(test.name,'NegRate',test.neg_rate) 231 summary.setElement(test.name,'Time',test.total_time) 232 return summary
233 234 #TODO: Unit tests 235