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.keypoints; 031 032import java.io.DataInput; 033import java.io.DataOutput; 034import java.io.IOException; 035 036import org.openimaj.image.FImage; 037import org.openimaj.image.processing.face.detection.DetectedFace; 038import org.openimaj.image.processing.face.detection.keypoints.FacialKeypoint.FacialKeypointType; 039import org.openimaj.math.geometry.point.Point2dImpl; 040import org.openimaj.math.geometry.shape.Rectangle; 041 042/** 043 * A K(eypoint)E(nriched)DetectedFace models a face detected by a face detector, 044 * together with the locations of certain facial features localised on the face. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 */ 048public class KEDetectedFace extends DetectedFace { 049 /** 050 * A list of detected facial keypoints. The coordinates are given in terms 051 * of the facePatch image. To project them into the original image you need 052 * to translate by bounds.x and bounds.y. 053 */ 054 protected FacialKeypoint[] keypoints; 055 056 /** 057 * Default constructor 058 */ 059 public KEDetectedFace() { 060 super(); 061 } 062 063 /** 064 * Construct with parameters. 065 * 066 * @param bounds 067 * the bounds rectangle in the detection image 068 * @param patch 069 * the patch 070 * @param keypoints 071 * the detected facial keypoints. 072 * @param confidence 073 * the confidence of detection 074 */ 075 public KEDetectedFace(Rectangle bounds, FImage patch, FacialKeypoint[] keypoints, float confidence) { 076 super(bounds, patch, confidence); 077 078 this.keypoints = keypoints; 079 } 080 081 /** 082 * Get a keypoint of the specified type. 083 * 084 * @param type 085 * the type of keypoint 086 * @return the keypoint, or null if it wasn't found 087 */ 088 public FacialKeypoint getKeypoint(FacialKeypointType type) { 089 return FacialKeypoint.getKeypoint(keypoints, type); 090 } 091 092 /** 093 * Get a keypoint of the specified type, interpolating the position if the 094 * internal model doesn't have a matching point. Currently only the 095 * {@link FacialKeypointType#MOUTH_CENTER}, 096 * {@link FacialKeypointType#EYE_LEFT_CENTER} and 097 * {@link FacialKeypointType#EYE_RIGHT_CENTER} points are supported iff the 098 * corresponding left and right points are available. 099 * 100 * @param type 101 * the type of keypoint 102 * @return the keypoint, or null if it wasn't found 103 */ 104 public FacialKeypoint getKeypointInterpolated(FacialKeypointType type) { 105 final FacialKeypoint kpt = getKeypoint(type); 106 107 if (kpt == null) { 108 switch (type) { 109 case EYE_LEFT_CENTER: 110 return createInterpolated(type, getKeypoint(FacialKeypointType.EYE_LEFT_LEFT), 111 getKeypoint(FacialKeypointType.EYE_LEFT_RIGHT)); 112 case EYE_RIGHT_CENTER: 113 return createInterpolated(type, getKeypoint(FacialKeypointType.EYE_RIGHT_LEFT), 114 getKeypoint(FacialKeypointType.EYE_RIGHT_RIGHT)); 115 case MOUTH_CENTER: 116 return createInterpolated(type, getKeypoint(FacialKeypointType.MOUTH_LEFT), 117 getKeypoint(FacialKeypointType.MOUTH_RIGHT)); 118 default: 119 break; 120 } 121 } 122 return null; 123 } 124 125 private FacialKeypoint createInterpolated(FacialKeypointType type, FacialKeypoint left, FacialKeypoint right) 126 { 127 if (left == null || right == null) 128 return null; 129 130 final float x = right.position.x - left.position.x; 131 final float y = right.position.y - left.position.y; 132 133 return new FacialKeypoint(type, new Point2dImpl(x, y)); 134 } 135 136 /** 137 * @return the {@link FacialKeypoint}s associated with this detection 138 */ 139 public FacialKeypoint[] getKeypoints() { 140 return keypoints; 141 } 142 143 @Override 144 public void writeBinary(DataOutput out) throws IOException { 145 super.writeBinary(out); 146 147 out.writeInt(keypoints.length); 148 for (final FacialKeypoint k : keypoints) 149 k.writeBinary(out); 150 } 151 152 @Override 153 public byte[] binaryHeader() { 154 return "KEDF".getBytes(); 155 } 156 157 @Override 158 public void readBinary(DataInput in) throws IOException { 159 super.readBinary(in); 160 161 final int sz = in.readInt(); 162 keypoints = new FacialKeypoint[sz]; 163 for (int i = 0; i < sz; i++) { 164 keypoints[i] = new FacialKeypoint(); 165 keypoints[i].readBinary(in); 166 } 167 } 168}