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}