基于OpenCV读取摄像头进行人脸检测和人脸识别

时间:2023-06-09 18:05:32
前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS2010+OpenCV2.4.4,opencv的环境配置网上有很多,不再赘述。检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_api.html

需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法最少训练图像为两张,而LBP可以单张图像训练。本人的实验采用的图片是100x100大小的,所以如果要添加自己的图像进行识别的话务必调整为100x100,不然会报错。当然在recog_and_draw这个函数里,笔者也将每次检测到的人脸进行了保存,拖出来重命名就可以,路径自己找吧。使用不同的方法识别时,其阈值设置也不同,LBP大概在100,其他两种方法大概在1000。本人的代码已共享,下载链接:http://download.csdn.net/detail/u010944555/6749725

ps:有人说代码的检测率不高,其实可以归结为两方面的原因,第一人脸检测率不高,这个可以通过嵌套检测嘴角、眼睛等来降低,或者背景、光照固定的话可以通过图像差分来解决;第二是识别方法本身的问题,如果想提高识别率,可以添加多张不同姿态、光照下的人脸作为训练的样本,如果有时间的话可以在采集图像时给出一个人脸框,引导用户对齐人脸进行采集,三星手机解除锁屏就有这么一个功能。

效果图:基于OpenCV读取摄像头进行人脸检测和人脸识别

废话不多说,上传代码。

