/Users/lyon/j4p/src/face/EigenFaceComputation.java

1    package face; 
2     
3     
4    import math.linearAlgebra.EigenvalueDecomposition; 
5    import math.linearAlgebra.Matrix; 
6     
7    /** 
8     * Computes an "face space" used for face recognition. 
9     * 
10    * This idea/algorhitm was derieved from Matthew A. Turn and Alex P. Pentland 
11    * paper, titled: "Face Recognition using Eigenfaces" 
12    * (<a href="<a href="http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf">http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf</a>&quot;&gt;<a href="http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf">http://www.cs.ucsb.edu/~mturk/Papers/mturk-CVPR91.pdf</a>&lt;/a&gt;) 
13    *<br><br>; 
14    * The way this algorhitm works is by treating face recognition as a 
15    * "two-dimensional recognition problem, taking advantage of the fact that 
16    * faces are normally upright and thus may be described by a small set 
17    * of 2-D characterisits views. Face images are projected onto a 
18    * feature space ('face space') that best encodes the variation 
19    * among known face images. The face space is defined by the 
20    * &quot;eigenfaces&quot;, which are the eigenvectors of the set of faces; 
21    * they do not necessarily correspond to isolated features such as eyes, 
22    * ears, and noses. 
23    *<br><br> 
24    * This work is released to the public with no license and no warranties. 
25    * Do with the code as you wish. 
26    * <br><b>NOTE</b>: This package uses linearAlgebra for computing eigenvalues and eigenvectors. 
27    * 
28    * Which is available at: 
29    * <a href="<a href="http://math.nist.gov/javanumerics/jama/">http://math.nist.gov/javanumerics/jama/</a>&quot;><a href="http://math.nist.gov/javanumerics/jama/">http://math.nist.gov/javanumerics/jama/</a></a><br><br> 
30    * And in case you are worreid about its copyright states:<br><br> 
31    * 
32    * This software is a cooperative product of The MathWorks and the National Institute of Standards 
33    * and Technology (NIST) which has been released to the public domain. Neither The MathWorks nor NIST assumes any 
34    * responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, 
35    * reliability, or any other characteristic. 
36    * 
37    * 
38    * 
39    *<br><br> 
40    * @author Konrad Rzeszutek 
41    * @version 1.0 
42    */ 
43   public class EigenFaceComputation { 
44    
45       /** 
46        * Our &quot;heuresticly&quot; determined value of how many faces we want to compute. 
47        * Just picked the value at random. You can fool around with it. 
48        */ 
49       private final static int MAGIC_NR = 11; 
50    
51       /** 
52        * Compute the &quot;face space&quot; used for face recognition. The recognition is 
53        * actually being carried out in the FaceBundle object, but the preparation 
54        * of such object requires to do lots of computations. The steps are: 
55        * <ol> 
56        *  <li> Compute an average face. 
57        *  <li> Build an covariance matrix. 
58        *  <li> Compute eigenvalues and eigenvector 
59        *  <li> Select only { MAGIC_NR} largest eigenvalues (and its corresponding eigenvectors) 
60        *  <li> Compute the faces using our eigenvectors 
61        *  <li> Compute our eigenspace for our given images. 
62        * </ol> 
63        * From then the rest of the algorithm (trying to match a face) has to be 
64        * called in {@link face.FaceBundle}. 
65        * 
66        * @param face_v  2-D array. Has to have 16 rows. Each column has 
67        * to have the same length. Each 
68        *  row contains the image in a vector representation. 
69        * @param width The width of the image in the row-vector in face_v. 
70        * @param height The height of the image in the row-vector in face_v. 
71        * @param id  The string representing each of the sixteen images. 
72        * 
73        * @return  An FaceBundle usable for recognition. 
74        * 
75        */ 
76       public static FaceBundle submit(double[][] face_v, int width, int height, String[] id) { 
77    
78           int length = width * height; 
79           int nrfaces = face_v.length; 
80           int i, j, col,rows, pix, image; 
81           double temp = 0.0; 
82           double[][] faces = new double[nrfaces][length]; 
83    
84           //TestPPM simple = new TestPPM(); 
85           //simple.setImage(face_v[0],width,height); 
86    
87           double[] avgF = new double[length]; 
88    
89           /* 
90            Compute average face of all of the faces. 1xN^2 
91            */ 
92           for (pix = 0; pix < length; pix++) { 
93               temp = 0; 
94               for (image = 0; image < nrfaces; image++) { 
95                   temp += face_v[image][pix]; 
96               } 
97               avgF[pix] = temp / nrfaces; 
98           } 
99    
100          //simple.setImage(avgF, width,height); 
101   
102          /* 
103           Compute difference. 
104          */ 
105   
106          for (image = 0; image < nrfaces; image++) { 
107   
108              for (pix = 0; pix < length; pix++) { 
109                  face_v[image][pix] = face_v[image][pix] - avgF[pix]; 
110              } 
111          } 
112          /* Copy our face vector (MxN^2). We will use it later */ 
113   
114          //for (image = 0; image < nrfaces; image++) 
115          //  System.arraycopy(face_v[image],0,faces[image],0,length); 
116          System.arraycopy(face_v, 0, faces, 0, face_v.length); 
117   
118          //simple.setImage(face_v[0],width,height); 
119   
120          /* 
121           Build covariance matrix. MxM 
122          */ 
123   
124          Matrix faceM = new Matrix(face_v, nrfaces, length); 
125          Matrix faceM_transpose = faceM.transpose(); 
126   
127          /* 
128           Covariance matrix - its MxM (nrfaces x nrfaces) 
129           */ 
130          Matrix covarM = faceM.times(faceM_transpose); 
131   
132          //double[][] z = covarM.getArray(); 
133          //System.out.println("Covariance matrix is " 
134          // +z.length+" x "+z[0].length); 
135   
136          /* 
137           Compute eigenvalues and eigenvector. Both are MxM 
138          */ 
139          EigenvalueDecomposition E = covarM.eig(); 
140   
141          double[] eigValue = diag(E.getD().getArray()); 
142          double[][] eigVector = E.getV().getArray(); 
143   
144          /* 
145           * We only need the largest associated values of the eigenvalues. 
146           * Thus we sort them (and keep an index of them) 
147           */ 
148          int[] index = new int[nrfaces]; 
149          double[][] tempVector = new double[nrfaces][nrfaces];  /* Temporary new eigVector */ 
150   
151          for (i = 0; i < nrfaces; i++) /* Enumerate all the entries */ 
152              index[i] = i; 
153   
154          doubleQuickSort(eigValue, index, 0, nrfaces - 1); 
155   
156          // Put the index in inverse 
157          int[] tempV = new int[nrfaces]; 
158          for (j = 0; j < nrfaces; j++) 
159              tempV[nrfaces - 1 - j] = index[j]; 
160          /* 
161          for (int j = 0; j< nrfaces; j++) { 
162            System.out.println(temp[j]+&quot; (was: &quot;+index[j]+&quot;) &quot;+eigValue[temp[j]]); 
163          } 
164          */ 
165          index = tempV; 
166   
167          /* 
168           * Put the sorted eigenvalues in the appropiate columns. 
169          */ 
170          for (col = nrfaces - 1; col >= 0; col--) { 
171              for (rows = 0; rows < nrfaces; rows++) { 
172                  tempVector[rows][col] = eigVector[rows][index[col]]; 
173              } 
174          } 
175          eigVector = tempVector; 
176          tempVector = null; 
177          eigValue = null; 
178          /* 
179           * From this point on we don't care about our eigenvalues anymore. 
180           * 
181           * 
182           * We multiply our faceM (MxN^2) with our new eigenvector (MxM), 
183           * getting eigenfaces (MxN^2) 
184           */ 
185          Matrix eigVectorM = new Matrix(eigVector, nrfaces, nrfaces); 
186          eigVector = eigVectorM.times(faceM).getArray(); 
187   
188   
189          /* Normalize our eigen vector matrix.  */ 
190   
191          for (image = 0; image < nrfaces; image++) { 
192              temp = max(eigVector[image]); // Our max 
193              for (pix = 0; pix < eigVector[0].length; pix++) 
194                      // Normalize 
195                  eigVector[image][pix] = Math.abs(eigVector[image][pix] / temp); 
196          } 
197   
198          /* 
199           * And now the computation of wk (face space), which is a vector and 
200           * is of some heuristically determind length. How does 11 sound? 
201           * 
202           * This is where we are using our copied faces vector (look at the 
203           * the beginning of the method) - the reason is b/c the faceM made the 
204           * array internally an covariance matrix. 
205           */ 
206   
207          double[][] wk = new double[nrfaces][MAGIC_NR]; // M rows, 11 columns 
208   
209          /* 
210           Compute our wk. 
211          */ 
212   
213          for (image = 0; image < nrfaces; image++) { 
214              for (j = 0; j < MAGIC_NR; j++) { 
215                  temp = 0.0; 
216                  for (pix = 0; pix < length; pix++) 
217                      temp += eigVector[j][pix] * faces[image][pix]; 
218                  wk[image][j] = Math.abs(temp); 
219              } 
220          } 
221   
222          /* 
223           That's it! 
224          */ 
225   
226          FaceBundle b = new FaceBundle(avgF, wk, eigVector, id); 
227   
228          /* 
229          //This is what you would use to recognize a face ... 
230   
231          double[] inputFace = new double[length]; 
232          // So we are trying to recognize the 14th image.. 
233          System.arraycopy(faces[14],0,inputFace,0,length); 
234   
235          // This is done for virgin images, not ones that we already subtracted. 
236          // for (  pix = 0; pix < inputFace.length; pix++) { 
237          //   inputFace[pix] = inputFace[pix] - avgF[pix]; 
238          //} 
239          */ 
240          /* 
241          double[] input_wk = new double[MAGIC_NR]; 
242          for (j = 0; j < MAGIC_NR; j++) { 
243            temp = 0.0; 
244            for ( pix=0; pix <length; pix++) 
245              temp += eigVector[j][pix] * inputFace[pix]; 
246   
247            input_wk[j] = Math.abs( temp ); 
248          } 
249          */ 
250          /* 
251           * Find the minimun distance from the input_wk as compared to wk 
252           */ 
253          /* 
254          int idx = 0; 
255          double[] distance = new double[MAGIC_NR]; 
256          double[] minDistance = new double[MAGIC_NR]; 
257   
258          for (image = 0; image < nrfaces; image++) { 
259              for (j = 0; j < MAGIC_NR; j++) 
260                distance[j] = Math.abs(input_wk[j] - wk[image][j]); 
261              if (image == 0) 
262                System.arraycopy(distance,0,minDistance,0,MAGIC_NR); 
263               if (sum(minDistance) > sum(distance)) { 
264                idx = image; 
265                System.arraycopy(distance,0,minDistance,0,MAGIC_NR); 
266   
267              } 
268          } 
269          */ 
270   
271          /* 
272           * Normalize our minimum distance. 
273           */ 
274          /* 
275         divide(minDistance, max(minDistance)+1); 
276         double  minD = sum(minDistance); 
277         System.out.println(&quot;image is idx &quot;+idx+&quot; distance from face: &quot;+minD); 
278   
279   
280         //TestPPM simple = new TestPPM(); 
281         int[] bb = new int[length]; 
282   
283         temp = max(eigVector[0]); 
284   
285         for ( i = 0; i < width*height; i++) { 
286           bb[i] = (int) (255*(1 - eigVector[0][i] / temp )); 
287         } 
288   
289         simple.setImage(bb,width,height); 
290         */ 
291          return b; 
292      } 
293   
294      /** 
295       * Find the diagonal of an matrix. 
296       * 
297       * @param m the number of rows and columns must be the same 
298       * @return  the diagonal of the matrix 
299       */ 
300      static double[] diag(double[][] m) { 
301   
302          double[] d = new double[m.length]; 
303          for (int i = 0; i < m.length; i++) 
304              d[i] = m[i][i]; 
305          return d; 
306      } 
307   
308      /** 
309       * Divide each element in <code>v</code> by <code>b</code> 
310       * No checking for division by zero. 
311       * 
312       * @param v vector containing numbers. 
313       * @param b scalar used to divied each element in the v vector 
314       * 
315       *   a vector having each element divided by <code>b</code> scalar. 
316       * 
317       */ 
318      static void divide(double[] v, double b) { 
319   
320          for (int i = 0; i < v.length; i++) 
321              v[i] = v[i] / b; 
322   
323   
324      } 
325   
326      /** 
327       * The sum of the vector. 
328       * 
329       * @param a vector with numbers 
330       * @return  a scalar with the sum of each element in the <code>a</code> vector 
331       */ 
332      static double sum(double[] a) { 
333   
334          double b = a[0]; 
335          for (int i = 0; i < a.length; i++) 
336              b += a[i]; 
337   
338          return b; 
339   
340      } 
341   
342      /** 
343       * The max of the vector a. 
344       * 
345       * @param a the vector 
346       * 
347       * @return  the sum of all the elements on <code>a</code> 
348       */ 
349      static double max(double[] a) { 
350          double b = a[0]; 
351          for (int i = 0; i < a.length; i++) 
352              if (a[i] > b) b = a[i]; 
353   
354          return b; 
355      } 
356   
357      /** 
358       * Quick sort on a vector with an index. 
359       * 
360       * @param a the array of numbers. This will be modified and sorted 
361       *  ascendingly (smalles to highest) 
362       * @param index the index of the numbers as related to original 
363       *  location. 
364       * @param lo0  the index where to start from. Usually 0. 
365       * @param hi0  the index where to stop. Usually a.length() 
366       */ 
367      static void doubleQuickSort(double a[], int index[], int lo0, int hi0) { 
368          int lo = lo0; 
369          int hi = hi0; 
370          double mid; 
371   
372          if (hi0 > lo0) { 
373   
374              /* Arbitrarily establishing partition element as the midpoint of 
375               * the array. 
376               */ 
377              mid = a[(lo0 + hi0) / 2]; 
378              // loop through the array until indices cross 
379              while (lo <= hi) { 
380                  /* find the first element that is greater than or equal to 
381                   * the partition element starting from the left Index. 
382                   */ 
383                  while ((lo < hi0) && (a[lo] < mid)) { 
384                      ++lo; 
385                  } 
386   
387                  /* find an element that is smaller than or equal to 
388                   * the partition element starting from the right Index. 
389                   */ 
390                  while ((hi > lo0) && (a[hi] > mid)) { 
391                      --hi; 
392                  } 
393   
394                  // if the indexes have not crossed, swap 
395                  if (lo <= hi) { 
396                      swap(a, index, lo, hi); 
397                      ++lo; 
398                      --hi; 
399                  } 
400              } 
401              /* If the right index has not reached the left side of array 
402               * must now sort the left partition. 
403               */ 
404              if (lo0 < hi) { 
405                  doubleQuickSort(a, index, lo0, hi); 
406              } 
407              /* If the left index has not reached the right side of array 
408               * must now sort the right partition. 
409               */ 
410              if (lo < hi0) { 
411                  doubleQuickSort(a, index, lo, hi0); 
412              } 
413          } 
414      } 
415   
416      static private void swap(double a[], int[] index, int i, int j) { 
417          double T; 
418          T = a[i]; 
419          a[i] = a[j]; 
420          a[j] = T; 
421          // Index 
422          index[i] = i; 
423          index[j] = j; 
424      } 
425  }