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.image.analysis.algorithm.TemplateMatcher;
055import org.openimaj.image.analysis.algorithm.TemplateMatcher.Mode;
056
057/**
058 * A patch on a face
059 * 
060 * @author Jason Mora Saragih
061 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
062 */
063public class Patch {
064        static { Tracker.init(); }
065        
066        /** Type of patch (0=raw, 1=grad, 2=lbp) */
067        public int _t;
068
069        /** scaling */
070        public double _a;
071
072        /** bias */
073        public double _b;
074
075        /** Gain */
076        public FImage _W;
077
078        protected FImage im_ = new FImage(0, 0);
079        protected TemplateMatcher matcher;
080
081        FImage Grad(FImage im) {
082                FImage grad = new FImage(im.width, im.height);
083
084                for (int y = 1; y < im.height - 1; y++) {
085                        for (int x = 1; x < im.width - 1; x++) {
086                                float vx = im.pixels[y][x + 1] - im.pixels[y][x - 1];
087                                float vy = im.pixels[y + 1][x] - im.pixels[y - 1][x];
088                                grad.pixels[y][x] = vx * vx + vy * vy;
089                        }
090                }
091                return grad;
092        }
093
094        final float SGN(float x) {
095                return (x < 0) ? 0 : 1;
096        }
097
098        FImage LBP(FImage im) {
099                FImage lp = new FImage(im.width, im.height);
100
101                // float [] v = new float[9];
102                // for(int y = 1; y < im.height-1; y++) {
103                // for(int x = 1; x < im.width-1; x++) {
104                // v[4] = im.pixels[y][x-1];
105                // v[0] = im.pixels[y][x];
106                // v[5] = im.pixels[y][x+1];
107                // v[1] = im.pixels[y-1][x-1];
108                // v[2] = im.pixels[y-1][x];
109                // v[3] = im.pixels[y-1][x+1];
110                // v[6] = im.pixels[y+1][x-1];
111                // v[7] = im.pixels[y+1][x];
112                // v[8] = im.pixels[y+1][x+1];
113                //
114                // lp.pixels[y][x] =
115                // SGN(v[0]-v[1])*2 + SGN(v[0]-v[2])*4 +
116                // SGN(v[0]-v[3])*8 + SGN(v[0]-v[4])*16 +
117                // SGN(v[0]-v[5])*32 + SGN(v[0]-v[6])*64 +
118                // SGN(v[0]-v[7])*128 + SGN(v[0]-v[8])*256 ;
119                // }
120                // }
121
122                return lp;
123        }
124
125        void load(final String fname) throws FileNotFoundException {
126                BufferedReader br = null;
127                try {
128                        br = new BufferedReader(new FileReader(fname));
129                        Scanner sc = new Scanner(br);
130                        read(sc, true);
131                } finally {
132                        try {
133                                br.close();
134                        } catch (IOException e) {
135                        }
136                }
137        }
138
139        void save(final String fname) throws IOException {
140                BufferedWriter bw = null;
141                try {
142                        bw = new BufferedWriter(new FileWriter(fname));
143
144                        write(bw);
145                } finally {
146                        try {
147                                if (bw != null)
148                                        bw.close();
149                        } catch (IOException e) {
150                        }
151                }
152        }
153
154        void write(BufferedWriter s) throws IOException {
155                s.write(IO.Types.PATCH.ordinal() + " " + _t + " " + _a + " " + _b + " ");
156                IO.writeImg(s, _W);
157        }
158
159        static Patch read(Scanner s, boolean readType) {
160                if (readType) {
161                        int type = s.nextInt();
162                        assert (type == IO.Types.PATCH.ordinal());
163                }
164
165                Patch p = new Patch(); // s.nextInt(), s.nextDouble(), s.nextDouble(),
166                                                                // IO.ReadImg(s) );
167                p._t = s.nextInt();
168                p._a = s.nextDouble();
169                p._b = s.nextDouble();
170                p._W = IO.readImg(s);
171                p.matcher = new TemplateMatcher(p._W.clone(),
172                                Mode.NORM_CORRELATION_COEFFICIENT);
173
174                return p;
175        }
176
177        Patch() {
178        }
179
180        /**
181         * @param t
182         * @param a
183         * @param b
184         * @param W
185         */
186        public Patch(int t, double a, double b, FImage W) {
187                _t = t;
188                _a = a;
189                _b = b;
190                _W = W;
191                matcher = new TemplateMatcher(W.clone(),
192                                Mode.NORM_CORRELATION_COEFFICIENT);
193        }
194
195        void response(FImage im, FImage resp) {
196                assert ((im.height >= _W.height) && (im.width >= _W.width));
197
198                int h = im.height - _W.height + 1;
199                int w = im.width - _W.width + 1;
200
201                if (resp.height != h || resp.width != w)
202                        resp.internalAssign(new FImage(w, h));
203
204                FImage I;
205                if (_t == 0) {
206                        I = im;
207                } else {
208                        if (im_.height == im.height && im_.width == im.width) {
209                                I = im_;
210                        } else if (im_.height >= im.height && im_.width >= im.width) {
211                                I = im_.extractROI(0, 0, im.width, im.height);
212                        } else {
213                                im_ = new FImage(im.width, im.height);
214                                I = im_;
215                        }
216
217                        if (_t == 1) {
218                                I = Grad(im);
219                        } else if (_t == 2) {
220                                I = LBP(im);
221                        } else {
222                                throw new RuntimeException("ERROR: Unsupported patch type!\n");
223                        }
224                }
225
226                matcher.analyseImage(I);
227                FImage res = matcher.getResponseMap();
228
229                for (int y = 0; y < resp.height; y++)
230                        for (int x = 0; x < resp.width; x++)
231                                resp.pixels[y][x] = (float) (1.0 / (1.0 + Math
232                                                .exp(res.pixels[y][x] * _a + _b)));
233        }
234
235        /**
236         * Returns a copy of this patch
237         * 
238         * @return a copy of this patch.
239         */
240        public Patch copy() {
241                return new Patch(_t, _a, _b, _W);
242        }
243}