main:

  1. #include "stdafx.h"
  2. #include "cv.h"
  3. #include "highgui.h"
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <assert.h>
  8. #include <math.h>
  9. #include <float.h>
  10. #include <limits.h>
  11. #include <time.h>
  12. #include <ctype.h>
  13. #include <opencv2\contrib\contrib.hpp>
  14. #include <opencv2\core\core.hpp>
  15. #include <opencv2\highgui\highgui.hpp>
  16. #include <iostream>
  17. #include <fstream>
  18. #include <sstream>
  19. #include "detect_recog.h"
  20. using namespace std;
  21. using namespace cv;
  22. #ifdef _EiC
  23. #define WIN32
  24. #endif
  25. CvMemStorage* storage = 0;
  26. CvHaarClassifierCascade* cascade = 0;
  27. CvHaarClassifierCascade* nested_cascade = 0;
  28. int use_nested_cascade = 0;
  29. const char* cascade_name =
  30. "./data/haarcascade_frontalface_alt.xml";//别人已经训练好的人脸检测xml数据
  31. const char* nested_cascade_name =
  32. "./data/haarcascade_eye_tree_eyeglasses.xml";
  33. CvCapture* capture = 0;
  34. IplImage *frame, *frame_copy = 0;
  35. IplImage *image = 0;
  36. const char* scale_opt = "--scale="; // 分类器选项指示符号
  37. int scale_opt_len = (int)strlen(scale_opt);
  38. const char* cascade_opt = "--cascade=";
  39. int cascade_opt_len = (int)strlen(cascade_opt);
  40. const char* nested_cascade_opt = "--nested-cascade";
  41. int nested_cascade_opt_len = (int)strlen(nested_cascade_opt);
  42. double scale = 1;
  43. int num_components = 9;
  44. double facethreshold = 9.0;
  45. //opencv的FaceRecogizer目前有三个类实现了他,特征脸和fisherface方法最少训练图像为两张,而LBP可以单张图像训练
  46. //cv::Ptr<cv::FaceRecognizer> model = cv::createEigenFaceRecognizer();
  47. //cv::Ptr<cv::FaceRecognizer> model = cv::createFisherFaceRecognizer();
  48. cv::Ptr<cv::FaceRecognizer> model = cv::createLBPHFaceRecognizer();//LBP的这个方法在单个人脸验证方面效果最好
  49. vector<Mat> images;//两个容器images,labels来存放图像数据和对应的标签
  50. vector<int> labels;
  51. int main( int argc, char** argv )
  52. {
  53. cascade = (CvHaarClassifierCascade*)cvLoad(cascade_name, 0, 0, 0); //加载分类器
  54. if(!cascade)
  55. {
  56. fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
  57. getchar();
  58. return -1;
  59. }
  60. model->set("threshold", 2100.0);
  61. string output_folder;
  62. output_folder = string("./einfacedata");
  63. //读取你的CSV文件路径
  64. string fn_csv = string("./einfacedata/at.txt");
  65. try
  66. {
  67. //通过./einfacedata/at.txt这个文件读取里面的训练图像和类别标签
  68. read_csv(fn_csv, images, labels);
  69. }
  70. catch(cv::Exception &e)
  71. {
  72. cerr<<"Error opening file "<<fn_csv<<". Reason: "<<e.msg<<endl;
  73. exit(1);
  74. }
  75. /*
  76. //read_img这个函数直接从einfacedata/trainingdata目录下读取图像数据并默认将图像置为0
  77. //所以如果用这个函数只能用来单个人脸验证
  78. if(!read_img(images, labels))
  79. {
  80. cout<< "Error in reading images!";
  81. images.clear();
  82. labels.clear();
  83. return 0;
  84. }
  85. */
  86. cout << images.size() << ":" << labels.size()<<endl;
  87. //如果没有读到足够的图片,就退出
  88. if(images.size() <= 2)
  89. {
  90. string error_message = "This demo needs at least 2 images to work.";
  91. CV_Error(CV_StsError, error_message);
  92. }
  93. //得到第一张照片的高度,在下面对图像变形到他们原始大小时需要
  94. //int height = images[0].rows;
  95. //移除最后一张图片,用于做测试
  96. //Mat testSample = images[images.size() - 1];
  97. //cv::imshow("testSample", testSample);
  98. //int testLabel = labels[labels.size() - 1];
  99. //images.pop_back();
  100. //labels.pop_back();
  101. //下面创建一个特征脸模型用于人脸识别,
  102. // 通过CSV文件读取的图像和标签训练它。
  103. //进行训练
  104. model->train(images, labels);
  105. storage = cvCreateMemStorage(0); // 创建内存存储器
  106. capture = cvCaptureFromCAM(0); // 创建视频读取结构
  107. cvNamedWindow( "result", 1 );
  108. if( capture ) // 如过是视频或摄像头采集图像,则循环处理每一帧
  109. {
  110. for(;;)
  111. {
  112. if( !cvGrabFrame( capture ))
  113. break;
  114. frame = cvRetrieveFrame( capture );
  115. if( !frame )
  116. break;
  117. if( !frame_copy )
  118. frame_copy = cvCreateImage( cvSize(640,480),IPL_DEPTH_8U, frame->nChannels );
  119. if( frame->origin == IPL_ORIGIN_TL )
  120. cvCopy( frame, frame_copy, 0 );
  121. else
  122. cvFlip( frame, frame_copy, 0 );
  123. //detect_and_draw( frame_copy ); // 如果调用这个函数,只是实现人脸检测
  124. //cout << frame_copy->width << "x" << frame_copy->height << endl;
  125. recog_and_draw( frame_copy );//该函数实现人脸检测和识别
  126. if( cvWaitKey( 100 ) >= 0 )//esc键值好像是100
  127. goto _cleanup_;
  128. }
  129. cvWaitKey(0);
  130. _cleanup_: // 标记使用,在汇编里用过,C语言,我还没见用过
  131. cvReleaseImage( &frame_copy );
  132. cvReleaseCapture( &capture );
  133. }
  134. cvDestroyWindow("result");
  135. return 0;
  136. }

