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.io.ReadWriteableBinary;
037import org.openimaj.math.geometry.point.Point2d;
038import org.openimaj.math.geometry.point.Point2dImpl;
039
040import Jama.Matrix;
041
042/**
043 * A {@link FacialKeypoint} represents a keypoint on a face. Keypoints
044 * are representative points found on all faces.
045 * <p>
046 * Keypoint types are based on Mark Everingham's 
047 * <a href="http://www.robots.ox.ac.uk/~vgg/research/nface/">Oxford VGG 
048 * Baseline Face Processing Code</a>
049 *  
050 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
051 */
052public class FacialKeypoint implements ReadWriteableBinary {
053        /**
054         * Types/locations of {@link FacialKeypoint}.
055         * 
056         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
057         */
058        public static enum FacialKeypointType {
059                /**
060                 * Left of the left eye
061                 */
062                EYE_LEFT_LEFT,
063                /**
064                 * Right of the left eye 
065                 */
066                EYE_LEFT_RIGHT,
067                /**
068                 * Left of the right eye
069                 */
070                EYE_RIGHT_LEFT,
071                /**
072                 * Right of the left eye
073                 */
074                EYE_RIGHT_RIGHT,
075                /**
076                 * Left-bottom of the nose
077                 */
078                NOSE_LEFT,
079                /**
080                 * Bottom-middle of the nose
081                 */
082                NOSE_MIDDLE,
083                /**
084                 * Bottom-right of the nose
085                 */
086                NOSE_RIGHT,
087                /**
088                 * Left corner of the mouth
089                 */
090                MOUTH_LEFT,
091                /**
092                 * Right corner of the mouth
093                 */
094                MOUTH_RIGHT,
095                /**
096                 * Centre of the left eye
097                 */
098                EYE_LEFT_CENTER,
099                /**
100                 * Centre of the right eye
101                 */
102                EYE_RIGHT_CENTER,
103                /**
104                 * Bridge of the nose
105                 */
106                NOSE_BRIDGE,
107                /**
108                 * Centre of the mouth
109                 */
110                MOUTH_CENTER
111                ;
112                
113                /**
114                 * Get the {@link FacialKeypointType} at the specified ordinal
115                 * @param ordinal the ordinal
116                 * @return the corresponding {@link FacialKeypointType}
117                 */
118                public static FacialKeypointType valueOf(int ordinal) {
119                        return FacialKeypointType.values()[ordinal]; 
120                }
121        }
122
123        /**
124         * The type of facial keypoint
125         */
126        public FacialKeypointType type;
127        
128        /**
129         * The position of the keypoint in the image
130         */
131        public Point2dImpl position;
132        
133        /**
134         * Default constructor. Sets type to {@link FacialKeypointType#EYE_LEFT_CENTER}
135         * and position to the origin.
136         */
137        public FacialKeypoint() {
138                this.type = FacialKeypointType.EYE_LEFT_CENTER;
139                position = new Point2dImpl(0, 0);
140        }
141        
142        /**
143         * Construct a FacialKeypoint at the origin with the specified type.
144         * @param type the type of facial keypoint
145         */
146        public FacialKeypoint(FacialKeypointType type) {
147                this.type = type;
148                position = new Point2dImpl(0, 0);
149        }
150        
151        /**
152         * Construct a FacialKeypoint with the specified type and position.
153         * @param type the type of facial keypoint
154         * @param pt the position in the image of the facial keypoint
155         */
156        public FacialKeypoint(FacialKeypointType type, Point2d pt) {
157                this.type = type;
158                position = new Point2dImpl(pt);
159        }
160        
161        protected void updatePosition(Matrix transform) {
162                position = position.transform(transform);
163        }
164        
165        protected static void updateImagePosition(FacialKeypoint[] kpts, Matrix transform) {
166                for (FacialKeypoint k : kpts) k.updatePosition(transform);
167        }
168
169        /**
170         * Search the given points for the a keypoint with the 
171         * specified type and return it.
172         * 
173         * @param pts the points to search
174         * @param type the type of facial keypoint 
175         * @return the selected keypoint; or null if not found
176         */
177        public static FacialKeypoint getKeypoint(FacialKeypoint[] pts, FacialKeypointType type) {
178                for (FacialKeypoint fk : pts) {
179                        if (fk.type == type)
180                                return fk;
181                }
182                return null;
183        }
184
185        @Override
186        public void readBinary(DataInput in) throws IOException {
187                type = FacialKeypointType.valueOf(in.readUTF());
188                position.readBinary(in);
189        }
190
191        @Override
192        public byte[] binaryHeader() {
193                return this.getClass().getName().getBytes();
194        }
195
196        @Override
197        public void writeBinary(DataOutput out) throws IOException {
198                out.writeUTF(type.name());
199                position.writeBinary(out);
200        }
201}