1. 本程序首先利用从摄像头检测到的人脸图片,先进行直方图均衡化  
2. 并缩放到92*112的图片大小,然后根据train.txt的采集到的人脸模版  
3. 进行匹配识别(最好是在统一光照下,采集不同角度的人脸图片各一张)  
4. 注意:影响的极大因素在于光照,模版若与采集的图像光照不一样,识别率很低。  
5. 经测试,模板若与检测的图像在同一光照下的话,侧脸,仰脸,正脸均可识别,且识别率较高

1. //  
2. #include <stdio.h>  
3. #include <string.h>  
4. #include "cv.h"  
5. #include "cvaux.h"  
6. #include "highgui.h"  
7. #include <stdlib.h>  
8. #include <assert.h>    
9. #include <math.h>    
10. #include <float.h>    
11. #include <limits.h>    
12. #include <time.h>    
13. #include <ctype.h>    
14.   
15. 定义几个重要的全局变量  
16. IplImage ** faceImgArr        = 0; // 指向训练人脸和测试人脸的指针(在学习和识别阶段指向不同)  
17. CvMat    *  personNumTruthMat = 0; // 人脸图像的ID号  
18. int nTrainFaces               = 0; // 训练图像的数目  
19. int nEigens                   = 0; // 自己取的主要特征值数目  
20. IplImage * pAvgTrainImg       = 0; // 训练人脸数据的平均值  
21. IplImage ** eigenVectArr      = 0; // 投影矩阵,也即主特征向量  
22. CvMat * eigenValMat           = 0; // 特征值  
23. CvMat * projectedTrainFaceMat = 0; // 训练图像的投影  
24. char *filename[5]={"face1.jpg","face2.jpg","face3.jpg","face4.jpg","face5.jpg"};  
25.   
26. static CvMemStorage* storage = 0;    
27. static CvHaarClassifierCascade* cascade = 0;    
28. int j=0;//统计记录的人脸数  
29. char a[512]={0};  
30. int a1,a2,a3,a4;  
31. time_t timeBegin, timeEnd;      
32. int timeuse;    
33.  函数原型  
34. void learn();  
35. void doPCA();  
36. void storeTrainingData();  
37. int  loadTrainingData(CvMat ** pTrainPersonNumMat);  
38. int  findNearestNeighbor(float * projectedTestFace);  
39. int  loadFaceImgArray(char * filename);  
40. void printUsage();  
41. int detect_and_draw( IplImage* image );    
42. int recognize(IplImage *faceimg);  
43.   
44. //主函数,主要包括学习和识别两个阶段,需要运行两次,通过命令行传入的参数区分  
45. int main( int argc, char** argv )  
46. {  
47.     CvCapture* capture = 0;    
48.     IplImage *frame, *frame_copy = 0;    
49. int optlen = strlen("--cascade=");    
50. char *cascade_name = "H://opencv-2.4.3//opencv//data//haarcascades//haarcascade_frontalface_alt2.xml";    
51. //opencv装好后haarcascade_frontalface_alt2.xml的路径,    
52. //也可以把这个文件拷到你的工程文件夹下然后不用写路径名cascade_name= "haarcascade_frontalface_alt2.xml";      
53. //或者cascade_name ="C:\\Program Files\\OpenCV\\data\\haarcascades\\haarcascade_frontalface_alt2.xml"    
54.     cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );    
55.      
56. if( !cascade )    
57.     {    
58. "ERROR: Could not load classifier cascade\n" );    
59.         fprintf( stderr,    
60. "Usage: facedetect --cascade=\"<cascade_path>\" [filename|camera_index]\n" );    
61. return -1;    
62.     }    
63.     storage = cvCreateMemStorage(0);    
64.      capture = cvCreateCameraCapture(-1);    
65. "result", 1 );    
66.      
67. if( capture )    
68.     {     timeBegin = time(NULL);      
69.           learn();  
70. for(;;)    
71.         {   timeEnd = time(NULL);      
72. //计算经过的时间,统计人数  
73. if( !cvGrabFrame( capture ))    
74. break;    
75.             frame = cvRetrieveFrame( capture );    
76. if( !frame )    
77. break;    
78. if( !frame_copy )    
79.                 frame_copy = cvCreateImage( cvSize(frame->width,frame->height),    
80.                                             IPL_DEPTH_8U, frame->nChannels );    
81. if( frame->origin == IPL_ORIGIN_TL )//如果图像的起点在左上角    
82.                 cvCopy( frame, frame_copy, 0 );    
83. else    
84. //如果图像的起点不在左上角,而在左下角时,进行X轴对称    
85.      
86. //检测并且识别  
87.             
88. if( cvWaitKey( 10 ) >= 0 )    
89. break;    
90.         }    
91.      
92.         cvReleaseImage( &frame_copy );    
93.         cvReleaseCapture( &capture );    
94.     }    
95. else    
96.     {    
97. "Cannot read from CAM");    
98. return -1;    
99.     }    
100.      
101. "result");    
102.      
103. return 0;    
104. }    
105.   
106. //学习阶段代码  
107. void learn()  
108. {  
109. int i, offset;  
110.   
111. //加载训练图像集  
112. "train.txt");  
113. if( nTrainFaces < 2 )  
114.     {  
115.         fprintf(stderr,  
116. "Need 2 or more training faces\n"  
117. "Input file contains only %d\n", nTrainFaces);  
118.           
119. return;  
120.     }  
121.   
122. // 进行主成分分析  
123.     doPCA();  
124.   
125. //将训练图集投影到子空间中  
126.     projectedTrainFaceMat = cvCreateMat( nTrainFaces, nEigens, CV_32FC1 );  
127. sizeof(float);  
128. for(i=0; i<nTrainFaces; i++)  
129.     {  
130. //int offset = i * nEigens;  
131.         cvEigenDecomposite(  
132.             faceImgArr[i],  
133.             nEigens,  
134.             eigenVectArr,  
135.             0, 0,  
136.             pAvgTrainImg,  
137. //projectedTrainFaceMat->data.fl + i*nEigens);  
138.             projectedTrainFaceMat->data.fl + i*offset);  
139.     }  
140.   
141. //将训练阶段得到的特征值,投影矩阵等数据存为.xml文件,以备测试时使用  
142.     storeTrainingData();  
143. }  
144.   
145.   
146. //加载保存过的训练结果  
147. int loadTrainingData(CvMat ** pTrainPersonNumMat)  
148. {  
149.     CvFileStorage * fileStorage;  
150. int i;  
151.   
152.       
153. "facedata.xml", 0, CV_STORAGE_READ );  
154. if( !fileStorage )  
155.     {  
156. "Can't open facedata.xml\n");  
157. return 0;  
158.     }  
159.   
160. "nEigens", 0);  
161. "nTrainFaces", 0);  
162. "trainPersonNumMat", 0);  
163. "eigenValMat", 0);  
164. "projectedTrainFaceMat", 0);  
165. "avgTrainImg", 0);  
166. sizeof(IplImage *));  
167. for(i=0; i<nEigens; i++)  
168.     {  
169. char varname[200];  
170. "eigenVect_%d", i );  
171.         eigenVectArr[i] = (IplImage *)cvReadByName(fileStorage, 0, varname, 0);  
172.     }  
173.   
174.       
175.     cvReleaseFileStorage( &fileStorage );  
176.   
177. return 1;  
178. }  
179.   
180. //存储训练结果  
181. void storeTrainingData()  
182. {  
183.     CvFileStorage * fileStorage;  
184. int i;  
185. "facedata.xml", 0, CV_STORAGE_WRITE );  
186.   
187. //存储特征值,投影矩阵,平均矩阵等训练结果  
188. "nEigens", nEigens );  
189. "nTrainFaces", nTrainFaces );  
190. "trainPersonNumMat", personNumTruthMat, cvAttrList(0,0));  
191. "eigenValMat", eigenValMat, cvAttrList(0,0));  
192. "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0));  
193. "avgTrainImg", pAvgTrainImg, cvAttrList(0,0));  
194. for(i=0; i<nEigens; i++)  
195.     {  
196. char varname[200];  
197. "eigenVect_%d", i );  
198.         cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0));  
199.     }  
200.     cvReleaseFileStorage( &fileStorage );  
201. }  
202.   
203. //寻找最接近的图像  
204. int findNearestNeighbor(float * projectedTestFace)  
205. {  
206.   
207. double leastDistSq = DBL_MAX;       //定义最小距离,并初始化为无穷大  
208. int i, iTrain, iNearest = 0;  
209.   
210. for(iTrain=0; iTrain<nTrainFaces; iTrain++)  
211.     {  
212. double distSq=0;  
213.   
214. for(i=0; i<nEigens; i++)  
215.         {  
216. float d_i =  
217.                 projectedTestFace[i] -  
218.                 projectedTrainFaceMat->data.fl[iTrain*nEigens + i];  
219. // Mahalanobis算法计算的距离,差的距离的平方除以平均脸的特征值  
220. //  distSq += d_i*d_i; // Euclidean算法计算的距离  
221.         }  
222.   
223. if(distSq < leastDistSq)  
224.         {  
225.             leastDistSq = distSq;  
226.             iNearest = iTrain;  
227.         }  
228.     }  
229. //printf("leastdistsq==%f",leastDistSq);  
230. return iNearest;  
231. }  
232.   
233.   
234.   
235. //主成分分析  
236. void doPCA()  
237. {  
238. int i;  
239.     CvTermCriteria calcLimit;  
240.     CvSize faceImgSize;  
241.   
242. // 自己设置主特征值个数  
243.     nEigens = nTrainFaces-1;  
244.   
245. //分配特征向量存储空间  
246.     faceImgSize.width  = faceImgArr[0]->width;  
247.     faceImgSize.height = faceImgArr[0]->height;  
248. sizeof(IplImage*) * nEigens);    //分配个数为住特征值个数  
249. for(i=0; i<nEigens; i++)  
250.         eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);  
251.   
252. //分配主特征值存储空间  
253.     eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 );  
254.   
255. // 分配平均图像存储空间  
256.     pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);  
257.   
258. // 设定PCA分析结束条件  
259. //最大迭代次数为nEigens  
260.   
261. // 计算平均图像,特征值,特征向量  
262.     cvCalcEigenObjects(  
263.         nTrainFaces,  
264. void*)faceImgArr,  
265. void*)eigenVectArr,  
266.         CV_EIGOBJ_NO_CALLBACK,  
267.         0,  
268.         0,  
269.         &calcLimit,  
270.         pAvgTrainImg,  
271. //存储求得的eigenvalue  
272.         );  
273.   
274.     cvNormalize(eigenValMat, eigenValMat, 1, 0, CV_L1, 0);  
275. }  
276.   
277.   
278.   
279. //加载txt文件的列举的图像  
280. int loadFaceImgArray(char * filename)  
281. {  
282. FILE * imgListFile = 0;  
283. char imgFilename[512];  
284. int iFace, nFaces=0;  
285.   
286.   
287. if( !(imgListFile = fopen(filename, "r")) )  
288.     {  
289. "Can\'t open file %s\n", filename);  
290. return 0;  
291.     }  
292.   
293. // 统计人脸数  
294. while( fgets(imgFilename, 512, imgListFile) ) ++nFaces;//char *fgets(char *buf, int bufsize, FILE *stream);从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋'\0'),如果文件中的该行,不足bufsize个字符,则读完该行就结束。如果函数读取成功,则返回指针buf,失败则返回NULL。  
295. //将文件内部的位置指针重新指向一个流(数据流/文件)的开头  
296.   
297. // 分配人脸图像存储空间和人脸ID号存储空间  
298. sizeof(IplImage *) );  
299. //CvMat* cvCreateMat( int rows, int cols, int type );  
300.   
301. for(iFace=0; iFace<nFaces; iFace++)  
302.     {  
303. // 从文件中读取序号和人脸名称  
304.         fscanf(imgListFile,  
305. "%d %s", personNumTruthMat->data.i+iFace, imgFilename);// fscanf(FILE *stream, char *format,[argument...])功 能: 从一个流中执行格式化输入,fscanf遇到空格和换行时结束  
306.   
307. // 加载人脸图像  
308.         faceImgArr[iFace] = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE);  
309.   
310. if( !faceImgArr[iFace] )  
311.         {  
312. "Can\'t load image from %s\n", imgFilename);  
313. return 0;  
314.         }  
315.     }  
316.   
317.     fclose(imgListFile);  
318.   
319. return nFaces;  
320. }  
321.   
322.   
323.   
324. //  
325. void printUsage()  
326. {  
327. "Usage: eigenface <command>\n",  
328. "  Valid commands are\n"  
329. "    train\n"  
330. "    test\n");  
331. }  
332.   
333. int detect_and_draw( IplImage* img )    
334. {    
335.  CvFont font;  
336.  cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 1, 8);  
337. static CvScalar colors[] =     
338.     {    
339.         {{0,0,255}},    
340.         {{0,128,255}},    
341.         {{0,255,255}},    
342.         {{0,255,0}},    
343.         {{255,128,0}},    
344.         {{255,255,0}},    
345.         {{255,0,0}},    
346.         {{255,0,255}}    
347.     };    
348.      
349. double scale = 1.3;    
350.     IplImage* gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );    
351.     IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),    
352.                          cvRound (img->height/scale)),    
353.                      8, 1 );    
354. int i,personnum=0;    
355.      
356.     cvCvtColor( img, gray, CV_BGR2GRAY );    
357.     cvResize( gray, small_img, CV_INTER_LINEAR );    
358.     cvEqualizeHist( small_img, small_img );    
359.     cvClearMemStorage( storage );    
360.      
361. if( cascade )    
362.     {    
363. double t = (double)cvGetTickCount();    
364.         CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,    
365. /*CV_HAAR_DO_CANNY_PRUNING*/,    
366.                                             cvSize(30, 30) );    
367. double)cvGetTickCount() - t;    
368. //  printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );    
369.          IplImage* temp_img=cvCreateImage(cvSize(92,112),8,1);    
370. for( i = 0; i < (faces ? faces->total : 0); i++ )    
371.         {    
372.             CvRect* r = (CvRect*)cvGetSeqElem( faces, i );    
373.             
374. //cvsize只能选取r->width,r->height不能再后面*scale或+100    
375.           CvPoint p1;    
376.           p1.x=cvRound((r->x)*scale);    
377.           p1.y=cvRound((r->y)*scale);    
378.           CvPoint p2;    
379.           p2.x=cvRound((r->x+r->width)*scale);    
380.           p2.y=cvRound((r->y+r->height)*scale);    
381.             cvRectangle(img,p1,p2,colors[i%8],3,8,0);    
382.             cvSetImageROI(small_img,*r);    
383.             cvCopy(small_img,dst);    
384.             cvResize(dst,temp_img);    
385.         cvEqualizeHist( temp_img, temp_img );    
386.              cvResetImageROI(small_img);       
387.             cvSaveImage(filename[i],temp_img);    
388.               cvReleaseImage(&dst);    
389. //开始识别temp_img  
390.             
391.               personnum=recognize(temp_img);  
392. if(personnum==1)  
393. "Yanming" , cvPoint(20, 20), &font, CV_RGB(255,255,255));//将正确识别的人的姓名显示在屏幕上  
394.                  
395.         }    
396.     }    
397.      
398. "result", img );    
399.     cvReleaseImage( &gray );    
400.     cvReleaseImage( &small_img );    
401. return -1;  
402. }    
403.   
404. int recognize(IplImage *faceimg)  
405. {  
406. int i, nTestFaces  = 0;         // 测试人脸数  
407. // 训练阶段的人脸数  
408. float * projectedTestFace = 0;  
409. // 加载保存在.xml文件中的训练结果  
410. if( !loadTrainingData( &trainPersonNumMat ) ) return -3;  
411.   
412. float *)cvAlloc( nEigens*sizeof(float) );  
413.       
414. int iNearest, nearest;  
415.   
416. //将测试图像投影到子空间中  
417.         cvEigenDecomposite(  
418.             faceimg,  
419.             nEigens,  
420.             eigenVectArr,  
421.             0, 0,  
422.             pAvgTrainImg,  
423.             projectedTestFace);  
424. //cvNormalize(projectedTestFace, projectedTestFace, 1, 0, CV_L1, 0);  
425.         iNearest = findNearestNeighbor(projectedTestFace);  
426.         nearest  = trainPersonNumMat->data.i[iNearest];  
427. "nearest = %d", nearest);  
428. if(timeuse<=10)  
429.         {  
430.               
431. if((nearest==1)|(nearest==11)|(nearest==111))//可以更改train.txt中训练图片的编号,这里将侧脸,仰脸,正脸都归为一起  
432.             a1++;  
433. if(nearest==2)  
434.             a2++;  
435. if(nearest==3)  
436.             a3++;  
437. if(nearest==4)  
438.             a4++;  
439.           
440.                   
441. if(a1>7)//如果10s中识别的次数为6则认定为a1  
442.         {  
443. "yanming\n");  
444. return 1;  
445.         }  
446. if(a2>6)  
447. "others\n");}  
448. if(a3>6)  
449. "ma\n");}  
450. if(a4>6)  
451. "ba\n");}  
452.         }  
453. else  
454.             {  
455.                 timeBegin=time(NULL);  
456.                 a1=0;  
457.                 a2=0;  
458.                 a3=0;  
459.                 a4=0;  
460. return 0;  
461.             }  
462. return -1;  
463. }