face_recognition_lib.cpp
Go to the documentation of this file.
00001 /* Copyright 2012 Pouyan Ziafati, University of Luxembourg and Utrecht University
00002 
00003 *The face_recognition_lib.cpp provides image processing and face recognition functionalities for the face_recognition ROS package. 
00004 * All image processing and face recognition functionalities are provided by utilizing the Shervin Emami's c++ source code for face recognition (http://www.shervinemami.info/faceRecognition.html).
00005 
00006 
00007 *License: Attribution-NonCommercial 3.0 Unported (http://creativecommons.org/licenses/by-nc/3.0/)
00008 *You are free:
00009     *to Share — to copy, distribute and transmit the work
00010     *to Remix — to adapt the work
00011 
00012 *Under the following conditions:
00013     *Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
00014     *Noncommercial — You may not use this work for commercial purposes.
00015 
00016 *With the understanding that:
00017     *Waiver — Any of the above conditions can be waived if you get permission from the copyright holder.
00018     *Public Domain — Where the work or any of its elements is in the public domain under applicable law, that status is in no way affected by the license.
00019     *Other Rights — In no way are any of the following rights affected by the license:
00020         *Your fair dealing or fair use rights, or other applicable copyright exceptions and limitations;
00021         *The author's moral rights;
00022         *Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights.
00023     *Notice — For any reuse or distribution, you must make clear to others the license terms of this work.
00024 
00025 
00026 */
00027 
00028 //Headers added by D.Portugal (needed for Catkinization):
00029 #include <cvaux.h>
00030 #include <image_transport/image_transport.h>
00031 #include <opencv2/highgui/highgui.hpp>
00032 #include <sys/stat.h>
00033 #include <ros/package.h> //to get pkg path
00034 
00035 using namespace std;
00036 static const char *faceCascadeFilename = "haarcascade_frontalface_alt.xml";   // Haar Cascade file, used for Face Detection.
00037 const std::string path = ros::package::getPath("face_recognition");     //D.Portugal => get pkg path
00038 //#define USE_MAHALANOBIS_DISTANCE      // You might get better recognition accuracy if you enable this.
00039 class FaceRecognitionLib
00040 {
00041 public:
00042   // Global variables
00043   int SAVE_EIGENFACE_IMAGES;// Set to 0 if you don't want images of the Eigenvectors saved to files (for debugging).
00044   IplImage ** faceImgArr; // array of face images
00045   vector<string> personNames;                   // array of person names (indexed by the person number). Added by Shervin.
00046   int faceWidth;        // Default dimensions for faces in the face recognition database. Added by Shervin.
00047   int faceHeight;       //      "               "               "               "               "               "
00048   int nPersons; // the number of people in the training set. Added by Shervin.
00049   int nTrainFaces; // the number of training images
00050   int nEigens; // the number of eigenvalues
00051   IplImage * pAvgTrainImg; // the average image
00052   IplImage ** eigenVectArr; // eigenvectors
00053   CvMat * eigenValMat; // eigenvalues
00054   CvMat * projectedTrainFaceMat; // projected training faces
00055   CvHaarClassifierCascade* faceCascade;
00056   CvMat * trainPersonNumMat;  // the person numbers during training
00057   bool database_updated;
00058   //Functions:
00059   bool learn(const char *szFileTrain);
00060   void doPCA();
00061   void storeTrainingData();
00062   int  loadTrainingData(CvMat ** pTrainPersonNumMat);
00063   int  findNearestNeighbor(float * projectedTestFace);
00064   int  findNearestNeighbor(float * projectedTestFace, float *pConfidence);
00065   int  loadFaceImgArray(const char * filename);
00066   void storeEigenfaceImages();
00067   IplImage* convertImageToGreyscale(const IplImage *imageSrc);
00068   IplImage* cropImage(const IplImage *img, const CvRect region);
00069   IplImage* resizeImage(const IplImage *origImg, int newWidth, int newHeight);
00070   IplImage* convertFloatImageToUcharImage(const IplImage *srcImg);
00071   CvRect detectFaceInImage(const IplImage *inputImg, const CvHaarClassifierCascade* cascade );
00072   bool retrainOnline(void);
00073   FaceRecognitionLib()
00074   {
00075      SAVE_EIGENFACE_IMAGES = 1;         
00076      faceImgArr= 0; 
00077      faceWidth = 120;
00078      faceHeight =  90;
00079      nPersons                  = 0;
00080      nTrainFaces               = 0;
00081      nEigens                   = 0;
00082      pAvgTrainImg       = 0;
00083      eigenVectArr      = 0; 
00084      eigenValMat           = 0; 
00085      projectedTrainFaceMat = 0; 
00086      database_updated = false;
00087      
00089      chdir(path.c_str());
00090      
00091      // Load the HaarCascade classifier for face detection.
00092      faceCascade = (CvHaarClassifierCascade*)cvLoad(faceCascadeFilename, 0, 0, 0 );
00093      if( !faceCascade ) 
00094      {
00095        ROS_INFO("Could not load Haar cascade Face detection classifier in '%s'.", faceCascadeFilename);
00096        exit(1);
00097      }   
00098      // Make sure there is a "data" folder, for storing the new person.
00099      mkdir("data",S_IRWXU | S_IRWXG | S_IRWXO); 
00100      // Load the previously saved training data
00101      trainPersonNumMat = 0;
00102      if( loadTrainingData( &trainPersonNumMat ) ) 
00103      {
00104         faceWidth = pAvgTrainImg->width;
00105         faceHeight = pAvgTrainImg->height;
00106         database_updated=true;
00107      }
00108      else
00109      {
00110         ROS_INFO("Will try to train from images");
00111         if(!retrainOnline()) 
00112            ROS_INFO("Could not train from images");     
00113         
00114      }
00115   }
00116   ~FaceRecognitionLib(void)
00117   {
00118         cvReleaseHaarClassifierCascade( &faceCascade );
00119         if(trainPersonNumMat)   cvReleaseMat( &trainPersonNumMat );
00120         int i;
00121         if (faceImgArr)
00122         {
00123            for (i=0; i<nTrainFaces; i++)
00124               if (faceImgArr[i])        cvReleaseImage( &faceImgArr[i] );
00125            cvFree( &faceImgArr );                                  
00126         }
00127         if(eigenVectArr)
00128         {
00129            for (i=0; i<nEigens; i++)
00130               if (eigenVectArr[i])      cvReleaseImage( &eigenVectArr[i] );
00131            cvFree( &eigenVectArr ); 
00132         }
00133         if(trainPersonNumMat) cvReleaseMat( &trainPersonNumMat ); 
00134         personNames.clear();            
00135         if(pAvgTrainImg) cvReleaseImage( &pAvgTrainImg );       
00136         if(eigenValMat)  cvReleaseMat( &eigenValMat );
00137         if(projectedTrainFaceMat) cvReleaseMat( &projectedTrainFaceMat );
00138    }
00139 
00140 };
00141 
00142 
00143 // Perform face detection on the input image, using the given Haar cascade classifier.
00144 // Returns a rectangle for the detected region in the given image.
00145 CvRect FaceRecognitionLib::detectFaceInImage(const IplImage *inputImg, const CvHaarClassifierCascade* cascade )
00146 {
00147         const CvSize minFeatureSize = cvSize(20, 20);
00148         const int flags = CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH;        // Only search for 1 face.
00149         const float search_scale_factor = 1.1f;
00150         IplImage *detectImg;
00151         IplImage *greyImg = 0;
00152         CvMemStorage* storage;
00153         CvRect rc;
00154         //double t;
00155         CvSeq* rects;
00156 
00157 
00158         storage = cvCreateMemStorage(0);
00159         cvClearMemStorage( storage );
00160 
00161         // If the image is color, use a greyscale copy of the image.
00162         detectImg = (IplImage*)inputImg;        // Assume the input image is to be used.
00163         if (inputImg->nChannels > 1)
00164         {
00165                 greyImg = cvCreateImage(cvSize(inputImg->width, inputImg->height), IPL_DEPTH_8U, 1 );
00166                 cvCvtColor( inputImg, greyImg, CV_BGR2GRAY );
00167                 detectImg = greyImg;    // Use the greyscale version as the input.
00168         }
00169 
00170         // Detect all the faces.
00171         //$$$$$t = (double)cvGetTickCount();
00172         rects = cvHaarDetectObjects( detectImg, (CvHaarClassifierCascade*)cascade, storage,
00173                                 search_scale_factor, 3, flags, minFeatureSize );
00174         //$$$$$t = (double)cvGetTickCount() - t;
00175         //$$$$$ROS_INFO("[Face Detection took %d ms and found %d objects]\n", cvRound( t/((double)cvGetTickFrequency()*1000.0) ), rects->total );
00176 
00177         // Get the first detected face (the biggest).
00178         if (rects->total > 0)
00179         {
00180           rc = *(CvRect*)cvGetSeqElem( rects, 0 );
00181         }
00182         else
00183                 rc = cvRect(-1,-1,-1,-1);       // Couldn't find the face.
00184 
00185         //cvReleaseHaarClassifierCascade( &cascade );
00186         //cvReleaseImage( &detectImg );
00187         if (greyImg)
00188         cvReleaseImage( &greyImg );
00189         cvReleaseMemStorage( &storage );
00190 
00191         return rc;      // Return the biggest face found, or (-1,-1,-1,-1).
00192 }
00193 
00194 
00195 
00196 // Return a new image that is always greyscale, whether the input image was RGB or Greyscale.
00197 // Remember to free the returned image using cvReleaseImage() when finished.
00198 IplImage* FaceRecognitionLib::convertImageToGreyscale(const IplImage *imageSrc)
00199 {
00200         IplImage *imageGrey;
00201         // Either convert the image to greyscale, or make a copy of the existing greyscale image.
00202         // This is to make sure that the user can always call cvReleaseImage() on the output, whether it was greyscale or not.
00203         if (imageSrc->nChannels == 3) {
00204                 imageGrey = cvCreateImage( cvGetSize(imageSrc), IPL_DEPTH_8U, 1 );
00205                 cvCvtColor( imageSrc, imageGrey, CV_BGR2GRAY );
00206         }
00207         else {
00208                 imageGrey = cvCloneImage(imageSrc);
00209         }
00210         return imageGrey;
00211 }
00212 
00213 // Creates a new image copy that is of a desired size.
00214 // Remember to free the new image later.
00215 IplImage* FaceRecognitionLib::resizeImage(const IplImage *origImg, int newWidth, int newHeight)
00216 {
00217         IplImage *outImg = 0;
00218         int origWidth;
00219         int origHeight;
00220         if (origImg) {
00221                 origWidth = origImg->width;
00222                 origHeight = origImg->height;
00223         }
00224         if (newWidth <= 0 || newHeight <= 0 || origImg == 0 || origWidth <= 0 || origHeight <= 0) {
00225                 ROS_INFO("ERROR in resizeImage: Bad desired image size of %dx%d.", newWidth, newHeight);
00226                 exit(1);
00227         }
00228 
00229         // Scale the image to the new dimensions, even if the aspect ratio will be changed.
00230         outImg = cvCreateImage(cvSize(newWidth, newHeight), origImg->depth, origImg->nChannels);
00231         if (newWidth > origImg->width && newHeight > origImg->height) {
00232                 // Make the image larger
00233                 cvResetImageROI((IplImage*)origImg);
00234                 cvResize(origImg, outImg, CV_INTER_LINEAR);     // CV_INTER_CUBIC or CV_INTER_LINEAR is good for enlarging
00235         }
00236         else {
00237                 // Make the image smaller
00238                 cvResetImageROI((IplImage*)origImg);
00239                 cvResize(origImg, outImg, CV_INTER_AREA);       // CV_INTER_AREA is good for shrinking / decimation, but bad at enlarging.
00240         }
00241 
00242         return outImg;
00243 }
00244 
00245 // Returns a new image that is a cropped version of the original image.
00246 IplImage* FaceRecognitionLib::cropImage(const IplImage *img, const CvRect region)
00247 {
00248         IplImage *imageTmp;
00249         IplImage *imageRGB;
00250         CvSize size;
00251         size.height = img->height;
00252         size.width = img->width;
00253 
00254         if (img->depth != IPL_DEPTH_8U) {
00255                 ROS_INFO("ERROR in cropImage: Unknown image depth of %d given in cropImage() instead of 8 bits per pixel.", img->depth);
00256                 exit(1);
00257         }
00258 
00259         // First create a new (color or greyscale) IPL Image and copy contents of img into it.
00260         imageTmp = cvCreateImage(size, IPL_DEPTH_8U, img->nChannels);
00261         cvCopy(img, imageTmp, NULL);
00262 
00263         // Create a new image of the detected region
00264         // Set region of interest to that surrounding the face
00265         cvSetImageROI(imageTmp, region);
00266         // Copy region of interest (i.e. face) into a new iplImage (imageRGB) and return it
00267         size.width = region.width;
00268         size.height = region.height;
00269         imageRGB = cvCreateImage(size, IPL_DEPTH_8U, img->nChannels);
00270         cvCopy(imageTmp, imageRGB, NULL);       // Copy just the region.
00271 
00272     cvReleaseImage( &imageTmp );
00273         return imageRGB;
00274 }
00275 
00276 // Get an 8-bit equivalent of the 32-bit Float image.
00277 // Returns a new image, so remember to call 'cvReleaseImage()' on the result.
00278 IplImage* FaceRecognitionLib::convertFloatImageToUcharImage(const IplImage *srcImg)
00279 {
00280         IplImage *dstImg = 0;
00281         if ((srcImg) && (srcImg->width > 0 && srcImg->height > 0)) {
00282 
00283                 // Spread the 32bit floating point pixels to fit within 8bit pixel range.
00284                 double minVal, maxVal;
00285                 cvMinMaxLoc(srcImg, &minVal, &maxVal);
00286 
00287                 //cout << "FloatImage:(minV=" << minVal << ", maxV=" << maxVal << ")." << endl;
00288 
00289                 // Deal with NaN and extreme values, since the DFT seems to give some NaN results.
00290                 if (cvIsNaN(minVal) || minVal < -1e30)
00291                         minVal = -1e30;
00292                 if (cvIsNaN(maxVal) || maxVal > 1e30)
00293                         maxVal = 1e30;
00294                 if (maxVal-minVal == 0.0f)
00295                         maxVal = minVal + 0.001;        // remove potential divide by zero errors.
00296 
00297                 // Convert the format
00298                 dstImg = cvCreateImage(cvSize(srcImg->width, srcImg->height), 8, 1);
00299                 cvConvertScale(srcImg, dstImg, 255.0 / (maxVal - minVal), - minVal * 255.0 / (maxVal-minVal));
00300         }
00301         return dstImg;
00302 }
00303 
00304 
00305 
00306 
00307 void FaceRecognitionLib::storeEigenfaceImages()
00308 {
00309         // Store the average image to a file
00310         ROS_INFO("Saving the image of the average face as 'out_averageImage.bmp'.");
00311         cvSaveImage("out_averageImage.bmp", pAvgTrainImg);
00312         // Create a large image made of many eigenface images.
00313         // Must also convert each eigenface image to a normal 8-bit UCHAR image instead of a 32-bit float image.
00314         ROS_INFO("Saving the %d eigenvector images as 'out_eigenfaces.bmp'", nEigens);
00315         if (nEigens > 0) {
00316                 // Put all the eigenfaces next to each other.
00317                 int COLUMNS = 8;        // Put upto 8 images on a row.
00318                 int nCols = min(nEigens, COLUMNS);
00319                 int nRows = 1 + (nEigens / COLUMNS);    // Put the rest on new rows.
00320                 int w = eigenVectArr[0]->width;
00321                 int h = eigenVectArr[0]->height;
00322                 CvSize size;
00323                 size = cvSize(nCols * w, nRows * h);
00324                 IplImage *bigImg = cvCreateImage(size, IPL_DEPTH_8U, 1);        // 8-bit Greyscale UCHAR image
00325                 for (int i=0; i<nEigens; i++) {
00326                         // Get the eigenface image.
00327                         IplImage *byteImg = convertFloatImageToUcharImage(eigenVectArr[i]);
00328                         // Paste it into the correct position.
00329                         int x = w * (i % COLUMNS);
00330                         int y = h * (i / COLUMNS);
00331                         CvRect ROI = cvRect(x, y, w, h);
00332                         cvSetImageROI(bigImg, ROI);
00333                         cvCopyImage(byteImg, bigImg);
00334                         cvResetImageROI(bigImg);
00335                         cvReleaseImage(&byteImg);
00336                 }
00337                 cvSaveImage("out_eigenfaces.bmp", bigImg);
00338                 cvReleaseImage(&bigImg);
00339         }
00340 }
00341 
00342 // Train from the data in the given text file, and store the trained data into the file 'facedata.xml'.
00343 bool FaceRecognitionLib::learn(const char *szFileTrain)
00344 {
00345         int i, offset;
00346 
00347         // load training data
00348         ROS_INFO("Loading the training images in '%s'", szFileTrain);
00349         nTrainFaces = loadFaceImgArray(szFileTrain);
00350         ROS_INFO("Got %d training images.\n", nTrainFaces);
00351         if( nTrainFaces < 2 )
00352         {
00353                 fprintf(stderr,
00354                         "Need 2 or more training faces"
00355                         "Input file contains only %d", nTrainFaces);
00356                 return false;
00357         }
00358 
00359         // do PCA on the training faces
00360         doPCA();
00361 
00362         // project the training images onto the PCA subspace
00363         projectedTrainFaceMat = cvCreateMat( nTrainFaces, nEigens, CV_32FC1 );
00364         offset = projectedTrainFaceMat->step / sizeof(float);
00365         for(i=0; i<nTrainFaces; i++)
00366         {
00367                 //int offset = i * nEigens;
00368                 cvEigenDecomposite(
00369                         faceImgArr[i],
00370                         nEigens,
00371                         eigenVectArr,
00372                         0, 0,
00373                         pAvgTrainImg,
00374                         //projectedTrainFaceMat->data.fl + i*nEigens);
00375                         projectedTrainFaceMat->data.fl + i*offset);
00376         }
00377 
00378         // store the recognition data as an xml file
00379         storeTrainingData();
00380 
00381         // Save all the eigenvectors as images, so that they can be checked.
00382         //if (SAVE_EIGENFACE_IMAGES) {
00383         //      storeEigenfaceImages();
00384         //}
00385         return true;
00386 
00387 }
00388 
00389 
00390 // Open the training data from the file 'facedata.xml'.
00391 int FaceRecognitionLib::loadTrainingData(CvMat ** pTrainPersonNumMat)
00392 {
00393         CvFileStorage * fileStorage;
00394         int i;
00395 
00396         // create a file-storage interface
00397         fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_READ );
00398         if( !fileStorage ) {
00399                 ROS_INFO("Can't open training database file 'facedata.xml'.");
00400                 return 0;
00401         }
00402 
00403         // Load the person names. Added by Shervin.
00404         personNames.clear();    // Make sure it starts as empty.
00405         nPersons = cvReadIntByName( fileStorage, 0, "nPersons", 0 );
00406         if (nPersons == 0) {
00407                 ROS_INFO("No people found in the training database 'facedata.xml'.");
00408                 return 0;
00409         }
00410         // Load each person's name.
00411         for (i=0; i<nPersons; i++) {
00412                 string sPersonName;
00413                 char varname[200];
00414                 sprintf( varname, "personName_%d", (i+1) );
00415                 sPersonName = cvReadStringByName(fileStorage, 0, varname );
00416                 personNames.push_back( sPersonName );
00417         }
00418 
00419         // Load the data
00420         nEigens = cvReadIntByName(fileStorage, 0, "nEigens", 0);
00421         nTrainFaces = cvReadIntByName(fileStorage, 0, "nTrainFaces", 0);
00422         *pTrainPersonNumMat = (CvMat *)cvReadByName(fileStorage, 0, "trainPersonNumMat", 0);
00423         eigenValMat  = (CvMat *)cvReadByName(fileStorage, 0, "eigenValMat", 0);
00424         projectedTrainFaceMat = (CvMat *)cvReadByName(fileStorage, 0, "projectedTrainFaceMat", 0);
00425         pAvgTrainImg = (IplImage *)cvReadByName(fileStorage, 0, "avgTrainImg", 0);
00426         eigenVectArr = (IplImage **)cvAlloc(nTrainFaces*sizeof(IplImage *));
00427         for(i=0; i<nEigens; i++)
00428         {
00429                 char varname[200];
00430                 sprintf( varname, "eigenVect_%d", i );
00431                 eigenVectArr[i] = (IplImage *)cvReadByName(fileStorage, 0, varname, 0);
00432         }
00433 
00434         // release the file-storage interface
00435         cvReleaseFileStorage( &fileStorage );
00436 
00437         ROS_INFO("Training data loaded (%d training images of %d people):", nTrainFaces, nPersons);
00438         ROS_INFO("People: ");
00439         if (nPersons > 0)
00440                 ROS_INFO("<%s>", personNames[0].c_str());
00441         for (i=1; i<nPersons; i++) {
00442                 ROS_INFO(", <%s>", personNames[i].c_str());
00443         }
00444         database_updated = true;
00445         return 1;
00446 }
00447 
00448 
00449 // Save the training data to the file 'facedata.xml'.
00450 void FaceRecognitionLib::storeTrainingData()
00451 {
00452         CvFileStorage * fileStorage;
00453         int i;
00454 
00455         // create a file-storage interface
00456         fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_WRITE );
00457 
00458         // Store the person names. Added by Shervin.
00459         cvWriteInt( fileStorage, "nPersons", nPersons );
00460         for (i=0; i<nPersons; i++) {
00461                 char varname[200];
00462                 sprintf( varname, "personName_%d", (i+1) );
00463                 cvWriteString(fileStorage, varname, personNames[i].c_str(), 0);
00464         }
00465 
00466         // store all the data
00467         cvWriteInt( fileStorage, "nEigens", nEigens );
00468         cvWriteInt( fileStorage, "nTrainFaces", nTrainFaces );
00469         cvWrite(fileStorage, "trainPersonNumMat", trainPersonNumMat, cvAttrList(0,0));
00470         cvWrite(fileStorage, "eigenValMat", eigenValMat, cvAttrList(0,0));
00471         cvWrite(fileStorage, "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0));
00472         cvWrite(fileStorage, "avgTrainImg", pAvgTrainImg, cvAttrList(0,0));
00473         for(i=0; i<nEigens; i++)
00474         {
00475                 char varname[200];
00476                 sprintf( varname, "eigenVect_%d", i );
00477                 cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0));
00478         }
00479 
00480         // release the file-storage interface
00481         cvReleaseFileStorage( &fileStorage );
00482 }
00483 
00484 // Find the most likely person based on a detection. Returns the index, and stores the confidence value into pConfidence.
00485 int FaceRecognitionLib::findNearestNeighbor(float * projectedTestFace, float *pConfidence)
00486 {
00487         //double leastDistSq = 1e12;
00488         double leastDistSq = DBL_MAX;
00489         int i, iTrain, iNearest = 0;
00490 
00491         for(iTrain=0; iTrain<nTrainFaces; iTrain++)
00492         {
00493                 double distSq=0;
00494 
00495                 for(i=0; i<nEigens; i++)
00496                 {
00497                         float d_i = projectedTestFace[i] - projectedTrainFaceMat->data.fl[iTrain*nEigens + i];
00498 #ifdef USE_MAHALANOBIS_DISTANCE
00499                         distSq += d_i*d_i / eigenValMat->data.fl[i];  // Mahalanobis distance (might give better results than Eucalidean distance)
00500 #else
00501                         distSq += d_i*d_i; // Euclidean distance.
00502 #endif
00503                 }
00504 
00505                 if(distSq < leastDistSq)
00506                 {
00507                         leastDistSq = distSq;
00508                         iNearest = iTrain;
00509                 }
00510         }
00511 
00512         // Return the confidence level based on the Euclidean distance,
00513         // so that similar images should give a confidence between 0.5 to 1.0,
00514         // and very different images should give a confidence between 0.0 to 0.5.
00515         *pConfidence = 1.0f - sqrt( leastDistSq / (float)(nTrainFaces * nEigens) ) / 255.0f;
00516 
00517         // Return the found index.
00518         return iNearest;
00519 }
00520 
00521 // Do the Principal Component Analysis, finding the average image
00522 // and the eigenfaces that represent any image in the given dataset.
00523 void FaceRecognitionLib::doPCA()
00524 {
00525         int i;
00526         CvTermCriteria calcLimit;
00527         CvSize faceImgSize;
00528 
00529         // set the number of eigenvalues to use
00530         nEigens = nTrainFaces-1;
00531 
00532         // allocate the eigenvector images
00533         faceImgSize.width  = faceImgArr[0]->width;
00534         faceImgSize.height = faceImgArr[0]->height;
00535         eigenVectArr = (IplImage**)cvAlloc(sizeof(IplImage*) * nEigens);
00536         for(i=0; i<nEigens; i++)
00537                 eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);
00538 
00539         // allocate the eigenvalue array
00540         eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 );
00541 
00542         // allocate the averaged image
00543         pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);
00544 
00545         // set the PCA termination criterion
00546         calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1);
00547         ROS_INFO("**** nTrainFaces: %d",nTrainFaces);
00548         // compute average image, eigenvalues, and eigenvectors
00549         cvCalcEigenObjects(
00550                 nTrainFaces,
00551                 (void*)faceImgArr,
00552                 (void*)eigenVectArr,
00553                 CV_EIGOBJ_NO_CALLBACK,
00554                 0,
00555                 0,
00556                 &calcLimit,
00557                 pAvgTrainImg,
00558                 eigenValMat->data.fl);
00559 
00560         cvNormalize(eigenValMat, eigenValMat, 1, 0, CV_L1, 0);
00561 }
00562 
00563 // Read the names & image filenames of people from a text file, and load all those images listed.
00564 int FaceRecognitionLib::loadFaceImgArray(const char * filename)
00565 {
00566         FILE * imgListFile = 0;
00567         char imgFilename[512];
00568         int iFace, nFaces=0;
00569         int i;
00570         IplImage *pfaceImg;
00571         IplImage *psizedImg;
00572         IplImage *pequalizedImg;
00573         // open the input file
00574         if( !(imgListFile = fopen(filename, "r")) )
00575         {
00576                 fprintf(stderr, "Can\'t open file %s\n", filename);
00577                 return 0;
00578         }
00579 
00580         // count the number of faces
00581         while( fgets(imgFilename, 512, imgListFile) ) ++nFaces;
00582         rewind(imgListFile);
00583 
00584         // allocate the face-image array and person number matrix
00585         faceImgArr        = (IplImage **)cvAlloc( nFaces*sizeof(IplImage *) );
00586         trainPersonNumMat = cvCreateMat( 1, nFaces, CV_32SC1 );
00587 
00588         personNames.clear();    // Make sure it starts as empty.
00589         nPersons = 0;
00590 
00591         // store the face images in an array
00592         for(iFace=0; iFace<nFaces; iFace++)
00593         {
00594                 char personName[256];
00595                 string sPersonName;
00596                 int personNumber;
00597                 // read person number (beginning with 1), their name and the image filename.
00598                 fscanf(imgListFile, "%d %s %s", &personNumber, personName, imgFilename);
00599                 sPersonName = personName;
00600                 //ROS_INFO("Got %d: %d, <%s>, <%s>.\n", iFace, personNumber, personName, imgFilename);
00601 
00602                 // Check if a new person is being loaded.
00603                 if (personNumber > nPersons) {
00604                         // Allocate memory for the extra person (or possibly multiple), using this new person's name.
00605                         for (i=nPersons; i < personNumber; i++) {
00606                                 personNames.push_back( sPersonName );
00607                         }
00608                         nPersons = personNumber;
00609                         //ROS_INFO("Got new person <%s> -> nPersons = %d [%d]\n", sPersonName.c_str(), nPersons, personNames.size());
00610                 }
00611 
00612                 // Keep the data
00613                 trainPersonNumMat->data.i[iFace] = personNumber;
00614 
00615                 // load the face image
00616                 pfaceImg = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE);
00617                 psizedImg = resizeImage(pfaceImg, faceWidth, faceHeight);
00618                 // Give the image a standard brightness and contrast, in case it was too dark or low contrast.
00619                 pequalizedImg = cvCreateImage(cvGetSize(psizedImg), 8, 1);      // Create an empty greyscale image
00620                 cvEqualizeHist(psizedImg, pequalizedImg);
00621                 faceImgArr[iFace] = pequalizedImg;
00622                 cvReleaseImage( &pfaceImg );cvReleaseImage( &psizedImg );   
00623                 if( !faceImgArr[iFace] )
00624                 {
00625                         fprintf(stderr, "Can\'t load image from %s\n", imgFilename);
00626                         return 0;
00627                 }
00628         }
00629 
00630         fclose(imgListFile);
00631 
00632         ROS_INFO("Data loaded from '%s': (%d images of %d people).\n", filename, nFaces, nPersons);
00633         ROS_INFO("People: ");
00634         if (nPersons > 0)
00635                 ROS_INFO("<%s>", personNames[0].c_str());
00636         for (i=1; i<nPersons; i++) {
00637                 ROS_INFO(", <%s>", personNames[i].c_str());
00638         }
00639         ROS_INFO(".\n");
00640 
00641         return nFaces;
00642 }
00643 
00644 
00645 
00646 // Re-train the new face rec database 
00647 // Depending on the number of images in the training set and number of people, it might take 30 seconds or so.
00648 bool FaceRecognitionLib::retrainOnline(void)
00649 {
00650         // Free & Re-initialize the global variables.
00651         if(trainPersonNumMat)   {cvReleaseMat( &trainPersonNumMat );trainPersonNumMat=0;}
00652         int i;
00653         if (faceImgArr)
00654         {
00655            for (i=0; i<nTrainFaces; i++)
00656               if (faceImgArr[i])        {cvReleaseImage( &faceImgArr[i] );}
00657            cvFree( &faceImgArr ); // array of face images
00658            faceImgArr=0;                           
00659         }
00660         if(eigenVectArr)
00661         {
00662            for (i=0; i<nEigens; i++)
00663               if (eigenVectArr[i])      {cvReleaseImage( &eigenVectArr[i] );}
00664            cvFree( &eigenVectArr ); // eigenvectors
00665            eigenVectArr=0;
00666         }
00667 
00668         if(trainPersonNumMat) {cvReleaseMat( &trainPersonNumMat ); trainPersonNumMat=0;}// array of person numbers
00669         personNames.clear();                    // array of person names (indexed by the person number). Added by Shervin.
00670         nPersons = 0; // the number of people in the training set. Added by Shervin.
00671         nTrainFaces = 0; // the number of training images
00672         nEigens = 0; // the number of eigenvalues
00673         if(pAvgTrainImg) {cvReleaseImage( &pAvgTrainImg ); pAvgTrainImg=0;}// the average image
00674         if(eigenValMat)  {cvReleaseMat( &eigenValMat );eigenValMat=0;} // eigenvalues
00675         if(projectedTrainFaceMat) {cvReleaseMat( &projectedTrainFaceMat );projectedTrainFaceMat=0;} // projected training faces
00676         // Retrain from the data in the files
00677         if(!learn("train.txt"))
00678            return(false);
00679         database_updated=true;
00680         return(true);
00681         
00682 }


face_recognition
Author(s): Pouyan Ziafati
autogenerated on Thu Jun 6 2019 18:29:21