/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