detect_recog.cpp:

  1. #include "stdafx.h"
  2. #include "cv.h"
  3. #include "highgui.h"
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <assert.h>
  8. #include <math.h>
  9. #include <float.h>
  10. #include <limits.h>
  11. #include <time.h>
  12. #include <ctype.h>
  13. #include "detect_recog.h"
  14. #include <opencv2\contrib\contrib.hpp>
  15. #include <opencv2\core\core.hpp>
  16. #include <opencv2\highgui\highgui.hpp>
  17. #include <iostream>
  18. #include <fstream>
  19. #include <sstream>
  20. #include <stdio.h>
  21. #include <io.h>
  22. #include <direct.h>
  23. using namespace std;
  24. using namespace cv;
  25. //检测并圈出人脸,并将检测到的人脸进行判断属于训练图像中的哪一类
  26. void recog_and_draw( IplImage* img )
  27. {
  28. static CvScalar colors[] =
  29. {
  30. {{0,0,255}},
  31. {{0,128,255}},
  32. {{0,255,255}},
  33. {{0,255,0}},
  34. {{255,128,0}},
  35. {{255,255,0}},
  36. {{255,0,0}},
  37. {{255,0,255}}
  38. };
  39. IplImage *gray, *small_img;
  40. int i, j;
  41. gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
  42. small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
  43. cvRound (img->height/scale)), 8, 1 );
  44. cvCvtColor( img, gray, CV_BGR2GRAY ); // 彩色RGB图像转为灰度图像
  45. cvResize( gray, small_img, CV_INTER_LINEAR );
  46. cvEqualizeHist( small_img, small_img ); // 直方图均衡化
  47. cvClearMemStorage( storage );
  48. if( cascade )
  49. {
  50. double t = (double)cvGetTickCount();
  51. CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
  52. 1.1, 2, 0
  53. //|CV_HAAR_FIND_BIGGEST_OBJECT
  54. //|CV_HAAR_DO_ROUGH_SEARCH
  55. |CV_HAAR_DO_CANNY_PRUNING
  56. //|CV_HAAR_SCALE_IMAGE
  57. ,
  58. cvSize(30, 30) );
  59. t = (double)cvGetTickCount() - t; // 统计检测使用时间
  60. //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
  61. for( i = 0; i < (faces ? faces->total : 0); i++ )
  62. {
  63. CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 将faces数据从CvSeq转为CvRect
  64. CvMat small_img_roi;
  65. CvSeq* nested_objects;
  66. CvPoint center;
  67. CvScalar color = colors[i%8]; // 使用不同颜色绘制各个face,共八种色
  68. int radius;
  69. center.x = cvRound((r->x + r->width*0.5)*scale); // 找出faces中心
  70. center.y = cvRound((r->y + r->height*0.5)*scale);
  71. radius = cvRound((r->width + r->height)*0.25*scale);
  72. cvGetSubRect( small_img, &small_img_roi, *r );
  73. //截取检测到的人脸区域作为识别的图像
  74. IplImage *result;
  75. CvRect roi;
  76. roi = *r;
  77. result = cvCreateImage( cvSize(r->width, r->height), img->depth, img->nChannels );
  78. cvSetImageROI(img,roi);
  79. // 创建子图像
  80. cvCopy(img,result);
  81. cvResetImageROI(img);
  82. IplImage *resizeRes;
  83. CvSize dst_cvsize;
  84. dst_cvsize.width=(int)(100);
  85. dst_cvsize.height=(int)(100);
  86. resizeRes=cvCreateImage(dst_cvsize,result->depth,result->nChannels);
  87. //检测到的区域可能不是100x100大小,所以需要插值处理到统一大小,图像的大小可以自己指定的
  88. cvResize(result,resizeRes,CV_INTER_NN);
  89. IplImage* img1 = cvCreateImage(cvGetSize(resizeRes), IPL_DEPTH_8U, 1);//创建目标图像
  90. cvCvtColor(resizeRes,img1,CV_BGR2GRAY);//cvCvtColor(src,des,CV_BGR2GRAY)
  91. cvShowImage( "resize", resizeRes );
  92. cvCircle( img, center, radius, color, 3, 8, 0 ); // 从中心位置画圆,圈出脸部区域
  93. int predictedLabel = -1;
  94. Mat test = img1;
  95. //images[images.size() - 1] = test;
  96. model->train(images, labels);
  97. //如果调用read_img函数时 chdir将默认目录做了更改,所以output.jpg自己找一下吧
  98. imwrite("../ouput.jpg",test);
  99. //在这里对人脸进行判别
  100. double predicted_confidence = 0.0;
  101. model->predict(test,predictedLabel,predicted_confidence);
  102. if(predictedLabel == 0)
  103. cvText(img, "yes", r->x+r->width*0.5, r->y);
  104. else
  105. cvText(img, "no", r->x+r->width*0.5, r->y);
  106. //cout << "predict:"<<model->predict(test) << endl;
  107. cout << "predict:"<< predictedLabel << "\nconfidence:" << predicted_confidence << endl;
  108. if( !nested_cascade )
  109. continue;
  110. nested_objects = cvHaarDetectObjects( &small_img_roi, nested_cascade, storage,
  111. 1.1, 2, 0
  112. //|CV_HAAR_FIND_BIGGEST_OBJECT
  113. //|CV_HAAR_DO_ROUGH_SEARCH
  114. //|CV_HAAR_DO_CANNY_PRUNING
  115. //|CV_HAAR_SCALE_IMAGE
  116. ,
  117. cvSize(0, 0) );
  118. for( j = 0; j < (nested_objects ? nested_objects->total : 0); j++ )
  119. {
  120. CvRect* nr = (CvRect*)cvGetSeqElem( nested_objects, j );
  121. center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
  122. center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
  123. radius = cvRound((nr->width + nr->height)*0.25*scale);
  124. cvCircle( img, center, radius, color, 3, 8, 0 );
  125. }
  126. }
  127. }
  128. cvShowImage( "result", img );
  129. cvReleaseImage( &gray );
  130. cvReleaseImage( &small_img );
  131. }
  132. void cvText(IplImage* img, const char* text, int x, int y)
  133. {
  134. CvFont font;
  135. double hscale = 1.0;
  136. double vscale = 1.0;
  137. int linewidth = 2;
  138. cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX | CV_FONT_ITALIC,hscale,vscale,0,linewidth);
  139. CvScalar textColor =cvScalar(0,255,255);
  140. CvPoint textPos =cvPoint(x, y);
  141. cvPutText(img, text, textPos, &font,textColor);
  142. }
  143. Mat norm_0_255(cv::InputArray _src)
  144. {
  145. Mat src = _src.getMat();
  146. Mat dst;
  147. switch(src.channels())
  148. {
  149. case 1:
  150. cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC1);
  151. break;
  152. case 3:
  153. cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC3);
  154. break;
  155. default:
  156. src.copyTo(dst);
  157. break;
  158. }
  159. return dst;
  160. }
  161. //读取文件中的图像数据和类别,存入images和labels这两个容器
  162. void read_csv(const string &filename, vector<Mat> &images, vector<int> &labels, char separator)
  163. {
  164. std::ifstream file(filename.c_str(), ifstream::in);
  165. if(!file)
  166. {
  167. string error_message = "No valid input file was given.";
  168. CV_Error(CV_StsBadArg, error_message);
  169. }
  170. string line, path, classlabel;
  171. while(getline(file, line))
  172. {
  173. stringstream liness(line);
  174. getline(liness, path, separator);  //遇到分号就结束
  175. getline(liness, classlabel);     //继续从分号后面开始,遇到换行结束
  176. if(!path.empty() && !classlabel.empty())
  177. {
  178. images.push_back(imread(path, 0));
  179. labels.push_back(atoi(classlabel.c_str()));
  180. }
  181. }
  182. }
  183. bool read_img(vector<Mat> &images, vector<int> &labels)
  184. {
  185. long file;
  186. struct _finddata_t find;
  187. _chdir("./einfacedata/trainingdata/");
  188. if((file=_findfirst("*.*", &find))==-1L) {
  189. //printf("空白!/n");
  190. return false;
  191. }
  192. //fileNum = 0;
  193. //strcpy(fileName[fileNum], find.name);
  194. int i = 0;
  195. while(_findnext(file, &find)==0)
  196. {
  197. if(i == 0)
  198. {
  199. i++;
  200. continue;
  201. }
  202. images.push_back(imread(find.name, 0));
  203. labels.push_back(0);
  204. cout << find.name << endl;
  205. }
  206. _findclose(file);
  207. return true;
  208. }
  209. // 只是检测人脸,并将人脸圈出
  210. void detect_and_draw( IplImage* img )
  211. {
  212. static CvScalar colors[] =
  213. {
  214. {{0,0,255}},
  215. {{0,128,255}},
  216. {{0,255,255}},
  217. {{0,255,0}},
  218. {{255,128,0}},
  219. {{255,255,0}},
  220. {{255,0,0}},
  221. {{255,0,255}}
  222. };
  223. IplImage *gray, *small_img;
  224. int i, j;
  225. gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
  226. small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
  227. cvRound (img->height/scale)), 8, 1 );
  228. cvCvtColor( img, gray, CV_BGR2GRAY ); // 彩色RGB图像转为灰度图像
  229. cvResize( gray, small_img, CV_INTER_LINEAR );
  230. cvEqualizeHist( small_img, small_img ); // 直方图均衡化
  231. cvClearMemStorage( storage );
  232. if( cascade )
  233. {
  234. double t = (double)cvGetTickCount();
  235. CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
  236. 1.1, 2, 0
  237. //|CV_HAAR_FIND_BIGGEST_OBJECT
  238. //|CV_HAAR_DO_ROUGH_SEARCH
  239. |CV_HAAR_DO_CANNY_PRUNING
  240. //|CV_HAAR_SCALE_IMAGE
  241. ,
  242. cvSize(30, 30) );
  243. t = (double)cvGetTickCount() - t; // 统计检测使用时间
  244. printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
  245. for( i = 0; i < (faces ? faces->total : 0); i++ )
  246. {
  247. CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 将faces数据从CvSeq转为CvRect
  248. CvMat small_img_roi;
  249. CvSeq* nested_objects;
  250. CvPoint center;
  251. CvScalar color = colors[i%8]; // 使用不同颜色绘制各个face,共八种色
  252. int radius;
  253. center.x = cvRound((r->x + r->width*0.5)*scale); // 找出faces中心
  254. center.y = cvRound((r->y + r->height*0.5)*scale);
  255. radius = cvRound((r->width + r->height)*0.25*scale);
  256. cvCircle( img, center, radius, color, 3, 8, 0 ); // 从中心位置画圆,圈出脸部区域
  257. if( !nested_cascade )
  258. continue;
  259. cvGetSubRect( small_img, &small_img_roi, *r );
  260. nested_objects = cvHaarDetectObjects( &small_img_roi, nested_cascade, storage,
  261. 1.1, 2, 0
  262. //|CV_HAAR_FIND_BIGGEST_OBJECT
  263. //|CV_HAAR_DO_ROUGH_SEARCH
  264. //|CV_HAAR_DO_CANNY_PRUNING
  265. //|CV_HAAR_SCALE_IMAGE
  266. ,cvSize(0, 0) );
  267. for( j = 0; j < (nested_objects ? nested_objects->total : 0); j++ )
  268. {
  269. CvRect* nr = (CvRect*)cvGetSeqElem( nested_objects, j );
  270. center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
  271. center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
  272. radius = cvRound((nr->width + nr->height)*0.25*scale);
  273. cvCircle( img, center, radius, color, 3, 8, 0 );
  274. }
  275. }
  276. }
  277. cvShowImage( "result", img );
  278. cvReleaseImage( &gray );
  279. cvReleaseImage( &small_img );
  280. }

