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.recognition;
031
032import org.openimaj.data.dataset.GroupedDataset;
033import org.openimaj.data.dataset.ListDataset;
034import org.openimaj.feature.DoubleFV;
035import org.openimaj.feature.DoubleFVComparator;
036import org.openimaj.feature.FVProviderExtractor;
037import org.openimaj.image.processing.face.alignment.FaceAligner;
038import org.openimaj.image.processing.face.detection.DetectedFace;
039import org.openimaj.image.processing.face.feature.FisherFaceFeature.Extractor;
040import org.openimaj.ml.annotation.IncrementalAnnotator;
041import org.openimaj.ml.annotation.basic.KNNAnnotator;
042
043/**
044 * Implementation of a {@link FaceRecogniser} based on Fisherfaces. Any kind of
045 * machine learning implementation can be used for the actual classification.
046 * 
047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
048 * 
049 * @param <FACE>
050 *            Type of {@link DetectedFace}
051 * @param <PERSON>
052 *            Type of object representing a person
053 */
054public class FisherFaceRecogniser<FACE extends DetectedFace, PERSON>
055                extends
056                LazyFaceRecogniser<FACE, PERSON, Extractor<FACE>>
057{
058        protected FisherFaceRecogniser() {
059        }
060
061        /**
062         * Construct with the given feature extractor and underlying
063         * {@link FaceRecogniser}.
064         * 
065         * @param extractor
066         *            the feature extractor
067         * @param internalRecogniser
068         *            the face recogniser
069         */
070        public FisherFaceRecogniser(Extractor<FACE> extractor,
071                        FaceRecogniser<FACE, PERSON> internalRecogniser)
072        {
073                super(extractor, internalRecogniser);
074        }
075
076        /**
077         * Construct with the given feature extractor and underlying
078         * {@link IncrementalAnnotator}.
079         * 
080         * @param extractor
081         *            the feature extractor
082         * @param annotator
083         *            the annotator
084         */
085        public FisherFaceRecogniser(Extractor<FACE> extractor,
086                        IncrementalAnnotator<FACE, PERSON> annotator)
087        {
088                this(extractor, AnnotatorFaceRecogniser.create(annotator));
089        }
090
091        /**
092         * Convenience method to create an {@link FisherFaceRecogniser} with a
093         * standard KNN classifier.
094         * 
095         * @param <FACE>
096         *            The type of {@link DetectedFace}
097         * @param <PERSON>
098         *            the type representing a person
099         * @param numComponents
100         *            the number of principal components to keep
101         * @param aligner
102         *            the face aligner
103         * @param k
104         *            the number of nearest neighbours
105         * @param compar
106         *            the distance comparison function
107         * @return a new {@link FisherFaceRecogniser}
108         */
109        public static <FACE extends DetectedFace, PERSON>
110                        FisherFaceRecogniser<FACE, PERSON> create(int numComponents, FaceAligner<FACE> aligner, int k,
111                                        DoubleFVComparator compar)
112        {
113                final Extractor<FACE> extractor = new Extractor<FACE>(numComponents, aligner);
114                final FVProviderExtractor<DoubleFV, FACE> extractor2 = FVProviderExtractor.create(extractor);
115
116                final KNNAnnotator<FACE, PERSON, DoubleFV> knn =
117                                KNNAnnotator.create(extractor2, compar, k);
118
119                return new FisherFaceRecogniser<FACE, PERSON>(extractor, knn);
120        }
121
122        /**
123         * Convenience method to create an {@link FisherFaceRecogniser} with a
124         * standard KNN classifier, incorporating a threshold on the maximum
125         * distance (or minimum similarity) to allow a match.
126         * 
127         * @param <FACE>
128         *            The type of {@link DetectedFace}
129         * @param <PERSON>
130         *            the type representing a person
131         * @param numComponents
132         *            the number of principal components to keep
133         * @param aligner
134         *            the face aligner
135         * @param k
136         *            the number of nearest neighbours
137         * @param compar
138         *            the distance comparison function
139         * @param threshold
140         *            a distance threshold to limit matches.
141         * @return a new {@link FisherFaceRecogniser}
142         */
143        public static <FACE extends DetectedFace, PERSON>
144                        FisherFaceRecogniser<FACE, PERSON> create(int numComponents, FaceAligner<FACE> aligner, int k,
145                                        DoubleFVComparator compar, float threshold)
146        {
147                final Extractor<FACE> extractor = new Extractor<FACE>(numComponents, aligner);
148                final FVProviderExtractor<DoubleFV, FACE> extractor2 = FVProviderExtractor.create(extractor);
149
150                final KNNAnnotator<FACE, PERSON, DoubleFV> knn =
151                                KNNAnnotator.create(extractor2, compar, k, threshold);
152
153                return new FisherFaceRecogniser<FACE, PERSON>(extractor, knn);
154        }
155
156        @Override
157        protected void beforeBatchTrain(GroupedDataset<PERSON, ListDataset<FACE>, FACE> dataset) {
158                extractor.train(dataset);
159        }
160
161        @Override
162        public String toString() {
163                return String.format("FisherFaceRecogniser[extractor=%s; recogniser=%s]",
164                                this.extractor, this.internalRecogniser);
165        }
166}