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}