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}