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.tools.faces.recognition; 031 032import java.io.File; 033import java.io.IOException; 034 035import org.kohsuke.args4j.CmdLineException; 036import org.kohsuke.args4j.CmdLineParser; 037import org.kohsuke.args4j.Option; 038import org.kohsuke.args4j.ProxyOptionHandler; 039import org.openimaj.data.dataset.GroupedDataset; 040import org.openimaj.data.dataset.ListDataset; 041import org.openimaj.experiment.ExperimentContext; 042import org.openimaj.experiment.ExperimentRunner; 043import org.openimaj.experiment.validation.cross.StratifiedGroupedKFold; 044import org.openimaj.image.FImage; 045import org.openimaj.image.processing.face.detection.DetectedFace; 046import org.openimaj.image.processing.face.recognition.FaceRecogniser; 047import org.openimaj.image.processing.face.recognition.FaceRecognitionEngine; 048import org.openimaj.image.processing.face.recognition.benchmarking.CrossValidationBenchmark; 049import org.openimaj.image.processing.face.recognition.benchmarking.FaceRecogniserProvider; 050import org.openimaj.image.processing.face.recognition.benchmarking.dataset.TextFileDataset; 051import org.openimaj.tools.faces.recognition.options.RecognitionEngineProvider; 052import org.openimaj.tools.faces.recognition.options.RecognitionStrategy; 053 054/** 055 * A command line tool for performing cross-validation experiments for face 056 * recognition or classification. {@link StratifiedGroupedKFold} cross 057 * validation is used internally. 058 * 059 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 060 * 061 * @param <FACE> 062 * Type of {@link DetectedFace} 063 */ 064public class FaceRecognitionCrossValidatorTool<FACE extends DetectedFace> { 065 @Option(name = "--strategy", usage = "Recognition strategy", required = false, handler = ProxyOptionHandler.class) 066 RecognitionStrategy strategy = RecognitionStrategy.EigenFaces_KNN; 067 RecognitionEngineProvider<FACE> strategyOp; 068 069 @Option(name = "--dataset", usage = "File formatted as each line being: IDENTIFIER,img", required = true) 070 File datasetFile; 071 072 @Option(name = "--num-folds", usage = "number of cross-validation folds", required = false) 073 int numFolds = 10; 074 075 @Option( 076 name = "--save-recogniser", 077 usage = "After cross-validation, create a recogniser using all folds and save it to the given file", 078 required = false) 079 File savedRecogniser; 080 081 protected void performBenchmark() throws IOException { 082 final FaceRecognitionEngine<FACE, String> engine = strategyOp.createRecognitionEngine(); 083 084 final CrossValidationBenchmark<String, FImage, FACE> benchmark = new CrossValidationBenchmark<String, FImage, FACE>( 085 new StratifiedGroupedKFold<String, FACE>(10), 086 getDataset(), 087 engine.getDetector(), 088 new FaceRecogniserProvider<FACE, String>() { 089 @Override 090 public FaceRecogniser<FACE, String> 091 create(GroupedDataset<String, ? extends ListDataset<FACE>, FACE> dataset) 092 { 093 // Note: we need a new instance of a recogniser, hence 094 // we don't 095 // use the engine object. 096 final FaceRecogniser<FACE, String> rec = strategyOp.createRecognitionEngine().getRecogniser(); 097 098 rec.train(dataset); 099 100 return rec; 101 } 102 103 @Override 104 public String toString() { 105 return engine.getRecogniser().toString(); 106 } 107 } 108 ); 109 110 final ExperimentContext ctx = ExperimentRunner.runExperiment(benchmark); 111 112 System.out.println(ctx); 113 } 114 115 protected void saveRecogniser() throws IOException { 116 final FaceRecognitionEngine<FACE, String> engine = strategyOp.createRecognitionEngine(); 117 engine.train(getDataset()); 118 engine.save(savedRecogniser); 119 } 120 121 private GroupedDataset<String, ListDataset<FImage>, FImage> getDataset() throws IOException { 122 return new TextFileDataset(datasetFile); 123 } 124 125 /** 126 * The main method for the tool 127 * 128 * @param <FACE> 129 * Type of {@link DetectedFace} 130 * @param args 131 * tool arguments 132 * @throws IOException 133 */ 134 public static <FACE extends DetectedFace> void main(String[] args) throws IOException { 135 final FaceRecognitionCrossValidatorTool<FACE> frcv = new FaceRecognitionCrossValidatorTool<FACE>(); 136 final CmdLineParser parser = new CmdLineParser(frcv); 137 138 try { 139 parser.parseArgument(args); 140 } catch (final CmdLineException e) { 141 System.err.println(e.getMessage()); 142 System.err.println("java FaceRecognitionCrossValidator options..."); 143 parser.printUsage(System.err); 144 145 System.err.println(); 146 System.err.println("Strategy information:"); 147 for (final RecognitionStrategy s : RecognitionStrategy.values()) { 148 System.err.println(s); 149 } 150 return; 151 } 152 153 frcv.performBenchmark(); 154 155 if (frcv.savedRecogniser != null) { 156 frcv.saveRecogniser(); 157 } 158 } 159}