detect_recog.h:

  1. #include "stdafx.h"
  2. #include "cv.h"
  3. #include "highgui.h"
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <assert.h>
  8. #include <math.h>
  9. #include <float.h>
  10. #include <limits.h>
  11. #include <time.h>
  12. #include <ctype.h>
  13. //////////////////////////////////s///////////////////////////////////
  14. #include <opencv2\contrib\contrib.hpp>
  15. #include <opencv2\core\core.hpp>
  16. #include <opencv2\highgui\highgui.hpp>
  17. #include <iostream>
  18. #include <fstream>
  19. #include <sstream>
  20. using namespace std;
  21. using namespace cv;
  22. #ifndef DETECT_RECOG_H
  23. #define DETECT_RECOG_H
  24. extern CvMemStorage* storage;
  25. extern CvHaarClassifierCascade* cascade;
  26. extern CvHaarClassifierCascade* nested_cascade;
  27. extern int use_nested_cascade;
  28. extern const char* cascade_name;
  29. extern const char* nested_cascade_name;
  30. extern double scale;
  31. extern cv::Ptr<cv::FaceRecognizer> model;
  32. extern vector<Mat> images;
  33. extern vector<int> labels;
  34. void detect_and_draw( IplImage* img ); // 检测和绘画
  35. void recog_and_draw( IplImage* img ); // 检测和绘画
  36. void read_csv(const string &filename, vector<Mat> &images, vector<int> &labels, char separator = ';');
  37. bool read_img(vector<Mat> &images, vector<int> &labels);
  38. Mat norm_0_255(cv::InputArray _src);
  39. void cvText(IplImage* img, const char* text, int x, int y);
  40. #endif