View Javadoc

1   /**
2    * FaceTracker Licence
3    * -------------------
4    * (Academic, non-commercial, not-for-profit licence)
5    *
6    * Copyright (c) 2010 Jason Mora Saragih
7    * All rights reserved.
8    *
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions are met:
11   *
12   *     * The software is provided under the terms of this licence stricly for
13   *       academic, non-commercial, not-for-profit purposes.
14   *     * Redistributions of source code must retain the above copyright notice,
15   *       this list of conditions (licence) and the following disclaimer.
16   *     * Redistributions in binary form must reproduce the above copyright
17   *       notice, this list of conditions (licence) and the following disclaimer
18   *       in the documentation and/or other materials provided with the
19   *       distribution.
20   *     * The name of the author may not be used to endorse or promote products
21   *       derived from this software without specific prior written permission.
22   *     * As this software depends on other libraries, the user must adhere to and
23   *       keep in place any licencing terms of those libraries.
24   *     * Any publications arising from the use of this software, including but
25   *       not limited to academic journal and conference publications, technical
26   *       reports and manuals, must cite the following work:
27   *
28   *       J. M. Saragih, S. Lucey, and J. F. Cohn. Face Alignment through Subspace
29   *       Constrained Mean-Shifts. International Journal of Computer Vision
30   *       (ICCV), September, 2009.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
33   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
35   * EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
36   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
37   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
39   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
41   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package com.jsaragih;
44  
45  import java.io.BufferedReader;
46  import java.io.BufferedWriter;
47  import java.io.FileNotFoundException;
48  import java.io.FileReader;
49  import java.io.FileWriter;
50  import java.io.IOException;
51  import java.util.Scanner;
52  
53  import org.openimaj.image.FImage;
54  import org.openimaj.image.analysis.algorithm.TemplateMatcher;
55  import org.openimaj.image.analysis.algorithm.TemplateMatcher.Mode;
56  
57  /**
58   * A patch on a face
59   * 
60   * @author Jason Mora Saragih
61   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
62   */
63  public class Patch {
64  	static { Tracker.init(); }
65  	
66  	/** Type of patch (0=raw, 1=grad, 2=lbp) */
67  	public int _t;
68  
69  	/** scaling */
70  	public double _a;
71  
72  	/** bias */
73  	public double _b;
74  
75  	/** Gain */
76  	public FImage _W;
77  
78  	protected FImage im_ = new FImage(0, 0);
79  	protected TemplateMatcher matcher;
80  
81  	FImage Grad(FImage im) {
82  		FImage grad = new FImage(im.width, im.height);
83  
84  		for (int y = 1; y < im.height - 1; y++) {
85  			for (int x = 1; x < im.width - 1; x++) {
86  				float vx = im.pixels[y][x + 1] - im.pixels[y][x - 1];
87  				float vy = im.pixels[y + 1][x] - im.pixels[y - 1][x];
88  				grad.pixels[y][x] = vx * vx + vy * vy;
89  			}
90  		}
91  		return grad;
92  	}
93  
94  	final float SGN(float x) {
95  		return (x < 0) ? 0 : 1;
96  	}
97  
98  	FImage LBP(FImage im) {
99  		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 }