1 #include <stdio.h> 2 #include <string.h> 3 #include "cv.h" 4 #include "cvaux.h" 5 #include "highgui.h" 6 7 using namespace cv; 8 9 //globle variables 10 int nTrainFaces = 0; // number of trainning images 11 int nEigens = 0; // number of eigenvalues 12 IplImage** faceImgArr = 0; // array of face images 13 CvMat* personNumTruthMat = 0; // array of person numbers 14 IplImage* pAvgTrainImg = 0; // the average image 15 IplImage** eigenVectArr = 0; // eigenvectors 16 CvMat* eigenValMat = 0; // eigenvalues 17 CvMat* projectedTrainFaceMat = 0; // projected training faces 18 19 //// Function prototypes 20 void learn(); 21 void recognize(); 22 void doPCA(); 23 void storeTrainingData(); 24 int loadTrainingData(CvMat** pTrainPersonNumMat); 25 int findNearestNeighbor(float* projectedTestFace); 26 int loadFaceImgArray(char* filename); 27 void printUsage(); 28 29 int main( int argc, char** argv ) 30 { 31 if((argc != 2) && (argc != 3)){ 32 printUsage(); 33 return -1; 34 } 35 36 if( !strcmp(argv[1], "train" )){ 37 learn(); 38 } else if( !strcmp(argv[1], "test") ){ 39 recognize(); 40 } else { 41 printf("Unknown command: %s\n", argv[1]); 42 } 43 return 0; 44 } 45 46 void printUsage(){ 47 printf("Usage: eigenface <command>\n", 48 " Valid commands are\n" 49 " train\n" 50 " test\n" 51 ); 52 } 53 54 void learn(){ 55 int i; 56 57 // load training data 58 nTrainFaces = loadFaceImgArray("train.txt"); 59 if( nTrainFaces < 2){ 60 fprintf( 61 stderr, 62 "Need 2 or more training faces\n" 63 "Input file contains only %d\n", 64 nTrainFaces 65 ); 66 return; 67 } 68 69 // do PCA on the training faces 70 doPCA(); 71 72 // project the training images onto the PCA subspace 73 projectedTrainFaceMat = cvCreateMat(nTrainFaces, nEigens, CV_32FC1); 74 for(i = 0; i < nTrainFaces; i ++){ 75 cvEigenDecomposite( 76 faceImgArr[i], 77 nEigens, 78 eigenVectArr, 79 0, 0, 80 pAvgTrainImg, 81 projectedTrainFaceMat->data.fl + i*nEigens 82 ); 83 } 84 85 // store the recognition data as an xml file 86 storeTrainingData(); 87 } 88 89 int loadFaceImgArray(char* filename){ 90 FILE* imgListFile = 0; 91 char imgFilename[512]; 92 int iFace, nFaces = 0; 93 94 // open the input file 95 imgListFile = fopen(filename, "r"); 96 97 // count the number of faces 98 while( fgets(imgFilename, 512, imgListFile) ) ++ nFaces; 99 rewind(imgListFile); 100 101 // allocate the face-image array and person number matrix 102 faceImgArr = (IplImage **)cvAlloc( nFaces*sizeof(IplImage *) ); 103 personNumTruthMat = cvCreateMat( 1, nFaces, CV_32SC1 ); 104 105 // store the face images in an array 106 for(iFace=0; iFace<nFaces; iFace++){ 107 //read person number and name of image file 108 fscanf(imgListFile, "%d %s", personNumTruthMat->data.i+iFace, imgFilename); 109 110 // load the face image 111 faceImgArr[iFace] = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE); 112 } 113 114 fclose(imgListFile); 115 116 return nFaces; 117 } 118 119 void doPCA(){ 120 int i; 121 CvTermCriteria calcLimit; 122 CvSize faceImgSize; 123 124 // set the number of eigenvalues to use 125 nEigens = nTrainFaces - 1; 126 127 // allocate the eigenvector images 128 faceImgSize.width = faceImgArr[0]->width; 129 faceImgSize.height = faceImgArr[0]->height; 130 eigenVectArr = (IplImage**)cvAlloc(sizeof(IplImage*) * nEigens); 131 for(i=0; i<nEigens; i++){ 132 eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1); 133 } 134 135 // allocate the eigenvalue array 136 eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 ); 137 138 // allocate the averaged image 139 pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1); 140 141 // set the PCA termination criterion 142 calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1); 143 144 // compute average image, eigenvalues, and eigenvectors 145 cvCalcEigenObjects( 146 nTrainFaces, 147 (void*)faceImgArr, 148 (void*)eigenVectArr, 149 CV_EIGOBJ_NO_CALLBACK, 150 0, 151 0, 152 &calcLimit, 153 pAvgTrainImg, 154 eigenValMat->data.fl 155 ); 156 } 157 158 void storeTrainingData(){ 159 CvFileStorage* fileStorage; 160 int i; 161 162 // create a file-storage interface 163 fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_WRITE); 164 165 // store all the data 166 cvWriteInt( fileStorage, "nEigens", nEigens); 167 cvWriteInt( fileStorage, "nTrainFaces", nTrainFaces ); 168 cvWrite(fileStorage, "trainPersonNumMat", personNumTruthMat, cvAttrList(0, 0)); 169 cvWrite(fileStorage, "eigenValMat", eigenValMat, cvAttrList(0,0)); 170 cvWrite(fileStorage, "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0)); 171 cvWrite(fileStorage, "avgTrainImg", pAvgTrainImg, cvAttrList(0,0)); 172 173 for(i=0; i<nEigens; i++){ 174 char varname[200]; 175 sprintf( varname, "eigenVect_%d", i); 176 cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0)); 177 } 178 179 //release the file-storage interface 180 cvReleaseFileStorage( &fileStorage ); 181 } 182 183 void recognize(){ 184 int i, nTestFaces = 0; // the number of test images 185 CvMat* trainPersonNumMat = 0; // the person numbers during training 186 float* projectedTestFace = 0; 187 188 // load test images and ground truth for person number 189 nTestFaces = loadFaceImgArray("test.txt"); 190 printf("%d test faces loaded\n", nTestFaces); 191 192 // load the saved training data 193 if( !loadTrainingData( &trainPersonNumMat ) ) return; 194 195 // project the test images onto the PCA subspace 196 projectedTestFace = (float*)cvAlloc( nEigens*sizeof(float) ); 197 for(i=0; i<nTestFaces; i++){ 198 int iNearest, nearest, truth; 199 200 // project the test image onto PCA subspace 201 cvEigenDecomposite( 202 faceImgArr[i], 203 nEigens, 204 eigenVectArr, 205 0, 0, 206 pAvgTrainImg, 207 projectedTestFace 208 ); 209 210 iNearest = findNearestNeighbor(projectedTestFace); 211 truth = personNumTruthMat->data.i[i]; 212 nearest = trainPersonNumMat->data.i[iNearest]; 213 214 printf("nearest = %d, Truth = %d\n", nearest, truth); 215 } 216 } 217 218 int loadTrainingData(CvMat** pTrainPersonNumMat){ 219 CvFileStorage* fileStorage; 220 int i; 221 222 // create a file-storage interface 223 fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_READ ); 224 if( !fileStorage ){ 225 fprintf(stderr, "Can't open facedata.xml\n"); 226 return 0; 227 } 228 229 nEigens = cvReadIntByName(fileStorage, 0, "nEigens", 0); 230 nTrainFaces = cvReadIntByName(fileStorage, 0, "nTrainFaces", 0); 231 *pTrainPersonNumMat = (CvMat*)cvReadByName(fileStorage, 0, "trainPersonNumMat", 0); 232 eigenValMat = (CvMat*)cvReadByName(fileStorage, 0, "eigenValMat", 0); 233 projectedTrainFaceMat = (CvMat*)cvReadByName(fileStorage, 0, "projectedTrainFaceMat", 0); 234 pAvgTrainImg = (IplImage*)cvReadByName(fileStorage, 0, "avgTrainImg", 0); 235 eigenVectArr = (IplImage**)cvAlloc(nTrainFaces*sizeof(IplImage*)); 236 for(i=0; i<nEigens; i++){ 237 char varname[200]; 238 sprintf( varname, "eigenVect_%d", i ); 239 eigenVectArr[i] = (IplImage*)cvReadByName(fileStorage, 0, varname, 0); 240 } 241 242 // release the file-storage interface 243 cvReleaseFileStorage( &fileStorage ); 244 245 return 1; 246 } 247 248 int findNearestNeighbor(float* projectedTestFace){ 249 double leastDistSq = DBL_MAX; 250 int i, iTrain, iNearest = 0; 251 252 for(iTrain=0; iTrain<nTrainFaces; iTrain++){ 253 double distSq = 0; 254 255 for(i=0; i<nEigens; i++){ 256 float d_i = projectedTestFace[i] - 257 projectedTrainFaceMat->data.fl[iTrain*nEigens + i]; 258 distSq += d_i*d_i; 259 } 260 261 if(distSq < leastDistSq){ 262 leastDistSq = distSq; 263 iNearest = iTrain; 264 } 265 } 266 267 return iNearest; 268 }