/Users/lyon/j4p/src/face/FaceBundle.java
|
1 package face;
2
3 import java.io.Serializable;
4
5 /**
6 * The face-space bundle generated by {@link EigenFaceComputation}.
7 * This bundle has all the relevant information to try to match an image
8 * The computation to be done (to find the submitted image in this
9 * bundles face-space) is q?uite fast.
10 * For more about the algorithm, consult (<a href="
11 * <a href="http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf">
12 * http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf</a>">
13 * <a href="http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf">
14 * http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf</a></a>)
15 *
16 * Each FaceBundle contains
17 * sixteen images and their identifying string.
18 * <br><b>NOTE</b>: This object is serializable, therefere its possible
19 * to cache these face-spaces thus eliminating the computation process
20 * in {@link EigenFaceComputation}.
21 *
22 *
23 *
24 * @author Konrad Rzeszutek
25 * @version 1.0
26 */
27 public class FaceBundle implements Serializable, Comparable {
28
29 private double[] avgFace = null;
30 private double[] cmpFace = null;
31 private double[][] eigVector = null;
32 private double[][] wk = null;
33 private String[] id = null;
34 private transient double minD = Double.MAX_VALUE;
35 /**
36 * The length of the vector-images stored in the face-space bundle. The
37 * submitted image <b>MUST</b> of the same length or greater (if its greater
38 * only <code>length</code> will be considered).
39 */
40 public int length = Integer.MIN_VALUE;
41 private transient boolean computed = false;
42 private transient int idx = Integer.MAX_VALUE;
43
44 /**
45 * The face-space object containing:
46 *
47 * <ol>
48 * <li>An average face array.
49 * <li>Eigenspace of images
50 * <li>Face space of images
51 * <li>List of names identifying each image
52 * </ol>
53 *
54 * @param avgF Average face (used to normalize the image to be matched against)
55 * @param wk The eigenface componenets (projected onto the eigenspace)
56 * @param eigV The eigenspace (onto which the matched image will be projected too)
57 * @param files A String array representing each of the sixteen images represented
58 * by this face-space.
59 *
60 */
61 public FaceBundle(double[] avgF, double wk[][], double[][] eigV, String[] files) {
62
63 avgFace = new double[avgF.length];
64 this.wk = new double[wk.length][wk[0].length];
65 eigVector = new double[eigV.length][eigV[0].length];
66 //this.id = new String[files.length];
67 length = avgFace.length;
68
69 System.arraycopy(avgF, 0, this.avgFace, 0, avgFace.length);
70 System.arraycopy(eigV, 0, this.eigVector, 0, eigVector.length);
71 System.arraycopy(wk, 0, this.wk, 0, wk.length);
72 //System.arraycopy(files, 0,this.id, 0, id.length);
73
74 this.id = files;
75 }
76
77 /**
78 * Submit an image of matching against the face-space.
79 * The results are published in <code>distance()</b> and <code>getID()</code>.
80 *
81 * @param face The vector-array of the image. The image must be off <b>length</b>
82 *
83 */
84 public void submitFace(byte[] face) {
85 // Convert it to double.
86 for (int i = 0; i < length; i++)
87 cmpFace[i] = (double) (face[i] & 0xFF);
88
89 compute();
90 }
91
92 /**
93 * Submit an image of matching against the face-space.
94 * The results are published in <code>distance()</b> and <code>getID()</code>.
95 *
96 * @param face The vector-array of the image. The image must be off <b>length</b>
97 *
98 */
99 public void submitFace(int[] face) {
100
101 for (int i = 0; i < length; i++)
102 cmpFace[i] = face[i];
103
104 compute();
105 }
106
107 /**
108 * Submit an image of matching against the face-space.
109 * The results are published in
110 * <code>distance()</b> and <code>getID()</code>.
111 *
112 * @param face The vector-array of the image.
113 * The image must be off <b>length</b>
114 *
115 */
116 public void submitFace(double[] face) {
117
118 this.cmpFace = face;
119 compute();
120
121
122 }
123
124 /**
125 * Clear the submitted image from the face-space object.
126 */
127 public void clearFace() {
128
129 cmpFace = null;
130 computed = false;
131 idx = Integer.MAX_VALUE;
132 minD = Double.MAX_VALUE;
133 }
134
135 /**
136 * The distance of how far away the submitted image is in this
137 * face-space object. Consult <code>getID()</code> for the name
138 * of the image that it was most near too.
139 *
140 * @return >= 0 or if no image has been submitted: <code>Double.MAX_VALUE</code>
141 */
142 public double distance() {
143
144 return minD;
145 }
146
147 /**
148 * The ID of the submitted image in this face-space object.
149 *
150 * @return A string, but if no image has been submitted a ArrayOutOfBoundException
151 *
152 */
153 public String getID() {
154
155 return this.id[idx];
156 }
157
158 /**
159 * All the names of the images in this face-space.
160 *
161 * @return getNames().length == 16
162 */
163 public String[] getNames() {
164 return id;
165 }
166
167 /**
168 * Compare this face-space bundle to another. If this bundle has
169 * a smaller distance than the other, -1 is returned. 1 if its opposite.
170 * <b>NOTE</b>: There is no checking if the other face-space bundle
171 * has computed its values for the same image.
172 */
173 public int compareTo(Object o) {
174
175
176 if (((FaceBundle) o).minD > minD)
177 return 1;
178 if (((FaceBundle) o).minD < minD)
179 return -1;
180
181 return 0;
182 }
183
184 /**
185 * Get a string representation.
186 */
187 public String toString() {
188
189 if (computed)
190 return "[" + id[idx] + "] with " + minD;
191 return "No image supplied";
192 }
193
194 /**
195 * Do the computation..
196 *
197 */
198 private void compute() {
199
200
201 double[] inputFace = new double[length];
202 int nrfaces = eigVector.length;
203 int MAGIC_NR = wk[0].length;
204 int j, pix, image;
205
206 computed = false;
207 System.arraycopy(cmpFace, 0, inputFace, 0, length);
208
209 for (pix = 0; pix < inputFace.length; pix++) {
210 inputFace[pix] = inputFace[pix] - avgFace[pix];
211 }
212
213 //System.out.println(nrfaces+" "+inputFace.length);
214 double[] input_wk = new double[MAGIC_NR];
215 double temp = 0;
216 /* Subtract the image from the average image */
217 for (j = 0; j < MAGIC_NR; j++) {
218 temp = 0.0;
219 for (pix = 0; pix < length; pix++)
220 temp += eigVector[j][pix] * inputFace[pix];
221
222 input_wk[j] = Math.abs(temp);
223 }
224
225 /*
226 * Find the minimun distance from the input_wk as compared to wk
227 */
228
229
230 double[] distance = new double[MAGIC_NR];
231 double[] minDistance = new double[MAGIC_NR];
232 idx = 0;
233 for (image = 0; image < nrfaces; image++) {
234 temp = 0.0;
235 for (j = 0; j < MAGIC_NR; j++) {
236 distance[j] = Math.abs(input_wk[j] - wk[image][j]);
237 //System.out.print(distance[j]+"\t");
238 }
239 //System.out.println();
240 if (image == 0)
241 System.arraycopy(distance, 0, minDistance, 0, MAGIC_NR);
242 if (sum(minDistance) > sum(distance)) {
243
244 this.idx = image;
245 System.arraycopy(distance, 0, minDistance, 0, MAGIC_NR);
246 }
247 }
248
249
250 /*
251 * Normalize our minimum distance.
252 */
253
254 if (max(minDistance) > 0.0)
255 divide(minDistance, max(minDistance));
256
257 minD = sum(minDistance);
258
259 computed = true;
260
261
262 }
263
264 /**
265 * Divide each element in <code>v</code> by <code>b</code>
266 * No checking for division by zero.
267 *
268 * @param v vector containing numbers.
269 * @param b scalar used to divied each element in the v vector
270 *
271 * a vector having each element divided by <code>b</code> scalar.
272 *
273 */
274 static void divide(double[] v, double b) {
275
276 for (int i = 0; i < v.length; i++)
277 v[i] = v[i] / b;
278
279
280 }
281
282 /**
283 * The sum of the vector.
284 *
285 * @param a vector with numbers
286 * @return a scalar with the sum of each element in the <code>a</code> vector
287 */
288 static double sum(double[] a) {
289
290 double b = a[0];
291 for (int i = 0; i < a.length; i++)
292 b += a[i];
293
294 return b;
295
296 }
297
298 /**
299 * The max of the vector a.
300 *
301 * @param a the vector
302 *
303 * @return the sum of all the elements on <code>a</code>
304 */
305 static double max(double[] a) {
306 double b = a[0];
307 for (int i = 0; i < a.length; i++)
308 if (a[i] > b) b = a[i];
309
310 return b;
311 }
312
313 }
314