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 im    : the image to add
00057 // \param label : the class label for the image
00058 // \returns 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 i: the index of the requested eigenvector
00092 // \returns 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 i: the index of the requested eigenvector
00116 // \returns 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 im: the image to convert
00245 // \returns 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 // \returns 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 im: the input image
00358 // \returns vnl_vector<double>*: the vector of weights
00359 //
00360 // \status under development
00361 // \author Brendan McCane
00362 //----------------------------------------------------------------------
00363 
00364 vnl_vector<double>* EigenFace::encode(Image *im)
00365 {
00366   if (eigenvectors.size()<=0)
00367   {
00368     return NULL;
00369   }
00370   vnl_vector<double> *im_vec = convert_image_to_vector(im);
00371   *im_vec -= *average_training_image;
00372   vnl_vector<double> *wts = new vnl_vector<double>(eigenvectors.size());
00373   for (unsigned int i=0; i<eigenvectors.size(); i++)
00374     (*wts)[i] = dot_product(*im_vec, *(eigenvectors[i]));
00375   delete im_vec;
00376   return wts;
00377 }
00378 
00379 vnl_vector<double>* EigenFace::encode(vnl_vector<double> *t_vec)
00380 {
00381   if (eigenvectors.size()<=0)
00382   {
00383     return NULL;
00384   }
00385   vnl_vector<double> *im_vec = new vnl_vector<double>(*t_vec);
00386   *im_vec -= *average_training_image;
00387   vnl_vector<double> *wts = new vnl_vector<double>(eigenvectors.size());
00388   for (unsigned int i=0; i<eigenvectors.size(); i++)
00389     (*wts)[i] = dot_product(*im_vec, *(eigenvectors[i]));
00390   delete im_vec;
00391   return wts;
00392 }
00393 
00394 //----------------------------------------------------------------------
00395 //: decode
00396 //
00397 // Given an input set of weights, rebuild an image.
00398 //
00399 // \param wts : the input set of weights
00400 // \returns vnl_vector<double>*: the reconstructed image in a vector
00401 //
00402 // \status under development
00403 // \author Brendan McCane
00404 //----------------------------------------------------------------------
00405 
00406 vnl_vector<double>* EigenFace::decode(vnl_vector<double> *wts)
00407 {
00408   vnl_vector<double> *new_im = new vnl_vector<double>(image_size, 0.0);
00409   for (unsigned int i=0; i<eigenvectors.size(); i++)
00410     *new_im += ((*wts)[i])*(*(eigenvectors[i]));
00411 
00412   return new_im;
00413 }
00414 
00415 
00416 //----------------------------------------------------------------------
00417 //: decode
00418 //
00419 // Try to classify the given image using k-nearest neighbours
00420 //
00421 // \param im        the image to classify
00422 // \param k         the number of nearest neighbours to use
00423 // \param dim       the number of dimensions of the eigenvectors to use
00424 // \param threshold distances above this threshold are treated as not recognised
00425 // \return the label of the recognised image or NULL if unsuccessful
00426 //
00427 // \status under development
00428 // \author Brendan McCane
00429 //----------------------------------------------------------------------
00430 
00431 char *EigenFace::classify(Image *im, double threshold, int k, int dim)
00432 {
00433   vcl_priority_queue<LabelDist> pq;
00434 
00435   if (num_vectors()==0) return NULL;
00436   if (eigenvectors.size()==0) return NULL;
00437 
00438   vnl_vector<double> *all_rep = encode(im);
00439   vnl_vector<double> rep(all_rep->extract(dim, all_rep->size()-dim));
00440   vnl_vector<double> diff(rep.size());
00441   double min_dist = DBL_MAX;
00442   int best=-1;
00443   char *ret=NULL;
00444 #if 0
00445   vcl_cout << "rep = " << *rep << vcl_endl;
00446 #endif
00447   for (int i=0; i<num_vectors(); i++)
00448   {
00449     vnl_vector<double> eigenvect=
00450       encoded_training_images[i]->extract
00451       (dim, encoded_training_images[i]->size()-dim);
00452     // vcl_cout << "vec " << i << " = " << *eigenvect << vcl_endl;
00453     diff = rep - eigenvect;
00454     double dist=diff.two_norm()/(double)diff.size();
00455     if ((dist<min_dist)&&(dist!=0.0))
00456     {
00457       min_dist=dist;
00458       best=i;
00459     }
00460     LabelDist t(get_label(i), dist);
00461     pq.push(t);
00462   }
00463   delete all_rep;
00464 
00465   vcl_cout << "min_dist = " << min_dist << " label = " << get_label(best) << vcl_endl;
00466   // now need to search through queue and find most likely label
00467   vcl_map<char *, int, ImageDatabase::ltstr> nns; // nearest neighbours
00468   for (int i=0; i<k; i++)
00469   {
00470     LabelDist t=pq.top();
00471     pq.pop();
00472     if ((t.dist<threshold)&&(t.dist!=0.0))
00473       nns[t.label]++;
00474   }
00475   vcl_map<char *, int, ImageDatabase::ltstr>::iterator iter;
00476   int max=0;
00477   for (iter=nns.begin(); iter!=nns.end(); iter++)
00478   {
00479     if ((*iter).second>max)
00480     {
00481       max = (*iter).second;
00482       ret = (*iter).first;
00483     }
00484   }
00485   vcl_cout << "label = " << ret << " num = " << max << vcl_endl;
00486   if (max<k/2+1) ret=NULL;
00487 #if 0
00488   char ch;
00489   vcl_cin >> ch;
00490 #endif
00491   return ret;
00492 }
00493 
00494 //----------------------------------------------------------------------
00495 //: output_xgobi
00496 //
00497 // Output the data into xgobi format files
00498 //
00499 // \param basefile  the basename of the files
00500 //
00501 // \status under development
00502 // \author Brendan McCane
00503 //----------------------------------------------------------------------
00504 
00505 void EigenFace::output_xgobi(char *basefile)
00506 {
00507   char filenames[200];
00508   vcl_sprintf(filenames, "%s.dat", basefile);
00509   vcl_ofstream datfile(filenames);
00510   vcl_sprintf(filenames, "%s.glyphs", basefile);
00511   vcl_ofstream glyphs(filenames);
00512   vcl_sprintf(filenames, "%s.row", basefile);
00513   vcl_ofstream rowfile(filenames);
00514   vcl_map<char*, int, ImageDatabase::ltstr> glyphmap;
00515   int glyphnum=1;
00516   for (int i=0; i<num_vectors(); i++)
00517   {
00518     datfile << *(encoded_training_images[i]) << vcl_endl;
00519     // no glyph associated
00520     if (glyphmap.find(training_labels[i])==glyphmap.end())
00521     {
00522       vcl_cout << "Adding training_label " << training_labels[i] << vcl_endl;
00523       glyphmap[training_labels[i]] = glyphnum;
00524       glyphnum += 3;
00525     }
00526     glyphs << glyphmap[training_labels[i]] << vcl_endl;
00527     rowfile << training_labels[i] << vcl_endl;
00528   }
00529   vcl_map<char *, int, ImageDatabase::ltstr>::iterator iter;
00530   for (iter=glyphmap.begin(); iter!=glyphmap.end(); iter++)
00531     vcl_cout << (*iter).first << vcl_endl;
00532   rowfile.close();
00533   glyphs.close();
00534   datfile.close();
00535 }