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.detection;
031
032import java.util.List;
033import java.util.Set;
034
035import org.openimaj.feature.DoubleFV;
036import org.openimaj.feature.FeatureVector;
037import org.openimaj.feature.MultidimensionalIntFV;
038import org.openimaj.image.Image;
039import org.openimaj.image.pixel.ConnectedComponent;
040import org.openimaj.image.pixel.Pixel;
041import org.openimaj.math.geometry.shape.Polygon;
042
043/**
044 * Simple features that can be extracted from a list of detected faces and an
045 * image. Contains things like the count of faces, bounding boxes, etc.
046 * 
047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
048 * 
049 */
050public enum FaceDetectorFeatures {
051        /**
052         * Count the faces in the image. Returns the count as a single element,
053         * 1-dimensional {@link MultidimensionalIntFV}.
054         * 
055         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
056         */
057        COUNT {
058                @Override
059                public <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img) {
060                        return new MultidimensionalIntFV(new int[] { faces.size() }, 1);
061                }
062        },
063        /**
064         * Get the set of pixels describing each face. Returns the result as a 2d
065         * {@link MultidimensionalIntFV} where each row has interlaced x and y
066         * values: x1, y1, x2, y2, ...
067         * 
068         * The returned feature is not square; the length of each row is dependent
069         * on the number of pixels associated with the respective face.
070         * 
071         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
072         */
073        BLOBS {
074                @Override
075                public <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img) {
076                        final int[][] fvs = new int[faces.size()][];
077                        int i = 0;
078
079                        for (final DetectedFace df : faces) {
080                                final Set<Pixel> pixels = getConnectedComponent(df).pixels;
081
082                                final int[] fv = new int[pixels.size() * 2];
083
084                                int j = 0;
085                                for (final Pixel p : pixels) {
086                                        fv[j++] = p.x;
087                                        fv[j++] = p.y;
088                                }
089
090                                fvs[i++] = fv;
091                        }
092
093                        return new MultidimensionalIntFV(fvs);
094                }
095        },
096        /**
097         * Get the bounding box describing each face. The bounding boxes are encoded
098         * as a 2D {@link MultidimensionalIntFV} with each row corresponding to a
099         * face. Each row is encoded as the x, y, width, height values of the
100         * bounding box.
101         * 
102         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
103         */
104        BOX {
105                @Override
106                public <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img) {
107                        final int[][] fvs = new int[faces.size()][];
108                        int i = 0;
109
110                        for (final DetectedFace df : faces) {
111                                fvs[i++] = new int[] {
112                                                (int) df.getBounds().x,
113                                                (int) df.getBounds().y,
114                                                (int) df.getBounds().width,
115                                                (int) df.getBounds().height
116                                };
117                        }
118
119                        return new MultidimensionalIntFV(fvs);
120                }
121        },
122        /**
123         * Get the oriented bounding box describing each face. The bounding boxes
124         * are encoded as a 2D {@link MultidimensionalIntFV} with each row
125         * corresponding to a face. Each row is encoded as the the four corners of
126         * the bounding box: x1, y1, x2, y2, x3, y3, x4, y4.
127         * 
128         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
129         */
130        ORIBOX {
131                @Override
132                public <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img) {
133                        final int[][] fvs = new int[faces.size()][];
134                        int i = 0;
135
136                        for (final DetectedFace df : faces) {
137                                final Polygon p = getConnectedComponent(df).calculateOrientatedBoundingBox().asPolygon();
138
139                                final int[] fv = new int[p.getVertices().size() * 2];
140
141                                for (int j = 0, k = 0; j < fv.length; j += 2, k++) {
142                                        fv[j] = (int) p.getVertices().get(k).getX();
143                                        fv[j + 1] = (int) p.getVertices().get(k).getY();
144                                }
145
146                                fvs[i++] = fv;
147                        }
148
149                        return new MultidimensionalIntFV(fvs);
150                }
151        },
152        /**
153         * Get the relative area of each detected face (normalised by the image
154         * area). The returned feature is a 1D {@link DoubleFV} with each element
155         * corresponding to an individual face detection.
156         * 
157         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
158         */
159        AREA {
160                @Override
161                public <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img) {
162                        final double[] fv = new double[faces.size()];
163                        final double area = img.getWidth() * img.getHeight();
164                        int i = 0;
165
166                        for (final DetectedFace df : faces) {
167                                fv[i++] = getConnectedComponent(df).calculateArea() / area;
168                        }
169
170                        return new DoubleFV(fv);
171                }
172        };
173
174        protected ConnectedComponent getConnectedComponent(DetectedFace df) {
175                if (df instanceof CCDetectedFace) {
176                        return ((CCDetectedFace) df).connectedComponent;
177                } else {
178                        return new ConnectedComponent(df.getBounds());
179                }
180        }
181
182        /**
183         * Compute a feature vector describing the detections.
184         * 
185         * @param <T>
186         *            Type of {@link Image}
187         * @param faces
188         *            The detected faces.
189         * @param img
190         *            The image the faces were detected from.
191         * @return a feature vector.
192         */
193        public abstract <T extends Image<?, T>> FeatureVector getFeatureVector(List<? extends DetectedFace> faces, T img);
194}