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.examples.image.faces; 031 032import java.io.IOException; 033import java.net.URL; 034import java.util.Map; 035import java.util.Map.Entry; 036 037import org.openimaj.feature.FloatFV; 038import org.openimaj.feature.FloatFVComparison; 039import org.openimaj.image.DisplayUtilities; 040import org.openimaj.image.FImage; 041import org.openimaj.image.ImageUtilities; 042import org.openimaj.image.processing.face.detection.HaarCascadeDetector; 043import org.openimaj.image.processing.face.detection.keypoints.FKEFaceDetector; 044import org.openimaj.image.processing.face.detection.keypoints.KEDetectedFace; 045import org.openimaj.image.processing.face.feature.FacePatchFeature; 046import org.openimaj.image.processing.face.feature.FacePatchFeature.Extractor; 047import org.openimaj.image.processing.face.feature.comparison.FaceFVComparator; 048import org.openimaj.image.processing.face.similarity.FaceSimilarityEngine; 049import org.openimaj.math.geometry.shape.Rectangle; 050 051/** 052 * Example showing how to use the {@link FaceSimilarityEngine} class to compare 053 * faces detected in two images. 054 * 055 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 056 * 057 */ 058public class FaceSimilarity { 059 /** 060 * Main method for the example. 061 * 062 * @param args 063 * Ignored. 064 * @throws IOException 065 */ 066 public static void main(String[] args) throws IOException { 067 // first, we load two images 068 final URL image1url = new URL( 069 "http://s3.amazonaws.com/rapgenius/fema_-_39841_-_official_portrait_of_president-elect_barack_obama_on_jan-_13.jpg"); 070 final URL image2url = new URL( 071 "http://nimg.sulekha.com/others/thumbnailfull/barack-obama-michelle-obama-mary-mcaleese-martin-mcaleese-2011-5-23-6-50-0.jpg"); 072 073 final FImage image1 = ImageUtilities.readF(image1url); 074 final FImage image2 = ImageUtilities.readF(image2url); 075 076 // then we set up a face detector; will use a haar cascade detector to 077 // find faces, followed by a keypoint-enhanced detector to find facial 078 // keypoints for our feature. There are many different combinations of 079 // features and detectors to choose from. 080 final HaarCascadeDetector detector = HaarCascadeDetector.BuiltInCascade.frontalface_alt2.load(); 081 final FKEFaceDetector kedetector = new FKEFaceDetector(detector); 082 083 // now we construct a feature extractor - this one will extract pixel 084 // patches around prominant facial keypoints (like the corners of the 085 // mouth, etc) and build them into a vector. 086 final Extractor extractor = new FacePatchFeature.Extractor(); 087 088 // in order to compare the features we need a comparator. In this case, 089 // we'll use the Euclidean distance between the vectors: 090 final FaceFVComparator<FacePatchFeature, FloatFV> comparator = 091 new FaceFVComparator<FacePatchFeature, FloatFV>(FloatFVComparison.EUCLIDEAN); 092 093 // Now we can construct the FaceSimilarityEngine. It is capable of 094 // running the face detector on a pair of images, extracting the 095 // features and then comparing every pair of detected faces in the two 096 // images: 097 final FaceSimilarityEngine<KEDetectedFace, FacePatchFeature, FImage> engine = 098 new FaceSimilarityEngine<KEDetectedFace, FacePatchFeature, FImage>(kedetector, extractor, comparator); 099 100 // we need to tell the engine to use our images: 101 engine.setQuery(image1, "image1"); 102 engine.setTest(image2, "image2"); 103 104 // and then to do its work of detecting, extracting and comparing 105 engine.performTest(); 106 107 // finally, for this example, we're going to display the "best" matching 108 // faces in the two images. The following loop goes through the map of 109 // each face in the first image to all the faces in the second: 110 for (final Entry<String, Map<String, Double>> e : engine.getSimilarityDictionary().entrySet()) { 111 // this computes the matching face in the second image with the 112 // smallest distance: 113 double bestScore = Double.MAX_VALUE; 114 String best = null; 115 for (final Entry<String, Double> matches : e.getValue().entrySet()) { 116 if (matches.getValue() < bestScore) { 117 bestScore = matches.getValue(); 118 best = matches.getKey(); 119 } 120 } 121 122 // and this composites the original two images together, and draws 123 // the matching pair of faces: 124 final FImage img = new FImage(image1.width + image2.width, Math.max(image1.height, image2.height)); 125 img.drawImage(image1, 0, 0); 126 img.drawImage(image2, image1.width, 0); 127 128 img.drawShape(engine.getBoundingBoxes().get(e.getKey()), 1F); 129 130 final Rectangle r = engine.getBoundingBoxes().get(best); 131 r.translate(image1.width, 0); 132 img.drawShape(r, 1F); 133 134 // and finally displays the result 135 DisplayUtilities.display(img); 136 } 137 } 138}