contrib/oul/ouml/eigenfaces.cxx
Go to the documentation of this file.
00001 // This is oul/ouml/eigenfaces.cxx
00002 #include "eigenfaces.h"
00003 //:
00004 // \file
00005 
00006 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00007 #include <vil1/vil1_save.h>
00008 #include <vcl_queue.h>
00009 #include <vcl_fstream.h>
00010 #include <vcl_cstdio.h> // for sprintf()
00011 #include <vcl_cstring.h>
00012 #include <vcl_cassert.h>
00013 
00014 
00015 //----------------------------------------------------------------------
00016 //: Destructor
00017 //
00018 // Scans through training_images and eigenvectors and deletes the
00019 // elements thereof.
00020 //
00021 //.status under development
00022 //.author Brendan McCane
00023 //----------------------------------------------------------------------
00024 
00025 EigenFace::~EigenFace()
00026 {
00027   delete average_training_image;
00028 
00029   // delete contents of the training images vector
00030   vcl_vector<vnl_vector<double> *>::iterator iter;
00031   for (iter=training_images.begin();
00032      iter!=training_images.end(); iter++)
00033     delete *iter;
00034 
00035   // delete contents of the eigenvectors
00036   for (iter=eigenvectors.begin(); iter!=eigenvectors.end(); iter++)
00037     delete *iter;
00038 
00039   // delete contents of the encoded_training_images
00040   for (iter=encoded_training_images.begin();
00041      iter!=encoded_training_images.end(); iter++)
00042     delete *iter;
00043 
00044   // delete contents of the training labels
00045   vcl_vector<char *>::iterator citer;
00046   for (citer=training_labels.begin(); citer!=training_labels.end(); citer++)
00047     delete *citer;
00048 }
00049 
00050 //----------------------------------------------------------------------
00051 //: Add a training image to the list of training images
00052 //
00053 // Converts the input training image into a vector first, then adds
00054 // this vector to the list.
00055 //
00056 // .param Image *im: the image to add
00057 // .param char *label: the class label for the image
00058 // .ret bool: success or otherwise of the insertion procedure
00059 //
00060 //.status under development
00061 //.author Brendan McCane
00062 //----------------------------------------------------------------------
00063 
00064 bool EigenFace::add_training_image(Image *im, const char * label)
00065 {
00066   // precondition
00067   assert(im!=NULL);
00068   vnl_vector<double> *image_vector = convert_image_to_vector(im);
00069   if (!image_vector) return false;
00070   if (image_size==0)
00071     image_size = image_vector->size();
00072   if (image_size!=image_vector->size())
00073   {
00074     vcl_cerr << "Error adding training image\n"
00075              << "Image of incorrect size\n";
00076     return false;
00077   }
00078   training_images.push_back(image_vector);
00079   training_labels.push_back(strdup(label));
00080   // postconditions: elements actually inserted
00081   assert(training_images.back()==image_vector);
00082   assert(vcl_strcmp(training_labels.back(),label)==0);
00083   return true;
00084 }
00085 
00086 //----------------------------------------------------------------------
00087 //: get_eigenvector
00088 //
00089 // Returns the requested eigenvector if it exists, NULL if it doesn't.
00090 //
00091 // .param int i: the index of the requested eigenvector
00092 // .ret vnl_vector<double>: the requested eigenvector
00093 //
00094 //.status under development
00095 //.author Brendan McCane
00096 //----------------------------------------------------------------------
00097 
00098 vnl_vector<double> *EigenFace::get_eigenvector(int i)
00099 {
00100   assert(i>=0);
00101   if ((unsigned int)i>=eigenvectors.size())
00102   {
00103     vcl_cerr << "Requesting eigenvector, " << i << " which doesn't exist\n"
00104              << "Number of eigenvectors is: " << eigenvectors.size() << vcl_endl;
00105     return NULL;
00106   }
00107   return eigenvectors[i];
00108 }
00109 
00110 //----------------------------------------------------------------------
00111 //: get_eigenvalue
00112 //
00113 // Returns the requested eigenvalue if it exists, 0 if it doesn't.
00114 //
00115 // .param int i: the index of the requested eigenvector
00116 // .ret double: the requested eigenvalue
00117 //
00118 //.status under development
00119 //.author Brendan McCane
00120 //----------------------------------------------------------------------
00121 
00122 double EigenFace::get_eigenvalue(int i)
00123 {
00124   assert(i>=0);
00125   if ((unsigned int)i>=eigenvalues.size())
00126   {
00127     vcl_cerr << "Requesting eigenvalue, " << i << " which doesn't exist\n"
00128              << "Number of eigenvalues is: " << eigenvalues.size() << vcl_endl;
00129     return 0.0;
00130   }
00131   return eigenvalues[i];
00132 }
00133 
00134 void EigenFace::cleanup()
00135 {
00136   // first clean up any old faces
00137   if (!average_training_image)
00138     average_training_image = new vnl_vector<double>(training_images[0]->size(), 0.0);
00139   else average_training_image->fill(0.0);
00140   // delete contents of the eigenvectors
00141   // need to sort this out - all should be deleted prior to relearning
00142 #if 0
00143   vector<vnl_vector<double> *>::iterator iter;
00144   for (iter=eigenvectors.begin(); iter!=eigenvectors.end(); iter++)
00145     delete *iter;
00146   eigenvectors.clear();
00147   eigenvalues.clear();
00148   // now remove the encoded training images
00149   for (iter=encoded_training_images.begin();
00150        iter!=encoded_training_images.end(); iter++)
00151   {
00152     delete *iter;
00153   }
00154   encoded_training_images.clear();
00155 #endif
00156 }
00157 //----------------------------------------------------------------------
00158 //: calculate_eigenfaces
00159 //
00160 // Actually perform the eigenfaces calculation. Returns true on
00161 // success, false on failure. This will populate the eigenvectors and
00162 // eigenvalues vectors.
00163 //
00164 //.status under development
00165 //.author Brendan McCane
00166 //----------------------------------------------------------------------
00167 
00168 bool EigenFace::calculate_eigenfaces()
00169 {
00170   if (training_images.size()<=0)
00171   {
00172     vcl_cerr << "No training images\n";
00173     return false;
00174   }
00175   // construct the A matrix from the training vectors
00176   vnl_matrix<double> A(image_size, training_images.size());
00177 
00178   vcl_cout << "cleaning up\n";
00179   cleanup();
00180   // now calculate the new vectors
00181   // first calculate the average training image
00182   vcl_cout << "Average training image\n"
00183            << "ati size = " << average_training_image->size() << vcl_endl;
00184   for (unsigned int i=0; i<training_images.size(); i++)
00185   {
00186     vcl_cout << "adding training image " << i << " size = "
00187              << training_images[i]->size() << vcl_endl;
00188     *average_training_image += *training_images[i];
00189   }
00190   *average_training_image /= (double)training_images.size();
00191   vcl_cout << "Populating A\n";
00192   for (unsigned int i=0; i<training_images.size(); i++)
00193   {
00194     vnl_vector<double> *training = training_images[i];
00195     for (unsigned int j=0; j<training->size(); j++)
00196       A(j, i) = (*training)[j]-(*average_training_image)[j];
00197   }
00198 
00199   vcl_cout << "AtA\n";
00200   // now build AtA - then find the eigenvectors of this matrix
00201   vnl_matrix<double> AtA = A.transpose()*A;
00202   vnl_symmetric_eigensystem<double> eigen(AtA);
00203 
00204   vcl_cout << "Eigenvectors\n";
00205   for (unsigned int i=0; i<training_images.size(); i++)
00206   {
00207     vnl_vector<double> *new_vec = new vnl_vector<double>(image_size);
00208     *new_vec = A*eigen.get_eigenvector(i);
00209     //new_vec->normalize();
00210     eigenvectors.push_back(new_vec);
00211     eigenvalues.push_back(eigen.get_eigenvalue(i));
00212   }
00213 
00214   vcl_cout << "Eigenvalues are:" ;
00215   vcl_vector<double>::iterator val_iter;
00216   for (val_iter=eigenvalues.begin(); val_iter!=eigenvalues.end(); val_iter++)
00217     vcl_cout << ' ' << (*val_iter);
00218   vcl_cout << "\nEncoding training images\n";
00219   encode_training_images();
00220   // should check they are in fact eigenvectors
00221   return true;
00222 }
00223 
00224 void EigenFace::check_training()
00225 {
00226   vcl_cout << "Check training image\n";
00227   if (average_training_image!=NULL)
00228     vcl_cout << "ati size = " << average_training_image->size() << vcl_endl;
00229   else
00230     vcl_cout << "ati not set\n";
00231   for (unsigned int i=0; i<training_images.size(); i++)
00232   {
00233     vcl_cout << "training image " << i << " size = "
00234              << training_images[i]->size() << vcl_endl;
00235   }
00236 }
00237 
00238 //----------------------------------------------------------------------
00239 //: convert_image_to_vector
00240 //
00241 // Convert the input image to a 1d vector by stacking the rows of the
00242 // image on top of each other (vertically).
00243 //
00244 // .param Image *im: the image to convert
00245 // .ret vnl_vector<double> *: the resultant vector
00246 //
00247 //.status under development
00248 //.author Brendan McCane
00249 //----------------------------------------------------------------------
00250 
00251 vnl_vector<double> *EigenFace::convert_image_to_vector(Image *im)
00252 {
00253   assert(im!=NULL);
00254   int index=0;
00255   vnl_vector<double> *new_vec = new vnl_vector<double>(im->width()*im->height());
00256   for (int j=0; j<im->height(); j++)
00257     for (int i=0; i<im->width(); i++)
00258       (*new_vec)[index++] = (double)(*im)(i,j);
00259   new_vec->normalize();
00260   return new_vec;
00261 }
00262 
00263 //----------------------------------------------------------------------
00264 //: check_eigenvectors
00265 //
00266 // Check to see if the calculated vectors are in fact eigenvectors.
00267 //
00268 // .ret bool: true if eigenvectors, false otherwise
00269 //
00270 //.status under development
00271 //.author Brendan McCane
00272 //----------------------------------------------------------------------
00273 
00274 bool EigenFace::check_eigenvectors()
00275 {
00276   if (eigenvectors.size()<=0) return false;
00277 
00278   vcl_cout << "Eigenvalues are:" ;
00279   vcl_vector<double>::iterator val_iter;
00280   for (val_iter=eigenvalues.begin(); val_iter!=eigenvalues.end(); val_iter++)
00281     vcl_cout << ' ' << (*val_iter);
00282   vcl_cout << vcl_endl;
00283   vcl_vector<vnl_vector<double> *>::iterator iter1, iter2;
00284   for (iter1=eigenvectors.begin(); iter1!=eigenvectors.end(); iter1++)
00285     for (iter2=iter1+1; iter2!=eigenvectors.end(); iter2++)
00286       if (!epsilon_equals(dot_product(**iter1, **iter2), 0.0))
00287       {
00288         vcl_cout << "vectors aren't eigenvectors\n"
00289                  << "offending vectors are: "
00290                  << '\t' << **iter1 << vcl_endl
00291                  << '\t' << **iter2 << vcl_endl
00292                  << "dot product is: " << dot_product(**iter1, **iter2) << vcl_endl;
00293         return false;
00294       }
00295   return true;
00296 }
00297 
00298 //----------------------------------------------------------------------
00299 //: save_as_images
00300 //
00301 // Save each of the eigenvectors as an image.
00302 //
00303 //.status under development
00304 //.author Brendan McCane
00305 //----------------------------------------------------------------------
00306 
00307 void EigenFace::save_as_images(int width, int height)
00308 {
00309   assert(width > 0 && height > 0);
00310   if ((unsigned int)(width*height)!=image_size)
00311   {
00312     vcl_cerr << "width*height must be equal to image size\n"
00313              << "image size is: " << image_size << vcl_endl;
00314     return;
00315   }
00316 
00317   Image im(width, height);
00318   vcl_vector<vnl_vector<double> *>::iterator iter;
00319   char name[100];
00320   int index=0;
00321   for (iter=eigenvectors.begin(); iter!=eigenvectors.end(); iter++)
00322   {
00323     double min = (**iter).min_value();
00324     double max = (**iter).max_value();
00325     for (int i=0; i<width; i++)
00326       for (int j=0; j<height; j++)
00327         im[i][j] = (unsigned char)
00328           (((**iter)[i+j*width]-min)/(max-min)*255);
00329     vcl_sprintf(name, "eigenface%03d.pgm", index++);
00330     vil1_save(im, name);
00331   }
00332 }
00333 
00334 
00335 //----------------------------------------------------------------------
00336 //: encode_training_images
00337 //
00338 // Encode all training images for later classification.
00339 //
00340 //.status under development
00341 //.author Brendan McCane
00342 //----------------------------------------------------------------------
00343 void EigenFace::encode_training_images()
00344 {
00345   vcl_vector<vnl_vector<double> *>::iterator iter;
00346   for (iter=training_images.begin(); iter!=training_images.end(); iter++)
00347   {
00348     encoded_training_images.push_back(encode(*iter));
00349   }
00350 }
00351 
00352 //----------------------------------------------------------------------
00353 //: encode
00354 //
00355 // Given an input image, encode it as a vector of weights.
00356 //
00357 // .param Image *im: the input image
00358 //
00359 // .ret vnl_vector<double>*: the vector of weights
00360 //
00361 //.status under development
00362 //.author Brendan McCane
00363 //----------------------------------------------------------------------
00364 
00365 vnl_vector<double>* EigenFace::encode(Image *im)
00366 {
00367   if (eigenvectors.size()<=0)
00368   {
00369     return NULL;
00370   }
00371   vnl_vector<double> *im_vec = convert_image_to_vector(im);
00372   *im_vec -= *average_training_image;
00373   vnl_vector<double> *wts = new vnl_vector<double>(eigenvectors.size());
00374   for (unsigned int i=0; i<eigenvectors.size(); i++)
00375     (*wts)[i] = dot_product(*im_vec, *(eigenvectors[i]));
00376   delete im_vec;
00377   return wts;
00378 }
00379 
00380 vnl_vector<double>* EigenFace::encode(vnl_vector<double> *t_vec)
00381 {
00382   if (eigenvectors.size()<=0)
00383   {
00384     return NULL;
00385   }
00386   vnl_vector<double> *im_vec = new vnl_vector<double>(*t_vec);
00387   *im_vec -= *average_training_image;
00388   vnl_vector<double> *wts = new vnl_vector<double>(eigenvectors.size());
00389   for (unsigned int i=0; i<eigenvectors.size(); i++)
00390     (*wts)[i] = dot_product(*im_vec, *(eigenvectors[i]));
00391   delete im_vec;
00392   return wts;
00393 }
00394 
00395 //----------------------------------------------------------------------
00396 //: decode
00397 //
00398 // Given an input set of weights, rebuild an image.
00399 //
00400 // .param vnl_vector<double>*: the input set of weights
00401 //
00402 // .ret vnl_vector<double>*: the reconstructed image in a vector
00403 //
00404 //.status under development
00405 //.author Brendan McCane
00406 //----------------------------------------------------------------------
00407 
00408 vnl_vector<double>* EigenFace::decode(vnl_vector<double> *wts)
00409 {
00410   vnl_vector<double> *new_im = new vnl_vector<double>(image_size, 0.0);
00411   for (unsigned int i=0; i<eigenvectors.size(); i++)
00412     *new_im += ((*wts)[i])*(*(eigenvectors[i]));
00413 
00414   return new_im;
00415 }
00416 
00417 
00418 //----------------------------------------------------------------------
00419 //: decode
00420 //
00421 // Try to classify the given image using k-nearest neighbours
00422 //
00423 // \param im        the image to classify
00424 //
00425 // \param k         the number of nearest neighbours to use
00426 //
00427 // \param dim       the number of dimensions of the eigenvectors to use
00428 //
00429 // \param threshold distances above this threshold are treated as not recognised
00430 //
00431 // \return the label of the recognised image or NULL if unsuccessful
00432 //
00433 //.status under development
00434 //.author Brendan McCane
00435 //----------------------------------------------------------------------
00436 
00437 char *EigenFace::classify(Image *im, double threshold, int k, int dim)
00438 {
00439   vcl_priority_queue<LabelDist> pq;
00440 
00441   if (num_vectors()==0) return NULL;
00442   if (eigenvectors.size()==0) return NULL;
00443 
00444   vnl_vector<double> *all_rep = encode(im);
00445   vnl_vector<double> rep(all_rep->extract(dim, all_rep->size()-dim));
00446   vnl_vector<double> diff(rep.size());
00447   double min_dist = DBL_MAX;
00448   int best=-1;
00449   char *ret=NULL;
00450 #if 0
00451   vcl_cout << "rep = " << *rep << vcl_endl;
00452 #endif
00453   for (int i=0; i<num_vectors(); i++)
00454   {
00455     vnl_vector<double> eigenvect=
00456       encoded_training_images[i]->extract
00457       (dim, encoded_training_images[i]->size()-dim);
00458     // vcl_cout << "vec " << i << " = " << *eigenvect << vcl_endl;
00459     diff = rep - eigenvect;
00460     double dist=diff.two_norm()/(double)diff.size();
00461     if ((dist<min_dist)&&(dist!=0.0))
00462     {
00463       min_dist=dist;
00464       best=i;
00465     }
00466     LabelDist t(get_label(i), dist);
00467     pq.push(t);
00468   }
00469   delete all_rep;
00470 
00471   vcl_cout << "min_dist = " << min_dist << " label = " << get_label(best) << vcl_endl;
00472   // now need to search through queue and find most likely label
00473   vcl_map<char *, int, ImageDatabase::ltstr> nns; // nearest neighbours
00474   for (int i=0; i<k; i++)
00475   {
00476     LabelDist t=pq.top();
00477     pq.pop();
00478     if ((t.dist<threshold)&&(t.dist!=0.0))
00479       nns[t.label]++;
00480   }
00481   vcl_map<char *, int, ImageDatabase::ltstr>::iterator iter;
00482   int max=0;
00483   for (iter=nns.begin(); iter!=nns.end(); iter++)
00484   {
00485     if ((*iter).second>max)
00486     {
00487       max = (*iter).second;
00488       ret = (*iter).first;
00489     }
00490   }
00491   vcl_cout << "label = " << ret << " num = " << max << vcl_endl;
00492   if (max<k/2+1) ret=NULL;
00493 #if 0
00494   char ch;
00495   vcl_cin >> ch;
00496 #endif
00497   return ret;
00498 }
00499 
00500 //----------------------------------------------------------------------
00501 //: output_xgobi
00502 //
00503 // Output the data into xgobi format files
00504 //
00505 // \param basefile  the basename of the files
00506 //
00507 //.status under development
00508 //.author Brendan McCane
00509 //----------------------------------------------------------------------
00510 
00511 void EigenFace::output_xgobi(char *basefile)
00512 {
00513   char filenames[200];
00514   vcl_sprintf(filenames, "%s.dat", basefile);
00515   vcl_ofstream datfile(filenames);
00516   vcl_sprintf(filenames, "%s.glyphs", basefile);
00517   vcl_ofstream glyphs(filenames);
00518   vcl_sprintf(filenames, "%s.row", basefile);
00519   vcl_ofstream rowfile(filenames);
00520   vcl_map<char*, int, ImageDatabase::ltstr> glyphmap;
00521   int glyphnum=1;
00522   for (int i=0; i<num_vectors(); i++)
00523   {
00524     datfile << *(encoded_training_images[i]) << vcl_endl;
00525     // no glyph associated
00526     if (glyphmap.find(training_labels[i])==glyphmap.end())
00527     {
00528       vcl_cout << "Adding training_label " << training_labels[i] << vcl_endl;
00529       glyphmap[training_labels[i]] = glyphnum;
00530       glyphnum += 3;
00531     }
00532     glyphs << glyphmap[training_labels[i]] << vcl_endl;
00533     rowfile << training_labels[i] << vcl_endl;
00534   }
00535   vcl_map<char *, int, ImageDatabase::ltstr>::iterator iter;
00536   for (iter=glyphmap.begin(); iter!=glyphmap.end(); iter++)
00537     vcl_cout << (*iter).first << vcl_endl;
00538   rowfile.close();
00539   glyphs.close();
00540   datfile.close();
00541 }