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.feature; 031 032import java.io.DataInput; 033import java.io.DataOutput; 034import java.io.IOException; 035import java.util.AbstractList; 036import java.util.List; 037 038import org.openimaj.citation.annotation.Reference; 039import org.openimaj.citation.annotation.ReferenceType; 040import org.openimaj.data.dataset.Dataset; 041import org.openimaj.experiment.dataset.util.DatasetAdaptors; 042import org.openimaj.feature.DoubleFV; 043import org.openimaj.feature.FeatureVectorProvider; 044import org.openimaj.image.FImage; 045import org.openimaj.image.model.EigenImages; 046import org.openimaj.image.processing.face.alignment.FaceAligner; 047import org.openimaj.image.processing.face.detection.DetectedFace; 048import org.openimaj.io.IOUtils; 049import org.openimaj.ml.training.BatchTrainer; 050 051/** 052 * A {@link FacialFeature} for EigenFaces. 053 * 054 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 055 */ 056@Reference( 057 type = ReferenceType.Inproceedings, 058 author = { "Turk, M.A.", "Pentland, A.P." }, 059 title = "Face recognition using eigenfaces", 060 year = "1991", 061 booktitle = "Computer Vision and Pattern Recognition, 1991. Proceedings CVPR '91., IEEE Computer Society Conference on", 062 pages = { "586 ", "591" }, 063 month = "jun", 064 number = "", 065 volume = "", 066 customData = { 067 "keywords", "eigenfaces;eigenvectors;face images;face recognition system;face space;feature space;human faces;two-dimensional recognition;unsupervised learning;computerised pattern recognition;eigenvalues and eigenfunctions;", 068 "doi", "10.1109/CVPR.1991.139758" 069 }) 070public class EigenFaceFeature implements FacialFeature, FeatureVectorProvider<DoubleFV> { 071 /** 072 * A {@link FacialFeatureExtractor} for producing EigenFaces. Unlike most 073 * {@link FacialFeatureExtractor}s, this one either needs to be trained or 074 * provided with a pre-trained {@link EigenImages} object. 075 * <p> 076 * A {@link FaceAligner} can be used to produce aligned faces for training 077 * and feature extraction. 078 * 079 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 080 * 081 * @param <T> 082 * 083 */ 084 public static class Extractor<T extends DetectedFace> 085 implements 086 FacialFeatureExtractor<EigenFaceFeature, T>, 087 BatchTrainer<T> 088 { 089 EigenImages eigen = null; 090 FaceAligner<T> aligner = null; 091 092 /** 093 * Construct with the requested number of components (the number of PCs 094 * to keep) and a face aligner. The principal components must be learned 095 * by calling {@link #train(List)}. 096 * 097 * @param numComponents 098 * the number of principal components to keep. 099 * @param aligner 100 * the face aligner 101 */ 102 public Extractor(int numComponents, FaceAligner<T> aligner) { 103 this(new EigenImages(numComponents), aligner); 104 } 105 106 /** 107 * Construct with given pre-trained {@link EigenImages} basis and a face 108 * aligner. 109 * 110 * @param basis 111 * the pre-trained basis 112 * @param aligner 113 * the face aligner 114 */ 115 public Extractor(EigenImages basis, FaceAligner<T> aligner) { 116 this.eigen = basis; 117 this.aligner = aligner; 118 } 119 120 @Override 121 public EigenFaceFeature extractFeature(T face) { 122 final FImage patch = aligner.align(face); 123 124 final DoubleFV fv = eigen.extractFeature(patch); 125 126 return new EigenFaceFeature(fv); 127 } 128 129 @Override 130 public void readBinary(DataInput in) throws IOException { 131 eigen.readBinary(in); 132 133 final String alignerClass = in.readUTF(); 134 aligner = IOUtils.newInstance(alignerClass); 135 aligner.readBinary(in); 136 } 137 138 @Override 139 public byte[] binaryHeader() { 140 return this.getClass().getName().getBytes(); 141 } 142 143 @Override 144 public void writeBinary(DataOutput out) throws IOException { 145 eigen.writeBinary(out); 146 147 out.writeUTF(aligner.getClass().getName()); 148 aligner.writeBinary(out); 149 } 150 151 @Override 152 public void train(final List<? extends T> data) { 153 final List<FImage> patches = new AbstractList<FImage>() { 154 155 @Override 156 public FImage get(int index) { 157 return aligner.align(data.get(index)); 158 } 159 160 @Override 161 public int size() { 162 return data.size(); 163 } 164 165 }; 166 167 eigen.train(patches); 168 } 169 170 /** 171 * Train from a dataset 172 * 173 * @param data 174 * the dataset 175 */ 176 public void train(final Dataset<? extends T> data) { 177 train(DatasetAdaptors.asList(data)); 178 } 179 180 @Override 181 public String toString() { 182 return String.format("EigenFaceFeature.Extractor[aligner=%s]", this.aligner); 183 } 184 } 185 186 private DoubleFV fv; 187 188 protected EigenFaceFeature() { 189 this(null); 190 } 191 192 /** 193 * Construct the EigenFaceFeature with the given feature vector. 194 * 195 * @param fv 196 * the feature vector 197 */ 198 public EigenFaceFeature(DoubleFV fv) { 199 this.fv = fv; 200 } 201 202 @Override 203 public void readBinary(DataInput in) throws IOException { 204 fv = new DoubleFV(); 205 fv.readBinary(in); 206 } 207 208 @Override 209 public byte[] binaryHeader() { 210 return getClass().getName().getBytes(); 211 } 212 213 @Override 214 public void writeBinary(DataOutput out) throws IOException { 215 fv.writeBinary(out); 216 } 217 218 @Override 219 public DoubleFV getFeatureVector() { 220 return fv; 221 } 222}