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. }