001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.processing.face.detection.benchmarking; 031 032import gnu.trove.set.hash.TDoubleHashSet; 033 034import java.io.File; 035import java.io.IOException; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.List; 039 040import org.openimaj.data.dataset.ListDataset; 041import org.openimaj.image.objectdetection.filtering.OpenCVGrouping; 042import org.openimaj.image.processing.face.detection.DetectedFace; 043import org.openimaj.image.processing.face.detection.HaarCascadeDetector; 044import org.openimaj.image.processing.face.detection.benchmarking.Matcher.Match; 045 046public class FDDBEvaluation { 047 public interface EvaluationDetector { 048 List<? extends DetectedFace> getDetections(FDDBRecord record); 049 } 050 051 public List<Results> performEvaluation(ListDataset<FDDBRecord> dataset, EvaluationDetector detector) { 052 // cumRes stores the cumulative results for all the images 053 List<Results> cumRes = new ArrayList<Results>(); 054 final Matcher matcher = new Matcher(); 055 056 // Process each image 057 final int numImages = dataset.size(); 058 for (int i = 0; i < numImages; i++) { 059 final FDDBRecord data = dataset.getInstance(i); 060 final String imName = data.getImageName(); 061 062 final List<? extends DetectedFace> annot = data.getGroundTruth(); 063 final List<? extends DetectedFace> det = detector.getDetections(data); 064 065 // imageResults holds the results for different thresholds 066 // applied to a single image 067 final List<Results> imageResults = new ArrayList<Results>(); 068 069 if (det.size() == 0) { 070 // create the image results for zero detections 071 final Results r = new Results(imName, Double.MAX_VALUE, null, annot, det); 072 imageResults.add(r); 073 } else { 074 // find the unique values for detection scores 075 final double[] uniqueScores = getUniqueConfidences(det); 076 077 // For each unique score value st, 078 // (a) filter the detections with score >= st 079 // (b) compute the matching annot-det pairs 080 // (c) compute the result statistics 081 for (final double scoreThreshold : uniqueScores) { 082 final ArrayList<DetectedFace> filteredDet = new ArrayList<DetectedFace>(); 083 // (a) filter the detections with score >= st 084 for (int di = 0; di < det.size(); di++) { 085 final DetectedFace rd = det.get(di); 086 if (rd.getConfidence() >= scoreThreshold) 087 filteredDet.add(rd); 088 } 089 090 // (b) match annotations to detections 091 final List<Match> mps = matcher.match(annot, filteredDet); 092 093 // (c) compute the result statistics and append to the list 094 final Results r = new Results(imName, scoreThreshold, mps, annot, filteredDet); 095 imageResults.add(r); 096 } 097 } 098 099 // merge the list of results for this image (imageResults) with the 100 // global list (cumRes) 101 cumRes = Results.merge(cumRes, imageResults); 102 } 103 104 return cumRes; 105 } 106 107 /** 108 * Get a list of unique confidences associated with the given list of faces. 109 * 110 * @param faces 111 * the faces 112 * @return the unique confidences, sorted in ascending order. 113 */ 114 private double[] getUniqueConfidences(List<? extends DetectedFace> faces) { 115 final TDoubleHashSet set = new TDoubleHashSet(); 116 117 for (final DetectedFace f : faces) { 118 set.add(f.getConfidence()); 119 } 120 121 final double[] ret = set.toArray(); 122 123 Arrays.sort(ret); 124 125 return ret; 126 } 127 128 public static void main(String[] args) throws IOException, InterruptedException { 129 final File fddbGroundTruth = new File("/Users/jsh2/Downloads/FDDB-folds/FDDB-fold-01-ellipseList.txt"); 130 final File imageBase = new File("/Users/jsh2/Downloads/originalPics/"); 131 final FDDBDataset dataset = new FDDBDataset(fddbGroundTruth, imageBase, true); 132 133 final HaarCascadeDetector det = HaarCascadeDetector.BuiltInCascade.frontalface_alt2.load(); 134 det.setGroupingFilter(new OpenCVGrouping(0)); 135 det.setMinSize(80); 136 final EvaluationDetector evDet = new EvaluationDetector() { 137 138 @Override 139 public synchronized List<? extends DetectedFace> getDetections(FDDBRecord record) { 140 final List<DetectedFace> faces = det.detectFaces(record.getFImage()); 141 142 // for (final DetectedFace f : faces) 143 // f.setConfidence(1); 144 145 return faces; 146 } 147 }; 148 149 final FDDBEvaluation eval = new FDDBEvaluation(); 150 final List<Results> result = eval.performEvaluation(dataset, evDet); 151 152 System.out.println(Results.getROCData(result)); 153 } 154}