001/**
002 * FaceTracker Licence
003 * -------------------
004 * (Academic, non-commercial, not-for-profit licence)
005 *
006 * Copyright (c) 2010 Jason Mora Saragih
007 * All rights reserved.
008 *
009 * Redistribution and use in source and binary forms, with or without
010 * modification, are permitted provided that the following conditions are met:
011 *
012 *     * The software is provided under the terms of this licence stricly for
013 *       academic, non-commercial, not-for-profit purposes.
014 *     * Redistributions of source code must retain the above copyright notice,
015 *       this list of conditions (licence) and the following disclaimer.
016 *     * Redistributions in binary form must reproduce the above copyright
017 *       notice, this list of conditions (licence) and the following disclaimer
018 *       in the documentation and/or other materials provided with the
019 *       distribution.
020 *     * The name of the author may not be used to endorse or promote products
021 *       derived from this software without specific prior written permission.
022 *     * As this software depends on other libraries, the user must adhere to and
023 *       keep in place any licencing terms of those libraries.
024 *     * Any publications arising from the use of this software, including but
025 *       not limited to academic journal and conference publications, technical
026 *       reports and manuals, must cite the following work:
027 *
028 *       J. M. Saragih, S. Lucey, and J. F. Cohn. Face Alignment through Subspace
029 *       Constrained Mean-Shifts. International Journal of Computer Vision
030 *       (ICCV), September, 2009.
031 *
032 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
033 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
034 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
035 * EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
036 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
037 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
038 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
039 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
040 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
041 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042 */
043package com.jsaragih;
044
045import java.io.BufferedReader;
046import java.io.BufferedWriter;
047import java.io.FileNotFoundException;
048import java.io.FileReader;
049import java.io.FileWriter;
050import java.io.IOException;
051import java.util.Scanner;
052
053import org.openimaj.image.FImage;
054import org.openimaj.math.matrix.MatrixUtils;
055
056import Jama.Matrix;
057
058/**
059 * Check whether a patch looks like a face by applying an SVM classifier
060 * 
061 * @author Jason Mora Saragih
062 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
063 */
064public class FCheck {
065        static { Tracker.init(); }
066        
067        /** < Piecewise affine warp */
068        PAW _paw;
069        
070        /** < SVM bias */
071        double _b;
072        
073        /** < SVM gain */
074        Matrix _w;
075
076        private Matrix vec_;
077        private FImage crop_;
078
079        /**
080         * Construct
081         * 
082         * @param b
083         * @param w
084         * @param paw
085         */
086        FCheck(double b, Matrix w, PAW paw) {
087                assert ((w.getRowDimension() == paw._nPix));
088
089                _b = b;
090                _w = w.copy();
091                _paw = paw;
092
093                crop_ = new FImage(_paw._mask.width, _paw._mask.height);
094                vec_ = new Matrix(_paw._nPix, 1);
095        }
096
097        protected FCheck() {
098        }
099
100        static FCheck load(final String fname) throws FileNotFoundException {
101                BufferedReader br = null;
102                try {
103                        br = new BufferedReader(new FileReader(fname));
104                        Scanner sc = new Scanner(br);
105                        return read(sc, true);
106                } finally {
107                        try {
108                                br.close();
109                        } catch (IOException e) {
110                        }
111                }
112        }
113
114        void save(final String fname) throws IOException {
115                BufferedWriter bw = null;
116                try {
117                        bw = new BufferedWriter(new FileWriter(fname));
118
119                        write(bw);
120                } finally {
121                        try {
122                                if (bw != null)
123                                        bw.close();
124                        } catch (IOException e) {
125                        }
126                }
127        }
128
129        void write(BufferedWriter s) throws IOException {
130                s.write(IO.Types.FCHECK.ordinal() + " " + _b + " ");
131                IO.writeMat(s, _w);
132                _paw.write(s);
133        }
134
135        static FCheck read(Scanner s, boolean readType) {
136                if (readType) {
137                        int type = s.nextInt();
138                        assert (type == IO.Types.FCHECK.ordinal());
139                }
140
141                FCheck fcheck = new FCheck();
142
143                fcheck._b = s.nextDouble();
144                fcheck._w = IO.readMat(s);
145                fcheck._paw = PAW.read(s, true);
146                fcheck.crop_ = new FImage(fcheck._paw._mask.width,
147                                fcheck._paw._mask.height);
148                fcheck.vec_ = new Matrix(fcheck._paw._nPix, 1);
149
150                return fcheck;
151        }
152
153        boolean check(FImage im, Matrix s) {
154                assert ((s.getRowDimension() / 2 == _paw.nPoints()) && (s
155                                .getColumnDimension() == 1));
156
157                _paw.crop(im, crop_, s);
158
159                if ((vec_.getRowDimension() != _paw._nPix)
160                                || (vec_.getColumnDimension() != 1))
161                        vec_ = new Matrix(_paw._nPix, 1);
162
163                final int w = crop_.width;
164                final int h = crop_.height;
165
166                final double[][] vp = vec_.getArray();
167                final float[][] cp = crop_.pixels;
168                final float[][] mp = _paw._mask.pixels;
169
170                for (int i = 0, k = 0; i < h; i++) {
171                        for (int j = 0; j < w; j++) {
172                                if (mp[i][j] != 0) {
173                                        vp[k][0] = cp[i][j];
174                                        k++;
175                                }
176                        }
177                }
178
179                double mean = MatrixUtils.sum(vec_) / vec_.getRowDimension();
180                MatrixUtils.minus(vec_, mean);
181
182                double var = 0;
183                for (int i = 0; i < _paw._nPix; i++)
184                        var += vec_.get(i, 0) * vec_.get(i, 0);
185
186                if (var < 1.0e-10)
187                        MatrixUtils.fill(vec_, 0);
188                else
189                        vec_ = vec_.times(1 / Math.sqrt(var)); // FIXME inline
190
191                double wdv = 0;
192                for (int i = 0; i < _paw._nPix; i++)
193                        wdv += _w.get(i, 0) * vec_.get(i, 0);
194
195                if ((wdv + _b) > 0)
196                        return true;
197
198                return false;
199        }
200}