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}